Skip to content

Commit 4686a35

Browse files
committed
start adding analyze function
1 parent 825828d commit 4686a35

File tree

5 files changed

+133
-130
lines changed

5 files changed

+133
-130
lines changed

packages/svelte/src/compiler/index.js

Lines changed: 89 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,93 @@ import { getLocator } from 'locate-character';
66
import { walk } from 'zimmerframe';
77
import { validate_component_options, validate_module_options } from './validate-options.js';
88
import { convert } from './legacy.js';
9+
import { transform_warnings } from './utils/warnings.js';
910
export { default as preprocess } from './preprocess/index.js';
1011

12+
/**
13+
* @param {string} source
14+
* @param {string | undefined} filename
15+
* @param {Function} fn
16+
*/
17+
function handle_error(source, filename, fn) {
18+
try {
19+
return fn();
20+
} catch (e) {
21+
if (/** @type {any} */ (e).name === 'CompileError') {
22+
const error = /** @type {import('#compiler').CompileError} */ (e);
23+
24+
error.filename = filename;
25+
26+
if (error.position) {
27+
// TODO this is reused with warnings — DRY out
28+
const locator = getLocator(source, { offsetLine: 1 });
29+
const start = locator(error.position[0]);
30+
const end = locator(error.position[1]);
31+
32+
error.start = start;
33+
error.end = end;
34+
}
35+
}
36+
37+
throw e;
38+
}
39+
}
40+
41+
/**
42+
* The parse function parses a component, returning only its abstract syntax tree.
43+
*
44+
* The `modern` option (`false` by default in Svelte 5) makes the parser return a modern AST instead of the legacy AST.
45+
* `modern` will become `true` by default in Svelte 6, and the option will be removed in Svelte 7.
46+
*
47+
* https://svelte.dev/docs/svelte-compiler#svelte-parse
48+
* @param {string} source
49+
* @param {{ filename?: string; modern?: boolean }} [options]
50+
* @returns {import('#compiler').SvelteNode | import('./types/legacy-nodes.js').LegacySvelteNode}
51+
*/
52+
export function parse(source, options = {}) {
53+
return handle_error(source, undefined, () => {
54+
/** @type {import('#compiler').Root} */
55+
const ast = _parse(source);
56+
57+
if (options.modern) {
58+
// remove things that we don't want to treat as public API
59+
return walk(/** @type {import('#compiler').SvelteNode} */ (ast), null, {
60+
_(node, { next }) {
61+
// @ts-ignore
62+
delete node.parent;
63+
// @ts-ignore
64+
delete node.metadata;
65+
next();
66+
}
67+
});
68+
}
69+
70+
return convert(source, ast);
71+
});
72+
}
73+
74+
/**
75+
* @param {string} source
76+
* @param {TODO} options
77+
*/
78+
export function analyze(source, options = {}) {
79+
return handle_error(source, options.filename, () => {
80+
const validated = validate_component_options(options, '');
81+
const parsed = _parse(source);
82+
83+
const combined_options = /** @type {import('#compiler').ValidatedCompileOptions} */ ({
84+
...validated,
85+
...parsed.options
86+
});
87+
88+
const analysis = analyze_component(parsed, combined_options);
89+
90+
return {
91+
warnings: transform_warnings(source, options.filename, analysis.warnings)
92+
};
93+
});
94+
}
95+
1196
/**
1297
* `compile` converts your `.svelte` source code into a JavaScript module that exports a component
1398
*
@@ -17,7 +102,7 @@ export { default as preprocess } from './preprocess/index.js';
17102
* @returns {import('#compiler').CompileResult}
18103
*/
19104
export function compile(source, options) {
20-
try {
105+
return handle_error(source, options.filename, () => {
21106
const validated = validate_component_options(options, '');
22107
const parsed = _parse(source);
23108

@@ -29,17 +114,7 @@ export function compile(source, options) {
29114
const analysis = analyze_component(parsed, combined_options);
30115
const result = transform_component(analysis, source, combined_options);
31116
return result;
32-
} catch (e) {
33-
if (/** @type {any} */ (e).name === 'CompileError') {
34-
handle_compile_error(
35-
/** @type {import('#compiler').CompileError} */ (e),
36-
options.filename,
37-
source
38-
);
39-
}
40-
41-
throw e;
42-
}
117+
});
43118
}
44119

45120
/**
@@ -51,86 +126,11 @@ export function compile(source, options) {
51126
* @returns {import('#compiler').CompileResult}
52127
*/
53128
export function compileModule(source, options) {
54-
try {
129+
return handle_error(source, options.filename, () => {
55130
const validated = validate_module_options(options, '');
56131
const analysis = analyze_module(parse_acorn(source), validated);
57132
return transform_module(analysis, source, validated);
58-
} catch (e) {
59-
if (/** @type {any} */ (e).name === 'CompileError') {
60-
handle_compile_error(
61-
/** @type {import('#compiler').CompileError} */ (e),
62-
options.filename,
63-
source
64-
);
65-
}
66-
67-
throw e;
68-
}
69-
}
70-
71-
/**
72-
* @param {import('#compiler').CompileError} error
73-
* @param {string | undefined} filename
74-
* @param {string} source
75-
*/
76-
function handle_compile_error(error, filename, source) {
77-
error.filename = filename;
78-
79-
if (error.position) {
80-
// TODO this is reused with warnings — DRY out
81-
const locator = getLocator(source, { offsetLine: 1 });
82-
const start = locator(error.position[0]);
83-
const end = locator(error.position[1]);
84-
85-
error.start = start;
86-
error.end = end;
87-
}
88-
89-
throw error;
90-
}
91-
92-
/**
93-
* The parse function parses a component, returning only its abstract syntax tree.
94-
*
95-
* The `modern` option (`false` by default in Svelte 5) makes the parser return a modern AST instead of the legacy AST.
96-
* `modern` will become `true` by default in Svelte 6, and the option will be removed in Svelte 7.
97-
*
98-
* https://svelte.dev/docs/svelte-compiler#svelte-parse
99-
* @param {string} source
100-
* @param {{ filename?: string; modern?: boolean }} [options]
101-
* @returns {import('#compiler').SvelteNode | import('./types/legacy-nodes.js').LegacySvelteNode}
102-
*/
103-
export function parse(source, options = {}) {
104-
/** @type {import('#compiler').Root} */
105-
let ast;
106-
try {
107-
ast = _parse(source);
108-
} catch (e) {
109-
if (/** @type {any} */ (e).name === 'CompileError') {
110-
handle_compile_error(
111-
/** @type {import('#compiler').CompileError} */ (e),
112-
options.filename,
113-
source
114-
);
115-
}
116-
117-
throw e;
118-
}
119-
120-
if (options.modern) {
121-
// remove things that we don't want to treat as public API
122-
return walk(/** @type {import('#compiler').SvelteNode} */ (ast), null, {
123-
_(node, { next }) {
124-
// @ts-ignore
125-
delete node.parent;
126-
// @ts-ignore
127-
delete node.metadata;
128-
next();
129-
}
130-
});
131-
}
132-
133-
return convert(source, ast);
133+
});
134134
}
135135

136136
/**

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

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { VERSION } from '../../../version.js';
33
import { server_component, server_module } from './server/transform-server.js';
44
import { client_component, client_module } from './client/transform-client.js';
55
import { getLocator } from 'locate-character';
6+
import { transform_warnings } from '../../utils/warnings.js';
67

78
/**
89
* @param {import('../types').ComponentAnalysis} analysis
@@ -102,38 +103,3 @@ export function transform_module(analysis, source, options) {
102103
}
103104
};
104105
}
105-
106-
/**
107-
* @param {string} source
108-
* @param {string | undefined} name
109-
* @param {import('../types').RawWarning[]} warnings
110-
* @returns {import('#compiler').Warning[]}
111-
*/
112-
function transform_warnings(source, name, warnings) {
113-
if (warnings.length === 0) return [];
114-
115-
const locate = getLocator(source, { offsetLine: 1 });
116-
117-
/** @type {import('#compiler').Warning[]} */
118-
const result = [];
119-
120-
for (const warning of warnings) {
121-
const start =
122-
warning.position &&
123-
/** @type {import('locate-character').Location} */ (locate(warning.position[0]));
124-
125-
const end =
126-
warning.position &&
127-
/** @type {import('locate-character').Location} */ (locate(warning.position[1]));
128-
129-
result.push({
130-
start,
131-
end,
132-
filename: name,
133-
message: warning.message,
134-
code: warning.code
135-
});
136-
}
137-
138-
return result;
139-
}

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {
22
BindDirective,
33
Binding,
44
Fragment,
5+
RawWarning,
56
RegularElement,
67
SvelteElement,
78
SvelteNode,
@@ -33,12 +34,6 @@ export interface BindingGroup {
3334
directives: BindDirective[];
3435
}
3536

36-
export interface RawWarning {
37-
code: string;
38-
message: string;
39-
position: [number, number] | undefined;
40-
}
41-
4237
/**
4338
* Analysis common to modules and components
4439
*/

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,3 +285,9 @@ export { Css };
285285

286286
// TODO this chain is a bit weird
287287
export { ReactiveStatement } from '../phases/types.js';
288+
289+
export interface RawWarning {
290+
code: string;
291+
message: string;
292+
position: [number, number] | undefined;
293+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { getLocator } from 'locate-character';
2+
3+
/**
4+
* @param {string} source
5+
* @param {string | undefined} name
6+
* @param {import('#compiler').RawWarning[]} warnings
7+
* @returns {import('#compiler').Warning[]}
8+
*/
9+
export function transform_warnings(source, name, warnings) {
10+
if (warnings.length === 0) return [];
11+
12+
const locate = getLocator(source, { offsetLine: 1 });
13+
14+
/** @type {import('#compiler').Warning[]} */
15+
const result = [];
16+
17+
for (const warning of warnings) {
18+
const start =
19+
warning.position &&
20+
/** @type {import('locate-character').Location} */ (locate(warning.position[0]));
21+
22+
const end =
23+
warning.position &&
24+
/** @type {import('locate-character').Location} */ (locate(warning.position[1]));
25+
26+
result.push({
27+
start,
28+
end,
29+
filename: name,
30+
message: warning.message,
31+
code: warning.code
32+
});
33+
}
34+
35+
return result;
36+
}

0 commit comments

Comments
 (0)