@autometa/vitest-plugins 1.0.0-rc.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/dist/index.js ADDED
@@ -0,0 +1,685 @@
1
+ import { dirname, isAbsolute, resolve, extname, relative } from 'path';
2
+ import { existsSync } from 'fs';
3
+ import jiti from 'jiti';
4
+
5
+ // src/index.ts
6
+ var STEP_FALLBACK_GLOB = "**/*.{ts,tsx,js,jsx,mjs,cjs,mts,cts}";
7
+ function flattenModuleDeclarations(declarations, prefix = []) {
8
+ if (!declarations || declarations.length === 0) {
9
+ return [];
10
+ }
11
+ const results = [];
12
+ for (const entry of declarations) {
13
+ if (typeof entry === "string") {
14
+ const next2 = [...prefix, entry];
15
+ results.push(next2);
16
+ continue;
17
+ }
18
+ const next = [...prefix, entry.name];
19
+ results.push(next);
20
+ const nested = flattenModuleDeclarations(entry.submodules ?? void 0, next);
21
+ for (const path of nested) {
22
+ results.push([...path]);
23
+ }
24
+ }
25
+ const unique = /* @__PURE__ */ new Map();
26
+ for (const path of results) {
27
+ unique.set(path.join("/"), path);
28
+ }
29
+ return Array.from(unique.values()).sort((a, b) => b.length - a.length);
30
+ }
31
+ function buildStepScopingData(resolvedConfig, projectRoot) {
32
+ const groups = resolvedConfig?.modules?.groups;
33
+ if (!groups || typeof groups !== "object") {
34
+ return null;
35
+ }
36
+ const entries = Object.entries(groups).map(([group, groupConfig]) => {
37
+ const configRecord = groupConfig && typeof groupConfig === "object" ? groupConfig : {};
38
+ const rootAbs = resolve(projectRoot, String(configRecord.root ?? ""));
39
+ const modules = configRecord.modules;
40
+ const modulePaths = Array.isArray(modules) ? flattenModuleDeclarations(modules) : [];
41
+ return { group, rootAbs, modulePaths };
42
+ });
43
+ return { groups: entries };
44
+ }
45
+ function autometa() {
46
+ let autometaConfig;
47
+ let configPath;
48
+ let projectRoot;
49
+ return {
50
+ name: "autometa-vitest-plugin",
51
+ enforce: "pre",
52
+ async configResolved(viteConfig) {
53
+ projectRoot = viteConfig.root || process.cwd();
54
+ const loaded = await loadAutometaConfig(projectRoot);
55
+ autometaConfig = loaded.config;
56
+ configPath = loaded.path;
57
+ },
58
+ transform(code, id) {
59
+ const cleanId = id.split("?", 1)[0] ?? id;
60
+ if (!cleanId.endsWith(".feature")) {
61
+ return;
62
+ }
63
+ if (!autometaConfig) {
64
+ throw new Error("Autometa config not found");
65
+ }
66
+ const resolved = autometaConfig.resolve();
67
+ const stepRoots = resolved.config.roots.steps;
68
+ const configDir = configPath ? dirname(configPath) : projectRoot ?? process.cwd();
69
+ const rootDir = projectRoot ?? process.cwd();
70
+ const stepGlobs = buildStepGlobs(stepRoots, {
71
+ configDir,
72
+ projectRoot: rootDir
73
+ });
74
+ if (stepGlobs.length === 0) {
75
+ throw new Error(
76
+ "Autometa config did not resolve any step files within the current project root."
77
+ );
78
+ }
79
+ const eventGlobs = buildStepGlobs(resolved.config.events, {
80
+ configDir,
81
+ projectRoot: rootDir
82
+ });
83
+ const runtimeConfig = JSON.stringify(resolved.config);
84
+ const stepScopingMode = resolved.config.modules?.stepScoping ?? "global";
85
+ const groupIndexData = buildStepScopingData(resolved.config, rootDir);
86
+ const stepScopingData = stepScopingMode === "scoped" ? groupIndexData : null;
87
+ const featureFile = cleanId;
88
+ return {
89
+ code: String.raw`
90
+ import { describe } from 'vitest';
91
+ import { execute } from '@autometa/vitest-executor';
92
+ import { coordinateRunnerFeature, CucumberRunner, STEPS_ENVIRONMENT_META } from '@autometa/runner';
93
+ import { parseGherkin } from '@autometa/gherkin';
94
+ import { relative as __pathRelative, resolve as __pathResolve, isAbsolute as __pathIsAbsolute } from 'node:path';
95
+
96
+ const __AUTOMETA_STEP_SCOPING_MODE = ${JSON.stringify(stepScopingMode)};
97
+ const __AUTOMETA_GROUP_INDEX = ${JSON.stringify(groupIndexData)};
98
+ const __AUTOMETA_STEP_SCOPING = ${JSON.stringify(stepScopingData)};
99
+ const __AUTOMETA_FEATURE_FILE = ${JSON.stringify(featureFile)};
100
+
101
+ const __AUTOMETA_EVENT_MODULES = ${eventGlobs.length > 0 ? `import.meta.glob(${JSON.stringify(eventGlobs)}, { eager: true })` : "{}"};
102
+ const stepModules = import.meta.glob(${JSON.stringify(stepGlobs)}, { eager: true });
103
+
104
+ function collectCandidateModules(imported) {
105
+ if (!imported || typeof imported !== 'object') {
106
+ return [];
107
+ }
108
+
109
+ const modules = new Set();
110
+ modules.add(imported);
111
+
112
+ const exportedModules = imported.modules;
113
+ if (Array.isArray(exportedModules)) {
114
+ for (const entry of exportedModules) {
115
+ if (entry && typeof entry === 'object') {
116
+ modules.add(entry);
117
+ }
118
+ }
119
+ }
120
+
121
+ const defaultExport = imported.default;
122
+ if (Array.isArray(defaultExport)) {
123
+ for (const entry of defaultExport) {
124
+ if (entry && typeof entry === 'object') {
125
+ modules.add(entry);
126
+ }
127
+ }
128
+ } else if (defaultExport && typeof defaultExport === 'object') {
129
+ modules.add(defaultExport);
130
+ }
131
+
132
+ return Array.from(modules);
133
+ }
134
+
135
+ function isStepsEnvironment(candidate) {
136
+ return Boolean(
137
+ candidate &&
138
+ typeof candidate === 'object' &&
139
+ typeof candidate.coordinateFeature === 'function' &&
140
+ typeof candidate.getPlan === 'function' &&
141
+ typeof candidate.Given === 'function' &&
142
+ typeof candidate.When === 'function' &&
143
+ typeof candidate.Then === 'function'
144
+ );
145
+ }
146
+
147
+ function extractStepsEnvironments(candidate) {
148
+ const environments = [];
149
+
150
+ if (!candidate || typeof candidate !== 'object') {
151
+ return environments;
152
+ }
153
+
154
+ if (isStepsEnvironment(candidate)) {
155
+ environments.push(candidate);
156
+ }
157
+
158
+ const stepsEnv = candidate.stepsEnvironment;
159
+ if (isStepsEnvironment(stepsEnv)) {
160
+ environments.push(stepsEnv);
161
+ }
162
+
163
+ const defaultExport = candidate.default;
164
+ if (isStepsEnvironment(defaultExport)) {
165
+ environments.push(defaultExport);
166
+ }
167
+
168
+ return environments;
169
+ }
170
+
171
+ function collectStepsEnvironments(modules) {
172
+ const environments = new Set();
173
+ for (const moduleExports of Object.values(modules)) {
174
+ for (const candidate of collectCandidateModules(moduleExports)) {
175
+ for (const env of extractStepsEnvironments(candidate)) {
176
+ environments.add(env);
177
+ }
178
+ }
179
+ }
180
+ return Array.from(environments);
181
+ }
182
+
183
+ function __normalizePathSegments(input) {
184
+ return String(input).replace(/\\/g, '/').split('/').filter(Boolean);
185
+ }
186
+
187
+ function __startsWithSegments(haystack, needle) {
188
+ if (needle.length > haystack.length) return false;
189
+ for (let i = 0; i < needle.length; i += 1) {
190
+ if (haystack[i] !== needle[i]) return false;
191
+ }
192
+ return true;
193
+ }
194
+
195
+ function __resolveFileScope(fileAbs) {
196
+ if (!__AUTOMETA_GROUP_INDEX || !__AUTOMETA_GROUP_INDEX.groups) {
197
+ return { kind: 'root' };
198
+ }
199
+ if (String(fileAbs).startsWith('node:')) {
200
+ return { kind: 'root' };
201
+ }
202
+
203
+ const absoluteFile = __pathIsAbsolute(String(fileAbs))
204
+ ? String(fileAbs)
205
+ : __pathResolve(process.cwd(), String(fileAbs));
206
+
207
+ for (const entry of __AUTOMETA_GROUP_INDEX.groups) {
208
+ const rootAbs = entry.rootAbs;
209
+ const rel = rootAbs ? __pathRelative(rootAbs, absoluteFile) : '';
210
+ if (rel === '' || (!rel.startsWith('..') && !rel.startsWith('../') && !rel.startsWith('..\\'))) {
211
+ const segments = __normalizePathSegments(rel);
212
+ const modulePaths = entry.modulePaths || [];
213
+ for (const modulePath of modulePaths) {
214
+ if (__startsWithSegments(segments, modulePath)) {
215
+ return { kind: 'module', group: entry.group, modulePath };
216
+ }
217
+ }
218
+ return { kind: 'group', group: entry.group };
219
+ }
220
+ }
221
+ return { kind: 'root' };
222
+ }
223
+
224
+ function __parseScopeOverrideTag(tags) {
225
+ if (!Array.isArray(tags)) return undefined;
226
+ for (const tag of tags) {
227
+ const match = String(tag).match(/^@scope(?::|=|\()(.+?)(?:\))?$/u);
228
+ if (!match) continue;
229
+ const raw = String(match[1] ?? '').trim();
230
+ if (!raw) continue;
231
+ const normalized = raw.replace(/\//g, ':');
232
+ const parts = normalized.split(':').filter(Boolean);
233
+ const group = parts[0];
234
+ const rest = parts.slice(1);
235
+ if (!group) continue;
236
+ return rest.length > 0 ? { group, modulePath: rest } : { group };
237
+ }
238
+ return undefined;
239
+ }
240
+
241
+ function __resolveFeatureScope(feature) {
242
+ const override = __parseScopeOverrideTag(feature.tags);
243
+ if (override) {
244
+ if (override.modulePath && override.modulePath.length > 0) {
245
+ return { kind: 'module', group: override.group, modulePath: override.modulePath };
246
+ }
247
+ return { kind: 'group', group: override.group };
248
+ }
249
+ return __resolveFileScope(__AUTOMETA_FEATURE_FILE);
250
+ }
251
+
252
+ function __inferEnvironmentGroup(environment) {
253
+ const meta = environment && typeof environment === 'object'
254
+ ? environment[STEPS_ENVIRONMENT_META]
255
+ : undefined;
256
+ if (meta && typeof meta === 'object') {
257
+ if (meta.kind === 'group' && typeof meta.group === 'string' && meta.group.trim().length > 0) {
258
+ return { kind: 'group', group: meta.group };
259
+ }
260
+ if (meta.kind === 'root') {
261
+ return { kind: 'root' };
262
+ }
263
+ }
264
+
265
+ const plan = environment.getPlan();
266
+ const groups = new Set();
267
+ for (const def of plan.stepsById.values()) {
268
+ const file = def && def.source ? def.source.file : undefined;
269
+ if (!file) continue;
270
+ const scope = __resolveFileScope(file);
271
+ if (scope.kind === 'group' || scope.kind === 'module') {
272
+ groups.add(scope.group);
273
+ if (groups.size > 1) return { kind: 'ambiguous' };
274
+ }
275
+ }
276
+ const only = Array.from(groups.values())[0];
277
+ return only ? { kind: 'group', group: only } : { kind: 'root' };
278
+ }
279
+
280
+ function selectStepsEnvironment(environments, feature) {
281
+ if (!Array.isArray(environments) || environments.length === 0) {
282
+ return undefined;
283
+ }
284
+ if (environments.length === 1) {
285
+ return environments[0];
286
+ }
287
+
288
+ const featureScope = __resolveFeatureScope(feature);
289
+ const indexed = environments
290
+ .map((env) => ({ env, inferred: __inferEnvironmentGroup(env) }))
291
+ .filter((entry) => entry.inferred.kind !== 'ambiguous');
292
+
293
+ if (featureScope.kind === 'root') {
294
+ const root = indexed.find((entry) => entry.inferred.kind === 'root');
295
+ return (root ? root.env : indexed[0]?.env) ?? environments[0];
296
+ }
297
+
298
+ const match = indexed.find((entry) => entry.inferred.kind === 'group' && entry.inferred.group === featureScope.group);
299
+ return match ? match.env : undefined;
300
+ }
301
+
302
+ function isScenario(element) {
303
+ return Boolean(
304
+ element &&
305
+ typeof element === 'object' &&
306
+ 'steps' in element &&
307
+ !('exampleGroups' in element) &&
308
+ !('elements' in element)
309
+ );
310
+ }
311
+
312
+ function isScenarioOutline(element) {
313
+ return Boolean(
314
+ element &&
315
+ typeof element === 'object' &&
316
+ 'steps' in element &&
317
+ 'exampleGroups' in element
318
+ );
319
+ }
320
+
321
+ function isRule(element) {
322
+ return Boolean(
323
+ element &&
324
+ typeof element === 'object' &&
325
+ 'elements' in element &&
326
+ Array.isArray(element.elements)
327
+ );
328
+ }
329
+
330
+ function createFeatureScopePlan(feature, basePlan) {
331
+ const allSteps = Array.from(basePlan.stepsById.values());
332
+
333
+ function normalizePathSegments(input) {
334
+ return String(input).replace(/\\/g, '/').split('/').filter(Boolean);
335
+ }
336
+
337
+ function startsWithSegments(haystack, needle) {
338
+ if (needle.length > haystack.length) return false;
339
+ for (let i = 0; i < needle.length; i += 1) {
340
+ if (haystack[i] !== needle[i]) return false;
341
+ }
342
+ return true;
343
+ }
344
+
345
+ function resolveFileScope(fileAbs) {
346
+ if (!__AUTOMETA_STEP_SCOPING || !__AUTOMETA_STEP_SCOPING.groups) {
347
+ return { kind: 'root' };
348
+ }
349
+ if (String(fileAbs).startsWith('node:')) {
350
+ return { kind: 'root' };
351
+ }
352
+
353
+ const absoluteFile = __pathIsAbsolute(String(fileAbs))
354
+ ? String(fileAbs)
355
+ : __pathResolve(process.cwd(), String(fileAbs));
356
+
357
+ for (const entry of __AUTOMETA_STEP_SCOPING.groups) {
358
+ const rootAbs = entry.rootAbs;
359
+ const rel = rootAbs ? __pathRelative(rootAbs, absoluteFile) : '';
360
+ if (rel === '' || (!rel.startsWith('..') && !rel.startsWith('../') && !rel.startsWith('..\\'))) {
361
+ const segments = normalizePathSegments(rel);
362
+ const modulePaths = entry.modulePaths || [];
363
+ for (const modulePath of modulePaths) {
364
+ if (startsWithSegments(segments, modulePath)) {
365
+ return { kind: 'module', group: entry.group, modulePath };
366
+ }
367
+ }
368
+ return { kind: 'group', group: entry.group };
369
+ }
370
+ }
371
+ return { kind: 'root' };
372
+ }
373
+
374
+ function parseScopeOverrideTag(tags) {
375
+ if (!Array.isArray(tags)) return undefined;
376
+ for (const tag of tags) {
377
+ const match = String(tag).match(/^@scope(?::|=|\()(.+?)(?:\))?$/u);
378
+ if (!match) continue;
379
+ const raw = String(match[1] ?? '').trim();
380
+ if (!raw) continue;
381
+ const normalized = raw.replace(/\//g, ':');
382
+ const parts = normalized.split(':').filter(Boolean);
383
+ const group = parts[0];
384
+ const rest = parts.slice(1);
385
+ if (!group) continue;
386
+ return rest.length > 0 ? { group, modulePath: rest } : { group };
387
+ }
388
+ return undefined;
389
+ }
390
+
391
+ function resolveFeatureScope() {
392
+ const override = parseScopeOverrideTag(feature.tags);
393
+ if (override) {
394
+ if (override.modulePath && override.modulePath.length > 0) {
395
+ return { kind: 'module', group: override.group, modulePath: override.modulePath };
396
+ }
397
+ return { kind: 'group', group: override.group };
398
+ }
399
+ return resolveFileScope(__AUTOMETA_FEATURE_FILE);
400
+ }
401
+
402
+ function isVisibleStepScope(stepScope, featureScope) {
403
+ if (featureScope.kind === 'root') {
404
+ return stepScope.kind === 'root';
405
+ }
406
+ if (featureScope.kind === 'group') {
407
+ if (stepScope.kind === 'root') return true;
408
+ return stepScope.kind === 'group' && stepScope.group === featureScope.group;
409
+ }
410
+ // module
411
+ if (stepScope.kind === 'root') return true;
412
+ if (stepScope.kind === 'group') return stepScope.group === featureScope.group;
413
+ if (stepScope.kind === 'module') {
414
+ return stepScope.group === featureScope.group && startsWithSegments(featureScope.modulePath, stepScope.modulePath);
415
+ }
416
+ return false;
417
+ }
418
+
419
+ function stepScopeRank(scope) {
420
+ if (scope.kind === 'module') return 200 + (scope.modulePath ? scope.modulePath.length : 0);
421
+ if (scope.kind === 'group') return 100;
422
+ return 0;
423
+ }
424
+
425
+ const useScopedSteps = __AUTOMETA_STEP_SCOPING_MODE === 'scoped' && __AUTOMETA_STEP_SCOPING && __AUTOMETA_STEP_SCOPING.groups;
426
+ const featureVisibilityScope = useScopedSteps ? resolveFeatureScope() : { kind: 'root' };
427
+ const visibleSteps = useScopedSteps
428
+ ? allSteps
429
+ .filter((definition) => {
430
+ const file = definition && definition.source ? definition.source.file : undefined;
431
+ const scope = file ? resolveFileScope(file) : { kind: 'root' };
432
+ return isVisibleStepScope(scope, featureVisibilityScope);
433
+ })
434
+ .sort((a, b) => {
435
+ const aFile = a && a.source ? a.source.file : undefined;
436
+ const bFile = b && b.source ? b.source.file : undefined;
437
+ const aScope = aFile ? resolveFileScope(aFile) : { kind: 'root' };
438
+ const bScope = bFile ? resolveFileScope(bFile) : { kind: 'root' };
439
+ const delta = stepScopeRank(bScope) - stepScopeRank(aScope);
440
+ return delta !== 0 ? delta : String(a.id).localeCompare(String(b.id));
441
+ })
442
+ : allSteps;
443
+
444
+ const scopedStepsById = useScopedSteps
445
+ ? (() => {
446
+ const allowed = new Set(visibleSteps.map((s) => s.id));
447
+ const next = new Map();
448
+ for (const [id, def] of basePlan.stepsById.entries()) {
449
+ if (allowed.has(id)) next.set(id, def);
450
+ }
451
+ return next;
452
+ })()
453
+ : basePlan.stepsById;
454
+ const featureChildren = [];
455
+ const scopesById = new Map(basePlan.scopesById);
456
+
457
+ for (const element of feature.elements ?? []) {
458
+ if (isScenario(element) || isScenarioOutline(element)) {
459
+ const scenarioScope = {
460
+ id: element.id ?? element.name,
461
+ kind: isScenarioOutline(element) ? 'scenarioOutline' : 'scenario',
462
+ name: element.name,
463
+ mode: 'default',
464
+ tags: element.tags ?? [],
465
+ steps: visibleSteps,
466
+ hooks: [],
467
+ children: [],
468
+ pending: false,
469
+ };
470
+ featureChildren.push(scenarioScope);
471
+ scopesById.set(scenarioScope.id, scenarioScope);
472
+ continue;
473
+ }
474
+
475
+ if (isRule(element)) {
476
+ const ruleChildren = [];
477
+ for (const ruleElement of element.elements ?? []) {
478
+ if (isScenario(ruleElement) || isScenarioOutline(ruleElement)) {
479
+ const scenarioScope = {
480
+ id: ruleElement.id ?? ruleElement.name,
481
+ kind: isScenarioOutline(ruleElement) ? 'scenarioOutline' : 'scenario',
482
+ name: ruleElement.name,
483
+ mode: 'default',
484
+ tags: ruleElement.tags ?? [],
485
+ steps: visibleSteps,
486
+ hooks: [],
487
+ children: [],
488
+ pending: false,
489
+ };
490
+ ruleChildren.push(scenarioScope);
491
+ scopesById.set(scenarioScope.id, scenarioScope);
492
+ }
493
+ }
494
+
495
+ const ruleScope = {
496
+ id: element.id ?? element.name,
497
+ kind: 'rule',
498
+ name: element.name,
499
+ mode: 'default',
500
+ tags: element.tags ?? [],
501
+ steps: visibleSteps,
502
+ hooks: [],
503
+ children: ruleChildren,
504
+ pending: false,
505
+ };
506
+ featureChildren.push(ruleScope);
507
+ scopesById.set(ruleScope.id, ruleScope);
508
+ }
509
+ }
510
+
511
+ const featureScope = {
512
+ id: feature.uri ?? feature.name,
513
+ kind: 'feature',
514
+ name: feature.name,
515
+ mode: 'default',
516
+ tags: feature.tags ?? [],
517
+ steps: visibleSteps,
518
+ hooks: [],
519
+ children: featureChildren,
520
+ pending: false,
521
+ };
522
+
523
+ const existingRoot = basePlan.root;
524
+ const updatedRoot = {
525
+ ...existingRoot,
526
+ children: [...existingRoot.children, featureScope],
527
+ };
528
+
529
+ scopesById.set(featureScope.id, featureScope);
530
+ scopesById.set(updatedRoot.id, updatedRoot);
531
+
532
+ const scopePlan = {
533
+ root: updatedRoot,
534
+ stepsById: scopedStepsById,
535
+ hooksById: basePlan.hooksById,
536
+ scopesById,
537
+ };
538
+
539
+ if (basePlan.worldFactory) {
540
+ scopePlan.worldFactory = basePlan.worldFactory;
541
+ }
542
+
543
+ if (basePlan.parameterRegistry) {
544
+ scopePlan.parameterRegistry = basePlan.parameterRegistry;
545
+ }
546
+
547
+ return scopePlan;
548
+ }
549
+
550
+ const gherkin = ${JSON.stringify(code)};
551
+ const feature = parseGherkin(gherkin);
552
+ const environments = collectStepsEnvironments(stepModules);
553
+ const steps = selectStepsEnvironment(environments, feature);
554
+
555
+ if (!steps) {
556
+ throw new Error('Autometa could not find a steps environment for this feature. If you are using per-group environments, ensure each group exports a steps environment ("stepsEnvironment" or default export) under the configured step roots.');
557
+ }
558
+
559
+ CucumberRunner.setSteps(steps);
560
+
561
+ describe(feature.name, () => {
562
+ const basePlan = steps.getPlan();
563
+ const scopedPlan = createFeatureScopePlan(feature, basePlan);
564
+ const { plan, adapter } = coordinateRunnerFeature({
565
+ feature,
566
+ environment: steps,
567
+ config: ${runtimeConfig},
568
+ plan: scopedPlan
569
+ });
570
+
571
+ execute({ plan, adapter, config: ${runtimeConfig} });
572
+ });
573
+ `,
574
+ map: null
575
+ };
576
+ }
577
+ };
578
+ }
579
+ function buildStepGlobs(entries, options) {
580
+ if (!entries || entries.length === 0) {
581
+ return [];
582
+ }
583
+ const patterns = /* @__PURE__ */ new Set();
584
+ for (const entry of entries) {
585
+ const normalized = entry.trim();
586
+ if (!normalized) {
587
+ continue;
588
+ }
589
+ for (const candidate of toPatterns(normalized, STEP_FALLBACK_GLOB)) {
590
+ const absolute = isAbsolute(candidate) ? normalizeSlashes(candidate) : normalizeSlashes(resolve(options.configDir, candidate));
591
+ const rootRelative = toRootRelativeGlob(absolute, options.projectRoot);
592
+ patterns.add(rootRelative);
593
+ }
594
+ }
595
+ return Array.from(patterns);
596
+ }
597
+ function toPatterns(entry, fallbackGlob) {
598
+ if (hasGlobMagic(entry) || hasFileExtension(entry)) {
599
+ return [entry];
600
+ }
601
+ return [appendGlob(entry, fallbackGlob)];
602
+ }
603
+ function hasGlobMagic(input) {
604
+ return /[*?{}()[\]!,@+]/u.test(input);
605
+ }
606
+ function hasFileExtension(input) {
607
+ const normalized = normalizeSlashes(input);
608
+ const trimmed = normalized === "/" ? normalized : normalized.replace(/\/+$/u, "");
609
+ if (!trimmed || trimmed === "." || trimmed === "..") {
610
+ return false;
611
+ }
612
+ return Boolean(extname(trimmed));
613
+ }
614
+ function appendGlob(entry, glob) {
615
+ const normalized = normalizeSlashes(entry);
616
+ const trimmed = normalized === "/" ? normalized : normalized.replace(/\/+$/u, "");
617
+ if (!trimmed || trimmed === ".") {
618
+ return glob;
619
+ }
620
+ if (trimmed === "/") {
621
+ return `/${glob}`;
622
+ }
623
+ return `${trimmed}/${glob}`;
624
+ }
625
+ function toRootRelativeGlob(pattern, rootDir) {
626
+ const normalizedRoot = normalizeSlashes(rootDir);
627
+ const normalizedPattern = normalizeSlashes(pattern);
628
+ let relativePattern = normalizeSlashes(relative(normalizedRoot, normalizedPattern));
629
+ if (!relativePattern || relativePattern === ".") {
630
+ relativePattern = "";
631
+ }
632
+ if (!relativePattern || relativePattern.startsWith("..")) {
633
+ return ensureGlobPrefix(normalizedPattern);
634
+ }
635
+ return ensureGlobPrefix(relativePattern);
636
+ }
637
+ function ensureGlobPrefix(pattern) {
638
+ if (/^[A-Za-z]:\//u.test(pattern)) {
639
+ return pattern;
640
+ }
641
+ if (pattern.startsWith("/")) {
642
+ return pattern;
643
+ }
644
+ return `/${pattern.replace(/^\/+/, "")}`;
645
+ }
646
+ function normalizeSlashes(pathname) {
647
+ return pathname.replace(/\\/gu, "/");
648
+ }
649
+ async function loadAutometaConfig(root) {
650
+ const _jiti = jiti(root, { interopDefault: true });
651
+ const candidates = [
652
+ "autometa.config.ts",
653
+ "autometa.config.js",
654
+ "autometa.config.mts",
655
+ "autometa.config.mjs",
656
+ "autometa.config.cts",
657
+ "autometa.config.cjs"
658
+ ];
659
+ for (const candidate of candidates) {
660
+ const path = resolve(root, candidate);
661
+ if (!existsSync(path)) {
662
+ continue;
663
+ }
664
+ try {
665
+ const mod = _jiti(path);
666
+ const config = mod.default || mod;
667
+ if (isConfig(config)) {
668
+ return { config, path };
669
+ } else {
670
+ console.warn(`Found config at ${path} but it does not export a Config instance.`);
671
+ }
672
+ } catch (error) {
673
+ console.error(`Error loading config at ${path}:`, error);
674
+ throw error;
675
+ }
676
+ }
677
+ throw new Error("Could not find autometa.config.{ts,js,mjs,cjs} in " + root);
678
+ }
679
+ function isConfig(config) {
680
+ return typeof config === "object" && config !== null && "resolve" in config && typeof config.resolve === "function" && "current" in config && typeof config.current === "function";
681
+ }
682
+
683
+ export { autometa };
684
+ //# sourceMappingURL=out.js.map
685
+ //# sourceMappingURL=index.js.map