svelte-docinfo 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/dist/analyze-core.d.ts +2 -2
- package/dist/diagnostics.d.ts +5 -29
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +4 -21
- package/dist/svelte-legacy.d.ts +145 -0
- package/dist/svelte-legacy.d.ts.map +1 -0
- package/dist/svelte-legacy.js +210 -0
- package/dist/svelte-scripts.d.ts +144 -0
- package/dist/svelte-scripts.d.ts.map +1 -0
- package/dist/svelte-scripts.js +242 -0
- package/dist/svelte.d.ts +33 -11
- package/dist/svelte.d.ts.map +1 -1
- package/dist/svelte.js +284 -213
- package/dist/types.d.ts +16 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +16 -4
- package/package.json +1 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locating a Svelte component's script tags: its instance `<script>` and its
|
|
3
|
+
* `<script module>`.
|
|
4
|
+
*
|
|
5
|
+
* Svelte takes a `<script>` for one of those only at the top level of the
|
|
6
|
+
* file. Nested in an element, a block, or `<svelte:head>` it's an ordinary
|
|
7
|
+
* element (a JSON-LD block, an inline analytics snippet), and its content says
|
|
8
|
+
* nothing about the component. So finding the tags takes two steps: a regex
|
|
9
|
+
* scan for candidates (`scanScriptTags`), then a decision about which of
|
|
10
|
+
* them are top-level (`findScriptTags`).
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
/** A located script tag: parsed attributes, content, and where the content starts in the source. */
|
|
17
|
+
export interface SvelteScriptTag {
|
|
18
|
+
/** Opening-tag attributes; bare attributes (`module`) map to `true`. */
|
|
19
|
+
attributes: Map<string, string | true>;
|
|
20
|
+
content: string;
|
|
21
|
+
/** Offset of `content` within the source, for mapping script positions to original lines. */
|
|
22
|
+
start: number;
|
|
23
|
+
}
|
|
24
|
+
/** A script tag the scan matched, before anything has decided whether it's top-level. */
|
|
25
|
+
export interface ScannedScriptTag extends SvelteScriptTag {
|
|
26
|
+
/** Offset of the tag's opening `<`, which is where Svelte's parser places the script. */
|
|
27
|
+
open: number;
|
|
28
|
+
/** Offset just past the tag's closing `>`. */
|
|
29
|
+
end: number;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the scan alone proves the tag top-level, with no parser: see
|
|
32
|
+
* `scanScriptTags` for the proof and its conditions.
|
|
33
|
+
*/
|
|
34
|
+
provenTopLevel: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Scan Svelte source for script tags with content, in source order, and mark
|
|
38
|
+
* the ones the scan itself proves top-level.
|
|
39
|
+
*
|
|
40
|
+
* The scan is syntactic and knows nothing about nesting, so it returns nested
|
|
41
|
+
* tags too; `findScriptTags` is the function that answers which are the
|
|
42
|
+
* component's. Limits: whitespace around an attribute `=` makes a tag
|
|
43
|
+
* unmatchable (Svelte rejects one of those spellings, and svelte2tsx matches
|
|
44
|
+
* neither), and `</script>` in a string literal ends the content early.
|
|
45
|
+
*
|
|
46
|
+
* **`provenTopLevel`** holds for a tag when everything from the start of the
|
|
47
|
+
* file to it is whitespace, complete HTML comments, and earlier script tags
|
|
48
|
+
* that are themselves proven. Svelte's parser reads each of those at the root
|
|
49
|
+
* and comes back to the root: whitespace is text, a comment runs to the first
|
|
50
|
+
* `-->`, and a top-level script runs from its opening tag to the first
|
|
51
|
+
* `</script>`, the same extents the regex matched. So it meets this tag at the
|
|
52
|
+
* root, where a `<script>` is the instance or module script.
|
|
53
|
+
*
|
|
54
|
+
* Anything that would need more than that argument leaves the tag unproven,
|
|
55
|
+
* along with every tag after it:
|
|
56
|
+
*
|
|
57
|
+
* - any other text or markup before the tag: a `<style>`,
|
|
58
|
+
* `<svelte:options>`, an unterminated comment, what's left over when a
|
|
59
|
+
* `</script>` in a string ended an earlier match early, and tags the regex
|
|
60
|
+
* doesn't match (`<SCRIPT>`, a spelling with whitespace around `=`)
|
|
61
|
+
* - a self-closing `<script />`, which Svelte's parser rejects at the top
|
|
62
|
+
* level
|
|
63
|
+
* - attributes outside `UNAMBIGUOUS_ATTRIBUTES_MATCHER`
|
|
64
|
+
*/
|
|
65
|
+
export declare const scanScriptTags: (svelteSource: string) => Array<ScannedScriptTag>;
|
|
66
|
+
/**
|
|
67
|
+
* A script tag is a module script when it carries a `module` attribute
|
|
68
|
+
* (Svelte 5) or `context="module"` (Svelte 4) — Svelte's `read_script` test,
|
|
69
|
+
* minus its error paths. `module` inside an attribute *value* doesn't count.
|
|
70
|
+
*/
|
|
71
|
+
export declare const isModuleScript: (attributes: Map<string, string | true>) => boolean;
|
|
72
|
+
/** The part of Svelte's parsed root that `findScriptTags` reads. */
|
|
73
|
+
export interface SvelteTemplateScripts {
|
|
74
|
+
/** The instance script, at the offset of its opening `<`. */
|
|
75
|
+
instance?: {
|
|
76
|
+
start: number;
|
|
77
|
+
} | null;
|
|
78
|
+
/** The module script, at the offset of its opening `<`. */
|
|
79
|
+
module?: {
|
|
80
|
+
start: number;
|
|
81
|
+
} | null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Parses a Svelte source whose script and style content is blanked, and says
|
|
85
|
+
* where its top-level scripts are. Throws when the source doesn't parse.
|
|
86
|
+
*/
|
|
87
|
+
export type ParseSvelteTemplate = (blankedSource: string) => SvelteTemplateScripts;
|
|
88
|
+
/**
|
|
89
|
+
* How `findScriptTags` decided which tags are top-level:
|
|
90
|
+
*
|
|
91
|
+
* - `'scan'` — the scan proved every tag top-level (`provenTopLevel`), so
|
|
92
|
+
* nothing was parsed
|
|
93
|
+
* - `'parser'` — Svelte's parser said which tags are the instance and module
|
|
94
|
+
* scripts
|
|
95
|
+
* - `'unparsed'` — the parser threw, and every matched tag is returned
|
|
96
|
+
*/
|
|
97
|
+
export type ScriptTagsDecision = 'scan' | 'parser' | 'unparsed';
|
|
98
|
+
/** What `findScriptTags` returns. */
|
|
99
|
+
export interface FoundScriptTags {
|
|
100
|
+
/** The component's script tags, in source order. */
|
|
101
|
+
tags: Array<SvelteScriptTag>;
|
|
102
|
+
decidedBy: ScriptTagsDecision;
|
|
103
|
+
}
|
|
104
|
+
/** Options for `findScriptTags`. */
|
|
105
|
+
export interface FindScriptTagsOptions {
|
|
106
|
+
/**
|
|
107
|
+
* Take the scan's proof when it covers every tag, without parsing.
|
|
108
|
+
* `false` sends every source with a tag to the parser.
|
|
109
|
+
*
|
|
110
|
+
* @default true
|
|
111
|
+
*/
|
|
112
|
+
scanProof?: boolean;
|
|
113
|
+
/** The parser asked about tags the scan can't prove. Defaults to Svelte's. */
|
|
114
|
+
parseTemplate?: ParseSvelteTemplate;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Find a component's script tags: the `<script>` tags at the top level of the
|
|
118
|
+
* source, in source order. A `<script>` nested in an element, a block, or
|
|
119
|
+
* `<svelte:head>` isn't one, matching Svelte, which reads only a top-level
|
|
120
|
+
* tag as the instance or module script.
|
|
121
|
+
*
|
|
122
|
+
* `scanScriptTags` finds the candidates. When it proves every one of them
|
|
123
|
+
* top-level, which covers the usual component with its scripts ahead of the
|
|
124
|
+
* template, that's the answer. Otherwise Svelte's parser decides: the source
|
|
125
|
+
* is parsed with script and style content blanked, and the tags at the
|
|
126
|
+
* offsets of the root's `instance` and `module` are kept. svelte2tsx parses
|
|
127
|
+
* the same blanked source, but demotes only the tags the parser reads as
|
|
128
|
+
* nested elements, so a tag spelled inside an expression string or an
|
|
129
|
+
* attribute value is still a script to it and isn't one here.
|
|
130
|
+
*
|
|
131
|
+
* Limits: a `</script>` or `</style>` inside a string or comment ends that
|
|
132
|
+
* tag's blanked content early, and what's left over is parsed as template.
|
|
133
|
+
* And svelte2tsx's own tag matcher is narrower in one spelling: it doesn't
|
|
134
|
+
* take a closing tag with whitespace before the `>` (`</script >`), so there
|
|
135
|
+
* its virtual doesn't have the scripts as written while this returns them. A
|
|
136
|
+
* legacy component
|
|
137
|
+
* written that way documents no props, with a `svelte_prop_failed` warning
|
|
138
|
+
* for each `export let`; a runes one documents none, with no warning.
|
|
139
|
+
*
|
|
140
|
+
* @param svelteSource - the full Svelte source code
|
|
141
|
+
* @returns the top-level script tags and how they were decided
|
|
142
|
+
*/
|
|
143
|
+
export declare const findScriptTags: (svelteSource: string, options?: FindScriptTagsOptions) => FoundScriptTags;
|
|
144
|
+
//# sourceMappingURL=svelte-scripts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svelte-scripts.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/svelte-scripts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,oGAAoG;AACpG,MAAM,WAAW,eAAe;IAC/B,wEAAwE;IACxE,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,6FAA6F;IAC7F,KAAK,EAAE,MAAM,CAAC;CACd;AAED,yFAAyF;AACzF,MAAM,WAAW,gBAAiB,SAAQ,eAAe;IACxD,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,cAAc,EAAE,OAAO,CAAC;CACxB;AAwCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,cAAc,GAAI,cAAc,MAAM,KAAG,KAAK,CAAC,gBAAgB,CA8B3E,CAAC;AAiBF;;;;GAIG;AACH,eAAO,MAAM,cAAc,GAAI,YAAY,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,KAAG,OACL,CAAC;AAuDpE,oEAAoE;AACpE,MAAM,WAAW,qBAAqB;IACrC,6DAA6D;IAC7D,QAAQ,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpC,2DAA2D;IAC3D,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAClC;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,aAAa,EAAE,MAAM,KAAK,qBAAqB,CAAC;AAUnF;;;;;;;;GAQG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEhE,qCAAqC;AACrC,MAAM,WAAW,eAAe;IAC/B,oDAAoD;IACpD,IAAI,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IAC7B,SAAS,EAAE,kBAAkB,CAAC;CAC9B;AAED,oCAAoC;AACpC,MAAM,WAAW,qBAAqB;IACrC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,8EAA8E;IAC9E,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACpC;AAQD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,cAAc,GAC1B,cAAc,MAAM,EACpB,UAAU,qBAAqB,KAC7B,eAwBF,CAAC"}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locating a Svelte component's script tags: its instance `<script>` and its
|
|
3
|
+
* `<script module>`.
|
|
4
|
+
*
|
|
5
|
+
* Svelte takes a `<script>` for one of those only at the top level of the
|
|
6
|
+
* file. Nested in an element, a block, or `<svelte:head>` it's an ordinary
|
|
7
|
+
* element (a JSON-LD block, an inline analytics snippet), and its content says
|
|
8
|
+
* nothing about the component. So finding the tags takes two steps: a regex
|
|
9
|
+
* scan for candidates (`scanScriptTags`), then a decision about which of
|
|
10
|
+
* them are top-level (`findScriptTags`).
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
import { parse } from 'svelte/compiler';
|
|
17
|
+
/**
|
|
18
|
+
* Matches, left to right, an HTML comment or a script tag.
|
|
19
|
+
*
|
|
20
|
+
* Ports Svelte's preprocessor regex (`svelte/src/compiler/preprocess`):
|
|
21
|
+
* quote-aware attributes (`>` inside a value doesn't end the tag),
|
|
22
|
+
* commented-out scripts consumed by the comment alternative, a self-closing
|
|
23
|
+
* form with no content. Adaptations: the `d` flag for content offsets, no `i`
|
|
24
|
+
* flag (`<Script>` is a component), `\s*` before the closing `>` per the
|
|
25
|
+
* parser's `/<\/script\s*>/`.
|
|
26
|
+
*/
|
|
27
|
+
const SCRIPT_TAG_MATCHER = /<!--[^]*?-->|<script((?:\s+[^=>'"/\s]+=(?:"[^"]*"|'[^']*'|[^>\s]+)|\s+[^=>'"/\s]+)*\s*)(?:\/>|>([^]*?)<\/script\s*>)/dg;
|
|
28
|
+
/** `SCRIPT_TAG_MATCHER` for style tags, whose content is blanked before parsing. */
|
|
29
|
+
const STYLE_TAG_MATCHER = /<!--[^]*?-->|<style(?:\s+[^=>'"/\s]+=(?:"[^"]*"|'[^']*'|[^>\s]+)|\s+[^=>'"/\s]+)*\s*>([^]*?)<\/style\s*>/dg;
|
|
30
|
+
/**
|
|
31
|
+
* The attribute spellings the scan's proof accepts: names of word characters,
|
|
32
|
+
* values double-quoted, single-quoted, or bare word characters, whitespace
|
|
33
|
+
* between attributes. Narrower than `SCRIPT_TAG_MATCHER`'s attribute branch
|
|
34
|
+
* on purpose. That branch can backtrack into a second reading of the same
|
|
35
|
+
* text (an unterminated quote read as a bare value), and Svelte's parser,
|
|
36
|
+
* which never backtracks, may take the other one. This grammar has a single
|
|
37
|
+
* reading, the one the parser's `read_static_attribute` makes.
|
|
38
|
+
*/
|
|
39
|
+
const UNAMBIGUOUS_ATTRIBUTES_MATCHER = /^(?:[ \t\r\n]+[A-Za-z][\w-]*(?:=(?:"[^"]*"|'[^']*'|[\w.-]+))?)*[ \t\r\n]*$/;
|
|
40
|
+
const WHITESPACE_MATCHER = /\s*/y;
|
|
41
|
+
/** Whether `source` holds only whitespace from `start` up to `end`. */
|
|
42
|
+
const isWhitespaceBetween = (source, start, end) => {
|
|
43
|
+
WHITESPACE_MATCHER.lastIndex = start;
|
|
44
|
+
WHITESPACE_MATCHER.test(source);
|
|
45
|
+
return WHITESPACE_MATCHER.lastIndex >= end;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Scan Svelte source for script tags with content, in source order, and mark
|
|
49
|
+
* the ones the scan itself proves top-level.
|
|
50
|
+
*
|
|
51
|
+
* The scan is syntactic and knows nothing about nesting, so it returns nested
|
|
52
|
+
* tags too; `findScriptTags` is the function that answers which are the
|
|
53
|
+
* component's. Limits: whitespace around an attribute `=` makes a tag
|
|
54
|
+
* unmatchable (Svelte rejects one of those spellings, and svelte2tsx matches
|
|
55
|
+
* neither), and `</script>` in a string literal ends the content early.
|
|
56
|
+
*
|
|
57
|
+
* **`provenTopLevel`** holds for a tag when everything from the start of the
|
|
58
|
+
* file to it is whitespace, complete HTML comments, and earlier script tags
|
|
59
|
+
* that are themselves proven. Svelte's parser reads each of those at the root
|
|
60
|
+
* and comes back to the root: whitespace is text, a comment runs to the first
|
|
61
|
+
* `-->`, and a top-level script runs from its opening tag to the first
|
|
62
|
+
* `</script>`, the same extents the regex matched. So it meets this tag at the
|
|
63
|
+
* root, where a `<script>` is the instance or module script.
|
|
64
|
+
*
|
|
65
|
+
* Anything that would need more than that argument leaves the tag unproven,
|
|
66
|
+
* along with every tag after it:
|
|
67
|
+
*
|
|
68
|
+
* - any other text or markup before the tag: a `<style>`,
|
|
69
|
+
* `<svelte:options>`, an unterminated comment, what's left over when a
|
|
70
|
+
* `</script>` in a string ended an earlier match early, and tags the regex
|
|
71
|
+
* doesn't match (`<SCRIPT>`, a spelling with whitespace around `=`)
|
|
72
|
+
* - a self-closing `<script />`, which Svelte's parser rejects at the top
|
|
73
|
+
* level
|
|
74
|
+
* - attributes outside `UNAMBIGUOUS_ATTRIBUTES_MATCHER`
|
|
75
|
+
*/
|
|
76
|
+
export const scanScriptTags = (svelteSource) => {
|
|
77
|
+
const scripts = [];
|
|
78
|
+
// whether everything up to `cursor` is whitespace, comments, and proven scripts
|
|
79
|
+
let atRoot = true;
|
|
80
|
+
let cursor = 0;
|
|
81
|
+
SCRIPT_TAG_MATCHER.lastIndex = 0;
|
|
82
|
+
let match;
|
|
83
|
+
while ((match = SCRIPT_TAG_MATCHER.exec(svelteSource)) !== null) {
|
|
84
|
+
atRoot &&= isWhitespaceBetween(svelteSource, cursor, match.index);
|
|
85
|
+
cursor = match.index + match[0].length;
|
|
86
|
+
const attrs = match[1];
|
|
87
|
+
// the comment alternative captures no attributes
|
|
88
|
+
if (attrs === undefined)
|
|
89
|
+
continue;
|
|
90
|
+
const content = match[2];
|
|
91
|
+
if (content === undefined) {
|
|
92
|
+
// self-closing, so no content to return
|
|
93
|
+
atRoot = false;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
atRoot &&= UNAMBIGUOUS_ATTRIBUTES_MATCHER.test(attrs);
|
|
97
|
+
scripts.push({
|
|
98
|
+
attributes: parseAttributes(attrs),
|
|
99
|
+
content,
|
|
100
|
+
start: match.indices[2][0],
|
|
101
|
+
open: match.index,
|
|
102
|
+
end: cursor,
|
|
103
|
+
provenTopLevel: atRoot
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return scripts;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Parse an opening-tag attribute string into a name → value map — bare
|
|
110
|
+
* attributes map to `true`. The token grammar mirrors `SCRIPT_TAG_MATCHER`'s
|
|
111
|
+
* attribute branch, so anything that regex matched parses cleanly here.
|
|
112
|
+
*/
|
|
113
|
+
const parseAttributes = (attrs) => {
|
|
114
|
+
const attributes = new Map();
|
|
115
|
+
const attributeRegex = /([^=>'"/\s]+)(?:=(?:"([^"]*)"|'([^']*)'|([^>\s]+)))?/g;
|
|
116
|
+
let match;
|
|
117
|
+
while ((match = attributeRegex.exec(attrs)) !== null) {
|
|
118
|
+
attributes.set(match[1], match[2] ?? match[3] ?? match[4] ?? true);
|
|
119
|
+
}
|
|
120
|
+
return attributes;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* A script tag is a module script when it carries a `module` attribute
|
|
124
|
+
* (Svelte 5) or `context="module"` (Svelte 4) — Svelte's `read_script` test,
|
|
125
|
+
* minus its error paths. `module` inside an attribute *value* doesn't count.
|
|
126
|
+
*/
|
|
127
|
+
export const isModuleScript = (attributes) => attributes.has('module') || attributes.get('context') === 'module';
|
|
128
|
+
/**
|
|
129
|
+
* Copy `svelteSource` with the content of its script and style tags
|
|
130
|
+
* overwritten by spaces, newlines kept, so every offset and line still means
|
|
131
|
+
* what it meant. Script content needn't be JavaScript Svelte's parser accepts,
|
|
132
|
+
* and a nested tag's content would be read as template.
|
|
133
|
+
*
|
|
134
|
+
* A style tag that opens inside a script tag is text in that script, and the
|
|
135
|
+
* search for styles resumes after the script. A leading byte-order mark
|
|
136
|
+
* becomes a space too: Svelte's `parse` strips one, which would shift every
|
|
137
|
+
* offset it reports.
|
|
138
|
+
*/
|
|
139
|
+
const blankVerbatimContent = (svelteSource, scripts) => {
|
|
140
|
+
const ranges = scripts.map((tag) => [
|
|
141
|
+
tag.start,
|
|
142
|
+
tag.start + tag.content.length
|
|
143
|
+
]);
|
|
144
|
+
STYLE_TAG_MATCHER.lastIndex = 0;
|
|
145
|
+
let match;
|
|
146
|
+
while ((match = STYLE_TAG_MATCHER.exec(svelteSource)) !== null) {
|
|
147
|
+
const range = match.indices[1];
|
|
148
|
+
// the comment alternative captures no content
|
|
149
|
+
if (!range)
|
|
150
|
+
continue;
|
|
151
|
+
const open = match.index;
|
|
152
|
+
const enclosing = scripts.find((tag) => tag.open < open && open < tag.end);
|
|
153
|
+
if (enclosing) {
|
|
154
|
+
STYLE_TAG_MATCHER.lastIndex = enclosing.end;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
ranges.push(range);
|
|
158
|
+
}
|
|
159
|
+
// a style's content can hold a matched script, so ranges can overlap: each
|
|
160
|
+
// is clipped to what's still ahead of the cursor
|
|
161
|
+
ranges.sort((a, b) => a[0] - b[0]);
|
|
162
|
+
let blanked = '';
|
|
163
|
+
let cursor = 0;
|
|
164
|
+
if (svelteSource.charCodeAt(0) === 0xfeff) {
|
|
165
|
+
blanked = ' ';
|
|
166
|
+
cursor = 1;
|
|
167
|
+
}
|
|
168
|
+
for (const [rangeStart, end] of ranges) {
|
|
169
|
+
const start = Math.max(rangeStart, cursor);
|
|
170
|
+
if (end <= start)
|
|
171
|
+
continue;
|
|
172
|
+
blanked +=
|
|
173
|
+
svelteSource.slice(cursor, start) + svelteSource.slice(start, end).replace(/[^\n]/g, ' ');
|
|
174
|
+
cursor = end;
|
|
175
|
+
}
|
|
176
|
+
return blanked + svelteSource.slice(cursor);
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* The default `ParseSvelteTemplate`: Svelte's own parser. `loose` keeps it
|
|
180
|
+
* from throwing on a template error, the way svelte2tsx parses under
|
|
181
|
+
* `emitOnTemplateError`; `modern` skips the conversion to the legacy AST.
|
|
182
|
+
*/
|
|
183
|
+
const parseSvelteTemplate = (blankedSource) => parse(blankedSource, { modern: true, loose: true });
|
|
184
|
+
const toScriptTag = ({ attributes, content, start }) => ({
|
|
185
|
+
attributes,
|
|
186
|
+
content,
|
|
187
|
+
start
|
|
188
|
+
});
|
|
189
|
+
/**
|
|
190
|
+
* Find a component's script tags: the `<script>` tags at the top level of the
|
|
191
|
+
* source, in source order. A `<script>` nested in an element, a block, or
|
|
192
|
+
* `<svelte:head>` isn't one, matching Svelte, which reads only a top-level
|
|
193
|
+
* tag as the instance or module script.
|
|
194
|
+
*
|
|
195
|
+
* `scanScriptTags` finds the candidates. When it proves every one of them
|
|
196
|
+
* top-level, which covers the usual component with its scripts ahead of the
|
|
197
|
+
* template, that's the answer. Otherwise Svelte's parser decides: the source
|
|
198
|
+
* is parsed with script and style content blanked, and the tags at the
|
|
199
|
+
* offsets of the root's `instance` and `module` are kept. svelte2tsx parses
|
|
200
|
+
* the same blanked source, but demotes only the tags the parser reads as
|
|
201
|
+
* nested elements, so a tag spelled inside an expression string or an
|
|
202
|
+
* attribute value is still a script to it and isn't one here.
|
|
203
|
+
*
|
|
204
|
+
* Limits: a `</script>` or `</style>` inside a string or comment ends that
|
|
205
|
+
* tag's blanked content early, and what's left over is parsed as template.
|
|
206
|
+
* And svelte2tsx's own tag matcher is narrower in one spelling: it doesn't
|
|
207
|
+
* take a closing tag with whitespace before the `>` (`</script >`), so there
|
|
208
|
+
* its virtual doesn't have the scripts as written while this returns them. A
|
|
209
|
+
* legacy component
|
|
210
|
+
* written that way documents no props, with a `svelte_prop_failed` warning
|
|
211
|
+
* for each `export let`; a runes one documents none, with no warning.
|
|
212
|
+
*
|
|
213
|
+
* @param svelteSource - the full Svelte source code
|
|
214
|
+
* @returns the top-level script tags and how they were decided
|
|
215
|
+
*/
|
|
216
|
+
export const findScriptTags = (svelteSource, options) => {
|
|
217
|
+
const scripts = scanScriptTags(svelteSource);
|
|
218
|
+
if (scripts.length === 0)
|
|
219
|
+
return { tags: [], decidedBy: 'scan' };
|
|
220
|
+
if (options?.scanProof !== false && scripts.every((tag) => tag.provenTopLevel)) {
|
|
221
|
+
return { tags: scripts.map(toScriptTag), decidedBy: 'scan' };
|
|
222
|
+
}
|
|
223
|
+
const parseTemplate = options?.parseTemplate ?? parseSvelteTemplate;
|
|
224
|
+
let parsed;
|
|
225
|
+
try {
|
|
226
|
+
parsed = parseTemplate(blankVerbatimContent(svelteSource, scripts));
|
|
227
|
+
}
|
|
228
|
+
catch (_) {
|
|
229
|
+
// Every matched tag, nested ones included: the answer the scan alone
|
|
230
|
+
// gives. Svelte's parser throws here on a template error when it predates
|
|
231
|
+
// `loose` parsing (Svelte 5.13), which the peer range (5.0 and up) still
|
|
232
|
+
// admits, and in any version on the few errors `loose` doesn't absorb.
|
|
233
|
+
// TODO: remove this fallback when the `svelte` peer floor reaches a
|
|
234
|
+
// version with `loose` parsing
|
|
235
|
+
return { tags: scripts.map(toScriptTag), decidedBy: 'unparsed' };
|
|
236
|
+
}
|
|
237
|
+
const topLevel = new Set([parsed.instance?.start, parsed.module?.start]);
|
|
238
|
+
return {
|
|
239
|
+
tags: scripts.filter((tag) => topLevel.has(tag.open)).map(toScriptTag),
|
|
240
|
+
decidedBy: 'parser'
|
|
241
|
+
};
|
|
242
|
+
};
|
package/dist/svelte.d.ts
CHANGED
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
* @see `typescript-extract-type-json.ts` for `resolveTypeInfo`, `referenceSymbolName`, `tupleElements`, `restElementForms`
|
|
20
20
|
* @see `tsdoc.ts` for `parseComment`, `applyToDeclaration`
|
|
21
21
|
* @see `source.ts` for `SourceFileInfo`, `getComponentName`
|
|
22
|
+
* @see `svelte-legacy.ts` for `scanLegacyExports`, `resolveLegacyPropsAnchor`
|
|
23
|
+
* @see `svelte-scripts.ts` for `findScriptTags`, `isModuleScript`
|
|
22
24
|
*
|
|
23
25
|
* @module
|
|
24
26
|
*/
|
|
@@ -31,6 +33,7 @@ import { type AliasRegistry } from './typescript-extract-type-json.js';
|
|
|
31
33
|
import { type SourceFileInfo } from './source.js';
|
|
32
34
|
import { type ModuleSourceOptions } from './source-config.js';
|
|
33
35
|
import { type Diagnostic } from './diagnostics.js';
|
|
36
|
+
import { type SvelteScriptTag } from './svelte-scripts.js';
|
|
34
37
|
/** Resolved source map type (avoids repeating the verbose `InstanceType<...>` inline). */
|
|
35
38
|
type SourceMap = InstanceType<typeof TraceMap>;
|
|
36
39
|
/**
|
|
@@ -55,6 +58,16 @@ export interface SvelteVirtualFile {
|
|
|
55
58
|
* derived from it at output.
|
|
56
59
|
*/
|
|
57
60
|
scriptKind: ts.ScriptKind;
|
|
61
|
+
/**
|
|
62
|
+
* The component's script tags as located in the original source: the
|
|
63
|
+
* top-level `<script>` tags, in source order (`findScriptTags`).
|
|
64
|
+
* `transformSvelteSource` locates them once, and `analyzeSvelteModule`
|
|
65
|
+
* reads the instance and module scripts from here on every analysis.
|
|
66
|
+
*
|
|
67
|
+
* Optional so a virtual built by hand needs none: `analyzeSvelteModule`
|
|
68
|
+
* then locates them in the source it's given.
|
|
69
|
+
*/
|
|
70
|
+
scriptTags?: ReadonlyArray<SvelteScriptTag>;
|
|
58
71
|
}
|
|
59
72
|
/**
|
|
60
73
|
* Result of `transformSvelteSource` — a virtual file (when transform succeeded)
|
|
@@ -92,18 +105,24 @@ export interface TransformResult {
|
|
|
92
105
|
*/
|
|
93
106
|
export declare const transformSvelteSource: (sourceFile: SourceFileInfo) => TransformResult;
|
|
94
107
|
/**
|
|
95
|
-
* Extract the content of the
|
|
96
|
-
*
|
|
97
|
-
* `<script context="module">`)
|
|
108
|
+
* Extract the content of the component's instance `<script>` from Svelte
|
|
109
|
+
* source: the top-level one that isn't a module script (`<script module>`,
|
|
110
|
+
* `<script context="module">`). A commented-out script doesn't count, and
|
|
111
|
+
* neither does one nested in the template (`<svelte:head>`, an element, a
|
|
112
|
+
* block), which is an ordinary element.
|
|
113
|
+
*
|
|
114
|
+
* Locates the tags itself on every call (`findScriptTags`), parsing the
|
|
115
|
+
* template when the scan can't prove them top-level.
|
|
98
116
|
*
|
|
99
|
-
* @returns the script tag content, or undefined if
|
|
117
|
+
* @returns the script tag content, or undefined if the source has no instance script
|
|
100
118
|
*/
|
|
101
119
|
export declare const extractScriptContent: (svelteSource: string) => string | undefined;
|
|
102
120
|
/**
|
|
103
|
-
* Extract the content of the module `<script>`
|
|
121
|
+
* Extract the content of the component's module `<script>` from Svelte source.
|
|
104
122
|
*
|
|
105
|
-
* Counterpart of `extractScriptContent`: matches only module
|
|
106
|
-
*
|
|
123
|
+
* Counterpart of `extractScriptContent`: matches only the module script, under
|
|
124
|
+
* the same top-level rule and at the same per-call cost, so the two partition
|
|
125
|
+
* a source's script tags consistently.
|
|
107
126
|
*
|
|
108
127
|
* @returns the module script tag content, or undefined if no module script is found
|
|
109
128
|
*/
|
|
@@ -253,10 +272,13 @@ export declare const synthesizeSnippetTypeSignature: (parameters: Array<Paramete
|
|
|
253
272
|
* 2. svelte2tsx internals are filtered (`isSvelte2tsxGeneratedExport`) and
|
|
254
273
|
* exported snippets reclassified to `kind: 'snippet'` (`isSnippetReturnType`);
|
|
255
274
|
* 2b remaps module-level `sourceLine`s to the original `.svelte` source
|
|
256
|
-
* 3. the component declaration is synthesized: props via `extractPropsViaChecker
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
275
|
+
* 3. the component declaration is synthesized: props via `extractPropsViaChecker`
|
|
276
|
+
* from the component's `PropsAnchor` (`resolveRunesPropsAnchor` for a
|
|
277
|
+
* `$props()` declaration, else `resolveExportLetPropsAnchor` for a legacy
|
|
278
|
+
* component's exports), `acceptsChildren` from a `Snippet`-typed `children`
|
|
279
|
+
* prop, template use (`__sveltets_2_ensureSnippet`) or, failing those, a
|
|
280
|
+
* legacy component's default slot (`hasDefaultSlot`), plus generics, doc
|
|
281
|
+
* comment, and source line from the landmarks `findComponentNodes` locates
|
|
260
282
|
* 4. the module comment is read from the original source
|
|
261
283
|
* 5. declarations combine, component first
|
|
262
284
|
* 6. dependencies are extracted
|
package/dist/svelte.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/svelte.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"svelte.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/svelte.ts"],"names":[],"mappings":"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"}
|