@markuplint/parser-utils 4.8.10 → 5.0.0-alpha.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/ARCHITECTURE.ja.md +208 -0
- package/ARCHITECTURE.md +251 -0
- package/CHANGELOG.md +33 -2
- package/README.md +6 -0
- package/SKILL.md +126 -0
- package/docs/maintenance.ja.md +176 -0
- package/docs/maintenance.md +176 -0
- package/docs/parser-class.ja.md +655 -0
- package/docs/parser-class.md +655 -0
- package/lib/debug.js +8 -24
- package/lib/debugger.d.ts +25 -0
- package/lib/debugger.js +34 -4
- package/lib/enums.d.ts +10 -0
- package/lib/enums.js +10 -0
- package/lib/get-location.d.ts +31 -0
- package/lib/get-location.js +33 -0
- package/lib/get-namespace.d.ts +11 -0
- package/lib/get-namespace.js +38 -0
- package/lib/idl-attributes.d.ts +9 -0
- package/lib/idl-attributes.js +9 -0
- package/lib/ignore-block.js +15 -14
- package/lib/index.d.ts +2 -1
- package/lib/index.js +1 -1
- package/lib/parser-error.d.ts +16 -0
- package/lib/parser-error.js +20 -3
- package/lib/parser.d.ts +285 -7
- package/lib/parser.js +763 -551
- package/lib/script-parser.d.ts +21 -0
- package/lib/script-parser.js +17 -0
- package/lib/sort-nodes.d.ts +8 -0
- package/lib/sort-nodes.js +11 -3
- package/lib/types.d.ts +60 -3
- package/package.json +11 -10
package/lib/debug.js
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
-
};
|
|
6
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
-
};
|
|
12
|
-
var _PerformanceTimer_logs, _PerformanceTimer_counter;
|
|
13
1
|
import debug from 'debug';
|
|
14
2
|
import { nodeListToDebugMaps } from './debugger.js';
|
|
15
3
|
export const log = debug('ml-parser');
|
|
@@ -17,32 +5,29 @@ export function domLog(nodeList) {
|
|
|
17
5
|
log('Parse result: %O', nodeListToDebugMaps(nodeList, true));
|
|
18
6
|
}
|
|
19
7
|
export class PerformanceTimer {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
_PerformanceTimer_counter.set(this, -1);
|
|
23
|
-
}
|
|
8
|
+
#logs = [];
|
|
9
|
+
#counter = -1;
|
|
24
10
|
push(name) {
|
|
25
|
-
var _a;
|
|
26
11
|
if (!log.enabled) {
|
|
27
12
|
return '';
|
|
28
13
|
}
|
|
29
|
-
|
|
14
|
+
this.#counter++;
|
|
30
15
|
const now = performance.now();
|
|
31
|
-
const last =
|
|
16
|
+
const last = this.#logs.at(-1);
|
|
32
17
|
if (last && Number.isNaN(last[2])) {
|
|
33
18
|
last[2] = now;
|
|
34
19
|
}
|
|
35
|
-
name = name || `#${
|
|
36
|
-
|
|
20
|
+
name = name || `#${this.#counter}`;
|
|
21
|
+
this.#logs.push([name, now, Number.NaN]);
|
|
37
22
|
}
|
|
38
23
|
log() {
|
|
39
24
|
if (!log.enabled) {
|
|
40
25
|
return;
|
|
41
26
|
}
|
|
42
27
|
this.push('end');
|
|
43
|
-
|
|
28
|
+
this.#logs.pop();
|
|
44
29
|
const map = new Map();
|
|
45
|
-
for (const content of
|
|
30
|
+
for (const content of this.#logs) {
|
|
46
31
|
const diff = content[2] - content[1];
|
|
47
32
|
const name = content[0];
|
|
48
33
|
if (map.has(name)) {
|
|
@@ -59,4 +44,3 @@ export class PerformanceTimer {
|
|
|
59
44
|
}
|
|
60
45
|
}
|
|
61
46
|
}
|
|
62
|
-
_PerformanceTimer_logs = new WeakMap(), _PerformanceTimer_counter = new WeakMap();
|
package/lib/debugger.d.ts
CHANGED
|
@@ -1,4 +1,29 @@
|
|
|
1
1
|
import type { MLASTAttr, MLASTNode } from '@markuplint/ml-ast';
|
|
2
|
+
/**
|
|
3
|
+
* Converts a list of AST nodes into human-readable debug strings showing
|
|
4
|
+
* each node's position, type, and raw content. Useful for snapshot testing.
|
|
5
|
+
*
|
|
6
|
+
* @param nodeList - The flat list of AST nodes to convert
|
|
7
|
+
* @param withAttr - Whether to include detailed attribute debug info for start tags
|
|
8
|
+
* @returns An array of formatted debug strings, one per node (plus attribute lines when enabled)
|
|
9
|
+
*/
|
|
2
10
|
export declare function nodeListToDebugMaps(nodeList: readonly (MLASTNode | null)[], withAttr?: boolean): string[];
|
|
11
|
+
/**
|
|
12
|
+
* Converts a list of AST attributes into detailed debug strings showing
|
|
13
|
+
* each attribute's components (name, equal sign, value, quotes) with
|
|
14
|
+
* their positions and additional metadata like directives and dynamic values.
|
|
15
|
+
*
|
|
16
|
+
* @param attributes - The list of attributes to convert into debug representations
|
|
17
|
+
* @returns An array of string arrays, one inner array per attribute containing its debug lines
|
|
18
|
+
*/
|
|
3
19
|
export declare function attributesToDebugMaps(attributes: readonly MLASTAttr[]): string[][];
|
|
20
|
+
/**
|
|
21
|
+
* Produces a tree-style debug view of AST nodes, showing indentation
|
|
22
|
+
* based on depth, parent-child relationships, pair node links,
|
|
23
|
+
* and ghost/bogus markers. Useful for visualizing the parsed DOM structure.
|
|
24
|
+
*
|
|
25
|
+
* @param nodeTree - The flat list of AST nodes to visualize as a tree
|
|
26
|
+
* @param idFilter - Whether to replace UUIDs with short sequential hex IDs for readability
|
|
27
|
+
* @returns An array of formatted strings representing the tree view
|
|
28
|
+
*/
|
|
4
29
|
export declare function nodeTreeDebugView(nodeTree: readonly MLASTNode[], idFilter?: boolean): (string | undefined)[];
|
package/lib/debugger.js
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
|
+
import { getEndCol, getEndLine } from './get-location.js';
|
|
2
|
+
/**
|
|
3
|
+
* Converts a list of AST nodes into human-readable debug strings showing
|
|
4
|
+
* each node's position, type, and raw content. Useful for snapshot testing.
|
|
5
|
+
*
|
|
6
|
+
* @param nodeList - The flat list of AST nodes to convert
|
|
7
|
+
* @param withAttr - Whether to include detailed attribute debug info for start tags
|
|
8
|
+
* @returns An array of formatted debug strings, one per node (plus attribute lines when enabled)
|
|
9
|
+
*/
|
|
1
10
|
export function nodeListToDebugMaps(nodeList, withAttr = false) {
|
|
2
11
|
return nodeList.flatMap(n => {
|
|
3
|
-
const r = [];
|
|
4
|
-
r.push(tokenDebug(n));
|
|
12
|
+
const r = [tokenDebug(n)];
|
|
5
13
|
if (withAttr && n && n.type === 'starttag') {
|
|
6
14
|
r.push(...attributesToDebugMaps(n.attributes).flat());
|
|
7
15
|
}
|
|
8
16
|
return r;
|
|
9
17
|
});
|
|
10
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Converts a list of AST attributes into detailed debug strings showing
|
|
21
|
+
* each attribute's components (name, equal sign, value, quotes) with
|
|
22
|
+
* their positions and additional metadata like directives and dynamic values.
|
|
23
|
+
*
|
|
24
|
+
* @param attributes - The list of attributes to convert into debug representations
|
|
25
|
+
* @returns An array of string arrays, one inner array per attribute containing its debug lines
|
|
26
|
+
*/
|
|
11
27
|
export function attributesToDebugMaps(attributes) {
|
|
12
28
|
return attributes.map(n => {
|
|
13
29
|
const r = [
|
|
@@ -36,6 +52,15 @@ export function attributesToDebugMaps(attributes) {
|
|
|
36
52
|
return r;
|
|
37
53
|
});
|
|
38
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Produces a tree-style debug view of AST nodes, showing indentation
|
|
57
|
+
* based on depth, parent-child relationships, pair node links,
|
|
58
|
+
* and ghost/bogus markers. Useful for visualizing the parsed DOM structure.
|
|
59
|
+
*
|
|
60
|
+
* @param nodeTree - The flat list of AST nodes to visualize as a tree
|
|
61
|
+
* @param idFilter - Whether to replace UUIDs with short sequential hex IDs for readability
|
|
62
|
+
* @returns An array of formatted strings representing the tree view
|
|
63
|
+
*/
|
|
39
64
|
export function nodeTreeDebugView(nodeTree, idFilter = false) {
|
|
40
65
|
const filter = idFilter ? uuidFilter : (id) => id;
|
|
41
66
|
return nodeTree
|
|
@@ -61,9 +86,14 @@ function tokenDebug(n, type = '') {
|
|
|
61
86
|
if (!n) {
|
|
62
87
|
return 'NULL';
|
|
63
88
|
}
|
|
64
|
-
|
|
89
|
+
const endOffset = n.offset + n.raw.length;
|
|
90
|
+
const endLine = getEndLine(n.raw, n.line);
|
|
91
|
+
const endCol = getEndCol(n.raw, n.col);
|
|
92
|
+
return `[${n.line}:${n.col}]>[${endLine}:${endCol}](${n.offset},${endOffset})${
|
|
65
93
|
// @ts-ignore
|
|
66
|
-
n.potentialName ?? n.nodeName ?? n.name ?? n.type ?? type}${'isGhost' in n && n.isGhost ? '(👻)' : ''}${'isBogus' in n && n.isBogus ? '(👿)' : ''}${
|
|
94
|
+
n.potentialName ?? n.nodeName ?? n.name ?? n.type ?? type}${'isGhost' in n && n.isGhost ? '(👻)' : ''}${'isBogus' in n && n.isBogus ? '(👿)' : ''}${
|
|
95
|
+
// @ts-ignore
|
|
96
|
+
n.blockBehavior?.type ? ` (${n.blockBehavior.type})` : ''}: ${visibleWhiteSpace(n.raw)}`;
|
|
67
97
|
}
|
|
68
98
|
function visibleWhiteSpace(chars) {
|
|
69
99
|
return chars.replaceAll('\n', '⏎').replaceAll('\t', '→').replaceAll(/\s/g, '␣');
|
package/lib/enums.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* States of the tag-level state machine used during tokenization.
|
|
3
|
+
* Transitions drive the parser through detecting the opening bracket,
|
|
4
|
+
* tag name, attributes, and closing bracket of an HTML/XML tag.
|
|
5
|
+
*/
|
|
1
6
|
export declare enum TagState {
|
|
2
7
|
BeforeOpenTag = 0,
|
|
3
8
|
FirstCharOfTagName = 1,
|
|
@@ -6,6 +11,11 @@ export declare enum TagState {
|
|
|
6
11
|
AfterAttrs = 4,
|
|
7
12
|
AfterOpenTag = 5
|
|
8
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* States of the attribute-level state machine used during attribute tokenization.
|
|
16
|
+
* Transitions drive the parser through the attribute name, equals sign,
|
|
17
|
+
* and value portions of an HTML/XML attribute.
|
|
18
|
+
*/
|
|
9
19
|
export declare enum AttrState {
|
|
10
20
|
BeforeName = 0,
|
|
11
21
|
Name = 1,
|
package/lib/enums.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* States of the tag-level state machine used during tokenization.
|
|
3
|
+
* Transitions drive the parser through detecting the opening bracket,
|
|
4
|
+
* tag name, attributes, and closing bracket of an HTML/XML tag.
|
|
5
|
+
*/
|
|
1
6
|
export var TagState;
|
|
2
7
|
(function (TagState) {
|
|
3
8
|
TagState[TagState["BeforeOpenTag"] = 0] = "BeforeOpenTag";
|
|
@@ -7,6 +12,11 @@ export var TagState;
|
|
|
7
12
|
TagState[TagState["AfterAttrs"] = 4] = "AfterAttrs";
|
|
8
13
|
TagState[TagState["AfterOpenTag"] = 5] = "AfterOpenTag";
|
|
9
14
|
})(TagState || (TagState = {}));
|
|
15
|
+
/**
|
|
16
|
+
* States of the attribute-level state machine used during attribute tokenization.
|
|
17
|
+
* Transitions drive the parser through the attribute name, equals sign,
|
|
18
|
+
* and value portions of an HTML/XML attribute.
|
|
19
|
+
*/
|
|
10
20
|
export var AttrState;
|
|
11
21
|
(function (AttrState) {
|
|
12
22
|
AttrState[AttrState["BeforeName"] = 0] = "BeforeName";
|
package/lib/get-location.d.ts
CHANGED
|
@@ -6,12 +6,43 @@ export declare function getLine(rawCodeFragment: string, startOffset: number): n
|
|
|
6
6
|
* @deprecated Use {@link getPosition} instead. Will be removed in v5.0.0.
|
|
7
7
|
*/
|
|
8
8
|
export declare function getCol(rawCodeFragment: string, startOffset: number): number;
|
|
9
|
+
/**
|
|
10
|
+
* Computes the line and column of a position within a code fragment.
|
|
11
|
+
*
|
|
12
|
+
* @param rawCodeFragment - The full raw source text
|
|
13
|
+
* @param startOffset - The zero-based byte offset to compute the position of
|
|
14
|
+
* @returns An object containing one-based `line` and `column`
|
|
15
|
+
*/
|
|
9
16
|
export declare function getPosition(rawCodeFragment: string, startOffset: number): {
|
|
10
17
|
readonly line: number;
|
|
11
18
|
readonly column: number;
|
|
12
19
|
};
|
|
13
20
|
export declare function getEndLine(rawCodeFragment: string, startLine: number): number;
|
|
14
21
|
export declare function getEndCol(rawCodeFragment: string, startCol: number): number;
|
|
22
|
+
/**
|
|
23
|
+
* Computes the end position of a code fragment given its start position.
|
|
24
|
+
*
|
|
25
|
+
* @param rawCodeFragment - The raw source text of the fragment
|
|
26
|
+
* @param startOffset - The zero-based byte offset where the fragment starts
|
|
27
|
+
* @param startLine - The one-based line number where the fragment starts
|
|
28
|
+
* @param startCol - The one-based column number where the fragment starts
|
|
29
|
+
* @returns An object containing `endOffset`, `endLine`, and `endCol`
|
|
30
|
+
*/
|
|
31
|
+
export declare function getEndPosition(rawCodeFragment: string, startOffset: number, startLine: number, startCol: number): {
|
|
32
|
+
endOffset: number;
|
|
33
|
+
endLine: number;
|
|
34
|
+
endCol: number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Converts line/column ranges to byte offsets within a code string.
|
|
38
|
+
*
|
|
39
|
+
* @param rawCode - The full raw source text
|
|
40
|
+
* @param startLine - The one-based start line number
|
|
41
|
+
* @param startCol - The one-based start column number
|
|
42
|
+
* @param endLine - The one-based end line number
|
|
43
|
+
* @param endCol - The one-based end column number
|
|
44
|
+
* @returns An object containing zero-based `offset` and `endOffset`
|
|
45
|
+
*/
|
|
15
46
|
export declare function getOffsetsFromCode(rawCode: string, startLine: number, startCol: number, endLine: number, endCol: number): {
|
|
16
47
|
offset: number;
|
|
17
48
|
endOffset: number;
|
package/lib/get-location.js
CHANGED
|
@@ -12,6 +12,13 @@ export function getCol(rawCodeFragment, startOffset) {
|
|
|
12
12
|
const lines = rawCodeFragment.slice(0, startOffset).split(LINE_BREAK);
|
|
13
13
|
return (lines.at(-1) ?? '').length + 1;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Computes the line and column of a position within a code fragment.
|
|
17
|
+
*
|
|
18
|
+
* @param rawCodeFragment - The full raw source text
|
|
19
|
+
* @param startOffset - The zero-based byte offset to compute the position of
|
|
20
|
+
* @returns An object containing one-based `line` and `column`
|
|
21
|
+
*/
|
|
15
22
|
export function getPosition(rawCodeFragment, startOffset) {
|
|
16
23
|
const lines = rawCodeFragment.slice(0, startOffset).split(LINE_BREAK);
|
|
17
24
|
const line = lines.length;
|
|
@@ -27,6 +34,32 @@ export function getEndCol(rawCodeFragment, startCol) {
|
|
|
27
34
|
const lastLine = lines.pop();
|
|
28
35
|
return lineCount > 1 ? lastLine.length + 1 : startCol + rawCodeFragment.length;
|
|
29
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Computes the end position of a code fragment given its start position.
|
|
39
|
+
*
|
|
40
|
+
* @param rawCodeFragment - The raw source text of the fragment
|
|
41
|
+
* @param startOffset - The zero-based byte offset where the fragment starts
|
|
42
|
+
* @param startLine - The one-based line number where the fragment starts
|
|
43
|
+
* @param startCol - The one-based column number where the fragment starts
|
|
44
|
+
* @returns An object containing `endOffset`, `endLine`, and `endCol`
|
|
45
|
+
*/
|
|
46
|
+
export function getEndPosition(rawCodeFragment, startOffset, startLine, startCol) {
|
|
47
|
+
return {
|
|
48
|
+
endOffset: startOffset + rawCodeFragment.length,
|
|
49
|
+
endLine: getEndLine(rawCodeFragment, startLine),
|
|
50
|
+
endCol: getEndCol(rawCodeFragment, startCol),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Converts line/column ranges to byte offsets within a code string.
|
|
55
|
+
*
|
|
56
|
+
* @param rawCode - The full raw source text
|
|
57
|
+
* @param startLine - The one-based start line number
|
|
58
|
+
* @param startCol - The one-based start column number
|
|
59
|
+
* @param endLine - The one-based end line number
|
|
60
|
+
* @param endCol - The one-based end column number
|
|
61
|
+
* @returns An object containing zero-based `offset` and `endOffset`
|
|
62
|
+
*/
|
|
30
63
|
export function getOffsetsFromCode(rawCode, startLine, startCol, endLine, endCol) {
|
|
31
64
|
const lines = rawCode.split('\n');
|
|
32
65
|
let offset = 0;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MLASTParentNode, NamespaceURI } from '@markuplint/ml-ast';
|
|
2
|
+
/**
|
|
3
|
+
* Determines the namespace URI for a node based on its tag name and parent node context.
|
|
4
|
+
* Handles namespace transitions between HTML, SVG, and MathML
|
|
5
|
+
* (e.g., entering `<svg>` switches to SVG namespace, `<foreignObject>` switches back to HTML).
|
|
6
|
+
*
|
|
7
|
+
* @param currentNodeName - The tag name of the current node, or null for anonymous nodes
|
|
8
|
+
* @param parentNode - The parent node in the AST, or null for root-level nodes
|
|
9
|
+
* @returns The resolved namespace URI for the node
|
|
10
|
+
*/
|
|
11
|
+
export declare function getNamespace(currentNodeName: string | null, parentNode: MLASTParentNode | null): NamespaceURI;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Determines the namespace URI for a node based on its tag name and parent node context.
|
|
3
|
+
* Handles namespace transitions between HTML, SVG, and MathML
|
|
4
|
+
* (e.g., entering `<svg>` switches to SVG namespace, `<foreignObject>` switches back to HTML).
|
|
5
|
+
*
|
|
6
|
+
* @param currentNodeName - The tag name of the current node, or null for anonymous nodes
|
|
7
|
+
* @param parentNode - The parent node in the AST, or null for root-level nodes
|
|
8
|
+
* @returns The resolved namespace URI for the node
|
|
9
|
+
*/
|
|
10
|
+
export function getNamespace(currentNodeName, parentNode) {
|
|
11
|
+
const parentNS = getParentNamespace(parentNode);
|
|
12
|
+
if (parentNS === 'http://www.w3.org/1999/xhtml' && currentNodeName?.toLowerCase() === 'svg') {
|
|
13
|
+
return 'http://www.w3.org/2000/svg';
|
|
14
|
+
}
|
|
15
|
+
else if (parentNS === 'http://www.w3.org/2000/svg' && parentNode?.nodeName === 'foreignObject') {
|
|
16
|
+
return 'http://www.w3.org/1999/xhtml';
|
|
17
|
+
}
|
|
18
|
+
else if (parentNS === 'http://www.w3.org/1999/xhtml' && currentNodeName?.toLowerCase() === 'math') {
|
|
19
|
+
return 'http://www.w3.org/1998/Math/MathML';
|
|
20
|
+
}
|
|
21
|
+
return parentNS;
|
|
22
|
+
}
|
|
23
|
+
function getParentNamespace(parentNode) {
|
|
24
|
+
if (!parentNode) {
|
|
25
|
+
return 'http://www.w3.org/1999/xhtml';
|
|
26
|
+
}
|
|
27
|
+
if ('namespace' in parentNode && parentNode.namespace) {
|
|
28
|
+
const ns = parentNode.namespace.toLowerCase().trim();
|
|
29
|
+
return ns === 'http://www.w3.org/1999/xhtml'
|
|
30
|
+
? 'http://www.w3.org/1999/xhtml'
|
|
31
|
+
: ns === 'http://www.w3.org/2000/svg'
|
|
32
|
+
? 'http://www.w3.org/2000/svg'
|
|
33
|
+
: ns === 'http://www.w3.org/1998/Math/MathML'
|
|
34
|
+
? 'http://www.w3.org/1998/Math/MathML'
|
|
35
|
+
: 'http://www.w3.org/1999/xhtml';
|
|
36
|
+
}
|
|
37
|
+
return getParentNamespace(parentNode.parentNode);
|
|
38
|
+
}
|
package/lib/idl-attributes.d.ts
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Searches the IDL-to-content-attribute mapping to find the corresponding
|
|
3
|
+
* IDL property name and content attribute name for a given attribute name.
|
|
4
|
+
* Handles camelCase IDL names, lowercase content attribute names, hyphenated names,
|
|
5
|
+
* and event handler attributes (e.g., `onclick`).
|
|
6
|
+
*
|
|
7
|
+
* @param name - The attribute name to look up (can be IDL, content, or hyphenated form)
|
|
8
|
+
* @returns An object with the matching `idlPropName` and `contentAttrName`, both undefined if no match is found
|
|
9
|
+
*/
|
|
1
10
|
export declare function searchIDLAttribute(name: string): {
|
|
2
11
|
idlPropName: string | undefined;
|
|
3
12
|
contentAttrName: string | undefined;
|
package/lib/idl-attributes.js
CHANGED
|
@@ -416,6 +416,15 @@ const idlContentMap = {
|
|
|
416
416
|
credentialless: 'credentialless',
|
|
417
417
|
};
|
|
418
418
|
const list = Object.entries(idlContentMap);
|
|
419
|
+
/**
|
|
420
|
+
* Searches the IDL-to-content-attribute mapping to find the corresponding
|
|
421
|
+
* IDL property name and content attribute name for a given attribute name.
|
|
422
|
+
* Handles camelCase IDL names, lowercase content attribute names, hyphenated names,
|
|
423
|
+
* and event handler attributes (e.g., `onclick`).
|
|
424
|
+
*
|
|
425
|
+
* @param name - The attribute name to look up (can be IDL, content, or hyphenated form)
|
|
426
|
+
* @returns An object with the matching `idlPropName` and `contentAttrName`, both undefined if no match is found
|
|
427
|
+
*/
|
|
419
428
|
export function searchIDLAttribute(name) {
|
|
420
429
|
const camelizedName = camelize(name);
|
|
421
430
|
const [idlPropName, contentAttrName] = /^on[a-z]/.test(name)
|
package/lib/ignore-block.js
CHANGED
|
@@ -15,11 +15,10 @@ export function ignoreBlock(source, tags, maskChar = MASK_CHAR) {
|
|
|
15
15
|
replaced = text.replaced;
|
|
16
16
|
stack.push(...text.stack.map(res => ({ ...res, type: tag.type })));
|
|
17
17
|
}
|
|
18
|
-
stack.sort((a, b) => a.index - b.index);
|
|
19
18
|
return {
|
|
20
19
|
source,
|
|
21
20
|
replaced,
|
|
22
|
-
stack,
|
|
21
|
+
stack: stack.toSorted((a, b) => a.index - b.index),
|
|
23
22
|
maskChar,
|
|
24
23
|
};
|
|
25
24
|
}
|
|
@@ -63,33 +62,34 @@ ignoreBlock, throwErrorWhenTagHasUnresolved = true) {
|
|
|
63
62
|
for (const tag of stack) {
|
|
64
63
|
const raw = `${tag.startTag}${tag.taggedCode}${tag.endTag ?? ''}`;
|
|
65
64
|
const tagIndexEnd = tag.index + raw.length;
|
|
66
|
-
const
|
|
65
|
+
const nodeEndOffset = (n) => n.offset + n.raw.length;
|
|
66
|
+
const node = newNodeList.find(node => node.offset <= tag.index && nodeEndOffset(node) >= tagIndexEnd);
|
|
67
67
|
if (!node) {
|
|
68
68
|
continue;
|
|
69
69
|
}
|
|
70
70
|
const replacementChildNodes = [];
|
|
71
|
-
if (node.
|
|
72
|
-
const token = parser.createToken(raw, node.
|
|
71
|
+
if (node.offset === tag.index && nodeEndOffset(node) === tagIndexEnd) {
|
|
72
|
+
const token = parser.createToken(raw, node.offset, node.line, node.col);
|
|
73
73
|
const psNode = {
|
|
74
74
|
...token,
|
|
75
75
|
type: 'psblock',
|
|
76
|
-
conditionalType: null,
|
|
77
76
|
depth: node.depth,
|
|
78
77
|
nodeName: `#ps:${tag.type}`,
|
|
79
78
|
parentNode: node.parentNode,
|
|
80
79
|
childNodes: [],
|
|
80
|
+
blockBehavior: null,
|
|
81
81
|
isBogus: false,
|
|
82
82
|
isFragment: false, // TODO: Case by case
|
|
83
83
|
};
|
|
84
84
|
replacementChildNodes.push(psNode);
|
|
85
85
|
}
|
|
86
86
|
else if (node.type === 'text') {
|
|
87
|
-
const offset = tag.index - node.
|
|
87
|
+
const offset = tag.index - node.offset;
|
|
88
88
|
const above = node.raw.slice(0, offset);
|
|
89
89
|
const below = node.raw.slice(offset + raw.length);
|
|
90
90
|
if (above) {
|
|
91
91
|
const { line, column } = getPosition(node.raw, 0);
|
|
92
|
-
const token = parser.createToken(above, node.
|
|
92
|
+
const token = parser.createToken(above, node.offset, node.line + line - 1, node.col + column - 1);
|
|
93
93
|
const aboveNode = {
|
|
94
94
|
...token,
|
|
95
95
|
nodeName: '#text',
|
|
@@ -100,22 +100,22 @@ ignoreBlock, throwErrorWhenTagHasUnresolved = true) {
|
|
|
100
100
|
replacementChildNodes.push(aboveNode);
|
|
101
101
|
}
|
|
102
102
|
const { line, column } = getPosition(raw, offset);
|
|
103
|
-
const token = parser.createToken(raw, node.
|
|
103
|
+
const token = parser.createToken(raw, node.offset + offset, node.line + line - 1, node.col + column - 1);
|
|
104
104
|
const psNode = {
|
|
105
105
|
...token,
|
|
106
106
|
type: 'psblock',
|
|
107
|
-
conditionalType: null,
|
|
108
107
|
depth: node.depth,
|
|
109
108
|
nodeName: `#ps:${tag.type}`,
|
|
110
109
|
parentNode: node.parentNode,
|
|
111
110
|
childNodes: [],
|
|
111
|
+
blockBehavior: null,
|
|
112
112
|
isBogus: false,
|
|
113
113
|
isFragment: false, // TODO: Case by case
|
|
114
114
|
};
|
|
115
115
|
replacementChildNodes.push(psNode);
|
|
116
116
|
if (below) {
|
|
117
117
|
const { line, column } = getPosition(node.raw, offset + raw.length);
|
|
118
|
-
const token = parser.createToken(below, node.
|
|
118
|
+
const token = parser.createToken(below, node.offset + offset + raw.length, node.line + line - 1, node.col + column - 1);
|
|
119
119
|
const aboveNode = {
|
|
120
120
|
...token,
|
|
121
121
|
nodeName: '#text',
|
|
@@ -144,8 +144,9 @@ ignoreBlock, throwErrorWhenTagHasUnresolved = true) {
|
|
|
144
144
|
for (const tag of stack) {
|
|
145
145
|
const raw = tag.startTag + tag.taggedCode + tag.endTag;
|
|
146
146
|
const length = raw.length;
|
|
147
|
-
if (attr.value.
|
|
148
|
-
|
|
147
|
+
if (attr.value.offset <= tag.index &&
|
|
148
|
+
tag.index + length <= attr.value.offset + attr.value.raw.length) {
|
|
149
|
+
const offset = tag.index - attr.value.offset;
|
|
149
150
|
const above = attr.value.raw.slice(0, offset);
|
|
150
151
|
const below = attr.value.raw.slice(offset + length);
|
|
151
152
|
parser.updateRaw(attr.value, above + raw + below);
|
|
@@ -162,7 +163,7 @@ ignoreBlock, throwErrorWhenTagHasUnresolved = true) {
|
|
|
162
163
|
}
|
|
163
164
|
// Update node raw
|
|
164
165
|
const length = attr.raw.length;
|
|
165
|
-
const offset = attr.
|
|
166
|
+
const offset = attr.offset - node.offset;
|
|
166
167
|
const above = node.raw.slice(0, offset);
|
|
167
168
|
const below = node.raw.slice(offset + length);
|
|
168
169
|
parser.updateRaw(node, above + attr.raw + below);
|
package/lib/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export * from './debugger.js';
|
|
2
2
|
export * from './enums.js';
|
|
3
|
+
export * from './get-namespace.js';
|
|
3
4
|
export * from './idl-attributes.js';
|
|
4
5
|
export * from './parser-error.js';
|
|
5
6
|
export * from './parser.js';
|
|
6
7
|
export * from './script-parser.js';
|
|
7
|
-
export * from './types.js';
|
|
8
|
+
export type * from './types.js';
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export * from './debugger.js';
|
|
2
2
|
export * from './enums.js';
|
|
3
|
+
export * from './get-namespace.js';
|
|
3
4
|
export * from './idl-attributes.js';
|
|
4
5
|
export * from './parser-error.js';
|
|
5
6
|
export * from './parser.js';
|
|
6
7
|
export * from './script-parser.js';
|
|
7
|
-
export * from './types.js';
|
package/lib/parser-error.d.ts
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Positional and contextual information associated with a parser error,
|
|
3
|
+
* used to construct meaningful error messages with source locations.
|
|
4
|
+
*/
|
|
1
5
|
export type ParserErrorInfo = {
|
|
2
6
|
readonly line?: number;
|
|
3
7
|
readonly col?: number;
|
|
4
8
|
readonly raw?: string;
|
|
5
9
|
readonly stack?: string;
|
|
6
10
|
};
|
|
11
|
+
/**
|
|
12
|
+
* An error that occurs during parsing, carrying the source line, column,
|
|
13
|
+
* and raw text where the error was encountered.
|
|
14
|
+
*/
|
|
7
15
|
export declare class ParserError extends Error {
|
|
8
16
|
readonly col: number;
|
|
9
17
|
readonly line: number;
|
|
@@ -11,6 +19,10 @@ export declare class ParserError extends Error {
|
|
|
11
19
|
readonly raw: string;
|
|
12
20
|
constructor(message: string, info: ParserErrorInfo);
|
|
13
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* A parser error specific to a particular HTML element, including
|
|
24
|
+
* the node name of the element that caused the error in the message.
|
|
25
|
+
*/
|
|
14
26
|
export declare class TargetParserError extends ParserError {
|
|
15
27
|
name: string;
|
|
16
28
|
readonly nodeName: string | null;
|
|
@@ -18,6 +30,10 @@ export declare class TargetParserError extends ParserError {
|
|
|
18
30
|
readonly nodeName?: string | null;
|
|
19
31
|
});
|
|
20
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* A parser error that occurs while reading a configuration file,
|
|
35
|
+
* including the file path in the error message for easier debugging.
|
|
36
|
+
*/
|
|
21
37
|
export declare class ConfigParserError extends ParserError {
|
|
22
38
|
readonly filePath: string;
|
|
23
39
|
name: string;
|
package/lib/parser-error.js
CHANGED
|
@@ -1,30 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An error that occurs during parsing, carrying the source line, column,
|
|
3
|
+
* and raw text where the error was encountered.
|
|
4
|
+
*/
|
|
1
5
|
export class ParserError extends Error {
|
|
6
|
+
col;
|
|
7
|
+
line;
|
|
8
|
+
name = 'ParserError';
|
|
9
|
+
raw;
|
|
2
10
|
constructor(message, info) {
|
|
3
11
|
super(message);
|
|
4
|
-
this.name = 'ParserError';
|
|
5
12
|
this.line = info.line ?? 1;
|
|
6
13
|
this.col = info.col ?? 0;
|
|
7
14
|
this.raw = info.raw ?? '';
|
|
8
15
|
this.stack = info.stack ?? this.stack;
|
|
9
16
|
}
|
|
10
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* A parser error specific to a particular HTML element, including
|
|
20
|
+
* the node name of the element that caused the error in the message.
|
|
21
|
+
*/
|
|
11
22
|
export class TargetParserError extends ParserError {
|
|
23
|
+
name = 'TargetParserError';
|
|
24
|
+
nodeName;
|
|
12
25
|
constructor(message, info) {
|
|
13
26
|
const errMsg = info.nodeName
|
|
14
27
|
? `The ${info.nodeName} is invalid element (${info.line}:${info.col}): ${message}`
|
|
15
28
|
: message;
|
|
16
29
|
super(errMsg, info);
|
|
17
|
-
this.name = 'TargetParserError';
|
|
18
30
|
this.nodeName = info.nodeName ?? null;
|
|
19
31
|
}
|
|
20
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* A parser error that occurs while reading a configuration file,
|
|
35
|
+
* including the file path in the error message for easier debugging.
|
|
36
|
+
*/
|
|
21
37
|
export class ConfigParserError extends ParserError {
|
|
38
|
+
filePath;
|
|
39
|
+
name = 'ConfigParserError';
|
|
22
40
|
constructor(message, info) {
|
|
23
41
|
const pos = info.line != null && info.line != null ? `(${info.line}:${info.col})` : '';
|
|
24
42
|
const file = ` in ${info.filePath}${pos}`;
|
|
25
43
|
const errMsg = `${message}${file}`;
|
|
26
44
|
super(errMsg, info);
|
|
27
|
-
this.name = 'ConfigParserError';
|
|
28
45
|
this.filePath = info.filePath;
|
|
29
46
|
}
|
|
30
47
|
}
|