Skip to content

Commit dc9b0d0

Browse files
dominikgRich-Harrisdummdidumm
authored
feat: add rootDir option and set __svelte_meta.file like in svelte4 (#11627)
* feat: add rootDir option and set __svelte_meta.file like in svelte4 * Update packages/svelte/src/compiler/validate-options.js * update tests * centralise logic * fix * note to self * Apply suggestions from code review * lint * one dollar towards the windows backslash bugfix foundation please --------- Co-authored-by: Rich Harris <[email protected]> Co-authored-by: Simon Holthausen <[email protected]>
1 parent 2e7e399 commit dc9b0d0

File tree

22 files changed

+94
-59
lines changed

22 files changed

+94
-59
lines changed

.changeset/khaki-mails-scream.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+
feat: introduce `rootDir` compiler option, make `filename` relative to it

.changeset/kind-doors-grin.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: rename `__svelte_meta.filename` to `__svelte_meta.file` to align with svelte 4

packages/svelte/src/compiler/index.js

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ export { default as preprocess } from './preprocess/index.js';
1919
* @returns {import('#compiler').CompileResult}
2020
*/
2121
export function compile(source, options) {
22-
try {
23-
state.reset({ source, filename: options.filename });
22+
const validated = validate_component_options(options, '');
23+
state.reset(source, validated);
2424

25-
const validated = validate_component_options(options, '');
25+
try {
2626
let parsed = _parse(source);
2727

2828
const { customElement: customElementOptions, ...parsed_options } = parsed.options || {};
@@ -49,7 +49,7 @@ export function compile(source, options) {
4949
return result;
5050
} catch (e) {
5151
if (e instanceof CompileError) {
52-
handle_compile_error(e, options.filename, source);
52+
handle_compile_error(e);
5353
}
5454

5555
throw e;
@@ -65,16 +65,16 @@ export function compile(source, options) {
6565
* @returns {import('#compiler').CompileResult}
6666
*/
6767
export function compileModule(source, options) {
68-
try {
69-
state.reset({ source, filename: options.filename });
68+
const validated = validate_module_options(options, '');
69+
state.reset(source, validated);
7070

71-
const validated = validate_module_options(options, '');
71+
try {
7272
const analysis = analyze_module(parse_acorn(source, false), validated);
7373
const result = transform_module(analysis, source, validated);
7474
return result;
7575
} catch (e) {
7676
if (e instanceof CompileError) {
77-
handle_compile_error(e, options.filename, source);
77+
handle_compile_error(e);
7878
}
7979

8080
throw e;
@@ -83,11 +83,9 @@ export function compileModule(source, options) {
8383

8484
/**
8585
* @param {import('#compiler').CompileError} error
86-
* @param {string | undefined} filename
87-
* @param {string} source
8886
*/
89-
function handle_compile_error(error, filename, source) {
90-
error.filename = filename;
87+
function handle_compile_error(error) {
88+
error.filename = state.filename;
9189

9290
if (error.position) {
9391
const start = state.locator(error.position[0]);
@@ -134,25 +132,25 @@ function handle_compile_error(error, filename, source) {
134132
*
135133
* https://svelte.dev/docs/svelte-compiler#svelte-parse
136134
* @param {string} source
137-
* @param {{ filename?: string; modern?: boolean }} [options]
135+
* @param {{ filename?: string; rootDir?: string; modern?: boolean }} [options]
138136
* @returns {import('#compiler').Root | import('./types/legacy-nodes.js').LegacyRoot}
139137
*/
140-
export function parse(source, options = {}) {
141-
state.reset({ source, filename: options.filename });
138+
export function parse(source, { filename, rootDir, modern } = {}) {
139+
state.reset(source, { filename, rootDir }); // TODO it's weird to require filename/rootDir here. reconsider the API
142140

143141
/** @type {import('#compiler').Root} */
144142
let ast;
145143
try {
146144
ast = _parse(source);
147145
} catch (e) {
148146
if (e instanceof CompileError) {
149-
handle_compile_error(e, options.filename, source);
147+
handle_compile_error(e);
150148
}
151149

152150
throw e;
153151
}
154152

155-
return to_public_ast(source, ast, options.modern);
153+
return to_public_ast(source, ast, modern);
156154
}
157155

158156
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { migrate_svelte_ignore } from '../utils/extract_svelte_ignore.js';
1818
*/
1919
export function migrate(source) {
2020
try {
21-
reset({ source, filename: 'migrate.svelte' });
21+
reset(source, { filename: 'migrate.svelte' });
2222

2323
let parsed = parse(source);
2424

packages/svelte/src/compiler/phases/3-transform/client/transform-client.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { javascript_visitors_runes } from './visitors/javascript-runes.js';
88
import { javascript_visitors_legacy } from './visitors/javascript-legacy.js';
99
import { serialize_get_binding } from './utils.js';
1010
import { render_stylesheet } from '../css/index.js';
11+
import { filename } from '../../../state.js';
1112

1213
/**
1314
* This function ensures visitor sets don't accidentally clobber each other
@@ -471,14 +472,7 @@ export function client_component(source, analysis, options) {
471472
}
472473

473474
if (options.dev) {
474-
if (options.filename) {
475-
let filename = options.filename;
476-
if (/(\/|\w:)/.test(options.filename)) {
477-
// filename is absolute — truncate it
478-
const parts = filename.split(/[/\\]/);
479-
filename = parts.length > 3 ? ['...', ...parts.slice(-3)].join('/') : filename;
480-
}
481-
475+
if (filename) {
482476
// add `App.filename = 'App.svelte'` so that we can print useful messages later
483477
body.unshift(
484478
b.stmt(

packages/svelte/src/compiler/phases/3-transform/server/transform-server.js

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { DOMBooleanAttributes, HYDRATION_END, HYDRATION_START } from '../../../.
2727
import { escape_html } from '../../../../escaping.js';
2828
import { sanitize_template_string } from '../../../utils/sanitize_template_string.js';
2929
import { BLOCK_CLOSE, BLOCK_CLOSE_ELSE } from '../../../../internal/server/hydration.js';
30-
import { locator } from '../../../state.js';
30+
import { filename, locator } from '../../../state.js';
3131

3232
export const block_open = t_string(`<!--${HYDRATION_START}-->`);
3333
export const block_close = t_string(`<!--${HYDRATION_END}-->`);
@@ -2412,14 +2412,7 @@ export function server_component(analysis, options) {
24122412
body.push(b.export_default(component_function));
24132413
}
24142414

2415-
if (options.dev && options.filename) {
2416-
let filename = options.filename;
2417-
if (/(\/|\w:)/.test(options.filename)) {
2418-
// filename is absolute — truncate it
2419-
const parts = filename.split(/[/\\]/);
2420-
filename = parts.length > 3 ? ['...', ...parts.slice(-3)].join('/') : filename;
2421-
}
2422-
2415+
if (options.dev && filename) {
24232416
// add `App.filename = 'App.svelte'` so that we can print useful messages later
24242417
body.unshift(
24252418
b.stmt(

packages/svelte/src/compiler/state.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import { getLocator } from 'locate-character';
55
/** @type {import('#compiler').Warning[]} */
66
export let warnings = [];
77

8-
/** @type {string | undefined} */
8+
/**
9+
* The filename (if specified in the compiler options) relative to the rootDir (if specified).
10+
* This should not be used in the compiler output except in dev mode
11+
* @type {string | undefined}
12+
*/
913
export let filename;
1014

1115
export let locator = getLocator('', { offsetLine: 1 });
@@ -26,14 +30,23 @@ export function pop_ignore() {
2630
}
2731

2832
/**
29-
* @param {{
30-
* source: string;
31-
* filename: string | undefined;
32-
* }} options
33+
* @param {string} source
34+
* @param {{ filename?: string, rootDir?: string }} options
3335
*/
34-
export function reset(options) {
35-
filename = options.filename;
36-
locator = getLocator(options.source, { offsetLine: 1 });
36+
export function reset(source, options) {
37+
const root_dir = options.rootDir?.replace(/\\/g, '/');
38+
filename = options.filename?.replace(/\\/g, '/');
39+
40+
if (
41+
typeof filename === 'string' &&
42+
typeof root_dir === 'string' &&
43+
filename.startsWith(root_dir)
44+
) {
45+
// make filename relative to rootDir
46+
filename = filename.replace(root_dir, '').replace(/^[/\\]/, '');
47+
}
48+
49+
locator = getLocator(source, { offsetLine: 1 });
3750
warnings = [];
3851
ignore_stack = [];
3952
}

packages/svelte/src/compiler/types/index.d.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,22 @@ export interface ModuleCompileOptions {
216216
* Used for debugging hints and sourcemaps. Your bundler plugin will set it automatically.
217217
*/
218218
filename?: string;
219+
220+
/**
221+
* Used for ensuring filenames don't leak filesystem information. Your bundler plugin will set it automatically.
222+
* @default process.cwd() on node-like environments, undefined elsewhere
223+
*/
224+
rootDir?: string;
219225
}
220226

221227
// The following two somewhat scary looking types ensure that certain types are required but can be undefined still
222228

223-
export type ValidatedModuleCompileOptions = Omit<Required<ModuleCompileOptions>, 'filename'> & {
229+
export type ValidatedModuleCompileOptions = Omit<
230+
Required<ModuleCompileOptions>,
231+
'filename' | 'rootDir'
232+
> & {
224233
filename: ModuleCompileOptions['filename'];
234+
rootDir: ModuleCompileOptions['rootDir'];
225235
};
226236

227237
export type ValidatedCompileOptions = ValidatedModuleCompileOptions &

packages/svelte/src/compiler/validate-options.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import * as w from './warnings.js';
1010
const common = {
1111
filename: string(undefined),
1212

13+
// default to process.cwd() where it exists to replicate svelte4 behavior
14+
// see https://github.com/sveltejs/svelte/blob/b62fc8c8fd2640c9b99168f01b9d958cb2f7574f/packages/svelte/src/compiler/compile/Component.js#L211
15+
rootDir: string(typeof process !== 'undefined' ? process.cwd?.() : undefined),
16+
1317
dev: boolean(false),
1418

1519
generate: validator('client', (input, keypath) => {

packages/svelte/src/internal/client/dev/elements.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export function add_locations(fn, filename, locations) {
3434
function assign_location(element, filename, location) {
3535
// @ts-expect-error
3636
element.__svelte_meta = {
37-
loc: { filename, line: location[0], column: location[1] }
37+
loc: { file: filename, line: location[0], column: location[1] }
3838
};
3939

4040
if (location[2]) {

0 commit comments

Comments
 (0)