fix(highlight): vendor pure-Lezer Svelte parser (#3534)
* fix(highlight): vendor pure-Lezer Svelte parser Replace @replit/codemirror-lang-svelte with a vendored, pure-Lezer Svelte parser so the daemon and desktop app stop pulling the interactive CodeMirror editor runtime. @replit/codemirror-lang-svelte executes EditorView.inputHandler and HTML autocompletion at module load and depends on @codemirror/view, @codemirror/state, and @codemirror/lang-* as peers. Those peers were not declared by @getpaseo/highlight, so electron-builder omitted them from app.asar and the daemon crashed on startup with ERR_MODULE_NOT_FOUND: Cannot find package '@codemirror/lang-css'. The vendored parser uses only @lezer/lr, @lezer/common, @lezer/highlight, @lezer/javascript, and @lezer/css, with @codemirror/language kept for the existing Language wrapper. Drops the now-unused @codemirror/autocomplete dependency and adds @lezer/lr as a direct dependency. * fix(nix): update npm-deps hash for vendored Svelte parser
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
||||
sha256-2TmrD5JqI4AQSbngbTOfOnR+Z6Gmk3bxC7Bif+FN7jM=
|
||||
sha256-NyHZR53Y2lZP0eRe068qED1PhB6qPYnSOblFO9NRAKM=
|
||||
|
||||
Generated
+53
-106
@@ -2554,6 +2554,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz",
|
||||
"integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
@@ -2573,54 +2574,6 @@
|
||||
"@lezer/common": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-css": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz",
|
||||
"integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@lezer/common": "^1.0.2",
|
||||
"@lezer/css": "^1.1.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-html": {
|
||||
"version": "6.4.12",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.12.tgz",
|
||||
"integrity": "sha512-pw2ReWKUqSkbvh76RAT4NYxiogRu+PWkR2ukAwO9uOgrm8uipkzjtKKtNpyeAQwHOqxEeSvAXZ6vr3AfyB9y/w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/lang-css": "^6.0.0",
|
||||
"@codemirror/lang-javascript": "^6.0.0",
|
||||
"@codemirror/language": "^6.4.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.17.0",
|
||||
"@lezer/common": "^1.0.0",
|
||||
"@lezer/css": "^1.1.0",
|
||||
"@lezer/html": "^1.3.12"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-javascript": {
|
||||
"version": "6.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz",
|
||||
"integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/language": "^6.6.0",
|
||||
"@codemirror/lint": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.17.0",
|
||||
"@lezer/common": "^1.0.0",
|
||||
"@lezer/javascript": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/language": {
|
||||
"version": "6.12.4",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.4.tgz",
|
||||
@@ -2644,18 +2597,6 @@
|
||||
"@codemirror/language": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lint": {
|
||||
"version": "6.9.7",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.7.tgz",
|
||||
"integrity": "sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.42.0",
|
||||
"crelt": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/search": {
|
||||
"version": "6.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.1.tgz",
|
||||
@@ -11229,25 +11170,6 @@
|
||||
"@lezer/lr": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@replit/codemirror-lang-svelte": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@replit/codemirror-lang-svelte/-/codemirror-lang-svelte-6.0.0.tgz",
|
||||
"integrity": "sha512-U2OqqgMM6jKelL0GNWbAmqlu1S078zZNoBqlJBW+retTc5M4Mha6/Y2cf4SVg6ddgloJvmcSpt4hHrVoM4ePRA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/lang-css": "^6.0.1",
|
||||
"@codemirror/lang-html": "^6.2.0",
|
||||
"@codemirror/lang-javascript": "^6.1.1",
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
"@lezer/common": "^1.0.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/javascript": "^1.2.0",
|
||||
"@lezer/lr": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@replit/codemirror-vim": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@replit/codemirror-vim/-/codemirror-vim-6.3.0.tgz",
|
||||
@@ -11302,7 +11224,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.59.0",
|
||||
@@ -11315,7 +11238,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.59.0",
|
||||
@@ -11328,7 +11252,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.59.0",
|
||||
@@ -11341,7 +11266,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.59.0",
|
||||
@@ -11354,7 +11280,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.59.0",
|
||||
@@ -11367,7 +11294,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.59.0",
|
||||
@@ -11380,7 +11308,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.59.0",
|
||||
@@ -11393,7 +11322,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.59.0",
|
||||
@@ -11406,7 +11336,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.59.0",
|
||||
@@ -11419,7 +11350,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||
"version": "4.59.0",
|
||||
@@ -11432,7 +11364,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-musl": {
|
||||
"version": "4.59.0",
|
||||
@@ -11445,7 +11378,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||
"version": "4.59.0",
|
||||
@@ -11458,7 +11392,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-musl": {
|
||||
"version": "4.59.0",
|
||||
@@ -11471,7 +11406,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.59.0",
|
||||
@@ -11484,7 +11420,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.59.0",
|
||||
@@ -11497,7 +11434,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.59.0",
|
||||
@@ -11510,7 +11448,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.59.0",
|
||||
@@ -11523,7 +11462,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.59.0",
|
||||
@@ -11536,7 +11476,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-openbsd-x64": {
|
||||
"version": "4.59.0",
|
||||
@@ -11549,7 +11490,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||
"version": "4.59.0",
|
||||
@@ -11562,7 +11504,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.59.0",
|
||||
@@ -11575,7 +11518,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.59.0",
|
||||
@@ -11588,7 +11532,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||
"version": "4.59.0",
|
||||
@@ -11601,7 +11546,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.59.0",
|
||||
@@ -11614,7 +11560,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rtsao/scc": {
|
||||
"version": "1.1.0",
|
||||
@@ -19208,6 +19155,7 @@
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -38694,7 +38642,6 @@
|
||||
"name": "@getpaseo/highlight",
|
||||
"version": "0.4.0",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "6.20.3",
|
||||
"@codemirror/language": "6.12.4",
|
||||
"@codemirror/legacy-modes": "^6.5.3",
|
||||
"@lezer/common": "^1.5.0",
|
||||
@@ -38706,6 +38653,7 @@
|
||||
"@lezer/java": "^1.1.3",
|
||||
"@lezer/javascript": "^1.5.4",
|
||||
"@lezer/json": "^1.0.3",
|
||||
"@lezer/lr": "^1.4.8",
|
||||
"@lezer/markdown": "^1.6.2",
|
||||
"@lezer/php": "^1.0.5",
|
||||
"@lezer/python": "^1.1.18",
|
||||
@@ -38714,7 +38662,6 @@
|
||||
"@lezer/yaml": "^1.0.4",
|
||||
"@replit/codemirror-lang-csharp": "^6.2.0",
|
||||
"@replit/codemirror-lang-nix": "^6.0.1",
|
||||
"@replit/codemirror-lang-svelte": "^6.0.0",
|
||||
"lezer-elixir": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
"typecheck": "tsgo --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "6.20.3",
|
||||
"@codemirror/language": "6.12.4",
|
||||
"@codemirror/legacy-modes": "^6.5.3",
|
||||
"@lezer/common": "^1.5.0",
|
||||
@@ -39,6 +38,7 @@
|
||||
"@lezer/java": "^1.1.3",
|
||||
"@lezer/javascript": "^1.5.4",
|
||||
"@lezer/json": "^1.0.3",
|
||||
"@lezer/lr": "^1.4.8",
|
||||
"@lezer/markdown": "^1.6.2",
|
||||
"@lezer/php": "^1.0.5",
|
||||
"@lezer/python": "^1.1.18",
|
||||
@@ -47,7 +47,6 @@
|
||||
"@lezer/yaml": "^1.0.4",
|
||||
"@replit/codemirror-lang-csharp": "^6.2.0",
|
||||
"@replit/codemirror-lang-nix": "^6.0.1",
|
||||
"@replit/codemirror-lang-svelte": "^6.0.0",
|
||||
"lezer-elixir": "^1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -16,9 +16,10 @@ import { parser as xmlParser } from "@lezer/xml";
|
||||
import { parser as yamlParser } from "@lezer/yaml";
|
||||
import { csharpLanguage } from "@replit/codemirror-lang-csharp";
|
||||
import { nixLanguage } from "@replit/codemirror-lang-nix";
|
||||
import { svelteLanguage } from "@replit/codemirror-lang-svelte";
|
||||
import { parser as elixirParser } from "lezer-elixir";
|
||||
import type { Parser } from "@lezer/common";
|
||||
import { parser as svelteBaseParser } from "./svelte/parser.js";
|
||||
import { configureNesting, defaultNesting } from "./svelte/nesting.js";
|
||||
|
||||
function language(parser: Parser): Language {
|
||||
return new Language(defineLanguageFacet(), parser);
|
||||
@@ -51,7 +52,7 @@ const languagesByExtension: Record<string, Language> = {
|
||||
html: language(htmlParser),
|
||||
htm: language(htmlParser),
|
||||
// Svelte
|
||||
svelte: svelteLanguage,
|
||||
svelte: language(svelteBaseParser.configure({ wrap: configureNesting(defaultNesting) })),
|
||||
// XML
|
||||
xml: language(xmlParser),
|
||||
// Java
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { styleTags, tags as t } from "@lezer/highlight";
|
||||
|
||||
export const svelteHighlighting = styleTags({
|
||||
"Text RawText": t.content,
|
||||
"StartTag StartCloseTag SelfClosingEndTag EndTag": t.angleBracket,
|
||||
TagName: t.tagName,
|
||||
"MismatchedCloseTag/TagName": [t.tagName, t.invalid],
|
||||
AttributeName: t.attributeName,
|
||||
UnquotedAttributeValue: t.attributeValue,
|
||||
"DoubleQuote SingleQuote AttributeValueContent": t.attributeValue,
|
||||
Is: t.definitionOperator,
|
||||
"EntityReference CharacterReference": t.character,
|
||||
Comment: t.blockComment,
|
||||
ProcessingInst: t.processingInstruction,
|
||||
DoctypeDecl: t.documentMeta,
|
||||
"{ }": t.bracket,
|
||||
"[ ]": t.squareBracket,
|
||||
"( )": t.paren,
|
||||
"| , :": t.punctuation,
|
||||
"...": t.derefOperator,
|
||||
ComponentName: t.className,
|
||||
SvelteElementNamespace: t.namespace,
|
||||
SvelteElementType: t.tagName,
|
||||
StyleAttributeName: t.propertyName,
|
||||
BlockType: t.controlKeyword,
|
||||
BlockPrefix: t.typeOperator,
|
||||
"UnknownBlock/BlockType": t.invalid,
|
||||
UnknownBlockContent: t.invalid,
|
||||
"if then catch": t.controlKeyword,
|
||||
as: t.definitionOperator,
|
||||
Variable: t.variableName,
|
||||
Modifier: t.modifier,
|
||||
DirectlyInterpolatedAttributeValue: t.attributeValue,
|
||||
"DirectiveOn/DirectiveName": t.controlKeyword,
|
||||
"DirectiveOn/DirectiveTarget": t.typeName,
|
||||
"DirectiveUse/DirectiveName": t.controlKeyword,
|
||||
"DirectiveUse/DirectiveTarget": t.function(t.variableName),
|
||||
"DirectiveBind/DirectiveName": t.controlKeyword,
|
||||
"DirectiveBind/DirectiveTarget": t.variableName,
|
||||
"DirectiveLet/DirectiveName": t.definitionKeyword,
|
||||
"DirectiveLet/DirectiveTarget": t.definition(t.variableName),
|
||||
"DirectiveTransition/DirectiveName": t.operatorKeyword,
|
||||
"DirectiveTransition/DirectiveTarget": t.function(t.variableName),
|
||||
"DirectiveIn/DirectiveName": t.operatorKeyword,
|
||||
"DirectiveIn/DirectiveTarget": t.function(t.variableName),
|
||||
"DirectiveOut/DirectiveName": t.operatorKeyword,
|
||||
"DirectiveOut/DirectiveTarget": t.function(t.variableName),
|
||||
"DirectiveAnimate/DirectiveName": t.operatorKeyword,
|
||||
"DirectiveAnimate/DirectiveTarget": t.function(t.variableName),
|
||||
"DirectiveClass/DirectiveName": t.attributeName,
|
||||
"DirectiveClass/DirectiveTarget": t.variableName,
|
||||
"DirectiveStyle/DirectiveName": t.attributeName,
|
||||
"DirectiveStyle/DirectiveTarget": t.propertyName,
|
||||
});
|
||||
@@ -0,0 +1,106 @@
|
||||
import {
|
||||
parseMixed,
|
||||
type Input,
|
||||
type NestedParse,
|
||||
type Parser,
|
||||
type SyntaxNodeRef,
|
||||
} from "@lezer/common";
|
||||
import { parser as cssParser } from "@lezer/css";
|
||||
import { parser as jsParser } from "@lezer/javascript";
|
||||
import { LongExpression, ScriptText, ShortExpression, StyleText, TextareaText } from "./terms.js";
|
||||
|
||||
export interface NestedLang {
|
||||
tag: "script" | "style" | "textarea";
|
||||
attrs?: (attrs: Record<string, string>) => boolean;
|
||||
parser: Parser;
|
||||
}
|
||||
|
||||
const expressionParser = jsParser.configure({ top: "SingleExpression" });
|
||||
|
||||
export const defaultNesting: NestedLang[] = [
|
||||
{
|
||||
tag: "script",
|
||||
attrs: (attrs) => attrs.type === "text/typescript" || attrs.lang === "ts",
|
||||
parser: jsParser.configure({ dialect: "ts" }),
|
||||
},
|
||||
{
|
||||
tag: "script",
|
||||
attrs(attrs) {
|
||||
return (
|
||||
!attrs.type ||
|
||||
/^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i.test(attrs.type)
|
||||
);
|
||||
},
|
||||
parser: jsParser,
|
||||
},
|
||||
{
|
||||
tag: "style",
|
||||
attrs(attrs) {
|
||||
return (
|
||||
(!attrs.lang || attrs.lang === "css" || attrs.lang === "scss") &&
|
||||
(!attrs.type || /^(text\/)?(x-)?(stylesheet|css|scss)$/i.test(attrs.type))
|
||||
);
|
||||
},
|
||||
parser: cssParser,
|
||||
},
|
||||
];
|
||||
|
||||
function getAttrs(element: SyntaxNodeRef, input: Input): Record<string, string> {
|
||||
const attrs: Record<string, string> = Object.create(null);
|
||||
const firstChild = element.node.firstChild;
|
||||
if (!firstChild) return attrs;
|
||||
for (const att of firstChild.getChildren("Attribute")) {
|
||||
const name = att.getChild("AttributeName");
|
||||
const value = att.getChild("AttributeValue") || att.getChild("UnquotedAttributeValue");
|
||||
if (name) {
|
||||
const attrName = input.read(name.from, name.to);
|
||||
if (!value) {
|
||||
attrs[attrName] = "";
|
||||
} else if (value.name === "AttributeValue") {
|
||||
attrs[attrName] = input.read(value.from + 1, value.to - 1);
|
||||
} else {
|
||||
attrs[attrName] = input.read(value.from, value.to);
|
||||
}
|
||||
}
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
|
||||
function maybeNest(node: SyntaxNodeRef, input: Input, tags: NestedLang[]): NestedParse | null {
|
||||
let attrs: Record<string, string> | undefined;
|
||||
const parent = node.node.parent;
|
||||
if (!parent) return null;
|
||||
for (const tag of tags) {
|
||||
if (!tag.attrs || tag.attrs(attrs || (attrs = getAttrs(parent, input)))) {
|
||||
return { parser: tag.parser };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function configureNesting(tags: NestedLang[]) {
|
||||
const script: NestedLang[] = [];
|
||||
const style: NestedLang[] = [];
|
||||
const textarea: NestedLang[] = [];
|
||||
for (const tag of tags) {
|
||||
let array: NestedLang[];
|
||||
if (tag.tag === "script") {
|
||||
array = script;
|
||||
} else if (tag.tag === "style") {
|
||||
array = style;
|
||||
} else if (tag.tag === "textarea") {
|
||||
array = textarea;
|
||||
} else {
|
||||
throw new RangeError("Only script, style, and textarea tags can host nested parsers");
|
||||
}
|
||||
array.push(tag);
|
||||
}
|
||||
return parseMixed((node, input) => {
|
||||
const id = node.type.id;
|
||||
if (id === LongExpression || id === ShortExpression) return { parser: expressionParser };
|
||||
if (id === ScriptText) return maybeNest(node, input, script);
|
||||
if (id === StyleText) return maybeNest(node, input, style);
|
||||
if (id === TextareaText) return maybeNest(node, input, textarea);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,23 @@
|
||||
export const scriptText = 145;
|
||||
export const StartCloseScriptTag = 1;
|
||||
export const styleText = 146;
|
||||
export const StartCloseStyleTag = 2;
|
||||
export const textareaText = 147;
|
||||
export const StartCloseTextareaTag = 3;
|
||||
export const StartTag = 4;
|
||||
export const StartScriptTag = 5;
|
||||
export const StartStyleTag = 6;
|
||||
export const StartTextareaTag = 7;
|
||||
export const StartSelfClosingTag = 8;
|
||||
export const StartCloseTag = 9;
|
||||
export const MismatchedStartCloseTag = 11;
|
||||
export const missingCloseTag = 148;
|
||||
export const IncompleteCloseTag = 12;
|
||||
export const commentContent$1 = 149;
|
||||
export const LongExpression = 13;
|
||||
export const ShortExpression = 14;
|
||||
export const Element = 67;
|
||||
export const ScriptText = 110;
|
||||
export const StyleText = 113;
|
||||
export const TextareaText = 116;
|
||||
export const OpenTag = 118;
|
||||
@@ -0,0 +1,545 @@
|
||||
import { ContextTracker, ExternalTokenizer, type InputStream, type Stack } from "@lezer/lr";
|
||||
import {
|
||||
commentContent$1,
|
||||
Element,
|
||||
IncompleteCloseTag,
|
||||
LongExpression,
|
||||
MismatchedStartCloseTag,
|
||||
missingCloseTag,
|
||||
OpenTag,
|
||||
scriptText,
|
||||
ShortExpression,
|
||||
StartCloseScriptTag,
|
||||
StartCloseStyleTag,
|
||||
StartCloseTag,
|
||||
StartCloseTextareaTag,
|
||||
StartScriptTag,
|
||||
StartSelfClosingTag,
|
||||
StartStyleTag,
|
||||
StartTag,
|
||||
StartTextareaTag,
|
||||
styleText,
|
||||
textareaText,
|
||||
} from "./terms.js";
|
||||
|
||||
const selfClosers: Record<string, boolean> = {
|
||||
area: true,
|
||||
base: true,
|
||||
br: true,
|
||||
col: true,
|
||||
command: true,
|
||||
embed: true,
|
||||
frame: true,
|
||||
hr: true,
|
||||
img: true,
|
||||
input: true,
|
||||
keygen: true,
|
||||
link: true,
|
||||
meta: true,
|
||||
param: true,
|
||||
source: true,
|
||||
track: true,
|
||||
wbr: true,
|
||||
menuitem: true,
|
||||
};
|
||||
|
||||
const implicitlyClosed: Record<string, boolean> = {
|
||||
dd: true,
|
||||
li: true,
|
||||
optgroup: true,
|
||||
option: true,
|
||||
p: true,
|
||||
rp: true,
|
||||
rt: true,
|
||||
tbody: true,
|
||||
td: true,
|
||||
tfoot: true,
|
||||
th: true,
|
||||
tr: true,
|
||||
};
|
||||
|
||||
const closeOnOpen: Record<string, Record<string, boolean>> = {
|
||||
dd: { dd: true, dt: true },
|
||||
dt: { dd: true, dt: true },
|
||||
li: { li: true },
|
||||
option: { option: true, optgroup: true },
|
||||
optgroup: { optgroup: true },
|
||||
p: {
|
||||
address: true,
|
||||
article: true,
|
||||
aside: true,
|
||||
blockquote: true,
|
||||
dir: true,
|
||||
div: true,
|
||||
dl: true,
|
||||
fieldset: true,
|
||||
footer: true,
|
||||
form: true,
|
||||
h1: true,
|
||||
h2: true,
|
||||
h3: true,
|
||||
h4: true,
|
||||
h5: true,
|
||||
h6: true,
|
||||
header: true,
|
||||
hgroup: true,
|
||||
hr: true,
|
||||
menu: true,
|
||||
nav: true,
|
||||
ol: true,
|
||||
p: true,
|
||||
pre: true,
|
||||
section: true,
|
||||
table: true,
|
||||
ul: true,
|
||||
},
|
||||
rp: { rp: true, rt: true },
|
||||
rt: { rp: true, rt: true },
|
||||
tbody: { tbody: true, tfoot: true },
|
||||
td: { td: true, th: true },
|
||||
tfoot: { tbody: true },
|
||||
th: { td: true, th: true },
|
||||
thead: { tbody: true, tfoot: true },
|
||||
tr: { tr: true },
|
||||
};
|
||||
|
||||
function nameChar(ch: number): boolean {
|
||||
return (
|
||||
ch === 45 ||
|
||||
ch === 46 ||
|
||||
ch === 58 ||
|
||||
(ch >= 65 && ch <= 90) ||
|
||||
ch === 95 ||
|
||||
(ch >= 97 && ch <= 122) ||
|
||||
ch >= 161
|
||||
);
|
||||
}
|
||||
|
||||
function isSpace(ch: number): boolean {
|
||||
return ch === 9 || ch === 10 || ch === 13 || ch === 32;
|
||||
}
|
||||
|
||||
const lessThan = 60;
|
||||
const greaterThan$1 = 62;
|
||||
const slash$1 = 47;
|
||||
const question = 63;
|
||||
const bang = 33;
|
||||
|
||||
let cachedName: string | null | undefined = null;
|
||||
let cachedInput: InputStream | null = null;
|
||||
let cachedPos = 0;
|
||||
|
||||
function tagNameAfter(input: InputStream, offset: number): string | null | undefined {
|
||||
const pos = input.pos + offset;
|
||||
if (cachedPos === pos && cachedInput === input) return cachedName;
|
||||
let next = input.peek(offset);
|
||||
let curOffset = offset;
|
||||
while (isSpace(next)) {
|
||||
curOffset++;
|
||||
next = input.peek(curOffset);
|
||||
}
|
||||
let name = "";
|
||||
for (;;) {
|
||||
if (!nameChar(next)) break;
|
||||
name += String.fromCharCode(next);
|
||||
curOffset++;
|
||||
next = input.peek(curOffset);
|
||||
}
|
||||
cachedInput = input;
|
||||
cachedPos = pos;
|
||||
let result: string | null | undefined;
|
||||
if (name) {
|
||||
result = name.toLowerCase();
|
||||
} else if (next === question || next === bang) {
|
||||
result = undefined;
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
cachedName = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
export class ElementContext {
|
||||
name: string;
|
||||
parent: ElementContext | null;
|
||||
hash: number;
|
||||
|
||||
constructor(name: string, parent: ElementContext | null) {
|
||||
this.name = name;
|
||||
this.parent = parent;
|
||||
this.hash = parent ? parent.hash : 0;
|
||||
for (let i = 0; i < name.length; i++) {
|
||||
this.hash += (this.hash << 4) + name.charCodeAt(i) + (name.charCodeAt(i) << 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const startTagTerms = new Set([
|
||||
StartTag,
|
||||
StartSelfClosingTag,
|
||||
StartScriptTag,
|
||||
StartStyleTag,
|
||||
StartTextareaTag,
|
||||
]);
|
||||
|
||||
export const elementContext = new ContextTracker<ElementContext | null>({
|
||||
start: null,
|
||||
shift(context, term, _stack, input) {
|
||||
return startTagTerms.has(term)
|
||||
? new ElementContext(tagNameAfter(input, 1) || "", context)
|
||||
: context;
|
||||
},
|
||||
reduce(context, term) {
|
||||
return term === Element && context ? context.parent : context;
|
||||
},
|
||||
reuse(context, node, _stack, input) {
|
||||
const type = node.type.id;
|
||||
return type === StartTag || type === OpenTag
|
||||
? new ElementContext(tagNameAfter(input, 1) || "", context)
|
||||
: context;
|
||||
},
|
||||
hash(context) {
|
||||
return context ? context.hash : 0;
|
||||
},
|
||||
strict: false,
|
||||
});
|
||||
|
||||
export const tagStart = new ExternalTokenizer(
|
||||
// oxlint-disable-next-line complexity -- vendored Lezer tokenizer
|
||||
(input: InputStream, stack: Stack) => {
|
||||
if (input.next !== lessThan) {
|
||||
if (input.next < 0 && stack.context) input.acceptToken(missingCloseTag);
|
||||
return;
|
||||
}
|
||||
input.advance();
|
||||
const close = (input.next as number) === slash$1;
|
||||
if (close) input.advance();
|
||||
const name = tagNameAfter(input, 0);
|
||||
if (name === undefined) return;
|
||||
if (!name) {
|
||||
input.acceptToken(close ? IncompleteCloseTag : StartTag);
|
||||
return;
|
||||
}
|
||||
|
||||
const parent = stack.context ? (stack.context as ElementContext).name : null;
|
||||
if (close) {
|
||||
if (name === parent) {
|
||||
input.acceptToken(StartCloseTag);
|
||||
return;
|
||||
}
|
||||
if (parent && implicitlyClosed[parent]) {
|
||||
input.acceptToken(missingCloseTag, -2);
|
||||
return;
|
||||
}
|
||||
for (
|
||||
let cx: ElementContext | null = stack.context as ElementContext | null;
|
||||
cx;
|
||||
cx = cx.parent
|
||||
) {
|
||||
if (cx.name === name) return;
|
||||
}
|
||||
input.acceptToken(MismatchedStartCloseTag);
|
||||
} else {
|
||||
if (name === "script") {
|
||||
input.acceptToken(StartScriptTag);
|
||||
return;
|
||||
}
|
||||
if (name === "style") {
|
||||
input.acceptToken(StartStyleTag);
|
||||
return;
|
||||
}
|
||||
if (name === "textarea") {
|
||||
input.acceptToken(StartTextareaTag);
|
||||
return;
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(selfClosers, name)) {
|
||||
input.acceptToken(StartSelfClosingTag);
|
||||
return;
|
||||
}
|
||||
if (parent && closeOnOpen[parent]?.[name]) {
|
||||
input.acceptToken(missingCloseTag, -1);
|
||||
} else {
|
||||
input.acceptToken(StartTag);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ contextual: true },
|
||||
);
|
||||
|
||||
function contentTokenizer(tag: string, textToken: number, endToken: number): ExternalTokenizer {
|
||||
const lastState = 2 + tag.length;
|
||||
return new ExternalTokenizer((input) => {
|
||||
for (let state = 0, matchedLen = 0, i = 0; ; i++) {
|
||||
if (input.next < 0) {
|
||||
if (i) input.acceptToken(textToken);
|
||||
break;
|
||||
}
|
||||
if (
|
||||
(state === 0 && input.next === lessThan) ||
|
||||
(state === 1 && input.next === slash$1) ||
|
||||
(state >= 2 && state < lastState && input.next === tag.charCodeAt(state - 2))
|
||||
) {
|
||||
state++;
|
||||
matchedLen++;
|
||||
} else if ((state === 2 || state === lastState) && isSpace(input.next)) {
|
||||
matchedLen++;
|
||||
} else if (state === lastState && input.next === greaterThan$1) {
|
||||
if (i > matchedLen) input.acceptToken(textToken, -matchedLen);
|
||||
else input.acceptToken(endToken, -(matchedLen - 2));
|
||||
break;
|
||||
} else if ((input.next === 10 || input.next === 13) && i) {
|
||||
input.acceptToken(textToken, 1);
|
||||
break;
|
||||
} else {
|
||||
state = 0;
|
||||
matchedLen = 0;
|
||||
}
|
||||
input.advance();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const scriptTokens = contentTokenizer("script", scriptText, StartCloseScriptTag);
|
||||
export const styleTokens = contentTokenizer("style", styleText, StartCloseStyleTag);
|
||||
export const textareaTokens = contentTokenizer("textarea", textareaText, StartCloseTextareaTag);
|
||||
|
||||
const space = new Set([
|
||||
9, 10, 11, 12, 13, 32, 133, 160, 5760, 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8199, 8200, 8201,
|
||||
8202, 8232, 8233, 8239, 8287, 12288,
|
||||
]);
|
||||
const parenOpen = 40;
|
||||
const parenClose = 41;
|
||||
const squareOpen = 91;
|
||||
const squareClose = 93;
|
||||
const curlyOpen = 123;
|
||||
const curlyClose = 125;
|
||||
const comma = 44;
|
||||
const colon = 58;
|
||||
const hash = 35;
|
||||
const at = 64;
|
||||
const slash = 47;
|
||||
const greaterThan = 62;
|
||||
const dash = 45;
|
||||
const quoteDouble = 34;
|
||||
const quoteSingle = 39;
|
||||
const backslash = 92;
|
||||
const newline = 10;
|
||||
const asterisk = 42;
|
||||
const tick = 96;
|
||||
const prefixes = new Set([colon, hash, at, slash]);
|
||||
|
||||
export const commentContent = new ExternalTokenizer((input) => {
|
||||
for (let dashes = 0, i = 0; ; i++) {
|
||||
if (input.next < 0) {
|
||||
if (i) input.acceptToken(commentContent$1);
|
||||
break;
|
||||
}
|
||||
if (input.next === dash) {
|
||||
dashes++;
|
||||
} else if (input.next === greaterThan && dashes >= 2) {
|
||||
if (i > 3) input.acceptToken(commentContent$1, -2);
|
||||
break;
|
||||
} else {
|
||||
dashes = 0;
|
||||
}
|
||||
input.advance();
|
||||
}
|
||||
});
|
||||
|
||||
function createStringHandler(input: InputStream) {
|
||||
let inString = false;
|
||||
let inStringType: "double" | "single" | "template" | null = null;
|
||||
let inStringIgnoreNext = false;
|
||||
return () => {
|
||||
if (inString) {
|
||||
if (inStringIgnoreNext) {
|
||||
inStringIgnoreNext = false;
|
||||
return true;
|
||||
}
|
||||
if (input.next === backslash) {
|
||||
inStringIgnoreNext = true;
|
||||
return true;
|
||||
}
|
||||
if (inStringType === "double" && input.next === quoteDouble) {
|
||||
inString = false;
|
||||
inStringType = null;
|
||||
return true;
|
||||
}
|
||||
if (inStringType === "single" && input.next === quoteSingle) {
|
||||
inString = false;
|
||||
inStringType = null;
|
||||
return true;
|
||||
}
|
||||
if (inStringType === "template" && input.next === tick) {
|
||||
inString = false;
|
||||
inStringType = null;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (input.next === quoteDouble) {
|
||||
inString = true;
|
||||
inStringType = "double";
|
||||
return true;
|
||||
}
|
||||
if (input.next === quoteSingle) {
|
||||
inString = true;
|
||||
inStringType = "single";
|
||||
return true;
|
||||
}
|
||||
if (input.next === tick) {
|
||||
inString = true;
|
||||
inStringType = "template";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
function createCommentHandler(input: InputStream) {
|
||||
let inLineComment = false;
|
||||
let inBlockComment = false;
|
||||
return () => {
|
||||
if (inLineComment) {
|
||||
if (input.next === newline) {
|
||||
inLineComment = false;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (inBlockComment) {
|
||||
if (input.next === asterisk && input.peek(1) === slash) {
|
||||
inBlockComment = false;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (input.next === slash && input.peek(1) === slash) {
|
||||
inLineComment = true;
|
||||
return true;
|
||||
}
|
||||
if (input.next === slash && input.peek(1) === asterisk) {
|
||||
inBlockComment = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
export const longExpression = new ExternalTokenizer((input) => {
|
||||
if (prefixes.has(input.next)) {
|
||||
return;
|
||||
}
|
||||
const commentHandler = createCommentHandler(input);
|
||||
const stringHandler = createStringHandler(input);
|
||||
const stack: ("(" | "{" | "[")[] = [];
|
||||
const popIfMatch = (match: "(" | "{" | "[") => {
|
||||
const idx = stack.lastIndexOf(match);
|
||||
if (idx !== -1) {
|
||||
while (stack.length > idx) {
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
for (let pos = 0; ; pos++) {
|
||||
if (input.next < 0) {
|
||||
if (pos > 0) input.acceptToken(LongExpression);
|
||||
break;
|
||||
}
|
||||
if (commentHandler() || stringHandler()) {
|
||||
input.advance();
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
stack.length === 0 &&
|
||||
(input.next === curlyClose || input.next === parenClose || input.next === squareClose)
|
||||
) {
|
||||
input.acceptToken(LongExpression);
|
||||
break;
|
||||
}
|
||||
switch (input.next) {
|
||||
case parenOpen:
|
||||
stack.push("(");
|
||||
break;
|
||||
case parenClose:
|
||||
popIfMatch("(");
|
||||
break;
|
||||
case squareOpen:
|
||||
stack.push("[");
|
||||
break;
|
||||
case squareClose:
|
||||
popIfMatch("[");
|
||||
break;
|
||||
case curlyOpen:
|
||||
stack.push("{");
|
||||
break;
|
||||
case curlyClose:
|
||||
popIfMatch("{");
|
||||
break;
|
||||
}
|
||||
input.advance();
|
||||
}
|
||||
});
|
||||
|
||||
// oxlint-disable-next-line complexity -- vendored Lezer tokenizer
|
||||
export const shortExpression = new ExternalTokenizer((input) => {
|
||||
if (prefixes.has(input.peek(0))) {
|
||||
return;
|
||||
}
|
||||
const commentHandler = createCommentHandler(input);
|
||||
const stringHandler = createStringHandler(input);
|
||||
const stack: ("(" | "{" | "[")[] = [];
|
||||
const popIfMatch = (match: "(" | "{" | "[") => {
|
||||
const idx = stack.lastIndexOf(match);
|
||||
if (idx !== -1) {
|
||||
while (stack.length > idx) {
|
||||
stack.pop();
|
||||
}
|
||||
}
|
||||
};
|
||||
for (let pos = 0; ; pos++) {
|
||||
if (input.next < 0) {
|
||||
if (pos > 0) input.acceptToken(ShortExpression);
|
||||
break;
|
||||
}
|
||||
if (commentHandler() || stringHandler()) {
|
||||
input.advance();
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
stack.length === 0 &&
|
||||
(input.next === curlyClose ||
|
||||
input.next === parenClose ||
|
||||
input.next === squareClose ||
|
||||
input.next === comma)
|
||||
) {
|
||||
input.acceptToken(ShortExpression);
|
||||
break;
|
||||
}
|
||||
switch (input.next) {
|
||||
case parenOpen:
|
||||
stack.push("(");
|
||||
break;
|
||||
case parenClose:
|
||||
popIfMatch("(");
|
||||
break;
|
||||
case squareOpen:
|
||||
stack.push("[");
|
||||
break;
|
||||
case squareClose:
|
||||
popIfMatch("[");
|
||||
break;
|
||||
case curlyOpen:
|
||||
stack.push("{");
|
||||
break;
|
||||
case curlyClose:
|
||||
popIfMatch("{");
|
||||
break;
|
||||
}
|
||||
if (pos !== 0 && stack.length === 0 && space.has(input.next)) {
|
||||
input.acceptToken(ShortExpression);
|
||||
break;
|
||||
}
|
||||
input.advance();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user