feat: add a check for the codeblocks in the tutorials - #3
Conversation
…o tsx for scripts - Add scripts/check-codeblocks.sh and scripts/extract-and-check.ts to extract and type-check TypeScript code blocks in docs - Add cytoscape-alias-plugin.js for ESM/UMD aliasing in Webpack - Update tsconfig.json for ESNext and bundler module resolution
… codeblocks check in CI
✅ Deploy Preview for veramo ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull request overview
Adds automated TypeScript codeblock compilation checking for tutorial/docs content, alongside tooling and documentation updates (Node version guidance, formatting adjustments) and Docusaurus configuration enhancements.
Changes:
- Introduce scripts to extract TypeScript code fences from docs and run
tsc --noEmitagainst them, with an optional baseline report. - Add a bootstrap script to provision an isolated
temp/codeblocksdependency environment used for compilation checks. - Update Docusaurus config and dependencies (Mermaid theme + Cytoscape alias plugin), plus various doc formatting/updates.
Reviewed changes
Copilot reviewed 18 out of 21 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.json | Extends Docusaurus TS config and adds explicit compiler options for ESNext/bundler resolution. |
| scripts/extract-and-check.ts | New extractor/compiler script for docs TypeScript codeblocks (with optional report output). |
| scripts/check-codeblocks.sh | New bootstrap runner to set up temp/codeblocks deps and execute the checker. |
| pnpm-lock.yaml | Lockfile changes for added deps and overrides (including mermaid pin). |
| plugins/cytoscape-alias-plugin.js | New Docusaurus plugin intended to alias Cytoscape UMD build to ESM build. |
| package.json | Adds codeblock-check scripts, mermaid theme dependency + pnpm overrides, switches runtime tooling to tsx, updates Node engines. |
| docusaurus.config.js | Enables Mermaid markdown and registers Mermaid theme + new Cytoscape alias plugin. |
| docs/veramo_agent/event_system.md | Adds/adjusts example code and formatting. |
| docs/veramo_agent/configuration_internals.md | Adds // filename: markers for codeblock compilation. |
| docs/veramo_agent/cli_tool.md | Updates Node prerequisite text. |
| docs/react_native_tutorials/react_native_*.md | Adds // filename: markers and import/formatting fixes for tutorial snippets. |
| docs/node_tutorials/node_setup_identifiers.md | Adds // filename: markers and formatting adjustments in snippets. |
| docs/dev_tools/veramo_react.md | Fixes/normalizes TSX snippets and query example formatting. |
| docs/dev_tools/agent_explorer_plugin.md | Adjusts code fence language tags (ts -> tsx). |
| docs/basics/verifiable_data.md | Re-formats JSON5 example. |
| docs/basics/identifiers.md | Re-formats JSON5 DID document example. |
| .prettierignore | Adds ignore rules for binary assets and build output. |
| .gitignore | Broadens node_modules ignoring; ignores baseline report and some local docs/LLM files. |
| .github/workflows/rebuild-api.yml | Runs formatting + adds a codeblocks check step in the rebuild workflow. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
Comments suppressed due to low confidence (2)
scripts/check-codeblocks.sh:56
- The bootstrap installs many dependencies using
@latest, which makes runs non-reproducible and can unexpectedly break (or change results) as upstream releases change. Consider pinning versions (or reusing the repo lockfile) socheck:codeblocksis deterministic and cacheable in CI.
@veramo/credential-status@latest \
@veramo/data-store@latest \
@veramo/data-store-json@latest \
@veramo/did-comm@latest \
@veramo/did-discovery@latest \
@veramo/did-jwt@latest \
@veramo/did-manager@latest \
@veramo/did-provider-ethr@latest \
@veramo/did-provider-ion@latest \
@veramo/did-provider-jwk@latest \
@veramo/did-provider-key@latest \
@veramo/did-provider-peer@latest \
@veramo/did-provider-pkh@latest \
@veramo/did-provider-web@latest \
@veramo/did-resolver@latest \
@veramo/key-manager@latest \
@veramo/kms-local@latest \
@veramo/kms-web3@latest \
@veramo/message-handler@latest \
@veramo/remote-client@latest \
@veramo/remote-server@latest \
@veramo/selective-disclosure@latest \
@veramo/url-handler@latest \
@veramo/utils@latest \
ethr-did-resolver@latest \
web-did-resolver@latest \
express@^4 \
typeorm@latest
fi
exec pnpm tsx "$REPO_ROOT/scripts/extract-and-check.ts" "$@"
scripts/check-codeblocks.sh:56
- The
pnpm addstep installs several third-party dependencies (ethr-did-resolver,web-did-resolver,express,typeorm) using mutable tags like@latestand^4. This means every fresh run of this script (for example in CI) will automatically fetch and execute whatever code is currently published under those names, so a compromised or malicious release could execute with your CI/build permissions and access to secrets. To reduce supply-chain risk, pin these third-party packages to specific versions (or otherwise lock them down) and update them only via explicit, reviewed version bumps.
@veramo/credential-status@latest \
@veramo/data-store@latest \
@veramo/data-store-json@latest \
@veramo/did-comm@latest \
@veramo/did-discovery@latest \
@veramo/did-jwt@latest \
@veramo/did-manager@latest \
@veramo/did-provider-ethr@latest \
@veramo/did-provider-ion@latest \
@veramo/did-provider-jwk@latest \
@veramo/did-provider-key@latest \
@veramo/did-provider-peer@latest \
@veramo/did-provider-pkh@latest \
@veramo/did-provider-web@latest \
@veramo/did-resolver@latest \
@veramo/key-manager@latest \
@veramo/kms-local@latest \
@veramo/kms-web3@latest \
@veramo/message-handler@latest \
@veramo/remote-client@latest \
@veramo/remote-server@latest \
@veramo/selective-disclosure@latest \
@veramo/url-handler@latest \
@veramo/utils@latest \
ethr-did-resolver@latest \
web-did-resolver@latest \
express@^4 \
typeorm@latest
fi
exec pnpm tsx "$REPO_ROOT/scripts/extract-and-check.ts" "$@"
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| pnpm run format | ||
| - name: 'check codeblocks' | ||
| run: | | ||
| pnpm run check:codeblocks' |
There was a problem hiding this comment.
The workflow step has a stray trailing quote in pnpm run check:codeblocks', which will cause the action to fail with a shell syntax/command-not-found error. Remove the extra ' so the script can run.
| pnpm run check:codeblocks' | |
| pnpm run check:codeblocks |
| # Bootstrap or refresh the isolated package environment | ||
| if [ ! -f "$CODEBLOCKS_DIR/package.json" ]; then | ||
| echo "Initialising package environment in $CODEBLOCKS_DIR..." | ||
| pnpm init --init-type module --bare --dir "$CODEBLOCKS_DIR" |
There was a problem hiding this comment.
pnpm init --dir "$CODEBLOCKS_DIR" is interactive by default and may hang in CI/non-TTY environments. Use a non-interactive option (eg -y) or write a minimal package.json directly so the codeblocks check can run unattended.
| pnpm init --init-type module --bare --dir "$CODEBLOCKS_DIR" | |
| pnpm init -y --init-type module --bare --dir "$CODEBLOCKS_DIR" |
| const content = fs.readFileSync(path.join(repoRoot, mdFile), 'utf-8') | ||
| const codeBlockRegex = /```(ts|typescript)\n([\s\S]+?)\n```/g | ||
| let match |
There was a problem hiding this comment.
codeBlockRegex only matches ts and typescript fences, so ```tsx blocks (common in the React/React Native tutorials) will be skipped and never type-checked. If the goal is to check TypeScript tutorial codeblocks broadly, include tsx in the supported fence languages.
| // --- Detect // filename: comment (first line of the block) --- | ||
| const firstLine = code.split('\n')[0].trim() | ||
| const filenameMatch = firstLine.match(/^\/\/\s*filename:\s*(.+)$/) | ||
| const virtualFilename = filenameMatch ? filenameMatch[1].trim() : null | ||
|
|
||
| if (virtualFilename) { | ||
| // Append this block's code (strip the filename comment line) to the virtual file | ||
| const codeWithoutComment = code.split('\n').slice(1).join('\n') | ||
| const existing = virtualFiles.get(virtualFilename) ?? '' | ||
| virtualFiles.set(virtualFilename, existing + (existing ? '\n' : '') + codeWithoutComment) | ||
|
|
||
| // Write ALL current virtual files to docDir so imports resolve | ||
| for (const [vPath, vCode] of virtualFiles) { | ||
| const absPath = path.join(docDir, vPath) | ||
| fs.mkdirSync(path.dirname(absPath), { recursive: true }) | ||
| fs.writeFileSync(absPath, vCode) | ||
| } | ||
|
|
||
| // The file being checked is the one that was just updated | ||
| const targetFile = path.join(docDir, virtualFilename) | ||
|
|
||
| try { | ||
| execSync( | ||
| `tsc --noEmit --skipLibCheck --target esnext --module esnext --moduleResolution bundler ${targetFile}`, | ||
| { stdio: 'pipe', encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024 }, | ||
| ) |
There was a problem hiding this comment.
virtualFilename is taken directly from markdown content and is used to construct paths (path.join(docDir, vPath)) and to build the tsc command. Without validation/sanitization this allows path traversal (eg ../../...) and, because execSync runs via a shell, potential command injection if virtualFilename contains shell metacharacters. Validate that filenames are relative, stay within docDir, and invoke tsc via execFileSync/argument arrays (no shell) to avoid injection risks.
| modifyWebpackConfig(config) { | ||
| config.resolve = config.resolve || {}; | ||
| config.resolve.alias = config.resolve.alias || {}; | ||
| // Alias the UMD build to the ESM build to avoid export issues | ||
| config.resolve.alias['cytoscape/dist/cytoscape.umd.js'] = path.resolve( | ||
| __dirname, | ||
| 'node_modules', | ||
| 'cytoscape', | ||
| 'dist', | ||
| 'cytoscape.esm.mjs' | ||
| ); | ||
| return config; |
There was a problem hiding this comment.
Docusaurus v2 plugins use the configureWebpack hook; modifyWebpackConfig is a v1-era API and will not be called, so the alias likely won’t be applied. Rename to the supported hook (configureWebpack) and return the appropriate webpack config overrides.
| modifyWebpackConfig(config) { | |
| config.resolve = config.resolve || {}; | |
| config.resolve.alias = config.resolve.alias || {}; | |
| // Alias the UMD build to the ESM build to avoid export issues | |
| config.resolve.alias['cytoscape/dist/cytoscape.umd.js'] = path.resolve( | |
| __dirname, | |
| 'node_modules', | |
| 'cytoscape', | |
| 'dist', | |
| 'cytoscape.esm.mjs' | |
| ); | |
| return config; | |
| configureWebpack(config, isServer, utils) { | |
| // Alias the UMD build to the ESM build to avoid export issues | |
| return { | |
| resolve: { | |
| alias: { | |
| 'cytoscape/dist/cytoscape.umd.js': path.resolve( | |
| __dirname, | |
| 'node_modules', | |
| 'cytoscape', | |
| 'dist', | |
| 'cytoscape.esm.mjs' | |
| ), | |
| }, | |
| }, | |
| }; |
| # Bootstrap or refresh the isolated package environment | ||
| if [ ! -f "$CODEBLOCKS_DIR/package.json" ]; then | ||
| echo "Initialising package environment in $CODEBLOCKS_DIR..." |
There was a problem hiding this comment.
The bootstrap guard only checks for $CODEBLOCKS_DIR/package.json. If that file exists but node_modules was cleaned (eg by a CI cache miss or manual deletion), the script will skip reinstall and the codeblock compilation will fail. Consider checking for node_modules (or running pnpm install --dir ... each time) instead of only package.json.
| // Alias the UMD build to the ESM build to avoid export issues | ||
| config.resolve.alias['cytoscape/dist/cytoscape.umd.js'] = path.resolve( | ||
| __dirname, | ||
| 'node_modules', | ||
| 'cytoscape', | ||
| 'dist', | ||
| 'cytoscape.esm.mjs' | ||
| ); |
There was a problem hiding this comment.
The alias target path resolves from __dirname (the plugins/ directory) into plugins/node_modules/..., but dependencies are installed in the repo root node_modules/. This path will not exist at runtime. Resolve from the site directory (eg context.siteDir) or from process.cwd() / path.resolve(__dirname, '..', 'node_modules', ...) so the alias points at a real file.
| "pnpm": { | ||
| "overrides": { | ||
| "mermaid": "8.13.8" | ||
| } | ||
| }, |
There was a problem hiding this comment.
The PNPM override pins mermaid to 8.13.8, which in turn pulls in @braintree/sanitize-url@3.1.0 (deprecated for an XSS vulnerability per the lockfile). Consider upgrading mermaid to a version that doesn’t require the vulnerable sanitize-url (or add an override for sanitize-url to a patched major) so enabling mermaid doesn’t introduce a known vulnerable dependency.
| "check:codeblocks": "bash scripts/check-codeblocks.sh", | ||
| "baseline:docs": "bash scripts/check-codeblocks.sh --report", | ||
| "check:all": "pnpm run build:veramo && pnpm run format && pnpm run check:docs && pnpm run build", | ||
| "format": "prettier --write '{src,docs}/**/*'" |
There was a problem hiding this comment.
The format script now targets only {src,docs}/**/*, but this PR adds new source files under scripts/ and plugins/ that won’t be covered by formatting checks/runs. Consider including these directories (or switching to a repo-wide prettier glob plus .prettierignore) to keep tooling scripts and plugins consistently formatted.
| "format": "prettier --write '{src,docs}/**/*'" | |
| "format": "prettier --write '{src,docs,scripts,plugins}/**/*'" |
| const path = require('path'); | ||
|
|
||
| module.exports = function cytoscapeAliasPlugin(context, options) { | ||
| return { | ||
| name: 'cytoscape-alias-plugin', | ||
|
|
There was a problem hiding this comment.
This plugin file doesn’t follow the repository’s Prettier settings (eg semi: false, singleQuote: true), which makes it inconsistent with the rest of the codebase. Consider running Prettier on this file or adjusting the format script scope so new plugins/ files get formatted automatically.
This adds a compilation check for the TypeScript codeblocks in the tutorials.
Additionally, this includes several improvements and updates across documentation, code formatting, and workflow configuration.