svelte-qa-ids 1.0.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/LICENSE +21 -0
- package/README.md +357 -0
- package/dist/chunk-7MRS72CP.js +404 -0
- package/dist/chunk-7MRS72CP.js.map +1 -0
- package/dist/cli.js +144 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/package-QZOBPTUC.js +107 -0
- package/dist/package-QZOBPTUC.js.map +1 -0
- package/package.json +66 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
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,357 @@
|
|
|
1
|
+
# svelte-qa-ids
|
|
2
|
+
|
|
3
|
+
> Automatically inject stable `data-qa-id` attributes into Svelte components for testing and automation
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Automated Injection**: Scans Svelte files and programmatically inserts `data-qa-id` attributes
|
|
8
|
+
- **Stable IDs**: Generates consistent, human-readable IDs based on component structure
|
|
9
|
+
- **Idempotent**: Removes old IDs before adding new ones for clean updates
|
|
10
|
+
- **Svelte 5 Compatible**: Uses `svelte.preprocess` for proper Svelte 5 syntax parsing
|
|
11
|
+
- **Smart Exclusion**: Skips icon libraries (e.g., `lucide-svelte`) and handles parsing errors gracefully
|
|
12
|
+
- **CLI & API**: Use as a command-line tool or programmatically in your build process
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -D svelte-qa-ids
|
|
18
|
+
# or
|
|
19
|
+
bun add -D svelte-qa-ids
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## CLI Usage
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Process all Svelte files in a directory
|
|
26
|
+
svelte-qa-ids src/
|
|
27
|
+
|
|
28
|
+
# Dry run to see what would change
|
|
29
|
+
svelte-qa-ids src/ --dry-run
|
|
30
|
+
|
|
31
|
+
# Process a single file
|
|
32
|
+
svelte-qa-ids src/routes/+page.svelte
|
|
33
|
+
|
|
34
|
+
# Verbose output
|
|
35
|
+
svelte-qa-ids src/ --verbose
|
|
36
|
+
|
|
37
|
+
# Custom component prefix length (2-10 characters)
|
|
38
|
+
svelte-qa-ids src/ --component 4
|
|
39
|
+
svelte-qa-ids src/ -c 2
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Programmatic Usage
|
|
43
|
+
|
|
44
|
+
### Effect-based API (Primary)
|
|
45
|
+
|
|
46
|
+
The library is built with Effect-ts for type-safe error handling and composability:
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { inject } from 'svelte-qa-ids';
|
|
50
|
+
import { Effect } from 'effect';
|
|
51
|
+
|
|
52
|
+
// Process a directory
|
|
53
|
+
const program = inject('src/', {
|
|
54
|
+
verbose: true,
|
|
55
|
+
prefixLength: 4,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Run the Effect
|
|
59
|
+
const results = await Effect.runPromise(program);
|
|
60
|
+
|
|
61
|
+
// Handle errors with Effect
|
|
62
|
+
const safeProgram = inject('src/', {
|
|
63
|
+
prefixLength: 4,
|
|
64
|
+
}).pipe(
|
|
65
|
+
Effect.catchAll((error) =>
|
|
66
|
+
Effect.succeed([
|
|
67
|
+
{
|
|
68
|
+
file: 'src/',
|
|
69
|
+
success: false,
|
|
70
|
+
error: error.message,
|
|
71
|
+
},
|
|
72
|
+
])
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const results = await Effect.runPromise(safeProgram);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Promise-based API (Convenience)
|
|
80
|
+
|
|
81
|
+
For simpler use cases, a Promise-based wrapper is provided:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { injectPromise } from 'svelte-qa-ids';
|
|
85
|
+
|
|
86
|
+
// Process a directory
|
|
87
|
+
const results = await injectPromise('src/', {
|
|
88
|
+
verbose: true,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Process a single file
|
|
92
|
+
await injectPromise('src/routes/+page.svelte');
|
|
93
|
+
|
|
94
|
+
// With custom options
|
|
95
|
+
await injectPromise('src/', {
|
|
96
|
+
dryRun: true,
|
|
97
|
+
prefixLength: 4,
|
|
98
|
+
abbreviations: {
|
|
99
|
+
'custom-button': 'cb',
|
|
100
|
+
},
|
|
101
|
+
skipComponents: ['MyIcon', 'AnotherIcon'],
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## data-qa-id Generation Rules
|
|
106
|
+
|
|
107
|
+
### Component Prefix
|
|
108
|
+
|
|
109
|
+
The component prefix is generated from the file path and can be customized with the `prefixLength` option (default: 3, range: 2-10).
|
|
110
|
+
|
|
111
|
+
**For pages (`src/routes/...`):**
|
|
112
|
+
- Uses first `prefixLength` letters of the route directory
|
|
113
|
+
- Example with default (prefixLength=3): `src/routes/analytics/+page.svelte` → `ana`
|
|
114
|
+
- Example with prefixLength=2: `src/routes/analytics/+page.svelte` → `an`
|
|
115
|
+
- Example with prefixLength=4: `src/routes/analytics/+page.svelte` → `anal`
|
|
116
|
+
- Root page (`src/routes/+page.svelte`) → `rt`
|
|
117
|
+
|
|
118
|
+
**For components (`src/lib/...`):**
|
|
119
|
+
- Uses first `prefixLength` letters of each word in filename
|
|
120
|
+
- Example with default (prefixLength=3): `upgrade-banner.svelte` → `upg-ban`
|
|
121
|
+
- Example with prefixLength=2: `upgrade-banner.svelte` → `up-ba`
|
|
122
|
+
- Example with prefixLength=4: `question-counter.svelte` → `quest-count`
|
|
123
|
+
|
|
124
|
+
### Tag Abbreviations
|
|
125
|
+
|
|
126
|
+
Common tags are abbreviated for conciseness:
|
|
127
|
+
|
|
128
|
+
| Tag | Abbreviation |
|
|
129
|
+
|-----|--------------|
|
|
130
|
+
| `div` | `d` |
|
|
131
|
+
| `button` | `btn` |
|
|
132
|
+
| `input` | `in` |
|
|
133
|
+
| `label` | `lbl` |
|
|
134
|
+
| `span` | `sp` |
|
|
135
|
+
| `section` | `sec` |
|
|
136
|
+
| `form` | `f` |
|
|
137
|
+
|
|
138
|
+
### Examples
|
|
139
|
+
|
|
140
|
+
```html
|
|
141
|
+
<!-- Before: src/routes/splash/+page.svelte -->
|
|
142
|
+
<div class="container">
|
|
143
|
+
<h1>Welcome</h1>
|
|
144
|
+
<button>Continue</button>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
147
|
+
<!-- After (default prefixLength=3) -->
|
|
148
|
+
<div class="container" data-qa-id="spl-d">
|
|
149
|
+
<h1 data-qa-id="spl-d-h1">Welcome</h1>
|
|
150
|
+
<button data-qa-id="spl-d-btn">Continue</button>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<!-- After (prefixLength=2) -->
|
|
154
|
+
<div class="container" data-qa-id="sp-d">
|
|
155
|
+
<h1 data-qa-id="sp-d-h1">Welcome</h1>
|
|
156
|
+
<button data-qa-id="sp-d-btn">Continue</button>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<!-- After (prefixLength=4) -->
|
|
160
|
+
<div class="container" data-qa-id="spla-d">
|
|
161
|
+
<h1 data-qa-id="spla-d-h1">Welcome</h1>
|
|
162
|
+
<button data-qa-id="spla-d-btn">Continue</button>
|
|
163
|
+
</div>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Component example** (`src/lib/question-counter.svelte`):
|
|
167
|
+
|
|
168
|
+
```html
|
|
169
|
+
<!-- Before -->
|
|
170
|
+
<div class="counter">
|
|
171
|
+
<button id="increment">+</button>
|
|
172
|
+
<span>0</span>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<!-- After (default prefixLength=3) -->
|
|
176
|
+
<div class="counter" data-qa-id="que-d">
|
|
177
|
+
<button id="increment" data-qa-id="que-d-btn">+</button>
|
|
178
|
+
<span data-qa-id="que-d-sp">0</span>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<!-- After (prefixLength=4) -->
|
|
182
|
+
<div class="counter" data-qa-id="quest-d">
|
|
183
|
+
<button id="increment" data-qa-id="quest-d-btn">+</button>
|
|
184
|
+
<span data-qa-id="quest-d-sp">0</span>
|
|
185
|
+
</div>
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Integration with Build Process
|
|
189
|
+
|
|
190
|
+
### SvelteKit
|
|
191
|
+
|
|
192
|
+
Add to your `package.json`:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"scripts": {
|
|
197
|
+
"prebuild": "svelte-qa-ids src/"
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Vite
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
// vite.config.ts
|
|
206
|
+
import { inject } from 'svelte-qa-ids';
|
|
207
|
+
|
|
208
|
+
export default {
|
|
209
|
+
plugins: [
|
|
210
|
+
{
|
|
211
|
+
name: 'inject-qa-ids',
|
|
212
|
+
async buildStart() {
|
|
213
|
+
await inject('src/');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
};
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Configuration Examples
|
|
221
|
+
|
|
222
|
+
### Custom Prefix Length
|
|
223
|
+
|
|
224
|
+
Control how many characters from the component name are used for the ID prefix:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import { inject } from 'svelte-qa-ids';
|
|
228
|
+
|
|
229
|
+
// Use 2-character prefixes (more concise)
|
|
230
|
+
await inject('src/', { prefixLength: 2 });
|
|
231
|
+
// question-counter.svelte → qu-btn
|
|
232
|
+
|
|
233
|
+
// Use 4-character prefixes (more descriptive)
|
|
234
|
+
await inject('src/', { prefixLength: 4 });
|
|
235
|
+
// question-counter.svelte → quest-btn
|
|
236
|
+
|
|
237
|
+
// Default is 3 characters
|
|
238
|
+
await inject('src/');
|
|
239
|
+
// question-counter.svelte → que-btn
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### CLI Prefix Length
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# Short prefix (2 chars)
|
|
246
|
+
svelte-qa-ids src/ --component 2
|
|
247
|
+
|
|
248
|
+
# Long prefix (5 chars)
|
|
249
|
+
svelte-qa-ids src/ --component 5
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Custom Abbreviations
|
|
253
|
+
|
|
254
|
+
Override default tag abbreviations:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
await inject('src/', {
|
|
258
|
+
abbreviations: {
|
|
259
|
+
'custom-button': 'cb',
|
|
260
|
+
'data-table': 'dt',
|
|
261
|
+
'user-card': 'uc',
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Skip Specific Components
|
|
267
|
+
|
|
268
|
+
Exclude certain components from processing:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
await inject('src/', {
|
|
272
|
+
skipComponents: ['Icon', 'MyIcon', 'ThirdPartyIcon'],
|
|
273
|
+
});
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## API Reference
|
|
277
|
+
|
|
278
|
+
### `inject(path, options?)`
|
|
279
|
+
|
|
280
|
+
Injects `data-qa-id` attributes into Svelte files. This is the primary Effect-based API.
|
|
281
|
+
|
|
282
|
+
**Parameters:**
|
|
283
|
+
|
|
284
|
+
- `path` (`string`) - Path to a file or directory
|
|
285
|
+
- `options` (`Options`, optional) - Configuration options
|
|
286
|
+
|
|
287
|
+
**Returns:** `Effect<Result[], Error>`
|
|
288
|
+
|
|
289
|
+
Use `Effect.runPromise()` to execute:
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
import { inject } from 'svelte-qa-ids';
|
|
293
|
+
import { Effect } from 'effect';
|
|
294
|
+
|
|
295
|
+
const results = await Effect.runPromise(inject('src/'));
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### `injectPromise(path, options?)`
|
|
299
|
+
|
|
300
|
+
Promise-based convenience wrapper for `inject()`.
|
|
301
|
+
|
|
302
|
+
**Parameters:**
|
|
303
|
+
|
|
304
|
+
- `path` (`string`) - Path to a file or directory
|
|
305
|
+
- `options` (`Options`, optional) - Configuration options
|
|
306
|
+
|
|
307
|
+
**Returns:** `Promise<Result[]>`
|
|
308
|
+
|
|
309
|
+
**Options:**
|
|
310
|
+
|
|
311
|
+
| Option | Type | Default | Description |
|
|
312
|
+
|--------|------|---------|-------------|
|
|
313
|
+
| `abbreviations` | `Record<string, string>` | built-in | Custom tag abbreviations |
|
|
314
|
+
| `prefixLength` | `number` | `3` | Characters from component name for prefix (2-10) |
|
|
315
|
+
| `prefixGenerator` | `(filePath: string, prefixLength?: number) => string` | built-in | Custom prefix function |
|
|
316
|
+
| `skipComponents` | `Set<string> \| string[]` | `lucide-svelte` | Components to skip |
|
|
317
|
+
| `include` | `string[]` | `undefined` | Include files matching glob patterns |
|
|
318
|
+
| `exclude` | `string[]` | `[]` | Exclude files matching glob patterns |
|
|
319
|
+
| `dryRun` | `boolean` | `false` | Don't modify files |
|
|
320
|
+
| `verbose` | `boolean` | `false` | Detailed logging |
|
|
321
|
+
| `preserveOnError` | `boolean` | `true` | Keep original content on parse errors |
|
|
322
|
+
|
|
323
|
+
**Result:**
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
interface Result {
|
|
327
|
+
file: string;
|
|
328
|
+
success: boolean;
|
|
329
|
+
error?: string;
|
|
330
|
+
injectedCount?: number;
|
|
331
|
+
skipped?: boolean;
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Known Limitations
|
|
336
|
+
|
|
337
|
+
### Svelte 5 `{#let}` Blocks
|
|
338
|
+
|
|
339
|
+
Files containing `{#let}` blocks nested within `{#each}` loops may not be parseable by the standalone Svelte parser. These files are automatically skipped with a warning, preserving their original content.
|
|
340
|
+
|
|
341
|
+
This is a limitation of the Svelte compiler in standalone mode and affects files like:
|
|
342
|
+
|
|
343
|
+
```svelte
|
|
344
|
+
{#each items as item}
|
|
345
|
+
{#let value = compute(item)}
|
|
346
|
+
<!-- content -->
|
|
347
|
+
{/let}
|
|
348
|
+
{/each}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## License
|
|
352
|
+
|
|
353
|
+
MIT
|
|
354
|
+
|
|
355
|
+
## Contributing
|
|
356
|
+
|
|
357
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { readdirSync, readFileSync, statSync, writeFileSync } from "fs";
|
|
5
|
+
import { basename, dirname, join, relative } from "path";
|
|
6
|
+
import { Data, Effect } from "effect";
|
|
7
|
+
import { parse, preprocess } from "svelte/compiler";
|
|
8
|
+
var FileReadError = class extends Data.TaggedError("FileReadError") {
|
|
9
|
+
};
|
|
10
|
+
var FileWriteError = class extends Data.TaggedError("FileWriteError") {
|
|
11
|
+
};
|
|
12
|
+
var SvelteParseError = class extends Data.TaggedError("SvelteParseError") {
|
|
13
|
+
};
|
|
14
|
+
var FileSystemError = class extends Data.TaggedError("FileSystemError") {
|
|
15
|
+
};
|
|
16
|
+
var ValidationError = class extends Data.TaggedError("ValidationError") {
|
|
17
|
+
};
|
|
18
|
+
var DEFAULT_ABBREVIATIONS = {
|
|
19
|
+
div: "d",
|
|
20
|
+
span: "sp",
|
|
21
|
+
section: "sec",
|
|
22
|
+
button: "btn",
|
|
23
|
+
a: "a",
|
|
24
|
+
p: "p",
|
|
25
|
+
img: "img",
|
|
26
|
+
ul: "ul",
|
|
27
|
+
li: "li",
|
|
28
|
+
form: "f",
|
|
29
|
+
input: "in",
|
|
30
|
+
label: "lbl",
|
|
31
|
+
h1: "h1",
|
|
32
|
+
h2: "h2",
|
|
33
|
+
h3: "h3",
|
|
34
|
+
h4: "h4",
|
|
35
|
+
h5: "h5",
|
|
36
|
+
h6: "h6"
|
|
37
|
+
};
|
|
38
|
+
var ROUTE_PREFIX_REGEX = /^src\/routes\/?/;
|
|
39
|
+
var validatePrefixLength = (prefixLength) => prefixLength !== void 0 && (prefixLength < 2 || prefixLength > 10) ? Effect.fail(
|
|
40
|
+
new ValidationError({
|
|
41
|
+
field: "prefixLength",
|
|
42
|
+
value: prefixLength,
|
|
43
|
+
message: "prefixLength must be between 2 and 10 characters"
|
|
44
|
+
})
|
|
45
|
+
) : Effect.void;
|
|
46
|
+
function getTagAbbreviation(tagName, abbreviations = DEFAULT_ABBREVIATIONS) {
|
|
47
|
+
return abbreviations[tagName] || tagName;
|
|
48
|
+
}
|
|
49
|
+
var ROOT_ROUTE_PREFIX = "rt";
|
|
50
|
+
function defaultPrefixGenerator(filePath, prefixLength = 3) {
|
|
51
|
+
const projectRoot = process.cwd();
|
|
52
|
+
const relativePath = relative(projectRoot, filePath);
|
|
53
|
+
if (relativePath.startsWith("src/routes")) {
|
|
54
|
+
const routeDir = dirname(relativePath).replace(ROUTE_PREFIX_REGEX, "");
|
|
55
|
+
if (routeDir === "") {
|
|
56
|
+
return ROOT_ROUTE_PREFIX;
|
|
57
|
+
}
|
|
58
|
+
const lastPart = routeDir.split("/").pop() || "";
|
|
59
|
+
return lastPart.substring(0, Math.min(prefixLength, lastPart.length));
|
|
60
|
+
}
|
|
61
|
+
if (relativePath.startsWith("src/lib/")) {
|
|
62
|
+
const fileName = basename(filePath, ".svelte");
|
|
63
|
+
return fileName.split("-").map(
|
|
64
|
+
(part) => part.substring(0, Math.min(prefixLength, part.length))
|
|
65
|
+
).join("-");
|
|
66
|
+
}
|
|
67
|
+
const baseName = basename(filePath, ".svelte");
|
|
68
|
+
return baseName.substring(0, Math.min(prefixLength, baseName.length));
|
|
69
|
+
}
|
|
70
|
+
function enhanceAst(node, parent = null) {
|
|
71
|
+
if (typeof node !== "object" || node === null) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
node.parent = parent;
|
|
75
|
+
const childrenProps = [
|
|
76
|
+
"children",
|
|
77
|
+
"consequent",
|
|
78
|
+
"alternate",
|
|
79
|
+
"blocks",
|
|
80
|
+
"fragment"
|
|
81
|
+
];
|
|
82
|
+
const allElementChildren = [];
|
|
83
|
+
const allChildrenNodes = [];
|
|
84
|
+
for (const prop of childrenProps) {
|
|
85
|
+
if (node[prop]) {
|
|
86
|
+
const childrenArray = Array.isArray(node[prop]) ? node[prop] : [node[prop]];
|
|
87
|
+
for (const child of childrenArray) {
|
|
88
|
+
if (child && typeof child === "object" && "type" in child) {
|
|
89
|
+
allChildrenNodes.push(child);
|
|
90
|
+
if (child.type === "Element") {
|
|
91
|
+
allElementChildren.push(child);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const childElementsGroupedByTagName = {};
|
|
98
|
+
for (const child of allElementChildren) {
|
|
99
|
+
if (!childElementsGroupedByTagName[child.name]) {
|
|
100
|
+
childElementsGroupedByTagName[child.name] = [];
|
|
101
|
+
}
|
|
102
|
+
childElementsGroupedByTagName[child.name].push(child);
|
|
103
|
+
}
|
|
104
|
+
for (const child of allChildrenNodes) {
|
|
105
|
+
if (child.type === "Element") {
|
|
106
|
+
const siblingsOfType = childElementsGroupedByTagName[child.name];
|
|
107
|
+
child.siblingIndex = siblingsOfType.indexOf(child);
|
|
108
|
+
child.siblingCount = siblingsOfType.length;
|
|
109
|
+
}
|
|
110
|
+
enhanceAst(child, node);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function buildPathSegments(node, abbreviations = DEFAULT_ABBREVIATIONS) {
|
|
114
|
+
const pathSegments = [];
|
|
115
|
+
let tempNode = node;
|
|
116
|
+
while (tempNode && tempNode.parent) {
|
|
117
|
+
if (tempNode.type === "Element" && tempNode.siblingIndex !== void 0) {
|
|
118
|
+
const tagName = getTagAbbreviation(tempNode.name, abbreviations);
|
|
119
|
+
let segment = tagName;
|
|
120
|
+
if (tempNode.siblingCount > 1) {
|
|
121
|
+
segment += `-${tempNode.siblingIndex}`;
|
|
122
|
+
}
|
|
123
|
+
pathSegments.unshift(segment);
|
|
124
|
+
}
|
|
125
|
+
tempNode = tempNode.parent;
|
|
126
|
+
}
|
|
127
|
+
return pathSegments;
|
|
128
|
+
}
|
|
129
|
+
function getLucideComponentNames(ast) {
|
|
130
|
+
const lucideComponents = /* @__PURE__ */ new Set();
|
|
131
|
+
if (!(ast && typeof ast === "object" && "instance" in ast && ast.instance && typeof ast.instance === "object" && "content" in ast.instance && ast.instance.content && typeof ast.instance.content === "object" && "body" in ast.instance.content && Array.isArray(ast.instance.content.body))) {
|
|
132
|
+
return lucideComponents;
|
|
133
|
+
}
|
|
134
|
+
const instanceBody = ast.instance.content.body;
|
|
135
|
+
for (const node of instanceBody) {
|
|
136
|
+
if (typeof node === "object" && node !== null && "type" in node && node.type === "ImportDeclaration" && "source" in node && typeof node.source === "object" && node.source !== null && "value" in node.source && node.source.value === "lucide-svelte" && "specifiers" in node && Array.isArray(node.specifiers)) {
|
|
137
|
+
for (const specifier of node.specifiers) {
|
|
138
|
+
if (typeof specifier === "object" && specifier !== null && "type" in specifier && specifier.type === "ImportSpecifier" && "imported" in specifier && specifier.imported && typeof specifier.imported === "object" && "name" in specifier.imported && typeof specifier.imported.name === "string") {
|
|
139
|
+
lucideComponents.add(specifier.imported.name);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return lucideComponents;
|
|
145
|
+
}
|
|
146
|
+
function collectModifications(node, componentPrefix, content, lucideComponentNames, abbreviations = DEFAULT_ABBREVIATIONS) {
|
|
147
|
+
const modifications = [];
|
|
148
|
+
const usedInsertionPoints = /* @__PURE__ */ new Set();
|
|
149
|
+
function traverse(currentNode) {
|
|
150
|
+
if (typeof currentNode !== "object" || currentNode === null) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if ((currentNode.type === "Element" || currentNode.type === "InlineComponent") && lucideComponentNames.has(currentNode.name)) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
if (currentNode.type === "Element" && (currentNode.name === "svg" || currentNode.name === "path")) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
if (currentNode.type === "Element") {
|
|
160
|
+
const pathSegments = buildPathSegments(currentNode, abbreviations);
|
|
161
|
+
const qaId = `${componentPrefix}${pathSegments.length > 0 ? "-" : ""}${pathSegments.join("-")}`;
|
|
162
|
+
let pos = currentNode.start;
|
|
163
|
+
let inQuotes = false;
|
|
164
|
+
let quoteChar = "";
|
|
165
|
+
let inBraces = 0;
|
|
166
|
+
while (pos < currentNode.end) {
|
|
167
|
+
const char = content[pos];
|
|
168
|
+
const prevChar = pos > 0 ? content[pos - 1] : "";
|
|
169
|
+
if (!inQuotes && (char === '"' || char === "'")) {
|
|
170
|
+
if (prevChar !== "\\") {
|
|
171
|
+
inQuotes = true;
|
|
172
|
+
quoteChar = char;
|
|
173
|
+
}
|
|
174
|
+
} else if (inQuotes && char === quoteChar && prevChar !== "\\") {
|
|
175
|
+
inQuotes = false;
|
|
176
|
+
quoteChar = "";
|
|
177
|
+
} else if (!inQuotes) {
|
|
178
|
+
if (char === "{") {
|
|
179
|
+
inBraces++;
|
|
180
|
+
} else if (char === "}") {
|
|
181
|
+
inBraces--;
|
|
182
|
+
} else if (char === ">" && inBraces === 0) {
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
pos++;
|
|
187
|
+
}
|
|
188
|
+
let insertionPoint = pos;
|
|
189
|
+
if (pos > currentNode.start + 1 && content[pos - 1] === "/") {
|
|
190
|
+
insertionPoint = pos - 1;
|
|
191
|
+
}
|
|
192
|
+
if (insertionPoint !== -1 && !usedInsertionPoints.has(insertionPoint)) {
|
|
193
|
+
usedInsertionPoints.add(insertionPoint);
|
|
194
|
+
modifications.push({ qaId, insertionPoint });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const childrenProps = [
|
|
198
|
+
"children",
|
|
199
|
+
"consequent",
|
|
200
|
+
"alternate",
|
|
201
|
+
"blocks",
|
|
202
|
+
"fragment"
|
|
203
|
+
];
|
|
204
|
+
for (const prop of childrenProps) {
|
|
205
|
+
if (currentNode[prop]) {
|
|
206
|
+
const childrenArray = Array.isArray(currentNode[prop]) ? currentNode[prop] : [currentNode[prop]];
|
|
207
|
+
for (const child of childrenArray) {
|
|
208
|
+
traverse(child);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
traverse(node);
|
|
214
|
+
return modifications;
|
|
215
|
+
}
|
|
216
|
+
var readFile = (filePath) => Effect.try({
|
|
217
|
+
try: () => readFileSync(filePath, "utf-8"),
|
|
218
|
+
catch: (cause) => new FileReadError({ filePath, cause })
|
|
219
|
+
});
|
|
220
|
+
var writeFile = (filePath, content) => Effect.try({
|
|
221
|
+
try: () => writeFileSync(filePath, content, "utf-8"),
|
|
222
|
+
catch: (cause) => new FileWriteError({ filePath, cause })
|
|
223
|
+
});
|
|
224
|
+
var getStats = (path) => Effect.try({
|
|
225
|
+
try: () => statSync(path),
|
|
226
|
+
catch: (cause) => new FileSystemError({ path, cause })
|
|
227
|
+
});
|
|
228
|
+
var readDirectory = (path) => Effect.try({
|
|
229
|
+
try: () => readdirSync(path),
|
|
230
|
+
catch: (cause) => new FileSystemError({ path, cause })
|
|
231
|
+
});
|
|
232
|
+
var processFile = (filePath, options = {}) => Effect.gen(function* () {
|
|
233
|
+
const {
|
|
234
|
+
abbreviations = DEFAULT_ABBREVIATIONS,
|
|
235
|
+
prefixLength,
|
|
236
|
+
prefixGenerator = defaultPrefixGenerator,
|
|
237
|
+
skipComponents,
|
|
238
|
+
preserveOnError = true,
|
|
239
|
+
dryRun = false,
|
|
240
|
+
verbose = false
|
|
241
|
+
} = options;
|
|
242
|
+
yield* validatePrefixLength(prefixLength);
|
|
243
|
+
if (verbose) {
|
|
244
|
+
yield* Effect.log(`Processing file: ${filePath}`);
|
|
245
|
+
}
|
|
246
|
+
const originalContent = yield* readFile(filePath);
|
|
247
|
+
const componentPrefix = prefixGenerator === defaultPrefixGenerator ? prefixGenerator(filePath, prefixLength) : prefixGenerator(filePath);
|
|
248
|
+
const processed = yield* Effect.tryPromise({
|
|
249
|
+
try: () => preprocess(
|
|
250
|
+
originalContent,
|
|
251
|
+
{
|
|
252
|
+
markup: ({ content, filename }) => {
|
|
253
|
+
const originalContentForFallback = content;
|
|
254
|
+
const cleanContent = content.replace(
|
|
255
|
+
/\s*data-qa-id="[^"]*"/g,
|
|
256
|
+
""
|
|
257
|
+
);
|
|
258
|
+
let modifiedContent = cleanContent;
|
|
259
|
+
try {
|
|
260
|
+
const ast = parse(cleanContent, { filename });
|
|
261
|
+
const lucideComponentNames = getLucideComponentNames(ast);
|
|
262
|
+
if (skipComponents) {
|
|
263
|
+
const skipSet = skipComponents instanceof Set ? skipComponents : new Set(skipComponents);
|
|
264
|
+
for (const comp of skipSet) {
|
|
265
|
+
lucideComponentNames.add(comp);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
enhanceAst(ast.html);
|
|
269
|
+
const modifications = collectModifications(
|
|
270
|
+
ast.html,
|
|
271
|
+
componentPrefix,
|
|
272
|
+
cleanContent,
|
|
273
|
+
lucideComponentNames,
|
|
274
|
+
abbreviations
|
|
275
|
+
);
|
|
276
|
+
modifications.sort(
|
|
277
|
+
(a, b) => b.insertionPoint - a.insertionPoint
|
|
278
|
+
);
|
|
279
|
+
modifiedContent = cleanContent;
|
|
280
|
+
for (const mod of modifications) {
|
|
281
|
+
modifiedContent = modifiedContent.substring(0, mod.insertionPoint) + ` data-qa-id="${mod.qaId}"` + modifiedContent.substring(mod.insertionPoint);
|
|
282
|
+
}
|
|
283
|
+
} catch (parseError) {
|
|
284
|
+
const isLetBlockError = parseError.code === "expected_block_type" || parseError.message?.includes(
|
|
285
|
+
"{#let"
|
|
286
|
+
) || parseError.message?.includes(
|
|
287
|
+
"Expected 'if', 'each', 'await', 'key' or 'snippet'"
|
|
288
|
+
);
|
|
289
|
+
if (isLetBlockError) {
|
|
290
|
+
return {
|
|
291
|
+
code: preserveOnError ? originalContentForFallback : cleanContent
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
throw parseError;
|
|
295
|
+
}
|
|
296
|
+
return { code: modifiedContent };
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
{ filename: filePath }
|
|
300
|
+
),
|
|
301
|
+
catch: (cause) => new SvelteParseError({ filePath, cause })
|
|
302
|
+
});
|
|
303
|
+
if (!dryRun) {
|
|
304
|
+
yield* writeFile(filePath, processed.code);
|
|
305
|
+
}
|
|
306
|
+
const injectedCount = (processed.code.match(/data-qa-id="[^"]*"/g) || []).length;
|
|
307
|
+
if (verbose && injectedCount > 0) {
|
|
308
|
+
yield* Effect.log(` Injected ${injectedCount} data-qa-id attributes`);
|
|
309
|
+
}
|
|
310
|
+
yield* Effect.log(
|
|
311
|
+
`Successfully updated data-qa-id attributes in ${filePath}.`
|
|
312
|
+
);
|
|
313
|
+
return {
|
|
314
|
+
file: filePath,
|
|
315
|
+
success: true,
|
|
316
|
+
injectedCount
|
|
317
|
+
};
|
|
318
|
+
}).pipe(
|
|
319
|
+
Effect.catchTag(
|
|
320
|
+
"FileReadError",
|
|
321
|
+
(error) => Effect.succeed({
|
|
322
|
+
file: filePath,
|
|
323
|
+
success: false,
|
|
324
|
+
error: `Error reading file: ${error.filePath}`
|
|
325
|
+
})
|
|
326
|
+
),
|
|
327
|
+
Effect.catchTag(
|
|
328
|
+
"FileWriteError",
|
|
329
|
+
(error) => Effect.succeed({
|
|
330
|
+
file: filePath,
|
|
331
|
+
success: false,
|
|
332
|
+
error: `Error writing file: ${error.filePath}`
|
|
333
|
+
})
|
|
334
|
+
),
|
|
335
|
+
Effect.catchTag(
|
|
336
|
+
"SvelteParseError",
|
|
337
|
+
(error) => Effect.succeed({
|
|
338
|
+
file: filePath,
|
|
339
|
+
success: false,
|
|
340
|
+
error: `Parse error in ${error.filePath}: ${error.cause instanceof Error ? error.cause.message : String(error.cause)}`
|
|
341
|
+
})
|
|
342
|
+
),
|
|
343
|
+
Effect.catchTag(
|
|
344
|
+
"ValidationError",
|
|
345
|
+
(error) => Effect.succeed({
|
|
346
|
+
file: filePath,
|
|
347
|
+
success: false,
|
|
348
|
+
error: error.message
|
|
349
|
+
})
|
|
350
|
+
)
|
|
351
|
+
);
|
|
352
|
+
var processPath = (path, options = {}) => Effect.gen(function* () {
|
|
353
|
+
const results = [];
|
|
354
|
+
const traverse = (currentPath) => Effect.gen(function* () {
|
|
355
|
+
const stats = yield* getStats(currentPath);
|
|
356
|
+
if (stats.isFile()) {
|
|
357
|
+
if (currentPath.endsWith(".svelte")) {
|
|
358
|
+
const result = yield* processFile(currentPath, options);
|
|
359
|
+
results.push(result);
|
|
360
|
+
}
|
|
361
|
+
} else if (stats.isDirectory()) {
|
|
362
|
+
const files = yield* readDirectory(currentPath);
|
|
363
|
+
for (const file of files) {
|
|
364
|
+
yield* traverse(join(currentPath, file));
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
yield* traverse(path);
|
|
369
|
+
return results;
|
|
370
|
+
});
|
|
371
|
+
var inject = (path, options = {}) => Effect.gen(function* () {
|
|
372
|
+
const stats = yield* getStats(path);
|
|
373
|
+
if (stats.isFile()) {
|
|
374
|
+
const result = yield* processFile(path, options);
|
|
375
|
+
return [result];
|
|
376
|
+
}
|
|
377
|
+
if (stats.isDirectory()) {
|
|
378
|
+
return yield* processPath(path, options);
|
|
379
|
+
}
|
|
380
|
+
throw new FileSystemError({
|
|
381
|
+
path,
|
|
382
|
+
cause: "Path does not exist"
|
|
383
|
+
});
|
|
384
|
+
}).pipe(
|
|
385
|
+
Effect.catchTag("FileSystemError", (error) => Effect.die(error.cause))
|
|
386
|
+
);
|
|
387
|
+
async function injectPromise(path, options = {}) {
|
|
388
|
+
return Effect.runPromise(inject(path, options));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export {
|
|
392
|
+
FileReadError,
|
|
393
|
+
FileWriteError,
|
|
394
|
+
SvelteParseError,
|
|
395
|
+
FileSystemError,
|
|
396
|
+
ValidationError,
|
|
397
|
+
getTagAbbreviation,
|
|
398
|
+
defaultPrefixGenerator,
|
|
399
|
+
processFile,
|
|
400
|
+
processPath,
|
|
401
|
+
inject,
|
|
402
|
+
injectPromise
|
|
403
|
+
};
|
|
404
|
+
//# sourceMappingURL=chunk-7MRS72CP.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * svelte-qa-ids\n * Automatically inject stable data-qa-id attributes into Svelte components\n */\n\nimport { readdirSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { basename, dirname, join, relative } from \"node:path\";\nimport { Data, Effect } from \"effect\";\nimport { parse, preprocess } from \"svelte/compiler\";\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\nexport class FileReadError extends Data.TaggedError(\"FileReadError\")<{\n readonly filePath: string;\n readonly cause: unknown;\n}> {}\n\nexport class FileWriteError extends Data.TaggedError(\"FileWriteError\")<{\n readonly filePath: string;\n readonly cause: unknown;\n}> {}\n\nexport class SvelteParseError extends Data.TaggedError(\"SvelteParseError\")<{\n readonly filePath: string;\n readonly cause: unknown;\n}> {}\n\nexport class FileSystemError extends Data.TaggedError(\"FileSystemError\")<{\n readonly path: string;\n readonly cause: unknown;\n}> {}\n\nexport class ValidationError extends Data.TaggedError(\"ValidationError\")<{\n readonly field: string;\n readonly value: unknown;\n readonly message: string;\n}> {}\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface Options {\n /**\n * Custom abbreviations for HTML tags\n * @default { common abbreviations }\n */\n abbreviations?: Record<string, string>;\n\n /**\n * Number of characters from component name to use for ID prefix\n * @default 3\n * @throws {ValidationError} When value is outside 2-10 range\n * @validation Must be between 2 and 10 characters\n */\n prefixLength?: number;\n\n /**\n * Custom prefix generator function\n * @default built-in prefix generator\n */\n prefixGenerator?: (filePath: string) => string;\n\n /**\n * Component names to skip (e.g., icon libraries)\n * @default lucide-svelte components\n */\n skipComponents?: Set<string> | string[];\n\n /**\n * Include files matching these patterns\n */\n include?: string[];\n\n /**\n * Exclude files matching these patterns\n * @default {string[]} []\n */\n exclude?: string[];\n\n /**\n * Whether to preserve original content for files with parsing errors\n * @default true\n */\n preserveOnError?: boolean;\n\n /**\n * Dry run - don't modify files\n * @default false\n */\n dryRun?: boolean;\n\n /**\n * Verbose logging\n * @default false\n */\n verbose?: boolean;\n}\n\nexport interface Result {\n file: string;\n success: boolean;\n error?: string;\n injectedCount?: number;\n skipped?: boolean;\n}\n\n// ============================================================================\n// AST Types\n// ============================================================================\n\ninterface SvelteAstNode {\n type: string;\n name?: string;\n start?: number;\n end?: number;\n parent?: SvelteAstNode | null;\n siblingIndex?: number;\n siblingCount?: number;\n children?: SvelteAstNode[];\n consequent?: SvelteAstNode | SvelteAstNode[];\n alternate?: SvelteAstNode | SvelteAstNode[];\n blocks?: SvelteAstNode[];\n fragment?: SvelteAstNode[];\n [key: string]: unknown;\n}\n\ninterface ElementToModify {\n qaId: string;\n insertionPoint: number;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_ABBREVIATIONS: Record<string, string> = {\n div: \"d\",\n span: \"sp\",\n section: \"sec\",\n button: \"btn\",\n a: \"a\",\n p: \"p\",\n img: \"img\",\n ul: \"ul\",\n li: \"li\",\n form: \"f\",\n input: \"in\",\n label: \"lbl\",\n h1: \"h1\",\n h2: \"h2\",\n h3: \"h3\",\n h4: \"h4\",\n h5: \"h5\",\n h6: \"h6\",\n};\n\nconst ROUTE_PREFIX_REGEX = /^src\\/routes\\/?/;\n\n// ============================================================================\n// Core Functions\n// ============================================================================\n\n/**\n * Validates prefixLength is within acceptable range (2-10).\n */\nconst validatePrefixLength = (\n prefixLength?: number\n): Effect.Effect<void, ValidationError> =>\n prefixLength !== undefined && (prefixLength < 2 || prefixLength > 10)\n ? Effect.fail(\n new ValidationError({\n field: \"prefixLength\",\n value: prefixLength,\n message: \"prefixLength must be between 2 and 10 characters\",\n })\n )\n : Effect.void;\n\n/**\n * Returns the abbreviation for a given HTML tag.\n */\nexport { getTagAbbreviation };\n\nfunction getTagAbbreviation(\n tagName: string,\n abbreviations: Record<string, string> = DEFAULT_ABBREVIATIONS\n): string {\n return abbreviations[tagName] || tagName;\n}\n\n/**\n * Default component prefix generator based on file path.\n */\nconst ROOT_ROUTE_PREFIX = \"rt\" as const;\n\nexport { defaultPrefixGenerator };\n\nfunction defaultPrefixGenerator(filePath: string, prefixLength = 3): string {\n const projectRoot = process.cwd();\n const relativePath = relative(projectRoot, filePath);\n\n if (relativePath.startsWith(\"src/routes\")) {\n const routeDir = dirname(relativePath).replace(ROUTE_PREFIX_REGEX, \"\");\n if (routeDir === \"\") {\n return ROOT_ROUTE_PREFIX;\n }\n const lastPart = routeDir.split(\"/\").pop() || \"\";\n return lastPart.substring(0, Math.min(prefixLength, lastPart.length));\n }\n if (relativePath.startsWith(\"src/lib/\")) {\n const fileName = basename(filePath, \".svelte\");\n return fileName\n .split(\"-\")\n .map((part: string) =>\n part.substring(0, Math.min(prefixLength, part.length))\n )\n .join(\"-\");\n }\n\n const baseName = basename(filePath, \".svelte\");\n return baseName.substring(0, Math.min(prefixLength, baseName.length));\n}\n\n/**\n * Walks the Svelte AST and enhances nodes with parent pointers and sibling info.\n */\nfunction enhanceAst(\n node: SvelteAstNode,\n parent: SvelteAstNode | null = null\n): void {\n if (typeof node !== \"object\" || node === null) {\n return;\n }\n node.parent = parent;\n\n const childrenProps = [\n \"children\",\n \"consequent\",\n \"alternate\",\n \"blocks\",\n \"fragment\",\n ];\n const allElementChildren: SvelteAstNode[] = [];\n const allChildrenNodes: SvelteAstNode[] = [];\n\n for (const prop of childrenProps) {\n if (node[prop]) {\n const childrenArray = Array.isArray(node[prop])\n ? node[prop]\n : [node[prop]];\n for (const child of childrenArray) {\n if (child && typeof child === \"object\" && \"type\" in child) {\n allChildrenNodes.push(child);\n if (child.type === \"Element\") {\n allElementChildren.push(child);\n }\n }\n }\n }\n }\n\n const childElementsGroupedByTagName: { [tagName: string]: SvelteAstNode[] } =\n {};\n for (const child of allElementChildren) {\n if (!childElementsGroupedByTagName[child.name!]) {\n childElementsGroupedByTagName[child.name!] = [];\n }\n childElementsGroupedByTagName[child.name!].push(child);\n }\n\n for (const child of allChildrenNodes) {\n if (child.type === \"Element\") {\n const siblingsOfType = childElementsGroupedByTagName[child.name!];\n child.siblingIndex = siblingsOfType.indexOf(child);\n child.siblingCount = siblingsOfType.length;\n }\n enhanceAst(child, node);\n }\n}\n\n/**\n * Builds the path segments for a data-qa-id by traversing up the AST.\n */\nfunction buildPathSegments(\n node: SvelteAstNode,\n abbreviations: Record<string, string> = DEFAULT_ABBREVIATIONS\n): string[] {\n const pathSegments: string[] = [];\n let tempNode: SvelteAstNode | null = node;\n while (tempNode && tempNode.parent) {\n if (tempNode.type === \"Element\" && tempNode.siblingIndex !== undefined) {\n const tagName = getTagAbbreviation(tempNode.name!, abbreviations);\n let segment = tagName;\n if (tempNode.siblingCount! > 1) {\n segment += `-${tempNode.siblingIndex}`;\n }\n pathSegments.unshift(segment);\n }\n tempNode = tempNode.parent;\n }\n return pathSegments;\n}\n\n/**\n * Extracts names of components imported from 'lucide-svelte'.\n */\nfunction getLucideComponentNames(ast: unknown): Set<string> {\n const lucideComponents = new Set<string>();\n if (\n !(\n ast &&\n typeof ast === \"object\" &&\n \"instance\" in ast &&\n ast.instance &&\n typeof ast.instance === \"object\" &&\n \"content\" in ast.instance &&\n ast.instance.content &&\n typeof ast.instance.content === \"object\" &&\n \"body\" in ast.instance.content &&\n Array.isArray(ast.instance.content.body)\n )\n ) {\n return lucideComponents;\n }\n\n const instanceBody = (ast.instance as { content: { body: unknown[] } })\n .content.body;\n\n for (const node of instanceBody) {\n if (\n typeof node === \"object\" &&\n node !== null &&\n \"type\" in node &&\n node.type === \"ImportDeclaration\" &&\n \"source\" in node &&\n typeof node.source === \"object\" &&\n node.source !== null &&\n \"value\" in node.source &&\n node.source.value === \"lucide-svelte\" &&\n \"specifiers\" in node &&\n Array.isArray(node.specifiers)\n ) {\n for (const specifier of node.specifiers) {\n if (\n typeof specifier === \"object\" &&\n specifier !== null &&\n \"type\" in specifier &&\n specifier.type === \"ImportSpecifier\" &&\n \"imported\" in specifier &&\n specifier.imported &&\n typeof specifier.imported === \"object\" &&\n \"name\" in specifier.imported &&\n typeof specifier.imported.name === \"string\"\n ) {\n lucideComponents.add(specifier.imported.name);\n }\n }\n }\n }\n return lucideComponents;\n}\n\n/**\n * Collects modifications to be made to the Svelte file.\n */\nfunction collectModifications(\n node: SvelteAstNode,\n componentPrefix: string,\n content: string,\n lucideComponentNames: Set<string>,\n abbreviations: Record<string, string> = DEFAULT_ABBREVIATIONS\n): ElementToModify[] {\n const modifications: ElementToModify[] = [];\n const usedInsertionPoints = new Set<number>();\n\n function traverse(currentNode: SvelteAstNode): void {\n if (typeof currentNode !== \"object\" || currentNode === null) {\n return;\n }\n\n // Skip Lucide Svelte components (both Element and InlineComponent types)\n if (\n (currentNode.type === \"Element\" ||\n currentNode.type === \"InlineComponent\") &&\n lucideComponentNames.has(currentNode.name!)\n ) {\n return;\n }\n\n // Skip SVG and path elements (icon internals) - only for Element type\n if (\n currentNode.type === \"Element\" &&\n (currentNode.name === \"svg\" || currentNode.name === \"path\")\n ) {\n return;\n }\n\n if (currentNode.type === \"Element\") {\n const pathSegments = buildPathSegments(currentNode, abbreviations);\n const qaId = `${componentPrefix}${\n pathSegments.length > 0 ? \"-\" : \"\"\n }${pathSegments.join(\"-\")}`;\n\n // Determine insertion point for data-qa-id attribute\n // First, find the end of the opening tag (first unquoted '>')\n let pos = currentNode.start!;\n let inQuotes = false;\n let quoteChar = \"\";\n let inBraces = 0; // Track {} for event handlers like onclick={() =>}\n\n while (pos < currentNode.end!) {\n const char = content[pos];\n const prevChar = pos > 0 ? content[pos - 1] : \"\";\n\n if (!inQuotes && (char === '\"' || char === \"'\")) {\n if (prevChar !== \"\\\\\") {\n inQuotes = true;\n quoteChar = char;\n }\n } else if (inQuotes && char === quoteChar && prevChar !== \"\\\\\") {\n inQuotes = false;\n quoteChar = \"\";\n } else if (!inQuotes) {\n if (char === \"{\") {\n inBraces++;\n } else if (char === \"}\") {\n inBraces--;\n } else if (char === \">\" && inBraces === 0) {\n // Found the closing '>' of the opening tag\n break;\n }\n }\n pos++;\n }\n\n // Now check if this is a self-closing tag (looks for '/>' before the '>')\n let insertionPoint = pos; // Default: insert before the '>'\n if (pos > currentNode.start! + 1 && content[pos - 1] === \"/\") {\n // Self-closing tag: insert before the '/'\n insertionPoint = pos - 1;\n }\n\n if (insertionPoint !== -1 && !usedInsertionPoints.has(insertionPoint)) {\n usedInsertionPoints.add(insertionPoint);\n modifications.push({ qaId, insertionPoint });\n }\n }\n\n const childrenProps = [\n \"children\",\n \"consequent\",\n \"alternate\",\n \"blocks\",\n \"fragment\",\n ];\n for (const prop of childrenProps) {\n if (currentNode[prop]) {\n const childrenArray = Array.isArray(currentNode[prop])\n ? currentNode[prop]\n : [currentNode[prop]];\n for (const child of childrenArray) {\n traverse(child);\n }\n }\n }\n }\n\n traverse(node);\n return modifications;\n}\n\n// ============================================================================\n// Effect-based File Operations\n// ============================================================================\n\n/**\n * Reads a file's content as an Effect.\n */\nconst readFile = (filePath: string): Effect.Effect<string, FileReadError> =>\n Effect.try({\n try: () => readFileSync(filePath, \"utf-8\"),\n catch: (cause) => new FileReadError({ filePath, cause }),\n });\n\n/**\n * Writes content to a file as an Effect.\n */\nconst writeFile = (\n filePath: string,\n content: string\n): Effect.Effect<void, FileWriteError> =>\n Effect.try({\n try: () => writeFileSync(filePath, content, \"utf-8\"),\n catch: (cause) => new FileWriteError({ filePath, cause }),\n });\n\n/**\n * Gets file stats as an Effect.\n */\nconst getStats = (\n path: string\n): Effect.Effect<ReturnType<typeof statSync>, FileSystemError> =>\n Effect.try({\n try: () => statSync(path),\n catch: (cause) => new FileSystemError({ path, cause }),\n });\n\n/**\n * Reads directory contents as an Effect.\n */\nconst readDirectory = (\n path: string\n): Effect.Effect<string[], FileSystemError> =>\n Effect.try({\n try: () => readdirSync(path),\n catch: (cause) => new FileSystemError({ path, cause }),\n });\n\n// ============================================================================\n// Processing Functions\n// ============================================================================\n\n/**\n * Processes a single Svelte component file to inject `data-qa-id` attributes.\n */\nexport const processFile = (\n filePath: string,\n options: Options = {}\n): Effect.Effect<\n Result,\n FileReadError | FileWriteError | SvelteParseError | ValidationError\n> =>\n Effect.gen(function* () {\n const {\n abbreviations = DEFAULT_ABBREVIATIONS,\n prefixLength,\n prefixGenerator = defaultPrefixGenerator,\n skipComponents,\n preserveOnError = true,\n dryRun = false,\n verbose = false,\n } = options;\n\n // Validate prefixLength if provided\n yield* validatePrefixLength(prefixLength);\n\n if (verbose) {\n yield* Effect.log(`Processing file: ${filePath}`);\n }\n\n const originalContent = yield* readFile(filePath);\n\n // Use defaultPrefixGenerator with prefixLength if no custom generator provided\n const componentPrefix =\n prefixGenerator === defaultPrefixGenerator\n ? prefixGenerator(filePath, prefixLength)\n : prefixGenerator(filePath);\n\n const processed = yield* Effect.tryPromise({\n try: () =>\n preprocess(\n originalContent,\n {\n markup: ({ content, filename }) => {\n const originalContentForFallback = content;\n const cleanContent = content.replace(\n /\\s*data-qa-id=\"[^\"]*\"/g,\n \"\"\n );\n let modifiedContent = cleanContent;\n\n try {\n const ast = parse(cleanContent, { filename });\n const lucideComponentNames = getLucideComponentNames(ast);\n\n // Add custom skip components\n if (skipComponents) {\n const skipSet =\n skipComponents instanceof Set\n ? skipComponents\n : new Set(skipComponents);\n for (const comp of skipSet) {\n lucideComponentNames.add(comp);\n }\n }\n\n enhanceAst(ast.html);\n const modifications = collectModifications(\n ast.html,\n componentPrefix,\n cleanContent,\n lucideComponentNames,\n abbreviations\n );\n\n modifications.sort(\n (a, b) => b.insertionPoint - a.insertionPoint\n );\n\n modifiedContent = cleanContent;\n for (const mod of modifications) {\n modifiedContent =\n modifiedContent.substring(0, mod.insertionPoint) +\n ` data-qa-id=\"${mod.qaId}\"` +\n modifiedContent.substring(mod.insertionPoint);\n }\n\n // Note: Verbose logging handled after processing completes\n } catch (parseError: unknown) {\n const isLetBlockError =\n (parseError as { code?: string }).code ===\n \"expected_block_type\" ||\n (parseError as { message?: string }).message?.includes(\n \"{#let\"\n ) ||\n (parseError as { message?: string }).message?.includes(\n \"Expected 'if', 'each', 'await', 'key' or 'snippet'\"\n );\n\n if (isLetBlockError) {\n // Note: Verbose logging handled after processing completes\n return {\n code: preserveOnError\n ? originalContentForFallback\n : cleanContent,\n };\n }\n throw parseError;\n }\n return { code: modifiedContent };\n },\n },\n { filename: filePath }\n ),\n catch: (cause) => new SvelteParseError({ filePath, cause }),\n });\n\n if (!dryRun) {\n yield* writeFile(filePath, processed.code);\n }\n\n const injectedCount = (processed.code.match(/data-qa-id=\"[^\"]*\"/g) || [])\n .length;\n\n if (verbose && injectedCount > 0) {\n yield* Effect.log(` Injected ${injectedCount} data-qa-id attributes`);\n }\n\n yield* Effect.log(\n `Successfully updated data-qa-id attributes in ${filePath}.`\n );\n\n return {\n file: filePath,\n success: true,\n injectedCount,\n };\n }).pipe(\n Effect.catchTag(\"FileReadError\", (error) =>\n Effect.succeed({\n file: filePath,\n success: false,\n error: `Error reading file: ${error.filePath}`,\n })\n ),\n Effect.catchTag(\"FileWriteError\", (error) =>\n Effect.succeed({\n file: filePath,\n success: false,\n error: `Error writing file: ${error.filePath}`,\n })\n ),\n Effect.catchTag(\"SvelteParseError\", (error) =>\n Effect.succeed({\n file: filePath,\n success: false,\n error: `Parse error in ${error.filePath}: ${\n error.cause instanceof Error\n ? error.cause.message\n : String(error.cause)\n }`,\n })\n ),\n Effect.catchTag(\"ValidationError\", (error) =>\n Effect.succeed({\n file: filePath,\n success: false,\n error: error.message,\n })\n )\n );\n\n/**\n * Recursively finds and processes all Svelte files in a given path.\n */\nexport const processPath = (\n path: string,\n options: Options = {}\n): Effect.Effect<\n Result[],\n | FileSystemError\n | FileReadError\n | FileWriteError\n | SvelteParseError\n | ValidationError\n> =>\n Effect.gen(function* () {\n const results: Result[] = [];\n\n const traverse = (\n currentPath: string\n ): Effect.Effect<\n void,\n | FileSystemError\n | FileReadError\n | FileWriteError\n | SvelteParseError\n | ValidationError\n > =>\n Effect.gen(function* () {\n const stats = yield* getStats(currentPath);\n\n if (stats.isFile()) {\n if (currentPath.endsWith(\".svelte\")) {\n const result = yield* processFile(currentPath, options);\n results.push(result);\n }\n } else if (stats.isDirectory()) {\n const files = yield* readDirectory(currentPath);\n for (const file of files) {\n yield* traverse(join(currentPath, file));\n }\n }\n });\n\n yield* traverse(path);\n return results;\n });\n\n/**\n * Main API - inject data-qa-id attributes into Svelte files\n */\nexport const inject = (\n path: string,\n options: Options = {}\n): Effect.Effect<\n Result[],\n | FileSystemError\n | FileReadError\n | FileWriteError\n | SvelteParseError\n | ValidationError\n> =>\n Effect.gen(function* () {\n const stats = yield* getStats(path);\n\n if (stats.isFile()) {\n const result = yield* processFile(path, options);\n return [result];\n }\n if (stats.isDirectory()) {\n return yield* processPath(path, options);\n }\n\n throw new FileSystemError({\n path,\n cause: \"Path does not exist\",\n });\n }).pipe(\n Effect.catchTag(\"FileSystemError\", (error) => Effect.die(error.cause))\n );\n\n/**\n * Promise-based API for compatibility\n */\nexport async function injectPromise(\n path: string,\n options: Options = {}\n): Promise<Result[]> {\n return Effect.runPromise(inject(path, options));\n}\n"],"mappings":";;;AAKA,SAAS,aAAa,cAAc,UAAU,qBAAqB;AACnE,SAAS,UAAU,SAAS,MAAM,gBAAgB;AAClD,SAAS,MAAM,cAAc;AAC7B,SAAS,OAAO,kBAAkB;AAM3B,IAAM,gBAAN,cAA4B,KAAK,YAAY,eAAe,EAGhE;AAAC;AAEG,IAAM,iBAAN,cAA6B,KAAK,YAAY,gBAAgB,EAGlE;AAAC;AAEG,IAAM,mBAAN,cAA+B,KAAK,YAAY,kBAAkB,EAGtE;AAAC;AAEG,IAAM,kBAAN,cAA8B,KAAK,YAAY,iBAAiB,EAGpE;AAAC;AAEG,IAAM,kBAAN,cAA8B,KAAK,YAAY,iBAAiB,EAIpE;AAAC;AAoGJ,IAAM,wBAAgD;AAAA,EACpD,KAAK;AAAA,EACL,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,GAAG;AAAA,EACH,GAAG;AAAA,EACH,KAAK;AAAA,EACL,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AAAA,EACP,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAEA,IAAM,qBAAqB;AAS3B,IAAM,uBAAuB,CAC3B,iBAEA,iBAAiB,WAAc,eAAe,KAAK,eAAe,MAC9D,OAAO;AAAA,EACL,IAAI,gBAAgB;AAAA,IAClB,OAAO;AAAA,IACP,OAAO;AAAA,IACP,SAAS;AAAA,EACX,CAAC;AACH,IACA,OAAO;AAOb,SAAS,mBACP,SACA,gBAAwC,uBAChC;AACR,SAAO,cAAc,OAAO,KAAK;AACnC;AAKA,IAAM,oBAAoB;AAI1B,SAAS,uBAAuB,UAAkB,eAAe,GAAW;AAC1E,QAAM,cAAc,QAAQ,IAAI;AAChC,QAAM,eAAe,SAAS,aAAa,QAAQ;AAEnD,MAAI,aAAa,WAAW,YAAY,GAAG;AACzC,UAAM,WAAW,QAAQ,YAAY,EAAE,QAAQ,oBAAoB,EAAE;AACrE,QAAI,aAAa,IAAI;AACnB,aAAO;AAAA,IACT;AACA,UAAM,WAAW,SAAS,MAAM,GAAG,EAAE,IAAI,KAAK;AAC9C,WAAO,SAAS,UAAU,GAAG,KAAK,IAAI,cAAc,SAAS,MAAM,CAAC;AAAA,EACtE;AACA,MAAI,aAAa,WAAW,UAAU,GAAG;AACvC,UAAM,WAAW,SAAS,UAAU,SAAS;AAC7C,WAAO,SACJ,MAAM,GAAG,EACT;AAAA,MAAI,CAAC,SACJ,KAAK,UAAU,GAAG,KAAK,IAAI,cAAc,KAAK,MAAM,CAAC;AAAA,IACvD,EACC,KAAK,GAAG;AAAA,EACb;AAEA,QAAM,WAAW,SAAS,UAAU,SAAS;AAC7C,SAAO,SAAS,UAAU,GAAG,KAAK,IAAI,cAAc,SAAS,MAAM,CAAC;AACtE;AAKA,SAAS,WACP,MACA,SAA+B,MACzB;AACN,MAAI,OAAO,SAAS,YAAY,SAAS,MAAM;AAC7C;AAAA,EACF;AACA,OAAK,SAAS;AAEd,QAAM,gBAAgB;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACA,QAAM,qBAAsC,CAAC;AAC7C,QAAM,mBAAoC,CAAC;AAE3C,aAAW,QAAQ,eAAe;AAChC,QAAI,KAAK,IAAI,GAAG;AACd,YAAM,gBAAgB,MAAM,QAAQ,KAAK,IAAI,CAAC,IAC1C,KAAK,IAAI,IACT,CAAC,KAAK,IAAI,CAAC;AACf,iBAAW,SAAS,eAAe;AACjC,YAAI,SAAS,OAAO,UAAU,YAAY,UAAU,OAAO;AACzD,2BAAiB,KAAK,KAAK;AAC3B,cAAI,MAAM,SAAS,WAAW;AAC5B,+BAAmB,KAAK,KAAK;AAAA,UAC/B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,gCACJ,CAAC;AACH,aAAW,SAAS,oBAAoB;AACtC,QAAI,CAAC,8BAA8B,MAAM,IAAK,GAAG;AAC/C,oCAA8B,MAAM,IAAK,IAAI,CAAC;AAAA,IAChD;AACA,kCAA8B,MAAM,IAAK,EAAE,KAAK,KAAK;AAAA,EACvD;AAEA,aAAW,SAAS,kBAAkB;AACpC,QAAI,MAAM,SAAS,WAAW;AAC5B,YAAM,iBAAiB,8BAA8B,MAAM,IAAK;AAChE,YAAM,eAAe,eAAe,QAAQ,KAAK;AACjD,YAAM,eAAe,eAAe;AAAA,IACtC;AACA,eAAW,OAAO,IAAI;AAAA,EACxB;AACF;AAKA,SAAS,kBACP,MACA,gBAAwC,uBAC9B;AACV,QAAM,eAAyB,CAAC;AAChC,MAAI,WAAiC;AACrC,SAAO,YAAY,SAAS,QAAQ;AAClC,QAAI,SAAS,SAAS,aAAa,SAAS,iBAAiB,QAAW;AACtE,YAAM,UAAU,mBAAmB,SAAS,MAAO,aAAa;AAChE,UAAI,UAAU;AACd,UAAI,SAAS,eAAgB,GAAG;AAC9B,mBAAW,IAAI,SAAS,YAAY;AAAA,MACtC;AACA,mBAAa,QAAQ,OAAO;AAAA,IAC9B;AACA,eAAW,SAAS;AAAA,EACtB;AACA,SAAO;AACT;AAKA,SAAS,wBAAwB,KAA2B;AAC1D,QAAM,mBAAmB,oBAAI,IAAY;AACzC,MACE,EACE,OACA,OAAO,QAAQ,YACf,cAAc,OACd,IAAI,YACJ,OAAO,IAAI,aAAa,YACxB,aAAa,IAAI,YACjB,IAAI,SAAS,WACb,OAAO,IAAI,SAAS,YAAY,YAChC,UAAU,IAAI,SAAS,WACvB,MAAM,QAAQ,IAAI,SAAS,QAAQ,IAAI,IAEzC;AACA,WAAO;AAAA,EACT;AAEA,QAAM,eAAgB,IAAI,SACvB,QAAQ;AAEX,aAAW,QAAQ,cAAc;AAC/B,QACE,OAAO,SAAS,YAChB,SAAS,QACT,UAAU,QACV,KAAK,SAAS,uBACd,YAAY,QACZ,OAAO,KAAK,WAAW,YACvB,KAAK,WAAW,QAChB,WAAW,KAAK,UAChB,KAAK,OAAO,UAAU,mBACtB,gBAAgB,QAChB,MAAM,QAAQ,KAAK,UAAU,GAC7B;AACA,iBAAW,aAAa,KAAK,YAAY;AACvC,YACE,OAAO,cAAc,YACrB,cAAc,QACd,UAAU,aACV,UAAU,SAAS,qBACnB,cAAc,aACd,UAAU,YACV,OAAO,UAAU,aAAa,YAC9B,UAAU,UAAU,YACpB,OAAO,UAAU,SAAS,SAAS,UACnC;AACA,2BAAiB,IAAI,UAAU,SAAS,IAAI;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,qBACP,MACA,iBACA,SACA,sBACA,gBAAwC,uBACrB;AACnB,QAAM,gBAAmC,CAAC;AAC1C,QAAM,sBAAsB,oBAAI,IAAY;AAE5C,WAAS,SAAS,aAAkC;AAClD,QAAI,OAAO,gBAAgB,YAAY,gBAAgB,MAAM;AAC3D;AAAA,IACF;AAGA,SACG,YAAY,SAAS,aACpB,YAAY,SAAS,sBACvB,qBAAqB,IAAI,YAAY,IAAK,GAC1C;AACA;AAAA,IACF;AAGA,QACE,YAAY,SAAS,cACpB,YAAY,SAAS,SAAS,YAAY,SAAS,SACpD;AACA;AAAA,IACF;AAEA,QAAI,YAAY,SAAS,WAAW;AAClC,YAAM,eAAe,kBAAkB,aAAa,aAAa;AACjE,YAAM,OAAO,GAAG,eAAe,GAC7B,aAAa,SAAS,IAAI,MAAM,EAClC,GAAG,aAAa,KAAK,GAAG,CAAC;AAIzB,UAAI,MAAM,YAAY;AACtB,UAAI,WAAW;AACf,UAAI,YAAY;AAChB,UAAI,WAAW;AAEf,aAAO,MAAM,YAAY,KAAM;AAC7B,cAAM,OAAO,QAAQ,GAAG;AACxB,cAAM,WAAW,MAAM,IAAI,QAAQ,MAAM,CAAC,IAAI;AAE9C,YAAI,CAAC,aAAa,SAAS,OAAO,SAAS,MAAM;AAC/C,cAAI,aAAa,MAAM;AACrB,uBAAW;AACX,wBAAY;AAAA,UACd;AAAA,QACF,WAAW,YAAY,SAAS,aAAa,aAAa,MAAM;AAC9D,qBAAW;AACX,sBAAY;AAAA,QACd,WAAW,CAAC,UAAU;AACpB,cAAI,SAAS,KAAK;AAChB;AAAA,UACF,WAAW,SAAS,KAAK;AACvB;AAAA,UACF,WAAW,SAAS,OAAO,aAAa,GAAG;AAEzC;AAAA,UACF;AAAA,QACF;AACA;AAAA,MACF;AAGA,UAAI,iBAAiB;AACrB,UAAI,MAAM,YAAY,QAAS,KAAK,QAAQ,MAAM,CAAC,MAAM,KAAK;AAE5D,yBAAiB,MAAM;AAAA,MACzB;AAEA,UAAI,mBAAmB,MAAM,CAAC,oBAAoB,IAAI,cAAc,GAAG;AACrE,4BAAoB,IAAI,cAAc;AACtC,sBAAc,KAAK,EAAE,MAAM,eAAe,CAAC;AAAA,MAC7C;AAAA,IACF;AAEA,UAAM,gBAAgB;AAAA,MACpB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,eAAW,QAAQ,eAAe;AAChC,UAAI,YAAY,IAAI,GAAG;AACrB,cAAM,gBAAgB,MAAM,QAAQ,YAAY,IAAI,CAAC,IACjD,YAAY,IAAI,IAChB,CAAC,YAAY,IAAI,CAAC;AACtB,mBAAW,SAAS,eAAe;AACjC,mBAAS,KAAK;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,WAAS,IAAI;AACb,SAAO;AACT;AASA,IAAM,WAAW,CAAC,aAChB,OAAO,IAAI;AAAA,EACT,KAAK,MAAM,aAAa,UAAU,OAAO;AAAA,EACzC,OAAO,CAAC,UAAU,IAAI,cAAc,EAAE,UAAU,MAAM,CAAC;AACzD,CAAC;AAKH,IAAM,YAAY,CAChB,UACA,YAEA,OAAO,IAAI;AAAA,EACT,KAAK,MAAM,cAAc,UAAU,SAAS,OAAO;AAAA,EACnD,OAAO,CAAC,UAAU,IAAI,eAAe,EAAE,UAAU,MAAM,CAAC;AAC1D,CAAC;AAKH,IAAM,WAAW,CACf,SAEA,OAAO,IAAI;AAAA,EACT,KAAK,MAAM,SAAS,IAAI;AAAA,EACxB,OAAO,CAAC,UAAU,IAAI,gBAAgB,EAAE,MAAM,MAAM,CAAC;AACvD,CAAC;AAKH,IAAM,gBAAgB,CACpB,SAEA,OAAO,IAAI;AAAA,EACT,KAAK,MAAM,YAAY,IAAI;AAAA,EAC3B,OAAO,CAAC,UAAU,IAAI,gBAAgB,EAAE,MAAM,MAAM,CAAC;AACvD,CAAC;AASI,IAAM,cAAc,CACzB,UACA,UAAmB,CAAC,MAKpB,OAAO,IAAI,aAAa;AACtB,QAAM;AAAA,IACJ,gBAAgB;AAAA,IAChB;AAAA,IACA,kBAAkB;AAAA,IAClB;AAAA,IACA,kBAAkB;AAAA,IAClB,SAAS;AAAA,IACT,UAAU;AAAA,EACZ,IAAI;AAGJ,SAAO,qBAAqB,YAAY;AAExC,MAAI,SAAS;AACX,WAAO,OAAO,IAAI,oBAAoB,QAAQ,EAAE;AAAA,EAClD;AAEA,QAAM,kBAAkB,OAAO,SAAS,QAAQ;AAGhD,QAAM,kBACJ,oBAAoB,yBAChB,gBAAgB,UAAU,YAAY,IACtC,gBAAgB,QAAQ;AAE9B,QAAM,YAAY,OAAO,OAAO,WAAW;AAAA,IACzC,KAAK,MACH;AAAA,MACE;AAAA,MACA;AAAA,QACE,QAAQ,CAAC,EAAE,SAAS,SAAS,MAAM;AACjC,gBAAM,6BAA6B;AACnC,gBAAM,eAAe,QAAQ;AAAA,YAC3B;AAAA,YACA;AAAA,UACF;AACA,cAAI,kBAAkB;AAEtB,cAAI;AACF,kBAAM,MAAM,MAAM,cAAc,EAAE,SAAS,CAAC;AAC5C,kBAAM,uBAAuB,wBAAwB,GAAG;AAGxD,gBAAI,gBAAgB;AAClB,oBAAM,UACJ,0BAA0B,MACtB,iBACA,IAAI,IAAI,cAAc;AAC5B,yBAAW,QAAQ,SAAS;AAC1B,qCAAqB,IAAI,IAAI;AAAA,cAC/B;AAAA,YACF;AAEA,uBAAW,IAAI,IAAI;AACnB,kBAAM,gBAAgB;AAAA,cACpB,IAAI;AAAA,cACJ;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAEA,0BAAc;AAAA,cACZ,CAAC,GAAG,MAAM,EAAE,iBAAiB,EAAE;AAAA,YACjC;AAEA,8BAAkB;AAClB,uBAAW,OAAO,eAAe;AAC/B,gCACE,gBAAgB,UAAU,GAAG,IAAI,cAAc,IAC/C,gBAAgB,IAAI,IAAI,MACxB,gBAAgB,UAAU,IAAI,cAAc;AAAA,YAChD;AAAA,UAGF,SAAS,YAAqB;AAC5B,kBAAM,kBACH,WAAiC,SAChC,yBACD,WAAoC,SAAS;AAAA,cAC5C;AAAA,YACF,KACC,WAAoC,SAAS;AAAA,cAC5C;AAAA,YACF;AAEF,gBAAI,iBAAiB;AAEnB,qBAAO;AAAA,gBACL,MAAM,kBACF,6BACA;AAAA,cACN;AAAA,YACF;AACA,kBAAM;AAAA,UACR;AACA,iBAAO,EAAE,MAAM,gBAAgB;AAAA,QACjC;AAAA,MACF;AAAA,MACA,EAAE,UAAU,SAAS;AAAA,IACvB;AAAA,IACF,OAAO,CAAC,UAAU,IAAI,iBAAiB,EAAE,UAAU,MAAM,CAAC;AAAA,EAC5D,CAAC;AAED,MAAI,CAAC,QAAQ;AACX,WAAO,UAAU,UAAU,UAAU,IAAI;AAAA,EAC3C;AAEA,QAAM,iBAAiB,UAAU,KAAK,MAAM,qBAAqB,KAAK,CAAC,GACpE;AAEH,MAAI,WAAW,gBAAgB,GAAG;AAChC,WAAO,OAAO,IAAI,cAAc,aAAa,wBAAwB;AAAA,EACvE;AAEA,SAAO,OAAO;AAAA,IACZ,iDAAiD,QAAQ;AAAA,EAC3D;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IACT;AAAA,EACF;AACF,CAAC,EAAE;AAAA,EACD,OAAO;AAAA,IAAS;AAAA,IAAiB,CAAC,UAChC,OAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO,uBAAuB,MAAM,QAAQ;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA,EACA,OAAO;AAAA,IAAS;AAAA,IAAkB,CAAC,UACjC,OAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO,uBAAuB,MAAM,QAAQ;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA,EACA,OAAO;AAAA,IAAS;AAAA,IAAoB,CAAC,UACnC,OAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO,kBAAkB,MAAM,QAAQ,KACrC,MAAM,iBAAiB,QACnB,MAAM,MAAM,UACZ,OAAO,MAAM,KAAK,CACxB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EACA,OAAO;AAAA,IAAS;AAAA,IAAmB,CAAC,UAClC,OAAO,QAAQ;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,MACT,OAAO,MAAM;AAAA,IACf,CAAC;AAAA,EACH;AACF;AAKK,IAAM,cAAc,CACzB,MACA,UAAmB,CAAC,MASpB,OAAO,IAAI,aAAa;AACtB,QAAM,UAAoB,CAAC;AAE3B,QAAM,WAAW,CACf,gBASA,OAAO,IAAI,aAAa;AACtB,UAAM,QAAQ,OAAO,SAAS,WAAW;AAEzC,QAAI,MAAM,OAAO,GAAG;AAClB,UAAI,YAAY,SAAS,SAAS,GAAG;AACnC,cAAM,SAAS,OAAO,YAAY,aAAa,OAAO;AACtD,gBAAQ,KAAK,MAAM;AAAA,MACrB;AAAA,IACF,WAAW,MAAM,YAAY,GAAG;AAC9B,YAAM,QAAQ,OAAO,cAAc,WAAW;AAC9C,iBAAW,QAAQ,OAAO;AACxB,eAAO,SAAS,KAAK,aAAa,IAAI,CAAC;AAAA,MACzC;AAAA,IACF;AAAA,EACF,CAAC;AAEH,SAAO,SAAS,IAAI;AACpB,SAAO;AACT,CAAC;AAKI,IAAM,SAAS,CACpB,MACA,UAAmB,CAAC,MASpB,OAAO,IAAI,aAAa;AACtB,QAAM,QAAQ,OAAO,SAAS,IAAI;AAElC,MAAI,MAAM,OAAO,GAAG;AAClB,UAAM,SAAS,OAAO,YAAY,MAAM,OAAO;AAC/C,WAAO,CAAC,MAAM;AAAA,EAChB;AACA,MAAI,MAAM,YAAY,GAAG;AACvB,WAAO,OAAO,YAAY,MAAM,OAAO;AAAA,EACzC;AAEA,QAAM,IAAI,gBAAgB;AAAA,IACxB;AAAA,IACA,OAAO;AAAA,EACT,CAAC;AACH,CAAC,EAAE;AAAA,EACD,OAAO,SAAS,mBAAmB,CAAC,UAAU,OAAO,IAAI,MAAM,KAAK,CAAC;AACvE;AAKF,eAAsB,cACpB,MACA,UAAmB,CAAC,GACD;AACnB,SAAO,OAAO,WAAW,OAAO,MAAM,OAAO,CAAC;AAChD;","names":[]}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
#!/usr/bin/env node
|
|
3
|
+
import {
|
|
4
|
+
injectPromise
|
|
5
|
+
} from "./chunk-7MRS72CP.js";
|
|
6
|
+
|
|
7
|
+
// src/cli.ts
|
|
8
|
+
import { resolve } from "path";
|
|
9
|
+
function parseArgs(args) {
|
|
10
|
+
const options = {
|
|
11
|
+
dryRun: false,
|
|
12
|
+
verbose: false,
|
|
13
|
+
help: false,
|
|
14
|
+
version: false
|
|
15
|
+
};
|
|
16
|
+
let path = "";
|
|
17
|
+
for (let i = 0; i < args.length; i++) {
|
|
18
|
+
const arg = args[i];
|
|
19
|
+
switch (arg) {
|
|
20
|
+
case "-h":
|
|
21
|
+
case "--help":
|
|
22
|
+
options.help = true;
|
|
23
|
+
break;
|
|
24
|
+
case "-v":
|
|
25
|
+
case "--version":
|
|
26
|
+
options.version = true;
|
|
27
|
+
break;
|
|
28
|
+
case "-n":
|
|
29
|
+
case "--dry-run":
|
|
30
|
+
options.dryRun = true;
|
|
31
|
+
break;
|
|
32
|
+
case "--verbose":
|
|
33
|
+
options.verbose = true;
|
|
34
|
+
break;
|
|
35
|
+
case "-c":
|
|
36
|
+
case "--component": {
|
|
37
|
+
const nextArg = args[i + 1];
|
|
38
|
+
if (nextArg && !nextArg.startsWith("-")) {
|
|
39
|
+
options.componentLength = Number.parseInt(nextArg, 10);
|
|
40
|
+
i++;
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
default:
|
|
45
|
+
if (!arg.startsWith("-")) {
|
|
46
|
+
path = arg;
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { path, options };
|
|
52
|
+
}
|
|
53
|
+
function showHelp() {
|
|
54
|
+
console.log(`
|
|
55
|
+
svelte-qa-ids - Automatically inject data-qa-id attributes into Svelte components
|
|
56
|
+
|
|
57
|
+
USAGE:
|
|
58
|
+
svelte-qa-ids <path> [options]
|
|
59
|
+
|
|
60
|
+
ARGUMENTS:
|
|
61
|
+
<path> Path to a Svelte file or directory to process
|
|
62
|
+
|
|
63
|
+
OPTIONS:
|
|
64
|
+
-h, --help Show this help message
|
|
65
|
+
-v, --version Show version number
|
|
66
|
+
-n, --dry-run Process files without modifying them
|
|
67
|
+
--verbose Show detailed logging
|
|
68
|
+
-c, --component <N> Number of characters for component prefix (2-10, default: 3)
|
|
69
|
+
|
|
70
|
+
EXAMPLES:
|
|
71
|
+
# Process all Svelte files in src/
|
|
72
|
+
svelte-qa-ids src/
|
|
73
|
+
|
|
74
|
+
# Dry run to see what would be changed
|
|
75
|
+
svelte-qa-ids src/ --dry-run
|
|
76
|
+
|
|
77
|
+
# Process a single file
|
|
78
|
+
svelte-qa-ids src/routes/+page.svelte
|
|
79
|
+
|
|
80
|
+
# Verbose output
|
|
81
|
+
svelte-qa-ids src/ --verbose
|
|
82
|
+
|
|
83
|
+
# Use 4-character component prefixes
|
|
84
|
+
svelte-qa-ids src/ --component 4
|
|
85
|
+
|
|
86
|
+
# Use 2-character component prefixes
|
|
87
|
+
svelte-qa-ids src/ -c 2
|
|
88
|
+
|
|
89
|
+
For more information, visit: https://github.com/your-org/svelte-qa-ids
|
|
90
|
+
`);
|
|
91
|
+
}
|
|
92
|
+
async function showVersion() {
|
|
93
|
+
const pkg = await import("./package-QZOBPTUC.js");
|
|
94
|
+
console.log(`svelte-qa-ids v${pkg.default.version}`);
|
|
95
|
+
}
|
|
96
|
+
async function main() {
|
|
97
|
+
const args = process.argv.slice(2);
|
|
98
|
+
const { path, options } = parseArgs(args);
|
|
99
|
+
if (options.help) {
|
|
100
|
+
showHelp();
|
|
101
|
+
process.exit(0);
|
|
102
|
+
}
|
|
103
|
+
if (options.version) {
|
|
104
|
+
await showVersion();
|
|
105
|
+
process.exit(0);
|
|
106
|
+
}
|
|
107
|
+
if (!path) {
|
|
108
|
+
console.error("Error: Please provide a path to process");
|
|
109
|
+
console.error("Run 'svelte-qa-ids --help' for usage information");
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
const resolvedPath = resolve(path);
|
|
113
|
+
const injectOptions = {
|
|
114
|
+
dryRun: options.dryRun,
|
|
115
|
+
verbose: options.verbose,
|
|
116
|
+
prefixLength: options.componentLength
|
|
117
|
+
};
|
|
118
|
+
if (options.dryRun) {
|
|
119
|
+
console.log("DRY RUN MODE - No files will be modified\n");
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const results = await injectPromise(resolvedPath, injectOptions);
|
|
123
|
+
const successCount = results.filter((r) => r.success).length;
|
|
124
|
+
const failCount = results.filter((r) => !r.success).length;
|
|
125
|
+
console.log(`
|
|
126
|
+
Processed ${results.length} file(s)`);
|
|
127
|
+
console.log(` Success: ${successCount}`);
|
|
128
|
+
console.log(` Failed: ${failCount}`);
|
|
129
|
+
if (failCount > 0) {
|
|
130
|
+
console.log("\nErrors:");
|
|
131
|
+
for (const result of results) {
|
|
132
|
+
if (!result.success) {
|
|
133
|
+
console.error(` ${result.file}: ${result.error}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error("Error:", error instanceof Error ? error.message : error);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
main();
|
|
144
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * CLI for svelte-qa-ids\n */\n\nimport { resolve } from \"node:path\";\nimport { injectPromise, type Options } from \"./index.js\";\n\ninterface CliOptions {\n dryRun: boolean;\n verbose: boolean;\n help: boolean;\n version: boolean;\n componentLength?: number;\n}\n\nfunction parseArgs(args: string[]): { path: string; options: CliOptions } {\n const options: CliOptions = {\n dryRun: false,\n verbose: false,\n help: false,\n version: false,\n };\n\n let path = \"\";\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n switch (arg) {\n case \"-h\":\n case \"--help\":\n options.help = true;\n break;\n case \"-v\":\n case \"--version\":\n options.version = true;\n break;\n case \"-n\":\n case \"--dry-run\":\n options.dryRun = true;\n break;\n case \"--verbose\":\n options.verbose = true;\n break;\n case \"-c\":\n case \"--component\": {\n const nextArg = args[i + 1];\n if (nextArg && !nextArg.startsWith(\"-\")) {\n options.componentLength = Number.parseInt(nextArg, 10);\n i++; // Skip next arg\n }\n break;\n }\n default:\n if (!arg.startsWith(\"-\")) {\n path = arg;\n }\n break;\n }\n }\n\n return { path, options };\n}\n\nfunction showHelp(): void {\n console.log(`\nsvelte-qa-ids - Automatically inject data-qa-id attributes into Svelte components\n\nUSAGE:\n svelte-qa-ids <path> [options]\n\nARGUMENTS:\n <path> Path to a Svelte file or directory to process\n\nOPTIONS:\n -h, --help Show this help message\n -v, --version Show version number\n -n, --dry-run Process files without modifying them\n --verbose Show detailed logging\n -c, --component <N> Number of characters for component prefix (2-10, default: 3)\n\nEXAMPLES:\n # Process all Svelte files in src/\n svelte-qa-ids src/\n\n # Dry run to see what would be changed\n svelte-qa-ids src/ --dry-run\n\n # Process a single file\n svelte-qa-ids src/routes/+page.svelte\n\n # Verbose output\n svelte-qa-ids src/ --verbose\n\n # Use 4-character component prefixes\n svelte-qa-ids src/ --component 4\n\n # Use 2-character component prefixes\n svelte-qa-ids src/ -c 2\n\nFor more information, visit: https://github.com/your-org/svelte-qa-ids\n`);\n}\n\nasync function showVersion(): Promise<void> {\n const pkg = await import(\"../package.json\", {\n assert: { type: \"json\" },\n });\n console.log(`svelte-qa-ids v${pkg.default.version}`);\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n const { path, options } = parseArgs(args);\n\n if (options.help) {\n showHelp();\n process.exit(0);\n }\n\n if (options.version) {\n await showVersion();\n process.exit(0);\n }\n\n if (!path) {\n console.error(\"Error: Please provide a path to process\");\n console.error(\"Run 'svelte-qa-ids --help' for usage information\");\n process.exit(1);\n }\n\n const resolvedPath = resolve(path);\n const injectOptions: Options = {\n dryRun: options.dryRun,\n verbose: options.verbose,\n prefixLength: options.componentLength,\n };\n\n if (options.dryRun) {\n console.log(\"DRY RUN MODE - No files will be modified\\n\");\n }\n\n try {\n const results = await injectPromise(resolvedPath, injectOptions);\n\n const successCount = results.filter((r) => r.success).length;\n const failCount = results.filter((r) => !r.success).length;\n\n console.log(`\\nProcessed ${results.length} file(s)`);\n console.log(` Success: ${successCount}`);\n console.log(` Failed: ${failCount}`);\n\n if (failCount > 0) {\n console.log(\"\\nErrors:\");\n for (const result of results) {\n if (!result.success) {\n console.error(` ${result.file}: ${result.error}`);\n }\n }\n process.exit(1);\n }\n } catch (error) {\n console.error(\"Error:\", error instanceof Error ? error.message : error);\n process.exit(1);\n }\n}\n\nmain();\n"],"mappings":"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","names":[]}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
FileReadError,
|
|
4
|
+
FileSystemError,
|
|
5
|
+
FileWriteError,
|
|
6
|
+
SvelteParseError,
|
|
7
|
+
ValidationError,
|
|
8
|
+
defaultPrefixGenerator,
|
|
9
|
+
getTagAbbreviation,
|
|
10
|
+
inject,
|
|
11
|
+
injectPromise,
|
|
12
|
+
processFile,
|
|
13
|
+
processPath
|
|
14
|
+
} from "./chunk-7MRS72CP.js";
|
|
15
|
+
export {
|
|
16
|
+
FileReadError,
|
|
17
|
+
FileSystemError,
|
|
18
|
+
FileWriteError,
|
|
19
|
+
SvelteParseError,
|
|
20
|
+
ValidationError,
|
|
21
|
+
defaultPrefixGenerator,
|
|
22
|
+
getTagAbbreviation,
|
|
23
|
+
inject,
|
|
24
|
+
injectPromise,
|
|
25
|
+
processFile,
|
|
26
|
+
processPath
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// package.json
|
|
4
|
+
var name = "svelte-qa-ids";
|
|
5
|
+
var version = "1.0.0";
|
|
6
|
+
var description = "Automatically inject stable data-qa-id attributes into Svelte components for testing and automation";
|
|
7
|
+
var type = "module";
|
|
8
|
+
var main = "./dist/index.js";
|
|
9
|
+
var bin = {
|
|
10
|
+
"svelte-qa-ids": "dist/cli.js"
|
|
11
|
+
};
|
|
12
|
+
var exports = {
|
|
13
|
+
".": {
|
|
14
|
+
import: "./dist/index.js",
|
|
15
|
+
types: "./dist/index.d.ts"
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var files = [
|
|
19
|
+
"dist",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
];
|
|
23
|
+
var scripts = {
|
|
24
|
+
build: "tsup",
|
|
25
|
+
dev: "tsup --watch",
|
|
26
|
+
test: "bun test",
|
|
27
|
+
lint: "bun x ultracite check src",
|
|
28
|
+
format: "bun x ultracite fix src",
|
|
29
|
+
prepublishOnly: "bun run build",
|
|
30
|
+
check: "ultracite check",
|
|
31
|
+
fix: "ultracite fix",
|
|
32
|
+
prepare: "lefthook install"
|
|
33
|
+
};
|
|
34
|
+
var keywords = [
|
|
35
|
+
"svelte",
|
|
36
|
+
"testing",
|
|
37
|
+
"automation",
|
|
38
|
+
"qa",
|
|
39
|
+
"data-qa-id",
|
|
40
|
+
"e2e",
|
|
41
|
+
"playwright",
|
|
42
|
+
"selectors"
|
|
43
|
+
];
|
|
44
|
+
var author = "";
|
|
45
|
+
var license = "MIT";
|
|
46
|
+
var repository = {
|
|
47
|
+
type: "git",
|
|
48
|
+
url: "git+https://github.com/edut/svelte-qa-ids.git"
|
|
49
|
+
};
|
|
50
|
+
var peerDependencies = {
|
|
51
|
+
svelte: "^3.0.0 || ^4.0.0 || ^5.0.0"
|
|
52
|
+
};
|
|
53
|
+
var devDependencies = {
|
|
54
|
+
"@biomejs/biome": "2.3.13",
|
|
55
|
+
"@types/node": "^20.17.6",
|
|
56
|
+
lefthook: "^2.0.16",
|
|
57
|
+
tsup: "^8.3.5",
|
|
58
|
+
typescript: "^5.7.2",
|
|
59
|
+
ultracite: "7.1.3"
|
|
60
|
+
};
|
|
61
|
+
var dependencies = {
|
|
62
|
+
effect: "^3.19.15",
|
|
63
|
+
svelte: "^5.3.0"
|
|
64
|
+
};
|
|
65
|
+
var engines = {
|
|
66
|
+
node: ">=18.0.0"
|
|
67
|
+
};
|
|
68
|
+
var package_default = {
|
|
69
|
+
name,
|
|
70
|
+
version,
|
|
71
|
+
description,
|
|
72
|
+
type,
|
|
73
|
+
main,
|
|
74
|
+
bin,
|
|
75
|
+
exports,
|
|
76
|
+
files,
|
|
77
|
+
scripts,
|
|
78
|
+
keywords,
|
|
79
|
+
author,
|
|
80
|
+
license,
|
|
81
|
+
repository,
|
|
82
|
+
peerDependencies,
|
|
83
|
+
devDependencies,
|
|
84
|
+
dependencies,
|
|
85
|
+
engines
|
|
86
|
+
};
|
|
87
|
+
export {
|
|
88
|
+
author,
|
|
89
|
+
bin,
|
|
90
|
+
package_default as default,
|
|
91
|
+
dependencies,
|
|
92
|
+
description,
|
|
93
|
+
devDependencies,
|
|
94
|
+
engines,
|
|
95
|
+
exports,
|
|
96
|
+
files,
|
|
97
|
+
keywords,
|
|
98
|
+
license,
|
|
99
|
+
main,
|
|
100
|
+
name,
|
|
101
|
+
peerDependencies,
|
|
102
|
+
repository,
|
|
103
|
+
scripts,
|
|
104
|
+
type,
|
|
105
|
+
version
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=package-QZOBPTUC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../package.json"],"sourcesContent":["{\n \"name\": \"svelte-qa-ids\",\n \"version\": \"1.0.0\",\n \"description\": \"Automatically inject stable data-qa-id attributes into Svelte components for testing and automation\",\n \"type\": \"module\",\n \"main\": \"./dist/index.js\",\n \"bin\": {\n \"svelte-qa-ids\": \"dist/cli.js\"\n },\n \"exports\": {\n \".\": {\n \"import\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"bun test\",\n \"lint\": \"bun x ultracite check src\",\n \"format\": \"bun x ultracite fix src\",\n \"prepublishOnly\": \"bun run build\",\n \"check\": \"ultracite check\",\n \"fix\": \"ultracite fix\",\n \"prepare\": \"lefthook install\"\n },\n \"keywords\": [\n \"svelte\",\n \"testing\",\n \"automation\",\n \"qa\",\n \"data-qa-id\",\n \"e2e\",\n \"playwright\",\n \"selectors\"\n ],\n \"author\": \"\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/edut/svelte-qa-ids.git\"\n },\n \"peerDependencies\": {\n \"svelte\": \"^3.0.0 || ^4.0.0 || ^5.0.0\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"2.3.13\",\n \"@types/node\": \"^20.17.6\",\n \"lefthook\": \"^2.0.16\",\n \"tsup\": \"^8.3.5\",\n \"typescript\": \"^5.7.2\",\n \"ultracite\": \"7.1.3\"\n },\n \"dependencies\": {\n \"effect\": \"^3.19.15\",\n \"svelte\": \"^5.3.0\"\n },\n \"engines\": {\n \"node\": \">=18.0.0\"\n }\n}\n"],"mappings":";;;AACE,WAAQ;AACR,cAAW;AACX,kBAAe;AACf,WAAQ;AACR,WAAQ;AACR,UAAO;AAAA,EACL,iBAAiB;AACnB;AACA,cAAW;AAAA,EACT,KAAK;AAAA,IACH,QAAU;AAAA,IACV,OAAS;AAAA,EACX;AACF;AACA,YAAS;AAAA,EACP;AAAA,EACA;AAAA,EACA;AACF;AACA,cAAW;AAAA,EACT,OAAS;AAAA,EACT,KAAO;AAAA,EACP,MAAQ;AAAA,EACR,MAAQ;AAAA,EACR,QAAU;AAAA,EACV,gBAAkB;AAAA,EAClB,OAAS;AAAA,EACT,KAAO;AAAA,EACP,SAAW;AACb;AACA,eAAY;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AACA,aAAU;AACV,cAAW;AACX,iBAAc;AAAA,EACZ,MAAQ;AAAA,EACR,KAAO;AACT;AACA,uBAAoB;AAAA,EAClB,QAAU;AACZ;AACA,sBAAmB;AAAA,EACjB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,UAAY;AAAA,EACZ,MAAQ;AAAA,EACR,YAAc;AAAA,EACd,WAAa;AACf;AACA,mBAAgB;AAAA,EACd,QAAU;AAAA,EACV,QAAU;AACZ;AACA,cAAW;AAAA,EACT,MAAQ;AACV;AAhEF;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAGA;AAAA,EAMA;AAAA,EAKA;AAAA,EAWA;AAAA,EAUA;AAAA,EACA;AAAA,EACA;AAAA,EAIA;AAAA,EAGA;AAAA,EAQA;AAAA,EAIA;AAGF;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "svelte-qa-ids",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Automatically inject stable data-qa-id attributes into Svelte components for testing and automation",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"svelte-qa-ids": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"dev": "tsup --watch",
|
|
24
|
+
"test": "bun test",
|
|
25
|
+
"lint": "bun x ultracite check src",
|
|
26
|
+
"format": "bun x ultracite fix src",
|
|
27
|
+
"prepublishOnly": "bun run build",
|
|
28
|
+
"check": "ultracite check",
|
|
29
|
+
"fix": "ultracite fix",
|
|
30
|
+
"prepare": "lefthook install"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"svelte",
|
|
34
|
+
"testing",
|
|
35
|
+
"automation",
|
|
36
|
+
"qa",
|
|
37
|
+
"data-qa-id",
|
|
38
|
+
"e2e",
|
|
39
|
+
"playwright",
|
|
40
|
+
"selectors"
|
|
41
|
+
],
|
|
42
|
+
"author": "",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/edut/svelte-qa-ids.git"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"svelte": "^3.0.0 || ^4.0.0 || ^5.0.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@biomejs/biome": "2.3.13",
|
|
53
|
+
"@types/node": "^20.17.6",
|
|
54
|
+
"lefthook": "^2.0.16",
|
|
55
|
+
"tsup": "^8.3.5",
|
|
56
|
+
"typescript": "^5.7.2",
|
|
57
|
+
"ultracite": "7.1.3"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"effect": "^3.19.15",
|
|
61
|
+
"svelte": "^5.3.0"
|
|
62
|
+
},
|
|
63
|
+
"engines": {
|
|
64
|
+
"node": ">=18.0.0"
|
|
65
|
+
}
|
|
66
|
+
}
|