@markuplint/tagged-template-literal-parser 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 +102 -0
- package/ARCHITECTURE.md +102 -0
- package/CHANGELOG.md +10 -0
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/SKILL.md +57 -0
- package/docs/maintenance.ja.md +96 -0
- package/docs/maintenance.md +96 -0
- package/lib/find-template-literals.d.ts +37 -0
- package/lib/find-template-literals.js +116 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +7 -0
- package/lib/parser.d.ts +36 -0
- package/lib/parser.js +109 -0
- package/package.json +34 -0
- package/src/find-template-literals.spec.ts +161 -0
- package/src/find-template-literals.ts +160 -0
- package/src/index.spec.ts +423 -0
- package/src/index.ts +8 -0
- package/src/parser.ts +122 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# @markuplint/tagged-template-literal-parser
|
|
2
|
+
|
|
3
|
+
## 概要
|
|
4
|
+
|
|
5
|
+
`@markuplint/tagged-template-literal-parser` は、TypeScript/JavaScript ソースファイル内のタグ付きテンプレートリテラル(例: `` html`<div>...</div>` ``)に埋め込まれた HTML をパースします。TypeScript AST 解析と標準 HTML パースパイプラインを組み合わせて、markuplint AST を生成します。
|
|
6
|
+
|
|
7
|
+
## 動作の仕組み
|
|
8
|
+
|
|
9
|
+
パーサーは2段階で動作します:
|
|
10
|
+
|
|
11
|
+
1. **抽出** — `@typescript-eslint/typescript-estree` を使用して TypeScript/JavaScript ソース全体をパースします。AST を走査し、タグ名が設定リスト(デフォルト: `html`)に一致する `TaggedTemplateExpression` ノードを検出します。各マッチに対して、テンプレートリテラルの内容(バッククォート間)と `${...}` 式の位置情報を抽出します。
|
|
12
|
+
|
|
13
|
+
2. **パース** — 抽出された各 HTML 文字列は、オフセットオプション(`offsetOffset`、`offsetLine`、`offsetColumn`)付きで基底の `HtmlParser` に渡されます。これにより、結果の AST 内のソース位置が元ファイルに正しくマッピングされます。`${...}` 式は `ignoreTags` メカニズム(開始: `${`、終了: `}`)により処理され、HTML パース前にマスクされた後、`#ps:ttl-expression` プリプロセッサ固有ブロックノードとして復元されます。
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
.ts/.js ソースファイル
|
|
17
|
+
|
|
|
18
|
+
v
|
|
19
|
+
[findTemplateLiterals] — typescript-estree AST 走査
|
|
20
|
+
| TaggedTemplateExpression ノードを検出
|
|
21
|
+
v
|
|
22
|
+
[HtmlParser.parse()] — 位置マッピング用オフセットオプション付き
|
|
23
|
+
| ${...} は ignoreTags でマスク
|
|
24
|
+
v
|
|
25
|
+
markuplint AST — 位置情報は元ファイルを参照
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## タグ名の解決
|
|
29
|
+
|
|
30
|
+
パーサーは `TaggedTemplateExpression.tag` ノードからタグ名を解決します:
|
|
31
|
+
|
|
32
|
+
| タグ形式 | 解決される名前 | 例 |
|
|
33
|
+
| ---------------- | -------------- | ---------------------------- |
|
|
34
|
+
| Identifier | `tag.name` | `` html`...` `` → `html` |
|
|
35
|
+
| MemberExpression | プロパティ名 | `` Lit.html`...` `` → `html` |
|
|
36
|
+
| その他の式形式 | `''`(空文字) | マッチしない |
|
|
37
|
+
|
|
38
|
+
## ignoreTags 設定
|
|
39
|
+
|
|
40
|
+
パーサーは単一の ignore パターンを定義します:
|
|
41
|
+
|
|
42
|
+
| タイプ | 開始 | 終了 | 説明 |
|
|
43
|
+
| ---------------- | ---- | ---- | -------------------------------- |
|
|
44
|
+
| `ttl-expression` | `${` | `}` | テンプレートリテラルの式スロット |
|
|
45
|
+
|
|
46
|
+
これは、テンプレートエンジンパーサー(EJS、Liquid 等)がテンプレート構文に使用するのと同じマスク/復元パイプラインを再利用しています。
|
|
47
|
+
|
|
48
|
+
## 複数テンプレートリテラル
|
|
49
|
+
|
|
50
|
+
ソースファイルに複数のタグ付きテンプレートリテラルがある場合、それぞれが独立してパースされ、結果のノードリストがソース順(`contentStart` 順)に連結されます。各テンプレートリテラルのノードは、元ソースファイル内の位置に正しくマッピングされます。
|
|
51
|
+
|
|
52
|
+
## 制限事項
|
|
53
|
+
|
|
54
|
+
- **`ignoreBlock` の文字列マッチング**: `${...}` のマスキングは単純な開始/終了デリミタマッチングを使用します。ネストされた `}` 文字を含む式(例: `${{ key: value }}`)は誤って分割される可能性があります。`findTemplateLiterals` 関数は AST から正確な式の位置情報を抽出していますが、この情報はまだ `ignoreBlock` メカニズムの代替として利用されていません。
|
|
55
|
+
- **JSX**: TypeScript パーサーは `jsx: false` で設定されています。JSX 構文を含むファイル(`.tsx`)はパースに失敗します。JSX/TSX ファイルには `@markuplint/jsx-parser` を使用してください。
|
|
56
|
+
|
|
57
|
+
## ディレクトリ構成
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
src/
|
|
61
|
+
├── index.ts — parser と TaggedTemplateLiteralParser クラスを再エクスポート
|
|
62
|
+
├── parser.ts — TaggedTemplateLiteralParser クラス
|
|
63
|
+
├── find-template-literals.ts — テンプレート抽出用 TypeScript AST 走査
|
|
64
|
+
├── index.spec.ts — パーサー統合テスト
|
|
65
|
+
└── find-template-literals.spec.ts — テンプレート抽出ユニットテスト
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## 主要ソースファイル
|
|
69
|
+
|
|
70
|
+
| ファイル | 用途 |
|
|
71
|
+
| ------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
72
|
+
| `src/parser.ts` | `HtmlParser` を拡張する `TaggedTemplateLiteralParser`。シングルトン `parser` |
|
|
73
|
+
| `src/find-template-literals.ts` | タグ付きテンプレートリテラルの検出・抽出のための AST 走査 |
|
|
74
|
+
| `src/index.ts` | パッケージエントリーポイント。`parser` と `TaggedTemplateLiteralParser` を再エクスポート |
|
|
75
|
+
|
|
76
|
+
## 統合ポイント
|
|
77
|
+
|
|
78
|
+
```mermaid
|
|
79
|
+
flowchart TD
|
|
80
|
+
subgraph upstream ["上流"]
|
|
81
|
+
htmlParser["@markuplint/html-parser\n(HtmlParser クラス)"]
|
|
82
|
+
tsEstree["@typescript-eslint/typescript-estree\n(TypeScript AST)"]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
subgraph pkg ["@markuplint/tagged-template-literal-parser"]
|
|
86
|
+
findTpl["findTemplateLiterals()\n(AST 走査)"]
|
|
87
|
+
ttlParser["TaggedTemplateLiteralParser\nextends HtmlParser"]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
subgraph downstream ["下流"]
|
|
91
|
+
mlCore["@markuplint/ml-core\n(MLASTDocument → MLDOM)"]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
tsEstree -->|"ソースをパース"| findTpl
|
|
95
|
+
findTpl -->|"テンプレート情報"| ttlParser
|
|
96
|
+
htmlParser -->|"継承"| ttlParser
|
|
97
|
+
ttlParser -->|"MLASTDocument を生成"| mlCore
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## ドキュメントマップ
|
|
101
|
+
|
|
102
|
+
- [メンテナンスガイド](docs/maintenance.ja.md) — コマンド、レシピ、テスト
|
package/ARCHITECTURE.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# @markuplint/tagged-template-literal-parser
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`@markuplint/tagged-template-literal-parser` parses HTML embedded in tagged template literals (e.g., `` html`<div>...</div>` ``) within TypeScript/JavaScript source files. It combines TypeScript AST analysis with the standard HTML parsing pipeline to produce a markuplint AST.
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
The parser operates in two stages:
|
|
10
|
+
|
|
11
|
+
1. **Extract** — The full TypeScript/JavaScript source is parsed using `@typescript-eslint/typescript-estree`. The AST is traversed to find `TaggedTemplateExpression` nodes whose tag name matches the configured list (default: `html`). For each match, the template literal's content (between the backticks) and its `${...}` expression positions are extracted.
|
|
12
|
+
|
|
13
|
+
2. **Parse** — Each extracted HTML string is passed to the base `HtmlParser` with offset options (`offsetOffset`, `offsetLine`, `offsetColumn`) so that source positions in the resulting AST map back to the original file. The `${...}` expressions are handled via the `ignoreTags` mechanism (start: `${`, end: `}`), which masks them before HTML parsing and restores them as `#ps:ttl-expression` preprocessor-specific block nodes.
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
.ts/.js source file
|
|
17
|
+
|
|
|
18
|
+
v
|
|
19
|
+
[findTemplateLiterals] — typescript-estree AST traversal
|
|
20
|
+
| finds TaggedTemplateExpression nodes
|
|
21
|
+
v
|
|
22
|
+
[HtmlParser.parse()] — with offset options for position mapping
|
|
23
|
+
| ${...} masked via ignoreTags
|
|
24
|
+
v
|
|
25
|
+
markuplint AST — positions reference the original file
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Tag Name Resolution
|
|
29
|
+
|
|
30
|
+
The parser resolves tag names from the `TaggedTemplateExpression.tag` node:
|
|
31
|
+
|
|
32
|
+
| Tag Form | Resolved Name | Example |
|
|
33
|
+
| ---------------------- | ------------- | ---------------------------- |
|
|
34
|
+
| Identifier | `tag.name` | `` html`...` `` → `html` |
|
|
35
|
+
| MemberExpression | property name | `` Lit.html`...` `` → `html` |
|
|
36
|
+
| Other expression forms | `''` (empty) | Not matched |
|
|
37
|
+
|
|
38
|
+
## ignoreTags Configuration
|
|
39
|
+
|
|
40
|
+
The parser defines a single ignore pattern:
|
|
41
|
+
|
|
42
|
+
| Type | Start | End | Description |
|
|
43
|
+
| ---------------- | ----- | --- | -------------------------------- |
|
|
44
|
+
| `ttl-expression` | `${` | `}` | Template literal expression slot |
|
|
45
|
+
|
|
46
|
+
This reuses the same masking/restoration pipeline that template engine parsers (EJS, Liquid, etc.) use for their template syntax.
|
|
47
|
+
|
|
48
|
+
## Multiple Template Literals
|
|
49
|
+
|
|
50
|
+
When a source file contains multiple tagged template literals, each is parsed independently and the resulting node lists are concatenated in source order (ordered by `contentStart`). Each template literal's nodes have positions correctly mapped to their location in the original source file.
|
|
51
|
+
|
|
52
|
+
## Limitations
|
|
53
|
+
|
|
54
|
+
- **`ignoreBlock` string matching**: The `${...}` masking uses simple start/end delimiter matching. Expressions containing nested `}` characters (e.g., `${{ key: value }}`) may be incorrectly split. The `findTemplateLiterals` function extracts precise expression positions via the AST, but this information is not yet used to replace the `ignoreBlock` mechanism.
|
|
55
|
+
- **JSX**: The TypeScript parser is configured with `jsx: false`. Files containing JSX syntax (`.tsx`) will fail to parse. Use `@markuplint/jsx-parser` for JSX/TSX files.
|
|
56
|
+
|
|
57
|
+
## Directory Structure
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
src/
|
|
61
|
+
├── index.ts — Re-exports parser and TaggedTemplateLiteralParser class
|
|
62
|
+
├── parser.ts — TaggedTemplateLiteralParser class
|
|
63
|
+
├── find-template-literals.ts — TypeScript AST traversal for template extraction
|
|
64
|
+
├── index.spec.ts — Parser integration tests
|
|
65
|
+
└── find-template-literals.spec.ts — Template extraction unit tests
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Key Source Files
|
|
69
|
+
|
|
70
|
+
| File | Purpose |
|
|
71
|
+
| ------------------------------- | -------------------------------------------------------------------------- |
|
|
72
|
+
| `src/parser.ts` | `TaggedTemplateLiteralParser` extending `HtmlParser`; singleton `parser` |
|
|
73
|
+
| `src/find-template-literals.ts` | AST traversal to locate and extract tagged template literals |
|
|
74
|
+
| `src/index.ts` | Package entry point; re-exports `parser` and `TaggedTemplateLiteralParser` |
|
|
75
|
+
|
|
76
|
+
## Integration Points
|
|
77
|
+
|
|
78
|
+
```mermaid
|
|
79
|
+
flowchart TD
|
|
80
|
+
subgraph upstream ["Upstream"]
|
|
81
|
+
htmlParser["@markuplint/html-parser\n(HtmlParser class)"]
|
|
82
|
+
tsEstree["@typescript-eslint/typescript-estree\n(TypeScript AST)"]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
subgraph pkg ["@markuplint/tagged-template-literal-parser"]
|
|
86
|
+
findTpl["findTemplateLiterals()\n(AST traversal)"]
|
|
87
|
+
ttlParser["TaggedTemplateLiteralParser\nextends HtmlParser"]
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
subgraph downstream ["Downstream"]
|
|
91
|
+
mlCore["@markuplint/ml-core\n(MLASTDocument → MLDOM)"]
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
tsEstree -->|"parses source"| findTpl
|
|
95
|
+
findTpl -->|"template info"| ttlParser
|
|
96
|
+
htmlParser -->|"extends"| ttlParser
|
|
97
|
+
ttlParser -->|"produces MLASTDocument"| mlCore
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Documentation Map
|
|
101
|
+
|
|
102
|
+
- [Maintenance Guide](docs/maintenance.md) — Commands, recipes, and testing
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
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
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- **tagged-template-literal-parser:** add tagged template literal parser ([5224ef4](https://github.com/markuplint/markuplint/commit/5224ef40c5d5c6baa4621b86f9a1b251a83b2b91)), closes [#221](https://github.com/markuplint/markuplint/issues/221)
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017-2024 Yusuke Hirao
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# @markuplint/tagged-template-literal-parser
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@markuplint/tagged-template-literal-parser)
|
|
4
|
+
|
|
5
|
+
Use **markuplint** with [**tagged template literals**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates) containing HTML, such as those used by [lit-html](https://lit.dev/docs/templates/overview/), [lit-element](https://lit.dev/docs/components/rendering/), and similar libraries.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```shell
|
|
10
|
+
$ npm install -D @markuplint/tagged-template-literal-parser
|
|
11
|
+
|
|
12
|
+
$ yarn add -D @markuplint/tagged-template-literal-parser
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Add `parser` option to your [configuration](https://markuplint.dev/configuration/#properties/parser).
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"parser": {
|
|
22
|
+
"\\.ts$": "@markuplint/tagged-template-literal-parser"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This parser extracts HTML from tagged template literals like:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { html } from 'lit';
|
|
31
|
+
|
|
32
|
+
const greeting = html` <h1>Hello ${name}</h1> `;
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The `${...}` expressions are treated as opaque blocks and preserved in the AST as `#ps:ttl-expression` nodes.
|
|
36
|
+
|
|
37
|
+
## Supported Tag Names
|
|
38
|
+
|
|
39
|
+
By default, this parser recognizes template literals tagged with `html`. Member expression tags are also supported (e.g., `LitElement.html`).
|
|
40
|
+
|
|
41
|
+
### Custom Tag Names
|
|
42
|
+
|
|
43
|
+
To support additional tag names (e.g., `svg`), import the `TaggedTemplateLiteralParser` class and create a custom instance:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { TaggedTemplateLiteralParser } from '@markuplint/tagged-template-literal-parser';
|
|
47
|
+
|
|
48
|
+
const parser = new TaggedTemplateLiteralParser(['html', 'svg']);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## :warning: Known Limitations
|
|
52
|
+
|
|
53
|
+
- **Nested `}` in expressions**: The `${...}` masking uses a simple start/end delimiter match. Expressions containing unmatched `}` characters (e.g., `${{ key: value }}`) may be incorrectly split.
|
|
54
|
+
- **Unquoted attribute values with expressions**: Template expressions inside unquoted attribute values are not supported. This is a shared limitation across all template engine parsers.
|
|
55
|
+
- **`.tsx` files**: This parser uses `jsx: false` when parsing TypeScript. Files containing JSX syntax (`.tsx`) may fail to parse. Use `@markuplint/jsx-parser` for JSX/TSX files instead.
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Maintenance tasks for @markuplint/tagged-template-literal-parser
|
|
3
|
+
globs:
|
|
4
|
+
- packages/@markuplint/tagged-template-literal-parser/src/**/*.ts
|
|
5
|
+
alwaysApply: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# @markuplint/tagged-template-literal-parser Maintenance
|
|
9
|
+
|
|
10
|
+
You are maintaining `@markuplint/tagged-template-literal-parser`, the tagged template literal parser for markuplint.
|
|
11
|
+
|
|
12
|
+
## Architecture
|
|
13
|
+
|
|
14
|
+
See [ARCHITECTURE.md](ARCHITECTURE.md) for the full architecture overview including the two-stage parsing pipeline and integration points.
|
|
15
|
+
|
|
16
|
+
For detailed maintenance procedures, see [docs/maintenance.md](docs/maintenance.md) ([Japanese](docs/maintenance.ja.md)).
|
|
17
|
+
|
|
18
|
+
## Key Files
|
|
19
|
+
|
|
20
|
+
| File | Role |
|
|
21
|
+
| ------------------------------- | --------------------------------------------------------- |
|
|
22
|
+
| `src/parser.ts` | TaggedTemplateLiteralParser class extending HtmlParser |
|
|
23
|
+
| `src/find-template-literals.ts` | TypeScript AST traversal to find tagged template literals |
|
|
24
|
+
| `src/index.ts` | Package entry point; re-exports parser and class |
|
|
25
|
+
|
|
26
|
+
## Tasks
|
|
27
|
+
|
|
28
|
+
### add-tag-name
|
|
29
|
+
|
|
30
|
+
Add support for a new tag function name (e.g., `svg`, `css`).
|
|
31
|
+
|
|
32
|
+
1. The default parser instance in `src/parser.ts` is configured with `['html']`
|
|
33
|
+
2. To add more default tags, modify the constructor default: `tagNames: readonly string[] = ['html', 'svg']`
|
|
34
|
+
3. Add a test in `src/find-template-literals.spec.ts` to verify the new tag is recognized
|
|
35
|
+
4. Add an integration test in `src/index.spec.ts` to verify HTML is correctly parsed from the new tag
|
|
36
|
+
5. Build: `yarn build --scope @markuplint/tagged-template-literal-parser`
|
|
37
|
+
6. Test: `npx vitest run packages/@markuplint/tagged-template-literal-parser/src/`
|
|
38
|
+
|
|
39
|
+
### modify-expression-handling
|
|
40
|
+
|
|
41
|
+
Modify how `${...}` expressions are handled (e.g., changing the PSBlock type name).
|
|
42
|
+
|
|
43
|
+
1. Open `src/parser.ts` and modify the `ignoreTags` entry in the constructor
|
|
44
|
+
2. Update the `type` field to change the PSBlock node name (currently `ttl-expression`)
|
|
45
|
+
3. Update all affected test assertions in `src/index.spec.ts` (search for `#ps:ttl-expression`)
|
|
46
|
+
4. Build: `yarn build --scope @markuplint/tagged-template-literal-parser`
|
|
47
|
+
5. Test: `npx vitest run packages/@markuplint/tagged-template-literal-parser/src/`
|
|
48
|
+
|
|
49
|
+
### add-tag-resolution-pattern
|
|
50
|
+
|
|
51
|
+
Add support for a new tag expression form (e.g., call expressions like `html(options)\`...\``).
|
|
52
|
+
|
|
53
|
+
1. Open `src/find-template-literals.ts`
|
|
54
|
+
2. Add a new case to `resolveTagName()` for the expression type
|
|
55
|
+
3. Add a test in `src/find-template-literals.spec.ts` to verify the tag is resolved
|
|
56
|
+
4. Add an integration test in `src/index.spec.ts`
|
|
57
|
+
5. Build and test as above
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# メンテナンスガイド
|
|
2
|
+
|
|
3
|
+
## コマンド
|
|
4
|
+
|
|
5
|
+
| コマンド | 説明 |
|
|
6
|
+
| -------------------------------------------------------------------- | ---------------------- |
|
|
7
|
+
| `yarn build --scope @markuplint/tagged-template-literal-parser` | このパッケージをビルド |
|
|
8
|
+
| `yarn dev --scope @markuplint/tagged-template-literal-parser` | ウォッチモードでビルド |
|
|
9
|
+
| `yarn clean --scope @markuplint/tagged-template-literal-parser` | ビルド成果物を削除 |
|
|
10
|
+
| `npx vitest run packages/@markuplint/tagged-template-literal-parser` | テストを実行 |
|
|
11
|
+
|
|
12
|
+
## テスト
|
|
13
|
+
|
|
14
|
+
テストファイルは `*.spec.ts` の命名規則に従い、`src/` ディレクトリに配置されています:
|
|
15
|
+
|
|
16
|
+
| テストファイル | カバレッジ |
|
|
17
|
+
| -------------------------------- | ------------------------------------------------------------ |
|
|
18
|
+
| `index.spec.ts` | パーサー統合テスト(ノードリスト構造、式、属性等) |
|
|
19
|
+
| `find-template-literals.spec.ts` | テンプレートリテラル抽出ユニットテスト(タグ検出、式の位置) |
|
|
20
|
+
|
|
21
|
+
主なテストパターンでは `nodeListToDebugMaps` を使用したスナップショット形式のアサーションを行います:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { nodeListToDebugMaps } from '@markuplint/parser-utils';
|
|
25
|
+
import { parser } from './parser.js';
|
|
26
|
+
|
|
27
|
+
const doc = parser.parse('const t = html`<div>${name}</div>`;');
|
|
28
|
+
expect(nodeListToDebugMaps(doc.nodeList)).toStrictEqual([
|
|
29
|
+
'[1:16]>[1:21](15,20)div: <div>',
|
|
30
|
+
'[1:21]>[1:28](20,27)#ps:ttl-expression: ${name}',
|
|
31
|
+
'[1:28]>[1:34](27,33)div: </div>',
|
|
32
|
+
]);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
テンプレートリテラル抽出テストでは、タグ付きテンプレートが正しく検出され位置が正確であることを検証します:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { findTemplateLiterals } from './find-template-literals.js';
|
|
39
|
+
|
|
40
|
+
const results = findTemplateLiterals('const t = html`<div></div>`;');
|
|
41
|
+
expect(results).toHaveLength(1);
|
|
42
|
+
expect(results[0].tagName).toBe('html');
|
|
43
|
+
expect(results[0].htmlContent).toBe('<div></div>');
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## レシピ
|
|
47
|
+
|
|
48
|
+
### 1. デフォルトタグ名の追加
|
|
49
|
+
|
|
50
|
+
新しいタグ関数名をデフォルトでサポートする必要がある場合:
|
|
51
|
+
|
|
52
|
+
1. `src/parser.ts` を開く
|
|
53
|
+
2. コンストラクタのデフォルトパラメータを変更して新しいタグを含める:
|
|
54
|
+
```ts
|
|
55
|
+
constructor(tagNames: readonly string[] = ['html', 'svg']) {
|
|
56
|
+
```
|
|
57
|
+
3. `src/find-template-literals.spec.ts` にテストを追加:
|
|
58
|
+
```ts
|
|
59
|
+
test('finds svg tagged template by default', () => {
|
|
60
|
+
const results = findTemplateLiterals('const t = svg`<circle />`;', ['svg']);
|
|
61
|
+
expect(results).toHaveLength(1);
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
4. `src/index.spec.ts` に統合テストを追加
|
|
65
|
+
5. ビルド: `yarn build --scope @markuplint/tagged-template-literal-parser`
|
|
66
|
+
6. テスト: `npx vitest run packages/@markuplint/tagged-template-literal-parser/src/`
|
|
67
|
+
|
|
68
|
+
### 2. 新しいタグ解決パターンの追加
|
|
69
|
+
|
|
70
|
+
新しいタグ式形式の認識が必要な場合(例: コール式):
|
|
71
|
+
|
|
72
|
+
1. `src/find-template-literals.ts` を開く
|
|
73
|
+
2. `resolveTagName` 関数に新しいケースを追加:
|
|
74
|
+
```ts
|
|
75
|
+
case AST_NODE_TYPES.CallExpression: {
|
|
76
|
+
// html(options)`...` パターンを処理
|
|
77
|
+
return resolveTagName(tag.callee);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
3. `src/find-template-literals.spec.ts` にテストを追加
|
|
81
|
+
4. `src/index.spec.ts` に統合テストを追加
|
|
82
|
+
5. 上記と同様にビルド・テスト
|
|
83
|
+
|
|
84
|
+
### 3. 式の処理の変更
|
|
85
|
+
|
|
86
|
+
`${...}` 式のマスクまたは復元方法を変更する場合:
|
|
87
|
+
|
|
88
|
+
1. `src/parser.ts` を開き、コンストラクタ内の `ignoreTags` 配列を見つける
|
|
89
|
+
2. `type`、`start`、`end` フィールドを必要に応じて変更
|
|
90
|
+
3. `src/index.spec.ts` 内の影響を受けるすべてのテストを更新:
|
|
91
|
+
- `#ps:ttl-expression` を検索し、新しいタイプ名に更新
|
|
92
|
+
4. 上記と同様にビルド・テスト
|
|
93
|
+
|
|
94
|
+
## 下流への影響
|
|
95
|
+
|
|
96
|
+
このパッケージはリーフパーサーであり、他のパッケージはこれに依存していません。`@markuplint/tagged-template-literal-parser` への変更は、下流パッケージのテストを必要としません。
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Maintenance Guide
|
|
2
|
+
|
|
3
|
+
## Commands
|
|
4
|
+
|
|
5
|
+
| Command | Description |
|
|
6
|
+
| -------------------------------------------------------------------- | ---------------------- |
|
|
7
|
+
| `yarn build --scope @markuplint/tagged-template-literal-parser` | Build this package |
|
|
8
|
+
| `yarn dev --scope @markuplint/tagged-template-literal-parser` | Watch mode build |
|
|
9
|
+
| `yarn clean --scope @markuplint/tagged-template-literal-parser` | Remove build artifacts |
|
|
10
|
+
| `npx vitest run packages/@markuplint/tagged-template-literal-parser` | Run tests |
|
|
11
|
+
|
|
12
|
+
## Testing
|
|
13
|
+
|
|
14
|
+
Test files follow the `*.spec.ts` naming convention and are located in the `src/` directory:
|
|
15
|
+
|
|
16
|
+
| Test File | Coverage |
|
|
17
|
+
| -------------------------------- | ----------------------------------------------------------------------------- |
|
|
18
|
+
| `index.spec.ts` | Parser integration tests (node list structure, expressions, attributes, etc.) |
|
|
19
|
+
| `find-template-literals.spec.ts` | Template literal extraction unit tests (tag detection, expression positions) |
|
|
20
|
+
|
|
21
|
+
The primary testing pattern uses `nodeListToDebugMaps` for snapshot-style assertions:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { nodeListToDebugMaps } from '@markuplint/parser-utils';
|
|
25
|
+
import { parser } from './parser.js';
|
|
26
|
+
|
|
27
|
+
const doc = parser.parse('const t = html`<div>${name}</div>`;');
|
|
28
|
+
expect(nodeListToDebugMaps(doc.nodeList)).toStrictEqual([
|
|
29
|
+
'[1:16]>[1:21](15,20)div: <div>',
|
|
30
|
+
'[1:21]>[1:28](20,27)#ps:ttl-expression: ${name}',
|
|
31
|
+
'[1:28]>[1:34](27,33)div: </div>',
|
|
32
|
+
]);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Template literal extraction tests verify that tagged templates are correctly found and positions are accurate:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { findTemplateLiterals } from './find-template-literals.js';
|
|
39
|
+
|
|
40
|
+
const results = findTemplateLiterals('const t = html`<div></div>`;');
|
|
41
|
+
expect(results).toHaveLength(1);
|
|
42
|
+
expect(results[0].tagName).toBe('html');
|
|
43
|
+
expect(results[0].htmlContent).toBe('<div></div>');
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Recipes
|
|
47
|
+
|
|
48
|
+
### 1. Adding a Default Tag Name
|
|
49
|
+
|
|
50
|
+
When a new tag function name needs to be supported by default:
|
|
51
|
+
|
|
52
|
+
1. Open `src/parser.ts`
|
|
53
|
+
2. Modify the constructor default parameter to include the new tag:
|
|
54
|
+
```ts
|
|
55
|
+
constructor(tagNames: readonly string[] = ['html', 'svg']) {
|
|
56
|
+
```
|
|
57
|
+
3. Add a test in `src/find-template-literals.spec.ts`:
|
|
58
|
+
```ts
|
|
59
|
+
test('finds svg tagged template by default', () => {
|
|
60
|
+
const results = findTemplateLiterals('const t = svg`<circle />`;', ['svg']);
|
|
61
|
+
expect(results).toHaveLength(1);
|
|
62
|
+
});
|
|
63
|
+
```
|
|
64
|
+
4. Add an integration test in `src/index.spec.ts`
|
|
65
|
+
5. Build: `yarn build --scope @markuplint/tagged-template-literal-parser`
|
|
66
|
+
6. Test: `npx vitest run packages/@markuplint/tagged-template-literal-parser/src/`
|
|
67
|
+
|
|
68
|
+
### 2. Adding a New Tag Resolution Pattern
|
|
69
|
+
|
|
70
|
+
When a new tag expression form needs to be recognized (e.g., call expressions):
|
|
71
|
+
|
|
72
|
+
1. Open `src/find-template-literals.ts`
|
|
73
|
+
2. Add a new case to the `resolveTagName` function:
|
|
74
|
+
```ts
|
|
75
|
+
case AST_NODE_TYPES.CallExpression: {
|
|
76
|
+
// Handle html(options)`...` patterns
|
|
77
|
+
return resolveTagName(tag.callee);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
3. Add a test in `src/find-template-literals.spec.ts`
|
|
81
|
+
4. Add an integration test in `src/index.spec.ts`
|
|
82
|
+
5. Build and test as above
|
|
83
|
+
|
|
84
|
+
### 3. Modifying Expression Handling
|
|
85
|
+
|
|
86
|
+
When changing how `${...}` expressions are masked or restored:
|
|
87
|
+
|
|
88
|
+
1. Open `src/parser.ts` and find the `ignoreTags` array in the constructor
|
|
89
|
+
2. Modify the `type`, `start`, or `end` fields as needed
|
|
90
|
+
3. Update all affected tests in `src/index.spec.ts`:
|
|
91
|
+
- Search for `#ps:ttl-expression` and update to the new type name
|
|
92
|
+
4. Build and test as above
|
|
93
|
+
|
|
94
|
+
## Downstream Impact
|
|
95
|
+
|
|
96
|
+
This package is a leaf parser — no other packages depend on it. Changes to `@markuplint/tagged-template-literal-parser` do not require testing downstream packages.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a template literal expression (`${...}`) within a tagged template.
|
|
3
|
+
*/
|
|
4
|
+
export interface TemplateExpression {
|
|
5
|
+
/** The raw source text of the expression including `${` and `}` */
|
|
6
|
+
readonly raw: string;
|
|
7
|
+
/** Start offset in the original source */
|
|
8
|
+
readonly start: number;
|
|
9
|
+
/** End offset in the original source */
|
|
10
|
+
readonly end: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Represents a tagged template literal found in the source code.
|
|
14
|
+
*/
|
|
15
|
+
export interface TemplateLiteralInfo {
|
|
16
|
+
/** The tag name (e.g., 'html') */
|
|
17
|
+
readonly tagName: string;
|
|
18
|
+
/** The full raw content between the backticks (excluding the backticks themselves) */
|
|
19
|
+
readonly htmlContent: string;
|
|
20
|
+
/** Start offset of the content (after the opening backtick) */
|
|
21
|
+
readonly contentStart: number;
|
|
22
|
+
/** End offset of the content (before the closing backtick) */
|
|
23
|
+
readonly contentEnd: number;
|
|
24
|
+
/** The expressions (`${...}`) found within the template literal */
|
|
25
|
+
readonly expressions: readonly TemplateExpression[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Finds all tagged template literals in a TypeScript/JavaScript source file
|
|
29
|
+
* that match the given tag names. Uses `@typescript-eslint/typescript-estree`
|
|
30
|
+
* to parse the source and recursively searches the AST for
|
|
31
|
+
* `TaggedTemplateExpression` nodes whose tag resolves to one of the specified names.
|
|
32
|
+
*
|
|
33
|
+
* @param sourceCode - The raw TypeScript/JavaScript source code to search
|
|
34
|
+
* @param tagNames - Array of tag function names to match (default: `['html']`)
|
|
35
|
+
* @returns Array of template literal information objects, ordered by their position in the source
|
|
36
|
+
*/
|
|
37
|
+
export declare function findTemplateLiterals(sourceCode: string, tagNames?: readonly string[]): readonly TemplateLiteralInfo[];
|