|
| 1 | +<!-- |
| 2 | +
|
| 3 | +@license Apache-2.0 |
| 4 | +
|
| 5 | +Copyright (c) 2026 The Stdlib Authors. |
| 6 | +
|
| 7 | +Licensed under the Apache License, Version 2.0 (the "License"); |
| 8 | +you may not use this file except in compliance with the License. |
| 9 | +You may obtain a copy of the License at |
| 10 | +
|
| 11 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 12 | +
|
| 13 | +Unless required by applicable law or agreed to in writing, software |
| 14 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 15 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 16 | +See the License for the specific language governing permissions and |
| 17 | +limitations under the License. |
| 18 | +
|
| 19 | +--> |
| 20 | + |
| 21 | +# toIPYNB |
| 22 | + |
| 23 | +> Convert a semantic document to a Jupyter notebook targeting the JupyterLite JavaScript kernel. |
| 24 | +
|
| 25 | +<section class="intro"> |
| 26 | + |
| 27 | +The converter produces deterministic nbformat 4.5 notebook objects from semantic document v1.0.0 input. It targets the browser-based [JupyterLite JavaScript kernel][jupyterlite-javascript-kernel] and defaults to its Web Worker runtime. |
| 28 | + |
| 29 | +</section> |
| 30 | + |
| 31 | +<!-- /.intro --> |
| 32 | + |
| 33 | +<section class="usage"> |
| 34 | + |
| 35 | +## Usage |
| 36 | + |
| 37 | +```javascript |
| 38 | +var fromMarkdown = require( '@stdlib/_tools/semantic-document/from-markdown' ); |
| 39 | +var toIPYNB = require( '@stdlib/_tools/semantic-document/to-ipynb' ); |
| 40 | + |
| 41 | +var doc = fromMarkdown( '# Example\n\n> A notebook example.', { |
| 42 | + 'profile': 'tutorial' |
| 43 | +}); |
| 44 | +var notebook = toIPYNB( doc ); |
| 45 | +var json = toIPYNB.stringify( notebook ); |
| 46 | +``` |
| 47 | + |
| 48 | +### toIPYNB( doc\[, options] ) |
| 49 | + |
| 50 | +Returns a fresh nbformat 4.5 notebook object. The function validates the semantic document, preserves semantic provenance in namespaced cell metadata, assigns deterministic cell IDs, and validates the generated notebook. |
| 51 | + |
| 52 | +If conversion fails after notebook construction begins, the thrown error exposes the completed prefix as `error.notebook`. Strict rejection of producer error diagnostics exposes a root notebook with metadata and no cells. Schema-, version-, and reference-integrity-invalid semantic documents are rejected before notebook construction and therefore do not expose a partial notebook. |
| 53 | + |
| 54 | +The default target is the JupyterLite Web Worker kernel: |
| 55 | + |
| 56 | +```javascript |
| 57 | +var workerNotebook = toIPYNB( doc, { |
| 58 | + 'target': 'jupyterlite-worker' |
| 59 | +}); |
| 60 | +``` |
| 61 | + |
| 62 | +To select the less-isolated IFrame kernel, set `target` to `'jupyterlite-iframe'`. The converter only records the requested kernel. A JupyterLite deployment must install and configure [`jupyterlite-javascript-kernel`][jupyterlite-javascript-kernel] when building the site. |
| 63 | + |
| 64 | +The adapter metadata intentionally does not claim tested JupyterLite package versions yet. Before release, the browser compatibility suite must pass against an exact `jupyterlite-core` and `jupyterlite-javascript-kernel` pair; only that verified pair should then be recorded in the preset. |
| 65 | + |
| 66 | +The following options are supported: |
| 67 | + |
| 68 | +- **excludeSectionRoles**: semantic section roles whose complete subtrees should be omitted. Default: `[ 'c', 'cli' ]`. |
| 69 | +- **mode**: conversion mode. Must be `'strict'` or `'permissive'`. Default: `'strict'`. |
| 70 | +- **target**: complete JupyterLite adapter preset. Must be `'jupyterlite-worker'` or `'jupyterlite-iframe'`. Default: `'jupyterlite-worker'`. |
| 71 | +- **moduleMap**: explicit module-resolution overlay described below. |
| 72 | +- **unsupportedCode**: disposition for an enabled execution candidate which is not browser eligible. Must be `'error'` or `'markdown'`. By default, strict mode uses `'error'` and permissive mode uses `'markdown'`. |
| 73 | +- **includeProvenance**: boolean indicating whether to copy detailed semantic source provenance to cell metadata. Default: `false`. |
| 74 | + |
| 75 | +By default, the converter removes C API and command-line interface sections which cannot be used interactively with the JavaScript kernel. Filtering uses semantic `section.role` values rather than heading text. To retain every section, provide an empty exclusion array: |
| 76 | + |
| 77 | +```javascript |
| 78 | +var completeNotebook = toIPYNB( doc, { |
| 79 | + 'excludeSectionRoles': [] |
| 80 | +}); |
| 81 | +``` |
| 82 | + |
| 83 | +Each excluded subtree is recorded under `notebook.metadata.stdlib.omissions` with its semantic ID, role, key, tree hash, and exclusion reason. If filtering makes two semantic thematic breaks consecutive, the converter retains the later rule associated with the surviving content and records the earlier rule as a `redundant-thematic-break` omission. Authored consecutive rules which were not joined by section filtering are preserved. The input semantic document is not modified. |
| 84 | + |
| 85 | +Enabled JavaScript candidates are parsed as ES2017 modules and checked conservatively for browser-incompatible dependencies and free globals. Direct, safe, top-level CommonJS bindings for canonical `@stdlib/*` packages are lowered according to stdlib's standalone ESM repository convention. For example, |
| 86 | + |
| 87 | +```javascript |
| 88 | +var ndarray = require( '@stdlib/ndarray/ctor' ); |
| 89 | +``` |
| 90 | + |
| 91 | +becomes |
| 92 | + |
| 93 | +```text |
| 94 | +import ndarray from 'https://cdn.jsdelivr.net/gh/stdlib-js/ndarray-ctor@esm/index.mjs'; |
| 95 | +``` |
| 96 | + |
| 97 | +The complete pre-transform source and ordered transform records are retained in `cell.metadata.stdlib`. |
| 98 | + |
| 99 | +This eligibility check is conservative static analysis, not a security sandbox or a guarantee that eligible code will terminate or run successfully. |
| 100 | + |
| 101 | +#### moduleMap |
| 102 | + |
| 103 | +`moduleMap` is a closed, explicit overlay on convention-based module resolution. It has two main uses: |
| 104 | + |
| 105 | +1. redirect a module to a deployment-controlled, often same-origin asset; and |
| 106 | +2. describe a non-stdlib module or an exceptional export shape. |
| 107 | + |
| 108 | +It is not a general import map and does not enable URL synthesis. Each record must declare the exact URL, export shape, supported runtimes, and whether the URL is mutable: |
| 109 | + |
| 110 | +```javascript |
| 111 | +var mappedNotebook = toIPYNB( doc, { |
| 112 | + 'moduleMap': { |
| 113 | + '@stdlib/ndarray/ctor': { |
| 114 | + 'url': '/assets/ndarray-ctor/index.mjs', |
| 115 | + 'export': 'default', |
| 116 | + 'runtimes': [ 'worker', 'iframe' ], |
| 117 | + 'mutable': false |
| 118 | + } |
| 119 | + } |
| 120 | +}); |
| 121 | +``` |
| 122 | + |
| 123 | +URLs must be HTTPS or root-absolute same-origin URLs. Relative, protocol-relative, credential-bearing, malformed, whitespace-containing, `data:`, `blob:`, and `javascript:` URLs are rejected. A module-map entry is an explicit caller assertion; the converter does not fetch or verify it. Transform metadata marks the resolver source as `module-map`. |
| 124 | + |
| 125 | +### toIPYNB.stringify( notebook ) |
| 126 | + |
| 127 | +Serializes a notebook using recursively sorted object keys, one-space indentation, and a final newline suitable for an `*.ipynb` file. |
| 128 | + |
| 129 | +</section> |
| 130 | + |
| 131 | +<!-- /.usage --> |
| 132 | + |
| 133 | +<section class="notes"> |
| 134 | + |
| 135 | +## Notes |
| 136 | + |
| 137 | +- Disabled execution candidates are emitted as dedicated Markdown cells. The converter does not rely on a notebook tag to prevent execution. |
| 138 | +- Expected-error code cells receive the advisory `raises-exception` tag. Interactive JupyterLite execution does not enforce that tag. |
| 139 | +- Worker code may produce rich visual output through standard Jupyter MIME bundles; it does not need direct DOM access for normal plots. |
| 140 | +- The IFrame target permits direct DOM globals and consequently executes with a stronger page-integrity risk than the worker target. |
| 141 | +- Standalone media requires exact semantic source slices in v1. Strict conversion rejects media without them; permissive conversion emits a visible placeholder and diagnostic. |
| 142 | +- Section-role exclusions remove the complete matching subtree. Nested usage, examples, resources, and display-only code belonging to an excluded C or CLI section are not promoted elsewhere in the notebook. |
| 143 | +- Convention-derived `@esm` URLs are mutable and network-dependent. Offline or reproducible deployments should supply reviewed, immutable same-origin module-map entries whose transitive dependency graphs are also vendored. |
| 144 | +- Canonical stdlib resolution assumes the standalone `index.mjs` default-export convention and derives support for both JupyterLite runtimes. The converter does not maintain a duplicate package compatibility database or promise that a remote module and its transitive dependencies will load successfully. |
| 145 | + |
| 146 | +</section> |
| 147 | + |
| 148 | +<!-- /.notes --> |
| 149 | + |
| 150 | +<section class="examples"> |
| 151 | + |
| 152 | +## Examples |
| 153 | + |
| 154 | +```javascript |
| 155 | +var exampleFromMarkdown = require( '@stdlib/_tools/semantic-document/from-markdown' ); |
| 156 | +var exampleToIPYNB = require( '@stdlib/_tools/semantic-document/to-ipynb' ); |
| 157 | + |
| 158 | +var markdown = [ |
| 159 | + '# Example', |
| 160 | + '', |
| 161 | + '> Compute an absolute value.', |
| 162 | + '', |
| 163 | + '```javascript', |
| 164 | + 'var abs = require( \'@stdlib/math/base/special/abs\' );', |
| 165 | + '```' |
| 166 | +].join( '\n' ); |
| 167 | + |
| 168 | +var exampleDoc = exampleFromMarkdown( markdown, { |
| 169 | + 'profile': 'tutorial' |
| 170 | +}); |
| 171 | +var exampleNotebook = exampleToIPYNB( exampleDoc ); |
| 172 | + |
| 173 | +console.log( exampleToIPYNB.stringify( exampleNotebook ) ); |
| 174 | +``` |
| 175 | + |
| 176 | +</section> |
| 177 | + |
| 178 | +<!-- /.examples --> |
| 179 | + |
| 180 | +<section class="links"> |
| 181 | + |
| 182 | +[jupyterlite-javascript-kernel]: https://github.com/jupyterlite/javascript-kernel |
| 183 | + |
| 184 | +</section> |
| 185 | + |
| 186 | +<!-- /.links --> |
0 commit comments