diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 9653affaa5..f8496cda31 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -171,7 +171,7 @@ "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", - "esrap": "https://pkg.pr.new/sveltejs/esrap@2558e72", + "esrap": "^2.0.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", diff --git a/packages/svelte/scripts/process-messages/index.js b/packages/svelte/scripts/process-messages/index.js index a0a433fc4c..e705a1c921 100644 --- a/packages/svelte/scripts/process-messages/index.js +++ b/packages/svelte/scripts/process-messages/index.js @@ -1,3 +1,7 @@ +/** @import { Node } from 'esrap/languages/ts' */ +/** @import * as ESTree from 'estree' */ +/** @import { AST } from 'svelte/compiler' */ + // @ts-check import process from 'node:process'; import fs from 'node:fs'; @@ -98,15 +102,19 @@ function run() { .readFileSync(new URL(`./templates/${name}.js`, import.meta.url), 'utf-8') .replace(/\r\n/g, '\n'); - /** @type {import('acorn').Comment[]} */ + /** @type {AST.JSComment[]} */ const comments = []; - let ast = acorn.parse(source, { - ecmaVersion: 'latest', - sourceType: 'module', - locations: true, - onComment: comments - }); + let ast = /** @type {ESTree.Node} */ ( + /** @type {unknown} */ ( + acorn.parse(source, { + ecmaVersion: 'latest', + sourceType: 'module', + locations: true, + onComment: comments + }) + ) + ); comments.forEach((comment) => { if (comment.type === 'Block') { @@ -115,24 +123,28 @@ function run() { }); ast = walk(ast, null, { - // @ts-expect-error Identifier(node, context) { if (node.name === 'CODES') { - return { + /** @type {ESTree.ArrayExpression} */ + const array = { type: 'ArrayExpression', elements: Object.keys(messages[name]).map((code) => ({ type: 'Literal', value: code })) }; + + return array; } } }); + const body = /** @type {ESTree.Program} */ (ast).body; + const category = messages[name]; // find the `export function CODE` node - const index = ast.body.findIndex((node) => { + const index = body.findIndex((node) => { if ( node.type === 'ExportNamedDeclaration' && node.declaration && @@ -144,15 +156,15 @@ function run() { if (index === -1) throw new Error(`missing export function CODE in ${name}.js`); - const template_node = ast.body[index]; - ast.body.splice(index, 1); + const template_node = body[index]; + body.splice(index, 1); - const jsdoc = /** @type {import('acorn').Comment} */ ( - comments.findLast((comment) => comment.start < template_node.start) + const jsdoc = /** @type {AST.JSComment} */ ( + comments.findLast((comment) => comment.start < /** @type {number} */ (template_node.start)) ); const printed = esrap.print( - ast, + /** @type {Node} */ (ast), ts({ comments: comments.filter((comment) => comment !== jsdoc) }) @@ -177,7 +189,7 @@ function run() { }; }); - /** @type {import('estree').Expression} */ + /** @type {ESTree.Expression} */ let message = { type: 'Literal', value: '' }; let prev_vars; @@ -195,10 +207,10 @@ function run() { const parts = text.split(/(%\w+%)/); - /** @type {import('estree').Expression[]} */ + /** @type {ESTree.Expression[]} */ const expressions = []; - /** @type {import('estree').TemplateElement[]} */ + /** @type {ESTree.TemplateElement[]} */ const quasis = []; for (let i = 0; i < parts.length; i += 1) { @@ -218,7 +230,7 @@ function run() { } } - /** @type {import('estree').Expression} */ + /** @type {ESTree.Expression} */ const expression = { type: 'TemplateLiteral', expressions, @@ -246,92 +258,94 @@ function run() { prev_vars = vars; } - const clone = walk(/** @type {import('estree').Node} */ (template_node), null, { - FunctionDeclaration(node, context) { - if (node.id.name !== 'CODE') return; - - const params = []; + const clone = /** @type {ESTree.Statement} */ ( + walk(/** @type {ESTree.Node} */ (template_node), null, { + FunctionDeclaration(node, context) { + if (node.id.name !== 'CODE') return; - for (const param of node.params) { - if (param.type === 'Identifier' && param.name === 'PARAMETER') { - params.push(...vars.map((name) => ({ type: 'Identifier', name }))); - } else { - params.push(param); - } - } + const params = []; - return /** @type {import('estree').FunctionDeclaration} */ ({ - .../** @type {import('estree').FunctionDeclaration} */ (context.next()), - params, - id: { - ...node.id, - name: code + for (const param of node.params) { + if (param.type === 'Identifier' && param.name === 'PARAMETER') { + params.push(...vars.map((name) => ({ type: 'Identifier', name }))); + } else { + params.push(param); + } } - }); - }, - TemplateLiteral(node, context) { - /** @type {import('estree').TemplateElement} */ - let quasi = { - type: 'TemplateElement', - value: { - ...node.quasis[0].value - }, - tail: node.quasis[0].tail - }; - /** @type {import('estree').TemplateLiteral} */ - let out = { - type: 'TemplateLiteral', - quasis: [quasi], - expressions: [] - }; + return /** @type {ESTree.FunctionDeclaration} */ ({ + .../** @type {ESTree.FunctionDeclaration} */ (context.next()), + params, + id: { + ...node.id, + name: code + } + }); + }, + TemplateLiteral(node, context) { + /** @type {ESTree.TemplateElement} */ + let quasi = { + type: 'TemplateElement', + value: { + ...node.quasis[0].value + }, + tail: node.quasis[0].tail + }; - for (let i = 0; i < node.expressions.length; i += 1) { - const q = structuredClone(node.quasis[i + 1]); - const e = node.expressions[i]; + /** @type {ESTree.TemplateLiteral} */ + let out = { + type: 'TemplateLiteral', + quasis: [quasi], + expressions: [] + }; - if (e.type === 'Literal' && e.value === 'CODE') { - quasi.value.raw += code + q.value.raw; - continue; - } + for (let i = 0; i < node.expressions.length; i += 1) { + const q = structuredClone(node.quasis[i + 1]); + const e = node.expressions[i]; - if (e.type === 'Identifier' && e.name === 'MESSAGE') { - if (message.type === 'Literal') { - const str = /** @type {string} */ (message.value).replace(/(`|\${)/g, '\\$1'); - quasi.value.raw += str + q.value.raw; + if (e.type === 'Literal' && e.value === 'CODE') { + quasi.value.raw += code + q.value.raw; continue; } - if (message.type === 'TemplateLiteral') { - const m = structuredClone(message); - quasi.value.raw += m.quasis[0].value.raw; - out.quasis.push(...m.quasis.slice(1)); - out.expressions.push(...m.expressions); - quasi = m.quasis[m.quasis.length - 1]; - quasi.value.raw += q.value.raw; - continue; + if (e.type === 'Identifier' && e.name === 'MESSAGE') { + if (message.type === 'Literal') { + const str = /** @type {string} */ (message.value).replace(/(`|\${)/g, '\\$1'); + quasi.value.raw += str + q.value.raw; + continue; + } + + if (message.type === 'TemplateLiteral') { + const m = structuredClone(message); + quasi.value.raw += m.quasis[0].value.raw; + out.quasis.push(...m.quasis.slice(1)); + out.expressions.push(...m.expressions); + quasi = m.quasis[m.quasis.length - 1]; + quasi.value.raw += q.value.raw; + continue; + } } - } - out.quasis.push((quasi = q)); - out.expressions.push(/** @type {import('estree').Expression} */ (context.visit(e))); - } + out.quasis.push((quasi = q)); + out.expressions.push(/** @type {ESTree.Expression} */ (context.visit(e))); + } - return out; - }, - Literal(node) { - if (node.value === 'CODE') { - return { - type: 'Literal', - value: code - }; + return out; + }, + Literal(node) { + if (node.value === 'CODE') { + return { + type: 'Literal', + value: code + }; + } + }, + Identifier(node) { + if (node.name !== 'MESSAGE') return; + return message; } - }, - Identifier(node) { - if (node.name !== 'MESSAGE') return; - return message; - } - }); + }) + ); const jsdoc_clone = { ...jsdoc, @@ -363,12 +377,15 @@ function run() { .join('\n') }; - const block = esrap.print({ ...ast, body: [clone] }, ts({ comments: [jsdoc_clone] })).code; + const block = esrap.print( + // @ts-expect-error some bullshit + /** @type {ESTree.Program} */ ({ ...ast, body: [clone] }), + ts({ comments: [jsdoc_clone] }) + ).code; printed.code += `\n\n${block}`; - // @ts-expect-error - ast.body.push(clone); + body.push(clone); } fs.writeFileSync( diff --git a/packages/svelte/src/compiler/phases/1-parse/acorn.js b/packages/svelte/src/compiler/phases/1-parse/acorn.js index 6fd7fcaf4d..77ce4a461c 100644 --- a/packages/svelte/src/compiler/phases/1-parse/acorn.js +++ b/packages/svelte/src/compiler/phases/1-parse/acorn.js @@ -1,4 +1,5 @@ /** @import { Comment, Program } from 'estree' */ +/** @import { AST } from '#compiler' */ import * as acorn from 'acorn'; import { walk } from 'zimmerframe'; import { tsPlugin } from '@sveltejs/acorn-typescript'; @@ -14,7 +15,7 @@ const ParserWithTS = acorn.Parser.extend(tsPlugin()); /** * @param {string} source - * @param {Comment[]} comments + * @param {AST.JSComment[]} comments * @param {boolean} typescript * @param {boolean} [is_script] */ diff --git a/packages/svelte/src/compiler/phases/2-analyze/index.js b/packages/svelte/src/compiler/phases/2-analyze/index.js index 530089dd67..f2341182ba 100644 --- a/packages/svelte/src/compiler/phases/2-analyze/index.js +++ b/packages/svelte/src/compiler/phases/2-analyze/index.js @@ -237,7 +237,7 @@ const RESERVED = ['$$props', '$$restProps', '$$slots']; * @returns {Analysis} */ export function analyze_module(source, options) { - /** @type {Comment[]} */ + /** @type {AST.JSComment[]} */ const comments = []; const ast = parse(source, comments, false, false); diff --git a/packages/svelte/src/compiler/phases/3-transform/index.js b/packages/svelte/src/compiler/phases/3-transform/index.js index fd29ccb7d3..33665097ab 100644 --- a/packages/svelte/src/compiler/phases/3-transform/index.js +++ b/packages/svelte/src/compiler/phases/3-transform/index.js @@ -1,3 +1,4 @@ +/** @import { Node } from 'esrap/languages/ts' */ /** @import { ValidatedCompileOptions, CompileResult, ValidatedModuleCompileOptions } from '#compiler' */ /** @import { ComponentAnalysis, Analysis } from '../types' */ import { print } from 'esrap'; @@ -35,7 +36,7 @@ export function transform_component(analysis, source, options) { const js_source_name = get_source_name(options.filename, options.outputFilename, 'input.svelte'); - const js = print(program, ts({ comments: analysis.comments }), { + const js = print(/** @type {Node} */ (program), ts({ comments: analysis.comments }), { // include source content; makes it easier/more robust looking up the source map code // (else esrap does return null for source and sourceMapContent which may trip up tooling) sourceMapContent: source, @@ -94,8 +95,7 @@ export function transform_module(analysis, source, options) { ]; } - // @ts-expect-error - const js = print(program, ts({ comments: analysis.comments }), { + const js = print(/** @type {Node} */ (program), ts({ comments: analysis.comments }), { // include source content; makes it easier/more robust looking up the source map code // (else esrap does return null for source and sourceMapContent which may trip up tooling) sourceMapContent: source, diff --git a/packages/svelte/src/compiler/phases/types.d.ts b/packages/svelte/src/compiler/phases/types.d.ts index aeb6184724..a672a0ecdb 100644 --- a/packages/svelte/src/compiler/phases/types.d.ts +++ b/packages/svelte/src/compiler/phases/types.d.ts @@ -38,7 +38,7 @@ export interface Analysis { runes: boolean; immutable: boolean; tracing: boolean; - comments: Comment[]; + comments: AST.JSComment[]; classes: Map>; diff --git a/packages/svelte/src/compiler/print/index.js b/packages/svelte/src/compiler/print/index.js index afa3556632..d908cc1435 100644 --- a/packages/svelte/src/compiler/print/index.js +++ b/packages/svelte/src/compiler/print/index.js @@ -8,11 +8,13 @@ import { is_void } from '../../utils.js'; * @param {AST.SvelteNode} ast */ export function print(ast) { - // @ts-expect-error some bullshit - return esrap.print(ast, { - ...ts({ comments: ast.type === 'Root' ? ast.comments : [] }), - ...visitors - }); + return esrap.print( + ast, + /** @type {Visitors} */ ({ + ...ts({ comments: ast.type === 'Root' ? ast.comments : [] }), + ...visitors + }) + ); } /** diff --git a/packages/svelte/src/compiler/types/template.d.ts b/packages/svelte/src/compiler/types/template.d.ts index 390582c07c..c9841e9d4e 100644 --- a/packages/svelte/src/compiler/types/template.d.ts +++ b/packages/svelte/src/compiler/types/template.d.ts @@ -73,7 +73,7 @@ export namespace AST { /** The parsed `