@prover-coder-ai/component-tagger 1.0.24 → 1.0.25
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/README.md +99 -3
- package/babel.cjs +12 -11
- package/dist/core/component-path.d.ts +1 -1
- package/dist/core/component-path.js +7 -7
- package/dist/core/jsx-tagger.d.ts +9 -4
- package/dist/core/jsx-tagger.js +15 -14
- package/dist/index.d.ts +1 -1
- package/dist/shell/babel-plugin.d.ts +5 -0
- package/dist/shell/babel-plugin.js +9 -8
- package/dist/shell/component-tagger.d.ts +12 -1
- package/dist/shell/component-tagger.js +15 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,17 +1,32 @@
|
|
|
1
1
|
# @prover-coder-ai/component-tagger
|
|
2
2
|
|
|
3
|
-
Vite plugin that adds a
|
|
3
|
+
Vite and Babel plugin that adds a `data-path` attribute to every JSX opening tag, enabling component source location tracking.
|
|
4
4
|
|
|
5
|
-
Example output
|
|
5
|
+
## Example output
|
|
6
6
|
|
|
7
7
|
```html
|
|
8
|
-
<h1 path="src/App.tsx:22:4">Hello</h1>
|
|
8
|
+
<h1 data-path="src/App.tsx:22:4">Hello</h1>
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Format: `<relative-file-path>:<line>:<column>`
|
|
12
12
|
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- ✅ **Idempotent**: adds `data-path` only if it doesn't already exist
|
|
16
|
+
- ✅ **HTML5 compliant**: uses standard `data-*` attributes
|
|
17
|
+
- ✅ **Configurable**: customize the attribute name via options
|
|
18
|
+
- ✅ **Dual plugin support**: works with both Vite and Babel
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install @prover-coder-ai/component-tagger
|
|
24
|
+
```
|
|
25
|
+
|
|
13
26
|
## Usage
|
|
14
27
|
|
|
28
|
+
### Vite Plugin
|
|
29
|
+
|
|
15
30
|
```ts
|
|
16
31
|
import { defineConfig, type PluginOption } from "vite"
|
|
17
32
|
import { componentTagger } from "@prover-coder-ai/component-tagger"
|
|
@@ -23,3 +38,84 @@ export default defineConfig(({ mode }) => {
|
|
|
23
38
|
return { plugins }
|
|
24
39
|
})
|
|
25
40
|
```
|
|
41
|
+
|
|
42
|
+
**With custom attribute name:**
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const plugins = [
|
|
46
|
+
isDevelopment && componentTagger({ attributeName: "data-component-path" })
|
|
47
|
+
].filter(Boolean) as PluginOption[]
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Babel Plugin (e.g., Next.js)
|
|
51
|
+
|
|
52
|
+
Add to your `.babelrc`:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"presets": ["next/babel"],
|
|
57
|
+
"env": {
|
|
58
|
+
"development": {
|
|
59
|
+
"plugins": ["@prover-coder-ai/component-tagger/babel"]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**With options:**
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"presets": ["next/babel"],
|
|
70
|
+
"env": {
|
|
71
|
+
"development": {
|
|
72
|
+
"plugins": [
|
|
73
|
+
[
|
|
74
|
+
"@prover-coder-ai/component-tagger/babel",
|
|
75
|
+
{
|
|
76
|
+
"rootDir": "/custom/root",
|
|
77
|
+
"attributeName": "data-component-path"
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Options
|
|
87
|
+
|
|
88
|
+
### Vite Plugin Options
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
type ComponentTaggerOptions = {
|
|
92
|
+
/**
|
|
93
|
+
* Name of the attribute to add to JSX elements.
|
|
94
|
+
* @default "data-path"
|
|
95
|
+
*/
|
|
96
|
+
attributeName?: string
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Babel Plugin Options
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
type ComponentTaggerBabelPluginOptions = {
|
|
104
|
+
/**
|
|
105
|
+
* Root directory for computing relative paths.
|
|
106
|
+
* @default process.cwd()
|
|
107
|
+
*/
|
|
108
|
+
rootDir?: string
|
|
109
|
+
/**
|
|
110
|
+
* Name of the attribute to add to JSX elements.
|
|
111
|
+
* @default "data-path"
|
|
112
|
+
*/
|
|
113
|
+
attributeName?: string
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Behavior Guarantees
|
|
118
|
+
|
|
119
|
+
- **Idempotency**: If `data-path` (or custom attribute) already exists on an element, no duplicate is added
|
|
120
|
+
- **Default attribute**: `data-path` is used when no `attributeName` is specified
|
|
121
|
+
- **Standard compliance**: Uses HTML5 `data-*` custom attributes by default
|
package/babel.cjs
CHANGED
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* "plugins": ["@prover-coder-ai/component-tagger/babel"]
|
|
12
12
|
* }
|
|
13
13
|
*/
|
|
14
|
-
// CHANGE: provide CommonJS entry point for Babel plugin.
|
|
15
|
-
// WHY: Babel configuration often requires CommonJS modules.
|
|
16
|
-
// REF: issue-12
|
|
14
|
+
// CHANGE: provide CommonJS entry point for Babel plugin with configurable attributeName.
|
|
15
|
+
// WHY: Babel configuration often requires CommonJS modules; support custom attribute names.
|
|
16
|
+
// REF: issue-12, issue-14
|
|
17
17
|
// FORMAT THEOREM: forall require: require(babel.cjs) -> PluginFactory
|
|
18
18
|
// PURITY: SHELL
|
|
19
19
|
// EFFECT: n/a
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
const path = require("node:path")
|
|
24
24
|
|
|
25
|
-
const componentPathAttributeName = "path"
|
|
25
|
+
const componentPathAttributeName = "data-path"
|
|
26
26
|
const jsxFilePattern = /\.(tsx|jsx)(\?.*)?$/u
|
|
27
27
|
|
|
28
28
|
const isJsxFile = (id) => jsxFilePattern.test(id)
|
|
@@ -58,21 +58,22 @@ module.exports = function componentTaggerBabelPlugin({ types: t }) {
|
|
|
58
58
|
return
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
//
|
|
62
|
-
if (attrExists(node, componentPathAttributeName, t)) {
|
|
63
|
-
return
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Compute relative path from root
|
|
61
|
+
// Compute relative path from root and get attribute name
|
|
67
62
|
const opts = state.opts || {}
|
|
68
63
|
const rootDir = opts.rootDir || state.cwd || process.cwd()
|
|
64
|
+
const attributeName = opts.attributeName || componentPathAttributeName
|
|
69
65
|
const relativeFilename = path.relative(rootDir, filename)
|
|
70
66
|
|
|
67
|
+
// Skip if already has the specified attribute (idempotency)
|
|
68
|
+
if (attrExists(node, attributeName, t)) {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
71
72
|
const { column, line } = node.loc.start
|
|
72
73
|
const value = formatComponentPathValue(relativeFilename, line, column)
|
|
73
74
|
|
|
74
75
|
node.attributes.push(
|
|
75
|
-
t.jsxAttribute(t.jsxIdentifier(
|
|
76
|
+
t.jsxAttribute(t.jsxIdentifier(attributeName), t.stringLiteral(value))
|
|
76
77
|
)
|
|
77
78
|
}
|
|
78
79
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
const jsxFilePattern = /\.(tsx|jsx)(\?.*)?$/u;
|
|
2
|
-
// CHANGE:
|
|
3
|
-
// WHY:
|
|
4
|
-
// QUOTE(
|
|
5
|
-
// REF:
|
|
6
|
-
// SOURCE:
|
|
7
|
-
// FORMAT THEOREM: forall a in AttributeName: a = "path"
|
|
2
|
+
// CHANGE: rename attribute from "path" to "data-path" for HTML5 compliance.
|
|
3
|
+
// WHY: data-* attributes are standard HTML5 custom data attributes, improving compatibility.
|
|
4
|
+
// QUOTE(issue-14): "Rename attribute path → data-path (breaking change)"
|
|
5
|
+
// REF: issue-14
|
|
6
|
+
// SOURCE: https://html.spec.whatwg.org/multipage/dom.html#custom-data-attribute
|
|
7
|
+
// FORMAT THEOREM: forall a in AttributeName: a = "data-path"
|
|
8
8
|
// PURITY: CORE
|
|
9
9
|
// EFFECT: n/a
|
|
10
10
|
// INVARIANT: attribute name remains stable across transforms
|
|
11
11
|
// COMPLEXITY: O(1)/O(1)
|
|
12
|
-
export const componentPathAttributeName = "path";
|
|
12
|
+
export const componentPathAttributeName = "data-path";
|
|
13
13
|
/**
|
|
14
14
|
* Checks whether the Vite id represents a JSX or TSX module.
|
|
15
15
|
*
|
|
@@ -9,6 +9,10 @@ export type JsxTaggerContext = {
|
|
|
9
9
|
* Relative file path from the project root.
|
|
10
10
|
*/
|
|
11
11
|
readonly relativeFilename: string;
|
|
12
|
+
/**
|
|
13
|
+
* Name of the attribute to add (defaults to "data-path").
|
|
14
|
+
*/
|
|
15
|
+
readonly attributeName: string;
|
|
12
16
|
};
|
|
13
17
|
/**
|
|
14
18
|
* Checks if a JSX attribute with the given name already exists on the element.
|
|
@@ -25,6 +29,7 @@ export declare const attrExists: (node: t.JSXOpeningElement, attrName: string, t
|
|
|
25
29
|
/**
|
|
26
30
|
* Creates a JSX attribute with the component path value.
|
|
27
31
|
*
|
|
32
|
+
* @param attributeName - Name of the attribute to create.
|
|
28
33
|
* @param relativeFilename - Relative path to the file.
|
|
29
34
|
* @param line - 1-based line number.
|
|
30
35
|
* @param column - 0-based column number.
|
|
@@ -32,10 +37,10 @@ export declare const attrExists: (node: t.JSXOpeningElement, attrName: string, t
|
|
|
32
37
|
* @returns JSX attribute node with the path value.
|
|
33
38
|
*
|
|
34
39
|
* @pure true
|
|
35
|
-
* @invariant attribute name
|
|
40
|
+
* @invariant attribute name matches the provided attributeName parameter
|
|
36
41
|
* @complexity O(1)
|
|
37
42
|
*/
|
|
38
|
-
export declare const createPathAttribute: (relativeFilename: string, line: number, column: number, types: typeof t) => t.JSXAttribute;
|
|
43
|
+
export declare const createPathAttribute: (attributeName: string, relativeFilename: string, line: number, column: number, types: typeof t) => t.JSXAttribute;
|
|
39
44
|
/**
|
|
40
45
|
* Processes a single JSX opening element and adds path attribute if needed.
|
|
41
46
|
*
|
|
@@ -43,12 +48,12 @@ export declare const createPathAttribute: (relativeFilename: string, line: numbe
|
|
|
43
48
|
* Both the Vite plugin and standalone Babel plugin use this function.
|
|
44
49
|
*
|
|
45
50
|
* @param node - JSX opening element to process.
|
|
46
|
-
* @param context - Tagging context with relative filename.
|
|
51
|
+
* @param context - Tagging context with relative filename and attribute name.
|
|
47
52
|
* @param types - Babel types module.
|
|
48
53
|
* @returns true if attribute was added, false if skipped.
|
|
49
54
|
*
|
|
50
55
|
* @pure false (mutates node)
|
|
51
|
-
* @invariant each JSX element has at most one
|
|
56
|
+
* @invariant each JSX element has at most one instance of the specified attribute after processing
|
|
52
57
|
* @complexity O(n) where n = number of existing attributes
|
|
53
58
|
*/
|
|
54
59
|
export declare const processJsxElement: (node: t.JSXOpeningElement, context: JsxTaggerContext, types: typeof t) => boolean;
|
package/dist/core/jsx-tagger.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { formatComponentPathValue } from "./component-path.js";
|
|
2
2
|
/**
|
|
3
3
|
* Checks if a JSX attribute with the given name already exists on the element.
|
|
4
4
|
*
|
|
@@ -22,6 +22,7 @@ export const attrExists = (node, attrName, types) => node.attributes.some((attr)
|
|
|
22
22
|
/**
|
|
23
23
|
* Creates a JSX attribute with the component path value.
|
|
24
24
|
*
|
|
25
|
+
* @param attributeName - Name of the attribute to create.
|
|
25
26
|
* @param relativeFilename - Relative path to the file.
|
|
26
27
|
* @param line - 1-based line number.
|
|
27
28
|
* @param column - 0-based column number.
|
|
@@ -29,20 +30,20 @@ export const attrExists = (node, attrName, types) => node.attributes.some((attr)
|
|
|
29
30
|
* @returns JSX attribute node with the path value.
|
|
30
31
|
*
|
|
31
32
|
* @pure true
|
|
32
|
-
* @invariant attribute name
|
|
33
|
+
* @invariant attribute name matches the provided attributeName parameter
|
|
33
34
|
* @complexity O(1)
|
|
34
35
|
*/
|
|
35
|
-
// CHANGE:
|
|
36
|
-
// WHY:
|
|
37
|
-
// REF: issue-
|
|
38
|
-
// FORMAT THEOREM: ∀ f, l, c: createPathAttribute(f, l, c) = JSXAttribute(
|
|
36
|
+
// CHANGE: add attributeName parameter for configurable attribute names.
|
|
37
|
+
// WHY: support customizable attribute names while maintaining default "data-path".
|
|
38
|
+
// REF: issue-14 (add attributeName option)
|
|
39
|
+
// FORMAT THEOREM: ∀ n, f, l, c: createPathAttribute(n, f, l, c) = JSXAttribute(n, f:l:c)
|
|
39
40
|
// PURITY: CORE
|
|
40
41
|
// EFFECT: n/a
|
|
41
|
-
// INVARIANT: output format is always path:line:column
|
|
42
|
+
// INVARIANT: output format is always path:line:column with configurable attribute name
|
|
42
43
|
// COMPLEXITY: O(1)/O(1)
|
|
43
|
-
export const createPathAttribute = (relativeFilename, line, column, types) => {
|
|
44
|
+
export const createPathAttribute = (attributeName, relativeFilename, line, column, types) => {
|
|
44
45
|
const value = formatComponentPathValue(relativeFilename, line, column);
|
|
45
|
-
return types.jsxAttribute(types.jsxIdentifier(
|
|
46
|
+
return types.jsxAttribute(types.jsxIdentifier(attributeName), types.stringLiteral(value));
|
|
46
47
|
};
|
|
47
48
|
/**
|
|
48
49
|
* Processes a single JSX opening element and adds path attribute if needed.
|
|
@@ -51,12 +52,12 @@ export const createPathAttribute = (relativeFilename, line, column, types) => {
|
|
|
51
52
|
* Both the Vite plugin and standalone Babel plugin use this function.
|
|
52
53
|
*
|
|
53
54
|
* @param node - JSX opening element to process.
|
|
54
|
-
* @param context - Tagging context with relative filename.
|
|
55
|
+
* @param context - Tagging context with relative filename and attribute name.
|
|
55
56
|
* @param types - Babel types module.
|
|
56
57
|
* @returns true if attribute was added, false if skipped.
|
|
57
58
|
*
|
|
58
59
|
* @pure false (mutates node)
|
|
59
|
-
* @invariant each JSX element has at most one
|
|
60
|
+
* @invariant each JSX element has at most one instance of the specified attribute after processing
|
|
60
61
|
* @complexity O(n) where n = number of existing attributes
|
|
61
62
|
*/
|
|
62
63
|
// CHANGE: extract unified JSX element processing logic.
|
|
@@ -73,12 +74,12 @@ export const processJsxElement = (node, context, types) => {
|
|
|
73
74
|
if (node.loc === null || node.loc === undefined) {
|
|
74
75
|
return false;
|
|
75
76
|
}
|
|
76
|
-
// Skip if already has
|
|
77
|
-
if (attrExists(node,
|
|
77
|
+
// Skip if already has the specified attribute (idempotency)
|
|
78
|
+
if (attrExists(node, context.attributeName, types)) {
|
|
78
79
|
return false;
|
|
79
80
|
}
|
|
80
81
|
const { column, line } = node.loc.start;
|
|
81
|
-
const attr = createPathAttribute(context.relativeFilename, line, column, types);
|
|
82
|
+
const attr = createPathAttribute(context.attributeName, context.relativeFilename, line, column, types);
|
|
82
83
|
node.attributes.push(attr);
|
|
83
84
|
return true;
|
|
84
85
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { componentPathAttributeName, formatComponentPathValue, isJsxFile } from "./core/component-path.js";
|
|
2
2
|
export { attrExists, createJsxTaggerVisitor, createPathAttribute, type JsxTaggerContext, processJsxElement } from "./core/jsx-tagger.js";
|
|
3
3
|
export { componentTaggerBabelPlugin, type ComponentTaggerBabelPluginOptions } from "./shell/babel-plugin.js";
|
|
4
|
-
export { componentTagger } from "./shell/component-tagger.js";
|
|
4
|
+
export { componentTagger, type ComponentTaggerOptions } from "./shell/component-tagger.js";
|
|
@@ -8,6 +8,11 @@ export type ComponentTaggerBabelPluginOptions = {
|
|
|
8
8
|
* Defaults to process.cwd().
|
|
9
9
|
*/
|
|
10
10
|
readonly rootDir?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Name of the attribute to add to JSX elements.
|
|
13
|
+
* Defaults to "data-path".
|
|
14
|
+
*/
|
|
15
|
+
readonly attributeName?: string;
|
|
11
16
|
};
|
|
12
17
|
type BabelState = {
|
|
13
18
|
readonly filename?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { types as t } from "@babel/core";
|
|
2
|
-
import { isJsxFile } from "../core/component-path.js";
|
|
2
|
+
import { componentPathAttributeName, isJsxFile } from "../core/component-path.js";
|
|
3
3
|
import { createJsxTaggerVisitor } from "../core/jsx-tagger.js";
|
|
4
4
|
import { computeRelativePath } from "../core/path-service.js";
|
|
5
5
|
/**
|
|
@@ -12,14 +12,14 @@ import { computeRelativePath } from "../core/path-service.js";
|
|
|
12
12
|
* @invariant returns null when filename is undefined or not a JSX file
|
|
13
13
|
* @complexity O(n) where n = path length
|
|
14
14
|
*/
|
|
15
|
-
// CHANGE:
|
|
16
|
-
// WHY: enable unified visitor to work with Babel state.
|
|
17
|
-
// QUOTE(
|
|
18
|
-
// REF: issue-
|
|
15
|
+
// CHANGE: add support for configurable attributeName from options.
|
|
16
|
+
// WHY: enable unified visitor to work with Babel state and custom attribute names.
|
|
17
|
+
// QUOTE(issue-14): "Add option attributeName (default: data-path) for both plugins"
|
|
18
|
+
// REF: issue-14
|
|
19
19
|
// FORMAT THEOREM: ∀ state: getContext(state) = context ↔ isValidState(state)
|
|
20
20
|
// PURITY: CORE
|
|
21
21
|
// EFFECT: n/a
|
|
22
|
-
// INVARIANT: context contains valid relative path
|
|
22
|
+
// INVARIANT: context contains valid relative path and attribute name
|
|
23
23
|
// COMPLEXITY: O(n)/O(1)
|
|
24
24
|
const getContextFromState = (state) => {
|
|
25
25
|
const filename = state.filename;
|
|
@@ -32,9 +32,10 @@ const getContextFromState = (state) => {
|
|
|
32
32
|
return null;
|
|
33
33
|
}
|
|
34
34
|
// Compute relative path from root using Effect's Path service
|
|
35
|
-
const rootDir = state.opts?.rootDir ?? state.cwd ??
|
|
35
|
+
const rootDir = state.opts?.rootDir ?? state.cwd ?? process.cwd();
|
|
36
36
|
const relativeFilename = computeRelativePath(rootDir, filename);
|
|
37
|
-
|
|
37
|
+
const attributeName = state.opts?.attributeName ?? componentPathAttributeName;
|
|
38
|
+
return { relativeFilename, attributeName };
|
|
38
39
|
};
|
|
39
40
|
/**
|
|
40
41
|
* Creates a Babel plugin that injects component path attributes into JSX elements.
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
import type { PluginOption } from "vite";
|
|
2
|
+
/**
|
|
3
|
+
* Options for the component tagger Vite plugin.
|
|
4
|
+
*/
|
|
5
|
+
export type ComponentTaggerOptions = {
|
|
6
|
+
/**
|
|
7
|
+
* Name of the attribute to add to JSX elements.
|
|
8
|
+
* Defaults to "data-path".
|
|
9
|
+
*/
|
|
10
|
+
readonly attributeName?: string;
|
|
11
|
+
};
|
|
2
12
|
/**
|
|
3
13
|
* Creates a Vite plugin that injects a single component-path data attribute.
|
|
4
14
|
*
|
|
15
|
+
* @param options - Configuration options for the plugin.
|
|
5
16
|
* @returns Vite PluginOption for pre-transform tagging.
|
|
6
17
|
*
|
|
7
18
|
* @pure false
|
|
@@ -10,4 +21,4 @@ import type { PluginOption } from "vite";
|
|
|
10
21
|
* @complexity O(n) time / O(1) space per JSX module
|
|
11
22
|
* @throws Never - errors are typed and surfaced by Effect
|
|
12
23
|
*/
|
|
13
|
-
export declare const componentTagger: () => PluginOption;
|
|
24
|
+
export declare const componentTagger: (options?: ComponentTaggerOptions) => PluginOption;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { transformAsync, types as t } from "@babel/core";
|
|
2
2
|
import { Effect, pipe } from "effect";
|
|
3
|
-
import { isJsxFile } from "../core/component-path.js";
|
|
3
|
+
import { componentPathAttributeName, isJsxFile } from "../core/component-path.js";
|
|
4
4
|
import { createJsxTaggerVisitor } from "../core/jsx-tagger.js";
|
|
5
5
|
import { NodePathLayer, relativeFromRoot } from "../core/path-service.js";
|
|
6
6
|
class ComponentTaggerError extends Error {
|
|
@@ -25,8 +25,8 @@ const toViteResult = (result) => {
|
|
|
25
25
|
map: result.map ?? null
|
|
26
26
|
};
|
|
27
27
|
};
|
|
28
|
-
const makeBabelTagger = (relativeFilename) => {
|
|
29
|
-
const context = { relativeFilename };
|
|
28
|
+
const makeBabelTagger = (relativeFilename, attributeName) => {
|
|
29
|
+
const context = { relativeFilename, attributeName };
|
|
30
30
|
return {
|
|
31
31
|
name: "component-path-babel-tagger",
|
|
32
32
|
visitor: createJsxTaggerVisitor(() => context, t)
|
|
@@ -54,7 +54,7 @@ const makeBabelTagger = (relativeFilename) => {
|
|
|
54
54
|
// EFFECT: Effect<ViteTransformResult | null, ComponentTaggerError, never>
|
|
55
55
|
// INVARIANT: errors are surfaced as ComponentTaggerError only
|
|
56
56
|
// COMPLEXITY: O(n)/O(1)
|
|
57
|
-
const runTransform = (code, id, rootDir) => {
|
|
57
|
+
const runTransform = (code, id, rootDir, attributeName) => {
|
|
58
58
|
const cleanId = stripQuery(id);
|
|
59
59
|
return pipe(relativeFromRoot(rootDir, cleanId), Effect.flatMap((relative) => Effect.tryPromise({
|
|
60
60
|
try: () => transformAsync(code, {
|
|
@@ -65,7 +65,7 @@ const runTransform = (code, id, rootDir) => {
|
|
|
65
65
|
sourceType: "module",
|
|
66
66
|
plugins: ["typescript", "jsx", "decorators-legacy"]
|
|
67
67
|
},
|
|
68
|
-
plugins: [makeBabelTagger(relative)],
|
|
68
|
+
plugins: [makeBabelTagger(relative, attributeName)],
|
|
69
69
|
sourceMaps: true
|
|
70
70
|
}),
|
|
71
71
|
catch: (cause) => {
|
|
@@ -77,6 +77,7 @@ const runTransform = (code, id, rootDir) => {
|
|
|
77
77
|
/**
|
|
78
78
|
* Creates a Vite plugin that injects a single component-path data attribute.
|
|
79
79
|
*
|
|
80
|
+
* @param options - Configuration options for the plugin.
|
|
80
81
|
* @returns Vite PluginOption for pre-transform tagging.
|
|
81
82
|
*
|
|
82
83
|
* @pure false
|
|
@@ -85,17 +86,18 @@ const runTransform = (code, id, rootDir) => {
|
|
|
85
86
|
* @complexity O(n) time / O(1) space per JSX module
|
|
86
87
|
* @throws Never - errors are typed and surfaced by Effect
|
|
87
88
|
*/
|
|
88
|
-
// CHANGE:
|
|
89
|
-
// WHY:
|
|
90
|
-
// QUOTE(
|
|
91
|
-
// REF:
|
|
89
|
+
// CHANGE: add attributeName option with default "data-path".
|
|
90
|
+
// WHY: support customizable attribute names while maintaining backwards compatibility.
|
|
91
|
+
// QUOTE(issue-14): "Add option attributeName (default: data-path) for both plugins"
|
|
92
|
+
// REF: issue-14
|
|
92
93
|
// SOURCE: n/a
|
|
93
|
-
// FORMAT THEOREM: forall id: isJsxFile(id) -> transform(id) adds
|
|
94
|
+
// FORMAT THEOREM: forall id: isJsxFile(id) -> transform(id) adds specified attribute
|
|
94
95
|
// PURITY: SHELL
|
|
95
96
|
// EFFECT: Effect<ViteTransformResult | null, ComponentTaggerError, never>
|
|
96
|
-
// INVARIANT: no duplicate
|
|
97
|
+
// INVARIANT: no duplicate attributes with the same name
|
|
97
98
|
// COMPLEXITY: O(n)/O(1)
|
|
98
|
-
export const componentTagger = () => {
|
|
99
|
+
export const componentTagger = (options) => {
|
|
100
|
+
const attributeName = options?.attributeName ?? componentPathAttributeName;
|
|
99
101
|
let resolvedRoot = process.cwd();
|
|
100
102
|
return {
|
|
101
103
|
name: "component-path-tagger",
|
|
@@ -108,7 +110,7 @@ export const componentTagger = () => {
|
|
|
108
110
|
if (!isJsxFile(id)) {
|
|
109
111
|
return null;
|
|
110
112
|
}
|
|
111
|
-
return Effect.runPromise(pipe(runTransform(code, id, resolvedRoot), Effect.provide(NodePathLayer)));
|
|
113
|
+
return Effect.runPromise(pipe(runTransform(code, id, resolvedRoot, attributeName), Effect.provide(NodePathLayer)));
|
|
112
114
|
}
|
|
113
115
|
};
|
|
114
116
|
};
|