Skip to content

Commit

Permalink
tools,doc: add support for several flavors of JS code snippets
Browse files Browse the repository at this point in the history
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: #37162
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
  • Loading branch information
aduh95 authored and targos committed Jun 11, 2021
1 parent 8399766 commit 9a5411a
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 10 deletions.
21 changes: 21 additions & 0 deletions doc/api/wasi.md
Expand Up @@ -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`:

Expand Down
5 changes: 5 additions & 0 deletions doc/api_assets/js-flavor-cjs.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions doc/api_assets/js-flavor-esm.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 47 additions & 0 deletions doc/api_assets/style.css
Expand Up @@ -782,6 +782,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;
Expand Down Expand Up @@ -832,4 +859,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);
}
}
10 changes: 9 additions & 1 deletion test/doctool/test-doctool-html.js
Expand Up @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions test/fixtures/document_with_cjs_and_esm_code_snippet.md
@@ -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';
```
11 changes: 11 additions & 0 deletions test/fixtures/document_with_esm_and_cjs_code_snippet.md
@@ -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');
```
49 changes: 40 additions & 9 deletions tools/doc/html.js
Expand Up @@ -196,14 +196,16 @@ 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) => {
const STABILITY_RE = /(.*:)\s*(\d)([\s\S]*)/;
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;
Expand All @@ -213,15 +215,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);

Expand Down

0 comments on commit 9a5411a

Please sign in to comment.