@autometa/vitest-plugins 1.0.0-rc.3 → 1.0.0-rc.4
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.cjs +164 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +164 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -10,6 +10,7 @@ var jiti__default = /*#__PURE__*/_interopDefault(jiti);
|
|
|
10
10
|
|
|
11
11
|
// src/index.ts
|
|
12
12
|
var STEP_FALLBACK_GLOB = "**/*.{ts,tsx,js,jsx,mjs,cjs,mts,cts}";
|
|
13
|
+
var FEATURE_FALLBACK_GLOB = "**/*.feature";
|
|
13
14
|
function flattenModuleDeclarations(declarations, prefix = []) {
|
|
14
15
|
if (!declarations || declarations.length === 0) {
|
|
15
16
|
return [];
|
|
@@ -88,8 +89,14 @@ function autometa() {
|
|
|
88
89
|
});
|
|
89
90
|
const runtimeConfig = JSON.stringify(resolved.config);
|
|
90
91
|
const stepScopingMode = resolved.config.modules?.stepScoping ?? "global";
|
|
92
|
+
const hoistedFeatureScopingMode = resolved.config.modules?.hoistedFeatures?.scope ?? "tag";
|
|
93
|
+
const hoistedFeatureScopingStrict = resolved.config.modules?.hoistedFeatures?.strict ?? true;
|
|
91
94
|
const groupIndexData = buildStepScopingData(resolved.config, rootDir);
|
|
92
95
|
const stepScopingData = stepScopingMode === "scoped" ? groupIndexData : null;
|
|
96
|
+
const featureRootDirs = buildFeatureRootDirs(resolved.config.roots.features, {
|
|
97
|
+
configDir,
|
|
98
|
+
projectRoot: rootDir
|
|
99
|
+
});
|
|
93
100
|
const featureFile = cleanId;
|
|
94
101
|
return {
|
|
95
102
|
code: String.raw`
|
|
@@ -103,6 +110,9 @@ function autometa() {
|
|
|
103
110
|
const __AUTOMETA_GROUP_INDEX = ${JSON.stringify(groupIndexData)};
|
|
104
111
|
const __AUTOMETA_STEP_SCOPING = ${JSON.stringify(stepScopingData)};
|
|
105
112
|
const __AUTOMETA_FEATURE_FILE = ${JSON.stringify(featureFile)};
|
|
113
|
+
const __AUTOMETA_HOISTED_FEATURE_SCOPING_MODE = ${JSON.stringify(hoistedFeatureScopingMode)};
|
|
114
|
+
const __AUTOMETA_HOISTED_FEATURE_SCOPING_STRICT = ${JSON.stringify(hoistedFeatureScopingStrict)};
|
|
115
|
+
const __AUTOMETA_HOISTED_FEATURE_ROOTS = ${JSON.stringify(featureRootDirs)};
|
|
106
116
|
|
|
107
117
|
const __AUTOMETA_EVENT_MODULES = ${eventGlobs.length > 0 ? `import.meta.glob(${JSON.stringify(eventGlobs)}, { eager: true })` : "{}"};
|
|
108
118
|
const stepModules = import.meta.glob(${JSON.stringify(stepGlobs)}, { eager: true });
|
|
@@ -198,6 +208,107 @@ function autometa() {
|
|
|
198
208
|
return true;
|
|
199
209
|
}
|
|
200
210
|
|
|
211
|
+
function __isUnderRoot(fileAbs, rootAbs) {
|
|
212
|
+
const rel = rootAbs ? __pathRelative(String(rootAbs), String(fileAbs)) : '';
|
|
213
|
+
return rel === '' || (!rel.startsWith('..') && !rel.startsWith('../') && !rel.startsWith('..\\'));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function __selectHoistedFeatureRoot(fileAbs) {
|
|
217
|
+
if (!Array.isArray(__AUTOMETA_HOISTED_FEATURE_ROOTS) || __AUTOMETA_HOISTED_FEATURE_ROOTS.length === 0) {
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const absoluteFile = __pathIsAbsolute(String(fileAbs))
|
|
222
|
+
? String(fileAbs)
|
|
223
|
+
: __pathResolve(process.cwd(), String(fileAbs));
|
|
224
|
+
|
|
225
|
+
let best;
|
|
226
|
+
for (const rootAbs of __AUTOMETA_HOISTED_FEATURE_ROOTS) {
|
|
227
|
+
if (!rootAbs) continue;
|
|
228
|
+
if (__isUnderRoot(absoluteFile, rootAbs)) {
|
|
229
|
+
if (!best || String(rootAbs).length > String(best).length) {
|
|
230
|
+
best = rootAbs;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return best;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function __resolveHoistedDirectoryScope(fileAbs, groupIndexData) {
|
|
238
|
+
if (!groupIndexData || !Array.isArray(groupIndexData.groups)) {
|
|
239
|
+
return { kind: 'root' };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const rootAbs = __selectHoistedFeatureRoot(fileAbs);
|
|
243
|
+
if (!rootAbs) {
|
|
244
|
+
return { kind: 'root' };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const absoluteFile = __pathIsAbsolute(String(fileAbs))
|
|
248
|
+
? String(fileAbs)
|
|
249
|
+
: __pathResolve(process.cwd(), String(fileAbs));
|
|
250
|
+
|
|
251
|
+
const rel = __pathRelative(String(rootAbs), String(absoluteFile));
|
|
252
|
+
const segments = __normalizePathSegments(rel);
|
|
253
|
+
const dirSegments = segments.slice(0, -1);
|
|
254
|
+
if (dirSegments.length === 0) {
|
|
255
|
+
return { kind: 'root' };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const group = dirSegments[0];
|
|
259
|
+
if (!group) {
|
|
260
|
+
return { kind: 'root' };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const groupEntry = groupIndexData.groups.find((entry) => entry && entry.group === group);
|
|
264
|
+
if (!groupEntry) {
|
|
265
|
+
if (__AUTOMETA_HOISTED_FEATURE_SCOPING_STRICT) {
|
|
266
|
+
throw new Error(
|
|
267
|
+
'[autometa] Feature "' +
|
|
268
|
+
absoluteFile +
|
|
269
|
+
'" is under "' +
|
|
270
|
+
rootAbs +
|
|
271
|
+
'" and implies group "' +
|
|
272
|
+
group +
|
|
273
|
+
'", but "' +
|
|
274
|
+
group +
|
|
275
|
+
'" is not declared in config.modules.groups.'
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
return { kind: 'root' };
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const moduleSegments = dirSegments.slice(1);
|
|
282
|
+
if (moduleSegments.length === 0) {
|
|
283
|
+
return { kind: 'group', group };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const modulePaths = groupEntry.modulePaths || [];
|
|
287
|
+
for (const modulePath of modulePaths) {
|
|
288
|
+
if (__startsWithSegments(moduleSegments, modulePath)) {
|
|
289
|
+
return { kind: 'module', group, modulePath };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (__AUTOMETA_HOISTED_FEATURE_SCOPING_STRICT) {
|
|
294
|
+
throw new Error(
|
|
295
|
+
'[autometa] Feature "' +
|
|
296
|
+
absoluteFile +
|
|
297
|
+
'" is under "' +
|
|
298
|
+
rootAbs +
|
|
299
|
+
'" and implies module "' +
|
|
300
|
+
group +
|
|
301
|
+
':' +
|
|
302
|
+
moduleSegments.join(':') +
|
|
303
|
+
'", but no matching module is declared in config.modules.groups.' +
|
|
304
|
+
group +
|
|
305
|
+
'.modules.'
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return { kind: 'group', group };
|
|
310
|
+
}
|
|
311
|
+
|
|
201
312
|
function __resolveFileScope(fileAbs) {
|
|
202
313
|
if (!__AUTOMETA_GROUP_INDEX || !__AUTOMETA_GROUP_INDEX.groups) {
|
|
203
314
|
return { kind: 'root' };
|
|
@@ -252,7 +363,14 @@ function autometa() {
|
|
|
252
363
|
}
|
|
253
364
|
return { kind: 'group', group: override.group };
|
|
254
365
|
}
|
|
255
|
-
|
|
366
|
+
const fileScope = __resolveFileScope(__AUTOMETA_FEATURE_FILE);
|
|
367
|
+
if (fileScope.kind !== 'root') {
|
|
368
|
+
return fileScope;
|
|
369
|
+
}
|
|
370
|
+
if (__AUTOMETA_HOISTED_FEATURE_SCOPING_MODE !== 'directory') {
|
|
371
|
+
return fileScope;
|
|
372
|
+
}
|
|
373
|
+
return __resolveHoistedDirectoryScope(__AUTOMETA_FEATURE_FILE, __AUTOMETA_GROUP_INDEX);
|
|
256
374
|
}
|
|
257
375
|
|
|
258
376
|
function __inferEnvironmentGroup(environment) {
|
|
@@ -402,7 +520,14 @@ function autometa() {
|
|
|
402
520
|
}
|
|
403
521
|
return { kind: 'group', group: override.group };
|
|
404
522
|
}
|
|
405
|
-
|
|
523
|
+
const fileScope = resolveFileScope(__AUTOMETA_FEATURE_FILE);
|
|
524
|
+
if (fileScope.kind !== 'root') {
|
|
525
|
+
return fileScope;
|
|
526
|
+
}
|
|
527
|
+
if (__AUTOMETA_HOISTED_FEATURE_SCOPING_MODE !== 'directory') {
|
|
528
|
+
return fileScope;
|
|
529
|
+
}
|
|
530
|
+
return __resolveHoistedDirectoryScope(__AUTOMETA_FEATURE_FILE, __AUTOMETA_STEP_SCOPING);
|
|
406
531
|
}
|
|
407
532
|
|
|
408
533
|
function isVisibleStepScope(stepScope, featureScope) {
|
|
@@ -582,6 +707,24 @@ function autometa() {
|
|
|
582
707
|
}
|
|
583
708
|
};
|
|
584
709
|
}
|
|
710
|
+
function buildFeatureRootDirs(entries, options) {
|
|
711
|
+
if (!entries || entries.length === 0) {
|
|
712
|
+
return [];
|
|
713
|
+
}
|
|
714
|
+
const roots = /* @__PURE__ */ new Set();
|
|
715
|
+
for (const entry of entries) {
|
|
716
|
+
const normalized = entry.trim();
|
|
717
|
+
if (!normalized) {
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
for (const candidate of toPatterns(normalized, FEATURE_FALLBACK_GLOB)) {
|
|
721
|
+
const base = inferGlobBaseDirectory(candidate);
|
|
722
|
+
const absolute = path.isAbsolute(base) ? normalizeSlashes(base) : normalizeSlashes(path.resolve(options.configDir, base));
|
|
723
|
+
roots.add(absolute.replace(/\/+$/u, ""));
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
return Array.from(roots).sort((a, b) => b.length - a.length);
|
|
727
|
+
}
|
|
585
728
|
function buildStepGlobs(entries, options) {
|
|
586
729
|
if (!entries || entries.length === 0) {
|
|
587
730
|
return [];
|
|
@@ -606,6 +749,25 @@ function toPatterns(entry, fallbackGlob) {
|
|
|
606
749
|
}
|
|
607
750
|
return [appendGlob(entry, fallbackGlob)];
|
|
608
751
|
}
|
|
752
|
+
function inferGlobBaseDirectory(pattern) {
|
|
753
|
+
const normalized = normalizeSlashes(pattern).replace(/^!/u, "");
|
|
754
|
+
for (let i = 0; i < normalized.length; i += 1) {
|
|
755
|
+
if (!hasGlobMagic(normalized[i] ?? "")) {
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
const prefix = normalized.slice(0, i);
|
|
759
|
+
const lastSlash = prefix.lastIndexOf("/");
|
|
760
|
+
if (lastSlash <= 0) {
|
|
761
|
+
return ".";
|
|
762
|
+
}
|
|
763
|
+
const base = prefix.slice(0, lastSlash);
|
|
764
|
+
return base || ".";
|
|
765
|
+
}
|
|
766
|
+
if (hasFileExtension(normalized)) {
|
|
767
|
+
return path.dirname(normalized);
|
|
768
|
+
}
|
|
769
|
+
return normalized || ".";
|
|
770
|
+
}
|
|
609
771
|
function hasGlobMagic(input) {
|
|
610
772
|
return /[*?{}()[\]!,@+]/u.test(input);
|
|
611
773
|
}
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["next"],"mappings":";AAAA,SAAS,SAAS,YAAY,UAAU,SAAS,eAAe;AAChE,SAAS,kBAAkB;AAG3B,OAAO,UAAU;AAEjB,IAAM,qBAAqB;AAM3B,SAAS,0BACP,cACA,SAA4B,CAAC,GACG;AAChC,MAAI,CAAC,gBAAgB,aAAa,WAAW,GAAG;AAC9C,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,UAAsB,CAAC;AAE7B,aAAW,SAAS,cAAc;AAChC,QAAI,OAAO,UAAU,UAAU;AAC7B,YAAMA,QAAO,CAAC,GAAG,QAAQ,KAAK;AAC9B,cAAQ,KAAKA,KAAI;AACjB;AAAA,IACF;AAEA,UAAM,OAAO,CAAC,GAAG,QAAQ,MAAM,IAAI;AACnC,YAAQ,KAAK,IAAI;AAEjB,UAAM,SAAS,0BAA0B,MAAM,cAAc,QAAW,IAAI;AAC5E,eAAW,QAAQ,QAAQ;AACzB,cAAQ,KAAK,CAAC,GAAG,IAAI,CAAC;AAAA,IACxB;AAAA,EACF;AAEA,QAAM,SAAS,oBAAI,IAA+B;AAClD,aAAW,QAAQ,SAAS;AAC1B,WAAO,IAAI,KAAK,KAAK,GAAG,GAAG,IAAI;AAAA,EACjC;AAEA,SAAO,MAAM,KAAK,OAAO,OAAO,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM;AACvE;AAEA,SAAS,qBAAqB,gBAAyB,aAA8B;AACnF,QAAM,SAAU,gBAA8D,SAAS;AACvF,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,OAAO,QAAQ,MAAiC,EAAE,IAAI,CAAC,CAAC,OAAO,WAAW,MAAM;AAC9F,UAAM,eACJ,eAAe,OAAO,gBAAgB,WAAY,cAA0C,CAAC;AAC/F,UAAM,UAAU,QAAQ,aAAa,OAAO,aAAa,QAAQ,EAAE,CAAC;AACpE,UAAM,UAAU,aAAa;AAC7B,UAAM,cAAc,MAAM,QAAQ,OAAO,IACrC,0BAA0B,OAA8B,IACxD,CAAC;AACL,WAAO,EAAE,OAAO,SAAS,YAAY;AAAA,EACvC,CAAC;AAED,SAAO,EAAE,QAAQ,QAAQ;AAC3B;AAEO,SAAS,WAAmB;AACjC,MAAI;AACJ,MAAI;AACJ,MAAI;AAEJ,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IACT,MAAM,eAAe,YAAY;AAC/B,oBAAc,WAAW,QAAQ,QAAQ,IAAI;AAC7C,YAAM,SAAS,MAAM,mBAAmB,WAAW;AACnD,uBAAiB,OAAO;AACxB,mBAAa,OAAO;AAAA,IACtB;AAAA,IACA,UAAU,MAAM,IAAI;AAClB,YAAM,UAAU,GAAG,MAAM,KAAK,CAAC,EAAE,CAAC,KAAK;AAEvC,UAAI,CAAC,QAAQ,SAAS,UAAU,GAAG;AACjC;AAAA,MACF;AAEA,UAAI,CAAC,gBAAgB;AACnB,cAAM,IAAI,MAAM,2BAA2B;AAAA,MAC7C;AAEA,YAAM,WAAW,eAAe,QAAQ;AACxC,YAAM,YAAY,SAAS,OAAO,MAAM;AACxC,YAAM,YAAY,aAAa,QAAQ,UAAU,IAAI,eAAe,QAAQ,IAAI;AAChF,YAAM,UAAU,eAAe,QAAQ,IAAI;AAC3C,YAAM,YAAY,eAAe,WAAW;AAAA,QAC1C;AAAA,QACA,aAAa;AAAA,MACf,CAAC;AACD,UAAI,UAAU,WAAW,GAAG;AAC1B,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AAEA,YAAM,aAAa,eAAe,SAAS,OAAO,QAAQ;AAAA,QACxD;AAAA,QACA,aAAa;AAAA,MACf,CAAC;AAED,YAAM,gBAAgB,KAAK,UAAU,SAAS,MAAM;AACpD,YAAM,kBAAkB,SAAS,OAAO,SAAS,eAAe;AAIhE,YAAM,iBAAiB,qBAAqB,SAAS,QAAQ,OAAO;AACpE,YAAM,kBAAkB,oBAAoB,WAAW,iBAAiB;AAExE,YAAM,cAAc;AAEpB,aAAO;AAAA,QACL,MAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mDAO8B,KAAK,UAAU,eAAe,CAAC;AAAA,6CACrC,KAAK,UAAU,cAAc,CAAC;AAAA,8CAC7B,KAAK,UAAU,eAAe,CAAC;AAAA,8CAC/B,KAAK,UAAU,WAAW,CAAC;AAAA;AAAA,+CAE1B,WAAW,SAAS,IACnD,oBAAoB,KAAK,UAAU,UAAU,CAAC,uBAC9C,IAAI;AAAA,mDAC+B,KAAK,UAAU,SAAS,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","sourcesContent":["import { resolve, isAbsolute, relative, dirname, extname } from \"path\";\nimport { existsSync } from \"fs\";\nimport type { Plugin } from \"vite\";\nimport type { Config } from \"@autometa/config\";\nimport jiti from \"jiti\";\n\nconst STEP_FALLBACK_GLOB = \"**/*.{ts,tsx,js,jsx,mjs,cjs,mts,cts}\";\n\ntype ModuleDeclaration =\n | string\n | { readonly name: string; readonly submodules?: readonly ModuleDeclaration[] | undefined };\n\nfunction flattenModuleDeclarations(\n declarations: readonly ModuleDeclaration[] | undefined,\n prefix: readonly string[] = []\n): readonly (readonly string[])[] {\n if (!declarations || declarations.length === 0) {\n return [];\n }\n\n const results: string[][] = [];\n\n for (const entry of declarations) {\n if (typeof entry === \"string\") {\n const next = [...prefix, entry];\n results.push(next);\n continue;\n }\n\n const next = [...prefix, entry.name];\n results.push(next);\n\n const nested = flattenModuleDeclarations(entry.submodules ?? undefined, next);\n for (const path of nested) {\n results.push([...path]);\n }\n }\n\n const unique = new Map<string, readonly string[]>();\n for (const path of results) {\n unique.set(path.join(\"/\"), path);\n }\n\n return Array.from(unique.values()).sort((a, b) => b.length - a.length);\n}\n\nfunction buildStepScopingData(resolvedConfig: unknown, projectRoot: string): unknown {\n const groups = (resolvedConfig as { modules?: { groups?: unknown } } | null)?.modules?.groups;\n if (!groups || typeof groups !== \"object\") {\n return null;\n }\n\n const entries = Object.entries(groups as Record<string, unknown>).map(([group, groupConfig]) => {\n const configRecord =\n groupConfig && typeof groupConfig === \"object\" ? (groupConfig as Record<string, unknown>) : {};\n const rootAbs = resolve(projectRoot, String(configRecord.root ?? \"\"));\n const modules = configRecord.modules;\n const modulePaths = Array.isArray(modules)\n ? flattenModuleDeclarations(modules as ModuleDeclaration[])\n : [];\n return { group, rootAbs, modulePaths };\n });\n\n return { groups: entries };\n}\n\nexport function autometa(): Plugin {\n let autometaConfig: Config | undefined;\n let configPath: string | undefined;\n let projectRoot: string | undefined;\n\n return {\n name: \"autometa-vitest-plugin\",\n enforce: \"pre\",\n async configResolved(viteConfig) {\n projectRoot = viteConfig.root || process.cwd();\n const loaded = await loadAutometaConfig(projectRoot);\n autometaConfig = loaded.config;\n configPath = loaded.path;\n },\n transform(code, id) {\n const cleanId = id.split(\"?\", 1)[0] ?? id;\n\n if (!cleanId.endsWith(\".feature\")) {\n return;\n }\n\n if (!autometaConfig) {\n throw new Error(\"Autometa config not found\");\n }\n\n const resolved = autometaConfig.resolve();\n const stepRoots = resolved.config.roots.steps;\n const configDir = configPath ? dirname(configPath) : projectRoot ?? process.cwd();\n const rootDir = projectRoot ?? process.cwd();\n const stepGlobs = buildStepGlobs(stepRoots, {\n configDir,\n projectRoot: rootDir,\n });\n if (stepGlobs.length === 0) {\n throw new Error(\n \"Autometa config did not resolve any step files within the current project root.\"\n );\n }\n\n const eventGlobs = buildStepGlobs(resolved.config.events, {\n configDir,\n projectRoot: rootDir,\n });\n\n const runtimeConfig = JSON.stringify(resolved.config);\n const stepScopingMode = resolved.config.modules?.stepScoping ?? \"global\";\n\n // Group/module index data is useful even when step scoping is disabled,\n // since we may need it to select the correct steps environment.\n const groupIndexData = buildStepScopingData(resolved.config, rootDir);\n const stepScopingData = stepScopingMode === \"scoped\" ? groupIndexData : null;\n\n const featureFile = cleanId;\n\n return {\n code: String.raw`\n import { describe } from 'vitest';\n import { execute } from '@autometa/vitest-executor';\n import { coordinateRunnerFeature, CucumberRunner, STEPS_ENVIRONMENT_META } from '@autometa/runner';\n import { parseGherkin } from '@autometa/gherkin';\n import { relative as __pathRelative, resolve as __pathResolve, isAbsolute as __pathIsAbsolute } from 'node:path';\n\n const __AUTOMETA_STEP_SCOPING_MODE = ${JSON.stringify(stepScopingMode)};\n const __AUTOMETA_GROUP_INDEX = ${JSON.stringify(groupIndexData)};\n const __AUTOMETA_STEP_SCOPING = ${JSON.stringify(stepScopingData)};\n const __AUTOMETA_FEATURE_FILE = ${JSON.stringify(featureFile)};\n\n const __AUTOMETA_EVENT_MODULES = ${eventGlobs.length > 0\n ? `import.meta.glob(${JSON.stringify(eventGlobs)}, { eager: true })`\n : \"{}\"};\n const stepModules = import.meta.glob(${JSON.stringify(stepGlobs)}, { eager: true });\n\n function collectCandidateModules(imported) {\n if (!imported || typeof imported !== 'object') {\n return [];\n }\n\n const modules = new Set();\n modules.add(imported);\n\n const exportedModules = imported.modules;\n if (Array.isArray(exportedModules)) {\n for (const entry of exportedModules) {\n if (entry && typeof entry === 'object') {\n modules.add(entry);\n }\n }\n }\n\n const defaultExport = imported.default;\n if (Array.isArray(defaultExport)) {\n for (const entry of defaultExport) {\n if (entry && typeof entry === 'object') {\n modules.add(entry);\n }\n }\n } else if (defaultExport && typeof defaultExport === 'object') {\n modules.add(defaultExport);\n }\n\n return Array.from(modules);\n }\n\n function isStepsEnvironment(candidate) {\n return Boolean(\n candidate &&\n typeof candidate === 'object' &&\n typeof candidate.coordinateFeature === 'function' &&\n typeof candidate.getPlan === 'function' &&\n typeof candidate.Given === 'function' &&\n typeof candidate.When === 'function' &&\n typeof candidate.Then === 'function'\n );\n }\n\n function extractStepsEnvironments(candidate) {\n const environments = [];\n\n if (!candidate || typeof candidate !== 'object') {\n return environments;\n }\n\n if (isStepsEnvironment(candidate)) {\n environments.push(candidate);\n }\n\n const stepsEnv = candidate.stepsEnvironment;\n if (isStepsEnvironment(stepsEnv)) {\n environments.push(stepsEnv);\n }\n\n const defaultExport = candidate.default;\n if (isStepsEnvironment(defaultExport)) {\n environments.push(defaultExport);\n }\n\n return environments;\n }\n\n function collectStepsEnvironments(modules) {\n const environments = new Set();\n for (const moduleExports of Object.values(modules)) {\n for (const candidate of collectCandidateModules(moduleExports)) {\n for (const env of extractStepsEnvironments(candidate)) {\n environments.add(env);\n }\n }\n }\n return Array.from(environments);\n }\n\n function __normalizePathSegments(input) {\n return String(input).replace(/\\\\/g, '/').split('/').filter(Boolean);\n }\n\n function __startsWithSegments(haystack, needle) {\n if (needle.length > haystack.length) return false;\n for (let i = 0; i < needle.length; i += 1) {\n if (haystack[i] !== needle[i]) return false;\n }\n return true;\n }\n\n function __resolveFileScope(fileAbs) {\n if (!__AUTOMETA_GROUP_INDEX || !__AUTOMETA_GROUP_INDEX.groups) {\n return { kind: 'root' };\n }\n if (String(fileAbs).startsWith('node:')) {\n return { kind: 'root' };\n }\n\n const absoluteFile = __pathIsAbsolute(String(fileAbs))\n ? String(fileAbs)\n : __pathResolve(process.cwd(), String(fileAbs));\n\n for (const entry of __AUTOMETA_GROUP_INDEX.groups) {\n const rootAbs = entry.rootAbs;\n const rel = rootAbs ? __pathRelative(rootAbs, absoluteFile) : '';\n if (rel === '' || (!rel.startsWith('..') && !rel.startsWith('../') && !rel.startsWith('..\\\\'))) {\n const segments = __normalizePathSegments(rel);\n const modulePaths = entry.modulePaths || [];\n for (const modulePath of modulePaths) {\n if (__startsWithSegments(segments, modulePath)) {\n return { kind: 'module', group: entry.group, modulePath };\n }\n }\n return { kind: 'group', group: entry.group };\n }\n }\n return { kind: 'root' };\n }\n\n function __parseScopeOverrideTag(tags) {\n if (!Array.isArray(tags)) return undefined;\n for (const tag of tags) {\n const match = String(tag).match(/^@scope(?::|=|\\()(.+?)(?:\\))?$/u);\n if (!match) continue;\n const raw = String(match[1] ?? '').trim();\n if (!raw) continue;\n const normalized = raw.replace(/\\//g, ':');\n const parts = normalized.split(':').filter(Boolean);\n const group = parts[0];\n const rest = parts.slice(1);\n if (!group) continue;\n return rest.length > 0 ? { group, modulePath: rest } : { group };\n }\n return undefined;\n }\n\n function __resolveFeatureScope(feature) {\n const override = __parseScopeOverrideTag(feature.tags);\n if (override) {\n if (override.modulePath && override.modulePath.length > 0) {\n return { kind: 'module', group: override.group, modulePath: override.modulePath };\n }\n return { kind: 'group', group: override.group };\n }\n return __resolveFileScope(__AUTOMETA_FEATURE_FILE);\n }\n\n function __inferEnvironmentGroup(environment) {\n const meta = environment && typeof environment === 'object'\n ? environment[STEPS_ENVIRONMENT_META]\n : undefined;\n if (meta && typeof meta === 'object') {\n if (meta.kind === 'group' && typeof meta.group === 'string' && meta.group.trim().length > 0) {\n return { kind: 'group', group: meta.group };\n }\n if (meta.kind === 'root') {\n return { kind: 'root' };\n }\n }\n\n const plan = environment.getPlan();\n const groups = new Set();\n for (const def of plan.stepsById.values()) {\n const file = def && def.source ? def.source.file : undefined;\n if (!file) continue;\n const scope = __resolveFileScope(file);\n if (scope.kind === 'group' || scope.kind === 'module') {\n groups.add(scope.group);\n if (groups.size > 1) return { kind: 'ambiguous' };\n }\n }\n const only = Array.from(groups.values())[0];\n return only ? { kind: 'group', group: only } : { kind: 'root' };\n }\n\n function selectStepsEnvironment(environments, feature) {\n if (!Array.isArray(environments) || environments.length === 0) {\n return undefined;\n }\n if (environments.length === 1) {\n return environments[0];\n }\n\n const featureScope = __resolveFeatureScope(feature);\n const indexed = environments\n .map((env) => ({ env, inferred: __inferEnvironmentGroup(env) }))\n .filter((entry) => entry.inferred.kind !== 'ambiguous');\n\n if (featureScope.kind === 'root') {\n const root = indexed.find((entry) => entry.inferred.kind === 'root');\n return (root ? root.env : indexed[0]?.env) ?? environments[0];\n }\n\n const match = indexed.find((entry) => entry.inferred.kind === 'group' && entry.inferred.group === featureScope.group);\n return match ? match.env : undefined;\n }\n\n function isScenario(element) {\n return Boolean(\n element &&\n typeof element === 'object' &&\n 'steps' in element &&\n !('exampleGroups' in element) &&\n !('elements' in element)\n );\n }\n\n function isScenarioOutline(element) {\n return Boolean(\n element &&\n typeof element === 'object' &&\n 'steps' in element &&\n 'exampleGroups' in element\n );\n }\n\n function isRule(element) {\n return Boolean(\n element &&\n typeof element === 'object' &&\n 'elements' in element &&\n Array.isArray(element.elements)\n );\n }\n\n function createFeatureScopePlan(feature, basePlan) {\n const allSteps = Array.from(basePlan.stepsById.values());\n\n function normalizePathSegments(input) {\n return String(input).replace(/\\\\/g, '/').split('/').filter(Boolean);\n }\n\n function startsWithSegments(haystack, needle) {\n if (needle.length > haystack.length) return false;\n for (let i = 0; i < needle.length; i += 1) {\n if (haystack[i] !== needle[i]) return false;\n }\n return true;\n }\n\n function resolveFileScope(fileAbs) {\n if (!__AUTOMETA_STEP_SCOPING || !__AUTOMETA_STEP_SCOPING.groups) {\n return { kind: 'root' };\n }\n if (String(fileAbs).startsWith('node:')) {\n return { kind: 'root' };\n }\n\n const absoluteFile = __pathIsAbsolute(String(fileAbs))\n ? String(fileAbs)\n : __pathResolve(process.cwd(), String(fileAbs));\n\n for (const entry of __AUTOMETA_STEP_SCOPING.groups) {\n const rootAbs = entry.rootAbs;\n const rel = rootAbs ? __pathRelative(rootAbs, absoluteFile) : '';\n if (rel === '' || (!rel.startsWith('..') && !rel.startsWith('../') && !rel.startsWith('..\\\\'))) {\n const segments = normalizePathSegments(rel);\n const modulePaths = entry.modulePaths || [];\n for (const modulePath of modulePaths) {\n if (startsWithSegments(segments, modulePath)) {\n return { kind: 'module', group: entry.group, modulePath };\n }\n }\n return { kind: 'group', group: entry.group };\n }\n }\n return { kind: 'root' };\n }\n\n function parseScopeOverrideTag(tags) {\n if (!Array.isArray(tags)) return undefined;\n for (const tag of tags) {\n const match = String(tag).match(/^@scope(?::|=|\\()(.+?)(?:\\))?$/u);\n if (!match) continue;\n const raw = String(match[1] ?? '').trim();\n if (!raw) continue;\n const normalized = raw.replace(/\\//g, ':');\n const parts = normalized.split(':').filter(Boolean);\n const group = parts[0];\n const rest = parts.slice(1);\n if (!group) continue;\n return rest.length > 0 ? { group, modulePath: rest } : { group };\n }\n return undefined;\n }\n\n function resolveFeatureScope() {\n const override = parseScopeOverrideTag(feature.tags);\n if (override) {\n if (override.modulePath && override.modulePath.length > 0) {\n return { kind: 'module', group: override.group, modulePath: override.modulePath };\n }\n return { kind: 'group', group: override.group };\n }\n return resolveFileScope(__AUTOMETA_FEATURE_FILE);\n }\n\n function isVisibleStepScope(stepScope, featureScope) {\n if (featureScope.kind === 'root') {\n return stepScope.kind === 'root';\n }\n if (featureScope.kind === 'group') {\n if (stepScope.kind === 'root') return true;\n return stepScope.kind === 'group' && stepScope.group === featureScope.group;\n }\n // module\n if (stepScope.kind === 'root') return true;\n if (stepScope.kind === 'group') return stepScope.group === featureScope.group;\n if (stepScope.kind === 'module') {\n return stepScope.group === featureScope.group && startsWithSegments(featureScope.modulePath, stepScope.modulePath);\n }\n return false;\n }\n\n function stepScopeRank(scope) {\n if (scope.kind === 'module') return 200 + (scope.modulePath ? scope.modulePath.length : 0);\n if (scope.kind === 'group') return 100;\n return 0;\n }\n\n const useScopedSteps = __AUTOMETA_STEP_SCOPING_MODE === 'scoped' && __AUTOMETA_STEP_SCOPING && __AUTOMETA_STEP_SCOPING.groups;\n const featureVisibilityScope = useScopedSteps ? resolveFeatureScope() : { kind: 'root' };\n const visibleSteps = useScopedSteps\n ? allSteps\n .filter((definition) => {\n const file = definition && definition.source ? definition.source.file : undefined;\n const scope = file ? resolveFileScope(file) : { kind: 'root' };\n return isVisibleStepScope(scope, featureVisibilityScope);\n })\n .sort((a, b) => {\n const aFile = a && a.source ? a.source.file : undefined;\n const bFile = b && b.source ? b.source.file : undefined;\n const aScope = aFile ? resolveFileScope(aFile) : { kind: 'root' };\n const bScope = bFile ? resolveFileScope(bFile) : { kind: 'root' };\n const delta = stepScopeRank(bScope) - stepScopeRank(aScope);\n return delta !== 0 ? delta : String(a.id).localeCompare(String(b.id));\n })\n : allSteps;\n\n const scopedStepsById = useScopedSteps\n ? (() => {\n const allowed = new Set(visibleSteps.map((s) => s.id));\n const next = new Map();\n for (const [id, def] of basePlan.stepsById.entries()) {\n if (allowed.has(id)) next.set(id, def);\n }\n return next;\n })()\n : basePlan.stepsById;\n const featureChildren = [];\n const scopesById = new Map(basePlan.scopesById);\n\n for (const element of feature.elements ?? []) {\n if (isScenario(element) || isScenarioOutline(element)) {\n const scenarioScope = {\n id: element.id ?? element.name,\n kind: isScenarioOutline(element) ? 'scenarioOutline' : 'scenario',\n name: element.name,\n mode: 'default',\n tags: element.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: [],\n pending: false,\n };\n featureChildren.push(scenarioScope);\n scopesById.set(scenarioScope.id, scenarioScope);\n continue;\n }\n\n if (isRule(element)) {\n const ruleChildren = [];\n for (const ruleElement of element.elements ?? []) {\n if (isScenario(ruleElement) || isScenarioOutline(ruleElement)) {\n const scenarioScope = {\n id: ruleElement.id ?? ruleElement.name,\n kind: isScenarioOutline(ruleElement) ? 'scenarioOutline' : 'scenario',\n name: ruleElement.name,\n mode: 'default',\n tags: ruleElement.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: [],\n pending: false,\n };\n ruleChildren.push(scenarioScope);\n scopesById.set(scenarioScope.id, scenarioScope);\n }\n }\n\n const ruleScope = {\n id: element.id ?? element.name,\n kind: 'rule',\n name: element.name,\n mode: 'default',\n tags: element.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: ruleChildren,\n pending: false,\n };\n featureChildren.push(ruleScope);\n scopesById.set(ruleScope.id, ruleScope);\n }\n }\n\n const featureScope = {\n id: feature.uri ?? feature.name,\n kind: 'feature',\n name: feature.name,\n mode: 'default',\n tags: feature.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: featureChildren,\n pending: false,\n };\n\n const existingRoot = basePlan.root;\n const updatedRoot = {\n ...existingRoot,\n children: [...existingRoot.children, featureScope],\n };\n\n scopesById.set(featureScope.id, featureScope);\n scopesById.set(updatedRoot.id, updatedRoot);\n\n const scopePlan = {\n root: updatedRoot,\n stepsById: scopedStepsById,\n hooksById: basePlan.hooksById,\n scopesById,\n };\n\n if (basePlan.worldFactory) {\n scopePlan.worldFactory = basePlan.worldFactory;\n }\n\n if (basePlan.parameterRegistry) {\n scopePlan.parameterRegistry = basePlan.parameterRegistry;\n }\n\n return scopePlan;\n }\n\n const gherkin = ${JSON.stringify(code)};\n const feature = parseGherkin(gherkin);\n const environments = collectStepsEnvironments(stepModules);\n const steps = selectStepsEnvironment(environments, feature);\n\n if (!steps) {\n 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.');\n }\n\n CucumberRunner.setSteps(steps);\n\n describe(feature.name, () => {\n const basePlan = steps.getPlan();\n const scopedPlan = createFeatureScopePlan(feature, basePlan);\n const { plan, adapter } = coordinateRunnerFeature({\n feature,\n environment: steps,\n config: ${runtimeConfig},\n plan: scopedPlan\n });\n\n execute({ plan, adapter, config: ${runtimeConfig} });\n });\n `,\n map: null,\n };\n },\n };\n}\n\ninterface StepGlobOptions {\n readonly configDir: string;\n readonly projectRoot: string;\n}\n\nfunction buildStepGlobs(\n entries: readonly string[] | undefined,\n options: StepGlobOptions\n): string[] {\n if (!entries || entries.length === 0) {\n return [];\n }\n\n const patterns = new Set<string>();\n for (const entry of entries) {\n const normalized = entry.trim();\n if (!normalized) {\n continue;\n }\n\n for (const candidate of toPatterns(normalized, STEP_FALLBACK_GLOB)) {\n const absolute = isAbsolute(candidate)\n ? normalizeSlashes(candidate)\n : normalizeSlashes(resolve(options.configDir, candidate));\n const rootRelative = toRootRelativeGlob(absolute, options.projectRoot);\n patterns.add(rootRelative);\n }\n }\n\n return Array.from(patterns);\n}\n\nfunction toPatterns(entry: string, fallbackGlob: string): string[] {\n if (hasGlobMagic(entry) || hasFileExtension(entry)) {\n return [entry];\n }\n return [appendGlob(entry, fallbackGlob)];\n}\n\nfunction hasGlobMagic(input: string): boolean {\n return /[*?{}()[\\]!,@+]/u.test(input);\n}\n\nfunction hasFileExtension(input: string): boolean {\n const normalized = normalizeSlashes(input);\n const trimmed = normalized === \"/\" ? normalized : normalized.replace(/\\/+$/u, \"\");\n if (!trimmed || trimmed === \".\" || trimmed === \"..\") {\n return false;\n }\n return Boolean(extname(trimmed));\n}\n\nfunction appendGlob(entry: string, glob: string): string {\n const normalized = normalizeSlashes(entry);\n const trimmed = normalized === \"/\" ? normalized : normalized.replace(/\\/+$/u, \"\");\n if (!trimmed || trimmed === \".\") {\n return glob;\n }\n if (trimmed === \"/\") {\n return `/${glob}`;\n }\n return `${trimmed}/${glob}`;\n}\n\nfunction toRootRelativeGlob(pattern: string, rootDir: string): string {\n const normalizedRoot = normalizeSlashes(rootDir);\n const normalizedPattern = normalizeSlashes(pattern);\n let relativePattern = normalizeSlashes(relative(normalizedRoot, normalizedPattern));\n\n if (!relativePattern || relativePattern === \".\") {\n relativePattern = \"\";\n }\n\n if (!relativePattern || relativePattern.startsWith(\"..\")) {\n return ensureGlobPrefix(normalizedPattern);\n }\n\n return ensureGlobPrefix(relativePattern);\n}\n\nfunction ensureGlobPrefix(pattern: string): string {\n if (/^[A-Za-z]:\\//u.test(pattern)) {\n return pattern;\n }\n if (pattern.startsWith(\"/\")) {\n return pattern;\n }\n return `/${pattern.replace(/^\\/+/, \"\")}`;\n}\n\nfunction normalizeSlashes(pathname: string): string {\n return pathname.replace(/\\\\/gu, \"/\");\n}\n\nasync function loadAutometaConfig(root: string): Promise<{ config: Config; path: string }> {\n const _jiti = jiti(root, { interopDefault: true });\n\n const candidates = [\n \"autometa.config.ts\",\n \"autometa.config.js\",\n \"autometa.config.mts\",\n \"autometa.config.mjs\",\n \"autometa.config.cts\",\n \"autometa.config.cjs\",\n ];\n\n for (const candidate of candidates) {\n const path = resolve(root, candidate);\n if (!existsSync(path)) {\n continue;\n }\n\n try {\n const mod = _jiti(path);\n const config = mod.default || mod;\n\n if (isConfig(config)) {\n return { config, path };\n } else {\n console.warn(`Found config at ${path} but it does not export a Config instance.`);\n }\n } catch (error: unknown) {\n console.error(`Error loading config at ${path}:`, error);\n throw error;\n }\n }\n throw new Error(\"Could not find autometa.config.{ts,js,mjs,cjs} in \" + root);\n}\n\nfunction isConfig(config: unknown): config is Config {\n return (\n typeof config === \"object\" &&\n config !== null &&\n \"resolve\" in config &&\n typeof (config as Config).resolve === \"function\" &&\n \"current\" in config &&\n typeof (config as Config).current === \"function\"\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["next"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","sourcesContent":["import { resolve, isAbsolute, relative, dirname, extname } from \"path\";\nimport { existsSync } from \"fs\";\nimport type { Plugin } from \"vite\";\nimport type { Config } from \"@autometa/config\";\nimport jiti from \"jiti\";\n\nconst STEP_FALLBACK_GLOB = \"**/*.{ts,tsx,js,jsx,mjs,cjs,mts,cts}\";\nconst FEATURE_FALLBACK_GLOB = \"**/*.feature\";\n\ntype ModuleDeclaration =\n | string\n | { readonly name: string; readonly submodules?: readonly ModuleDeclaration[] | undefined };\n\nfunction flattenModuleDeclarations(\n declarations: readonly ModuleDeclaration[] | undefined,\n prefix: readonly string[] = []\n): readonly (readonly string[])[] {\n if (!declarations || declarations.length === 0) {\n return [];\n }\n\n const results: string[][] = [];\n\n for (const entry of declarations) {\n if (typeof entry === \"string\") {\n const next = [...prefix, entry];\n results.push(next);\n continue;\n }\n\n const next = [...prefix, entry.name];\n results.push(next);\n\n const nested = flattenModuleDeclarations(entry.submodules ?? undefined, next);\n for (const path of nested) {\n results.push([...path]);\n }\n }\n\n const unique = new Map<string, readonly string[]>();\n for (const path of results) {\n unique.set(path.join(\"/\"), path);\n }\n\n return Array.from(unique.values()).sort((a, b) => b.length - a.length);\n}\n\nfunction buildStepScopingData(resolvedConfig: unknown, projectRoot: string): unknown {\n const groups = (resolvedConfig as { modules?: { groups?: unknown } } | null)?.modules?.groups;\n if (!groups || typeof groups !== \"object\") {\n return null;\n }\n\n const entries = Object.entries(groups as Record<string, unknown>).map(([group, groupConfig]) => {\n const configRecord =\n groupConfig && typeof groupConfig === \"object\" ? (groupConfig as Record<string, unknown>) : {};\n const rootAbs = resolve(projectRoot, String(configRecord.root ?? \"\"));\n const modules = configRecord.modules;\n const modulePaths = Array.isArray(modules)\n ? flattenModuleDeclarations(modules as ModuleDeclaration[])\n : [];\n return { group, rootAbs, modulePaths };\n });\n\n return { groups: entries };\n}\n\nexport function autometa(): Plugin {\n let autometaConfig: Config | undefined;\n let configPath: string | undefined;\n let projectRoot: string | undefined;\n\n return {\n name: \"autometa-vitest-plugin\",\n enforce: \"pre\",\n async configResolved(viteConfig) {\n projectRoot = viteConfig.root || process.cwd();\n const loaded = await loadAutometaConfig(projectRoot);\n autometaConfig = loaded.config;\n configPath = loaded.path;\n },\n transform(code, id) {\n const cleanId = id.split(\"?\", 1)[0] ?? id;\n\n if (!cleanId.endsWith(\".feature\")) {\n return;\n }\n\n if (!autometaConfig) {\n throw new Error(\"Autometa config not found\");\n }\n\n const resolved = autometaConfig.resolve();\n const stepRoots = resolved.config.roots.steps;\n const configDir = configPath ? dirname(configPath) : projectRoot ?? process.cwd();\n const rootDir = projectRoot ?? process.cwd();\n const stepGlobs = buildStepGlobs(stepRoots, {\n configDir,\n projectRoot: rootDir,\n });\n if (stepGlobs.length === 0) {\n throw new Error(\n \"Autometa config did not resolve any step files within the current project root.\"\n );\n }\n\n const eventGlobs = buildStepGlobs(resolved.config.events, {\n configDir,\n projectRoot: rootDir,\n });\n\n const runtimeConfig = JSON.stringify(resolved.config);\n const stepScopingMode = resolved.config.modules?.stepScoping ?? \"global\";\n const hoistedFeatureScopingMode = resolved.config.modules?.hoistedFeatures?.scope ?? \"tag\";\n const hoistedFeatureScopingStrict = resolved.config.modules?.hoistedFeatures?.strict ?? true;\n\n // Group/module index data is useful even when step scoping is disabled,\n // since we may need it to select the correct steps environment.\n const groupIndexData = buildStepScopingData(resolved.config, rootDir);\n const stepScopingData = stepScopingMode === \"scoped\" ? groupIndexData : null;\n const featureRootDirs = buildFeatureRootDirs(resolved.config.roots.features, {\n configDir,\n projectRoot: rootDir,\n });\n\n const featureFile = cleanId;\n\n return {\n code: String.raw`\n import { describe } from 'vitest';\n import { execute } from '@autometa/vitest-executor';\n import { coordinateRunnerFeature, CucumberRunner, STEPS_ENVIRONMENT_META } from '@autometa/runner';\n import { parseGherkin } from '@autometa/gherkin';\n import { relative as __pathRelative, resolve as __pathResolve, isAbsolute as __pathIsAbsolute } from 'node:path';\n\n const __AUTOMETA_STEP_SCOPING_MODE = ${JSON.stringify(stepScopingMode)};\n const __AUTOMETA_GROUP_INDEX = ${JSON.stringify(groupIndexData)};\n const __AUTOMETA_STEP_SCOPING = ${JSON.stringify(stepScopingData)};\n const __AUTOMETA_FEATURE_FILE = ${JSON.stringify(featureFile)};\n const __AUTOMETA_HOISTED_FEATURE_SCOPING_MODE = ${JSON.stringify(hoistedFeatureScopingMode)};\n const __AUTOMETA_HOISTED_FEATURE_SCOPING_STRICT = ${JSON.stringify(hoistedFeatureScopingStrict)};\n const __AUTOMETA_HOISTED_FEATURE_ROOTS = ${JSON.stringify(featureRootDirs)};\n\n const __AUTOMETA_EVENT_MODULES = ${eventGlobs.length > 0\n ? `import.meta.glob(${JSON.stringify(eventGlobs)}, { eager: true })`\n : \"{}\"};\n const stepModules = import.meta.glob(${JSON.stringify(stepGlobs)}, { eager: true });\n\n function collectCandidateModules(imported) {\n if (!imported || typeof imported !== 'object') {\n return [];\n }\n\n const modules = new Set();\n modules.add(imported);\n\n const exportedModules = imported.modules;\n if (Array.isArray(exportedModules)) {\n for (const entry of exportedModules) {\n if (entry && typeof entry === 'object') {\n modules.add(entry);\n }\n }\n }\n\n const defaultExport = imported.default;\n if (Array.isArray(defaultExport)) {\n for (const entry of defaultExport) {\n if (entry && typeof entry === 'object') {\n modules.add(entry);\n }\n }\n } else if (defaultExport && typeof defaultExport === 'object') {\n modules.add(defaultExport);\n }\n\n return Array.from(modules);\n }\n\n function isStepsEnvironment(candidate) {\n return Boolean(\n candidate &&\n typeof candidate === 'object' &&\n typeof candidate.coordinateFeature === 'function' &&\n typeof candidate.getPlan === 'function' &&\n typeof candidate.Given === 'function' &&\n typeof candidate.When === 'function' &&\n typeof candidate.Then === 'function'\n );\n }\n\n function extractStepsEnvironments(candidate) {\n const environments = [];\n\n if (!candidate || typeof candidate !== 'object') {\n return environments;\n }\n\n if (isStepsEnvironment(candidate)) {\n environments.push(candidate);\n }\n\n const stepsEnv = candidate.stepsEnvironment;\n if (isStepsEnvironment(stepsEnv)) {\n environments.push(stepsEnv);\n }\n\n const defaultExport = candidate.default;\n if (isStepsEnvironment(defaultExport)) {\n environments.push(defaultExport);\n }\n\n return environments;\n }\n\n function collectStepsEnvironments(modules) {\n const environments = new Set();\n for (const moduleExports of Object.values(modules)) {\n for (const candidate of collectCandidateModules(moduleExports)) {\n for (const env of extractStepsEnvironments(candidate)) {\n environments.add(env);\n }\n }\n }\n return Array.from(environments);\n }\n\n function __normalizePathSegments(input) {\n return String(input).replace(/\\\\/g, '/').split('/').filter(Boolean);\n }\n\n function __startsWithSegments(haystack, needle) {\n if (needle.length > haystack.length) return false;\n for (let i = 0; i < needle.length; i += 1) {\n if (haystack[i] !== needle[i]) return false;\n }\n return true;\n }\n\n function __isUnderRoot(fileAbs, rootAbs) {\n const rel = rootAbs ? __pathRelative(String(rootAbs), String(fileAbs)) : '';\n return rel === '' || (!rel.startsWith('..') && !rel.startsWith('../') && !rel.startsWith('..\\\\'));\n }\n\n function __selectHoistedFeatureRoot(fileAbs) {\n if (!Array.isArray(__AUTOMETA_HOISTED_FEATURE_ROOTS) || __AUTOMETA_HOISTED_FEATURE_ROOTS.length === 0) {\n return undefined;\n }\n\n const absoluteFile = __pathIsAbsolute(String(fileAbs))\n ? String(fileAbs)\n : __pathResolve(process.cwd(), String(fileAbs));\n\n let best;\n for (const rootAbs of __AUTOMETA_HOISTED_FEATURE_ROOTS) {\n if (!rootAbs) continue;\n if (__isUnderRoot(absoluteFile, rootAbs)) {\n if (!best || String(rootAbs).length > String(best).length) {\n best = rootAbs;\n }\n }\n }\n return best;\n }\n\n function __resolveHoistedDirectoryScope(fileAbs, groupIndexData) {\n if (!groupIndexData || !Array.isArray(groupIndexData.groups)) {\n return { kind: 'root' };\n }\n\n const rootAbs = __selectHoistedFeatureRoot(fileAbs);\n if (!rootAbs) {\n return { kind: 'root' };\n }\n\n const absoluteFile = __pathIsAbsolute(String(fileAbs))\n ? String(fileAbs)\n : __pathResolve(process.cwd(), String(fileAbs));\n\n const rel = __pathRelative(String(rootAbs), String(absoluteFile));\n const segments = __normalizePathSegments(rel);\n const dirSegments = segments.slice(0, -1);\n if (dirSegments.length === 0) {\n return { kind: 'root' };\n }\n\n const group = dirSegments[0];\n if (!group) {\n return { kind: 'root' };\n }\n\n const groupEntry = groupIndexData.groups.find((entry) => entry && entry.group === group);\n if (!groupEntry) {\n if (__AUTOMETA_HOISTED_FEATURE_SCOPING_STRICT) {\n throw new Error(\n '[autometa] Feature \"' +\n absoluteFile +\n '\" is under \"' +\n rootAbs +\n '\" and implies group \"' +\n group +\n '\", but \"' +\n group +\n '\" is not declared in config.modules.groups.'\n );\n }\n return { kind: 'root' };\n }\n\n const moduleSegments = dirSegments.slice(1);\n if (moduleSegments.length === 0) {\n return { kind: 'group', group };\n }\n\n const modulePaths = groupEntry.modulePaths || [];\n for (const modulePath of modulePaths) {\n if (__startsWithSegments(moduleSegments, modulePath)) {\n return { kind: 'module', group, modulePath };\n }\n }\n\n if (__AUTOMETA_HOISTED_FEATURE_SCOPING_STRICT) {\n throw new Error(\n '[autometa] Feature \"' +\n absoluteFile +\n '\" is under \"' +\n rootAbs +\n '\" and implies module \"' +\n group +\n ':' +\n moduleSegments.join(':') +\n '\", but no matching module is declared in config.modules.groups.' +\n group +\n '.modules.'\n );\n }\n\n return { kind: 'group', group };\n }\n\n function __resolveFileScope(fileAbs) {\n if (!__AUTOMETA_GROUP_INDEX || !__AUTOMETA_GROUP_INDEX.groups) {\n return { kind: 'root' };\n }\n if (String(fileAbs).startsWith('node:')) {\n return { kind: 'root' };\n }\n\n const absoluteFile = __pathIsAbsolute(String(fileAbs))\n ? String(fileAbs)\n : __pathResolve(process.cwd(), String(fileAbs));\n\n for (const entry of __AUTOMETA_GROUP_INDEX.groups) {\n const rootAbs = entry.rootAbs;\n const rel = rootAbs ? __pathRelative(rootAbs, absoluteFile) : '';\n if (rel === '' || (!rel.startsWith('..') && !rel.startsWith('../') && !rel.startsWith('..\\\\'))) {\n const segments = __normalizePathSegments(rel);\n const modulePaths = entry.modulePaths || [];\n for (const modulePath of modulePaths) {\n if (__startsWithSegments(segments, modulePath)) {\n return { kind: 'module', group: entry.group, modulePath };\n }\n }\n return { kind: 'group', group: entry.group };\n }\n }\n return { kind: 'root' };\n }\n\n function __parseScopeOverrideTag(tags) {\n if (!Array.isArray(tags)) return undefined;\n for (const tag of tags) {\n const match = String(tag).match(/^@scope(?::|=|\\()(.+?)(?:\\))?$/u);\n if (!match) continue;\n const raw = String(match[1] ?? '').trim();\n if (!raw) continue;\n const normalized = raw.replace(/\\//g, ':');\n const parts = normalized.split(':').filter(Boolean);\n const group = parts[0];\n const rest = parts.slice(1);\n if (!group) continue;\n return rest.length > 0 ? { group, modulePath: rest } : { group };\n }\n return undefined;\n }\n\n function __resolveFeatureScope(feature) {\n const override = __parseScopeOverrideTag(feature.tags);\n if (override) {\n if (override.modulePath && override.modulePath.length > 0) {\n return { kind: 'module', group: override.group, modulePath: override.modulePath };\n }\n return { kind: 'group', group: override.group };\n }\n const fileScope = __resolveFileScope(__AUTOMETA_FEATURE_FILE);\n if (fileScope.kind !== 'root') {\n return fileScope;\n }\n if (__AUTOMETA_HOISTED_FEATURE_SCOPING_MODE !== 'directory') {\n return fileScope;\n }\n return __resolveHoistedDirectoryScope(__AUTOMETA_FEATURE_FILE, __AUTOMETA_GROUP_INDEX);\n }\n\n function __inferEnvironmentGroup(environment) {\n const meta = environment && typeof environment === 'object'\n ? environment[STEPS_ENVIRONMENT_META]\n : undefined;\n if (meta && typeof meta === 'object') {\n if (meta.kind === 'group' && typeof meta.group === 'string' && meta.group.trim().length > 0) {\n return { kind: 'group', group: meta.group };\n }\n if (meta.kind === 'root') {\n return { kind: 'root' };\n }\n }\n\n const plan = environment.getPlan();\n const groups = new Set();\n for (const def of plan.stepsById.values()) {\n const file = def && def.source ? def.source.file : undefined;\n if (!file) continue;\n const scope = __resolveFileScope(file);\n if (scope.kind === 'group' || scope.kind === 'module') {\n groups.add(scope.group);\n if (groups.size > 1) return { kind: 'ambiguous' };\n }\n }\n const only = Array.from(groups.values())[0];\n return only ? { kind: 'group', group: only } : { kind: 'root' };\n }\n\n function selectStepsEnvironment(environments, feature) {\n if (!Array.isArray(environments) || environments.length === 0) {\n return undefined;\n }\n if (environments.length === 1) {\n return environments[0];\n }\n\n const featureScope = __resolveFeatureScope(feature);\n const indexed = environments\n .map((env) => ({ env, inferred: __inferEnvironmentGroup(env) }))\n .filter((entry) => entry.inferred.kind !== 'ambiguous');\n\n if (featureScope.kind === 'root') {\n const root = indexed.find((entry) => entry.inferred.kind === 'root');\n return (root ? root.env : indexed[0]?.env) ?? environments[0];\n }\n\n const match = indexed.find((entry) => entry.inferred.kind === 'group' && entry.inferred.group === featureScope.group);\n return match ? match.env : undefined;\n }\n\n function isScenario(element) {\n return Boolean(\n element &&\n typeof element === 'object' &&\n 'steps' in element &&\n !('exampleGroups' in element) &&\n !('elements' in element)\n );\n }\n\n function isScenarioOutline(element) {\n return Boolean(\n element &&\n typeof element === 'object' &&\n 'steps' in element &&\n 'exampleGroups' in element\n );\n }\n\n function isRule(element) {\n return Boolean(\n element &&\n typeof element === 'object' &&\n 'elements' in element &&\n Array.isArray(element.elements)\n );\n }\n\n function createFeatureScopePlan(feature, basePlan) {\n const allSteps = Array.from(basePlan.stepsById.values());\n\n function normalizePathSegments(input) {\n return String(input).replace(/\\\\/g, '/').split('/').filter(Boolean);\n }\n\n function startsWithSegments(haystack, needle) {\n if (needle.length > haystack.length) return false;\n for (let i = 0; i < needle.length; i += 1) {\n if (haystack[i] !== needle[i]) return false;\n }\n return true;\n }\n\n function resolveFileScope(fileAbs) {\n if (!__AUTOMETA_STEP_SCOPING || !__AUTOMETA_STEP_SCOPING.groups) {\n return { kind: 'root' };\n }\n if (String(fileAbs).startsWith('node:')) {\n return { kind: 'root' };\n }\n\n const absoluteFile = __pathIsAbsolute(String(fileAbs))\n ? String(fileAbs)\n : __pathResolve(process.cwd(), String(fileAbs));\n\n for (const entry of __AUTOMETA_STEP_SCOPING.groups) {\n const rootAbs = entry.rootAbs;\n const rel = rootAbs ? __pathRelative(rootAbs, absoluteFile) : '';\n if (rel === '' || (!rel.startsWith('..') && !rel.startsWith('../') && !rel.startsWith('..\\\\'))) {\n const segments = normalizePathSegments(rel);\n const modulePaths = entry.modulePaths || [];\n for (const modulePath of modulePaths) {\n if (startsWithSegments(segments, modulePath)) {\n return { kind: 'module', group: entry.group, modulePath };\n }\n }\n return { kind: 'group', group: entry.group };\n }\n }\n return { kind: 'root' };\n }\n\n function parseScopeOverrideTag(tags) {\n if (!Array.isArray(tags)) return undefined;\n for (const tag of tags) {\n const match = String(tag).match(/^@scope(?::|=|\\()(.+?)(?:\\))?$/u);\n if (!match) continue;\n const raw = String(match[1] ?? '').trim();\n if (!raw) continue;\n const normalized = raw.replace(/\\//g, ':');\n const parts = normalized.split(':').filter(Boolean);\n const group = parts[0];\n const rest = parts.slice(1);\n if (!group) continue;\n return rest.length > 0 ? { group, modulePath: rest } : { group };\n }\n return undefined;\n }\n\n function resolveFeatureScope() {\n const override = parseScopeOverrideTag(feature.tags);\n if (override) {\n if (override.modulePath && override.modulePath.length > 0) {\n return { kind: 'module', group: override.group, modulePath: override.modulePath };\n }\n return { kind: 'group', group: override.group };\n }\n const fileScope = resolveFileScope(__AUTOMETA_FEATURE_FILE);\n if (fileScope.kind !== 'root') {\n return fileScope;\n }\n if (__AUTOMETA_HOISTED_FEATURE_SCOPING_MODE !== 'directory') {\n return fileScope;\n }\n return __resolveHoistedDirectoryScope(__AUTOMETA_FEATURE_FILE, __AUTOMETA_STEP_SCOPING);\n }\n\n function isVisibleStepScope(stepScope, featureScope) {\n if (featureScope.kind === 'root') {\n return stepScope.kind === 'root';\n }\n if (featureScope.kind === 'group') {\n if (stepScope.kind === 'root') return true;\n return stepScope.kind === 'group' && stepScope.group === featureScope.group;\n }\n // module\n if (stepScope.kind === 'root') return true;\n if (stepScope.kind === 'group') return stepScope.group === featureScope.group;\n if (stepScope.kind === 'module') {\n return stepScope.group === featureScope.group && startsWithSegments(featureScope.modulePath, stepScope.modulePath);\n }\n return false;\n }\n\n function stepScopeRank(scope) {\n if (scope.kind === 'module') return 200 + (scope.modulePath ? scope.modulePath.length : 0);\n if (scope.kind === 'group') return 100;\n return 0;\n }\n\n const useScopedSteps = __AUTOMETA_STEP_SCOPING_MODE === 'scoped' && __AUTOMETA_STEP_SCOPING && __AUTOMETA_STEP_SCOPING.groups;\n const featureVisibilityScope = useScopedSteps ? resolveFeatureScope() : { kind: 'root' };\n const visibleSteps = useScopedSteps\n ? allSteps\n .filter((definition) => {\n const file = definition && definition.source ? definition.source.file : undefined;\n const scope = file ? resolveFileScope(file) : { kind: 'root' };\n return isVisibleStepScope(scope, featureVisibilityScope);\n })\n .sort((a, b) => {\n const aFile = a && a.source ? a.source.file : undefined;\n const bFile = b && b.source ? b.source.file : undefined;\n const aScope = aFile ? resolveFileScope(aFile) : { kind: 'root' };\n const bScope = bFile ? resolveFileScope(bFile) : { kind: 'root' };\n const delta = stepScopeRank(bScope) - stepScopeRank(aScope);\n return delta !== 0 ? delta : String(a.id).localeCompare(String(b.id));\n })\n : allSteps;\n\n const scopedStepsById = useScopedSteps\n ? (() => {\n const allowed = new Set(visibleSteps.map((s) => s.id));\n const next = new Map();\n for (const [id, def] of basePlan.stepsById.entries()) {\n if (allowed.has(id)) next.set(id, def);\n }\n return next;\n })()\n : basePlan.stepsById;\n const featureChildren = [];\n const scopesById = new Map(basePlan.scopesById);\n\n for (const element of feature.elements ?? []) {\n if (isScenario(element) || isScenarioOutline(element)) {\n const scenarioScope = {\n id: element.id ?? element.name,\n kind: isScenarioOutline(element) ? 'scenarioOutline' : 'scenario',\n name: element.name,\n mode: 'default',\n tags: element.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: [],\n pending: false,\n };\n featureChildren.push(scenarioScope);\n scopesById.set(scenarioScope.id, scenarioScope);\n continue;\n }\n\n if (isRule(element)) {\n const ruleChildren = [];\n for (const ruleElement of element.elements ?? []) {\n if (isScenario(ruleElement) || isScenarioOutline(ruleElement)) {\n const scenarioScope = {\n id: ruleElement.id ?? ruleElement.name,\n kind: isScenarioOutline(ruleElement) ? 'scenarioOutline' : 'scenario',\n name: ruleElement.name,\n mode: 'default',\n tags: ruleElement.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: [],\n pending: false,\n };\n ruleChildren.push(scenarioScope);\n scopesById.set(scenarioScope.id, scenarioScope);\n }\n }\n\n const ruleScope = {\n id: element.id ?? element.name,\n kind: 'rule',\n name: element.name,\n mode: 'default',\n tags: element.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: ruleChildren,\n pending: false,\n };\n featureChildren.push(ruleScope);\n scopesById.set(ruleScope.id, ruleScope);\n }\n }\n\n const featureScope = {\n id: feature.uri ?? feature.name,\n kind: 'feature',\n name: feature.name,\n mode: 'default',\n tags: feature.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: featureChildren,\n pending: false,\n };\n\n const existingRoot = basePlan.root;\n const updatedRoot = {\n ...existingRoot,\n children: [...existingRoot.children, featureScope],\n };\n\n scopesById.set(featureScope.id, featureScope);\n scopesById.set(updatedRoot.id, updatedRoot);\n\n const scopePlan = {\n root: updatedRoot,\n stepsById: scopedStepsById,\n hooksById: basePlan.hooksById,\n scopesById,\n };\n\n if (basePlan.worldFactory) {\n scopePlan.worldFactory = basePlan.worldFactory;\n }\n\n if (basePlan.parameterRegistry) {\n scopePlan.parameterRegistry = basePlan.parameterRegistry;\n }\n\n return scopePlan;\n }\n\n const gherkin = ${JSON.stringify(code)};\n const feature = parseGherkin(gherkin);\n const environments = collectStepsEnvironments(stepModules);\n const steps = selectStepsEnvironment(environments, feature);\n\n if (!steps) {\n 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.');\n }\n\n CucumberRunner.setSteps(steps);\n\n describe(feature.name, () => {\n const basePlan = steps.getPlan();\n const scopedPlan = createFeatureScopePlan(feature, basePlan);\n const { plan, adapter } = coordinateRunnerFeature({\n feature,\n environment: steps,\n config: ${runtimeConfig},\n plan: scopedPlan\n });\n\n execute({ plan, adapter, config: ${runtimeConfig} });\n });\n `,\n map: null,\n };\n },\n };\n}\n\ninterface StepGlobOptions {\n readonly configDir: string;\n readonly projectRoot: string;\n}\n\nfunction buildFeatureRootDirs(\n entries: readonly string[] | undefined,\n options: StepGlobOptions\n): string[] {\n if (!entries || entries.length === 0) {\n return [];\n }\n\n const roots = new Set<string>();\n for (const entry of entries) {\n const normalized = entry.trim();\n if (!normalized) {\n continue;\n }\n\n for (const candidate of toPatterns(normalized, FEATURE_FALLBACK_GLOB)) {\n const base = inferGlobBaseDirectory(candidate);\n const absolute = isAbsolute(base)\n ? normalizeSlashes(base)\n : normalizeSlashes(resolve(options.configDir, base));\n roots.add(absolute.replace(/\\/+$/u, \"\"));\n }\n }\n\n return Array.from(roots).sort((a, b) => b.length - a.length);\n}\n\nfunction buildStepGlobs(\n entries: readonly string[] | undefined,\n options: StepGlobOptions\n): string[] {\n if (!entries || entries.length === 0) {\n return [];\n }\n\n const patterns = new Set<string>();\n for (const entry of entries) {\n const normalized = entry.trim();\n if (!normalized) {\n continue;\n }\n\n for (const candidate of toPatterns(normalized, STEP_FALLBACK_GLOB)) {\n const absolute = isAbsolute(candidate)\n ? normalizeSlashes(candidate)\n : normalizeSlashes(resolve(options.configDir, candidate));\n const rootRelative = toRootRelativeGlob(absolute, options.projectRoot);\n patterns.add(rootRelative);\n }\n }\n\n return Array.from(patterns);\n}\n\nfunction toPatterns(entry: string, fallbackGlob: string): string[] {\n if (hasGlobMagic(entry) || hasFileExtension(entry)) {\n return [entry];\n }\n return [appendGlob(entry, fallbackGlob)];\n}\n\nfunction inferGlobBaseDirectory(pattern: string): string {\n const normalized = normalizeSlashes(pattern).replace(/^!/u, \"\");\n\n for (let i = 0; i < normalized.length; i += 1) {\n if (!hasGlobMagic(normalized[i] ?? \"\")) {\n continue;\n }\n\n const prefix = normalized.slice(0, i);\n const lastSlash = prefix.lastIndexOf(\"/\");\n if (lastSlash <= 0) {\n return \".\";\n }\n const base = prefix.slice(0, lastSlash);\n return base || \".\";\n }\n\n if (hasFileExtension(normalized)) {\n return dirname(normalized);\n }\n\n return normalized || \".\";\n}\n\nfunction hasGlobMagic(input: string): boolean {\n return /[*?{}()[\\]!,@+]/u.test(input);\n}\n\nfunction hasFileExtension(input: string): boolean {\n const normalized = normalizeSlashes(input);\n const trimmed = normalized === \"/\" ? normalized : normalized.replace(/\\/+$/u, \"\");\n if (!trimmed || trimmed === \".\" || trimmed === \"..\") {\n return false;\n }\n return Boolean(extname(trimmed));\n}\n\nfunction appendGlob(entry: string, glob: string): string {\n const normalized = normalizeSlashes(entry);\n const trimmed = normalized === \"/\" ? normalized : normalized.replace(/\\/+$/u, \"\");\n if (!trimmed || trimmed === \".\") {\n return glob;\n }\n if (trimmed === \"/\") {\n return `/${glob}`;\n }\n return `${trimmed}/${glob}`;\n}\n\nfunction toRootRelativeGlob(pattern: string, rootDir: string): string {\n const normalizedRoot = normalizeSlashes(rootDir);\n const normalizedPattern = normalizeSlashes(pattern);\n let relativePattern = normalizeSlashes(relative(normalizedRoot, normalizedPattern));\n\n if (!relativePattern || relativePattern === \".\") {\n relativePattern = \"\";\n }\n\n if (!relativePattern || relativePattern.startsWith(\"..\")) {\n return ensureGlobPrefix(normalizedPattern);\n }\n\n return ensureGlobPrefix(relativePattern);\n}\n\nfunction ensureGlobPrefix(pattern: string): string {\n if (/^[A-Za-z]:\\//u.test(pattern)) {\n return pattern;\n }\n if (pattern.startsWith(\"/\")) {\n return pattern;\n }\n return `/${pattern.replace(/^\\/+/, \"\")}`;\n}\n\nfunction normalizeSlashes(pathname: string): string {\n return pathname.replace(/\\\\/gu, \"/\");\n}\n\nasync function loadAutometaConfig(root: string): Promise<{ config: Config; path: string }> {\n const _jiti = jiti(root, { interopDefault: true });\n\n const candidates = [\n \"autometa.config.ts\",\n \"autometa.config.js\",\n \"autometa.config.mts\",\n \"autometa.config.mjs\",\n \"autometa.config.cts\",\n \"autometa.config.cjs\",\n ];\n\n for (const candidate of candidates) {\n const path = resolve(root, candidate);\n if (!existsSync(path)) {\n continue;\n }\n\n try {\n const mod = _jiti(path);\n const config = mod.default || mod;\n\n if (isConfig(config)) {\n return { config, path };\n } else {\n console.warn(`Found config at ${path} but it does not export a Config instance.`);\n }\n } catch (error: unknown) {\n console.error(`Error loading config at ${path}:`, error);\n throw error;\n }\n }\n throw new Error(\"Could not find autometa.config.{ts,js,mjs,cjs} in \" + root);\n}\n\nfunction isConfig(config: unknown): config is Config {\n return (\n typeof config === \"object\" &&\n config !== null &&\n \"resolve\" in config &&\n typeof (config as Config).resolve === \"function\" &&\n \"current\" in config &&\n typeof (config as Config).current === \"function\"\n );\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import jiti from 'jiti';
|
|
|
4
4
|
|
|
5
5
|
// src/index.ts
|
|
6
6
|
var STEP_FALLBACK_GLOB = "**/*.{ts,tsx,js,jsx,mjs,cjs,mts,cts}";
|
|
7
|
+
var FEATURE_FALLBACK_GLOB = "**/*.feature";
|
|
7
8
|
function flattenModuleDeclarations(declarations, prefix = []) {
|
|
8
9
|
if (!declarations || declarations.length === 0) {
|
|
9
10
|
return [];
|
|
@@ -82,8 +83,14 @@ function autometa() {
|
|
|
82
83
|
});
|
|
83
84
|
const runtimeConfig = JSON.stringify(resolved.config);
|
|
84
85
|
const stepScopingMode = resolved.config.modules?.stepScoping ?? "global";
|
|
86
|
+
const hoistedFeatureScopingMode = resolved.config.modules?.hoistedFeatures?.scope ?? "tag";
|
|
87
|
+
const hoistedFeatureScopingStrict = resolved.config.modules?.hoistedFeatures?.strict ?? true;
|
|
85
88
|
const groupIndexData = buildStepScopingData(resolved.config, rootDir);
|
|
86
89
|
const stepScopingData = stepScopingMode === "scoped" ? groupIndexData : null;
|
|
90
|
+
const featureRootDirs = buildFeatureRootDirs(resolved.config.roots.features, {
|
|
91
|
+
configDir,
|
|
92
|
+
projectRoot: rootDir
|
|
93
|
+
});
|
|
87
94
|
const featureFile = cleanId;
|
|
88
95
|
return {
|
|
89
96
|
code: String.raw`
|
|
@@ -97,6 +104,9 @@ function autometa() {
|
|
|
97
104
|
const __AUTOMETA_GROUP_INDEX = ${JSON.stringify(groupIndexData)};
|
|
98
105
|
const __AUTOMETA_STEP_SCOPING = ${JSON.stringify(stepScopingData)};
|
|
99
106
|
const __AUTOMETA_FEATURE_FILE = ${JSON.stringify(featureFile)};
|
|
107
|
+
const __AUTOMETA_HOISTED_FEATURE_SCOPING_MODE = ${JSON.stringify(hoistedFeatureScopingMode)};
|
|
108
|
+
const __AUTOMETA_HOISTED_FEATURE_SCOPING_STRICT = ${JSON.stringify(hoistedFeatureScopingStrict)};
|
|
109
|
+
const __AUTOMETA_HOISTED_FEATURE_ROOTS = ${JSON.stringify(featureRootDirs)};
|
|
100
110
|
|
|
101
111
|
const __AUTOMETA_EVENT_MODULES = ${eventGlobs.length > 0 ? `import.meta.glob(${JSON.stringify(eventGlobs)}, { eager: true })` : "{}"};
|
|
102
112
|
const stepModules = import.meta.glob(${JSON.stringify(stepGlobs)}, { eager: true });
|
|
@@ -192,6 +202,107 @@ function autometa() {
|
|
|
192
202
|
return true;
|
|
193
203
|
}
|
|
194
204
|
|
|
205
|
+
function __isUnderRoot(fileAbs, rootAbs) {
|
|
206
|
+
const rel = rootAbs ? __pathRelative(String(rootAbs), String(fileAbs)) : '';
|
|
207
|
+
return rel === '' || (!rel.startsWith('..') && !rel.startsWith('../') && !rel.startsWith('..\\'));
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function __selectHoistedFeatureRoot(fileAbs) {
|
|
211
|
+
if (!Array.isArray(__AUTOMETA_HOISTED_FEATURE_ROOTS) || __AUTOMETA_HOISTED_FEATURE_ROOTS.length === 0) {
|
|
212
|
+
return undefined;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const absoluteFile = __pathIsAbsolute(String(fileAbs))
|
|
216
|
+
? String(fileAbs)
|
|
217
|
+
: __pathResolve(process.cwd(), String(fileAbs));
|
|
218
|
+
|
|
219
|
+
let best;
|
|
220
|
+
for (const rootAbs of __AUTOMETA_HOISTED_FEATURE_ROOTS) {
|
|
221
|
+
if (!rootAbs) continue;
|
|
222
|
+
if (__isUnderRoot(absoluteFile, rootAbs)) {
|
|
223
|
+
if (!best || String(rootAbs).length > String(best).length) {
|
|
224
|
+
best = rootAbs;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return best;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function __resolveHoistedDirectoryScope(fileAbs, groupIndexData) {
|
|
232
|
+
if (!groupIndexData || !Array.isArray(groupIndexData.groups)) {
|
|
233
|
+
return { kind: 'root' };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const rootAbs = __selectHoistedFeatureRoot(fileAbs);
|
|
237
|
+
if (!rootAbs) {
|
|
238
|
+
return { kind: 'root' };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const absoluteFile = __pathIsAbsolute(String(fileAbs))
|
|
242
|
+
? String(fileAbs)
|
|
243
|
+
: __pathResolve(process.cwd(), String(fileAbs));
|
|
244
|
+
|
|
245
|
+
const rel = __pathRelative(String(rootAbs), String(absoluteFile));
|
|
246
|
+
const segments = __normalizePathSegments(rel);
|
|
247
|
+
const dirSegments = segments.slice(0, -1);
|
|
248
|
+
if (dirSegments.length === 0) {
|
|
249
|
+
return { kind: 'root' };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const group = dirSegments[0];
|
|
253
|
+
if (!group) {
|
|
254
|
+
return { kind: 'root' };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const groupEntry = groupIndexData.groups.find((entry) => entry && entry.group === group);
|
|
258
|
+
if (!groupEntry) {
|
|
259
|
+
if (__AUTOMETA_HOISTED_FEATURE_SCOPING_STRICT) {
|
|
260
|
+
throw new Error(
|
|
261
|
+
'[autometa] Feature "' +
|
|
262
|
+
absoluteFile +
|
|
263
|
+
'" is under "' +
|
|
264
|
+
rootAbs +
|
|
265
|
+
'" and implies group "' +
|
|
266
|
+
group +
|
|
267
|
+
'", but "' +
|
|
268
|
+
group +
|
|
269
|
+
'" is not declared in config.modules.groups.'
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
return { kind: 'root' };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const moduleSegments = dirSegments.slice(1);
|
|
276
|
+
if (moduleSegments.length === 0) {
|
|
277
|
+
return { kind: 'group', group };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const modulePaths = groupEntry.modulePaths || [];
|
|
281
|
+
for (const modulePath of modulePaths) {
|
|
282
|
+
if (__startsWithSegments(moduleSegments, modulePath)) {
|
|
283
|
+
return { kind: 'module', group, modulePath };
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (__AUTOMETA_HOISTED_FEATURE_SCOPING_STRICT) {
|
|
288
|
+
throw new Error(
|
|
289
|
+
'[autometa] Feature "' +
|
|
290
|
+
absoluteFile +
|
|
291
|
+
'" is under "' +
|
|
292
|
+
rootAbs +
|
|
293
|
+
'" and implies module "' +
|
|
294
|
+
group +
|
|
295
|
+
':' +
|
|
296
|
+
moduleSegments.join(':') +
|
|
297
|
+
'", but no matching module is declared in config.modules.groups.' +
|
|
298
|
+
group +
|
|
299
|
+
'.modules.'
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return { kind: 'group', group };
|
|
304
|
+
}
|
|
305
|
+
|
|
195
306
|
function __resolveFileScope(fileAbs) {
|
|
196
307
|
if (!__AUTOMETA_GROUP_INDEX || !__AUTOMETA_GROUP_INDEX.groups) {
|
|
197
308
|
return { kind: 'root' };
|
|
@@ -246,7 +357,14 @@ function autometa() {
|
|
|
246
357
|
}
|
|
247
358
|
return { kind: 'group', group: override.group };
|
|
248
359
|
}
|
|
249
|
-
|
|
360
|
+
const fileScope = __resolveFileScope(__AUTOMETA_FEATURE_FILE);
|
|
361
|
+
if (fileScope.kind !== 'root') {
|
|
362
|
+
return fileScope;
|
|
363
|
+
}
|
|
364
|
+
if (__AUTOMETA_HOISTED_FEATURE_SCOPING_MODE !== 'directory') {
|
|
365
|
+
return fileScope;
|
|
366
|
+
}
|
|
367
|
+
return __resolveHoistedDirectoryScope(__AUTOMETA_FEATURE_FILE, __AUTOMETA_GROUP_INDEX);
|
|
250
368
|
}
|
|
251
369
|
|
|
252
370
|
function __inferEnvironmentGroup(environment) {
|
|
@@ -396,7 +514,14 @@ function autometa() {
|
|
|
396
514
|
}
|
|
397
515
|
return { kind: 'group', group: override.group };
|
|
398
516
|
}
|
|
399
|
-
|
|
517
|
+
const fileScope = resolveFileScope(__AUTOMETA_FEATURE_FILE);
|
|
518
|
+
if (fileScope.kind !== 'root') {
|
|
519
|
+
return fileScope;
|
|
520
|
+
}
|
|
521
|
+
if (__AUTOMETA_HOISTED_FEATURE_SCOPING_MODE !== 'directory') {
|
|
522
|
+
return fileScope;
|
|
523
|
+
}
|
|
524
|
+
return __resolveHoistedDirectoryScope(__AUTOMETA_FEATURE_FILE, __AUTOMETA_STEP_SCOPING);
|
|
400
525
|
}
|
|
401
526
|
|
|
402
527
|
function isVisibleStepScope(stepScope, featureScope) {
|
|
@@ -576,6 +701,24 @@ function autometa() {
|
|
|
576
701
|
}
|
|
577
702
|
};
|
|
578
703
|
}
|
|
704
|
+
function buildFeatureRootDirs(entries, options) {
|
|
705
|
+
if (!entries || entries.length === 0) {
|
|
706
|
+
return [];
|
|
707
|
+
}
|
|
708
|
+
const roots = /* @__PURE__ */ new Set();
|
|
709
|
+
for (const entry of entries) {
|
|
710
|
+
const normalized = entry.trim();
|
|
711
|
+
if (!normalized) {
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
for (const candidate of toPatterns(normalized, FEATURE_FALLBACK_GLOB)) {
|
|
715
|
+
const base = inferGlobBaseDirectory(candidate);
|
|
716
|
+
const absolute = isAbsolute(base) ? normalizeSlashes(base) : normalizeSlashes(resolve(options.configDir, base));
|
|
717
|
+
roots.add(absolute.replace(/\/+$/u, ""));
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
return Array.from(roots).sort((a, b) => b.length - a.length);
|
|
721
|
+
}
|
|
579
722
|
function buildStepGlobs(entries, options) {
|
|
580
723
|
if (!entries || entries.length === 0) {
|
|
581
724
|
return [];
|
|
@@ -600,6 +743,25 @@ function toPatterns(entry, fallbackGlob) {
|
|
|
600
743
|
}
|
|
601
744
|
return [appendGlob(entry, fallbackGlob)];
|
|
602
745
|
}
|
|
746
|
+
function inferGlobBaseDirectory(pattern) {
|
|
747
|
+
const normalized = normalizeSlashes(pattern).replace(/^!/u, "");
|
|
748
|
+
for (let i = 0; i < normalized.length; i += 1) {
|
|
749
|
+
if (!hasGlobMagic(normalized[i] ?? "")) {
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
const prefix = normalized.slice(0, i);
|
|
753
|
+
const lastSlash = prefix.lastIndexOf("/");
|
|
754
|
+
if (lastSlash <= 0) {
|
|
755
|
+
return ".";
|
|
756
|
+
}
|
|
757
|
+
const base = prefix.slice(0, lastSlash);
|
|
758
|
+
return base || ".";
|
|
759
|
+
}
|
|
760
|
+
if (hasFileExtension(normalized)) {
|
|
761
|
+
return dirname(normalized);
|
|
762
|
+
}
|
|
763
|
+
return normalized || ".";
|
|
764
|
+
}
|
|
603
765
|
function hasGlobMagic(input) {
|
|
604
766
|
return /[*?{}()[\]!,@+]/u.test(input);
|
|
605
767
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["next"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","sourcesContent":["import { resolve, isAbsolute, relative, dirname, extname } from \"path\";\nimport { existsSync } from \"fs\";\nimport type { Plugin } from \"vite\";\nimport type { Config } from \"@autometa/config\";\nimport jiti from \"jiti\";\n\nconst STEP_FALLBACK_GLOB = \"**/*.{ts,tsx,js,jsx,mjs,cjs,mts,cts}\";\n\ntype ModuleDeclaration =\n | string\n | { readonly name: string; readonly submodules?: readonly ModuleDeclaration[] | undefined };\n\nfunction flattenModuleDeclarations(\n declarations: readonly ModuleDeclaration[] | undefined,\n prefix: readonly string[] = []\n): readonly (readonly string[])[] {\n if (!declarations || declarations.length === 0) {\n return [];\n }\n\n const results: string[][] = [];\n\n for (const entry of declarations) {\n if (typeof entry === \"string\") {\n const next = [...prefix, entry];\n results.push(next);\n continue;\n }\n\n const next = [...prefix, entry.name];\n results.push(next);\n\n const nested = flattenModuleDeclarations(entry.submodules ?? undefined, next);\n for (const path of nested) {\n results.push([...path]);\n }\n }\n\n const unique = new Map<string, readonly string[]>();\n for (const path of results) {\n unique.set(path.join(\"/\"), path);\n }\n\n return Array.from(unique.values()).sort((a, b) => b.length - a.length);\n}\n\nfunction buildStepScopingData(resolvedConfig: unknown, projectRoot: string): unknown {\n const groups = (resolvedConfig as { modules?: { groups?: unknown } } | null)?.modules?.groups;\n if (!groups || typeof groups !== \"object\") {\n return null;\n }\n\n const entries = Object.entries(groups as Record<string, unknown>).map(([group, groupConfig]) => {\n const configRecord =\n groupConfig && typeof groupConfig === \"object\" ? (groupConfig as Record<string, unknown>) : {};\n const rootAbs = resolve(projectRoot, String(configRecord.root ?? \"\"));\n const modules = configRecord.modules;\n const modulePaths = Array.isArray(modules)\n ? flattenModuleDeclarations(modules as ModuleDeclaration[])\n : [];\n return { group, rootAbs, modulePaths };\n });\n\n return { groups: entries };\n}\n\nexport function autometa(): Plugin {\n let autometaConfig: Config | undefined;\n let configPath: string | undefined;\n let projectRoot: string | undefined;\n\n return {\n name: \"autometa-vitest-plugin\",\n enforce: \"pre\",\n async configResolved(viteConfig) {\n projectRoot = viteConfig.root || process.cwd();\n const loaded = await loadAutometaConfig(projectRoot);\n autometaConfig = loaded.config;\n configPath = loaded.path;\n },\n transform(code, id) {\n const cleanId = id.split(\"?\", 1)[0] ?? id;\n\n if (!cleanId.endsWith(\".feature\")) {\n return;\n }\n\n if (!autometaConfig) {\n throw new Error(\"Autometa config not found\");\n }\n\n const resolved = autometaConfig.resolve();\n const stepRoots = resolved.config.roots.steps;\n const configDir = configPath ? dirname(configPath) : projectRoot ?? process.cwd();\n const rootDir = projectRoot ?? process.cwd();\n const stepGlobs = buildStepGlobs(stepRoots, {\n configDir,\n projectRoot: rootDir,\n });\n if (stepGlobs.length === 0) {\n throw new Error(\n \"Autometa config did not resolve any step files within the current project root.\"\n );\n }\n\n const eventGlobs = buildStepGlobs(resolved.config.events, {\n configDir,\n projectRoot: rootDir,\n });\n\n const runtimeConfig = JSON.stringify(resolved.config);\n const stepScopingMode = resolved.config.modules?.stepScoping ?? \"global\";\n\n // Group/module index data is useful even when step scoping is disabled,\n // since we may need it to select the correct steps environment.\n const groupIndexData = buildStepScopingData(resolved.config, rootDir);\n const stepScopingData = stepScopingMode === \"scoped\" ? groupIndexData : null;\n\n const featureFile = cleanId;\n\n return {\n code: String.raw`\n import { describe } from 'vitest';\n import { execute } from '@autometa/vitest-executor';\n import { coordinateRunnerFeature, CucumberRunner, STEPS_ENVIRONMENT_META } from '@autometa/runner';\n import { parseGherkin } from '@autometa/gherkin';\n import { relative as __pathRelative, resolve as __pathResolve, isAbsolute as __pathIsAbsolute } from 'node:path';\n\n const __AUTOMETA_STEP_SCOPING_MODE = ${JSON.stringify(stepScopingMode)};\n const __AUTOMETA_GROUP_INDEX = ${JSON.stringify(groupIndexData)};\n const __AUTOMETA_STEP_SCOPING = ${JSON.stringify(stepScopingData)};\n const __AUTOMETA_FEATURE_FILE = ${JSON.stringify(featureFile)};\n\n const __AUTOMETA_EVENT_MODULES = ${eventGlobs.length > 0\n ? `import.meta.glob(${JSON.stringify(eventGlobs)}, { eager: true })`\n : \"{}\"};\n const stepModules = import.meta.glob(${JSON.stringify(stepGlobs)}, { eager: true });\n\n function collectCandidateModules(imported) {\n if (!imported || typeof imported !== 'object') {\n return [];\n }\n\n const modules = new Set();\n modules.add(imported);\n\n const exportedModules = imported.modules;\n if (Array.isArray(exportedModules)) {\n for (const entry of exportedModules) {\n if (entry && typeof entry === 'object') {\n modules.add(entry);\n }\n }\n }\n\n const defaultExport = imported.default;\n if (Array.isArray(defaultExport)) {\n for (const entry of defaultExport) {\n if (entry && typeof entry === 'object') {\n modules.add(entry);\n }\n }\n } else if (defaultExport && typeof defaultExport === 'object') {\n modules.add(defaultExport);\n }\n\n return Array.from(modules);\n }\n\n function isStepsEnvironment(candidate) {\n return Boolean(\n candidate &&\n typeof candidate === 'object' &&\n typeof candidate.coordinateFeature === 'function' &&\n typeof candidate.getPlan === 'function' &&\n typeof candidate.Given === 'function' &&\n typeof candidate.When === 'function' &&\n typeof candidate.Then === 'function'\n );\n }\n\n function extractStepsEnvironments(candidate) {\n const environments = [];\n\n if (!candidate || typeof candidate !== 'object') {\n return environments;\n }\n\n if (isStepsEnvironment(candidate)) {\n environments.push(candidate);\n }\n\n const stepsEnv = candidate.stepsEnvironment;\n if (isStepsEnvironment(stepsEnv)) {\n environments.push(stepsEnv);\n }\n\n const defaultExport = candidate.default;\n if (isStepsEnvironment(defaultExport)) {\n environments.push(defaultExport);\n }\n\n return environments;\n }\n\n function collectStepsEnvironments(modules) {\n const environments = new Set();\n for (const moduleExports of Object.values(modules)) {\n for (const candidate of collectCandidateModules(moduleExports)) {\n for (const env of extractStepsEnvironments(candidate)) {\n environments.add(env);\n }\n }\n }\n return Array.from(environments);\n }\n\n function __normalizePathSegments(input) {\n return String(input).replace(/\\\\/g, '/').split('/').filter(Boolean);\n }\n\n function __startsWithSegments(haystack, needle) {\n if (needle.length > haystack.length) return false;\n for (let i = 0; i < needle.length; i += 1) {\n if (haystack[i] !== needle[i]) return false;\n }\n return true;\n }\n\n function __resolveFileScope(fileAbs) {\n if (!__AUTOMETA_GROUP_INDEX || !__AUTOMETA_GROUP_INDEX.groups) {\n return { kind: 'root' };\n }\n if (String(fileAbs).startsWith('node:')) {\n return { kind: 'root' };\n }\n\n const absoluteFile = __pathIsAbsolute(String(fileAbs))\n ? String(fileAbs)\n : __pathResolve(process.cwd(), String(fileAbs));\n\n for (const entry of __AUTOMETA_GROUP_INDEX.groups) {\n const rootAbs = entry.rootAbs;\n const rel = rootAbs ? __pathRelative(rootAbs, absoluteFile) : '';\n if (rel === '' || (!rel.startsWith('..') && !rel.startsWith('../') && !rel.startsWith('..\\\\'))) {\n const segments = __normalizePathSegments(rel);\n const modulePaths = entry.modulePaths || [];\n for (const modulePath of modulePaths) {\n if (__startsWithSegments(segments, modulePath)) {\n return { kind: 'module', group: entry.group, modulePath };\n }\n }\n return { kind: 'group', group: entry.group };\n }\n }\n return { kind: 'root' };\n }\n\n function __parseScopeOverrideTag(tags) {\n if (!Array.isArray(tags)) return undefined;\n for (const tag of tags) {\n const match = String(tag).match(/^@scope(?::|=|\\()(.+?)(?:\\))?$/u);\n if (!match) continue;\n const raw = String(match[1] ?? '').trim();\n if (!raw) continue;\n const normalized = raw.replace(/\\//g, ':');\n const parts = normalized.split(':').filter(Boolean);\n const group = parts[0];\n const rest = parts.slice(1);\n if (!group) continue;\n return rest.length > 0 ? { group, modulePath: rest } : { group };\n }\n return undefined;\n }\n\n function __resolveFeatureScope(feature) {\n const override = __parseScopeOverrideTag(feature.tags);\n if (override) {\n if (override.modulePath && override.modulePath.length > 0) {\n return { kind: 'module', group: override.group, modulePath: override.modulePath };\n }\n return { kind: 'group', group: override.group };\n }\n return __resolveFileScope(__AUTOMETA_FEATURE_FILE);\n }\n\n function __inferEnvironmentGroup(environment) {\n const meta = environment && typeof environment === 'object'\n ? environment[STEPS_ENVIRONMENT_META]\n : undefined;\n if (meta && typeof meta === 'object') {\n if (meta.kind === 'group' && typeof meta.group === 'string' && meta.group.trim().length > 0) {\n return { kind: 'group', group: meta.group };\n }\n if (meta.kind === 'root') {\n return { kind: 'root' };\n }\n }\n\n const plan = environment.getPlan();\n const groups = new Set();\n for (const def of plan.stepsById.values()) {\n const file = def && def.source ? def.source.file : undefined;\n if (!file) continue;\n const scope = __resolveFileScope(file);\n if (scope.kind === 'group' || scope.kind === 'module') {\n groups.add(scope.group);\n if (groups.size > 1) return { kind: 'ambiguous' };\n }\n }\n const only = Array.from(groups.values())[0];\n return only ? { kind: 'group', group: only } : { kind: 'root' };\n }\n\n function selectStepsEnvironment(environments, feature) {\n if (!Array.isArray(environments) || environments.length === 0) {\n return undefined;\n }\n if (environments.length === 1) {\n return environments[0];\n }\n\n const featureScope = __resolveFeatureScope(feature);\n const indexed = environments\n .map((env) => ({ env, inferred: __inferEnvironmentGroup(env) }))\n .filter((entry) => entry.inferred.kind !== 'ambiguous');\n\n if (featureScope.kind === 'root') {\n const root = indexed.find((entry) => entry.inferred.kind === 'root');\n return (root ? root.env : indexed[0]?.env) ?? environments[0];\n }\n\n const match = indexed.find((entry) => entry.inferred.kind === 'group' && entry.inferred.group === featureScope.group);\n return match ? match.env : undefined;\n }\n\n function isScenario(element) {\n return Boolean(\n element &&\n typeof element === 'object' &&\n 'steps' in element &&\n !('exampleGroups' in element) &&\n !('elements' in element)\n );\n }\n\n function isScenarioOutline(element) {\n return Boolean(\n element &&\n typeof element === 'object' &&\n 'steps' in element &&\n 'exampleGroups' in element\n );\n }\n\n function isRule(element) {\n return Boolean(\n element &&\n typeof element === 'object' &&\n 'elements' in element &&\n Array.isArray(element.elements)\n );\n }\n\n function createFeatureScopePlan(feature, basePlan) {\n const allSteps = Array.from(basePlan.stepsById.values());\n\n function normalizePathSegments(input) {\n return String(input).replace(/\\\\/g, '/').split('/').filter(Boolean);\n }\n\n function startsWithSegments(haystack, needle) {\n if (needle.length > haystack.length) return false;\n for (let i = 0; i < needle.length; i += 1) {\n if (haystack[i] !== needle[i]) return false;\n }\n return true;\n }\n\n function resolveFileScope(fileAbs) {\n if (!__AUTOMETA_STEP_SCOPING || !__AUTOMETA_STEP_SCOPING.groups) {\n return { kind: 'root' };\n }\n if (String(fileAbs).startsWith('node:')) {\n return { kind: 'root' };\n }\n\n const absoluteFile = __pathIsAbsolute(String(fileAbs))\n ? String(fileAbs)\n : __pathResolve(process.cwd(), String(fileAbs));\n\n for (const entry of __AUTOMETA_STEP_SCOPING.groups) {\n const rootAbs = entry.rootAbs;\n const rel = rootAbs ? __pathRelative(rootAbs, absoluteFile) : '';\n if (rel === '' || (!rel.startsWith('..') && !rel.startsWith('../') && !rel.startsWith('..\\\\'))) {\n const segments = normalizePathSegments(rel);\n const modulePaths = entry.modulePaths || [];\n for (const modulePath of modulePaths) {\n if (startsWithSegments(segments, modulePath)) {\n return { kind: 'module', group: entry.group, modulePath };\n }\n }\n return { kind: 'group', group: entry.group };\n }\n }\n return { kind: 'root' };\n }\n\n function parseScopeOverrideTag(tags) {\n if (!Array.isArray(tags)) return undefined;\n for (const tag of tags) {\n const match = String(tag).match(/^@scope(?::|=|\\()(.+?)(?:\\))?$/u);\n if (!match) continue;\n const raw = String(match[1] ?? '').trim();\n if (!raw) continue;\n const normalized = raw.replace(/\\//g, ':');\n const parts = normalized.split(':').filter(Boolean);\n const group = parts[0];\n const rest = parts.slice(1);\n if (!group) continue;\n return rest.length > 0 ? { group, modulePath: rest } : { group };\n }\n return undefined;\n }\n\n function resolveFeatureScope() {\n const override = parseScopeOverrideTag(feature.tags);\n if (override) {\n if (override.modulePath && override.modulePath.length > 0) {\n return { kind: 'module', group: override.group, modulePath: override.modulePath };\n }\n return { kind: 'group', group: override.group };\n }\n return resolveFileScope(__AUTOMETA_FEATURE_FILE);\n }\n\n function isVisibleStepScope(stepScope, featureScope) {\n if (featureScope.kind === 'root') {\n return stepScope.kind === 'root';\n }\n if (featureScope.kind === 'group') {\n if (stepScope.kind === 'root') return true;\n return stepScope.kind === 'group' && stepScope.group === featureScope.group;\n }\n // module\n if (stepScope.kind === 'root') return true;\n if (stepScope.kind === 'group') return stepScope.group === featureScope.group;\n if (stepScope.kind === 'module') {\n return stepScope.group === featureScope.group && startsWithSegments(featureScope.modulePath, stepScope.modulePath);\n }\n return false;\n }\n\n function stepScopeRank(scope) {\n if (scope.kind === 'module') return 200 + (scope.modulePath ? scope.modulePath.length : 0);\n if (scope.kind === 'group') return 100;\n return 0;\n }\n\n const useScopedSteps = __AUTOMETA_STEP_SCOPING_MODE === 'scoped' && __AUTOMETA_STEP_SCOPING && __AUTOMETA_STEP_SCOPING.groups;\n const featureVisibilityScope = useScopedSteps ? resolveFeatureScope() : { kind: 'root' };\n const visibleSteps = useScopedSteps\n ? allSteps\n .filter((definition) => {\n const file = definition && definition.source ? definition.source.file : undefined;\n const scope = file ? resolveFileScope(file) : { kind: 'root' };\n return isVisibleStepScope(scope, featureVisibilityScope);\n })\n .sort((a, b) => {\n const aFile = a && a.source ? a.source.file : undefined;\n const bFile = b && b.source ? b.source.file : undefined;\n const aScope = aFile ? resolveFileScope(aFile) : { kind: 'root' };\n const bScope = bFile ? resolveFileScope(bFile) : { kind: 'root' };\n const delta = stepScopeRank(bScope) - stepScopeRank(aScope);\n return delta !== 0 ? delta : String(a.id).localeCompare(String(b.id));\n })\n : allSteps;\n\n const scopedStepsById = useScopedSteps\n ? (() => {\n const allowed = new Set(visibleSteps.map((s) => s.id));\n const next = new Map();\n for (const [id, def] of basePlan.stepsById.entries()) {\n if (allowed.has(id)) next.set(id, def);\n }\n return next;\n })()\n : basePlan.stepsById;\n const featureChildren = [];\n const scopesById = new Map(basePlan.scopesById);\n\n for (const element of feature.elements ?? []) {\n if (isScenario(element) || isScenarioOutline(element)) {\n const scenarioScope = {\n id: element.id ?? element.name,\n kind: isScenarioOutline(element) ? 'scenarioOutline' : 'scenario',\n name: element.name,\n mode: 'default',\n tags: element.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: [],\n pending: false,\n };\n featureChildren.push(scenarioScope);\n scopesById.set(scenarioScope.id, scenarioScope);\n continue;\n }\n\n if (isRule(element)) {\n const ruleChildren = [];\n for (const ruleElement of element.elements ?? []) {\n if (isScenario(ruleElement) || isScenarioOutline(ruleElement)) {\n const scenarioScope = {\n id: ruleElement.id ?? ruleElement.name,\n kind: isScenarioOutline(ruleElement) ? 'scenarioOutline' : 'scenario',\n name: ruleElement.name,\n mode: 'default',\n tags: ruleElement.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: [],\n pending: false,\n };\n ruleChildren.push(scenarioScope);\n scopesById.set(scenarioScope.id, scenarioScope);\n }\n }\n\n const ruleScope = {\n id: element.id ?? element.name,\n kind: 'rule',\n name: element.name,\n mode: 'default',\n tags: element.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: ruleChildren,\n pending: false,\n };\n featureChildren.push(ruleScope);\n scopesById.set(ruleScope.id, ruleScope);\n }\n }\n\n const featureScope = {\n id: feature.uri ?? feature.name,\n kind: 'feature',\n name: feature.name,\n mode: 'default',\n tags: feature.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: featureChildren,\n pending: false,\n };\n\n const existingRoot = basePlan.root;\n const updatedRoot = {\n ...existingRoot,\n children: [...existingRoot.children, featureScope],\n };\n\n scopesById.set(featureScope.id, featureScope);\n scopesById.set(updatedRoot.id, updatedRoot);\n\n const scopePlan = {\n root: updatedRoot,\n stepsById: scopedStepsById,\n hooksById: basePlan.hooksById,\n scopesById,\n };\n\n if (basePlan.worldFactory) {\n scopePlan.worldFactory = basePlan.worldFactory;\n }\n\n if (basePlan.parameterRegistry) {\n scopePlan.parameterRegistry = basePlan.parameterRegistry;\n }\n\n return scopePlan;\n }\n\n const gherkin = ${JSON.stringify(code)};\n const feature = parseGherkin(gherkin);\n const environments = collectStepsEnvironments(stepModules);\n const steps = selectStepsEnvironment(environments, feature);\n\n if (!steps) {\n 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.');\n }\n\n CucumberRunner.setSteps(steps);\n\n describe(feature.name, () => {\n const basePlan = steps.getPlan();\n const scopedPlan = createFeatureScopePlan(feature, basePlan);\n const { plan, adapter } = coordinateRunnerFeature({\n feature,\n environment: steps,\n config: ${runtimeConfig},\n plan: scopedPlan\n });\n\n execute({ plan, adapter, config: ${runtimeConfig} });\n });\n `,\n map: null,\n };\n },\n };\n}\n\ninterface StepGlobOptions {\n readonly configDir: string;\n readonly projectRoot: string;\n}\n\nfunction buildStepGlobs(\n entries: readonly string[] | undefined,\n options: StepGlobOptions\n): string[] {\n if (!entries || entries.length === 0) {\n return [];\n }\n\n const patterns = new Set<string>();\n for (const entry of entries) {\n const normalized = entry.trim();\n if (!normalized) {\n continue;\n }\n\n for (const candidate of toPatterns(normalized, STEP_FALLBACK_GLOB)) {\n const absolute = isAbsolute(candidate)\n ? normalizeSlashes(candidate)\n : normalizeSlashes(resolve(options.configDir, candidate));\n const rootRelative = toRootRelativeGlob(absolute, options.projectRoot);\n patterns.add(rootRelative);\n }\n }\n\n return Array.from(patterns);\n}\n\nfunction toPatterns(entry: string, fallbackGlob: string): string[] {\n if (hasGlobMagic(entry) || hasFileExtension(entry)) {\n return [entry];\n }\n return [appendGlob(entry, fallbackGlob)];\n}\n\nfunction hasGlobMagic(input: string): boolean {\n return /[*?{}()[\\]!,@+]/u.test(input);\n}\n\nfunction hasFileExtension(input: string): boolean {\n const normalized = normalizeSlashes(input);\n const trimmed = normalized === \"/\" ? normalized : normalized.replace(/\\/+$/u, \"\");\n if (!trimmed || trimmed === \".\" || trimmed === \"..\") {\n return false;\n }\n return Boolean(extname(trimmed));\n}\n\nfunction appendGlob(entry: string, glob: string): string {\n const normalized = normalizeSlashes(entry);\n const trimmed = normalized === \"/\" ? normalized : normalized.replace(/\\/+$/u, \"\");\n if (!trimmed || trimmed === \".\") {\n return glob;\n }\n if (trimmed === \"/\") {\n return `/${glob}`;\n }\n return `${trimmed}/${glob}`;\n}\n\nfunction toRootRelativeGlob(pattern: string, rootDir: string): string {\n const normalizedRoot = normalizeSlashes(rootDir);\n const normalizedPattern = normalizeSlashes(pattern);\n let relativePattern = normalizeSlashes(relative(normalizedRoot, normalizedPattern));\n\n if (!relativePattern || relativePattern === \".\") {\n relativePattern = \"\";\n }\n\n if (!relativePattern || relativePattern.startsWith(\"..\")) {\n return ensureGlobPrefix(normalizedPattern);\n }\n\n return ensureGlobPrefix(relativePattern);\n}\n\nfunction ensureGlobPrefix(pattern: string): string {\n if (/^[A-Za-z]:\\//u.test(pattern)) {\n return pattern;\n }\n if (pattern.startsWith(\"/\")) {\n return pattern;\n }\n return `/${pattern.replace(/^\\/+/, \"\")}`;\n}\n\nfunction normalizeSlashes(pathname: string): string {\n return pathname.replace(/\\\\/gu, \"/\");\n}\n\nasync function loadAutometaConfig(root: string): Promise<{ config: Config; path: string }> {\n const _jiti = jiti(root, { interopDefault: true });\n\n const candidates = [\n \"autometa.config.ts\",\n \"autometa.config.js\",\n \"autometa.config.mts\",\n \"autometa.config.mjs\",\n \"autometa.config.cts\",\n \"autometa.config.cjs\",\n ];\n\n for (const candidate of candidates) {\n const path = resolve(root, candidate);\n if (!existsSync(path)) {\n continue;\n }\n\n try {\n const mod = _jiti(path);\n const config = mod.default || mod;\n\n if (isConfig(config)) {\n return { config, path };\n } else {\n console.warn(`Found config at ${path} but it does not export a Config instance.`);\n }\n } catch (error: unknown) {\n console.error(`Error loading config at ${path}:`, error);\n throw error;\n }\n }\n throw new Error(\"Could not find autometa.config.{ts,js,mjs,cjs} in \" + root);\n}\n\nfunction isConfig(config: unknown): config is Config {\n return (\n typeof config === \"object\" &&\n config !== null &&\n \"resolve\" in config &&\n typeof (config as Config).resolve === \"function\" &&\n \"current\" in config &&\n typeof (config as Config).current === \"function\"\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["next"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","sourcesContent":["import { resolve, isAbsolute, relative, dirname, extname } from \"path\";\nimport { existsSync } from \"fs\";\nimport type { Plugin } from \"vite\";\nimport type { Config } from \"@autometa/config\";\nimport jiti from \"jiti\";\n\nconst STEP_FALLBACK_GLOB = \"**/*.{ts,tsx,js,jsx,mjs,cjs,mts,cts}\";\nconst FEATURE_FALLBACK_GLOB = \"**/*.feature\";\n\ntype ModuleDeclaration =\n | string\n | { readonly name: string; readonly submodules?: readonly ModuleDeclaration[] | undefined };\n\nfunction flattenModuleDeclarations(\n declarations: readonly ModuleDeclaration[] | undefined,\n prefix: readonly string[] = []\n): readonly (readonly string[])[] {\n if (!declarations || declarations.length === 0) {\n return [];\n }\n\n const results: string[][] = [];\n\n for (const entry of declarations) {\n if (typeof entry === \"string\") {\n const next = [...prefix, entry];\n results.push(next);\n continue;\n }\n\n const next = [...prefix, entry.name];\n results.push(next);\n\n const nested = flattenModuleDeclarations(entry.submodules ?? undefined, next);\n for (const path of nested) {\n results.push([...path]);\n }\n }\n\n const unique = new Map<string, readonly string[]>();\n for (const path of results) {\n unique.set(path.join(\"/\"), path);\n }\n\n return Array.from(unique.values()).sort((a, b) => b.length - a.length);\n}\n\nfunction buildStepScopingData(resolvedConfig: unknown, projectRoot: string): unknown {\n const groups = (resolvedConfig as { modules?: { groups?: unknown } } | null)?.modules?.groups;\n if (!groups || typeof groups !== \"object\") {\n return null;\n }\n\n const entries = Object.entries(groups as Record<string, unknown>).map(([group, groupConfig]) => {\n const configRecord =\n groupConfig && typeof groupConfig === \"object\" ? (groupConfig as Record<string, unknown>) : {};\n const rootAbs = resolve(projectRoot, String(configRecord.root ?? \"\"));\n const modules = configRecord.modules;\n const modulePaths = Array.isArray(modules)\n ? flattenModuleDeclarations(modules as ModuleDeclaration[])\n : [];\n return { group, rootAbs, modulePaths };\n });\n\n return { groups: entries };\n}\n\nexport function autometa(): Plugin {\n let autometaConfig: Config | undefined;\n let configPath: string | undefined;\n let projectRoot: string | undefined;\n\n return {\n name: \"autometa-vitest-plugin\",\n enforce: \"pre\",\n async configResolved(viteConfig) {\n projectRoot = viteConfig.root || process.cwd();\n const loaded = await loadAutometaConfig(projectRoot);\n autometaConfig = loaded.config;\n configPath = loaded.path;\n },\n transform(code, id) {\n const cleanId = id.split(\"?\", 1)[0] ?? id;\n\n if (!cleanId.endsWith(\".feature\")) {\n return;\n }\n\n if (!autometaConfig) {\n throw new Error(\"Autometa config not found\");\n }\n\n const resolved = autometaConfig.resolve();\n const stepRoots = resolved.config.roots.steps;\n const configDir = configPath ? dirname(configPath) : projectRoot ?? process.cwd();\n const rootDir = projectRoot ?? process.cwd();\n const stepGlobs = buildStepGlobs(stepRoots, {\n configDir,\n projectRoot: rootDir,\n });\n if (stepGlobs.length === 0) {\n throw new Error(\n \"Autometa config did not resolve any step files within the current project root.\"\n );\n }\n\n const eventGlobs = buildStepGlobs(resolved.config.events, {\n configDir,\n projectRoot: rootDir,\n });\n\n const runtimeConfig = JSON.stringify(resolved.config);\n const stepScopingMode = resolved.config.modules?.stepScoping ?? \"global\";\n const hoistedFeatureScopingMode = resolved.config.modules?.hoistedFeatures?.scope ?? \"tag\";\n const hoistedFeatureScopingStrict = resolved.config.modules?.hoistedFeatures?.strict ?? true;\n\n // Group/module index data is useful even when step scoping is disabled,\n // since we may need it to select the correct steps environment.\n const groupIndexData = buildStepScopingData(resolved.config, rootDir);\n const stepScopingData = stepScopingMode === \"scoped\" ? groupIndexData : null;\n const featureRootDirs = buildFeatureRootDirs(resolved.config.roots.features, {\n configDir,\n projectRoot: rootDir,\n });\n\n const featureFile = cleanId;\n\n return {\n code: String.raw`\n import { describe } from 'vitest';\n import { execute } from '@autometa/vitest-executor';\n import { coordinateRunnerFeature, CucumberRunner, STEPS_ENVIRONMENT_META } from '@autometa/runner';\n import { parseGherkin } from '@autometa/gherkin';\n import { relative as __pathRelative, resolve as __pathResolve, isAbsolute as __pathIsAbsolute } from 'node:path';\n\n const __AUTOMETA_STEP_SCOPING_MODE = ${JSON.stringify(stepScopingMode)};\n const __AUTOMETA_GROUP_INDEX = ${JSON.stringify(groupIndexData)};\n const __AUTOMETA_STEP_SCOPING = ${JSON.stringify(stepScopingData)};\n const __AUTOMETA_FEATURE_FILE = ${JSON.stringify(featureFile)};\n const __AUTOMETA_HOISTED_FEATURE_SCOPING_MODE = ${JSON.stringify(hoistedFeatureScopingMode)};\n const __AUTOMETA_HOISTED_FEATURE_SCOPING_STRICT = ${JSON.stringify(hoistedFeatureScopingStrict)};\n const __AUTOMETA_HOISTED_FEATURE_ROOTS = ${JSON.stringify(featureRootDirs)};\n\n const __AUTOMETA_EVENT_MODULES = ${eventGlobs.length > 0\n ? `import.meta.glob(${JSON.stringify(eventGlobs)}, { eager: true })`\n : \"{}\"};\n const stepModules = import.meta.glob(${JSON.stringify(stepGlobs)}, { eager: true });\n\n function collectCandidateModules(imported) {\n if (!imported || typeof imported !== 'object') {\n return [];\n }\n\n const modules = new Set();\n modules.add(imported);\n\n const exportedModules = imported.modules;\n if (Array.isArray(exportedModules)) {\n for (const entry of exportedModules) {\n if (entry && typeof entry === 'object') {\n modules.add(entry);\n }\n }\n }\n\n const defaultExport = imported.default;\n if (Array.isArray(defaultExport)) {\n for (const entry of defaultExport) {\n if (entry && typeof entry === 'object') {\n modules.add(entry);\n }\n }\n } else if (defaultExport && typeof defaultExport === 'object') {\n modules.add(defaultExport);\n }\n\n return Array.from(modules);\n }\n\n function isStepsEnvironment(candidate) {\n return Boolean(\n candidate &&\n typeof candidate === 'object' &&\n typeof candidate.coordinateFeature === 'function' &&\n typeof candidate.getPlan === 'function' &&\n typeof candidate.Given === 'function' &&\n typeof candidate.When === 'function' &&\n typeof candidate.Then === 'function'\n );\n }\n\n function extractStepsEnvironments(candidate) {\n const environments = [];\n\n if (!candidate || typeof candidate !== 'object') {\n return environments;\n }\n\n if (isStepsEnvironment(candidate)) {\n environments.push(candidate);\n }\n\n const stepsEnv = candidate.stepsEnvironment;\n if (isStepsEnvironment(stepsEnv)) {\n environments.push(stepsEnv);\n }\n\n const defaultExport = candidate.default;\n if (isStepsEnvironment(defaultExport)) {\n environments.push(defaultExport);\n }\n\n return environments;\n }\n\n function collectStepsEnvironments(modules) {\n const environments = new Set();\n for (const moduleExports of Object.values(modules)) {\n for (const candidate of collectCandidateModules(moduleExports)) {\n for (const env of extractStepsEnvironments(candidate)) {\n environments.add(env);\n }\n }\n }\n return Array.from(environments);\n }\n\n function __normalizePathSegments(input) {\n return String(input).replace(/\\\\/g, '/').split('/').filter(Boolean);\n }\n\n function __startsWithSegments(haystack, needle) {\n if (needle.length > haystack.length) return false;\n for (let i = 0; i < needle.length; i += 1) {\n if (haystack[i] !== needle[i]) return false;\n }\n return true;\n }\n\n function __isUnderRoot(fileAbs, rootAbs) {\n const rel = rootAbs ? __pathRelative(String(rootAbs), String(fileAbs)) : '';\n return rel === '' || (!rel.startsWith('..') && !rel.startsWith('../') && !rel.startsWith('..\\\\'));\n }\n\n function __selectHoistedFeatureRoot(fileAbs) {\n if (!Array.isArray(__AUTOMETA_HOISTED_FEATURE_ROOTS) || __AUTOMETA_HOISTED_FEATURE_ROOTS.length === 0) {\n return undefined;\n }\n\n const absoluteFile = __pathIsAbsolute(String(fileAbs))\n ? String(fileAbs)\n : __pathResolve(process.cwd(), String(fileAbs));\n\n let best;\n for (const rootAbs of __AUTOMETA_HOISTED_FEATURE_ROOTS) {\n if (!rootAbs) continue;\n if (__isUnderRoot(absoluteFile, rootAbs)) {\n if (!best || String(rootAbs).length > String(best).length) {\n best = rootAbs;\n }\n }\n }\n return best;\n }\n\n function __resolveHoistedDirectoryScope(fileAbs, groupIndexData) {\n if (!groupIndexData || !Array.isArray(groupIndexData.groups)) {\n return { kind: 'root' };\n }\n\n const rootAbs = __selectHoistedFeatureRoot(fileAbs);\n if (!rootAbs) {\n return { kind: 'root' };\n }\n\n const absoluteFile = __pathIsAbsolute(String(fileAbs))\n ? String(fileAbs)\n : __pathResolve(process.cwd(), String(fileAbs));\n\n const rel = __pathRelative(String(rootAbs), String(absoluteFile));\n const segments = __normalizePathSegments(rel);\n const dirSegments = segments.slice(0, -1);\n if (dirSegments.length === 0) {\n return { kind: 'root' };\n }\n\n const group = dirSegments[0];\n if (!group) {\n return { kind: 'root' };\n }\n\n const groupEntry = groupIndexData.groups.find((entry) => entry && entry.group === group);\n if (!groupEntry) {\n if (__AUTOMETA_HOISTED_FEATURE_SCOPING_STRICT) {\n throw new Error(\n '[autometa] Feature \"' +\n absoluteFile +\n '\" is under \"' +\n rootAbs +\n '\" and implies group \"' +\n group +\n '\", but \"' +\n group +\n '\" is not declared in config.modules.groups.'\n );\n }\n return { kind: 'root' };\n }\n\n const moduleSegments = dirSegments.slice(1);\n if (moduleSegments.length === 0) {\n return { kind: 'group', group };\n }\n\n const modulePaths = groupEntry.modulePaths || [];\n for (const modulePath of modulePaths) {\n if (__startsWithSegments(moduleSegments, modulePath)) {\n return { kind: 'module', group, modulePath };\n }\n }\n\n if (__AUTOMETA_HOISTED_FEATURE_SCOPING_STRICT) {\n throw new Error(\n '[autometa] Feature \"' +\n absoluteFile +\n '\" is under \"' +\n rootAbs +\n '\" and implies module \"' +\n group +\n ':' +\n moduleSegments.join(':') +\n '\", but no matching module is declared in config.modules.groups.' +\n group +\n '.modules.'\n );\n }\n\n return { kind: 'group', group };\n }\n\n function __resolveFileScope(fileAbs) {\n if (!__AUTOMETA_GROUP_INDEX || !__AUTOMETA_GROUP_INDEX.groups) {\n return { kind: 'root' };\n }\n if (String(fileAbs).startsWith('node:')) {\n return { kind: 'root' };\n }\n\n const absoluteFile = __pathIsAbsolute(String(fileAbs))\n ? String(fileAbs)\n : __pathResolve(process.cwd(), String(fileAbs));\n\n for (const entry of __AUTOMETA_GROUP_INDEX.groups) {\n const rootAbs = entry.rootAbs;\n const rel = rootAbs ? __pathRelative(rootAbs, absoluteFile) : '';\n if (rel === '' || (!rel.startsWith('..') && !rel.startsWith('../') && !rel.startsWith('..\\\\'))) {\n const segments = __normalizePathSegments(rel);\n const modulePaths = entry.modulePaths || [];\n for (const modulePath of modulePaths) {\n if (__startsWithSegments(segments, modulePath)) {\n return { kind: 'module', group: entry.group, modulePath };\n }\n }\n return { kind: 'group', group: entry.group };\n }\n }\n return { kind: 'root' };\n }\n\n function __parseScopeOverrideTag(tags) {\n if (!Array.isArray(tags)) return undefined;\n for (const tag of tags) {\n const match = String(tag).match(/^@scope(?::|=|\\()(.+?)(?:\\))?$/u);\n if (!match) continue;\n const raw = String(match[1] ?? '').trim();\n if (!raw) continue;\n const normalized = raw.replace(/\\//g, ':');\n const parts = normalized.split(':').filter(Boolean);\n const group = parts[0];\n const rest = parts.slice(1);\n if (!group) continue;\n return rest.length > 0 ? { group, modulePath: rest } : { group };\n }\n return undefined;\n }\n\n function __resolveFeatureScope(feature) {\n const override = __parseScopeOverrideTag(feature.tags);\n if (override) {\n if (override.modulePath && override.modulePath.length > 0) {\n return { kind: 'module', group: override.group, modulePath: override.modulePath };\n }\n return { kind: 'group', group: override.group };\n }\n const fileScope = __resolveFileScope(__AUTOMETA_FEATURE_FILE);\n if (fileScope.kind !== 'root') {\n return fileScope;\n }\n if (__AUTOMETA_HOISTED_FEATURE_SCOPING_MODE !== 'directory') {\n return fileScope;\n }\n return __resolveHoistedDirectoryScope(__AUTOMETA_FEATURE_FILE, __AUTOMETA_GROUP_INDEX);\n }\n\n function __inferEnvironmentGroup(environment) {\n const meta = environment && typeof environment === 'object'\n ? environment[STEPS_ENVIRONMENT_META]\n : undefined;\n if (meta && typeof meta === 'object') {\n if (meta.kind === 'group' && typeof meta.group === 'string' && meta.group.trim().length > 0) {\n return { kind: 'group', group: meta.group };\n }\n if (meta.kind === 'root') {\n return { kind: 'root' };\n }\n }\n\n const plan = environment.getPlan();\n const groups = new Set();\n for (const def of plan.stepsById.values()) {\n const file = def && def.source ? def.source.file : undefined;\n if (!file) continue;\n const scope = __resolveFileScope(file);\n if (scope.kind === 'group' || scope.kind === 'module') {\n groups.add(scope.group);\n if (groups.size > 1) return { kind: 'ambiguous' };\n }\n }\n const only = Array.from(groups.values())[0];\n return only ? { kind: 'group', group: only } : { kind: 'root' };\n }\n\n function selectStepsEnvironment(environments, feature) {\n if (!Array.isArray(environments) || environments.length === 0) {\n return undefined;\n }\n if (environments.length === 1) {\n return environments[0];\n }\n\n const featureScope = __resolveFeatureScope(feature);\n const indexed = environments\n .map((env) => ({ env, inferred: __inferEnvironmentGroup(env) }))\n .filter((entry) => entry.inferred.kind !== 'ambiguous');\n\n if (featureScope.kind === 'root') {\n const root = indexed.find((entry) => entry.inferred.kind === 'root');\n return (root ? root.env : indexed[0]?.env) ?? environments[0];\n }\n\n const match = indexed.find((entry) => entry.inferred.kind === 'group' && entry.inferred.group === featureScope.group);\n return match ? match.env : undefined;\n }\n\n function isScenario(element) {\n return Boolean(\n element &&\n typeof element === 'object' &&\n 'steps' in element &&\n !('exampleGroups' in element) &&\n !('elements' in element)\n );\n }\n\n function isScenarioOutline(element) {\n return Boolean(\n element &&\n typeof element === 'object' &&\n 'steps' in element &&\n 'exampleGroups' in element\n );\n }\n\n function isRule(element) {\n return Boolean(\n element &&\n typeof element === 'object' &&\n 'elements' in element &&\n Array.isArray(element.elements)\n );\n }\n\n function createFeatureScopePlan(feature, basePlan) {\n const allSteps = Array.from(basePlan.stepsById.values());\n\n function normalizePathSegments(input) {\n return String(input).replace(/\\\\/g, '/').split('/').filter(Boolean);\n }\n\n function startsWithSegments(haystack, needle) {\n if (needle.length > haystack.length) return false;\n for (let i = 0; i < needle.length; i += 1) {\n if (haystack[i] !== needle[i]) return false;\n }\n return true;\n }\n\n function resolveFileScope(fileAbs) {\n if (!__AUTOMETA_STEP_SCOPING || !__AUTOMETA_STEP_SCOPING.groups) {\n return { kind: 'root' };\n }\n if (String(fileAbs).startsWith('node:')) {\n return { kind: 'root' };\n }\n\n const absoluteFile = __pathIsAbsolute(String(fileAbs))\n ? String(fileAbs)\n : __pathResolve(process.cwd(), String(fileAbs));\n\n for (const entry of __AUTOMETA_STEP_SCOPING.groups) {\n const rootAbs = entry.rootAbs;\n const rel = rootAbs ? __pathRelative(rootAbs, absoluteFile) : '';\n if (rel === '' || (!rel.startsWith('..') && !rel.startsWith('../') && !rel.startsWith('..\\\\'))) {\n const segments = normalizePathSegments(rel);\n const modulePaths = entry.modulePaths || [];\n for (const modulePath of modulePaths) {\n if (startsWithSegments(segments, modulePath)) {\n return { kind: 'module', group: entry.group, modulePath };\n }\n }\n return { kind: 'group', group: entry.group };\n }\n }\n return { kind: 'root' };\n }\n\n function parseScopeOverrideTag(tags) {\n if (!Array.isArray(tags)) return undefined;\n for (const tag of tags) {\n const match = String(tag).match(/^@scope(?::|=|\\()(.+?)(?:\\))?$/u);\n if (!match) continue;\n const raw = String(match[1] ?? '').trim();\n if (!raw) continue;\n const normalized = raw.replace(/\\//g, ':');\n const parts = normalized.split(':').filter(Boolean);\n const group = parts[0];\n const rest = parts.slice(1);\n if (!group) continue;\n return rest.length > 0 ? { group, modulePath: rest } : { group };\n }\n return undefined;\n }\n\n function resolveFeatureScope() {\n const override = parseScopeOverrideTag(feature.tags);\n if (override) {\n if (override.modulePath && override.modulePath.length > 0) {\n return { kind: 'module', group: override.group, modulePath: override.modulePath };\n }\n return { kind: 'group', group: override.group };\n }\n const fileScope = resolveFileScope(__AUTOMETA_FEATURE_FILE);\n if (fileScope.kind !== 'root') {\n return fileScope;\n }\n if (__AUTOMETA_HOISTED_FEATURE_SCOPING_MODE !== 'directory') {\n return fileScope;\n }\n return __resolveHoistedDirectoryScope(__AUTOMETA_FEATURE_FILE, __AUTOMETA_STEP_SCOPING);\n }\n\n function isVisibleStepScope(stepScope, featureScope) {\n if (featureScope.kind === 'root') {\n return stepScope.kind === 'root';\n }\n if (featureScope.kind === 'group') {\n if (stepScope.kind === 'root') return true;\n return stepScope.kind === 'group' && stepScope.group === featureScope.group;\n }\n // module\n if (stepScope.kind === 'root') return true;\n if (stepScope.kind === 'group') return stepScope.group === featureScope.group;\n if (stepScope.kind === 'module') {\n return stepScope.group === featureScope.group && startsWithSegments(featureScope.modulePath, stepScope.modulePath);\n }\n return false;\n }\n\n function stepScopeRank(scope) {\n if (scope.kind === 'module') return 200 + (scope.modulePath ? scope.modulePath.length : 0);\n if (scope.kind === 'group') return 100;\n return 0;\n }\n\n const useScopedSteps = __AUTOMETA_STEP_SCOPING_MODE === 'scoped' && __AUTOMETA_STEP_SCOPING && __AUTOMETA_STEP_SCOPING.groups;\n const featureVisibilityScope = useScopedSteps ? resolveFeatureScope() : { kind: 'root' };\n const visibleSteps = useScopedSteps\n ? allSteps\n .filter((definition) => {\n const file = definition && definition.source ? definition.source.file : undefined;\n const scope = file ? resolveFileScope(file) : { kind: 'root' };\n return isVisibleStepScope(scope, featureVisibilityScope);\n })\n .sort((a, b) => {\n const aFile = a && a.source ? a.source.file : undefined;\n const bFile = b && b.source ? b.source.file : undefined;\n const aScope = aFile ? resolveFileScope(aFile) : { kind: 'root' };\n const bScope = bFile ? resolveFileScope(bFile) : { kind: 'root' };\n const delta = stepScopeRank(bScope) - stepScopeRank(aScope);\n return delta !== 0 ? delta : String(a.id).localeCompare(String(b.id));\n })\n : allSteps;\n\n const scopedStepsById = useScopedSteps\n ? (() => {\n const allowed = new Set(visibleSteps.map((s) => s.id));\n const next = new Map();\n for (const [id, def] of basePlan.stepsById.entries()) {\n if (allowed.has(id)) next.set(id, def);\n }\n return next;\n })()\n : basePlan.stepsById;\n const featureChildren = [];\n const scopesById = new Map(basePlan.scopesById);\n\n for (const element of feature.elements ?? []) {\n if (isScenario(element) || isScenarioOutline(element)) {\n const scenarioScope = {\n id: element.id ?? element.name,\n kind: isScenarioOutline(element) ? 'scenarioOutline' : 'scenario',\n name: element.name,\n mode: 'default',\n tags: element.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: [],\n pending: false,\n };\n featureChildren.push(scenarioScope);\n scopesById.set(scenarioScope.id, scenarioScope);\n continue;\n }\n\n if (isRule(element)) {\n const ruleChildren = [];\n for (const ruleElement of element.elements ?? []) {\n if (isScenario(ruleElement) || isScenarioOutline(ruleElement)) {\n const scenarioScope = {\n id: ruleElement.id ?? ruleElement.name,\n kind: isScenarioOutline(ruleElement) ? 'scenarioOutline' : 'scenario',\n name: ruleElement.name,\n mode: 'default',\n tags: ruleElement.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: [],\n pending: false,\n };\n ruleChildren.push(scenarioScope);\n scopesById.set(scenarioScope.id, scenarioScope);\n }\n }\n\n const ruleScope = {\n id: element.id ?? element.name,\n kind: 'rule',\n name: element.name,\n mode: 'default',\n tags: element.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: ruleChildren,\n pending: false,\n };\n featureChildren.push(ruleScope);\n scopesById.set(ruleScope.id, ruleScope);\n }\n }\n\n const featureScope = {\n id: feature.uri ?? feature.name,\n kind: 'feature',\n name: feature.name,\n mode: 'default',\n tags: feature.tags ?? [],\n steps: visibleSteps,\n hooks: [],\n children: featureChildren,\n pending: false,\n };\n\n const existingRoot = basePlan.root;\n const updatedRoot = {\n ...existingRoot,\n children: [...existingRoot.children, featureScope],\n };\n\n scopesById.set(featureScope.id, featureScope);\n scopesById.set(updatedRoot.id, updatedRoot);\n\n const scopePlan = {\n root: updatedRoot,\n stepsById: scopedStepsById,\n hooksById: basePlan.hooksById,\n scopesById,\n };\n\n if (basePlan.worldFactory) {\n scopePlan.worldFactory = basePlan.worldFactory;\n }\n\n if (basePlan.parameterRegistry) {\n scopePlan.parameterRegistry = basePlan.parameterRegistry;\n }\n\n return scopePlan;\n }\n\n const gherkin = ${JSON.stringify(code)};\n const feature = parseGherkin(gherkin);\n const environments = collectStepsEnvironments(stepModules);\n const steps = selectStepsEnvironment(environments, feature);\n\n if (!steps) {\n 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.');\n }\n\n CucumberRunner.setSteps(steps);\n\n describe(feature.name, () => {\n const basePlan = steps.getPlan();\n const scopedPlan = createFeatureScopePlan(feature, basePlan);\n const { plan, adapter } = coordinateRunnerFeature({\n feature,\n environment: steps,\n config: ${runtimeConfig},\n plan: scopedPlan\n });\n\n execute({ plan, adapter, config: ${runtimeConfig} });\n });\n `,\n map: null,\n };\n },\n };\n}\n\ninterface StepGlobOptions {\n readonly configDir: string;\n readonly projectRoot: string;\n}\n\nfunction buildFeatureRootDirs(\n entries: readonly string[] | undefined,\n options: StepGlobOptions\n): string[] {\n if (!entries || entries.length === 0) {\n return [];\n }\n\n const roots = new Set<string>();\n for (const entry of entries) {\n const normalized = entry.trim();\n if (!normalized) {\n continue;\n }\n\n for (const candidate of toPatterns(normalized, FEATURE_FALLBACK_GLOB)) {\n const base = inferGlobBaseDirectory(candidate);\n const absolute = isAbsolute(base)\n ? normalizeSlashes(base)\n : normalizeSlashes(resolve(options.configDir, base));\n roots.add(absolute.replace(/\\/+$/u, \"\"));\n }\n }\n\n return Array.from(roots).sort((a, b) => b.length - a.length);\n}\n\nfunction buildStepGlobs(\n entries: readonly string[] | undefined,\n options: StepGlobOptions\n): string[] {\n if (!entries || entries.length === 0) {\n return [];\n }\n\n const patterns = new Set<string>();\n for (const entry of entries) {\n const normalized = entry.trim();\n if (!normalized) {\n continue;\n }\n\n for (const candidate of toPatterns(normalized, STEP_FALLBACK_GLOB)) {\n const absolute = isAbsolute(candidate)\n ? normalizeSlashes(candidate)\n : normalizeSlashes(resolve(options.configDir, candidate));\n const rootRelative = toRootRelativeGlob(absolute, options.projectRoot);\n patterns.add(rootRelative);\n }\n }\n\n return Array.from(patterns);\n}\n\nfunction toPatterns(entry: string, fallbackGlob: string): string[] {\n if (hasGlobMagic(entry) || hasFileExtension(entry)) {\n return [entry];\n }\n return [appendGlob(entry, fallbackGlob)];\n}\n\nfunction inferGlobBaseDirectory(pattern: string): string {\n const normalized = normalizeSlashes(pattern).replace(/^!/u, \"\");\n\n for (let i = 0; i < normalized.length; i += 1) {\n if (!hasGlobMagic(normalized[i] ?? \"\")) {\n continue;\n }\n\n const prefix = normalized.slice(0, i);\n const lastSlash = prefix.lastIndexOf(\"/\");\n if (lastSlash <= 0) {\n return \".\";\n }\n const base = prefix.slice(0, lastSlash);\n return base || \".\";\n }\n\n if (hasFileExtension(normalized)) {\n return dirname(normalized);\n }\n\n return normalized || \".\";\n}\n\nfunction hasGlobMagic(input: string): boolean {\n return /[*?{}()[\\]!,@+]/u.test(input);\n}\n\nfunction hasFileExtension(input: string): boolean {\n const normalized = normalizeSlashes(input);\n const trimmed = normalized === \"/\" ? normalized : normalized.replace(/\\/+$/u, \"\");\n if (!trimmed || trimmed === \".\" || trimmed === \"..\") {\n return false;\n }\n return Boolean(extname(trimmed));\n}\n\nfunction appendGlob(entry: string, glob: string): string {\n const normalized = normalizeSlashes(entry);\n const trimmed = normalized === \"/\" ? normalized : normalized.replace(/\\/+$/u, \"\");\n if (!trimmed || trimmed === \".\") {\n return glob;\n }\n if (trimmed === \"/\") {\n return `/${glob}`;\n }\n return `${trimmed}/${glob}`;\n}\n\nfunction toRootRelativeGlob(pattern: string, rootDir: string): string {\n const normalizedRoot = normalizeSlashes(rootDir);\n const normalizedPattern = normalizeSlashes(pattern);\n let relativePattern = normalizeSlashes(relative(normalizedRoot, normalizedPattern));\n\n if (!relativePattern || relativePattern === \".\") {\n relativePattern = \"\";\n }\n\n if (!relativePattern || relativePattern.startsWith(\"..\")) {\n return ensureGlobPrefix(normalizedPattern);\n }\n\n return ensureGlobPrefix(relativePattern);\n}\n\nfunction ensureGlobPrefix(pattern: string): string {\n if (/^[A-Za-z]:\\//u.test(pattern)) {\n return pattern;\n }\n if (pattern.startsWith(\"/\")) {\n return pattern;\n }\n return `/${pattern.replace(/^\\/+/, \"\")}`;\n}\n\nfunction normalizeSlashes(pathname: string): string {\n return pathname.replace(/\\\\/gu, \"/\");\n}\n\nasync function loadAutometaConfig(root: string): Promise<{ config: Config; path: string }> {\n const _jiti = jiti(root, { interopDefault: true });\n\n const candidates = [\n \"autometa.config.ts\",\n \"autometa.config.js\",\n \"autometa.config.mts\",\n \"autometa.config.mjs\",\n \"autometa.config.cts\",\n \"autometa.config.cjs\",\n ];\n\n for (const candidate of candidates) {\n const path = resolve(root, candidate);\n if (!existsSync(path)) {\n continue;\n }\n\n try {\n const mod = _jiti(path);\n const config = mod.default || mod;\n\n if (isConfig(config)) {\n return { config, path };\n } else {\n console.warn(`Found config at ${path} but it does not export a Config instance.`);\n }\n } catch (error: unknown) {\n console.error(`Error loading config at ${path}:`, error);\n throw error;\n }\n }\n throw new Error(\"Could not find autometa.config.{ts,js,mjs,cjs} in \" + root);\n}\n\nfunction isConfig(config: unknown): config is Config {\n return (\n typeof config === \"object\" &&\n config !== null &&\n \"resolve\" in config &&\n typeof (config as Config).resolve === \"function\" &&\n \"current\" in config &&\n typeof (config as Config).current === \"function\"\n );\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autometa/vitest-plugins",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.4",
|
|
4
4
|
"description": "Vitest plugin for Autometa - transforms .feature files into test suites",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"jiti": "^1.21.0",
|
|
21
|
-
"@autometa/config": "1.0.0-rc.
|
|
21
|
+
"@autometa/config": "1.0.0-rc.3"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^18.11.18",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"vite": "^5.0.0",
|
|
37
37
|
"vitest": "^1.0.0",
|
|
38
38
|
"@autometa/gherkin": "1.0.0-rc.2",
|
|
39
|
-
"@autometa/
|
|
40
|
-
"@autometa/
|
|
39
|
+
"@autometa/vitest-executor": "1.0.0-rc.4",
|
|
40
|
+
"@autometa/runner": "1.0.0-rc.4"
|
|
41
41
|
},
|
|
42
42
|
"scripts": {
|
|
43
43
|
"type-check": "tsc --noEmit",
|