From b5aee913bdb73d1980529118fdaf07cfc015f663 Mon Sep 17 00:00:00 2001 From: Dylan Piercey Date: Thu, 6 Aug 2026 16:01:15 -0700 Subject: [PATCH] Extract parser into new @marko/parse package --- .changeset/language-tools-parse.md | 5 + .changeset/parse-package.md | 5 + .../index.ts | 34 +- .../attr-tags-dynamic-for.expected/index.ts | 372 +++++++++--------- .../attr-tags-dynamic-while.expected/index.ts | 2 +- .../index.ts | 52 ++- .../attr-tags-for-narrowing.expected/index.ts | 2 - .../attr-tags-generic.expected/index.ts | 2 +- .../index.ts | 36 +- .../attr-tags-nested-type.expected/index.ts | 1 - .../attr-tags-params-js.expected/index.js | 42 +- .../attr-tags-params.expected/index.ts | 42 +- .../index.ts | 2 +- .../__snapshots__/basic.expected/index.ts | 3 +- .../index.ts | 1 + .../bound-attr-const-ident.expected/index.ts | 1 + .../index.ts | 1 + .../index.ts | 1 + .../index.ts | 1 + .../index.ts | 1 + .../index.ts | 1 + .../index.ts | 1 + .../index.ts | 1 + .../index.ts | 1 + .../class-api-type-no-state.expected/index.ts | 1 + .../index.ts | 1 + .../index.ts | 1 + .../index.ts | 1 + .../index.ts | 1 + .../index.ts | 1 + .../index.ts | 1 + .../el-ref-hoist.expected/index.ts | 3 +- .../for-tag-input.expected/index.ts | 142 ++++--- .../__snapshots__/for-tag.expected/index.ts | 84 ++-- .../index.ts | 1 + .../input-type-basic.expected/index.ts | 1 + .../index.ts | 1 + .../input-type-duplicated.expected/index.ts | 1 + .../index.ts | 1 + .../input-type-import-type.expected/index.ts | 1 + .../input-type-import.expected/index.ts | 1 + .../index.ts | 1 + .../index.ts | 1 + .../index.html | 1 + .../leading-comment-pragma.expected/index.md | 0 .../leading-comment-pragma.expected/index.ts | 55 +++ .../script/leading-comment-pragma/index.marko | 2 + .../mutate-within-attr-tag.expected/index.ts | 1 - .../program-text-only.expected/index.ts | 1 + .../recursive-define-tag.expected/index.ts | 8 +- .../return-as-type.expected/index.ts | 1 - .../return-as-type.expected/tags/oneOrTwo.ts | 1 + .../return-tag-nested.expected/index.ts | 2 +- .../split-component-d-ts.expected/index.ts | 1 + .../tag-params-basic.expected/index.ts | 2 +- .../index.ts | 1 - .../tag-type-params.expected/index.ts | 3 +- .../index.ts | 2 +- .../tags-api-basic.expected/index.ts | 24 +- .../__snapshots__/while-tag.expected/index.ts | 2 +- .../src/service/marko/document-links.ts | 2 +- .../src/service/marko/document-symbols.ts | 2 +- .../src/service/script/index.ts | 10 +- packages/language-tools/package.json | 2 +- .../src/extractors/css-module/index.ts | 3 +- .../src/extractors/html/index.ts | 14 +- .../src/extractors/script/index.ts | 105 +++-- .../extractors/script/util/attach-scopes.ts | 6 +- .../extractors/script/util/get-runtime-api.ts | 7 +- .../script/util/is-text-only-script.ts | 2 +- .../script/util/jsdoc-input-type.ts | 3 +- .../extractors/script/util/script-parser.ts | 2 +- .../src/extractors/style/index.ts | 4 +- packages/language-tools/src/index.ts | 2 +- .../language-tools/src/processors/marko.ts | 2 +- packages/language-tools/src/util/extractor.ts | 2 +- .../src/util/find-style-selectors.ts | 2 +- packages/language-tools/tsconfig.json | 7 +- packages/parse/.mocharc.json | 5 + packages/parse/README.md | 30 ++ packages/parse/build.mts | 35 ++ packages/parse/package.json | 42 ++ packages/parse/src/__tests__/parse.test.ts | 71 ++++ .../util => parse/src}/get-node-at-offset.ts | 7 +- .../src/parser.ts => parse/src/index.ts} | 348 +++++++++------- packages/parse/src/normalize-path.ts | 8 + packages/parse/tsconfig.json | 10 + pnpm-lock.yaml | 16 +- tsconfig.json | 1 + 89 files changed, 1053 insertions(+), 659 deletions(-) create mode 100644 .changeset/language-tools-parse.md create mode 100644 .changeset/parse-package.md create mode 100644 packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/__snapshots__/leading-comment-pragma.expected/index.html create mode 100644 packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/__snapshots__/leading-comment-pragma.expected/index.md create mode 100644 packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/__snapshots__/leading-comment-pragma.expected/index.ts create mode 100644 packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/index.marko create mode 100644 packages/parse/.mocharc.json create mode 100644 packages/parse/README.md create mode 100644 packages/parse/build.mts create mode 100644 packages/parse/package.json create mode 100644 packages/parse/src/__tests__/parse.test.ts rename packages/{language-tools/src/util => parse/src}/get-node-at-offset.ts (94%) rename packages/{language-tools/src/parser.ts => parse/src/index.ts} (74%) create mode 100644 packages/parse/src/normalize-path.ts create mode 100644 packages/parse/tsconfig.json diff --git a/.changeset/language-tools-parse.md b/.changeset/language-tools-parse.md new file mode 100644 index 00000000..e9b7bdc7 --- /dev/null +++ b/.changeset/language-tools-parse.md @@ -0,0 +1,5 @@ +--- +"@marko/language-tools": major +--- + +The parser now lives in (and is re-exported from) the new `@marko/parse` package. Breaking changes for consumers of the parser API: `NodeType` values are now strings rather than numbers, `program.body` now includes comment and static statement nodes in document order (`Node.ChildNode` includes `Comment`, and `program.body` is typed as `Node.RootBodyNode[]`), `Comment` nodes carry a `commentType`, and `Static` nodes carry `target`/`name`. Additions: `parse` accepts an options argument with a `getTagType` hook, the parse result exposes `errors` and a flat `comments` list, and the full `htmljs-parser` surface is re-exported. diff --git a/.changeset/parse-package.md b/.changeset/parse-package.md new file mode 100644 index 00000000..e8599532 --- /dev/null +++ b/.changeset/parse-package.md @@ -0,0 +1,5 @@ +--- +"@marko/parse": major +--- + +New package: the Marko CST parser previously embedded in `@marko/language-tools`, extracted so it can be shared by other tooling. Node types are strings, syntax errors and comments are exposed on the parse result, comments and static statements are part of `program.body`, and a `getTagType` option allows overriding how tag bodies parse. diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-for-typed/__snapshots__/attr-tags-dynamic-for-typed.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-for-typed/__snapshots__/attr-tags-dynamic-for-typed.expected/index.ts index 4b8bbca1..52a37397 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-for-typed/__snapshots__/attr-tags-dynamic-for-typed.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-for-typed/__snapshots__/attr-tags-dynamic-for-typed.expected/index.ts @@ -34,6 +34,7 @@ export { type Component }; }; })(), }); + // ^? return Marko._.voidReturn; }, [Symbol.iterator]: Marko._.any, @@ -48,25 +49,23 @@ export { type Component }; Marko._.attrTagNames(__marko_internal_tag_2, (input) => { input["@menuitem"]; }); - Marko._.renderTemplate(__marko_internal_tag_2)()()( - // ^? - { - ["menuitem"]: { - [Marko._.contentFor(__marko_internal_tag_2)]: ({ foo }) => { - Marko._.renderNativeTag("div")()()({ - [Marko._.content]: (() => { - foo; - return () => { - return Marko._.voidReturn; - }; - })(), - }); - return Marko._.voidReturn; - }, - [Symbol.iterator]: Marko._.any, + Marko._.renderTemplate(__marko_internal_tag_2)()()({ + ["menuitem"]: { + [Marko._.contentFor(__marko_internal_tag_2)]: ({ foo }) => { + Marko._.renderNativeTag("div")()()({ + [Marko._.content]: (() => { + foo; + return () => { + return Marko._.voidReturn; + }; + })(), + }); + // ^? + return Marko._.voidReturn; }, + [Symbol.iterator]: Marko._.any, }, - ); + }); Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); @@ -107,4 +106,3 @@ export default new (class Template extends Marko._.Template<{ Marko._.Relate<__marko_internal_input, Marko.Directives & Input>, ) => Marko._.ReturnWithScope<__marko_internal_input, void>; }> {})(); -// ^? diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-for/__snapshots__/attr-tags-dynamic-for.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-for/__snapshots__/attr-tags-dynamic-for.expected/index.ts index 8be84be3..244a2af8 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-for/__snapshots__/attr-tags-dynamic-for.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-for/__snapshots__/attr-tags-dynamic-for.expected/index.ts @@ -84,6 +84,7 @@ export interface Input {} item; index; all; + // ^? ^? ^? return () => { return Marko._.voidReturn; }; @@ -99,84 +100,79 @@ export interface Input {} input["@a"]; input["@b"]; }); - Marko._.renderDynamicTag(__marko_internal_tag_5)()()( - // ^? ^? ^? - { - ...Marko._.forOfAttrTag( - { - of: list, + Marko._.renderDynamicTag(__marko_internal_tag_5)()()({ + ...Marko._.forOfAttrTag( + { + of: list, + }, + (item, index) => ({ + ["a"]: { + [Marko._.contentFor(__marko_internal_tag_5)]: (() => { + item; + // ^? + return () => { + return Marko._.voidReturn; + }; + })(), + [Symbol.iterator]: Marko._.any, }, - (item, index) => ({ - ["a"]: { - [Marko._.contentFor(__marko_internal_tag_5)]: (() => { - item; - return () => { - return Marko._.voidReturn; - }; - })(), - [Symbol.iterator]: Marko._.any, - }, - // ^? - ["b"]: { - [Marko._.contentFor(__marko_internal_tag_5)]: (() => { - index; - return () => { - return Marko._.voidReturn; - }; - })(), - [Symbol.iterator]: Marko._.any, - }, - }), - Marko._.input(__marko_internal_tag_5), - ), - }, - ); + ["b"]: { + [Marko._.contentFor(__marko_internal_tag_5)]: (() => { + index; + // ^? + return () => { + return Marko._.voidReturn; + }; + })(), + [Symbol.iterator]: Marko._.any, + }, + }), + Marko._.input(__marko_internal_tag_5), + ), + }); const __marko_internal_tag_6 = custom; Marko._.attrTagNames(__marko_internal_tag_6, (input) => { input["@a"]; }); const __marko_internal_rendered_6 = Marko._.renderDynamicTag( __marko_internal_tag_6, - )()()( - // ^? - { - ...Marko._.forOfAttrTag( - { - of: list, - }, - (item) => ({ - ["a"]: { - [Marko._.contentFor(__marko_internal_tag_6)]: (() => { - const __marko_internal_tag_7 = Marko._.resolveTemplate( - import("@marko/runtime-tags/tags/const.d.marko"), + )()()({ + ...Marko._.forOfAttrTag( + { + of: list, + }, + (item) => ({ + ["a"]: { + [Marko._.contentFor(__marko_internal_tag_6)]: (() => { + const __marko_internal_tag_7 = Marko._.resolveTemplate( + import("@marko/runtime-tags/tags/const.d.marko"), + ); + { + const hoistedFromForOf = Marko._.returned( + () => __marko_internal_rendered_7, ); - { - const hoistedFromForOf = Marko._.returned( - () => __marko_internal_rendered_7, - ); - const __marko_internal_rendered_7 = Marko._.renderTemplate( - __marko_internal_tag_7, - )()()({ - value() { - return item.value; - }, - }); - return () => { - return new (class MarkoReturn { - readonly [Marko._.scope] = { hoistedFromForOf }; - declare return: Return; - constructor(_?: Return) {} - })(); - }; - } - })(), - [Symbol.iterator]: Marko._.any, - }, - }), - Marko._.input(__marko_internal_tag_6), - ), - }, - ); + const __marko_internal_rendered_7 = Marko._.renderTemplate( + __marko_internal_tag_7, + )()()({ + value() { + return item.value; + }, + }); + return () => { + return new (class MarkoReturn { + readonly [Marko._.scope] = { hoistedFromForOf }; + declare return: Return; + constructor(_?: Return) {} + })(); + }; + } + })(), + [Symbol.iterator]: Marko._.any, + }, + }), + Marko._.input(__marko_internal_tag_6), + ), + }); const __marko_internal_tag_8 = Marko._.resolveTemplate( import("@marko/runtime-tags/tags/effect.d.marko"), ); @@ -210,6 +206,7 @@ export interface Input {} [Marko._.contentFor(__marko_internal_tag_10)]: (() => { key; value; + // ^? ^? return () => { return Marko._.voidReturn; }; @@ -226,46 +223,43 @@ export interface Input {} }); const __marko_internal_rendered_11 = Marko._.renderDynamicTag( __marko_internal_tag_11, - )()()( - // ^? ^? - { - ...Marko._.forInAttrTag( - { - in: record, - }, - (key) => ({ - ["a"]: { - [Marko._.contentFor(__marko_internal_tag_11)]: (() => { - const __marko_internal_tag_12 = Marko._.resolveTemplate( - import("@marko/runtime-tags/tags/const.d.marko"), + )()()({ + ...Marko._.forInAttrTag( + { + in: record, + }, + (key) => ({ + ["a"]: { + [Marko._.contentFor(__marko_internal_tag_11)]: (() => { + const __marko_internal_tag_12 = Marko._.resolveTemplate( + import("@marko/runtime-tags/tags/const.d.marko"), + ); + { + const hoistedFromForIn = Marko._.returned( + () => __marko_internal_rendered_12, ); - { - const hoistedFromForIn = Marko._.returned( - () => __marko_internal_rendered_12, - ); - const __marko_internal_rendered_12 = Marko._.renderTemplate( - __marko_internal_tag_12, - )()()({ - value() { - return key; - }, - }); - return () => { - return new (class MarkoReturn { - readonly [Marko._.scope] = { hoistedFromForIn }; - declare return: Return; - constructor(_?: Return) {} - })(); - }; - } - })(), - [Symbol.iterator]: Marko._.any, - }, - }), - Marko._.input(__marko_internal_tag_11), - ), - }, - ); + const __marko_internal_rendered_12 = Marko._.renderTemplate( + __marko_internal_tag_12, + )()()({ + value() { + return key; + }, + }); + return () => { + return new (class MarkoReturn { + readonly [Marko._.scope] = { hoistedFromForIn }; + declare return: Return; + constructor(_?: Return) {} + })(); + }; + } + })(), + [Symbol.iterator]: Marko._.any, + }, + }), + Marko._.input(__marko_internal_tag_11), + ), + }); const __marko_internal_tag_13 = Marko._.resolveTemplate( import("@marko/runtime-tags/tags/effect.d.marko"), ); @@ -288,6 +282,7 @@ export interface Input {} ["a"]: { [Marko._.contentFor(__marko_internal_tag_14)]: (() => { index; + // ^? return () => { return Marko._.voidReturn; }; @@ -302,102 +297,95 @@ export interface Input {} Marko._.attrTagNames(__marko_internal_tag_15, (input) => { input["@a"]; }); - Marko._.renderDynamicTag(__marko_internal_tag_15)()()( - // ^? - { - ...Marko._.forToAttrTag( - { - from: 1, - to: 10, + Marko._.renderDynamicTag(__marko_internal_tag_15)()()({ + ...Marko._.forToAttrTag( + { + from: 1, + to: 10, + }, + (index) => ({ + ["a"]: { + [Marko._.contentFor(__marko_internal_tag_15)]: (() => { + index; + // ^? + return () => { + return Marko._.voidReturn; + }; + })(), + [Symbol.iterator]: Marko._.any, }, - (index) => ({ - ["a"]: { - [Marko._.contentFor(__marko_internal_tag_15)]: (() => { - index; - return () => { - return Marko._.voidReturn; - }; - })(), - [Symbol.iterator]: Marko._.any, - }, - }), - Marko._.input(__marko_internal_tag_15), - ), - }, - ); + }), + Marko._.input(__marko_internal_tag_15), + ), + }); const __marko_internal_tag_16 = custom; Marko._.attrTagNames(__marko_internal_tag_16, (input) => { input["@a"]; }); - Marko._.renderDynamicTag(__marko_internal_tag_16)()()( - // ^? - { - ...Marko._.forToAttrTag( - { - to: 10, - step: 2, + Marko._.renderDynamicTag(__marko_internal_tag_16)()()({ + ...Marko._.forToAttrTag( + { + to: 10, + step: 2, + }, + (index) => ({ + ["a"]: { + [Marko._.contentFor(__marko_internal_tag_16)]: (() => { + index; + // ^? + return () => { + return Marko._.voidReturn; + }; + })(), + [Symbol.iterator]: Marko._.any, }, - (index) => ({ - ["a"]: { - [Marko._.contentFor(__marko_internal_tag_16)]: (() => { - index; - return () => { - return Marko._.voidReturn; - }; - })(), - [Symbol.iterator]: Marko._.any, - }, - }), - Marko._.input(__marko_internal_tag_16), - ), - }, - ); + }), + Marko._.input(__marko_internal_tag_16), + ), + }); const __marko_internal_tag_17 = custom; Marko._.attrTagNames(__marko_internal_tag_17, (input) => { input["@a"]; }); const __marko_internal_rendered_17 = Marko._.renderDynamicTag( __marko_internal_tag_17, - )()()( - // ^? - { - ...Marko._.forToAttrTag( - { - to: 10, - }, - (index) => ({ - ["a"]: { - [Marko._.contentFor(__marko_internal_tag_17)]: (() => { - const __marko_internal_tag_18 = Marko._.resolveTemplate( - import("@marko/runtime-tags/tags/const.d.marko"), + )()()({ + ...Marko._.forToAttrTag( + { + to: 10, + }, + (index) => ({ + ["a"]: { + [Marko._.contentFor(__marko_internal_tag_17)]: (() => { + const __marko_internal_tag_18 = Marko._.resolveTemplate( + import("@marko/runtime-tags/tags/const.d.marko"), + ); + { + const hoistedFromForTo = Marko._.returned( + () => __marko_internal_rendered_18, ); - { - const hoistedFromForTo = Marko._.returned( - () => __marko_internal_rendered_18, - ); - const __marko_internal_rendered_18 = Marko._.renderTemplate( - __marko_internal_tag_18, - )()()({ - value() { - return index; - }, - }); - return () => { - return new (class MarkoReturn { - readonly [Marko._.scope] = { hoistedFromForTo }; - declare return: Return; - constructor(_?: Return) {} - })(); - }; - } - })(), - [Symbol.iterator]: Marko._.any, - }, - }), - Marko._.input(__marko_internal_tag_17), - ), - }, - ); + const __marko_internal_rendered_18 = Marko._.renderTemplate( + __marko_internal_tag_18, + )()()({ + value() { + return index; + }, + }); + return () => { + return new (class MarkoReturn { + readonly [Marko._.scope] = { hoistedFromForTo }; + declare return: Return; + constructor(_?: Return) {} + })(); + }; + } + })(), + [Symbol.iterator]: Marko._.any, + }, + }), + Marko._.input(__marko_internal_tag_17), + ), + }); const __marko_internal_tag_19 = Marko._.resolveTemplate( import("@marko/runtime-tags/tags/effect.d.marko"), ); diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-while/__snapshots__/attr-tags-dynamic-while.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-while/__snapshots__/attr-tags-dynamic-while.expected/index.ts index fe0f476e..51f40fc8 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-while/__snapshots__/attr-tags-dynamic-while.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-while/__snapshots__/attr-tags-dynamic-while.expected/index.ts @@ -23,6 +23,7 @@ export { type Component }; ["a"]: { [Marko._.contentFor(__marko_internal_tag_1)]: (() => { i; + // ^? return () => { return Marko._.voidReturn; }; @@ -32,7 +33,6 @@ export { type Component }; } : {}), }); - // ^? let done = false; i = 0; const __marko_internal_tag_2 = custom; diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-with-scriptlets/__snapshots__/attr-tags-dynamic-with-scriptlets.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-with-scriptlets/__snapshots__/attr-tags-dynamic-with-scriptlets.expected/index.ts index b90188af..6584c9b3 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-with-scriptlets/__snapshots__/attr-tags-dynamic-with-scriptlets.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-dynamic-with-scriptlets/__snapshots__/attr-tags-dynamic-with-scriptlets.expected/index.ts @@ -59,6 +59,7 @@ export { type Component }; ["a"]: { [Marko._.contentFor(__marko_internal_tag_2)]: (() => { doubleIndex; + // ^? return () => { return Marko._.voidReturn; }; @@ -75,33 +76,30 @@ export { type Component }; input["@a"]; input["@b"]; }); - Marko._.renderDynamicTag(__marko_internal_tag_3)()()( - // ^? - { - x: 1, - ...(x - ? (() => { - const a = 1 as const; - return { - // ^? - ["a"]: { - a: a, - [Symbol.iterator]: Marko._.any, - }, - }; - })() - : (() => { - const b = 2 as const; - return { - // ^? - ["b"]: { - b: b, - [Symbol.iterator]: Marko._.any, - }, - }; - })()), - }, - ); + Marko._.renderDynamicTag(__marko_internal_tag_3)()()({ + x: 1, + ...(x + ? (() => { + const a = 1 as const; + return { + // ^? + ["a"]: { + a: a, + [Symbol.iterator]: Marko._.any, + }, + }; + })() + : (() => { + const b = 2 as const; + return { + // ^? + ["b"]: { + b: b, + [Symbol.iterator]: Marko._.any, + }, + }; + })()), + }); Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-for-narrowing/__snapshots__/attr-tags-for-narrowing.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/attr-tags-for-narrowing/__snapshots__/attr-tags-for-narrowing.expected/index.ts index 421a3e4f..8ef45aa4 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-for-narrowing/__snapshots__/attr-tags-for-narrowing.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-for-narrowing/__snapshots__/attr-tags-for-narrowing.expected/index.ts @@ -37,7 +37,6 @@ export { type Component }; { of: [1, 2, 3], }, - // ^? () => ({ ["item"]: Marko._.attrTagFor(__marko_internal_tag_1, "item")( "item", @@ -100,4 +99,3 @@ export default new (class Template extends Marko._.Template<{ Marko._.Relate<__marko_internal_input, Marko.Directives & Input>, ) => Marko._.ReturnWithScope<__marko_internal_input, void>; }> {})(); -// ^? diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-generic/__snapshots__/attr-tags-generic.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/attr-tags-generic/__snapshots__/attr-tags-generic.expected/index.ts index 5208fd77..218d765e 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-generic/__snapshots__/attr-tags-generic.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-generic/__snapshots__/attr-tags-generic.expected/index.ts @@ -47,6 +47,7 @@ export { type Component }; i; //^? foo; + //^? return Marko._.voidReturn; }, [Symbol.iterator]: Marko._.any, @@ -95,4 +96,3 @@ export default new (class Template extends Marko._.Template<{ Marko._.Relate<__marko_internal_input, Marko.Directives & Input>, ) => Marko._.ReturnWithScope<__marko_internal_input, void>; }> {})(); -//^? diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-dynamic-for-typed/__snapshots__/attr-tags-nested-dynamic-for-typed.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-dynamic-for-typed/__snapshots__/attr-tags-nested-dynamic-for-typed.expected/index.ts index 88a9017c..6dab8d27 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-dynamic-for-typed/__snapshots__/attr-tags-nested-dynamic-for-typed.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-dynamic-for-typed/__snapshots__/attr-tags-nested-dynamic-for-typed.expected/index.ts @@ -38,6 +38,7 @@ export { type Component }; }; })(), }); + // ^? return Marko._.voidReturn; }, [Symbol.iterator]: Marko._.any, @@ -57,28 +58,26 @@ export { type Component }; input["@item"]; }); }); - Marko._.renderTemplate(__marko_internal_tag_2)()()( - // ^? - { - ["section"]: { - ["item"]: { - [Marko._.contentFor(__marko_internal_tag_2)]: ({ label }) => { - Marko._.renderNativeTag("div")()()({ - [Marko._.content]: (() => { - label; - return () => { - return Marko._.voidReturn; - }; - })(), - }); - return Marko._.voidReturn; - }, - [Symbol.iterator]: Marko._.any, + Marko._.renderTemplate(__marko_internal_tag_2)()()({ + ["section"]: { + ["item"]: { + [Marko._.contentFor(__marko_internal_tag_2)]: ({ label }) => { + Marko._.renderNativeTag("div")()()({ + [Marko._.content]: (() => { + label; + return () => { + return Marko._.voidReturn; + }; + })(), + }); + // ^? + return Marko._.voidReturn; }, [Symbol.iterator]: Marko._.any, }, + [Symbol.iterator]: Marko._.any, }, - ); + }); Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); @@ -119,4 +118,3 @@ export default new (class Template extends Marko._.Template<{ Marko._.Relate<__marko_internal_input, Marko.Directives & Input>, ) => Marko._.ReturnWithScope<__marko_internal_input, void>; }> {})(); -// ^? diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-type/__snapshots__/attr-tags-nested-type.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-type/__snapshots__/attr-tags-nested-type.expected/index.ts index e6f3aa31..e5c23c69 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-type/__snapshots__/attr-tags-nested-type.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-nested-type/__snapshots__/attr-tags-nested-type.expected/index.ts @@ -77,4 +77,3 @@ export default new (class Template extends Marko._.Template<{ Marko._.Relate<__marko_internal_input, Marko.Directives & Input>, ) => Marko._.ReturnWithScope<__marko_internal_input, void>; }> {})(); -// ^? diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-params-js/__snapshots__/attr-tags-params-js.expected/index.js b/packages/language-server/src/__tests__/fixtures/script/attr-tags-params-js/__snapshots__/attr-tags-params-js.expected/index.js index 03f84a95..35868dd4 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-params-js/__snapshots__/attr-tags-params-js.expected/index.js +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-params-js/__snapshots__/attr-tags-params-js.expected/index.js @@ -38,6 +38,7 @@ export class Component extends Marko.Component {} ["foo"]: { [Marko._.contentFor(__marko_internal_tag_1)]: (data) => { data; + //^? return Marko._.voidReturn; }, [Symbol.iterator]: Marko._.any, @@ -50,36 +51,33 @@ export class Component extends Marko.Component {} input["@foo"]; input["@foo"]; }); - Marko._.renderDynamicTag(__marko_internal_tag_2)()()( - //^? - { - ["foo"]: Marko._.attrTagFor(__marko_internal_tag_2, "foo")( - "foo", - { - ["foo"]: { - bar: true, - [Symbol.iterator]: Marko._.any, - }, + Marko._.renderDynamicTag(__marko_internal_tag_2)()()({ + ["foo"]: Marko._.attrTagFor(__marko_internal_tag_2, "foo")( + "foo", + { + ["foo"]: { + bar: true, + [Symbol.iterator]: Marko._.any, }, - { - ["foo"]: { - [Marko._.contentFor(__marko_internal_tag_2)]: (data) => { - data; - return Marko._.voidReturn; - }, - [Symbol.iterator]: Marko._.any, + }, + { + ["foo"]: { + [Marko._.contentFor(__marko_internal_tag_2)]: (data) => { + data; + //^? + return Marko._.voidReturn; }, + [Symbol.iterator]: Marko._.any, }, - ), - }, - ); + }, + ), + }); const __marko_internal_tag_3 = true && Child; Marko._.attrTagNames(__marko_internal_tag_3, (input) => { input["@foo"]; input["@foo"]; }); Marko._.renderDynamicTag(__marko_internal_tag_3)()()( - //^? // This errors for now, because `typeof (some + expression)` is not allowed { ["foo"]: Marko._.attrTagFor(__marko_internal_tag_3, "foo")( @@ -94,6 +92,7 @@ export class Component extends Marko.Component {} ["foo"]: { [Marko._.contentFor(__marko_internal_tag_3)]: (data) => { data; + //^? return Marko._.voidReturn; }, [Symbol.iterator]: Marko._.any, @@ -111,4 +110,3 @@ export default new /** * @extends { Marko._.Template<{ render( input: Marko.TemplateInput, stream?: { write: (chunk: string) => void; end: (chunk?: string) => void; }, ): Marko.Out; render( input: Marko.TemplateInput, cb?: ( err: Error | null, result: Marko.RenderResult, ) => void, ): Marko.Out; renderSync( input: Marko.TemplateInput, ): Marko.RenderResult; renderToString(input: Marko.TemplateInput): string; stream( input: Marko.TemplateInput, ): ReadableStream & NodeJS.ReadableStream; mount( input: Marko.TemplateInput, reference: Node, position?: "afterbegin" | "afterend" | "beforebegin" | "beforeend", ): Marko.MountedTemplate; api: typeof __marko_internal_api, _(): () => <__marko_internal_input extends unknown>(input: Marko.Directives & Input & Marko._.Relate<__marko_internal_input, Marko.Directives & Input>) => (Marko._.ReturnWithScope<__marko_internal_input, void>); }>} */ (class Template extends Marko._.Template {})(); -//^? diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-params/__snapshots__/attr-tags-params.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/attr-tags-params/__snapshots__/attr-tags-params.expected/index.ts index b80737f0..95de78e0 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-params/__snapshots__/attr-tags-params.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-params/__snapshots__/attr-tags-params.expected/index.ts @@ -32,6 +32,7 @@ export { type Component }; ["foo"]: { [Marko._.contentFor(__marko_internal_tag_1)]: (data) => { data; + //^? return Marko._.voidReturn; }, [Symbol.iterator]: Marko._.any, @@ -44,36 +45,33 @@ export { type Component }; input["@foo"]; input["@foo"]; }); - Marko._.renderDynamicTag(__marko_internal_tag_2)()()( - //^? - { - ["foo"]: Marko._.attrTagFor(__marko_internal_tag_2, "foo")( - "foo", - { - ["foo"]: { - bar: true, - [Symbol.iterator]: Marko._.any, - }, + Marko._.renderDynamicTag(__marko_internal_tag_2)()()({ + ["foo"]: Marko._.attrTagFor(__marko_internal_tag_2, "foo")( + "foo", + { + ["foo"]: { + bar: true, + [Symbol.iterator]: Marko._.any, }, - { - ["foo"]: { - [Marko._.contentFor(__marko_internal_tag_2)]: (data) => { - data; - return Marko._.voidReturn; - }, - [Symbol.iterator]: Marko._.any, + }, + { + ["foo"]: { + [Marko._.contentFor(__marko_internal_tag_2)]: (data) => { + data; + //^? + return Marko._.voidReturn; }, + [Symbol.iterator]: Marko._.any, }, - ), - }, - ); + }, + ), + }); const __marko_internal_tag_3 = true && Child; Marko._.attrTagNames(__marko_internal_tag_3, (input) => { input["@foo"]; input["@foo"]; }); Marko._.renderDynamicTag(__marko_internal_tag_3)()()( - //^? // This errors for now, because `typeof (some + expression)` is not allowed { ["foo"]: Marko._.attrTagFor(__marko_internal_tag_3, "foo")( @@ -88,6 +86,7 @@ export { type Component }; ["foo"]: { [Marko._.contentFor(__marko_internal_tag_3)]: (data) => { data; + //^? return Marko._.voidReturn; }, [Symbol.iterator]: Marko._.any, @@ -136,4 +135,3 @@ export default new (class Template extends Marko._.Template<{ Marko._.Relate<__marko_internal_input, Marko.Directives & Input>, ) => Marko._.ReturnWithScope<__marko_internal_input, void>; }> {})(); -//^? diff --git a/packages/language-server/src/__tests__/fixtures/script/attr-tags-static-dynamic-renderbody/__snapshots__/attr-tags-static-dynamic-renderbody.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/attr-tags-static-dynamic-renderbody/__snapshots__/attr-tags-static-dynamic-renderbody.expected/index.ts index 038ea40f..0b5eef8d 100644 --- a/packages/language-server/src/__tests__/fixtures/script/attr-tags-static-dynamic-renderbody/__snapshots__/attr-tags-static-dynamic-renderbody.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/attr-tags-static-dynamic-renderbody/__snapshots__/attr-tags-static-dynamic-renderbody.expected/index.ts @@ -60,6 +60,7 @@ export interface Input {} return Marko._.voidReturn; }, ); + // ^? return () => { return Marko._.voidReturn; }; @@ -107,4 +108,3 @@ export default new (class Template extends Marko._.Template<{ Marko._.Relate<__marko_internal_input, Marko.Directives & Input>, ) => Marko._.ReturnWithScope<__marko_internal_input, void>; }> {})(); -// ^? diff --git a/packages/language-server/src/__tests__/fixtures/script/basic/__snapshots__/basic.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/basic/__snapshots__/basic.expected/index.ts index dfced034..5bad6a7f 100644 --- a/packages/language-server/src/__tests__/fixtures/script/basic/__snapshots__/basic.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/basic/__snapshots__/basic.expected/index.ts @@ -26,6 +26,7 @@ export interface Input { x; // ^? input.name; + // ^? return () => { return new (class MarkoReturn { readonly [Marko._.scope] = { x }; @@ -36,8 +37,8 @@ export interface Input { } })(), }); - // ^? x; + // ^? var { x: __marko_internal_hoist__x } = Marko._.readScope( __marko_internal_rendered_1, ); diff --git a/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-dynamic-member/__snapshots__/bound-attr-const-dynamic-member.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-dynamic-member/__snapshots__/bound-attr-const-dynamic-member.expected/index.ts index 72ce06c8..b97d6a88 100644 --- a/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-dynamic-member/__snapshots__/bound-attr-const-dynamic-member.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-dynamic-member/__snapshots__/bound-attr-const-dynamic-member.expected/index.ts @@ -29,6 +29,7 @@ export interface Input {} value: a["b"], valueChange: a[`${"b"}Change`], }); + // ^? ^? var __marko_internal_hoist__a = a; var __marko_internal_hoist__b = b; } diff --git a/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-ident/__snapshots__/bound-attr-const-ident.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-ident/__snapshots__/bound-attr-const-ident.expected/index.ts index db932f65..77b30741 100644 --- a/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-ident/__snapshots__/bound-attr-const-ident.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-ident/__snapshots__/bound-attr-const-ident.expected/index.ts @@ -39,6 +39,7 @@ export interface Input {} __marko_internal_change__a.a = _a; }, }); + // ^? ^? var __marko_internal_hoist__a = a; var __marko_internal_hoist__b = b; } diff --git a/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-literal-member/__snapshots__/bound-attr-const-literal-member.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-literal-member/__snapshots__/bound-attr-const-literal-member.expected/index.ts index 20a8761f..1c5b171c 100644 --- a/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-literal-member/__snapshots__/bound-attr-const-literal-member.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-literal-member/__snapshots__/bound-attr-const-literal-member.expected/index.ts @@ -29,6 +29,7 @@ export interface Input {} value: a.b, valueChange: a.bChange, }); + // ^? ^? var __marko_internal_hoist__a = a; var __marko_internal_hoist__b = b; } diff --git a/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-with-type/__snapshots__/bound-attr-const-with-type.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-with-type/__snapshots__/bound-attr-const-with-type.expected/index.ts index b4169d51..1a36b5e5 100644 --- a/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-with-type/__snapshots__/bound-attr-const-with-type.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/bound-attr-const-with-type/__snapshots__/bound-attr-const-with-type.expected/index.ts @@ -29,6 +29,7 @@ export interface Input {} value: a["b"] as 1, valueChange: a[`${"b"}Change`], }); + // ^? ^? var __marko_internal_hoist__a = a; var __marko_internal_hoist__b = b; } diff --git a/packages/language-server/src/__tests__/fixtures/script/bound-attr-missing-ident/__snapshots__/bound-attr-missing-ident.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/bound-attr-missing-ident/__snapshots__/bound-attr-missing-ident.expected/index.ts index 4dbe3c4d..e5d4a4df 100644 --- a/packages/language-server/src/__tests__/fixtures/script/bound-attr-missing-ident/__snapshots__/bound-attr-missing-ident.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/bound-attr-missing-ident/__snapshots__/bound-attr-missing-ident.expected/index.ts @@ -23,6 +23,7 @@ export interface Input {} a = _a; }, }); + // ^? ^? var __marko_internal_hoist__b = b; } Marko._.noop({ b, input, $global, $signal }); diff --git a/packages/language-server/src/__tests__/fixtures/script/bound-attr-modifier-dynamic-member/__snapshots__/bound-attr-modifier-dynamic-member.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/bound-attr-modifier-dynamic-member/__snapshots__/bound-attr-modifier-dynamic-member.expected/index.ts index fa840f51..bc3ce71f 100644 --- a/packages/language-server/src/__tests__/fixtures/script/bound-attr-modifier-dynamic-member/__snapshots__/bound-attr-modifier-dynamic-member.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/bound-attr-modifier-dynamic-member/__snapshots__/bound-attr-modifier-dynamic-member.expected/index.ts @@ -48,6 +48,7 @@ export interface Input {} }), }, ); + // ^? ^? var __marko_internal_hoist__a = a; } Marko._.noop({ a, input, $global, $signal }); diff --git a/packages/language-server/src/__tests__/fixtures/script/bound-attr-modifier-ident/__snapshots__/bound-attr-modifier-ident.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/bound-attr-modifier-ident/__snapshots__/bound-attr-modifier-ident.expected/index.ts index 5a7c7bef..2c412baa 100644 --- a/packages/language-server/src/__tests__/fixtures/script/bound-attr-modifier-ident/__snapshots__/bound-attr-modifier-ident.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/bound-attr-modifier-ident/__snapshots__/bound-attr-modifier-ident.expected/index.ts @@ -52,6 +52,7 @@ export interface Input {} }, }, ); + // ^? ^? ^? var __marko_internal_hoist__b = b; } Marko._.noop({ b, input, $global, $signal }); diff --git a/packages/language-server/src/__tests__/fixtures/script/bound-attr-modifier-literal-member/__snapshots__/bound-attr-modifier-literal-member.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/bound-attr-modifier-literal-member/__snapshots__/bound-attr-modifier-literal-member.expected/index.ts index 8aa6016f..f343adaa 100644 --- a/packages/language-server/src/__tests__/fixtures/script/bound-attr-modifier-literal-member/__snapshots__/bound-attr-modifier-literal-member.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/bound-attr-modifier-literal-member/__snapshots__/bound-attr-modifier-literal-member.expected/index.ts @@ -48,6 +48,7 @@ export interface Input {} }), }, ); + // ^? ^? var __marko_internal_hoist__a = a; } Marko._.noop({ a, input, $global, $signal }); diff --git a/packages/language-server/src/__tests__/fixtures/script/bound-attr-mut-dynamic-member/__snapshots__/bound-attr-mut-dynamic-member.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/bound-attr-mut-dynamic-member/__snapshots__/bound-attr-mut-dynamic-member.expected/index.ts index da133540..255d35a0 100644 --- a/packages/language-server/src/__tests__/fixtures/script/bound-attr-mut-dynamic-member/__snapshots__/bound-attr-mut-dynamic-member.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/bound-attr-mut-dynamic-member/__snapshots__/bound-attr-mut-dynamic-member.expected/index.ts @@ -29,6 +29,7 @@ export interface Input {} value: a["b"], valueChange: a[`${"b"}Change`], }); + // ^? ^? var __marko_internal_hoist__a = a; var __marko_internal_hoist__b = b; } diff --git a/packages/language-server/src/__tests__/fixtures/script/bound-attr-mut-literal-member/__snapshots__/bound-attr-mut-literal-member.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/bound-attr-mut-literal-member/__snapshots__/bound-attr-mut-literal-member.expected/index.ts index 5014d1c9..492113a5 100644 --- a/packages/language-server/src/__tests__/fixtures/script/bound-attr-mut-literal-member/__snapshots__/bound-attr-mut-literal-member.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/bound-attr-mut-literal-member/__snapshots__/bound-attr-mut-literal-member.expected/index.ts @@ -29,6 +29,7 @@ export interface Input {} value: a.b, valueChange: a.bChange, }); + // ^? ^? var __marko_internal_hoist__a = a; var __marko_internal_hoist__b = b; } diff --git a/packages/language-server/src/__tests__/fixtures/script/class-api-type-no-state/__snapshots__/class-api-type-no-state.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/class-api-type-no-state/__snapshots__/class-api-type-no-state.expected/index.ts index cbf15504..453c1a58 100644 --- a/packages/language-server/src/__tests__/fixtures/script/class-api-type-no-state/__snapshots__/class-api-type-no-state.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/class-api-type-no-state/__snapshots__/class-api-type-no-state.expected/index.ts @@ -14,6 +14,7 @@ export { type Component }; (Marko._.error, Marko._.any as MarkoRun.Context), ); state.name; + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/class-api-type-state-basic/__snapshots__/class-api-type-state-basic.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/class-api-type-state-basic/__snapshots__/class-api-type-state-basic.expected/index.ts index 7483e60c..b9583605 100644 --- a/packages/language-server/src/__tests__/fixtures/script/class-api-type-state-basic/__snapshots__/class-api-type-state-basic.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/class-api-type-state-basic/__snapshots__/class-api-type-state-basic.expected/index.ts @@ -20,6 +20,7 @@ export { type Component }; (Marko._.error, Marko._.any as MarkoRun.Context), ); state.name; + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/class-api-type-state-multiple-generics/__snapshots__/class-api-type-state-multiple-generics.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/class-api-type-state-multiple-generics/__snapshots__/class-api-type-state-multiple-generics.expected/index.ts index 5b6e2d62..e245bd06 100644 --- a/packages/language-server/src/__tests__/fixtures/script/class-api-type-state-multiple-generics/__snapshots__/class-api-type-state-multiple-generics.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/class-api-type-state-multiple-generics/__snapshots__/class-api-type-state-multiple-generics.expected/index.ts @@ -31,6 +31,7 @@ export { type Component }; (Marko._.error, Marko._.any as MarkoRun.Context), ); state.name; + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/class-api-type-state-single-generic/__snapshots__/class-api-type-state-single-generic.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/class-api-type-state-single-generic/__snapshots__/class-api-type-state-single-generic.expected/index.ts index dd122c15..741a279e 100644 --- a/packages/language-server/src/__tests__/fixtures/script/class-api-type-state-single-generic/__snapshots__/class-api-type-state-single-generic.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/class-api-type-state-single-generic/__snapshots__/class-api-type-state-single-generic.expected/index.ts @@ -27,6 +27,7 @@ export { type Component }; (Marko._.error, Marko._.any as MarkoRun.Context), ); state.name; + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/class-api-with-component-auto-import/__snapshots__/class-api-with-component-auto-import.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/class-api-with-component-auto-import/__snapshots__/class-api-with-component-auto-import.expected/index.ts index 05573af2..55607cd0 100644 --- a/packages/language-server/src/__tests__/fixtures/script/class-api-with-component-auto-import/__snapshots__/class-api-with-component-auto-import.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/class-api-with-component-auto-import/__snapshots__/class-api-with-component-auto-import.expected/index.ts @@ -19,6 +19,7 @@ export interface Component extends Marko._.ResolveComponent< onClick: component["strange-handler"], }); console.log(component); + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/class-api-with-generics-and-component-auto-import/__snapshots__/class-api-with-generics-and-component-auto-import.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/class-api-with-generics-and-component-auto-import/__snapshots__/class-api-with-generics-and-component-auto-import.expected/index.ts index 56ad6a84..50c0a469 100644 --- a/packages/language-server/src/__tests__/fixtures/script/class-api-with-generics-and-component-auto-import/__snapshots__/class-api-with-generics-and-component-auto-import.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/class-api-with-generics-and-component-auto-import/__snapshots__/class-api-with-generics-and-component-auto-import.expected/index.ts @@ -20,6 +20,7 @@ export type { Component }; onClick: component["strange-handler"], }); console.log(component.input); + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/class-api-with-pojo-component-auto-import/__snapshots__/class-api-with-pojo-component-auto-import.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/class-api-with-pojo-component-auto-import/__snapshots__/class-api-with-pojo-component-auto-import.expected/index.ts index 05573af2..55607cd0 100644 --- a/packages/language-server/src/__tests__/fixtures/script/class-api-with-pojo-component-auto-import/__snapshots__/class-api-with-pojo-component-auto-import.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/class-api-with-pojo-component-auto-import/__snapshots__/class-api-with-pojo-component-auto-import.expected/index.ts @@ -19,6 +19,7 @@ export interface Component extends Marko._.ResolveComponent< onClick: component["strange-handler"], }); console.log(component); + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/el-ref-hoist/__snapshots__/el-ref-hoist.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/el-ref-hoist/__snapshots__/el-ref-hoist.expected/index.ts index d42d307f..15219e41 100644 --- a/packages/language-server/src/__tests__/fixtures/script/el-ref-hoist/__snapshots__/el-ref-hoist.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/el-ref-hoist/__snapshots__/el-ref-hoist.expected/index.ts @@ -25,8 +25,7 @@ Marko._.renderNativeTag("div")()()({ const __marko_internal_tag_3 = ( Marko._.resolveTemplate(import("marko/src/core-tags/core/script.d.marko")) ); -Marko._.renderTemplate(__marko_internal_tag_3)()()(// ^? -{ +Marko._.renderTemplate(__marko_internal_tag_3)()()({ async value(){$el; //^? $hoistedEl; diff --git a/packages/language-server/src/__tests__/fixtures/script/for-tag-input/__snapshots__/for-tag-input.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/for-tag-input/__snapshots__/for-tag-input.expected/index.ts index d4e42143..8566a067 100644 --- a/packages/language-server/src/__tests__/fixtures/script/for-tag-input/__snapshots__/for-tag-input.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/for-tag-input/__snapshots__/for-tag-input.expected/index.ts @@ -28,6 +28,7 @@ export { type Component }; href: href, [Marko._.content]: (() => { title; + // ^? return () => { return Marko._.voidReturn; }; @@ -37,36 +38,34 @@ export { type Component }; return Marko._.voidReturn; }, ); - Marko._.renderNativeTag("select")()()( - // ^? - { - [Marko._.content]: (() => { - Marko._.forOfTag( - { - of: input.option, - }, - ({ value, content }) => { - Marko._.renderNativeTag("option")()()( - // ^? - { - value: value, - [Marko._.content]: (() => { - content; - return () => { - return Marko._.voidReturn; - }; - })(), - }, - ); - return Marko._.voidReturn; - }, - ); - return () => { + Marko._.renderNativeTag("select")()()({ + [Marko._.content]: (() => { + Marko._.forOfTag( + { + of: input.option, + }, + ({ value, content }) => { + Marko._.renderNativeTag("option")()()( + // ^? + { + value: value, + [Marko._.content]: (() => { + content; + // ^? + return () => { + return Marko._.voidReturn; + }; + })(), + }, + ); return Marko._.voidReturn; - }; - })(), - }, - ); + }, + ); + return () => { + return Marko._.voidReturn; + }; + })(), + }); const __marko_internal_tag_1 = Marko._.resolveTemplate( import("./components/my-select.marko"), ); @@ -74,53 +73,50 @@ export { type Component }; input["@option"]; input["@option"]; }); - Marko._.renderTemplate(__marko_internal_tag_1)()()( - // ^? - { - ...Marko._.mergeAttrTags( - Marko._.forOfAttrTag( - { - of: input.tab, + Marko._.renderTemplate(__marko_internal_tag_1)()()({ + ...Marko._.mergeAttrTags( + Marko._.forOfAttrTag( + { + of: input.tab, + }, + ({ href, title }) => ({ + // ^? + ["option"]: { + value: href, + [Marko._.contentFor(__marko_internal_tag_1)]: (() => { + // ^? + title; + return () => { + return Marko._.voidReturn; + }; + })(), + [Symbol.iterator]: Marko._.any, }, - ({ href, title }) => ({ - // ^? - ["option"]: { - value: href, - [Marko._.contentFor(__marko_internal_tag_1)]: (() => { - // ^? - title; - return () => { - return Marko._.voidReturn; - }; - })(), - [Symbol.iterator]: Marko._.any, - }, - }), - Marko._.input(__marko_internal_tag_1), - ), - Marko._.forOfAttrTag( - { - of: input.option, + }), + Marko._.input(__marko_internal_tag_1), + ), + Marko._.forOfAttrTag( + { + of: input.option, + }, + ({ value, content }) => ({ + // ^? + ["option"]: { + value: value, + [Marko._.contentFor(__marko_internal_tag_1)]: (() => { + // ^? + content; + return () => { + return Marko._.voidReturn; + }; + })(), + [Symbol.iterator]: Marko._.any, }, - ({ value, content }) => ({ - // ^? - ["option"]: { - value: value, - [Marko._.contentFor(__marko_internal_tag_1)]: (() => { - // ^? - content; - return () => { - return Marko._.voidReturn; - }; - })(), - [Symbol.iterator]: Marko._.any, - }, - }), - Marko._.input(__marko_internal_tag_1), - ), + }), + Marko._.input(__marko_internal_tag_1), ), - }, - ); + ), + }); Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/for-tag/__snapshots__/for-tag.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/for-tag/__snapshots__/for-tag.expected/index.ts index ebb28d16..7aa5cfd9 100644 --- a/packages/language-server/src/__tests__/fixtures/script/for-tag/__snapshots__/for-tag.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/for-tag/__snapshots__/for-tag.expected/index.ts @@ -56,6 +56,7 @@ export interface Input {} item; index; all; + // ^? ^? ^? return Marko._.voidReturn; }, ); @@ -64,21 +65,15 @@ export interface Input {} of: list, by: (item, index) => `${item}-${index}`, }, - () => - // ^? ^? ^? - - { - return Marko._.voidReturn; - }, + () => { + return Marko._.voidReturn; + }, ); const __marko_internal_rendered_2 = Marko._.forOfTag( { of: list, }, - ( - // ^? ^? - item, - ) => { + (item) => { const __marko_internal_tag_3 = Marko._.resolveTemplate( import("@marko/runtime-tags/tags/const.d.marko"), ); @@ -133,6 +128,7 @@ export interface Input {} (key, value) => { key; value; + // ^? ^? return Marko._.voidReturn; }, ); @@ -141,21 +137,15 @@ export interface Input {} in: record, by: (value, key) => `${value}-${key}`, }, - () => - // ^? ^? - - { - return Marko._.voidReturn; - }, + () => { + return Marko._.voidReturn; + }, ); const __marko_internal_rendered_6 = Marko._.forInTag( { in: record, }, - ( - // ^? ^? - key, - ) => { + (key) => { const __marko_internal_tag_7 = Marko._.resolveTemplate( import("@marko/runtime-tags/tags/const.d.marko"), ); @@ -191,6 +181,7 @@ export interface Input {} }, (index) => { index; + // ^? return Marko._.voidReturn; }, ); @@ -199,23 +190,18 @@ export interface Input {} to: 10, by: (index) => `${index}`, }, - () => - // ^? - - { - return Marko._.voidReturn; - }, + () => { + return Marko._.voidReturn; + }, ); Marko._.forToTag( { from: 1, to: 10, }, - ( - // ^? - index, - ) => { + (index) => { index; + // ^? return Marko._.voidReturn; }, ); @@ -224,11 +210,9 @@ export interface Input {} to: 10, step: 2, }, - ( - // ^? - index, - ) => { + (index) => { index; + // ^? return Marko._.voidReturn; }, ); @@ -236,10 +220,7 @@ export interface Input {} { to: 10, }, - ( - // ^? - index, - ) => { + (index) => { const __marko_internal_tag_10 = Marko._.resolveTemplate( import("@marko/runtime-tags/tags/const.d.marko"), ); @@ -275,6 +256,7 @@ export interface Input {} }, (index) => { index; + // ^? return Marko._.voidReturn; }, ); @@ -283,23 +265,18 @@ export interface Input {} until: 10, by: (index) => `${index}`, }, - () => - // ^? - - { - return Marko._.voidReturn; - }, + () => { + return Marko._.voidReturn; + }, ); Marko._.forUntilTag( { from: 1, until: 10, }, - ( - // ^? - index, - ) => { + (index) => { index; + // ^? return Marko._.voidReturn; }, ); @@ -308,11 +285,9 @@ export interface Input {} until: 10, step: 2, }, - ( - // ^? - index, - ) => { + (index) => { index; + // ^? return Marko._.voidReturn; }, ); @@ -320,10 +295,7 @@ export interface Input {} { until: 10, }, - ( - // ^? - index, - ) => { + (index) => { const __marko_internal_tag_13 = Marko._.resolveTemplate( import("@marko/runtime-tags/tags/const.d.marko"), ); diff --git a/packages/language-server/src/__tests__/fixtures/script/hover-before-and-after-tokens/__snapshots__/hover-before-and-after-tokens.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/hover-before-and-after-tokens/__snapshots__/hover-before-and-after-tokens.expected/index.ts index cc319013..0c386aaf 100644 --- a/packages/language-server/src/__tests__/fixtures/script/hover-before-and-after-tokens/__snapshots__/hover-before-and-after-tokens.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/hover-before-and-after-tokens/__snapshots__/hover-before-and-after-tokens.expected/index.ts @@ -14,6 +14,7 @@ export { type Component }; //^? console.log("hello"); console.log("world"); + //^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/input-type-basic/__snapshots__/input-type-basic.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/input-type-basic/__snapshots__/input-type-basic.expected/index.ts index d0f1cc5e..bff09740 100644 --- a/packages/language-server/src/__tests__/fixtures/script/input-type-basic/__snapshots__/input-type-basic.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/input-type-basic/__snapshots__/input-type-basic.expected/index.ts @@ -14,6 +14,7 @@ export { type Component }; (Marko._.error, Marko._.any as MarkoRun.Context), ); input.name; + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/input-type-default-generic/__snapshots__/input-type-default-generic.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/input-type-default-generic/__snapshots__/input-type-default-generic.expected/index.ts index 493435c1..1ab752de 100644 --- a/packages/language-server/src/__tests__/fixtures/script/input-type-default-generic/__snapshots__/input-type-default-generic.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/input-type-default-generic/__snapshots__/input-type-default-generic.expected/index.ts @@ -20,6 +20,7 @@ export { type Component }; input.options; // ^? input.onChange; + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/input-type-duplicated/__snapshots__/input-type-duplicated.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/input-type-duplicated/__snapshots__/input-type-duplicated.expected/index.ts index a0ac5a42..c0bd3744 100644 --- a/packages/language-server/src/__tests__/fixtures/script/input-type-duplicated/__snapshots__/input-type-duplicated.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/input-type-duplicated/__snapshots__/input-type-duplicated.expected/index.ts @@ -19,6 +19,7 @@ export { type Component }; input.name; // ^? input.other; + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/input-type-import-default/__snapshots__/input-type-import-default.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/input-type-import-default/__snapshots__/input-type-import-default.expected/index.ts index e1614984..8501d67f 100644 --- a/packages/language-server/src/__tests__/fixtures/script/input-type-import-default/__snapshots__/input-type-import-default.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/input-type-import-default/__snapshots__/input-type-import-default.expected/index.ts @@ -12,6 +12,7 @@ export { type Component }; (Marko._.error, Marko._.any as MarkoRun.Context), ); input.name; + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/input-type-import-type/__snapshots__/input-type-import-type.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/input-type-import-type/__snapshots__/input-type-import-type.expected/index.ts index d241a960..55a21c3d 100644 --- a/packages/language-server/src/__tests__/fixtures/script/input-type-import-type/__snapshots__/input-type-import-type.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/input-type-import-type/__snapshots__/input-type-import-type.expected/index.ts @@ -12,6 +12,7 @@ export { type Component }; (Marko._.error, Marko._.any as MarkoRun.Context), ); input.name; + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/input-type-import/__snapshots__/input-type-import.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/input-type-import/__snapshots__/input-type-import.expected/index.ts index c636a525..fbf03293 100644 --- a/packages/language-server/src/__tests__/fixtures/script/input-type-import/__snapshots__/input-type-import.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/input-type-import/__snapshots__/input-type-import.expected/index.ts @@ -12,6 +12,7 @@ export { type Component }; (Marko._.error, Marko._.any as MarkoRun.Context), ); input.name; + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/input-type-multiple-generics/__snapshots__/input-type-multiple-generics.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/input-type-multiple-generics/__snapshots__/input-type-multiple-generics.expected/index.ts index bfcf4dc0..9c7d24e5 100644 --- a/packages/language-server/src/__tests__/fixtures/script/input-type-multiple-generics/__snapshots__/input-type-multiple-generics.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/input-type-multiple-generics/__snapshots__/input-type-multiple-generics.expected/index.ts @@ -26,6 +26,7 @@ export { type Component }; (Marko._.error, Marko._.any as MarkoRun.Context), ); input.fullName; + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/input-type-single-generic/__snapshots__/input-type-single-generic.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/input-type-single-generic/__snapshots__/input-type-single-generic.expected/index.ts index 609ded97..381866b1 100644 --- a/packages/language-server/src/__tests__/fixtures/script/input-type-single-generic/__snapshots__/input-type-single-generic.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/input-type-single-generic/__snapshots__/input-type-single-generic.expected/index.ts @@ -19,6 +19,7 @@ export { type Component }; input.options; // ^? input.onChange; + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/__snapshots__/leading-comment-pragma.expected/index.html b/packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/__snapshots__/leading-comment-pragma.expected/index.html new file mode 100644 index 00000000..3143e0dc --- /dev/null +++ b/packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/__snapshots__/leading-comment-pragma.expected/index.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/__snapshots__/leading-comment-pragma.expected/index.md b/packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/__snapshots__/leading-comment-pragma.expected/index.md new file mode 100644 index 00000000..e69de29b diff --git a/packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/__snapshots__/leading-comment-pragma.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/__snapshots__/leading-comment-pragma.expected/index.ts new file mode 100644 index 00000000..3049d66e --- /dev/null +++ b/packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/__snapshots__/leading-comment-pragma.expected/index.ts @@ -0,0 +1,55 @@ +// @ts-check +export interface Input {} +abstract class Component extends Marko.Component {} +export { type Component }; +(function (this: void) { + const input = Marko._.any as Input; + const component = Marko._.any as Component; + const state = Marko._.state(component); + const out = Marko._.any as Marko.Out; + const $signal = Marko._.any as AbortSignal; + const $global = Marko._.getGlobal( + // @ts-expect-error We expect the compiler to error because we are checking if the MarkoRun.Context is defined. + (Marko._.error, Marko._.any as MarkoRun.Context), + ); + Marko._.renderNativeTag("div")()()({}); + Marko._.noop({ component, state, out, input, $global, $signal }); + return; +})(); +const __marko_internal_api = "class"; +export { __marko_internal_api as "~api" }; +export default new (class Template extends Marko._.Template<{ + render( + input: Marko.TemplateInput, + stream?: { + write: (chunk: string) => void; + end: (chunk?: string) => void; + }, + ): Marko.Out; + + render( + input: Marko.TemplateInput, + cb?: (err: Error | null, result: Marko.RenderResult) => void, + ): Marko.Out; + + renderSync(input: Marko.TemplateInput): Marko.RenderResult; + + renderToString(input: Marko.TemplateInput): string; + + stream( + input: Marko.TemplateInput, + ): ReadableStream & NodeJS.ReadableStream; + + mount( + input: Marko.TemplateInput, + reference: Node, + position?: "afterbegin" | "afterend" | "beforebegin" | "beforeend", + ): Marko.MountedTemplate; + + api: typeof __marko_internal_api; + _(): () => <__marko_internal_input extends unknown>( + input: Marko.Directives & + Input & + Marko._.Relate<__marko_internal_input, Marko.Directives & Input>, + ) => Marko._.ReturnWithScope<__marko_internal_input, void>; +}> {})(); diff --git a/packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/index.marko b/packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/index.marko new file mode 100644 index 00000000..b5410f4e --- /dev/null +++ b/packages/language-server/src/__tests__/fixtures/script/leading-comment-pragma/index.marko @@ -0,0 +1,2 @@ +// @ts-check +
diff --git a/packages/language-server/src/__tests__/fixtures/script/mutate-within-attr-tag/__snapshots__/mutate-within-attr-tag.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/mutate-within-attr-tag/__snapshots__/mutate-within-attr-tag.expected/index.ts index 01b75d91..e2b0c79b 100644 --- a/packages/language-server/src/__tests__/fixtures/script/mutate-within-attr-tag/__snapshots__/mutate-within-attr-tag.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/mutate-within-attr-tag/__snapshots__/mutate-within-attr-tag.expected/index.ts @@ -76,4 +76,3 @@ export default new (class Template extends Marko._.Template<{ Marko._.Relate<__marko_internal_input, Marko.Directives & Input>, ) => Marko._.ReturnWithScope<__marko_internal_input, void>; }> {})(); -// ^? diff --git a/packages/language-server/src/__tests__/fixtures/script/program-text-only/__snapshots__/program-text-only.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/program-text-only/__snapshots__/program-text-only.expected/index.ts index 5939b0bc..7effc9ba 100644 --- a/packages/language-server/src/__tests__/fixtures/script/program-text-only/__snapshots__/program-text-only.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/program-text-only/__snapshots__/program-text-only.expected/index.ts @@ -11,6 +11,7 @@ export { type Component }; // @ts-expect-error We expect the compiler to error because we are checking if the MarkoRun.Context is defined. (Marko._.error, Marko._.any as MarkoRun.Context), ); + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/recursive-define-tag/__snapshots__/recursive-define-tag.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/recursive-define-tag/__snapshots__/recursive-define-tag.expected/index.ts index ca6dd4af..14c00154 100644 --- a/packages/language-server/src/__tests__/fixtures/script/recursive-define-tag/__snapshots__/recursive-define-tag.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/recursive-define-tag/__snapshots__/recursive-define-tag.expected/index.ts @@ -35,6 +35,7 @@ export interface Input { Marko._.renderDynamicTag(__marko_internal_tag_2)()()({ children: children, }); + // ^? return Marko._.voidReturn; }, ); @@ -45,14 +46,12 @@ export interface Input { { of: input.value, }, - ( - // ^? - help, - ) => { + (help) => { const __marko_internal_tag_3 = Section; Marko._.renderDynamicTag(__marko_internal_tag_3)()()({ ...help, }); + // ^? return Marko._.voidReturn; }, ); @@ -98,4 +97,3 @@ export default new (class Template extends Marko._.Template<{ Marko._.Relate<__marko_internal_input, Marko.Directives & Input>, ) => Marko._.ReturnWithScope<__marko_internal_input, void>; }> {})(); -// ^? diff --git a/packages/language-server/src/__tests__/fixtures/script/return-as-type/__snapshots__/return-as-type.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/return-as-type/__snapshots__/return-as-type.expected/index.ts index b53bca47..f7706631 100644 --- a/packages/language-server/src/__tests__/fixtures/script/return-as-type/__snapshots__/return-as-type.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/return-as-type/__snapshots__/return-as-type.expected/index.ts @@ -57,4 +57,3 @@ export default new (class Template extends Marko._.Template<{ Marko._.Relate<__marko_internal_input, Marko.Directives & Input>, ) => Marko._.ReturnWithScope<__marko_internal_input, void>; }> {})(); -// ^? diff --git a/packages/language-server/src/__tests__/fixtures/script/return-as-type/__snapshots__/return-as-type.expected/tags/oneOrTwo.ts b/packages/language-server/src/__tests__/fixtures/script/return-as-type/__snapshots__/return-as-type.expected/tags/oneOrTwo.ts index b3c4530d..aaacc712 100644 --- a/packages/language-server/src/__tests__/fixtures/script/return-as-type/__snapshots__/return-as-type.expected/tags/oneOrTwo.ts +++ b/packages/language-server/src/__tests__/fixtures/script/return-as-type/__snapshots__/return-as-type.expected/tags/oneOrTwo.ts @@ -22,6 +22,7 @@ function __marko_internal_template(this: void) { "value", __marko_internal_rendered_1.return, ); + // ^? var __marko_internal_return = Marko._.returnTag({ value: x as 1 | 2, valueChange( diff --git a/packages/language-server/src/__tests__/fixtures/script/return-tag-nested/__snapshots__/return-tag-nested.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/return-tag-nested/__snapshots__/return-tag-nested.expected/index.ts index 94391cde..5e210371 100644 --- a/packages/language-server/src/__tests__/fixtures/script/return-tag-nested/__snapshots__/return-tag-nested.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/return-tag-nested/__snapshots__/return-tag-nested.expected/index.ts @@ -13,6 +13,7 @@ export interface Input {} Marko._.renderTemplate(__marko_internal_tag_1)()()({ [Marko._.contentFor(__marko_internal_tag_1)]: (a) => { a; + // ^? return Marko._.voidReturn; }, }); @@ -20,7 +21,6 @@ export interface Input {} import("./components/test-tag.marko"), ); Marko._.renderTemplate(__marko_internal_tag_2)()()({ - // ^? [Marko._.contentFor(__marko_internal_tag_2)]: (a) => { var __marko_internal_return = Marko._.returnTag({ value: a, diff --git a/packages/language-server/src/__tests__/fixtures/script/split-component-d-ts/__snapshots__/split-component-d-ts.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/split-component-d-ts/__snapshots__/split-component-d-ts.expected/index.ts index 5fc8abd1..3f36807c 100644 --- a/packages/language-server/src/__tests__/fixtures/script/split-component-d-ts/__snapshots__/split-component-d-ts.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/split-component-d-ts/__snapshots__/split-component-d-ts.expected/index.ts @@ -32,6 +32,7 @@ export { type Component }; })(), }, ); + // ^? Marko._.noop({ component, state, out, input, $global, $signal }); return; })(); diff --git a/packages/language-server/src/__tests__/fixtures/script/tag-params-basic/__snapshots__/tag-params-basic.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/tag-params-basic/__snapshots__/tag-params-basic.expected/index.ts index 79ee066f..22cf9512 100644 --- a/packages/language-server/src/__tests__/fixtures/script/tag-params-basic/__snapshots__/tag-params-basic.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/tag-params-basic/__snapshots__/tag-params-basic.expected/index.ts @@ -17,6 +17,7 @@ export interface Input {} // ^? ^? a; b; + // ^? ^? return Marko._.voidReturn; }, }); @@ -26,7 +27,6 @@ export interface Input {} const __marko_internal_rendered_2 = Marko._.renderTemplate( __marko_internal_tag_2, )()()({ - // ^? ^? [Marko._.contentFor(__marko_internal_tag_2)]: (a) => { const __marko_internal_tag_3 = Marko._.resolveTemplate( import("@marko/runtime-tags/tags/const.d.marko"), diff --git a/packages/language-server/src/__tests__/fixtures/script/tag-params-with-generics/__snapshots__/tag-params-with-generics.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/tag-params-with-generics/__snapshots__/tag-params-with-generics.expected/index.ts index c702c510..363e2d18 100644 --- a/packages/language-server/src/__tests__/fixtures/script/tag-params-with-generics/__snapshots__/tag-params-with-generics.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/tag-params-with-generics/__snapshots__/tag-params-with-generics.expected/index.ts @@ -83,4 +83,3 @@ export default new (class Template extends Marko._.Template<{ Marko._.Relate<__marko_internal_input, Marko.Directives & Input>, ) => Marko._.ReturnWithScope<__marko_internal_input, void>; }> {})(); -// ^? diff --git a/packages/language-server/src/__tests__/fixtures/script/tag-type-params/__snapshots__/tag-type-params.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/tag-type-params/__snapshots__/tag-type-params.expected/index.ts index 25d484bf..073cd9d5 100644 --- a/packages/language-server/src/__tests__/fixtures/script/tag-type-params/__snapshots__/tag-type-params.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/tag-type-params/__snapshots__/tag-type-params.expected/index.ts @@ -17,6 +17,7 @@ export interface Input {} )()()({ data: 1 as const, [Marko._.contentFor(__marko_internal_tag_1)]: (data: A) => { + // ^? var __marko_internal_return = Marko._.returnTag( // ^? { @@ -29,8 +30,8 @@ export interface Input {} })(__marko_internal_return); }, }); - // ^? result; + // ^? var __marko_internal_hoist__result = result; } Marko._.noop({ result, input, $global, $signal }); diff --git a/packages/language-server/src/__tests__/fixtures/script/tag-var-hoisting-from-control-flow/__snapshots__/tag-var-hoisting-from-control-flow.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/tag-var-hoisting-from-control-flow/__snapshots__/tag-var-hoisting-from-control-flow.expected/index.ts index e7ca6214..3e548343 100644 --- a/packages/language-server/src/__tests__/fixtures/script/tag-var-hoisting-from-control-flow/__snapshots__/tag-var-hoisting-from-control-flow.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/tag-var-hoisting-from-control-flow/__snapshots__/tag-var-hoisting-from-control-flow.expected/index.ts @@ -54,6 +54,7 @@ export interface Input {} value: () => a.filter((e) => e.length), }); baz; + // ^? return new (class MarkoReturn { readonly [Marko._.scope] = { c }; declare return: Return; @@ -72,7 +73,6 @@ export interface Input {} } })(); const __marko_internal_rendered_6 = (() => { - // ^? if ($global.foo) { const __marko_internal_tag_7 = Marko._.resolveTemplate( import("@marko/runtime-tags/tags/const.d.marko"), diff --git a/packages/language-server/src/__tests__/fixtures/script/tags-api-basic/__snapshots__/tags-api-basic.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/tags-api-basic/__snapshots__/tags-api-basic.expected/index.ts index a9dbd6c1..7b6286a9 100644 --- a/packages/language-server/src/__tests__/fixtures/script/tags-api-basic/__snapshots__/tags-api-basic.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/tags-api-basic/__snapshots__/tags-api-basic.expected/index.ts @@ -87,22 +87,20 @@ function __marko_internal_template(this: void) { })(), }, ); + // ^? ^? return Marko._.voidReturn; }, ); - Marko._.renderNativeTag("div")()()( - // ^? ^? - { - class: Marko._.interpolated`container`, - [Marko._.content]: (() => { - const __marko_internal_tag_2 = renderBody; - Marko._.renderDynamicTag(__marko_internal_tag_2)()()({}); - return () => { - return Marko._.voidReturn; - }; - })(), - }, - ); + Marko._.renderNativeTag("div")()()({ + class: Marko._.interpolated`container`, + [Marko._.content]: (() => { + const __marko_internal_tag_2 = renderBody; + Marko._.renderDynamicTag(__marko_internal_tag_2)()()({}); + return () => { + return Marko._.voidReturn; + }; + })(), + }); const __marko_internal_tag_3 = FancyButton; Marko._.renderDynamicTag(__marko_internal_tag_3)()()({ "no-update": true, diff --git a/packages/language-server/src/__tests__/fixtures/script/while-tag/__snapshots__/while-tag.expected/index.ts b/packages/language-server/src/__tests__/fixtures/script/while-tag/__snapshots__/while-tag.expected/index.ts index d2998709..4605e6cc 100644 --- a/packages/language-server/src/__tests__/fixtures/script/while-tag/__snapshots__/while-tag.expected/index.ts +++ b/packages/language-server/src/__tests__/fixtures/script/while-tag/__snapshots__/while-tag.expected/index.ts @@ -15,8 +15,8 @@ export { type Component }; while (++i < 10) { // ^? i; + // ^? } - // ^? i = 0; while (++i < 10) {} // ^? diff --git a/packages/language-server/src/service/marko/document-links.ts b/packages/language-server/src/service/marko/document-links.ts index a503d8c9..a87ab5e1 100644 --- a/packages/language-server/src/service/marko/document-links.ts +++ b/packages/language-server/src/service/marko/document-links.ts @@ -28,7 +28,7 @@ function extractDocumentLinks({ const links: DocumentLink[] = []; const { program, read } = parsed; - const visit = (node: Node.ChildNode) => { + const visit = (node: Node.RootBodyNode) => { switch (node.type) { case NodeType.AttrTag: if (node.body) { diff --git a/packages/language-server/src/service/marko/document-symbols.ts b/packages/language-server/src/service/marko/document-symbols.ts index fa6e79e7..dae2563b 100644 --- a/packages/language-server/src/service/marko/document-symbols.ts +++ b/packages/language-server/src/service/marko/document-symbols.ts @@ -23,7 +23,7 @@ function extractDocumentSymbols({ const symbols: SymbolInformation[] = []; const { program } = parsed; - const visit = (node: Node.ChildNode) => { + const visit = (node: Node.RootBodyNode) => { switch (node.type) { case NodeType.Tag: case NodeType.AttrTag: diff --git a/packages/language-server/src/service/script/index.ts b/packages/language-server/src/service/script/index.ts index ee2304ca..5dae1e4d 100644 --- a/packages/language-server/src/service/script/index.ts +++ b/packages/language-server/src/service/script/index.ts @@ -774,10 +774,14 @@ function getInsertModuleStatementOffset(parsed: Parsed) { firstNode = program.static[0]; } - if (program.body.length) { - if (!firstNode || firstNode.start > program.body[0].start) { - firstNode = program.body[0]; + for (const node of program.body) { + // Comments in the body are the attached leading comments of the node + // which follows them, so skip to that node. + if (node.type === NodeType.Comment) continue; + if (!firstNode || firstNode.start > node.start) { + firstNode = node; } + break; } // Fall back to after the comments of the first node, diff --git a/packages/language-tools/package.json b/packages/language-tools/package.json index 79103ed6..ffb0d7c7 100644 --- a/packages/language-tools/package.json +++ b/packages/language-tools/package.json @@ -39,7 +39,7 @@ "dependencies": { "@luxass/strip-json-comments": "^1.4.0", "@marko/compiler": "^5.41.7", - "htmljs-parser": "^5.12.1", + "@marko/parse": "^0.0.0", "relative-import-path": "^1.0.1" }, "devDependencies": { diff --git a/packages/language-tools/src/extractors/css-module/index.ts b/packages/language-tools/src/extractors/css-module/index.ts index e63a23ae..0598b024 100644 --- a/packages/language-tools/src/extractors/css-module/index.ts +++ b/packages/language-tools/src/extractors/css-module/index.ts @@ -1,4 +1,5 @@ -import { getLines, getLocation, getPosition, type Range } from "../../parser"; +import { getLines, getLocation, getPosition, type Range } from "@marko/parse"; + import { type Extracted, Extractor, diff --git a/packages/language-tools/src/extractors/html/index.ts b/packages/language-tools/src/extractors/html/index.ts index 3896b3ac..028dd289 100644 --- a/packages/language-tools/src/extractors/html/index.ts +++ b/packages/language-tools/src/extractors/html/index.ts @@ -1,4 +1,5 @@ -import { type Node, NodeType, type Parsed, type Range } from "../../parser"; +import { type Node, NodeType, type Parsed, type Range } from "@marko/parse"; + import { Extractor } from "../../util/extractor"; import { AttributeValueType, @@ -30,7 +31,7 @@ class HTMLExtractor { return { extracted: this.#extractor.end(), nodeDetails: this.#nodeDetails }; } - #visitNode(node: Node.ChildNode) { + #visitNode(node: Node.RootBodyNode) { let hasDynamicBody = false, hasDynamicAttrs = false, isDynamic = false; @@ -107,7 +108,7 @@ class HTMLExtractor { } #writeCustomTag(node: Node.Tag) { - if (node.body) { + if (node.body && hasRenderedChildren(node.body)) { // Replace all unknown and undefined tag names with `div`s this.#extractor.write("
"); node.body.forEach((node) => this.#visitNode(node)); @@ -192,3 +193,10 @@ function isVoidTag(tagName: string | undefined) { function isEmptyRange(range: Range) { return range.start === range.end; } + +function hasRenderedChildren(body: Node.ChildNode[]) { + for (const child of body) { + if (child.type !== NodeType.Comment) return true; + } + return false; +} diff --git a/packages/language-tools/src/extractors/script/index.ts b/packages/language-tools/src/extractors/script/index.ts index df9024dd..0f1742a7 100644 --- a/packages/language-tools/src/extractors/script/index.ts +++ b/packages/language-tools/src/extractors/script/index.ts @@ -1,9 +1,5 @@ import type { types as t } from "@marko/compiler"; import type { TagDefinition, TaglibLookup } from "@marko/compiler/babel-utils"; -import path from "path"; -import { relativeImportPath } from "relative-import-path"; -import type TS from "typescript/lib/tsserverlibrary"; - import { isControlFlowTag, type Node, @@ -12,7 +8,11 @@ import { type Range, type Repeatable, type Repeated, -} from "../../parser"; +} from "@marko/parse"; +import path from "path"; +import { relativeImportPath } from "relative-import-path"; +import type TS from "typescript/lib/tsserverlibrary"; + import { Extractor } from "../../util/extractor"; import { findStyleSelectors } from "../../util/find-style-selectors"; import { normalizePath } from "../../util/normalize-path"; @@ -222,7 +222,10 @@ class ScriptExtractor { break; } case NodeType.Static: { - let start = node.start + "static ".length; + let start = + (node.name + ? node.name.end + : node.start + (node.target || "static").length) + 1; let end = node.end; this.#writeComments(node); @@ -514,10 +517,20 @@ function ${templateName}() {\n`); } #writeCommentPragmas(program: Node.Program) { + // Leading comments are attached to the node which follows them, so when + // the body begins with comment nodes look past them for the attachment. + let firstBodyNode: Node.RootBodyNode | undefined; + for (const node of program.body) { + if (node.type !== NodeType.Comment) { + firstBodyNode = node; + break; + } + } + const firstComments = program.static.length ? program.static[0].comments - : program.body.length - ? (program.body[0] as Node.Commentable).comments + : firstBodyNode + ? (firstBodyNode as Node.Commentable).comments : program.comments; if (firstComments) { @@ -533,29 +546,31 @@ function ${templateName}() {\n`); #writeComments(node: Node.Commentable) { if (node.comments) { for (const comment of node.comments) { - if (!WROTE_COMMENT.has(comment)) { - if (this.#code.charAt(comment.start + 1) === "/") { - this.#extractor.write("//").copy(comment.value).write("\n"); - } else if (this.#code.charAt(comment.start + 1) === "!") { - this.#extractor.write("/*"); - let startIndex = comment.value.start; - // handle closing JS comments _within_ the HTML comment - for (const { index } of this.#read(comment.value).matchAll( - /\*\//g, - )) { - this.#extractor - .copy({ - start: startIndex, - end: (startIndex = comment.value.start + index + 1), - }) - .write("\\"); - } - this.#extractor.copy({ start: startIndex, end: comment.value.end }); - this.#extractor.write("*/"); - } else { - this.#extractor.write("/*").copy(comment.value).write("*/"); - } + this.#writeComment(comment); + } + } + } + + #writeComment(comment: Node.Comment) { + if (!WROTE_COMMENT.has(comment)) { + if (this.#code.charAt(comment.start + 1) === "/") { + this.#extractor.write("//").copy(comment.value).write("\n"); + } else if (this.#code.charAt(comment.start + 1) === "!") { + this.#extractor.write("/*"); + let startIndex = comment.value.start; + // handle closing JS comments _within_ the HTML comment + for (const { index } of this.#read(comment.value).matchAll(/\*\//g)) { + this.#extractor + .copy({ + start: startIndex, + end: (startIndex = comment.value.start + index + 1), + }) + .write("\\"); } + this.#extractor.copy({ start: startIndex, end: comment.value.end }); + this.#extractor.write("*/"); + } else { + this.#extractor.write("/*").copy(comment.value).write("*/"); } } } @@ -783,6 +798,9 @@ constructor(_) {} case NodeType.Scriptlet: this.#writeScriptlet(child); break; + case NodeType.Comment: + this.#writeComment(child); + break; } } @@ -1601,11 +1619,21 @@ constructor(_) {} let content: ProcessedBody["content"]; let staticAttrTags: ProcessedBody["staticAttrTags"]; let dynamicAttrTagParents: ProcessedBody["dynamicAttrTagParents"]; + let pendingComments: Node.Comment[] | undefined; let i = 0; while (i <= last) { const child = body[i++]; + // Comments followed by a node are written as that node's leading + // comments; only comments at the end of the body (ignoring whitespace) + // have no node to attach to and must be written explicitly. + if (child.type === NodeType.Comment) { + (pendingComments ||= []).push(child); + } else if (!(child.type === NodeType.Text && this.#isEmptyText(child))) { + pendingComments = undefined; + } + switch (child.type) { case NodeType.AttrTag: { const attrName = child.nameText; @@ -1637,6 +1665,8 @@ constructor(_) {} loop: while (i <= last) { const nextChild = body[i++]; switch (nextChild.type) { + case NodeType.Comment: + continue loop; case NodeType.Text: // Ignore empty text nodes. if (this.#isEmptyText(nextChild)) { @@ -1727,6 +1757,14 @@ constructor(_) {} break; } + case NodeType.Comment: + case NodeType.Import: + case NodeType.Export: + case NodeType.Class: + case NodeType.Style: + case NodeType.Static: + break; + default: if (content) { content.push(child); @@ -1737,6 +1775,13 @@ constructor(_) {} } } + // Only written when there is other content; a comment-only body must not + // become `content` since that changes the shape of the generated code + // (eg for attr tag bodies) and with it diagnostic locations. + if (pendingComments && content) { + content.push(...pendingComments); + } + if (content || staticAttrTags || dynamicAttrTagParents) { return { content, staticAttrTags, dynamicAttrTagParents }; } diff --git a/packages/language-tools/src/extractors/script/util/attach-scopes.ts b/packages/language-tools/src/extractors/script/util/attach-scopes.ts index 388d9981..ca2cd206 100644 --- a/packages/language-tools/src/extractors/script/util/attach-scopes.ts +++ b/packages/language-tools/src/extractors/script/util/attach-scopes.ts @@ -1,5 +1,4 @@ import { types as t } from "@marko/compiler"; - import { type Node, NodeType, @@ -7,7 +6,8 @@ import { type Range, type Repeatable, type Repeated, -} from "../../../parser"; +} from "@marko/parse"; + import { ScriptParser } from "./script-parser"; export type Scope = ProgramScope | TagScope; @@ -162,7 +162,7 @@ export function crawlProgramScope(parsed: Parsed, ast: ScriptParser) { return mutations.sort(byStart) as Repeated; } - function visit(body: Node.ChildNode[], parentScope: Scope) { + function visit(body: Node.RootBodyNode[], parentScope: Scope) { for (const child of body) { switch (child.type) { case NodeType.Tag: diff --git a/packages/language-tools/src/extractors/script/util/get-runtime-api.ts b/packages/language-tools/src/extractors/script/util/get-runtime-api.ts index d0311bbd..07433068 100644 --- a/packages/language-tools/src/extractors/script/util/get-runtime-api.ts +++ b/packages/language-tools/src/extractors/script/util/get-runtime-api.ts @@ -1,6 +1,5 @@ import type { TaglibLookup } from "@marko/compiler/babel-utils"; - -import { type Node, NodeType, type Parsed } from "../../../parser"; +import { type Node, NodeType, type Parsed } from "@marko/parse"; export type RuntimeAPI = (typeof RuntimeAPI)[keyof typeof RuntimeAPI]; export const RuntimeAPI = { @@ -98,7 +97,7 @@ function detectAPIFromProgram( function detectAPIFromBody( parsed: Parsed, - body: undefined | Node.ChildNode[], + body: undefined | Node.RootBodyNode[], ): RuntimeAPI | void { if (body) { for (const child of body) { @@ -110,7 +109,7 @@ function detectAPIFromBody( function detectAPIFromChild( parsed: Parsed, - child: Node.ChildNode, + child: Node.RootBodyNode, ): RuntimeAPI | void { switch (child.type) { case NodeType.Scriptlet: diff --git a/packages/language-tools/src/extractors/script/util/is-text-only-script.ts b/packages/language-tools/src/extractors/script/util/is-text-only-script.ts index 4d1d9aef..cbe68a61 100644 --- a/packages/language-tools/src/extractors/script/util/is-text-only-script.ts +++ b/packages/language-tools/src/extractors/script/util/is-text-only-script.ts @@ -1,4 +1,4 @@ -import { type Node, NodeType, type Repeated } from "../../../parser"; +import { type Node, NodeType, type Repeated } from "@marko/parse"; export function isTextOnlyScript(tag: Node.ParentTag): tag is Node.Tag & { nameText: "script"; diff --git a/packages/language-tools/src/extractors/script/util/jsdoc-input-type.ts b/packages/language-tools/src/extractors/script/util/jsdoc-input-type.ts index 6b077f44..857ec1f9 100644 --- a/packages/language-tools/src/extractors/script/util/jsdoc-input-type.ts +++ b/packages/language-tools/src/extractors/script/util/jsdoc-input-type.ts @@ -1,7 +1,6 @@ +import type { Repeatable } from "@marko/parse"; import type TS from "typescript/lib/tsserverlibrary"; -import type { Repeatable } from "../../../parser"; - const MaybeInputTypedefReg = /@typedef\b[\s\S]*\bInput\b/; export default function getJSDocInputType(comment: string, ts: typeof TS) { diff --git a/packages/language-tools/src/extractors/script/util/script-parser.ts b/packages/language-tools/src/extractors/script/util/script-parser.ts index 06d8e642..2ccd84e3 100644 --- a/packages/language-tools/src/extractors/script/util/script-parser.ts +++ b/packages/language-tools/src/extractors/script/util/script-parser.ts @@ -3,8 +3,8 @@ import { parse as parseStatement, parseExpression, } from "@marko/compiler/internal/babel"; +import type { Node, Parsed } from "@marko/parse"; -import type { Node, Parsed } from "../../../parser"; import { isTextOnlyScript } from "./is-text-only-script"; const plugins = ["exportDefaultFrom", "importAssertions", "typescript"]; diff --git a/packages/language-tools/src/extractors/style/index.ts b/packages/language-tools/src/extractors/style/index.ts index 294f0cfc..7d3967cf 100644 --- a/packages/language-tools/src/extractors/style/index.ts +++ b/packages/language-tools/src/extractors/style/index.ts @@ -1,6 +1,6 @@ import type { TaglibLookup } from "@marko/compiler/babel-utils"; +import { type Node, NodeType, type Parsed } from "@marko/parse"; -import { type Node, NodeType, type Parsed } from "../../parser"; import { type Extracted, Extractor } from "../../util/extractor"; export interface ExtractStyleOptions { @@ -34,7 +34,7 @@ export function extractStyle(opts: ExtractStyleOptions) { return extractedByExt; - function visit(node: Node.ChildNode) { + function visit(node: Node.RootBodyNode) { switch (node.type) { case NodeType.AttrTag: if (node.body) { diff --git a/packages/language-tools/src/index.ts b/packages/language-tools/src/index.ts index d4618664..3856e175 100644 --- a/packages/language-tools/src/index.ts +++ b/packages/language-tools/src/index.ts @@ -2,10 +2,10 @@ export * from "./extractors/css-module"; export * from "./extractors/html"; export * from "./extractors/script"; export * from "./extractors/style"; -export * from "./parser"; export * as Processors from "./processors"; export { type Extracted } from "./util/extractor"; export { getExt } from "./util/get-ext"; export { isDefinitionFile } from "./util/is-definition-file"; export { normalizePath } from "./util/normalize-path"; export * as Project from "./util/project"; +export * from "@marko/parse"; diff --git a/packages/language-tools/src/processors/marko.ts b/packages/language-tools/src/processors/marko.ts index 204ede8a..2a3ebe2a 100644 --- a/packages/language-tools/src/processors/marko.ts +++ b/packages/language-tools/src/processors/marko.ts @@ -1,4 +1,5 @@ import type { Config, types as t } from "@marko/compiler"; +import { parse } from "@marko/parse"; import path from "path"; import type ts from "typescript/lib/tsserverlibrary"; @@ -8,7 +9,6 @@ import { INTERNAL_API_VAR, ScriptLang, } from "../extractors/script"; -import { parse } from "../parser"; import * as Project from "../util/project"; import type { ProcessorConfig } from "."; diff --git a/packages/language-tools/src/util/extractor.ts b/packages/language-tools/src/util/extractor.ts index 41abb916..f3359532 100644 --- a/packages/language-tools/src/util/extractor.ts +++ b/packages/language-tools/src/util/extractor.ts @@ -5,7 +5,7 @@ import { type Location, type Position, type Range, -} from "../parser"; +} from "@marko/parse"; /** * The minimal source an {@link Extractor} needs to maintain a source mapping. diff --git a/packages/language-tools/src/util/find-style-selectors.ts b/packages/language-tools/src/util/find-style-selectors.ts index 4ceeba3e..4bc5a2e3 100644 --- a/packages/language-tools/src/util/find-style-selectors.ts +++ b/packages/language-tools/src/util/find-style-selectors.ts @@ -1,4 +1,4 @@ -import type { Range } from "../parser"; +import type { Range } from "@marko/parse"; /** * Collect class (`.name`) and id (`#name`) selectors, mapping each unique name diff --git a/packages/language-tools/tsconfig.json b/packages/language-tools/tsconfig.json index 6ffbb5bd..98628250 100644 --- a/packages/language-tools/tsconfig.json +++ b/packages/language-tools/tsconfig.json @@ -6,5 +6,10 @@ "outDir": "dist", "rootDir": "src", "tsBuildInfoFile": "dist/.tsbuildinfo" - } + }, + "references": [ + { + "path": "../parse" + } + ] } diff --git a/packages/parse/.mocharc.json b/packages/parse/.mocharc.json new file mode 100644 index 00000000..bf5b11c3 --- /dev/null +++ b/packages/parse/.mocharc.json @@ -0,0 +1,5 @@ +{ + "timeout": 20000, + "enable-source-maps": true, + "require": ["tsx"] +} diff --git a/packages/parse/README.md b/packages/parse/README.md new file mode 100644 index 00000000..c272425a --- /dev/null +++ b/packages/parse/README.md @@ -0,0 +1,30 @@ +# `@marko/parse` + +A syntax tree parser for [Marko](https://markojs.com) templates, built on +[`htmljs-parser`](https://github.com/marko-js/htmljs-parser) (which is fully +re-exported, so consumers of this package never need to depend on it +directly). + +```ts +import { parse, NodeType } from "@marko/parse"; + +const { + program, // The syntax tree (comments and static statements are part of + // `program.body` in document order; statics are also available + // via `program.static`, and leading comments are attached to the + // nodes which follow them). + errors, // Syntax errors encountered while parsing (the tree is still + // produced on a best effort basis). + comments, // Every comment in document order. + nodeAt, // (offset: number) => the most specific node at that offset. + read, // (range: Range) => string + positionAt, // (offset: number) => { line, character } (0-based) + locationAt, // (range: Range) => { start, end } positions +} = parse(source, filename, { + // Optionally override how tag bodies parse (eg from taglib `parseOptions`). + getTagType(name) {}, +}); +``` + +Every node is a plain object with `start`/`end` source offsets, a string +`type` (see `NodeType`) and a `parent` back-reference. diff --git a/packages/parse/build.mts b/packages/parse/build.mts new file mode 100644 index 00000000..a9d6cfd5 --- /dev/null +++ b/packages/parse/build.mts @@ -0,0 +1,35 @@ +import { build, BuildOptions } from "esbuild"; + +const opts: BuildOptions = { + bundle: true, + outdir: "dist", + platform: "node", + target: ["node16"], + entryPoints: ["src/index.ts"], + plugins: [ + { + name: "external-modules", + setup(build) { + build.onResolve( + { filter: /^[^./]|^\.[^./]|^\.\.[^/]/ }, + ({ path }) => ({ + path, + external: true, + }), + ); + }, + }, + ], +}; + +await Promise.all([ + build({ + ...opts, + format: "cjs", + }), + build({ + ...opts, + format: "esm", + outExtension: { ".js": ".mjs" }, + }), +]); diff --git a/packages/parse/package.json b/packages/parse/package.json new file mode 100644 index 00000000..f2d565de --- /dev/null +++ b/packages/parse/package.json @@ -0,0 +1,42 @@ +{ + "name": "@marko/parse", + "version": "0.0.0", + "description": "A syntax tree parser for Marko templates", + "keywords": [ + "cst", + "marko", + "parse", + "parser", + "syntax" + ], + "homepage": "https://github.com/marko-js/language-server/tree/main/packages/parse/README.md", + "bugs": "https://github.com/marko-js/language-server/issues/new?template=bug-report.yml", + "repository": { + "type": "git", + "url": "https://github.com/marko-js/language-server/tree/main/packages/parse" + }, + "license": "MIT", + "exports": { + ".": { + "import": "./dist/index.mjs", + "default": "./dist/index.js" + } + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "files": [ + "dist", + "!**/__tests__", + "!**/*.tsbuildinfo" + ], + "scripts": { + "build": "tsx build.mts", + "test": "mocha './src/**/__tests__/*.test.ts'" + }, + "dependencies": { + "htmljs-parser": "^5.12.1" + }, + "devDependencies": { + "tsx": "^4.22.4" + } +} diff --git a/packages/parse/src/__tests__/parse.test.ts b/packages/parse/src/__tests__/parse.test.ts new file mode 100644 index 00000000..2c47f15d --- /dev/null +++ b/packages/parse/src/__tests__/parse.test.ts @@ -0,0 +1,71 @@ +import assert from "assert"; + +import { NodeType, parse, TagType } from ".."; + +describe("@marko/parse", () => { + it("parses a template with string node types", () => { + const parsed = parse('import x from "x"\n// hi\n
${x}
\n'); + assert.deepStrictEqual( + parsed.program.body.map((node) => node.type), + [NodeType.Import, NodeType.Comment, NodeType.Tag], + ); + assert.deepStrictEqual( + parsed.program.static.map((node) => node.type), + [NodeType.Import], + ); + }); + + it("attaches leading comments to the following node", () => { + const parsed = parse("// lead\n
\n// trailing\n"); + const tag = parsed.program.body.find((node) => node.type === NodeType.Tag)!; + assert.strictEqual((tag as any).comments?.length, 1); + assert.strictEqual(parsed.program.comments?.length, 1); + assert.strictEqual(parsed.comments.length, 2); + assert.strictEqual(parsed.comments[0].commentType, "line"); + }); + + it("scopes trailing comments to their body", () => { + const parsed = parse("
\n // inside\n
\n\n"); + const [div, span] = parsed.program.body; + assert.strictEqual( + (div as any).body.some((node: any) => node.type === NodeType.Comment), + true, + ); + assert.strictEqual((span as any).comments, undefined); + assert.strictEqual(parsed.program.comments, undefined); + }); + + it("collects syntax errors while still producing a tree", () => { + const parsed = parse("
"); + assert.strictEqual(parsed.errors.length, 1); + assert.match(parsed.errors[0].message, /closing "div" tag/); + assert.strictEqual(parsed.program.body[0].type, NodeType.Tag); + }); + + it("honors the getTagType hook over built in handling", () => { + const parsed = parse( + "style {\n .a { color: red }\n}\nfoo raw text\n", + "index.marko", + { + getTagType(name) { + if (name === "style") return TagType.text; + if (name === "foo") return TagType.statement; + }, + }, + ); + + // The built in `style {}` statement handling is overridden to a text tag. + const style = parsed.program.body[0]; + assert.strictEqual(style.type, NodeType.Tag); + // The forced statement becomes a generic Static node with a name range. + const forced = parsed.program.static[0]; + assert.strictEqual(forced.type, NodeType.Static); + assert.strictEqual(forced.target, undefined); + assert.strictEqual(parsed.read(forced.name!), "foo"); + }); + + it("finds nodes by offset", () => { + const parsed = parse("
${x}
"); + assert.strictEqual(parsed.nodeAt(7).type, NodeType.Placeholder); + }); +}); diff --git a/packages/language-tools/src/util/get-node-at-offset.ts b/packages/parse/src/get-node-at-offset.ts similarity index 94% rename from packages/language-tools/src/util/get-node-at-offset.ts rename to packages/parse/src/get-node-at-offset.ts index 21a10f07..f151d349 100644 --- a/packages/language-tools/src/util/get-node-at-offset.ts +++ b/packages/parse/src/get-node-at-offset.ts @@ -1,4 +1,4 @@ -import { type Node, NodeType } from "../parser"; +import { type Node, NodeType } from "./index"; export function getNodeAtOffset( offset: number, @@ -9,7 +9,10 @@ export function getNodeAtOffset( return childAtOffset(offset, program.static) || program; } -function visitChildNode(offset: number, child: Node.ChildNode): Node.AnyNode { +function visitChildNode( + offset: number, + child: Node.ChildNode | Node.StaticNode, +): Node.AnyNode { switch (child.type) { case NodeType.Tag: case NodeType.AttrTag: diff --git a/packages/language-tools/src/parser.ts b/packages/parse/src/index.ts similarity index 74% rename from packages/language-tools/src/parser.ts rename to packages/parse/src/index.ts index 8d121e74..370894f6 100644 --- a/packages/language-tools/src/parser.ts +++ b/packages/parse/src/index.ts @@ -1,7 +1,7 @@ import { createParser, type Range, type Ranges, TagType } from "htmljs-parser"; -import { getNodeAtOffset } from "./util/get-node-at-offset"; -import { normalizePath } from "./util/normalize-path"; +import { getNodeAtOffset } from "./get-node-at-offset"; +import { normalizePath } from "./normalize-path"; const styleBlockReg = /((?:\.[^\s\\/:*?"<>|({]+)*)\s*\{/y; @@ -9,47 +9,61 @@ export type Repeated = [T, ...T[]] | [...T[], T] | [T, ...T[], T]; export type Repeatable = undefined | Repeated; export const UNFINISHED = Number.MAX_SAFE_INTEGER; -export { - getLines, - getLocation, - getPosition, - type Location, - type Position, - type Range, - type Ranges, -} from "htmljs-parser"; +export * from "htmljs-parser"; export type Parsed = ReturnType; + +/** + * Node type names are strings so that tools which select on a node's `type` + * (eg ESLint selectors) work naturally. + */ export enum NodeType { - Program, - Tag, - OpenTagName, - ShorthandId, - ShorthandClassName, - TagTypeArgs, - TagTypeParams, - TagVar, - TagArgs, - TagParams, - AttrNamed, - AttrName, - AttrArgs, - AttrValue, - AttrMethod, - AttrSpread, - AttrTag, - Text, - CDATA, - Doctype, - Declaration, - Comment, - Placeholder, - Scriptlet, - Import, - Export, - Class, - Style, - Static, + Program = "Program", + Tag = "Tag", + OpenTagName = "OpenTagName", + ShorthandId = "ShorthandId", + ShorthandClassName = "ShorthandClassName", + TagTypeArgs = "TagTypeArgs", + TagTypeParams = "TagTypeParams", + TagVar = "TagVar", + TagArgs = "TagArgs", + TagParams = "TagParams", + AttrNamed = "AttrNamed", + AttrName = "AttrName", + AttrArgs = "AttrArgs", + AttrValue = "AttrValue", + AttrMethod = "AttrMethod", + AttrSpread = "AttrSpread", + AttrTag = "AttrTag", + Text = "Text", + CDATA = "CDATA", + Doctype = "Doctype", + Declaration = "Declaration", + Comment = "Comment", + Placeholder = "Placeholder", + Scriptlet = "Scriptlet", + Import = "Import", + Export = "Export", + Class = "Class", + Style = "Style", + Static = "Static", +} + +export enum CommentType { + line = "line", + block = "block", + html = "html", +} + +export interface ParseOptions { + /** + * Overrides how a tag with the given name is parsed (eg from taglib + * `parseOptions`). When a type is returned it takes precedence over the + * built in core/html tag handling (including the core statements, which + * become generic `Static` nodes with a `name` range when forced); + * returning nothing falls back to the built in behavior. + */ + getTagType?(name: string, range: Range): TagType | undefined | void; } export namespace Node { @@ -84,6 +98,8 @@ export namespace Node { | Style | Static; export type ParentNode = Program | Tag | AttrTag; + /** Anything that can appear in the root `program.body`. */ + export type RootBodyNode = ChildNode | StaticNode; export type StaticNode = Import | Export | Class | Style | Static; export type ParentTag = Tag | AttrTag; export type AttrNode = AttrNamed | AttrSpread; @@ -99,7 +115,8 @@ export namespace Node { | Declaration | CDATA | Placeholder - | Scriptlet; + | Scriptlet + | Comment; export interface Commentable { comments: Repeatable; @@ -109,7 +126,7 @@ export namespace Node { type: NodeType.Program; parent: undefined; static: StaticNode[]; - body: ChildNode[]; + body: RootBodyNode[]; } export interface Tag extends Range, Commentable { @@ -221,6 +238,7 @@ export namespace Node { export interface Comment extends Ranges.Value { type: NodeType.Comment; parent: ParentNode; + commentType: CommentType; } export interface Placeholder extends Ranges.Value, Commentable { @@ -298,11 +316,23 @@ export namespace Node { export interface Static extends Range, Commentable { type: NodeType.Static; parent: ParentNode; + /** + * The statement keyword (undefined when forced via `getTagType`). + */ + target: "client" | "server" | "static" | undefined; + /** + * The range of the statement's name (set when forced via `getTagType`). + */ + name: Range | undefined; } } -export function parse(code: string, filename = "index.marko") { - const builder = new Builder(code); +export function parse( + code: string, + filename = "index.marko", + options?: ParseOptions, +) { + const builder = new Builder(code, options); const parser = createParser(builder); parser.parse(code); @@ -314,11 +344,17 @@ export function parse(code: string, filename = "index.marko") { nodeAt: (offset: number) => getNodeAtOffset(offset, program), filename: normalizePath(filename), program, + /** Every comment in the template, in document order. */ + comments: builder.comments, + /** Syntax errors encountered while parsing. */ + errors: builder.errors, code, }; } class Builder { + public comments: Node.Comment[] = []; + public errors: Ranges.Error[] = []; #code: string; #program: Node.Program; #openTagStart: Range | undefined; @@ -326,9 +362,11 @@ class Builder { #staticNode: Node.StaticNode | undefined; #attrNode: Node.AttrNamed | undefined; #comments: Repeatable; + #getTagType: ParseOptions["getTagType"]; - constructor(code: string) { + constructor(code: string, options?: ParseOptions) { this.#code = code; + this.#getTagType = options?.getTagType; this.#program = this.#parentNode = { type: NodeType.Program, comments: undefined, @@ -345,6 +383,12 @@ class Builder { return this.#program; } + #pushStatic(node: Node.StaticNode) { + this.#staticNode = node; + this.#program.static.push(node); + this.#program.body.push(node); + } + onText(range: Range) { pushBody(this.#parentNode, { type: NodeType.Text, @@ -381,19 +425,41 @@ class Builder { }); } onComment(range: Ranges.Value) { + let commentType = CommentType.html; + switch (this.#code.charCodeAt(range.start + 1)) { + case 47: // / + commentType = CommentType.line; + break; + case 42: // * + commentType = CommentType.block; + break; + } + const comment: Node.Comment = { type: NodeType.Comment, parent: this.#parentNode, + commentType, value: range.value, start: range.start, end: range.end, }; + + // The same comment node is indexed three ways for different consumers: + // the flat list (all comments without a tree walk), the parent body + // (authoritative document-order position) and, below, buffered to attach + // as the leading comments of the node which follows it. + this.comments.push(comment); + pushBody(this.#parentNode, comment); + if (this.#comments) { this.#comments.push(comment); } else { this.#comments = [comment]; } } + onError(range: Ranges.Error) { + this.errors.push(range); + } onPlaceholder(range: Ranges.Placeholder) { pushBody(this.#parentNode, { type: NodeType.Placeholder, @@ -436,16 +502,38 @@ class Builder { } if (!range.expressions.length) { - switch ((nameText = this.#code.slice(range.start, range.end) || "div")) { - // All statement types will early return. - case "style": { - styleBlockReg.lastIndex = range.end; - const styleBlockMatch = styleBlockReg.exec(this.#code); - - if (styleBlockMatch) { - const [{ length }, ext] = styleBlockMatch; - this.#program.static.push( - (this.#staticNode = { + nameText = this.#code.slice(range.start, range.end) || "div"; + + const hookedType = + nameText[0] === "@" ? undefined : this.#getTagType?.(nameText, range); + + if (hookedType !== undefined) { + if (hookedType === TagType.statement) { + this.#pushStatic({ + type: NodeType.Static, + parent: this.#program, + comments: this.#comments, + target: undefined, + name: { start: range.start, end: range.end }, + start: range.start, + end: UNFINISHED, + }); + + this.#comments = undefined; + return TagType.statement; + } + + bodyType = hookedType; + } else + switch (nameText) { + // All statement types will early return. + case "style": { + styleBlockReg.lastIndex = range.end; + const styleBlockMatch = styleBlockReg.exec(this.#code); + + if (styleBlockMatch) { + const [{ length }, ext] = styleBlockMatch; + this.#pushStatic({ type: NodeType.Style, parent: this.#program, comments: this.#comments, @@ -456,109 +544,102 @@ class Builder { }, start: range.start, end: UNFINISHED, - }), - ); - - this.#comments = undefined; - return TagType.statement; - } else { - bodyType = TagType.text; - break; + }); + + this.#comments = undefined; + return TagType.statement; + } else { + bodyType = TagType.text; + break; + } } - } - case "class": - this.#program.static.push( - (this.#staticNode = { + case "class": + this.#pushStatic({ type: NodeType.Class, parent: this.#program, comments: this.#comments, start: range.start, end: UNFINISHED, - }), - ); + }); - this.#comments = undefined; - return TagType.statement; - case "export": - this.#program.static.push( - (this.#staticNode = { + this.#comments = undefined; + return TagType.statement; + case "export": + this.#pushStatic({ type: NodeType.Export, parent: this.#program, comments: this.#comments, start: range.start, end: UNFINISHED, - }), - ); + }); - this.#comments = undefined; - return TagType.statement; - case "import": - this.#program.static.push( - (this.#staticNode = { + this.#comments = undefined; + return TagType.statement; + case "import": + this.#pushStatic({ type: NodeType.Import, parent: this.#program, comments: this.#comments, start: range.start, end: UNFINISHED, - }), - ); + }); - this.#comments = undefined; - return TagType.statement; - case "server": - case "client": - case "static": - this.#program.static.push( - (this.#staticNode = { + this.#comments = undefined; + return TagType.statement; + case "server": + case "client": + case "static": + this.#pushStatic({ type: NodeType.Static, parent: this.#program, comments: this.#comments, + target: nameText, + name: undefined, start: range.start, end: UNFINISHED, - }), - ); + }); - this.#comments = undefined; - return TagType.statement; + this.#comments = undefined; + return TagType.statement; - // The following are all still tags, - // but with a different body type. - case "area": - case "base": - case "br": - case "col": - case "embed": - case "hr": - case "img": - case "input": - case "link": - case "meta": - case "param": - case "source": - case "track": - case "wbr": - case "const": - case "debug": - case "id": - case "let": - case "lifecycle": - case "log": - case "return": - bodyType = TagType.void; - break; - case "html-comment": - case "html-script": - case "html-style": - case "script": - case "textarea": - bodyType = TagType.text; - break; - default: - if (nameText[0] === "@") { - type = NodeType.AttrTag; - } - break; - } + // The following are all still tags, + // but with a different body type. + case "area": + case "base": + case "br": + case "col": + case "embed": + case "hr": + case "img": + case "input": + case "link": + case "meta": + case "param": + case "source": + case "track": + case "wbr": + case "const": + case "debug": + case "id": + case "let": + case "lifecycle": + case "log": + case "return": + bodyType = TagType.void; + break; + case "html-comment": + case "html-script": + case "html-style": + case "script": + case "textarea": + bodyType = TagType.text; + break; + default: + if (nameText[0] === "@") { + type = NodeType.AttrTag; + } + break; + } } const parent = this.#parentNode as Node.ParentNode; @@ -811,6 +892,9 @@ class Builder { if (hasCloseTag(parent)) parent.close.end = range.end; parent.end = range.end; this.#parentNode = parent.parent; + // Trailing comments in the closed body must not attach as the leading + // comments of whatever follows the tag; they remain body children only. + this.#comments = undefined; } } diff --git a/packages/parse/src/normalize-path.ts b/packages/parse/src/normalize-path.ts new file mode 100644 index 00000000..23f3883b --- /dev/null +++ b/packages/parse/src/normalize-path.ts @@ -0,0 +1,8 @@ +import path from "path"; + +const sepReg = /\//g; + +export const normalizePath: (filename: string) => string = + path.sep === "/" + ? (filename) => filename + : (filename) => filename.replace(sepReg, path.sep); diff --git a/packages/parse/tsconfig.json b/packages/parse/tsconfig.json new file mode 100644 index 00000000..6ffbb5bd --- /dev/null +++ b/packages/parse/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*"], + "exclude": ["**/__tests__"], + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "tsBuildInfoFile": "dist/.tsbuildinfo" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f09b6f47..56f51c7f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -142,9 +142,9 @@ importers: '@marko/compiler': specifier: ^5.41.7 version: 5.41.7 - htmljs-parser: - specifier: ^5.12.1 - version: 5.12.1 + '@marko/parse': + specifier: ^0.0.0 + version: link:../parse relative-import-path: specifier: ^1.0.1 version: 1.0.1 @@ -165,6 +165,16 @@ importers: specifier: ^4.22.4 version: 4.22.4 + packages/parse: + dependencies: + htmljs-parser: + specifier: ^5.12.1 + version: 5.12.1 + devDependencies: + tsx: + specifier: ^4.22.4 + version: 4.22.4 + packages/ts-plugin: devDependencies: '@marko/language-server': diff --git a/tsconfig.json b/tsconfig.json index 7e7d3e4a..7f6fb248 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "files": [], "references": [ + { "path": "./packages/parse" }, { "path": "./packages/language-tools" }, { "path": "./packages/language-server" }, { "path": "./packages/ts-plugin" },