@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.
@@ -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.0",
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.0",
36
- "@markuplint/ml-ast": "5.0.0-rc.0",
37
- "@markuplint/parser-utils": "5.0.0-rc.0",
38
- "svelte": "5.53.10"
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": "ebf4d7cfca0c259aead3b292c6b8a202db4cd802"
44
+ "gitHead": "0d6b4324d9a7d6b9e1ba57d4a57e45d36975cba9"
41
45
  }