@markuplint/svelte-parser 5.0.0-rc.0 → 5.0.0-rc.1
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/lib/component-scanner.d.ts +37 -0
- package/lib/component-scanner.js +103 -0
- package/lib/parser.js +1 -0
- package/package.json +10 -6
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of scanning a single component file for its root element information.
|
|
3
|
+
*/
|
|
4
|
+
export interface ComponentScanResult {
|
|
5
|
+
readonly rootElement: string | null;
|
|
6
|
+
readonly attrs: readonly ComponentScanAttr[];
|
|
7
|
+
readonly hasSlots: boolean;
|
|
8
|
+
readonly scriptSource?: ComponentScanScriptSource;
|
|
9
|
+
readonly namespace?: 'svg';
|
|
10
|
+
readonly line?: number;
|
|
11
|
+
readonly col?: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* A static attribute extracted from a component's root element.
|
|
15
|
+
*/
|
|
16
|
+
export interface ComponentScanAttr {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly value?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A script/ESM source block extracted from a component file.
|
|
22
|
+
*/
|
|
23
|
+
export interface ComponentScanScriptSource {
|
|
24
|
+
readonly content: string;
|
|
25
|
+
readonly offset: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Component scanner for Svelte component files.
|
|
29
|
+
*
|
|
30
|
+
* Parses a Svelte component using markuplint's Svelte parser, extracts the root
|
|
31
|
+
* element at depth=0, detects static attributes, slot/render usage, and the
|
|
32
|
+
* `<script>` block for import analysis.
|
|
33
|
+
*/
|
|
34
|
+
export declare const componentScanner: {
|
|
35
|
+
scanComponent(sourceCode: string): ComponentScanResult | null;
|
|
36
|
+
extractScriptSource(sourceCode: string): ComponentScanScriptSource | null;
|
|
37
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { parser } from './parser.js';
|
|
2
|
+
/**
|
|
3
|
+
* Extracts root element information from a parsed MLAST document.
|
|
4
|
+
*/
|
|
5
|
+
function extractComponentInfo(doc) {
|
|
6
|
+
const root = doc.nodeList.find((n) => n.type === 'starttag' && n.depth === 0 && !n.isFragment);
|
|
7
|
+
if (!root) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
const attrs = [];
|
|
11
|
+
for (const attr of root.attributes) {
|
|
12
|
+
if (attr.type !== 'attr') {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const value = attr.value.raw;
|
|
16
|
+
if (value === '') {
|
|
17
|
+
attrs.push({ name: attr.nodeName });
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
attrs.push({ name: attr.nodeName, value });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
rootElement: root.nodeName,
|
|
25
|
+
attrs,
|
|
26
|
+
namespace: root.namespace === 'http://www.w3.org/2000/svg' ? 'svg' : undefined,
|
|
27
|
+
line: root.line,
|
|
28
|
+
col: root.col,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Detects whether the parsed Svelte template contains slot usage.
|
|
33
|
+
*
|
|
34
|
+
* Supports:
|
|
35
|
+
* - Svelte 4: `<slot>` element (parsed as psblock `#ps:SlotElement`)
|
|
36
|
+
* - Svelte 5: `{@render children()}` (parsed as psblock `#ps:RenderTag`)
|
|
37
|
+
* - Standard `<slot>` elements
|
|
38
|
+
*/
|
|
39
|
+
function detectSlots(doc) {
|
|
40
|
+
return doc.nodeList.some(n => (n.type === 'starttag' && n.nodeName === 'slot') ||
|
|
41
|
+
(n.type === 'psblock' && (n.nodeName === '#ps:SlotElement' || n.nodeName === '#ps:RenderTag')));
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Extracts the instance `<script>` block from a Svelte component source.
|
|
45
|
+
* Prefers the instance script over `<script context="module">`.
|
|
46
|
+
*/
|
|
47
|
+
function extractSvelteScript(source) {
|
|
48
|
+
const re = /<script(?:\s[^>]*)?>/gi;
|
|
49
|
+
let match;
|
|
50
|
+
let moduleBlock = null;
|
|
51
|
+
while ((match = re.exec(source)) !== null) {
|
|
52
|
+
const startTag = match[0];
|
|
53
|
+
const isModule = /\bcontext\s*=\s*["']module["']/i.test(startTag);
|
|
54
|
+
const contentStart = match.index + startTag.length;
|
|
55
|
+
const endTagRe = /<\/script\s*>/i;
|
|
56
|
+
const remaining = source.slice(contentStart);
|
|
57
|
+
const endMatch = endTagRe.exec(remaining);
|
|
58
|
+
if (!endMatch) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const block = {
|
|
62
|
+
content: remaining.slice(0, endMatch.index),
|
|
63
|
+
offset: contentStart,
|
|
64
|
+
};
|
|
65
|
+
if (!isModule) {
|
|
66
|
+
return block; // Prefer instance script
|
|
67
|
+
}
|
|
68
|
+
// Remember module script as fallback
|
|
69
|
+
moduleBlock ??= block;
|
|
70
|
+
}
|
|
71
|
+
return moduleBlock;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Component scanner for Svelte component files.
|
|
75
|
+
*
|
|
76
|
+
* Parses a Svelte component using markuplint's Svelte parser, extracts the root
|
|
77
|
+
* element at depth=0, detects static attributes, slot/render usage, and the
|
|
78
|
+
* `<script>` block for import analysis.
|
|
79
|
+
*/
|
|
80
|
+
export const componentScanner = {
|
|
81
|
+
scanComponent(sourceCode) {
|
|
82
|
+
let doc;
|
|
83
|
+
try {
|
|
84
|
+
doc = parser.parse(sourceCode);
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (error instanceof SyntaxError || (error instanceof Error && error.constructor.name === 'ParserError')) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
const info = extractComponentInfo(doc);
|
|
93
|
+
if (!info) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const hasSlots = detectSlots(doc);
|
|
97
|
+
const scriptSource = extractSvelteScript(sourceCode) ?? undefined;
|
|
98
|
+
return { ...info, hasSlots, scriptSource };
|
|
99
|
+
},
|
|
100
|
+
extractScriptSource(sourceCode) {
|
|
101
|
+
return extractSvelteScript(sourceCode);
|
|
102
|
+
},
|
|
103
|
+
};
|
package/lib/parser.js
CHANGED
|
@@ -231,6 +231,7 @@ export class SvelteParser extends Parser {
|
|
|
231
231
|
if (node.nodeName === '#text' && /^<script[\s>]/i.test(node.raw)) {
|
|
232
232
|
return this.visitPsBlock({
|
|
233
233
|
...node,
|
|
234
|
+
parentNode: node.parentNode ?? null,
|
|
234
235
|
nodeName: 'Script',
|
|
235
236
|
isFragment: false,
|
|
236
237
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/svelte-parser",
|
|
3
|
-
"version": "5.0.0-rc.
|
|
3
|
+
"version": "5.0.0-rc.1",
|
|
4
4
|
"description": "Svelte parser for markuplint",
|
|
5
5
|
"repository": "git@github.com:markuplint/markuplint.git",
|
|
6
6
|
"author": "Yusuke Hirao <yusukehirao@me.com>",
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"./kit": {
|
|
18
18
|
"import": "./lib/sveltekit-parser.js",
|
|
19
19
|
"types": "./lib/sveltekit-parser.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./component-scanner": {
|
|
22
|
+
"import": "./lib/component-scanner.js",
|
|
23
|
+
"types": "./lib/component-scanner.d.ts"
|
|
20
24
|
}
|
|
21
25
|
},
|
|
22
26
|
"types": "lib/index.d.ts",
|
|
@@ -32,10 +36,10 @@
|
|
|
32
36
|
"clean": "tsc --build --clean tsconfig.build.json"
|
|
33
37
|
},
|
|
34
38
|
"dependencies": {
|
|
35
|
-
"@markuplint/html-parser": "5.0.0-rc.
|
|
36
|
-
"@markuplint/ml-ast": "5.0.0-rc.
|
|
37
|
-
"@markuplint/parser-utils": "5.0.0-rc.
|
|
38
|
-
"svelte": "5.
|
|
39
|
+
"@markuplint/html-parser": "5.0.0-rc.1",
|
|
40
|
+
"@markuplint/ml-ast": "5.0.0-rc.1",
|
|
41
|
+
"@markuplint/parser-utils": "5.0.0-rc.1",
|
|
42
|
+
"svelte": "5.55.0"
|
|
39
43
|
},
|
|
40
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "0d6b4324d9a7d6b9e1ba57d4a57e45d36975cba9"
|
|
41
45
|
}
|