@autometa/cli 1.0.0-rc.4 → 1.0.0-rc.6
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 +224 -20
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +224 -20
- package/dist/bin.js.map +1 -1
- package/dist/index.cjs +224 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +224 -20
- package/dist/index.js.map +1 -1
- package/dist/orchestrator/index.d.ts +2 -0
- package/package.json +10 -10
package/dist/index.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,27 @@ 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);
|
|
2258
2430
|
if (override) {
|
|
2259
2431
|
if (override.modulePath && override.modulePath.length > 0) {
|
|
2260
2432
|
return { kind: "module", group: override.group, modulePath: override.modulePath };
|
|
2261
2433
|
}
|
|
2262
2434
|
return { kind: "group", group: override.group };
|
|
2263
2435
|
}
|
|
2264
|
-
|
|
2436
|
+
const fileScope = resolveFileScope(options.featureAbsPath, options.groupIndex);
|
|
2437
|
+
if (fileScope.kind !== "root") {
|
|
2438
|
+
return fileScope;
|
|
2439
|
+
}
|
|
2440
|
+
if (options.hoistedFeatureScopingMode !== "directory") {
|
|
2441
|
+
return fileScope;
|
|
2442
|
+
}
|
|
2443
|
+
return resolveHoistedDirectoryScope({
|
|
2444
|
+
featureAbsPath: options.featureAbsPath,
|
|
2445
|
+
hoistedFeatureRootsAbs: options.hoistedFeatureRootsAbs,
|
|
2446
|
+
groupIndex: options.groupIndex,
|
|
2447
|
+
strict: options.hoistedFeatureScopingStrict
|
|
2448
|
+
});
|
|
2265
2449
|
}
|
|
2266
2450
|
function isVisibleStepScope(stepScope, featureScope) {
|
|
2267
2451
|
if (featureScope.kind === "root") {
|
|
@@ -2354,7 +2538,14 @@ function createFeatureScopePlan(feature, basePlan, options) {
|
|
|
2354
2538
|
const scopingMode = options.config.modules?.stepScoping ?? "global";
|
|
2355
2539
|
const useScopedSteps = scopingMode === "scoped" && options.groupIndex.length > 0;
|
|
2356
2540
|
const allSteps = Array.from(basePlan.stepsById.values());
|
|
2357
|
-
const featureFileScope = useScopedSteps ? resolveFeatureScope(
|
|
2541
|
+
const featureFileScope = useScopedSteps ? resolveFeatureScope({
|
|
2542
|
+
featureAbsPath: options.featureAbsPath,
|
|
2543
|
+
feature,
|
|
2544
|
+
groupIndex: options.groupIndex,
|
|
2545
|
+
hoistedFeatureRootsAbs: options.hoistedFeatureRootsAbs,
|
|
2546
|
+
hoistedFeatureScopingMode: options.config.modules?.hoistedFeatures?.scope ?? "tag",
|
|
2547
|
+
hoistedFeatureScopingStrict: options.config.modules?.hoistedFeatures?.strict ?? true
|
|
2548
|
+
}) : { kind: "root" };
|
|
2358
2549
|
const visibleSteps = useScopedSteps ? allSteps.filter((definition) => {
|
|
2359
2550
|
const file = definition.source?.file;
|
|
2360
2551
|
const stepScope = file ? resolveFileScope(path2.resolve(options.cwd, file), options.groupIndex) : { kind: "root" };
|
|
@@ -2481,7 +2672,9 @@ async function runFeatures(options = {}) {
|
|
|
2481
2672
|
...options.runnerArgs ? { runnerArgs: options.runnerArgs } : {},
|
|
2482
2673
|
...options.dryRun !== void 0 ? { dryRun: options.dryRun } : {},
|
|
2483
2674
|
...options.watch !== void 0 ? { watch: options.watch } : {},
|
|
2484
|
-
...options.verbose !== void 0 ? { verbose: options.verbose } : {}
|
|
2675
|
+
...options.verbose !== void 0 ? { verbose: options.verbose } : {},
|
|
2676
|
+
groups: options.groups,
|
|
2677
|
+
modules: options.modules
|
|
2485
2678
|
});
|
|
2486
2679
|
if (orchestratorResult.runner !== "default") {
|
|
2487
2680
|
return {
|
|
@@ -2523,7 +2716,7 @@ async function runFeatures(options = {}) {
|
|
|
2523
2716
|
const allowedRoots = Object.entries(groups).filter(([group]) => allowedGroups.has(group)).map(([, groupConfig]) => groupConfig.root);
|
|
2524
2717
|
if (allowedRoots.length > 0) {
|
|
2525
2718
|
return patterns.some(
|
|
2526
|
-
(pattern) => !allowedRoots.some((root) =>
|
|
2719
|
+
(pattern) => !allowedRoots.some((root) => patternIsUnderRoot2(pattern, root))
|
|
2527
2720
|
);
|
|
2528
2721
|
}
|
|
2529
2722
|
}
|
|
@@ -2544,7 +2737,7 @@ async function runFeatures(options = {}) {
|
|
|
2544
2737
|
const allowedGroups = (options.groups?.length ?? 0) > 0 ? new Set(options.groups) : new Set(Object.keys(executorConfig.modules.groups));
|
|
2545
2738
|
const allowedRoots = Object.entries(executorConfig.modules.groups).filter(([group]) => allowedGroups.has(group)).map(([, groupConfig]) => groupConfig.root);
|
|
2546
2739
|
const filtered = roots.filter(
|
|
2547
|
-
(pattern) => allowedRoots.some((root) =>
|
|
2740
|
+
(pattern) => allowedRoots.some((root) => patternIsUnderRoot2(pattern, root))
|
|
2548
2741
|
);
|
|
2549
2742
|
if (filtered.length > 0) {
|
|
2550
2743
|
return filtered;
|
|
@@ -2575,6 +2768,7 @@ async function runFeatures(options = {}) {
|
|
|
2575
2768
|
}
|
|
2576
2769
|
const groupIndex = buildGroupIndex(executorConfig, cwd);
|
|
2577
2770
|
const environmentIndex = indexStepsEnvironments(stepsEnvironments, cwd, groupIndex);
|
|
2771
|
+
const hoistedFeatureRootsAbs = inferFeatureRootDirectories(executorConfig.roots.features, cwd);
|
|
2578
2772
|
for (const featurePath of featureFiles) {
|
|
2579
2773
|
const feature = await readFeatureFile(featurePath, cwd);
|
|
2580
2774
|
const featureAbsPath = path2.resolve(cwd, featurePath);
|
|
@@ -2582,7 +2776,9 @@ async function runFeatures(options = {}) {
|
|
|
2582
2776
|
featureAbsPath,
|
|
2583
2777
|
feature,
|
|
2584
2778
|
environmentIndex,
|
|
2585
|
-
groupIndex
|
|
2779
|
+
groupIndex,
|
|
2780
|
+
executorConfig,
|
|
2781
|
+
hoistedFeatureRootsAbs
|
|
2586
2782
|
);
|
|
2587
2783
|
runner.CucumberRunner.setSteps(selectedStepsEnvironment);
|
|
2588
2784
|
const basePlan = selectedStepsEnvironment.getPlan();
|
|
@@ -2590,7 +2786,8 @@ async function runFeatures(options = {}) {
|
|
|
2590
2786
|
featureAbsPath,
|
|
2591
2787
|
cwd,
|
|
2592
2788
|
config: executorConfig,
|
|
2593
|
-
groupIndex
|
|
2789
|
+
groupIndex,
|
|
2790
|
+
hoistedFeatureRootsAbs
|
|
2594
2791
|
});
|
|
2595
2792
|
const coordinated = selectedStepsEnvironment.coordinateFeature({
|
|
2596
2793
|
feature,
|
|
@@ -2697,8 +2894,15 @@ function indexStepsEnvironments(environments, cwd, groupIndex) {
|
|
|
2697
2894
|
}
|
|
2698
2895
|
return entries;
|
|
2699
2896
|
}
|
|
2700
|
-
function resolveFeatureStepsEnvironment(featureAbsPath, feature, environments, groupIndex) {
|
|
2701
|
-
const featureScope = resolveFeatureScope(
|
|
2897
|
+
function resolveFeatureStepsEnvironment(featureAbsPath, feature, environments, groupIndex, config, hoistedFeatureRootsAbs) {
|
|
2898
|
+
const featureScope = resolveFeatureScope({
|
|
2899
|
+
featureAbsPath,
|
|
2900
|
+
feature,
|
|
2901
|
+
groupIndex,
|
|
2902
|
+
hoistedFeatureRootsAbs,
|
|
2903
|
+
hoistedFeatureScopingMode: config.modules?.hoistedFeatures?.scope ?? "tag",
|
|
2904
|
+
hoistedFeatureScopingStrict: config.modules?.hoistedFeatures?.strict ?? true
|
|
2905
|
+
});
|
|
2702
2906
|
if (featureScope.kind === "root") {
|
|
2703
2907
|
const root = environments.find((entry) => entry.kind === "root");
|
|
2704
2908
|
return root?.environment ?? environments[0]?.environment ?? runner.CucumberRunner.steps();
|