@autometa/cli 1.0.0-rc.5 → 1.0.0-rc.7
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/bin.cjs +233 -20
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +233 -20
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +233 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +233 -20
- package/dist/index.js.map +1 -1
- package/dist/orchestrator/index.d.ts +2 -0
- package/package.json +10 -10
package/dist/bin.cjs
CHANGED
|
@@ -2010,19 +2010,112 @@ function detectRunner(config, cwd) {
|
|
|
2010
2010
|
}
|
|
2011
2011
|
return "default";
|
|
2012
2012
|
}
|
|
2013
|
+
function normalizeGlobLikePath(input) {
|
|
2014
|
+
return input.replace(/\\/g, "/").replace(/^\.\//u, "");
|
|
2015
|
+
}
|
|
2016
|
+
function patternIsUnderRoot(pattern, root) {
|
|
2017
|
+
const p = normalizeGlobLikePath(pattern);
|
|
2018
|
+
const r = normalizeGlobLikePath(root).replace(/\/+$/u, "");
|
|
2019
|
+
if (!r) {
|
|
2020
|
+
return false;
|
|
2021
|
+
}
|
|
2022
|
+
return p === r || p.startsWith(`${r}/`);
|
|
2023
|
+
}
|
|
2024
|
+
function filterPatternsByGroupsAndModules(options) {
|
|
2025
|
+
const { patterns, groups, modules, config, verbose } = options;
|
|
2026
|
+
const hasGroupSelection = (groups?.length ?? 0) > 0;
|
|
2027
|
+
const hasModuleSelection = (modules?.length ?? 0) > 0;
|
|
2028
|
+
const hasAnySelection = hasGroupSelection || hasModuleSelection;
|
|
2029
|
+
const hasExplicitPatterns = patterns.length > 0;
|
|
2030
|
+
if (hasExplicitPatterns) {
|
|
2031
|
+
if (hasAnySelection && verbose) {
|
|
2032
|
+
const shouldWarn = (() => {
|
|
2033
|
+
const configGroups = config.modules?.groups;
|
|
2034
|
+
if (!configGroups) {
|
|
2035
|
+
return true;
|
|
2036
|
+
}
|
|
2037
|
+
if (hasGroupSelection) {
|
|
2038
|
+
const allowedGroups = new Set(groups);
|
|
2039
|
+
const allowedRoots = Object.entries(configGroups).filter(([group]) => allowedGroups.has(group)).map(([, groupConfig]) => groupConfig.root);
|
|
2040
|
+
if (allowedRoots.length > 0) {
|
|
2041
|
+
return patterns.some(
|
|
2042
|
+
(pattern) => !allowedRoots.some((root) => patternIsUnderRoot(pattern, root))
|
|
2043
|
+
);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
return true;
|
|
2047
|
+
})();
|
|
2048
|
+
if (shouldWarn) {
|
|
2049
|
+
console.warn(
|
|
2050
|
+
"[autometa] Note: when you pass explicit feature patterns, they are used as-is. Group/module filters (-g/-m) affect module/step loading, but do not automatically filter your feature patterns."
|
|
2051
|
+
);
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
return patterns;
|
|
2055
|
+
}
|
|
2056
|
+
const roots = [...config.roots.features];
|
|
2057
|
+
if (hasAnySelection && config.modules?.groups) {
|
|
2058
|
+
const allowedGroups = hasGroupSelection ? new Set(groups) : new Set(Object.keys(config.modules.groups));
|
|
2059
|
+
if (hasModuleSelection && modules && modules.length > 0) {
|
|
2060
|
+
const modulePatterns = [];
|
|
2061
|
+
const relativeRoots = config.modules.relativeRoots?.features ?? [".features/**/*.feature"];
|
|
2062
|
+
for (const [groupName, groupConfig] of Object.entries(config.modules.groups)) {
|
|
2063
|
+
if (!allowedGroups.has(groupName)) {
|
|
2064
|
+
continue;
|
|
2065
|
+
}
|
|
2066
|
+
const groupRoot = groupConfig.root;
|
|
2067
|
+
const groupModules = groupConfig.modules ?? [];
|
|
2068
|
+
for (const moduleName of modules) {
|
|
2069
|
+
const moduleExists = Array.isArray(groupModules) && groupModules.some((m) => {
|
|
2070
|
+
if (typeof m === "string") {
|
|
2071
|
+
return m === moduleName || m.endsWith(`/${moduleName}`) || m.endsWith(`:${moduleName}`);
|
|
2072
|
+
}
|
|
2073
|
+
return false;
|
|
2074
|
+
});
|
|
2075
|
+
if (moduleExists) {
|
|
2076
|
+
for (const relativeRoot of relativeRoots) {
|
|
2077
|
+
const normalizedRelative = relativeRoot.replace(/^\.\//, "");
|
|
2078
|
+
const modulePath = path2.join(groupRoot, moduleName, normalizedRelative);
|
|
2079
|
+
modulePatterns.push(modulePath);
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
if (modulePatterns.length > 0) {
|
|
2085
|
+
return modulePatterns;
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
const allowedRoots = Object.entries(config.modules.groups).filter(([group]) => allowedGroups.has(group)).map(([, groupConfig]) => groupConfig.root);
|
|
2089
|
+
const filtered = roots.filter(
|
|
2090
|
+
(pattern) => allowedRoots.some((root) => patternIsUnderRoot(pattern, root))
|
|
2091
|
+
);
|
|
2092
|
+
if (filtered.length > 0) {
|
|
2093
|
+
return filtered;
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
return roots;
|
|
2097
|
+
}
|
|
2013
2098
|
async function orchestrate(options) {
|
|
2014
|
-
const { cwd, config, patterns = [], runnerArgs = [], dryRun = false, watch = false, verbose = false } = options;
|
|
2099
|
+
const { cwd, config, patterns = [], runnerArgs = [], dryRun = false, watch = false, verbose = false, groups, modules } = options;
|
|
2015
2100
|
const runner = detectRunner(config, cwd);
|
|
2016
2101
|
if (verbose) {
|
|
2017
2102
|
console.log(`[autometa] Detected runner: ${runner}`);
|
|
2018
2103
|
}
|
|
2104
|
+
const filteredPatterns = filterPatternsByGroupsAndModules({
|
|
2105
|
+
patterns,
|
|
2106
|
+
groups,
|
|
2107
|
+
modules,
|
|
2108
|
+
config,
|
|
2109
|
+
cwd,
|
|
2110
|
+
verbose
|
|
2111
|
+
});
|
|
2019
2112
|
switch (runner) {
|
|
2020
2113
|
case "vitest":
|
|
2021
|
-
return spawnVitest({ cwd, patterns, runnerArgs, dryRun, watch, verbose });
|
|
2114
|
+
return spawnVitest({ cwd, patterns: filteredPatterns, runnerArgs, dryRun, watch, verbose });
|
|
2022
2115
|
case "jest":
|
|
2023
|
-
return spawnJest({ cwd, patterns, runnerArgs, dryRun, watch, verbose });
|
|
2116
|
+
return spawnJest({ cwd, patterns: filteredPatterns, runnerArgs, dryRun, watch, verbose });
|
|
2024
2117
|
case "playwright":
|
|
2025
|
-
return spawnPlaywright({ cwd, patterns, runnerArgs, dryRun, watch, verbose });
|
|
2118
|
+
return spawnPlaywright({ cwd, patterns: filteredPatterns, runnerArgs, dryRun, watch, verbose });
|
|
2026
2119
|
case "default":
|
|
2027
2120
|
return { success: true, exitCode: 0, runner: "default" };
|
|
2028
2121
|
}
|
|
@@ -2034,7 +2127,8 @@ async function spawnVitest(options) {
|
|
|
2034
2127
|
args.push("run");
|
|
2035
2128
|
}
|
|
2036
2129
|
if (patterns.length > 0) {
|
|
2037
|
-
|
|
2130
|
+
const expanded = await expandFilePatterns(patterns, cwd);
|
|
2131
|
+
args.push(...expanded.length > 0 ? expanded : patterns);
|
|
2038
2132
|
}
|
|
2039
2133
|
if (dryRun) {
|
|
2040
2134
|
args.push("--passWithNoTests");
|
|
@@ -2136,17 +2230,53 @@ var STEP_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"
|
|
|
2136
2230
|
var STEP_FALLBACK_GLOB = "**/*.{ts,tsx,js,jsx,mjs,cjs,mts,cts}";
|
|
2137
2231
|
var FEATURE_FALLBACK_GLOB = "**/*.feature";
|
|
2138
2232
|
var ROOT_LOAD_ORDER = ["parameterTypes", "support", "hooks", "app"];
|
|
2139
|
-
|
|
2233
|
+
var GLOB_MAGIC_CHARACTERS = /* @__PURE__ */ new Set(["*", "?", "[", "]", "{", "}", "(", ")", "!"]);
|
|
2234
|
+
function normalizeGlobLikePath2(input) {
|
|
2140
2235
|
return input.replace(/\\/g, "/").replace(/^\.\//u, "");
|
|
2141
2236
|
}
|
|
2142
|
-
function
|
|
2143
|
-
const p =
|
|
2144
|
-
const r =
|
|
2237
|
+
function patternIsUnderRoot2(pattern, root) {
|
|
2238
|
+
const p = normalizeGlobLikePath2(pattern);
|
|
2239
|
+
const r = normalizeGlobLikePath2(root).replace(/\/+$/u, "");
|
|
2145
2240
|
if (!r) {
|
|
2146
2241
|
return false;
|
|
2147
2242
|
}
|
|
2148
2243
|
return p === r || p.startsWith(`${r}/`);
|
|
2149
2244
|
}
|
|
2245
|
+
function inferGlobBaseDirectory(pattern) {
|
|
2246
|
+
const normalized = normalizeGlobLikePath2(pattern).replace(/^!/u, "");
|
|
2247
|
+
for (let i = 0; i < normalized.length; i += 1) {
|
|
2248
|
+
if (!GLOB_MAGIC_CHARACTERS.has(normalized[i] ?? "")) {
|
|
2249
|
+
continue;
|
|
2250
|
+
}
|
|
2251
|
+
const prefix = normalized.slice(0, i);
|
|
2252
|
+
const lastSlash = prefix.lastIndexOf("/");
|
|
2253
|
+
if (lastSlash <= 0) {
|
|
2254
|
+
return ".";
|
|
2255
|
+
}
|
|
2256
|
+
const base = prefix.slice(0, lastSlash);
|
|
2257
|
+
return base || ".";
|
|
2258
|
+
}
|
|
2259
|
+
if (path2.extname(normalized)) {
|
|
2260
|
+
return path2.dirname(normalized);
|
|
2261
|
+
}
|
|
2262
|
+
return normalized || ".";
|
|
2263
|
+
}
|
|
2264
|
+
function inferFeatureRootDirectories(patterns, cwd) {
|
|
2265
|
+
if (!patterns || patterns.length === 0) {
|
|
2266
|
+
return [];
|
|
2267
|
+
}
|
|
2268
|
+
const roots = /* @__PURE__ */ new Set();
|
|
2269
|
+
for (const entry of patterns) {
|
|
2270
|
+
const trimmed = entry.trim();
|
|
2271
|
+
if (!trimmed) {
|
|
2272
|
+
continue;
|
|
2273
|
+
}
|
|
2274
|
+
const base = hasGlobMagic(trimmed) ? inferGlobBaseDirectory(trimmed) : trimmed;
|
|
2275
|
+
const resolved = path2.resolve(cwd, base);
|
|
2276
|
+
roots.add(resolved.replace(/\/+$/u, ""));
|
|
2277
|
+
}
|
|
2278
|
+
return Array.from(roots).sort((a, b) => b.length - a.length);
|
|
2279
|
+
}
|
|
2150
2280
|
function flattenModuleDeclarations(declarations, prefix = []) {
|
|
2151
2281
|
if (!declarations || declarations.length === 0) {
|
|
2152
2282
|
return [];
|
|
@@ -2230,6 +2360,48 @@ function resolveFileScope(fileAbs, groupIndex) {
|
|
|
2230
2360
|
}
|
|
2231
2361
|
return { kind: "root" };
|
|
2232
2362
|
}
|
|
2363
|
+
function resolveHoistedDirectoryScope(options) {
|
|
2364
|
+
const rootAbs = options.hoistedFeatureRootsAbs.find(
|
|
2365
|
+
(root) => isPathUnderRoot(options.featureAbsPath, root)
|
|
2366
|
+
);
|
|
2367
|
+
if (!rootAbs) {
|
|
2368
|
+
return { kind: "root" };
|
|
2369
|
+
}
|
|
2370
|
+
const rel = path2.relative(rootAbs, options.featureAbsPath);
|
|
2371
|
+
const segments = normalizePathSegments(rel);
|
|
2372
|
+
const dirSegments = segments.slice(0, -1);
|
|
2373
|
+
if (dirSegments.length === 0) {
|
|
2374
|
+
return { kind: "root" };
|
|
2375
|
+
}
|
|
2376
|
+
const group = dirSegments[0];
|
|
2377
|
+
if (!group) {
|
|
2378
|
+
return { kind: "root" };
|
|
2379
|
+
}
|
|
2380
|
+
const groupEntry = options.groupIndex.find((entry) => entry.group === group);
|
|
2381
|
+
if (!groupEntry) {
|
|
2382
|
+
if (options.strict) {
|
|
2383
|
+
throw new Error(
|
|
2384
|
+
`[autometa] Feature "${options.featureAbsPath}" is under "${rootAbs}" and implies group "${group}", but "${group}" is not declared in config.modules.groups.`
|
|
2385
|
+
);
|
|
2386
|
+
}
|
|
2387
|
+
return { kind: "root" };
|
|
2388
|
+
}
|
|
2389
|
+
const moduleSegments = dirSegments.slice(1);
|
|
2390
|
+
if (moduleSegments.length === 0) {
|
|
2391
|
+
return { kind: "group", group };
|
|
2392
|
+
}
|
|
2393
|
+
for (const modulePath of groupEntry.modulePaths) {
|
|
2394
|
+
if (startsWithSegments(moduleSegments, modulePath)) {
|
|
2395
|
+
return { kind: "module", group, modulePath };
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
if (options.strict) {
|
|
2399
|
+
throw new Error(
|
|
2400
|
+
`[autometa] Feature "${options.featureAbsPath}" is under "${rootAbs}" and implies module "${group}:${moduleSegments.join(":")}", but no matching module is declared in config.modules.groups.${group}.modules.`
|
|
2401
|
+
);
|
|
2402
|
+
}
|
|
2403
|
+
return { kind: "group", group };
|
|
2404
|
+
}
|
|
2233
2405
|
function parseScopeOverrideTag(tags) {
|
|
2234
2406
|
if (!tags || tags.length === 0) {
|
|
2235
2407
|
return void 0;
|
|
@@ -2253,15 +2425,36 @@ function parseScopeOverrideTag(tags) {
|
|
|
2253
2425
|
}
|
|
2254
2426
|
return void 0;
|
|
2255
2427
|
}
|
|
2256
|
-
function resolveFeatureScope(
|
|
2257
|
-
const override = parseScopeOverrideTag(feature.tags);
|
|
2428
|
+
function resolveFeatureScope(options) {
|
|
2429
|
+
const override = parseScopeOverrideTag(options.feature.tags);
|
|
2430
|
+
const fileScope = resolveFileScope(options.featureAbsPath, options.groupIndex);
|
|
2431
|
+
const inferred = (() => {
|
|
2432
|
+
if (fileScope.kind !== "root") {
|
|
2433
|
+
return fileScope;
|
|
2434
|
+
}
|
|
2435
|
+
if (options.hoistedFeatureScopingMode !== "directory") {
|
|
2436
|
+
return fileScope;
|
|
2437
|
+
}
|
|
2438
|
+
return resolveHoistedDirectoryScope({
|
|
2439
|
+
featureAbsPath: options.featureAbsPath,
|
|
2440
|
+
hoistedFeatureRootsAbs: options.hoistedFeatureRootsAbs,
|
|
2441
|
+
groupIndex: options.groupIndex,
|
|
2442
|
+
strict: options.hoistedFeatureScopingStrict
|
|
2443
|
+
});
|
|
2444
|
+
})();
|
|
2258
2445
|
if (override) {
|
|
2259
2446
|
if (override.modulePath && override.modulePath.length > 0) {
|
|
2260
2447
|
return { kind: "module", group: override.group, modulePath: override.modulePath };
|
|
2261
2448
|
}
|
|
2449
|
+
if (inferred.kind === "module" && inferred.group === override.group) {
|
|
2450
|
+
return inferred;
|
|
2451
|
+
}
|
|
2452
|
+
if (inferred.kind === "group" && inferred.group === override.group) {
|
|
2453
|
+
return inferred;
|
|
2454
|
+
}
|
|
2262
2455
|
return { kind: "group", group: override.group };
|
|
2263
2456
|
}
|
|
2264
|
-
return
|
|
2457
|
+
return inferred;
|
|
2265
2458
|
}
|
|
2266
2459
|
function isVisibleStepScope(stepScope, featureScope) {
|
|
2267
2460
|
if (featureScope.kind === "root") {
|
|
@@ -2354,7 +2547,14 @@ function createFeatureScopePlan(feature, basePlan, options) {
|
|
|
2354
2547
|
const scopingMode = options.config.modules?.stepScoping ?? "global";
|
|
2355
2548
|
const useScopedSteps = scopingMode === "scoped" && options.groupIndex.length > 0;
|
|
2356
2549
|
const allSteps = Array.from(basePlan.stepsById.values());
|
|
2357
|
-
const featureFileScope = useScopedSteps ? resolveFeatureScope(
|
|
2550
|
+
const featureFileScope = useScopedSteps ? resolveFeatureScope({
|
|
2551
|
+
featureAbsPath: options.featureAbsPath,
|
|
2552
|
+
feature,
|
|
2553
|
+
groupIndex: options.groupIndex,
|
|
2554
|
+
hoistedFeatureRootsAbs: options.hoistedFeatureRootsAbs,
|
|
2555
|
+
hoistedFeatureScopingMode: options.config.modules?.hoistedFeatures?.scope ?? "tag",
|
|
2556
|
+
hoistedFeatureScopingStrict: options.config.modules?.hoistedFeatures?.strict ?? true
|
|
2557
|
+
}) : { kind: "root" };
|
|
2358
2558
|
const visibleSteps = useScopedSteps ? allSteps.filter((definition) => {
|
|
2359
2559
|
const file = definition.source?.file;
|
|
2360
2560
|
const stepScope = file ? resolveFileScope(path2.resolve(options.cwd, file), options.groupIndex) : { kind: "root" };
|
|
@@ -2481,7 +2681,9 @@ async function runFeatures(options = {}) {
|
|
|
2481
2681
|
...options.runnerArgs ? { runnerArgs: options.runnerArgs } : {},
|
|
2482
2682
|
...options.dryRun !== void 0 ? { dryRun: options.dryRun } : {},
|
|
2483
2683
|
...options.watch !== void 0 ? { watch: options.watch } : {},
|
|
2484
|
-
...options.verbose !== void 0 ? { verbose: options.verbose } : {}
|
|
2684
|
+
...options.verbose !== void 0 ? { verbose: options.verbose } : {},
|
|
2685
|
+
groups: options.groups,
|
|
2686
|
+
modules: options.modules
|
|
2485
2687
|
});
|
|
2486
2688
|
if (orchestratorResult.runner !== "default") {
|
|
2487
2689
|
return {
|
|
@@ -2523,7 +2725,7 @@ async function runFeatures(options = {}) {
|
|
|
2523
2725
|
const allowedRoots = Object.entries(groups).filter(([group]) => allowedGroups.has(group)).map(([, groupConfig]) => groupConfig.root);
|
|
2524
2726
|
if (allowedRoots.length > 0) {
|
|
2525
2727
|
return patterns.some(
|
|
2526
|
-
(pattern) => !allowedRoots.some((root) =>
|
|
2728
|
+
(pattern) => !allowedRoots.some((root) => patternIsUnderRoot2(pattern, root))
|
|
2527
2729
|
);
|
|
2528
2730
|
}
|
|
2529
2731
|
}
|
|
@@ -2544,7 +2746,7 @@ async function runFeatures(options = {}) {
|
|
|
2544
2746
|
const allowedGroups = (options.groups?.length ?? 0) > 0 ? new Set(options.groups) : new Set(Object.keys(executorConfig.modules.groups));
|
|
2545
2747
|
const allowedRoots = Object.entries(executorConfig.modules.groups).filter(([group]) => allowedGroups.has(group)).map(([, groupConfig]) => groupConfig.root);
|
|
2546
2748
|
const filtered = roots.filter(
|
|
2547
|
-
(pattern) => allowedRoots.some((root) =>
|
|
2749
|
+
(pattern) => allowedRoots.some((root) => patternIsUnderRoot2(pattern, root))
|
|
2548
2750
|
);
|
|
2549
2751
|
if (filtered.length > 0) {
|
|
2550
2752
|
return filtered;
|
|
@@ -2575,6 +2777,7 @@ async function runFeatures(options = {}) {
|
|
|
2575
2777
|
}
|
|
2576
2778
|
const groupIndex = buildGroupIndex(executorConfig, cwd);
|
|
2577
2779
|
const environmentIndex = indexStepsEnvironments(stepsEnvironments, cwd, groupIndex);
|
|
2780
|
+
const hoistedFeatureRootsAbs = inferFeatureRootDirectories(executorConfig.roots.features, cwd);
|
|
2578
2781
|
for (const featurePath of featureFiles) {
|
|
2579
2782
|
const feature = await readFeatureFile(featurePath, cwd);
|
|
2580
2783
|
const featureAbsPath = path2.resolve(cwd, featurePath);
|
|
@@ -2582,7 +2785,9 @@ async function runFeatures(options = {}) {
|
|
|
2582
2785
|
featureAbsPath,
|
|
2583
2786
|
feature,
|
|
2584
2787
|
environmentIndex,
|
|
2585
|
-
groupIndex
|
|
2788
|
+
groupIndex,
|
|
2789
|
+
executorConfig,
|
|
2790
|
+
hoistedFeatureRootsAbs
|
|
2586
2791
|
);
|
|
2587
2792
|
runner.CucumberRunner.setSteps(selectedStepsEnvironment);
|
|
2588
2793
|
const basePlan = selectedStepsEnvironment.getPlan();
|
|
@@ -2590,7 +2795,8 @@ async function runFeatures(options = {}) {
|
|
|
2590
2795
|
featureAbsPath,
|
|
2591
2796
|
cwd,
|
|
2592
2797
|
config: executorConfig,
|
|
2593
|
-
groupIndex
|
|
2798
|
+
groupIndex,
|
|
2799
|
+
hoistedFeatureRootsAbs
|
|
2594
2800
|
});
|
|
2595
2801
|
const coordinated = selectedStepsEnvironment.coordinateFeature({
|
|
2596
2802
|
feature,
|
|
@@ -2697,8 +2903,15 @@ function indexStepsEnvironments(environments, cwd, groupIndex) {
|
|
|
2697
2903
|
}
|
|
2698
2904
|
return entries;
|
|
2699
2905
|
}
|
|
2700
|
-
function resolveFeatureStepsEnvironment(featureAbsPath, feature, environments, groupIndex) {
|
|
2701
|
-
const featureScope = resolveFeatureScope(
|
|
2906
|
+
function resolveFeatureStepsEnvironment(featureAbsPath, feature, environments, groupIndex, config, hoistedFeatureRootsAbs) {
|
|
2907
|
+
const featureScope = resolveFeatureScope({
|
|
2908
|
+
featureAbsPath,
|
|
2909
|
+
feature,
|
|
2910
|
+
groupIndex,
|
|
2911
|
+
hoistedFeatureRootsAbs,
|
|
2912
|
+
hoistedFeatureScopingMode: config.modules?.hoistedFeatures?.scope ?? "tag",
|
|
2913
|
+
hoistedFeatureScopingStrict: config.modules?.hoistedFeatures?.strict ?? true
|
|
2914
|
+
});
|
|
2702
2915
|
if (featureScope.kind === "root") {
|
|
2703
2916
|
const root = environments.find((entry) => entry.kind === "root");
|
|
2704
2917
|
return root?.environment ?? environments[0]?.environment ?? runner.CucumberRunner.steps();
|