print
Rich Harris 3 months ago
parent 132d84d0ad
commit 226b4ca746

@ -171,7 +171,7 @@
"axobject-query": "^4.1.0", "axobject-query": "^4.1.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"esm-env": "^1.2.1", "esm-env": "^1.2.1",
"esrap": "https://pkg.pr.new/sveltejs/esrap@2558e72", "esrap": "^2.0.0",
"is-reference": "^3.0.3", "is-reference": "^3.0.3",
"locate-character": "^3.0.0", "locate-character": "^3.0.0",
"magic-string": "^0.30.11", "magic-string": "^0.30.11",

@ -1,3 +1,7 @@
/** @import { Node } from 'esrap/languages/ts' */
/** @import * as ESTree from 'estree' */
/** @import { AST } from 'svelte/compiler' */
// @ts-check // @ts-check
import process from 'node:process'; import process from 'node:process';
import fs from 'node:fs'; import fs from 'node:fs';
@ -98,15 +102,19 @@ function run() {
.readFileSync(new URL(`./templates/${name}.js`, import.meta.url), 'utf-8') .readFileSync(new URL(`./templates/${name}.js`, import.meta.url), 'utf-8')
.replace(/\r\n/g, '\n'); .replace(/\r\n/g, '\n');
/** @type {import('acorn').Comment[]} */ /** @type {AST.JSComment[]} */
const comments = []; const comments = [];
let ast = acorn.parse(source, { let ast = /** @type {ESTree.Node} */ (
/** @type {unknown} */ (
acorn.parse(source, {
ecmaVersion: 'latest', ecmaVersion: 'latest',
sourceType: 'module', sourceType: 'module',
locations: true, locations: true,
onComment: comments onComment: comments
}); })
)
);
comments.forEach((comment) => { comments.forEach((comment) => {
if (comment.type === 'Block') { if (comment.type === 'Block') {
@ -115,24 +123,28 @@ function run() {
}); });
ast = walk(ast, null, { ast = walk(ast, null, {
// @ts-expect-error
Identifier(node, context) { Identifier(node, context) {
if (node.name === 'CODES') { if (node.name === 'CODES') {
return { /** @type {ESTree.ArrayExpression} */
const array = {
type: 'ArrayExpression', type: 'ArrayExpression',
elements: Object.keys(messages[name]).map((code) => ({ elements: Object.keys(messages[name]).map((code) => ({
type: 'Literal', type: 'Literal',
value: code value: code
})) }))
}; };
return array;
} }
} }
}); });
const body = /** @type {ESTree.Program} */ (ast).body;
const category = messages[name]; const category = messages[name];
// find the `export function CODE` node // find the `export function CODE` node
const index = ast.body.findIndex((node) => { const index = body.findIndex((node) => {
if ( if (
node.type === 'ExportNamedDeclaration' && node.type === 'ExportNamedDeclaration' &&
node.declaration && node.declaration &&
@ -144,15 +156,15 @@ function run() {
if (index === -1) throw new Error(`missing export function CODE in ${name}.js`); if (index === -1) throw new Error(`missing export function CODE in ${name}.js`);
const template_node = ast.body[index]; const template_node = body[index];
ast.body.splice(index, 1); body.splice(index, 1);
const jsdoc = /** @type {import('acorn').Comment} */ ( const jsdoc = /** @type {AST.JSComment} */ (
comments.findLast((comment) => comment.start < template_node.start) comments.findLast((comment) => comment.start < /** @type {number} */ (template_node.start))
); );
const printed = esrap.print( const printed = esrap.print(
ast, /** @type {Node} */ (ast),
ts({ ts({
comments: comments.filter((comment) => comment !== jsdoc) 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 message = { type: 'Literal', value: '' };
let prev_vars; let prev_vars;
@ -195,10 +207,10 @@ function run() {
const parts = text.split(/(%\w+%)/); const parts = text.split(/(%\w+%)/);
/** @type {import('estree').Expression[]} */ /** @type {ESTree.Expression[]} */
const expressions = []; const expressions = [];
/** @type {import('estree').TemplateElement[]} */ /** @type {ESTree.TemplateElement[]} */
const quasis = []; const quasis = [];
for (let i = 0; i < parts.length; i += 1) { for (let i = 0; i < parts.length; i += 1) {
@ -218,7 +230,7 @@ function run() {
} }
} }
/** @type {import('estree').Expression} */ /** @type {ESTree.Expression} */
const expression = { const expression = {
type: 'TemplateLiteral', type: 'TemplateLiteral',
expressions, expressions,
@ -246,7 +258,8 @@ function run() {
prev_vars = vars; prev_vars = vars;
} }
const clone = walk(/** @type {import('estree').Node} */ (template_node), null, { const clone = /** @type {ESTree.Statement} */ (
walk(/** @type {ESTree.Node} */ (template_node), null, {
FunctionDeclaration(node, context) { FunctionDeclaration(node, context) {
if (node.id.name !== 'CODE') return; if (node.id.name !== 'CODE') return;
@ -260,8 +273,8 @@ function run() {
} }
} }
return /** @type {import('estree').FunctionDeclaration} */ ({ return /** @type {ESTree.FunctionDeclaration} */ ({
.../** @type {import('estree').FunctionDeclaration} */ (context.next()), .../** @type {ESTree.FunctionDeclaration} */ (context.next()),
params, params,
id: { id: {
...node.id, ...node.id,
@ -270,7 +283,7 @@ function run() {
}); });
}, },
TemplateLiteral(node, context) { TemplateLiteral(node, context) {
/** @type {import('estree').TemplateElement} */ /** @type {ESTree.TemplateElement} */
let quasi = { let quasi = {
type: 'TemplateElement', type: 'TemplateElement',
value: { value: {
@ -279,7 +292,7 @@ function run() {
tail: node.quasis[0].tail tail: node.quasis[0].tail
}; };
/** @type {import('estree').TemplateLiteral} */ /** @type {ESTree.TemplateLiteral} */
let out = { let out = {
type: 'TemplateLiteral', type: 'TemplateLiteral',
quasis: [quasi], quasis: [quasi],
@ -314,7 +327,7 @@ function run() {
} }
out.quasis.push((quasi = q)); out.quasis.push((quasi = q));
out.expressions.push(/** @type {import('estree').Expression} */ (context.visit(e))); out.expressions.push(/** @type {ESTree.Expression} */ (context.visit(e)));
} }
return out; return out;
@ -331,7 +344,8 @@ function run() {
if (node.name !== 'MESSAGE') return; if (node.name !== 'MESSAGE') return;
return message; return message;
} }
}); })
);
const jsdoc_clone = { const jsdoc_clone = {
...jsdoc, ...jsdoc,
@ -363,12 +377,15 @@ function run() {
.join('\n') .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}`; printed.code += `\n\n${block}`;
// @ts-expect-error body.push(clone);
ast.body.push(clone);
} }
fs.writeFileSync( fs.writeFileSync(

@ -1,4 +1,5 @@
/** @import { Comment, Program } from 'estree' */ /** @import { Comment, Program } from 'estree' */
/** @import { AST } from '#compiler' */
import * as acorn from 'acorn'; import * as acorn from 'acorn';
import { walk } from 'zimmerframe'; import { walk } from 'zimmerframe';
import { tsPlugin } from '@sveltejs/acorn-typescript'; import { tsPlugin } from '@sveltejs/acorn-typescript';
@ -14,7 +15,7 @@ const ParserWithTS = acorn.Parser.extend(tsPlugin());
/** /**
* @param {string} source * @param {string} source
* @param {Comment[]} comments * @param {AST.JSComment[]} comments
* @param {boolean} typescript * @param {boolean} typescript
* @param {boolean} [is_script] * @param {boolean} [is_script]
*/ */

@ -237,7 +237,7 @@ const RESERVED = ['$$props', '$$restProps', '$$slots'];
* @returns {Analysis} * @returns {Analysis}
*/ */
export function analyze_module(source, options) { export function analyze_module(source, options) {
/** @type {Comment[]} */ /** @type {AST.JSComment[]} */
const comments = []; const comments = [];
const ast = parse(source, comments, false, false); const ast = parse(source, comments, false, false);

@ -1,3 +1,4 @@
/** @import { Node } from 'esrap/languages/ts' */
/** @import { ValidatedCompileOptions, CompileResult, ValidatedModuleCompileOptions } from '#compiler' */ /** @import { ValidatedCompileOptions, CompileResult, ValidatedModuleCompileOptions } from '#compiler' */
/** @import { ComponentAnalysis, Analysis } from '../types' */ /** @import { ComponentAnalysis, Analysis } from '../types' */
import { print } from 'esrap'; 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_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 // 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) // (else esrap does return null for source and sourceMapContent which may trip up tooling)
sourceMapContent: source, sourceMapContent: source,
@ -94,8 +95,7 @@ export function transform_module(analysis, source, options) {
]; ];
} }
// @ts-expect-error const js = print(/** @type {Node} */ (program), ts({ comments: analysis.comments }), {
const js = print(program, ts({ comments: analysis.comments }), {
// include source content; makes it easier/more robust looking up the source map code // 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) // (else esrap does return null for source and sourceMapContent which may trip up tooling)
sourceMapContent: source, sourceMapContent: source,

@ -38,7 +38,7 @@ export interface Analysis {
runes: boolean; runes: boolean;
immutable: boolean; immutable: boolean;
tracing: boolean; tracing: boolean;
comments: Comment[]; comments: AST.JSComment[];
classes: Map<ClassBody, Map<string, StateField>>; classes: Map<ClassBody, Map<string, StateField>>;

@ -8,11 +8,13 @@ import { is_void } from '../../utils.js';
* @param {AST.SvelteNode} ast * @param {AST.SvelteNode} ast
*/ */
export function print(ast) { export function print(ast) {
// @ts-expect-error some bullshit return esrap.print(
return esrap.print(ast, { ast,
/** @type {Visitors<AST.SvelteNode>} */ ({
...ts({ comments: ast.type === 'Root' ? ast.comments : [] }), ...ts({ comments: ast.type === 'Root' ? ast.comments : [] }),
...visitors ...visitors
}); })
);
} }
/** /**

@ -73,7 +73,7 @@ export namespace AST {
/** The parsed `<script module>` element, if exists */ /** The parsed `<script module>` element, if exists */
module: Script | null; module: Script | null;
/** Comments found in <script> and {expressions} */ /** Comments found in <script> and {expressions} */
comments: Array<import('estree').Comment & { start: number; end: number }>; comments: JSComment[];
/** @internal */ /** @internal */
metadata: { metadata: {
/** Whether the component was parsed with typescript */ /** Whether the component was parsed with typescript */
@ -539,6 +539,17 @@ export namespace AST {
attributes: Attribute[]; attributes: Attribute[];
} }
export interface JSComment {
type: 'Line' | 'Block';
value: string;
start: number;
end: number;
loc: {
start: { line: number; column: number };
end: { line: number; column: number };
};
}
export type AttributeLike = Attribute | SpreadAttribute | Directive; export type AttributeLike = Attribute | SpreadAttribute | Directive;
export type Directive = export type Directive =

@ -398,10 +398,13 @@ export function merge_with_preprocessor_map(result, options, source_name) {
// map may contain a different file name. Patch our map beforehand to align sources so merging // map may contain a different file name. Patch our map beforehand to align sources so merging
// with the preprocessor map works correctly. // with the preprocessor map works correctly.
result.map.sources = [file_basename]; result.map.sources = [file_basename];
result.map = apply_preprocessor_sourcemap( Object.assign(
result.map,
apply_preprocessor_sourcemap(
file_basename, file_basename,
result.map, result.map,
/** @type {any} */ (options.sourcemap) /** @type {any} */ (options.sourcemap)
)
); );
// After applying the preprocessor map, we need to do the inverse and make the sources // After applying the preprocessor map, we need to do the inverse and make the sources
// relative to the input file again in case the output code is in a different directory. // relative to the input file again in case the output code is in a different directory.

@ -1120,6 +1120,8 @@ declare module 'svelte/compiler' {
instance: Script | null; instance: Script | null;
/** The parsed `<script module>` element, if exists */ /** The parsed `<script module>` element, if exists */
module: Script | null; module: Script | null;
/** Comments found in <script> and {expressions} */
comments: Array<import('estree').Comment & { start: number; end: number }>;
} }
export interface SvelteOptions { export interface SvelteOptions {
@ -1437,6 +1439,17 @@ declare module 'svelte/compiler' {
attributes: Attribute[]; attributes: Attribute[];
} }
export interface JSComment {
type: 'Line' | 'Block';
value: string;
start: number;
end: number;
loc: {
start: { line: number; column: number };
end: { line: number; column: number };
};
}
export type AttributeLike = Attribute | SpreadAttribute | Directive; export type AttributeLike = Attribute | SpreadAttribute | Directive;
export type Directive = export type Directive =

@ -4,9 +4,16 @@ settings:
autoInstallPeers: true autoInstallPeers: true
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
overrides:
esrap: link:../../esrap
importers: importers:
.: .:
dependencies:
esrap:
specifier: link:../../esrap
version: link:../../esrap
devDependencies: devDependencies:
'@changesets/cli': '@changesets/cli':
specifier: ^2.27.8 specifier: ^2.27.8
@ -87,8 +94,8 @@ importers:
specifier: ^1.2.1 specifier: ^1.2.1
version: 1.2.1 version: 1.2.1
esrap: esrap:
specifier: https://pkg.pr.new/sveltejs/esrap@2558e72 specifier: ^2.0.0
version: https://pkg.pr.new/sveltejs/esrap@2558e72 version: 2.0.0
is-reference: is-reference:
specifier: ^3.0.3 specifier: ^3.0.3
version: 3.0.3 version: 3.0.3
@ -1261,9 +1268,8 @@ packages:
resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
engines: {node: '>=0.10'} engines: {node: '>=0.10'}
esrap@https://pkg.pr.new/sveltejs/esrap@2558e72: esrap@2.0.0:
resolution: {tarball: https://pkg.pr.new/sveltejs/esrap@2558e72} resolution: {integrity: sha512-zhw1TDqno99Ld5wOpe0t47rzVyxfGc1fvxNzPsqk4idUf5dcAePkAyfTceLJaSTytjiWDu26S5tI+grjvymXJA==}
version: 1.4.9
esrecurse@4.3.0: esrecurse@4.3.0:
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
@ -3623,7 +3629,7 @@ snapshots:
dependencies: dependencies:
estraverse: 5.3.0 estraverse: 5.3.0
esrap@https://pkg.pr.new/sveltejs/esrap@2558e72: esrap@2.0.0:
dependencies: dependencies:
'@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/sourcemap-codec': 1.5.0

Loading…
Cancel
Save