Rich Harris 3 months ago
parent a0f86c5e1b
commit 486d10c880

@ -4,6 +4,7 @@ import fs from 'node:fs';
import * as acorn from 'acorn';
import { walk } from 'zimmerframe';
import * as esrap from 'esrap';
import ts from 'esrap/languages/ts';
const DIR = '../../documentation/docs/98-reference/.generated';
@ -98,55 +99,18 @@ function run() {
.replace(/\r\n/g, '\n');
/**
* @type {Array<{
* type: string;
* value: string;
* start: number;
* end: number
* }>}
* @type {any[]}
*/
const comments = [];
let ast = acorn.parse(source, {
ecmaVersion: 'latest',
sourceType: 'module',
onComment: (block, value, start, end) => {
if (block && /\n/.test(value)) {
let a = start;
while (a > 0 && source[a - 1] !== '\n') a -= 1;
let b = a;
while (/[ \t]/.test(source[b])) b += 1;
const indentation = source.slice(a, b);
value = value.replace(new RegExp(`^${indentation}`, 'gm'), '');
}
comments.push({ type: block ? 'Block' : 'Line', value, start, end });
}
locations: true,
onComment: comments
});
ast = walk(ast, null, {
_(node, { next }) {
let comment;
while (comments[0] && comments[0].start < node.start) {
comment = comments.shift();
// @ts-expect-error
(node.leadingComments ||= []).push(comment);
}
next();
if (comments[0]) {
const slice = source.slice(node.end, comments[0].start);
if (/^[,) \t]*$/.test(slice)) {
// @ts-expect-error
node.trailingComments = [comments.shift()];
}
}
},
// @ts-expect-error
Identifier(node, context) {
if (node.name === 'CODES') {
@ -161,11 +125,6 @@ function run() {
}
});
if (comments.length > 0) {
// @ts-expect-error
(ast.trailingComments ||= []).push(...comments);
}
const category = messages[name];
// find the `export function CODE` node
@ -184,6 +143,16 @@ function run() {
const template_node = ast.body[index];
ast.body.splice(index, 1);
const jsdoc = comments.findLast((comment) => comment.start < template_node.start);
const printed = esrap.print(
ast,
// @ts-expect-error
ts({
comments: comments.filter((comment) => comment !== jsdoc)
})
);
for (const code in category) {
const { messages } = category[code];
/** @type {string[]} */
@ -273,41 +242,6 @@ function run() {
}
const clone = walk(/** @type {import('estree').Node} */ (template_node), null, {
// @ts-expect-error Block is a block comment, which is not recognised
Block(node, context) {
if (!node.value.includes('PARAMETER')) return;
const value = /** @type {string} */ (node.value)
.split('\n')
.map((line) => {
if (line === ' * MESSAGE') {
return messages[messages.length - 1]
.split('\n')
.map((line) => ` * ${line}`)
.join('\n');
}
if (line.includes('PARAMETER')) {
return vars
.map((name, i) => {
const optional = i >= group[0].vars.length;
return optional
? ` * @param {string | undefined | null} [${name}]`
: ` * @param {string} ${name}`;
})
.join('\n');
}
return line;
})
.filter((x) => x !== '')
.join('\n');
if (value !== node.value) {
return { ...node, value };
}
},
FunctionDeclaration(node, context) {
if (node.id.name !== 'CODE') return;
@ -394,16 +328,49 @@ function run() {
}
});
const jsdoc_clone = {
...jsdoc,
value: /** @type {string} */ (jsdoc.value)
.split('\n')
.map((line) => {
if (line === ' * MESSAGE') {
return messages[messages.length - 1]
.split('\n')
.map((line) => ` * ${line}`)
.join('\n');
}
if (line.includes('PARAMETER')) {
return vars
.map((name, i) => {
const optional = i >= group[0].vars.length;
return optional
? ` * @param {string | undefined | null} [${name}]`
: ` * @param {string} ${name}`;
})
.join('\n');
}
return line;
})
.filter((x) => x !== '')
.join('\n')
};
// @ts-expect-error
const block = esrap.print({ ...ast, body: [clone] }, ts({ comments: [jsdoc_clone] })).code;
printed.code += `\n\n${block}`;
// @ts-expect-error
ast.body.push(clone);
}
const module = esrap.print(ast);
fs.writeFileSync(
dest,
`/* This file is generated by scripts/process-messages/index.js. Do not edit! */\n\n` +
module.code,
printed.code,
'utf-8'
);
}

@ -4,7 +4,9 @@ import { CompileDiagnostic } from './utils/compile_diagnostic.js';
/** @typedef {{ start?: number, end?: number }} NodeLike */
class InternalCompileError extends Error {
message = ''; // ensure this property is enumerable
message = '';
// ensure this property is enumerable
#diagnostic;
/**
@ -15,10 +17,12 @@ class InternalCompileError extends Error {
constructor(code, message, position) {
super(message);
this.stack = ''; // avoid unnecessary noise; don't set it as a class property or it becomes enumerable
// We want to extend from Error so that various bundler plugins properly handle it.
// But we also want to share the same object shape with that of warnings, therefore
// we create an instance of the shared class an copy over its properties.
this.#diagnostic = new CompileDiagnostic(code, message, position);
Object.assign(this, this.#diagnostic);
this.name = 'CompileError';
}
@ -816,7 +820,9 @@ export function bind_invalid_expression(node) {
* @returns {never}
*/
export function bind_invalid_name(node, name, explanation) {
e(node, 'bind_invalid_name', `${explanation ? `\`bind:${name}\` is not a valid binding. ${explanation}` : `\`bind:${name}\` is not a valid binding`}\nhttps://svelte.dev/e/bind_invalid_name`);
e(node, 'bind_invalid_name', `${explanation
? `\`bind:${name}\` is not a valid binding. ${explanation}`
: `\`bind:${name}\` is not a valid binding`}\nhttps://svelte.dev/e/bind_invalid_name`);
}
/**

@ -11,6 +11,7 @@ import { transform_component, transform_module } from './phases/3-transform/inde
import { validate_component_options, validate_module_options } from './validate-options.js';
import * as state from './state.js';
export { default as preprocess } from './preprocess/index.js';
export { print } from './print/index.js';
/**
* `compile` converts your `.svelte` source code into a JavaScript module that exports a component

@ -451,6 +451,7 @@ export function convert(source, ast) {
SpreadAttribute(node) {
return { ...node, type: 'Spread' };
},
// @ts-ignore
StyleSheet(node, context) {
return {
...node,

@ -1,6 +1,7 @@
/** @import { ValidatedCompileOptions, CompileResult, ValidatedModuleCompileOptions } from '#compiler' */
/** @import { ComponentAnalysis, Analysis } from '../types' */
import { print } from 'esrap';
import ts from 'esrap/languages/ts';
import { VERSION } from '../../../version.js';
import { server_component, server_module } from './server/transform-server.js';
import { client_component, client_module } from './client/transform-client.js';
@ -34,7 +35,8 @@ 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-ignore TODO
const js = print(program, ts(), {
// 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,7 +96,8 @@ export function transform_module(analysis, source, options) {
}
return {
js: print(program, {
// @ts-expect-error
js: print(program, ts(), {
// 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,

@ -0,0 +1,133 @@
/** @import { AST } from '#compiler'; */
/** @import { Visitors } from 'esrap' */
import * as esrap from 'esrap';
import ts from 'esrap/languages/ts';
/**
* @param {AST.SvelteNode} ast
*/
export function print(ast) {
// @ts-expect-error some bullshit
return esrap.print(ast, {
...ts(),
...visitors
});
}
/** @type {Visitors<AST.SvelteNode>} */
const visitors = {
Root(node, context) {
if (node.options) {
throw new Error('TODO');
}
for (const item of [node.module, node.instance, node.fragment, node.css]) {
if (!item) continue;
context.margin();
context.newline();
context.visit(item);
}
},
Script(node, context) {
context.write('<script');
if (node.context === 'module') {
context.write(' module');
}
for (const attribute of node.attributes) {
context.write(' ');
context.visit(attribute);
}
context.write('>');
context.indent();
context.newline();
context.visit(node.content);
context.dedent();
context.newline();
context.write('</script>');
},
Fragment(node, context) {
for (let i = 0; i < node.nodes.length; i += 1) {
const child = node.nodes[i];
if (child.type === 'Text') {
let data = child.data;
if (i === 0) data = data.trimStart();
if (i === node.nodes.length - 1) data = data.trimEnd();
context.write(data);
} else {
context.visit(child);
}
}
},
Attribute(node, context) {
context.write(node.name);
if (node.value === true) return;
context.write('=');
if (Array.isArray(node.value)) {
if (node.value.length > 1) {
context.write('"');
}
for (const chunk of node.value) {
context.visit(chunk);
}
if (node.value.length > 1) {
context.write('"');
}
} else {
context.visit(node.value);
}
},
Text(node, context) {
context.write(node.data);
},
ExpressionTag(node, context) {
context.write('{');
context.visit(node.expression);
context.write('}');
},
IfBlock(node, context) {
context.write('{#if ');
context.visit(node.test);
context.write('}');
context.visit(node.consequent);
// TODO handle alternate/else if
context.write('{/if}');
},
RegularElement(node, context) {
context.write('<' + node.name);
for (const attribute of node.attributes) {
// TODO handle multiline
context.write(' ');
context.visit(attribute);
}
context.write('>');
// TODO handle void elements
if (node.fragment) {
context.visit(node.fragment);
}
context.write(`</${node.name}>`);
},
TransitionDirective(node, context) {
// TODO
}
};

@ -572,7 +572,7 @@ export namespace AST {
| AST.Comment
| Block;
export type SvelteNode = Node | TemplateNode | AST.Fragment | _CSS.Node;
export type SvelteNode = Node | TemplateNode | AST.Fragment | _CSS.Node | Script;
export type { _CSS as CSS };
}

@ -1,12 +1,6 @@
/* This file is generated by scripts/process-messages/index.js. Do not edit! */
import {
warnings,
ignore_stack,
ignore_map,
warning_filter
} from './state.js';
import { warnings, ignore_stack, ignore_map, warning_filter } from './state.js';
import { CompileDiagnostic } from './utils/compile_diagnostic.js';
/** @typedef {{ start?: number, end?: number }} NodeLike */
@ -40,6 +34,7 @@ function w(node, code, message) {
const warning = new InternalCompileWarning(code, message, node && node.start !== undefined ? [node.start, node.end ?? node.start] : undefined);
if (!warning_filter(warning)) return;
warnings.push(warning);
}
@ -496,7 +491,9 @@ export function a11y_role_supports_aria_props_implicit(node, attribute, role, na
* @param {string | undefined | null} [suggestion]
*/
export function a11y_unknown_aria_attribute(node, attribute, suggestion) {
w(node, 'a11y_unknown_aria_attribute', `${suggestion ? `Unknown aria attribute 'aria-${attribute}'. Did you mean '${suggestion}'?` : `Unknown aria attribute 'aria-${attribute}'`}\nhttps://svelte.dev/e/a11y_unknown_aria_attribute`);
w(node, 'a11y_unknown_aria_attribute', `${suggestion
? `Unknown aria attribute 'aria-${attribute}'. Did you mean '${suggestion}'?`
: `Unknown aria attribute 'aria-${attribute}'`}\nhttps://svelte.dev/e/a11y_unknown_aria_attribute`);
}
/**
@ -506,7 +503,9 @@ export function a11y_unknown_aria_attribute(node, attribute, suggestion) {
* @param {string | undefined | null} [suggestion]
*/
export function a11y_unknown_role(node, role, suggestion) {
w(node, 'a11y_unknown_role', `${suggestion ? `Unknown role '${role}'. Did you mean '${suggestion}'?` : `Unknown role '${role}'`}\nhttps://svelte.dev/e/a11y_unknown_role`);
w(node, 'a11y_unknown_role', `${suggestion
? `Unknown role '${role}'. Did you mean '${suggestion}'?`
: `Unknown role '${role}'`}\nhttps://svelte.dev/e/a11y_unknown_role`);
}
/**
@ -534,7 +533,9 @@ export function legacy_code(node, code, suggestion) {
* @param {string | undefined | null} [suggestion]
*/
export function unknown_code(node, code, suggestion) {
w(node, 'unknown_code', `${suggestion ? `\`${code}\` is not a recognised code (did you mean \`${suggestion}\`?)` : `\`${code}\` is not a recognised code`}\nhttps://svelte.dev/e/unknown_code`);
w(node, 'unknown_code', `${suggestion
? `\`${code}\` is not a recognised code (did you mean \`${suggestion}\`?)`
: `\`${code}\` is not a recognised code`}\nhttps://svelte.dev/e/unknown_code`);
}
/**

@ -11,6 +11,7 @@ export function bind_invalid_checkbox_value() {
const error = new Error(`bind_invalid_checkbox_value\nUsing \`bind:value\` together with a checkbox input is not allowed. Use \`bind:checked\` instead\nhttps://svelte.dev/e/bind_invalid_checkbox_value`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/bind_invalid_checkbox_value`);
@ -29,6 +30,7 @@ export function bind_invalid_export(component, key, name) {
const error = new Error(`bind_invalid_export\nComponent ${component} has an export named \`${key}\` that a consumer component is trying to access using \`bind:${key}\`, which is disallowed. Instead, use \`bind:this\` (e.g. \`<${name} bind:this={component} />\`) and then access the property on the bound component instance (e.g. \`component.${key}\`)\nhttps://svelte.dev/e/bind_invalid_export`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/bind_invalid_export`);
@ -47,6 +49,7 @@ export function bind_not_bindable(key, component, name) {
const error = new Error(`bind_not_bindable\nA component is attempting to bind to a non-bindable property \`${key}\` belonging to ${component} (i.e. \`<${name} bind:${key}={...}>\`). To mark a property as bindable: \`let { ${key} = $bindable() } = $props()\`\nhttps://svelte.dev/e/bind_not_bindable`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/bind_not_bindable`);
@ -64,6 +67,7 @@ export function component_api_changed(method, component) {
const error = new Error(`component_api_changed\nCalling \`${method}\` on a component instance (of ${component}) is no longer valid in Svelte 5\nhttps://svelte.dev/e/component_api_changed`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/component_api_changed`);
@ -81,6 +85,7 @@ export function component_api_invalid_new(component, name) {
const error = new Error(`component_api_invalid_new\nAttempted to instantiate ${component} with \`new ${name}\`, which is no longer valid in Svelte 5. If this component is not under your control, set the \`compatibility.componentApi\` compiler option to \`4\` to keep it working.\nhttps://svelte.dev/e/component_api_invalid_new`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/component_api_invalid_new`);
@ -96,6 +101,7 @@ export function derived_references_self() {
const error = new Error(`derived_references_self\nA derived value cannot reference itself recursively\nhttps://svelte.dev/e/derived_references_self`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/derived_references_self`);
@ -111,9 +117,12 @@ export function derived_references_self() {
*/
export function each_key_duplicate(a, b, value) {
if (DEV) {
const error = new Error(`each_key_duplicate\n${value ? `Keyed each block has duplicate key \`${value}\` at indexes ${a} and ${b}` : `Keyed each block has duplicate key at indexes ${a} and ${b}`}\nhttps://svelte.dev/e/each_key_duplicate`);
const error = new Error(`each_key_duplicate\n${value
? `Keyed each block has duplicate key \`${value}\` at indexes ${a} and ${b}`
: `Keyed each block has duplicate key at indexes ${a} and ${b}`}\nhttps://svelte.dev/e/each_key_duplicate`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/each_key_duplicate`);
@ -130,6 +139,7 @@ export function effect_in_teardown(rune) {
const error = new Error(`effect_in_teardown\n\`${rune}\` cannot be used inside an effect cleanup function\nhttps://svelte.dev/e/effect_in_teardown`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/effect_in_teardown`);
@ -145,6 +155,7 @@ export function effect_in_unowned_derived() {
const error = new Error(`effect_in_unowned_derived\nEffect cannot be created inside a \`$derived\` value that was not itself created inside an effect\nhttps://svelte.dev/e/effect_in_unowned_derived`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/effect_in_unowned_derived`);
@ -161,6 +172,7 @@ export function effect_orphan(rune) {
const error = new Error(`effect_orphan\n\`${rune}\` can only be used inside an effect (e.g. during component initialisation)\nhttps://svelte.dev/e/effect_orphan`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/effect_orphan`);
@ -176,6 +188,7 @@ export function effect_update_depth_exceeded() {
const error = new Error(`effect_update_depth_exceeded\nMaximum update depth exceeded. This can happen when a reactive block or effect repeatedly sets a new value. Svelte limits the number of nested updates to prevent infinite loops\nhttps://svelte.dev/e/effect_update_depth_exceeded`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/effect_update_depth_exceeded`);
@ -191,6 +204,7 @@ export function hydration_failed() {
const error = new Error(`hydration_failed\nFailed to hydrate the application\nhttps://svelte.dev/e/hydration_failed`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/hydration_failed`);
@ -206,6 +220,7 @@ export function invalid_snippet() {
const error = new Error(`invalid_snippet\nCould not \`{@render}\` snippet due to the expression being \`null\` or \`undefined\`. Consider using optional chaining \`{@render snippet?.()}\`\nhttps://svelte.dev/e/invalid_snippet`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/invalid_snippet`);
@ -222,6 +237,7 @@ export function lifecycle_legacy_only(name) {
const error = new Error(`lifecycle_legacy_only\n\`${name}(...)\` cannot be used in runes mode\nhttps://svelte.dev/e/lifecycle_legacy_only`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/lifecycle_legacy_only`);
@ -238,6 +254,7 @@ export function props_invalid_value(key) {
const error = new Error(`props_invalid_value\nCannot do \`bind:${key}={undefined}\` when \`${key}\` has a fallback value\nhttps://svelte.dev/e/props_invalid_value`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/props_invalid_value`);
@ -254,6 +271,7 @@ export function props_rest_readonly(property) {
const error = new Error(`props_rest_readonly\nRest element properties of \`$props()\` such as \`${property}\` are readonly\nhttps://svelte.dev/e/props_rest_readonly`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/props_rest_readonly`);
@ -270,6 +288,7 @@ export function rune_outside_svelte(rune) {
const error = new Error(`rune_outside_svelte\nThe \`${rune}\` rune is only available inside \`.svelte\` and \`.svelte.js/ts\` files\nhttps://svelte.dev/e/rune_outside_svelte`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/rune_outside_svelte`);
@ -285,6 +304,7 @@ export function state_descriptors_fixed() {
const error = new Error(`state_descriptors_fixed\nProperty descriptors defined on \`$state\` objects must contain \`value\` and always be \`enumerable\`, \`configurable\` and \`writable\`.\nhttps://svelte.dev/e/state_descriptors_fixed`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/state_descriptors_fixed`);
@ -300,6 +320,7 @@ export function state_prototype_fixed() {
const error = new Error(`state_prototype_fixed\nCannot set prototype of \`$state\` object\nhttps://svelte.dev/e/state_prototype_fixed`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/state_prototype_fixed`);
@ -315,6 +336,7 @@ export function state_unsafe_mutation() {
const error = new Error(`state_unsafe_mutation\nUpdating state inside a derived or a template expression is forbidden. If the value should not be reactive, declare it without \`$state\`\nhttps://svelte.dev/e/state_unsafe_mutation`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/state_unsafe_mutation`);

@ -25,7 +25,13 @@ export function assignment_value_stale(property, location) {
*/
export function binding_property_non_reactive(binding, location) {
if (DEV) {
console.warn(`%c[svelte] binding_property_non_reactive\n%c${location ? `\`${binding}\` (${location}) is binding to a non-reactive property` : `\`${binding}\` is binding to a non-reactive property`}\nhttps://svelte.dev/e/binding_property_non_reactive`, bold, normal);
console.warn(
`%c[svelte] binding_property_non_reactive\n%c${location
? `\`${binding}\` (${location}) is binding to a non-reactive property`
: `\`${binding}\` is binding to a non-reactive property`}\nhttps://svelte.dev/e/binding_property_non_reactive`,
bold,
normal
);
} else {
console.warn(`https://svelte.dev/e/binding_property_non_reactive`);
}
@ -76,7 +82,13 @@ export function hydration_attribute_changed(attribute, html, value) {
*/
export function hydration_html_changed(location) {
if (DEV) {
console.warn(`%c[svelte] hydration_html_changed\n%c${location ? `The value of an \`{@html ...}\` block ${location} changed between server and client renders. The client value will be ignored in favour of the server value` : 'The value of an `{@html ...}` block changed between server and client renders. The client value will be ignored in favour of the server value'}\nhttps://svelte.dev/e/hydration_html_changed`, bold, normal);
console.warn(
`%c[svelte] hydration_html_changed\n%c${location
? `The value of an \`{@html ...}\` block ${location} changed between server and client renders. The client value will be ignored in favour of the server value`
: 'The value of an `{@html ...}` block changed between server and client renders. The client value will be ignored in favour of the server value'}\nhttps://svelte.dev/e/hydration_html_changed`,
bold,
normal
);
} else {
console.warn(`https://svelte.dev/e/hydration_html_changed`);
}
@ -88,7 +100,13 @@ export function hydration_html_changed(location) {
*/
export function hydration_mismatch(location) {
if (DEV) {
console.warn(`%c[svelte] hydration_mismatch\n%c${location ? `Hydration failed because the initial UI does not match what was rendered on the server. The error occurred near ${location}` : 'Hydration failed because the initial UI does not match what was rendered on the server'}\nhttps://svelte.dev/e/hydration_mismatch`, bold, normal);
console.warn(
`%c[svelte] hydration_mismatch\n%c${location
? `Hydration failed because the initial UI does not match what was rendered on the server. The error occurred near ${location}`
: 'Hydration failed because the initial UI does not match what was rendered on the server'}\nhttps://svelte.dev/e/hydration_mismatch`,
bold,
normal
);
} else {
console.warn(`https://svelte.dev/e/hydration_mismatch`);
}

@ -1,5 +1,7 @@
/* This file is generated by scripts/process-messages/index.js. Do not edit! */
/**
* `%name%(...)` is not available on the server
* @param {string} name
@ -9,5 +11,6 @@ export function lifecycle_function_unavailable(name) {
const error = new Error(`lifecycle_function_unavailable\n\`${name}(...)\` is not available on the server\nhttps://svelte.dev/e/lifecycle_function_unavailable`);
error.name = 'Svelte error';
throw error;
}

@ -11,6 +11,7 @@ export function invalid_default_snippet() {
const error = new Error(`invalid_default_snippet\nCannot use \`{@render children(...)}\` if the parent component uses \`let:\` directives. Consider using a named snippet instead\nhttps://svelte.dev/e/invalid_default_snippet`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/invalid_default_snippet`);
@ -26,6 +27,7 @@ export function invalid_snippet_arguments() {
const error = new Error(`invalid_snippet_arguments\nA snippet function was passed invalid arguments. Snippets should only be instantiated via \`{@render ...}\`\nhttps://svelte.dev/e/invalid_snippet_arguments`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/invalid_snippet_arguments`);
@ -42,6 +44,7 @@ export function lifecycle_outside_component(name) {
const error = new Error(`lifecycle_outside_component\n\`${name}(...)\` can only be used during component initialisation\nhttps://svelte.dev/e/lifecycle_outside_component`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/lifecycle_outside_component`);
@ -57,6 +60,7 @@ export function snippet_without_render_tag() {
const error = new Error(`snippet_without_render_tag\nAttempted to render a snippet without a \`{@render}\` block. This would cause the snippet code to be stringified instead of its content being rendered to the DOM. To fix this, change \`{snippet}\` to \`{@render snippet()}\`.\nhttps://svelte.dev/e/snippet_without_render_tag`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/snippet_without_render_tag`);
@ -73,6 +77,7 @@ export function store_invalid_shape(name) {
const error = new Error(`store_invalid_shape\n\`${name}\` is not a store with a \`subscribe\` method\nhttps://svelte.dev/e/store_invalid_shape`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/store_invalid_shape`);
@ -88,6 +93,7 @@ export function svelte_element_invalid_this_value() {
const error = new Error(`svelte_element_invalid_this_value\nThe \`this\` prop on \`<svelte:element>\` must be a string, if defined\nhttps://svelte.dev/e/svelte_element_invalid_this_value`);
error.name = 'Svelte error';
throw error;
} else {
throw new Error(`https://svelte.dev/e/svelte_element_invalid_this_value`);

@ -25,11 +25,15 @@ export function dynamic_void_element_content(tag) {
*/
export function state_snapshot_uncloneable(properties) {
if (DEV) {
console.warn(`%c[svelte] state_snapshot_uncloneable\n%c${properties
console.warn(
`%c[svelte] state_snapshot_uncloneable\n%c${properties
? `The following properties cannot be cloned with \`$state.snapshot\` — the return value contains the originals:
${properties}`
: 'Value cannot be cloned with `$state.snapshot` — the original value was returned'}\nhttps://svelte.dev/e/state_snapshot_uncloneable`, bold, normal);
: 'Value cannot be cloned with `$state.snapshot` — the original value was returned'}\nhttps://svelte.dev/e/state_snapshot_uncloneable`,
bold,
normal
);
} else {
console.warn(`https://svelte.dev/e/state_snapshot_uncloneable`);
}

@ -1,8 +1,9 @@
import * as fs from 'node:fs';
import { assert, it } from 'vitest';
import { parse } from 'svelte/compiler';
import { parse, print } from 'svelte/compiler';
import { try_load_json } from '../helpers.js';
import { suite, type BaseTest } from '../suite.js';
import { walk } from 'zimmerframe';
interface ParserTest extends BaseTest {}
@ -30,6 +31,28 @@ const { test, run } = suite<ParserTest>(async (config, cwd) => {
const expected = try_load_json(`${cwd}/output.json`);
assert.deepEqual(actual, expected);
}
const printed = print(actual);
const reparsed = JSON.parse(
JSON.stringify(
parse(printed.code, {
modern: true,
loose: cwd.split('/').pop()!.startsWith('loose-')
})
)
);
fs.writeFileSync(`${cwd}/_actual.svelte`, JSON.stringify(printed.code, null, '\t'));
const actual_cleaned = walk(actual, null, {
_(node, context) {}
});
const reparsed_cleaned = walk(actual, null, {
_(node, context) {}
});
assert.deepEqual(actual_cleaned, reparsed_cleaned);
});
export { test };

@ -1493,7 +1493,7 @@ declare module 'svelte/compiler' {
| AST.Comment
| Block;
export type SvelteNode = Node | TemplateNode | AST.Fragment | _CSS.Node;
export type SvelteNode = Node | TemplateNode | AST.Fragment | _CSS.Node | Script;
export type { _CSS as CSS };
}
@ -1505,6 +1505,10 @@ declare module 'svelte/compiler' {
export function preprocess(source: string, preprocessor: PreprocessorGroup | PreprocessorGroup[], options?: {
filename?: string;
} | undefined): Promise<Processed>;
export function print(ast: AST.SvelteNode): {
code: string;
map: any;
};
/**
* The current version, as set in package.json.
* */

@ -3,7 +3,7 @@ import * as path from 'node:path';
import { fileURLToPath } from 'node:url';
import { parseArgs } from 'node:util';
import { globSync } from 'tinyglobby';
import { compile, compileModule, parse, migrate } from 'svelte/compiler';
import { compile, compileModule, parse, print, migrate } from 'svelte/compiler';
const argv = parseArgs({ options: { runes: { type: 'boolean' } }, args: process.argv.slice(2) });
@ -70,6 +70,10 @@ for (const generate of /** @type {const} */ (['client', 'server'])) {
} catch (e) {
console.warn(`Error migrating ${file}`, e);
}
const printed = print(ast);
write(`${cwd}/output/printed/${file}`, printed.code);
}
const compiled = compile(source, {

Loading…
Cancel
Save