Skip to content

Commit 97c5d98

Browse files
committed
WIP
1 parent 486d10c commit 97c5d98

File tree

16 files changed

+103
-40
lines changed

16 files changed

+103
-40
lines changed

packages/svelte/src/compiler/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export function compileModule(source, options) {
7070
const validated = validate_module_options(options, '');
7171
state.reset(source, validated);
7272

73-
const analysis = analyze_module(parse_acorn(source, false), validated);
73+
const analysis = analyze_module(source, validated);
7474
return transform_module(analysis, source, validated);
7575
}
7676

packages/svelte/src/compiler/phases/1-parse/acorn.js

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,27 @@ import { tsPlugin } from '@sveltejs/acorn-typescript';
55

66
const ParserWithTS = acorn.Parser.extend(tsPlugin());
77

8+
/**
9+
* @typedef {Comment & {
10+
* start: number;
11+
* end: number;
12+
* }} CommentWithLocation
13+
*/
14+
815
/**
916
* @param {string} source
17+
* @param {Comment[]} comments
1018
* @param {boolean} typescript
1119
* @param {boolean} [is_script]
1220
*/
13-
export function parse(source, typescript, is_script) {
21+
export function parse(source, comments, typescript, is_script) {
1422
const parser = typescript ? ParserWithTS : acorn.Parser;
15-
const { onComment, add_comments } = get_comment_handlers(source);
23+
24+
const { onComment, add_comments } = get_comment_handlers(
25+
source,
26+
/** @type {CommentWithLocation[]} */ (comments)
27+
);
28+
1629
// @ts-ignore
1730
const parse_statement = parser.prototype.parseStatement;
1831

@@ -53,13 +66,18 @@ export function parse(source, typescript, is_script) {
5366

5467
/**
5568
* @param {string} source
69+
* @param {Comment[]} comments
5670
* @param {boolean} typescript
5771
* @param {number} index
5872
* @returns {acorn.Expression & { leadingComments?: CommentWithLocation[]; trailingComments?: CommentWithLocation[]; }}
5973
*/
60-
export function parse_expression_at(source, typescript, index) {
74+
export function parse_expression_at(source, comments, typescript, index) {
6175
const parser = typescript ? ParserWithTS : acorn.Parser;
62-
const { onComment, add_comments } = get_comment_handlers(source);
76+
77+
const { onComment, add_comments } = get_comment_handlers(
78+
source,
79+
/** @type {CommentWithLocation[]} */ (comments)
80+
);
6381

6482
const ast = parser.parseExpressionAt(source, index, {
6583
onComment,
@@ -78,26 +96,17 @@ export function parse_expression_at(source, typescript, index) {
7896
* to add them after the fact. They are needed in order to support `svelte-ignore` comments
7997
* in JS code and so that `prettier-plugin-svelte` doesn't remove all comments when formatting.
8098
* @param {string} source
99+
* @param {CommentWithLocation[]} comments
81100
*/
82-
function get_comment_handlers(source) {
83-
/**
84-
* @typedef {Comment & {
85-
* start: number;
86-
* end: number;
87-
* }} CommentWithLocation
88-
*/
89-
90-
/** @type {CommentWithLocation[]} */
91-
const comments = [];
92-
101+
function get_comment_handlers(source, comments) {
93102
return {
94103
/**
95104
* @param {boolean} block
96105
* @param {string} value
97106
* @param {number} start
98107
* @param {number} end
99108
*/
100-
onComment: (block, value, start, end) => {
109+
onComment: (block, value, start, end, start_loc, end_loc) => {
101110
if (block && /\n/.test(value)) {
102111
let a = start;
103112
while (a > 0 && source[a - 1] !== '\n') a -= 1;
@@ -109,13 +118,21 @@ function get_comment_handlers(source) {
109118
value = value.replace(new RegExp(`^${indentation}`, 'gm'), '');
110119
}
111120

112-
comments.push({ type: block ? 'Block' : 'Line', value, start, end });
121+
comments.push({
122+
type: block ? 'Block' : 'Line',
123+
value,
124+
start,
125+
end,
126+
loc: { start: start_loc, end: end_loc }
127+
});
113128
},
114129

115130
/** @param {acorn.Node & { leadingComments?: CommentWithLocation[]; trailingComments?: CommentWithLocation[]; }} ast */
116131
add_comments(ast) {
117132
if (comments.length === 0) return;
118133

134+
comments = comments.slice();
135+
119136
walk(ast, null, {
120137
_(node, { next, path }) {
121138
let comment;

packages/svelte/src/compiler/phases/1-parse/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/** @import { AST } from '#compiler' */
2+
/** @import { Comment } from 'estree' */
23
// @ts-expect-error acorn type definitions are borked in the release we use
34
import { isIdentifierStart, isIdentifierChar } from 'acorn';
45
import fragment from './state/fragment.js';
@@ -87,6 +88,7 @@ export class Parser {
8788
type: 'Root',
8889
fragment: create_fragment(),
8990
options: null,
91+
comments: [],
9092
metadata: {
9193
ts: this.ts
9294
}

packages/svelte/src/compiler/phases/1-parse/read/context.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,12 @@ export default function read_pattern(parser) {
5959
space_with_newline.slice(0, first_space) + space_with_newline.slice(first_space + 1);
6060

6161
const expression = /** @type {any} */ (
62-
parse_expression_at(`${space_with_newline}(${pattern_string} = 1)`, parser.ts, start - 1)
62+
parse_expression_at(
63+
`${space_with_newline}(${pattern_string} = 1)`,
64+
parser.root.comments,
65+
parser.ts,
66+
start - 1
67+
)
6368
).left;
6469

6570
expression.typeAnnotation = read_type_annotation(parser);
@@ -96,13 +101,13 @@ function read_type_annotation(parser) {
96101
// parameters as part of a sequence expression instead, and will then error on optional
97102
// parameters (`?:`). Therefore replace that sequence with something that will not error.
98103
parser.template.slice(parser.index).replace(/\?\s*:/g, ':');
99-
let expression = parse_expression_at(template, parser.ts, a);
104+
let expression = parse_expression_at(template, parser.root.comments, parser.ts, a);
100105

101106
// `foo: bar = baz` gets mangled — fix it
102107
if (expression.type === 'AssignmentExpression') {
103108
let b = expression.right.start;
104109
while (template[b] !== '=') b -= 1;
105-
expression = parse_expression_at(template.slice(0, b), parser.ts, a);
110+
expression = parse_expression_at(template.slice(0, b), parser.root.comments, parser.ts, a);
106111
}
107112

108113
// `array as item: string, index` becomes `string, index`, which is mistaken as a sequence expression - fix that

packages/svelte/src/compiler/phases/1-parse/read/expression.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,12 @@ export function get_loose_identifier(parser, opening_token) {
3434
*/
3535
export default function read_expression(parser, opening_token, disallow_loose) {
3636
try {
37-
const node = parse_expression_at(parser.template, parser.ts, parser.index);
37+
const node = parse_expression_at(
38+
parser.template,
39+
parser.root.comments,
40+
parser.ts,
41+
parser.index
42+
);
3843

3944
let num_parens = 0;
4045

packages/svelte/src/compiler/phases/1-parse/read/script.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export function read_script(parser, start, attributes) {
3434
let ast;
3535

3636
try {
37-
ast = acorn.parse(source, parser.ts, true);
37+
ast = acorn.parse(source, parser.root.comments, parser.ts, true);
3838
} catch (err) {
3939
parser.acorn_error(err);
4040
}

packages/svelte/src/compiler/phases/1-parse/state/tag.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,12 @@ function open(parser) {
389389

390390
let function_expression = matched
391391
? /** @type {ArrowFunctionExpression} */ (
392-
parse_expression_at(prelude + `${params} => {}`, parser.ts, params_start)
392+
parse_expression_at(
393+
prelude + `${params} => {}`,
394+
parser.root.comments,
395+
parser.ts,
396+
params_start
397+
)
393398
)
394399
: { params: [] };
395400

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
/** @import { Expression, Node, Program } from 'estree' */
1+
/** @import { Comment, Expression, Node, Program } from 'estree' */
22
/** @import { Binding, AST, ValidatedCompileOptions, ValidatedModuleCompileOptions } from '#compiler' */
33
/** @import { AnalysisState, Visitors } from './types' */
44
/** @import { Analysis, ComponentAnalysis, Js, ReactiveStatement, Template } from '../types' */
55
import { walk } from 'zimmerframe';
6+
import { parse } from '../1-parse/acorn.js';
67
import * as e from '../../errors.js';
78
import * as w from '../../warnings.js';
89
import { extract_identifiers } from '../../utils/ast.js';
@@ -231,11 +232,16 @@ function get_component_name(filename) {
231232
const RESERVED = ['$$props', '$$restProps', '$$slots'];
232233

233234
/**
234-
* @param {Program} ast
235+
* @param {string} source
235236
* @param {ValidatedModuleCompileOptions} options
236237
* @returns {Analysis}
237238
*/
238-
export function analyze_module(ast, options) {
239+
export function analyze_module(source, options) {
240+
/** @type {Comment[]} */
241+
const comments = [];
242+
243+
const ast = parse(source, comments, false, false);
244+
239245
const { scope, scopes } = create_scopes(ast, new ScopeRoot(), false, null);
240246

241247
for (const [name, references] of scope.references) {
@@ -259,6 +265,7 @@ export function analyze_module(ast, options) {
259265
runes: true,
260266
immutable: true,
261267
tracing: false,
268+
comments,
262269
classes: new Map()
263270
};
264271

@@ -429,6 +436,7 @@ export function analyze_component(root, source, options) {
429436
module,
430437
instance,
431438
template,
439+
comments: root.comments,
432440
elements: [],
433441
runes,
434442
tracing: false,

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,9 @@ export function client_component(analysis, options) {
362362
.../** @type {ESTree.Statement[]} */ (template.body)
363363
]);
364364

365+
// trick esrap into including comments
366+
component_block.loc = instance.loc;
367+
365368
if (!analysis.runes) {
366369
// Bind static exports to props so that people can access them with bind:x
367370
for (const { name, alias } of analysis.exports) {

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export function transform_component(analysis, source, options) {
3636
const js_source_name = get_source_name(options.filename, options.outputFilename, 'input.svelte');
3737

3838
// @ts-ignore TODO
39-
const js = print(program, ts(), {
39+
const js = print(program, ts({ comments: analysis.comments }), {
4040
// include source content; makes it easier/more robust looking up the source map code
4141
// (else esrap does return null for source and sourceMapContent which may trip up tooling)
4242
sourceMapContent: source,
@@ -95,14 +95,20 @@ export function transform_module(analysis, source, options) {
9595
];
9696
}
9797

98+
// @ts-expect-error
99+
const js = print(program, ts({ comments: analysis.comments }), {
100+
// include source content; makes it easier/more robust looking up the source map code
101+
// (else esrap does return null for source and sourceMapContent which may trip up tooling)
102+
sourceMapContent: source,
103+
sourceMapSource: get_source_name(options.filename, undefined, 'input.svelte.js')
104+
});
105+
106+
// prepend comment
107+
js.code = `/* ${basename} generated by Svelte v${VERSION} */\n${js.code}`;
108+
js.map.mappings = ';' + js.map.mappings;
109+
98110
return {
99-
// @ts-expect-error
100-
js: print(program, ts(), {
101-
// include source content; makes it easier/more robust looking up the source map code
102-
// (else esrap does return null for source and sourceMapContent which may trip up tooling)
103-
sourceMapContent: source,
104-
sourceMapSource: get_source_name(options.filename, undefined, 'input.svelte.js')
105-
}),
111+
js,
106112
css: null,
107113
metadata: {
108114
runes: true

0 commit comments

Comments
 (0)