tools,doc: add support for several flavors of JS code snippets

Enable code example using both modern ESM syntax and legacy CJS syntax.
It adds a toggle on the web interface to let users switch from one
JavaScript flavor to the other.

PR-URL: https://github.com/nodejs/node/pull/37162
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
This commit is contained in:
Antoine du Hamel 2021-01-31 23:10:11 +01:00
parent 0afc8ac12e
commit 2e1e74e9f8
8 changed files with 149 additions and 10 deletions

View file

@ -29,6 +29,27 @@ const instance = await WebAssembly.instantiate(wasm, importObject);
wasi.start(instance);
```
```cjs
'use strict';
const fs = require('fs');
const { WASI } = require('wasi');
const wasi = new WASI({
args: process.argv,
env: process.env,
preopens: {
'/sandbox': '/some/real/path/that/wasm/can/access'
}
});
const importObject = { wasi_snapshot_preview1: wasi.wasiImport };
(async () => {
const wasm = await WebAssembly.compile(fs.readFileSync('./demo.wasm'));
const instance = await WebAssembly.instantiate(wasm, importObject);
wasi.start(instance);
})();
```
To run the above example, create a new WebAssembly text format file named
`demo.wat`:

View file

@ -0,0 +1,5 @@
<!--
* Font Awesome Free 5.15.2 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free - CC BY 4.0
-->
<svg xmlns="http://www.w3.org/2000/svg" width="2719" height="384"><path d="M1191.326 384h192c106 0 192-86 192-192s-86-192-192-192h-192c-106 0-192 86-192 192s86 192 192 192zm0-320c70.8 0 128 57.3 128 128 0 70.8-57.3 128-128 128-70.8 0-128-57.3-128-128 0-70.8 57.3-128 128-128z"/><text stroke-width="42" font-family="sans-serif" font-weight="lighter" font-size="490" y="370">CJS</text><text stroke-width="42" font-weight="lighter" font-family="sans-serif" font-size="490" y="370" x="1682">ESM</text></svg>

After

Width:  |  Height:  |  Size: 645 B

View file

@ -0,0 +1,5 @@
<!--
* Font Awesome Free 5.15.2 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free - CC BY 4.0
-->
<svg xmlns="http://www.w3.org/2000/svg" height="384" width="2719"><defs><path d="M-136.321-230.336h2994.365v653.401H-136.321z"/></defs><path d="M1383.326 0h-192c-106 0-192 86-192 192s86 192 192 192h192c106 0 192-86 192-192s-86-192-192-192zm0 320c-70.8 0-128-57.3-128-128 0-70.8 57.3-128 128-128 70.8 0 128 57.3 128 128 0 70.8-57.3 128-128 128z"/><text stroke-width="42" font-family="sans-serif" font-weight="lighter" font-size="490" y="370">CJS</text><text stroke-width="42" font-weight="lighter" font-family="sans-serif" font-size="490" y="370" x="1682">ESM</text></svg>

After

Width:  |  Height:  |  Size: 713 B

View file

@ -765,6 +765,33 @@ kbd {
display: block;
}
.js-flavor-selector {
appearance: none;
float: right;
background-image: url(./js-flavor-cjs.svg);
background-size: contain;
background-repeat: no-repeat;
width: 142px;
height: 20px;
}
.js-flavor-selector:checked {
background-image: url(./js-flavor-esm.svg);
}
.js-flavor-selector:not(:checked) ~ .esm,
.js-flavor-selector:checked ~ .cjs {
display: none;
}
.dark-mode .js-flavor-selector {
filter: invert(1);
}
@supports (aspect-ratio: 1 / 1) {
.js-flavor-selector {
height: 1.5em;
width: auto;
aspect-ratio: 2719 / 384;
}
}
@media print {
html {
height: auto;
@ -815,4 +842,24 @@ kbd {
#apicontent {
overflow: hidden;
}
.js-flavor-selector {
display: none;
}
.js-flavor-selector + * {
margin-bottom: 2rem;
padding-bottom: 2rem;
border-bottom: 1px solid var(--color-text-primary);
}
.js-flavor-selector ~ * {
display: block !important;
background-position: top right;
background-size: 142px 20px;
background-repeat: no-repeat;
}
.js-flavor-selector ~ .cjs {
background-image: url(./js-flavor-cjs.svg);
}
.js-flavor-selector ~ .mjs {
background-image: url(./js-flavor-esm.svg);
}
}

View file

@ -129,7 +129,15 @@ const testData = [
{
file: fixtures.path('document_with_special_heading.md'),
html: '<title>Sample markdown with special heading |',
}
},
{
file: fixtures.path('document_with_esm_and_cjs_code_snippet.md'),
html: '<input class="js-flavor-selector" type="checkbox" checked',
},
{
file: fixtures.path('document_with_cjs_and_esm_code_snippet.md'),
html: '<input class="js-flavor-selector" type="checkbox" aria-label',
},
];
const spaces = /\s/g;

View file

@ -0,0 +1,11 @@
# Usage and Example
CJS snippet is first, it should be the one displayed by default.
```cjs
require('path');
```
```mjs
import 'node:url';
```

View file

@ -0,0 +1,11 @@
# Usage and Example
ESM snippet is first, it should be the one displayed by default.
```mjs
import 'node:url';
```
```cjs
require('path');
```

View file

@ -187,6 +187,8 @@ function linkJsTypeDocs(text) {
return parts.join('`');
}
const isJSFlavorSnippet = (node) => node.lang === 'cjs' || node.lang === 'mjs';
// Preprocess headers, stability blockquotes, and YAML blocks.
function preprocessElements({ filename }) {
return (tree) => {
@ -194,7 +196,7 @@ function preprocessElements({ filename }) {
let headingIndex = -1;
let heading = null;
visit(tree, null, (node, index) => {
visit(tree, null, (node, index, parent) => {
if (node.type === 'heading') {
headingIndex = index;
heading = node;
@ -204,15 +206,44 @@ function preprocessElements({ filename }) {
`No language set in ${filename}, ` +
`line ${node.position.start.line}`);
}
const language = (node.lang || '').split(' ')[0];
const highlighted = getLanguage(language) ?
highlight(language, node.value).value :
node.value;
const className = isJSFlavorSnippet(node) ?
`language-js ${node.lang}` :
`language-${node.lang}`;
const highlighted =
`<code class='${className}'>` +
(getLanguage(node.lang || '') ?
highlight(node.lang, node.value) : node).value +
'</code>';
node.type = 'html';
node.value = '<pre>' +
`<code class = 'language-${node.lang}'>` +
highlighted +
'</code></pre>';
if (isJSFlavorSnippet(node)) {
const previousNode = parent.children[index - 1] || {};
const nextNode = parent.children[index + 1] || {};
if (!isJSFlavorSnippet(previousNode) &&
isJSFlavorSnippet(nextNode) &&
nextNode.lang !== node.lang) {
// Saving the highlight code as value to be added in the next node.
node.value = highlighted;
} else if (isJSFlavorSnippet(previousNode)) {
node.value = '<pre>' +
'<input class="js-flavor-selector" type="checkbox"' +
// If CJS comes in second, ESM should display by default.
(node.lang === 'cjs' ? ' checked' : '') +
' aria-label="Show modern ES modules syntax">' +
previousNode.value +
highlighted +
'</pre>';
node.lang = null;
previousNode.value = '';
previousNode.lang = null;
} else {
// Isolated JS snippet, no need to add the checkbox.
node.value = `<pre>${highlighted}</pre>`;
}
} else {
node.value = `<pre>${highlighted}</pre>`;
}
} else if (node.type === 'html' && common.isYAMLBlock(node.value)) {
node.value = parseYAML(node.value);