From 542561bb7f3d9de52142d5e031addb8526f99bb2 Mon Sep 17 00:00:00 2001 From: "Charles-P. Clermont" Date: Fri, 30 Jan 2026 16:39:34 -0500 Subject: [PATCH 1/6] Configure TypeScript for ESM output Update tsconfig.json for ES2020 target and node20 module system. Add explicit moduleResolution: node to codemirror-language-client and lang-jsonc packages. This is preliminary configuration work for ESM compatibility. Co-Authored-By: Claude Opus 4.5 --- packages/codemirror-language-client/tsconfig.json | 2 +- packages/lang-jsonc/tsconfig.json | 2 +- tsconfig.json | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/codemirror-language-client/tsconfig.json b/packages/codemirror-language-client/tsconfig.json index 48524f58b..d064ecbf8 100644 --- a/packages/codemirror-language-client/tsconfig.json +++ b/packages/codemirror-language-client/tsconfig.json @@ -6,6 +6,7 @@ "outDir": "./dist/esm", "tsBuildInfoFile": "./dist/esm/tsconfig.tsbuildInfo", "module": "es6", + "moduleResolution": "node", "rootDir": "src", "resolveJsonModule": false, "lib": [ @@ -15,4 +16,3 @@ ] } } - diff --git a/packages/lang-jsonc/tsconfig.json b/packages/lang-jsonc/tsconfig.json index 48524f58b..d064ecbf8 100644 --- a/packages/lang-jsonc/tsconfig.json +++ b/packages/lang-jsonc/tsconfig.json @@ -6,6 +6,7 @@ "outDir": "./dist/esm", "tsBuildInfoFile": "./dist/esm/tsconfig.tsbuildInfo", "module": "es6", + "moduleResolution": "node", "rootDir": "src", "resolveJsonModule": false, "lib": [ @@ -15,4 +16,3 @@ ] } } - diff --git a/tsconfig.json b/tsconfig.json index 7cc798d6d..c17ef1956 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,9 +9,9 @@ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "es2019", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "es2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ "lib": [ - "es2019" + "es2020" ], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ @@ -24,9 +24,9 @@ // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ + "module": "node20", /* Specify what module code is generated. */ "rootDir": ".", /* Specify the root folder within your source files. */ - "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "moduleResolution": "", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ "paths": { "@shopify/theme-check-common": [ From 925a74997bc9f0d1a5981322aa7feb3d9d68ebdd Mon Sep 17 00:00:00 2001 From: "Charles-P. Clermont" Date: Fri, 30 Jan 2026 16:39:51 -0500 Subject: [PATCH 2/6] Migrate liquid-html-parser to @ohm-js/wasm Replace ohm-js with @ohm-js/wasm for browser compatibility. Key changes: - Add @ohm-js/wasm dependency - Update imports to use @ohm-js/wasm and @ohm-js/wasm/compat - Replace toAST() with new AstBuilder(mappings).toAst() pattern - Update CST mapping functions for WASM CST structure: - Use opt.when({ Some:..., None:... }) for optional nodes - Use iter.when() for iteration nodes - Adjust numeric indices for non-flattened Opt nodes - Use res.use() for MatchResult lifetime management - Update CstNode types and AstBuilder generics This enables the parser to work in browser environments where CommonJS require() is not available. Co-Authored-By: Patrick Dubroy Co-Authored-By: Claude Opus 4.5 --- packages/liquid-html-parser/package.json | 1 + packages/liquid-html-parser/src/errors.ts | 2 +- .../liquid-html-parser/src/grammar.spec.ts | 6 +- packages/liquid-html-parser/src/grammar.ts | 2 +- .../liquid-html-parser/src/stage-1-cst.ts | 187 ++++++++---------- yarn.lock | 85 ++++---- 6 files changed, 138 insertions(+), 145 deletions(-) diff --git a/packages/liquid-html-parser/package.json b/packages/liquid-html-parser/package.json index cf0fe5ddf..25044bd6a 100644 --- a/packages/liquid-html-parser/package.json +++ b/packages/liquid-html-parser/package.json @@ -32,6 +32,7 @@ "type-check": "tsc --noEmit" }, "dependencies": { + "@ohm-js/wasm": "^0.6.8", "line-column": "^1.0.2", "ohm-js": "^17.0.0" }, diff --git a/packages/liquid-html-parser/src/errors.ts b/packages/liquid-html-parser/src/errors.ts index 254604455..5b4d67d2e 100644 --- a/packages/liquid-html-parser/src/errors.ts +++ b/packages/liquid-html-parser/src/errors.ts @@ -1,5 +1,5 @@ import lineColumn from 'line-column'; -import { MatchResult } from 'ohm-js'; +import { MatchResult } from '@ohm-js/wasm'; import { NodeTypes, Position } from './types'; interface LineColPosition { diff --git a/packages/liquid-html-parser/src/grammar.spec.ts b/packages/liquid-html-parser/src/grammar.spec.ts index 01e08a8d0..0cb27eafb 100644 --- a/packages/liquid-html-parser/src/grammar.spec.ts +++ b/packages/liquid-html-parser/src/grammar.spec.ts @@ -101,7 +101,7 @@ describe('Unit: liquidHtmlGrammar', () => { expectMatchSucceeded('<6h>').to.be.false; function expectMatchSucceeded(text: string) { - const match = grammar.LiquidHTML.match(text, 'Node'); + using match = grammar.LiquidHTML.match(text, 'Node'); return expect(match.succeeded(), text); } }); @@ -129,7 +129,7 @@ describe('Unit: liquidHtmlGrammar', () => { `).to.be.true; function expectMatchSucceeded(text: string) { - const match = grammar.LiquidStatement.match(text.trimStart(), 'Node'); + using match = grammar.LiquidStatement.match(text.trimStart(), 'Node'); return expect(match.succeeded(), text); } }); @@ -156,7 +156,7 @@ describe('Unit: liquidHtmlGrammar', () => { }); function expectMatchSucceeded(text: string) { - const match = placeholderGrammars.LiquidHTML.match(text.trimStart(), 'Node'); + using match = placeholderGrammars.LiquidHTML.match(text.trimStart(), 'Node'); return expect(match.succeeded(), text); } }); diff --git a/packages/liquid-html-parser/src/grammar.ts b/packages/liquid-html-parser/src/grammar.ts index 2d91f00fd..e43266c6d 100644 --- a/packages/liquid-html-parser/src/grammar.ts +++ b/packages/liquid-html-parser/src/grammar.ts @@ -1,4 +1,4 @@ -import { grammars, Grammar } from 'ohm-js'; +import { grammars, Grammar } from '@ohm-js/wasm/compat'; export const liquidHtmlGrammars = grammars(require('../grammar/liquid-html.ohm.js')); diff --git a/packages/liquid-html-parser/src/stage-1-cst.ts b/packages/liquid-html-parser/src/stage-1-cst.ts index f28bd0891..1c65cd0e6 100644 --- a/packages/liquid-html-parser/src/stage-1-cst.ts +++ b/packages/liquid-html-parser/src/stage-1-cst.ts @@ -31,8 +31,9 @@ */ import { Parser } from 'prettier'; -import { Grammar, Node } from 'ohm-js'; -import { toAST } from 'ohm-js/extras'; +import { AstBuilder, CstNode as Node, AstMapping as Mapping } from '@ohm-js/wasm'; +import { Grammar } from '@ohm-js/wasm/compat'; + import { LiquidDocGrammar, LiquidGrammars, @@ -508,10 +509,6 @@ export type LiquidDocConcreteNode = | ConcreteLiquidDocDescriptionNode | ConcreteLiquidDocPromptNode; -interface Mapping { - [k: string]: number | TemplateMapping | TopLevelFunctionMapping; -} - interface TemplateMapping { type: ConcreteNodeTypes; locStart: (node: Node[]) => number; @@ -577,12 +574,11 @@ function toCST( // for the offset of the {% liquid %} markup const locStart = (tokens: Node[]) => offset + tokens[0].source.startIdx; const locEnd = (tokens: Node[]) => offset + tokens[tokens.length - 1].source.endIdx; - const locEndSecondToLast = (tokens: Node[]) => offset + tokens[tokens.length - 2].source.endIdx; const textNode = { type: ConcreteNodeTypes.TextNode, - value: function () { - return (this as any).sourceString; + value: function (this: AstBuilder) { + return this.currNode!.sourceString; }, locStart, locEnd, @@ -591,10 +587,12 @@ function toCST( const res = grammar.match(matchingSource, 'Node'); if (res.failed()) { - throw new LiquidHTMLCSTParsingError(res); + res.use((r) => { + throw new LiquidHTMLCSTParsingError(r); + }); } - const HelperMappings: Mapping = { + const HelperMappings: Mapping = { Node: 0, TextNode: textNode, orderedListOf: 0, @@ -602,14 +600,11 @@ function toCST( empty: () => null, nonemptyOrderedListOf: 0, nonemptyOrderedListOfBoth(nonemptyListOfA: Node, _sep: Node, nonemptyListOfB: Node) { - const self = this as any; - return nonemptyListOfA - .toAST(self.args.mapping) - .concat(nonemptyListOfB.toAST(self.args.mapping)); + return this.toAst(nonemptyListOfA).concat(this.toAst(nonemptyListOfB)); }, }; - const LiquidMappings: Mapping = { + const LiquidMappings: Mapping = { liquidNode: 0, liquidRawTag: 0, liquidRawTagImpl: { @@ -734,7 +729,7 @@ function toCST( const markupNode = nodes[6]; const nameNode = nodes[3]; if (NamedTags.hasOwnProperty(nameNode.sourceString)) { - return markupNode.toAST((this as any).args.mapping); + return this.toAst(markupNode); } return markupNode.sourceString.trim(); }, @@ -753,8 +748,8 @@ function toCST( type: ConcreteNodeTypes.ForMarkup, variableName: 0, collection: 4, - reversed: 6, - args: 8, + reversed: (children: Node[]) => children[5].children[1]?.sourceString ?? null, + args: 7, locStart, locEnd, source, @@ -830,7 +825,7 @@ function toCST( const markupNode = nodes[6]; const nameNode = nodes[3]; if (NamedTags.hasOwnProperty(nameNode.sourceString)) { - return markupNode.toAST((this as any).args.mapping); + return this.toAst(markupNode); } return markupNode.sourceString.trim(); }, @@ -869,7 +864,7 @@ function toCST( liquidTagCycleMarkup: { type: ConcreteNodeTypes.CycleMarkup, groupName: 0, - args: 3, + args: 2, locStart, locEnd, source, @@ -896,22 +891,13 @@ function toCST( source, }, renderArguments: 1, - completionModeRenderArguments: function ( - _0, - namedArguments, - _2, - _3, - _4, - _5, - variableLookup, - _7, - ) { - const self = this as any; - - // variableLookup.sourceString can be '' when there are no incomplete params - return namedArguments - .toAST(self.args.mapping) - .concat(variableLookup.sourceString === '' ? [] : variableLookup.toAST(self.args.mapping)); + completionModeRenderArguments: function (_0, namedArguments, _2, _3, opt) { + return this.toAst(namedArguments).concat( + opt.when({ + Some: (_sep, variableLookup, _space) => this.toAst(variableLookup), + None: () => [], + }), + ); }, snippetExpression: 0, renderVariableExpression: { @@ -949,11 +935,9 @@ function toCST( expression: 0, filters: 1, rawSource: (tokens: Node[]) => - source.slice(locStart(tokens), tokens[tokens.length - 2].source.endIdx).trimEnd(), + source.slice(locStart(tokens), tokens[tokens.length - 1].source.endIdx).trimEnd(), locStart, - // The last node of this rule is a positive lookahead, we don't - // want its endIdx, we want the endIdx of the previous one. - locEnd: locEndSecondToLast, + locEnd, source, }, @@ -963,36 +947,35 @@ function toCST( locStart, locEnd, source, - args(nodes: Node[]) { - // Traditinally, this would get transformed into null or array. But + args([_sp1, _bar, _sp2, ident, opt]: Node[]) { + // Traditionally, this would get transformed into null or array. But // it's better if we have an empty array instead of null here. - if (nodes[7].sourceString === '') { - return []; - } else { - return nodes[7].toAST((this as any).args.mapping); - } + return opt.when({ + Some: (_sp1, _colon, _sp2, args, _opt) => this.toAst(args), + None: () => [], + }); }, }, filterArguments: 0, arguments: 0, - complexArguments: function (completeParams, _space1, _comma, _space2, incompleteParam) { - const self = this as any; - - return completeParams - .toAST(self.args.mapping) - .concat( - incompleteParam.sourceString === '' ? [] : incompleteParam.toAST(self.args.mapping), - ); + complexArguments: function (completeParams, opt) { + return this.toAst(completeParams).concat( + opt.when({ + Some: (_space1, _comma, _space2, incompleteParam) => this.toAst(incompleteParam), + None: () => [], + }), + ); }, simpleArgument: 0, tagArguments: 0, contentForTagArgument: 0, - completionModeContentForTagArgument: function (namedArguments, _separator, variableLookup) { - const self = this as any; - - return namedArguments - .toAST(self.args.mapping) - .concat(variableLookup.sourceString === '' ? [] : variableLookup.toAST(self.args.mapping)); + completionModeContentForTagArgument: function (namedArguments, opt) { + return this.toAst(namedArguments).concat( + opt.when({ + Some: (_sep, variableLookup) => this.toAst(variableLookup), + None: () => [], + }), + ); }, positionalArgument: 0, namedArgument: { @@ -1007,19 +990,16 @@ function toCST( contentForNamedArgument: { type: ConcreteNodeTypes.NamedArgument, name: (node) => node[0].sourceString + node[1].sourceString, - value: 6, + value: 5, locStart, locEnd, source, }, + // @ts-ignore liquidBooleanExpression(initialCondition: Node, subsequentConditions: Node) { - const initialConditionAst = initialCondition.toAST( - (this as any).args.mapping, - ) as ConcreteLiquidCondition; - const subsequentConditionAsts = subsequentConditions.toAST( - (this as any).args.mapping, - ) as ConcreteLiquidCondition[]; + const initialConditionAst = this.toAst(initialCondition) as ConcreteLiquidCondition; + const subsequentConditionAsts = this.toAst(subsequentConditions) as ConcreteLiquidCondition[]; // liquidBooleanExpression can capture too much. If there are no comparisons (e.g. `==`, `>`, etc.) // and we only have a single condition (i.e. no `and` or `or` operators), we can return the expression directly. @@ -1136,7 +1116,7 @@ function toCST( tagMarkup: (n: Node) => n.sourceString.trim(), }; - const LiquidStatement: Mapping = { + const LiquidStatement: Mapping = { LiquidStatement: 0, liquidTagOpenRule: { type: ConcreteNodeTypes.LiquidTagOpen, @@ -1145,14 +1125,14 @@ function toCST( const markupNode = nodes[2]; const nameNode = nodes[0]; if (NamedTags.hasOwnProperty(nameNode.sourceString)) { - return markupNode.toAST((this as any).args.mapping); + return this.toAst(markupNode); } return markupNode.sourceString.trim(); }, whitespaceStart: null, whitespaceEnd: null, locStart, - locEnd: locEndSecondToLast, + locEnd, source, }, @@ -1162,7 +1142,7 @@ function toCST( whitespaceStart: null, whitespaceEnd: null, locStart, - locEnd: locEndSecondToLast, + locEnd, source, }, @@ -1173,14 +1153,14 @@ function toCST( const markupNode = nodes[2]; const nameNode = nodes[0]; if (NamedTags.hasOwnProperty(nameNode.sourceString)) { - return markupNode.toAST((this as any).args.mapping); + return this.toAst(markupNode); } return markupNode.sourceString.trim(); }, whitespaceStart: null, whitespaceEnd: null, locStart, - locEnd: locEndSecondToLast, + locEnd, source, }, @@ -1203,7 +1183,7 @@ function toCST( delimiterWhitespaceStart: null, delimiterWhitespaceEnd: null, locStart, - locEnd: locEndSecondToLast, + locEnd, source, blockStartLocStart: (tokens: Node[]) => offset + tokens[0].source.startIdx, blockStartLocEnd: (tokens: Node[]) => offset + tokens[2].source.endIdx, @@ -1242,8 +1222,8 @@ function toCST( source, blockStartLocStart: (tokens: Node[]) => offset + tokens[0].source.startIdx, blockStartLocEnd: (tokens: Node[]) => offset + tokens[0].source.endIdx, - blockEndLocStart: (tokens: Node[]) => offset + tokens[4].source.startIdx, - blockEndLocEnd: (tokens: Node[]) => offset + tokens[4].source.endIdx, + blockEndLocStart: (tokens: Node[]) => offset + tokens[3].source.startIdx, + blockEndLocEnd: (tokens: Node[]) => offset + tokens[3].source.endIdx, }, liquidInlineComment: { @@ -1253,18 +1233,17 @@ function toCST( whitespaceStart: null, whitespaceEnd: null, locStart, - locEnd: locEndSecondToLast, + locEnd, source, }, }; - const LiquidHTMLMappings: Mapping = { + const LiquidHTMLMappings: Mapping = { Node(frontmatter: Node, nodes: Node) { - const self = this as any; - const frontmatterNode = - frontmatter.sourceString.length === 0 ? [] : [frontmatter.toAST(self.args.mapping)]; + const frontmatterNode: T[] = + frontmatter.sourceString.length === 0 ? [] : [this.toAst(frontmatter)]; - return frontmatterNode.concat(nodes.toAST(self.args.mapping)); + return frontmatterNode.concat(this.toAst(nodes)); }, yamlFrontmatter: { @@ -1295,8 +1274,7 @@ function toCST( type: ConcreteNodeTypes.HtmlRawTag, name: (tokens: Node[]) => tokens[0].children[1].sourceString, attrList(tokens: Node[]) { - const mappings = (this as any).args.mapping; - return tokens[0].children[2].toAST(mappings); + return this.toAst(tokens[0].children[2]); }, body: (tokens: Node[]) => source.slice(tokens[0].source.endIdx, tokens[2].source.startIdx), children: (tokens: Node[]) => { @@ -1322,7 +1300,7 @@ function toCST( HtmlVoidElement: { type: ConcreteNodeTypes.HtmlVoidElement, name: 1, - attrList: 3, + attrList: 2, locStart, locEnd, source, @@ -1359,8 +1337,7 @@ function toCST( trailingTagNamePart: 0, trailingTagNameTextNode: textNode, tagName(leadingPart: Node, trailingParts: Node) { - const mappings = (this as any).args.mapping; - return [leadingPart.toAST(mappings)].concat(trailingParts.toAST(mappings)); + return [this.toAst(leadingPart)].concat(this.toAst(trailingParts)); }, AttrUnquoted: { @@ -1422,8 +1399,7 @@ function toCST( }), {}, ); - - return toAST(res, selectedMappings) as T; + return res.use((r) => new AstBuilder(selectedMappings).toAst(r) as T); } /** @@ -1439,7 +1415,9 @@ function toLiquidDocAST(source: string, matchingSource: string, offset: number) const res = LiquidDocGrammar.match(matchingSource, 'Node'); if (res.failed()) { - throw new LiquidHTMLCSTParsingError(res); + res.use((r) => { + throw new LiquidHTMLCSTParsingError(r); + }); } /** @@ -1447,22 +1425,19 @@ function toLiquidDocAST(source: string, matchingSource: string, offset: number) */ const textNode = () => ({ type: ConcreteNodeTypes.TextNode, - value: function () { - return (this as any).sourceString; + value: function (this: AstBuilder): string { + return this.currNode!.sourceString; }, locStart, locEnd, source, }); - const LiquidDocMappings: Mapping = { + const LiquidDocMappings: Mapping = { Node(implicitDescription: Node, body: Node) { - const self = this as any; const implicitDescriptionNode = - implicitDescription.sourceString.length === 0 - ? [] - : [implicitDescription.toAST(self.args.mapping)]; - return implicitDescriptionNode.concat(body.toAST(self.args.mapping)); + implicitDescription.sourceString.length === 0 ? [] : [this.toAst(implicitDescription)]; + return implicitDescriptionNode.concat(this.toAst(body)); }, ImplicitDescription: { type: ConcreteNodeTypes.LiquidDocDescriptionNode, @@ -1483,7 +1458,7 @@ function toLiquidDocAST(source: string, matchingSource: string, offset: number) source, paramType: 2, paramName: 4, - paramDescription: 8, + paramDescription: 7, }, descriptionNode: { type: ConcreteNodeTypes.LiquidDocDescriptionNode, @@ -1493,8 +1468,8 @@ function toLiquidDocAST(source: string, matchingSource: string, offset: number) source, content: 2, isImplicit: false, - isInline: function (this: Node) { - return !this.children[1].sourceString.includes('\n'); + isInline: function (children) { + return !children[1].sourceString.includes('\n'); }, }, descriptionContent: textNode(), @@ -1524,8 +1499,8 @@ function toLiquidDocAST(source: string, matchingSource: string, offset: number) locEnd, source, content: 2, - isInline: function (this: Node) { - return !this.children[1].sourceString.includes('\n'); + isInline: function (children) { + return !children[1].sourceString.includes('\n'); }, }, promptNode: { @@ -1541,5 +1516,5 @@ function toLiquidDocAST(source: string, matchingSource: string, offset: number) fallbackNode: textNode(), }; - return toAST(res, LiquidDocMappings); + return res.use((r) => new AstBuilder(LiquidDocMappings).toAst(r)); } diff --git a/yarn.lock b/yarn.lock index 6372d7a67..112b71f4e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -910,12 +910,19 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@ohm-js/wasm@^0.6.8": + version "0.6.8" + resolved "https://registry.yarnpkg.com/@ohm-js/wasm/-/wasm-0.6.8.tgz#9436eccee089dcfc4dd79bb493905abd4e7f96df" + integrity sha512-Z8zXCT8A9lTcMgTvLKjx2ZxDL67esf+9P0ymXWoCIBgIEtTswoluW4g/edjXE4WnjhTTQDbPc+bP7IXLgYfefg== + dependencies: + "@wasmgroundup/emit" "^1.0.2" + "@playwright/browser-chromium@^1.57.0": - version "1.58.0" - resolved "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.58.0.tgz#f502cc974253777abdeabff6bb2f8bc078daa3ca" - integrity sha512-nWvMnhcux/fTzlzCBcJZicrsPEKNSaJ9Ad3Ve3sEf5BJY6l1TkYBLcRNx0VlNlziERNvpQBYW8r5xY+zpMPuCw== + version "1.58.1" + resolved "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.58.1.tgz#9281dcfbb0ae6b6fef15cc84917ca9f3566d7f9c" + integrity sha512-L0hO/dvKTjiEyW+ReRdmw1D389qa9XkCCGH58MaSpMdGtHVJzbTsHpAN+fWcAO8Fdc+vTQTZQzjq1uZoEKOGmQ== dependencies: - playwright-core "1.58.0" + playwright-core "1.58.1" "@replit/codemirror-vim@^6.2.1": version "6.2.1" @@ -1769,6 +1776,11 @@ resolved "https://registry.npmjs.org/@vscode/web-custom-data/-/web-custom-data-0.4.6.tgz" integrity sha512-4zen0CzChZ0cvVqttxe9B+G56M3A0pMoaG6Y9784EN2zvr5n61m6JebfYhJ5rzINbTqePEqmwG+7tsdr5g9BJA== +"@wasmgroundup/emit@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@wasmgroundup/emit/-/emit-1.0.2.tgz#8d428e98468562b745aa584ac259334765c7826b" + integrity sha512-9FupNg9RUOQIBe2zNp0DY78Rb03xeElaL9NQB4haz+JDOF+0Bq4tNSuB/vwG4DFTKSffBKFCa85YdVZaf5vQPw== + "@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.12.1": version "1.14.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.14.1.tgz#a9f6a07f2b03c95c8d38c4536a1fdfb521ff55b6" @@ -3195,9 +3207,9 @@ detect-node@^2.0.4: integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== diff@^4.0.1: - version "4.0.4" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.4.tgz#7a6dbfda325f25f07517e9b518f897c08332e07d" - integrity sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ== + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== diff@^5.2.0: version "5.2.2" @@ -5067,17 +5079,17 @@ js-tokens@^4.0.0: integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== js-yaml@^3.13.0, js-yaml@^3.13.1, js-yaml@^3.14.1, js-yaml@^3.6.1: - version "3.14.2" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.2.tgz#77485ce1dd7f33c061fd1b16ecea23b55fcb04b0" - integrity sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg== + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" esprima "^4.0.0" js-yaml@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.1.tgz#854c292467705b699476e1a2decc0c8a3458806b" - integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA== + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" @@ -5189,7 +5201,7 @@ jszip@^3.10.1: readable-stream "~2.3.6" setimmediate "^1.0.5" -jwa@^1.4.2: +jwa@^1.4.1: version "1.4.2" resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.2.tgz#16011ac6db48de7b102777e57897901520eec7b9" integrity sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw== @@ -5199,11 +5211,11 @@ jwa@^1.4.2: safe-buffer "^5.0.1" jws@^3.2.2: - version "3.2.3" - resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.3.tgz#5ac0690b460900a27265de24520526853c0b8ca1" - integrity sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g== + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== dependencies: - jwa "^1.4.2" + jwa "^1.4.1" safe-buffer "^5.0.1" keygrip@~1.1.0: @@ -5438,9 +5450,14 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== -lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.23: +lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lodash@^4.17.23: version "4.17.23" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== log-symbols@^4.1.0: @@ -5876,9 +5893,9 @@ node-fetch@^2.6.11, node-fetch@^2.6.12: whatwg-url "^5.0.0" node-forge@^1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.2.tgz#d0d2659a26eef778bf84d73e7f55c08144ee7750" - integrity sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw== + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== node-releases@^2.0.19: version "2.0.19" @@ -6308,17 +6325,17 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -playwright-core@1.58.0: - version "1.58.0" - resolved "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.0.tgz#f0c74217837e5dda1ecfae78e214fe97112c1f97" - integrity sha512-aaoB1RWrdNi3//rOeKuMiS65UCcgOVljU46At6eFcOFPFHWtd2weHRRow6z/n+Lec0Lvu0k9ZPKJSjPugikirw== +playwright-core@1.58.1: + version "1.58.1" + resolved "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.1.tgz#d63be2c9b7dcbdb035beddd4b42437bd3ca89107" + integrity sha512-bcWzOaTxcW+VOOGBCQgnaKToLJ65d6AqfLVKEWvexyS3AS6rbXl+xdpYRMGSRBClPvyj44njOWoxjNdL/H9UNg== playwright@^1.57.0: - version "1.58.0" - resolved "https://registry.npmjs.org/playwright/-/playwright-1.58.0.tgz#f0a0d9b6b93d153338951ac7fb9dd8d0ea256e53" - integrity sha512-2SVA0sbPktiIY/MCOPX8e86ehA/e+tDNq+e5Y8qjKYti2Z/JG7xnronT/TXTIkKbYGWlCbuucZ6dziEgkoEjQQ== + version "1.58.1" + resolved "https://registry.npmjs.org/playwright/-/playwright-1.58.1.tgz#63300e77a604c77264e1b499c0d94b54ed96d6ba" + integrity sha512-+2uTZHxSCcxjvGc5C891LrS1/NlxglGxzrC4seZiVjcYVQfUa87wBL6rTDqzGjuoWNjnBzRqKmF6zRYGMvQUaQ== dependencies: - playwright-core "1.58.0" + playwright-core "1.58.1" optionalDependencies: fsevents "2.3.2" @@ -8049,9 +8066,9 @@ vite-node@2.1.9: vite "^5.0.0" vite@^5.0.0: - version "5.4.21" - resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.21.tgz#84a4f7c5d860b071676d39ba513c0d598fdc7027" - integrity sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw== + version "5.4.20" + resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.20.tgz#3267a5e03f21212f44edfd72758138e8fcecd76a" + integrity sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g== dependencies: esbuild "^0.21.3" postcss "^8.4.43" From 1de48811b8a25192fdfa44eb100dc58c49b21e8c Mon Sep 17 00:00:00 2001 From: "Charles-P. Clermont" Date: Fri, 30 Jan 2026 16:45:25 -0500 Subject: [PATCH 3/6] Fix browser bundle: define process.env.OHM_DEBUG The @ohm-js/wasm Compiler module references process.env.OHM_DEBUG for debug logging. This doesn't exist in browser environments, causing "process is not defined" errors when the extension activates. Add DefinePlugin entries for both browserClientConfig and browserServerConfig to set this to false at build time. Co-Authored-By: Claude Opus 4.5 --- packages/vscode-extension/webpack.config.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/vscode-extension/webpack.config.js b/packages/vscode-extension/webpack.config.js index 480425844..af97349b0 100644 --- a/packages/vscode-extension/webpack.config.js +++ b/packages/vscode-extension/webpack.config.js @@ -124,6 +124,8 @@ const browserClientConfig = { new webpack.DefinePlugin({ // A flag that lets us do fun webpack-only shenanigans... 'process.env.WEBPACK_MODE': true, + // @ohm-js/wasm Compiler references process.env.OHM_DEBUG for debug logging + 'process.env.OHM_DEBUG': JSON.stringify(false), }), ], }; @@ -153,6 +155,8 @@ const browserServerConfig = async () => { new webpack.DefinePlugin({ // A flag that lets us do fun webpack-only shenanigans... 'process.env.WEBPACK_MODE': true, + // @ohm-js/wasm Compiler references process.env.OHM_DEBUG for debug logging + 'process.env.OHM_DEBUG': JSON.stringify(false), WEBPACK_TAGS: JSON.stringify(tags), WEBPACK_FILTERS: JSON.stringify(filters), WEBPACK_OBJECTS: JSON.stringify(objects), From 39c29d8ad70c5bd5bf3e3e267147c2924bf218a0 Mon Sep 17 00:00:00 2001 From: "Charles-P. Clermont" Date: Sat, 31 Jan 2026 12:04:47 -0500 Subject: [PATCH 4/6] Complete ESM migration for liquid-html-parser The previous ESM commit (542561bb) configured TypeScript but left the source files using CommonJS patterns. This commit completes the migration: - Add `"type": "module"` to package.json (required for Node.js to recognize .js files as ESM) - Add .js extensions to all relative imports (required by ESM spec) - Convert grammar loader from CommonJS require() to ESM import - Rename shims.js to shims.cjs (build script uses CommonJS) - Update shims to generate `export default` instead of `module.exports` - Exclude grammar folder from TypeScript build (prevents overwrite error) Without `"type": "module"`, TypeScript's node20 module setting defaults to CommonJS output based on the package.json type field. Co-Authored-By: Claude Opus 4.5 --- packages/liquid-html-parser/.gitignore | 1 + .../liquid-html-parser/build/{shims.js => shims.cjs} | 2 +- packages/liquid-html-parser/package.json | 3 ++- packages/liquid-html-parser/src/errors.ts | 2 +- packages/liquid-html-parser/src/grammar.ts | 3 ++- packages/liquid-html-parser/src/index.ts | 10 +++++----- packages/liquid-html-parser/src/stage-1-cst.ts | 6 +++--- packages/liquid-html-parser/src/stage-2-ast.ts | 12 ++++++------ packages/liquid-html-parser/src/utils.ts | 2 +- packages/liquid-html-parser/tsconfig.build.json | 2 +- 10 files changed, 23 insertions(+), 20 deletions(-) rename packages/liquid-html-parser/build/{shims.js => shims.cjs} (93%) diff --git a/packages/liquid-html-parser/.gitignore b/packages/liquid-html-parser/.gitignore index d59600e6e..e492cc32c 100644 --- a/packages/liquid-html-parser/.gitignore +++ b/packages/liquid-html-parser/.gitignore @@ -5,6 +5,7 @@ yarn-error.log dawn TODO grammar/liquid-html.ohm.js +grammar/liquid-html.ohm.d.ts standalone.js standalone.js.LICENSE.txt **/actual.liquid diff --git a/packages/liquid-html-parser/build/shims.js b/packages/liquid-html-parser/build/shims.cjs similarity index 93% rename from packages/liquid-html-parser/build/shims.js rename to packages/liquid-html-parser/build/shims.cjs index 56dfdf8d2..eb301b788 100644 --- a/packages/liquid-html-parser/build/shims.js +++ b/packages/liquid-html-parser/build/shims.cjs @@ -5,7 +5,7 @@ const grammarPath = path.join(__dirname, '../grammar'); fs.writeFileSync( path.join(grammarPath, 'liquid-html.ohm.js'), - 'module.exports = ' + + 'export default ' + 'String.raw`' + fs .readFileSync(path.join(grammarPath, 'liquid-html.ohm'), 'utf8') diff --git a/packages/liquid-html-parser/package.json b/packages/liquid-html-parser/package.json index 25044bd6a..f992acb71 100644 --- a/packages/liquid-html-parser/package.json +++ b/packages/liquid-html-parser/package.json @@ -1,6 +1,7 @@ { "name": "@shopify/liquid-html-parser", "version": "2.9.1", + "type": "module", "description": "Liquid HTML parser by Shopify", "author": "CP Clermont ", "homepage": "https://github.com/Shopify/theme-tools/tree/main/packages/liquid-html-parser#readme", @@ -28,7 +29,7 @@ "build": "yarn build:ts", "build:ci": "yarn build", "build:ts": "tsc -p tsconfig.build.json", - "prebuild:ts": "node build/shims.js", + "prebuild:ts": "node build/shims.cjs", "type-check": "tsc --noEmit" }, "dependencies": { diff --git a/packages/liquid-html-parser/src/errors.ts b/packages/liquid-html-parser/src/errors.ts index 5b4d67d2e..d1d46c046 100644 --- a/packages/liquid-html-parser/src/errors.ts +++ b/packages/liquid-html-parser/src/errors.ts @@ -1,6 +1,6 @@ import lineColumn from 'line-column'; import { MatchResult } from '@ohm-js/wasm'; -import { NodeTypes, Position } from './types'; +import { NodeTypes, Position } from './types.js'; interface LineColPosition { line: number; diff --git a/packages/liquid-html-parser/src/grammar.ts b/packages/liquid-html-parser/src/grammar.ts index e43266c6d..9e09bf746 100644 --- a/packages/liquid-html-parser/src/grammar.ts +++ b/packages/liquid-html-parser/src/grammar.ts @@ -1,6 +1,7 @@ import { grammars, Grammar } from '@ohm-js/wasm/compat'; +import liquidHtmlOhm from '../grammar/liquid-html.ohm.js'; -export const liquidHtmlGrammars = grammars(require('../grammar/liquid-html.ohm.js')); +export const liquidHtmlGrammars = grammars(liquidHtmlOhm); export const TextNodeGrammar = liquidHtmlGrammars['Helpers']; export const LiquidDocGrammar = liquidHtmlGrammars['LiquidDoc']; diff --git a/packages/liquid-html-parser/src/index.ts b/packages/liquid-html-parser/src/index.ts index 5c3d2ff80..7a023e8a0 100644 --- a/packages/liquid-html-parser/src/index.ts +++ b/packages/liquid-html-parser/src/index.ts @@ -1,5 +1,5 @@ -export * from './stage-2-ast'; -export * from './types'; -export * from './errors'; -export { TAGS_WITHOUT_MARKUP, RAW_TAGS, VOID_ELEMENTS, BLOCKS } from './grammar'; -export { getConditionalComment } from './conditional-comment'; +export * from './stage-2-ast.js'; +export * from './types.js'; +export * from './errors.js'; +export { TAGS_WITHOUT_MARKUP, RAW_TAGS, VOID_ELEMENTS, BLOCKS } from './grammar.js'; +export { getConditionalComment } from './conditional-comment.js'; diff --git a/packages/liquid-html-parser/src/stage-1-cst.ts b/packages/liquid-html-parser/src/stage-1-cst.ts index 1c65cd0e6..0798c7b38 100644 --- a/packages/liquid-html-parser/src/stage-1-cst.ts +++ b/packages/liquid-html-parser/src/stage-1-cst.ts @@ -41,9 +41,9 @@ import { placeholderGrammars, strictGrammars, tolerantGrammars, -} from './grammar'; -import { LiquidHTMLCSTParsingError } from './errors'; -import { Comparators, NamedTags } from './types'; +} from './grammar.js'; +import { LiquidHTMLCSTParsingError } from './errors.js'; +import { Comparators, NamedTags } from './types.js'; export enum ConcreteNodeTypes { HtmlDoctype = 'HtmlDoctype', diff --git a/packages/liquid-html-parser/src/stage-2-ast.ts b/packages/liquid-html-parser/src/stage-2-ast.ts index e05c298a0..be619d4e1 100644 --- a/packages/liquid-html-parser/src/stage-2-ast.ts +++ b/packages/liquid-html-parser/src/stage-2-ast.ts @@ -75,12 +75,12 @@ import { ConcreteLiquidTagBaseCase, ConcreteLiquidTagContentForMarkup, ConcreteComplexLiquidExpression, -} from './stage-1-cst'; -import { Comparators, NamedTags, NodeTypes, nonTraversableProperties, Position } from './types'; -import { assertNever, deepGet, dropLast } from './utils'; -import { LiquidHTMLASTParsingError, UnclosedNode } from './errors'; -import { TAGS_WITHOUT_MARKUP } from './grammar'; -import { toLiquidCST } from './stage-1-cst'; +} from './stage-1-cst.js'; +import { Comparators, NamedTags, NodeTypes, nonTraversableProperties, Position } from './types.js'; +import { assertNever, deepGet, dropLast } from './utils.js'; +import { LiquidHTMLASTParsingError, UnclosedNode } from './errors.js'; +import { TAGS_WITHOUT_MARKUP } from './grammar.js'; +import { toLiquidCST } from './stage-1-cst.js'; interface HasPosition { locStart: number; diff --git a/packages/liquid-html-parser/src/utils.ts b/packages/liquid-html-parser/src/utils.ts index c55706fae..9755dea86 100644 --- a/packages/liquid-html-parser/src/utils.ts +++ b/packages/liquid-html-parser/src/utils.ts @@ -1,4 +1,4 @@ -import { Position } from './types'; +import { Position } from './types.js'; export function assertNever(x: never): never { throw new Error(`Unexpected object: ${(x as any).type}`); diff --git a/packages/liquid-html-parser/tsconfig.build.json b/packages/liquid-html-parser/tsconfig.build.json index 74b3f9e5d..34926e1f1 100644 --- a/packages/liquid-html-parser/tsconfig.build.json +++ b/packages/liquid-html-parser/tsconfig.build.json @@ -1,5 +1,5 @@ { "extends": "./tsconfig.json", "include": ["src/**/*"], - "exclude": ["node_modules", "src/**/*.spec.ts"] + "exclude": ["node_modules", "src/**/*.spec.ts", "grammar/**/*"] } From 75093c7bafa37558a19e3ebdad8ec3444b340c0e Mon Sep 17 00:00:00 2001 From: "Charles-P. Clermont" Date: Sat, 31 Jan 2026 12:08:45 -0500 Subject: [PATCH 5/6] Fix CI: disable allowJs and generate .d.ts for grammar The CI build failed because TypeScript tried to emit the grammar JS file back to its source location. This happened because: 1. Root tsconfig has `allowJs: true` 2. TypeScript processed the imported grammar file as input 3. With ESM, the grammar file is in the module graph Fix by: - Set `allowJs: false` in tsconfig.build.json to prevent TS from processing the .js file as an input - Generate a .d.ts alongside the .js in shims.cjs to provide types Co-Authored-By: Claude Opus 4.5 --- packages/liquid-html-parser/build/shims.cjs | 8 ++++++++ packages/liquid-html-parser/tsconfig.build.json | 5 ++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/liquid-html-parser/build/shims.cjs b/packages/liquid-html-parser/build/shims.cjs index eb301b788..d7ba8d396 100644 --- a/packages/liquid-html-parser/build/shims.cjs +++ b/packages/liquid-html-parser/build/shims.cjs @@ -3,6 +3,7 @@ const path = require('path'); const grammarPath = path.join(__dirname, '../grammar'); +// Generate the ESM grammar module fs.writeFileSync( path.join(grammarPath, 'liquid-html.ohm.js'), 'export default ' + @@ -13,3 +14,10 @@ fs.writeFileSync( '`;', 'utf8', ); + +// Generate type declaration to prevent TypeScript from emitting one +fs.writeFileSync( + path.join(grammarPath, 'liquid-html.ohm.d.ts'), + 'declare const _default: string;\nexport default _default;\n', + 'utf8', +); diff --git a/packages/liquid-html-parser/tsconfig.build.json b/packages/liquid-html-parser/tsconfig.build.json index 34926e1f1..0676e5748 100644 --- a/packages/liquid-html-parser/tsconfig.build.json +++ b/packages/liquid-html-parser/tsconfig.build.json @@ -1,5 +1,8 @@ { "extends": "./tsconfig.json", "include": ["src/**/*"], - "exclude": ["node_modules", "src/**/*.spec.ts", "grammar/**/*"] + "exclude": ["node_modules", "src/**/*.spec.ts"], + "compilerOptions": { + "allowJs": false + } } From aa7a5ebfdbb4797d934aae3add82f5348eafb898 Mon Sep 17 00:00:00 2001 From: "Charles-P. Clermont" Date: Sat, 31 Jan 2026 12:14:34 -0500 Subject: [PATCH 6/6] Revert ESM changes - keep CommonJS output The previous commits attempted a full ESM migration for liquid-html-parser which broke downstream CommonJS consumers (ERR_REQUIRE_ESM error). Revert to CommonJS output while keeping @ohm-js/wasm imports. This works because: 1. TypeScript's module:node20 outputs CommonJS when there's no "type":"module" 2. Node.js can require() ESM packages from CommonJS code (internal handling) 3. Webpack bundles both CJS and ESM seamlessly Key reversions: - Remove "type": "module" from package.json - Revert grammar.ts to use require() for grammar file - Revert shims.cjs to generate module.exports - Remove .js extensions from relative imports - Remove allowJs:false and .d.ts generation This reverts commits 39c29d8a and 75093c7b. Co-Authored-By: Claude Opus 4.5 --- packages/liquid-html-parser/.gitignore | 1 - packages/liquid-html-parser/build/shims.cjs | 10 +--------- packages/liquid-html-parser/package.json | 1 - packages/liquid-html-parser/src/errors.ts | 2 +- packages/liquid-html-parser/src/grammar.ts | 3 +-- packages/liquid-html-parser/src/index.ts | 10 +++++----- packages/liquid-html-parser/src/stage-1-cst.ts | 6 +++--- packages/liquid-html-parser/src/stage-2-ast.ts | 12 ++++++------ packages/liquid-html-parser/src/utils.ts | 2 +- packages/liquid-html-parser/tsconfig.build.json | 5 +---- 10 files changed, 19 insertions(+), 33 deletions(-) diff --git a/packages/liquid-html-parser/.gitignore b/packages/liquid-html-parser/.gitignore index e492cc32c..d59600e6e 100644 --- a/packages/liquid-html-parser/.gitignore +++ b/packages/liquid-html-parser/.gitignore @@ -5,7 +5,6 @@ yarn-error.log dawn TODO grammar/liquid-html.ohm.js -grammar/liquid-html.ohm.d.ts standalone.js standalone.js.LICENSE.txt **/actual.liquid diff --git a/packages/liquid-html-parser/build/shims.cjs b/packages/liquid-html-parser/build/shims.cjs index d7ba8d396..56dfdf8d2 100644 --- a/packages/liquid-html-parser/build/shims.cjs +++ b/packages/liquid-html-parser/build/shims.cjs @@ -3,10 +3,9 @@ const path = require('path'); const grammarPath = path.join(__dirname, '../grammar'); -// Generate the ESM grammar module fs.writeFileSync( path.join(grammarPath, 'liquid-html.ohm.js'), - 'export default ' + + 'module.exports = ' + 'String.raw`' + fs .readFileSync(path.join(grammarPath, 'liquid-html.ohm'), 'utf8') @@ -14,10 +13,3 @@ fs.writeFileSync( '`;', 'utf8', ); - -// Generate type declaration to prevent TypeScript from emitting one -fs.writeFileSync( - path.join(grammarPath, 'liquid-html.ohm.d.ts'), - 'declare const _default: string;\nexport default _default;\n', - 'utf8', -); diff --git a/packages/liquid-html-parser/package.json b/packages/liquid-html-parser/package.json index f992acb71..248c9e75a 100644 --- a/packages/liquid-html-parser/package.json +++ b/packages/liquid-html-parser/package.json @@ -1,7 +1,6 @@ { "name": "@shopify/liquid-html-parser", "version": "2.9.1", - "type": "module", "description": "Liquid HTML parser by Shopify", "author": "CP Clermont ", "homepage": "https://github.com/Shopify/theme-tools/tree/main/packages/liquid-html-parser#readme", diff --git a/packages/liquid-html-parser/src/errors.ts b/packages/liquid-html-parser/src/errors.ts index d1d46c046..5b4d67d2e 100644 --- a/packages/liquid-html-parser/src/errors.ts +++ b/packages/liquid-html-parser/src/errors.ts @@ -1,6 +1,6 @@ import lineColumn from 'line-column'; import { MatchResult } from '@ohm-js/wasm'; -import { NodeTypes, Position } from './types.js'; +import { NodeTypes, Position } from './types'; interface LineColPosition { line: number; diff --git a/packages/liquid-html-parser/src/grammar.ts b/packages/liquid-html-parser/src/grammar.ts index 9e09bf746..e43266c6d 100644 --- a/packages/liquid-html-parser/src/grammar.ts +++ b/packages/liquid-html-parser/src/grammar.ts @@ -1,7 +1,6 @@ import { grammars, Grammar } from '@ohm-js/wasm/compat'; -import liquidHtmlOhm from '../grammar/liquid-html.ohm.js'; -export const liquidHtmlGrammars = grammars(liquidHtmlOhm); +export const liquidHtmlGrammars = grammars(require('../grammar/liquid-html.ohm.js')); export const TextNodeGrammar = liquidHtmlGrammars['Helpers']; export const LiquidDocGrammar = liquidHtmlGrammars['LiquidDoc']; diff --git a/packages/liquid-html-parser/src/index.ts b/packages/liquid-html-parser/src/index.ts index 7a023e8a0..5c3d2ff80 100644 --- a/packages/liquid-html-parser/src/index.ts +++ b/packages/liquid-html-parser/src/index.ts @@ -1,5 +1,5 @@ -export * from './stage-2-ast.js'; -export * from './types.js'; -export * from './errors.js'; -export { TAGS_WITHOUT_MARKUP, RAW_TAGS, VOID_ELEMENTS, BLOCKS } from './grammar.js'; -export { getConditionalComment } from './conditional-comment.js'; +export * from './stage-2-ast'; +export * from './types'; +export * from './errors'; +export { TAGS_WITHOUT_MARKUP, RAW_TAGS, VOID_ELEMENTS, BLOCKS } from './grammar'; +export { getConditionalComment } from './conditional-comment'; diff --git a/packages/liquid-html-parser/src/stage-1-cst.ts b/packages/liquid-html-parser/src/stage-1-cst.ts index 0798c7b38..1c65cd0e6 100644 --- a/packages/liquid-html-parser/src/stage-1-cst.ts +++ b/packages/liquid-html-parser/src/stage-1-cst.ts @@ -41,9 +41,9 @@ import { placeholderGrammars, strictGrammars, tolerantGrammars, -} from './grammar.js'; -import { LiquidHTMLCSTParsingError } from './errors.js'; -import { Comparators, NamedTags } from './types.js'; +} from './grammar'; +import { LiquidHTMLCSTParsingError } from './errors'; +import { Comparators, NamedTags } from './types'; export enum ConcreteNodeTypes { HtmlDoctype = 'HtmlDoctype', diff --git a/packages/liquid-html-parser/src/stage-2-ast.ts b/packages/liquid-html-parser/src/stage-2-ast.ts index be619d4e1..e05c298a0 100644 --- a/packages/liquid-html-parser/src/stage-2-ast.ts +++ b/packages/liquid-html-parser/src/stage-2-ast.ts @@ -75,12 +75,12 @@ import { ConcreteLiquidTagBaseCase, ConcreteLiquidTagContentForMarkup, ConcreteComplexLiquidExpression, -} from './stage-1-cst.js'; -import { Comparators, NamedTags, NodeTypes, nonTraversableProperties, Position } from './types.js'; -import { assertNever, deepGet, dropLast } from './utils.js'; -import { LiquidHTMLASTParsingError, UnclosedNode } from './errors.js'; -import { TAGS_WITHOUT_MARKUP } from './grammar.js'; -import { toLiquidCST } from './stage-1-cst.js'; +} from './stage-1-cst'; +import { Comparators, NamedTags, NodeTypes, nonTraversableProperties, Position } from './types'; +import { assertNever, deepGet, dropLast } from './utils'; +import { LiquidHTMLASTParsingError, UnclosedNode } from './errors'; +import { TAGS_WITHOUT_MARKUP } from './grammar'; +import { toLiquidCST } from './stage-1-cst'; interface HasPosition { locStart: number; diff --git a/packages/liquid-html-parser/src/utils.ts b/packages/liquid-html-parser/src/utils.ts index 9755dea86..c55706fae 100644 --- a/packages/liquid-html-parser/src/utils.ts +++ b/packages/liquid-html-parser/src/utils.ts @@ -1,4 +1,4 @@ -import { Position } from './types.js'; +import { Position } from './types'; export function assertNever(x: never): never { throw new Error(`Unexpected object: ${(x as any).type}`); diff --git a/packages/liquid-html-parser/tsconfig.build.json b/packages/liquid-html-parser/tsconfig.build.json index 0676e5748..74b3f9e5d 100644 --- a/packages/liquid-html-parser/tsconfig.build.json +++ b/packages/liquid-html-parser/tsconfig.build.json @@ -1,8 +1,5 @@ { "extends": "./tsconfig.json", "include": ["src/**/*"], - "exclude": ["node_modules", "src/**/*.spec.ts"], - "compilerOptions": { - "allowJs": false - } + "exclude": ["node_modules", "src/**/*.spec.ts"] }