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
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
# ChangeLog
|
# Changelog
|
||||||
|
|
||||||
This document contains an overview of all changes between Owl 1.x and
|
This document contains an overview of all changes between Owl 1.x and
|
||||||
Owl 2.x, with some pointers on how to update the code.
|
Owl 2.x, with some pointers on how to update the code.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<h1 align="center">🦉 <a href="https://odoo.github.io/owl/">OWL Framework</a> 🦉</h1>
|
<h1 align="center">🦉 <a href="https://odoo.github.io/owl/">Owl Framework</a> 🦉</h1>
|
||||||
|
|
||||||
[](https://www.gnu.org/licenses/lgpl-3.0)
|
[](https://www.gnu.org/licenses/lgpl-3.0)
|
||||||
[](https://badge.fury.io/js/@odoo%2Fowl)
|
[](https://badge.fury.io/js/@odoo%2Fowl)
|
||||||
@@ -6,10 +6,12 @@
|
|||||||
|
|
||||||
_Class based components with hooks, reactive state and concurrent mode_
|
_Class based components with hooks, reactive state and concurrent mode_
|
||||||
|
|
||||||
|
**Try it online!** you can experiment with the Owl framework in an online [playground](https://odoo.github.io/owl/playground).
|
||||||
|
|
||||||
## Project Overview
|
## Project Overview
|
||||||
|
|
||||||
The Odoo Web Library (OWL) is a smallish (~<20kb gzipped) UI framework intended to
|
The Odoo Web Library (Owl) is a smallish (~<20kb gzipped) UI framework built by
|
||||||
be the basis for the [Odoo](https://www.odoo.com/) Web Client. Owl is a modern
|
[Odoo](https://www.odoo.com/) for its products. Owl is a modern
|
||||||
framework, written in Typescript, taking the best ideas from React and Vue in a
|
framework, written in Typescript, taking the best ideas from React and Vue in a
|
||||||
simple and consistent way. Owl's main features are:
|
simple and consistent way. Owl's main features are:
|
||||||
|
|
||||||
@@ -17,39 +19,26 @@ simple and consistent way. Owl's main features are:
|
|||||||
- a reactivity system based on hooks,
|
- a reactivity system based on hooks,
|
||||||
- concurrent mode by default,
|
- concurrent mode by default,
|
||||||
|
|
||||||
Owl components are defined with ES6 classes, they use QWeb templates, an
|
Owl components are defined with ES6 classes and xml templates, uses an
|
||||||
underlying virtual DOM, integrates beautifully with hooks, and the rendering is
|
underlying virtual DOM, integrates beautifully with hooks, and the rendering is
|
||||||
asynchronous.
|
asynchronous.
|
||||||
|
|
||||||
**Try it online!** An online playground is available at
|
Quick links:
|
||||||
[https://odoo.github.io/owl/playground](https://odoo.github.io/owl/playground)
|
|
||||||
to let you experiment with the Owl framework. There are some code examples to
|
|
||||||
showcase some interesting features.
|
|
||||||
|
|
||||||
Owl is currently stable. Possible future changes are explained in the
|
- [documentation](#documentation),
|
||||||
[roadmap](roadmap.md).
|
- [changelog](CHANGELOG.md) (from Owl 1.x to 2.x),
|
||||||
|
- [playground](https://odoo.github.io/owl/playground)
|
||||||
## Why Owl?
|
|
||||||
|
|
||||||
Why did Odoo decide to make Yet Another Framework? This is really a question
|
|
||||||
that deserves [a long answer](doc/miscellaneous/why_owl.md). But in short, we believe that
|
|
||||||
while the current state of the art frameworks are excellent, they are not
|
|
||||||
optimized for our use case, and there is still room for something else.
|
|
||||||
|
|
||||||
If you are interested in a comparison with React or Vue, you will
|
|
||||||
find some more additional information [here](doc/miscellaneous/comparison.md).
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
Here is a short example to illustrate interactive components:
|
Here is a short example to illustrate interactive components:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const { Component, useState, mount } = owl;
|
const { Component, useState, mount, xml } = owl;
|
||||||
const { xml } = owl.tags;
|
|
||||||
|
|
||||||
class Counter extends Component {
|
class Counter extends Component {
|
||||||
static template = xml`
|
static template = xml`
|
||||||
<button t-on-click="state.value++">
|
<button t-on-click="() => state.value++">
|
||||||
Click Me! [<t t-esc="state.value"/>]
|
Click Me! [<t t-esc="state.value"/>]
|
||||||
</button>`;
|
</button>`;
|
||||||
|
|
||||||
@@ -66,7 +55,7 @@ class App extends Component {
|
|||||||
static components = { Counter };
|
static components = { Counter };
|
||||||
}
|
}
|
||||||
|
|
||||||
mount(App, { target: document.body });
|
mount(App, document.body);
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the counter component is made reactive with the [`useState` hook](doc/reference/hooks.md#usestate).
|
Note that the counter component is made reactive with the [`useState` hook](doc/reference/hooks.md#usestate).
|
||||||
@@ -76,41 +65,56 @@ 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
|
||||||
[playground](https://odoo.github.io/owl/playground) application.
|
[playground](https://odoo.github.io/owl/playground) application.
|
||||||
|
|
||||||
## Design Principles
|
|
||||||
|
|
||||||
OWL is designed to be used in highly dynamic applications where changing
|
|
||||||
requirements are common and code needs to be maintained by large teams.
|
|
||||||
|
|
||||||
- **XML based**: templates are based on the XML format, which allows interesting
|
|
||||||
applications. For example, they could be stored in a database and modified
|
|
||||||
dynamically with `xpaths`.
|
|
||||||
- **templates compilation in the browser**: this may not be a good fit for all
|
|
||||||
applications, but if you need to generate dynamically user interfaces in the
|
|
||||||
browser, this is very powerful. For example, a generic form view component
|
|
||||||
could generate a specific form user interface for each various models, from a XML view.
|
|
||||||
- **no toolchain required**: this is extremely useful for some applications, if,
|
|
||||||
for various reasons (security/deployment/dynamic modules/specific assets tools),
|
|
||||||
it is not ok to use standard web tools based on `npm`.
|
|
||||||
|
|
||||||
Owl is not designed to be fast nor small (even though it is quite good on those
|
|
||||||
two topics). It is a no nonsense framework to build applications. There is only
|
|
||||||
one way to define components (with classes). There is no black magic. It just
|
|
||||||
works.
|
|
||||||
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
A complete documentation for Owl can be found here:
|
### Learning Owl
|
||||||
|
|
||||||
- [Main documentation page](doc/readme.md).
|
Are you new to Owl? This is the place to start!
|
||||||
|
|
||||||
Some of the most important pages are:
|
- [Tutorial: create a TodoList application](doc/learning/tutorial_todoapp.md)
|
||||||
|
- [Quick Overview](doc/learning/overview.md)
|
||||||
- [Tutorial: TodoList application](doc/learning/tutorial_todoapp.md)
|
|
||||||
- [How to start an Owl project](doc/learning/quick_start.md)
|
- [How to start an Owl project](doc/learning/quick_start.md)
|
||||||
- [QWeb templating language](doc/reference/qweb_templating_language.md)
|
- [How to test Components](doc/learning/how_to_test.md)
|
||||||
|
- [How to write Single File Components](doc/learning/how_to_write_sfc.md)
|
||||||
|
|
||||||
|
### Reference
|
||||||
|
|
||||||
|
You will find here a complete reference of every feature, class or object
|
||||||
|
provided by Owl.
|
||||||
|
|
||||||
|
- [Animations](doc/reference/animations.md)
|
||||||
|
- [Browser](doc/reference/browser.md)
|
||||||
- [Component](doc/reference/component.md)
|
- [Component](doc/reference/component.md)
|
||||||
|
- [Content](doc/reference/content.md)
|
||||||
|
- [Concurrency Model](doc/reference/concurrency_model.md)
|
||||||
|
- [Configuration](doc/reference/config.md)
|
||||||
|
- [Context](doc/reference/context.md)
|
||||||
|
- [Environment](doc/reference/environment.md)
|
||||||
|
- [Event Bus](doc/reference/event_bus.md)
|
||||||
|
- [Event Handling](doc/reference/event_handling.md)
|
||||||
|
- [Error Handling](doc/reference/error_handling.md)
|
||||||
- [Hooks](doc/reference/hooks.md)
|
- [Hooks](doc/reference/hooks.md)
|
||||||
|
- [Mounting a component](doc/reference/mounting.md)
|
||||||
|
- [Miscellaneous Components](doc/reference/misc.md)
|
||||||
|
- [Observer](doc/reference/observer.md)
|
||||||
|
- [Props](doc/reference/props.md)
|
||||||
|
- [Props Validation](doc/reference/props_validation.md)
|
||||||
|
- [QWeb Templating Language](doc/reference/qweb_templating_language.md)
|
||||||
|
- [QWeb Engine](doc/reference/qweb_engine.md)
|
||||||
|
- [Slots](doc/reference/slots.md)
|
||||||
|
- [Tags](doc/reference/tags.md)
|
||||||
|
- [Utils](doc/reference/utils.md)
|
||||||
|
|
||||||
|
### Other Topics
|
||||||
|
|
||||||
|
This section provides miscellaneous document that explains some topics
|
||||||
|
which cannot be considered either a tutorial, or reference documentation.
|
||||||
|
|
||||||
|
- [Owl architecture: the Virtual DOM](doc/miscellaneous/vdom.md)
|
||||||
|
- [Owl architecture: the rendering pipeline](doc/miscellaneous/rendering.md)
|
||||||
|
- [Comparison with React/Vue](doc/miscellaneous/comparison.md)
|
||||||
|
- [Why did Odoo built Owl?](doc/miscellaneous/why_owl.md)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Installing Owl
|
## Installing Owl
|
||||||
@@ -125,6 +129,3 @@ If you want to use a simple `<script>` tag, the last release can be downloaded h
|
|||||||
|
|
||||||
- [owl-1.4.10](https://github.com/odoo/owl/releases/tag/v1.4.10)
|
- [owl-1.4.10](https://github.com/odoo/owl/releases/tag/v1.4.10)
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
OWL is [LGPL licensed](./LICENSE).
|
|
||||||
|
|||||||
@@ -539,8 +539,7 @@ deleteTask(ev) {
|
|||||||
|
|
||||||
Looking at the code, it is apparent that we now have code to handle tasks
|
Looking at the code, it is apparent that we now have code to handle tasks
|
||||||
scattered in more than one place. Also, it mixes UI code and business logic
|
scattered in more than one place. Also, it mixes UI code and business logic
|
||||||
code. Owl has a way to manage state separately from the user interface: a
|
code. Owl has a way to manage state separately from the user interface: a store.
|
||||||
[`Store`](../reference/store.md).
|
|
||||||
|
|
||||||
Let us use it in our application. This is a pretty large refactoring (for our
|
Let us use it in our application. This is a pretty large refactoring (for our
|
||||||
application), since it involves extracting all task related code out of the
|
application), since it involves extracting all task related code out of the
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ discussed, feel free to open an issue/submit a PR to correct this text.
|
|||||||
- [Tooling/Build Step](#toolingbuild-step)
|
- [Tooling/Build Step](#toolingbuild-step)
|
||||||
- [Templating](#templating)
|
- [Templating](#templating)
|
||||||
- [Asynchronous rendering](#asynchronous-rendering)
|
- [Asynchronous rendering](#asynchronous-rendering)
|
||||||
- [Reactiveness](#reactiveness)
|
- [Reactivity](#reactivity)
|
||||||
- [State Management](#state-management)
|
|
||||||
- [Hooks](#hooks)
|
- [Hooks](#hooks)
|
||||||
|
|
||||||
## Size
|
## Size
|
||||||
@@ -173,7 +172,7 @@ more convoluted. For example, in Vue, you need to use a dynamic import keyword
|
|||||||
that needs to be transpiled at build time in order for the component to be loaded
|
that needs to be transpiled at build time in order for the component to be loaded
|
||||||
asynchronously (see [the documentation](https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components)).
|
asynchronously (see [the documentation](https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components)).
|
||||||
|
|
||||||
## Reactiveness
|
## Reactivity
|
||||||
|
|
||||||
React has a simple model: whenever the state changes, it is
|
React has a simple model: whenever the state changes, it is
|
||||||
replaced with a new state (via the `setState` method). Then, the DOM is patched.
|
replaced with a new state (via the `setState` method). Then, the DOM is patched.
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
# 🦉 OWL Documentation 🦉
|
|
||||||
|
|
||||||
## Learning Owl
|
|
||||||
|
|
||||||
Are you new to Owl? This is the place to start!
|
|
||||||
|
|
||||||
- [Tutorial: create a TodoList application](learning/tutorial_todoapp.md)
|
|
||||||
- [Quick Overview](learning/overview.md)
|
|
||||||
- [How to start an Owl project](learning/quick_start.md)
|
|
||||||
- [How to test Components](learning/how_to_test.md)
|
|
||||||
- [How to write Single File Components](learning/how_to_write_sfc.md)
|
|
||||||
|
|
||||||
## Reference
|
|
||||||
|
|
||||||
You will find here a complete reference of every feature, class or object
|
|
||||||
provided by Owl.
|
|
||||||
|
|
||||||
- [Animations](reference/animations.md)
|
|
||||||
- [Browser](reference/browser.md)
|
|
||||||
- [Component](reference/component.md)
|
|
||||||
- [Content](reference/content.md)
|
|
||||||
- [Concurrency Model](reference/concurrency_model.md)
|
|
||||||
- [Configuration](reference/config.md)
|
|
||||||
- [Context](reference/context.md)
|
|
||||||
- [Environment](reference/environment.md)
|
|
||||||
- [Event Bus](reference/event_bus.md)
|
|
||||||
- [Event Handling](reference/event_handling.md)
|
|
||||||
- [Error Handling](reference/error_handling.md)
|
|
||||||
- [Hooks](reference/hooks.md)
|
|
||||||
- [Mounting a component](reference/mounting.md)
|
|
||||||
- [Miscellaneous Components](reference/misc.md)
|
|
||||||
- [Observer](reference/observer.md)
|
|
||||||
- [Props](reference/props.md)
|
|
||||||
- [Props Validation](reference/props_validation.md)
|
|
||||||
- [QWeb Templating Language](reference/qweb_templating_language.md)
|
|
||||||
- [QWeb Engine](reference/qweb_engine.md)
|
|
||||||
- [Slots](reference/slots.md)
|
|
||||||
- [Tags](reference/tags.md)
|
|
||||||
- [Utils](reference/utils.md)
|
|
||||||
|
|
||||||
## Other Topics
|
|
||||||
|
|
||||||
This section provides miscellaneous document that explains some topics
|
|
||||||
which cannot be considered either a tutorial, or reference documentation.
|
|
||||||
|
|
||||||
- [Owl architecture: the Virtual DOM](miscellaneous/vdom.md)
|
|
||||||
- [Owl architecture: the rendering pipeline](miscellaneous/rendering.md)
|
|
||||||
- [Comparison with React/Vue](miscellaneous/comparison.md)
|
|
||||||
- [Why did Odoo built Owl?](miscellaneous/why_owl.md)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Found an issue in the documentation? A broken link? Some outdated information?
|
|
||||||
Please open an issue or submit a PR!
|
|
||||||
@@ -372,6 +372,16 @@ to be closed:
|
|||||||
useExternalListener(window, "click", this.closeMenu);
|
useExternalListener(window, "click", this.closeMenu);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `useComponent`
|
||||||
|
|
||||||
|
The `useComponent` hook is useful as a building block for some customized hooks,
|
||||||
|
that may need a reference to the component calling them.
|
||||||
|
|
||||||
|
### `useEnv`
|
||||||
|
|
||||||
|
The `useEnv` hook is useful as a building block for some customized hooks,
|
||||||
|
that may need a reference to the env of the component calling them.
|
||||||
|
|
||||||
### Making customized hooks
|
### Making customized hooks
|
||||||
|
|
||||||
Hooks are a wonderful way to organize the code of a complex component by feature
|
Hooks are a wonderful way to organize the code of a complex component by feature
|
||||||
|
|||||||
@@ -4,180 +4,180 @@
|
|||||||
* We define here a test to make sure that there are no dead link in the Owl
|
* We define here a test to make sure that there are no dead link in the Owl
|
||||||
* documentation.
|
* documentation.
|
||||||
*/
|
*/
|
||||||
// import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
|
|
||||||
// //--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// // Helpers
|
// Helpers
|
||||||
// //--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
// interface MarkDownLink {
|
interface MarkDownLink {
|
||||||
// name: string;
|
name: string;
|
||||||
// link: string;
|
link: string;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// interface MarkDownSection {
|
interface MarkDownSection {
|
||||||
// name: string;
|
name: string;
|
||||||
// slug: string;
|
slug: string;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// interface FileData {
|
interface FileData {
|
||||||
// name: string;
|
name: string;
|
||||||
// path: string[];
|
path: string[];
|
||||||
// fullName: string;
|
fullName: string;
|
||||||
// links: MarkDownLink[];
|
links: MarkDownLink[];
|
||||||
// sections: MarkDownSection[];
|
sections: MarkDownSection[];
|
||||||
// }
|
}
|
||||||
|
|
||||||
// const LINK_REGEXP = /\[([^\[]+)\]\(([^\)]+)\)/g;
|
const LINK_REGEXP = /\[([^\[]+)\]\(([^\)]+)\)/g;
|
||||||
// const HEADING_REGEXP = /\n(#+\s*)(.*)/g;
|
const HEADING_REGEXP = /\n(#+\s*)(.*)/g;
|
||||||
|
|
||||||
// export function addMardownData(fileData): void {
|
export function addMarkdownData(fileData: FileData): void {
|
||||||
// const sep = fileData.path.length > 0 ? "/" : "";
|
const sep = fileData.path.length > 0 ? "/" : "";
|
||||||
// const fullName = fileData.path.join("/") + sep + fileData.name;
|
const fullName = fileData.path.join("/") + sep + fileData.name;
|
||||||
// const content = fs.readFileSync(fullName, { encoding: "utf8" });
|
const content = fs.readFileSync(fullName, { encoding: "utf8" });
|
||||||
// let m;
|
let m;
|
||||||
// // get links info
|
// get links info
|
||||||
// do {
|
do {
|
||||||
// m = LINK_REGEXP.exec(content);
|
m = LINK_REGEXP.exec(content);
|
||||||
// if (m) {
|
if (m) {
|
||||||
// fileData.links.push({ name: m[0], link: m[2] });
|
fileData.links.push({ name: m[0], link: m[2] });
|
||||||
// }
|
}
|
||||||
// } while (m);
|
} while (m);
|
||||||
// // get sections info
|
// get sections info
|
||||||
// do {
|
do {
|
||||||
// m = HEADING_REGEXP.exec(content);
|
m = HEADING_REGEXP.exec(content);
|
||||||
// if (m) {
|
if (m) {
|
||||||
// fileData.sections.push({ name: m[0], slug: slugify(m[2]) });
|
fileData.sections.push({ name: m[0], slug: slugify(m[2]) });
|
||||||
// }
|
}
|
||||||
// } while (m);
|
} while (m);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// /**
|
/**
|
||||||
// * Returns a list of FileData corresponding to all files that need to be
|
* Returns a list of FileData corresponding to all files that need to be
|
||||||
// * validated.
|
* validated.
|
||||||
// */
|
*/
|
||||||
// function getFiles(path: string[] = []): FileData[] {
|
function getFiles(path: string[] = []): FileData[] {
|
||||||
// if (path.length === 0) {
|
if (path.length === 0) {
|
||||||
// const baseFiles: FileData[] = [
|
const baseFiles: FileData[] = [
|
||||||
// { name: "README.md", path: [], links: [], sections: [], fullName: "README.md" },
|
{ name: "README.md", path: [], links: [], sections: [], fullName: "README.md" },
|
||||||
// { name: "roadmap.md", path: [], links: [], sections: [], fullName: "roadmap.md" },
|
{ name: "roadmap.md", path: [], links: [], sections: [], fullName: "roadmap.md" },
|
||||||
// ];
|
];
|
||||||
// const rest = getFiles(["doc"]);
|
const rest = getFiles(["doc"]);
|
||||||
// const result = baseFiles.concat(rest);
|
const result = baseFiles.concat(rest);
|
||||||
// result.forEach(addMardownData);
|
result.forEach(addMarkdownData);
|
||||||
// return result;
|
return result;
|
||||||
// }
|
}
|
||||||
// const files = fs.readdirSync(path.join("/"), { withFileTypes: true }).map((f) => {
|
const files = fs.readdirSync(path.join("/"), { withFileTypes: true }).map((f) => {
|
||||||
// if (f.isDirectory()) {
|
if (f.isDirectory()) {
|
||||||
// return getFiles(path.concat(f.name));
|
return getFiles(path.concat(f.name));
|
||||||
// }
|
}
|
||||||
// const fullName = path.join("/") + (path.length > 0 ? "/" : "") + f.name;
|
const fullName = path.join("/") + (path.length > 0 ? "/" : "") + f.name;
|
||||||
// return [
|
return [
|
||||||
// {
|
{
|
||||||
// name: f.name,
|
name: f.name,
|
||||||
// path,
|
path,
|
||||||
// links: [],
|
links: [],
|
||||||
// sections: [],
|
sections: [],
|
||||||
// fullName,
|
fullName,
|
||||||
// },
|
},
|
||||||
// ];
|
];
|
||||||
// });
|
});
|
||||||
// return Array.prototype.concat(...files);
|
return Array.prototype.concat(...files);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// const LOCAL_FILES = ["LICENSE", "tools/debug.js"];
|
const LOCAL_FILES = ["LICENSE", "CHANGELOG.md"];
|
||||||
// export function isLinkValid(link: MarkDownLink, current: FileData, files: FileData[]): boolean {
|
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;
|
||||||
// }
|
}
|
||||||
// // Step 1: extract path, name, hash
|
// Step 1: extract path, name, hash
|
||||||
// // path = ['doc', 'architecture]
|
// path = ['doc', 'architecture]
|
||||||
// // name = 'rendering.md'
|
// name = 'rendering.md'
|
||||||
// // hash = 'blabla' (or '' if no hash)
|
// hash = 'blabla' (or '' if no hash)
|
||||||
|
|
||||||
// const parts = link.link.split("#");
|
const parts = link.link.split("#");
|
||||||
// const hash = parts[1] || "";
|
const hash = parts[1] || "";
|
||||||
// let name;
|
let name;
|
||||||
// let path;
|
let path;
|
||||||
// if (parts[0]) {
|
if (parts[0]) {
|
||||||
// let temp = parts[0].split("/");
|
let temp = parts[0].split("/");
|
||||||
// name = temp[temp.length - 1];
|
name = temp[temp.length - 1];
|
||||||
// temp.splice(-1);
|
temp.splice(-1);
|
||||||
// path = current.path.slice();
|
path = current.path.slice();
|
||||||
// for (let elem of temp) {
|
for (let elem of temp) {
|
||||||
// if (elem === "..") {
|
if (elem === "..") {
|
||||||
// path.splice(-1);
|
path.splice(-1);
|
||||||
// } else if (elem !== ".") {
|
} else if (elem !== ".") {
|
||||||
// path.push(elem);
|
path.push(elem);
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// } else {
|
} else {
|
||||||
// // there are no file name, so this is a relative link to the current file
|
// there are no file name, so this is a relative link to the current file
|
||||||
// name = current.name;
|
name = current.name;
|
||||||
// path = current.path;
|
path = current.path;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // Step 2: build normalized link file name
|
// Step 2: build normalized link file name
|
||||||
// const linkFullName = path.join("/") + (path.length > 0 ? "/" : "") + name;
|
const linkFullName = path.join("/") + (path.length > 0 ? "/" : "") + name;
|
||||||
|
|
||||||
// // Step 3: check link name against white list of local files
|
// Step 3: check link name against white list of local files
|
||||||
// if (LOCAL_FILES.includes(linkFullName)) {
|
if (LOCAL_FILES.includes(linkFullName)) {
|
||||||
// return true;
|
return true;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // Step 4: check if there is a matching file
|
// Step 4: check if there is a matching file
|
||||||
// let target: FileData | undefined = files.find((f) => f.fullName === linkFullName);
|
let target: FileData | undefined = files.find((f) => f.fullName === linkFullName);
|
||||||
// if (!target) {
|
if (!target) {
|
||||||
// return false;
|
return false;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // Step 5: if necessary, check if there is a corresponding link inside the target
|
// Step 5: if necessary, check if there is a corresponding link inside the target
|
||||||
// // link name
|
// link name
|
||||||
// if (hash) {
|
if (hash) {
|
||||||
// if (!target.sections.find((s) => s.slug === hash)) {
|
if (!target.sections.find((s) => s.slug === hash)) {
|
||||||
// return false;
|
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: string): string {
|
||||||
// const a = "àáäâãåăæçèéëêǵḧìíïîḿńǹñòóöôœøṕŕßśșțùúüûǘẃẍÿź·_,:;";
|
const a = "àáäâãåăæçèéëêǵḧìíïîḿńǹñòóöôœøṕŕßśșțùúüûǘẃẍÿź·_,:;";
|
||||||
// const b = "aaaaaaaaceeeeghiiiimnnnooooooprssstuuuuuwxyz-----";
|
const b = "aaaaaaaaceeeeghiiiimnnnooooooprssstuuuuuwxyz-----";
|
||||||
// const p = new RegExp(a.split("").join("|"), "g");
|
const p = new RegExp(a.split("").join("|"), "g");
|
||||||
// return str
|
return str
|
||||||
// .toString()
|
.toString()
|
||||||
// .toLowerCase()
|
.toLowerCase()
|
||||||
// .replace(/\//g, "") // remove /
|
.replace(/\//g, "") // remove /
|
||||||
// .replace(/\s+/g, "-") // Replace spaces with -
|
.replace(/\s+/g, "-") // Replace spaces with -
|
||||||
// .replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters
|
.replace(p, (c) => b.charAt(a.indexOf(c))) // Replace special characters
|
||||||
// .replace(/&/g, "-and-") // Replace & with ‘and’
|
.replace(/&/g, "-and-") // Replace & with ‘and’
|
||||||
// .replace(/[^\w\-]+/g, "") // Remove all non-word characters
|
.replace(/[^\w\-]+/g, "") // Remove all non-word characters
|
||||||
// .replace(/\-\-+/g, "-") // Replace multiple - with single -
|
.replace(/\-\-+/g, "-") // Replace multiple - with single -
|
||||||
// .replace(/^-+/, "") // Trim - from start of text
|
.replace(/^-+/, "") // Trim - from start of text
|
||||||
// .replace(/-+$/, ""); // Trim - from end of text
|
.replace(/-+$/, ""); // Trim - from end of text
|
||||||
// }
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// Test
|
// Test
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
test.skip("All markdown links work", () => {
|
test("All markdown links work", () => {
|
||||||
// let linkNumber = 0;
|
let linkNumber = 0;
|
||||||
// let invalidLinkNumber = 0;
|
let invalidLinkNumber = 0;
|
||||||
// const data = getFiles();
|
const data = getFiles();
|
||||||
// for (let file of data) {
|
for (let file of data) {
|
||||||
// for (let link of file.links) {
|
for (let link of file.links) {
|
||||||
// linkNumber++;
|
linkNumber++;
|
||||||
// if (!isLinkValid(link, file, data)) {
|
if (!isLinkValid(link, file, data)) {
|
||||||
// console.warn(`Invalid Link: "${link.name}" in "${file.name}"`);
|
console.warn(`Invalid Link: "${link.name}" in "${file.name}"`);
|
||||||
// invalidLinkNumber++;
|
invalidLinkNumber++;
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// expect(invalidLinkNumber).toBe(0);
|
expect(invalidLinkNumber).toBe(0);
|
||||||
// expect(linkNumber).toBeGreaterThan(10);
|
expect(linkNumber).toBeGreaterThan(10);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user