From 25d0bbf4e4dcd2c95deeca1cd87127b8fe93442f Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sat, 28 Jun 2025 11:03:57 +0200 Subject: [PATCH 01/45] chore: update `esrap` --- packages/core/package.json | 2 +- packages/core/tests/js/index.ts | 4 +- packages/core/tests/utils.ts | 64 ++------------------- packages/core/tooling/index.ts | 91 +++++++++--------------------- packages/core/tooling/js/common.ts | 16 +++--- packages/core/tooling/parsers.ts | 10 ++-- pnpm-lock.yaml | 11 +++- 7 files changed, 59 insertions(+), 139 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 8897e9904..cc4707e72 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -51,7 +51,7 @@ "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", - "esrap": "^1.4.9", + "esrap": "^2.0.0", "htmlparser2": "^9.1.0", "magic-string": "^0.30.17", "picocolors": "^1.1.1", diff --git a/packages/core/tests/js/index.ts b/packages/core/tests/js/index.ts index c7f2021e6..9aca4de6a 100644 --- a/packages/core/tests/js/index.ts +++ b/packages/core/tests/js/index.ts @@ -16,13 +16,13 @@ for (const categoryDirectory of categoryDirectories) { const inputFilePath = join(testDirectoryPath, 'input.ts'); const input = fs.existsSync(inputFilePath) ? fs.readFileSync(inputFilePath, 'utf8') : ''; - const ast = parseScript(input); + const { ast, comments } = parseScript(input); // dynamic imports always need to provide the path inline for static analysis const module = await import(`./${categoryDirectory}/${testName}/run.ts`); module.run(ast); - let output = serializeScript(ast, input); + let output = serializeScript(ast, comments, input); if (!output.endsWith('\n')) output += '\n'; await expect(output).toMatchFileSnapshot(`${testDirectoryPath}/output.ts`); }); diff --git a/packages/core/tests/utils.ts b/packages/core/tests/utils.ts index 37979d0be..ef3272504 100644 --- a/packages/core/tests/utils.ts +++ b/packages/core/tests/utils.ts @@ -4,7 +4,6 @@ import { parseScript, serializeScript, guessIndentString, - guessQuoteStyle, type AstTypes } from '../tooling/index.ts'; @@ -48,57 +47,6 @@ test('guessIndentString - eight spaces', () => { expect(guessIndentString(code)).toBe(' '); }); -test('guessQuoteStyle - single simple', () => { - const code = dedent` - console.log('asd'); - `; - const ast = parseScript(code); - - expect(guessQuoteStyle(ast)).toBe('single'); -}); - -test('guessQuoteStyle - single complex', () => { - const code = dedent` - import foo from 'bar'; - - console.log("bar"); - const foobar = 'foo'; - `; - const ast = parseScript(code); - - expect(guessQuoteStyle(ast)).toBe('single'); -}); - -test('guessQuoteStyle - double simple', () => { - const code = dedent` - console.log("asd"); - `; - const ast = parseScript(code); - - expect(guessQuoteStyle(ast)).toBe('double'); -}); - -test('guessQuoteStyle - double complex', () => { - const code = dedent` - import foo from 'bar'; - - console.log("bar"); - const foobar = "foo"; - `; - const ast = parseScript(code); - - expect(guessQuoteStyle(ast)).toBe('double'); -}); - -test('guessQuoteStyle - no quotes', () => { - const code = dedent` - const foo = true; - `; - const ast = parseScript(code); - - expect(guessQuoteStyle(ast)).toBe(undefined); -}); - const newVariableDeclaration: AstTypes.VariableDeclaration = { type: 'VariableDeclaration', kind: 'const', @@ -126,13 +74,13 @@ test('integration - simple', () => { const foobar = "foo"; } `; - const ast = parseScript(code); + const { ast, comments } = parseScript(code); const method = ast.body[1] as AstTypes.FunctionDeclaration; method.body.body.push(newVariableDeclaration); // new variable is added with correct indentation and matching quotes - expect(serializeScript(ast, code)).toMatchInlineSnapshot(` + expect(serializeScript(ast, comments, code)).toMatchInlineSnapshot(` "import foo from 'bar'; function bar() { @@ -153,13 +101,13 @@ test('integration - simple 2', () => { const foobar = 'foo'; } `; - const ast = parseScript(code); + const { ast, comments } = parseScript(code); const method = ast.body[1] as AstTypes.FunctionDeclaration; method.body.body.push(newVariableDeclaration); // new variable is added with correct indentation and matching quotes - expect(serializeScript(ast, code)).toMatchInlineSnapshot(` + expect(serializeScript(ast, comments, code)).toMatchInlineSnapshot(` "import foo from 'bar'; function bar() { @@ -176,9 +124,9 @@ test('integration - preserves comments', () => { /** @type {string} */ let foo = 'bar'; `; - const ast = parseScript(code); + const { ast, comments } = parseScript(code); - expect(serializeScript(ast, code)).toMatchInlineSnapshot(` + expect(serializeScript(ast, comments, code)).toMatchInlineSnapshot(` "/** @type {string} */ let foo = 'bar';" `); diff --git a/packages/core/tooling/index.ts b/packages/core/tooling/index.ts index ac42aa48f..450666edb 100644 --- a/packages/core/tooling/index.ts +++ b/packages/core/tooling/index.ts @@ -14,6 +14,7 @@ import { } from 'postcss'; import * as fleece from 'silver-fleece'; import { print as esrapPrint } from 'esrap'; +import ts from 'esrap/languages/ts'; import * as acorn from 'acorn'; import { tsPlugin } from '@sveltejs/acorn-typescript'; @@ -47,19 +48,21 @@ export type { /** * Parses as string to an AST. Code below is taken from `esrap` to ensure compatibilty. - * https://github.com/sveltejs/esrap/blob/9daf5dd43b31f17f596aa7da91678f2650666dd0/test/common.js#L12 + * https://github.com/sveltejs/esrap/blob/920491535d31484ac5fae2327c7826839d851aed/test/common.js#L14 */ -export function parseScript(content: string): TsEstree.Program { +export function parseScript(content: string): { + ast: TsEstree.Program; + comments: TsEstree.Comment[]; +} { const comments: TsEstree.Comment[] = []; const acornTs = acorn.Parser.extend(tsPlugin()); - // Acorn doesn't add comments to the AST by itself. This factory returns the capabilities to add them after the fact. const ast = acornTs.parse(content, { ecmaVersion: 'latest', sourceType: 'module', locations: true, - onComment: (block, value, start, end) => { + onComment: (block, value, start, end, startLoc, endLoc) => { if (block && /\n/.test(value)) { let a = start; while (a > 0 && content[a - 1] !== '\n') a -= 1; @@ -71,38 +74,31 @@ export function parseScript(content: string): TsEstree.Program { value = value.replace(new RegExp(`^${indentation}`, 'gm'), ''); } - comments.push({ type: block ? 'Block' : 'Line', value, start, end }); + comments.push({ + type: block ? 'Block' : 'Line', + value, + start, + end, + loc: { start: startLoc as TsEstree.Position, end: endLoc as TsEstree.Position } + }); } }) as TsEstree.Program; - Walker.walk(ast as TsEstree.Node, null, { - _(commentNode, { next }) { - let comment: TsEstree.Comment; - - while (comments[0] && commentNode.start && comments[0].start! < commentNode.start) { - comment = comments.shift()!; - (commentNode.leadingComments ??= []).push(comment); - } - - next(); - - if (comments[0]) { - const slice = content.slice(commentNode.end, comments[0].start); - - if (/^[,) \t]*$/.test(slice)) { - commentNode.trailingComments = [comments.shift()!]; - } - } - } - }); - - return ast; + return { + ast, + comments + }; } -export function serializeScript(ast: TsEstree.Node, previousContent?: string): string { - const { code } = esrapPrint(ast, { - indent: guessIndentString(previousContent), - quotes: guessQuoteStyle(ast) +export function serializeScript( + ast: TsEstree.Node, + comments: TsEstree.Comment[], + previousContent?: string +): string { + // @ts-expect-error we are still using `estree` while `esrap` is using `@typescript-eslint/types` + // which is causing these errors. But they are simmilar enough to work together. + const { code } = esrapPrint(ast, ts({ comments }), { + indent: guessIndentString(previousContent) }); return code; } @@ -205,36 +201,3 @@ export function guessIndentString(str: string | undefined): string { return '\t'; } } - -export function guessQuoteStyle(ast: TsEstree.Node): 'single' | 'double' | undefined { - let singleCount = 0; - let doubleCount = 0; - - Walker.walk(ast, null, { - Literal(node) { - if (node.raw && node.raw.length >= 2) { - // we have at least two characters in the raw string that could represent both quotes - const quotes = [node.raw[0], node.raw[node.raw.length - 1]]; - for (const quote of quotes) { - switch (quote) { - case "'": - singleCount++; - break; - case '"': - doubleCount++; - break; - default: - break; - } - } - } - } - }); - - if (singleCount === 0 && doubleCount === 0) { - // new file or file without any quotes - return undefined; - } - - return singleCount > doubleCount ? 'single' : 'double'; -} diff --git a/packages/core/tooling/js/common.ts b/packages/core/tooling/js/common.ts index 8812660e2..d249b693b 100644 --- a/packages/core/tooling/js/common.ts +++ b/packages/core/tooling/js/common.ts @@ -93,7 +93,7 @@ export function areNodesEqual(node: AstTypes.Node, otherNode: AstTypes.Node): bo const nodeClone = stripAst(decircular(node), ['loc', 'raw']); const otherNodeClone = stripAst(decircular(otherNode), ['loc', 'raw']); - return serializeScript(nodeClone) === serializeScript(otherNodeClone); + return serializeScript(nodeClone, []) === serializeScript(otherNodeClone, []); } export function createBlockStatement(): AstTypes.BlockStatement { @@ -118,18 +118,18 @@ export function appendFromString( node: AstTypes.BlockStatement | AstTypes.Program, options: { code: string } ): void { - const program = parseScript(dedent(options.code)); + const { ast } = parseScript(dedent(options.code)); - for (const childNode of program.body) { + for (const childNode of ast.body) { // @ts-expect-error node.body.push(childNode); } } export function parseExpression(code: string): AstTypes.Expression { - const program = parseScript(dedent(code)); - stripAst(program, ['raw']); - const statement = program.body[0]!; + const { ast } = parseScript(dedent(code)); + stripAst(ast, ['raw']); + const statement = ast.body[0]!; if (statement.type !== 'ExpressionStatement') { throw new Error('Code provided was not an expression'); } @@ -142,8 +142,8 @@ export function parseStatement(code: string): AstTypes.Statement { } export function parseFromString(code: string): T { - const program = parseScript(dedent(code)); - const statement = program.body[0]!; + const { ast } = parseScript(dedent(code)); + const statement = ast.body[0]!; return statement as T; } diff --git a/packages/core/tooling/parsers.ts b/packages/core/tooling/parsers.ts index f7764d34f..7d39bad23 100644 --- a/packages/core/tooling/parsers.ts +++ b/packages/core/tooling/parsers.ts @@ -6,11 +6,13 @@ type ParseBase = { generateCode(): string; }; -export function parseScript(source: string): { ast: utils.AstTypes.Program } & ParseBase { - const ast = utils.parseScript(source); - const generateCode = () => utils.serializeScript(ast, source); +export function parseScript( + source: string +): { ast: utils.AstTypes.Program; comments: utils.AstTypes.Comment[] } & ParseBase { + const { ast, comments } = utils.parseScript(source); + const generateCode = () => utils.serializeScript(ast, comments, source); - return { ast, source, generateCode }; + return { ast, comments, source, generateCode }; } export function parseCss(source: string): { ast: utils.CssAst } & ParseBase { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 959d7df38..0f3596458 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -170,8 +170,8 @@ importers: specifier: ^3.2.2 version: 3.2.2 esrap: - specifier: ^1.4.9 - version: 1.4.9 + specifier: ^2.0.0 + version: 2.0.1 htmlparser2: specifier: ^9.1.0 version: 9.1.0 @@ -1299,6 +1299,9 @@ packages: esrap@1.4.9: resolution: {integrity: sha512-3OMlcd0a03UGuZpPeUC1HxR3nA23l+HEyCiZw3b3FumJIN9KphoGzDJKMXI1S72jVS1dsenDyQC0kJlO1U9E1g==} + esrap@2.0.1: + resolution: {integrity: sha512-6n1JodkxeMvyTDCog7J//t8Yti//fGicZgtFLko6h/aEpc54BK9O8k9cZgC2J8+2Dh1U5uYIxuJWSsylybvFBA==} + esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -3298,6 +3301,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + esrap@2.0.1: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 From 9394c80a3803bc60dadb2ff40636509e620d1766 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 26 Oct 2025 06:55:29 +0100 Subject: [PATCH 02/45] use esrap pkg.pr.new --- packages/core/package.json | 2 +- packages/core/tooling/index.ts | 7 ++++--- pnpm-lock.yaml | 11 ++++++----- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index cc4707e72..350e3fdcf 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -51,7 +51,7 @@ "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", - "esrap": "^2.0.0", + "esrap": "https://pkg.pr.new/sveltejs/esrap@718afce", "htmlparser2": "^9.1.0", "magic-string": "^0.30.17", "picocolors": "^1.1.1", diff --git a/packages/core/tooling/index.ts b/packages/core/tooling/index.ts index 450666edb..1d56b51e8 100644 --- a/packages/core/tooling/index.ts +++ b/packages/core/tooling/index.ts @@ -14,7 +14,7 @@ import { } from 'postcss'; import * as fleece from 'silver-fleece'; import { print as esrapPrint } from 'esrap'; -import ts from 'esrap/languages/ts'; +import ts, { type AdditionalComment } from 'esrap/languages/ts'; import * as acorn from 'acorn'; import { tsPlugin } from '@sveltejs/acorn-typescript'; @@ -93,11 +93,12 @@ export function parseScript(content: string): { export function serializeScript( ast: TsEstree.Node, comments: TsEstree.Comment[], - previousContent?: string + previousContent?: string, + additionalComments?: WeakMap ): string { // @ts-expect-error we are still using `estree` while `esrap` is using `@typescript-eslint/types` // which is causing these errors. But they are simmilar enough to work together. - const { code } = esrapPrint(ast, ts({ comments }), { + const { code } = esrapPrint(ast, ts({ comments, additionalComments }), { indent: guessIndentString(previousContent) }); return code; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b9000f76..af8676d00 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -170,8 +170,8 @@ importers: specifier: ^3.2.2 version: 3.2.2 esrap: - specifier: ^2.0.0 - version: 2.0.1 + specifier: https://pkg.pr.new/sveltejs/esrap@718afce + version: https://pkg.pr.new/sveltejs/esrap@718afce htmlparser2: specifier: ^9.1.0 version: 9.1.0 @@ -1301,8 +1301,9 @@ packages: esrap@1.4.9: resolution: {integrity: sha512-3OMlcd0a03UGuZpPeUC1HxR3nA23l+HEyCiZw3b3FumJIN9KphoGzDJKMXI1S72jVS1dsenDyQC0kJlO1U9E1g==} - esrap@2.0.1: - resolution: {integrity: sha512-6n1JodkxeMvyTDCog7J//t8Yti//fGicZgtFLko6h/aEpc54BK9O8k9cZgC2J8+2Dh1U5uYIxuJWSsylybvFBA==} + esrap@https://pkg.pr.new/sveltejs/esrap@718afce: + resolution: {tarball: https://pkg.pr.new/sveltejs/esrap@718afce} + version: 2.1.0 esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} @@ -3306,7 +3307,7 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - esrap@2.0.1: + esrap@https://pkg.pr.new/sveltejs/esrap@718afce: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 From 79f262eef3b6486b711787f2b7e69585d8e071b5 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 26 Oct 2025 06:55:54 +0100 Subject: [PATCH 03/45] fix `svelte-kit` adapter addon --- packages/addons/sveltekit-adapter/index.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/addons/sveltekit-adapter/index.ts b/packages/addons/sveltekit-adapter/index.ts index 3d8d9fc7c..f3ae2bcf9 100644 --- a/packages/addons/sveltekit-adapter/index.ts +++ b/packages/addons/sveltekit-adapter/index.ts @@ -49,7 +49,7 @@ export default defineAddon({ sv.devDependency(adapter.package, adapter.version); sv.file('svelte.config.js', (content) => { - const { ast, generateCode } = parseScript(content); + const { ast, comments, generateCode } = parseScript(content); // finds any existing adapter's import declaration const importDecls = ast.body.filter((n) => n.type === 'ImportDeclaration'); @@ -79,12 +79,20 @@ export default defineAddon({ ) as AstTypes.Property | undefined; if (kitConfig && kitConfig.value.type === 'ObjectExpression') { - const adapterProp = kitConfig.value.properties.find( - (p) => p.type === 'Property' && p.key.type === 'Identifier' && p.key.name === 'adapter' + // removes any existing adapter auto comments + const adapterAutoComments = comments.filter( + (c) => + c.loc && + kitConfig.loc && + c.loc.start.line >= kitConfig.loc.start.line && + c.loc.end.line <= kitConfig.loc.end.line + ); + // modify the array in place + comments.splice( + 0, + comments.length, + ...comments.filter((c) => !adapterAutoComments.includes(c)) ); - if (adapterProp) { - adapterProp.leadingComments = []; - } // only overrides the `adapter` property so we can reset it's args object.overrideProperties(kitConfig.value, { From 7c0c0d2592a586291de0af0a4c4ec4db6be5eede Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 26 Oct 2025 07:00:44 +0100 Subject: [PATCH 04/45] remove useless comment --- packages/core/tests/js/object/create/run.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/tests/js/object/create/run.ts b/packages/core/tests/js/object/create/run.ts index 8bdd272f5..5b19937e0 100644 --- a/packages/core/tests/js/object/create/run.ts +++ b/packages/core/tests/js/object/create/run.ts @@ -37,6 +37,5 @@ export function run(ast: AstTypes.Program): void { name: 'created2', value: createdObject2 }); - createdVariable2.leadingComments = [{ type: 'Line', value: ' prettier-ignore' }]; ast.body.push(createdVariable2); } From f41da148e0ea805c8643189efeff7406655e8f8b Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 26 Oct 2025 07:08:34 +0100 Subject: [PATCH 05/45] fix eslint --- packages/addons/eslint/index.ts | 12 +++++++----- packages/core/tooling/index.ts | 4 +++- packages/core/tooling/parsers.ts | 16 ++++++++++------ 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/packages/addons/eslint/index.ts b/packages/addons/eslint/index.ts index e0130e5b4..80424282d 100644 --- a/packages/addons/eslint/index.ts +++ b/packages/addons/eslint/index.ts @@ -52,7 +52,7 @@ export default defineAddon({ }); sv.file('eslint.config.js', (content) => { - const { ast, generateCode } = parseScript(content); + const { ast, additionalComments, generateCode } = parseScript(content); const eslintConfigs: Array = []; imports.addDefault(ast, { from: './svelte.config.js', as: 'svelteConfig' }); @@ -84,18 +84,20 @@ export default defineAddon({ if (rules.properties[0].type !== 'Property') { throw new Error('rules.properties[0].type !== "Property"'); } - rules.properties[0].key.leadingComments = [ + additionalComments.set(rules.properties[0].key, [ { type: 'Line', value: - ' typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.' + ' typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.', + position: 'leading' }, { type: 'Line', value: - ' see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors' + ' see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors', + position: 'leading' } - ]; + ]); const globalsConfig = object.create({ languageOptions: { diff --git a/packages/core/tooling/index.ts b/packages/core/tooling/index.ts index 1d56b51e8..181270aa1 100644 --- a/packages/core/tooling/index.ts +++ b/packages/core/tooling/index.ts @@ -53,6 +53,7 @@ export type { export function parseScript(content: string): { ast: TsEstree.Program; comments: TsEstree.Comment[]; + additionalComments: WeakMap; } { const comments: TsEstree.Comment[] = []; @@ -86,7 +87,8 @@ export function parseScript(content: string): { return { ast, - comments + comments, + additionalComments: new WeakMap() }; } diff --git a/packages/core/tooling/parsers.ts b/packages/core/tooling/parsers.ts index 7d39bad23..cd6f3b568 100644 --- a/packages/core/tooling/parsers.ts +++ b/packages/core/tooling/parsers.ts @@ -1,18 +1,22 @@ import * as utils from './index.ts'; import MagicString from 'magic-string'; +import type { TsEstree } from './js/ts-estree.ts'; +import type { AdditionalComment } from 'esrap/languages/ts'; type ParseBase = { source: string; generateCode(): string; }; -export function parseScript( - source: string -): { ast: utils.AstTypes.Program; comments: utils.AstTypes.Comment[] } & ParseBase { - const { ast, comments } = utils.parseScript(source); - const generateCode = () => utils.serializeScript(ast, comments, source); +export function parseScript(source: string): { + ast: utils.AstTypes.Program; + comments: utils.AstTypes.Comment[]; + additionalComments: WeakMap; +} & ParseBase { + const { ast, comments, additionalComments } = utils.parseScript(source); + const generateCode = () => utils.serializeScript(ast, comments, source, additionalComments); - return { ast, comments, source, generateCode }; + return { ast, comments, additionalComments, source, generateCode }; } export function parseCss(source: string): { ast: utils.CssAst } & ParseBase { From 7307cc4d6333e22fbead282ffd6edb300cb49076 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 26 Oct 2025 07:35:06 +0100 Subject: [PATCH 06/45] fix eslint --- packages/addons/eslint/index.ts | 4 +- .../core/tests/js/common/jsdoc-comment/run.ts | 6 +-- .../tests/js/common/jsdoc-type-comment/run.ts | 6 +-- packages/core/tests/js/index.ts | 6 +-- packages/core/tooling/index.ts | 7 +++- packages/core/tooling/js/common.ts | 41 ++++++++++++++----- packages/core/tooling/js/index.ts | 2 +- packages/core/tooling/parsers.ts | 4 +- 8 files changed, 49 insertions(+), 27 deletions(-) diff --git a/packages/addons/eslint/index.ts b/packages/addons/eslint/index.ts index 80424282d..d20dd0df2 100644 --- a/packages/addons/eslint/index.ts +++ b/packages/addons/eslint/index.ts @@ -154,7 +154,9 @@ export default defineAddon({ // type annotate config if (!typescript) - common.addJsDocTypeComment(astNode, { type: "import('eslint').Linter.Config[]" }); + common.addJsDocTypeComment(astNode, additionalComments, { + type: "import('eslint').Linter.Config[]" + }); if (typescript) imports.addDefault(ast, { from: 'typescript-eslint', as: 'ts' }); imports.addNamed(ast, { from: 'node:url', imports: ['fileURLToPath'] }); diff --git a/packages/core/tests/js/common/jsdoc-comment/run.ts b/packages/core/tests/js/common/jsdoc-comment/run.ts index c0b6c06f1..15d0369b9 100644 --- a/packages/core/tests/js/common/jsdoc-comment/run.ts +++ b/packages/core/tests/js/common/jsdoc-comment/run.ts @@ -1,9 +1,9 @@ -import { common, type AstTypes } from '@sveltejs/cli-core/js'; +import { common, type AdditionalCommentMap, type AstTypes } from '@sveltejs/cli-core/js'; -export function run(ast: AstTypes.Program): void { +export function run(ast: AstTypes.Program, additionalComments: AdditionalCommentMap): void { const functionDeclaration = ast.body[0] as AstTypes.FunctionDeclaration; - common.addJsDocComment(functionDeclaration, { + common.addJsDocComment(functionDeclaration, additionalComments, { params: { 'import("$lib/paraglide/runtime").AvailableLanguageTag': 'newLanguage' } }); } diff --git a/packages/core/tests/js/common/jsdoc-type-comment/run.ts b/packages/core/tests/js/common/jsdoc-type-comment/run.ts index 9b5e5a065..5074afd58 100644 --- a/packages/core/tests/js/common/jsdoc-type-comment/run.ts +++ b/packages/core/tests/js/common/jsdoc-type-comment/run.ts @@ -1,13 +1,13 @@ -import { common, variables, type AstTypes } from '@sveltejs/cli-core/js'; +import { common, variables, type AdditionalCommentMap, type AstTypes } from '@sveltejs/cli-core/js'; -export function run(ast: AstTypes.Program): void { +export function run(ast: AstTypes.Program, additionalComments: AdditionalCommentMap): void { const declaration = variables.declaration(ast, { kind: 'const', name: 'foo', value: { type: 'Literal', value: 42 } }); - common.addJsDocTypeComment(declaration, { + common.addJsDocTypeComment(declaration, additionalComments, { type: 'number' }); diff --git a/packages/core/tests/js/index.ts b/packages/core/tests/js/index.ts index 9aca4de6a..bc32a5c83 100644 --- a/packages/core/tests/js/index.ts +++ b/packages/core/tests/js/index.ts @@ -16,13 +16,13 @@ for (const categoryDirectory of categoryDirectories) { const inputFilePath = join(testDirectoryPath, 'input.ts'); const input = fs.existsSync(inputFilePath) ? fs.readFileSync(inputFilePath, 'utf8') : ''; - const { ast, comments } = parseScript(input); + const { ast, comments, additionalComments } = parseScript(input); // dynamic imports always need to provide the path inline for static analysis const module = await import(`./${categoryDirectory}/${testName}/run.ts`); - module.run(ast); + module.run(ast, additionalComments); - let output = serializeScript(ast, comments, input); + let output = serializeScript(ast, comments, input, additionalComments); if (!output.endsWith('\n')) output += '\n'; await expect(output).toMatchFileSnapshot(`${testDirectoryPath}/output.ts`); }); diff --git a/packages/core/tooling/index.ts b/packages/core/tooling/index.ts index 181270aa1..d2f1aa0f3 100644 --- a/packages/core/tooling/index.ts +++ b/packages/core/tooling/index.ts @@ -18,6 +18,8 @@ import ts, { type AdditionalComment } from 'esrap/languages/ts'; import * as acorn from 'acorn'; import { tsPlugin } from '@sveltejs/acorn-typescript'; +type AdditionalCommentMap = WeakMap; + export { // html Document as HtmlDocument, @@ -41,6 +43,7 @@ export type { // js TsEstree as AstTypes, + AdditionalCommentMap, //css CssChildNode @@ -53,7 +56,7 @@ export type { export function parseScript(content: string): { ast: TsEstree.Program; comments: TsEstree.Comment[]; - additionalComments: WeakMap; + additionalComments: AdditionalCommentMap; } { const comments: TsEstree.Comment[] = []; @@ -96,7 +99,7 @@ export function serializeScript( ast: TsEstree.Node, comments: TsEstree.Comment[], previousContent?: string, - additionalComments?: WeakMap + additionalComments?: AdditionalCommentMap ): string { // @ts-expect-error we are still using `estree` while `esrap` is using `@typescript-eslint/types` // which is causing these errors. But they are simmilar enough to work together. diff --git a/packages/core/tooling/js/common.ts b/packages/core/tooling/js/common.ts index d249b693b..1f51dcf6a 100644 --- a/packages/core/tooling/js/common.ts +++ b/packages/core/tooling/js/common.ts @@ -1,18 +1,30 @@ -import { type AstTypes, Walker, parseScript, serializeScript, stripAst } from '../index.ts'; +import { + type AdditionalCommentMap, + type AstTypes, + Walker, + parseScript, + serializeScript, + stripAst +} from '../index.ts'; import decircular from 'decircular'; import dedent from 'dedent'; -export function addJsDocTypeComment(node: AstTypes.Node, options: { type: string }): void { +export function addJsDocTypeComment( + node: AstTypes.Node, + additionalComments: AdditionalCommentMap, + options: { type: string } +): void { const comment: AstTypes.Comment = { type: 'Block', value: `* @type {${options.type}} ` }; - addComment(node, comment); + addComment(node, additionalComments, comment); } export function addJsDocComment( node: AstTypes.Node, + additionalComments: AdditionalCommentMap, options: { params: Record } ): void { const commentLines: string[] = []; @@ -25,16 +37,23 @@ export function addJsDocComment( value: `*\n * ${commentLines.join('\n * ')}\n ` }; - addComment(node, comment); + addComment(node, additionalComments, comment); } -function addComment(node: AstTypes.Node, comment: AstTypes.Comment) { - node.leadingComments ??= []; - - const found = node.leadingComments.find( - (item) => item.type === 'Block' && item.value === comment.value - ); - if (!found) node.leadingComments.push(comment); +function addComment( + node: AstTypes.Node, + additionalComments: AdditionalCommentMap, + comment: AstTypes.Comment +) { + const found = additionalComments + .get(node) + ?.find((item) => item.type === 'Block' && item.value === comment.value); + + if (!found) { + const comments = additionalComments.get(node) ?? []; + comments.push({ ...comment, position: 'leading' }); + additionalComments.set(node, comments); + } } export function typeAnnotate( diff --git a/packages/core/tooling/js/index.ts b/packages/core/tooling/js/index.ts index 0b4c6e632..206d49d5a 100644 --- a/packages/core/tooling/js/index.ts +++ b/packages/core/tooling/js/index.ts @@ -7,4 +7,4 @@ export * as variables from './variables.ts'; export * as exports from './exports.ts'; export * as kit from './kit.ts'; export * as vite from './vite.ts'; -export type { AstTypes } from '../index.ts'; +export type { AstTypes, AdditionalCommentMap } from '../index.ts'; diff --git a/packages/core/tooling/parsers.ts b/packages/core/tooling/parsers.ts index cd6f3b568..12c2131e5 100644 --- a/packages/core/tooling/parsers.ts +++ b/packages/core/tooling/parsers.ts @@ -1,7 +1,5 @@ import * as utils from './index.ts'; import MagicString from 'magic-string'; -import type { TsEstree } from './js/ts-estree.ts'; -import type { AdditionalComment } from 'esrap/languages/ts'; type ParseBase = { source: string; @@ -11,7 +9,7 @@ type ParseBase = { export function parseScript(source: string): { ast: utils.AstTypes.Program; comments: utils.AstTypes.Comment[]; - additionalComments: WeakMap; + additionalComments: utils.AdditionalCommentMap; } & ParseBase { const { ast, comments, additionalComments } = utils.parseScript(source); const generateCode = () => utils.serializeScript(ast, comments, source, additionalComments); From 6df65fc99717d31837b57e63aaa72f9e8810f48f Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 26 Oct 2025 07:39:04 +0100 Subject: [PATCH 07/45] add changed tests --- packages/core/tests/js/object/create/output.ts | 8 +------- packages/core/tests/js/vite/add-plugin-mode/output.ts | 2 ++ packages/core/tests/js/vite/with-satisfies/output.ts | 11 ++++++----- packages/core/tests/utils.ts | 2 +- 4 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/core/tests/js/object/create/output.ts b/packages/core/tests/js/object/create/output.ts index 28ad96e4f..df190f0f4 100644 --- a/packages/core/tests/js/object/create/output.ts +++ b/packages/core/tests/js/object/create/output.ts @@ -1,15 +1,9 @@ const empty = {}; const created = { foo: 1, bar: 'string' }; -// prettier-ignore const created2 = { foo: 1, bar: 'string', object: { foo: 'hello', nested: { bar: 'world' } }, - array: [ - 123, - 'hello', - { foo: 'bar', bool: true }, - [456, '789'] - ] + array: [123, 'hello', { foo: 'bar', bool: true }, [456, '789']] }; diff --git a/packages/core/tests/js/vite/add-plugin-mode/output.ts b/packages/core/tests/js/vite/add-plugin-mode/output.ts index f112d5fcc..df6d4692a 100644 --- a/packages/core/tests/js/vite/add-plugin-mode/output.ts +++ b/packages/core/tests/js/vite/add-plugin-mode/output.ts @@ -7,8 +7,10 @@ import { defineConfig } from 'vite'; export default defineConfig({ plugins: [ firstPlugin(), + // a default plugin sveltekit(), + middlePlugin(), lastPlugin() ] diff --git a/packages/core/tests/js/vite/with-satisfies/output.ts b/packages/core/tests/js/vite/with-satisfies/output.ts index 4d6ed36e0..9c43348e2 100644 --- a/packages/core/tests/js/vite/with-satisfies/output.ts +++ b/packages/core/tests/js/vite/with-satisfies/output.ts @@ -18,23 +18,24 @@ const config = defineConfig({ plugins: [ // all plugins examples, + tailwindcss(), sveltekit(), kitRoutes(), myPlugin() ], + resolve: { alias: { $lib, $routes, $scripts, $actions } }, - build: { - sourcemap: true, - target: 'esnext', - cssMinify: 'lightningcss' - }, + build: { sourcemap: true, target: 'esnext', cssMinify: 'lightningcss' }, + css: { transformer: 'lightningcss', + lightningcss: { targets: browserslistToTargets(browserslist('defaults, not ie 11')) } }, + experimental: { enableNativePlugin: true } }) satisfies UserConfig; diff --git a/packages/core/tests/utils.ts b/packages/core/tests/utils.ts index ef3272504..f27f91fc8 100644 --- a/packages/core/tests/utils.ts +++ b/packages/core/tests/utils.ts @@ -87,7 +87,7 @@ test('integration - simple', () => { console.log("bar"); const foobar = "foo"; - const foobar2 = "test"; + const foobar2 = 'test'; }" `); }); From ae784550dae249f8b34713c9ebd573f72d52e901 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 26 Oct 2025 07:40:21 +0100 Subject: [PATCH 08/45] thats ok too --- packages/core/tests/js/common/jsdoc-type-comment/output.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/tests/js/common/jsdoc-type-comment/output.ts b/packages/core/tests/js/common/jsdoc-type-comment/output.ts index 1aa734391..e8a715681 100644 --- a/packages/core/tests/js/common/jsdoc-type-comment/output.ts +++ b/packages/core/tests/js/common/jsdoc-type-comment/output.ts @@ -1,2 +1 @@ -/** @type {number} */ -const foo = 42; +/** @type {number} */ const foo = 42; From 1982ac405e60306265e6596fd7273c9632332699 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 26 Oct 2025 07:51:22 +0100 Subject: [PATCH 09/45] those are ok as well --- .../tests/js/object/ensure-nested-property/output.ts | 9 ++++++++- .../core/tests/js/object/override-property/output.ts | 8 +++++++- packages/core/tests/js/object/property-node/output.ts | 7 ++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/core/tests/js/object/ensure-nested-property/output.ts b/packages/core/tests/js/object/ensure-nested-property/output.ts index 08bd5fbf9..b3824d36a 100644 --- a/packages/core/tests/js/object/ensure-nested-property/output.ts +++ b/packages/core/tests/js/object/ensure-nested-property/output.ts @@ -1 +1,8 @@ -const test = { a: { /** a comment */ keep: 'you', b: { c: '007' } } }; +const test = { + a: { + /** a comment */ + keep: 'you', + + b: { c: '007' } + } +}; diff --git a/packages/core/tests/js/object/override-property/output.ts b/packages/core/tests/js/object/override-property/output.ts index fbdef76fe..987c50888 100644 --- a/packages/core/tests/js/object/override-property/output.ts +++ b/packages/core/tests/js/object/override-property/output.ts @@ -1 +1,7 @@ -const test = { /** a comment */ foo: 2, bar: 'string2', lorem: false }; +const test = { + /** a comment */ + foo: 2, + + bar: 'string2', + lorem: false +}; diff --git a/packages/core/tests/js/object/property-node/output.ts b/packages/core/tests/js/object/property-node/output.ts index 98da8dea4..3719acb91 100644 --- a/packages/core/tests/js/object/property-node/output.ts +++ b/packages/core/tests/js/object/property-node/output.ts @@ -1 +1,6 @@ -const test = { /*a comment updated*/ foo: 1, /*aka: bond, james bond*/ james: '007' }; +const test = { + /** a comment */ + foo: 1, + + james: '007' +}; From 62444cc432a419521ff58b48c4dc17eb79751c7a Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 26 Oct 2025 08:28:09 +0100 Subject: [PATCH 10/45] fix last failing test --- packages/core/package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 224452035..23c9e2303 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -34,7 +34,7 @@ "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", - "esrap": "https://pkg.pr.new/sveltejs/esrap@718afce", + "esrap": "https://pkg.pr.new/sveltejs/esrap@af12b38", "htmlparser2": "^9.1.0", "magic-string": "^0.30.17", "picocolors": "^1.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b189db0f..f8ccbd66c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -167,8 +167,8 @@ importers: specifier: ^3.2.2 version: 3.2.2 esrap: - specifier: https://pkg.pr.new/sveltejs/esrap@718afce - version: https://pkg.pr.new/sveltejs/esrap@718afce + specifier: https://pkg.pr.new/sveltejs/esrap@af12b38 + version: https://pkg.pr.new/sveltejs/esrap@af12b38 htmlparser2: specifier: ^9.1.0 version: 9.1.0 @@ -1339,8 +1339,8 @@ packages: esrap@1.4.9: resolution: {integrity: sha512-3OMlcd0a03UGuZpPeUC1HxR3nA23l+HEyCiZw3b3FumJIN9KphoGzDJKMXI1S72jVS1dsenDyQC0kJlO1U9E1g==} - esrap@https://pkg.pr.new/sveltejs/esrap@718afce: - resolution: {tarball: https://pkg.pr.new/sveltejs/esrap@718afce} + esrap@https://pkg.pr.new/sveltejs/esrap@af12b38: + resolution: {tarball: https://pkg.pr.new/sveltejs/esrap@af12b38} version: 2.1.0 esrecurse@4.3.0: @@ -3402,7 +3402,7 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - esrap@https://pkg.pr.new/sveltejs/esrap@718afce: + esrap@https://pkg.pr.new/sveltejs/esrap@af12b38: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 From e4af8c752323b28704cda1534718f1a64a8ec8b9 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 26 Oct 2025 13:42:18 +0100 Subject: [PATCH 11/45] implement proper svelte file processing --- packages/addons/common.ts | 29 ++++-- packages/core/package.json | 1 + packages/core/tooling/html/index.ts | 10 ++- packages/core/tooling/index.ts | 10 +++ packages/core/tooling/parsers.ts | 134 ++-------------------------- pnpm-lock.yaml | 32 +++++++ 6 files changed, 78 insertions(+), 138 deletions(-) diff --git a/packages/addons/common.ts b/packages/addons/common.ts index a1a8f2ce5..2ea48eae4 100644 --- a/packages/addons/common.ts +++ b/packages/addons/common.ts @@ -1,4 +1,5 @@ import { imports, exports, common } from '@sveltejs/cli-core/js'; +import { toSvelteFragment, type SvelteAst } from '@sveltejs/cli-core/html'; import { parseScript, parseSvelte } from '@sveltejs/cli-core/parsers'; import process from 'node:process'; @@ -64,17 +65,29 @@ export function addEslintConfigPrettier(content: string): string { } export function addToDemoPage(content: string, path: string): string { - const { template, generateCode } = parseSvelte(content); - - for (const node of template.ast.childNodes) { - if (node.type === 'tag' && node.attribs['href'] === `/demo/${path}`) { - return content; + const { ast, generateCode } = parseSvelte(content); + + for (const node of ast.fragment.nodes) { + if (node.type === 'RegularElement') { + const hrefAttribute = node.attributes.find( + (x) => x.type === 'Attribute' && x.name === 'href' + ) as SvelteAst.Attribute; + if (!hrefAttribute || !hrefAttribute.value) continue; + + if (!Array.isArray(hrefAttribute.value)) continue; + + const hasDemo = hrefAttribute.value.find( + (x) => x.type === 'Text' && x.data === `/demo/${path}` + ); + if (hasDemo) { + return content; + } } } - const newLine = template.source ? '\n' : ''; - const src = template.source + `${newLine}${path}`; - return generateCode({ template: src }); + ast.fragment.nodes.push(...toSvelteFragment(`${path}`)); + + return generateCode(); } /** diff --git a/packages/core/package.json b/packages/core/package.json index 23c9e2303..ddaeab0fa 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -40,6 +40,7 @@ "picocolors": "^1.1.1", "postcss": "^8.5.6", "silver-fleece": "^1.2.1", + "svelte": "https://pkg.pr.new/sveltejs/svelte@1377c40", "yaml": "^2.8.1", "zimmerframe": "^1.1.2" }, diff --git a/packages/core/tooling/html/index.ts b/packages/core/tooling/html/index.ts index 60b2ae078..e245c3bd9 100644 --- a/packages/core/tooling/html/index.ts +++ b/packages/core/tooling/html/index.ts @@ -7,9 +7,11 @@ import { parseHtml } from '../index.ts'; import { appendFromString } from '../js/common.ts'; +import { parseSvelte } from '../parsers.ts'; +import type { AST as SvelteAst } from 'svelte/compiler'; export { HtmlElement, HtmlElementType }; -export type { HtmlDocument }; +export type { HtmlDocument, SvelteAst }; export function createDiv(attributes: Record = {}): HtmlElement { return createElement('div', attributes); @@ -58,3 +60,9 @@ export function addSlot( }); addFromRawHtml(options.htmlAst.childNodes, '{@render children()}'); } + +export function toSvelteFragment(content: string): SvelteAst.Fragment['nodes'] { + // TODO write test + const { ast } = parseSvelte(content); + return ast.fragment.nodes; +} diff --git a/packages/core/tooling/index.ts b/packages/core/tooling/index.ts index 9c62d5c7b..bcc9e04a1 100644 --- a/packages/core/tooling/index.ts +++ b/packages/core/tooling/index.ts @@ -17,6 +17,7 @@ import { print as esrapPrint } from 'esrap'; import ts, { type AdditionalComment } from 'esrap/languages/ts'; import * as acorn from 'acorn'; import { tsPlugin } from '@sveltejs/acorn-typescript'; +import { parse as svelteParse, type AST as SvelteAst, print as sveltePrint } from 'svelte/compiler'; import * as yaml from 'yaml'; type AdditionalCommentMap = WeakMap; @@ -41,6 +42,7 @@ export { export type { // html ChildNode as HtmlChildNode, + SvelteAst, // js TsEstree as AstTypes, @@ -216,3 +218,11 @@ export function parseYaml(content: string): ReturnType): string { return yaml.stringify(data, { singleQuote: true }); } + +export function parseSvelte(content: string): SvelteAst.Root { + return svelteParse(content, { modern: true }); +} + +export function serializeSvelte(ast: SvelteAst.Root): string { + return sveltePrint(ast).code; +} diff --git a/packages/core/tooling/parsers.ts b/packages/core/tooling/parsers.ts index b578453fd..b030a16b5 100644 --- a/packages/core/tooling/parsers.ts +++ b/packages/core/tooling/parsers.ts @@ -1,5 +1,4 @@ import * as utils from './index.ts'; -import MagicString from 'magic-string'; type ParseBase = { source: string; @@ -49,136 +48,13 @@ export function parseYaml( return { data, source, generateCode }; } -type SvelteGenerator = (code: { - script?: string; - module?: string; - css?: string; - template?: string; -}) => string; -export function parseSvelte( - source: string, - options?: { typescript?: boolean } -): { - script: ReturnType; - module: ReturnType; - css: ReturnType; - template: ReturnType; - generateCode: SvelteGenerator; -} { - // `xTag` captures the whole tag block (ex: ) - // `xSource` is the contents within the tags - const scripts = extractScripts(source); - // instance block - const { tag: scriptTag = '', src: scriptSource = '' } = - scripts.find(({ attrs }) => !attrs.includes('module')) ?? {}; - // module block - const { tag: moduleScriptTag = '', src: moduleSource = '' } = - scripts.find(({ attrs }) => attrs.includes('module')) ?? {}; - // style block - const { styleTag, cssSource } = extractStyle(source); - // rest of the template - // TODO: needs more testing - const templateSource = source - .replace(moduleScriptTag, '') - .replace(scriptTag, '') - .replace(styleTag, '') - .trim(); - - const script = parseScript(scriptSource); - const module = parseScript(moduleSource); - const css = parseCss(cssSource); - const template = parseHtml(templateSource); - - const generateCode: SvelteGenerator = (code) => { - const ms = new MagicString(source); - // TODO: this is imperfect and needs adjustments - if (code.script !== undefined) { - if (scriptSource.length === 0) { - const ts = options?.typescript ? ' lang="ts"' : ''; - const indented = code.script.split('\n').join('\n\t'); - const script = `\n\t${indented}\n\n\n`; - ms.prepend(script); - } else { - const { start, end } = locations(source, scriptSource); - const formatted = indent(code.script, ms.getIndentString()); - ms.update(start, end, formatted); - } - } - if (code.module !== undefined) { - if (moduleSource.length === 0) { - const ts = options?.typescript ? ' lang="ts"' : ''; - const indented = code.module.split('\n').join('\n\t'); - // TODO: make a svelte 5 variant - const module = `\n\t${indented}\n\n\n`; - ms.prepend(module); - } else { - const { start, end } = locations(source, moduleSource); - const formatted = indent(code.module, ms.getIndentString()); - ms.update(start, end, formatted); - } - } - if (code.css !== undefined) { - if (cssSource.length === 0) { - const indented = code.css.split('\n').join('\n\t'); - const style = `\n\n`; - ms.append(style); - } else { - const { start, end } = locations(source, cssSource); - const formatted = indent(code.css, ms.getIndentString()); - ms.update(start, end, formatted); - } - } - if (code.template !== undefined) { - if (templateSource.length === 0) { - ms.appendLeft(0, code.template); - } else { - const { start, end } = locations(source, templateSource); - ms.update(start, end, code.template); - } - } - return ms.toString(); - }; +export function parseSvelte(source: string): { ast: utils.SvelteAst.Root } & ParseBase { + const ast = utils.parseSvelte(source); + const generateCode = () => utils.serializeSvelte(ast); return { - script: { ...script, source: scriptSource }, - module: { ...module, source: moduleSource }, - css: { ...css, source: cssSource }, - template: { ...template, source: templateSource }, + ast, + source, generateCode }; } - -function locations(source: string, search: string): { start: number; end: number } { - const start = source.indexOf(search); - const end = start + search.length; - return { start, end }; -} - -function indent(content: string, indent: string): string { - const indented = indent + content.split('\n').join(`\n${indent}`); - return `\n${indented}\n`; -} - -// sourced from Svelte: https://github.com/sveltejs/svelte/blob/0d3d5a2a85c0f9eccb2c8dbbecc0532ec918b157/packages/svelte/src/compiler/preprocess/index.js#L253-L256 -const regexScriptTags = - /|'"/\s]+=(?:"[^"]*"|'[^']*'|[^>\s]+)|\s+[^=>'"/\s]+)*\s*)(?:\/>|>([\S\s]*?)<\/script>)/; -const regexStyleTags = - /|'"/\s]+=(?:"[^"]*"|'[^']*'|[^>\s]+)|\s+[^=>'"/\s]+)*\s*)(?:\/>|>([\S\s]*?)<\/style>)/; - -type Script = { tag: string; attrs: string; src: string }; -function extractScripts(source: string): Script[] { - const scripts = []; - const [tag = '', attrs = '', src = ''] = regexScriptTags.exec(source) ?? []; - if (tag) { - const stripped = source.replace(tag, ''); - scripts.push({ tag, attrs, src }, ...extractScripts(stripped)); - return scripts; - } - - return []; -} - -function extractStyle(source: string) { - const [styleTag = '', attributes = '', cssSource = ''] = regexStyleTags.exec(source) ?? []; - return { styleTag, attributes, cssSource }; -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f8ccbd66c..9192faee6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -184,6 +184,9 @@ importers: silver-fleece: specifier: ^1.2.1 version: 1.2.1 + svelte: + specifier: https://pkg.pr.new/sveltejs/svelte@1377c40 + version: https://pkg.pr.new/sveltejs/svelte@1377c40 yaml: specifier: ^2.8.1 version: 2.8.1 @@ -1339,6 +1342,9 @@ packages: esrap@1.4.9: resolution: {integrity: sha512-3OMlcd0a03UGuZpPeUC1HxR3nA23l+HEyCiZw3b3FumJIN9KphoGzDJKMXI1S72jVS1dsenDyQC0kJlO1U9E1g==} + esrap@2.1.0: + resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==} + esrap@https://pkg.pr.new/sveltejs/esrap@af12b38: resolution: {tarball: https://pkg.pr.new/sveltejs/esrap@af12b38} version: 2.1.0 @@ -2060,6 +2066,11 @@ packages: resolution: {integrity: sha512-5PEg+QQKce4t1qiOtVUhUS3AQRTtxJyGBTpxLcNWnr0Ve8q4r06bMo0Gv8uhtCPWlztZHoi3Ye7elLhu+PCTMg==} engines: {node: '>=18'} + svelte@https://pkg.pr.new/sveltejs/svelte@1377c40: + resolution: {tarball: https://pkg.pr.new/sveltejs/svelte@1377c40} + version: 5.34.9 + engines: {node: '>=18'} + synckit@0.11.8: resolution: {integrity: sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==} engines: {node: ^14.18.0 || >=16.0.0} @@ -3402,6 +3413,10 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + esrap@2.1.0: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + esrap@https://pkg.pr.new/sveltejs/esrap@af12b38: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -4079,6 +4094,23 @@ snapshots: magic-string: 0.30.19 zimmerframe: 1.1.2 + svelte@https://pkg.pr.new/sveltejs/svelte@1377c40: + dependencies: + '@ampproject/remapping': 2.3.0 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) + '@types/estree': 1.0.8 + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + clsx: 2.1.1 + esm-env: 1.2.2 + esrap: 2.1.0 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.19 + zimmerframe: 1.1.2 + synckit@0.11.8: dependencies: '@pkgr/core': 0.2.7 From ccf1ab3c9043e88fe7a3fbabdf122195f02b39c6 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 26 Oct 2025 13:42:27 +0100 Subject: [PATCH 12/45] fix paraglide --- packages/addons/paraglide/index.ts | 52 ++++++++++++++++++------------ 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/packages/addons/paraglide/index.ts b/packages/addons/paraglide/index.ts index 02663e041..90298f38b 100644 --- a/packages/addons/paraglide/index.ts +++ b/packages/addons/paraglide/index.ts @@ -1,4 +1,3 @@ -import MagicString from 'magic-string'; import { colors, defineAddon, defineAddonOptions, log } from '@sveltejs/cli-core'; import { common, imports, variables, exports, kit as kitJs, vite } from '@sveltejs/cli-core/js'; import * as html from '@sveltejs/cli-core/html'; @@ -183,35 +182,46 @@ export default defineAddon({ // add usage example sv.file(`${kit.routesDirectory}/demo/paraglide/+page.svelte`, (content) => { - const { script, template, generateCode } = parseSvelte(content, { typescript }); - imports.addNamed(script.ast, { from: '$lib/paraglide/messages.js', imports: ['m'] }); - imports.addNamed(script.ast, { from: '$app/navigation', imports: ['goto'] }); - imports.addNamed(script.ast, { from: '$app/state', imports: ['page'] }); - imports.addNamed(script.ast, { from: '$lib/paraglide/runtime', imports: ['setLocale'] }); - - const scriptCode = new MagicString(script.generateCode()); + const { ast, generateCode } = parseSvelte(content); + + let scriptAst = ast.instance?.content; + if (!scriptAst) { + scriptAst = parseScript('').ast; + ast.instance = { + type: 'Script', + start: 0, + end: 0, + context: 'default', + attributes: [], + content: scriptAst + }; + } - const templateCode = new MagicString(template.source); + imports.addNamed(scriptAst, { imports: { m: 'm' }, from: '$lib/paraglide/messages.js' }); + imports.addNamed(scriptAst, { + imports: { + setLocale: 'setLocale' + }, + from: '$lib/paraglide/runtime' + }); // add localized message - templateCode.append("\n\n

{m.hello_world({ name: 'SvelteKit User' })}

\n"); + let templateCode = "

{m.hello_world({ name: 'SvelteKit User' })}

"; // add links to other localized pages, the first one is the default // language, thus it does not require any localized route const { validLanguageTags } = parseLanguageTagInput(options.languageTags); const links = validLanguageTags - .map( - (x) => - `${templateCode.getIndentString()}` - ) - .join('\n'); - templateCode.append(`
\n${links}\n
`); - - templateCode.append( - '

\nIf you use VSCode, install the Sherlock i18n extension for a better i18n experience.\n

' - ); + .map((x) => ``) + .join(''); + templateCode += `
${links}
`; - return generateCode({ script: scriptCode.toString(), template: templateCode.toString() }); + templateCode += + '

If you use VSCode, install the Sherlock i18n extension for a better i18n experience.

'; + + ast.fragment.nodes.push(...html.toSvelteFragment(templateCode)); + + return generateCode(); }); } From f243210afa9de9492d324a86fc11e4e06ad8fa15 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sat, 22 Nov 2025 20:34:32 +0100 Subject: [PATCH 13/45] fix lockfile --- pnpm-lock.yaml | 214 ++++--------------------------------------------- 1 file changed, 17 insertions(+), 197 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0596ddc74..1e07c8497 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -260,6 +260,10 @@ importers: packages: + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + '@babel/generator@7.28.5': resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} @@ -640,125 +644,66 @@ packages: '@quansync/fs@0.1.5': resolution: {integrity: sha512-lNS9hL2aS2NZgNW7BBj+6EBl4rOf8l+tQ0eRY6JWCI8jI2kc53gSoqbjojU0OnAWhzoXiOjFyGsHcDGePB3lhA==} -<<<<<<< HEAD - '@rolldown/binding-android-arm64@1.0.0-beta.44': - resolution: {integrity: sha512-g9ejDOehJFhxC1DIXQuZQ9bKv4lRDioOTL42cJjFjqKPl1L7DVb9QQQE1FxokGEIMr6FezLipxwnzOXWe7DNPg==} -======= '@rolldown/binding-android-arm64@1.0.0-beta.45': resolution: {integrity: sha512-bfgKYhFiXJALeA/riil908+2vlyWGdwa7Ju5S+JgWZYdR4jtiPOGdM6WLfso1dojCh+4ZWeiTwPeV9IKQEX+4g==} ->>>>>>> main engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] -<<<<<<< HEAD - '@rolldown/binding-darwin-arm64@1.0.0-beta.44': - resolution: {integrity: sha512-PxAW1PXLPmCzfhfKIS53kwpjLGTUdIfX4Ht+l9mj05C3lYCGaGowcNsYi2rdxWH24vSTmeK+ajDNRmmmrK0M7g==} -======= '@rolldown/binding-darwin-arm64@1.0.0-beta.45': resolution: {integrity: sha512-xjCv4CRVsSnnIxTuyH1RDJl5OEQ1c9JYOwfDAHddjJDxCw46ZX9q80+xq7Eok7KC4bRSZudMJllkvOKv0T9SeA==} ->>>>>>> main engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] -<<<<<<< HEAD - '@rolldown/binding-darwin-x64@1.0.0-beta.44': - resolution: {integrity: sha512-/CtQqs1oO9uSb5Ju60rZvsdjE7Pzn8EK2ISAdl2jedjMzeD/4neNyCbwyJOAPzU+GIQTZVyrFZJX+t7HXR1R/g==} -======= '@rolldown/binding-darwin-x64@1.0.0-beta.45': resolution: {integrity: sha512-ddcO9TD3D/CLUa/l8GO8LHzBOaZqWg5ClMy3jICoxwCuoz47h9dtqPsIeTiB6yR501LQTeDsjA4lIFd7u3Ljfw==} ->>>>>>> main engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] -<<<<<<< HEAD - '@rolldown/binding-freebsd-x64@1.0.0-beta.44': - resolution: {integrity: sha512-V5Q5W9c4+2GJ4QabmjmVV6alY97zhC/MZBaLkDtHwGy3qwzbM4DYgXUbun/0a8AH5hGhuU27tUIlYz6ZBlvgOA==} -======= '@rolldown/binding-freebsd-x64@1.0.0-beta.45': resolution: {integrity: sha512-MBTWdrzW9w+UMYDUvnEuh0pQvLENkl2Sis15fHTfHVW7ClbGuez+RWopZudIDEGkpZXdeI4CkRXk+vdIIebrmg==} ->>>>>>> main engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] -<<<<<<< HEAD - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.44': - resolution: {integrity: sha512-X6adjkHeFqKsTU0FXdNN9HY4LDozPqIfHcnXovE5RkYLWIjMWuc489mIZ6iyhrMbCqMUla9IOsh5dvXSGT9o9A==} -======= '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.45': resolution: {integrity: sha512-4YgoCFiki1HR6oSg+GxxfzfnVCesQxLF1LEnw9uXS/MpBmuog0EOO2rYfy69rWP4tFZL9IWp6KEfGZLrZ7aUog==} ->>>>>>> main engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] -<<<<<<< HEAD - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.44': - resolution: {integrity: sha512-kRRKGZI4DXWa6ANFr3dLA85aSVkwPdgXaRjfanwY84tfc3LncDiIjyWCb042e3ckPzYhHSZ3LmisO+cdOIYL6Q==} -======= '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.45': resolution: {integrity: sha512-LE1gjAwQRrbCOorJJ7LFr10s5vqYf5a00V5Ea9wXcT2+56n5YosJkcp8eQ12FxRBv2YX8dsdQJb+ZTtYJwb6XQ==} ->>>>>>> main engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] -<<<<<<< HEAD - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.44': - resolution: {integrity: sha512-hMtiN9xX1NhxXBa2U3Up4XkVcsVp2h73yYtMDY59z9CDLEZLrik9RVLhBL5QtoX4zZKJ8HZKJtWuGYvtmkCbIQ==} -======= '@rolldown/binding-linux-arm64-musl@1.0.0-beta.45': resolution: {integrity: sha512-tdy8ThO/fPp40B81v0YK3QC+KODOmzJzSUOO37DinQxzlTJ026gqUSOM8tzlVixRbQJltgVDCTYF8HNPRErQTA==} ->>>>>>> main engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] -<<<<<<< HEAD - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.44': - resolution: {integrity: sha512-rd1LzbpXQuR8MTG43JB9VyXDjG7ogSJbIkBpZEHJ8oMKzL6j47kQT5BpIXrg3b5UVygW9QCI2fpFdMocT5Kudg==} -======= '@rolldown/binding-linux-x64-gnu@1.0.0-beta.45': resolution: {integrity: sha512-lS082ROBWdmOyVY/0YB3JmsiClaWoxvC+dA8/rbhyB9VLkvVEaihLEOr4CYmrMse151C4+S6hCw6oa1iewox7g==} ->>>>>>> main engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] -<<<<<<< HEAD - '@rolldown/binding-linux-x64-musl@1.0.0-beta.44': - resolution: {integrity: sha512-qI2IiPqmPRW25exXkuQr3TlweCDc05YvvbSDRPCuPsWkwb70dTiSoXn8iFxT4PWqTi71wWHg1Wyta9PlVhX5VA==} -======= '@rolldown/binding-linux-x64-musl@1.0.0-beta.45': resolution: {integrity: sha512-Hi73aYY0cBkr1/SvNQqH8Cd+rSV6S9RB5izCv0ySBcRnd/Wfn5plguUoGYwBnhHgFbh6cPw9m2dUVBR6BG1gxA==} ->>>>>>> main engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] -<<<<<<< HEAD - '@rolldown/binding-openharmony-arm64@1.0.0-beta.44': - resolution: {integrity: sha512-+vHvEc1pL5iJRFlldLC8mjm6P4Qciyfh2bh5ZI6yxDQKbYhCHRKNURaKz1mFcwxhVL5YMYsLyaqM3qizVif9MQ==} -======= '@rolldown/binding-openharmony-arm64@1.0.0-beta.45': resolution: {integrity: sha512-fljEqbO7RHHogNDxYtTzr+GNjlfOx21RUyGmF+NrkebZ8emYYiIqzPxsaMZuRx0rgZmVmliOzEp86/CQFDKhJQ==} ->>>>>>> main engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] -<<<<<<< HEAD - '@rolldown/binding-wasm32-wasi@1.0.0-beta.44': - resolution: {integrity: sha512-XSgLxRrtFj6RpTeMYmmQDAwHjKseYGKUn5LPiIdW4Cq+f5SBSStL2ToBDxkbdxKPEbCZptnLPQ/nfKcAxrC8Xg==} - engines: {node: '>=14.0.0'} - cpu: [wasm32] - - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.44': - resolution: {integrity: sha512-cF1LJdDIX02cJrFrX3wwQ6IzFM7I74BYeKFkzdcIA4QZ0+2WA7/NsKIgjvrunupepWb1Y6PFWdRlHSaz5AW1Wg==} -======= '@rolldown/binding-wasm32-wasi@1.0.0-beta.45': resolution: {integrity: sha512-ZJDB7lkuZE9XUnWQSYrBObZxczut+8FZ5pdanm8nNS1DAo8zsrPuvGwn+U3fwU98WaiFsNrA4XHngesCGr8tEQ==} engines: {node: '>=14.0.0'} @@ -766,40 +711,24 @@ packages: '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.45': resolution: {integrity: sha512-zyzAjItHPUmxg6Z8SyRhLdXlJn3/D9KL5b9mObUrBHhWS/GwRH4665xCiFqeuktAhhWutqfc+rOV2LjK4VYQGQ==} ->>>>>>> main engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] -<<<<<<< HEAD - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.44': - resolution: {integrity: sha512-5uaJonDafhHiMn+iEh7qUp3QQ4Gihv3lEOxKfN8Vwadpy0e+5o28DWI42DpJ9YBYMrVy4JOWJ/3etB/sptpUwA==} -======= '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.45': resolution: {integrity: sha512-wODcGzlfxqS6D7BR0srkJk3drPwXYLu7jPHN27ce2c4PUnVVmJnp9mJzUQGT4LpmHmmVdMZ+P6hKvyTGBzc1CA==} ->>>>>>> main engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] -<<<<<<< HEAD - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.44': - resolution: {integrity: sha512-vsqhWAFJkkmgfBN/lkLCWTXF1PuPhMjfnAyru48KvF7mVh2+K7WkKYHezF3Fjz4X/mPScOcIv+g6cf6wnI6eWg==} -======= '@rolldown/binding-win32-x64-msvc@1.0.0-beta.45': resolution: {integrity: sha512-wiU40G1nQo9rtfvF9jLbl79lUgjfaD/LTyUEw2Wg/gdF5OhjzpKMVugZQngO+RNdwYaNj+Fs+kWBWfp4VXPMHA==} ->>>>>>> main engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] -<<<<<<< HEAD - '@rolldown/pluginutils@1.0.0-beta.44': - resolution: {integrity: sha512-g6eW7Zwnr2c5RADIoqziHoVs6b3W5QTQ4+qbpfjbkMJ9x+8Og211VW/oot2dj9dVwaK/UyC6Yo+02gV+wWQVNg==} -======= '@rolldown/pluginutils@1.0.0-beta.45': resolution: {integrity: sha512-Le9ulGCrD8ggInzWw/k2J8QcbPz7eGIOWqfJ2L+1R0Opm7n6J37s2hiDWlh6LJN0Lk9L5sUzMvRHKW7UxBZsQA==} ->>>>>>> main '@rollup/rollup-android-arm-eabi@4.53.2': resolution: {integrity: sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==} @@ -1419,20 +1348,15 @@ packages: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} - esrap@1.4.9: - resolution: {integrity: sha512-3OMlcd0a03UGuZpPeUC1HxR3nA23l+HEyCiZw3b3FumJIN9KphoGzDJKMXI1S72jVS1dsenDyQC0kJlO1U9E1g==} - -<<<<<<< HEAD esrap@2.1.0: resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==} + esrap@2.1.3: + resolution: {integrity: sha512-T/Dhhv/QH+yYmiaLz9SA3PW+YyenlnRKDNdtlYJrSOBmNsH4nvPux+mTwx7p+wAedlJrGoZtXNI0a0MjQ2QkVg==} + esrap@https://pkg.pr.new/sveltejs/esrap@af12b38: resolution: {tarball: https://pkg.pr.new/sveltejs/esrap@af12b38} version: 2.1.0 -======= - esrap@2.1.3: - resolution: {integrity: sha512-T/Dhhv/QH+yYmiaLz9SA3PW+YyenlnRKDNdtlYJrSOBmNsH4nvPux+mTwx7p+wAedlJrGoZtXNI0a0MjQ2QkVg==} ->>>>>>> main esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} @@ -2013,13 +1937,8 @@ packages: vue-tsc: optional: true -<<<<<<< HEAD - rolldown@1.0.0-beta.44: - resolution: {integrity: sha512-gcqgyCi3g93Fhr49PKvymE8PoaGS0sf6ajQrsYaQ8o5de6aUEbD6rJZiJbhOfpcqOnycgsAsUNPYri1h25NgsQ==} -======= rolldown@1.0.0-beta.45: resolution: {integrity: sha512-iMmuD72XXLf26Tqrv1cryNYLX6NNPLhZ3AmNkSf8+xda0H+yijjGJ+wVT9UdBUHOpKzq9RjKtQKRCWoEKQQBZQ==} ->>>>>>> main engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -2144,18 +2063,13 @@ packages: resolution: {integrity: sha512-d53/xClCjHsuFXuHsn7+F/0NKkkwgRv8kLg2his5YBYqVtfIrBqkvWd+5ZjYN6ryk/jv/rJF00vexXHkK8ofXA==} engines: {node: '>=18'} -<<<<<<< HEAD svelte@https://pkg.pr.new/sveltejs/svelte@1377c40: resolution: {tarball: https://pkg.pr.new/sveltejs/svelte@1377c40} version: 5.34.9 engines: {node: '>=18'} - synckit@0.11.8: - resolution: {integrity: sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==} -======= synckit@0.11.11: resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} ->>>>>>> main engines: {node: ^14.18.0 || >=16.0.0} tapable@2.3.0: @@ -2418,6 +2332,11 @@ packages: snapshots: + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@babel/generator@7.28.5': dependencies: '@babel/parser': 7.28.5 @@ -2841,39 +2760,6 @@ snapshots: dependencies: quansync: 0.2.11 -<<<<<<< HEAD - '@rolldown/binding-android-arm64@1.0.0-beta.44': - optional: true - - '@rolldown/binding-darwin-arm64@1.0.0-beta.44': - optional: true - - '@rolldown/binding-darwin-x64@1.0.0-beta.44': - optional: true - - '@rolldown/binding-freebsd-x64@1.0.0-beta.44': - optional: true - - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.44': - optional: true - - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.44': - optional: true - - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.44': - optional: true - - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.44': - optional: true - - '@rolldown/binding-linux-x64-musl@1.0.0-beta.44': - optional: true - - '@rolldown/binding-openharmony-arm64@1.0.0-beta.44': - optional: true - - '@rolldown/binding-wasm32-wasi@1.0.0-beta.44': -======= '@rolldown/binding-android-arm64@1.0.0-beta.45': optional: true @@ -2905,23 +2791,10 @@ snapshots: optional: true '@rolldown/binding-wasm32-wasi@1.0.0-beta.45': ->>>>>>> main dependencies: '@napi-rs/wasm-runtime': 1.0.7 optional: true -<<<<<<< HEAD - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.44': - optional: true - - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.44': - optional: true - - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.44': - optional: true - - '@rolldown/pluginutils@1.0.0-beta.44': {} -======= '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.45': optional: true @@ -2932,7 +2805,6 @@ snapshots: optional: true '@rolldown/pluginutils@1.0.0-beta.45': {} ->>>>>>> main '@rollup/rollup-android-arm-eabi@4.53.2': optional: true @@ -3547,19 +3419,15 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@1.4.9: + esrap@2.1.0: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 -<<<<<<< HEAD - esrap@2.1.0: + esrap@2.1.3: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 esrap@https://pkg.pr.new/sveltejs/esrap@af12b38: -======= - esrap@2.1.3: ->>>>>>> main dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -4029,20 +3897,6 @@ snapshots: reusify@1.1.0: {} -<<<<<<< HEAD - rolldown-plugin-dts@0.16.9(rolldown@1.0.0-beta.44)(typescript@5.8.3): - dependencies: - '@babel/generator': 7.28.3 - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 - ast-kit: 2.1.2 - birpc: 2.6.1 - debug: 4.4.3 - dts-resolver: 2.1.2 - get-tsconfig: 4.10.1 - magic-string: 0.30.19 - rolldown: 1.0.0-beta.44 -======= rolldown-plugin-dts@0.17.8(ms@2.1.3)(rolldown@1.0.0-beta.45)(typescript@5.9.3): dependencies: '@babel/generator': 7.28.5 @@ -4055,34 +3909,12 @@ snapshots: magic-string: 0.30.21 obug: 2.0.0(ms@2.1.3) rolldown: 1.0.0-beta.45 ->>>>>>> main optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - ms - oxc-resolver -<<<<<<< HEAD - rolldown@1.0.0-beta.44: - dependencies: - '@oxc-project/types': 0.95.0 - '@rolldown/pluginutils': 1.0.0-beta.44 - optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-beta.44 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.44 - '@rolldown/binding-darwin-x64': 1.0.0-beta.44 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.44 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.44 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.44 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.44 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.44 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.44 - '@rolldown/binding-openharmony-arm64': 1.0.0-beta.44 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.44 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.44 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.44 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.44 -======= rolldown@1.0.0-beta.45: dependencies: '@oxc-project/types': 0.95.0 @@ -4102,7 +3934,6 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.45 '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.45 '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.45 ->>>>>>> main rollup@4.53.2: dependencies: @@ -4261,12 +4092,11 @@ snapshots: magic-string: 0.30.21 zimmerframe: 1.1.4 -<<<<<<< HEAD svelte@https://pkg.pr.new/sveltejs/svelte@1377c40: dependencies: '@ampproject/remapping': 2.3.0 '@jridgewell/sourcemap-codec': 1.5.5 - '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) + '@sveltejs/acorn-typescript': 1.0.7(acorn@8.15.0) '@types/estree': 1.0.8 acorn: 8.15.0 aria-query: 5.3.2 @@ -4276,13 +4106,10 @@ snapshots: esrap: 2.1.0 is-reference: 3.0.3 locate-character: 3.0.0 - magic-string: 0.30.19 - zimmerframe: 1.1.2 + magic-string: 0.30.21 + zimmerframe: 1.1.4 - synckit@0.11.8: -======= synckit@0.11.11: ->>>>>>> main dependencies: '@pkgr/core': 0.2.9 @@ -4348,17 +4175,10 @@ snapshots: diff: 8.0.2 empathic: 2.0.0 hookable: 5.5.3 -<<<<<<< HEAD - rolldown: 1.0.0-beta.44 - rolldown-plugin-dts: 0.16.9(rolldown@1.0.0-beta.44)(typescript@5.8.3) - semver: 7.7.2 - tinyexec: 1.0.1 -======= rolldown: 1.0.0-beta.45 rolldown-plugin-dts: 0.17.8(ms@2.1.3)(rolldown@1.0.0-beta.45)(typescript@5.9.3) semver: 7.7.3 tinyexec: 1.0.2 ->>>>>>> main tinyglobby: 0.2.15 tree-kill: 1.2.2 unconfig: 7.4.1 From 7f0bf7158f75c6aeb1861d2c4d5578d90ce4a733 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sat, 22 Nov 2025 20:36:09 +0100 Subject: [PATCH 14/45] update deps --- packages/core/package.json | 4 ++-- pnpm-lock.yaml | 44 +++++++++----------------------------- 2 files changed, 12 insertions(+), 36 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 8dad4408c..ae402e668 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -34,13 +34,13 @@ "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", - "esrap": "https://pkg.pr.new/sveltejs/esrap@af12b38", + "esrap": "^2.1.3", "htmlparser2": "^9.1.0", "magic-string": "^0.30.21", "picocolors": "^1.1.1", "postcss": "^8.5.6", "silver-fleece": "^1.2.1", - "svelte": "https://pkg.pr.new/sveltejs/svelte@1377c40", + "svelte": "https://pkg.pr.new/sveltejs/svelte@2372f17", "yaml": "^2.8.1", "zimmerframe": "^1.1.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1e07c8497..9a21bb18b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -167,8 +167,8 @@ importers: specifier: ^3.2.2 version: 3.2.2 esrap: - specifier: https://pkg.pr.new/sveltejs/esrap@af12b38 - version: https://pkg.pr.new/sveltejs/esrap@af12b38 + specifier: ^2.1.3 + version: 2.1.3 htmlparser2: specifier: ^9.1.0 version: 9.1.0 @@ -185,8 +185,8 @@ importers: specifier: ^1.2.1 version: 1.2.1 svelte: - specifier: https://pkg.pr.new/sveltejs/svelte@1377c40 - version: https://pkg.pr.new/sveltejs/svelte@1377c40 + specifier: https://pkg.pr.new/sveltejs/svelte@2372f17 + version: https://pkg.pr.new/sveltejs/svelte@2372f17 yaml: specifier: ^2.8.1 version: 2.8.1 @@ -260,10 +260,6 @@ importers: packages: - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@babel/generator@7.28.5': resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} @@ -1348,16 +1344,9 @@ packages: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} - esrap@2.1.0: - resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==} - esrap@2.1.3: resolution: {integrity: sha512-T/Dhhv/QH+yYmiaLz9SA3PW+YyenlnRKDNdtlYJrSOBmNsH4nvPux+mTwx7p+wAedlJrGoZtXNI0a0MjQ2QkVg==} - esrap@https://pkg.pr.new/sveltejs/esrap@af12b38: - resolution: {tarball: https://pkg.pr.new/sveltejs/esrap@af12b38} - version: 2.1.0 - esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -2063,9 +2052,9 @@ packages: resolution: {integrity: sha512-d53/xClCjHsuFXuHsn7+F/0NKkkwgRv8kLg2his5YBYqVtfIrBqkvWd+5ZjYN6ryk/jv/rJF00vexXHkK8ofXA==} engines: {node: '>=18'} - svelte@https://pkg.pr.new/sveltejs/svelte@1377c40: - resolution: {tarball: https://pkg.pr.new/sveltejs/svelte@1377c40} - version: 5.34.9 + svelte@https://pkg.pr.new/sveltejs/svelte@2372f17: + resolution: {tarball: https://pkg.pr.new/sveltejs/svelte@2372f17} + version: 5.43.14 engines: {node: '>=18'} synckit@0.11.11: @@ -2332,11 +2321,6 @@ packages: snapshots: - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - '@babel/generator@7.28.5': dependencies: '@babel/parser': 7.28.5 @@ -3419,18 +3403,10 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@2.1.0: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - esrap@2.1.3: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - esrap@https://pkg.pr.new/sveltejs/esrap@af12b38: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -4092,9 +4068,9 @@ snapshots: magic-string: 0.30.21 zimmerframe: 1.1.4 - svelte@https://pkg.pr.new/sveltejs/svelte@1377c40: + svelte@https://pkg.pr.new/sveltejs/svelte@2372f17: dependencies: - '@ampproject/remapping': 2.3.0 + '@jridgewell/remapping': 2.3.5 '@jridgewell/sourcemap-codec': 1.5.5 '@sveltejs/acorn-typescript': 1.0.7(acorn@8.15.0) '@types/estree': 1.0.8 @@ -4103,7 +4079,7 @@ snapshots: axobject-query: 4.1.0 clsx: 2.1.1 esm-env: 1.2.2 - esrap: 2.1.0 + esrap: 2.1.3 is-reference: 3.0.3 locate-character: 3.0.0 magic-string: 0.30.21 From 0b551556a1a0ae486e90d92248210e83f4fd07c3 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Wed, 26 Nov 2025 20:37:22 +0100 Subject: [PATCH 15/45] update esrap --- packages/core/package.json | 2 +- pnpm-lock.yaml | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 4373ab211..e7e70798a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -34,7 +34,7 @@ "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", - "esrap": "https://pkg.pr.new/sveltejs/esrap@af12b38", + "esrap": "^2.2.0", "htmlparser2": "^9.1.0", "magic-string": "^0.30.21", "picocolors": "^1.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3e7545caa..56936eab8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -167,8 +167,8 @@ importers: specifier: ^3.2.2 version: 3.2.2 esrap: - specifier: https://pkg.pr.new/sveltejs/esrap@af12b38 - version: https://pkg.pr.new/sveltejs/esrap@af12b38 + specifier: ^2.2.0 + version: 2.2.0 htmlparser2: specifier: ^9.1.0 version: 9.1.0 @@ -1344,9 +1344,8 @@ packages: esrap@2.1.3: resolution: {integrity: sha512-T/Dhhv/QH+yYmiaLz9SA3PW+YyenlnRKDNdtlYJrSOBmNsH4nvPux+mTwx7p+wAedlJrGoZtXNI0a0MjQ2QkVg==} - esrap@https://pkg.pr.new/sveltejs/esrap@af12b38: - resolution: {tarball: https://pkg.pr.new/sveltejs/esrap@af12b38} - version: 2.1.0 + esrap@2.2.0: + resolution: {integrity: sha512-WBmtxe7R9C5mvL4n2le8nMUe4mD5V9oiK2vJpQ9I3y20ENPUomPcphBXE8D1x/Bm84oN1V+lOfgXxtqmxTp3Xg==} esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} @@ -3403,7 +3402,7 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - esrap@https://pkg.pr.new/sveltejs/esrap@af12b38: + esrap@2.2.0: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 From 54685d8c51221ff9c5c54ae8cfd38ff9ced17b4f Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 10:38:37 +0100 Subject: [PATCH 16/45] adapt to new api --- packages/addons/eslint/index.ts | 28 +++-- packages/addons/sveltekit-adapter/index.ts | 8 +- .../core/tests/js/common/jsdoc-comment/run.ts | 6 +- .../tests/js/common/jsdoc-type-comment/run.ts | 6 +- packages/core/tests/js/index.ts | 6 +- packages/core/tooling/index.ts | 100 ++++++++++++++---- packages/core/tooling/js/common.ts | 32 +++--- packages/core/tooling/js/index.ts | 2 +- packages/core/tooling/parsers.ts | 11 +- 9 files changed, 128 insertions(+), 71 deletions(-) diff --git a/packages/addons/eslint/index.ts b/packages/addons/eslint/index.ts index 444d4a3e7..1109778da 100644 --- a/packages/addons/eslint/index.ts +++ b/packages/addons/eslint/index.ts @@ -53,7 +53,7 @@ export default defineAddon({ }); sv.file(files.eslintConfig, (content) => { - const { ast, additionalComments, generateCode } = parseScript(content); + const { ast, comments, generateCode } = parseScript(content); const eslintConfigs: Array = []; imports.addDefault(ast, { from: './svelte.config.js', as: 'svelteConfig' }); @@ -85,20 +85,16 @@ export default defineAddon({ if (rules.properties[0].type !== 'Property') { throw new Error('rules.properties[0].type !== "Property"'); } - additionalComments.set(rules.properties[0].key, [ - { - type: 'Line', - value: - ' typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.', - position: 'leading' - }, - { - type: 'Line', - value: - ' see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors', - position: 'leading' - } - ]); + comments.addLeading(rules.properties[0].key, { + type: 'Line', + value: + ' typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects.' + }); + comments.addLeading(rules.properties[0].key, { + type: 'Line', + value: + ' see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors' + }); const globalsConfig = object.create({ languageOptions: { @@ -155,7 +151,7 @@ export default defineAddon({ // type annotate config if (!typescript) - common.addJsDocTypeComment(astNode, additionalComments, { + common.addJsDocTypeComment(astNode, comments, { type: "import('eslint').Linter.Config[]" }); diff --git a/packages/addons/sveltekit-adapter/index.ts b/packages/addons/sveltekit-adapter/index.ts index 302129a47..ea27157a0 100644 --- a/packages/addons/sveltekit-adapter/index.ts +++ b/packages/addons/sveltekit-adapter/index.ts @@ -88,7 +88,7 @@ export default defineAddon({ const cfgKitValue = object.property(config, { name: 'kit', fallback }); // removes any existing adapter auto comments - const adapterAutoComments = comments.filter( + const adapterAutoComments = comments.original.filter( (c) => c.loc && cfgKitValue.loc && @@ -96,10 +96,10 @@ export default defineAddon({ c.loc.end.line <= cfgKitValue.loc.end.line ); // modify the array in place - comments.splice( + comments.original.splice( 0, - comments.length, - ...comments.filter((c) => !adapterAutoComments.includes(c)) + comments.original.length, + ...comments.original.filter((c) => !adapterAutoComments.includes(c)) ); } diff --git a/packages/core/tests/js/common/jsdoc-comment/run.ts b/packages/core/tests/js/common/jsdoc-comment/run.ts index 15d0369b9..7b8ec6287 100644 --- a/packages/core/tests/js/common/jsdoc-comment/run.ts +++ b/packages/core/tests/js/common/jsdoc-comment/run.ts @@ -1,9 +1,9 @@ -import { common, type AdditionalCommentMap, type AstTypes } from '@sveltejs/cli-core/js'; +import { common, type Comments, type AstTypes } from '@sveltejs/cli-core/js'; -export function run(ast: AstTypes.Program, additionalComments: AdditionalCommentMap): void { +export function run(ast: AstTypes.Program, comments: Comments): void { const functionDeclaration = ast.body[0] as AstTypes.FunctionDeclaration; - common.addJsDocComment(functionDeclaration, additionalComments, { + common.addJsDocComment(functionDeclaration, comments, { params: { 'import("$lib/paraglide/runtime").AvailableLanguageTag': 'newLanguage' } }); } diff --git a/packages/core/tests/js/common/jsdoc-type-comment/run.ts b/packages/core/tests/js/common/jsdoc-type-comment/run.ts index 5074afd58..129d725a1 100644 --- a/packages/core/tests/js/common/jsdoc-type-comment/run.ts +++ b/packages/core/tests/js/common/jsdoc-type-comment/run.ts @@ -1,13 +1,13 @@ -import { common, variables, type AdditionalCommentMap, type AstTypes } from '@sveltejs/cli-core/js'; +import { common, variables, type Comments, type AstTypes } from '@sveltejs/cli-core/js'; -export function run(ast: AstTypes.Program, additionalComments: AdditionalCommentMap): void { +export function run(ast: AstTypes.Program, comments: Comments): void { const declaration = variables.declaration(ast, { kind: 'const', name: 'foo', value: { type: 'Literal', value: 42 } }); - common.addJsDocTypeComment(declaration, additionalComments, { + common.addJsDocTypeComment(declaration, comments, { type: 'number' }); diff --git a/packages/core/tests/js/index.ts b/packages/core/tests/js/index.ts index bc32a5c83..191aaeac7 100644 --- a/packages/core/tests/js/index.ts +++ b/packages/core/tests/js/index.ts @@ -16,13 +16,13 @@ for (const categoryDirectory of categoryDirectories) { const inputFilePath = join(testDirectoryPath, 'input.ts'); const input = fs.existsSync(inputFilePath) ? fs.readFileSync(inputFilePath, 'utf8') : ''; - const { ast, comments, additionalComments } = parseScript(input); + const { ast, comments } = parseScript(input); // dynamic imports always need to provide the path inline for static analysis const module = await import(`./${categoryDirectory}/${testName}/run.ts`); - module.run(ast, additionalComments); + module.run(ast, comments); - let output = serializeScript(ast, comments, input, additionalComments); + let output = serializeScript(ast, comments, input); if (!output.endsWith('\n')) output += '\n'; await expect(output).toMatchFileSnapshot(`${testDirectoryPath}/output.ts`); }); diff --git a/packages/core/tooling/index.ts b/packages/core/tooling/index.ts index 9c62d5c7b..e2c23833f 100644 --- a/packages/core/tooling/index.ts +++ b/packages/core/tooling/index.ts @@ -14,12 +14,59 @@ import { } from 'postcss'; import * as fleece from 'silver-fleece'; import { print as esrapPrint } from 'esrap'; -import ts, { type AdditionalComment } from 'esrap/languages/ts'; +import ts from 'esrap/languages/ts'; import * as acorn from 'acorn'; import { tsPlugin } from '@sveltejs/acorn-typescript'; import * as yaml from 'yaml'; -type AdditionalCommentMap = WeakMap; +export type CommentType = { type: 'Line' | 'Block'; value: string }; + +export class InternalComments { + comments: Comments; + leading: WeakMap; + trailing: WeakMap; + + constructor() { + this.leading = new WeakMap(); + this.trailing = new WeakMap(); + this.comments = new Comments([], this.leading, this.trailing); + } +} + +/** + * A helper class for managing comments that should be added to AST nodes during code generation. + * Provides methods to add leading comments (before a node) and trailing comments (after a node). + */ +export class Comments { + /** The original comments parsed from source code */ + original: TsEstree.Comment[]; + #leading: WeakMap; + #trailing: WeakMap; + + constructor( + original: TsEstree.Comment[], + leading: WeakMap, + trailing: WeakMap + ) { + this.original = original; + this.#leading = leading; + this.#trailing = trailing; + } + + /** Add a comment that will appear before the given node */ + addLeading(node: TsEstree.Node, comment: CommentType): void { + const list = this.#leading.get(node) ?? []; + list.push(comment); + this.#leading.set(node, list); + } + + /** Add a comment that will appear after the given node */ + addTrailing(node: TsEstree.Node, comment: CommentType): void { + const list = this.#trailing.get(node) ?? []; + list.push(comment); + this.#trailing.set(node, list); + } +} export { // html @@ -44,7 +91,6 @@ export type { // js TsEstree as AstTypes, - AdditionalCommentMap, //css CssChildNode @@ -54,14 +100,15 @@ export type { * Parses as string to an AST. Code below is taken from `esrap` to ensure compatibilty. * https://github.com/sveltejs/esrap/blob/920491535d31484ac5fae2327c7826839d851aed/test/common.js#L14 */ -export function parseScript(content: string): { +export function parseScript( + content: string, + internalComments?: InternalComments +): { ast: TsEstree.Program; - comments: TsEstree.Comment[]; - additionalComments: AdditionalCommentMap; + comments: Comments; } { - const comments: TsEstree.Comment[] = []; - const acornTs = acorn.Parser.extend(tsPlugin()); + internalComments ??= new InternalComments(); const ast = acornTs.parse(content, { ecmaVersion: 'latest', @@ -79,7 +126,7 @@ export function parseScript(content: string): { value = value.replace(new RegExp(`^${indentation}`, 'gm'), ''); } - comments.push({ + internalComments.comments.original.push({ type: block ? 'Block' : 'Line', value, start, @@ -91,22 +138,37 @@ export function parseScript(content: string): { return { ast, - comments, - additionalComments: new WeakMap() + comments: internalComments.comments }; } export function serializeScript( ast: TsEstree.Node, - comments: TsEstree.Comment[], - previousContent?: string, - additionalComments?: AdditionalCommentMap + comments: Comments | InternalComments, + previousContent?: string ): string { - // @ts-expect-error we are still using `estree` while `esrap` is using `@typescript-eslint/types` - // which is causing these errors. But they are simmilar enough to work together. - const { code } = esrapPrint(ast, ts({ comments, additionalComments }), { - indent: guessIndentString(previousContent) - }); + const originalComments = 'comments' in comments ? comments.comments.original : comments.original; + const trailingComments = + 'trailing' in comments ? comments.trailing : new WeakMap(); + const leadingComments = + 'leading' in comments ? comments.leading : new WeakMap(); + + const { code } = esrapPrint( + // @ts-expect-error we are still using `estree` while `esrap` is using `@typescript-eslint/types` + // which is causing these errors. But they are simmilar enough to work together. + ast, + ts({ + // @ts-expect-error see above + comments: originalComments?.original, + // @ts-expect-error see above + getLeadingComments: (node) => leadingComments?.get(node), + // @ts-expect-error see above + getTrailingComments: (node) => trailingComments?.get(node) + }), + { + indent: guessIndentString(previousContent) + } + ); return code; } diff --git a/packages/core/tooling/js/common.ts b/packages/core/tooling/js/common.ts index 1f51dcf6a..5af6b8672 100644 --- a/packages/core/tooling/js/common.ts +++ b/packages/core/tooling/js/common.ts @@ -1,6 +1,7 @@ import { - type AdditionalCommentMap, type AstTypes, + type Comments, + InternalComments, Walker, parseScript, serializeScript, @@ -11,7 +12,7 @@ import dedent from 'dedent'; export function addJsDocTypeComment( node: AstTypes.Node, - additionalComments: AdditionalCommentMap, + comments: Comments, options: { type: string } ): void { const comment: AstTypes.Comment = { @@ -19,12 +20,12 @@ export function addJsDocTypeComment( value: `* @type {${options.type}} ` }; - addComment(node, additionalComments, comment); + addComment(node, comments, comment); } export function addJsDocComment( node: AstTypes.Node, - additionalComments: AdditionalCommentMap, + comments: Comments, options: { params: Record } ): void { const commentLines: string[] = []; @@ -37,22 +38,16 @@ export function addJsDocComment( value: `*\n * ${commentLines.join('\n * ')}\n ` }; - addComment(node, additionalComments, comment); + addComment(node, comments, comment); } -function addComment( - node: AstTypes.Node, - additionalComments: AdditionalCommentMap, - comment: AstTypes.Comment -) { - const found = additionalComments - .get(node) - ?.find((item) => item.type === 'Block' && item.value === comment.value); +function addComment(node: AstTypes.Node, comments: Comments, comment: AstTypes.Comment) { + const found = comments.original.find( + (item) => item.type === 'Block' && item.value === comment.value + ); if (!found) { - const comments = additionalComments.get(node) ?? []; - comments.push({ ...comment, position: 'leading' }); - additionalComments.set(node, comments); + comments.addLeading(node, comment); } } @@ -112,7 +107,10 @@ export function areNodesEqual(node: AstTypes.Node, otherNode: AstTypes.Node): bo const nodeClone = stripAst(decircular(node), ['loc', 'raw']); const otherNodeClone = stripAst(decircular(otherNode), ['loc', 'raw']); - return serializeScript(nodeClone, []) === serializeScript(otherNodeClone, []); + return ( + serializeScript(nodeClone, new InternalComments()) === + serializeScript(otherNodeClone, new InternalComments()) + ); } export function createBlockStatement(): AstTypes.BlockStatement { diff --git a/packages/core/tooling/js/index.ts b/packages/core/tooling/js/index.ts index 206d49d5a..21b4faf75 100644 --- a/packages/core/tooling/js/index.ts +++ b/packages/core/tooling/js/index.ts @@ -7,4 +7,4 @@ export * as variables from './variables.ts'; export * as exports from './exports.ts'; export * as kit from './kit.ts'; export * as vite from './vite.ts'; -export type { AstTypes, AdditionalCommentMap } from '../index.ts'; +export type { AstTypes, Comments, CommentType } from '../index.ts'; diff --git a/packages/core/tooling/parsers.ts b/packages/core/tooling/parsers.ts index b578453fd..b0c0242ef 100644 --- a/packages/core/tooling/parsers.ts +++ b/packages/core/tooling/parsers.ts @@ -8,13 +8,14 @@ type ParseBase = { export function parseScript(source: string): { ast: utils.AstTypes.Program; - comments: utils.AstTypes.Comment[]; - additionalComments: utils.AdditionalCommentMap; + comments: utils.Comments; } & ParseBase { - const { ast, comments, additionalComments } = utils.parseScript(source); - const generateCode = () => utils.serializeScript(ast, comments, source, additionalComments); + const internalComments = new utils.InternalComments(); - return { ast, comments, additionalComments, source, generateCode }; + const { ast, comments } = utils.parseScript(source, internalComments); + const generateCode = () => utils.serializeScript(ast, internalComments, source); + + return { ast, comments, source, generateCode }; } export function parseCss(source: string): { ast: utils.CssAst } & ParseBase { From 0e56b5602ca860b186f4aa0ca2fc0e5cb4e521c7 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 10:48:22 +0100 Subject: [PATCH 17/45] improvements --- packages/core/tests/js/index.ts | 7 ++++--- packages/core/tests/utils.ts | 16 ++++++++++------ packages/core/tooling/index.ts | 24 +++++++++--------------- packages/core/tooling/js/common.ts | 6 +----- packages/core/tooling/parsers.ts | 6 +++--- 5 files changed, 27 insertions(+), 32 deletions(-) diff --git a/packages/core/tests/js/index.ts b/packages/core/tests/js/index.ts index 191aaeac7..a25dffce0 100644 --- a/packages/core/tests/js/index.ts +++ b/packages/core/tests/js/index.ts @@ -2,7 +2,7 @@ import fs from 'node:fs'; import { join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, test } from 'vitest'; -import { parseScript, serializeScript } from '../../tooling/index.ts'; +import { CommentState, parseScript, serializeScript } from '../../tooling/index.ts'; const baseDir = resolve(fileURLToPath(import.meta.url), '..'); const categoryDirectories = getDirectoryNames(baseDir); @@ -16,13 +16,14 @@ for (const categoryDirectory of categoryDirectories) { const inputFilePath = join(testDirectoryPath, 'input.ts'); const input = fs.existsSync(inputFilePath) ? fs.readFileSync(inputFilePath, 'utf8') : ''; - const { ast, comments } = parseScript(input); + const commentState = new CommentState(); + const { ast, comments } = parseScript(input, commentState); // dynamic imports always need to provide the path inline for static analysis const module = await import(`./${categoryDirectory}/${testName}/run.ts`); module.run(ast, comments); - let output = serializeScript(ast, comments, input); + let output = serializeScript(ast, commentState, input); if (!output.endsWith('\n')) output += '\n'; await expect(output).toMatchFileSnapshot(`${testDirectoryPath}/output.ts`); }); diff --git a/packages/core/tests/utils.ts b/packages/core/tests/utils.ts index fd0a9783f..f65afd061 100644 --- a/packages/core/tests/utils.ts +++ b/packages/core/tests/utils.ts @@ -1,6 +1,7 @@ import { describe, expect, test } from 'vitest'; import dedent from 'dedent'; import { + CommentState, parseScript, serializeScript, guessIndentString, @@ -76,13 +77,14 @@ test('integration - simple', () => { const foobar = "foo"; } `; - const { ast, comments } = parseScript(code); + const commentState = new CommentState(); + const { ast } = parseScript(code, commentState); const method = ast.body[1] as AstTypes.FunctionDeclaration; method.body.body.push(newVariableDeclaration); // new variable is added with correct indentation and matching quotes - expect(serializeScript(ast, comments, code)).toMatchInlineSnapshot(` + expect(serializeScript(ast, commentState, code)).toMatchInlineSnapshot(` "import foo from 'bar'; function bar() { @@ -103,13 +105,14 @@ test('integration - simple 2', () => { const foobar = 'foo'; } `; - const { ast, comments } = parseScript(code); + const commentState = new CommentState(); + const { ast } = parseScript(code, commentState); const method = ast.body[1] as AstTypes.FunctionDeclaration; method.body.body.push(newVariableDeclaration); // new variable is added with correct indentation and matching quotes - expect(serializeScript(ast, comments, code)).toMatchInlineSnapshot(` + expect(serializeScript(ast, commentState, code)).toMatchInlineSnapshot(` "import foo from 'bar'; function bar() { @@ -126,9 +129,10 @@ test('integration - preserves comments', () => { /** @type {string} */ let foo = 'bar'; `; - const { ast, comments } = parseScript(code); + const commentState = new CommentState(); + const { ast } = parseScript(code, commentState); - expect(serializeScript(ast, comments, code)).toMatchInlineSnapshot(` + expect(serializeScript(ast, commentState, code)).toMatchInlineSnapshot(` "/** @type {string} */ let foo = 'bar';" `); diff --git a/packages/core/tooling/index.ts b/packages/core/tooling/index.ts index e2c23833f..65ee75cf4 100644 --- a/packages/core/tooling/index.ts +++ b/packages/core/tooling/index.ts @@ -21,7 +21,7 @@ import * as yaml from 'yaml'; export type CommentType = { type: 'Line' | 'Block'; value: string }; -export class InternalComments { +export class CommentState { comments: Comments; leading: WeakMap; trailing: WeakMap; @@ -102,13 +102,13 @@ export type { */ export function parseScript( content: string, - internalComments?: InternalComments + commentState?: CommentState ): { ast: TsEstree.Program; comments: Comments; } { const acornTs = acorn.Parser.extend(tsPlugin()); - internalComments ??= new InternalComments(); + commentState ??= new CommentState(); const ast = acornTs.parse(content, { ecmaVersion: 'latest', @@ -126,7 +126,7 @@ export function parseScript( value = value.replace(new RegExp(`^${indentation}`, 'gm'), ''); } - internalComments.comments.original.push({ + commentState.comments.original.push({ type: block ? 'Block' : 'Line', value, start, @@ -138,32 +138,26 @@ export function parseScript( return { ast, - comments: internalComments.comments + comments: commentState.comments }; } export function serializeScript( ast: TsEstree.Node, - comments: Comments | InternalComments, + commentState?: CommentState, previousContent?: string ): string { - const originalComments = 'comments' in comments ? comments.comments.original : comments.original; - const trailingComments = - 'trailing' in comments ? comments.trailing : new WeakMap(); - const leadingComments = - 'leading' in comments ? comments.leading : new WeakMap(); - const { code } = esrapPrint( // @ts-expect-error we are still using `estree` while `esrap` is using `@typescript-eslint/types` // which is causing these errors. But they are simmilar enough to work together. ast, ts({ // @ts-expect-error see above - comments: originalComments?.original, + comments: commentState?.comments.original, // @ts-expect-error see above - getLeadingComments: (node) => leadingComments?.get(node), + getLeadingComments: (node) => commentState?.leading.get(node), // @ts-expect-error see above - getTrailingComments: (node) => trailingComments?.get(node) + getTrailingComments: (node) => commentState?.trailing.get(node) }), { indent: guessIndentString(previousContent) diff --git a/packages/core/tooling/js/common.ts b/packages/core/tooling/js/common.ts index 5af6b8672..7f5fb1f01 100644 --- a/packages/core/tooling/js/common.ts +++ b/packages/core/tooling/js/common.ts @@ -1,7 +1,6 @@ import { type AstTypes, type Comments, - InternalComments, Walker, parseScript, serializeScript, @@ -107,10 +106,7 @@ export function areNodesEqual(node: AstTypes.Node, otherNode: AstTypes.Node): bo const nodeClone = stripAst(decircular(node), ['loc', 'raw']); const otherNodeClone = stripAst(decircular(otherNode), ['loc', 'raw']); - return ( - serializeScript(nodeClone, new InternalComments()) === - serializeScript(otherNodeClone, new InternalComments()) - ); + return serializeScript(nodeClone) === serializeScript(otherNodeClone); } export function createBlockStatement(): AstTypes.BlockStatement { diff --git a/packages/core/tooling/parsers.ts b/packages/core/tooling/parsers.ts index b0c0242ef..82fbb0353 100644 --- a/packages/core/tooling/parsers.ts +++ b/packages/core/tooling/parsers.ts @@ -10,10 +10,10 @@ export function parseScript(source: string): { ast: utils.AstTypes.Program; comments: utils.Comments; } & ParseBase { - const internalComments = new utils.InternalComments(); + const state = new utils.CommentState(); - const { ast, comments } = utils.parseScript(source, internalComments); - const generateCode = () => utils.serializeScript(ast, internalComments, source); + const { ast, comments } = utils.parseScript(source, state); + const generateCode = () => utils.serializeScript(ast, state, source); return { ast, comments, source, generateCode }; } From a89a0e90e9f0ce6bbc56d2378d718af0eb17216a Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 10:57:45 +0100 Subject: [PATCH 18/45] cleanup --- packages/core/tests/js/index.ts | 7 +- packages/core/tests/utils.ts | 10 +-- packages/core/tooling/index.ts | 109 +++++++++++++++--------------- packages/core/tooling/js/index.ts | 2 +- packages/core/tooling/parsers.ts | 8 +-- 5 files changed, 63 insertions(+), 73 deletions(-) diff --git a/packages/core/tests/js/index.ts b/packages/core/tests/js/index.ts index a25dffce0..c2abe2e19 100644 --- a/packages/core/tests/js/index.ts +++ b/packages/core/tests/js/index.ts @@ -2,7 +2,7 @@ import fs from 'node:fs'; import { join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, test } from 'vitest'; -import { CommentState, parseScript, serializeScript } from '../../tooling/index.ts'; +import { parseScript, serializeScript } from '../../tooling/index.ts'; const baseDir = resolve(fileURLToPath(import.meta.url), '..'); const categoryDirectories = getDirectoryNames(baseDir); @@ -16,12 +16,11 @@ for (const categoryDirectory of categoryDirectories) { const inputFilePath = join(testDirectoryPath, 'input.ts'); const input = fs.existsSync(inputFilePath) ? fs.readFileSync(inputFilePath, 'utf8') : ''; - const commentState = new CommentState(); - const { ast, comments } = parseScript(input, commentState); + const { ast, commentState } = parseScript(input); // dynamic imports always need to provide the path inline for static analysis const module = await import(`./${categoryDirectory}/${testName}/run.ts`); - module.run(ast, comments); + module.run(ast, commentState.comments); let output = serializeScript(ast, commentState, input); if (!output.endsWith('\n')) output += '\n'; diff --git a/packages/core/tests/utils.ts b/packages/core/tests/utils.ts index f65afd061..7bdc2bc3b 100644 --- a/packages/core/tests/utils.ts +++ b/packages/core/tests/utils.ts @@ -1,7 +1,6 @@ import { describe, expect, test } from 'vitest'; import dedent from 'dedent'; import { - CommentState, parseScript, serializeScript, guessIndentString, @@ -77,8 +76,7 @@ test('integration - simple', () => { const foobar = "foo"; } `; - const commentState = new CommentState(); - const { ast } = parseScript(code, commentState); + const { ast, commentState } = parseScript(code); const method = ast.body[1] as AstTypes.FunctionDeclaration; method.body.body.push(newVariableDeclaration); @@ -105,8 +103,7 @@ test('integration - simple 2', () => { const foobar = 'foo'; } `; - const commentState = new CommentState(); - const { ast } = parseScript(code, commentState); + const { ast, commentState } = parseScript(code); const method = ast.body[1] as AstTypes.FunctionDeclaration; method.body.body.push(newVariableDeclaration); @@ -129,8 +126,7 @@ test('integration - preserves comments', () => { /** @type {string} */ let foo = 'bar'; `; - const commentState = new CommentState(); - const { ast } = parseScript(code, commentState); + const { ast, commentState } = parseScript(code); expect(serializeScript(ast, commentState, code)).toMatchInlineSnapshot(` "/** @type {string} */ diff --git a/packages/core/tooling/index.ts b/packages/core/tooling/index.ts index 65ee75cf4..c637cf928 100644 --- a/packages/core/tooling/index.ts +++ b/packages/core/tooling/index.ts @@ -19,55 +19,6 @@ import * as acorn from 'acorn'; import { tsPlugin } from '@sveltejs/acorn-typescript'; import * as yaml from 'yaml'; -export type CommentType = { type: 'Line' | 'Block'; value: string }; - -export class CommentState { - comments: Comments; - leading: WeakMap; - trailing: WeakMap; - - constructor() { - this.leading = new WeakMap(); - this.trailing = new WeakMap(); - this.comments = new Comments([], this.leading, this.trailing); - } -} - -/** - * A helper class for managing comments that should be added to AST nodes during code generation. - * Provides methods to add leading comments (before a node) and trailing comments (after a node). - */ -export class Comments { - /** The original comments parsed from source code */ - original: TsEstree.Comment[]; - #leading: WeakMap; - #trailing: WeakMap; - - constructor( - original: TsEstree.Comment[], - leading: WeakMap, - trailing: WeakMap - ) { - this.original = original; - this.#leading = leading; - this.#trailing = trailing; - } - - /** Add a comment that will appear before the given node */ - addLeading(node: TsEstree.Node, comment: CommentType): void { - const list = this.#leading.get(node) ?? []; - list.push(comment); - this.#leading.set(node, list); - } - - /** Add a comment that will appear after the given node */ - addTrailing(node: TsEstree.Node, comment: CommentType): void { - const list = this.#trailing.get(node) ?? []; - list.push(comment); - this.#trailing.set(node, list); - } -} - export { // html Document as HtmlDocument, @@ -100,15 +51,12 @@ export type { * Parses as string to an AST. Code below is taken from `esrap` to ensure compatibilty. * https://github.com/sveltejs/esrap/blob/920491535d31484ac5fae2327c7826839d851aed/test/common.js#L14 */ -export function parseScript( - content: string, - commentState?: CommentState -): { +export function parseScript(content: string): { ast: TsEstree.Program; - comments: Comments; + commentState: CommentState; } { const acornTs = acorn.Parser.extend(tsPlugin()); - commentState ??= new CommentState(); + const commentState = new CommentState(); const ast = acornTs.parse(content, { ecmaVersion: 'latest', @@ -138,7 +86,7 @@ export function parseScript( return { ast, - comments: commentState.comments + commentState }; } @@ -272,3 +220,52 @@ export function parseYaml(content: string): ReturnType): string { return yaml.stringify(data, { singleQuote: true }); } + +export type CommentType = { type: 'Line' | 'Block'; value: string }; + +export class CommentState { + comments: Comments; + leading: WeakMap; + trailing: WeakMap; + + constructor() { + this.leading = new WeakMap(); + this.trailing = new WeakMap(); + this.comments = new Comments([], this.leading, this.trailing); + } +} + +/** + * A helper class for managing comments that should be added to AST nodes during code generation. + * Provides methods to add leading comments (before a node) and trailing comments (after a node). + */ +export class Comments { + /** The original comments parsed from source code */ + original: TsEstree.Comment[]; + #leading: WeakMap; + #trailing: WeakMap; + + constructor( + original: TsEstree.Comment[], + leading: WeakMap, + trailing: WeakMap + ) { + this.original = original; + this.#leading = leading; + this.#trailing = trailing; + } + + /** Add a comment that will appear before the given node */ + addLeading(node: TsEstree.Node, comment: CommentType): void { + const list = this.#leading.get(node) ?? []; + list.push(comment); + this.#leading.set(node, list); + } + + /** Add a comment that will appear after the given node */ + addTrailing(node: TsEstree.Node, comment: CommentType): void { + const list = this.#trailing.get(node) ?? []; + list.push(comment); + this.#trailing.set(node, list); + } +} diff --git a/packages/core/tooling/js/index.ts b/packages/core/tooling/js/index.ts index 21b4faf75..c53336b61 100644 --- a/packages/core/tooling/js/index.ts +++ b/packages/core/tooling/js/index.ts @@ -7,4 +7,4 @@ export * as variables from './variables.ts'; export * as exports from './exports.ts'; export * as kit from './kit.ts'; export * as vite from './vite.ts'; -export type { AstTypes, Comments, CommentType } from '../index.ts'; +export type { AstTypes, Comments } from '../index.ts'; diff --git a/packages/core/tooling/parsers.ts b/packages/core/tooling/parsers.ts index 82fbb0353..bb466f60a 100644 --- a/packages/core/tooling/parsers.ts +++ b/packages/core/tooling/parsers.ts @@ -10,12 +10,10 @@ export function parseScript(source: string): { ast: utils.AstTypes.Program; comments: utils.Comments; } & ParseBase { - const state = new utils.CommentState(); + const { ast, commentState } = utils.parseScript(source); + const generateCode = () => utils.serializeScript(ast, commentState, source); - const { ast, comments } = utils.parseScript(source, state); - const generateCode = () => utils.serializeScript(ast, state, source); - - return { ast, comments, source, generateCode }; + return { ast, comments: commentState.comments, source, generateCode }; } export function parseCss(source: string): { ast: utils.CssAst } & ParseBase { From 4efbc25189347fedf2fa9f116d29165a21464ca5 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 11:04:22 +0100 Subject: [PATCH 19/45] remove useless comment --- packages/core/tooling/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/core/tooling/index.ts b/packages/core/tooling/index.ts index c637cf928..4fc010460 100644 --- a/packages/core/tooling/index.ts +++ b/packages/core/tooling/index.ts @@ -235,10 +235,6 @@ export class CommentState { } } -/** - * A helper class for managing comments that should be added to AST nodes during code generation. - * Provides methods to add leading comments (before a node) and trailing comments (after a node). - */ export class Comments { /** The original comments parsed from source code */ original: TsEstree.Comment[]; From 15d3400b7d4bb2104046bf4a8cb970bd802b1e61 Mon Sep 17 00:00:00 2001 From: Manuel <30698007+manuel3108@users.noreply.github.com> Date: Sun, 30 Nov 2025 11:10:06 +0100 Subject: [PATCH 20/45] Update packages/core/tooling/index.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/core/tooling/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/tooling/index.ts b/packages/core/tooling/index.ts index c637cf928..1cf64801f 100644 --- a/packages/core/tooling/index.ts +++ b/packages/core/tooling/index.ts @@ -97,7 +97,7 @@ export function serializeScript( ): string { const { code } = esrapPrint( // @ts-expect-error we are still using `estree` while `esrap` is using `@typescript-eslint/types` - // which is causing these errors. But they are simmilar enough to work together. + // which is causing these errors. But they are similar enough to work together. ast, ts({ // @ts-expect-error see above From 18debe70d0e0d346df5d853e76953a80ff09b21a Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 14:23:45 +0100 Subject: [PATCH 21/45] fix all type errors --- packages/addons/common.ts | 4 +- packages/addons/lucia/index.ts | 2 +- packages/addons/paraglide/index.ts | 19 ++----- packages/addons/tailwindcss/index.ts | 24 ++++----- packages/cli/commands/create.ts | 14 ++---- packages/core/package.json | 1 + packages/core/tooling/html/index.ts | 32 +----------- packages/core/tooling/svelte/index.ts | 72 +++++++++++++++++++++++++++ packages/create/playground.ts | 52 ++++++++++--------- packages/create/test/playground.ts | 2 - 10 files changed, 124 insertions(+), 98 deletions(-) create mode 100644 packages/core/tooling/svelte/index.ts diff --git a/packages/addons/common.ts b/packages/addons/common.ts index 445df540c..dea913440 100644 --- a/packages/addons/common.ts +++ b/packages/addons/common.ts @@ -1,5 +1,5 @@ import { imports, exports, common } from '@sveltejs/cli-core/js'; -import { toSvelteFragment, type SvelteAst } from '@sveltejs/cli-core/html'; +import { toFragment, type SvelteAst } from '@sveltejs/cli-core/svelte'; import { parseScript, parseSvelte } from '@sveltejs/cli-core/parsers'; import process from 'node:process'; @@ -85,7 +85,7 @@ export function addToDemoPage(existingContent: string, path: string): string { } } - ast.fragment.nodes.push(...toSvelteFragment(`${path}`)); + ast.fragment.nodes.push(...toFragment(`${path}`)); return generateCode(); } diff --git a/packages/addons/lucia/index.ts b/packages/addons/lucia/index.ts index 1507c4918..3fa87d713 100644 --- a/packages/addons/lucia/index.ts +++ b/packages/addons/lucia/index.ts @@ -396,7 +396,7 @@ export default defineAddon({ if (options.demo) { sv.file(`${kit?.routesDirectory}/demo/+page.svelte`, (content) => { - return addToDemoPage(content, 'lucia', typescript); + return addToDemoPage(content, 'lucia'); }); sv.file(`${kit!.routesDirectory}/demo/lucia/login/+page.server.${ext}`, (content) => { diff --git a/packages/addons/paraglide/index.ts b/packages/addons/paraglide/index.ts index db2551ae3..706e520d8 100644 --- a/packages/addons/paraglide/index.ts +++ b/packages/addons/paraglide/index.ts @@ -1,6 +1,7 @@ import { colors, defineAddon, defineAddonOptions, log } from '@sveltejs/cli-core'; import { common, imports, variables, exports, kit as kitJs, vite } from '@sveltejs/cli-core/js'; import * as html from '@sveltejs/cli-core/html'; +import * as svelte from '@sveltejs/cli-core/svelte'; import { parseHtml, parseJson, parseScript, parseSvelte } from '@sveltejs/cli-core/parsers'; import { addToDemoPage } from '../common.ts'; @@ -177,25 +178,13 @@ export default defineAddon({ if (options.demo) { sv.file(`${kit.routesDirectory}/demo/+page.svelte`, (content) => { - return addToDemoPage(content, 'paraglide', typescript); + return addToDemoPage(content, 'paraglide'); }); // add usage example sv.file(`${kit.routesDirectory}/demo/paraglide/+page.svelte`, (content) => { const { ast, generateCode } = parseSvelte(content); - - let scriptAst = ast.instance?.content; - if (!scriptAst) { - scriptAst = parseScript('').ast; - ast.instance = { - type: 'Script', - start: 0, - end: 0, - context: 'default', - attributes: [], - content: scriptAst - }; - } + const scriptAst = svelte.ensureScript(ast); imports.addNamed(scriptAst, { imports: { m: 'm' }, from: '$lib/paraglide/messages.js' }); imports.addNamed(scriptAst, { @@ -219,7 +208,7 @@ export default defineAddon({ templateCode += '

If you use VSCode, install the Sherlock i18n extension for a better i18n experience.

'; - ast.fragment.nodes.push(...html.toSvelteFragment(templateCode)); + ast.fragment.nodes.push(...svelte.toFragment(templateCode)); return generateCode(); }); diff --git a/packages/addons/tailwindcss/index.ts b/packages/addons/tailwindcss/index.ts index 55b39aab5..f374e1135 100644 --- a/packages/addons/tailwindcss/index.ts +++ b/packages/addons/tailwindcss/index.ts @@ -1,7 +1,7 @@ import { defineAddon, defineAddonOptions } from '@sveltejs/cli-core'; import { imports, vite } from '@sveltejs/cli-core/js'; +import * as svelte from '@sveltejs/cli-core/svelte'; import { parseCss, parseJson, parseScript, parseSvelte } from '@sveltejs/cli-core/parsers'; -import { addSlot } from '@sveltejs/cli-core/html'; const plugins = [ { @@ -32,7 +32,7 @@ export default defineAddon({ shortDescription: 'css framework', homepage: 'https://tailwindcss.com', options, - run: ({ sv, options, files, typescript, kit, dependencyVersion }) => { + run: ({ sv, options, files, kit, dependencyVersion }) => { const prettierInstalled = Boolean(dependencyVersion('prettier')); sv.devDependency('tailwindcss', '^4.1.17'); @@ -97,30 +97,28 @@ export default defineAddon({ const appSvelte = 'src/App.svelte'; const stylesheetRelative = files.getRelative({ from: appSvelte, to: files.stylesheet }); sv.file(appSvelte, (content) => { - const { script, generateCode } = parseSvelte(content, { typescript }); - imports.addEmpty(script.ast, { from: stylesheetRelative }); - return generateCode({ script: script.generateCode() }); + const { ast, generateCode } = parseSvelte(content); + const scriptAst = svelte.ensureScript(ast); + imports.addEmpty(scriptAst, { from: stylesheetRelative }); + return generateCode(); }); } else { const layoutSvelte = `${kit?.routesDirectory}/+layout.svelte`; const stylesheetRelative = files.getRelative({ from: layoutSvelte, to: files.stylesheet }); sv.file(layoutSvelte, (content) => { - const { script, template, generateCode } = parseSvelte(content, { typescript }); - imports.addEmpty(script.ast, { from: stylesheetRelative }); + const { ast, generateCode } = parseSvelte(content); + const scriptAst = svelte.ensureScript(ast); + imports.addEmpty(scriptAst, { from: stylesheetRelative }); if (content.length === 0) { const svelteVersion = dependencyVersion('svelte'); if (!svelteVersion) throw new Error('Failed to determine svelte version'); - addSlot(script.ast, { - htmlAst: template.ast, + svelte.addSlot(ast, { svelteVersion }); } - return generateCode({ - script: script.generateCode(), - template: content.length === 0 ? template.generateCode() : undefined - }); + return generateCode(); }); } diff --git a/packages/cli/commands/create.ts b/packages/cli/commands/create.ts index b75e39a9c..f2bcefdaf 100644 --- a/packages/cli/commands/create.ts +++ b/packages/cli/commands/create.ts @@ -255,11 +255,7 @@ async function createProject(cwd: ProjectPath, options: Options) { }); if (options.fromPlayground) { - await createProjectFromPlayground( - options.fromPlayground, - projectPath, - language === 'typescript' - ); + await createProjectFromPlayground(options.fromPlayground, projectPath); } p.log.success('Project created'); @@ -313,11 +309,7 @@ async function createProject(cwd: ProjectPath, options: Options) { return { directory: projectPath, addOnNextSteps, packageManager }; } -async function createProjectFromPlayground( - url: string, - cwd: string, - typescript: boolean -): Promise { +async function createProjectFromPlayground(url: string, cwd: string): Promise { const urlData = parsePlaygroundUrl(url); const playground = await downloadPlaygroundData(urlData); @@ -325,7 +317,7 @@ async function createProjectFromPlayground( const dependencies = detectPlaygroundDependencies(playground.files); const installDependencies = await confirmExternalDependencies(Array.from(dependencies.keys())); - setupPlaygroundProject(url, playground, cwd, installDependencies, typescript); + setupPlaygroundProject(url, playground, cwd, installDependencies); } async function confirmExternalDependencies(dependencies: string[]): Promise { diff --git a/packages/core/package.json b/packages/core/package.json index 322cf1d6a..a4f212980 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -22,6 +22,7 @@ "./css": "./tooling/css/index.ts", "./html": "./tooling/html/index.ts", "./js": "./tooling/js/index.ts", + "./svelte": "./tooling/svelte/index.ts", "./parsers": "./tooling/parsers.ts" }, "devDependencies": { diff --git a/packages/core/tooling/html/index.ts b/packages/core/tooling/html/index.ts index e245c3bd9..b79c5074c 100644 --- a/packages/core/tooling/html/index.ts +++ b/packages/core/tooling/html/index.ts @@ -1,17 +1,13 @@ import { - type AstTypes, type HtmlChildNode, type HtmlDocument, HtmlElement, HtmlElementType, parseHtml } from '../index.ts'; -import { appendFromString } from '../js/common.ts'; -import { parseSvelte } from '../parsers.ts'; -import type { AST as SvelteAst } from 'svelte/compiler'; export { HtmlElement, HtmlElementType }; -export type { HtmlDocument, SvelteAst }; +export type { HtmlDocument }; export function createDiv(attributes: Record = {}): HtmlElement { return createElement('div', attributes); @@ -40,29 +36,3 @@ export function addFromRawHtml(childNodes: HtmlChildNode[], html: string): void childNodes.push(childNode); } } - -export function addSlot( - jsAst: AstTypes.Program, - options: { htmlAst: HtmlDocument; svelteVersion: string } -): void { - const slotSyntax = - options.svelteVersion && - (options.svelteVersion.startsWith('4') || options.svelteVersion.startsWith('3')); - - if (slotSyntax) { - const slot = createElement('slot'); - appendElement(options.htmlAst.childNodes, slot); - return; - } - - appendFromString(jsAst, { - code: 'let { children } = $props();' - }); - addFromRawHtml(options.htmlAst.childNodes, '{@render children()}'); -} - -export function toSvelteFragment(content: string): SvelteAst.Fragment['nodes'] { - // TODO write test - const { ast } = parseSvelte(content); - return ast.fragment.nodes; -} diff --git a/packages/core/tooling/svelte/index.ts b/packages/core/tooling/svelte/index.ts new file mode 100644 index 000000000..a4dcf445e --- /dev/null +++ b/packages/core/tooling/svelte/index.ts @@ -0,0 +1,72 @@ +import { parseScript, type AstTypes, type SvelteAst } from '../index.ts'; +import { parseSvelte } from '../parsers.ts'; +import { appendFromString } from '../js/common.ts'; + +export type { SvelteAst }; + +export function ensureScript(ast: SvelteAst.Root): AstTypes.Program { + let scriptAst = ast.instance?.content; + if (!scriptAst) { + scriptAst = parseScript('').ast; + ast.instance = { + type: 'Script', + start: 0, + end: 0, + context: 'default', + attributes: [], + content: scriptAst + }; + } + + return scriptAst; +} + +export function addSlot(ast: SvelteAst.Root, options: { svelteVersion: string }): void { + const slotSyntax = + options.svelteVersion && + (options.svelteVersion.startsWith('4') || options.svelteVersion.startsWith('3')); + + if (slotSyntax) { + ast.fragment.nodes.push({ + type: 'SlotElement', + attributes: [], + fragment: { + type: 'Fragment', + nodes: [] + }, + name: 'slot', + start: 0, + end: 0 + }); + + return; + } + + const scriptAst = ensureScript(ast); + appendFromString(scriptAst, { + code: 'let { children } = $props();' + }); + + ast.fragment.nodes.push({ + type: 'RenderTag', + expression: { + type: 'CallExpression', + callee: { + type: 'Identifier', + name: 'children', + start: 0, + end: 0 + }, + optional: false, + arguments: [] + }, + start: 0, + end: 0 + }); +} + +export function toFragment(content: string): SvelteAst.Fragment['nodes'] { + // TODO write test + const { ast } = parseSvelte(content); + return ast.fragment.nodes; +} diff --git a/packages/create/playground.ts b/packages/create/playground.ts index 0741e30d0..ff1fbf57d 100644 --- a/packages/create/playground.ts +++ b/packages/create/playground.ts @@ -1,9 +1,11 @@ import fs from 'node:fs'; import path from 'node:path'; import * as js from '@sveltejs/cli-core/js'; +import * as svelte from '@sveltejs/cli-core/svelte'; import { parseJson, parseScript, parseSvelte } from '@sveltejs/cli-core/parsers'; import { isVersionUnsupportedBelow } from '@sveltejs/cli-core'; import { getSharedFiles } from './utils.ts'; +import { walk } from 'zimmerframe'; export function validatePlaygroundUrl(link: string): boolean { try { @@ -100,7 +102,8 @@ export function detectPlaygroundDependencies(files: PlaygroundData['files']): Ma for (const file of files) { let ast: js.AstTypes.Program | undefined; if (file.name.endsWith('.svelte')) { - ast = parseSvelte(file.content).script.ast; + const { ast: svelteAst } = parseSvelte(file.content); + ast = svelte.ensureScript(svelteAst); } else if (file.name.endsWith('.js') || file.name.endsWith('.ts')) { ast = parseScript(file.content).ast; } @@ -158,8 +161,7 @@ export function setupPlaygroundProject( url: string, playground: PlaygroundData, cwd: string, - installDependencies: boolean, - typescript: boolean + installDependencies: boolean ): void { const mainFile = playground.files.find((file) => file.name === 'App.svelte'); if (!mainFile) throw new Error('Failed to find `App.svelte` entrypoint.'); @@ -188,19 +190,22 @@ export function setupPlaygroundProject( if (file.name === 'src/lib/PlaygroundLayout.svelte') { // getting raw content - const { script, template, css } = parseSvelte(file.contents); - // generating new content with the right language style - const { generateCode } = parseSvelte('', { typescript }); - contentToWrite = generateCode({ - script: script - .generateCode() - .replaceAll('$sv-title-$sv', playground.name) - .replaceAll('$sv-url-$sv', url), - template: template - .generateCode() - .replaceAll('onclick="{switchTheme}"', 'onclick={switchTheme}'), - css: css.generateCode() + const { ast, generateCode } = parseSvelte(file.contents); + // change title and url placeholders + const scriptAst = svelte.ensureScript(ast); + walk(scriptAst as js.AstTypes.Node, null, { + Literal(node) { + if (node.value === '$sv-title-$sv') { + node.value = playground.name; + node.raw = undefined; + } else if (node.value === '$sv-url-$sv') { + node.value = url; + node.raw = undefined; + } + } }); + + contentToWrite = generateCode(); } fs.writeFileSync(path.join(cwd, file.name), contentToWrite, 'utf-8'); @@ -210,18 +215,19 @@ export function setupPlaygroundProject( // add app import to +page.svelte const filePath = path.join(cwd, 'src/routes/+page.svelte'); const content = fs.readFileSync(filePath, 'utf-8'); - const { script, generateCode } = parseSvelte(content, { typescript }); - js.imports.addDefault(script.ast, { as: 'App', from: `$lib/playground/${mainFile.name}` }); - js.imports.addDefault(script.ast, { + const { ast, generateCode } = parseSvelte(content); + const scriptAst = svelte.ensureScript(ast); + js.imports.addDefault(scriptAst, { as: 'App', from: `$lib/playground/${mainFile.name}` }); + js.imports.addDefault(scriptAst, { as: 'PlaygroundLayout', from: `$lib/PlaygroundLayout.svelte` }); - const newContent = generateCode({ - script: script.generateCode(), - template: ` + ast.fragment.nodes.push( + ...svelte.toFragment(` -` - }); +`) + ); + const newContent = generateCode(); fs.writeFileSync(filePath, newContent, 'utf-8'); // add packages as dependencies to package.json if requested diff --git a/packages/create/test/playground.ts b/packages/create/test/playground.ts index cb9277495..f06ad2644 100644 --- a/packages/create/test/playground.ts +++ b/packages/create/test/playground.ts @@ -169,7 +169,6 @@ test('real world download and convert playground async', async () => { 'https://svelte.dev/playground/770bbef086034b9f8e337bab57efe8d8', playground, directory, - true, true ); @@ -221,7 +220,6 @@ test('real world download and convert playground without async', async () => { 'https://svelte.dev/playground/770bbef086034b9f8e337bab57efe8d8', playground, directory, - true, true ); From 8e084629e4c5c00540c4fb2ea9bf0e2e1c8c487b Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 14:25:00 +0100 Subject: [PATCH 22/45] fix deb --- packages/create/package.json | 3 ++- pnpm-lock.yaml | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/create/package.json b/packages/create/package.json index 6568e2d8e..99908763c 100644 --- a/packages/create/package.json +++ b/packages/create/package.json @@ -29,7 +29,8 @@ "@types/gitignore-parser": "^0.0.3", "gitignore-parser": "^0.0.2", "sucrase": "^3.35.0", - "tiny-glob": "^0.2.9" + "tiny-glob": "^0.2.9", + "zimmerframe": "^1.1.4" }, "keywords": [ "create", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a92d81644..e67a403e7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -211,6 +211,9 @@ importers: tiny-glob: specifier: ^0.2.9 version: 0.2.9 + zimmerframe: + specifier: ^1.1.4 + version: 1.1.4 packages/migrate: dependencies: From 07a56fae076a55af73894450c77a0db5f928a8a9 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 14:46:20 +0100 Subject: [PATCH 23/45] fix svelte version --- packages/core/package.json | 2 +- pnpm-lock.yaml | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index a4f212980..b677f6d3b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -41,7 +41,7 @@ "picocolors": "^1.1.1", "postcss": "^8.5.6", "silver-fleece": "^1.2.1", - "svelte": "https://pkg.pr.new/sveltejs/svelte@2372f17", + "svelte": "^5.45.0", "yaml": "^2.8.1", "zimmerframe": "^1.1.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e67a403e7..04e4d484b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -185,8 +185,8 @@ importers: specifier: ^1.2.1 version: 1.2.1 svelte: - specifier: https://pkg.pr.new/sveltejs/svelte@2372f17 - version: https://pkg.pr.new/sveltejs/svelte@2372f17 + specifier: ^5.45.0 + version: 5.45.2 yaml: specifier: ^2.8.1 version: 2.8.1 @@ -1197,6 +1197,9 @@ packages: resolution: {integrity: sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + devalue@5.5.0: + resolution: {integrity: sha512-69sM5yrHfFLJt0AZ9QqZXGCPfJ7fQjvpln3Rq5+PS03LD32Ost1Q9N+eEnaQwGRIriKkMImXD56ocjQmfjbV3w==} + diff@8.0.2: resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} engines: {node: '>=0.3.1'} @@ -2058,9 +2061,8 @@ packages: resolution: {integrity: sha512-d53/xClCjHsuFXuHsn7+F/0NKkkwgRv8kLg2his5YBYqVtfIrBqkvWd+5ZjYN6ryk/jv/rJF00vexXHkK8ofXA==} engines: {node: '>=18'} - svelte@https://pkg.pr.new/sveltejs/svelte@2372f17: - resolution: {tarball: https://pkg.pr.new/sveltejs/svelte@2372f17} - version: 5.43.14 + svelte@5.45.2: + resolution: {integrity: sha512-yyXdW2u3H0H/zxxWoGwJoQlRgaSJLp+Vhktv12iRw2WRDlKqUPT54Fi0K/PkXqrdkcQ98aBazpy0AH4BCBVfoA==} engines: {node: '>=18'} synckit@0.11.11: @@ -3214,6 +3216,8 @@ snapshots: detect-newline@4.0.1: {} + devalue@5.5.0: {} + diff@8.0.2: {} dir-glob@3.0.1: @@ -4078,7 +4082,7 @@ snapshots: magic-string: 0.30.21 zimmerframe: 1.1.4 - svelte@https://pkg.pr.new/sveltejs/svelte@2372f17: + svelte@5.45.2: dependencies: '@jridgewell/remapping': 2.3.5 '@jridgewell/sourcemap-codec': 1.5.5 @@ -4088,8 +4092,9 @@ snapshots: aria-query: 5.3.2 axobject-query: 4.1.0 clsx: 2.1.1 + devalue: 5.5.0 esm-env: 1.2.2 - esrap: 2.1.3 + esrap: 2.2.0 is-reference: 3.0.3 locate-character: 3.0.0 magic-string: 0.30.21 From a474b28490a8a49f14887f8a5d5b82113906c017 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 14:48:13 +0100 Subject: [PATCH 24/45] update test --- packages/core/tests/js/object/property-node/output.ts | 4 ++-- packages/core/tests/js/object/property-node/run.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/tests/js/object/property-node/output.ts b/packages/core/tests/js/object/property-node/output.ts index 3719acb91..3a47a5151 100644 --- a/packages/core/tests/js/object/property-node/output.ts +++ b/packages/core/tests/js/object/property-node/output.ts @@ -1,6 +1,6 @@ const test = { - /** a comment */ + /*a comment updated*/ /** a comment */ foo: 1, - james: '007' + /*aka: bond, james bond*/ james: '007' }; diff --git a/packages/core/tests/js/object/property-node/run.ts b/packages/core/tests/js/object/property-node/run.ts index 9db2672ea..dec3a3290 100644 --- a/packages/core/tests/js/object/property-node/run.ts +++ b/packages/core/tests/js/object/property-node/run.ts @@ -1,18 +1,18 @@ -import { object, common, type AstTypes } from '@sveltejs/cli-core/js'; +import { object, common, type AstTypes, type Comments } from '@sveltejs/cli-core/js'; import { getTestObjectExpression } from '../objectTestHelper.ts'; -export function run(ast: AstTypes.Program): void { +export function run(ast: AstTypes.Program, comments: Comments): void { const obj = getTestObjectExpression(ast); const p1 = object.propertyNode(obj, { name: 'foo', fallback: object.create({}) }); - p1.leadingComments = [{ type: 'Block', value: 'a comment updated' }]; + comments.addLeading(p1, { type: 'Block', value: 'a comment updated' }); const p2 = object.propertyNode(obj, { name: 'james', fallback: common.createLiteral('007') }); - p2.leadingComments = [{ type: 'Block', value: 'aka: bond, james bond' }]; + comments.addLeading(p2, { type: 'Block', value: 'aka: bond, james bond' }); } From cd408ad3abf9848d217d254323b2c3db1c618d5e Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 14:56:49 +0100 Subject: [PATCH 25/45] fix more places --- community-addon-template/src/index.js | 10 ++++++---- packages/addons/_tests/mdsvex/test.ts | 12 ++++++------ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/community-addon-template/src/index.js b/community-addon-template/src/index.js index 61dc312ea..0b4f997e4 100644 --- a/community-addon-template/src/index.js +++ b/community-addon-template/src/index.js @@ -1,5 +1,6 @@ import { defineAddon, defineAddonOptions } from '@sveltejs/cli-core'; import { imports } from '@sveltejs/cli-core/js'; +import * as svelte from '@sveltejs/cli-core/svelte'; import { parseSvelte } from '@sveltejs/cli-core/parsers'; export const options = defineAddonOptions() @@ -16,7 +17,7 @@ export default defineAddon({ setup: ({ kit, unsupported }) => { if (!kit) unsupported('Requires SvelteKit'); }, - run: ({ sv, options, typescript }) => { + run: ({ sv, options }) => { sv.file('addon-template-demo.txt', (content) => { if (options.demo) { return 'This is a text file made by the Community Addon Template demo!'; @@ -26,9 +27,10 @@ export default defineAddon({ sv.file('src/DemoComponent.svelte', (content) => { if (!options.demo) return content; - const { script, generateCode } = parseSvelte(content, { typescript }); - imports.addDefault(script.ast, { from: '../addon-template-demo.txt?raw', as: 'demo' }); - return generateCode({ script: script.generateCode(), template: '{demo}' }); + const { ast, generateCode } = parseSvelte(content); + const scriptAst = svelte.ensureScript(ast); + imports.addDefault(scriptAst, { from: '../addon-template-demo.txt?raw', as: 'demo' }); + return generateCode(); }); } }); diff --git a/packages/addons/_tests/mdsvex/test.ts b/packages/addons/_tests/mdsvex/test.ts index 07c7a0ecd..37f4599df 100644 --- a/packages/addons/_tests/mdsvex/test.ts +++ b/packages/addons/_tests/mdsvex/test.ts @@ -4,6 +4,7 @@ import { expect } from '@playwright/test'; import { parseSvelte } from '@sveltejs/cli-core/parsers'; import { imports } from '@sveltejs/cli-core/js'; import * as html from '@sveltejs/cli-core/html'; +import * as svelte from '@sveltejs/cli-core/svelte'; import { setupTest } from '../_setup/suite.ts'; import { svxFile } from './fixtures.ts'; import mdsvex from '../../mdsvex/index.ts'; @@ -40,18 +41,17 @@ function addFixture(cwd: string, variant: string) { } const src = fs.readFileSync(page, 'utf8'); - const { script, template, generateCode } = parseSvelte(src); - imports.addDefault(script.ast, { from: './Demo.svx', as: 'Demo' }); + const { ast, generateCode } = parseSvelte(src); + const scriptAst = svelte.ensureScript(ast); + imports.addDefault(scriptAst, { from: './Demo.svx', as: 'Demo' }); + // TODO: what are we trying to do here? const div = html.createDiv({ class: 'mdsvex' }); html.appendElement(template.ast.childNodes, div); const mdsvexNode = html.createElement('Demo'); html.appendElement(div.childNodes, mdsvexNode); - const content = generateCode({ - script: script.generateCode(), - template: template.generateCode() - }); + const content = generateCode(); fs.writeFileSync(page, content, 'utf8'); fs.writeFileSync(svx, svxFile, 'utf8'); From b7a0d9942d4f7db301205a533ca39dee9f81cd62 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 14:59:42 +0100 Subject: [PATCH 26/45] fix todos --- packages/migrate/utils.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/migrate/utils.js b/packages/migrate/utils.js index a2826bab1..bff1eda97 100644 --- a/packages/migrate/utils.js +++ b/packages/migrate/utils.js @@ -314,8 +314,7 @@ export function update_svelte_file(file_path, transform_script_code, transform_s ); fs.writeFileSync(file_path, transform_svelte_code(updated, file_path), 'utf-8'); } catch (err) { - // TODO: change to import('svelte/compiler').Warning after upgrading to Svelte 5 - const e = /** @type {any} */ (err); + const e = /** @type {import('svelte/compiler').Warning} */ (err); console.warn(buildExtendedLogMessage(e), e.frame); console.info(e.stack); } @@ -332,8 +331,7 @@ export function update_js_file(file_path, transform_code) { const updated = transform_code(content, file_path.endsWith('.ts'), file_path); fs.writeFileSync(file_path, updated, 'utf-8'); } catch (err) { - // TODO: change to import('svelte/compiler').Warning after upgrading to Svelte 5 - const e = /** @type {any} */ (err); + const e = /** @type {import('svelte/compiler').Warning} */ (err); console.warn(buildExtendedLogMessage(e), e.frame); console.info(e.stack); } From ad3ab8ea4c02a6cb4708761ab0213c68fef05402 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 14:59:56 +0100 Subject: [PATCH 27/45] remove now useless workarounds --- packages/cli/lib/install.ts | 2 - packages/core/tests/utils.ts | 83 ------------------------------------ 2 files changed, 85 deletions(-) diff --git a/packages/cli/lib/install.ts b/packages/cli/lib/install.ts index 80fed8ea6..4c042a741 100644 --- a/packages/cli/lib/install.ts +++ b/packages/cli/lib/install.ts @@ -151,8 +151,6 @@ async function runAddon({ addon, multiple, workspace, workspaceOptions }: RunAdd fileContent = content(fileContent); if (!fileContent) return fileContent; - // TODO: fix https://github.com/rolldown/tsdown/issues/575 to remove the `replaceAll` - writeFile(workspace, path, fileContent.replaceAll('<\\/script>', '')); files.add(path); } catch (e) { if (e instanceof Error) { diff --git a/packages/core/tests/utils.ts b/packages/core/tests/utils.ts index 7bdc2bc3b..3f8ead945 100644 --- a/packages/core/tests/utils.ts +++ b/packages/core/tests/utils.ts @@ -217,86 +217,3 @@ describe('yaml', () => { `); }); }); - -// TODO: fix https://github.com/rolldown/tsdown/issues/575 to remove the `skip` -test.skip('tsdown escapes script tags in bundled source code', async () => { - const { execSync } = await import('node:child_process'); - const fs = await import('node:fs'); - const path = await import('node:path'); - - const testDir = path.join('../..', '.test-output', `tsdown-test`); - fs.rmSync(testDir, { recursive: true, force: true }); - fs.mkdirSync(testDir, { recursive: true }); - - // Create a test file that uses dedent with script tags - const testFileLiteral = path.join(testDir, 'testLiteral.ts'); - fs.writeFileSync( - testFileLiteral, - `import dedent from 'dedent'; - -export const result = dedent\` - -\`; -` - ); - - const testFileFunction = path.join(testDir, 'testFunction.ts'); - fs.writeFileSync( - testFileFunction, - `import dedent from 'dedent'; - -export const result = dedent(\` - -\`); -` - ); - - // Create a tsdown config - const configFile = path.join(testDir, 'tsdown.config.ts'); - fs.writeFileSync( - configFile, - `import { defineConfig } from 'tsdown'; - -export default defineConfig({ - entry: ['testLiteral.ts', 'testFunction.ts'], - format: ['esm'], - outDir: 'dist', -}); -` - ); - - // Create package.json with tsdown - const pkgJson = { - name: 'test', - type: 'module', - devDependencies: { - tsdown: '^0.15.2', - dedent: '^1.6.0' - } - }; - fs.writeFileSync(path.join(testDir, 'package.json'), JSON.stringify(pkgJson, null, 2)); - - // Install dependencies and build - execSync('npm install', { cwd: testDir, stdio: 'pipe' }); - execSync('npx tsdown', { cwd: testDir, stdio: 'pipe' }); - - // Read the bundled output - const bundledFileLiteral = path.join(testDir, 'dist', 'testLiteral.js'); - const bundledFileFunction = path.join(testDir, 'dist', 'testFunction.js'); - const bundledCodeLiteral = fs.readFileSync(bundledFileLiteral, 'utf-8'); - const bundledCodeFunction = fs.readFileSync(bundledFileFunction, 'utf-8'); - - // Check if the bundled code contains escaped script tags - const hasEscapedScriptTagLiteral = bundledCodeLiteral.includes('<\\/script>'); - const hasEscapedScriptTagFunction = bundledCodeFunction.includes('<\\/script>'); - - // This test demonstrates the issue: tsdown escapes in the bundled source - // Expected: Bundled code should NOT contain escaped script tags - // Actual: Bundled code contains <\/script> when using dedent`...` syntax - expect(hasEscapedScriptTagLiteral).toBe(false); - expect(hasEscapedScriptTagFunction).toBe(false); -}, 30000); // 30s timeout for npm install and build From dd18d5b7ee40a47fd023558fe4ad478b053127e8 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 17:19:12 +0100 Subject: [PATCH 28/45] fix --- packages/cli/lib/install.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/lib/install.ts b/packages/cli/lib/install.ts index 4c042a741..32a41dda6 100644 --- a/packages/cli/lib/install.ts +++ b/packages/cli/lib/install.ts @@ -151,6 +151,7 @@ async function runAddon({ addon, multiple, workspace, workspaceOptions }: RunAdd fileContent = content(fileContent); if (!fileContent) return fileContent; + writeFile(workspace, path, fileContent); files.add(path); } catch (e) { if (e instanceof Error) { From 6a4ceb1a3e436a9b8d65aaee438300b5b77da57a Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 17:25:47 +0100 Subject: [PATCH 29/45] fix mdsvex --- packages/addons/_tests/mdsvex/test.ts | 25 ++++++++++++++----- .../tests/js/object/property-node/output.ts | 3 +-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/addons/_tests/mdsvex/test.ts b/packages/addons/_tests/mdsvex/test.ts index 37f4599df..ebf622898 100644 --- a/packages/addons/_tests/mdsvex/test.ts +++ b/packages/addons/_tests/mdsvex/test.ts @@ -3,7 +3,6 @@ import path from 'node:path'; import { expect } from '@playwright/test'; import { parseSvelte } from '@sveltejs/cli-core/parsers'; import { imports } from '@sveltejs/cli-core/js'; -import * as html from '@sveltejs/cli-core/html'; import * as svelte from '@sveltejs/cli-core/svelte'; import { setupTest } from '../_setup/suite.ts'; import { svxFile } from './fixtures.ts'; @@ -45,11 +44,25 @@ function addFixture(cwd: string, variant: string) { const scriptAst = svelte.ensureScript(ast); imports.addDefault(scriptAst, { from: './Demo.svx', as: 'Demo' }); - // TODO: what are we trying to do here? - const div = html.createDiv({ class: 'mdsvex' }); - html.appendElement(template.ast.childNodes, div); - const mdsvexNode = html.createElement('Demo'); - html.appendElement(div.childNodes, mdsvexNode); + ast.fragment.nodes.push({ + type: 'RegularElement', + name: 'div', + attributes: [ + { + type: 'Attribute', + name: 'class', + value: [{ type: 'Text', data: 'mdsvex', raw: 'mdsvex', start: 0, end: 0 }], + start: 0, + end: 0 + } + ], + fragment: { + type: 'Fragment', + nodes: svelte.toFragment('') + }, + start: 0, + end: 0 + }); const content = generateCode(); diff --git a/packages/core/tests/js/object/property-node/output.ts b/packages/core/tests/js/object/property-node/output.ts index 3a47a5151..d00f52aca 100644 --- a/packages/core/tests/js/object/property-node/output.ts +++ b/packages/core/tests/js/object/property-node/output.ts @@ -1,6 +1,5 @@ const test = { - /*a comment updated*/ /** a comment */ - foo: 1, + /*a comment updated*/ /** a comment */ foo: 1, /*aka: bond, james bond*/ james: '007' }; From 52b2c88d5b54e7ab1d59b8492806857022fcae69 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 17:33:47 +0100 Subject: [PATCH 30/45] fix failing test? --- packages/core/tests/js/object/property-node/input.ts | 2 +- packages/core/tests/js/object/property-node/output.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core/tests/js/object/property-node/input.ts b/packages/core/tests/js/object/property-node/input.ts index 4b075890b..537cd089c 100644 --- a/packages/core/tests/js/object/property-node/input.ts +++ b/packages/core/tests/js/object/property-node/input.ts @@ -1,4 +1,4 @@ const test = { - /** a comment */ + // prettier-ignore foo: 1 }; diff --git a/packages/core/tests/js/object/property-node/output.ts b/packages/core/tests/js/object/property-node/output.ts index d00f52aca..c163865df 100644 --- a/packages/core/tests/js/object/property-node/output.ts +++ b/packages/core/tests/js/object/property-node/output.ts @@ -1,5 +1,6 @@ const test = { - /*a comment updated*/ /** a comment */ foo: 1, + /*a comment updated*/ // prettier-ignore + foo: 1, /*aka: bond, james bond*/ james: '007' }; From eff481953400809b1a495f36c828f157d73f47fd Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 17:50:49 +0100 Subject: [PATCH 31/45] add tests for new svelte utils --- .../svelte/common/ensure-script/input.svelte | 3 ++ .../svelte/common/ensure-script/output.svelte | 5 +++ .../tests/svelte/common/ensure-script/run.ts | 5 +++ .../svelte/common/slot-svelte-4/output.svelte | 1 + .../tests/svelte/common/slot-svelte-4/run.ts | 5 +++ .../svelte/common/slot-svelte-5/output.svelte | 5 +++ .../tests/svelte/common/slot-svelte-5/run.ts | 5 +++ .../svelte/common/to-fragment/input.svelte | 3 ++ .../svelte/common/to-fragment/output.svelte | 1 + .../tests/svelte/common/to-fragment/run.ts | 5 +++ packages/core/tests/svelte/index.ts | 38 +++++++++++++++++++ packages/core/tooling/svelte/index.ts | 1 - 12 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 packages/core/tests/svelte/common/ensure-script/input.svelte create mode 100644 packages/core/tests/svelte/common/ensure-script/output.svelte create mode 100644 packages/core/tests/svelte/common/ensure-script/run.ts create mode 100644 packages/core/tests/svelte/common/slot-svelte-4/output.svelte create mode 100644 packages/core/tests/svelte/common/slot-svelte-4/run.ts create mode 100644 packages/core/tests/svelte/common/slot-svelte-5/output.svelte create mode 100644 packages/core/tests/svelte/common/slot-svelte-5/run.ts create mode 100644 packages/core/tests/svelte/common/to-fragment/input.svelte create mode 100644 packages/core/tests/svelte/common/to-fragment/output.svelte create mode 100644 packages/core/tests/svelte/common/to-fragment/run.ts create mode 100644 packages/core/tests/svelte/index.ts diff --git a/packages/core/tests/svelte/common/ensure-script/input.svelte b/packages/core/tests/svelte/common/ensure-script/input.svelte new file mode 100644 index 000000000..f7cb47851 --- /dev/null +++ b/packages/core/tests/svelte/common/ensure-script/input.svelte @@ -0,0 +1,3 @@ +
+

This is a Svelte component without script block

+
diff --git a/packages/core/tests/svelte/common/ensure-script/output.svelte b/packages/core/tests/svelte/common/ensure-script/output.svelte new file mode 100644 index 000000000..c0e89bbf3 --- /dev/null +++ b/packages/core/tests/svelte/common/ensure-script/output.svelte @@ -0,0 +1,5 @@ + + +
+

This is a Svelte component without script block

+
diff --git a/packages/core/tests/svelte/common/ensure-script/run.ts b/packages/core/tests/svelte/common/ensure-script/run.ts new file mode 100644 index 000000000..27992909c --- /dev/null +++ b/packages/core/tests/svelte/common/ensure-script/run.ts @@ -0,0 +1,5 @@ +import { type SvelteAst, ensureScript } from '@sveltejs/cli-core/svelte'; + +export function run(ast: SvelteAst.Root): void { + ensureScript(ast); +} diff --git a/packages/core/tests/svelte/common/slot-svelte-4/output.svelte b/packages/core/tests/svelte/common/slot-svelte-4/output.svelte new file mode 100644 index 000000000..13e0e91ed --- /dev/null +++ b/packages/core/tests/svelte/common/slot-svelte-4/output.svelte @@ -0,0 +1 @@ + diff --git a/packages/core/tests/svelte/common/slot-svelte-4/run.ts b/packages/core/tests/svelte/common/slot-svelte-4/run.ts new file mode 100644 index 000000000..11d06ce56 --- /dev/null +++ b/packages/core/tests/svelte/common/slot-svelte-4/run.ts @@ -0,0 +1,5 @@ +import { type SvelteAst, addSlot } from '@sveltejs/cli-core/svelte'; + +export function run(ast: SvelteAst.Root): void { + addSlot(ast, { svelteVersion: '4.0.0' }); +} diff --git a/packages/core/tests/svelte/common/slot-svelte-5/output.svelte b/packages/core/tests/svelte/common/slot-svelte-5/output.svelte new file mode 100644 index 000000000..17d34e7cf --- /dev/null +++ b/packages/core/tests/svelte/common/slot-svelte-5/output.svelte @@ -0,0 +1,5 @@ + + +{@render children()} diff --git a/packages/core/tests/svelte/common/slot-svelte-5/run.ts b/packages/core/tests/svelte/common/slot-svelte-5/run.ts new file mode 100644 index 000000000..5ee31421e --- /dev/null +++ b/packages/core/tests/svelte/common/slot-svelte-5/run.ts @@ -0,0 +1,5 @@ +import { type SvelteAst, addSlot } from '@sveltejs/cli-core/svelte'; + +export function run(ast: SvelteAst.Root): void { + addSlot(ast, { svelteVersion: '5.0.0' }); +} diff --git a/packages/core/tests/svelte/common/to-fragment/input.svelte b/packages/core/tests/svelte/common/to-fragment/input.svelte new file mode 100644 index 000000000..6491ab509 --- /dev/null +++ b/packages/core/tests/svelte/common/to-fragment/input.svelte @@ -0,0 +1,3 @@ +
+

This is a Svelte component.

+
diff --git a/packages/core/tests/svelte/common/to-fragment/output.svelte b/packages/core/tests/svelte/common/to-fragment/output.svelte new file mode 100644 index 000000000..518bb5112 --- /dev/null +++ b/packages/core/tests/svelte/common/to-fragment/output.svelte @@ -0,0 +1 @@ +

This is a Svelte component.

Appended Fragment diff --git a/packages/core/tests/svelte/common/to-fragment/run.ts b/packages/core/tests/svelte/common/to-fragment/run.ts new file mode 100644 index 000000000..fad358c42 --- /dev/null +++ b/packages/core/tests/svelte/common/to-fragment/run.ts @@ -0,0 +1,5 @@ +import { type SvelteAst, toFragment } from '@sveltejs/cli-core/svelte'; + +export function run(ast: SvelteAst.Root): void { + ast.fragment.nodes.push(...toFragment('Appended Fragment')); +} diff --git a/packages/core/tests/svelte/index.ts b/packages/core/tests/svelte/index.ts new file mode 100644 index 000000000..08b5c238a --- /dev/null +++ b/packages/core/tests/svelte/index.ts @@ -0,0 +1,38 @@ +import fs from 'node:fs'; +import { join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { describe, expect, test } from 'vitest'; +import { parseSvelte, serializeSvelte } from '../../tooling/index.ts'; + +const baseDir = resolve(fileURLToPath(import.meta.url), '..'); +const categoryDirectories = getDirectoryNames(baseDir); + +for (const categoryDirectory of categoryDirectories) { + describe(categoryDirectory, () => { + const testNames = getDirectoryNames(join(baseDir, categoryDirectory)); + for (const testName of testNames) { + test(testName, async () => { + const testDirectoryPath = join(baseDir, categoryDirectory, testName); + + const inputFilePath = join(testDirectoryPath, 'input.svelte'); + const input = fs.existsSync(inputFilePath) ? fs.readFileSync(inputFilePath, 'utf8') : ''; + const ast = parseSvelte(input); + + // dynamic imports always need to provide the path inline for static analysis + const module = await import(`./${categoryDirectory}/${testName}/run.ts`); + module.run(ast); + + let output = serializeSvelte(ast); + if (!output.endsWith('\n')) output += '\n'; + await expect(output).toMatchFileSnapshot(`${testDirectoryPath}/output.svelte`); + }); + } + }); +} + +function getDirectoryNames(dir: string) { + return fs + .readdirSync(dir, { withFileTypes: true }) + .filter((dirent) => dirent.isDirectory()) + .map((dirent) => dirent.name); +} diff --git a/packages/core/tooling/svelte/index.ts b/packages/core/tooling/svelte/index.ts index a4dcf445e..722c7f7fb 100644 --- a/packages/core/tooling/svelte/index.ts +++ b/packages/core/tooling/svelte/index.ts @@ -66,7 +66,6 @@ export function addSlot(ast: SvelteAst.Root, options: { svelteVersion: string }) } export function toFragment(content: string): SvelteAst.Fragment['nodes'] { - // TODO write test const { ast } = parseSvelte(content); return ast.fragment.nodes; } From 18a7266510feecc70d34384122f8394a3a081d62 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 17:56:51 +0100 Subject: [PATCH 32/45] fix formatting --- packages/core/tests/svelte/common/to-fragment/input.svelte | 2 ++ packages/core/tests/svelte/common/to-fragment/output.svelte | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/core/tests/svelte/common/to-fragment/input.svelte b/packages/core/tests/svelte/common/to-fragment/input.svelte index 6491ab509..f4f500fb8 100644 --- a/packages/core/tests/svelte/common/to-fragment/input.svelte +++ b/packages/core/tests/svelte/common/to-fragment/input.svelte @@ -1,3 +1,5 @@ +

This is a Svelte component.

+ diff --git a/packages/core/tests/svelte/common/to-fragment/output.svelte b/packages/core/tests/svelte/common/to-fragment/output.svelte index 518bb5112..ed8025189 100644 --- a/packages/core/tests/svelte/common/to-fragment/output.svelte +++ b/packages/core/tests/svelte/common/to-fragment/output.svelte @@ -1 +1,3 @@ -

This is a Svelte component.

Appended Fragment + +

This is a Svelte component.

+Appended Fragment From 741c01c94d50e12538be0d3a778eebf068272177 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Sun, 30 Nov 2025 17:59:58 +0100 Subject: [PATCH 33/45] make lint happy --- packages/core/tests/svelte/common/slot-svelte-5/output.svelte | 2 +- packages/core/tooling/svelte/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/tests/svelte/common/slot-svelte-5/output.svelte b/packages/core/tests/svelte/common/slot-svelte-5/output.svelte index 17d34e7cf..f82c4d3da 100644 --- a/packages/core/tests/svelte/common/slot-svelte-5/output.svelte +++ b/packages/core/tests/svelte/common/slot-svelte-5/output.svelte @@ -1,5 +1,5 @@ {@render children()} diff --git a/packages/core/tooling/svelte/index.ts b/packages/core/tooling/svelte/index.ts index 722c7f7fb..508355c79 100644 --- a/packages/core/tooling/svelte/index.ts +++ b/packages/core/tooling/svelte/index.ts @@ -44,7 +44,7 @@ export function addSlot(ast: SvelteAst.Root, options: { svelteVersion: string }) const scriptAst = ensureScript(ast); appendFromString(scriptAst, { - code: 'let { children } = $props();' + code: 'const { children } = $props();' }); ast.fragment.nodes.push({ From 887ab4c7480682aaef48dc672535f2223f476016 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Fri, 5 Dec 2025 15:16:12 +0100 Subject: [PATCH 34/45] re-add guessQuoteStyle --- packages/core/tooling/index.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/core/tooling/index.ts b/packages/core/tooling/index.ts index 3cb22f4b0..9fb28cfa3 100644 --- a/packages/core/tooling/index.ts +++ b/packages/core/tooling/index.ts @@ -214,6 +214,39 @@ export function guessIndentString(str: string | undefined): string { } } +export function guessQuoteStyle(ast: TsEstree.Node): 'single' | 'double' | undefined { + let singleCount = 0; + let doubleCount = 0; + + Walker.walk(ast, null, { + Literal(node) { + if (node.raw && node.raw.length >= 2) { + // we have at least two characters in the raw string that could represent both quotes + const quotes = [node.raw[0], node.raw[node.raw.length - 1]]; + for (const quote of quotes) { + switch (quote) { + case "'": + singleCount++; + break; + case '"': + doubleCount++; + break; + default: + break; + } + } + } + } + }); + + if (singleCount === 0 && doubleCount === 0) { + // new file or file without any quotes + return undefined; + } + + return singleCount > doubleCount ? 'single' : 'double'; +} + export function parseYaml(content: string): ReturnType { return yaml.parseDocument(content); } From bc06007ccb11fa7f1c86cacdd138bcf3ae6e1bd2 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Fri, 5 Dec 2025 15:16:25 +0100 Subject: [PATCH 35/45] fix wrong snapshot --- packages/core/tests/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/tests/utils.ts b/packages/core/tests/utils.ts index a0b14ce71..476b78610 100644 --- a/packages/core/tests/utils.ts +++ b/packages/core/tests/utils.ts @@ -141,7 +141,7 @@ test('integration - simple', () => { console.log("bar"); const foobar = "foo"; - const foobar2 = 'test'; + const foobar2 = "test"; }" `); }); From e668c3eb3588fc7a0b653bfea3e5f405528e63b9 Mon Sep 17 00:00:00 2001 From: Manuel Serret Date: Fri, 5 Dec 2025 15:17:34 +0100 Subject: [PATCH 36/45] fix last test --- packages/core/tests/js/object/property-node/output.ts | 4 ++-- packages/core/tests/js/object/property-node/run.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/tests/js/object/property-node/output.ts b/packages/core/tests/js/object/property-node/output.ts index 3719acb91..c163865df 100644 --- a/packages/core/tests/js/object/property-node/output.ts +++ b/packages/core/tests/js/object/property-node/output.ts @@ -1,6 +1,6 @@ const test = { - /** a comment */ + /*a comment updated*/ // prettier-ignore foo: 1, - james: '007' + /*aka: bond, james bond*/ james: '007' }; diff --git a/packages/core/tests/js/object/property-node/run.ts b/packages/core/tests/js/object/property-node/run.ts index dec3a3290..18372acb4 100644 --- a/packages/core/tests/js/object/property-node/run.ts +++ b/packages/core/tests/js/object/property-node/run.ts @@ -8,11 +8,11 @@ export function run(ast: AstTypes.Program, comments: Comments): void { name: 'foo', fallback: object.create({}) }); - comments.addLeading(p1, { type: 'Block', value: 'a comment updated' }); + comments.add(p1, { type: 'Block', value: 'a comment updated' }); const p2 = object.propertyNode(obj, { name: 'james', fallback: common.createLiteral('007') }); - comments.addLeading(p2, { type: 'Block', value: 'aka: bond, james bond' }); + comments.add(p2, { type: 'Block', value: 'aka: bond, james bond' }); } From d86ec9739db932a00d4063604c90f5812c0b0921 Mon Sep 17 00:00:00 2001 From: jycouet Date: Fri, 5 Dec 2025 15:44:16 +0100 Subject: [PATCH 37/45] keep replaceAll for script ending --- packages/cli/lib/install.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/cli/lib/install.ts b/packages/cli/lib/install.ts index 2ef1ab8d3..fb9b0b692 100644 --- a/packages/cli/lib/install.ts +++ b/packages/cli/lib/install.ts @@ -155,7 +155,8 @@ async function runAddon({ addon, multiple, workspace, workspaceOptions }: RunAdd fileContent = content(fileContent); if (!fileContent) return fileContent; - writeFile(workspace, path, fileContent); + // TODO: fix https://github.com/rolldown/tsdown/issues/575 to remove the `replaceAll` + writeFile(workspace, path, fileContent.replaceAll('<\\/script>', '')); files.add(path); } catch (e) { if (e instanceof Error) { From 1de46a1fc7f6153e9aec19922d0f3c2ab599eb9e Mon Sep 17 00:00:00 2001 From: jycouet Date: Fri, 5 Dec 2025 15:51:57 +0100 Subject: [PATCH 38/45] new format --- .../create-with-all-addons/src/routes/+layout.svelte | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/cli/tests/snapshots/create-with-all-addons/src/routes/+layout.svelte b/packages/cli/tests/snapshots/create-with-all-addons/src/routes/+layout.svelte index 8b9bd05ca..0d8eb0307 100644 --- a/packages/cli/tests/snapshots/create-with-all-addons/src/routes/+layout.svelte +++ b/packages/cli/tests/snapshots/create-with-all-addons/src/routes/+layout.svelte @@ -1,12 +1,9 @@ - - - - + {@render children()} From 42e898949b3f533f4273008d60f5b1b496e1c299 Mon Sep 17 00:00:00 2001 From: jycouet Date: Fri, 5 Dec 2025 15:52:14 +0100 Subject: [PATCH 39/45] with resolve --- packages/addons/common.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/addons/common.ts b/packages/addons/common.ts index dea913440..cc0b98475 100644 --- a/packages/addons/common.ts +++ b/packages/addons/common.ts @@ -1,5 +1,5 @@ import { imports, exports, common } from '@sveltejs/cli-core/js'; -import { toFragment, type SvelteAst } from '@sveltejs/cli-core/svelte'; +import { toFragment, type SvelteAst, ensureScript } from '@sveltejs/cli-core/svelte'; import { parseScript, parseSvelte } from '@sveltejs/cli-core/parsers'; import process from 'node:process'; @@ -85,7 +85,8 @@ export function addToDemoPage(existingContent: string, path: string): string { } } - ast.fragment.nodes.push(...toFragment(`${path}`)); + imports.addNamed(ensureScript(ast), { imports: ['resolve'], from: '$app/paths' }); + ast.fragment.nodes.push(...toFragment(`${path}`)); return generateCode(); } From 9235e8b7961c99f6a3aab495938354cac36d7b15 Mon Sep 17 00:00:00 2001 From: jycouet Date: Fri, 5 Dec 2025 15:52:50 +0100 Subject: [PATCH 40/45] update snap --- .../src/routes/demo/+page.svelte | 3 +-- .../src/routes/demo/paraglide/+page.svelte | 16 ++++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/+page.svelte b/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/+page.svelte index 11d979836..9394e40ae 100644 --- a/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/+page.svelte +++ b/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/+page.svelte @@ -2,5 +2,4 @@ import { resolve } from '$app/paths'; -lucia -paraglide +paraglidelucia diff --git a/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/paraglide/+page.svelte b/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/paraglide/+page.svelte index 04d3480cc..671434223 100644 --- a/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/paraglide/+page.svelte +++ b/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/paraglide/+page.svelte @@ -1,16 +1,20 @@ - -

{m.hello_world({ name: 'SvelteKit User' })}

-

-If you use VSCode, install the Sherlock i18n extension for a better i18n experience. + +

+ If you use VSCode, install the + + Sherlock i18n extension + + for a better i18n experience.

From b0de335976ca71c3d4829c299742e3e5cf672aa6 Mon Sep 17 00:00:00 2001 From: jycouet Date: Fri, 5 Dec 2025 18:47:34 +0100 Subject: [PATCH 41/45] add the ts thingy on no file --- packages/addons/common.ts | 4 ++-- packages/addons/lucia/index.ts | 2 +- packages/addons/paraglide/index.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/addons/common.ts b/packages/addons/common.ts index cc0b98475..b019b2ce1 100644 --- a/packages/addons/common.ts +++ b/packages/addons/common.ts @@ -64,7 +64,7 @@ export function addEslintConfigPrettier(content: string): string { return generateCode(); } -export function addToDemoPage(existingContent: string, path: string): string { +export function addToDemoPage(existingContent: string, path: string, langTs: boolean): string { const { ast, generateCode } = parseSvelte(existingContent); for (const node of ast.fragment.nodes) { @@ -85,7 +85,7 @@ export function addToDemoPage(existingContent: string, path: string): string { } } - imports.addNamed(ensureScript(ast), { imports: ['resolve'], from: '$app/paths' }); + imports.addNamed(ensureScript(ast, { langTs }), { imports: ['resolve'], from: '$app/paths' }); ast.fragment.nodes.push(...toFragment(`${path}`)); return generateCode(); diff --git a/packages/addons/lucia/index.ts b/packages/addons/lucia/index.ts index 3fa87d713..1507c4918 100644 --- a/packages/addons/lucia/index.ts +++ b/packages/addons/lucia/index.ts @@ -396,7 +396,7 @@ export default defineAddon({ if (options.demo) { sv.file(`${kit?.routesDirectory}/demo/+page.svelte`, (content) => { - return addToDemoPage(content, 'lucia'); + return addToDemoPage(content, 'lucia', typescript); }); sv.file(`${kit!.routesDirectory}/demo/lucia/login/+page.server.${ext}`, (content) => { diff --git a/packages/addons/paraglide/index.ts b/packages/addons/paraglide/index.ts index 706e520d8..81b73dc62 100644 --- a/packages/addons/paraglide/index.ts +++ b/packages/addons/paraglide/index.ts @@ -178,13 +178,13 @@ export default defineAddon({ if (options.demo) { sv.file(`${kit.routesDirectory}/demo/+page.svelte`, (content) => { - return addToDemoPage(content, 'paraglide'); + return addToDemoPage(content, 'paraglide', typescript); }); // add usage example sv.file(`${kit.routesDirectory}/demo/paraglide/+page.svelte`, (content) => { const { ast, generateCode } = parseSvelte(content); - const scriptAst = svelte.ensureScript(ast); + const scriptAst = svelte.ensureScript(ast, { langTs: typescript }); imports.addNamed(scriptAst, { imports: { m: 'm' }, from: '$lib/paraglide/messages.js' }); imports.addNamed(scriptAst, { From bdea815893e632118481ec3a277514dd791cbaf1 Mon Sep 17 00:00:00 2001 From: jycouet Date: Fri, 5 Dec 2025 18:53:41 +0100 Subject: [PATCH 42/45] the implem & tests --- .../svelte/common/ensure-script-ts/input.svelte | 1 + .../common/ensure-script-ts/output.svelte | 3 +++ .../tests/svelte/common/ensure-script-ts/run.ts | 5 +++++ .../svelte/common/keep-script-ts/input.svelte | 3 +++ .../svelte/common/keep-script-ts/output.svelte | 3 +++ .../tests/svelte/common/keep-script-ts/run.ts | 5 +++++ packages/core/tooling/svelte/index.ts | 17 +++++++++++++++-- 7 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 packages/core/tests/svelte/common/ensure-script-ts/input.svelte create mode 100644 packages/core/tests/svelte/common/ensure-script-ts/output.svelte create mode 100644 packages/core/tests/svelte/common/ensure-script-ts/run.ts create mode 100644 packages/core/tests/svelte/common/keep-script-ts/input.svelte create mode 100644 packages/core/tests/svelte/common/keep-script-ts/output.svelte create mode 100644 packages/core/tests/svelte/common/keep-script-ts/run.ts diff --git a/packages/core/tests/svelte/common/ensure-script-ts/input.svelte b/packages/core/tests/svelte/common/ensure-script-ts/input.svelte new file mode 100644 index 000000000..175b82b52 --- /dev/null +++ b/packages/core/tests/svelte/common/ensure-script-ts/input.svelte @@ -0,0 +1 @@ +Nothing to see here diff --git a/packages/core/tests/svelte/common/ensure-script-ts/output.svelte b/packages/core/tests/svelte/common/ensure-script-ts/output.svelte new file mode 100644 index 000000000..ff42b6951 --- /dev/null +++ b/packages/core/tests/svelte/common/ensure-script-ts/output.svelte @@ -0,0 +1,3 @@ + + +Nothing to see here diff --git a/packages/core/tests/svelte/common/ensure-script-ts/run.ts b/packages/core/tests/svelte/common/ensure-script-ts/run.ts new file mode 100644 index 000000000..2895bd1cd --- /dev/null +++ b/packages/core/tests/svelte/common/ensure-script-ts/run.ts @@ -0,0 +1,5 @@ +import { type SvelteAst, ensureScript } from '@sveltejs/cli-core/svelte'; + +export function run(ast: SvelteAst.Root): void { + ensureScript(ast, { langTs: true }); +} diff --git a/packages/core/tests/svelte/common/keep-script-ts/input.svelte b/packages/core/tests/svelte/common/keep-script-ts/input.svelte new file mode 100644 index 000000000..3382f954b --- /dev/null +++ b/packages/core/tests/svelte/common/keep-script-ts/input.svelte @@ -0,0 +1,3 @@ + + +A script tag with ts lang attribute diff --git a/packages/core/tests/svelte/common/keep-script-ts/output.svelte b/packages/core/tests/svelte/common/keep-script-ts/output.svelte new file mode 100644 index 000000000..3382f954b --- /dev/null +++ b/packages/core/tests/svelte/common/keep-script-ts/output.svelte @@ -0,0 +1,3 @@ + + +A script tag with ts lang attribute diff --git a/packages/core/tests/svelte/common/keep-script-ts/run.ts b/packages/core/tests/svelte/common/keep-script-ts/run.ts new file mode 100644 index 000000000..27992909c --- /dev/null +++ b/packages/core/tests/svelte/common/keep-script-ts/run.ts @@ -0,0 +1,5 @@ +import { type SvelteAst, ensureScript } from '@sveltejs/cli-core/svelte'; + +export function run(ast: SvelteAst.Root): void { + ensureScript(ast); +} diff --git a/packages/core/tooling/svelte/index.ts b/packages/core/tooling/svelte/index.ts index 508355c79..f341afa60 100644 --- a/packages/core/tooling/svelte/index.ts +++ b/packages/core/tooling/svelte/index.ts @@ -4,7 +4,10 @@ import { appendFromString } from '../js/common.ts'; export type { SvelteAst }; -export function ensureScript(ast: SvelteAst.Root): AstTypes.Program { +export function ensureScript( + ast: SvelteAst.Root, + options?: { langTs?: boolean } +): AstTypes.Program { let scriptAst = ast.instance?.content; if (!scriptAst) { scriptAst = parseScript('').ast; @@ -13,7 +16,17 @@ export function ensureScript(ast: SvelteAst.Root): AstTypes.Program { start: 0, end: 0, context: 'default', - attributes: [], + attributes: options?.langTs + ? [ + { + type: 'Attribute', + start: 8, + end: 17, + name: 'lang', + value: [{ start: 14, end: 16, type: 'Text', raw: 'ts', data: 'ts' }] + } + ] + : [], content: scriptAst }; } From 214cbed7ee65380252c3dc9b8875be601ea92be3 Mon Sep 17 00:00:00 2001 From: jycouet Date: Fri, 5 Dec 2025 19:05:06 +0100 Subject: [PATCH 43/45] update some add-on --- community-addon-template/src/index.js | 4 ++-- packages/addons/tailwindcss/index.ts | 6 +++--- packages/core/tooling/svelte/index.ts | 7 +++++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/community-addon-template/src/index.js b/community-addon-template/src/index.js index 0b4f997e4..57d34e1d1 100644 --- a/community-addon-template/src/index.js +++ b/community-addon-template/src/index.js @@ -17,7 +17,7 @@ export default defineAddon({ setup: ({ kit, unsupported }) => { if (!kit) unsupported('Requires SvelteKit'); }, - run: ({ sv, options }) => { + run: ({ sv, options, typescript }) => { sv.file('addon-template-demo.txt', (content) => { if (options.demo) { return 'This is a text file made by the Community Addon Template demo!'; @@ -28,7 +28,7 @@ export default defineAddon({ sv.file('src/DemoComponent.svelte', (content) => { if (!options.demo) return content; const { ast, generateCode } = parseSvelte(content); - const scriptAst = svelte.ensureScript(ast); + const scriptAst = svelte.ensureScript(ast, { langTs: typescript }); imports.addDefault(scriptAst, { from: '../addon-template-demo.txt?raw', as: 'demo' }); return generateCode(); }); diff --git a/packages/addons/tailwindcss/index.ts b/packages/addons/tailwindcss/index.ts index 992c2c17c..3d3540bbc 100644 --- a/packages/addons/tailwindcss/index.ts +++ b/packages/addons/tailwindcss/index.ts @@ -32,7 +32,7 @@ export default defineAddon({ shortDescription: 'css framework', homepage: 'https://tailwindcss.com', options, - run: ({ sv, options, files, kit, dependencyVersion }) => { + run: ({ sv, options, files, kit, dependencyVersion, typescript }) => { const prettierInstalled = Boolean(dependencyVersion('prettier')); sv.devDependency('tailwindcss', '^4.1.17'); @@ -98,7 +98,7 @@ export default defineAddon({ const stylesheetRelative = files.getRelative({ from: appSvelte, to: files.stylesheet }); sv.file(appSvelte, (content) => { const { ast, generateCode } = parseSvelte(content); - const scriptAst = svelte.ensureScript(ast); + const scriptAst = svelte.ensureScript(ast, { langTs: typescript }); imports.addEmpty(scriptAst, { from: stylesheetRelative }); return generateCode(); }); @@ -107,7 +107,7 @@ export default defineAddon({ const stylesheetRelative = files.getRelative({ from: layoutSvelte, to: files.stylesheet }); sv.file(layoutSvelte, (content) => { const { ast, generateCode } = parseSvelte(content); - const scriptAst = svelte.ensureScript(ast); + const scriptAst = svelte.ensureScript(ast, { langTs: typescript }); imports.addEmpty(scriptAst, { from: stylesheetRelative }); if (content.length === 0) { diff --git a/packages/core/tooling/svelte/index.ts b/packages/core/tooling/svelte/index.ts index f341afa60..ea5dcd33a 100644 --- a/packages/core/tooling/svelte/index.ts +++ b/packages/core/tooling/svelte/index.ts @@ -34,7 +34,10 @@ export function ensureScript( return scriptAst; } -export function addSlot(ast: SvelteAst.Root, options: { svelteVersion: string }): void { +export function addSlot( + ast: SvelteAst.Root, + options: { svelteVersion: string; langTs?: boolean } +): void { const slotSyntax = options.svelteVersion && (options.svelteVersion.startsWith('4') || options.svelteVersion.startsWith('3')); @@ -55,7 +58,7 @@ export function addSlot(ast: SvelteAst.Root, options: { svelteVersion: string }) return; } - const scriptAst = ensureScript(ast); + const scriptAst = ensureScript(ast, { langTs: options.langTs }); appendFromString(scriptAst, { code: 'const { children } = $props();' }); From 7ecdfc901eef84d470af1e84b82dd8dac9272a8b Mon Sep 17 00:00:00 2001 From: jycouet Date: Fri, 5 Dec 2025 19:46:23 +0100 Subject: [PATCH 44/45] nicer? --- packages/addons/common.ts | 1 + .../create-with-all-addons/src/routes/demo/+page.svelte | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/addons/common.ts b/packages/addons/common.ts index b019b2ce1..0731347c4 100644 --- a/packages/addons/common.ts +++ b/packages/addons/common.ts @@ -86,6 +86,7 @@ export function addToDemoPage(existingContent: string, path: string, langTs: boo } imports.addNamed(ensureScript(ast, { langTs }), { imports: ['resolve'], from: '$app/paths' }); + ast.fragment.nodes.unshift({ type: 'Text', data: '\n', raw: '\n', start: 0, end: 0 }); ast.fragment.nodes.push(...toFragment(`${path}`)); return generateCode(); diff --git a/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/+page.svelte b/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/+page.svelte index 9394e40ae..879e3db7b 100644 --- a/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/+page.svelte +++ b/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/+page.svelte @@ -2,4 +2,5 @@ import { resolve } from '$app/paths'; -paraglidelucia +paraglide +lucia From b540ab50b18e71593580f1f01a9468fef4d72527 Mon Sep 17 00:00:00 2001 From: jycouet Date: Fri, 5 Dec 2025 20:00:53 +0100 Subject: [PATCH 45/45] like before --- packages/addons/common.ts | 10 ++++++---- .../src/routes/demo/+page.svelte | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/addons/common.ts b/packages/addons/common.ts index 0731347c4..f7f73c868 100644 --- a/packages/addons/common.ts +++ b/packages/addons/common.ts @@ -76,8 +76,9 @@ export function addToDemoPage(existingContent: string, path: string, langTs: boo if (!Array.isArray(hrefAttribute.value)) continue; - const hasDemo = hrefAttribute.value.find( - (x) => x.type === 'Text' && x.data === `/demo/${path}` + const hasDemo = hrefAttribute.value.some( + // we use includes as it could be "/demo/${path}" or "resolve("demo/${path}")" or "resolve('demo/${path}')" + (x) => x.type === 'Text' && x.data.includes(`/demo/${path}`) ); if (hasDemo) { return existingContent; @@ -86,8 +87,9 @@ export function addToDemoPage(existingContent: string, path: string, langTs: boo } imports.addNamed(ensureScript(ast, { langTs }), { imports: ['resolve'], from: '$app/paths' }); - ast.fragment.nodes.unshift({ type: 'Text', data: '\n', raw: '\n', start: 0, end: 0 }); - ast.fragment.nodes.push(...toFragment(`${path}`)); + + ast.fragment.nodes.unshift(...toFragment(`${path}`)); + ast.fragment.nodes.unshift(); return generateCode(); } diff --git a/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/+page.svelte b/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/+page.svelte index 879e3db7b..11d979836 100644 --- a/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/+page.svelte +++ b/packages/cli/tests/snapshots/create-with-all-addons/src/routes/demo/+page.svelte @@ -2,5 +2,5 @@ import { resolve } from '$app/paths'; -paraglide lucia +paraglide