@step-forge/step-forge 0.0.20 → 0.0.22

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.
Files changed (47) hide show
  1. package/README.md +2 -0
  2. package/RUNTIME.md +242 -0
  3. package/dist/{analyzer-DJyJbU_V.js → analyzer-byS8yRrY.js} +202 -34
  4. package/dist/analyzer-byS8yRrY.js.map +1 -0
  5. package/dist/analyzer-cli.js +1 -1
  6. package/dist/analyzer.d.ts +2 -0
  7. package/dist/analyzer.js +1 -2
  8. package/dist/cli.cjs +525 -0
  9. package/dist/cli.cjs.map +1 -0
  10. package/dist/cli.d.cts +1 -0
  11. package/dist/cli.d.ts +1 -0
  12. package/dist/cli.js +526 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/{hooks-Dar49TtT.d.ts → config-C7PCYgYy.d.cts} +65 -16
  15. package/dist/{hooks-Dar49TtT.d.cts → config-C7PCYgYy.d.ts} +65 -16
  16. package/dist/engine-DPVLEHBi.js +163 -0
  17. package/dist/engine-DPVLEHBi.js.map +1 -0
  18. package/dist/engine-vqA-eL_T.cjs +186 -0
  19. package/dist/engine-vqA-eL_T.cjs.map +1 -0
  20. package/dist/gherkinParser-BT40q_i3.cjs +338 -0
  21. package/dist/gherkinParser-BT40q_i3.cjs.map +1 -0
  22. package/dist/gherkinParser-NcttZgN4.js +259 -0
  23. package/dist/gherkinParser-NcttZgN4.js.map +1 -0
  24. package/dist/hooks-BDCMKeNq.js +71 -0
  25. package/dist/{hooks-CywugMQQ.js.map → hooks-BDCMKeNq.js.map} +1 -1
  26. package/dist/{hooks-CGYzwDOv.cjs → hooks-Be0cjULN.cjs} +20 -31
  27. package/dist/{hooks-CGYzwDOv.cjs.map → hooks-Be0cjULN.cjs.map} +1 -1
  28. package/dist/runtime.cjs +7 -162
  29. package/dist/runtime.d.cts +44 -8
  30. package/dist/runtime.d.ts +44 -8
  31. package/dist/runtime.js +3 -159
  32. package/dist/step-forge.cjs +73 -216
  33. package/dist/step-forge.cjs.map +1 -1
  34. package/dist/step-forge.d.cts +19 -10
  35. package/dist/step-forge.d.ts +19 -10
  36. package/dist/step-forge.js +67 -185
  37. package/dist/step-forge.js.map +1 -1
  38. package/package.json +12 -18
  39. package/dist/analyzer-DJyJbU_V.js.map +0 -1
  40. package/dist/gherkinParser-Dp2d7JNr.js +0 -116
  41. package/dist/gherkinParser-Dp2d7JNr.js.map +0 -1
  42. package/dist/hooks-CywugMQQ.js +0 -82
  43. package/dist/runtime.cjs.map +0 -1
  44. package/dist/runtime.js.map +0 -1
  45. package/dist/vitest.d.ts +0 -74
  46. package/dist/vitest.js +0 -136
  47. package/dist/vitest.js.map +0 -1
@@ -1,39 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region \0rolldown/runtime.js
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
- get: ((k) => from[k]).bind(null, key),
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- });
16
- }
17
- return to;
18
- };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
- value: mod,
21
- enumerable: true
22
- }) : target, mod));
23
- //#endregion
24
- const require_hooks = require("./hooks-CGYzwDOv.cjs");
2
+ const require_gherkinParser = require("./gherkinParser-BT40q_i3.cjs");
3
+ const require_hooks = require("./hooks-Be0cjULN.cjs");
25
4
  let lodash = require("lodash");
26
- lodash = __toESM(lodash, 1);
27
- let node_fs_promises = require("node:fs/promises");
28
- let node_path = require("node:path");
29
- node_path = __toESM(node_path, 1);
5
+ lodash = require_gherkinParser.__toESM(lodash, 1);
30
6
  let typescript = require("typescript");
31
- typescript = __toESM(typescript, 1);
32
- let node_fs = require("node:fs");
33
- node_fs = __toESM(node_fs, 1);
34
- let _cucumber_gherkin = require("@cucumber/gherkin");
35
- let _cucumber_messages = require("@cucumber/messages");
36
- _cucumber_messages = __toESM(_cucumber_messages, 1);
7
+ typescript = require_gherkinParser.__toESM(typescript, 1);
37
8
  //#region src/builderTypeUtils.ts
38
9
  const isString = (statement) => typeof statement === "string";
39
10
  //#endregion
@@ -146,11 +117,13 @@ const addStep = (statement, stepType, dependencies = {
146
117
  });
147
118
  world[stepType].merge({ ...result });
148
119
  };
120
+ const source = require_gherkinParser.captureDefinitionSite();
149
121
  require_hooks.globalRegistry.add({
150
122
  stepType,
151
123
  expression,
152
124
  parsers,
153
- execute
125
+ execute,
126
+ source
154
127
  });
155
128
  return {
156
129
  statement,
@@ -224,42 +197,6 @@ const thenStatement = () => (statement) => {
224
197
  };
225
198
  const thenBuilder = () => ({ statement: thenStatement() });
226
199
  //#endregion
227
- //#region src/world.ts
228
- function mergeCustomizer(objValue, srcValue) {
229
- if (lodash.default.isArray(objValue)) return objValue.concat(srcValue);
230
- else if (objValue && !lodash.default.isPlainObject(objValue) && objValue !== srcValue) throw new Error(`Merge would have destroyed previous value ${objValue} with ${srcValue}`);
231
- return objValue;
232
- }
233
- var BasicWorld = class {
234
- givenState = {};
235
- whenState = {};
236
- thenState = {};
237
- get given() {
238
- return {
239
- ...this.givenState,
240
- merge: (newState) => {
241
- this.givenState = lodash.default.merge({ ...this.givenState }, newState, mergeCustomizer);
242
- }
243
- };
244
- }
245
- get when() {
246
- return {
247
- ...this.whenState,
248
- merge: (newState) => {
249
- this.whenState = lodash.default.merge({ ...this.whenState }, newState, mergeCustomizer);
250
- }
251
- };
252
- }
253
- get then() {
254
- return {
255
- ...this.thenState,
256
- merge: (newState) => {
257
- this.thenState = lodash.default.merge({ ...this.thenState }, newState, mergeCustomizer);
258
- }
259
- };
260
- }
261
- };
262
- //#endregion
263
200
  //#region src/analyzer/stepExtractor.ts
264
201
  const BUILDER_NAMES = {
265
202
  givenBuilder: "given",
@@ -267,6 +204,21 @@ const BUILDER_NAMES = {
267
204
  thenBuilder: "then"
268
205
  };
269
206
  function extractStepDefinitions(filePaths, tsConfigPath) {
207
+ const fast = extractWithSources(filePaths.map(parseSourceFile).filter((s) => s !== null), null);
208
+ if (!fast.needsChecker) return fast.results;
209
+ const program = typescript.default.createProgram(filePaths, resolveCompilerOptions(tsConfigPath));
210
+ const checker = program.getTypeChecker();
211
+ return extractWithSources(filePaths.map((fp) => program.getSourceFile(fp)).filter((s) => s !== void 0), checker).results;
212
+ }
213
+ /** Parse a single file into an AST with no type resolution. */
214
+ function parseSourceFile(filePath) {
215
+ const text = typescript.default.sys.readFile(filePath);
216
+ if (text === void 0) return null;
217
+ const scriptKind = filePath.endsWith(".tsx") ? typescript.default.ScriptKind.TSX : typescript.default.ScriptKind.TS;
218
+ return typescript.default.createSourceFile(filePath, text, typescript.default.ScriptTarget.ESNext, true, scriptKind);
219
+ }
220
+ /** Resolve compiler options from tsconfig (fallback to sane defaults). */
221
+ function resolveCompilerOptions(tsConfigPath) {
270
222
  const configPath = tsConfigPath ?? typescript.default.findConfigFile(process.cwd(), typescript.default.sys.fileExists);
271
223
  let compilerOptions = {
272
224
  target: typescript.default.ScriptTarget.ESNext,
@@ -280,22 +232,25 @@ function extractStepDefinitions(filePaths, tsConfigPath) {
280
232
  if (!configFile.error) compilerOptions = typescript.default.parseJsonConfigFileContent(configFile.config, typescript.default.sys, configPath.replace(/[/\\][^/\\]+$/, "")).options;
281
233
  }
282
234
  compilerOptions.noEmit = true;
283
- const program = typescript.default.createProgram(filePaths, compilerOptions);
284
- const checker = program.getTypeChecker();
235
+ return compilerOptions;
236
+ }
237
+ function extractWithSources(sources, checker) {
238
+ const ctx = {
239
+ checker,
240
+ needsChecker: false
241
+ };
285
242
  const results = [];
286
- for (const filePath of filePaths) {
287
- const sourceFile = program.getSourceFile(filePath);
288
- if (!sourceFile) continue;
289
- const fileResults = extractFromSourceFile(sourceFile, checker);
290
- results.push(...fileResults);
291
- }
292
- return results;
243
+ for (const sourceFile of sources) results.push(...extractFromSourceFile(sourceFile, ctx));
244
+ return {
245
+ results,
246
+ needsChecker: ctx.needsChecker
247
+ };
293
248
  }
294
- function extractFromSourceFile(sourceFile, checker) {
249
+ function extractFromSourceFile(sourceFile, ctx) {
295
250
  const results = [];
296
251
  function visit(node) {
297
252
  if (typescript.default.isCallExpression(node) && typescript.default.isPropertyAccessExpression(node.expression) && node.expression.name.text === "step") {
298
- const meta = extractFromRegisterCall(node, sourceFile, checker);
253
+ const meta = extractFromRegisterCall(node, sourceFile, ctx);
299
254
  if (meta) results.push(meta);
300
255
  }
301
256
  typescript.default.forEachChild(node, visit);
@@ -303,7 +258,7 @@ function extractFromSourceFile(sourceFile, checker) {
303
258
  visit(sourceFile);
304
259
  return results;
305
260
  }
306
- function extractFromRegisterCall(registerCall, sourceFile, checker) {
261
+ function extractFromRegisterCall(registerCall, sourceFile, ctx) {
307
262
  const chain = collectCallChain(registerCall);
308
263
  let stepType = null;
309
264
  let expression = null;
@@ -318,7 +273,7 @@ function extractFromRegisterCall(registerCall, sourceFile, checker) {
318
273
  if (!name) continue;
319
274
  if (name === "register") continue;
320
275
  if (name === "step") {
321
- produces = extractProducedKeys(link, checker);
276
+ produces = extractProducedKeys(link, ctx);
322
277
  continue;
323
278
  }
324
279
  if (name === "dependencies") {
@@ -335,7 +290,7 @@ function extractFromRegisterCall(registerCall, sourceFile, checker) {
335
290
  }
336
291
  }
337
292
  if (!stepType || !expression) {
338
- const reExport = resolveReExportedCall(chain, checker);
293
+ const reExport = resolveReExportedCall(chain, ctx);
339
294
  if (reExport) {
340
295
  if (!stepType) stepType = reExport.stepType;
341
296
  if (!expression) expression = reExport.expression;
@@ -422,33 +377,37 @@ function extractDependencies(depsCall) {
422
377
  }
423
378
  return deps;
424
379
  }
425
- function extractProducedKeys(stepCall, checker) {
380
+ function extractProducedKeys(stepCall, ctx) {
426
381
  const callback = stepCall.arguments[0];
427
382
  if (!callback) return [];
428
- if (typescript.default.isArrowFunction(callback) || typescript.default.isFunctionExpression(callback)) return extractProducedKeysFromCallback(callback, checker);
383
+ if (typescript.default.isArrowFunction(callback) || typescript.default.isFunctionExpression(callback)) return extractProducedKeysFromCallback(callback, ctx);
429
384
  return [];
430
385
  }
431
- function extractProducedKeysFromCallback(callback, checker) {
386
+ function extractProducedKeysFromCallback(callback, ctx) {
432
387
  const body = callback.body;
433
- if (!typescript.default.isBlock(body)) return extractKeysFromExpression(body, checker);
388
+ if (!typescript.default.isBlock(body)) return extractKeysFromExpression(body, ctx);
434
389
  const keys = /* @__PURE__ */ new Set();
435
390
  function visitReturn(node) {
436
- if (typescript.default.isReturnStatement(node) && node.expression) for (const key of extractKeysFromExpression(node.expression, checker)) keys.add(key);
391
+ if (typescript.default.isReturnStatement(node) && node.expression) for (const key of extractKeysFromExpression(node.expression, ctx)) keys.add(key);
437
392
  typescript.default.forEachChild(node, visitReturn);
438
393
  }
439
394
  visitReturn(body);
440
395
  return [...keys];
441
396
  }
442
- function extractKeysFromExpression(expr, checker) {
397
+ function extractKeysFromExpression(expr, ctx) {
443
398
  while (typescript.default.isParenthesizedExpression(expr)) expr = expr.expression;
444
399
  if (typescript.default.isObjectLiteralExpression(expr)) return expr.properties.filter((p) => typescript.default.isPropertyAssignment(p) || typescript.default.isShorthandPropertyAssignment(p)).map((p) => p.name.getText()).filter(Boolean);
400
+ if (!ctx.checker) {
401
+ ctx.needsChecker = true;
402
+ return [];
403
+ }
445
404
  try {
446
- return checker.getTypeAtLocation(expr).getProperties().map((p) => p.name).filter((n) => n !== "merge");
405
+ return ctx.checker.getTypeAtLocation(expr).getProperties().map((p) => p.name).filter((n) => n !== "merge");
447
406
  } catch {
448
407
  return [];
449
408
  }
450
409
  }
451
- function resolveReExportedCall(chain, checker) {
410
+ function resolveReExportedCall(chain, ctx) {
452
411
  const lastCall = chain[chain.length - 1];
453
412
  if (!lastCall) return null;
454
413
  const expr = lastCall.expression;
@@ -456,7 +415,11 @@ function resolveReExportedCall(chain, checker) {
456
415
  if (typescript.default.isIdentifier(expr)) identifier = expr;
457
416
  else if (typescript.default.isPropertyAccessExpression(expr) && typescript.default.isIdentifier(expr.expression)) identifier = expr.expression;
458
417
  if (!identifier) return null;
459
- const symbol = checker.getSymbolAtLocation(identifier);
418
+ if (!ctx.checker) {
419
+ ctx.needsChecker = true;
420
+ return null;
421
+ }
422
+ const symbol = ctx.checker.getSymbolAtLocation(identifier);
460
423
  if (!symbol) return null;
461
424
  const decl = symbol.valueDeclaration;
462
425
  if (!decl || !typescript.default.isVariableDeclaration(decl) || !decl.initializer) return null;
@@ -477,116 +440,6 @@ function resolveReExportedCall(chain, checker) {
477
440
  return null;
478
441
  }
479
442
  //#endregion
480
- //#region src/analyzer/gherkinParser.ts
481
- function parseFeatureFiles(filePaths) {
482
- const scenarios = [];
483
- for (const filePath of filePaths) {
484
- const parsed = parseFeatureContent(node_fs.readFileSync(filePath, "utf-8"), filePath);
485
- scenarios.push(...parsed);
486
- }
487
- return scenarios;
488
- }
489
- function parseFeatureContent(content, filePath) {
490
- const feature = new _cucumber_gherkin.Parser(new _cucumber_gherkin.AstBuilder(_cucumber_messages.IdGenerator.uuid()), new _cucumber_gherkin.GherkinClassicTokenMatcher()).parse(content).feature;
491
- if (!feature) return [];
492
- const featureBackground = [];
493
- const scenarios = [];
494
- const featureTags = tagNames(feature.tags);
495
- for (const child of feature.children) {
496
- if (child.background) featureBackground.push(...child.background.steps);
497
- if (child.scenario) scenarios.push(...expandScenario(child.scenario, featureBackground, filePath, featureTags));
498
- if (child.rule) {
499
- const ruleBackground = [...featureBackground];
500
- const ruleTags = [...featureTags, ...tagNames(child.rule.tags)];
501
- for (const ruleChild of child.rule.children) {
502
- if (ruleChild.background) ruleBackground.push(...ruleChild.background.steps);
503
- if (ruleChild.scenario) scenarios.push(...expandScenario(ruleChild.scenario, ruleBackground, filePath, ruleTags));
504
- }
505
- }
506
- }
507
- return scenarios;
508
- }
509
- /** Extract tag names (each keeping its leading `@`), deduped in order. */
510
- function tagNames(tags) {
511
- return [...new Set((tags ?? []).map((t) => t.name))];
512
- }
513
- function expandScenario(scenario, backgroundSteps, filePath, inheritedTags) {
514
- const scenarioTags = [...inheritedTags, ...tagNames(scenario.tags)];
515
- if (!(scenario.examples.length > 0 && scenario.examples.some((e) => e.tableBody.length > 0))) {
516
- const bgParsed = convertSteps(backgroundSteps);
517
- const scenarioParsed = convertSteps(scenario.steps);
518
- const allSteps = resolveEffectiveKeywords([...bgParsed, ...scenarioParsed]);
519
- return [{
520
- name: scenario.name,
521
- file: filePath,
522
- steps: allSteps,
523
- tags: scenarioTags
524
- }];
525
- }
526
- const results = [];
527
- for (const example of scenario.examples) {
528
- if (!example.tableHeader || example.tableBody.length === 0) continue;
529
- const headers = example.tableHeader.cells.map((c) => c.value);
530
- const exampleTags = [...scenarioTags, ...tagNames(example.tags)];
531
- for (const row of example.tableBody) {
532
- const values = row.cells.map((c) => c.value);
533
- const substitution = {};
534
- headers.forEach((h, i) => {
535
- substitution[h] = values[i];
536
- });
537
- const bgParsed = convertSteps(backgroundSteps);
538
- const scenarioSteps = convertSteps(scenario.steps).map((step) => ({
539
- ...step,
540
- text: substituteExampleValues(step.text, substitution)
541
- }));
542
- const allSteps = resolveEffectiveKeywords([...bgParsed, ...scenarioSteps]);
543
- results.push({
544
- name: headers.map((h, i) => `${h}=${values[i]}`).join(", "),
545
- file: filePath,
546
- steps: allSteps,
547
- tags: exampleTags,
548
- outline: { name: scenario.name }
549
- });
550
- }
551
- }
552
- return results;
553
- }
554
- function convertSteps(steps) {
555
- return steps.map((step) => ({
556
- keyword: normalizeKeyword(step.keyword),
557
- text: step.text,
558
- line: step.location.line,
559
- column: (step.location.column ?? 1) + step.keyword.length
560
- }));
561
- }
562
- function normalizeKeyword(keyword) {
563
- const trimmed = keyword.trim();
564
- if (trimmed === "Given") return "Given";
565
- if (trimmed === "When") return "When";
566
- if (trimmed === "Then") return "Then";
567
- if (trimmed === "And") return "And";
568
- if (trimmed === "But") return "But";
569
- return "Given";
570
- }
571
- function resolveEffectiveKeywords(steps) {
572
- let lastEffective = "Given";
573
- return steps.map((step) => {
574
- let effectiveKeyword;
575
- if (step.keyword === "And" || step.keyword === "But") effectiveKeyword = lastEffective;
576
- else effectiveKeyword = step.keyword;
577
- lastEffective = effectiveKeyword;
578
- return {
579
- ...step,
580
- effectiveKeyword
581
- };
582
- });
583
- }
584
- function substituteExampleValues(text, substitution) {
585
- let result = text;
586
- for (const [key, value] of Object.entries(substitution)) result = result.replace(new RegExp(`<${key}>`, "g"), value);
587
- return result;
588
- }
589
- //#endregion
590
443
  //#region src/analyzer/stepMatcher.ts
591
444
  function compileDefinitions(definitions) {
592
445
  const compiled = [];
@@ -721,12 +574,12 @@ function runRules(rules, scenario, matchedSteps) {
721
574
  //#region src/analyzer/index.ts
722
575
  async function analyze(config, options) {
723
576
  const rules = options?.rules ?? defaultRules;
724
- const stepFilePaths = await resolveGlobs(config.stepFiles);
725
- const featureFilePaths = await resolveGlobs(config.featureFiles);
577
+ const stepFilePaths = await require_gherkinParser.globFiles(config.stepFiles);
578
+ const featureFilePaths = await require_gherkinParser.globFiles(config.featureFiles);
726
579
  if (stepFilePaths.length === 0) return [];
727
580
  if (featureFilePaths.length === 0) return [];
728
581
  const stepDefinitions = extractStepDefinitions(stepFilePaths, config.tsConfigPath);
729
- const scenarios = parseFeatureFiles(featureFilePaths);
582
+ const scenarios = require_gherkinParser.parseFeatureFiles(featureFilePaths);
730
583
  const diagnostics = [];
731
584
  for (const scenario of scenarios) {
732
585
  const scenarioDiags = runRules(rules, scenario, matchScenarioSteps(scenario, stepDefinitions));
@@ -734,11 +587,6 @@ async function analyze(config, options) {
734
587
  }
735
588
  return diagnostics;
736
589
  }
737
- async function resolveGlobs(patterns) {
738
- const files = [];
739
- for (const pattern of patterns) for await (const file of (0, node_fs_promises.glob)(pattern)) files.push(node_path.resolve(file));
740
- return [...new Set(files)];
741
- }
742
590
  //#endregion
743
591
  //#region src/init.ts
744
592
  const createBuilders = () => {
@@ -791,7 +639,12 @@ function afterFeature(fn) {
791
639
  fn
792
640
  });
793
641
  }
794
- /** Run once before the entire test run, across all feature files. */
642
+ /**
643
+ * Run once before the entire run, ahead of any scenario (and before
644
+ * concurrency starts). Multiple `beforeAll` hooks run **in parallel** with no
645
+ * ordering between them — if a step of setup must precede another, sequence both
646
+ * inside a single hook.
647
+ */
795
648
  function beforeAll(fn) {
796
649
  require_hooks.globalHookRegistry.add({
797
650
  scope: "global",
@@ -799,7 +652,11 @@ function beforeAll(fn) {
799
652
  fn
800
653
  });
801
654
  }
802
- /** Run once after the entire test run (reverse registration order). */
655
+ /**
656
+ * Run once after the entire run, once every scenario is done. Multiple
657
+ * `afterAll` hooks run **in parallel** with no ordering between them — sequence
658
+ * dependent teardown inside a single hook.
659
+ */
803
660
  function afterAll(fn) {
804
661
  require_hooks.globalHookRegistry.add({
805
662
  scope: "global",
@@ -812,14 +669,14 @@ function afterAll(fn) {
812
669
  const analyzer = {
813
670
  analyze,
814
671
  extractStepDefinitions,
815
- parseFeatureFiles,
816
- parseFeatureContent,
672
+ parseFeatureFiles: require_gherkinParser.parseFeatureFiles,
673
+ parseFeatureContent: require_gherkinParser.parseFeatureContent,
817
674
  matchScenarioSteps,
818
675
  findMatchingDefinitions,
819
676
  defaultRules
820
677
  };
821
678
  //#endregion
822
- exports.BasicWorld = BasicWorld;
679
+ exports.BasicWorld = require_gherkinParser.BasicWorld;
823
680
  exports.afterAll = afterAll;
824
681
  exports.afterFeature = afterFeature;
825
682
  exports.afterScenario = afterScenario;