From 7433b8aad41968410da8c5146d4fece541c85681 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Thu, 2 Jan 2025 18:33:14 +0800 Subject: [PATCH] feat: introduce precompiled-grammars (#880) --- .gitignore | 1 + bench/bundle-test/bundle.bench.ts | 34 ++ bench/bundle-test/index-lite.ts | 18 + bench/bundle-test/index-wasm.ts | 18 + bench/bundle-test/package.json | 6 + bench/bundle-test/rollup.config.mjs | 31 ++ bench/{ => engines}/engines.bench.ts | 26 +- docs/package.json | 4 - docs/packages/vitepress.md | 4 +- package.json | 8 +- packages/compat/build.config.ts | 3 + packages/compat/package.json | 4 + packages/core/rollup.config.mjs | 101 ----- packages/engine-javascript/build.config.ts | 2 + packages/engine-javascript/package.json | 4 + .../engine-javascript/src/engine-compile.ts | 79 ++++ packages/engine-javascript/src/engine-raw.ts | 30 ++ packages/engine-javascript/src/index.ts | 207 +-------- packages/engine-javascript/src/scanner.ts | 139 ++++++ .../engine-javascript/test/compare.test.ts | 2 +- .../engine-javascript/test/general.test.ts | 2 +- packages/engine-javascript/test/raw.test.ts | 23 + packages/engine-javascript/test/types.ts | 2 +- .../engine-javascript/test/verify.test.ts | 6 +- packages/engine-oniguruma/rollup.config.mjs | 6 +- packages/engine-oniguruma/src/index.ts | 2 +- packages/langs-precompiled/README.md | 15 + packages/langs-precompiled/package.json | 360 ++++++++++++++++ .../__snapshots__/precompile.test.ts.snap | 404 ++++++++++++++++++ packages/langs-precompiled/scripts/langs.ts | 180 ++++++++ .../scripts/precompile.test.ts | 22 + .../langs-precompiled/scripts/precompile.ts | 17 + packages/langs-precompiled/scripts/prepare.ts | 6 + packages/langs/scripts/langs.ts | 28 +- packages/shiki/rollup.config.mjs | 1 + packages/shiki/test/core-sync.test.ts | 2 +- packages/types/src/engines.ts | 2 +- pnpm-lock.yaml | 266 +++--------- pnpm-workspace.yaml | 3 +- scripts/report-engine-js-compat.ts | 48 +-- scripts/utils.ts | 60 +++ tsconfig.json | 3 +- vitest.config.ts | 3 +- 43 files changed, 1588 insertions(+), 594 deletions(-) create mode 100644 bench/bundle-test/bundle.bench.ts create mode 100644 bench/bundle-test/index-lite.ts create mode 100644 bench/bundle-test/index-wasm.ts create mode 100644 bench/bundle-test/package.json create mode 100644 bench/bundle-test/rollup.config.mjs rename bench/{ => engines}/engines.bench.ts (50%) delete mode 100644 packages/core/rollup.config.mjs create mode 100644 packages/engine-javascript/src/engine-compile.ts create mode 100644 packages/engine-javascript/src/engine-raw.ts create mode 100644 packages/engine-javascript/src/scanner.ts create mode 100644 packages/engine-javascript/test/raw.test.ts create mode 100644 packages/langs-precompiled/README.md create mode 100644 packages/langs-precompiled/package.json create mode 100644 packages/langs-precompiled/scripts/__snapshots__/precompile.test.ts.snap create mode 100644 packages/langs-precompiled/scripts/langs.ts create mode 100644 packages/langs-precompiled/scripts/precompile.test.ts create mode 100644 packages/langs-precompiled/scripts/precompile.ts create mode 100644 packages/langs-precompiled/scripts/prepare.ts create mode 100644 scripts/utils.ts diff --git a/.gitignore b/.gitignore index 0dde4780b..5adaf61fc 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ cache .eslintcache report-engine-js-compat.json scripts/compares +tm-grammars-themes diff --git a/bench/bundle-test/bundle.bench.ts b/bench/bundle-test/bundle.bench.ts new file mode 100644 index 000000000..fbae69d2b --- /dev/null +++ b/bench/bundle-test/bundle.bench.ts @@ -0,0 +1,34 @@ +/* eslint-disable ts/ban-ts-comment */ +/* eslint-disable antfu/no-import-dist */ + +import { bench, describe } from 'vitest' +// @ts-ignore - ignore type error +import { highlight as highlightA } from './dist/index-lite.min.mjs' +// @ts-ignore - ignore type error +import { highlight as highlightB } from './dist/index-wasm.min.mjs' + +const code = ` +import { ref } from 'vue' + +const message = ref('Hello World!') + +function reverseMessage() { + // Access/mutate the value of a ref via + // its .value property. + message.value = message.value.split('').reverse().join('') +} + +function notify() { + alert('navigation was prevented.') +} +` + +describe('bundle', () => { + bench('js-precompiled', async () => { + await highlightA(code) + }) + + bench('wasm', async () => { + await highlightB(code) + }) +}) diff --git a/bench/bundle-test/index-lite.ts b/bench/bundle-test/index-lite.ts new file mode 100644 index 000000000..c56aca978 --- /dev/null +++ b/bench/bundle-test/index-lite.ts @@ -0,0 +1,18 @@ +import { codeToHtml, createShikiInternal } from '@shikijs/core' +import { createJavaScriptRawEngine } from '@shikijs/engine-javascript/raw' + +const shiki = createShikiInternal( + { + langs: [ + import('@shikijs/langs-precompiled/ts'), + ], + themes: [ + import('@shikijs/themes/vitesse-dark'), + ], + engine: createJavaScriptRawEngine(), + }, +) + +export async function highlight(code: string): Promise { + return codeToHtml(await shiki, code, { lang: 'ts', theme: 'vitesse-dark' }) +} diff --git a/bench/bundle-test/index-wasm.ts b/bench/bundle-test/index-wasm.ts new file mode 100644 index 000000000..e31309b14 --- /dev/null +++ b/bench/bundle-test/index-wasm.ts @@ -0,0 +1,18 @@ +import { codeToHtml, createShikiInternal } from '@shikijs/core' +import { createWasmOnigEngine } from '@shikijs/engine-oniguruma' + +const shiki = createShikiInternal( + { + langs: [ + import('@shikijs/langs/ts'), + ], + themes: [ + import('@shikijs/themes/vitesse-dark'), + ], + engine: createWasmOnigEngine(import('@shikijs/engine-oniguruma/wasm-inlined')), + }, +) + +export async function highlight(code: string): Promise { + return codeToHtml(await shiki, code, { lang: 'ts', theme: 'vitesse-dark' }) +} diff --git a/bench/bundle-test/package.json b/bench/bundle-test/package.json new file mode 100644 index 000000000..a9aed2f0d --- /dev/null +++ b/bench/bundle-test/package.json @@ -0,0 +1,6 @@ +{ + "private": true, + "scripts": { + "bench:prepare": "rollup -c && du -h dist/*" + } +} diff --git a/bench/bundle-test/rollup.config.mjs b/bench/bundle-test/rollup.config.mjs new file mode 100644 index 000000000..16efa65db --- /dev/null +++ b/bench/bundle-test/rollup.config.mjs @@ -0,0 +1,31 @@ +import resolve from '@rollup/plugin-node-resolve' +import esbuild from 'rollup-plugin-esbuild' + +const plugins = [ + resolve(), + esbuild({ + minify: true, + target: 'esnext', + }), +] + +export default [ + { + input: 'index-wasm.ts', + output: { + file: 'dist/index-wasm.min.mjs', + format: 'es', + inlineDynamicImports: true, + }, + plugins, + }, + { + input: 'index-lite.ts', + output: { + file: 'dist/index-lite.min.mjs', + format: 'es', + inlineDynamicImports: true, + }, + plugins, + }, +] diff --git a/bench/engines.bench.ts b/bench/engines/engines.bench.ts similarity index 50% rename from bench/engines.bench.ts rename to bench/engines/engines.bench.ts index 7d89661c3..468418d66 100644 --- a/bench/engines.bench.ts +++ b/bench/engines/engines.bench.ts @@ -1,20 +1,22 @@ /* eslint-disable no-console */ import type { BundledLanguage } from 'shiki' -import type { ReportItem } from '../scripts/report-engine-js-compat' +import type { ReportItem } from '../../scripts/report-engine-js-compat' import fs from 'node:fs/promises' -import { createHighlighter, createJavaScriptRegexEngine, createOnigurumaEngine } from 'shiki' +import { createJavaScriptRawEngine, createJavaScriptRegexEngine } from '@shikijs/engine-javascript' +import { createHighlighter, createOnigurumaEngine } from 'shiki' import { bench, describe } from 'vitest' const js = createJavaScriptRegexEngine() +const jsRaw = createJavaScriptRawEngine() const wasm = await createOnigurumaEngine(() => import('shiki/wasm')) const RANGE = [0, 20] // Run `npx jiti scripts/report-engine-js-compat.ts` to generate the report first -const report = await fs.readFile(new URL('../scripts/report-engine-js-compat.json', import.meta.url), 'utf-8').then(JSON.parse) as ReportItem[] +const report = await fs.readFile(new URL('../../scripts/report-engine-js-compat.json', import.meta.url), 'utf-8').then(JSON.parse) as ReportItem[] const langs = report.filter(i => i.highlightMatch === true).map(i => i.lang).slice(...RANGE) as BundledLanguage[] // Clone https://github.com/shikijs/textmate-grammars-themes to `../tm-grammars-themes` -const samples = await Promise.all(langs.map(lang => fs.readFile(`../tm-grammars-themes/samples/${lang}.sample`, 'utf-8'))) +const samples = await Promise.all(langs.map(lang => fs.readFile(new URL(`../../tm-grammars-themes/samples/${lang}.sample`, import.meta.url), 'utf-8'))) console.log('Benchmarking engines with', langs.length, 'languages') @@ -30,14 +32,26 @@ const shikiWasm = await createHighlighter({ engine: wasm, }) +const shikiJsPrecompiled = await createHighlighter({ + langs: await Promise.all(langs.map(lang => import(`@shikijs/langs-precompiled/${lang}`))), + themes: ['vitesse-dark'], + engine: jsRaw, +}) + for (const lang of langs) { describe(lang, () => { + const code = samples[langs.indexOf(lang)] + bench('js', () => { - shikiJs.codeToTokensBase(samples[langs.indexOf(lang)], { lang, theme: 'vitesse-dark' }) + shikiJs.codeToTokensBase(code, { lang, theme: 'vitesse-dark' }) + }) + + bench('js-precompiled', () => { + shikiJsPrecompiled.codeToTokensBase(code, { lang, theme: 'vitesse-dark' }) }) bench('wasm', () => { - shikiWasm.codeToTokensBase(samples[langs.indexOf(lang)], { lang, theme: 'vitesse-dark' }) + shikiWasm.codeToTokensBase(code, { lang, theme: 'vitesse-dark' }) }) }) } diff --git a/docs/package.json b/docs/package.json index 47dc3805a..e0eddf198 100644 --- a/docs/package.json +++ b/docs/package.json @@ -12,14 +12,10 @@ }, "devDependencies": { "@iconify-json/svg-spinners": "catalog:", - "@shikijs/colorized-brackets": "workspace:*", - "@shikijs/transformers": "workspace:*", - "@shikijs/twoslash": "workspace:*", "@unocss/reset": "catalog:", "@vueuse/core": "catalog:", "floating-vue": "catalog:", "pinia": "catalog:", - "shiki": "workspace:*", "unocss": "catalog:", "unplugin-vue-components": "catalog:", "vitepress": "catalog:", diff --git a/docs/packages/vitepress.md b/docs/packages/vitepress.md index cb2b3d321..c61f462d6 100644 --- a/docs/packages/vitepress.md +++ b/docs/packages/vitepress.md @@ -39,9 +39,9 @@ export default defineConfig({ And then in your [`.vitepress/theme/index.ts`](https://vitepress.dev/guide/custom-theme), install the Vue plugin and import the css with `@shikijs/vitepress-twoslash/styles.css`. ```ts twoslash -import type { EnhanceAppContext } from 'vitepress' // [!code hl] // @noErrors: true // .vitepress/theme/index.ts +import type { EnhanceAppContext } from 'vitepress' // [!code hl] import TwoslashFloatingVue from '@shikijs/vitepress-twoslash/client' import Theme from 'vitepress/theme' @@ -86,7 +86,7 @@ console.log('hello') // ^? ``` -
+
### Vue Single File Component diff --git a/package.json b/package.json index a57ec65b2..bfb520f47 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "docs": "pnpm -C docs run docs:dev", "docs:build": "pnpm -C docs run docs:build", "report-engine-js": "esno scripts/report-engine-js-compat.ts", - "bench": "vitest bench", + "bench": "pnpm -r run bench:prepare && vitest bench", "prepare": "simple-git-hooks" }, "devDependencies": { @@ -26,12 +26,15 @@ "@rollup/plugin-node-resolve": "catalog:", "@rollup/plugin-replace": "catalog:", "@rollup/plugin-terser": "catalog:", + "@shikijs/colorized-brackets": "workspace:*", "@shikijs/engine-javascript": "workspace:*", "@shikijs/engine-oniguruma": "workspace:*", "@shikijs/markdown-it": "workspace:*", "@shikijs/monaco": "workspace:*", "@shikijs/rehype": "workspace:*", "@shikijs/transformers": "workspace:*", + "@shikijs/twoslash": "workspace:*", + "@shikijs/types": "workspace:*", "@shikijs/vitepress-twoslash": "workspace:*", "@types/fs-extra": "catalog:", "@types/hast": "catalog:", @@ -62,7 +65,6 @@ "rollup-plugin-copy": "catalog:", "rollup-plugin-dts": "catalog:", "rollup-plugin-esbuild": "catalog:", - "rollup-plugin-typescript2": "catalog:", "shiki": "workspace:*", "simple-git-hooks": "catalog:", "taze": "catalog:", @@ -76,6 +78,7 @@ "wrangler": "catalog:" }, "resolutions": { + "@shikijs/colorized-brackets": "workspace:*", "@shikijs/compat": "workspace:*", "@shikijs/core": "workspace:*", "@shikijs/engine-javascript": "workspace:*", @@ -84,6 +87,7 @@ "@shikijs/rehype": "workspace:*", "@shikijs/transformers": "workspace:*", "@shikijs/twoslash": "workspace:*", + "@shikijs/types": "workspace:*", "@shikijs/vitepress-twoslash": "workspace:*", "@types/hast": "catalog:", "@types/mdast": "catalog:", diff --git a/packages/compat/build.config.ts b/packages/compat/build.config.ts index 8d091fd64..da09e2a08 100644 --- a/packages/compat/build.config.ts +++ b/packages/compat/build.config.ts @@ -8,6 +8,7 @@ export default defineBuildConfig({ rollup: { emitCJS: false, dts: { + respectExternal: true, compilerOptions: { paths: {}, }, @@ -16,5 +17,7 @@ export default defineBuildConfig({ externals: [ 'hast', 'shiki', + '@shikijs/types', + /^@shikijs[\\/].*/, ], }) diff --git a/packages/compat/package.json b/packages/compat/package.json index 21b14e06f..eb4e8495b 100644 --- a/packages/compat/package.json +++ b/packages/compat/package.json @@ -38,7 +38,11 @@ }, "dependencies": { "@shikijs/core": "workspace:*", + "@shikijs/langs": "workspace:*", + "@shikijs/themes": "workspace:*", "@shikijs/transformers": "workspace:*", + "@shikijs/types": "workspace:*", + "@shikijs/vscode-textmate": "catalog:", "shiki": "workspace:*" }, "devDependencies": { diff --git a/packages/core/rollup.config.mjs b/packages/core/rollup.config.mjs deleted file mode 100644 index 9e103c4e2..000000000 --- a/packages/core/rollup.config.mjs +++ /dev/null @@ -1,101 +0,0 @@ -// @ts-check -import commonjs from '@rollup/plugin-commonjs' -import json from '@rollup/plugin-json' -import { nodeResolve } from '@rollup/plugin-node-resolve' -import replace from '@rollup/plugin-replace' -import fs from 'fs-extra' -import { defineConfig } from 'rollup' -import dts from 'rollup-plugin-dts' -import ts from 'rollup-plugin-typescript2' - -const entries = [ - 'src/index.ts', - 'src/types.ts', - 'src/wasm-inlined.ts', -] - -const plugins = [ - ts({ - check: false, - }), - replace({ - 'DebugFlags.InDebugMode': 'false', - 'preventAssignment': true, - }), - nodeResolve(), - commonjs(), - json({ - namedExports: false, - preferConst: true, - compact: true, - }), - wasmPlugin(), -] - -const external = [ - 'hast', - '@shikijs/vscode-textmate', - - // Externalize to make it easier to patch and experiment - // Versions are pinned to avoid regressions - 'oniguruma-to-es', -] - -export default defineConfig([ - { - input: entries, - output: { - dir: 'dist', - format: 'esm', - entryFileNames: '[name].mjs', - chunkFileNames: () => { - return 'chunks-[name].mjs' - }, - }, - plugins: [ - ...plugins, - ], - external, - }, - { - input: entries, - output: { - dir: 'dist', - format: 'esm', - chunkFileNames: 'chunk-[name].d.mts', - entryFileNames: f => `${f.name.replace(/src[\\/]/, '')}.d.mts`, - }, - plugins: [ - dts({ - respectExternal: true, - }), - { - name: 'post', - async buildEnd() { - await fs.writeFile('dist/onig.d.mts', 'declare const binary: ArrayBuffer; export default binary;', 'utf-8') - }, - }, - ], - onwarn: (warning, warn) => { - if (!/Circular|an empty chunk/.test(warning.message)) - warn(warning) - }, - external, - }, -]) - -/** - * @returns {import('rollup').Plugin} Plugin - */ -export function wasmPlugin() { - return { - name: 'wasm', - async load(id) { - if (!id.endsWith('.wasm')) - return - const binary = await fs.readFile(id) - const base64 = binary.toString('base64') - return `export default Uint8Array.from(atob(${JSON.stringify(base64)}), c => c.charCodeAt(0))` - }, - } -} diff --git a/packages/engine-javascript/build.config.ts b/packages/engine-javascript/build.config.ts index 6afc6c962..1352d15b0 100644 --- a/packages/engine-javascript/build.config.ts +++ b/packages/engine-javascript/build.config.ts @@ -3,6 +3,8 @@ import { defineBuildConfig } from 'unbuild' export default defineBuildConfig({ entries: [ 'src/index.ts', + 'src/engine-compile.ts', + 'src/engine-raw.ts', ], declaration: true, rollup: { diff --git a/packages/engine-javascript/package.json b/packages/engine-javascript/package.json index 5d5c7107f..74902ed4b 100644 --- a/packages/engine-javascript/package.json +++ b/packages/engine-javascript/package.json @@ -21,6 +21,10 @@ ".": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" + }, + "./raw": { + "types": "./dist/engine-raw.d.mts", + "default": "./dist/engine-raw.mjs" } }, "main": "./dist/index.mjs", diff --git a/packages/engine-javascript/src/engine-compile.ts b/packages/engine-javascript/src/engine-compile.ts new file mode 100644 index 000000000..3fdeb3264 --- /dev/null +++ b/packages/engine-javascript/src/engine-compile.ts @@ -0,0 +1,79 @@ +import type { RegexEngine } from '@shikijs/types' +import type { OnigurumaToEsOptions } from 'oniguruma-to-es' +import type { JavaScriptRegexScannerOptions } from './scanner' +import { toRegExp } from 'oniguruma-to-es' +import { JavaScriptScanner } from './scanner' + +export interface JavaScriptRegexEngineOptions extends JavaScriptRegexScannerOptions { + /** + * The target ECMAScript version. + * + * Oniguruma-To-ES uses RegExp features from later versions of ECMAScript to provide improved + * accuracy and add support for more grammars. If using target `ES2024` or later, the RegExp `v` + * flag is used which requires Node.js 20+ or Chrome 112+. + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicodeSets + * + * For maximum compatibility, you can set it to `ES2018` which uses the RegExp `u` flag but + * supports a few less grammars. + * + * Set to `auto` to automatically detect the latest version supported by the environment. + * + * @default 'auto' + */ + target?: 'auto' | 'ES2025' | 'ES2024' | 'ES2018' +} +/** + * The default RegExp constructor for JavaScript regex engine. + */ + +export function defaultJavaScriptRegexConstructor(pattern: string, options?: OnigurumaToEsOptions): RegExp { + return toRegExp( + pattern, + { + global: true, + hasIndices: true, + rules: { + // Needed since TextMate grammars merge backrefs across patterns + allowOrphanBackrefs: true, + // Improves search performance for generated regexes + asciiWordBoundaries: true, + // Follow `vscode-oniguruma` which enables this Oniguruma option by default + captureGroup: true, + // Removing `\G` anchors in cases when they're not supported for emulation allows + // supporting more grammars, but also allows some mismatches + ignoreUnsupportedGAnchors: true, + }, + ...options, + }, + ) +} +/** + * Use the modern JavaScript RegExp engine to implement the OnigScanner. + * + * As Oniguruma supports some features that can't be emulated using native JavaScript regexes, some + * patterns are not supported. Errors will be thrown when parsing TextMate grammars with + * unsupported patterns, and when the grammar includes patterns that use invalid Oniguruma syntax. + * Set `forgiving` to `true` to ignore these errors and skip any unsupported or invalid patterns. + */ + +export function createJavaScriptRegexEngine(options: JavaScriptRegexEngineOptions = {}): RegexEngine { + const _options: JavaScriptRegexEngineOptions = Object.assign( + { + target: 'auto', + cache: new Map(), + }, + options, + ) + _options.regexConstructor ||= pattern => defaultJavaScriptRegexConstructor(pattern, { target: _options.target }) + + return { + createScanner(patterns) { + return new JavaScriptScanner(patterns, _options) + }, + createString(s: string) { + return { + content: s, + } + }, + } +} diff --git a/packages/engine-javascript/src/engine-raw.ts b/packages/engine-javascript/src/engine-raw.ts new file mode 100644 index 000000000..f0296d962 --- /dev/null +++ b/packages/engine-javascript/src/engine-raw.ts @@ -0,0 +1,30 @@ +import type { RegexEngine } from '@shikijs/types' +import type { JavaScriptRegexScannerOptions } from './scanner' +import { JavaScriptScanner } from './scanner' + +/** + * Raw JavaScript regex engine that only supports precompiled grammars. + * + * This further simplifies the engine by excluding the regex compilation step. + * + * Zero dependencies. + */ +export function createJavaScriptRawEngine(): RegexEngine { + const options: JavaScriptRegexScannerOptions = { + cache: new Map(), + regexConstructor: () => { + throw new Error('JavaScriptRawEngine: only support precompiled grammar') + }, + } + + return { + createScanner(patterns) { + return new JavaScriptScanner(patterns, options) + }, + createString(s: string) { + return { + content: s, + } + }, + } +} diff --git a/packages/engine-javascript/src/index.ts b/packages/engine-javascript/src/index.ts index a837bf4b9..6288d18e2 100644 --- a/packages/engine-javascript/src/index.ts +++ b/packages/engine-javascript/src/index.ts @@ -1,204 +1,3 @@ -import type { - PatternScanner, - RegexEngine, - RegexEngineString, -} from '@shikijs/types' -import type { IOnigMatch } from '@shikijs/vscode-textmate' -import type { OnigurumaToEsOptions } from 'oniguruma-to-es' -import { toRegExp } from 'oniguruma-to-es' - -export interface JavaScriptRegexEngineOptions { - /** - * Whether to allow invalid regex patterns. - * - * @default false - */ - forgiving?: boolean - - /** - * The target ECMAScript version. - * - * Oniguruma-To-ES uses RegExp features from later versions of ECMAScript to provide improved - * accuracy and add support for more grammars. If using target `ES2024` or later, the RegExp `v` - * flag is used which requires Node.js 20+ or Chrome 112+. - * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicodeSets - * - * For maximum compatibility, you can set it to `ES2018` which uses the RegExp `u` flag but - * supports a few less grammars. - * - * Set to `auto` to automatically detect the latest version supported by the environment. - * - * @default 'auto' - */ - target?: 'auto' | 'ES2025' | 'ES2024' | 'ES2018' - - /** - * Cache for regex patterns. - */ - cache?: Map | null - - /** - * Custom pattern to RegExp constructor. - * - * By default `oniguruma-to-es` is used. - */ - regexConstructor?: (pattern: string) => RegExp -} - -const MAX = 4294967295 - -/** - * The default RegExp constructor for JavaScript regex engine. - */ -export function defaultJavaScriptRegexConstructor(pattern: string, options?: OnigurumaToEsOptions): RegExp { - return toRegExp( - pattern, - { - global: true, - hasIndices: true, - rules: { - // Needed since TextMate grammars merge backrefs across patterns - allowOrphanBackrefs: true, - // Improves search performance for generated regexes - asciiWordBoundaries: true, - // Follow `vscode-oniguruma` which enables this Oniguruma option by default - captureGroup: true, - // Removing `\G` anchors in cases when they're not supported for emulation allows - // supporting more grammars, but also allows some mismatches - ignoreUnsupportedGAnchors: true, - }, - ...options, - }, - ) -} - -export class JavaScriptScanner implements PatternScanner { - regexps: (RegExp | null)[] - - constructor( - public patterns: string[], - public options: JavaScriptRegexEngineOptions = {}, - ) { - const { - forgiving = false, - cache, - target = 'auto', - regexConstructor = (pattern: string) => defaultJavaScriptRegexConstructor(pattern, { target }), - } = options - - this.regexps = patterns.map((p) => { - // Cache - const cached = cache?.get(p) - if (cached) { - if (cached instanceof RegExp) { - return cached - } - if (forgiving) - return null - throw cached - } - try { - const regex = regexConstructor(p) - cache?.set(p, regex) - return regex - } - catch (e) { - cache?.set(p, e as Error) - if (forgiving) - return null - // console.error({ ...e }) - throw e - } - }) - } - - findNextMatchSync(string: string | RegexEngineString, startPosition: number, _options: number): IOnigMatch | null { - const str = typeof string === 'string' - ? string - : string.content - const pending: [index: number, match: RegExpExecArray, offset: number][] = [] - - function toResult(index: number, match: RegExpExecArray, offset = 0): IOnigMatch { - return { - index, - captureIndices: match.indices!.map((indice) => { - if (indice == null) { - return { - start: MAX, - end: MAX, - length: 0, - } - } - return { - start: indice[0] + offset, - end: indice[1] + offset, - length: indice[1] - indice[0], - } - }), - } - } - - for (let i = 0; i < this.regexps.length; i++) { - const regexp = this.regexps[i] - if (!regexp) - continue - try { - regexp.lastIndex = startPosition - const match = regexp.exec(str) - - if (!match) - continue - - // If the match is at the start position, return it immediately - if (match.index === startPosition) { - return toResult(i, match, 0) - } - // Otherwise, store it for later - pending.push([i, match, 0]) - } - catch (e) { - if (this.options.forgiving) - continue - throw e - } - } - - // Find the closest match to the start position - if (pending.length) { - const minIndex = Math.min(...pending.map(m => m[1].index)) - for (const [i, match, offset] of pending) { - if (match.index === minIndex) { - return toResult(i, match, offset) - } - } - } - - return null - } -} - -/** - * Use the modern JavaScript RegExp engine to implement the OnigScanner. - * - * As Oniguruma supports some features that can't be emulated using native JavaScript regexes, some - * patterns are not supported. Errors will be thrown when parsing TextMate grammars with - * unsupported patterns, and when the grammar includes patterns that use invalid Oniguruma syntax. - * Set `forgiving` to `true` to ignore these errors and skip any unsupported or invalid patterns. - */ -export function createJavaScriptRegexEngine(options: JavaScriptRegexEngineOptions = {}): RegexEngine { - const _options = { - cache: new Map(), - ...options, - } - - return { - createScanner(patterns: string[]) { - return new JavaScriptScanner(patterns, _options) - }, - createString(s: string) { - return { - content: s, - } - }, - } -} +export * from './engine-compile' +export * from './engine-raw' +export * from './scanner' diff --git a/packages/engine-javascript/src/scanner.ts b/packages/engine-javascript/src/scanner.ts new file mode 100644 index 000000000..ad5b58290 --- /dev/null +++ b/packages/engine-javascript/src/scanner.ts @@ -0,0 +1,139 @@ +import type { + PatternScanner, + RegexEngineString, +} from '@shikijs/types' +import type { IOnigMatch } from '@shikijs/vscode-textmate' + +const MAX = 4294967295 + +export interface JavaScriptRegexScannerOptions { + /** + * Whether to allow invalid regex patterns. + * + * @default false + */ + forgiving?: boolean + + /** + * Cache for regex patterns. + */ + cache?: Map | null + + /** + * Custom pattern to RegExp constructor. + * + * By default `oniguruma-to-es` is used. + */ + regexConstructor?: (pattern: string) => RegExp +} + +export class JavaScriptScanner implements PatternScanner { + regexps: (RegExp | null)[] + + constructor( + public patterns: (string | RegExp)[], + public options: JavaScriptRegexScannerOptions = {}, + ) { + const { + forgiving = false, + cache, + regexConstructor, + } = options + + if (!regexConstructor) { + throw new Error('Option `regexConstructor` is not provided') + } + + this.regexps = patterns.map((p) => { + if (typeof p !== 'string') { + return p + } + // Cache + const cached = cache?.get(p) + if (cached) { + if (cached instanceof RegExp) { + return cached + } + if (forgiving) + return null + throw cached + } + try { + const regex = regexConstructor(p) + cache?.set(p, regex) + return regex + } + catch (e) { + cache?.set(p, e as Error) + if (forgiving) + return null + // console.error({ ...e }) + throw e + } + }) + } + + findNextMatchSync(string: string | RegexEngineString, startPosition: number, _options: number): IOnigMatch | null { + const str = typeof string === 'string' + ? string + : string.content + const pending: [index: number, match: RegExpExecArray, offset: number][] = [] + + function toResult(index: number, match: RegExpExecArray, offset = 0): IOnigMatch { + return { + index, + captureIndices: match.indices!.map((indice) => { + if (indice == null) { + return { + start: MAX, + end: MAX, + length: 0, + } + } + return { + start: indice[0] + offset, + end: indice[1] + offset, + length: indice[1] - indice[0], + } + }), + } + } + + for (let i = 0; i < this.regexps.length; i++) { + const regexp = this.regexps[i] + if (!regexp) + continue + try { + regexp.lastIndex = startPosition + const match = regexp.exec(str) + + if (!match) + continue + + // If the match is at the start position, return it immediately + if (match.index === startPosition) { + return toResult(i, match, 0) + } + // Otherwise, store it for later + pending.push([i, match, 0]) + } + catch (e) { + if (this.options.forgiving) + continue + throw e + } + } + + // Find the closest match to the start position + if (pending.length) { + const minIndex = Math.min(...pending.map(m => m[1].index)) + for (const [i, match, offset] of pending) { + if (match.index === minIndex) { + return toResult(i, match, offset) + } + } + } + + return null + } +} diff --git a/packages/engine-javascript/test/compare.test.ts b/packages/engine-javascript/test/compare.test.ts index b7ae6e739..1a5748b85 100644 --- a/packages/engine-javascript/test/compare.test.ts +++ b/packages/engine-javascript/test/compare.test.ts @@ -6,7 +6,7 @@ import { hash as createHash } from 'ohash' import { describe, expect, it } from 'vitest' import { createWasmOnigEngine, loadWasm } from '../../engine-oniguruma/src' import { createHighlighterCore } from '../../shiki/src/core' -import { createJavaScriptRegexEngine } from '../src' +import { createJavaScriptRegexEngine } from '../src/engine-compile' function createEngineWrapper(engine: RegexEngine): RegexEngine & { executions: Execution[] } { const executions: Execution[] = [] diff --git a/packages/engine-javascript/test/general.test.ts b/packages/engine-javascript/test/general.test.ts index 7fbe236c2..c9df17a58 100644 --- a/packages/engine-javascript/test/general.test.ts +++ b/packages/engine-javascript/test/general.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' import { createHighlighter } from '../../shiki/src/index' -import { createJavaScriptRegexEngine } from '../src' +import { createJavaScriptRegexEngine } from '../src/engine-compile' describe('should', () => { it('works', async () => { diff --git a/packages/engine-javascript/test/raw.test.ts b/packages/engine-javascript/test/raw.test.ts new file mode 100644 index 000000000..ecc3f8e5c --- /dev/null +++ b/packages/engine-javascript/test/raw.test.ts @@ -0,0 +1,23 @@ +import { createHighlighterCore } from '@shikijs/core' +import { expect, it } from 'vitest' +import { createJavaScriptRawEngine } from '../src/engine-raw' + +// Only run this test on Node.js 20+ +it.runIf( + process.version.replace(/^v/, '').split('.').map(Number)[0] >= 20, +)('work with precompile grammar', async () => { + const shiki = await createHighlighterCore({ + themes: [ + import('@shikijs/themes/vitesse-light'), + ], + langs: [ + import('@shikijs/langs-precompiled/js'), + ], + engine: createJavaScriptRawEngine(), + }) + + expect( + shiki.codeToHtml('console.log', { lang: 'js', theme: 'vitesse-light' }), + ) + .toMatchInlineSnapshot(`"
console.log
"`) +}) diff --git a/packages/engine-javascript/test/types.ts b/packages/engine-javascript/test/types.ts index db10d0cde..8bd52be33 100644 --- a/packages/engine-javascript/test/types.ts +++ b/packages/engine-javascript/test/types.ts @@ -2,7 +2,7 @@ import type { IOnigMatch } from '@shikijs/vscode-textmate' export interface Execution { id: string - patterns: string[] + patterns: (string | RegExp)[] args: [str: string, start: number, options: number] result: IOnigMatch | null } diff --git a/packages/engine-javascript/test/verify.test.ts b/packages/engine-javascript/test/verify.test.ts index dd59ab409..5041a19a9 100644 --- a/packages/engine-javascript/test/verify.test.ts +++ b/packages/engine-javascript/test/verify.test.ts @@ -5,6 +5,7 @@ import { fileURLToPath } from 'node:url' import fg from 'fast-glob' import { describe, expect, it, onTestFailed } from 'vitest' import { JavaScriptScanner } from '../src' +import { defaultJavaScriptRegexConstructor } from '../src/engine-compile' describe('verify', async () => { const files = await fg('*.wasm.json', { @@ -29,7 +30,10 @@ describe('verify', async () => { i += 1 it(`case ${i}`, () => { - const scanner = new JavaScriptScanner(execution.patterns, { cache }) + const scanner = new JavaScriptScanner(execution.patterns, { + cache, + regexConstructor: pattern => defaultJavaScriptRegexConstructor(pattern), + }) onTestFailed(() => { console.error(execution.result?.index != null diff --git a/packages/engine-oniguruma/rollup.config.mjs b/packages/engine-oniguruma/rollup.config.mjs index ed8280d7b..fd208c7da 100644 --- a/packages/engine-oniguruma/rollup.config.mjs +++ b/packages/engine-oniguruma/rollup.config.mjs @@ -5,7 +5,7 @@ import { nodeResolve } from '@rollup/plugin-node-resolve' import fs from 'fs-extra' import { defineConfig } from 'rollup' import dts from 'rollup-plugin-dts' -import ts from 'rollup-plugin-typescript2' +import esbuild from 'rollup-plugin-esbuild' const entries = [ 'src/index.ts', @@ -13,9 +13,7 @@ const entries = [ ] const plugins = [ - ts({ - check: false, - }), + esbuild(), nodeResolve(), commonjs(), json({ diff --git a/packages/engine-oniguruma/src/index.ts b/packages/engine-oniguruma/src/index.ts index e43b7f133..180c05f86 100644 --- a/packages/engine-oniguruma/src/index.ts +++ b/packages/engine-oniguruma/src/index.ts @@ -27,7 +27,7 @@ export async function createOnigurumaEngine(options?: LoadWasmOptions | null): P return { createScanner(patterns) { - return new OnigScanner(patterns) + return new OnigScanner(patterns.map(p => typeof p === 'string' ? p : p.source)) }, createString(s) { return new OnigString(s) diff --git a/packages/langs-precompiled/README.md b/packages/langs-precompiled/README.md new file mode 100644 index 000000000..ccc5b5583 --- /dev/null +++ b/packages/langs-precompiled/README.md @@ -0,0 +1,15 @@ +# @shikijs/langs-precompiled + +Precompiled languages for Shiki that can runs in pure JavaScript environment. + +Requires ES2024+ environment. + +> **Experimental** + +## Unsupported Languages + + + +## License + +MIT diff --git a/packages/langs-precompiled/package.json b/packages/langs-precompiled/package.json new file mode 100644 index 000000000..b8e5498e2 --- /dev/null +++ b/packages/langs-precompiled/package.json @@ -0,0 +1,360 @@ +{ + "name": "@shikijs/langs-precompiled", + "type": "module", + "version": "1.24.4", + "description": "TextMate grammars for Shiki in ESM", + "author": "Anthony Fu ", + "license": "MIT", + "homepage": "https://github.com/shikijs/shiki#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/shikijs/shiki.git", + "directory": "packages/langs" + }, + "bugs": "https://github.com/shikijs/shiki/issues", + "keywords": [ + "shiki", + "textmate-grammars" + ], + "sideEffects": false, + "exports": { + ".": "./dist/index.mjs", + "./abap": "./dist/abap.mjs", + "./actionscript-3": "./dist/actionscript-3.mjs", + "./ada": "./dist/ada.mjs", + "./angular-expression": "./dist/angular-expression.mjs", + "./angular-html": "./dist/angular-html.mjs", + "./angular-inline-style": "./dist/angular-inline-style.mjs", + "./angular-inline-template": "./dist/angular-inline-template.mjs", + "./angular-let-declaration": "./dist/angular-let-declaration.mjs", + "./angular-template-blocks": "./dist/angular-template-blocks.mjs", + "./angular-template": "./dist/angular-template.mjs", + "./angular-ts": "./dist/angular-ts.mjs", + "./apache": "./dist/apache.mjs", + "./apex": "./dist/apex.mjs", + "./apl": "./dist/apl.mjs", + "./applescript": "./dist/applescript.mjs", + "./ara": "./dist/ara.mjs", + "./adoc": "./dist/adoc.mjs", + "./asciidoc": "./dist/asciidoc.mjs", + "./asm": "./dist/asm.mjs", + "./astro": "./dist/astro.mjs", + "./awk": "./dist/awk.mjs", + "./ballerina": "./dist/ballerina.mjs", + "./batch": "./dist/batch.mjs", + "./bat": "./dist/bat.mjs", + "./beancount": "./dist/beancount.mjs", + "./be": "./dist/be.mjs", + "./berry": "./dist/berry.mjs", + "./bibtex": "./dist/bibtex.mjs", + "./bicep": "./dist/bicep.mjs", + "./blade": "./dist/blade.mjs", + "./1c": "./dist/1c.mjs", + "./bsl": "./dist/bsl.mjs", + "./c": "./dist/c.mjs", + "./cdc": "./dist/cdc.mjs", + "./cadence": "./dist/cadence.mjs", + "./cairo": "./dist/cairo.mjs", + "./clarity": "./dist/clarity.mjs", + "./clj": "./dist/clj.mjs", + "./clojure": "./dist/clojure.mjs", + "./cmake": "./dist/cmake.mjs", + "./cobol": "./dist/cobol.mjs", + "./codeowners": "./dist/codeowners.mjs", + "./ql": "./dist/ql.mjs", + "./codeql": "./dist/codeql.mjs", + "./coffeescript": "./dist/coffeescript.mjs", + "./coffee": "./dist/coffee.mjs", + "./lisp": "./dist/lisp.mjs", + "./common-lisp": "./dist/common-lisp.mjs", + "./coq": "./dist/coq.mjs", + "./cpp-macro": "./dist/cpp-macro.mjs", + "./cpp": "./dist/cpp.mjs", + "./crystal": "./dist/crystal.mjs", + "./cs": "./dist/cs.mjs", + "./csharp": "./dist/csharp.mjs", + "./css": "./dist/css.mjs", + "./csv": "./dist/csv.mjs", + "./cue": "./dist/cue.mjs", + "./cql": "./dist/cql.mjs", + "./cypher": "./dist/cypher.mjs", + "./d": "./dist/d.mjs", + "./dart": "./dist/dart.mjs", + "./dax": "./dist/dax.mjs", + "./desktop": "./dist/desktop.mjs", + "./diff": "./dist/diff.mjs", + "./dockerfile": "./dist/dockerfile.mjs", + "./docker": "./dist/docker.mjs", + "./dotenv": "./dist/dotenv.mjs", + "./dream-maker": "./dist/dream-maker.mjs", + "./edge": "./dist/edge.mjs", + "./elixir": "./dist/elixir.mjs", + "./elm": "./dist/elm.mjs", + "./elisp": "./dist/elisp.mjs", + "./emacs-lisp": "./dist/emacs-lisp.mjs", + "./erb": "./dist/erb.mjs", + "./erl": "./dist/erl.mjs", + "./erlang": "./dist/erlang.mjs", + "./es-tag-css": "./dist/es-tag-css.mjs", + "./es-tag-glsl": "./dist/es-tag-glsl.mjs", + "./es-tag-html": "./dist/es-tag-html.mjs", + "./es-tag-sql": "./dist/es-tag-sql.mjs", + "./es-tag-xml": "./dist/es-tag-xml.mjs", + "./fennel": "./dist/fennel.mjs", + "./fish": "./dist/fish.mjs", + "./ftl": "./dist/ftl.mjs", + "./fluent": "./dist/fluent.mjs", + "./f": "./dist/f.mjs", + "./for": "./dist/for.mjs", + "./f77": "./dist/f77.mjs", + "./fortran-fixed-form": "./dist/fortran-fixed-form.mjs", + "./f90": "./dist/f90.mjs", + "./f95": "./dist/f95.mjs", + "./f03": "./dist/f03.mjs", + "./f08": "./dist/f08.mjs", + "./f18": "./dist/f18.mjs", + "./fortran-free-form": "./dist/fortran-free-form.mjs", + "./fs": "./dist/fs.mjs", + "./fsharp": "./dist/fsharp.mjs", + "./gdresource": "./dist/gdresource.mjs", + "./gdscript": "./dist/gdscript.mjs", + "./gdshader": "./dist/gdshader.mjs", + "./genie": "./dist/genie.mjs", + "./gherkin": "./dist/gherkin.mjs", + "./git-commit": "./dist/git-commit.mjs", + "./git-rebase": "./dist/git-rebase.mjs", + "./gleam": "./dist/gleam.mjs", + "./gjs": "./dist/gjs.mjs", + "./glimmer-js": "./dist/glimmer-js.mjs", + "./gts": "./dist/gts.mjs", + "./glimmer-ts": "./dist/glimmer-ts.mjs", + "./glsl": "./dist/glsl.mjs", + "./gnuplot": "./dist/gnuplot.mjs", + "./go": "./dist/go.mjs", + "./gql": "./dist/gql.mjs", + "./graphql": "./dist/graphql.mjs", + "./groovy": "./dist/groovy.mjs", + "./hack": "./dist/hack.mjs", + "./haml": "./dist/haml.mjs", + "./hbs": "./dist/hbs.mjs", + "./handlebars": "./dist/handlebars.mjs", + "./hs": "./dist/hs.mjs", + "./haskell": "./dist/haskell.mjs", + "./haxe": "./dist/haxe.mjs", + "./hcl": "./dist/hcl.mjs", + "./hjson": "./dist/hjson.mjs", + "./hlsl": "./dist/hlsl.mjs", + "./html-derivative": "./dist/html-derivative.mjs", + "./html": "./dist/html.mjs", + "./http": "./dist/http.mjs", + "./hxml": "./dist/hxml.mjs", + "./hy": "./dist/hy.mjs", + "./imba": "./dist/imba.mjs", + "./properties": "./dist/properties.mjs", + "./ini": "./dist/ini.mjs", + "./java": "./dist/java.mjs", + "./js": "./dist/js.mjs", + "./javascript": "./dist/javascript.mjs", + "./jinja-html": "./dist/jinja-html.mjs", + "./jinja": "./dist/jinja.mjs", + "./jison": "./dist/jison.mjs", + "./json": "./dist/json.mjs", + "./json5": "./dist/json5.mjs", + "./jsonc": "./dist/jsonc.mjs", + "./jsonl": "./dist/jsonl.mjs", + "./jsonnet": "./dist/jsonnet.mjs", + "./fsl": "./dist/fsl.mjs", + "./jssm": "./dist/jssm.mjs", + "./jsx": "./dist/jsx.mjs", + "./jl": "./dist/jl.mjs", + "./julia": "./dist/julia.mjs", + "./kt": "./dist/kt.mjs", + "./kts": "./dist/kts.mjs", + "./kotlin": "./dist/kotlin.mjs", + "./kql": "./dist/kql.mjs", + "./kusto": "./dist/kusto.mjs", + "./latex": "./dist/latex.mjs", + "./lean4": "./dist/lean4.mjs", + "./lean": "./dist/lean.mjs", + "./less": "./dist/less.mjs", + "./liquid": "./dist/liquid.mjs", + "./log": "./dist/log.mjs", + "./logo": "./dist/logo.mjs", + "./lua": "./dist/lua.mjs", + "./luau": "./dist/luau.mjs", + "./makefile": "./dist/makefile.mjs", + "./make": "./dist/make.mjs", + "./markdown-vue": "./dist/markdown-vue.mjs", + "./md": "./dist/md.mjs", + "./markdown": "./dist/markdown.mjs", + "./marko": "./dist/marko.mjs", + "./matlab": "./dist/matlab.mjs", + "./mdc": "./dist/mdc.mjs", + "./mdx": "./dist/mdx.mjs", + "./mmd": "./dist/mmd.mjs", + "./mermaid": "./dist/mermaid.mjs", + "./mips": "./dist/mips.mjs", + "./mipsasm": "./dist/mipsasm.mjs", + "./mojo": "./dist/mojo.mjs", + "./move": "./dist/move.mjs", + "./nar": "./dist/nar.mjs", + "./narrat": "./dist/narrat.mjs", + "./nf": "./dist/nf.mjs", + "./nextflow": "./dist/nextflow.mjs", + "./nginx": "./dist/nginx.mjs", + "./nim": "./dist/nim.mjs", + "./nix": "./dist/nix.mjs", + "./nu": "./dist/nu.mjs", + "./nushell": "./dist/nushell.mjs", + "./objc": "./dist/objc.mjs", + "./objective-c": "./dist/objective-c.mjs", + "./objective-cpp": "./dist/objective-cpp.mjs", + "./ocaml": "./dist/ocaml.mjs", + "./pascal": "./dist/pascal.mjs", + "./perl": "./dist/perl.mjs", + "./php": "./dist/php.mjs", + "./plsql": "./dist/plsql.mjs", + "./pot": "./dist/pot.mjs", + "./potx": "./dist/potx.mjs", + "./po": "./dist/po.mjs", + "./polar": "./dist/polar.mjs", + "./postcss": "./dist/postcss.mjs", + "./powerquery": "./dist/powerquery.mjs", + "./ps": "./dist/ps.mjs", + "./ps1": "./dist/ps1.mjs", + "./powershell": "./dist/powershell.mjs", + "./prisma": "./dist/prisma.mjs", + "./prolog": "./dist/prolog.mjs", + "./protobuf": "./dist/protobuf.mjs", + "./proto": "./dist/proto.mjs", + "./jade": "./dist/jade.mjs", + "./pug": "./dist/pug.mjs", + "./puppet": "./dist/puppet.mjs", + "./purescript": "./dist/purescript.mjs", + "./py": "./dist/py.mjs", + "./python": "./dist/python.mjs", + "./qml": "./dist/qml.mjs", + "./qmldir": "./dist/qmldir.mjs", + "./qss": "./dist/qss.mjs", + "./r": "./dist/r.mjs", + "./racket": "./dist/racket.mjs", + "./perl6": "./dist/perl6.mjs", + "./raku": "./dist/raku.mjs", + "./razor": "./dist/razor.mjs", + "./reg": "./dist/reg.mjs", + "./regex": "./dist/regex.mjs", + "./regexp": "./dist/regexp.mjs", + "./rel": "./dist/rel.mjs", + "./riscv": "./dist/riscv.mjs", + "./rst": "./dist/rst.mjs", + "./rb": "./dist/rb.mjs", + "./ruby": "./dist/ruby.mjs", + "./rs": "./dist/rs.mjs", + "./rust": "./dist/rust.mjs", + "./sas": "./dist/sas.mjs", + "./sass": "./dist/sass.mjs", + "./scala": "./dist/scala.mjs", + "./scheme": "./dist/scheme.mjs", + "./scss": "./dist/scss.mjs", + "./1c-query": "./dist/1c-query.mjs", + "./sdbl": "./dist/sdbl.mjs", + "./shader": "./dist/shader.mjs", + "./shaderlab": "./dist/shaderlab.mjs", + "./bash": "./dist/bash.mjs", + "./sh": "./dist/sh.mjs", + "./shell": "./dist/shell.mjs", + "./zsh": "./dist/zsh.mjs", + "./shellscript": "./dist/shellscript.mjs", + "./console": "./dist/console.mjs", + "./shellsession": "./dist/shellsession.mjs", + "./smalltalk": "./dist/smalltalk.mjs", + "./solidity": "./dist/solidity.mjs", + "./closure-templates": "./dist/closure-templates.mjs", + "./soy": "./dist/soy.mjs", + "./sparql": "./dist/sparql.mjs", + "./spl": "./dist/spl.mjs", + "./splunk": "./dist/splunk.mjs", + "./sql": "./dist/sql.mjs", + "./ssh-config": "./dist/ssh-config.mjs", + "./stata": "./dist/stata.mjs", + "./styl": "./dist/styl.mjs", + "./stylus": "./dist/stylus.mjs", + "./svelte": "./dist/svelte.mjs", + "./swift": "./dist/swift.mjs", + "./system-verilog": "./dist/system-verilog.mjs", + "./systemd": "./dist/systemd.mjs", + "./talon": "./dist/talon.mjs", + "./talonscript": "./dist/talonscript.mjs", + "./tasl": "./dist/tasl.mjs", + "./tcl": "./dist/tcl.mjs", + "./templ": "./dist/templ.mjs", + "./tf": "./dist/tf.mjs", + "./tfvars": "./dist/tfvars.mjs", + "./terraform": "./dist/terraform.mjs", + "./tex": "./dist/tex.mjs", + "./toml": "./dist/toml.mjs", + "./lit": "./dist/lit.mjs", + "./ts-tags": "./dist/ts-tags.mjs", + "./tsv": "./dist/tsv.mjs", + "./tsx": "./dist/tsx.mjs", + "./turtle": "./dist/turtle.mjs", + "./twig": "./dist/twig.mjs", + "./ts": "./dist/ts.mjs", + "./typescript": "./dist/typescript.mjs", + "./tsp": "./dist/tsp.mjs", + "./typespec": "./dist/typespec.mjs", + "./typ": "./dist/typ.mjs", + "./typst": "./dist/typst.mjs", + "./v": "./dist/v.mjs", + "./vala": "./dist/vala.mjs", + "./cmd": "./dist/cmd.mjs", + "./vb": "./dist/vb.mjs", + "./verilog": "./dist/verilog.mjs", + "./vhdl": "./dist/vhdl.mjs", + "./vim": "./dist/vim.mjs", + "./vimscript": "./dist/vimscript.mjs", + "./viml": "./dist/viml.mjs", + "./vue-directives": "./dist/vue-directives.mjs", + "./vue-html": "./dist/vue-html.mjs", + "./vue-interpolations": "./dist/vue-interpolations.mjs", + "./vue-sfc-style-variable-injection": "./dist/vue-sfc-style-variable-injection.mjs", + "./vue": "./dist/vue.mjs", + "./vy": "./dist/vy.mjs", + "./vyper": "./dist/vyper.mjs", + "./wasm": "./dist/wasm.mjs", + "./wenyan": "./dist/wenyan.mjs", + "./wgsl": "./dist/wgsl.mjs", + "./mediawiki": "./dist/mediawiki.mjs", + "./wiki": "./dist/wiki.mjs", + "./wikitext": "./dist/wikitext.mjs", + "./wl": "./dist/wl.mjs", + "./wolfram": "./dist/wolfram.mjs", + "./xml": "./dist/xml.mjs", + "./xsl": "./dist/xsl.mjs", + "./yml": "./dist/yml.mjs", + "./yaml": "./dist/yaml.mjs", + "./zenscript": "./dist/zenscript.mjs", + "./zig": "./dist/zig.mjs" + }, + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "files": [ + "dist" + ], + "engines": { + "node": ">=20" + }, + "scripts": { + "prepare": "esno scripts/prepare.ts", + "build": "pnpm prepare", + "prepublishOnly": "nr prepare" + }, + "dependencies": { + "@shikijs/types": "workspace:*", + "oniguruma-to-es": "catalog:" + }, + "devDependencies": { + "tm-grammars": "catalog:" + } +} diff --git a/packages/langs-precompiled/scripts/__snapshots__/precompile.test.ts.snap b/packages/langs-precompiled/scripts/__snapshots__/precompile.test.ts.snap new file mode 100644 index 000000000..cc4f42d0e --- /dev/null +++ b/packages/langs-precompiled/scripts/__snapshots__/precompile.test.ts.snap @@ -0,0 +1,404 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`precompile 1`] = ` +"export default { + displayName: "YAML", + fileTypes: [ + "yaml", + "yml", + "rviz", + "reek", + "clang-format", + "yaml-tmlanguage", + "syntax", + "sublime-syntax", + ], + firstLineMatch: /(?<=^|\\n(?!$))%YAML( ?1[^\\n]\\p{Nd}+)?/dgv, + name: "yaml", + patterns: [ + { include: "#comment" }, + { include: "#property" }, + { include: "#directive" }, + { match: /(?<=^|\\n(?!$))---/dgv, name: "entity.other.document.begin.yaml" }, + { match: /(?<=^|\\n(?!$))\\.{3}/dgv, name: "entity.other.document.end.yaml" }, + { include: "#node" }, + ], + repository: { + "block-collection": { + patterns: [{ include: "#block-sequence" }, { include: "#block-mapping" }], + }, + "block-mapping": { patterns: [{ include: "#block-pair" }] }, + "block-node": { + patterns: [ + { include: "#prototype" }, + { include: "#block-scalar" }, + { include: "#block-collection" }, + { include: "#flow-scalar-plain-out" }, + { include: "#flow-node" }, + ], + }, + "block-pair": { + patterns: [ + { + begin: /\\?/dgv, + beginCaptures: { + "1": { name: "punctuation.definition.key-value.begin.yaml" }, + }, + end: /(?=\\?)|(?<=^|\\n(?!$)) *(:)|(:)/dgv, + endCaptures: { + "1": { name: "punctuation.separator.key-value.mapping.yaml" }, + "2": { name: "invalid.illegal.expected-newline.yaml" }, + }, + name: "meta.block-mapping.yaml", + patterns: [{ include: "#block-node" }], + }, + { + begin: + /(?=(?:[^\\p{space}\\-\\?\\:\\,\\[\\]\\{\\}\\#\\&\\*\\!\\|\\>'"\\%\\@\\\`]|[\\?\\:\\-]\\P{space})([^\\p{space}\\:]|:\\P{space}|\\p{space}+(?![\\#\\p{space}]))*\\p{space}*:(\\p{space}|(?=$|\\n)))/dgv, + end: /(?=\\p{space}*(?=$|\\n)|\\p{space}+#|\\p{space}*:(\\p{space}|(?=$|\\n)))/dgv, + patterns: [ + { include: "#flow-scalar-plain-out-implicit-type" }, + { + begin: + /[^\\p{space}\\-\\?\\:\\,\\[\\]\\{\\}\\#\\&\\*\\!\\|\\>'"\\%\\@\\\`]|[\\?\\:\\-]\\P{space}/dgv, + beginCaptures: { "0": { name: "entity.name.tag.yaml" } }, + contentName: "entity.name.tag.yaml", + end: /(?=\\p{space}*(?=$|\\n)|\\p{space}+#|\\p{space}*:(\\p{space}|(?=$|\\n)))/dgv, + name: "string.unquoted.plain.out.yaml", + }, + ], + }, + { + match: /:(?=\\p{space}|(?=$|\\n))/dgv, + name: "punctuation.separator.key-value.mapping.yaml", + }, + ], + }, + "block-scalar": { + begin: /(?:(\\|)|(>))([1-9])?([\\-\\+])?([^\\n]*\\n?)/dgv, + beginCaptures: { + "1": { name: "keyword.control.flow.block-scalar.literal.yaml" }, + "2": { name: "keyword.control.flow.block-scalar.folded.yaml" }, + "3": { name: "constant.numeric.indentation-indicator.yaml" }, + "4": { name: "storage.modifier.chomping-indicator.yaml" }, + "5": { + patterns: [ + { include: "#comment" }, + { + match: /[^\\n]+/dgv, + name: "invalid.illegal.expected-comment-or-newline.yaml", + }, + ], + }, + }, + end: /(?<=^|\\n(?!$))(?=\\P{space})|(?!)/dgv, + patterns: [ + { + begin: /(?<=^|\\n(?!$))([ ]+)(?! )/dgv, + end: /(?<=^|\\n(?!$))(?!\\1|\\p{space}*(?=$|\\n))()/dgv, + name: "string.unquoted.block.yaml", + }, + ], + }, + "block-sequence": { + match: /(-)(?!\\P{space})/dgv, + name: "punctuation.definition.block.sequence.item.yaml", + }, + comment: { + begin: + /(?:((?<=^|\\n(?!$))[ \\t]*)|[ \\t]+)(?=#[[\\P{space}&&\\P{cntrl}&&\\P{Cn}&&\\P{Cs}]\\p{Zs}]*(?=$|\\n))/dgv, + beginCaptures: { + "1": { name: "punctuation.whitespace.comment.leading.yaml" }, + }, + end: new EmulatedRegExp("", "dgv", { strategy: "not_search_start" }), + patterns: [ + { + begin: /#/dgv, + beginCaptures: { + "0": { name: "punctuation.definition.comment.yaml" }, + }, + end: /\\n/dgv, + name: "comment.line.number-sign.yaml", + }, + ], + }, + directive: { + begin: /(?<=^|\\n(?!$))%/dgv, + beginCaptures: { + "0": { name: "punctuation.definition.directive.begin.yaml" }, + }, + end: /(?=(?=$|\\n)|[ \\t]+((?=$|\\n)|#))/dgv, + name: "meta.directive.yaml", + patterns: [ + { + captures: { + "1": { name: "keyword.other.directive.yaml.yaml" }, + "2": { name: "constant.numeric.yaml-version.yaml" }, + }, + match: /(YAML)[ \\t]+(\\p{Nd}+\\.\\p{Nd}+)/dgvy, + }, + { + captures: { + "1": { name: "keyword.other.directive.tag.yaml" }, + "2": { name: "storage.type.tag-handle.yaml" }, + "3": { name: "support.type.tag-prefix.yaml" }, + }, + match: + /(TAG)(?:[ \\t]+(!(?:[0-9A-Za-z\\-]*!)?)(?:[ \\t]+(!(?:%[0-9A-Fa-f]{2}|[0-9A-Za-z\\-\\#\\;\\/\\?\\:\\@\\&\\=\\+\\$\\,_\\.\\!\\~\\*'\\(\\)\\[\\]])*|(?![\\,\\!\\[\\]\\{\\}])(?:%[0-9A-Fa-f]{2}|[0-9A-Za-z\\-\\#\\;\\/\\?\\:\\@\\&\\=\\+\\$\\,_\\.\\!\\~\\*'\\(\\)\\[\\]])+))?)?/dgvy, + }, + { + captures: { + "1": { name: "support.other.directive.reserved.yaml" }, + "2": { name: "string.unquoted.directive-name.yaml" }, + "3": { name: "string.unquoted.directive-parameter.yaml" }, + }, + match: + /([\\p{L}\\p{M}\\p{N}\\p{Pc}]+)(?:[ \\t]+([\\p{L}\\p{M}\\p{N}\\p{Pc}]+)(?:[ \\t]+([\\p{L}\\p{M}\\p{N}\\p{Pc}]+))?)?/dgvy, + }, + { match: /\\P{space}+/dgv, name: "invalid.illegal.unrecognized.yaml" }, + ], + }, + "flow-alias": { + captures: { + "1": { name: "keyword.control.flow.alias.yaml" }, + "2": { name: "punctuation.definition.alias.yaml" }, + "3": { name: "variable.other.alias.yaml" }, + "4": { name: "invalid.illegal.character.anchor.yaml" }, + }, + match: + /((\\*))([^\\p{space}\\[\\]\\/\\{\\/\\}\\,]+)([^\\p{space}\\]\\}\\,]\\P{space}*)?/dgv, + }, + "flow-collection": { + patterns: [{ include: "#flow-sequence" }, { include: "#flow-mapping" }], + }, + "flow-mapping": { + begin: /\\{/dgv, + beginCaptures: { + "0": { name: "punctuation.definition.mapping.begin.yaml" }, + }, + end: /\\}/dgv, + endCaptures: { "0": { name: "punctuation.definition.mapping.end.yaml" } }, + name: "meta.flow-mapping.yaml", + patterns: [ + { include: "#prototype" }, + { match: /,/dgv, name: "punctuation.separator.mapping.yaml" }, + { include: "#flow-pair" }, + ], + }, + "flow-node": { + patterns: [ + { include: "#prototype" }, + { include: "#flow-alias" }, + { include: "#flow-collection" }, + { include: "#flow-scalar" }, + ], + }, + "flow-pair": { + patterns: [ + { + begin: /\\?/dgv, + beginCaptures: { + "0": { name: "punctuation.definition.key-value.begin.yaml" }, + }, + end: /(?=[\\}\\,\\]])/dgv, + name: "meta.flow-pair.explicit.yaml", + patterns: [ + { include: "#prototype" }, + { include: "#flow-pair" }, + { include: "#flow-node" }, + { + begin: /:(?=\\p{space}|(?=$|\\n)|[\\[\\]\\{\\}\\,])/dgv, + beginCaptures: { + "0": { name: "punctuation.separator.key-value.mapping.yaml" }, + }, + end: /(?=[\\}\\,\\]])/dgv, + patterns: [{ include: "#flow-value" }], + }, + ], + }, + { + begin: + /(?=(?:[^\\p{space}\\-\\?\\:\\,\\[\\]\\{\\}\\#\\&\\*\\!\\|\\>'"\\%\\@\\\`]|[\\?\\:\\-][^\\p{space}\\[\\]\\{\\}\\,])([^\\p{space}\\:\\[\\]\\{\\}\\,]|:[^\\p{space}\\[\\]\\{\\}\\,]|\\p{space}+(?![\\#\\p{space}]))*\\p{space}*:(\\p{space}|(?=$|\\n)))/dgv, + end: /(?=\\p{space}*(?=$|\\n)|\\p{space}+#|\\p{space}*:(\\p{space}|(?=$|\\n))|\\p{space}*:[\\[\\]\\{\\}\\,]|\\p{space}*[\\[\\]\\{\\}\\,])/dgv, + name: "meta.flow-pair.key.yaml", + patterns: [ + { include: "#flow-scalar-plain-in-implicit-type" }, + { + begin: + /[^\\p{space}\\-\\?\\:\\,\\[\\]\\{\\}\\#\\&\\*\\!\\|\\>'"\\%\\@\\\`]|[\\?\\:\\-][^\\p{space}\\[\\]\\{\\}\\,]/dgv, + beginCaptures: { "0": { name: "entity.name.tag.yaml" } }, + contentName: "entity.name.tag.yaml", + end: /(?=\\p{space}*(?=$|\\n)|\\p{space}+#|\\p{space}*:(\\p{space}|(?=$|\\n))|\\p{space}*:[\\[\\]\\{\\}\\,]|\\p{space}*[\\[\\]\\{\\}\\,])/dgv, + name: "string.unquoted.plain.in.yaml", + }, + ], + }, + { include: "#flow-node" }, + { + begin: /:(?=\\p{space}|(?=$|\\n)|[\\[\\]\\{\\}\\,])/dgv, + captures: { + "0": { name: "punctuation.separator.key-value.mapping.yaml" }, + }, + end: /(?=[\\}\\,\\]])/dgv, + name: "meta.flow-pair.yaml", + patterns: [{ include: "#flow-value" }], + }, + ], + }, + "flow-scalar": { + patterns: [ + { include: "#flow-scalar-double-quoted" }, + { include: "#flow-scalar-single-quoted" }, + { include: "#flow-scalar-plain-in" }, + ], + }, + "flow-scalar-double-quoted": { + begin: /"/dgv, + beginCaptures: { + "0": { name: "punctuation.definition.string.begin.yaml" }, + }, + end: /"/dgv, + endCaptures: { "0": { name: "punctuation.definition.string.end.yaml" } }, + name: "string.quoted.double.yaml", + patterns: [ + { + match: + /\\\\([0abtnvfre "\\/\\\\N_Lp]|x\\p{Nd}\\p{Nd}|u\\p{Nd}{4}|U\\p{Nd}{8})/dgv, + name: "constant.character.escape.yaml", + }, + { + match: /\\\\\\n/dgv, + name: "constant.character.escape.double-quoted.newline.yaml", + }, + ], + }, + "flow-scalar-plain-in": { + patterns: [ + { include: "#flow-scalar-plain-in-implicit-type" }, + { + begin: + /[^\\p{space}\\-\\?\\:\\,\\[\\]\\{\\}\\#\\&\\*\\!\\|\\>'"\\%\\@\\\`]|[\\?\\:\\-][^\\p{space}\\[\\]\\{\\}\\,]/dgv, + end: /(?=\\p{space}*(?=$|\\n)|\\p{space}+#|\\p{space}*:(\\p{space}|(?=$|\\n))|\\p{space}*:[\\[\\]\\{\\}\\,]|\\p{space}*[\\[\\]\\{\\}\\,])/dgv, + name: "string.unquoted.plain.in.yaml", + }, + ], + }, + "flow-scalar-plain-in-implicit-type": { + patterns: [ + { + captures: { + "1": { name: "constant.language.null.yaml" }, + "2": { name: "constant.language.boolean.yaml" }, + "3": { name: "constant.numeric.integer.yaml" }, + "4": { name: "constant.numeric.float.yaml" }, + "5": { name: "constant.other.timestamp.yaml" }, + "6": { name: "constant.language.value.yaml" }, + "7": { name: "constant.language.merge.yaml" }, + }, + match: + /(?:(null|Null|NULL|~)|(y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)|((?:[\\-\\+]?0b[0-1_]+|[\\-\\+]?0[0-7_]+|[\\-\\+]?(?:0|[1-9][0-9_]*)|[\\-\\+]?0x[0-9a-fA-F_]+|[\\-\\+]?[1-9][0-9_]*(?::[0-5]?\\p{Nd})+))|((?:[\\-\\+]?(?:\\p{Nd}[0-9_]*)?\\.[0-9\\.]*(?:[eE][\\-\\+]\\p{Nd}+)?|[\\-\\+]?\\p{Nd}[0-9_]*(?::[0-5]?\\p{Nd})+\\.[0-9_]*|[\\-\\+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN)))|((?:\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}|\\p{Nd}{4}-\\p{Nd}{1,2}-\\p{Nd}{1,2}(?:[Tt]|[ \\t]+)\\p{Nd}{1,2}:\\p{Nd}{2}:\\p{Nd}{2}(?:\\.\\p{Nd}*)?(?:[ \\t]*Z|[\\-\\+]\\p{Nd}{1,2}(?::\\p{Nd}{1,2})?)?))|(=)|(<<))(?=\\p{space}*(?=$|\\n)|\\p{space}+#|\\p{space}*:(\\p{space}|(?=$|\\n))|\\p{space}*:[\\[\\]\\{\\}\\,]|\\p{space}*[\\[\\]\\{\\}\\,])/dgv, + }, + ], + }, + "flow-scalar-plain-out": { + patterns: [ + { include: "#flow-scalar-plain-out-implicit-type" }, + { + begin: + /[^\\p{space}\\-\\?\\:\\,\\[\\]\\{\\}\\#\\&\\*\\!\\|\\>'"\\%\\@\\\`]|[\\?\\:\\-]\\P{space}/dgv, + end: /(?=\\p{space}*(?=$|\\n)|\\p{space}+#|\\p{space}*:(\\p{space}|(?=$|\\n)))/dgv, + name: "string.unquoted.plain.out.yaml", + }, + ], + }, + "flow-scalar-plain-out-implicit-type": { + patterns: [ + { + captures: { + "1": { name: "constant.language.null.yaml" }, + "2": { name: "constant.language.boolean.yaml" }, + "3": { name: "constant.numeric.integer.yaml" }, + "4": { name: "constant.numeric.float.yaml" }, + "5": { name: "constant.other.timestamp.yaml" }, + "6": { name: "constant.language.value.yaml" }, + "7": { name: "constant.language.merge.yaml" }, + }, + match: + /(?:(null|Null|NULL|~)|(y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)|((?:[\\-\\+]?0b[0-1_]+|[\\-\\+]?0[0-7_]+|[\\-\\+]?(?:0|[1-9][0-9_]*)|[\\-\\+]?0x[0-9a-fA-F_]+|[\\-\\+]?[1-9][0-9_]*(?::[0-5]?\\p{Nd})+))|((?:[\\-\\+]?(?:\\p{Nd}[0-9_]*)?\\.[0-9\\.]*(?:[eE][\\-\\+]\\p{Nd}+)?|[\\-\\+]?\\p{Nd}[0-9_]*(?::[0-5]?\\p{Nd})+\\.[0-9_]*|[\\-\\+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN)))|((?:\\p{Nd}{4}-\\p{Nd}{2}-\\p{Nd}{2}|\\p{Nd}{4}-\\p{Nd}{1,2}-\\p{Nd}{1,2}(?:[Tt]|[ \\t]+)\\p{Nd}{1,2}:\\p{Nd}{2}:\\p{Nd}{2}(?:\\.\\p{Nd}*)?(?:[ \\t]*Z|[\\-\\+]\\p{Nd}{1,2}(?::\\p{Nd}{1,2})?)?))|(=)|(<<))(?=\\p{space}*(?=$|\\n)|\\p{space}+#|\\p{space}*:(\\p{space}|(?=$|\\n)))/dgv, + }, + ], + }, + "flow-scalar-single-quoted": { + begin: /'/dgv, + beginCaptures: { + "0": { name: "punctuation.definition.string.begin.yaml" }, + }, + end: /'(?!')/dgv, + endCaptures: { "0": { name: "punctuation.definition.string.end.yaml" } }, + name: "string.quoted.single.yaml", + patterns: [ + { + match: /''/dgv, + name: "constant.character.escape.single-quoted.yaml", + }, + ], + }, + "flow-sequence": { + begin: /\\[/dgv, + beginCaptures: { + "0": { name: "punctuation.definition.sequence.begin.yaml" }, + }, + end: /\\]/dgv, + endCaptures: { + "0": { name: "punctuation.definition.sequence.end.yaml" }, + }, + name: "meta.flow-sequence.yaml", + patterns: [ + { include: "#prototype" }, + { match: /,/dgv, name: "punctuation.separator.sequence.yaml" }, + { include: "#flow-pair" }, + { include: "#flow-node" }, + ], + }, + "flow-value": { + patterns: [ + { + begin: /(?![\\}\\,\\]])/dgvy, + end: /(?=[\\}\\,\\]])/dgv, + name: "meta.flow-pair.value.yaml", + patterns: [{ include: "#flow-node" }], + }, + ], + }, + node: { patterns: [{ include: "#block-node" }] }, + property: { + begin: /(?=!|&)/dgv, + end: new EmulatedRegExp("", "dgv", { strategy: "not_search_start" }), + name: "meta.property.yaml", + patterns: [ + { + captures: { + "1": { name: "keyword.control.property.anchor.yaml" }, + "2": { name: "punctuation.definition.anchor.yaml" }, + "3": { name: "entity.name.type.anchor.yaml" }, + "4": { name: "invalid.illegal.character.anchor.yaml" }, + }, + match: /((&))([^\\p{space}\\[\\]\\/\\{\\/\\}\\,]+)(\\P{space}+)?/dgvy, + }, + { + match: + /(?:!<(?:%[0-9A-Fa-f]{2}|[0-9A-Za-z\\-\\#\\;\\/\\?\\:\\@\\&\\=\\+\\$\\,_\\.\\!\\~\\*'\\(\\)\\[\\]])+>|!(?:[0-9A-Za-z\\-]*!)?(?:%[0-9A-Fa-f]{2}|[0-9A-Za-z\\-\\#\\;\\/\\?\\:\\@\\&\\=\\+\\$_\\.\\~\\*'\\(\\)])+|!)(?= |\\t|(?=$|\\n))/dgvy, + name: "storage.type.tag-handle.yaml", + }, + { match: /\\P{space}+/dgv, name: "invalid.illegal.tag-handle.yaml" }, + ], + }, + prototype: { + patterns: [{ include: "#comment" }, { include: "#property" }], + }, + }, + scopeName: "source.yaml", + aliases: ["yml"], +}; +" +`; diff --git a/packages/langs-precompiled/scripts/langs.ts b/packages/langs-precompiled/scripts/langs.ts new file mode 100644 index 000000000..c832c3a2c --- /dev/null +++ b/packages/langs-precompiled/scripts/langs.ts @@ -0,0 +1,180 @@ +import fs from 'fs-extra' +import { EmulatedRegExp } from 'oniguruma-to-es' +import { loadLangs } from '../../langs/scripts/langs' +import { precompileGrammar } from './precompile' + +export async function prepareLangs() { + const resolvedLangs = await loadLangs() + const exportedFileNames: string[] = [] + + for (const json of resolvedLangs) { + const deps: string[] = json.embeddedLangs || [] + const depsStr = [ + ...deps.map(i => `...${i.replace(/\W/g, '_')}`), + 'lang', + ].join(',\n') || '' + + let precompiledStr: string | undefined + try { + const precompiled = precompileGrammar(json) + precompiledStr = toJsLiteral(precompiled) + } + catch (e) { + console.error(`Failed to precompile ${json.name}: ${e}`) + } + + await fs.writeFile( + `./dist/${json.name}.mjs`, + precompiledStr == null + ? `export default [] + +throw new Error("${json.name} is not supported due to the grammar limits") +` + : ` +${precompiledStr.includes('new EmulatedRegExp') ? 'import { EmulatedRegExp } from \'oniguruma-to-es\'' : ''} +${deps.map(i => `import ${i.replace(/\W/g, '_')} from './${i}.mjs'`).join('\n')} + +const lang = Object.freeze(${precompiledStr}) + +export default [\n${depsStr}\n] +`.replace(/\n{2,}/g, '\n\n').trimStart(), + 'utf-8', + ) + + for (const alias of json.aliases || []) { + if (isInvalidFilename(alias)) + continue + await fs.writeFile( + `./dist/${alias}.mjs`, + `/* Alias ${alias} for ${json.name} */ +export { default } from './${json.name}.mjs' +`, + 'utf-8', + ) + } + + for (const name of [...json.aliases || [], json.name]) { + if (isInvalidFilename(name)) + continue + exportedFileNames.push(name) + await fs.writeFile( + `./dist/${name}.d.mts`, + `import type { LanguageRegistration } from '@shikijs/types' +const langs: LanguageRegistration [] +export default langs +`, + 'utf-8', + ) + } + } + + await fs.writeFile( + './dist/index.mjs', + `// Generated by scripts/prepare.ts + +export const languageNames = [ +${resolvedLangs.map(i => JSON.stringify(i.name)).join(',\n')} +] +`, + 'utf-8', + ) + + await fs.writeFile( + './dist/index.d.mts', + `export const languageNames: string[]`, + 'utf-8', + ) + + const packageJson = JSON.parse(await fs.readFile('./package.json', 'utf-8')) + packageJson.exports = { + '.': './dist/index.mjs', + ...Object.fromEntries( + exportedFileNames.map(i => [ + `./${i}`, + `./dist/${i}.mjs`, + ]), + ), + } + await fs.writeFile('./package.json', `${JSON.stringify(packageJson, null, 2)}\n`, 'utf-8') +} + +function isInvalidFilename(filename: string) { + return !filename.match(/^[\w-]+$/) +} + +export function toJsLiteral(value: any, seen = new Set()): string { + // null + if (value === null) { + return 'null' + } + + // undefined + if (typeof value === 'undefined') { + return 'undefined' + } + + // Boolean or number + if (typeof value === 'boolean' || typeof value === 'number') { + return String(value) + } + + if (value instanceof EmulatedRegExp) { + return `new EmulatedRegExp(${JSON.stringify(value.rawArgs.pattern)},${JSON.stringify(value.rawArgs.flags)},${JSON.stringify(value.rawArgs.options)})` + } + + // RegExp + if (value instanceof RegExp) { + // e.g., /pattern/gi + return value.toString() + } + + // String + if (typeof value === 'string') { + // Use JSON.stringify for correct escaping + return JSON.stringify(value) + } + + // Array + if (Array.isArray(value)) { + // Before recursing, check for cycles. + if (seen.has(value)) { + throw new Error('Circular reference detected in array') + } + seen.add(value) + + const elements = value.map(item => toJsLiteral(item, seen)) + const content = elements.join(',') + return `[${content}]` + } + + // Object + if (typeof value === 'object') { + // Before recursing, check for cycles. + if (seen.has(value)) { + throw new Error('Circular reference detected in object') + } + seen.add(value) + + const entries = [] + for (const key of Object.keys(value)) { + entries.push(`${safeKey(key)}:${toJsLiteral(value[key], seen)}`) + } + return `{${entries.join(',')}}` + } + + // Fallback + return JSON.stringify(value) +} + +/** + * Safely wraps the key in quotes if it's not a valid JS identifier. + */ +function safeKey(key: string) { + // A simple check for valid identifier names + const validIdentifier = /^[a-z_$][\w$]*$/i + if (validIdentifier.test(key)) { + return key // leave as is + } + // otherwise, wrap in quotes + return JSON.stringify(key) +} diff --git a/packages/langs-precompiled/scripts/precompile.test.ts b/packages/langs-precompiled/scripts/precompile.test.ts new file mode 100644 index 000000000..4564b1b99 --- /dev/null +++ b/packages/langs-precompiled/scripts/precompile.test.ts @@ -0,0 +1,22 @@ +import { EmulatedRegExp } from 'oniguruma-to-es' +import { format } from 'prettier' +import { expect, it } from 'vitest' +import { toJsLiteral } from './langs' +import { precompileGrammar } from './precompile' + +const isNode20andUp = process.version.replace(/^v/, '').split('.').map(Number)[0] >= 20 + +it.runIf(isNode20andUp)('precompile', async () => { + const grammar = await import('@shikijs/langs/yaml').then(m => m.default[0]) + const precompiled = precompileGrammar(grammar) + expect( + await format(`export default ${toJsLiteral(precompiled)}`, { + parser: 'babel-ts', + }), + ).toMatchSnapshot() +}) + +it.runIf(isNode20andUp)('should EmulatedRegExp inherits from RegExp', async () => { + const regex = new EmulatedRegExp('a', 'g') + expect(regex instanceof RegExp).toBe(true) +}) diff --git a/packages/langs-precompiled/scripts/precompile.ts b/packages/langs-precompiled/scripts/precompile.ts new file mode 100644 index 000000000..51c7e44fe --- /dev/null +++ b/packages/langs-precompiled/scripts/precompile.ts @@ -0,0 +1,17 @@ +import type { LanguageRegistration } from '@shikijs/types' +import { traverseGrammarPatterns } from '../../../scripts/utils' +import { defaultJavaScriptRegexConstructor } from '../../engine-javascript/src/engine-compile' + +export function precompileGrammar(grammar: LanguageRegistration): LanguageRegistration { + const precompiled: LanguageRegistration = structuredClone(grammar) + + traverseGrammarPatterns(precompiled, (pattern) => { + if (typeof pattern !== 'string') + return pattern + return defaultJavaScriptRegexConstructor(pattern, { + target: 'ES2024', + }) + }) + + return precompiled +} diff --git a/packages/langs-precompiled/scripts/prepare.ts b/packages/langs-precompiled/scripts/prepare.ts new file mode 100644 index 000000000..a6cda0ba9 --- /dev/null +++ b/packages/langs-precompiled/scripts/prepare.ts @@ -0,0 +1,6 @@ +import fs from 'fs-extra' +import { prepareLangs } from './langs' + +await fs.ensureDir('./dist') +await fs.emptyDir('./dist') +await prepareLangs() diff --git a/packages/langs/scripts/langs.ts b/packages/langs/scripts/langs.ts index 12424447d..b1f467362 100644 --- a/packages/langs/scripts/langs.ts +++ b/packages/langs/scripts/langs.ts @@ -6,7 +6,7 @@ import { grammars, injections } from 'tm-grammars' /** * Document-like languages that have embedded langs */ -const LANGS_LAZY_EMBEDDED_ALL = { +export const LANGS_LAZY_EMBEDDED_ALL = { markdown: [], mdx: [], wikitext: [], @@ -18,7 +18,7 @@ const LANGS_LAZY_EMBEDDED_ALL = { * Single-file-component-like languages that have embedded langs * For these langs, we exclude the standalone embedded langs from the main bundle */ -const LANGS_LAZY_EMBEDDED_PARTIAL = [ +export const LANGS_LAZY_EMBEDDED_PARTIAL = [ 'vue', 'vue-html', 'svelte', @@ -30,7 +30,7 @@ const LANGS_LAZY_EMBEDDED_PARTIAL = [ /** * Languages to be excluded from SFC langs */ -const STANDALONE_LANGS_EMBEDDED = [ +export const STANDALONE_LANGS_EMBEDDED = [ 'pug', 'stylus', 'sass', @@ -49,7 +49,7 @@ const STANDALONE_LANGS_EMBEDDED = [ 'ruby', ] -export async function prepareLangs() { +export async function loadLangs() { const allLangFiles = await fg('*.json', { cwd: './node_modules/tm-grammars/grammars', absolute: true, @@ -59,7 +59,6 @@ export async function prepareLangs() { allLangFiles.sort() const resolvedLangs: LanguageRegistration[] = [] - const exportedFileNames: string[] = [] for (const file of allLangFiles) { const content = await fs.readJSON(file) @@ -89,9 +88,18 @@ export async function prepareLangs() { json.embeddedLangs = (json.embeddedLangs || []).filter(i => !STANDALONE_LANGS_EMBEDDED.includes(i)) || [] } - const deps: string[] = json.embeddedLangs || [] resolvedLangs.push(json) + } + + return resolvedLangs +} +export async function prepareLangs() { + const resolvedLangs = await loadLangs() + const exportedFileNames: string[] = [] + + for (const json of resolvedLangs) { + const deps: string[] = json.embeddedLangs || [] if (deps.length > 10) console.log(json.name, json.embeddedLangs) @@ -101,7 +109,7 @@ export async function prepareLangs() { ].join(',\n') || '' await fs.writeFile( - `./dist/${lang.name}.mjs`, + `./dist/${json.name}.mjs`, `${deps.map(i => `import ${i.replace(/\W/g, '_')} from './${i}.mjs'`).join('\n')} const lang = Object.freeze(JSON.parse(${JSON.stringify(JSON.stringify(json))})) @@ -116,14 +124,14 @@ export default [\n${depsStr}\n] continue await fs.writeFile( `./dist/${alias}.mjs`, - `/* Alias ${alias} for ${lang.name} */ -export { default } from './${lang.name}.mjs' + `/* Alias ${alias} for ${json.name} */ +export { default } from './${json.name}.mjs' `, 'utf-8', ) } - for (const name of [...json.aliases || [], lang.name]) { + for (const name of [...json.aliases || [], json.name]) { if (isInvalidFilename(name)) continue exportedFileNames.push(name) diff --git a/packages/shiki/rollup.config.mjs b/packages/shiki/rollup.config.mjs index 875a8d83b..9e28d1c16 100644 --- a/packages/shiki/rollup.config.mjs +++ b/packages/shiki/rollup.config.mjs @@ -28,6 +28,7 @@ const entries = [ const external = [ 'shiki/wasm', + '@shikijs/types', /^@shikijs[/\\].*/g, /[/\\](langs|themes)[/\\]/g, ] diff --git a/packages/shiki/test/core-sync.test.ts b/packages/shiki/test/core-sync.test.ts index 6afca6624..62df787ce 100644 --- a/packages/shiki/test/core-sync.test.ts +++ b/packages/shiki/test/core-sync.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { createJavaScriptRegexEngine } from '../../engine-javascript/src' +import { createJavaScriptRegexEngine } from '../../engine-javascript/src/engine-compile' import { createHighlighterCoreSync } from '../src/core' import js from '../src/langs/javascript.mjs' import nord from '../src/themes/nord.mjs' diff --git a/packages/types/src/engines.ts b/packages/types/src/engines.ts index 8019ea982..9fdd9e64b 100644 --- a/packages/types/src/engines.ts +++ b/packages/types/src/engines.ts @@ -9,7 +9,7 @@ export interface RegexEngineString extends OnigString {} * Engine for RegExp matching and scanning. */ export interface RegexEngine { - createScanner: (patterns: string[]) => PatternScanner + createScanner: (patterns: (string | RegExp)[]) => PatternScanner createString: (s: string) => RegexEngineString } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f43169fb0..63c1daa99 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,8 +46,8 @@ catalogs: specifier: ^0.4.4 version: 0.4.4 '@shikijs/vscode-textmate': - specifier: ^9.3.1 - version: 9.3.1 + specifier: ^10.0.1 + version: 10.0.1 '@types/fs-extra': specifier: ^11.0.4 version: 11.0.4 @@ -180,9 +180,6 @@ catalogs: rollup-plugin-esbuild: specifier: ^6.1.1 version: 6.1.1 - rollup-plugin-typescript2: - specifier: ^0.36.0 - version: 0.36.0 shiki-legacy: specifier: npm:shiki@^0.14.7 version: 0.14.7 @@ -239,6 +236,7 @@ catalogs: version: 3.99.0 overrides: + '@shikijs/colorized-brackets': workspace:* '@shikijs/compat': workspace:* '@shikijs/core': workspace:* '@shikijs/engine-javascript': workspace:* @@ -247,6 +245,7 @@ overrides: '@shikijs/rehype': workspace:* '@shikijs/transformers': workspace:* '@shikijs/twoslash': workspace:* + '@shikijs/types': workspace:* '@shikijs/vitepress-twoslash': workspace:* '@types/hast': ^3.0.4 '@types/mdast': ^4.0.4 @@ -289,6 +288,9 @@ importers: '@rollup/plugin-terser': specifier: 'catalog:' version: 0.4.4(rollup@4.29.1) + '@shikijs/colorized-brackets': + specifier: workspace:* + version: link:packages/colorized-brackets '@shikijs/engine-javascript': specifier: workspace:* version: link:packages/engine-javascript @@ -307,6 +309,12 @@ importers: '@shikijs/transformers': specifier: workspace:* version: link:packages/transformers + '@shikijs/twoslash': + specifier: workspace:* + version: link:packages/twoslash + '@shikijs/types': + specifier: workspace:* + version: link:packages/types '@shikijs/vitepress-twoslash': specifier: workspace:* version: link:packages/vitepress-twoslash @@ -397,9 +405,6 @@ importers: rollup-plugin-esbuild: specifier: 'catalog:' version: 6.1.1(esbuild@0.17.19)(rollup@4.29.1) - rollup-plugin-typescript2: - specifier: 'catalog:' - version: 0.36.0(rollup@4.29.1)(typescript@5.7.2) shiki: specifier: workspace:* version: link:packages/shiki @@ -446,15 +451,6 @@ importers: '@iconify-json/svg-spinners': specifier: 'catalog:' version: 1.2.2 - '@shikijs/colorized-brackets': - specifier: workspace:* - version: link:../packages/colorized-brackets - '@shikijs/transformers': - specifier: workspace:* - version: link:../packages/transformers - '@shikijs/twoslash': - specifier: workspace:* - version: link:../packages/twoslash '@unocss/reset': specifier: 'catalog:' version: 0.65.3 @@ -467,9 +463,6 @@ importers: pinia: specifier: 'catalog:' version: 2.3.0(typescript@5.7.2)(vue@3.5.13(typescript@5.7.2)) - shiki: - specifier: workspace:* - version: link:../packages/shiki unocss: specifier: 'catalog:' version: 0.65.3(postcss@8.4.49)(rollup@4.29.1)(vite@6.0.6(@types/node@22.10.3)(jiti@2.4.2)(terser@5.32.0)(tsx@4.19.1)(yaml@2.6.1))(vue@3.5.13(typescript@5.7.2)) @@ -487,7 +480,7 @@ importers: dependencies: '@shikijs/vscode-textmate': specifier: 'catalog:' - version: 9.3.1 + version: 10.0.1 chalk: specifier: 'catalog:' version: 5.4.1 @@ -513,9 +506,21 @@ importers: '@shikijs/core': specifier: workspace:* version: link:../core + '@shikijs/langs': + specifier: workspace:* + version: link:../langs + '@shikijs/themes': + specifier: workspace:* + version: link:../themes '@shikijs/transformers': specifier: workspace:* version: link:../transformers + '@shikijs/types': + specifier: workspace:* + version: link:../types + '@shikijs/vscode-textmate': + specifier: 'catalog:' + version: 10.0.1 shiki: specifier: workspace:* version: link:../shiki @@ -537,7 +542,7 @@ importers: version: link:../types '@shikijs/vscode-textmate': specifier: 'catalog:' - version: 9.3.1 + version: 10.0.1 '@types/hast': specifier: ^3.0.4 version: 3.0.4 @@ -552,7 +557,7 @@ importers: version: link:../types '@shikijs/vscode-textmate': specifier: 'catalog:' - version: 9.3.1 + version: 10.0.1 oniguruma-to-es: specifier: 'catalog:' version: 0.10.0 @@ -564,7 +569,7 @@ importers: version: link:../types '@shikijs/vscode-textmate': specifier: 'catalog:' - version: 9.3.1 + version: 10.0.1 devDependencies: vscode-oniguruma: specifier: ^1.7.0 @@ -580,6 +585,19 @@ importers: specifier: 'catalog:' version: 1.22.1 + packages/langs-precompiled: + dependencies: + '@shikijs/types': + specifier: workspace:* + version: link:../types + oniguruma-to-es: + specifier: 'catalog:' + version: 0.10.0 + devDependencies: + tm-grammars: + specifier: 'catalog:' + version: 1.22.1 + packages/markdown-it: dependencies: markdown-it: @@ -606,7 +624,7 @@ importers: version: link:../types '@shikijs/vscode-textmate': specifier: 'catalog:' - version: 9.3.1 + version: 10.0.1 devDependencies: monaco-editor-core: specifier: 'catalog:' @@ -680,7 +698,7 @@ importers: version: link:../types '@shikijs/vscode-textmate': specifier: 'catalog:' - version: 9.3.1 + version: 10.0.1 '@types/hast': specifier: ^3.0.4 version: 3.0.4 @@ -746,7 +764,7 @@ importers: dependencies: '@shikijs/vscode-textmate': specifier: 'catalog:' - version: 9.3.1 + version: 10.0.1 '@types/hast': specifier: ^3.0.4 version: 3.0.4 @@ -1714,10 +1732,6 @@ packages: rollup: optional: true - '@rollup/pluginutils@4.2.1': - resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} - engines: {node: '>= 8.0.0'} - '@rollup/pluginutils@5.1.4': resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==} engines: {node: '>=14.0.0'} @@ -1822,11 +1836,8 @@ packages: cpu: [x64] os: [win32] - '@shikijs/types@1.22.2': - resolution: {integrity: sha512-NCWDa6LGZqTuzjsGfXOBWfjS/fDIbDdmVDug+7ykVe1IKT4c1gakrvlfFYp5NhAXH/lyqLM8wsAPo5wNy73Feg==} - - '@shikijs/vscode-textmate@9.3.1': - resolution: {integrity: sha512-79QfK1393x9Ho60QFyLti+QfdJzRQCVLFb97kOIV7Eo9vQU/roINgk7m24uv0a7AUvN//RDH36FLjjK48v0s9g==} + '@shikijs/vscode-textmate@10.0.1': + resolution: {integrity: sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==} '@stylistic/eslint-plugin@2.12.1': resolution: {integrity: sha512-fubZKIHSPuo07FgRTn6S4Nl0uXPRPYVNpyZzIDGfp7Fny6JjNus6kReLD7NI380JXi4HtUTSOZ34LBuNPO1XLQ==} @@ -1928,10 +1939,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: ^5.7.2 - '@typescript-eslint/scope-manager@8.18.0': - resolution: {integrity: sha512-PNGcHop0jkK2WVYGotk/hxj+UFLhXtGPiGtiaWgVBVP1jhMoMCHlTyJA+hEj4rszoSdLTK3fN4oOatrL0Cp+Xw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/scope-manager@8.18.2': resolution: {integrity: sha512-YJFSfbd0CJjy14r/EvWapYgV4R5CHzptssoag2M7y3Ra7XNta6GPAJPPP5KGB9j14viYXyrzRO5GkX7CRfo8/g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1943,33 +1950,16 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: ^5.7.2 - '@typescript-eslint/types@8.18.0': - resolution: {integrity: sha512-FNYxgyTCAnFwTrzpBGq+zrnoTO4x0c1CKYY5MuUTzpScqmY5fmsh2o3+57lqdI3NZucBDCzDgdEbIaNfAjAHQA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.18.2': resolution: {integrity: sha512-Z/zblEPp8cIvmEn6+tPDIHUbRu/0z5lqZ+NvolL5SvXWT5rQy7+Nch83M0++XzO0XrWRFWECgOAyE8bsJTl1GQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.18.0': - resolution: {integrity: sha512-rqQgFRu6yPkauz+ms3nQpohwejS8bvgbPyIDq13cgEDbkXt4LH4OkDMT0/fN1RUtzG8e8AKJyDBoocuQh8qNeg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: ^5.7.2 - '@typescript-eslint/typescript-estree@8.18.2': resolution: {integrity: sha512-WXAVt595HjpmlfH4crSdM/1bcsqh+1weFRWIa9XMTx/XHZ9TCKMcr725tLYqWOgzKdeDrqVHxFotrvWcEsk2Tg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: ^5.7.2 - '@typescript-eslint/utils@8.18.0': - resolution: {integrity: sha512-p6GLdY383i7h5b0Qrfbix3Vc3+J2k6QWw6UMUeY5JGfm3C5LbZ4QIZzJNoNOfgyRe0uuYKjvVOsO/jD4SJO+xg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: ^5.7.2 - '@typescript-eslint/utils@8.18.2': resolution: {integrity: sha512-Cr4A0H7DtVIPkauj4sTSXVl+VBWewE9/o40KcF3TV9aqDEOWoXF3/+oRXNby3DYzZeCATvbdksYsGZzplwnK/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1977,10 +1967,6 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: ^5.7.2 - '@typescript-eslint/visitor-keys@8.18.0': - resolution: {integrity: sha512-pCh/qEA8Lb1wVIqNvBke8UaRjJ6wrAWkJO5yyIbs8Yx6TNGYyfNjOo61tLv+WwLvoLPp4BQ8B7AHKijl8NGUfw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/visitor-keys@8.18.2': resolution: {integrity: sha512-zORcwn4C3trOWiCqFQP1x6G3xTRyZ1LYydnj51cRnJ6hxBlr/cKPckk+PKPUw/fXmvfKTcw7bwY3w9izgx5jZw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2119,15 +2105,9 @@ packages: '@vitest/utils@2.0.5': resolution: {integrity: sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==} - '@volar/language-core@2.4.10': - resolution: {integrity: sha512-hG3Z13+nJmGaT+fnQzAkS0hjJRa2FCeqZt6Bd+oGNhUkQ+mTFsDETg5rqUTxyzIh5pSOGY7FHCWUS8G82AzLCA==} - '@volar/language-core@2.4.11': resolution: {integrity: sha512-lN2C1+ByfW9/JRPpqScuZt/4OrUUse57GLI6TbLgTIqBVemdl1wNcZ1qYGEo2+Gw8coYLgCy7SuKqn6IrQcQgg==} - '@volar/source-map@2.4.10': - resolution: {integrity: sha512-OCV+b5ihV0RF3A7vEvNyHPi4G4kFa6ukPmyVocmqm5QzOd8r5yAtiNvaPEjl8dNvgC/lj4JPryeeHLdXd62rWA==} - '@volar/source-map@2.4.11': resolution: {integrity: sha512-ZQpmafIGvaZMn/8iuvCFGrW3smeqkq/IIh9F1SdSx9aUl0J4Iurzd6/FhmjNO5g2ejF3rT45dKskgXWiofqlZQ==} @@ -2527,10 +2507,6 @@ packages: confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - consola@3.2.3: - resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} - engines: {node: ^14.18.0 || >=16.10.0} - consola@3.3.3: resolution: {integrity: sha512-Qil5KwghMzlqd51UXM0b6fyaGHtOC22scxrwrz4A2882LyUMwQjnvaedN1HAeXzphspQ6CpHkzMAWxBTUruDLg==} engines: {node: ^14.18.0 || >=16.10.0} @@ -3196,10 +3172,6 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - find-cache-dir@3.3.2: - resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} - engines: {node: '>=8'} - find-up-simple@1.0.0: resolution: {integrity: sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==} engines: {node: '>=18'} @@ -3238,10 +3210,6 @@ packages: fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} - fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} - fs-extra@11.2.0: resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} engines: {node: '>=14.14'} @@ -3334,10 +3302,6 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@15.13.0: - resolution: {integrity: sha512-49TewVEz0UxZjr1WYYsWpPrhyC/B/pA8Bq0fUmet2n+eR7yn0IvNzNaoBwnK6mdkzcN+se7Ez9zUgULTz2QH4g==} - engines: {node: '>=18'} - globals@15.14.0: resolution: {integrity: sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==} engines: {node: '>=18'} @@ -3712,10 +3676,6 @@ packages: magicast@0.3.5: resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} - make-dir@3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} - engines: {node: '>=8'} - make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -4239,13 +4199,6 @@ packages: typescript: optional: true - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - - pkg-types@1.2.1: - resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==} - pkg-types@1.3.0: resolution: {integrity: sha512-kS7yWjVFCkIw9hqdJBoMxDdzEngmkr5FXeWZZfQ6GoYacjVnsW6l2CcYW/0ThD0vF4LPJgVYnrg4d0uuhwYQbg==} @@ -4600,12 +4553,6 @@ packages: rollup-plugin-node-polyfills@0.2.1: resolution: {integrity: sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==} - rollup-plugin-typescript2@0.36.0: - resolution: {integrity: sha512-NB2CSQDxSe9+Oe2ahZbf+B4bh7pHwjV5L+RSYpCu7Q5ROuN94F9b6ioWwKfz3ueL3KTtmX4o2MUH2cgHDIEUsw==} - peerDependencies: - rollup: ^4.29.1 - typescript: ^5.7.2 - rollup-pluginutils@2.8.2: resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==} @@ -6025,7 +5972,7 @@ snapshots: '@antfu/utils': 0.7.10 '@iconify/types': 2.0.0 debug: 4.4.0 - globals: 15.13.0 + globals: 15.14.0 kolorist: 1.8.0 local-pkg: 0.5.1 mlly: 1.7.3 @@ -6147,11 +6094,6 @@ snapshots: optionalDependencies: rollup: 4.29.1 - '@rollup/pluginutils@4.2.1': - dependencies: - estree-walker: 2.0.2 - picomatch: 2.3.1 - '@rollup/pluginutils@5.1.4(rollup@4.29.1)': dependencies: '@types/estree': 1.0.6 @@ -6217,16 +6159,11 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.29.1': optional: true - '@shikijs/types@1.22.2': - dependencies: - '@shikijs/vscode-textmate': 9.3.1 - '@types/hast': 3.0.4 - - '@shikijs/vscode-textmate@9.3.1': {} + '@shikijs/vscode-textmate@10.0.1': {} '@stylistic/eslint-plugin@2.12.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2)': dependencies: - '@typescript-eslint/utils': 8.18.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2) + '@typescript-eslint/utils': 8.18.2(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2) eslint: 9.17.0(jiti@2.4.2) eslint-visitor-keys: 4.2.0 espree: 10.3.0 @@ -6342,11 +6279,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.18.0': - dependencies: - '@typescript-eslint/types': 8.18.0 - '@typescript-eslint/visitor-keys': 8.18.0 - '@typescript-eslint/scope-manager@8.18.2': dependencies: '@typescript-eslint/types': 8.18.2 @@ -6363,24 +6295,8 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.18.0': {} - '@typescript-eslint/types@8.18.2': {} - '@typescript-eslint/typescript-estree@8.18.0(typescript@5.7.2)': - dependencies: - '@typescript-eslint/types': 8.18.0 - '@typescript-eslint/visitor-keys': 8.18.0 - debug: 4.4.0 - fast-glob: 3.3.2 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.7.2) - typescript: 5.7.2 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/typescript-estree@8.18.2(typescript@5.7.2)': dependencies: '@typescript-eslint/types': 8.18.2 @@ -6395,17 +6311,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.18.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2)': - dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@2.4.2)) - '@typescript-eslint/scope-manager': 8.18.0 - '@typescript-eslint/types': 8.18.0 - '@typescript-eslint/typescript-estree': 8.18.0(typescript@5.7.2) - eslint: 9.17.0(jiti@2.4.2) - typescript: 5.7.2 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/utils@8.18.2(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.17.0(jiti@2.4.2)) @@ -6417,11 +6322,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.18.0': - dependencies: - '@typescript-eslint/types': 8.18.0 - eslint-visitor-keys: 4.2.0 - '@typescript-eslint/visitor-keys@8.18.2': dependencies: '@typescript-eslint/types': 8.18.2 @@ -6655,16 +6555,10 @@ snapshots: loupe: 3.1.1 tinyrainbow: 1.2.0 - '@volar/language-core@2.4.10': - dependencies: - '@volar/source-map': 2.4.10 - '@volar/language-core@2.4.11': dependencies: '@volar/source-map': 2.4.11 - '@volar/source-map@2.4.10': {} - '@volar/source-map@2.4.11': {} '@volar/typescript@2.4.11': @@ -6730,7 +6624,7 @@ snapshots: '@vue/language-core@2.1.10(typescript@5.7.2)': dependencies: - '@volar/language-core': 2.4.10 + '@volar/language-core': 2.4.11 '@vue/compiler-dom': 3.5.13 '@vue/compiler-vue2': 2.7.16 '@vue/shared': 3.5.13 @@ -6994,7 +6888,7 @@ snapshots: ohash: 1.1.4 pathe: 1.1.2 perfect-debounce: 1.0.0 - pkg-types: 1.2.1 + pkg-types: 1.3.0 rc9: 2.1.2 optionalDependencies: magicast: 0.3.5 @@ -7117,8 +7011,6 @@ snapshots: confbox@0.1.8: {} - consola@3.2.3: {} - consola@3.3.3: {} convert-source-map@2.0.0: {} @@ -7683,8 +7575,8 @@ snapshots: eslint-plugin-import-x@4.6.1(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2): dependencies: '@types/doctrine': 0.0.9 - '@typescript-eslint/scope-manager': 8.18.0 - '@typescript-eslint/utils': 8.18.0(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2) + '@typescript-eslint/scope-manager': 8.18.2 + '@typescript-eslint/utils': 8.18.2(eslint@9.17.0(jiti@2.4.2))(typescript@5.7.2) debug: 4.4.0 doctrine: 3.0.0 enhanced-resolve: 5.17.1 @@ -7973,12 +7865,6 @@ snapshots: dependencies: to-regex-range: 5.0.1 - find-cache-dir@3.3.2: - dependencies: - commondir: 1.0.1 - make-dir: 3.1.0 - pkg-dir: 4.2.0 - find-up-simple@1.0.0: {} find-up@4.1.0: @@ -8015,12 +7901,6 @@ snapshots: fraction.js@4.3.7: {} - fs-extra@10.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - fs-extra@11.2.0: dependencies: graceful-fs: 4.2.11 @@ -8068,7 +7948,7 @@ snapshots: giget@1.2.3: dependencies: citty: 0.1.6 - consola: 3.2.3 + consola: 3.3.3 defu: 6.1.4 node-fetch-native: 1.6.4 nypm: 0.3.11 @@ -8121,8 +8001,6 @@ snapshots: globals@14.0.0: {} - globals@15.13.0: {} - globals@15.14.0: {} globby@10.0.1: @@ -8472,7 +8350,7 @@ snapshots: local-pkg@0.5.1: dependencies: mlly: 1.7.3 - pkg-types: 1.2.1 + pkg-types: 1.3.0 locate-path@5.0.0: dependencies: @@ -8528,10 +8406,6 @@ snapshots: '@babel/types': 7.26.3 source-map-js: 1.2.1 - make-dir@3.1.0: - dependencies: - semver: 6.3.1 - make-dir@4.0.0: dependencies: semver: 7.6.3 @@ -9133,7 +9007,7 @@ snapshots: dependencies: acorn: 8.14.0 pathe: 1.1.2 - pkg-types: 1.2.1 + pkg-types: 1.3.0 ufo: 1.5.4 monaco-editor-core@0.52.2: {} @@ -9184,10 +9058,10 @@ snapshots: nypm@0.3.11: dependencies: citty: 0.1.6 - consola: 3.2.3 + consola: 3.3.3 execa: 8.0.1 pathe: 1.1.2 - pkg-types: 1.2.1 + pkg-types: 1.3.0 ufo: 1.5.4 ofetch@1.4.1: @@ -9319,16 +9193,6 @@ snapshots: transitivePeerDependencies: - '@vue/composition-api' - pkg-dir@4.2.0: - dependencies: - find-up: 4.1.0 - - pkg-types@1.2.1: - dependencies: - confbox: 0.1.8 - mlly: 1.7.3 - pathe: 1.1.2 - pkg-types@1.3.0: dependencies: confbox: 0.1.8 @@ -9686,16 +9550,6 @@ snapshots: dependencies: rollup-plugin-inject: 3.0.2 - rollup-plugin-typescript2@0.36.0(rollup@4.29.1)(typescript@5.7.2): - dependencies: - '@rollup/pluginutils': 4.2.1 - find-cache-dir: 3.3.2 - fs-extra: 10.1.0 - rollup: 4.29.1 - semver: 7.6.3 - tslib: 2.7.0 - typescript: 5.7.2 - rollup-pluginutils@2.8.2: dependencies: estree-walker: 0.6.1 @@ -10317,7 +10171,7 @@ snapshots: '@iconify-json/simple-icons': 1.2.11 '@shikijs/core': link:packages/core '@shikijs/transformers': link:packages/transformers - '@shikijs/types': 1.22.2 + '@shikijs/types': link:packages/types '@types/markdown-it': 14.1.2 '@vitejs/plugin-vue': 5.1.4(vite@6.0.6(@types/node@22.10.3)(jiti@2.4.2)(terser@5.32.0)(tsx@4.19.1)(yaml@2.6.1))(vue@3.5.13(typescript@5.7.2)) '@vue/devtools-api': 7.6.4 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 724b3384a..a1fc9bb18 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -19,7 +19,7 @@ catalog: '@rollup/plugin-node-resolve': ^16.0.0 '@rollup/plugin-replace': ^6.0.2 '@rollup/plugin-terser': ^0.4.4 - '@shikijs/vscode-textmate': ^9.3.1 + '@shikijs/vscode-textmate': ^10.0.1 '@types/fs-extra': ^11.0.4 '@types/hast': ^3.0.4 '@types/markdown-it': ^14.1.2 @@ -68,7 +68,6 @@ catalog: rollup-plugin-copy: ^3.5.0 rollup-plugin-dts: ^6.1.1 rollup-plugin-esbuild: ^6.1.1 - rollup-plugin-typescript2: ^0.36.0 shiki-legacy: npm:shiki@^0.14.7 simple-git-hooks: ^2.11.1 taze: ^0.18.0 diff --git a/scripts/report-engine-js-compat.ts b/scripts/report-engine-js-compat.ts index d05eeb5b4..6df911982 100644 --- a/scripts/report-engine-js-compat.ts +++ b/scripts/report-engine-js-compat.ts @@ -11,6 +11,7 @@ import c from 'picocolors' import { format } from 'prettier' import { bundledLanguages, createHighlighter, createJavaScriptRegexEngine } from 'shiki' import { version } from '../package.json' +import { traverseGrammarPatterns } from './utils' const engine = createJavaScriptRegexEngine({ target: 'ES2024', @@ -278,50 +279,9 @@ function serializeTokens(shiki: HighlighterGeneric()): Set { - function traverse(a: any): void { - if (Array.isArray(a)) { - a.forEach((j: any) => { - traverse(j) - }) - return - } - if (!a || typeof a !== 'object') - return - if (a.foldingStartMarker) { - set.add(a.foldingStartMarker) - } - if (a.foldingStopMarker) { - set.add(a.foldingStopMarker) - } - if (a.firstLineMatch) { - set.add(a.firstLineMatch) - } - if (a.match) - set.add(a.match) - if (a.begin) - set.add(a.begin) - if (a.end) - set.add(a.end) - if (a.while) - set.add(a.while) - if (a.patterns) { - traverse(a.patterns) - } - if (a.captures) { - traverse(Object.values(a.captures)) - } - if (a.beginCaptures) { - traverse(Object.values(a.beginCaptures)) - } - if (a.endCaptures) { - traverse(Object.values(a.endCaptures)) - } - Object.values(a.repository || {}).forEach((j: any) => { - traverse(j) - }) - } - - traverse(grammar) + traverseGrammarPatterns(grammar, (pattern) => { + set.add(pattern) + }) return set } diff --git a/scripts/utils.ts b/scripts/utils.ts new file mode 100644 index 000000000..91bcceac4 --- /dev/null +++ b/scripts/utils.ts @@ -0,0 +1,60 @@ +export function traverseGrammarPatterns(a: any, callback: (pattern: string) => any | void): void { + if (Array.isArray(a)) { + a.forEach((j: any) => { + traverseGrammarPatterns(j, callback) + }) + return + } + if (!a || typeof a !== 'object') + return + if (a.foldingStartMarker) { + const pattern = callback(a.foldingStartMarker) + if (pattern != null) + a.foldingStartMarker = pattern + } + if (a.foldingStopMarker) { + const pattern = callback(a.foldingStopMarker) + if (pattern != null) + a.foldingStopMarker = pattern + } + if (a.firstLineMatch) { + const pattern = callback(a.firstLineMatch) + if (pattern != null) + a.firstLineMatch = pattern + } + if (a.match) { + const pattern = callback(a.match) + if (pattern != null) + a.match = pattern + } + if (a.begin) { + const pattern = callback(a.begin) + if (pattern != null) + a.begin = pattern + } + if (a.end) { + const pattern = callback(a.end) + if (pattern != null) + a.end = pattern + } + if (a.while) { + const pattern = callback(a.while) + if (pattern != null) + a.while = pattern + } + if (a.patterns) { + traverseGrammarPatterns(a.patterns, callback) + } + if (a.captures) { + traverseGrammarPatterns(Object.values(a.captures), callback) + } + if (a.beginCaptures) { + traverseGrammarPatterns(Object.values(a.beginCaptures), callback) + } + if (a.endCaptures) { + traverseGrammarPatterns(Object.values(a.endCaptures), callback) + } + Object.values(a.repository || {}).forEach((j: any) => { + traverseGrammarPatterns(j, callback) + }) +} diff --git a/tsconfig.json b/tsconfig.json index 8798c4e07..50d76f51a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -50,6 +50,7 @@ "**/vendor/**", "**/node_modules/**", "**/dist/**", - "**/fixtures/**" + "**/fixtures/**", + "tm-grammars-themes/**" ] } diff --git a/vitest.config.ts b/vitest.config.ts index 264a606cc..b90843cc7 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,6 +1,6 @@ import tsconfigPaths from 'vite-tsconfig-paths' import { defineConfig } from 'vitest/config' -import { wasmPlugin } from './packages/core/rollup.config.mjs' +import { wasmPlugin } from './packages/engine-oniguruma/rollup.config.mjs' export default defineConfig({ plugins: [ @@ -12,6 +12,7 @@ export default defineConfig({ exclude: [ '**/vendor/**', '**/node_modules/**', + '**/tm-grammars-themes/**', ], server: { deps: {