[DOC] move reference doc in subfolder

This commit is contained in:
Géry Debongnie
2019-10-28 09:03:55 +01:00
parent c2adb429bd
commit 3035a9f009
18 changed files with 163 additions and 106 deletions
+6 -6
View File
@@ -54,8 +54,8 @@ const app = new App({ qweb: new QWeb() });
app.mount(document.body); app.mount(document.body);
``` ```
Note that the counter component is made reactive with the [`useState` hook](doc/hooks.md#usestate). Note that the counter component is made reactive with the [`useState` hook](doc/reference/hooks.md#usestate).
Also, all examples here uses the [`xml` helper](doc/tags.md#xml-tag) to define inline templates. Also, all examples here uses the [`xml` helper](doc/reference/tags.md#xml-tag) to define inline templates.
But this is not mandatory, many applications will load templates separately. But this is not mandatory, many applications will load templates separately.
More interesting examples can be found on the More interesting examples can be found on the
@@ -93,8 +93,8 @@ A complete documentation for Owl can be found here:
The most important sections are: The most important sections are:
- [Quick Start](doc/quick_start.md) - [Quick Start](doc/quick_start.md)
- [Component](doc/component.md) - [Component](doc/reference/component.md)
- [Hooks](doc/hooks.md) - [Hooks](doc/reference/hooks.md)
Found an issue in the documentation? A broken link? Some outdated information? Found an issue in the documentation? A broken link? Some outdated information?
Submit a PR! Submit a PR!
@@ -128,7 +128,7 @@ Owl components in an application are used to define a (dynamic) tree of componen
``` ```
**Environment:** the root component is special: it is created with an environment, **Environment:** the root component is special: it is created with an environment,
which should contain a [`QWeb` instance](doc/qweb.md). The environment is then automatically which should contain a [`QWeb` instance](doc/reference/qweb.md). The environment is then automatically
propagated to each sub components (and accessible in the `this.env` property). propagated to each sub components (and accessible in the `this.env` property).
```js ```js
@@ -255,7 +255,7 @@ class Parent extends Component {
In this example, the `OrderLine` component trigger a `add-to-order` event. This In this example, the `OrderLine` component trigger a `add-to-order` event. This
will generate a DOM event which will bubble along the DOM tree. It will then be will generate a DOM event which will bubble along the DOM tree. It will then be
intercepted by the parent component, which will then get the line (from the intercepted by the parent component, which will then get the line (from the
`detail` key) and then increment its quantity. See the section on [event handling](doc/component.md#event-handling) `detail` key) and then increment its quantity. See the section on [event handling](doc/reference/component.md#event-handling)
for more details on how events work. for more details on how events work.
Note that this example would have also worked if the `OrderLine` component Note that this example would have also worked if the `OrderLine` component
+4 -4
View File
@@ -79,7 +79,7 @@ additional tools, we made a lot of effort to make the most of the web platform.
For example, Owl uses the standard `xml` parser that comes with every browser. For example, Owl uses the standard `xml` parser that comes with every browser.
Because of that, Owl did not have to write its own template parser. Another Because of that, Owl did not have to write its own template parser. Another
example is the [`xml`](tags.md#xml-tag) tag helper function, which makes use of example is the [`xml`](reference/tags.md#xml-tag) tag helper function, which makes use of
native template literals to allow in a natural way to write `xml` templates native template literals to allow in a natural way to write `xml` templates
directly in the javascript code. This can be easily integrated with editor directly in the javascript code. This can be easily integrated with editor
plugins to have autocompletion inside the template. plugins to have autocompletion inside the template.
@@ -127,7 +127,7 @@ structured than a template language. Note that the tooling is quite impressive:
there is a syntax highlighter for jsx here on github! there is a syntax highlighter for jsx here on github!
By comparison, here is the equivalent Owl component, written with the By comparison, here is the equivalent Owl component, written with the
[`xml`](tags.md#xml-tag) tag helper: [`xml`](reference/tags.md#xml-tag) tag helper:
```js ```js
class Clock extends Component { class Clock extends Component {
@@ -251,7 +251,7 @@ keeps track of who get data, and retrigger a render when it was changed.
Owl store is a little bit like a mix of redux and vuex: it has actions (but not Owl store is a little bit like a mix of redux and vuex: it has actions (but not
mutations), and like VueX, it keeps track of the state changes. However, it does mutations), and like VueX, it keeps track of the state changes. However, it does
not notify a component when the state changes. Instead, components need to connect not notify a component when the state changes. Instead, components need to connect
to the store like in redux, with the `useStore` hook (see the [store documentation](store.md#connecting-a-component)). to the store like in redux, with the `useStore` hook (see the [store documentation](reference/store.md#connecting-a-component)).
```javascript ```javascript
const actions = { const actions = {
@@ -314,7 +314,7 @@ This work is based on the new ideas introduced by React hooks.
From the way React and Vue introduce their hooks, it may look like hooks are not From the way React and Vue introduce their hooks, it may look like hooks are not
compatible with class components. However, this is not the case, as shown by compatible with class components. However, this is not the case, as shown by
Owl [hooks](hooks.md). They are inspired by both React and Vue. For example, Owl [hooks](reference/hooks.md). They are inspired by both React and Vue. For example,
the `useState` hook is named after React, but its API is closer to the `reactive` the `useState` hook is named after React, but its API is closer to the `reactive`
Vue hook. Vue hook.
+13 -13
View File
@@ -50,19 +50,19 @@ Note that for convenience, the `useState` hook is also exported at the root of t
## Reference ## Reference
- [Animations](animations.md) - [Animations](reference/animations.md)
- [Component](component.md) - [Component](reference/component.md)
- [Context](context.md) - [Context](reference/context.md)
- [Event Bus](event_bus.md) - [Event Bus](reference/event_bus.md)
- [Hooks](hooks.md) - [Hooks](reference/hooks.md)
- [Misc](misc.md) - [Misc](reference/misc.md)
- [Observer](observer.md) - [Observer](reference/observer.md)
- [QWeb](qweb.md) - [QWeb](reference/qweb.md)
- [Router](router.md) - [Router](reference/router.md)
- [Store](store.md) - [Store](reference/store.md)
- [Tags](tags.md) - [Tags](reference/tags.md)
- [Utils](utils.md) - [Utils](reference/utils.md)
- [Virtual DOM](vdom.md) - [Virtual DOM](architecture/vdom.md)
## Learning Resources ## Learning Resources
@@ -986,7 +986,7 @@ of the props. Here is how it works in Owl:
- `props` key is a static key (so, different from `this.props` in a component instance) - `props` key is a static key (so, different from `this.props` in a component instance)
- it is optional: it is ok for a component to not define a `props` key. - it is optional: it is ok for a component to not define a `props` key.
- props are validated whenever a component is created/updated - props are validated whenever a component is created/updated
- props are only validated in `dev` mode (see [tooling page](tooling.md#development-mode)) - props are only validated in `dev` mode (see [tooling page](../tooling.md#development-mode))
- if a key does not match the description, an error is thrown - if a key does not match the description, an error is thrown
- it validates keys defined in (static) `props`. Additional keys given by the - it validates keys defined in (static) `props`. Additional keys given by the
parent will cause an error. parent will cause an error.
+1 -1
View File
@@ -121,7 +121,7 @@ Its API is quite simple:
``` ```
- **`render(name, context, extra)`**: renders a template. This returns a `vnode`, - **`render(name, context, extra)`**: renders a template. This returns a `vnode`,
which is a virtual representation of the DOM (see [vdom doc](vdom.md)). which is a virtual representation of the DOM (see [vdom doc](../architecture/vdom.md)).
```js ```js
const vnode = qweb.render("App", component); const vnode = qweb.render("App", component);
+1 -1
View File
@@ -9,7 +9,7 @@
Tags are very small helpers to make it easy to write inline templates. There is Tags are very small helpers to make it easy to write inline templates. There is
only one currently available tag: `xml`, but we plan to add other tags later, only one currently available tag: `xml`, but we plan to add other tags later,
such as a `css` tag, which will be used to write [single file components](tooling.md#single-file-component). such as a `css` tag, which will be used to write [single file components](../tooling.md#single-file-component).
## XML tag ## XML tag
+2 -2
View File
@@ -64,8 +64,8 @@ It is very useful to group code by feature instead of by type of file. It makes
it easier to scale application to larger size. it easier to scale application to larger size.
To do so, Owl currently has a small helper that makes it easy to define a To do so, Owl currently has a small helper that makes it easy to define a
template inside a javascript (or typescript) file: the [`xml`](tags.md#xml-tag) template inside a javascript (or typescript) file: the [`xml`](reference/tags.md#xml-tag)
helper. With this, a template is automatically registered to [QWeb](qweb.md). helper. With this, a template is automatically registered to [QWeb](reference/qweb.md).
This means that the template and the javascript code can be defined in the same This means that the template and the javascript code can be defined in the same
file. It is not currently possible to add css to the same file, but Owl may file. It is not currently possible to add css to the same file, but Owl may
+135 -78
View File
@@ -6,36 +6,9 @@
*/ */
import * as fs from "fs"; import * as fs from "fs";
const LINK_REGEXP = /\[([^\[]+)\]\(([^\)]+)\)/g; //--------------------------------------------------------------------------
const HEADING_REGEXP = /\n(#+\s*)(.*)/g; // Helpers
//--------------------------------------------------------------------------
// files to be checked
function getFiles(): string[] {
const DOCFILES = fs.readdirSync("doc").map(f => `doc/${f}`);
const MAINREADME = "README.md";
DOCFILES.push("roadmap.md", MAINREADME);
return DOCFILES;
}
test("All markdown links work", () => {
let linkNumber = 0;
let invalidLinkNumber = 0;
const files = getFiles();
const data = readDocData(files);
for (let file of data) {
for (let link of file.links) {
// DEBUG: uncomment next line
// console.warn(`Checking "${link.name}" in "${file.name}"`);
linkNumber++;
if (!isLinkValid(link, file, data)) {
console.warn(`Invalid Link: "${link.name}" in "${file.name}"`);
invalidLinkNumber++;
}
}
}
expect(invalidLinkNumber).toBe(0);
expect(linkNumber).toBeGreaterThan(10);
});
interface MarkDownLink { interface MarkDownLink {
name: string; name: string;
@@ -49,38 +22,130 @@ interface MarkDownSection {
interface FileData { interface FileData {
name: string; name: string;
path: string[];
fullName: string
links: MarkDownLink[]; links: MarkDownLink[];
sections: MarkDownSection[]; sections: MarkDownSection[];
} }
function isLinkValid(link: MarkDownLink, current: FileData, files: FileData[]): boolean { 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);
}
/**
* 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);
}
const LOCAL_FILES = ['LICENSE'];
export function isLinkValid(link: MarkDownLink, current: FileData, files: FileData[]): boolean {
if (link.link.startsWith("http")) { if (link.link.startsWith("http")) {
// no check on external links // no check on external links
return true; return true;
} }
const parts = link.link.split("#"); // Step 1: extract path, name, hash
const currentParts = current.name.split("/"); // path = ['doc', 'architecture]
const path = currentParts.length > 1 ? currentParts[0] + "/" : ""; // name = 'rendering.md'
const fullName = path + parts[0]; // hash = 'blabla' (or '' if no hash)
if (parts.length === 1) {
// no # in url const parts = link.link.split('#');
if (parts[0].endsWith(".md")) { const hash = parts[1] || '';
// it is a local md file let name;
if (!files.find(f => f.name === fullName)) { 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 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; return false;
}
} }
} else {
const file = parts[0] === "" ? current : files.find(f => f.name === fullName); // Step 5: if necessary, check if there is a corresponding link inside the target
if (!file) { // link name
return false; if (hash) {
if (!target.sections.find(s => s.slug === hash)) {
return false;
}
} }
if (!file.sections.find(s => s.slug === parts[1])) {
return false; return true;
}
}
return true;
} }
// adapted from https://medium.com/@mhagemann/the-ultimate-way-to-slugify-a-url-string-in-javascript-b8e4a0d849e1 // adapted from https://medium.com/@mhagemann/the-ultimate-way-to-slugify-a-url-string-in-javascript-b8e4a0d849e1
function slugify(str) { function slugify(str) {
const a = "àáäâãåăæçèéëêǵḧìíïîḿńǹñòóöôœøṕŕßśșțùúüûǘẃẍÿź·_,:;"; const a = "àáäâãåăæçèéëêǵḧìíïîḿńǹñòóöôœøṕŕßśșțùúüûǘẃẍÿź·_,:;";
@@ -99,33 +164,25 @@ function slugify(str) {
.replace(/-+$/, ""); // Trim - from end of text .replace(/-+$/, ""); // Trim - from end of text
} }
function readDocData(files: string[]): FileData[] {
const result: FileData[] = [];
for (let file of files) {
const fileData: FileData = {
name: file,
links: [],
sections: []
};
const content = fs.readFileSync(file, { 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);
result.push(fileData); //--------------------------------------------------------------------------
} // Test
return result; //--------------------------------------------------------------------------
}
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);
});