@autometa/cli 1.0.0-rc.5 → 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.js
CHANGED
|
@@ -2006,19 +2006,112 @@ function detectRunner(config, cwd) {
|
|
|
2006
2006
|
}
|
|
2007
2007
|
return "default";
|
|
2008
2008
|
}
|
|
2009
|
+
function normalizeGlobLikePath(input) {
|
|
2010
|
+
return input.replace(/\\/g, "/").replace(/^\.\//u, "");
|
|
2011
|
+
}
|
|
2012
|
+
function patternIsUnderRoot(pattern, root) {
|
|
2013
|
+
const p = normalizeGlobLikePath(pattern);
|
|
2014
|
+
const r = normalizeGlobLikePath(root).replace(/\/+$/u, "");
|
|
2015
|
+
if (!r) {
|
|
2016
|
+
return false;
|
|
2017
|
+
}
|
|
2018
|
+
return p === r || p.startsWith(`${r}/`);
|
|
2019
|
+
}
|
|
2020
|
+
function filterPatternsByGroupsAndModules(options) {
|
|
2021
|
+
const { patterns, groups, modules, config, verbose } = options;
|
|
2022
|
+
const hasGroupSelection = (groups?.length ?? 0) > 0;
|
|
2023
|
+
const hasModuleSelection = (modules?.length ?? 0) > 0;
|
|
2024
|
+
const hasAnySelection = hasGroupSelection || hasModuleSelection;
|
|
2025
|
+
const hasExplicitPatterns = patterns.length > 0;
|
|
2026
|
+
if (hasExplicitPatterns) {
|
|
2027
|
+
if (hasAnySelection && verbose) {
|
|
2028
|
+
const shouldWarn = (() => {
|
|
2029
|
+
const configGroups = config.modules?.groups;
|
|
2030
|
+
if (!configGroups) {
|
|
2031
|
+
return true;
|
|
2032
|
+
}
|
|
2033
|
+
if (hasGroupSelection) {
|
|
2034
|
+
const allowedGroups = new Set(groups);
|
|
2035
|
+
const allowedRoots = Object.entries(configGroups).filter(([group]) => allowedGroups.has(group)).map(([, groupConfig]) => groupConfig.root);
|
|
2036
|
+
if (allowedRoots.length > 0) {
|
|
2037
|
+
return patterns.some(
|
|
2038
|
+
(pattern) => !allowedRoots.some((root) => patternIsUnderRoot(pattern, root))
|
|
2039
|
+
);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
return true;
|
|
2043
|
+
})();
|
|
2044
|
+
if (shouldWarn) {
|
|
2045
|
+
console.warn(
|
|
2046
|
+
"[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."
|
|
2047
|
+
);
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
return patterns;
|
|
2051
|
+
}
|
|
2052
|
+
const roots = [...config.roots.features];
|
|
2053
|
+
if (hasAnySelection && config.modules?.groups) {
|
|
2054
|
+
const allowedGroups = hasGroupSelection ? new Set(groups) : new Set(Object.keys(config.modules.groups));
|
|
2055
|
+
if (hasModuleSelection && modules && modules.length > 0) {
|
|
2056
|
+
const modulePatterns = [];
|
|
2057
|
+
const relativeRoots = config.modules.relativeRoots?.features ?? [".features/**/*.feature"];
|
|
2058
|
+
for (const [groupName, groupConfig] of Object.entries(config.modules.groups)) {
|
|
2059
|
+
if (!allowedGroups.has(groupName)) {
|
|
2060
|
+
continue;
|
|
2061
|
+
}
|
|
2062
|
+
const groupRoot = groupConfig.root;
|
|
2063
|
+
const groupModules = groupConfig.modules ?? [];
|
|
2064
|
+
for (const moduleName of modules) {
|
|
2065
|
+
const moduleExists = Array.isArray(groupModules) && groupModules.some((m) => {
|
|
2066
|
+
if (typeof m === "string") {
|
|
2067
|
+
return m === moduleName || m.endsWith(`/${moduleName}`) || m.endsWith(`:${moduleName}`);
|
|
2068
|
+
}
|
|
2069
|
+
return false;
|
|
2070
|
+
});
|
|
2071
|
+
if (moduleExists) {
|
|
2072
|
+
for (const relativeRoot of relativeRoots) {
|
|
2073
|
+
const normalizedRelative = relativeRoot.replace(/^\.\//, "");
|
|
2074
|
+
const modulePath = join(groupRoot, moduleName, normalizedRelative);
|
|
2075
|
+
modulePatterns.push(modulePath);
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
if (modulePatterns.length > 0) {
|
|
2081
|
+
return modulePatterns;
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
const allowedRoots = Object.entries(config.modules.groups).filter(([group]) => allowedGroups.has(group)).map(([, groupConfig]) => groupConfig.root);
|
|
2085
|
+
const filtered = roots.filter(
|
|
2086
|
+
(pattern) => allowedRoots.some((root) => patternIsUnderRoot(pattern, root))
|
|
2087
|
+
);
|
|
2088
|
+
if (filtered.length > 0) {
|
|
2089
|
+
return filtered;
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
return roots;
|
|
2093
|
+
}
|
|
2009
2094
|
async function orchestrate(options) {
|
|
2010
|
-
const { cwd, config, patterns = [], runnerArgs = [], dryRun = false, watch = false, verbose = false } = options;
|
|
2095
|
+
const { cwd, config, patterns = [], runnerArgs = [], dryRun = false, watch = false, verbose = false, groups, modules } = options;
|
|
2011
2096
|
const runner = detectRunner(config, cwd);
|
|
2012
2097
|
if (verbose) {
|
|
2013
2098
|
console.log(`[autometa] Detected runner: ${runner}`);
|
|
2014
2099
|
}
|
|
2100
|
+
const filteredPatterns = filterPatternsByGroupsAndModules({
|
|
2101
|
+
patterns,
|
|
2102
|
+
groups,
|
|
2103
|
+
modules,
|
|
2104
|
+
config,
|
|
2105
|
+
cwd,
|
|
2106
|
+
verbose
|
|
2107
|
+
});
|
|
2015
2108
|
switch (runner) {
|
|
2016
2109
|
case "vitest":
|
|
2017
|
-
return spawnVitest({ cwd, patterns, runnerArgs, dryRun, watch, verbose });
|
|
2110
|
+
return spawnVitest({ cwd, patterns: filteredPatterns, runnerArgs, dryRun, watch, verbose });
|
|
2018
2111
|
case "jest":
|
|
2019
|
-
return spawnJest({ cwd, patterns, runnerArgs, dryRun, watch, verbose });
|
|
2112
|
+
return spawnJest({ cwd, patterns: filteredPatterns, runnerArgs, dryRun, watch, verbose });
|
|
2020
2113
|
case "playwright":
|
|
2021
|
-
return spawnPlaywright({ cwd, patterns, runnerArgs, dryRun, watch, verbose });
|
|
2114
|
+
return spawnPlaywright({ cwd, patterns: filteredPatterns, runnerArgs, dryRun, watch, verbose });
|
|
2022
2115
|
case "default":
|
|
2023
2116
|
return { success: true, exitCode: 0, runner: "default" };
|
|
2024
2117
|
}
|
|
@@ -2030,7 +2123,8 @@ async function spawnVitest(options) {
|
|
|
2030
2123
|
args.push("run");
|
|
2031
2124
|
}
|
|
2032
2125
|
if (patterns.length > 0) {
|
|
2033
|
-
|
|
2126
|
+
const expanded = await expandFilePatterns(patterns, cwd);
|
|
2127
|
+
args.push(...expanded.length > 0 ? expanded : patterns);
|
|
2034
2128
|
}
|
|
2035
2129
|
if (dryRun) {
|
|
2036
2130
|
args.push("--passWithNoTests");
|
|
@@ -2132,17 +2226,53 @@ var STEP_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"
|
|
|
2132
2226
|
var STEP_FALLBACK_GLOB = "**/*.{ts,tsx,js,jsx,mjs,cjs,mts,cts}";
|
|
2133
2227
|
var FEATURE_FALLBACK_GLOB = "**/*.feature";
|
|
2134
2228
|
var ROOT_LOAD_ORDER = ["parameterTypes", "support", "hooks", "app"];
|
|
2135
|
-
|
|
2229
|
+
var GLOB_MAGIC_CHARACTERS = /* @__PURE__ */ new Set(["*", "?", "[", "]", "{", "}", "(", ")", "!"]);
|
|
2230
|
+
function normalizeGlobLikePath2(input) {
|
|
2136
2231
|
return input.replace(/\\/g, "/").replace(/^\.\//u, "");
|
|
2137
2232
|
}
|
|
2138
|
-
function
|
|
2139
|
-
const p =
|
|
2140
|
-
const r =
|
|
2233
|
+
function patternIsUnderRoot2(pattern, root) {
|
|
2234
|
+
const p = normalizeGlobLikePath2(pattern);
|
|
2235
|
+
const r = normalizeGlobLikePath2(root).replace(/\/+$/u, "");
|
|
2141
2236
|
if (!r) {
|
|
2142
2237
|
return false;
|
|
2143
2238
|
}
|
|
2144
2239
|
return p === r || p.startsWith(`${r}/`);
|
|
2145
2240
|
}
|
|
2241
|
+
function inferGlobBaseDirectory(pattern) {
|
|
2242
|
+
const normalized = normalizeGlobLikePath2(pattern).replace(/^!/u, "");
|
|
2243
|
+
for (let i = 0; i < normalized.length; i += 1) {
|
|
2244
|
+
if (!GLOB_MAGIC_CHARACTERS.has(normalized[i] ?? "")) {
|
|
2245
|
+
continue;
|
|
2246
|
+
}
|
|
2247
|
+
const prefix = normalized.slice(0, i);
|
|
2248
|
+
const lastSlash = prefix.lastIndexOf("/");
|
|
2249
|
+
if (lastSlash <= 0) {
|
|
2250
|
+
return ".";
|
|
2251
|
+
}
|
|
2252
|
+
const base = prefix.slice(0, lastSlash);
|
|
2253
|
+
return base || ".";
|
|
2254
|
+
}
|
|
2255
|
+
if (extname(normalized)) {
|
|
2256
|
+
return dirname(normalized);
|
|
2257
|
+
}
|
|
2258
|
+
return normalized || ".";
|
|
2259
|
+
}
|
|
2260
|
+
function inferFeatureRootDirectories(patterns, cwd) {
|
|
2261
|
+
if (!patterns || patterns.length === 0) {
|
|
2262
|
+
return [];
|
|
2263
|
+
}
|
|
2264
|
+
const roots = /* @__PURE__ */ new Set();
|
|
2265
|
+
for (const entry of patterns) {
|
|
2266
|
+
const trimmed = entry.trim();
|
|
2267
|
+
if (!trimmed) {
|
|
2268
|
+
continue;
|
|
2269
|
+
}
|
|
2270
|
+
const base = hasGlobMagic(trimmed) ? inferGlobBaseDirectory(trimmed) : trimmed;
|
|
2271
|
+
const resolved = resolve(cwd, base);
|
|
2272
|
+
roots.add(resolved.replace(/\/+$/u, ""));
|
|
2273
|
+
}
|
|
2274
|
+
return Array.from(roots).sort((a, b) => b.length - a.length);
|
|
2275
|
+
}
|
|
2146
2276
|
function flattenModuleDeclarations(declarations, prefix = []) {
|
|
2147
2277
|
if (!declarations || declarations.length === 0) {
|
|
2148
2278
|
return [];
|
|
@@ -2226,6 +2356,48 @@ function resolveFileScope(fileAbs, groupIndex) {
|
|
|
2226
2356
|
}
|
|
2227
2357
|
return { kind: "root" };
|
|
2228
2358
|
}
|
|
2359
|
+
function resolveHoistedDirectoryScope(options) {
|
|
2360
|
+
const rootAbs = options.hoistedFeatureRootsAbs.find(
|
|
2361
|
+
(root) => isPathUnderRoot(options.featureAbsPath, root)
|
|
2362
|
+
);
|
|
2363
|
+
if (!rootAbs) {
|
|
2364
|
+
return { kind: "root" };
|
|
2365
|
+
}
|
|
2366
|
+
const rel = relative(rootAbs, options.featureAbsPath);
|
|
2367
|
+
const segments = normalizePathSegments(rel);
|
|
2368
|
+
const dirSegments = segments.slice(0, -1);
|
|
2369
|
+
if (dirSegments.length === 0) {
|
|
2370
|
+
return { kind: "root" };
|
|
2371
|
+
}
|
|
2372
|
+
const group = dirSegments[0];
|
|
2373
|
+
if (!group) {
|
|
2374
|
+
return { kind: "root" };
|
|
2375
|
+
}
|
|
2376
|
+
const groupEntry = options.groupIndex.find((entry) => entry.group === group);
|
|
2377
|
+
if (!groupEntry) {
|
|
2378
|
+
if (options.strict) {
|
|
2379
|
+
throw new Error(
|
|
2380
|
+
`[autometa] Feature "${options.featureAbsPath}" is under "${rootAbs}" and implies group "${group}", but "${group}" is not declared in config.modules.groups.`
|
|
2381
|
+
);
|
|
2382
|
+
}
|
|
2383
|
+
return { kind: "root" };
|
|
2384
|
+
}
|
|
2385
|
+
const moduleSegments = dirSegments.slice(1);
|
|
2386
|
+
if (moduleSegments.length === 0) {
|
|
2387
|
+
return { kind: "group", group };
|
|
2388
|
+
}
|
|
2389
|
+
for (const modulePath of groupEntry.modulePaths) {
|
|
2390
|
+
if (startsWithSegments(moduleSegments, modulePath)) {
|
|
2391
|
+
return { kind: "module", group, modulePath };
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
if (options.strict) {
|
|
2395
|
+
throw new Error(
|
|
2396
|
+
`[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.`
|
|
2397
|
+
);
|
|
2398
|
+
}
|
|
2399
|
+
return { kind: "group", group };
|
|
2400
|
+
}
|
|
2229
2401
|
function parseScopeOverrideTag(tags) {
|
|
2230
2402
|
if (!tags || tags.length === 0) {
|
|
2231
2403
|
return void 0;
|
|
@@ -2249,15 +2421,27 @@ function parseScopeOverrideTag(tags) {
|
|
|
2249
2421
|
}
|
|
2250
2422
|
return void 0;
|
|
2251
2423
|
}
|
|
2252
|
-
function resolveFeatureScope(
|
|
2253
|
-
const override = parseScopeOverrideTag(feature.tags);
|
|
2424
|
+
function resolveFeatureScope(options) {
|
|
2425
|
+
const override = parseScopeOverrideTag(options.feature.tags);
|
|
2254
2426
|
if (override) {
|
|
2255
2427
|
if (override.modulePath && override.modulePath.length > 0) {
|
|
2256
2428
|
return { kind: "module", group: override.group, modulePath: override.modulePath };
|
|
2257
2429
|
}
|
|
2258
2430
|
return { kind: "group", group: override.group };
|
|
2259
2431
|
}
|
|
2260
|
-
|
|
2432
|
+
const fileScope = resolveFileScope(options.featureAbsPath, options.groupIndex);
|
|
2433
|
+
if (fileScope.kind !== "root") {
|
|
2434
|
+
return fileScope;
|
|
2435
|
+
}
|
|
2436
|
+
if (options.hoistedFeatureScopingMode !== "directory") {
|
|
2437
|
+
return fileScope;
|
|
2438
|
+
}
|
|
2439
|
+
return resolveHoistedDirectoryScope({
|
|
2440
|
+
featureAbsPath: options.featureAbsPath,
|
|
2441
|
+
hoistedFeatureRootsAbs: options.hoistedFeatureRootsAbs,
|
|
2442
|
+
groupIndex: options.groupIndex,
|
|
2443
|
+
strict: options.hoistedFeatureScopingStrict
|
|
2444
|
+
});
|
|
2261
2445
|
}
|
|
2262
2446
|
function isVisibleStepScope(stepScope, featureScope) {
|
|
2263
2447
|
if (featureScope.kind === "root") {
|
|
@@ -2350,7 +2534,14 @@ function createFeatureScopePlan(feature, basePlan, options) {
|
|
|
2350
2534
|
const scopingMode = options.config.modules?.stepScoping ?? "global";
|
|
2351
2535
|
const useScopedSteps = scopingMode === "scoped" && options.groupIndex.length > 0;
|
|
2352
2536
|
const allSteps = Array.from(basePlan.stepsById.values());
|
|
2353
|
-
const featureFileScope = useScopedSteps ? resolveFeatureScope(
|
|
2537
|
+
const featureFileScope = useScopedSteps ? resolveFeatureScope({
|
|
2538
|
+
featureAbsPath: options.featureAbsPath,
|
|
2539
|
+
feature,
|
|
2540
|
+
groupIndex: options.groupIndex,
|
|
2541
|
+
hoistedFeatureRootsAbs: options.hoistedFeatureRootsAbs,
|
|
2542
|
+
hoistedFeatureScopingMode: options.config.modules?.hoistedFeatures?.scope ?? "tag",
|
|
2543
|
+
hoistedFeatureScopingStrict: options.config.modules?.hoistedFeatures?.strict ?? true
|
|
2544
|
+
}) : { kind: "root" };
|
|
2354
2545
|
const visibleSteps = useScopedSteps ? allSteps.filter((definition) => {
|
|
2355
2546
|
const file = definition.source?.file;
|
|
2356
2547
|
const stepScope = file ? resolveFileScope(resolve(options.cwd, file), options.groupIndex) : { kind: "root" };
|
|
@@ -2477,7 +2668,9 @@ async function runFeatures(options = {}) {
|
|
|
2477
2668
|
...options.runnerArgs ? { runnerArgs: options.runnerArgs } : {},
|
|
2478
2669
|
...options.dryRun !== void 0 ? { dryRun: options.dryRun } : {},
|
|
2479
2670
|
...options.watch !== void 0 ? { watch: options.watch } : {},
|
|
2480
|
-
...options.verbose !== void 0 ? { verbose: options.verbose } : {}
|
|
2671
|
+
...options.verbose !== void 0 ? { verbose: options.verbose } : {},
|
|
2672
|
+
groups: options.groups,
|
|
2673
|
+
modules: options.modules
|
|
2481
2674
|
});
|
|
2482
2675
|
if (orchestratorResult.runner !== "default") {
|
|
2483
2676
|
return {
|
|
@@ -2519,7 +2712,7 @@ async function runFeatures(options = {}) {
|
|
|
2519
2712
|
const allowedRoots = Object.entries(groups).filter(([group]) => allowedGroups.has(group)).map(([, groupConfig]) => groupConfig.root);
|
|
2520
2713
|
if (allowedRoots.length > 0) {
|
|
2521
2714
|
return patterns.some(
|
|
2522
|
-
(pattern) => !allowedRoots.some((root) =>
|
|
2715
|
+
(pattern) => !allowedRoots.some((root) => patternIsUnderRoot2(pattern, root))
|
|
2523
2716
|
);
|
|
2524
2717
|
}
|
|
2525
2718
|
}
|
|
@@ -2540,7 +2733,7 @@ async function runFeatures(options = {}) {
|
|
|
2540
2733
|
const allowedGroups = (options.groups?.length ?? 0) > 0 ? new Set(options.groups) : new Set(Object.keys(executorConfig.modules.groups));
|
|
2541
2734
|
const allowedRoots = Object.entries(executorConfig.modules.groups).filter(([group]) => allowedGroups.has(group)).map(([, groupConfig]) => groupConfig.root);
|
|
2542
2735
|
const filtered = roots.filter(
|
|
2543
|
-
(pattern) => allowedRoots.some((root) =>
|
|
2736
|
+
(pattern) => allowedRoots.some((root) => patternIsUnderRoot2(pattern, root))
|
|
2544
2737
|
);
|
|
2545
2738
|
if (filtered.length > 0) {
|
|
2546
2739
|
return filtered;
|
|
@@ -2571,6 +2764,7 @@ async function runFeatures(options = {}) {
|
|
|
2571
2764
|
}
|
|
2572
2765
|
const groupIndex = buildGroupIndex(executorConfig, cwd);
|
|
2573
2766
|
const environmentIndex = indexStepsEnvironments(stepsEnvironments, cwd, groupIndex);
|
|
2767
|
+
const hoistedFeatureRootsAbs = inferFeatureRootDirectories(executorConfig.roots.features, cwd);
|
|
2574
2768
|
for (const featurePath of featureFiles) {
|
|
2575
2769
|
const feature = await readFeatureFile(featurePath, cwd);
|
|
2576
2770
|
const featureAbsPath = resolve(cwd, featurePath);
|
|
@@ -2578,7 +2772,9 @@ async function runFeatures(options = {}) {
|
|
|
2578
2772
|
featureAbsPath,
|
|
2579
2773
|
feature,
|
|
2580
2774
|
environmentIndex,
|
|
2581
|
-
groupIndex
|
|
2775
|
+
groupIndex,
|
|
2776
|
+
executorConfig,
|
|
2777
|
+
hoistedFeatureRootsAbs
|
|
2582
2778
|
);
|
|
2583
2779
|
CucumberRunner.setSteps(selectedStepsEnvironment);
|
|
2584
2780
|
const basePlan = selectedStepsEnvironment.getPlan();
|
|
@@ -2586,7 +2782,8 @@ async function runFeatures(options = {}) {
|
|
|
2586
2782
|
featureAbsPath,
|
|
2587
2783
|
cwd,
|
|
2588
2784
|
config: executorConfig,
|
|
2589
|
-
groupIndex
|
|
2785
|
+
groupIndex,
|
|
2786
|
+
hoistedFeatureRootsAbs
|
|
2590
2787
|
});
|
|
2591
2788
|
const coordinated = selectedStepsEnvironment.coordinateFeature({
|
|
2592
2789
|
feature,
|
|
@@ -2693,8 +2890,15 @@ function indexStepsEnvironments(environments, cwd, groupIndex) {
|
|
|
2693
2890
|
}
|
|
2694
2891
|
return entries;
|
|
2695
2892
|
}
|
|
2696
|
-
function resolveFeatureStepsEnvironment(featureAbsPath, feature, environments, groupIndex) {
|
|
2697
|
-
const featureScope = resolveFeatureScope(
|
|
2893
|
+
function resolveFeatureStepsEnvironment(featureAbsPath, feature, environments, groupIndex, config, hoistedFeatureRootsAbs) {
|
|
2894
|
+
const featureScope = resolveFeatureScope({
|
|
2895
|
+
featureAbsPath,
|
|
2896
|
+
feature,
|
|
2897
|
+
groupIndex,
|
|
2898
|
+
hoistedFeatureRootsAbs,
|
|
2899
|
+
hoistedFeatureScopingMode: config.modules?.hoistedFeatures?.scope ?? "tag",
|
|
2900
|
+
hoistedFeatureScopingStrict: config.modules?.hoistedFeatures?.strict ?? true
|
|
2901
|
+
});
|
|
2698
2902
|
if (featureScope.kind === "root") {
|
|
2699
2903
|
const root = environments.find((entry) => entry.kind === "root");
|
|
2700
2904
|
return root?.environment ?? environments[0]?.environment ?? CucumberRunner.steps();
|