mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[DOC] reorganize doc, unskip test, fix some links
This commit is contained in:
committed by
Aaron Bohy
parent
63fbcf99fd
commit
702fb3b253
@@ -4,180 +4,180 @@
|
||||
* We define here a test to make sure that there are no dead link in the Owl
|
||||
* documentation.
|
||||
*/
|
||||
// import * as fs from "fs";
|
||||
import * as fs from "fs";
|
||||
|
||||
// //--------------------------------------------------------------------------
|
||||
// // Helpers
|
||||
// //--------------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------------
|
||||
// Helpers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
// interface MarkDownLink {
|
||||
// name: string;
|
||||
// link: string;
|
||||
// }
|
||||
interface MarkDownLink {
|
||||
name: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
// interface MarkDownSection {
|
||||
// name: string;
|
||||
// slug: string;
|
||||
// }
|
||||
interface MarkDownSection {
|
||||
name: string;
|
||||
slug: string;
|
||||
}
|
||||
|
||||
// interface FileData {
|
||||
// name: string;
|
||||
// path: string[];
|
||||
// fullName: string;
|
||||
// links: MarkDownLink[];
|
||||
// sections: MarkDownSection[];
|
||||
// }
|
||||
interface FileData {
|
||||
name: string;
|
||||
path: string[];
|
||||
fullName: string;
|
||||
links: MarkDownLink[];
|
||||
sections: MarkDownSection[];
|
||||
}
|
||||
|
||||
// const LINK_REGEXP = /\[([^\[]+)\]\(([^\)]+)\)/g;
|
||||
// const HEADING_REGEXP = /\n(#+\s*)(.*)/g;
|
||||
const LINK_REGEXP = /\[([^\[]+)\]\(([^\)]+)\)/g;
|
||||
const HEADING_REGEXP = /\n(#+\s*)(.*)/g;
|
||||
|
||||
// export function addMardownData(fileData): void {
|
||||
// const sep = fileData.path.length > 0 ? "/" : "";
|
||||
// const fullName = fileData.path.join("/") + sep + fileData.name;
|
||||
// const content = fs.readFileSync(fullName, { encoding: "utf8" });
|
||||
// let m;
|
||||
// // get links info
|
||||
// do {
|
||||
// m = LINK_REGEXP.exec(content);
|
||||
// if (m) {
|
||||
// fileData.links.push({ name: m[0], link: m[2] });
|
||||
// }
|
||||
// } while (m);
|
||||
// // get sections info
|
||||
// do {
|
||||
// m = HEADING_REGEXP.exec(content);
|
||||
// if (m) {
|
||||
// fileData.sections.push({ name: m[0], slug: slugify(m[2]) });
|
||||
// }
|
||||
// } while (m);
|
||||
// }
|
||||
export function addMarkdownData(fileData: FileData): void {
|
||||
const sep = fileData.path.length > 0 ? "/" : "";
|
||||
const fullName = fileData.path.join("/") + sep + fileData.name;
|
||||
const content = fs.readFileSync(fullName, { encoding: "utf8" });
|
||||
let m;
|
||||
// get links info
|
||||
do {
|
||||
m = LINK_REGEXP.exec(content);
|
||||
if (m) {
|
||||
fileData.links.push({ name: m[0], link: m[2] });
|
||||
}
|
||||
} while (m);
|
||||
// get sections info
|
||||
do {
|
||||
m = HEADING_REGEXP.exec(content);
|
||||
if (m) {
|
||||
fileData.sections.push({ name: m[0], slug: slugify(m[2]) });
|
||||
}
|
||||
} while (m);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Returns a list of FileData corresponding to all files that need to be
|
||||
// * validated.
|
||||
// */
|
||||
// function getFiles(path: string[] = []): FileData[] {
|
||||
// if (path.length === 0) {
|
||||
// const baseFiles: FileData[] = [
|
||||
// { name: "README.md", path: [], links: [], sections: [], fullName: "README.md" },
|
||||
// { name: "roadmap.md", path: [], links: [], sections: [], fullName: "roadmap.md" },
|
||||
// ];
|
||||
// const rest = getFiles(["doc"]);
|
||||
// const result = baseFiles.concat(rest);
|
||||
// result.forEach(addMardownData);
|
||||
// return result;
|
||||
// }
|
||||
// const files = fs.readdirSync(path.join("/"), { withFileTypes: true }).map((f) => {
|
||||
// if (f.isDirectory()) {
|
||||
// return getFiles(path.concat(f.name));
|
||||
// }
|
||||
// const fullName = path.join("/") + (path.length > 0 ? "/" : "") + f.name;
|
||||
// return [
|
||||
// {
|
||||
// name: f.name,
|
||||
// path,
|
||||
// links: [],
|
||||
// sections: [],
|
||||
// fullName,
|
||||
// },
|
||||
// ];
|
||||
// });
|
||||
// return Array.prototype.concat(...files);
|
||||
// }
|
||||
/**
|
||||
* Returns a list of FileData corresponding to all files that need to be
|
||||
* validated.
|
||||
*/
|
||||
function getFiles(path: string[] = []): FileData[] {
|
||||
if (path.length === 0) {
|
||||
const baseFiles: FileData[] = [
|
||||
{ name: "README.md", path: [], links: [], sections: [], fullName: "README.md" },
|
||||
{ name: "roadmap.md", path: [], links: [], sections: [], fullName: "roadmap.md" },
|
||||
];
|
||||
const rest = getFiles(["doc"]);
|
||||
const result = baseFiles.concat(rest);
|
||||
result.forEach(addMarkdownData);
|
||||
return result;
|
||||
}
|
||||
const files = fs.readdirSync(path.join("/"), { withFileTypes: true }).map((f) => {
|
||||
if (f.isDirectory()) {
|
||||
return getFiles(path.concat(f.name));
|
||||
}
|
||||
const fullName = path.join("/") + (path.length > 0 ? "/" : "") + f.name;
|
||||
return [
|
||||
{
|
||||
name: f.name,
|
||||
path,
|
||||
links: [],
|
||||
sections: [],
|
||||
fullName,
|
||||
},
|
||||
];
|
||||
});
|
||||
return Array.prototype.concat(...files);
|
||||
}
|
||||
|
||||
// const LOCAL_FILES = ["LICENSE", "tools/debug.js"];
|
||||
// export function isLinkValid(link: MarkDownLink, current: FileData, files: FileData[]): boolean {
|
||||
// if (link.link.startsWith("http")) {
|
||||
// // no check on external links
|
||||
// return true;
|
||||
// }
|
||||
// // Step 1: extract path, name, hash
|
||||
// // path = ['doc', 'architecture]
|
||||
// // name = 'rendering.md'
|
||||
// // hash = 'blabla' (or '' if no hash)
|
||||
const LOCAL_FILES = ["LICENSE", "CHANGELOG.md"];
|
||||
export function isLinkValid(link: MarkDownLink, current: FileData, files: FileData[]): boolean {
|
||||
if (link.link.startsWith("http")) {
|
||||
// no check on external links
|
||||
return true;
|
||||
}
|
||||
// Step 1: extract path, name, hash
|
||||
// path = ['doc', 'architecture]
|
||||
// name = 'rendering.md'
|
||||
// hash = 'blabla' (or '' if no hash)
|
||||
|
||||
// const parts = link.link.split("#");
|
||||
// const hash = parts[1] || "";
|
||||
// let name;
|
||||
// let path;
|
||||
// if (parts[0]) {
|
||||
// let temp = parts[0].split("/");
|
||||
// name = temp[temp.length - 1];
|
||||
// temp.splice(-1);
|
||||
// path = current.path.slice();
|
||||
// for (let elem of temp) {
|
||||
// if (elem === "..") {
|
||||
// path.splice(-1);
|
||||
// } else if (elem !== ".") {
|
||||
// path.push(elem);
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// // there are no file name, so this is a relative link to the current file
|
||||
// name = current.name;
|
||||
// path = current.path;
|
||||
// }
|
||||
const parts = link.link.split("#");
|
||||
const hash = parts[1] || "";
|
||||
let name;
|
||||
let path;
|
||||
if (parts[0]) {
|
||||
let temp = parts[0].split("/");
|
||||
name = temp[temp.length - 1];
|
||||
temp.splice(-1);
|
||||
path = current.path.slice();
|
||||
for (let elem of temp) {
|
||||
if (elem === "..") {
|
||||
path.splice(-1);
|
||||
} else if (elem !== ".") {
|
||||
path.push(elem);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// there are no file name, so this is a relative link to the current file
|
||||
name = current.name;
|
||||
path = current.path;
|
||||
}
|
||||
|
||||
// // Step 2: build normalized link file name
|
||||
// const linkFullName = path.join("/") + (path.length > 0 ? "/" : "") + name;
|
||||
// Step 2: build normalized link file name
|
||||
const linkFullName = path.join("/") + (path.length > 0 ? "/" : "") + name;
|
||||
|
||||
// // Step 3: check link name against white list of local files
|
||||
// if (LOCAL_FILES.includes(linkFullName)) {
|
||||
// return true;
|
||||
// }
|
||||
// Step 3: check link name against white list of local files
|
||||
if (LOCAL_FILES.includes(linkFullName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// // Step 4: check if there is a matching file
|
||||
// let target: FileData | undefined = files.find((f) => f.fullName === linkFullName);
|
||||
// if (!target) {
|
||||
// return false;
|
||||
// }
|
||||
// Step 4: check if there is a matching file
|
||||
let target: FileData | undefined = files.find((f) => f.fullName === linkFullName);
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// // Step 5: if necessary, check if there is a corresponding link inside the target
|
||||
// // link name
|
||||
// if (hash) {
|
||||
// if (!target.sections.find((s) => s.slug === hash)) {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// Step 5: if necessary, check if there is a corresponding link inside the target
|
||||
// link name
|
||||
if (hash) {
|
||||
if (!target.sections.find((s) => s.slug === hash)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// return true;
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
// // adapted from https://medium.com/@mhagemann/the-ultimate-way-to-slugify-a-url-string-in-javascript-b8e4a0d849e1
|
||||
// function slugify(str) {
|
||||
// const a = "àáäâãåăæçèéëêǵḧìíïîḿńǹñòóöôœøṕŕßśșțùúüûǘẃẍÿź·_,:;";
|
||||
// const b = "aaaaaaaaceeeeghiiiimnnnooooooprssstuuuuuwxyz-----";
|
||||
// const p = new RegExp(a.split("").join("|"), "g");
|
||||
// return str
|
||||
// .toString()
|
||||
// .toLowerCase()
|
||||
// .replace(/\//g, "") // remove /
|
||||
// .replace(/\s+/g, "-") // Replace spaces with -
|
||||
// .replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters
|
||||
// .replace(/&/g, "-and-") // Replace & with ‘and’
|
||||
// .replace(/[^\w\-]+/g, "") // Remove all non-word characters
|
||||
// .replace(/\-\-+/g, "-") // Replace multiple - with single -
|
||||
// .replace(/^-+/, "") // Trim - from start of text
|
||||
// .replace(/-+$/, ""); // Trim - from end of text
|
||||
// }
|
||||
// adapted from https://medium.com/@mhagemann/the-ultimate-way-to-slugify-a-url-string-in-javascript-b8e4a0d849e1
|
||||
function slugify(str: string): string {
|
||||
const a = "àáäâãåăæçèéëêǵḧìíïîḿńǹñòóöôœøṕŕßśșțùúüûǘẃẍÿź·_,:;";
|
||||
const b = "aaaaaaaaceeeeghiiiimnnnooooooprssstuuuuuwxyz-----";
|
||||
const p = new RegExp(a.split("").join("|"), "g");
|
||||
return str
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.replace(/\//g, "") // remove /
|
||||
.replace(/\s+/g, "-") // Replace spaces with -
|
||||
.replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters
|
||||
.replace(/&/g, "-and-") // Replace & with ‘and’
|
||||
.replace(/[^\w\-]+/g, "") // Remove all non-word characters
|
||||
.replace(/\-\-+/g, "-") // Replace multiple - with single -
|
||||
.replace(/^-+/, "") // Trim - from start of text
|
||||
.replace(/-+$/, ""); // Trim - from end of text
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Test
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
test.skip("All markdown links work", () => {
|
||||
// let linkNumber = 0;
|
||||
// let invalidLinkNumber = 0;
|
||||
// const data = getFiles();
|
||||
// for (let file of data) {
|
||||
// for (let link of file.links) {
|
||||
// linkNumber++;
|
||||
// if (!isLinkValid(link, file, data)) {
|
||||
// console.warn(`Invalid Link: "${link.name}" in "${file.name}"`);
|
||||
// invalidLinkNumber++;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// expect(invalidLinkNumber).toBe(0);
|
||||
// expect(linkNumber).toBeGreaterThan(10);
|
||||
test("All markdown links work", () => {
|
||||
let linkNumber = 0;
|
||||
let invalidLinkNumber = 0;
|
||||
const data = getFiles();
|
||||
for (let file of data) {
|
||||
for (let link of file.links) {
|
||||
linkNumber++;
|
||||
if (!isLinkValid(link, file, data)) {
|
||||
console.warn(`Invalid Link: "${link.name}" in "${file.name}"`);
|
||||
invalidLinkNumber++;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect(invalidLinkNumber).toBe(0);
|
||||
expect(linkNumber).toBeGreaterThan(10);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user