diff --git a/.changeset/all-planets-know.md b/.changeset/all-planets-know.md new file mode 100644 index 0000000000..bb2c151218 --- /dev/null +++ b/.changeset/all-planets-know.md @@ -0,0 +1,5 @@ +--- +'myst-execute': patch +--- + +Fix type export diff --git a/.changeset/dirty-steaks-burn.md b/.changeset/dirty-steaks-burn.md new file mode 100644 index 0000000000..6f4b17cf48 --- /dev/null +++ b/.changeset/dirty-steaks-burn.md @@ -0,0 +1,9 @@ +--- +'myst-execute': minor +'myst-cli': minor +'myst-to-html': patch +'myst-common': patch +'myst-spec': patch +--- + +Switch to ipynb build cache diff --git a/.changeset/eighty-dots-train.md b/.changeset/eighty-dots-train.md new file mode 100644 index 0000000000..169bdc7cbd --- /dev/null +++ b/.changeset/eighty-dots-train.md @@ -0,0 +1,5 @@ +--- +'myst-cli': patch +--- + +effectively discard files matched by pattern if already mentioned diff --git a/.changeset/fifty-waves-smile.md b/.changeset/fifty-waves-smile.md new file mode 100644 index 0000000000..ef6cca11b8 --- /dev/null +++ b/.changeset/fifty-waves-smile.md @@ -0,0 +1,5 @@ +--- +'myst-execute': patch +--- + +Set type to console for execution options diff --git a/.changeset/fruity-ties-speak.md b/.changeset/fruity-ties-speak.md new file mode 100644 index 0000000000..70d28ba5a4 --- /dev/null +++ b/.changeset/fruity-ties-speak.md @@ -0,0 +1,7 @@ +--- +'myst-frontmatter': patch +'myst-transforms': patch +'myst-cli': patch +--- + +Relax overaggressive DOI coercion diff --git a/.changeset/grumpy-lions-push.md b/.changeset/grumpy-lions-push.md new file mode 100644 index 0000000000..1ce1be7d28 --- /dev/null +++ b/.changeset/grumpy-lions-push.md @@ -0,0 +1,5 @@ +--- +'myst-cli': patch +--- + +Add public.json file manifest diff --git a/.changeset/khaki-papayas-jog.md b/.changeset/khaki-papayas-jog.md new file mode 100644 index 0000000000..26254d098a --- /dev/null +++ b/.changeset/khaki-papayas-jog.md @@ -0,0 +1,6 @@ +--- +'myst-to-typst': patch +'mystmd': patch +--- + +`tex-to-typst` bumped to 0.0.21 diff --git a/.changeset/neat-shirts-attend.md b/.changeset/neat-shirts-attend.md new file mode 100644 index 0000000000..96b22b9014 --- /dev/null +++ b/.changeset/neat-shirts-attend.md @@ -0,0 +1,5 @@ +--- +'myst-cli': patch +--- + +Bump version for GitHub actions and node in GH pages publish action diff --git a/.changeset/nine-times-dream.md b/.changeset/nine-times-dream.md new file mode 100644 index 0000000000..c06441e899 --- /dev/null +++ b/.changeset/nine-times-dream.md @@ -0,0 +1,5 @@ +--- +'myst-execute': patch +--- + +Fix path handling on Windows diff --git a/.changeset/silly-phones-warn.md b/.changeset/silly-phones-warn.md new file mode 100644 index 0000000000..afa8bbb6ae --- /dev/null +++ b/.changeset/silly-phones-warn.md @@ -0,0 +1,5 @@ +--- +'citation-js-utils': patch +--- + +Decode HTML entities in CrossRef citation data diff --git a/.changeset/tangy-cameras-cross.md b/.changeset/tangy-cameras-cross.md new file mode 100644 index 0000000000..831cccbc13 --- /dev/null +++ b/.changeset/tangy-cameras-cross.md @@ -0,0 +1,35 @@ +--- +'citation-js-utils': patch +'myst-ext-exercise': patch +'markdown-it-myst': patch +'myst-frontmatter': patch +'myst-directives': patch +'myst-ext-button': patch +'myst-transforms': patch +'myst-cli-utils': patch +'myst-ext-proof': patch +'myst-templates': patch +'myst-ext-grid': patch +'myst-ext-icon': patch +'myst-ext-tabs': patch +'myst-to-typst': patch +'jats-to-myst': patch +'myst-execute': patch +'myst-migrate': patch +'myst-to-docx': patch +'myst-to-html': patch +'myst-to-jats': patch +'myst-common': patch +'myst-config': patch +'myst-parser': patch +'myst-to-tex': patch +'tex-to-myst': patch +'myst-roles': patch +'myst-to-md': patch +'myst-spec': patch +'myst-cli': patch +'mystmd': patch +'jtex': patch +--- + +Update package metadata diff --git a/.changeset/true-buses-fail.md b/.changeset/true-buses-fail.md new file mode 100644 index 0000000000..53cbb03ee3 --- /dev/null +++ b/.changeset/true-buses-fail.md @@ -0,0 +1,6 @@ +--- +'myst-execute': patch +'myst-cli': patch +--- + +Bump @jupyterlite/services and fix types diff --git a/.changeset/vast-brooms-clap.md b/.changeset/vast-brooms-clap.md new file mode 100644 index 0000000000..025de5f099 --- /dev/null +++ b/.changeset/vast-brooms-clap.md @@ -0,0 +1,5 @@ +--- +'mystmd': patch +--- + +Use NodeJS parallelism parallelism feature diff --git a/.changeset/yellow-bikes-carry.md b/.changeset/yellow-bikes-carry.md new file mode 100644 index 0000000000..881365ffc3 --- /dev/null +++ b/.changeset/yellow-bikes-carry.md @@ -0,0 +1,6 @@ +--- +'myst-ext-proof': patch +'myst-roles': patch +--- + +Add proof:_ as alias for prf:_ diff --git a/bun.lock b/bun.lock index faa87ae355..117a8b864f 100644 --- a/bun.lock +++ b/bun.lock @@ -7,8 +7,16 @@ "devDependencies": { "@changesets/cli": "^2.26.1", "@types/node": "^24.6.2", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", "esbuild": "^0.25.10", + "eslint": "^8.45.0", "eslint-config-curvenote": "^0.0.4", + "eslint-config-next": "^13.4.10", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-react": "7.32.2", "husky": "^9.1.7", "lint-staged": "^15.5.0", "madge": "^6.1.0", @@ -20,6 +28,26 @@ "vitest": "^3.2.4", }, }, + "docs": { + "name": "mystmd-docs", + "version": "0.0.0", + "dependencies": { + "myst-common": "workspace:*", + "myst-directives": "workspace:*", + "myst-ext-button": "workspace:*", + "myst-ext-card": "workspace:*", + "myst-ext-exercise": "workspace:*", + "myst-ext-grid": "workspace:*", + "myst-ext-proof": "workspace:*", + "myst-ext-tabs": "workspace:*", + "myst-migrate": "workspace:*", + "myst-parser": "workspace:*", + "myst-roles": "workspace:*", + "tex-to-myst": "workspace:*", + "unist-builder": "^3.0.0", + "unist-util-select": "^4.0.3", + }, + }, "packages/citation-js-utils": { "name": "citation-js-utils", "version": "1.2.8", @@ -27,9 +55,12 @@ "@citation-js/core": "^0.7.18", "@citation-js/plugin-bibtex": "^0.7.18", "@citation-js/plugin-csl": "^0.7.18", + "doi-utils": "^2.0.5", + "he": "^1.2.0", "sanitize-html": "^2.7.0", }, "devDependencies": { + "@types/he": "^1.2.0", "@types/sanitize-html": "^2.6.2", }, }, @@ -39,7 +70,7 @@ "dependencies": { "doi-utils": "^2.0.5", "jats-tags": "^1.0.8", - "jats-xml": "^1.0.8", + "jats-xml": "^1.1.7", "myst-common": "^1.9.2", "myst-frontmatter": "^1.9.2", "myst-spec": "^0.0.5", @@ -76,6 +107,7 @@ }, "devDependencies": { "@types/adm-zip": "^0.5.0", + "@types/js-yaml": "^4.0.5", "@types/nunjucks": "^3.2.2", "memfs": "^3.5.3", }, @@ -89,6 +121,7 @@ "vfile-message": "^3.0.0", }, "devDependencies": { + "@types/js-yaml": "^4.0.5", "markdown-it-footnote": "^4.0.0", }, "peerDependencies": { @@ -100,7 +133,7 @@ "name": "myst-cli", "version": "1.10.1", "dependencies": { - "@jupyterlab/services": "^7.3.0", + "@jupyterlab/services": "^7.6.0", "@reduxjs/toolkit": "^2.1.0", "adm-zip": "^0.5.10", "async-mutex": "^0.5.0", @@ -169,6 +202,7 @@ "unist-util-visit": "^4.0.0", "uuid": "^8.3.2", "vfile": "^5.3.5", + "vfile-message": "^3.0.0", "which": "^3.0.1", "ws": "^8.18.1", "xml-js": "^1.6.11", @@ -181,12 +215,15 @@ "@types/express": "^4.17.17", "@types/fs-extra": "^11.0.1", "@types/inquirer": "^9.0.7", + "@types/js-yaml": "^4.0.5", + "@types/mdast": "^3.0.12", "@types/mime-types": "^2.1.1", "@types/picomatch": "^4.0.2", "@types/uuid": "^8.3.4", "@types/which": "^3.0.0", "@types/ws": "^8.18.1", "concurrently": "^8.2.0", + "memfs": "^3.5.3", }, }, "packages/myst-cli-utils": { @@ -194,6 +231,7 @@ "version": "2.0.14", "dependencies": { "chalk": "^5.2.0", + "commander": "^10.0.1", "pretty-hrtime": "^1.0.3", "tree-kill": "^1.2.2", }, @@ -220,6 +258,7 @@ "devDependencies": { "@jupyterlab/nbformat": "^3.5.2", "@lumino/coreutils": "^2.0.0", + "@types/unist": "^2.0.7", "myst-spec-ext": "^1.10.0", "unist-builder": "3.0.0", }, @@ -233,6 +272,8 @@ "simple-validators": "^1.2.0", }, "devDependencies": { + "glob": "^10.3.1", + "js-yaml": "^4.1.0", "moment": "^2.29.4", }, }, @@ -245,28 +286,37 @@ "js-yaml": "^4.1.0", "json5": "^1.0.2", "myst-common": "^1.10.0", + "myst-spec": "^0.0.5", "myst-spec-ext": "^1.10.0", "nanoid": "^5.1.6", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", }, + "devDependencies": { + "@types/js-yaml": "^4.0.5", + "@types/json5": "^0.0.29", + }, }, "packages/myst-execute": { "name": "myst-execute", "version": "0.4.0", "dependencies": { "@jupyterlab/nbformat": "^3.5.2", - "@jupyterlab/services": "^7.3.0", + "@jupyterlab/services": "^7.6.0", "chalk": "^5.2.0", "get-port": "^6.1.2", "myst-cli-utils": "^2.0.13", "myst-common": "^1.10.0", + "myst-frontmatter": "^1.10.0", + "myst-spec": "^0.0.5", "node-fetch": "^3.3.0", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", "which": "^4.0.0", }, "devDependencies": { + "@types/which": "^3.0.4", "js-yaml": "^4.1.0", }, }, @@ -279,6 +329,7 @@ }, "devDependencies": { "myst-parser": "^1.5.11", + "vfile": "^5.3.7", }, }, "packages/myst-ext-card": { @@ -296,9 +347,11 @@ "version": "1.0.9", "dependencies": { "myst-common": "^1.7.2", + "myst-directives": "^1.7.3", }, "devDependencies": { "myst-parser": "^1.5.9", + "unist-util-select": "^4.0.3", }, }, "packages/myst-ext-grid": { @@ -306,6 +359,7 @@ "version": "1.1.0", "dependencies": { "myst-common": "^1.7.2", + "myst-directives": "^1.7.3", }, "devDependencies": { "myst-parser": "^1.7.1", @@ -319,6 +373,7 @@ }, "devDependencies": { "myst-parser": "^1.5.7", + "unist-util-select": "^4.0.3", }, }, "packages/myst-ext-proof": { @@ -326,6 +381,7 @@ "version": "1.0.12", "dependencies": { "myst-common": "^1.7.2", + "myst-directives": "^1.7.3", "myst-spec": "^0.0.5", }, "devDependencies": { @@ -347,9 +403,11 @@ "version": "1.0.9", "dependencies": { "myst-common": "^1.7.2", + "myst-directives": "^1.7.3", }, "devDependencies": { "myst-parser": "^1.5.9", + "unist-util-select": "^4.0.3", }, }, "packages/myst-frontmatter": { @@ -364,6 +422,8 @@ "spdx-correct": "^3.2.0", }, "devDependencies": { + "@types/js-yaml": "^4.0.5", + "@types/json-schema": "^7.0.15", "@types/spdx-correct": "^3.1.0", "glob": "^10.3.1", "js-yaml": "^4.1.0", @@ -382,6 +442,7 @@ }, "devDependencies": { "@jupyterlab/nbformat": "^3.5.2", + "@types/mdast": "^3.0.12", }, }, "packages/myst-parser": { @@ -416,6 +477,7 @@ "@types/js-yaml": "^4.0.5", "@types/mdast": "^3.0.10", "js-yaml": "^4.1.0", + "myst-spec-ext": "^1.9.4", "myst-to-html": "^1.7.3", "myst-transforms": "^1.3.50", "rehype-stringify": "^9.0.3", @@ -428,11 +490,15 @@ "myst-common": "^1.10.0", "myst-spec-ext": "^1.10.0", }, + "devDependencies": { + "vfile": "^5.3.7", + }, }, "packages/myst-spec": { "name": "myst-spec", "version": "0.0.5", "devDependencies": { + "@jupyterlab/nbformat": "^3.5.2", "copyfiles": "^2.4.1", }, }, @@ -460,6 +526,8 @@ "simple-validators": "^1.2.0", }, "devDependencies": { + "@types/adm-zip": "^0.5.0", + "@types/js-yaml": "^4.0.5", "@types/nunjucks": "^3.2.2", "memfs": "^3.5.3", }, @@ -474,7 +542,12 @@ "myst-frontmatter": "^1.9.3", "myst-spec": "^0.0.5", "myst-spec-ext": "^1.9.3", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", + "vfile": "^5.3.7", + }, + "devDependencies": { + "unist-builder": "^3.0.0", }, }, "packages/myst-to-html": { @@ -490,6 +563,7 @@ "mdast-util-find-and-replace": "^2.1.0", "mdast-util-to-hast": "^12.3.0", "myst-common": "^1.10.0", + "myst-spec": "^0.0.5", "rehype-format": "^4.0.1", "rehype-parse": "^8.0.4", "rehype-remark": "^9.1.2", @@ -502,7 +576,10 @@ "unist-util-visit": "^4.1.0", }, "devDependencies": { + "@types/hast": "^2.3.4", + "@types/mdast": "^3.0.12", "hastscript": "^7.0.0", + "js-yaml": "^4.1.0", }, }, "packages/myst-to-ipynb": { @@ -515,11 +592,17 @@ "mdast-util-to-markdown": "^1.5.0", "myst-common": "^1.7.6", "myst-frontmatter": "^1.7.6", + "myst-spec": "^0.0.5", + "myst-spec-ext": "^1.10.0", "myst-to-md": "^1.0.15", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", "vfile-reporter": "^7.0.4", }, + "devDependencies": { + "@types/js-yaml": "^4.0.5", + }, }, "packages/myst-to-jats": { "name": "myst-to-jats", @@ -537,6 +620,7 @@ "myst-spec-ext": "^1.9.2", "myst-transforms": "^1.3.44", "nbtx": "^0.4.0", + "orcid": "^1.0.0", "unified": "^10.1.2", "unist-util-remove": "^3.1.0", "unist-util-select": "^4.0.3", @@ -548,9 +632,11 @@ "@types/js-yaml": "^4.0.5", "@types/katex": "^0.14.0", "@types/mdast": "^3.0.10", - "jats-xml": "^1.0.7", + "jats-xml": "^1.1.7", "js-yaml": "^4.1.0", "myst-cli-utils": "^2.0.11", + "simple-validators": "^1.2.0", + "unist-builder": "^3.0.0", }, }, "packages/myst-to-md": { @@ -563,10 +649,15 @@ "mdast-util-to-markdown": "^1.5.0", "myst-common": "^1.8.3", "myst-frontmatter": "^1.8.3", + "myst-spec": "^0.0.5", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", "vfile-reporter": "^7.0.4", }, + "devDependencies": { + "@types/js-yaml": "^4.0.5", + }, }, "packages/myst-to-tex": { "name": "myst-to-tex", @@ -575,13 +666,18 @@ "myst-common": "^1.9.4", "myst-ext-proof": "^1.0.11", "myst-frontmatter": "^1.9.4", + "myst-spec": "^0.0.5", "myst-spec-ext": "^1.9.4", + "unified": "^10.1.2", "unist-util-remove": "^3.1.0", "unist-util-select": "^4.0.3", + "vfile": "^5.3.7", "vfile-reporter": "^7.0.4", }, "devDependencies": { + "js-yaml": "^4.1.0", "myst-parser": "^1.7.1", + "unist-builder": "^3.0.0", }, }, "packages/myst-to-typst": { @@ -590,11 +686,18 @@ "dependencies": { "myst-common": "^1.10.0", "myst-frontmatter": "^1.10.0", + "myst-spec": "^0.0.5", "myst-spec-ext": "^1.10.0", - "tex-to-typst": "^0.0.20", + "tex-to-typst": "^0.0.21", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", + "vfile": "^5.3.7", "vfile-reporter": "^7.0.4", }, + "devDependencies": { + "js-yaml": "^4.1.0", + "unist-builder": "^3.0.0", + }, }, "packages/myst-toc": { "name": "myst-toc", @@ -619,6 +722,7 @@ "myst-spec": "^0.0.5", "myst-spec-ext": "^1.10.0", "myst-to-html": "1.7.3", + "rehype-format": "^4.0.1", "rehype-parse": "^8.0.4", "rehype-remark": "^9.1.2", "unified": "^10.0.0", @@ -633,7 +737,9 @@ "vfile-message": "^3.1.2", }, "devDependencies": { + "@types/js-yaml": "^4.0.5", "@types/katex": "^0.14.0", + "@types/mdast": "^3.0.12", }, }, "packages/mystmd": { @@ -643,12 +749,16 @@ "myst": "./dist/myst.cjs", }, "devDependencies": { + "@types/js-yaml": "^4.0.5", "async-mutex": "^0.5.0", "chalk": "^5.2.0", "commander": "^10.0.1", "core-js": "^3.31.1", "js-yaml": "^4.1.0", - "myst-cli": "^1.8.2", + "myst-cli": "^1.10.1", + "myst-cli-utils": "^2.0.14", + "myst-common": "^1.10.0", + "myst-templates": "^1.0.27", }, }, "packages/simple-validators": { @@ -670,8 +780,12 @@ "unist-builder": "^3.0.0", "unist-util-remove": "^3.1.0", "unist-util-select": "^4.0.3", + "vfile": "^5.3.7", "vfile-reporter": "^7.0.4", }, + "devDependencies": { + "js-yaml": "^4.1.0", + }, }, "packages/tsconfig": { "name": "tsconfig", @@ -812,37 +926,37 @@ "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - "@jupyter/ydoc": ["@jupyter/ydoc@3.1.0", "", { "dependencies": { "@jupyterlab/nbformat": "^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0", "@lumino/coreutils": "^1.11.0 || ^2.0.0", "@lumino/disposable": "^1.10.0 || ^2.0.0", "@lumino/signaling": "^1.10.0 || ^2.0.0", "y-protocols": "^1.0.5", "yjs": "^13.5.40" } }, "sha512-+hLNUBZr8Zz8NiuaoYKINDURDJHbjFGxg8EcSU52y+rBe412TsoFxfPSng4eP7w3cZFrVlm7D+8K0nAMHxj0ZQ=="], + "@jupyter/ydoc": ["@jupyter/ydoc@4.1.0", "", { "dependencies": { "@jupyterlab/nbformat": "^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0", "@lumino/coreutils": "^1.11.0 || ^2.0.0", "@lumino/disposable": "^1.10.0 || ^2.0.0", "@lumino/signaling": "^1.10.0 || ^2.0.0", "y-protocols": "^1.0.5", "yjs": "^13.5.40" } }, "sha512-XOJbuSKCWpyd87ekpppGTm7iGiEZPONxLiiM6jOBZ2y8uvDhEdb6ltn0Rt8GR2ZEvcLC+MgSE+IGTnguKEhzNA=="], - "@jupyterlab/coreutils": ["@jupyterlab/coreutils@6.4.8", "", { "dependencies": { "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/signaling": "^2.1.4", "minimist": "~1.2.0", "path-browserify": "^1.0.0", "url-parse": "~1.5.4" } }, "sha512-y7NGkh31fJaRMDGPfi6uwUeP3eCJrwNSVqmEo0bN9qxiJsysRGOBKPAFuFUS3IKD/au79EwyKHgxhBZxLwiqBQ=="], + "@jupyterlab/coreutils": ["@jupyterlab/coreutils@6.6.1", "", { "dependencies": { "@lumino/coreutils": "^2.2.2", "@lumino/disposable": "^2.1.5", "@lumino/signaling": "^2.1.5", "minimist": "~1.2.0", "path-browserify": "^1.0.0", "url-parse": "~1.5.4" } }, "sha512-1DjMEoiHi3CwycFVM0RWfn53+0zGhJ064SKqVeYKxxFFJdN3u7ezClduRiIGEQXAiums8w+9QLlljBx/M+kaUw=="], "@jupyterlab/nbformat": ["@jupyterlab/nbformat@3.5.2", "", { "dependencies": { "@lumino/coreutils": "^1.11.0" } }, "sha512-Ml5hNpS9tMqZ9ThI24+iXHgX71XWQAysyPOU1vA3idvTGCbGhVc4FaZcDX17uepA7yIEUitlj4xQGtJR8hNzuA=="], - "@jupyterlab/services": ["@jupyterlab/services@7.4.8", "", { "dependencies": { "@jupyter/ydoc": "^3.1.0", "@jupyterlab/coreutils": "^6.4.8", "@jupyterlab/nbformat": "^4.4.8", "@jupyterlab/settingregistry": "^4.4.8", "@jupyterlab/statedb": "^4.4.8", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/polling": "^2.1.4", "@lumino/properties": "^2.0.3", "@lumino/signaling": "^2.1.4", "ws": "^8.11.0" } }, "sha512-ftv5+It2nGu+i0lWFgTIqMR9KV4YfG+Mg37lscndDGOfJ5YM3sxl61pU2Zv7YrdYLyMwA9xzXpcMHQdzCoIuZw=="], + "@jupyterlab/services": ["@jupyterlab/services@7.6.1", "", { "dependencies": { "@jupyter/ydoc": "^4.0.0", "@jupyterlab/coreutils": "^6.6.1", "@jupyterlab/nbformat": "^4.6.1", "@jupyterlab/settingregistry": "^4.6.1", "@jupyterlab/statedb": "^4.6.1", "@lumino/coreutils": "^2.2.2", "@lumino/disposable": "^2.1.5", "@lumino/polling": "^2.1.5", "@lumino/properties": "^2.0.4", "@lumino/signaling": "^2.1.5", "ws": "^8.11.0" } }, "sha512-pZ3B+m6bPZs3oUO0hgNd61hI9LlDKN3q931XWe4gWr8S/BwY9RHxoIe/mDgP38G+dS5P1ONKpaFXGICJkvUoQg=="], - "@jupyterlab/settingregistry": ["@jupyterlab/settingregistry@4.4.8", "", { "dependencies": { "@jupyterlab/nbformat": "^4.4.8", "@jupyterlab/statedb": "^4.4.8", "@lumino/commands": "^2.3.2", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/signaling": "^2.1.4", "@rjsf/utils": "^5.13.4", "ajv": "^8.12.0", "json5": "^2.2.3" }, "peerDependencies": { "react": ">=16" } }, "sha512-V7fP4nmstjzGttCMP0ISJB9Gy1S76Z1AYmyXtEgsSFzOeF51hjWAUJpLUiRqMebSmy5aVDj1CdjWIYAxx/sGXw=="], + "@jupyterlab/settingregistry": ["@jupyterlab/settingregistry@4.6.1", "", { "dependencies": { "@jupyterlab/nbformat": "^4.6.1", "@jupyterlab/statedb": "^4.6.1", "@lumino/commands": "^2.3.3", "@lumino/coreutils": "^2.2.2", "@lumino/disposable": "^2.1.5", "@lumino/signaling": "^2.1.5", "@rjsf/utils": "^5.13.4", "ajv": "^8.12.0", "json5": "^2.2.3" }, "peerDependencies": { "react": ">=16" } }, "sha512-iQlzSchm7TsnXZUKSXJApGHO3KB1WQE3svenGkPBuqP+kO6DiXY7w5v4c+SCRmAXSrvSZ03QfGLWzCeAi8DsRQ=="], - "@jupyterlab/statedb": ["@jupyterlab/statedb@4.4.8", "", { "dependencies": { "@lumino/commands": "^2.3.2", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/properties": "^2.0.3", "@lumino/signaling": "^2.1.4" } }, "sha512-VbFEKSDb6Rz8BLToYg4Fz33FNLLp2EYXulA6fcBmELJVyQNB0ZtdTnHKtzpRr0ihmdVLi7vv4PCChHdm49lSaQ=="], + "@jupyterlab/statedb": ["@jupyterlab/statedb@4.6.1", "", { "dependencies": { "@lumino/commands": "^2.3.3", "@lumino/coreutils": "^2.2.2", "@lumino/disposable": "^2.1.5", "@lumino/properties": "^2.0.4", "@lumino/signaling": "^2.1.5" } }, "sha512-rF+3oTMCCyiw4OV0caIB0E/lUHncxR73S2uxFEzMxTnIPyA1usO3X5Ln92XqrLLj0acUqexZELCY2PYZSX0wZA=="], - "@lumino/algorithm": ["@lumino/algorithm@2.0.3", "", {}, "sha512-DIcF7cIrGEC1Wh8DNjdwaL7IdcNs4Jj1VjO/90oHefeQPszKgc6DSfCxvbQiRanKR6tl/JL7tq4ZRPZES2oVAA=="], + "@lumino/algorithm": ["@lumino/algorithm@2.0.4", "", {}, "sha512-gddBhESPqu25KWLeAK9Kz8tS9Ph7P45i0CNG7Ia4XMhK9PHLtTsBdJTC9jP+MqhbzC8zDT/4ekvYRV9ojRPj7Q=="], - "@lumino/commands": ["@lumino/commands@2.3.2", "", { "dependencies": { "@lumino/algorithm": "^2.0.3", "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/domutils": "^2.0.3", "@lumino/keyboard": "^2.0.3", "@lumino/signaling": "^2.1.4", "@lumino/virtualdom": "^2.0.3" } }, "sha512-aAFEiUpp2hrkQU82Z85w1L80g0iDzsQRncLBa+pqVR/k0k1lz6H9F6xZ1ff+lBumZKKtsxBxNomvd0hfxLLqGw=="], + "@lumino/commands": ["@lumino/commands@2.3.3", "", { "dependencies": { "@lumino/algorithm": "^2.0.4", "@lumino/coreutils": "^2.2.2", "@lumino/disposable": "^2.1.5", "@lumino/domutils": "^2.0.4", "@lumino/keyboard": "^2.0.4", "@lumino/signaling": "^2.1.5", "@lumino/virtualdom": "^2.0.4" } }, "sha512-7Ci0QdFzt4NKFMhULr19sJPpOLHJw/oYlq6Pb0/Kq1s05+cIoLimr5wiyjkbAlNoGO/8A8SEBGHy3uctZz6G3A=="], - "@lumino/coreutils": ["@lumino/coreutils@2.2.1", "", { "dependencies": { "@lumino/algorithm": "^2.0.3" } }, "sha512-yij4TnxDIum7xfFUsVvZB0oLv4shs2mNbn3juwtEIsruvVBPmurNzKX0Y8z2QetbP2AZ6MSFtBzEKsihf0H0VA=="], + "@lumino/coreutils": ["@lumino/coreutils@2.2.2", "", { "dependencies": { "@lumino/algorithm": "^2.0.4" } }, "sha512-zaKJaK7rawPATn2BGHkbMrR6oK3s9PxNe9KreLwWF2dB4ZBHDiEmNLRyHRorfJ7XqVOEXAsAAj0jFn+qJPC/4Q=="], - "@lumino/disposable": ["@lumino/disposable@2.1.4", "", { "dependencies": { "@lumino/signaling": "^2.1.4" } }, "sha512-qTJiDbglPE2QnG4x4gtBcRbcfKQibxyyinNGKcNDrcK2TGTbbhK5PpMQ8d70l2V2Xw2pb/LfksBAg5pxkJ/G4A=="], + "@lumino/disposable": ["@lumino/disposable@2.1.5", "", { "dependencies": { "@lumino/signaling": "^2.1.5" } }, "sha512-hO9AkJK0oEGzxopuxI8LaZqwzSNwXJTGCdr5K4gh6al+zxpN7rOCh6Aq3zDxkIHJU4zybxv8r02ardx9XJsG3A=="], - "@lumino/domutils": ["@lumino/domutils@2.0.3", "", {}, "sha512-bXAbZg3mf2ZDNdBBpCGDike3U+osRGHePTh8H2Ud2KwaN4g/5IryFJm/TiO4K5IYs91bWF2Zqhf3FsdbZKHlGw=="], + "@lumino/domutils": ["@lumino/domutils@2.0.4", "", {}, "sha512-naYGUQn3e0CLtz/tjKOZP8SOBg0SW7EguhkxLpNUXlVUvx7rVsfr0VI22FVL+jgI0FbxXpEkxpSMxtK73jxJAg=="], - "@lumino/keyboard": ["@lumino/keyboard@2.0.3", "", {}, "sha512-bU2OxAR8a9eNBdV0YFjU6/lVVpbOw1gM7yHOuDGDdNu4J0UpKapFoR9gopNGSaHTmTwDtx9RHdFfIAgHwjZ+VQ=="], + "@lumino/keyboard": ["@lumino/keyboard@2.0.4", "", {}, "sha512-kIVkdSz8F5wtZr8hZp0CMX+E0eMCOnFH6XCT7j2UBQ80ERJHFy0eX+IbNo3dtRQ7+CcDhBV4hQquFNFa+/04QQ=="], - "@lumino/polling": ["@lumino/polling@2.1.4", "", { "dependencies": { "@lumino/coreutils": "^2.2.1", "@lumino/disposable": "^2.1.4", "@lumino/signaling": "^2.1.4" } }, "sha512-gSkxlIJ/4/esY2G7bsRrY9A4KimDMHTo0shaD+MCbhd09fZMCWJoDMcA447/dykB1rM5NXgugNLjpdGGL/e8cw=="], + "@lumino/polling": ["@lumino/polling@2.1.5", "", { "dependencies": { "@lumino/coreutils": "^2.2.2", "@lumino/disposable": "^2.1.5", "@lumino/signaling": "^2.1.5" } }, "sha512-YhQRWTNRVSi5R5uatwh1jkxASY5JKyAGWmtnfQOZWLDUFmsIjOTsS8NaYg1BgneZjWM3fbA18dCDDT7PPs5X1g=="], - "@lumino/properties": ["@lumino/properties@2.0.3", "", {}, "sha512-zkXIU5uYz/ScHCHGl5Bt4gMYsfPxZEduZd80zqDslBWvTIMro3NnzLe66NMnecbdr5N3hDJagYyA8//Qy3XjiA=="], + "@lumino/properties": ["@lumino/properties@2.0.4", "", {}, "sha512-XsL2qLZk+1FbfuTrkyjciI8PMDw3YcaBkqVQ+iv7OOJf9bUlrmTpCMY0Hu5d3hV2W3TWlRsdbvRRLEBJSKv0iA=="], - "@lumino/signaling": ["@lumino/signaling@2.1.4", "", { "dependencies": { "@lumino/algorithm": "^2.0.3", "@lumino/coreutils": "^2.2.1" } }, "sha512-nC5Z6d9om369Jkh1Vp3b7C89hV4cjr1fQDVcxhemyKXwc9r6VW7FpKixC+jElcAknP5KLj1FAa8Np+K06mMkEA=="], + "@lumino/signaling": ["@lumino/signaling@2.1.5", "", { "dependencies": { "@lumino/algorithm": "^2.0.4", "@lumino/coreutils": "^2.2.2" } }, "sha512-Wkx6WR45ynmKBlW0GBEoh4xk9+QluKr1JHuMftqcStBHSQBCnN54UKRRDbySXHGRhhx6p4neu7sGomgQSlQK8w=="], - "@lumino/virtualdom": ["@lumino/virtualdom@2.0.3", "", { "dependencies": { "@lumino/algorithm": "^2.0.3" } }, "sha512-q2C8eBxPvvOOQjN3KuxZ+vJi082JH/GF7KwMdaWsy5g+7wjKdnXPuLQFTBLOrVqIzmbxBDlLeFr93CEhdQXcyQ=="], + "@lumino/virtualdom": ["@lumino/virtualdom@2.0.4", "", { "dependencies": { "@lumino/algorithm": "^2.0.4" } }, "sha512-7MFthA9KUsqZTGm/D98FZt1QupjIGyd3XyB4SIugn6DQAqhjBiyykCZydnRq3qmuMHybQel33dNIbHpzyNyQwA=="], "@manypkg/find-root": ["@manypkg/find-root@1.1.0", "", { "dependencies": { "@babel/runtime": "^7.5.5", "@types/node": "^12.7.1", "find-up": "^4.1.0", "fs-extra": "^8.1.0" } }, "sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA=="], @@ -1014,7 +1128,7 @@ "@types/uuid": ["@types/uuid@8.3.4", "", {}, "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="], - "@types/which": ["@types/which@3.0.0", "", {}, "sha512-ASCxdbsrwNfSMXALlC3Decif9rwDMu+80KGp5zI2RLRotfMsTv7fHL8W8VDp24wymzDyIFudhUeSCugrgRFfHQ=="], + "@types/which": ["@types/which@3.0.4", "", {}, "sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w=="], "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], @@ -1106,7 +1220,7 @@ "agent-base": ["agent-base@7.1.0", "", { "dependencies": { "debug": "^4.3.4" } }, "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg=="], - "ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" }, "peerDependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], @@ -1296,7 +1410,7 @@ "credit-roles": ["credit-roles@2.1.0", "", {}, "sha512-wt1jw7lDnzY1Ob4cDHpXboN4Bfu6l7reKal0zxtKnxogqw916l+Iu862LxIze4U4y44ssAd0TOQqVg2cXY2V6Q=="], - "cross-spawn": ["cross-spawn@6.0.6", "", { "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw=="], + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], "crypto": ["crypto@1.0.1", "", {}, "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig=="], @@ -1376,7 +1490,7 @@ "docx": ["docx@7.8.2", "", { "dependencies": { "@types/node": "^18.0.0", "jszip": "^3.1.5", "nanoid": "^3.1.20", "xml": "^1.0.1", "xml-js": "^1.6.8" } }, "sha512-ptaiVZunYO04z113E/jR3yZx7/VaeUap2CHM9V1YwlmGygJ2W8y2KBmAsVgzlzs+qT7+3d5W3Q7+57BgvGiL+Q=="], - "doi-utils": ["doi-utils@2.0.5", "", {}, "sha512-BAaEF9L9KqJmQfk1frtgAySPnwOdaY2UzZ85XvFAMLdGeqrH/GiwIziQU2y3WgrCuQ0ng74bq5uYBo8Qma/WqA=="], + "doi-utils": ["doi-utils@2.0.6", "", {}, "sha512-QkaDmWbr5lIugDgl9fCxDMos2OJJ9Rp4c9XB7wgRGm6hJQ+hgeqI78DqToU8+vmtU+1XAyyVYJoLZUAgBdsFtg=="], "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], @@ -1484,8 +1598,6 @@ "extendable-error": ["extendable-error@0.1.7", "", {}, "sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg=="], - "fair-principles": ["fair-principles@2.0.0", "", {}, "sha512-yjZ2BW52nOWnpEeiY16v3O5NogwER53pSKiRLslPkAcgn0cZX3k7udWUgpaGMXdora4MxA3bwVo6V2ZMfQDwrg=="], - "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], "fast-diff": ["fast-diff@1.3.0", "", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="], @@ -1568,7 +1680,7 @@ "glob": ["glob@10.3.3", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.0.3", "minimatch": "^9.0.1", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", "path-scurry": "^1.10.1" }, "bin": "dist/cjs/src/bin.js" }, "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw=="], - "glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], "globals": ["globals@13.20.0", "", { "dependencies": { "type-fest": "^0.20.2" } }, "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ=="], @@ -1772,17 +1884,15 @@ "jackspeak": ["jackspeak@2.3.6", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ=="], - "jats-fetch": ["jats-fetch@1.0.8", "", { "dependencies": { "doi-utils": "^2.0.0", "myst-cli-utils": "^2.0.0", "node-fetch": "^3.3.1" } }, "sha512-NszocWquZ5bB36gzw45c6Tv9+wyGqJ4/c9Q4MggqqxzNQ4urADXjsypK7j9douBR+RjKJy73/LitDJnEBN3JuA=="], - - "jats-tags": ["jats-tags@1.0.8", "", {}, "sha512-eTnBvOc5XKha7j8SyWupKIkV9lbkqr7c4CjW6wNNALMrpTJ39GgV5JZ6ZaoUl6zUC2/T2W97A51MXvQWAAoF/Q=="], + "jats-tags": ["jats-tags@1.1.8", "", {}, "sha512-/sTtXe3cacc8VgHLBV5rdKmSDNALM6SCbaruAY5PVwR2+J60XGk1PvWkyAMQ/th5RY698OeDxQ2LmY162gC1pg=="], "jats-to-myst": ["jats-to-myst@workspace:packages/jats-to-myst"], - "jats-utils": ["jats-utils@1.0.8", "", { "dependencies": { "unist-util-select": "^4.0.0" }, "peerDependencies": { "jats-tags": "^1.0.8", "xml-js": "^1" } }, "sha512-e2RUoznZXK1GLPll6opEMt2aQePdWQOwBCHCWkLnhphlnE356sw4WRUM2leCG5xVxMV7XQBIfhHFulWCO47RnA=="], + "jats-utils": ["jats-utils@1.1.8", "", { "dependencies": { "jats-tags": "^1.1.8", "myst-common": "^1.0.0", "unist-util-select": "^4.0.0" }, "peerDependencies": { "xml-js": "^1" } }, "sha512-7aClX64cv55orWlnOdgLifKJ8H4XcD/sO+qOd7kqloD8AzlddpgiGiSReTAzRDDStwDttMKfjE7qVTdm3TnQsA=="], - "jats-xml": ["jats-xml@1.0.8", "", { "dependencies": { "adm-zip": "^0.5.10", "doi-utils": "^2.0.0", "fair-principles": "^2.0.0", "jats-fetch": "^1.0.8", "jats-tags": "^1.0.8", "jats-utils": "^1.0.8", "js-yaml": "^4.1.0", "node-fetch": "^3.3.1", "unist-util-is": "^5.2.1", "unist-util-remove": "^3.1.0", "unist-util-select": "^4.0.0", "which": "^3.0.1", "xml-js": "^1.6.11" }, "peerDependencies": { "chalk": "^5.2.0", "commander": "^10.0.1" }, "bin": { "jats": "dist/jats.cjs" } }, "sha512-yI1rbW/toz4x04/pDp1gsNcySxC4kFhJcU91sqBZdliCg5PXvCOCnb/OhGGPAD/vehBWGiGbdjuLbQ4WDcBSNA=="], + "jats-xml": ["jats-xml@1.1.8", "", { "dependencies": { "adm-zip": "^0.5.10", "character-entities": "^2.0.2", "doi-utils": "^2.0.6", "jats-tags": "^1.1.8", "jats-utils": "^1.1.8", "myst-cli-utils": "^2.0.11", "myst-common": "^1.7.5", "myst-frontmatter": "^1.7.5", "myst-spec": "^0.0.5", "node-fetch": "^3.3.1", "unist-util-remove": "4.0.0", "unist-util-select": "^4.0.0", "vfile": "^5.0.0", "which": "^3.0.1", "xml-js": "^1.6.11" }, "peerDependencies": { "chalk": "^5.2.0" } }, "sha512-S4hslkbXM/EN5KLxMNFgXIC+u2y6nvOUhVFPQK6CN0y6DReTSGiMOFxTct3gv0Ba2B3dTDtN4L5M7zsxqXqc8w=="], - "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": "bin/js-yaml.js" }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], @@ -1794,7 +1904,7 @@ "json-schema-merge-allof": ["json-schema-merge-allof@0.8.1", "", { "dependencies": { "compute-lcm": "^1.1.2", "json-schema-compare": "^0.2.2", "lodash": "^4.17.20" } }, "sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w=="], - "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], @@ -1908,7 +2018,7 @@ "mdurl": ["mdurl@1.0.1", "", {}, "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g=="], - "meca": ["meca@1.0.8", "", { "dependencies": { "adm-zip": "^0.5.10", "jats-utils": "^1.0.8", "jats-xml": "^1.0.8", "node-fetch": "^3.3.1", "unist-util-select": "^4.0.0", "xml-js": "^1.6.11" }, "peerDependencies": { "chalk": "^5.2.0", "commander": "^10.0.1" }, "bin": "dist/meca.cjs" }, "sha512-CIctq7IldEEKHcYsNeCw0+MOtoblXX5pzI7qShnaWpP+WHETf27blyqNAMyJk6O/YUxXwuJ2eqYmcwmNy2k40Q=="], + "meca": ["meca@1.1.8", "", { "dependencies": { "adm-zip": "^0.5.10", "jats-utils": "^1.1.8", "jats-xml": "^1.1.8", "myst-cli-utils": "^2.0.11", "myst-common": "^1.7.5", "node-fetch": "^3.3.1", "unist-util-select": "^4.0.0", "xml-js": "^1.6.11" }, "peerDependencies": { "chalk": "^5.2.0", "commander": "^10.0.1" }, "bin": { "meca": "dist/meca.cjs" } }, "sha512-GnRyStOHuD9IRSBoaZQgh1DvgD3q2983BXJe7lmCpzoTcacNc3c6ZrYYnIbDLst+5Uz2FBo42eEpg7kSyqUxZQ=="], "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], @@ -2064,6 +2174,8 @@ "mystmd": ["mystmd@workspace:packages/mystmd"], + "mystmd-docs": ["mystmd-docs@workspace:docs"], + "nanoid": ["nanoid@5.1.6", "", { "bin": "bin/nanoid.js" }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], @@ -2170,7 +2282,7 @@ "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], - "path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="], + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], @@ -2340,9 +2452,9 @@ "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], - "shebang-command": ["shebang-command@1.2.0", "", { "dependencies": { "shebang-regex": "^1.0.0" } }, "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg=="], + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - "shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="], + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], "shell-quote": ["shell-quote@1.8.1", "", {}, "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA=="], @@ -2418,7 +2530,7 @@ "strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="], - "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + "strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], @@ -2438,7 +2550,7 @@ "tex-to-myst": ["tex-to-myst@workspace:packages/tex-to-myst"], - "tex-to-typst": ["tex-to-typst@0.0.20", "", { "dependencies": { "@unified-latex/unified-latex": "^1.4.0", "@unified-latex/unified-latex-util-arguments": "^1.4.0", "@unified-latex/unified-latex-util-parse": "^1.4.0", "unified": "^10.1.2" } }, "sha512-q6cVBkEYAX+N8wgA5pmyw4I1URm12X0JDpBEEozo626G8MjJuOfsDeMwuLzodHJPuvsk/LRRtANxpqK8JwpiXQ=="], + "tex-to-typst": ["tex-to-typst@0.0.21", "", { "dependencies": { "@unified-latex/unified-latex": "^1.4.0", "@unified-latex/unified-latex-util-arguments": "^1.4.0", "@unified-latex/unified-latex-util-parse": "^1.4.0", "unified": "^10.1.2" } }, "sha512-EgAErpOxUaE0heF/GZ1aQ10QXCEr9Wy7FIMBkgwU5dLzXu1d3/MijvTvc5wJOgeKrx2ptzXz/2GfUDyguD9XlQ=="], "text-table": ["text-table@0.2.0", "", {}, "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw=="], @@ -2678,17 +2790,15 @@ "@changesets/write/prettier": ["prettier@2.8.8", "", { "bin": "bin-prettier.js" }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], - "@eslint/eslintrc/ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - - "@eslint/eslintrc/strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="], - - "@jupyter/ydoc/@lumino/coreutils": ["@lumino/coreutils@1.12.1", "", { "peerDependencies": { "crypto": "1.0.1" } }, "sha512-JLu3nTHzJk9N8ohZ85u75YxemMrmDzJdNgZztfP7F7T7mxND3YVNCkJG35a6aJ7edu1sIgCjBxOvV+hv27iYvQ=="], + "@jupyter/ydoc/@jupyterlab/nbformat": ["@jupyterlab/nbformat@4.6.1", "", { "dependencies": { "@lumino/coreutils": "^2.2.2" } }, "sha512-+dB/edJjn7JDFiyN4nGw+quxBwUFGktgbR8udQeJweDne5mPMArpwCez/HBFkIW/816UX6+gSji01wTP4y10Lw=="], "@jupyterlab/nbformat/@lumino/coreutils": ["@lumino/coreutils@1.12.1", "", { "peerDependencies": { "crypto": "1.0.1" } }, "sha512-JLu3nTHzJk9N8ohZ85u75YxemMrmDzJdNgZztfP7F7T7mxND3YVNCkJG35a6aJ7edu1sIgCjBxOvV+hv27iYvQ=="], - "@jupyterlab/services/@jupyterlab/nbformat": ["@jupyterlab/nbformat@4.4.8", "", { "dependencies": { "@lumino/coreutils": "^2.2.1" } }, "sha512-zhpmu4jkIGgjLRzClifnVmM1NKfrC+A7Gcz8hnXJBinJwf6nMZaQnnXo+J6G93e0epw0E/YBrJ1kI/JJWLMeCg=="], + "@jupyterlab/services/@jupyterlab/nbformat": ["@jupyterlab/nbformat@4.6.1", "", { "dependencies": { "@lumino/coreutils": "^2.2.2" } }, "sha512-+dB/edJjn7JDFiyN4nGw+quxBwUFGktgbR8udQeJweDne5mPMArpwCez/HBFkIW/816UX6+gSji01wTP4y10Lw=="], - "@jupyterlab/settingregistry/@jupyterlab/nbformat": ["@jupyterlab/nbformat@4.4.8", "", { "dependencies": { "@lumino/coreutils": "^2.2.1" } }, "sha512-zhpmu4jkIGgjLRzClifnVmM1NKfrC+A7Gcz8hnXJBinJwf6nMZaQnnXo+J6G93e0epw0E/YBrJ1kI/JJWLMeCg=="], + "@jupyterlab/settingregistry/@jupyterlab/nbformat": ["@jupyterlab/nbformat@4.6.1", "", { "dependencies": { "@lumino/coreutils": "^2.2.2" } }, "sha512-+dB/edJjn7JDFiyN4nGw+quxBwUFGktgbR8udQeJweDne5mPMArpwCez/HBFkIW/816UX6+gSji01wTP4y10Lw=="], + + "@jupyterlab/settingregistry/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], "@jupyterlab/settingregistry/json5": ["json5@2.2.3", "", { "bin": "lib/cli.js" }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], @@ -2704,8 +2814,6 @@ "@next/eslint-plugin-next/glob": ["glob@7.1.7", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ=="], - "@pkgr/utils/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "@pnpm/network.ca-file/graceful-fs": ["graceful-fs@4.2.10", "", {}, "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="], "@rjsf/utils/react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], @@ -2716,6 +2824,8 @@ "@unified-latex/unified-latex-util-to-string/prettier": ["prettier@2.8.8", "", { "bin": "bin-prettier.js" }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], + "ajv-formats/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], @@ -2730,10 +2840,14 @@ "cacheable-request/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], + "cffjs/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], + "check-node-version/chalk": ["chalk@3.0.0", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg=="], "check-node-version/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "cli-truncate/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], "cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -2756,9 +2870,7 @@ "copyfiles/yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], - "cross-spawn/semver": ["semver@5.7.2", "", { "bin": "bin/semver" }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], - - "cross-spawn/which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": "bin/which" }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], + "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "decompress-response/mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], @@ -2786,14 +2898,8 @@ "enquirer/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "eslint/ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - "eslint/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "eslint/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - - "eslint/glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], - "eslint/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "eslint-config-next/@typescript-eslint/parser": ["@typescript-eslint/parser@5.62.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", "@typescript-eslint/typescript-estree": "5.62.0", "debug": "^4.3.4" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA=="], @@ -2818,10 +2924,10 @@ "eslint-plugin-react/semver": ["semver@6.3.1", "", { "bin": "bin/semver.js" }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "execa/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "express/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "fdir/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "fetch-ponyfill/node-fetch": ["node-fetch@2.6.12", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g=="], @@ -2834,8 +2940,6 @@ "flat-cache/rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": "bin.js" }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], - "foreground-child/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "glob/minimatch": ["minimatch@9.0.3", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg=="], "globals/type-fest": ["type-fest@0.20.2", "", {}, "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ=="], @@ -2868,6 +2972,8 @@ "is-wsl/is-docker": ["is-docker@2.2.1", "", { "bin": "cli.js" }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="], + "jats-xml/unist-util-remove": ["unist-util-remove@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg=="], + "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], "lint-staged/commander": ["commander@13.1.0", "", {}, "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw=="], @@ -2886,8 +2992,6 @@ "log-update/wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="], - "loose-envify/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "madge/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "madge/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], @@ -2912,6 +3016,8 @@ "npm-run-all/chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="], + "npm-run-all/cross-spawn": ["cross-spawn@6.0.6", "", { "dependencies": { "nice-try": "^1.0.4", "path-key": "^2.0.1", "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" } }, "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw=="], + "npm-run-all/pidtree": ["pidtree@0.3.1", "", { "bin": "bin/pidtree.js" }, "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA=="], "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], @@ -2950,6 +3056,8 @@ "raw-body/iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + "rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + "read-yaml-file/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": "bin/js-yaml.js" }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="], "readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], @@ -2974,8 +3082,6 @@ "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], - "spawndamnit/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -2984,6 +3090,8 @@ "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], + "stylus-lookup/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], "sync-fetch/node-fetch": ["node-fetch@2.6.12", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g=="], @@ -3032,7 +3140,7 @@ "@changesets/write/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], - "@eslint/eslintrc/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], + "@jupyterlab/settingregistry/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "@manypkg/find-root/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], @@ -3044,20 +3152,18 @@ "@manypkg/get-packages/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], - "@pkgr/utils/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "@pkgr/utils/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "@pkgr/utils/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "@typescript-eslint/utils/eslint-scope/estraverse": ["estraverse@4.3.0", "", {}, "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="], + "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "cffjs/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "check-node-version/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "check-node-version/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -3080,8 +3186,6 @@ "copyfiles/yargs/yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], - "default-browser/execa/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "default-browser/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], "default-browser/execa/human-signals": ["human-signals@4.3.1", "", {}, "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ=="], @@ -3108,38 +3212,18 @@ "eslint-import-resolver-typescript/globby/slash": ["slash@4.0.0", "", {}, "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew=="], - "eslint/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], - "eslint/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "eslint/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "eslint/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "eslint/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "eslint/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "eslint/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "execa/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "execa/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "execa/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "flat-cache/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], - "foreground-child/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "foreground-child/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "foreground-child/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "hast-util-from-html/@types/hast/@types/unist": ["@types/unist@3.0.0", "", {}, "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w=="], @@ -3168,6 +3252,12 @@ "intersphinx/myst-cli-utils/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "jats-xml/unist-util-remove/@types/unist": ["@types/unist@3.0.0", "", {}, "sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w=="], + + "jats-xml/unist-util-remove/unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], + + "jats-xml/unist-util-remove/unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + "listr2/wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], "listr2/wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], @@ -3200,6 +3290,14 @@ "npm-run-all/chalk/supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="], + "npm-run-all/cross-spawn/path-key": ["path-key@2.0.1", "", {}, "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw=="], + + "npm-run-all/cross-spawn/semver": ["semver@5.7.2", "", { "bin": "bin/semver" }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + + "npm-run-all/cross-spawn/shebang-command": ["shebang-command@1.2.0", "", { "dependencies": { "shebang-regex": "^1.0.0" } }, "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg=="], + + "npm-run-all/cross-spawn/which": ["which@1.3.1", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": "bin/which" }, "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ=="], + "ora/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "ora/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -3230,8 +3328,6 @@ "rimraf/glob/path-scurry": ["path-scurry@2.0.0", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg=="], - "run-applescript/execa/cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "run-applescript/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], "run-applescript/execa/human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="], @@ -3248,12 +3344,6 @@ "send/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], - "spawndamnit/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "spawndamnit/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "spawndamnit/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "wrap-ansi-cjs/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], @@ -3268,8 +3358,6 @@ "@manypkg/find-root/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], - "@pkgr/utils/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "check-node-version/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], @@ -3286,24 +3374,12 @@ "copyfiles/yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "default-browser/execa/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "default-browser/execa/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "default-browser/execa/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "eslint-config-next/@typescript-eslint/parser/@typescript-eslint/scope-manager/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@5.62.0", "", { "dependencies": { "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "^3.3.0" } }, "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw=="], "eslint-config-next/@typescript-eslint/parser/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@5.62.0", "", { "dependencies": { "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "^3.3.0" } }, "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw=="], "eslint/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], - "eslint/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - - "execa/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - - "foreground-child/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "hast-util-from-html/hast-util-from-parse5/hastscript/hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="], "inquirer/wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], @@ -3324,6 +3400,8 @@ "npm-run-all/chalk/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], + "npm-run-all/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@1.0.0", "", {}, "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ=="], + "ora/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "precinct/detective-typescript/@typescript-eslint/typescript-estree/@typescript-eslint/types": ["@typescript-eslint/types@4.33.0", "", {}, "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ=="], @@ -3332,18 +3410,8 @@ "rimraf/glob/path-scurry/lru-cache": ["lru-cache@11.2.2", "", {}, "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg=="], - "run-applescript/execa/cross-spawn/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - - "run-applescript/execa/cross-spawn/shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], - - "run-applescript/execa/cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - - "run-applescript/execa/npm-run-path/path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], - "run-applescript/execa/onetime/mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], - "spawndamnit/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "@manypkg/find-root/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], @@ -3354,14 +3422,10 @@ "copyfiles/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "default-browser/execa/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "intersphinx/myst-cli-utils/chalk/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], "precinct/detective-typescript/@typescript-eslint/typescript-estree/@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@2.1.0", "", {}, "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw=="], - "run-applescript/execa/cross-spawn/shebang-command/shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "copyfiles/yargs/cliui/wrap-ansi/ansi-styles/color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], } } diff --git a/bunfig.toml b/bunfig.toml index f1e441c4bc..284f1af3fc 100644 --- a/bunfig.toml +++ b/bunfig.toml @@ -1,6 +1,8 @@ [install] # Use the new text-based lockfile (Bun 1.2+) lockfile = "bun.lock" +# Use pnpm-style isolated node_modules instead of the hoisted layout +linker = "isolated" [test] # Enable some Bun test features, in case we decide to use bun test diff --git a/docs/contribute-docs.md b/docs/contribute-docs.md index 3c1e426c91..ac6d992f76 100644 --- a/docs/contribute-docs.md +++ b/docs/contribute-docs.md @@ -27,28 +27,33 @@ Alternatively, you can use the [**`github.dev` web-based editor**](https://docs. ## How to build the MyST guide documentation locally +The documentation in `docs/` uses its own MyST plugins (`docs/*.mjs`) that require an install and build of the packages, so previewing it requires the full local development setup rather than a globally installed `myst`. + To build the MyST guide documentation: -1. Clone this repository: +1. Clone this repository and build: ``` git clone https://github.com/jupyter-book/mystmd + cd mystmd + bun install + bun run build + bun run link # install this version of mystmd globally ``` -2. [Install MyST Markdown by following these instructions](https://mystmd.org/guide/quickstart) -3. Navigate to the docs folder: +2. Navigate to the docs folder: ``` - cd docs/ + cd docs ``` -4. Start a MyST server to preview the documentation: +3. Start a MyST server to preview the documentation: ``` myst start ``` -This will build the documentation locally so that you can preview what your changes will look like. +The `bun run docs` command is a shorthand for steps 2 and 3 (see [the developer guide](#local-docs-workflows)). ### Build with executable content diff --git a/docs/deployment-github-pages.md b/docs/deployment-github-pages.md index 56de922479..c8e54bf6ef 100644 --- a/docs/deployment-github-pages.md +++ b/docs/deployment-github-pages.md @@ -77,7 +77,7 @@ jobs: uses: actions/configure-pages@v3 - uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 24.x - name: Install MyST Markdown run: npm install -g mystmd - name: Build HTML Assets diff --git a/docs/deployment-gitlab.md b/docs/deployment-gitlab.md index c05010ec6e..f69025c096 100644 --- a/docs/deployment-gitlab.md +++ b/docs/deployment-gitlab.md @@ -9,41 +9,56 @@ This page has important information for how to do so. ## Instructions +:::{warning} +Because of your institution’s GitLab configuration, the descriptions below may differ from the actual deployment on GitLab Pages. + +If you are new to GitLab or you are not familiar with `git`, please proceed to [](#instructions-for-beginners) +::: + To get setup with GitLab Pages, ensure that your repository is hosted in GitLab and you are in the root of the Git repository. -Create a file called `.gitlab-ci.yml` with the following content: +### Deployment with uv +Create `.gitlab-ci.yml` file in the root of your project with the content provided below: -`````{tab-set} -````{tab-item} Pixi based ```{code} yaml :filename: .gitlab-ci.yml +:linenos: +:emphasize-lines: 47 - -image: ghcr.io/prefix-dev/pixi:latest +image: ghcr.io/astral-sh/uv:debian-slim stages: - build - deploy variables: - PIXI_CACHE_DIR: "$CI_PROJECT_DIR/.pixi" HOST: "127.0.0.1" cache: paths: - - .pixi + - .venv before_script: - - pixi --version + - uv --version build: stage: build script: - # install environment from pixi.toml + pixi.lock - - pixi install --locked - - # run jupyter-book via pixi environment - - pixi run jupyter-book build --html + # initialize uv project and install jupyter-book + - uv init + - uv add "jupyter-book>=2.1.2,<3" + + # install node + - apt-get update + - apt-get install -y curl + - apt-get install -y procps + - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - + - apt-get install -y nodejs + + # run jupyter-book via uv + - uv run jupyter-book clean --all -y + - export BASE_URL="" + - uv run jupyter-book build --html artifacts: paths: - _build/html @@ -51,16 +66,27 @@ build: pages: stage: deploy script: - - mkdir public - - cp -r _build/html/* public/ + - mv _build/html/ public artifacts: paths: - public only: - - main + - main # replace it with YOUR branch name +``` + +then push this file to your GitLab repo. + +> If your Git branch is different from `main`, you should replace main in the `.gitlab-ci.yml` file on the highlighted line with your branch name. + +Once everything done, you should see `GitLab Pages` link in the right menu as shown below: +```{figure} ./images/gitlab-pages.png +:height:300px ``` -```` -````{tab-item} Poetry based + +### Deployment with poetry +Create a file called `.gitlab-ci.yml` with the following content: + + ```{code} yaml :filename: .gitlab-ci.yml variables: @@ -123,21 +149,72 @@ pages: tags: - ubuntu ``` -```` -````` +### Deployment with pixi +*Note*: You may not use pixi in your project but you may use it for GitLab deployment. + +1. Make sure that you have [`pixi`](https://pixi.prefix.dev/latest/#installation) installed on your machine. -You must set the `HOST` - this is a fix for a [known issue](https://github.com/jupyter-book/mystmd/issues/2471). +2. Create a file called `.gitlab-ci.yml` with the following content: -Note that a [pixi.toml](https://pixi.prefix.dev/latest/python/pyproject_toml/) and pixi.lock file should be included! +```{code} yaml +:filename: .gitlab-ci.yml +:linenos: +:emphasize-lines: 39 -A minimal version is shown below. +image: ghcr.io/prefix-dev/pixi:latest + +stages: + - build + - deploy + +variables: + PIXI_CACHE_DIR: "$CI_PROJECT_DIR/.pixi" + HOST: "127.0.0.1" + +cache: + paths: + - .pixi + +before_script: + - pixi --version + +build: + stage: build + script: + # install environment from pixi.toml + pixi.lock + - pixi install --locked + + # run jupyter-book via pixi environment + - pixi run jupyter-book build --html + artifacts: + paths: + - _build/html + +pages: + stage: deploy + script: + - mkdir public + - cp -r _build/html/* public/ + artifacts: + paths: + - public + only: + - main # replace it with your branch name! +``` + +> If your Git branch is different from `main`, you should replace main in the `.gitlab-ci.yml` file on the highlighted line with your branch name. + +3. Make sure your pixi project is initialized (you may use `pixi init` CLI command to do so) + +4. Make sure that your `pixi.toml` file contains `linux-64` as a platform and `python` with `jupyter-book` as dependencies. + A minimal version of the `pixi.toml` file is shown below. ```{code-block} toml :filename: pixi.toml [workspace] -authors = [{name = "Me", email = "me@me.com"}] +authors = ["Me "] channels = ["conda-forge"] name = "jbtest" platforms = ["win-64", "linux-64"] @@ -150,10 +227,18 @@ python = ">=3.14.3,<3.15" jupyter-book = ">=2.1.2,<3" ``` +5. Synchronize `pixi.lock` file using `pixi lock` CLI command. + +> Note that a [pixi.toml](https://pixi.prefix.dev/latest/python/pyproject_toml/) and pixi.lock file should be tracked by Git and pushed to the repository with other files. + +6. Add your project files to git using `git add ` (e.g `git add myst.yml main.md pixi.toml pixi.lock`) +7. Commit the changes +8. Push your branch to GitLab (e.g `git push -u origin main`) + ## External server through GitLab CI/CD -Another option is to deploy your MyST site to an external server through GitLab CI/CD. A main difference is setting 'variables' (settings $\rightarrow$ CI/CD $\rightarrow$ variables) for connecting to the server. +Another option is to deploy your MyST site to an external server through GitLab CI/CD. A main difference is setting 'variables' (settings $\rightarrow$ CI/CD $\rightarrow$ variables) for connecting to the server. An example `.gitlab-ci.yml` file for this deployment method is shown below. @@ -171,12 +256,12 @@ variables: LOCAL_BUILD_DIR: "_build/html" HOST: "127.0.0.1" BASE_URL: "" - + before_script: - apt-get update - - apt-get install -y --no-install-recommends curl rsync openssh-client git + - apt-get install -y --no-install-recommends curl rsync openssh-client git procps - # Node.js + # Node.js - curl -fsSL https://deb.nodesource.com/setup_20.x | bash - - apt-get install -y --no-install-recommends nodejs - node --version @@ -186,7 +271,7 @@ before_script: - python -m pip install --upgrade pip - pip install mystmd - # Load SSH key + # Load SSH key - eval "$(ssh-agent -s)" - chmod 400 "$WEBSITE_UPLOAD_KEY" - ssh-add "$WEBSITE_UPLOAD_KEY" @@ -195,10 +280,49 @@ deploy: stage: deploy script: # Build (important: define the project path) - - myst build --html - + - myst build --html + - rsync -ravz "${LOCAL_BUILD_DIR}/" -e "${SSH_COMMAND} -i ${WEBSITE_UPLOAD_KEY}" "${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/" ``` -Note that this way of deploying requires a gitlab runner. \ No newline at end of file +Note that this way of deploying requires a gitlab runner. + +## Instructions for beginners +If this is your first time using GitLab, complete these one-time setup steps: +1. [Install Git](https://docs.gitlab.com/topics/git/how_to_install_git/) +2. Configure your git by using the following commands in the terminal + ```shell + git config --global user.name "Your Name" + git config --global user.email "your-mail@your-domain.com" + ``` +3. Create a GitLab account [on official + GitLab](https://gitlab.com/users/sign_in/) or on the GitLab of your + institution. +4. [Set up SSH authentication](https://docs.gitlab.com/user/ssh/) + +Once set up, you have two options to connect your project to GitLab: +1. *Start from GitLab*: [Create a repository on GitLab, clone it to your computer, add your files, and push them](https://docs.gitlab.com/tutorials/make_first_git_commit/#steps) +2. *Start from your computer*: Initialize your local folder as a Git repository and push it to GitLab using the following commands: + ```bash + cd your-project-folder # go to your local folder with your myst project + git init # initialize git + git add . # stage all files + git commit -m "first commit" + git remote add origin git@gitlab.com:username/project.git # you may change this link with the link of your repository + git push -u origin main + ``` + +Once your git project is initialized, create `.gitlab-ci.yml` file in the root +of your myst project and paste the contents described in [](#deployment-with-uv) to the file. + + +Once you saved the file with provided contents, execute the next commands: +```shell +git add .gitlab-ci.yml +git commit -m "added .gitlab-ci.yml to set up GitLab Pages" +git push +``` + +You are all done. You may proceed to the page of your GitLab repository and +open GitLab Pages. diff --git a/docs/developer.md b/docs/developer.md index 799e3e5651..988278d333 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -23,6 +23,10 @@ However, for a developer it is important to understand that `mystmd` consists of 1. An engine that converts input documents into an AST (abstract syntax tree). 2. A renderer, that converts that AST into a given output format. +:::{seealso} Learn more about the AST +If the AST is unfamiliar, start with the [AST primer](xref:spec/ast-primer) for a short introduction to its structure. +::: + This model is equally applicable to exporting/converting static documents and rich web applications. Here's a workflow for static documents: ```{mermaid} @@ -315,6 +319,8 @@ The build process uses unix commands that might not work properly on Windows. When building on Windows, use either WSL or a unix-like shell (such as Git Bash or MSYS2). ``` +(local-docs-workflows)= + ### Local docs workflows (preview/build) To run a MyST server with your local changes and a preview of the documentation, run: @@ -323,6 +329,10 @@ To run a MyST server with your local changes and a preview of the documentation, $ bun run docs ``` +This builds the `mystmd` packages, and starts a live-reloading preview server. + +The `docs/` plugins import several `myst-*` packages as `workspace:*` dependencies, so that they build against the source versions in this repository (not against npm). If you only change the plugins in `docs/*.mjs`, you will need to rebuild the packages and restart the server. + ## Local development: myst-theme The [`myst-theme` README](https://github.com/jupyter-book/myst-theme/) provides a more detailed overview of the components of that package. diff --git a/docs/document-parts.md b/docs/document-parts.md index f22d85dbbe..ab0671101f 100644 --- a/docs/document-parts.md +++ b/docs/document-parts.md @@ -228,3 +228,80 @@ site: :::{seealso} Referring to parts directly with a URL is coming See the [issue tracking this enhancement](https://github.com/jupyter-book/mystmd/issues/2127). ::: + + +(parts-ast)= +## How parts appear in the AST + +Each way of defining a part lands in a different place in the JSON that `myst build` emits. +This section describes how these are represented in the AST. +See [](xref:spec) for more details about the MyST AST. + +### Page-level parts in the page AST + +There are three ways to mark a region as a named part in a document. +Each one ends up in a different place in the served JSON, even though they are often treated the same in the rendered output (see [](#ast-parse-helper) for guidance in parsing them). +We show each method below, and where it lands in the AST: + +````{list-table} +- - Method + - Example + - Location in AST +- - **Page frontmatter** (in the `parts:` key) + - ```markdown + --- + parts: + acknowledgments: "..." + abstract: "My abstract" + --- + My content... + ``` + - Stored in the page frontmatter key: + + `frontmatter.parts..mdast`. +- - **Block metadata** (within content of a page) + - ```markdown + +++ {"part": "abstract"} + + My abstract content. + + +++ + + My content + + +++ {"part": "acknowledgements"} + Thanks to ... + ``` + - Stored in a `block` node inside `mdast` with `data.part` set. + + `.mdast.children..data.part = "acknowledgements"` +- - **Heading auto-match** (heading text equals a known part name) + - ```markdown + ## Abstract + + My abstract ... + + ## Header one + + A normal (non-part) header + + ## Acknowledgments + + Thanks to ... + ``` + - Remains a regular `heading` + paragraphs sequence. + Only becomes a part block when [`extractPart()`](#ast-parse-helper) is called. +```` + +### Site-level parts in the project AST + +Site-level `parts:` (under `site:` in `myst.yml`) is a different `mystmd`-specific system for content shared across pages (like a project footer or header). +These are project-level and not part of any page's AST. +The [theme developer guide](./theme-developer.md) covers the rest of what mystmd serves alongside per-page JSON. + +(ast-parse-helper)= +### Programmatically extract a part from an AST + +Instead of manually parsing the page AST for these parts, use the [`extractPart()`](https://github.com/jupyter-book/mystmd/blob/0d626b198093fa740125d02267a111d84547fd36/packages/myst-common/src/extractParts.ts) helper to retrieve a named part regardless of how it was defined. + +For example, exporters like [`myst-to-jats`](https://github.com/jupyter-book/mystmd/blob/0d626b198093fa740125d02267a111d84547fd36/packages/myst-to-jats/src/index.ts#L720-L729) call `extractPart()` to pull these regions into their output. diff --git a/docs/embed.md b/docs/embed.md index 1139090d4d..0006d46c51 100644 --- a/docs/embed.md +++ b/docs/embed.md @@ -137,7 +137,7 @@ You can then refer to content in these sites in two ways: When you build your MyST project, the content that your `#label` points to will be embedded in-place of the directive. -:::{prf:example} Embedding from the MyST Spec +:::{proof:example} Embedding from the MyST Spec The following content is embedded from `![](xref:spec#admonition)`: @@ -157,7 +157,7 @@ This is also helpful for including content snippets, such as a table or an equat See {myst:directive}`the {include} directive documentation ` for details about all the arguments you can give to `{include}`. -:::{prf:example} Equation Bank +:::{proof:example} Equation Bank :label: eg:equation-bank It is common practice to keep complex equations out of the main document so that they can be shared between slides, papers, and different renderings of a document. The `equations` folder in our documentation has a `curl.tex` file: diff --git a/docs/frontmatter.md b/docs/frontmatter.md index 59ebe56d0f..1a365c478b 100644 --- a/docs/frontmatter.md +++ b/docs/frontmatter.md @@ -189,7 +189,7 @@ project: ## Titles -There are several fields to title MyST projects and pages. Primary page and project titles can be specified simply as `title`. Pages and projects also both have `short_title`; this should provide a summarized title in less than 40 characters. It is used where space is limited, for example a site navigation panel, running-head titles in an static export, etc. You may specify `subtitle`; this conveys complimentary information to the title and may be displayed below the title. +There are several fields to title MyST projects and pages. Primary page and project titles can be specified simply as `title`. Pages and projects also both have `short_title`; this should provide a summarized title in less than 40 characters. It is used where space is limited, for example a site navigation panel, running-head titles in an static export, etc. You may specify `subtitle`; this conveys complementary information to the title and may be displayed below the title. ````{note} Defining Page Title in Markdown diff --git a/docs/glossary.md b/docs/glossary.md index c7a1b5a0a4..3502f0c3e1 100644 --- a/docs/glossary.md +++ b/docs/glossary.md @@ -71,10 +71,10 @@ MyST Document Engine [^myst]: Often we focus upon a MyST Engine's ability to parse {term}`MyST Markdown`. However, a MyST Engine may consume _any_ kind of input markup as long as the _result_ is a {term}`MyST AST` that follows the {term}`MyST Specification`. This is why the [official MyST Document Engine](https://mystmd.org) can parse other kinds of markup, such as a subset of LaTeX. MyST Specification -: A specification that describes the structure and function of the MysT AST. It provides a framework for defining all of the kinds of content and metadata that a MyST document can contain. For example, the MyST specification defines how sections can contain paragraphs, how paragraphs can contain sentences, and how sentences can contain a "bolded" chunk of text. The specification is [defined at mystmd.org/spec](xref:spec/#overview). +: A specification that describes the structure and function of the MysT AST. It provides a framework for defining all of the kinds of content and metadata that a MyST document can contain. For example, the MyST specification defines how sections can contain paragraphs, how paragraphs can contain sentences, and how sentences can contain a "bolded" chunk of text. The specification is [defined at mystmd.org/spec](xref:spec#overview). MyST AST -: A structured representation of a MyST Document, typically built from a markup language (like MyST Markdown) by a {term}`MyST Document Engine`, that adheres to the {term}`MyST Specification`. Usually, a MyST [Abstract Syntax Tree (AST)](wiki:Abstract_syntax_tree) is represented as a JSON data structure, with metadata attached to each piece of content that describes its role, relationships with other content, etc. The AST allows us to separate the steps of parsing content (such as {term}`MyST Markdown`) from generating output like HTML, PDF, docx, etc. +: A structured representation of a MyST Document, typically built from a markup language (like MyST Markdown) by a {term}`MyST Document Engine`, that adheres to the {term}`MyST Specification`. Usually, a MyST [Abstract Syntax Tree (AST)](wiki:Abstract_syntax_tree) is represented as a JSON data structure, with metadata attached to each piece of content that describes its role, relationships with other content, etc. The AST allows us to separate the steps of parsing content (such as {term}`MyST Markdown`) from generating output like HTML, PDF, docx, etc. For an introduction to its structure, see the [AST primer](xref:spec/ast-primer). AST node : A single node in the {term}`MyST AST`. It has an explicit `type` and contains 0 or more children. diff --git a/docs/images/gitlab-pages.png b/docs/images/gitlab-pages.png new file mode 100644 index 0000000000..52e1e728d4 Binary files /dev/null and b/docs/images/gitlab-pages.png differ diff --git a/docs/javascript-plugins.md b/docs/javascript-plugins.md index e90c6b4447..e686bd6e5c 100644 --- a/docs/javascript-plugins.md +++ b/docs/javascript-plugins.md @@ -5,6 +5,7 @@ description: Plugins implemented in JavaScript are easily used across different JavaScript plugins are native MyST plugins, which are loaded as modules into the MyST engine. Transforms defined in these modules have access to helpful AST manipulation routines made available by MyST. Edits to JavaScript plugins have no effect during execution of a MyST build, instead the build must be restarted. +(defining-a-new-directive)= ## Defining a new directive To create a plugin, you will need a single JavaScript file[^esm] that exports one or more of the objects above. For example, a simple directive that pulls a random image from [Unsplash](https://picsum.photos/) can be created with a single file that exports an `picsum` directive. @@ -85,6 +86,12 @@ I am **special bold text**, whilst I am **normal bold text** I am **special bold text**, whilst I am **normal bold text** +(plugins:multi-page)= +:::{seealso} Aggregate information across multiple pages +A transform runs on one page at a time, but a plugin can still collect information from every page in a project and render it in one place - for example, for a site-wide glossary or a list of figures. +See the [multi-page plugin demo](https://github.com/myst-contrib/plugin-multi-page-demo) for a working example and a walk-through of the pattern. +::: + ## Examples of plugins The documentation you're reading now defines several of its own plugins to extend MyST functionality. diff --git a/docs/overview.md b/docs/overview.md index 7954c7e3da..75ea64f9b2 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -76,6 +76,8 @@ Here's an example of MyST content, and the same content parsed as MyST AST: The easiest way to understand how {term}`MyST Markdown` gets converted to {term}`MyST AST` is to [explore the MyST Sandbox](https://mystmd.org/sandbox). Click the {kbd}`AST` tab to see the underlying AST structure for anything that you type. The {kbd}`PRE` tab represents the initial parsing phase, and the {kbd}`POST` tab represents the AST after the resolving phase. +If you're building your own renderer or parser, start with the [AST primer](xref:spec/ast-primer). It walks through the node structure, the PRE/POST distinction, and what the spec formalizes vs. what's implementation-specific. + (overview-transformers)= ## How do MyST Transformers work? diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 0000000000..f5cc6c0aa2 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,21 @@ +{ + "name": "mystmd-docs", + "private": true, + "version": "0.0.0", + "dependencies": { + "myst-common": "workspace:*", + "myst-directives": "workspace:*", + "myst-ext-button": "workspace:*", + "myst-ext-card": "workspace:*", + "myst-ext-exercise": "workspace:*", + "myst-ext-grid": "workspace:*", + "myst-ext-proof": "workspace:*", + "myst-ext-tabs": "workspace:*", + "myst-migrate": "workspace:*", + "myst-parser": "workspace:*", + "myst-roles": "workspace:*", + "tex-to-myst": "workspace:*", + "unist-builder": "^3.0.0", + "unist-util-select": "^4.0.3" + } +} diff --git a/docs/plugins-ast.md b/docs/plugins-ast.md index 6ebafa74ca..c0c6d21954 100644 --- a/docs/plugins-ast.md +++ b/docs/plugins-ast.md @@ -5,6 +5,10 @@ title: Generate MyST AST with Plugins A common use case with plugins involves generating {term}`MyST AST` and inserting it into the document. This page covers a few ways that you can do so. +:::{tip} New to the MyST AST? +Start with the [AST primer](xref:spec/ast-primer) for an overview of how MyST documents are structured at a high- and low-level. +::: + (plugins:ctx)= ## Parse MyST markdown to AST in a directive or role diff --git a/docs/plugins.md b/docs/plugins.md index d76c671981..5abd13e187 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -27,6 +27,7 @@ roles transforms : These plugins transform the document source while it is rendered. : For example, add metadata or transform a link to a DOI. +: Transforms can also [aggregate information across multiple pages](#plugins:multi-page), like a site-wide glossary. :::{warning} Planned - Not Yet Implemented :class: dropdown diff --git a/docs/proofs-and-theorems.md b/docs/proofs-and-theorems.md index 3b53f245fb..313855e012 100644 --- a/docs/proofs-and-theorems.md +++ b/docs/proofs-and-theorems.md @@ -5,7 +5,7 @@ description: MyST supports creating and referencing algorithms, axioms, conjectu thumbnail: ./thumbnails/proof.png --- -All proof directives can be included using the `prf:kind` pattern, where the proof directives are shown in [](#proof-list). The directive is enumerated by default — except the bare proof directive (`proof` / `prf:proof`), which is unnumbered by default to match the LaTeX `amsthm` convention (opt in with `:enumerated: true`) — and can take in an optional title argument which is shown in brackets after the proof. +All proof directives can be included using the `proof:kind` pattern, where the proof directives are shown in [](#proof-list). A plain proof can also be written without a kind, as `{proof}`. The directive is enumerated by default — except the bare proof directive (`proof` / `prf:proof` / `proof:proof`), which is unnumbered by default to match the LaTeX `amsthm` convention (opt in with `:enumerated: true`) — and can take in an optional title argument which is shown in brackets after the proof. :::{note} Same as Sphinx Proof 🎉 :class: dropdown @@ -14,12 +14,14 @@ The implementation and documentation for proofs, theorems, etc. is based on [Sph Changes to the original extension include being able to click on the proof label (e.g. "Theorem 1"), and having a link to that proof anchor. We have also updated the styles from both Sphinx and Jupyter Book to be more distinct from admonitions. -You can also reference proofs with any cross-reference syntax (including the {myst:role}`prf:ref` role). We recommend the Markdown link syntax. +A plain proof does not need a kind prefix at all — use `{proof}`. + +You can also reference proofs with any cross-reference syntax (including the {myst:role}`proof:ref` role). We recommend the Markdown link syntax. ::: -Here is an example of a `{prf:theorem}` with a custom title: +Here is an example of a `{proof:theorem}` with a custom title: -:::{prf:theorem} Orthogonal-Projection-Theorem +:::{proof:theorem} Orthogonal-Projection-Theorem :label: my-theorem Given $y \in \mathbb R^n$ and linear subspace $S \subset \mathbb R^n$, @@ -42,20 +44,20 @@ The vector $\hat y$ is called the **orthogonal projection** of $y$ onto $S$. :label: proof-list :header-rows: 0 -* - `prf:algorithm` - - `prf:axiom` - - `prf:conjecture` -* - `prf:corollary` - - `prf:criteria` - - `prf:definition` -* - `prf:example` - - `prf:lemma` - - `prf:observation` -* - `prf:property` - - `prf:proposition` - - `prf:proof` -* - `prf:remark` - - `prf:theorem` +* - `proof:algorithm` + - `proof:axiom` + - `proof:conjecture` +* - `proof:corollary` + - `proof:criteria` + - `proof:definition` +* - `proof:example` + - `proof:lemma` + - `proof:observation` +* - `proof:property` + - `proof:proposition` + - `proof` (plain) +* - `proof:remark` + - `proof:theorem` - ``` :::{myst:directive} proof @@ -73,7 +75,7 @@ You can refer to a proof using the standard link syntax: :::{tip} Compatibility with Sphinx Proof :class: dropdown -You may also use the `{prf:ref}` role like: `` {prf:ref}`my-theorem` ``, which will replace the reference with the theorem number like so: {prf:ref}`my-theorem`. When an explicit text is provided, this caption will serve as the title of the reference. For example, ``{prf:ref}`Orthogonal-Projection-Theorem ` `` will produce: {prf:ref}`Orthogonal-Projection-Theorem `. +You may also use the `{proof:ref}` role like: `` {proof:ref}`my-theorem` ``, which will replace the reference with the theorem number like so: {proof:ref}`my-theorem`. When an explicit text is provided, this caption will serve as the title of the reference. For example, ``{proof:ref}`Orthogonal-Projection-Theorem ` `` will produce: {proof:ref}`Orthogonal-Projection-Theorem `. ::: ## Hiding Proof Content @@ -83,7 +85,7 @@ To hide the directive, simply add `:class: dropdown` as a directive option. **Example** ```{myst} -:::{prf:theorem} +:::{proof:theorem} :class: dropdown This is an example of how to hide the content of a directive. @@ -95,7 +97,7 @@ This is an example of how to hide the content of a directive. ### Proofs ````{myst} -:::{prf:proof} +:::{proof} :label: full-proof Let $z$ be any other point in $S$ and use the fact that $S$ is a linear subspace to deduce @@ -114,7 +116,7 @@ _Source:_ Adapted from [QuantEcon](https://python-advanced.quantecon.org/orth_pr ### Theorems ````{myst} -:::{prf:theorem} Orthogonal-Projection-Theorem +:::{proof:theorem} Orthogonal-Projection-Theorem :label: my-theorem Given $y \in \mathbb R^n$ and linear subspace $S \subset \mathbb R^n$, @@ -140,7 +142,7 @@ _Source:_ [QuantEcon](https://python-advanced.quantecon.org/orth_proj.html#The-O ### Axioms ```{myst} -:::{prf:axiom} Completeness of $\mathbb{R}$ +:::{proof:axiom} Completeness of $\mathbb{R}$ :label: my-axiom Every Cauchy sequence on the real line is convergent. @@ -152,7 +154,7 @@ _Source:_ {cite}`economic-dynamics-book` ### Lemmas ````{myst} -:::{prf:lemma} +:::{proof:lemma} :label: my-lemma If $\hat P$ is the fixed point of the map $\mathcal B \circ \mathcal D$ and $\hat F$ is the robust policy as given in [(7)](https://python-advanced.quantecon.org/robustness.html#equation-rb-oc-ih), then @@ -170,7 +172,7 @@ _Source:_ [QuantEcon](https://python-advanced.quantecon.org/robustness.html#Appe ### Definitions ```{myst} -:::{prf:definition} +:::{proof:definition} :label: my-definition The *economical expansion problem* (EEP) for @@ -191,7 +193,7 @@ _Source:_ [QuantEcon](https://python-advanced.quantecon.org) ### Criteria ````{myst} -:::{prf:criterion} Weyl's criterion +:::{proof:criterion} Weyl's criterion :label: weyls-criterion Weyl's criterion states that the sequence $a_n$ is equidistributed modulo $1$ if @@ -208,7 +210,7 @@ _Source:_ [Wikipedia](https://en.wikipedia.org/wiki/Equidistributed_sequence#Wey ### Remarks ```{myst} -:::{prf:remark} +:::{proof:remark} :label: my-remark More generally there is a class of density functions @@ -229,7 +231,7 @@ _Source:_ [QuantEcon](https://python-advanced.quantecon.org/black_litterman.html ### Conjectures ```{myst} -:::{prf:conjecture} Fake $\gamma$ conjecture +:::{proof:conjecture} Fake $\gamma$ conjecture :label: my-conjecture This is a dummy conjecture to illustrate that one can use math in titles. ::: @@ -238,7 +240,7 @@ This is a dummy conjecture to illustrate that one can use math in titles. ### Corollaries ```{myst} -:::{prf:corollary} +:::{proof:corollary} :label: my-corollary If $A$ is a convergent matrix, then there exists a matrix norm such @@ -251,7 +253,7 @@ _Source:_ [QuantEcon](https://python-intro.quantecon.org/_static/lecture_specifi ### Algorithms ```{myst} -:::{prf:algorithm} Ford–Fulkerson +:::{proof:algorithm} Ford–Fulkerson :label: my-algorithm **Inputs** Given a Network $G=(V,E)$ with flow capacity $c$, a source node $s$, and a sink node $t$ @@ -274,7 +276,7 @@ _Source:_ [Wikipedia](https://en.wikipedia.org/wiki/Ford%E2%80%93Fulkerson_algor ### Examples ````{myst} -:::{prf:example} +:::{proof:example} :label: my-example Next, we shut down randomness in demand and assume that the demand shock @@ -301,7 +303,7 @@ _Source:_ [QuantEcon](https://python.quantecon.org/lq_inventories.html#Example-2 ### Properties ```{myst} -:::{prf:property} +:::{proof:property} :label: my-property This is a dummy property to illustrate the directive. @@ -311,7 +313,7 @@ This is a dummy property to illustrate the directive. ### Observations ```{myst} -:::{prf:observation} +:::{proof:observation} :label: my-observation This is a dummy observation directive. @@ -321,7 +323,7 @@ This is a dummy observation directive. ### Propositions ```{myst} -:::{prf:proposition} +:::{proof:proposition} :label: my-proposition This is a dummy proposition directive. @@ -331,7 +333,7 @@ This is a dummy proposition directive. ### Assumptions ```{myst} -:::{prf:assumption} +:::{proof:assumption} :label: my-assumption This is a dummy assumption directive. diff --git a/docs/settings.md b/docs/settings.md index 0b40d8a7a7..13d7fef788 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -85,6 +85,13 @@ dollarmath When disabled, dollar signs (`$`) will be treated as regular text characters and not parsed as math delimiters. This can be useful if you have frequent uses of dollar signs, such as currency, in your content that are not meant to be math. +(setting:infer_dois_from_urls)= +infer_dois_from_urls +: Infer DOIs from non-doi.org link URLs (for example, publisher article pages, biorxiv, or zenodo). + + - `false` (default): Only `doi.org` links, raw DOI strings, and `doi:` links are recognized + - `true`: infer DOIs from all links that end in a valid DOI (legacy behavior) + ## LaTeX Rendering Settings Adding an object of `myst_to_tex` to the settings will allow you to control various default parts of how the LaTeX renderer behaves. diff --git a/docs/theme-developer.md b/docs/theme-developer.md index dd916421e5..03202d4527 100644 --- a/docs/theme-developer.md +++ b/docs/theme-developer.md @@ -21,6 +21,8 @@ Specifically: - When you run `myst start`, the engine builds `_build/site` (content JSON, config, assets), serves it on `CONTENT_CDN_PORT`, and launches your theme with the template’s `build.start` command. - A theme should read `_build/site/config.json` first, fetch page JSON from `/content/{slug}.json`, and serve the app on `PORT` (Remix/Vite/Next-style). +Each page JSON contains an `mdast` field holding the resolved AST that the theme then renders - see the [AST primer](xref:spec/ast-primer) for its structure. + Live reloads are facilitated by a websocket endpoint at `/socket`, which processes JSON messages. Static assets, and template options with type `file` are copied into `_build/site/public` and served from `/`. ## Theme metadata (`template.yml`) @@ -44,6 +46,7 @@ It then produces `_build/site/config.json`, validates it against your `doc`/`opt - Reference and search files at `/objects.inv`, `/myst.xref.json`, `/myst.search.json`. - Static assets from `/` and a `/socket` websocket for `LOG` and `RELOAD` events. Your start script receives `HOST`, `CONTENT_CDN_PORT`, `PORT`, `MODE` (set to `static` for `myst build --html`), and optional `BASE_URL`; any framework or server is fine as long as it reads these and fetches content from the endpoints above. +- An index of known static files is included in `public.json`, to enable the theme to retrieve _all_ static files without access to the file-system of the content server. ## Building HTML diff --git a/package.json b/package.json index 0cfaff8b9a..b611396627 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "name": "mystmd", "private": true, "workspaces": [ - "packages/*" + "packages/*", + "docs" ], "scripts": { "build": "turbo run build", @@ -25,8 +26,16 @@ "devDependencies": { "@changesets/cli": "^2.26.1", "@types/node": "^24.6.2", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", "esbuild": "^0.25.10", + "eslint": "^8.45.0", "eslint-config-curvenote": "^0.0.4", + "eslint-config-next": "^13.4.10", + "eslint-config-prettier": "^8.8.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-react": "7.32.2", "husky": "^9.1.7", "lint-staged": "^15.5.0", "madge": "^6.1.0", diff --git a/packages/citation-js-utils/package.json b/packages/citation-js-utils/package.json index 352f537b0c..086b5b2076 100644 --- a/packages/citation-js-utils/package.json +++ b/packages/citation-js-utils/package.json @@ -38,9 +38,12 @@ "@citation-js/core": "^0.7.18", "@citation-js/plugin-bibtex": "^0.7.18", "@citation-js/plugin-csl": "^0.7.18", + "he": "^1.2.0", + "doi-utils": "^2.0.5", "sanitize-html": "^2.7.0" }, "devDependencies": { + "@types/he": "^1.2.0", "@types/sanitize-html": "^2.6.2" } } diff --git a/packages/citation-js-utils/src/index.ts b/packages/citation-js-utils/src/index.ts index 6c3abc080f..6f2b5af590 100644 --- a/packages/citation-js-utils/src/index.ts +++ b/packages/citation-js-utils/src/index.ts @@ -2,6 +2,7 @@ import { Cite, plugins } from '@citation-js/core'; import { doi as doiUtils } from 'doi-utils'; import { clean as cleanCSL } from '@citation-js/core/lib/plugins/input/csl.js'; import sanitizeHtml from 'sanitize-html'; +import he from 'he'; import '@citation-js/plugin-bibtex'; import '@citation-js/plugin-csl'; @@ -267,7 +268,8 @@ export function getCitationRenderers(data: CSL[]): CitationRenderer { if (!c.id) { c.id = formatLabel(c); } - // Trim the titles, DOIs, etc. on load + // Trim the titles, DOIs, etc. on load; decode HTML entities from + // CrossRef metadata. [ 'title', 'note', @@ -279,7 +281,7 @@ export function getCitationRenderers(data: CSL[]): CitationRenderer { 'DOI', 'ISSN', ].forEach((tag) => { - if (c[tag] && typeof c[tag] === 'string') c[tag] = c[tag].trim(); + if (c[tag] && typeof c[tag] === 'string') c[tag] = he.decode(c[tag].trim()); }); // Trim the DOIs and URLs (these are encoded) on load if (c.URL) c.URL = c.URL.replace(/^(%20)*/, '').replace(/(%20)*$/, ''); diff --git a/packages/citation-js-utils/tests/basic.spec.ts b/packages/citation-js-utils/tests/basic.spec.ts index ea75c89215..154255d0df 100644 --- a/packages/citation-js-utils/tests/basic.spec.ts +++ b/packages/citation-js-utils/tests/basic.spec.ts @@ -79,6 +79,26 @@ describe('yearFromCitation', () => { }); }); +describe('HTML entity decoding in CSL fields', () => { + it('decodes HTML entities in title and container-title from CrossRef CSL-JSON', () => { + const csl = [ + { + id: 'test2024', + type: 'article-journal', + title: 'Signal & Image Processing & More', + 'container-title': 'Computers & Geosciences', + author: [{ family: 'Smith', given: 'J.' }], + issued: { 'date-parts': [[2024]] }, + DOI: '10.1234/test', + }, + ]; + const renderers = getCitationRenderers(csl); + const cite = renderers['test2024']; + expect(cite.cite.title).toEqual('Signal & Image Processing & More'); + expect(cite.cite['container-title']).toEqual('Computers & Geosciences'); + }); +}); + describe('firstNonDoiUrl', () => { it('no url returns undefined', async () => { expect(firstNonDoiUrl('my citation', 'abc123')).toEqual(undefined); diff --git a/packages/jats-to-myst/package.json b/packages/jats-to-myst/package.json index f6b35911bd..234f83f47d 100644 --- a/packages/jats-to-myst/package.json +++ b/packages/jats-to-myst/package.json @@ -37,7 +37,7 @@ "dependencies": { "doi-utils": "^2.0.5", "jats-tags": "^1.0.8", - "jats-xml": "^1.0.8", + "jats-xml": "^1.1.7", "myst-common": "^1.9.2", "myst-frontmatter": "^1.9.2", "myst-spec": "^0.0.5", diff --git a/packages/jtex/package.json b/packages/jtex/package.json index 09b226993d..da085aa9bb 100644 --- a/packages/jtex/package.json +++ b/packages/jtex/package.json @@ -59,6 +59,7 @@ }, "devDependencies": { "@types/adm-zip": "^0.5.0", + "@types/js-yaml": "^4.0.5", "@types/nunjucks": "^3.2.2", "memfs": "^3.5.3" } diff --git a/packages/markdown-it-myst/package.json b/packages/markdown-it-myst/package.json index b57d861af5..6988e1c6bd 100644 --- a/packages/markdown-it-myst/package.json +++ b/packages/markdown-it-myst/package.json @@ -37,6 +37,7 @@ "vfile-message": "^3.0.0" }, "devDependencies": { + "@types/js-yaml": "^4.0.5", "markdown-it-footnote": "^4.0.0" }, "peerDependencies": { diff --git a/packages/myst-cli-utils/package.json b/packages/myst-cli-utils/package.json index 8b541e4a9d..7b7eb498eb 100644 --- a/packages/myst-cli-utils/package.json +++ b/packages/myst-cli-utils/package.json @@ -33,6 +33,7 @@ }, "dependencies": { "chalk": "^5.2.0", + "commander": "^10.0.1", "pretty-hrtime": "^1.0.3", "tree-kill": "^1.2.2" }, diff --git a/packages/myst-cli-utils/src/session.ts b/packages/myst-cli-utils/src/session.ts index ffacd68d0a..1a5034c2ae 100644 --- a/packages/myst-cli-utils/src/session.ts +++ b/packages/myst-cli-utils/src/session.ts @@ -7,6 +7,7 @@ export class Session implements ISession { constructor(opts?: { logger?: Logger }) { this.log = opts?.logger ?? chalkLogger(LogLevel.debug, process.cwd()); } + // eslint-disable-next-line @typescript-eslint/no-unused-vars fetch(url: URL | RequestInfo, init?: RequestInit | undefined): Promise { throw new Error('fetch not implemented on session'); } diff --git a/packages/myst-cli/package.json b/packages/myst-cli/package.json index a47870aeee..df46cd3e39 100644 --- a/packages/myst-cli/package.json +++ b/packages/myst-cli/package.json @@ -42,7 +42,7 @@ "npm": ">=10" }, "dependencies": { - "@jupyterlab/services": "^7.3.0", + "@jupyterlab/services": "^7.6.0", "@reduxjs/toolkit": "^2.1.0", "adm-zip": "^0.5.10", "async-mutex": "^0.5.0", @@ -111,6 +111,7 @@ "unist-util-visit": "^4.0.0", "uuid": "^8.3.2", "vfile": "^5.3.5", + "vfile-message": "^3.0.0", "which": "^3.0.1", "ws": "^8.18.1", "xml-js": "^1.6.11", @@ -123,11 +124,14 @@ "@types/express": "^4.17.17", "@types/fs-extra": "^11.0.1", "@types/inquirer": "^9.0.7", + "@types/js-yaml": "^4.0.5", + "@types/mdast": "^3.0.12", "@types/mime-types": "^2.1.1", "@types/picomatch": "^4.0.2", "@types/uuid": "^8.3.4", "@types/which": "^3.0.0", "@types/ws": "^8.18.1", - "concurrently": "^8.2.0" + "concurrently": "^8.2.0", + "memfs": "^3.5.3" } } diff --git a/packages/myst-cli/src/build/html/index.ts b/packages/myst-cli/src/build/html/index.ts index 648c452db7..01126785cb 100644 --- a/packages/myst-cli/src/build/html/index.ts +++ b/packages/myst-cli/src/build/html/index.ts @@ -29,9 +29,13 @@ export async function currentSiteRoutes( session: ISession, host: string, baseurl: string | undefined, -): Promise<{ url: string; path: string; binary?: boolean }[]> { +): Promise<{ url: string; path: string; binary?: boolean; optional?: boolean }[]> { const state = session.store.getState(); const siteConfig = selectors.selectCurrentSiteConfig(state); + // Without a configured favicon, the theme falls back to fetching a default + // icon from mystmd.org; that fetch is optional, so that if the remote site is + // down it doesn't break the build. + const hasFavicon = !!siteConfig?.options?.favicon; return (siteConfig?.projects ?? []) ?.map((proj) => { if (!proj.path) return []; @@ -70,11 +74,12 @@ export async function currentSiteRoutes( url: `${host}/${asset}`, path: asset, })), - ...['favicon.ico'].map((asset) => ({ - url: `${host}/${asset}`, - path: asset, + { + url: `${host}/favicon.ico`, + path: 'favicon.ico', binary: true, - })), + optional: !hasFavicon, + }, ]; }) .flat(); @@ -178,22 +183,32 @@ export async function buildHtml(session: ISession, opts: StartOptions) { await Promise.all( routes.map(async (route) => limitConnections(async () => { - const resp = await fetchWithRetry(session, route.url); - if (!resp.ok) { - session.log.error(`Error fetching ${route.url}`); - return; - } - if (route.binary && resp.body) { - await new Promise((resolve) => { - const filename = path.join(htmlDir, route.path); - if (!fs.existsSync(filename)) fs.mkdirSync(path.dirname(filename), { recursive: true }); - const fileWriteStream = fs.createWriteStream(filename); - resp.body!.pipe(fileWriteStream); - fileWriteStream.on('finish', resolve); - }); - } else { - const content = await resp.text(); - writeFileToFolder(path.join(htmlDir, route.path), content); + try { + const resp = await fetchWithRetry(session, route.url); + if (!resp.ok) { + session.log.error(`Error fetching ${route.url}`); + return; + } + if (route.binary && resp.body) { + await new Promise((resolve, reject) => { + const filename = path.join(htmlDir, route.path); + if (!fs.existsSync(filename)) + fs.mkdirSync(path.dirname(filename), { recursive: true }); + const fileWriteStream = fs.createWriteStream(filename); + resp.body!.pipe(fileWriteStream); + resp.body!.on('error', reject); + fileWriteStream.on('error', reject); + fileWriteStream.on('finish', resolve); + }); + } else { + const content = await resp.text(); + writeFileToFolder(path.join(htmlDir, route.path), content); + } + } catch (error) { + if (!route.optional) throw error; + session.log.warn( + `Could not fetch optional asset ${route.url}: ${(error as Error).message}`, + ); } }), ), @@ -219,6 +234,7 @@ export async function buildHtml(session: ISession, opts: StartOptions) { copyStaticFiles(session, projectConfig?.static_files ?? [], htmlDir, proj.path); } fs.copySync(path.join(session.sitePath(), 'config.json'), path.join(htmlDir, 'config.json')); + fs.copySync(path.join(session.sitePath(), 'public.json'), path.join(htmlDir, 'public.json')); fs.copySync(path.join(session.sitePath(), 'objects.inv'), path.join(htmlDir, 'objects.inv')); // NOTE: HTML static output needs to patch the contents, this is done on the fly by the server diff --git a/packages/myst-cli/src/build/site/start.ts b/packages/myst-cli/src/build/site/start.ts index 124b6c4d3a..c521f42e6c 100644 --- a/packages/myst-cli/src/build/site/start.ts +++ b/packages/myst-cli/src/build/site/start.ts @@ -83,6 +83,7 @@ export async function startContentServer(session: ISession, opts?: ServerOptions app.use('/', express.static(session.publicPath())); app.use('/content', express.static(session.contentPath())); app.use('/config.json', express.static(join(session.sitePath(), 'config.json'))); + app.use('/public.json', express.static(join(session.sitePath(), 'public.json'))); app.use('/objects.inv', express.static(join(session.sitePath(), 'objects.inv'))); app.use('/myst.xref.json', express.static(join(session.sitePath(), 'myst.xref.json'))); app.use('/myst.search.json', express.static(join(session.sitePath(), 'myst.search.json'))); diff --git a/packages/myst-cli/src/init/gh-actions/index.ts b/packages/myst-cli/src/init/gh-actions/index.ts index 4205fa263f..03f0a51312 100644 --- a/packages/myst-cli/src/init/gh-actions/index.ts +++ b/packages/myst-cli/src/init/gh-actions/index.ts @@ -52,23 +52,23 @@ jobs: url: \${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Setup Pages - uses: actions/configure-pages@v3 - - uses: actions/setup-node@v4 + uses: actions/configure-pages@v6 + - uses: actions/setup-node@v7 with: - node-version: 18.x + node-version: 24.x - name: Install ${readableName()} run: npm install -g ${npmPackageName()} - name: Build HTML Assets run: ${npmBinaryName()} build --html - name: Upload artifact - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v5 with: path: './_build/html' - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@v4 + uses: actions/deploy-pages@v5 `; } diff --git a/packages/myst-cli/src/process/mdast.ts b/packages/myst-cli/src/process/mdast.ts index b8fc9e7ed3..2fcd1dc03e 100644 --- a/packages/myst-cli/src/process/mdast.ts +++ b/packages/myst-cli/src/process/mdast.ts @@ -1,6 +1,6 @@ import path from 'node:path'; import { tic } from 'myst-cli-utils'; -import type { GenericParent, IExpressionResult, PluginUtils, References } from 'myst-common'; +import type { GenericParent, PluginUtils, References } from 'myst-common'; import { fileError, fileWarn, RuleId, slugToUrl } from 'myst-common'; import type { PageFrontmatter } from 'myst-frontmatter'; import { SourceFileKind } from 'myst-spec-ext'; @@ -74,7 +74,13 @@ import { logMessagesFromVFile } from '../utils/logging.js'; import { combineCitationRenderers } from './citations.js'; import { bibFilesInDir, selectFile } from './file.js'; import { parseMyst } from './myst.js'; -import { kernelExecutionTransform, LocalDiskCache } from 'myst-execute'; +import { + kernelExecutionTransform, + LocalDiskCache, + LegacyExecutionCache, + NotebookExecutionCache, + TieredExecutionCache, +} from 'myst-execute'; import type { IOutput } from '@jupyterlab/nbformat'; import type { BookSection } from 'myst-toc'; import { rawDirectiveTransform } from '../transforms/raw.js'; @@ -309,7 +315,10 @@ export async function transformMdast( session.log.debug(`▶️ Executing: ${fileName}`); await kernelExecutionTransform(mdast, vfile, { basePath: session.sourcePath(), - cache: new LocalDiskCache<(IExpressionResult | IOutput[])[]>(cachePath), + cache: new TieredExecutionCache( + new NotebookExecutionCache(new LocalDiskCache(cachePath, '.ipynb')), + new LegacyExecutionCache(cachePath), + ), sessionFactory: () => session.jupyterSessionManager(), frontmatter: frontmatter, ignoreCache: false, @@ -352,6 +361,7 @@ export async function transformMdast( mdast, cache.$doiRenderers, file, + { inferDoisFromUrls: frontmatter.settings?.infer_dois_from_urls }, ); const rendererFiles = [file]; if (projectPath) { @@ -470,7 +480,7 @@ export async function postProcessMdast( new GithubTransformer(), new RRIDTransformer(), new RORTransformer(), - new DOITransformer(), // This also is picked up in the next transform + new DOITransformer({ inferDoisFromUrls: frontmatter.settings?.infer_dois_from_urls }), // This also is picked up in the next transform new MystTransformer(externalReferences), new SphinxTransformer(externalReferences), new StaticFileTransformer(session, file), // Links static files and internally linked files diff --git a/packages/myst-cli/src/process/notebook.ts b/packages/myst-cli/src/process/notebook.ts index e782084ea4..d6f842e705 100644 --- a/packages/myst-cli/src/process/notebook.ts +++ b/packages/myst-cli/src/process/notebook.ts @@ -16,9 +16,9 @@ import { logMessagesFromVFile } from '../utils/logging.js'; import type { ISession } from '../session/types.js'; import { BASE64_HEADER_SPLIT } from '../transforms/images.js'; import { parseMyst } from './myst.js'; -import type { Code, InlineExpression } from 'myst-spec-ext'; -import type { IUserExpressionMetadata } from '../transforms/inlineExpressions.js'; +import type { Code, InlineExpression } from 'myst-spec'; import { findExpression, metadataSection } from '../transforms/inlineExpressions.js'; +import type { IUserExpressionMetadata } from '../transforms/inlineExpressions.js'; import { frontmatterValidationOpts } from '../frontmatter.js'; import { filterKeys } from 'simple-validators'; @@ -92,9 +92,7 @@ function embedInlineExpressions( ) { const inlineNodes = selectAll('inlineExpression', block) as InlineExpression[]; inlineNodes.forEach((inlineExpression) => { - const data = findExpression(userExpressions, inlineExpression.value); - if (!data) return; - inlineExpression.result = data.result as unknown as Record; + inlineExpression.result = findExpression(userExpressions, inlineExpression.value)?.result; }); } diff --git a/packages/myst-cli/src/process/site.ts b/packages/myst-cli/src/process/site.ts index cd5982477d..5966eca1b5 100644 --- a/packages/myst-cli/src/process/site.ts +++ b/packages/myst-cli/src/process/site.ts @@ -1,5 +1,5 @@ import yaml from 'js-yaml'; -import { basename, extname, join } from 'node:path'; +import { basename, extname, join, relative } from 'node:path'; import fs from 'fs-extra'; import chalk from 'chalk'; import { Inventory, Domains } from 'intersphinx'; @@ -50,6 +50,7 @@ import { finalizeMdast, postProcessMdast, transformMdast } from './mdast.js'; import { toSectionedParts, buildHierarchy, sectionToHeadingLevel } from './search.js'; import { SPEC_VERSION } from '../spec-version.js'; import { cpus } from 'node:os'; +import { opendir } from 'node:fs/promises'; const WEB_IMAGE_EXTENSIONS = [ ImageExtensions.mp4, @@ -163,6 +164,19 @@ export async function writeMystXRefJson(session: ISession, states: ReferenceStat writeFileToFolder(filename, JSON.stringify(mystXRefs)); } +export async function writePublicJson(session: ISession) { + const paths = []; + const publicPath = session.publicPath(); + const dir = await opendir(publicPath, { recursive: true }); + for await (const path of dir) { + const subPath = relative(publicPath, join(path.parentPath, path.name)); + paths.push(`/${subPath}`); + } + + const filename = join(session.sitePath(), 'public.json'); + writeFileToFolder(filename, JSON.stringify(paths)); +} + export async function writeMystSearchJson(session: ISession, pages: LocalProjectPage[]) { const records = [...pages] // Ensure deterministic ordering @@ -788,6 +802,7 @@ export async function processSite(session: ISession, opts?: ProcessSiteOptions): await writeMystXRefJson(session, states); // Search does not include parts await writeMystSearchJson(session, allPages); + await writePublicJson(session); } return true; } diff --git a/packages/myst-cli/src/project/fromTOC.ts b/packages/myst-cli/src/project/fromTOC.ts index 5d0ca02e64..fd001a54e2 100644 --- a/packages/myst-cli/src/project/fromTOC.ts +++ b/packages/myst-cli/src/project/fromTOC.ts @@ -136,7 +136,8 @@ export function patternsToFileEntries( const { pattern, sort, ...leftover } = entry as PatternEntry; // Glob matches, relative to `path`, ordered naturally let matches = globSync(pattern, { cwd: path, nodir: true, ...opts }) - .filter((item) => !ignore || !ignore.includes(item)) + // ignore contains resolved entries, so we need to search for item resolved + .filter((item) => !ignore || !ignore.includes(resolve(path, item))) .sort(comparePaths); // Reverse order if descending sort is requested if (sort === 'descending') { diff --git a/packages/myst-cli/src/project/toc.pattern.spec.ts b/packages/myst-cli/src/project/toc.pattern.spec.ts index 6d6c7cd142..ecc5b25e68 100644 --- a/packages/myst-cli/src/project/toc.pattern.spec.ts +++ b/packages/myst-cli/src/project/toc.pattern.spec.ts @@ -1,4 +1,5 @@ import { describe, expect, it, beforeEach, vi } from 'vitest'; +import { resolve } from 'node:path'; import memfs from 'memfs'; import { Session } from '../session'; import { listExplicitFiles, patternsToFileEntries } from './fromTOC'; @@ -141,6 +142,21 @@ describe('patternsToFileEntries', () => { { file: 'meetings/2026-01-01.md', implicit: true }, ]); }); + it('explicitly listed files are excluded from subsequent glob patterns', () => { + memfs.vol.fromJSON({ + 'index.md': '', + 'test1.md': '', + 'test2.md': '', + }); + // Simulate ignore list built from explicit file entries (absolute paths, as listExplicitFiles returns) + const cwd = '.'; + const ignore = [resolve(cwd, 'index.md'), resolve(cwd, 'test1.md')]; + expect( + patternsToFileEntries(session, [{ pattern: '*.md' }], cwd, ignore, '/tmp/warn.txt', { + fs: memfs, + }), + ).toEqual([{ file: 'test2.md', implicit: true }]); + }); }); describe('listExplicitFiles', () => { diff --git a/packages/myst-cli/src/transforms/doi.spec.ts b/packages/myst-cli/src/transforms/doi.spec.ts index ba7d52551d..d368d55005 100644 --- a/packages/myst-cli/src/transforms/doi.spec.ts +++ b/packages/myst-cli/src/transforms/doi.spec.ts @@ -1,7 +1,8 @@ import { describe, expect, it } from 'vitest'; +import { VFile } from 'vfile'; import type { ISession } from '../session/types'; import { Session } from '../session'; -import { resolveDOIAsBibTeX, resolveDOIAsCSLJSON } from './dois'; +import { resolveDOIAsBibTeX, resolveDOIAsCSLJSON, transformLinkedDOIs } from './dois'; import fixtures from './fixtures/doi.json'; const PRIESTLEY_1972_CSL_JSON = [ @@ -146,3 +147,40 @@ sessions.forEach(({ name: sessionName, makeSession }) => { }); }); }); + +describe('transformLinkedDOIs', () => { + describe.each([ + { + name: 'mybinder zenodo dataverse link', + url: 'https://mybinder.org/v2/zenodo/10.7910/DVN/EOYZKH/', + }, + { + name: 'iopscience article link', + url: 'https://iopscience.iop.org/article/10.3847/1538-3881/ace32f#ajace32ff10', + }, + { + name: 'frontiers article link', + url: 'https://www.frontiersin.org/articles/10.3389/fonc.2018.00134/full#supplementary-material', + }, + ])('$name', ({ url }) => { + it('does not auto-cite by default', async () => { + const mdast = { + type: 'root', + children: [ + { + type: 'paragraph', + children: [ + { + type: 'link', + url, + children: [{ type: 'text', value: 'article' }], + }, + ], + }, + ], + }; + await transformLinkedDOIs(new Session(), new VFile(), mdast, {}, 'test.md'); + expect(mdast.children[0].children[0].type).toBe('link'); + }); + }); +}); diff --git a/packages/myst-cli/src/transforms/dois.ts b/packages/myst-cli/src/transforms/dois.ts index 8a463d1105..bc1fbf9238 100644 --- a/packages/myst-cli/src/transforms/dois.ts +++ b/packages/myst-cli/src/transforms/dois.ts @@ -1,6 +1,7 @@ import type { CitationRenderer, CSL } from 'citation-js-utils'; import { getCitationRenderers, parseBibTeX, parseCSLJSON } from 'citation-js-utils'; import { doi } from 'doi-utils'; +import { isRecognizedDoi, type DoiOptions } from 'myst-transforms'; import type { Link } from 'myst-spec'; import type { GenericNode, GenericParent } from 'myst-common'; import { toText, RuleId, plural, fileError } from 'myst-common'; @@ -229,6 +230,7 @@ export async function transformLinkedDOIs( mdast: GenericParent, doiRenderer: Record, path: string, + doiOpts?: DoiOptions, ): Promise { const toc = tic(); const renderer: CitationRenderer = {}; @@ -236,12 +238,12 @@ export async function transformLinkedDOIs( const citeDois: Cite[] = []; selectAll('link', mdast).forEach((node: GenericNode) => { const { url } = node as Link; - if (!doi.validate(url)) return; + if (!isRecognizedDoi(url, doiOpts)) return; linkedDois.push(node as Link); }); selectAll('cite', mdast).forEach((node: GenericNode) => { const { label } = node as Cite; - if (!doi.validate(label)) return; + if (!isRecognizedDoi(label, doiOpts)) return; citeDois.push(node as Cite); }); if (linkedDois.length === 0 && citeDois.length === 0) return renderer; @@ -278,7 +280,7 @@ export async function transformLinkedDOIs( citeNode.kind = 'narrative'; citeNode.label = label; citeNode.identifier = node.url; - if (doi.validate(toText(citeNode.children))) { + if (isRecognizedDoi(toText(citeNode.children), doiOpts)) { // If the link text is the DOI, update with a citation in a following pass citeNode.children = []; } diff --git a/packages/myst-cli/src/transforms/inlineExpressions.ts b/packages/myst-cli/src/transforms/inlineExpressions.ts index e467a8ea39..3054320cca 100644 --- a/packages/myst-cli/src/transforms/inlineExpressions.ts +++ b/packages/myst-cli/src/transforms/inlineExpressions.ts @@ -1,8 +1,7 @@ -import type { GenericParent, IExpressionResult } from 'myst-common'; +import type { GenericParent } from 'myst-common'; import { fileWarn, RuleId } from 'myst-common'; import { selectAll } from 'unist-util-select'; -import type { InlineExpression } from 'myst-spec-ext'; -import type { StaticPhrasingContent } from 'myst-spec'; +import type { StaticPhrasingContent, InlineExpression, IExpressionResult } from 'myst-spec'; import type { Plugin } from 'unified'; import type { VFile } from 'vfile'; import { BASE64_HEADER_SPLIT } from './images.js'; @@ -79,9 +78,6 @@ function renderExpression(node: InlineExpression, file: VFile): StaticPhrasingCo export function transformRenderInlineExpressions(mdast: GenericParent, file: VFile) { const inlineNodes = selectAll('inlineExpression', mdast) as InlineExpression[]; inlineNodes.forEach((inlineExpression) => { - if (!inlineExpression.result) { - return; - } inlineExpression.children = renderExpression(inlineExpression, file); }); } diff --git a/packages/myst-common/package.json b/packages/myst-common/package.json index 23ef1e4d49..878aafadf6 100644 --- a/packages/myst-common/package.json +++ b/packages/myst-common/package.json @@ -39,6 +39,7 @@ "myst-spec-ext": "^1.10.0", "unist-builder": "3.0.0", "@jupyterlab/nbformat": "^3.5.2", - "@lumino/coreutils": "^2.0.0" + "@lumino/coreutils": "^2.0.0", + "@types/unist": "^2.0.7" } } diff --git a/packages/myst-common/src/index.ts b/packages/myst-common/src/index.ts index 18ab060cd3..e2147d0e8f 100644 --- a/packages/myst-common/src/index.ts +++ b/packages/myst-common/src/index.ts @@ -34,9 +34,6 @@ export { export type { IndexTypeLists } from './indices.js'; export type { MessageInfo } from './utils.js'; export type { - IExpressionResult, - IExpressionError, - IExpressionOutput, GenericNode, GenericParent, Citations, diff --git a/packages/myst-common/src/types.ts b/packages/myst-common/src/types.ts index 90a2165ba5..e6643de669 100644 --- a/packages/myst-common/src/types.ts +++ b/packages/myst-common/src/types.ts @@ -1,8 +1,6 @@ import type { Plugin } from 'unified'; import type { Directive, Node, Role } from 'myst-spec'; import type { VFile } from 'vfile'; -import type * as nbformat from '@jupyterlab/nbformat'; -import type { PartialJSONObject } from '@lumino/coreutils'; import type { PageFrontmatter } from 'myst-frontmatter'; export type GenericNode = Record> = { @@ -173,31 +171,6 @@ export enum AdmonitionKind { tip = 'tip', warning = 'warning', } - -export interface IExpressionOutput { - status: 'ok'; - data: nbformat.IMimeBundle; - metadata: PartialJSONObject; -} - -export interface IExpressionError { - status: 'error'; - /** - * Exception name - */ - ename: string; - /** - * Exception value - */ - evalue: string; - /** - * Traceback - */ - traceback: string[]; -} - -export type IExpressionResult = IExpressionError | IExpressionOutput; - export type FrontmatterPart = { mdast: GenericParent; frontmatter?: PageFrontmatter }; export type FrontmatterParts = Record; diff --git a/packages/myst-config/package.json b/packages/myst-config/package.json index b9568162d2..80369ccda0 100644 --- a/packages/myst-config/package.json +++ b/packages/myst-config/package.json @@ -36,6 +36,8 @@ "simple-validators": "^1.2.0" }, "devDependencies": { + "glob": "^10.3.1", + "js-yaml": "^4.1.0", "moment": "^2.29.4" } } diff --git a/packages/myst-directives/package.json b/packages/myst-directives/package.json index b69e1adf04..cd88a2cf7b 100644 --- a/packages/myst-directives/package.json +++ b/packages/myst-directives/package.json @@ -36,10 +36,15 @@ "csv-parse": "^5.5.5", "js-yaml": "^4.1.0", "myst-common": "^1.10.0", + "myst-spec": "^0.0.5", "myst-spec-ext": "^1.10.0", "json5": "^1.0.2", "nanoid": "^5.1.6", "unist-util-select": "^4.0.3", "vfile": "^5.3.7" + }, + "devDependencies": { + "@types/js-yaml": "^4.0.5", + "@types/json5": "^0.0.29" } } diff --git a/packages/myst-execute/package.json b/packages/myst-execute/package.json index 4adb5b8d2d..178a7aa5d2 100644 --- a/packages/myst-execute/package.json +++ b/packages/myst-execute/package.json @@ -32,18 +32,22 @@ "url": "https://github.com/jupyter-book/mystmd/issues" }, "dependencies": { - "@jupyterlab/services": "^7.3.0", + "@jupyterlab/services": "^7.6.0", "@jupyterlab/nbformat": "^3.5.2", "chalk": "^5.2.0", "get-port": "^6.1.2", "myst-cli-utils": "^2.0.13", "myst-common": "^1.10.0", + "myst-frontmatter": "^1.10.0", + "myst-spec": "^0.0.5", "node-fetch": "^3.3.0", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", "which": "^4.0.0" }, "devDependencies": { + "@types/which": "^3.0.4", "js-yaml": "^4.1.0" } } diff --git a/packages/myst-execute/src/cache.ts b/packages/myst-execute/src/cache.ts index af6f25f918..f35643760e 100644 --- a/packages/myst-execute/src/cache.ts +++ b/packages/myst-execute/src/cache.ts @@ -1,20 +1,33 @@ +import type { + DocumentExecutionResult, + ExecutionResult, + LegacyExecutionResult, + CodeResult, +} from './types.js'; +import { isCodeResult } from './types.js'; import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; import path from 'node:path'; +import type { INotebookContent, IOutput } from '@jupyterlab/nbformat'; +import { isCode, isError, isDisplayData } from '@jupyterlab/nbformat'; +import type { IExpressionError, IExpressionDisplay } from 'myst-spec'; export interface ICache { test(key: string): boolean; get(key: string): T | undefined; - set(key: string, value: T): void; + set(key: string, result: T): void; } +export type IDocumentExecutionCache = ICache; + /** * An implementation of a basic cache */ export class LocalDiskCache implements ICache { - constructor(cachePath: string) { + constructor(cachePath: string, extension: string) { this._cachePath = cachePath; + this._extension = extension; if (!existsSync(cachePath)) { mkdirSync(cachePath, { recursive: true }); @@ -22,9 +35,10 @@ export class LocalDiskCache implements ICache { } private readonly _cachePath: string; + private readonly _extension: string; private _makeKeyPath(key: string): string { - return path.join(this._cachePath, `${key}.json`); + return path.join(this._cachePath, `${key}${this._extension}`); } test(key: string): boolean { @@ -39,8 +53,212 @@ export class LocalDiskCache implements ICache { return JSON.parse(readFileSync(keyPath, { encoding: 'utf8' })); } - set(key: string, item: T) { + set(key: string, document: T) { const keyPath = this._makeKeyPath(key); - return writeFileSync(keyPath, JSON.stringify(item), { encoding: 'utf8' }); + return writeFileSync(keyPath, JSON.stringify(document), { encoding: 'utf8' }); + } +} + +/** + * Type guard for legacy IOutput[] arrays + * + * @param result legacy cache result + */ +function isLegacyOutputArray(result: LegacyExecutionResult): result is IOutput[] { + return Array.isArray(result); +} + +/** + * Implement IDocumentExecutionCache for legacy caches + */ +export class LegacyExecutionCache implements IDocumentExecutionCache { + private readonly cache: LocalDiskCache; + constructor(cachePath: string) { + this.cache = new LocalDiskCache(cachePath, '.json'); + } + + test(key: string): boolean { + return this.cache.test(key); + } + + get(key: string): DocumentExecutionResult | undefined { + const legacyHit = this.cache.get(key); + if (legacyHit === undefined) { + return undefined; + } + return { + context: {}, + results: legacyHit.map((item) => { + if (isLegacyOutputArray(item)) { + return { + type: 'code', + responses: item, + }; + } else { + return { + type: 'inlineExpression', + response: item, + }; + } + }), + }; + } + + set(key: string, document: DocumentExecutionResult) { + const legacyDocument = document.results.map((result) => { + if (result.type === 'code') { + return result.responses; + } else { + return result.response; + } + }); + this.cache.set(key, legacyDocument); + } +} + +export type LocalExecutionCache = LocalDiskCache; + +type NotebookResultMetadata = { + mystResultType: ExecutionResult['type']; +}; + +/** + * IDocumentExecutionCache that stores outputs and expression results in ipynb files + */ +export class NotebookExecutionCache implements IDocumentExecutionCache { + private baseCache: ICache; + + constructor(baseCache: ICache) { + this.baseCache = baseCache; + } + + test(key: string): boolean { + return this.baseCache.test(key); + } + get(key: string): DocumentExecutionResult | undefined { + const notebook = this.baseCache.get(key); + if (notebook === undefined) { + return undefined; + } + return { + context: (notebook.metadata?.mystContext ?? {}) as Record, + results: notebook.cells.map((cell) => { + if (!isCode(cell)) { + throw new Error('Invalid cell in cache'); + } else { + const resultType = (cell.metadata as NotebookResultMetadata).mystResultType; + if (resultType === 'code') { + return { + type: 'code', + responses: cell.outputs, + } satisfies CodeResult; + } else { + return { + type: 'inlineExpression', + response: cell.outputs + .map((output) => { + if (isError(output)) { + return { + status: 'error', + ename: output.ename, + evalue: output.evalue, + traceback: output.traceback, + } satisfies IExpressionError; + } else if (isDisplayData(output)) { + return { + status: 'ok', + data: output.data, + metadata: output.metadata, + } satisfies IExpressionDisplay; + } else { + throw new Error('Invalid cell for inlineExpression result'); + } + }) + .shift()!, + }; + } + } + }), + } satisfies DocumentExecutionResult; + } + set(key: string, document: DocumentExecutionResult) { + const notebook: INotebookContent = { + nbformat: 4, + nbformat_minor: 5, + metadata: { + mystContext: document.context, + }, + cells: document.results.map((result, index) => { + if (isCodeResult(result)) { + return { + cell_type: 'code', + source: [], + metadata: { + mystResultType: result.type, + } satisfies NotebookResultMetadata, + outputs: result.responses, + execution_count: index, + }; + } else { + return { + cell_type: 'code', + source: [], + metadata: { + mystResultType: result.type, + } satisfies NotebookResultMetadata, + outputs: [result.response].map((response) => { + switch (response.status) { + case 'ok': { + return { + output_type: 'display_data', + data: response.data, + metadata: response.metadata, + }; + } + case 'error': { + return { + output_type: 'error', + ename: response.ename, + evalue: response.evalue, + traceback: response.traceback, + }; + } + default: { + throw new Error('This cannot happen'); + } + } + }), + execution_count: index, + }; + } + }), + }; + this.baseCache.set(key, notebook); + } +} + +/** + * IDocumentExecutionCache that reads and writes to a primary cache, + * but falls back upon a secondary for reading + */ +export class TieredExecutionCache implements IDocumentExecutionCache { + private primary: IDocumentExecutionCache; + private secondary: IDocumentExecutionCache; + + constructor(primary: IDocumentExecutionCache, secondary: IDocumentExecutionCache) { + this.primary = primary; + this.secondary = secondary; + } + + test(key: string): boolean { + return this.primary.test(key) || this.secondary.test(key); + } + + get(key: string): DocumentExecutionResult | undefined { + return this.primary.get(key) || this.secondary.get(key); + } + + set(key: string, document: DocumentExecutionResult) { + return this.primary.set(key, document); } } diff --git a/packages/myst-execute/src/execute.ts b/packages/myst-execute/src/execute.ts index 47789f8691..35f4d30f5c 100644 --- a/packages/myst-execute/src/execute.ts +++ b/packages/myst-execute/src/execute.ts @@ -1,12 +1,11 @@ import { select, selectAll } from 'unist-util-select'; import type { Kernel } from '@jupyterlab/services'; -import type { Code, InlineExpression, Outputs } from 'myst-spec-ext'; -import type { IOutput } from '@jupyterlab/nbformat'; -import type { GenericParent, IExpressionError } from 'myst-common'; +import type { Code, InlineExpression, Outputs, CodeBlock, IExpressionError } from 'myst-spec'; +import type { GenericParent } from 'myst-common'; import { NotebookCell, fileError, RuleId } from 'myst-common'; import type { VFile } from 'vfile'; import assert from 'node:assert'; -import type { CodeBlock, ExecutionResult, ExecutableNode } from './types.js'; +import type { ExecutionResult, ExecutableNode, DocumentExecutionResult } from './types.js'; import { executeCodeCell, evaluateInlineExpression } from './kernel.js'; import { isCodeBlock, @@ -14,6 +13,7 @@ import { codeBlockSkipsExecution, isInlineExpression, } from './utils.js'; +import { isCodeResult, isExpressionResult } from './types.js'; /** * For each executable node, perform a kernel execution request and return the results. @@ -28,7 +28,7 @@ export async function computeExecutableNodes( nodes: ExecutableNode[], opts: { vfile: VFile }, ): Promise<{ - results: ExecutionResult[]; + results: DocumentExecutionResult['results']; errorOccurred: boolean; }> { let errorOccurred = false; @@ -40,7 +40,10 @@ export async function computeExecutableNodes( const code = select('code', matchedNode) as Code; const { status, outputs } = await executeCodeCell(kernel, code.value); // Cache result - results.push(outputs); + results.push({ + type: 'code', + responses: outputs, + }); // Check for errors const allowErrors = codeBlockRaisesException(matchedNode); @@ -78,12 +81,18 @@ export async function computeExecutableNodes( break; } // Cache result - results.push(result); + results.push({ + type: 'inlineExpression', + response: result, + }); } else { assert(false); } } - return { results, errorOccurred }; + return { + results, + errorOccurred, + }; } /** @@ -96,25 +105,29 @@ export async function computeExecutableNodes( */ export function applyComputedOutputsToNodes( nodes: ExecutableNode[], - computedResult: ExecutionResult[], + computedResult: DocumentExecutionResult['results'], ) { for (const matchedNode of nodes) { // Pull out the result for this node const thisResult = computedResult.shift(); if (isCodeBlock(matchedNode)) { - const rawOutputData = (thisResult as IOutput[]) ?? []; + if (thisResult !== undefined && !isCodeResult(thisResult)) { + throw new Error('Expected code result for code cell'); + } // Pull out outputs to set data const outputs = select('outputs', matchedNode) as Outputs; // Ensure that whether this fails or succeeds, we write to `children` (e.g. due to a kernel error) - outputs.children = rawOutputData.map((data, index) => { + outputs.children = (thisResult?.responses ?? []).map((data, index) => { const identifier = outputs.identifier ? `${outputs.identifier}-${index}` : undefined; return { type: 'output', children: [], jupyter_data: data as any, identifier }; }); } else if (isInlineExpression(matchedNode)) { - const rawOutputData = thisResult as Record | undefined; + if (thisResult !== undefined && !isExpressionResult(thisResult)) { + throw new Error('Expected expression result for inline expression'); + } // Set data of expression to the result, or empty if we don't have one - matchedNode.result = rawOutputData; + matchedNode.result = thisResult?.response; } else { // This should never happen throw new Error('Node must be either code block or inline expression.'); @@ -122,7 +135,7 @@ export function applyComputedOutputsToNodes( } } -export function getExecutableNodes(tree: GenericParent) { +export function getExecutableNodes(tree: GenericParent): ExecutableNode[] { // Pull out code-like nodes return ( ( diff --git a/packages/myst-execute/src/index.ts b/packages/myst-execute/src/index.ts index 5e33996ded..a1aa6451aa 100644 --- a/packages/myst-execute/src/index.ts +++ b/packages/myst-execute/src/index.ts @@ -1,5 +1,10 @@ export { kernelExecutionTransform } from './transform.js'; export type { JupyterServerSettings } from './manager.js'; export { findExistingJupyterServer, launchJupyterServer } from './manager.js'; -export { LocalDiskCache } from './cache.js'; -export type { ICache } from './cache.js'; +export { + NotebookExecutionCache, + LegacyExecutionCache, + LocalDiskCache, + TieredExecutionCache, +} from './cache.js'; +export type { ICache, LocalExecutionCache } from './cache.js'; diff --git a/packages/myst-execute/src/kernel.ts b/packages/myst-execute/src/kernel.ts index 6ade13ee96..24342823cd 100644 --- a/packages/myst-execute/src/kernel.ts +++ b/packages/myst-execute/src/kernel.ts @@ -1,7 +1,7 @@ import type { KernelSpec } from 'myst-frontmatter'; import type { Kernel, KernelMessage, SessionManager } from '@jupyterlab/services'; import type { IOutput } from '@jupyterlab/nbformat'; -import type { IExpressionResult } from 'myst-common'; +import type { IExpressionResult } from 'myst-spec'; import type { Logger } from 'myst-cli-utils'; import type { VFile } from 'vfile'; import path from 'node:path'; @@ -27,10 +27,15 @@ export async function createKernelConnection( vfile: VFile, log?: Logger, ): Promise { + // Jupyter Server API paths are `/`-delimited, but `path.relative` returns + // native separators (backslashes on Windows). + // With a windows path, jupyter_server silently fall back to the server root. + // Normalize to a forward-slash path so kernels start in same location on all platforms. + const sessionPath = path.relative(basePath, vfile.path).split(path.sep).join('/'); const sessionOpts = { - type: 'notebook', - path: path.relative(basePath, vfile.path), + path: sessionPath, name: path.basename(vfile.path), + type: 'console', kernel: { name: kernelspec.name, }, @@ -76,7 +81,9 @@ function IOPubAsOutput(msg: KernelMessage.IIOPubMessage): IOutput { */ export async function executeCodeCell(kernel: Kernel.IKernelConnection, code: string) { const future = kernel.requestExecute({ - code: code, + code, + allow_stdin: false, + stop_on_error: false, }); const outputs: IOutput[] = []; @@ -131,7 +138,7 @@ export async function executeCodeCell(kernel: Kernel.IKernelConnection, code: st } } }; - let status: 'abort' | 'error' | 'ok' | undefined; + let status: KernelMessage.IExecuteReplyMsg['content']['status'] | undefined; future.onReply = (msg: KernelMessage.IExecuteReplyMsg) => { status = msg.content.status; }; @@ -153,6 +160,8 @@ export async function evaluateInlineExpression(kernel: Kernel.IKernelConnection, user_expressions: { expr: expr, }, + allow_stdin: false, + stop_on_error: false, }); let result: IExpressionResult | undefined; future.onReply = (msg: KernelMessage.IExecuteReplyMsg) => { diff --git a/packages/myst-execute/src/transform.ts b/packages/myst-execute/src/transform.ts index 9aac044d07..de5fcdb531 100644 --- a/packages/myst-execute/src/transform.ts +++ b/packages/myst-execute/src/transform.ts @@ -2,22 +2,24 @@ import { select } from 'unist-util-select'; import type { Logger } from 'myst-cli-utils'; import type { PageFrontmatter, KernelSpec } from 'myst-frontmatter'; import type { SessionManager } from '@jupyterlab/services'; -import type { Code } from 'myst-spec-ext'; +import type { Code } from 'myst-spec'; import type { GenericParent } from 'myst-common'; import { fileError } from 'myst-common'; import type { VFile } from 'vfile'; import assert from 'node:assert'; +import path from 'node:path'; import type { Plugin } from 'unified'; import { createHash } from 'node:crypto'; -import type { ICache } from './cache.js'; +import type { IDocumentExecutionCache } from './cache.js'; import { createKernelConnection } from './kernel.js'; import { isCodeBlock, isInlineExpression, codeBlockRaisesException } from './utils.js'; -import type { ExecutableNode, ExecutionResult } from './types.js'; +import type { ExecutableNode } from './types.js'; import { getExecutableNodes, computeExecutableNodes, applyComputedOutputsToNodes, } from './execute.js'; + /** * Build a cache key from an array of executable nodes * @@ -65,7 +67,7 @@ function buildCacheKey( export type Options = { basePath: string; - cache: ICache; + cache: IDocumentExecutionCache; sessionFactory: () => Promise; frontmatter: PageFrontmatter; ignoreCache?: boolean; @@ -107,7 +109,9 @@ export async function kernelExecutionTransform(tree: GenericParent, vfile: VFile // See if we already cached this execution const cacheEnv = getCacheEnvironment(executeConfig?.depends_on_env ?? []); const cacheKey = buildCacheKey(kernelspec, executableNodes, cacheEnv); - let cachedResults = opts.cache.get(cacheKey); + + const cacheHit = opts.cache.get(cacheKey); + let cachedResults = cacheHit?.results; const ignoreCachedDocument = // If we don't globally ignore caching' @@ -153,14 +157,26 @@ export async function kernelExecutionTransform(tree: GenericParent, vfile: VFile } const { kernel } = sessionConnection; log.debug(`Connected to kernel ${kernel.name}`); + + // Execution count + const startTimeNS = process.hrtime.bigint(); try { // Execute notebook const { results, errorOccurred } = await computeExecutableNodes(kernel, executableNodes, { vfile, }); + const stopTimeNS = process.hrtime.bigint(); // Populate cache if things were successful if (!errorOccurred) { - opts.cache.set(cacheKey, results); + opts.cache.set(cacheKey, { + context: { + kernelspec, + path: path.basename(opts.basePath, vfile.path), + timestamp: new Date().toISOString(), + duration_ms: Number((stopTimeNS - startTimeNS) / 1_000_000n), + }, + results, + }); } // Refer to these computed results cachedResults = results; diff --git a/packages/myst-execute/src/types.ts b/packages/myst-execute/src/types.ts index c1eca19af8..f133589214 100644 --- a/packages/myst-execute/src/types.ts +++ b/packages/myst-execute/src/types.ts @@ -1,28 +1,40 @@ -import type { Block, Code, Output, InlineExpression } from 'myst-spec-ext'; +import type { InlineExpression, CodeBlock, IExpressionResult } from 'myst-spec'; import type { IOutput } from '@jupyterlab/nbformat'; -import type { IExpressionResult } from 'myst-common'; + /** - * Type narrowing Output to contain IOutput data - * - * TODO: lift this to the myst-spec definition + * Kinds of nodes that we can "execute" */ -export type CodeBlockOutput = Output & { - data: IOutput[]; -}; +export type ExecutableNode = CodeBlock | InlineExpression; /** - * Type narrowing Block to contain code-cells and code-cell outputs - * - * TODO: lift this to the myst-spec definition + * Interface types for serialising execution results */ +export type CodeResult = { + type: 'code'; + responses: IOutput[]; +}; -export type CodeBlock = Block & { - kind: 'code'; - data?: { - tags?: string[]; - }; - children: (Code | CodeBlockOutput)[]; +export type ExpressionResult = { + type: 'inlineExpression'; + response: IExpressionResult; }; -export type ExecutableNode = CodeBlock | InlineExpression; -export type ExecutionResult = IOutput[] | IExpressionResult; +export type ExecutionResult = CodeResult | ExpressionResult; +export type LegacyExecutionResult = IOutput[] | IExpressionResult; + +export function isCodeResult(result: ExecutionResult): result is CodeResult { + return result.type === 'code'; +} +export function isExpressionResult(result: ExecutionResult): result is ExpressionResult { + return result.type === 'inlineExpression'; +} + +/** + * Result of document execution + */ +export type DocumentExecutionResult = { + // Additional execution metadata + context: Record; + // Array of results, one for each "executable" AST node + results: ExecutionResult[]; +}; diff --git a/packages/myst-execute/src/utils.ts b/packages/myst-execute/src/utils.ts index 8d46b49d96..536a0750bc 100644 --- a/packages/myst-execute/src/utils.ts +++ b/packages/myst-execute/src/utils.ts @@ -1,7 +1,7 @@ -import type { InlineExpression } from 'myst-spec-ext'; +import type { InlineExpression, CodeBlock } from 'myst-spec'; import type { GenericNode } from 'myst-common'; import { NotebookCell, NotebookCellTags } from 'myst-common'; -import type { CodeBlock } from './types.js'; + /** * Return true if the given node is a block over a code node and output node * diff --git a/packages/myst-ext-button/package.json b/packages/myst-ext-button/package.json index 8cc630d6cd..5e455f57c4 100644 --- a/packages/myst-ext-button/package.json +++ b/packages/myst-ext-button/package.json @@ -36,6 +36,7 @@ "myst-spec-ext": "^1.7.8" }, "devDependencies": { - "myst-parser": "^1.5.11" + "myst-parser": "^1.5.11", + "vfile": "^5.3.7" } } diff --git a/packages/myst-ext-exercise/package.json b/packages/myst-ext-exercise/package.json index b6a4e324d8..fb39c3b5c5 100644 --- a/packages/myst-ext-exercise/package.json +++ b/packages/myst-ext-exercise/package.json @@ -32,9 +32,11 @@ "url": "https://github.com/jupyter-book/mystmd/issues" }, "dependencies": { - "myst-common": "^1.7.2" + "myst-common": "^1.7.2", + "myst-directives": "^1.7.3" }, "devDependencies": { - "myst-parser": "^1.5.9" + "myst-parser": "^1.5.9", + "unist-util-select": "^4.0.3" } } diff --git a/packages/myst-ext-grid/package.json b/packages/myst-ext-grid/package.json index 9af95701ea..ff3a6e2cca 100644 --- a/packages/myst-ext-grid/package.json +++ b/packages/myst-ext-grid/package.json @@ -32,7 +32,8 @@ "url": "https://github.com/jupyter-book/mystmd/issues" }, "dependencies": { - "myst-common": "^1.7.2" + "myst-common": "^1.7.2", + "myst-directives": "^1.7.3" }, "devDependencies": { "myst-parser": "^1.7.1" diff --git a/packages/myst-ext-icon/package.json b/packages/myst-ext-icon/package.json index f17e893680..4c99db29fb 100644 --- a/packages/myst-ext-icon/package.json +++ b/packages/myst-ext-icon/package.json @@ -35,6 +35,7 @@ "myst-common": "^1.7.2" }, "devDependencies": { - "myst-parser": "^1.5.7" + "myst-parser": "^1.5.7", + "unist-util-select": "^4.0.3" } } diff --git a/packages/myst-ext-proof/package.json b/packages/myst-ext-proof/package.json index 25935e33ec..6443aa4771 100644 --- a/packages/myst-ext-proof/package.json +++ b/packages/myst-ext-proof/package.json @@ -33,7 +33,8 @@ }, "dependencies": { "myst-spec": "^0.0.5", - "myst-common": "^1.7.2" + "myst-common": "^1.7.2", + "myst-directives": "^1.7.3" }, "devDependencies": { "myst-parser": "^1.5.9" diff --git a/packages/myst-ext-proof/src/proof.ts b/packages/myst-ext-proof/src/proof.ts index d154afd0dc..4520eec8d7 100644 --- a/packages/myst-ext-proof/src/proof.ts +++ b/packages/myst-ext-proof/src/proof.ts @@ -1,25 +1,13 @@ import type { DirectiveSpec, DirectiveData, GenericNode } from 'myst-common'; import { addCommonDirectiveOptions, commonDirectiveOptions } from 'myst-directives'; +import { PROOF_KINDS } from './types.js'; + +// e.g. 'prf:theorem' (legacy, kept for backward compatibility) and 'proof:theorem' (preferred) +const KIND_ALIASES = PROOF_KINDS.flatMap((kind) => [`prf:${kind}`, `proof:${kind}`]); export const proofDirective: DirectiveSpec = { name: 'proof', - alias: [ - 'prf:proof', - 'prf:theorem', - 'prf:axiom', - 'prf:lemma', - 'prf:definition', - 'prf:criterion', - 'prf:remark', - 'prf:conjecture', - 'prf:corollary', - 'prf:algorithm', - 'prf:example', - 'prf:property', - 'prf:observation', - 'prf:proposition', - 'prf:assumption', - ], + alias: KIND_ALIASES, arg: { type: 'myst', }, @@ -53,12 +41,13 @@ export const proofDirective: DirectiveSpec = { } else { // amsthm convention: the bare proof environment is unnumbered by // default; theorem-like kinds are numbered. Opt in with `enumerated: true` - const isBareProof = data.name === 'proof' || data.name === 'prf:proof'; + // Covers all three spellings: `proof`, `prf:proof`, `proof:proof` + const isBareProof = data.name.replace(/^(prf|proof):/, '') === 'proof'; enumerated = (data.options?.enumerated as boolean) ?? !isBareProof; } const proof = { type: 'proof', - kind: data.name !== 'proof' ? data.name.replace('prf:', '') : undefined, + kind: data.name !== 'proof' ? data.name.replace(/^(prf|proof):/, '') : undefined, enumerated, children: children as any[], }; diff --git a/packages/myst-ext-proof/src/types.ts b/packages/myst-ext-proof/src/types.ts index e1f41af0b1..0e959e5b39 100644 --- a/packages/myst-ext-proof/src/types.ts +++ b/packages/myst-ext-proof/src/types.ts @@ -1,6 +1,6 @@ import type { Container } from 'myst-spec'; -const PROOF_KINDS = [ +export const PROOF_KINDS = [ 'proof', 'axiom', 'lemma', diff --git a/packages/myst-ext-proof/tests/proof.spec.ts b/packages/myst-ext-proof/tests/proof.spec.ts index 7c0a939637..8092cc2d45 100644 --- a/packages/myst-ext-proof/tests/proof.spec.ts +++ b/packages/myst-ext-proof/tests/proof.spec.ts @@ -91,7 +91,9 @@ describe('proof directive', () => { it.each([ ['proof', false], ['prf:proof', false], + ['proof:proof', false], ['prf:theorem', true], + ['proof:theorem', true], ['prf:lemma', true], ['prf:algorithm', true], ])('%s defaults enumerated: %s', (name, enumerated) => { @@ -116,4 +118,24 @@ describe('proof directive', () => { const proof = (output as any).children[0].children[0]; expect(proof.enumerated).toBe(false); }); + + it('parses proof directive without prf: prefix', async () => { + const content = '```{proof} Proof Title\nProof content\n```'; + const output = mystParse(content, { + directives: [proofDirective], + }); + const proof = (output as any).children[0].children[0]; + expect(proof.type).toEqual('proof'); + expect(proof.kind).toEqual(undefined); + }); + + it('parses proof:theorem the same as prf:theorem', async () => { + const content = '```{proof:theorem} Theorem Title\nTheorem content\n```'; + const output = mystParse(content, { + directives: [proofDirective], + }); + const theorem = (output as any).children[0].children[0]; + expect(theorem.type).toEqual('proof'); + expect(theorem.kind).toEqual('theorem'); + }); }); diff --git a/packages/myst-ext-tabs/package.json b/packages/myst-ext-tabs/package.json index c2a49b05ba..b4ff6a897c 100644 --- a/packages/myst-ext-tabs/package.json +++ b/packages/myst-ext-tabs/package.json @@ -32,9 +32,11 @@ "url": "https://github.com/jupyter-book/mystmd/issues" }, "dependencies": { - "myst-common": "^1.7.2" + "myst-common": "^1.7.2", + "myst-directives": "^1.7.3" }, "devDependencies": { - "myst-parser": "^1.5.9" + "myst-parser": "^1.5.9", + "unist-util-select": "^4.0.3" } } diff --git a/packages/myst-frontmatter/package.json b/packages/myst-frontmatter/package.json index c9d9d47c35..6fcf92e546 100644 --- a/packages/myst-frontmatter/package.json +++ b/packages/myst-frontmatter/package.json @@ -40,6 +40,8 @@ "myst-toc": "^0.1.4" }, "devDependencies": { + "@types/js-yaml": "^4.0.5", + "@types/json-schema": "^7.0.15", "@types/spdx-correct": "^3.1.0", "glob": "^10.3.1", "js-yaml": "^4.1.0", diff --git a/packages/myst-frontmatter/src/settings/settings.yml b/packages/myst-frontmatter/src/settings/settings.yml index 32e751b4d6..f824ef8e25 100644 --- a/packages/myst-frontmatter/src/settings/settings.yml +++ b/packages/myst-frontmatter/src/settings/settings.yml @@ -24,3 +24,19 @@ cases: what: ok warnings: 1 normalized: {} + - title: infer dois from urls + raw: + settings: + infer_dois_from_urls: true + errors: 0 + normalized: + settings: + infer_dois_from_urls: true + - title: infer dois from urls alias + raw: + settings: + inferDoisFromUrls: true + errors: 0 + normalized: + settings: + infer_dois_from_urls: true diff --git a/packages/myst-frontmatter/src/settings/types.ts b/packages/myst-frontmatter/src/settings/types.ts index 2c659c80ee..71d818b63c 100644 --- a/packages/myst-frontmatter/src/settings/types.ts +++ b/packages/myst-frontmatter/src/settings/types.ts @@ -15,4 +15,5 @@ export type ProjectSettings = { output_matplotlib_strings?: OutputRemovalOptions; myst_to_tex?: MystToTexSettings; parser?: ParserSettings; + infer_dois_from_urls?: boolean; }; diff --git a/packages/myst-frontmatter/src/settings/validators.ts b/packages/myst-frontmatter/src/settings/validators.ts index 83e7c4c2a7..4ae0c06fd6 100644 --- a/packages/myst-frontmatter/src/settings/validators.ts +++ b/packages/myst-frontmatter/src/settings/validators.ts @@ -1,5 +1,11 @@ import type { ValidationOptions } from 'simple-validators'; -import { defined, incrementOptions, validateChoice, validateObjectKeys } from 'simple-validators'; +import { + defined, + incrementOptions, + validateBoolean, + validateChoice, + validateObjectKeys, +} from 'simple-validators'; import type { ProjectSettings } from './types.js'; import { validateMystToTexSettings } from './validatorsMystToTex.js'; import { validateParserSettings } from './validatorsParser.js'; @@ -18,12 +24,14 @@ export const PROJECT_SETTINGS = [ 'output_matplotlib_strings', 'myst_to_tex', 'parser', + 'infer_dois_from_urls', ]; export const PROJECT_SETTINGS_ALIAS = { stderr_output: 'output_stderr', stdout_output: 'output_stdout', mystToTex: 'myst_to_tex', tex: 'myst_to_tex', // The default is the renderer, not the parser + inferDoisFromUrls: 'infer_dois_from_urls', }; export function validateProjectAndPageSettings( @@ -69,6 +77,13 @@ export function validateProjectAndPageSettings( const parser = validateParserSettings(settings.parser, incrementOptions('parser', opts)); if (parser) output.parser = parser; } + if (defined(settings.infer_dois_from_urls)) { + const infer_dois_from_urls = validateBoolean( + settings.infer_dois_from_urls, + incrementOptions('infer_dois_from_urls', opts), + ); + if (infer_dois_from_urls != null) output.infer_dois_from_urls = infer_dois_from_urls; + } if (Object.keys(output).length === 0) return undefined; return output; } diff --git a/packages/myst-migrate/package.json b/packages/myst-migrate/package.json index b3aa76ec1f..95d7adacb5 100644 --- a/packages/myst-migrate/package.json +++ b/packages/myst-migrate/package.json @@ -29,6 +29,7 @@ "vfile-message": "^3.0.0" }, "devDependencies": { - "@jupyterlab/nbformat": "^3.5.2" + "@jupyterlab/nbformat": "^3.5.2", + "@types/mdast": "^3.0.12" } } diff --git a/packages/myst-parser/package.json b/packages/myst-parser/package.json index 6e04856fc9..2b20a8bf3a 100644 --- a/packages/myst-parser/package.json +++ b/packages/myst-parser/package.json @@ -69,6 +69,7 @@ "@types/js-yaml": "^4.0.5", "@types/mdast": "^3.0.10", "js-yaml": "^4.1.0", + "myst-spec-ext": "^1.9.4", "myst-to-html": "^1.7.3", "myst-transforms": "^1.3.50", "rehype-stringify": "^9.0.3" diff --git a/packages/myst-parser/tests/myst.spec.ts b/packages/myst-parser/tests/myst.spec.ts index 78a24fe010..c989b78dfb 100644 --- a/packages/myst-parser/tests/myst.spec.ts +++ b/packages/myst-parser/tests/myst.spec.ts @@ -1,6 +1,7 @@ import { describe, expect, test } from 'vitest'; import fs from 'node:fs'; import path from 'node:path'; +import { createRequire } from 'node:module'; import yaml from 'js-yaml'; import { visit } from 'unist-util-visit'; import { mystParse } from '../src'; @@ -34,10 +35,8 @@ const SKIP_TESTS = [ '506', // This is a link issue? ]; -// TODO: import this from myst-spec properly! -const directory = fs.existsSync('../../node_modules/myst-spec/dist/examples') - ? '../../node_modules/myst-spec/dist/examples' - : '../../../node_modules/myst-spec/dist/examples'; +const mystSpecPackageJson = createRequire(import.meta.url).resolve('myst-spec/package.json'); +const directory = path.join(path.dirname(mystSpecPackageJson), 'dist/examples'); const files: string[] = fs.readdirSync(directory).filter((name) => name.endsWith('.yml')); diff --git a/packages/myst-roles/package.json b/packages/myst-roles/package.json index cf4acf4571..c16b4700d1 100644 --- a/packages/myst-roles/package.json +++ b/packages/myst-roles/package.json @@ -34,5 +34,8 @@ "dependencies": { "myst-common": "^1.10.0", "myst-spec-ext": "^1.10.0" + }, + "devDependencies": { + "vfile": "^5.3.7" } } diff --git a/packages/myst-roles/src/reference.ts b/packages/myst-roles/src/reference.ts index 6cd83f36d3..7fae85a1cf 100644 --- a/packages/myst-roles/src/reference.ts +++ b/packages/myst-roles/src/reference.ts @@ -5,7 +5,7 @@ const REF_PATTERN = /^(.+?)<([^<>]+)>$/; // e.g. 'Labeled Reference ' export const refRole: RoleSpec = { name: 'ref', - alias: ['eq', 'numref', 'prf:ref'], + alias: ['eq', 'numref', 'prf:ref', 'proof:ref'], body: { type: String, required: true, diff --git a/packages/myst-spec/docs/ast-primer.md b/packages/myst-spec/docs/ast-primer.md new file mode 100644 index 0000000000..47e57133c3 --- /dev/null +++ b/packages/myst-spec/docs/ast-primer.md @@ -0,0 +1,171 @@ +--- +title: A primer on the MyST AST +short_title: AST primer +description: A getting-started guide to the structure of the MyST abstract syntax tree, for renderer and parser authors. +--- + +The MyST Abstract Syntax Tree (AST) is the structured representation of a MyST document after parsing. +It is plain JSON, consumed by tools that render the document (to HTML, LaTeX, Typst, etc). +This page is a brief overview of the structure and how the pieces fit together. + +:::{seealso} The MyST User Guide is for users of the CLI +If you just want to author MyST documents, you probably don't need this page - head to the [MyST Guide](xref:guide). +If you want to build a renderer, write a parser, or generate AST from a plugin, read on. +::: + +:::{seealso} The MyST Sandbox is a great place to play around with AST +The [MyST Sandbox](https://mystmd.org/sandbox) is an interactive place to write your own MyST and see what AST happens as a result. +::: + +## The spec and mystmd + +This primer covers both **the MyST Specification** (this site) and **mystmd** (the official reference implementation): + +- The **spec** defines what a conforming MyST parser must produce - node types, their properties, and how they correspond to source. It evolves through the [MyST Enhancement Proposal process](https://mep.mystmd.org). +- **mystmd** is a command-line application that bundles several modular packages that make use of MyST. `myst-parser` is a parser for this spec, `myst-transforms` have a collection of useful transforms to modify a MyST AST (e.g., resolving cross-references and appling numbering numbering). `mystmd` may emits additional node types that aren't yet in the spec as part of its build pipeline (like [named parts](xref:guide#parts-ast)). + +If you're writing a parser, stick to the spec. mystmd's conventions (like specific named parts) aren't part of the spec, but they're still useful to know, since most MyST content today comes from mystmd. This page flags which is which as we go. + +## The shape of a node + +Every node in the AST is a JSON object with a `type` field that names what kind of node it is. +Beyond `type`, a node has either a `value` or `children`: + +- **Leaf nodes** typically have a `value` (typically a string). +- **Parent nodes** have a `children` array (more nodes). + +Nodes may also carry additional properties - `url` on a link, `depth` on a heading, `identifier` on a cross-reference, etc. +These properties are specific to each node type and are documented in the [node reference](./myst.schema.md). + +The **interactive demo** below lets you explore the AST for simple MyST text: a single line of text. Change the text to see the results, and click each box to see a different representation of the data, and stage in the processing pipeline (`PRE` or `POST` transforms). + +```{myst} +Hello, world! +``` + +Click the **AST** tab above to see the AST. (The tab displays YAML for readability; MyST sites serve it as JSON.) +You'll find a `root` node (always the outermost node) wrapping a `paragraph`, which wraps a `text` leaf. + +## Inline formatting nests parent nodes + +Inline formatting introduces parent nodes around the text they decorate. + +```{myst} +A paragraph with **bold** and *emphasis*. +``` + +In the AST tab, the paragraph's `children` is a flat sequence of `text`, `strong`, and `emphasis` nodes. Each `strong` and `emphasis` wraps its own `text` node. +The markup characters (`**`, `*`) are gone from the source text. They've been turned into nested nodes (e.g. `*word*` becomes a `type: emphasis` node wrapping a `text` node). + +(pre-and-post)= +## Two processing stages: PRE and POST transforms + +In the `myst-cli`, a MyST document is processed through a series of transformations, and the AST has a different shape after each one. +The short-hand for this in MyST documentation is `PRE`- and `POST`-transforms. You can get very granular by applying various [`myst-transforms`](https://npmjs.com/package/myst-transforms) in your own libraries or packages. + +1. **PRE**: the output of *parsing*. The parser walks the source and expands directives and roles by calling their `run` functions. Cross-references still look like ordinary `link` nodes (e.g. `url: "#some-label"`). Directives appear as `mystDirective` wrapper nodes containing their expanded children. Numbering has not yet been applied. +2. **POST**: the output after [transforms](xref:guide#develop:transforms) run over the PRE tree. Link nodes pointing at labels become `crossReference` nodes with resolved targets. Most directive wrappers are unwrapped, leaving only their semantic children. Numbering and enumeration are applied. This is the form most renderers consume. + +You can see both stages inline: the `{myst}` directive used on this page exposes a **PRE** / **POST** toggle inside its AST tab, and so does the [MyST sandbox](https://mystmd.org/sandbox). Try it on this cross-reference: + +```{myst} +(my-label)= +**A labeled paragraph.** + +See [this paragraph](#my-label) for context. +``` + +In **PRE**, the second paragraph contains an ordinary `link` node with `url: "#my-label"`. +In **POST**, that same node has become a `crossReference` with `identifier`, `kind`, and `resolved: true` properties. + +### Which stage will you encounter? + +- The **spec's [test cases](https://unpkg.com/browse/myst-spec/dist/)** describe the **PRE** form. They are the canonical "source -> AST" pairs that any conforming parser must produce. +- The [node reference](./myst.schema.md) documents node *types* that may appear in either stage. Some properties (like `resolved` on `crossReference`, or computed numbering fields) appear only after transforms. + +In short: + +- A parser produces PRE. Match the test cases. +- A renderer receives POST. It's what `myst build` emits. +- A transform pipeline applies a series of transformations that change the tree from `PRE` to `POST` + +## Directives in PRE vs. POST + +Directives change shape between PRE and POST (roles behave similarly): + +```{myst} +:::{note} +Heads up! +::: +``` + +In **PRE**, you'll see a `mystDirective` wrapper (name: `note`) containing an `admonition` child. The wrapper records the directive that produced it. The child is what its `run` function returned from a `mystmd` plugin. + +In **POST**, the `mystDirective` wrapper is typically stripped, and transforms may add extra structure. For example, [`admonitionHeadersTransform`](https://github.com/jupyter-book/mystmd/blob/0d626b198093fa740125d02267a111d84547fd36/packages/myst-transforms/src/admonitions.ts#L36) inserts an `admonitionTitle` child. + +When directive parsing fails, the POST AST signals it in one of two ways: + +- **Unknown directive name** (no plugin registered): the node stays as a `mystDirective` leaf, with the original source text in its `value`. +- **Invalid arguments or body**: the node's type becomes `mystDirectiveError`, and its children are dropped. Renderers can use this to display error messages in the output. + +## Plugins often emit a custom node that is transformed + +A common plugin pattern uses a directive (or role) to emit a *placeholder* node, then a separate transform to expand that placeholder into real content. + +For a working in-repo example of this pattern, see [`docs/templates.mjs`](https://github.com/jupyter-book/mystmd/blob/main/docs/templates.mjs) (the `myst:template` directive emits a `myst-template-ref` placeholder, then `mystTemplateTransform` fetches template metadata and expands them). + +This split is useful for anything you can't do inside a directive's [`run()` function](xref:guide#defining-a-new-directive): async work, anything that needs the whole document for context, or anything that depends on cross-references being resolved first. + +## Frontmatter and document structure + +A document's frontmatter (the YAML at the top of a `.md` file) doesn't appear as a node in the tree. +It is attached separately, alongside the `mdast` (the AST) in the JSON output of `myst build`. +A built page on disk looks roughly like: + +```{code} json +:caption: A page from `myst build --site` +{ + "kind": "Article", + "frontmatter": { "title": "...", "authors": [...] }, + "mdast": { "type": "root", "children": [ ... ] }, + "references": { ... } +} +``` + +The `mdast` field is the tree you've been inspecting in the AST tabs above. +The `references` field holds cross-document data like resolved cross-references and citations, which a renderer needs when working across pages. + +## Chunking a page into blocks + +The `+++` syntax divides a page into `block` nodes. Each `block` wraps the content that follows it, and optional JSON after `+++` becomes block metadata: + +```{myst} ++++ {"label": "intro", "foo": "bar"} + +A first chunk. + ++++ + +A second chunk. +``` + +In the AST (POST), each chunk ends up inside a `block` node. Recognized keys (`kind`, `class`, `label`, `identifier`) are moved onto the `block` node, anything else is put under `block.data`. See [](./features/blocks.md) for the formal block specification. + +## "Parts": named regions of a page or project + +Plugins and themes often need to pull out a specific region of a page (the abstract, the acknowledgments, etc.). MyST has "parts" for this. +Parts aren't part of the `myst-specification`, they are a concept used by the [MyST Document Engine](xref:guide) to structure pages and projects. +See [](xref:guide#parts-ast) for details. + +## How the MyST AST and its upstream foundation (`mdast`) relate + +The MyST AST extends [`mdast`](https://github.com/syntax-tree/mdast), the Markdown AST used widely in the JavaScript [unified](https://unifiedjs.com/) ecosystem, which in turn builds on [`unist`](https://github.com/syntax-tree/unist) (Universal Syntax Tree). +If you've used `remark` or `rehype`, the shape will feel familiar. +MyST adds node types for directives, roles, cross-references, citations, admonitions, equations, and other constructs that don't exist in plain Markdown. + +## Where to go next + +- The [MyST sandbox](https://mystmd.org/sandbox) parses any MyST input you type and shows the AST in PRE and POST stages. +- The [AST Node index](./myst.schema.md) describes the AST of all nodes defined in the specification. +- The [test cases](https://unpkg.com/browse/myst-spec/dist/) provide source -> expected (PRE) AST pairs. +- [Generate MyST AST with Plugins](xref:guide/plugins-ast) in the MyST Guide shows a few ways to generate AST from a plugin. diff --git a/packages/myst-spec/docs/myst.yml b/packages/myst-spec/docs/myst.yml index 565ee73364..501bc1264a 100644 --- a/packages/myst-spec/docs/myst.yml +++ b/packages/myst-spec/docs/myst.yml @@ -8,10 +8,14 @@ project: subject: Specification venue: title: MyST Spec + references: + guide: https://mystmd.org/guide toc: # Auto-generated by `myst init --write-toc` - file: index.md - file: features/overview.md + - file: ast-primer.md + - file: myst.schema.md - file: features/commonmark.md - file: features/admonitions.md - file: features/figures.md @@ -20,7 +24,6 @@ project: - file: features/references.md - file: features/footnotes.md - file: features/blocks.md - - file: myst.schema.md site: title: MyST Spec actions: diff --git a/packages/myst-spec/package.json b/packages/myst-spec/package.json index df56f46b49..11b57a63b6 100644 --- a/packages/myst-spec/package.json +++ b/packages/myst-spec/package.json @@ -30,6 +30,7 @@ "url": "https://github.com/jupyter-book/myst-spec/issues" }, "devDependencies": { + "@jupyterlab/nbformat": "^3.5.2", "copyfiles": "^2.4.1" } } diff --git a/packages/myst-spec/src/ext.ts b/packages/myst-spec/src/ext.ts index 106ff700bc..d8cd4536c9 100644 --- a/packages/myst-spec/src/ext.ts +++ b/packages/myst-spec/src/ext.ts @@ -21,6 +21,7 @@ import type { CrossReference as SpecCrossReference, Link as SpecLink, } from './schema.d.ts'; +import type { IOutput, IMimeBundle } from '@jupyterlab/nbformat'; type Visibility = 'show' | 'hide' | 'remove'; @@ -198,6 +199,30 @@ export type SiUnit = { value: string; }; +export interface IExpressionDisplay { + status: 'ok'; + data: IMimeBundle; + metadata: Record; +} + +export interface IExpressionError { + status: 'error'; + /** + * Exception name + */ + ename: string; + /** + * Exception value + */ + evalue: string; + /** + * Traceback + */ + traceback: string[]; +} + +export type IExpressionResult = IExpressionError | IExpressionDisplay; + export type InlineExpression = { type: 'inlineExpression'; value: string; @@ -333,3 +358,15 @@ export type AnyWidget = { /** Tailwind classes to apply to the container element */ class?: string; }; + +/** + * Type narrowed Block to contain code-cells + */ + +export type CodeBlock = Block & { + kind: 'notebook-code'; + data?: { + tags?: string[]; + }; + children: Code[]; +}; diff --git a/packages/myst-spec/src/index.ts b/packages/myst-spec/src/index.ts index d75e218d91..1f3ebd0369 100644 --- a/packages/myst-spec/src/index.ts +++ b/packages/myst-spec/src/index.ts @@ -58,6 +58,7 @@ export type { CiteGroup, CiteKind, Code, + CodeBlock, Container, CrossReference, DefinitionDescription, @@ -73,6 +74,9 @@ export type { Image, Include, IndexEntry, + IExpressionResult, + IExpressionError, + IExpressionDisplay, InlineExpression, InlineMath, Link, diff --git a/packages/myst-templates/package.json b/packages/myst-templates/package.json index 3cd43c32b9..b1ac8c2a0e 100644 --- a/packages/myst-templates/package.json +++ b/packages/myst-templates/package.json @@ -50,6 +50,8 @@ "simple-validators": "^1.2.0" }, "devDependencies": { + "@types/adm-zip": "^0.5.0", + "@types/js-yaml": "^4.0.5", "@types/nunjucks": "^3.2.2", "memfs": "^3.5.3" } diff --git a/packages/myst-to-docx/package.json b/packages/myst-to-docx/package.json index a83cb07b22..4fa90b3684 100644 --- a/packages/myst-to-docx/package.json +++ b/packages/myst-to-docx/package.json @@ -40,6 +40,11 @@ "myst-frontmatter": "^1.9.3", "myst-spec": "^0.0.5", "myst-spec-ext": "^1.9.3", - "unist-util-select": "^4.0.3" + "unified": "^10.1.2", + "unist-util-select": "^4.0.3", + "vfile": "^5.3.7" + }, + "devDependencies": { + "unist-builder": "^3.0.0" } } diff --git a/packages/myst-to-html/package.json b/packages/myst-to-html/package.json index 9408600fe8..b49e89a210 100644 --- a/packages/myst-to-html/package.json +++ b/packages/myst-to-html/package.json @@ -46,6 +46,7 @@ "mdast-util-find-and-replace": "^2.1.0", "mdast-util-to-hast": "^12.3.0", "myst-common": "^1.10.0", + "myst-spec": "^0.0.5", "rehype-format": "^4.0.1", "rehype-parse": "^8.0.4", "rehype-remark": "^9.1.2", @@ -58,6 +59,9 @@ "unist-util-visit": "^4.1.0" }, "devDependencies": { - "hastscript": "^7.0.0" + "@types/hast": "^2.3.4", + "@types/mdast": "^3.0.12", + "hastscript": "^7.0.0", + "js-yaml": "^4.1.0" } } diff --git a/packages/myst-to-ipynb/package.json b/packages/myst-to-ipynb/package.json index 329175ee7a..6acb6c0cf9 100644 --- a/packages/myst-to-ipynb/package.json +++ b/packages/myst-to-ipynb/package.json @@ -42,9 +42,15 @@ "mdast-util-to-markdown": "^1.5.0", "myst-common": "^1.7.6", "myst-frontmatter": "^1.7.6", + "myst-spec": "^0.0.5", + "myst-spec-ext": "^1.10.0", "myst-to-md": "^1.0.15", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", "vfile-reporter": "^7.0.4" + }, + "devDependencies": { + "@types/js-yaml": "^4.0.5" } } diff --git a/packages/myst-to-jats/package.json b/packages/myst-to-jats/package.json index ae38135705..adff9009ef 100644 --- a/packages/myst-to-jats/package.json +++ b/packages/myst-to-jats/package.json @@ -47,6 +47,7 @@ "myst-spec-ext": "^1.9.2", "myst-transforms": "^1.3.44", "nbtx": "^0.4.0", + "orcid": "^1.0.0", "unified": "^10.1.2", "unist-util-remove": "^3.1.0", "unist-util-select": "^4.0.3", @@ -58,8 +59,10 @@ "@types/js-yaml": "^4.0.5", "@types/katex": "^0.14.0", "@types/mdast": "^3.0.10", - "jats-xml": "^1.0.7", + "jats-xml": "^1.1.7", "js-yaml": "^4.1.0", - "myst-cli-utils": "^2.0.11" + "myst-cli-utils": "^2.0.11", + "simple-validators": "^1.2.0", + "unist-builder": "^3.0.0" } } diff --git a/packages/myst-to-jats/tests/basic.spec.ts b/packages/myst-to-jats/tests/basic.spec.ts index fd9ffbb25c..d979336be9 100644 --- a/packages/myst-to-jats/tests/basic.spec.ts +++ b/packages/myst-to-jats/tests/basic.spec.ts @@ -6,7 +6,6 @@ import { validateProjectFrontmatter } from 'myst-frontmatter'; import { SourceFileKind } from 'myst-spec-ext'; import type { ValidationOptions } from 'simple-validators'; import path from 'node:path'; -import { validateJatsAgainstDtd } from 'jats-xml'; import yaml from 'js-yaml'; import { VFile } from 'vfile'; import mystToJats, { writeJats } from '../src'; diff --git a/packages/myst-to-md/package.json b/packages/myst-to-md/package.json index 9e89707114..6fd3c877b6 100644 --- a/packages/myst-to-md/package.json +++ b/packages/myst-to-md/package.json @@ -42,8 +42,13 @@ "mdast-util-to-markdown": "^1.5.0", "myst-common": "^1.8.3", "myst-frontmatter": "^1.8.3", + "myst-spec": "^0.0.5", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", "vfile": "^5.3.7", "vfile-reporter": "^7.0.4" + }, + "devDependencies": { + "@types/js-yaml": "^4.0.5" } } diff --git a/packages/myst-to-tex/package.json b/packages/myst-to-tex/package.json index bc1c6c43c3..1d49139800 100644 --- a/packages/myst-to-tex/package.json +++ b/packages/myst-to-tex/package.json @@ -39,13 +39,18 @@ "dependencies": { "myst-common": "^1.9.4", "myst-frontmatter": "^1.9.4", + "myst-spec": "^0.0.5", "myst-spec-ext": "^1.9.4", "myst-ext-proof": "^1.0.11", + "unified": "^10.1.2", "unist-util-remove": "^3.1.0", "unist-util-select": "^4.0.3", + "vfile": "^5.3.7", "vfile-reporter": "^7.0.4" }, "devDependencies": { - "myst-parser": "^1.7.1" + "js-yaml": "^4.1.0", + "myst-parser": "^1.7.1", + "unist-builder": "^3.0.0" } } diff --git a/packages/myst-to-typst/package.json b/packages/myst-to-typst/package.json index ed69e7c95f..3862c12935 100644 --- a/packages/myst-to-typst/package.json +++ b/packages/myst-to-typst/package.json @@ -39,9 +39,16 @@ "dependencies": { "myst-common": "^1.10.0", "myst-frontmatter": "^1.10.0", + "myst-spec": "^0.0.5", "myst-spec-ext": "^1.10.0", - "tex-to-typst": "^0.0.20", + "tex-to-typst": "^0.0.21", + "unified": "^10.1.2", "unist-util-select": "^4.0.3", + "vfile": "^5.3.7", "vfile-reporter": "^7.0.4" + }, + "devDependencies": { + "js-yaml": "^4.1.0", + "unist-builder": "^3.0.0" } } diff --git a/packages/myst-transforms/package.json b/packages/myst-transforms/package.json index 3e46abf70c..3f8dda6e53 100644 --- a/packages/myst-transforms/package.json +++ b/packages/myst-transforms/package.json @@ -35,6 +35,7 @@ "myst-spec": "^0.0.5", "myst-spec-ext": "^1.10.0", "myst-to-html": "1.7.3", + "rehype-format": "^4.0.1", "rehype-parse": "^8.0.4", "rehype-remark": "^9.1.2", "unified": "^10.0.0", @@ -49,6 +50,8 @@ "vfile-message": "^3.1.2" }, "devDependencies": { - "@types/katex": "^0.14.0" + "@types/js-yaml": "^4.0.5", + "@types/katex": "^0.14.0", + "@types/mdast": "^3.0.12" } } diff --git a/packages/myst-transforms/src/links/doi.spec.ts b/packages/myst-transforms/src/links/doi.spec.ts index 176732d6bb..51824a95b1 100644 --- a/packages/myst-transforms/src/links/doi.spec.ts +++ b/packages/myst-transforms/src/links/doi.spec.ts @@ -1,10 +1,75 @@ import { describe, expect, test } from 'vitest'; import { VFile } from 'vfile'; import type { Link } from 'myst-spec-ext'; -import { DOITransformer } from './doi'; +import { DOITransformer, isRecognizedDoi } from './doi'; -describe('Test GithubTransformer', () => { - test('any github link', async () => { +describe('isRecognizedDoi', () => { + test('dx.doi.org URL', () => { + expect(isRecognizedDoi('https://dx.doi.org/10.1016/j.ultramic.2018.06.001')).toBe(true); + }); + test('www.doi.org URL', () => { + expect(isRecognizedDoi('https://www.doi.org/10.1016/j.ultramic.2018.06.001')).toBe(true); + }); + test('hostname ending in doi.org is not treated as doi.org', () => { + expect(isRecognizedDoi('https://notadoi.org/10.1038/s41586-020-2649-2')).toBe(false); + }); + test('short doi.org URL', () => { + expect(isRecognizedDoi('https://doi.org/cr3qwn')).toBe(true); + }); + test('doi.org URL with special characters', () => { + expect( + isRecognizedDoi( + 'https://doi.org/10.1002/(SICI)1096-987X(199709)18:12%3C1450::AID-JCC3%3E3.0.CO;2-I', + ), + ).toBe(true); + }); + test('raw DOI', () => { + expect(isRecognizedDoi('10.1016/j.ultramic.2018.06.001')).toBe(true); + }); + test('doi: prefix', () => { + expect(isRecognizedDoi('doi:10.1016/j.ultramic.2018.06.001')).toBe(true); + }); + test('invalid doi: prefix is not recognized', () => { + expect(isRecognizedDoi('doi:invalid')).toBe(false); + }); + test('publisher URL with embedded DOI', () => { + expect(isRecognizedDoi('https://www.nature.com/articles/10.1038/s41586-020-2649-2')).toBe( + false, + ); + }); + test('publisher URL with embedded DOI when inferDoisFromUrls is enabled', () => { + expect( + isRecognizedDoi('https://www.nature.com/articles/10.1038/s41586-020-2649-2', { + inferDoisFromUrls: true, + }), + ).toBe(true); + }); + + describe.each([ + { + name: 'mybinder zenodo dataverse link', + url: 'https://mybinder.org/v2/zenodo/10.7910/DVN/EOYZKH/', + }, + { + name: 'iopscience article link', + url: 'https://iopscience.iop.org/article/10.3847/1538-3881/ace32f#ajace32ff10', + }, + { + name: 'frontiers article link', + url: 'https://www.frontiersin.org/articles/10.3389/fonc.2018.00134/full#supplementary-material', + }, + ])('$name', ({ url }) => { + test('is not recognized by default', () => { + expect(isRecognizedDoi(url)).toBe(false); + }); + test('is recognized when inferDoisFromUrls is enabled', () => { + expect(isRecognizedDoi(url, { inferDoisFromUrls: true })).toBe(true); + }); + }); +}); + +describe('DOITransformer', () => { + test('doi.org link', async () => { const file = new VFile(); const t = new DOITransformer(); const url = 'https://doi.org/10.1016/j.ultramic.2018.06.001'; @@ -18,4 +83,45 @@ describe('Test GithubTransformer', () => { expect(link.url).toBe(url); expect(link.data?.doi).toEqual('10.1016/j.ultramic.2018.06.001'); }); + test('publisher URL with embedded DOI is not matched', () => { + const t = new DOITransformer(); + const url = 'https://www.nature.com/articles/10.1038/s41586-020-2649-2'; + expect(t.test(url)).toBe(false); + }); + test('publisher URL with embedded DOI is matched when enabled', () => { + const t = new DOITransformer({ inferDoisFromUrls: true }); + const url = 'https://www.nature.com/articles/10.1038/s41586-020-2649-2'; + expect(t.test(url)).toBe(true); + }); + test('invalid doi: link fails transform with error', () => { + const file = new VFile(); + const t = new DOITransformer(); + const url = 'doi:invalid'; + const link: Link = { type: 'link', url, children: [] }; + expect(t.test(url)).toBe(true); + expect(t.transform(link, file)).toBe(false); + expect(file.messages.length).toBeGreaterThan(0); + }); + test('short doi.org link transforms', () => { + const file = new VFile(); + const t = new DOITransformer(); + const url = 'https://doi.org/cr3qwn'; + const link: Link = { type: 'link', url, children: [] }; + expect(t.test(url)).toBe(true); + expect(t.transform(link, file)).toBe(true); + expect(link.url).toBe(url); + expect(link.data?.doi).toEqual('cr3qwn'); + }); + test('doi.org link with special characters transforms', () => { + const file = new VFile(); + const t = new DOITransformer(); + const url = + 'https://doi.org/10.1002/(SICI)1096-987X(199709)18:12%3C1450::AID-JCC3%3E3.0.CO;2-I'; + const link: Link = { type: 'link', url, children: [] }; + expect(t.test(url)).toBe(true); + expect(t.transform(link, file)).toBe(true); + expect(link.data?.doi).toEqual( + '10.1002/(SICI)1096-987X(199709)18:12%3C1450::AID-JCC3%3E3.0.CO;2-I', + ); + }); }); diff --git a/packages/myst-transforms/src/links/doi.ts b/packages/myst-transforms/src/links/doi.ts index b6ca0aee05..9f33fb5453 100644 --- a/packages/myst-transforms/src/links/doi.ts +++ b/packages/myst-transforms/src/links/doi.ts @@ -7,16 +7,48 @@ import { updateLinkTextIfEmpty } from './utils.js'; const TRANSFORM_SOURCE = 'LinkTransform:DOITransformer'; +/** Canonical doi.org resolver hosts (exact hostname match). */ +const DOI_ORG_HOSTNAMES = new Set(['doi.org', 'www.doi.org', 'dx.doi.org']); + +function isDoiOrgHostname(hostname: string): boolean { + return DOI_ORG_HOSTNAMES.has(hostname.toLowerCase()); +} + +export type DoiOptions = { + /** When true, infer DOIs from non-doi.org URLs (publisher pages, biorxiv, zenodo, etc.). */ + inferDoisFromUrls?: boolean; +}; + +/** + * Decide whether a string should be treated as a DOI link or citation label. + * + * Unless `inferDoisFromUrls` is enabled, URLs with a non-doi.org hostname are rejected. + * Otherwise, delegates to doi-utils validation. + */ +export function isRecognizedDoi(uri?: string, opts?: DoiOptions): boolean { + if (!uri) return false; + if (!opts?.inferDoisFromUrls) { + try { + const url = new URL(uri); + if (url.hostname && !isDoiOrgHostname(url.hostname)) return false; + } catch { + // Not a URL — fall through to doi-utils validation. + } + } + return doi.validate(uri); +} + export class DOITransformer implements LinkTransformer { protocol = 'doi'; + constructor(private doiOpts?: DoiOptions) {} + test(uri?: string): boolean { if (uri?.startsWith('doi:')) { - // This may not be valid but flag it for the transform + // May not be valid, but flag for transform to report errors. return true; } - if (uri && doi.validate(uri)) return true; - return false; + return isRecognizedDoi(uri, this.doiOpts); } transform(link: Link, file: VFile): boolean { diff --git a/packages/myst-transforms/src/links/index.ts b/packages/myst-transforms/src/links/index.ts index 74e0be07fc..427f88b0a3 100644 --- a/packages/myst-transforms/src/links/index.ts +++ b/packages/myst-transforms/src/links/index.ts @@ -4,7 +4,8 @@ export { SphinxTransformer } from './sphinx.js'; export { WikiTransformer } from './wiki.js'; export { RRIDTransformer } from './rrid.js'; export { RORTransformer } from './ror.js'; -export { DOITransformer } from './doi.js'; +export { DOITransformer, isRecognizedDoi } from './doi.js'; +export type { DoiOptions } from './doi.js'; export { GithubTransformer } from './github.js'; export type { LinkTransformer, MystXRef, MystXRefs, ResolvedExternalReference } from './types.js'; export { updateLinkTextIfEmpty } from './utils.js'; diff --git a/packages/mystmd/package.json b/packages/mystmd/package.json index e4e9c3d01e..cc586c0902 100644 --- a/packages/mystmd/package.json +++ b/packages/mystmd/package.json @@ -43,11 +43,15 @@ "build": "npm-run-all -l clean copy:version copy:qe-version -p build:cli" }, "devDependencies": { + "@types/js-yaml": "^4.0.5", "async-mutex": "^0.5.0", "chalk": "^5.2.0", "commander": "^10.0.1", "core-js": "^3.31.1", "js-yaml": "^4.1.0", - "myst-cli": "^1.10.1" + "myst-cli": "^1.10.1", + "myst-cli-utils": "^2.0.14", + "myst-common": "^1.10.0", + "myst-templates": "^1.0.27" } } diff --git a/packages/mystmd/src/clirun.ts b/packages/mystmd/src/clirun.ts index 3c260104e6..7523151098 100644 --- a/packages/mystmd/src/clirun.ts +++ b/packages/mystmd/src/clirun.ts @@ -3,7 +3,7 @@ import type { ISession, Session } from 'myst-cli'; import { checkNodeVersion, getNodeVersion, logVersions } from 'myst-cli'; import { chalkLogger, LogLevel } from 'myst-cli-utils'; import { Semaphore } from 'async-mutex'; -import { cpus } from 'node:os'; +import { availableParallelism } from 'node:os'; type SessionOpts = { debug?: boolean; @@ -35,7 +35,7 @@ export function clirun( const logger = chalkLogger(opts?.debug ? LogLevel.debug : LogLevel.info, process.cwd()); // Override default myst.yml if --config option is given. const configFiles = opts?.config ? [opts.config] : undefined; - const parallelCount = opts?.executeParallel ?? Math.max(1, cpus().length - 1); + const parallelCount = opts?.executeParallel ?? Math.max(1, availableParallelism() - 1); const executionSemaphore = new Semaphore(parallelCount); const session = new sessionClass({ logger, configFiles, executionSemaphore }); await session.reload(); diff --git a/packages/tex-to-myst/package.json b/packages/tex-to-myst/package.json index 41c5937da3..3f74f76d57 100644 --- a/packages/tex-to-myst/package.json +++ b/packages/tex-to-myst/package.json @@ -44,6 +44,10 @@ "unist-builder": "^3.0.0", "unist-util-remove": "^3.1.0", "unist-util-select": "^4.0.3", + "vfile": "^5.3.7", "vfile-reporter": "^7.0.4" + }, + "devDependencies": { + "js-yaml": "^4.1.0" } } diff --git a/quantecon/UPSTREAM-PRS.yml b/quantecon/UPSTREAM-PRS.yml index 7b9da33e4f..5957b14071 100644 --- a/quantecon/UPSTREAM-PRS.yml +++ b/quantecon/UPSTREAM-PRS.yml @@ -48,6 +48,14 @@ upstream_candidates: review (image titles, files/ output-folder resolution, async reads) — cherry-pick it in the same upstream PR. + Cherry-pick caveat: `packages/myst-to-ipynb/package.json` needs + `myst-spec`, `myst-spec-ext`, `unified` and `@types/js-yaml` + declared. Both squash commits predate upstream's isolated-linker + switch (jupyter-book/mystmd#2987) and relied on hoisting; the + declarations were added in the 2026-08-10 sync merge, so they are + *not* carried by either sha. Add them on the upstream branch after + cherry-picking, or the package will not build upstream. + - id: book-mode-with-section-scope title: Book-style numbering (with LaTeX-style section scope and counter sharing) description: | diff --git a/quantecon/VERSION.yml b/quantecon/VERSION.yml index 0e63d59071..50988b42cc 100644 --- a/quantecon/VERSION.yml +++ b/quantecon/VERSION.yml @@ -33,7 +33,7 @@ # `qe-v` sees a self-consistent file (qe_version matches the tag). qe_version: qe-v8 -upstream_base: 1.9.1 +upstream_base: 1.10.1 merged_features: - id: 1