Skip to content

Commit ee1ef60

Browse files
authored
fix: ensure compiler state is reset before compilation (#16396)
#16268 introduced a slight regression where the state is not reset completely upon compilation. It did reset warnings but not other state, which meant if file A succeeds but file B fails in the parsing state (before the state was reset for real) it would get wrong filename info. This fixes it by setting the filename at the very beginning.
1 parent 4947283 commit ee1ef60

File tree

8 files changed

+55
-29
lines changed

8 files changed

+55
-29
lines changed

.changeset/two-terms-draw.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: ensure compiler state is reset before compilation

packages/svelte/src/compiler/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export { default as preprocess } from './preprocess/index.js';
2020
*/
2121
export function compile(source, options) {
2222
source = remove_bom(source);
23-
state.reset_warnings(options.warningFilter);
23+
state.reset({ warning: options.warningFilter, filename: options.filename });
2424
const validated = validate_component_options(options, '');
2525

2626
let parsed = _parse(source);
@@ -63,7 +63,7 @@ export function compile(source, options) {
6363
*/
6464
export function compileModule(source, options) {
6565
source = remove_bom(source);
66-
state.reset_warnings(options.warningFilter);
66+
state.reset({ warning: options.warningFilter, filename: options.filename });
6767
const validated = validate_module_options(options, '');
6868

6969
const analysis = analyze_module(source, validated);
@@ -111,7 +111,7 @@ export function compileModule(source, options) {
111111
*/
112112
export function parse(source, { modern, loose } = {}) {
113113
source = remove_bom(source);
114-
state.reset_warnings(() => false);
114+
state.reset({ warning: () => false, filename: undefined });
115115

116116
const ast = _parse(source, loose);
117117
return to_public_ast(source, ast, modern);

packages/svelte/src/compiler/migrate/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { parse } from '../phases/1-parse/index.js';
99
import { regex_valid_component_name } from '../phases/1-parse/state/element.js';
1010
import { analyze_component } from '../phases/2-analyze/index.js';
1111
import { get_rune } from '../phases/scope.js';
12-
import { reset, reset_warnings } from '../state.js';
12+
import { reset, UNKNOWN_FILENAME } from '../state.js';
1313
import {
1414
extract_identifiers,
1515
extract_all_identifiers_from_expression,
@@ -134,7 +134,7 @@ export function migrate(source, { filename, use_ts } = {}) {
134134
return start + style_placeholder + end;
135135
});
136136

137-
reset_warnings(() => false);
137+
reset({ warning: () => false, filename });
138138

139139
let parsed = parse(source);
140140

@@ -145,7 +145,7 @@ export function migrate(source, { filename, use_ts } = {}) {
145145
...validate_component_options({}, ''),
146146
...parsed_options,
147147
customElementOptions,
148-
filename: filename ?? '(unknown)',
148+
filename: filename ?? UNKNOWN_FILENAME,
149149
experimental: {
150150
async: true
151151
}

packages/svelte/src/compiler/phases/2-analyze/index.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,8 @@ export function analyze_module(source, options) {
279279
classes: new Map()
280280
};
281281

282-
state.reset({
282+
state.adjust({
283283
dev: options.dev,
284-
filename: options.filename,
285284
rootDir: options.rootDir,
286285
runes: true
287286
});
@@ -531,12 +530,11 @@ export function analyze_component(root, source, options) {
531530
async_deriveds: new Set()
532531
};
533532

534-
state.reset({
533+
state.adjust({
535534
component_name: analysis.name,
536535
dev: options.dev,
537-
filename: options.filename,
538536
rootDir: options.rootDir,
539-
runes: true
537+
runes
540538
});
541539

542540
if (!runes) {

packages/svelte/src/compiler/phases/2-analyze/visitors/SvelteSelf.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { visit_component } from './shared/component.js';
44
import * as e from '../../../errors.js';
55
import * as w from '../../../warnings.js';
6-
import { filename } from '../../../state.js';
6+
import { filename, UNKNOWN_FILENAME } from '../../../state.js';
77

88
/**
99
* @param {AST.SvelteSelf} node
@@ -23,9 +23,9 @@ export function SvelteSelf(node, context) {
2323
}
2424

2525
if (context.state.analysis.runes) {
26-
const name = filename === '(unknown)' ? 'Self' : context.state.analysis.name;
26+
const name = filename === UNKNOWN_FILENAME ? 'Self' : context.state.analysis.name;
2727
const basename =
28-
filename === '(unknown)'
28+
filename === UNKNOWN_FILENAME
2929
? 'Self.svelte'
3030
: /** @type {string} */ (filename.split(/[/\\]/).pop());
3131

packages/svelte/src/compiler/state.js

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ export let warnings = [];
1616
*/
1717
export let filename;
1818

19+
/**
20+
* This is the fallback used when no filename is specified.
21+
*/
22+
export const UNKNOWN_FILENAME = '(unknown)';
23+
1924
/**
2025
* The name of the component that is used in the `export default function ...` statement.
2126
*/
@@ -80,15 +85,6 @@ export function pop_ignore() {
8085
ignore_stack.pop();
8186
}
8287

83-
/**
84-
*
85-
* @param {(warning: Warning) => boolean} fn
86-
*/
87-
export function reset_warnings(fn = () => true) {
88-
warning_filter = fn;
89-
warnings = [];
90-
}
91-
9288
/**
9389
* @param {AST.SvelteNode | NodeLike} node
9490
* @param {import('../constants.js').IGNORABLE_RUNTIME_WARNINGS[number]} code
@@ -99,21 +95,36 @@ export function is_ignored(node, code) {
9995
}
10096

10197
/**
98+
* Call this to reset the compiler state. Should be called before each compilation.
99+
* @param {{ warning?: (warning: Warning) => boolean; filename: string | undefined }} state
100+
*/
101+
export function reset(state) {
102+
dev = false;
103+
runes = false;
104+
component_name = UNKNOWN_FILENAME;
105+
source = '';
106+
locator = () => undefined;
107+
filename = (state.filename ?? UNKNOWN_FILENAME).replace(/\\/g, '/');
108+
warning_filter = state.warning ?? (() => true);
109+
warnings = [];
110+
}
111+
112+
/**
113+
* Adjust the compiler state based on the provided state object.
114+
* Call this after parsing and basic analysis happened.
102115
* @param {{
103116
* dev: boolean;
104-
* filename: string;
105117
* component_name?: string;
106118
* rootDir?: string;
107119
* runes: boolean;
108120
* }} state
109121
*/
110-
export function reset(state) {
122+
export function adjust(state) {
111123
const root_dir = state.rootDir?.replace(/\\/g, '/');
112-
filename = state.filename.replace(/\\/g, '/');
113124

114125
dev = state.dev;
115126
runes = state.runes;
116-
component_name = state.component_name ?? '(unknown)';
127+
component_name = state.component_name ?? UNKNOWN_FILENAME;
117128

118129
if (typeof root_dir === 'string' && filename.startsWith(root_dir)) {
119130
// make filename relative to rootDir

packages/svelte/src/compiler/utils/compile_diagnostic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export class CompileDiagnostic {
6161
this.code = code;
6262
this.message = message;
6363

64-
if (state.filename) {
64+
if (state.filename !== state.UNKNOWN_FILENAME) {
6565
this.filename = state.filename;
6666
}
6767

packages/svelte/tests/compiler-errors/test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as fs from 'node:fs';
2-
import { assert, expect } from 'vitest';
2+
import { assert, expect, it } from 'vitest';
33
import { compile, compileModule, type CompileError } from 'svelte/compiler';
44
import { suite, type BaseTest } from '../suite';
55
import { read_file } from '../helpers.js';
@@ -78,3 +78,15 @@ const { test, run } = suite<CompilerErrorTest>((config, cwd) => {
7878
export { test };
7979

8080
await run(__dirname);
81+
82+
it('resets the compiler state including filename', () => {
83+
// start with something that succeeds
84+
compile('<div>hello</div>', { filename: 'foo.svelte' });
85+
// then try something that fails in the parsing stage
86+
try {
87+
compile('<p>hello<div>invalid</p>', { filename: 'bar.svelte' });
88+
expect.fail('Expected an error');
89+
} catch (e: any) {
90+
expect(e.toString()).toContain('bar.svelte');
91+
}
92+
});

0 commit comments

Comments
 (0)