@markuplint/parser-utils 4.8.11 → 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 +6 -6
- package/ARCHITECTURE.md +5 -5
- package/CHANGELOG.md +31 -0
- package/docs/parser-class.ja.md +6 -6
- package/docs/parser-class.md +6 -6
- package/lib/debug.js +8 -24
- package/lib/debugger.js +9 -4
- package/lib/get-location.d.ts +31 -0
- package/lib/get-location.js +33 -0
- package/lib/get-namespace.d.ts +9 -0
- package/lib/get-namespace.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.js +8 -3
- package/lib/parser.d.ts +12 -16
- package/lib/parser.js +502 -552
- package/lib/sort-nodes.d.ts +8 -0
- package/lib/sort-nodes.js +11 -3
- package/lib/types.d.ts +3 -3
- package/package.json +10 -9
package/ARCHITECTURE.ja.md
CHANGED
|
@@ -57,7 +57,7 @@ flowchart TD
|
|
|
57
57
|
mlAst["@markuplint/ml-ast\n型定義"]
|
|
58
58
|
mlSpec["@markuplint/ml-spec\nvoid 要素"]
|
|
59
59
|
espree["espree\nJS トークナイザ"]
|
|
60
|
-
|
|
60
|
+
cryptoLib["node:crypto\nノード ID"]
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
parser --> attrTokenizer
|
|
@@ -79,7 +79,7 @@ flowchart TD
|
|
|
79
79
|
|
|
80
80
|
parser --> mlAst
|
|
81
81
|
parser --> mlSpec
|
|
82
|
-
parser -->
|
|
82
|
+
parser --> cryptoLib
|
|
83
83
|
decision --> mlAst
|
|
84
84
|
```
|
|
85
85
|
|
|
@@ -95,12 +95,12 @@ flowchart TD
|
|
|
95
95
|
| `ignore-block.ts` | テンプレート式のマスキングと復元 | `ignoreBlock`, `restoreNode` |
|
|
96
96
|
| `ignore-front-matter.ts` | YAML フロントマター処理 | `ignoreFrontMatter` |
|
|
97
97
|
| `detect-element-type.ts` | 要素の分類 | `detectElementType` |
|
|
98
|
-
| `idl-attributes.ts` | IDL ↔ コンテンツ属性名マッピング
|
|
98
|
+
| `idl-attributes.ts` | IDL ↔ コンテンツ属性名マッピング | `searchIDLAttribute` |
|
|
99
99
|
| `debugger.ts` | テスト・デバッグユーティリティ | `nodeListToDebugMaps`, `attributesToDebugMaps`, `nodeTreeDebugView` |
|
|
100
100
|
| `parser-error.ts` | エラークラス | `ParserError`, `TargetParserError`, `ConfigParserError` |
|
|
101
101
|
| `sort-nodes.ts` | ノードの位置ソート | `sortNodes` |
|
|
102
102
|
| `const.ts` | 定数 | `MASK_CHAR`, `svgElementList`, `defaultSpaces` |
|
|
103
|
-
| `get-location.ts` | 位置計算 | `getPosition`, `getEndLine`, `getEndCol`, `getOffsetsFromCode`
|
|
103
|
+
| `get-location.ts` | 位置計算 | `getPosition`, `getEndLine`, `getEndCol`, `getEndPosition`, `getOffsetsFromCode` |
|
|
104
104
|
| `decision.ts` | カスタム要素名判定 | `isPotentialCustomElementName`, `isSVGElement` |
|
|
105
105
|
|
|
106
106
|
## パースパイプライン概要
|
|
@@ -148,7 +148,7 @@ ParserError (基底クラス)
|
|
|
148
148
|
|
|
149
149
|
## IDL 属性マッピング
|
|
150
150
|
|
|
151
|
-
`searchIDLAttribute` は React スタイルの IDL 属性名と HTML コンテンツ属性名の双方向マッピングを提供します(例: `className` → `class`、`htmlFor` → `for`)。
|
|
151
|
+
`searchIDLAttribute` は React スタイルの IDL 属性名と HTML コンテンツ属性名の双方向マッピングを提供します(例: `className` → `class`、`htmlFor` → `for`)。spec が `useIDLAttributeNames: true` を設定している場合に、`@markuplint/ml-core` の `MLAttr` コンストラクタから呼び出されます。
|
|
152
152
|
|
|
153
153
|
## 外部依存
|
|
154
154
|
|
|
@@ -157,7 +157,7 @@ ParserError (基底クラス)
|
|
|
157
157
|
| `@markuplint/ml-ast` | AST 型定義 |
|
|
158
158
|
| `@markuplint/ml-spec` | void 要素判定 |
|
|
159
159
|
| `@markuplint/types` | カスタム要素名検証 |
|
|
160
|
-
| `
|
|
160
|
+
| `node:crypto` | AST ノード UUID 生成 |
|
|
161
161
|
| `debug` | パフォーマンスタイミング・ロギング |
|
|
162
162
|
| `espree` | JavaScript トークン化・パース |
|
|
163
163
|
| `type-fest` | TypeScript ユーティリティ型 |
|
package/ARCHITECTURE.md
CHANGED
|
@@ -67,7 +67,7 @@ flowchart TD
|
|
|
67
67
|
mlSpec["@markuplint/ml-spec\n(void element detection)"]
|
|
68
68
|
mlTypes["@markuplint/types\n(custom element validation)"]
|
|
69
69
|
espree["espree\n(JS tokenization)"]
|
|
70
|
-
|
|
70
|
+
cryptoLib["node:crypto\n(node ID generation)"]
|
|
71
71
|
debugLib["debug\n(logging)"]
|
|
72
72
|
end
|
|
73
73
|
|
|
@@ -90,7 +90,7 @@ flowchart TD
|
|
|
90
90
|
decision --> mlTypes
|
|
91
91
|
parser --> mlAst
|
|
92
92
|
parser --> mlSpec
|
|
93
|
-
parser -->
|
|
93
|
+
parser --> cryptoLib
|
|
94
94
|
scriptParser --> espree
|
|
95
95
|
debugMod --> debugLib
|
|
96
96
|
```
|
|
@@ -113,7 +113,7 @@ flowchart TD
|
|
|
113
113
|
| `parser-error.ts` | Error classes with positional information | `ParserError`, `TargetParserError`, `ConfigParserError` |
|
|
114
114
|
| `sort-nodes.ts` | Node position sorting by offset | `sortNodes` |
|
|
115
115
|
| `const.ts` | Constants used across the package | `MASK_CHAR`, `svgElementList`, `defaultSpaces` |
|
|
116
|
-
| `get-location.ts` | Line/column/offset position calculations | `getPosition`, `getEndLine`, `getEndCol`, `getOffsetsFromCode`
|
|
116
|
+
| `get-location.ts` | Line/column/offset position calculations | `getPosition`, `getEndLine`, `getEndCol`, `getEndPosition`, `getOffsetsFromCode` |
|
|
117
117
|
| `decision.ts` | Custom element name detection and SVG element lookup | `isPotentialCustomElementName`, `isSVGElement` |
|
|
118
118
|
|
|
119
119
|
## Parse Pipeline Overview
|
|
@@ -174,7 +174,7 @@ Additionally, `debug.ts` provides `PerformanceTimer` for measuring parse phase d
|
|
|
174
174
|
|
|
175
175
|
## IDL Attribute Mapping
|
|
176
176
|
|
|
177
|
-
`searchIDLAttribute` maps between React-style IDL attribute names and HTML content attribute names. It maintains a comprehensive mapping table derived from React's `possibleStandardNames.js`, covering:
|
|
177
|
+
`searchIDLAttribute` maps between React-style IDL attribute names and HTML content attribute names. It is called by `@markuplint/ml-core`'s `MLAttr` constructor when the spec sets `useIDLAttributeNames: true`. It maintains a comprehensive mapping table derived from React's `possibleStandardNames.js`, covering:
|
|
178
178
|
|
|
179
179
|
- HTML attributes (e.g., `className` -> `class`, `htmlFor` -> `for`, `tabIndex` -> `tabindex`)
|
|
180
180
|
- SVG attributes (e.g., `strokeWidth` -> `stroke-width`, `clipPath` -> `clip-path`)
|
|
@@ -189,7 +189,7 @@ The lookup handles camelCase IDL names, lowercase content attribute names, and h
|
|
|
189
189
|
| `@markuplint/ml-ast` | AST type definitions (`MLASTDocument`, `MLASTElement`, `MLASTToken`, etc.) |
|
|
190
190
|
| `@markuplint/ml-spec` | Void element detection (`isVoidElement`) for self-closing tag handling |
|
|
191
191
|
| `@markuplint/types` | Custom element name validation (`isCustomElementName`) |
|
|
192
|
-
| `
|
|
192
|
+
| `node:crypto` | AST node UUID generation via `crypto.randomUUID()` |
|
|
193
193
|
| `debug` | Performance timing and structured logging |
|
|
194
194
|
| `espree` | JavaScript tokenization and parsing for embedded script content |
|
|
195
195
|
| `type-fest` | TypeScript utility types |
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,37 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [5.0.0-alpha.0](https://github.com/markuplint/markuplint/compare/v4.14.1...v5.0.0-alpha.0) (2026-02-20)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **ml-core:** improve detection of namespace ([5b507ad](https://github.com/markuplint/markuplint/commit/5b507ad7c19c5015b8ce587845d901e31dfa6518))
|
|
11
|
+
- resolve additional eslint-plugin-unicorn v63 errors ([e58a72c](https://github.com/markuplint/markuplint/commit/e58a72c17c97bbec522f9513b99777fac6904d64))
|
|
12
|
+
- use explicit `export type` for type-only re-exports ([7c77c05](https://github.com/markuplint/markuplint/commit/7c77c05619518c8d18a183132040f5b2cd0ab6ec))
|
|
13
|
+
|
|
14
|
+
- feat(parser-utils)!: adapt to simplified MLASTToken properties ([5cbbc9c](https://github.com/markuplint/markuplint/commit/5cbbc9ca8f77a71d99bffa14b193c79b26c1c415))
|
|
15
|
+
|
|
16
|
+
### BREAKING CHANGES
|
|
17
|
+
|
|
18
|
+
- Update Token type and parser internals for
|
|
19
|
+
simplified AST token properties.
|
|
20
|
+
|
|
21
|
+
Token type property renames:
|
|
22
|
+
|
|
23
|
+
- startOffset -> offset
|
|
24
|
+
- startLine -> line
|
|
25
|
+
- startCol -> col
|
|
26
|
+
|
|
27
|
+
Parser changes:
|
|
28
|
+
|
|
29
|
+
- createToken() no longer produces endOffset/endLine/endCol
|
|
30
|
+
- visitPsBlock() parameter: conditionalType -> blockBehavior
|
|
31
|
+
- visitElement() accepts blockBehavior option
|
|
32
|
+
- Remove selfClosingSolidus token generation
|
|
33
|
+
- Add getEndPosition() helper to get-location.ts
|
|
34
|
+
|
|
35
|
+
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
36
|
+
|
|
6
37
|
## [4.8.11](https://github.com/markuplint/markuplint/compare/@markuplint/parser-utils@4.8.10...@markuplint/parser-utils@4.8.11) (2026-02-10)
|
|
7
38
|
|
|
8
39
|
**Note:** Version bump only for package @markuplint/parser-utils
|
package/docs/parser-class.ja.md
CHANGED
|
@@ -255,12 +255,12 @@ doctype 名、パブリック ID、システム ID を含むトークンから d
|
|
|
255
255
|
visitPsBlock(
|
|
256
256
|
token: ChildToken & { nodeName: string; isFragment: boolean },
|
|
257
257
|
childNodes?: readonly Node[],
|
|
258
|
-
|
|
258
|
+
blockBehavior?: MLASTBlockBehavior | null,
|
|
259
259
|
originBlockNode?: Node
|
|
260
260
|
): readonly MLASTNodeTreeItem[]
|
|
261
261
|
```
|
|
262
262
|
|
|
263
|
-
プリプロセッサ固有のブロックノードを作成します。`nodeName` には自動的に `#ps:` プレフィックスが付加されます(例: `#ps:if`、`#ps:each`、`#ps:front-matter`)。`visitChildren()` を通じて子ノードを再帰的にトラバースします。
|
|
263
|
+
プリプロセッサ固有のブロックノードを作成します。`nodeName` には自動的に `#ps:` プレフィックスが付加されます(例: `#ps:if`、`#ps:each`、`#ps:front-matter`)。`blockBehavior` パラメータはブロックの制御フロー(型と式)を記述します。`visitChildren()` を通じて子ノードを再帰的にトラバースします。
|
|
264
264
|
|
|
265
265
|
### visitAttr()
|
|
266
266
|
|
|
@@ -347,10 +347,10 @@ stateDiagram-v2
|
|
|
347
347
|
|
|
348
348
|
```ts
|
|
349
349
|
createToken(token: Token): MLASTToken;
|
|
350
|
-
createToken(token: string,
|
|
350
|
+
createToken(token: string, offset: number, line: number, col: number): MLASTToken;
|
|
351
351
|
```
|
|
352
352
|
|
|
353
|
-
生成された UUID(8
|
|
353
|
+
生成された UUID(8 文字)を持つ新しい `MLASTToken` を作成します。`Token` オブジェクトまたは明示的な座標を持つ生の文字列を受け取ります。
|
|
354
354
|
|
|
355
355
|
### sliceFragment()
|
|
356
356
|
|
|
@@ -412,11 +412,11 @@ walk<Node extends MLASTNodeTreeItem>(
|
|
|
412
412
|
```ts
|
|
413
413
|
updateLocation(
|
|
414
414
|
node: MLASTNodeTreeItem,
|
|
415
|
-
props: Partial<Pick<MLASTNodeTreeItem, '
|
|
415
|
+
props: Partial<Pick<MLASTNodeTreeItem, 'offset' | 'line' | 'col' | 'depth'>>
|
|
416
416
|
): void
|
|
417
417
|
```
|
|
418
418
|
|
|
419
|
-
AST
|
|
419
|
+
AST ノードの位置と深さのプロパティを更新します。
|
|
420
420
|
|
|
421
421
|
### updateRaw()
|
|
422
422
|
|
package/docs/parser-class.md
CHANGED
|
@@ -255,12 +255,12 @@ Creates a doctype node from a token containing the doctype name, public ID, and
|
|
|
255
255
|
visitPsBlock(
|
|
256
256
|
token: ChildToken & { nodeName: string; isFragment: boolean },
|
|
257
257
|
childNodes?: readonly Node[],
|
|
258
|
-
|
|
258
|
+
blockBehavior?: MLASTBlockBehavior | null,
|
|
259
259
|
originBlockNode?: Node
|
|
260
260
|
): readonly MLASTNodeTreeItem[]
|
|
261
261
|
```
|
|
262
262
|
|
|
263
|
-
Creates a preprocessor-specific block node. The `nodeName` is automatically prefixed with `#ps:` (e.g., `#ps:if`, `#ps:each`, `#ps:front-matter`). Recursively traverses child nodes via `visitChildren()`.
|
|
263
|
+
Creates a preprocessor-specific block node. The `nodeName` is automatically prefixed with `#ps:` (e.g., `#ps:if`, `#ps:each`, `#ps:front-matter`). The `blockBehavior` parameter describes the control-flow semantics (type and expression) of the block. Recursively traverses child nodes via `visitChildren()`.
|
|
264
264
|
|
|
265
265
|
### visitAttr()
|
|
266
266
|
|
|
@@ -347,10 +347,10 @@ stateDiagram-v2
|
|
|
347
347
|
|
|
348
348
|
```ts
|
|
349
349
|
createToken(token: Token): MLASTToken;
|
|
350
|
-
createToken(token: string,
|
|
350
|
+
createToken(token: string, offset: number, line: number, col: number): MLASTToken;
|
|
351
351
|
```
|
|
352
352
|
|
|
353
|
-
Creates a new `MLASTToken` with a generated UUID (8 chars)
|
|
353
|
+
Creates a new `MLASTToken` with a generated UUID (8 chars). Accepts either a `Token` object or a raw string with explicit coordinates.
|
|
354
354
|
|
|
355
355
|
### sliceFragment()
|
|
356
356
|
|
|
@@ -412,11 +412,11 @@ Walks a node list depth-first, invoking the walker callback for each node. The w
|
|
|
412
412
|
```ts
|
|
413
413
|
updateLocation(
|
|
414
414
|
node: MLASTNodeTreeItem,
|
|
415
|
-
props: Partial<Pick<MLASTNodeTreeItem, '
|
|
415
|
+
props: Partial<Pick<MLASTNodeTreeItem, 'offset' | 'line' | 'col' | 'depth'>>
|
|
416
416
|
): void
|
|
417
417
|
```
|
|
418
418
|
|
|
419
|
-
Updates position and depth properties of an AST node
|
|
419
|
+
Updates position and depth properties of an AST node.
|
|
420
420
|
|
|
421
421
|
### updateRaw()
|
|
422
422
|
|
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.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getEndCol, getEndLine } from './get-location.js';
|
|
1
2
|
/**
|
|
2
3
|
* Converts a list of AST nodes into human-readable debug strings showing
|
|
3
4
|
* each node's position, type, and raw content. Useful for snapshot testing.
|
|
@@ -8,8 +9,7 @@
|
|
|
8
9
|
*/
|
|
9
10
|
export function nodeListToDebugMaps(nodeList, withAttr = false) {
|
|
10
11
|
return nodeList.flatMap(n => {
|
|
11
|
-
const r = [];
|
|
12
|
-
r.push(tokenDebug(n));
|
|
12
|
+
const r = [tokenDebug(n)];
|
|
13
13
|
if (withAttr && n && n.type === 'starttag') {
|
|
14
14
|
r.push(...attributesToDebugMaps(n.attributes).flat());
|
|
15
15
|
}
|
|
@@ -86,9 +86,14 @@ function tokenDebug(n, type = '') {
|
|
|
86
86
|
if (!n) {
|
|
87
87
|
return 'NULL';
|
|
88
88
|
}
|
|
89
|
-
|
|
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})${
|
|
90
93
|
// @ts-ignore
|
|
91
|
-
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)}`;
|
|
92
97
|
}
|
|
93
98
|
function visibleWhiteSpace(chars) {
|
|
94
99
|
return chars.replaceAll('\n', '⏎').replaceAll('\t', '→').replaceAll(/\s/g, '␣');
|
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;
|
package/lib/get-namespace.d.ts
CHANGED
|
@@ -1,2 +1,11 @@
|
|
|
1
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
|
+
*/
|
|
2
11
|
export declare function getNamespace(currentNodeName: string | null, parentNode: MLASTParentNode | null): NamespaceURI;
|
package/lib/get-namespace.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
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
|
+
*/
|
|
1
10
|
export function getNamespace(currentNodeName, parentNode) {
|
|
2
11
|
const parentNS = getParentNamespace(parentNode);
|
|
3
12
|
if (parentNS === 'http://www.w3.org/1999/xhtml' && currentNodeName?.toLowerCase() === 'svg') {
|
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.js
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
* and raw text where the error was encountered.
|
|
4
4
|
*/
|
|
5
5
|
export class ParserError extends Error {
|
|
6
|
+
col;
|
|
7
|
+
line;
|
|
8
|
+
name = 'ParserError';
|
|
9
|
+
raw;
|
|
6
10
|
constructor(message, info) {
|
|
7
11
|
super(message);
|
|
8
|
-
this.name = 'ParserError';
|
|
9
12
|
this.line = info.line ?? 1;
|
|
10
13
|
this.col = info.col ?? 0;
|
|
11
14
|
this.raw = info.raw ?? '';
|
|
@@ -17,12 +20,13 @@ export class ParserError extends Error {
|
|
|
17
20
|
* the node name of the element that caused the error in the message.
|
|
18
21
|
*/
|
|
19
22
|
export class TargetParserError extends ParserError {
|
|
23
|
+
name = 'TargetParserError';
|
|
24
|
+
nodeName;
|
|
20
25
|
constructor(message, info) {
|
|
21
26
|
const errMsg = info.nodeName
|
|
22
27
|
? `The ${info.nodeName} is invalid element (${info.line}:${info.col}): ${message}`
|
|
23
28
|
: message;
|
|
24
29
|
super(errMsg, info);
|
|
25
|
-
this.name = 'TargetParserError';
|
|
26
30
|
this.nodeName = info.nodeName ?? null;
|
|
27
31
|
}
|
|
28
32
|
}
|
|
@@ -31,12 +35,13 @@ export class TargetParserError extends ParserError {
|
|
|
31
35
|
* including the file path in the error message for easier debugging.
|
|
32
36
|
*/
|
|
33
37
|
export class ConfigParserError extends ParserError {
|
|
38
|
+
filePath;
|
|
39
|
+
name = 'ConfigParserError';
|
|
34
40
|
constructor(message, info) {
|
|
35
41
|
const pos = info.line != null && info.line != null ? `(${info.line}:${info.col})` : '';
|
|
36
42
|
const file = ` in ${info.filePath}${pos}`;
|
|
37
43
|
const errMsg = `${message}${file}`;
|
|
38
44
|
super(errMsg, info);
|
|
39
|
-
this.name = 'ConfigParserError';
|
|
40
45
|
this.filePath = info.filePath;
|
|
41
46
|
}
|
|
42
47
|
}
|