From d9e87f47d6c94b9b3a2ea812c9e2b2d358304066 Mon Sep 17 00:00:00 2001 From: scripthunter7 Date: Sun, 5 Nov 2023 23:37:42 +0100 Subject: [PATCH] Base Fork API docs --- docs/fork.md | 327 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 docs/fork.md diff --git a/docs/fork.md b/docs/fork.md new file mode 100644 index 0000000..366487d --- /dev/null +++ b/docs/fork.md @@ -0,0 +1,327 @@ +# Fork API + +CSSTree offers a flexible API for customizing and extending the functionality of the library, known as the "Fork API". + +> [!WARNING] +> **This feature is more advanced and assumes familiarity with the library's internals.** Regular users typically won't +> need to fork the library, so you can skip this section if it doesn't apply to your use case. + +## Getting started + +Here is an example of how to fork the library and add a new pseudo-class: + +```js +import * as cssTree from 'css-tree'; +import { inspect } from 'util'; + +const CUSTOM_PSEUDO_NAME = 'custom-pseudo'; +const TEST_INPUT = `:${CUSTOM_PSEUDO_NAME}(#id)`; + +// Create a new CSSTree fork +const customCssTree = cssTree.fork({ + // Customize the fork's behavior, e.g., adding a new pseudo-class + pseudo: { + // Add a new pseudo-class + [CUSTOM_PSEUDO_NAME]: { + parse() { + // Via the `this`, you can access the full internal state of the parser + return this.createSingleNodeList(this.Selector()); + }, + }, + }, +}); + +// Parse the input with the forked and the default CSSTree as well +[cssTree, customCssTree].forEach((cssTreeInstance) => { + console.log( + inspect( + cssTree.toPlainObject( + cssTreeInstance.parse(TEST_INPUT, { context: 'selector' }), + ), + { depth: null, colors: true }, + ), + ); +}); +``` + +The key difference between the original and forked ASTs is that the original library treats the new pseudo-class as a +simple `Raw` node, whereas the forked library recognizes it as a `Selector` node: + +```diff +{ + type: 'Selector', + loc: null, + children: [ + { + type: 'PseudoClassSelector', + loc: null, + name: 'custom-pseudo', +- children: [ { type: 'Raw', loc: null, value: '#id' } ] ++ children: [ ++ { ++ type: 'Selector', ++ loc: null, ++ children: [ { type: 'IdSelector', loc: null, name: 'id' } ] ++ } ++ ] + } + ] +} +``` + +The Fork API provides the same API as the original library, including the `fork()` function, even allowing you to fork +the fork if necessary. + +## Fork options + +You can customize the fork's behavior by passing an options object to the `fork()` function. Here are the available +properties: + +### `generic` + +A boolean flag that enables or disables generic types in the lexer. + +```js +import * as cssTree from 'css-tree'; +import assert from 'assert'; + +const customCssTree = cssTree.fork({ + // Disable generic types + generic: false, +}); + +const node = cssTree.parse('1px', { context: 'value' }); + +// Forked CSSTree should throw for a generic type, because it is disabled +// ( is a generic type) +assert.throws(() => customCssTree.lexer.match('', node)); + +// Default CSSTree should not throw for a generic type, because it is enabled +assert.doesNotThrow(() => cssTree.lexer.match('', node)); +``` + +> [!NOTE] +> You can find more about the generic syntax in the +> [source code](https://github.com/csstree/csstree/blob/master/lib/lexer/generic.js). + +### `units` + +Units that will be added to the lexer. + +```js +import * as cssTree from 'css-tree'; +import assert from 'assert'; + +const customCssTree = cssTree.fork({ + // Change time units (this will override the default time units) + units: { + time: ['millisecond'], + }, +}); + +const node = cssTree.parse('1millisecond', { + context: 'value', +}); + +// Forked CSSTree should match for a valid custom type +assert.strictEqual(customCssTree.lexer.match('