sxy-test-runner 2.5.2 → 2.5.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.
Files changed (35) hide show
  1. package/AGENTS.md +14 -6
  2. package/dist/cli/init.js +2 -0
  3. package/dist/cli/init.js.map +1 -1
  4. package/dist/cli/lib/buildDependencyTree.d.ts +1 -2
  5. package/dist/cli/lib/buildDependencyTree.js +25 -23
  6. package/dist/cli/lib/buildDependencyTree.js.map +1 -1
  7. package/dist/cli/lib/buildDependencyTrees.d.ts +1 -2
  8. package/dist/cli/lib/buildDependencyTrees.js +18 -16
  9. package/dist/cli/lib/buildDependencyTrees.js.map +1 -1
  10. package/dist/cli/lib/defaultConfig.js +1 -0
  11. package/dist/cli/lib/defaultConfig.js.map +1 -1
  12. package/dist/cli/lib/failureEntries.d.ts +1 -1
  13. package/dist/cli/lib/failureEntries.js +8 -8
  14. package/dist/cli/lib/failureEntries.js.map +1 -1
  15. package/dist/cli/lib/makeDependencyGraph.d.ts +9 -0
  16. package/dist/cli/lib/makeDependencyGraph.js +191 -0
  17. package/dist/cli/lib/makeDependencyGraph.js.map +1 -0
  18. package/dist/cli/lib/setEnv.d.ts +1 -0
  19. package/dist/cli/lib/setEnv.js +11 -0
  20. package/dist/cli/lib/setEnv.js.map +1 -1
  21. package/dist/cli/lib/showTestResultsSummary.js +1 -1
  22. package/dist/cli/lib/showTestResultsSummary.js.map +1 -1
  23. package/dist/cli/lib/validateConfig.js +4 -0
  24. package/dist/cli/lib/validateConfig.js.map +1 -1
  25. package/dist/cli/lib/watchFilesAndRunTestsNodeWatch.d.ts +3 -2
  26. package/dist/cli/lib/watchFilesAndRunTestsNodeWatch.js +21 -44
  27. package/dist/cli/lib/watchFilesAndRunTestsNodeWatch.js.map +1 -1
  28. package/dist/cli/once.js +2 -1
  29. package/dist/cli/once.js.map +1 -1
  30. package/dist/cli/watch.js +13 -12
  31. package/dist/cli/watch.js.map +1 -1
  32. package/dist/types.d.ts +1 -0
  33. package/dist/types.js.map +1 -1
  34. package/package.json +7 -5
  35. package/readme.md +9 -2
package/AGENTS.md CHANGED
@@ -335,8 +335,10 @@ predicted. On a small or fast suite it is often the better setting.
335
335
 
336
336
  Important limitations:
337
337
 
338
- - Dynamic imports are not found by dependency analysis, so a test reached only that way is
339
- not rerun when its dependency changes. `watch.dependencyEvaluation: false` avoids this.
338
+ - Dependency analysis finds `import()` only when its path is a plain string literal. A path
339
+ computed at runtime (`import(name)`, `import('./' + name)`), or written as a template
340
+ literal, is not found, so a test reached only that way is not rerun when its dependency
341
+ changes. `watch.dependencyEvaluation: false` avoids this.
340
342
  - All reruns still share one long-lived process.
341
343
  - A fresh ESM instance reloads the test's static module graph, but unrelated global state
342
344
  and external resources remain the test author's responsibility.
@@ -469,9 +471,9 @@ maps it to a test. Either half can miss:
469
471
  all**. The stream simply stays quiet, and the previous `done` remains the last word while
470
472
  silently going stale.
471
473
  - Even for a watched file, the changed-to-test mapping is the static import graph, so it
472
- misses dynamic imports and anything reached through a `._cached_.*` boundary. A test file
473
- that was neither changed nor already failing is not re-run, so a break in it never reaches
474
- `failing`. Setting `watch.dependencyEvaluation: false` removes this gap - every watched
474
+ misses imports whose path is computed at runtime and anything reached through a
475
+ `._cached_.*` boundary. A test file that was neither changed nor already failing is not
476
+ re-run, so a break in it never reaches `failing`. Setting `watch.dependencyEvaluation: false` removes this gap - every watched
475
477
  change then reruns everything, and those runs report `covers=all`.
476
478
 
477
479
  So `failing=0` means "nothing is known to be broken", not "everything passes", and silence
@@ -490,7 +492,12 @@ line ending in `:` - because the lines after the first are often the point: `nod
490
492
  puts only a preamble on the first, with the values below, so its failure reads
491
493
  `AssertionError: Expected values to be strictly equal: 1 !== 2`, and a deep-equal diff reads
492
494
  `... deep-equal: + actual - expected / { / + a: 1, / - a: 2 / }`. Blank lines are
493
- dropped and the message is cut at 500 characters; the terminal keeps the full diff.
495
+ dropped and the message is cut at `agentLineLengthLimit` characters - 500 by default,
496
+ `Infinity` for no cut; the terminal keeps the full diff.
497
+
498
+ Agent mode sets `SXYT_AGENT` and `SXY_TEST_RUNNER_AGENT` to `true` once the config has
499
+ loaded, before `setup` runs, so a config enabling it is seen too. When agent mode is off
500
+ both are removed, so a value inherited from a parent agent run is not mistaken for this one.
494
501
 
495
502
  In `once`, read the process's exit code for pass/fail and the `status` on `done` for what
496
503
  kind of failure it was - `status=failed` accompanies exit `20`. `watching` and `change`
@@ -517,6 +524,7 @@ export default {
517
524
  compact: false,
518
525
  restateFailures: true,
519
526
  agent: false,
527
+ agentLineLengthLimit: 500,
520
528
 
521
529
  setup: undefined,
522
530
  beforeEachFile: undefined,
package/dist/cli/init.js CHANGED
@@ -80,6 +80,8 @@ export default {
80
80
 
81
81
  // report only minimal info, failures and results, as single lines, suited for agents
82
82
  agent: false,
83
+ // the most characters of each failure's error text in agent mode, Infinity for no limit
84
+ agentLineLengthLimit: 500,
83
85
 
84
86
 
85
87
  // tasks to run on startup
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport { join } from 'path'\nimport fs from 'fs'\n\nimport chalk from '../packages/chalk.js'\nimport { packageName, cliName, configFileNames, configLocations } from '../config.js'\nimport { out } from '../output.js'\nimport { exitCodes } from './lib/exitCodes.js'\n\n\nexport function init(): void {\n const projectDir = process.cwd()\n\n for (const configLocation of configLocations) {\n for (const configFileName of configFileNames) {\n let loc\n loc = join(projectDir, configLocation, configFileName + '.js')\n if (fs.existsSync(loc)) {\n configAlreadyExists(loc)\n }\n loc = join(projectDir, configLocation, configFileName + '.cjs')\n if (fs.existsSync(loc)) {\n configAlreadyExists(loc)\n }\n loc = join(projectDir, configLocation, configFileName + '.mjs')\n if (fs.existsSync(loc)) {\n configAlreadyExists(loc)\n }\n }\n }\n\n function configAlreadyExists(loc: string): never {\n reportAndExit(`[${packageName}] A matching config already exists at ${loc}`\n + `\\nPlease remove this config before generating a new one with \\`${cliName} init\\``)\n }\n\n const projectPackageJsonLocation = join(projectDir, 'package.json')\n if (!fs.existsSync(projectPackageJsonLocation)) {\n reportAndExit(`[${packageName}] No package.json exists in this location`\n + '\\nPlease initialise the package with `npm init` or `yarn init`'\n + ' before running this command, and run this command from the project root folder')\n }\n\n let packageJson: { type?: string }\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n packageJson = JSON.parse(fs.readFileSync(projectPackageJsonLocation, 'utf8'))\n } catch (error) {\n reportAndExit(`[${packageName}] Could not read the package.json at ${projectPackageJsonLocation}`\n + `\\n${error instanceof Error ? error.message : String(error)}`\n + '\\nPlease make sure the file is readable and contains valid JSON, then try again')\n }\n\n const targetConfigName = (packageJson.type === 'module')\n // ES6 module\n ? join(projectDir, configLocations[0] ?? '', configFileNames[0] + '.js')\n // common\n : join(projectDir, configLocations[0] ?? '', configFileNames[0] + '.mjs')\n\n const defaultConfig =\n `\n// @ts-check\n\n/** @type {import('sxy-test-runner').Config} */\nexport default {\n\n // base folder for tests match and ignore patterns\n testsBase: '.',\n\n // glob pattern or array of glob patterns of test files\n tests: [\n '**/*.sxyt.{js,mjs,jsx}',\n '**/*.test.{js,mjs,jsx}',\n '**/*.spec.{js,mjs,jsx}',\n '**/tests?/*.{js,mjs,jsx}',\n '**/__tests?__/*.{js,mjs,jsx}'\n ],\n\n // glob pattern or array of patterns of test files to ignore\n testsIgnore: ['**/node_modules/**'],\n\n // repeat the failures above the tallies at the end of a run, when there are a small number of them\n restateFailures: true,\n\n // whether to show the full error trace when an error occurs in a test file\n showErrorTrace: false,\n\n // drop the blank lines within each test file's output, does nothing when agent: is true\n compact: false,\n\n // report only minimal info, failures and results, as single lines, suited for agents\n agent: false,\n\n\n // tasks to run on startup\n // accepts a function, file location (relative to project base), or array of functions and or files\n setup: undefined,\n\n // tasks to run before each test file is run\n beforeEachFile: undefined,\n\n // tasks to run after each test file is run\n afterEachFile: undefined,\n\n // tasks to run before each describe block is processed\n // if the function returns an object with named properties, or the file exports named exports,\n // these will be passed to the callback function in argument 0, and can be access by destructruing e.g.\n // describe('thing', ({exportA, exportB}) => { it('should', ...) })\n beforeEachDescribe: undefined,\n\n // tasks to run after each describe block\n afterEachDescribe: undefined,\n\n // tasks to run before each it/test block\n // function returns and file exports will be passed to the it/test block as with describe above, e.g.\n // it('should', ({exportA, exportB}) => { assert('something') })\n beforeEachTest: undefined,\n\n // tasks to run after each it/test block\n afterEachTest: undefined,\n\n // tasks to run once the whole run has finished\n teardown: undefined,\n\n // how to execute the different parts of test files\n execution: {\n\n // run test files in 'parallel' or 'sequential'ly\n files: 'sequential',\n\n // run describe blocks within a test file in 'parallel' or 'sequential'ly\n describes: 'sequential',\n\n // run it/test blocks within a describe blocks in 'parallel' or 'sequential'ly\n tests: 'sequential'\n\n },\n\n // watch mode configurations\n watch: {\n\n //// the base directory to watch files in\n watchFilesBase: '.',\n\n //// glob filter or array of glob filters of files to watch\n watchFiles: '**/*.js',\n\n //// glob filter or array of globl filters of files to ignore\n watchFilesIgnore: '**/node_modules/**',\n\n // run all tests when starting the watcher\n runAllOnStartup: true,\n\n // re run all previously failed tests on each test run, until they pass\n reRunFailingTests: true,\n\n // evaluate the dependencies of each test file, and re-run that test file only when they change. \n // Set this to false to re-run everything on any change\n dependencyEvaluation: true,\n\n // run watch mode keyboard commands immediately, without waiting for Enter\n ttyActionsOnKeypress: false,\n\n // file to write the latest run status to, or false for none\n statusFile: false,\n\n },\n\n}`\n\n try {\n fs.writeFileSync(targetConfigName, defaultConfig)\n } catch (error) {\n reportAndExit(`[${packageName}] Could not write the config to ${targetConfigName}`\n + `\\n${error instanceof Error ? error.message : String(error)}`\n + '\\nPlease check the folder is writable, then try again')\n }\n\n out(chalk.mediumgreen`[${packageName}] Created config at ` + chalk.blue(targetConfigName))\n}\n\n// init cannot continue past any of these, so they report in the error colour and stop\nfunction reportAndExit(message: string): never {\n out(chalk.mediumred(message))\n process.exit(exitCodes.usageError)\n}\n"]}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport { join } from 'path'\nimport fs from 'fs'\n\nimport chalk from '../packages/chalk.js'\nimport { packageName, cliName, configFileNames, configLocations } from '../config.js'\nimport { out } from '../output.js'\nimport { exitCodes } from './lib/exitCodes.js'\n\n\nexport function init(): void {\n const projectDir = process.cwd()\n\n for (const configLocation of configLocations) {\n for (const configFileName of configFileNames) {\n let loc\n loc = join(projectDir, configLocation, configFileName + '.js')\n if (fs.existsSync(loc)) {\n configAlreadyExists(loc)\n }\n loc = join(projectDir, configLocation, configFileName + '.cjs')\n if (fs.existsSync(loc)) {\n configAlreadyExists(loc)\n }\n loc = join(projectDir, configLocation, configFileName + '.mjs')\n if (fs.existsSync(loc)) {\n configAlreadyExists(loc)\n }\n }\n }\n\n function configAlreadyExists(loc: string): never {\n reportAndExit(`[${packageName}] A matching config already exists at ${loc}`\n + `\\nPlease remove this config before generating a new one with \\`${cliName} init\\``)\n }\n\n const projectPackageJsonLocation = join(projectDir, 'package.json')\n if (!fs.existsSync(projectPackageJsonLocation)) {\n reportAndExit(`[${packageName}] No package.json exists in this location`\n + '\\nPlease initialise the package with `npm init` or `yarn init`'\n + ' before running this command, and run this command from the project root folder')\n }\n\n let packageJson: { type?: string }\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n packageJson = JSON.parse(fs.readFileSync(projectPackageJsonLocation, 'utf8'))\n } catch (error) {\n reportAndExit(`[${packageName}] Could not read the package.json at ${projectPackageJsonLocation}`\n + `\\n${error instanceof Error ? error.message : String(error)}`\n + '\\nPlease make sure the file is readable and contains valid JSON, then try again')\n }\n\n const targetConfigName = (packageJson.type === 'module')\n // ES6 module\n ? join(projectDir, configLocations[0] ?? '', configFileNames[0] + '.js')\n // common\n : join(projectDir, configLocations[0] ?? '', configFileNames[0] + '.mjs')\n\n const defaultConfig =\n `\n// @ts-check\n\n/** @type {import('sxy-test-runner').Config} */\nexport default {\n\n // base folder for tests match and ignore patterns\n testsBase: '.',\n\n // glob pattern or array of glob patterns of test files\n tests: [\n '**/*.sxyt.{js,mjs,jsx}',\n '**/*.test.{js,mjs,jsx}',\n '**/*.spec.{js,mjs,jsx}',\n '**/tests?/*.{js,mjs,jsx}',\n '**/__tests?__/*.{js,mjs,jsx}'\n ],\n\n // glob pattern or array of patterns of test files to ignore\n testsIgnore: ['**/node_modules/**'],\n\n // repeat the failures above the tallies at the end of a run, when there are a small number of them\n restateFailures: true,\n\n // whether to show the full error trace when an error occurs in a test file\n showErrorTrace: false,\n\n // drop the blank lines within each test file's output, does nothing when agent: is true\n compact: false,\n\n // report only minimal info, failures and results, as single lines, suited for agents\n agent: false,\n // the most characters of each failure's error text in agent mode, Infinity for no limit\n agentLineLengthLimit: 500,\n\n\n // tasks to run on startup\n // accepts a function, file location (relative to project base), or array of functions and or files\n setup: undefined,\n\n // tasks to run before each test file is run\n beforeEachFile: undefined,\n\n // tasks to run after each test file is run\n afterEachFile: undefined,\n\n // tasks to run before each describe block is processed\n // if the function returns an object with named properties, or the file exports named exports,\n // these will be passed to the callback function in argument 0, and can be access by destructruing e.g.\n // describe('thing', ({exportA, exportB}) => { it('should', ...) })\n beforeEachDescribe: undefined,\n\n // tasks to run after each describe block\n afterEachDescribe: undefined,\n\n // tasks to run before each it/test block\n // function returns and file exports will be passed to the it/test block as with describe above, e.g.\n // it('should', ({exportA, exportB}) => { assert('something') })\n beforeEachTest: undefined,\n\n // tasks to run after each it/test block\n afterEachTest: undefined,\n\n // tasks to run once the whole run has finished\n teardown: undefined,\n\n // how to execute the different parts of test files\n execution: {\n\n // run test files in 'parallel' or 'sequential'ly\n files: 'sequential',\n\n // run describe blocks within a test file in 'parallel' or 'sequential'ly\n describes: 'sequential',\n\n // run it/test blocks within a describe blocks in 'parallel' or 'sequential'ly\n tests: 'sequential'\n\n },\n\n // watch mode configurations\n watch: {\n\n //// the base directory to watch files in\n watchFilesBase: '.',\n\n //// glob filter or array of glob filters of files to watch\n watchFiles: '**/*.js',\n\n //// glob filter or array of globl filters of files to ignore\n watchFilesIgnore: '**/node_modules/**',\n\n // run all tests when starting the watcher\n runAllOnStartup: true,\n\n // re run all previously failed tests on each test run, until they pass\n reRunFailingTests: true,\n\n // evaluate the dependencies of each test file, and re-run that test file only when they change. \n // Set this to false to re-run everything on any change\n dependencyEvaluation: true,\n\n // run watch mode keyboard commands immediately, without waiting for Enter\n ttyActionsOnKeypress: false,\n\n // file to write the latest run status to, or false for none\n statusFile: false,\n\n },\n\n}`\n\n try {\n fs.writeFileSync(targetConfigName, defaultConfig)\n } catch (error) {\n reportAndExit(`[${packageName}] Could not write the config to ${targetConfigName}`\n + `\\n${error instanceof Error ? error.message : String(error)}`\n + '\\nPlease check the folder is writable, then try again')\n }\n\n out(chalk.mediumgreen`[${packageName}] Created config at ` + chalk.blue(targetConfigName))\n}\n\n// init cannot continue past any of these, so they report in the error colour and stop\nfunction reportAndExit(message: string): never {\n out(chalk.mediumred(message))\n process.exit(exitCodes.usageError)\n}\n"]}
@@ -1,3 +1,2 @@
1
- import type { FinalConfig } from '../../types.js';
2
- export declare function buildDependencyTree(config: FinalConfig, filename: string): string[];
1
+ export {};
3
2
  //# sourceMappingURL=buildDependencyTree.d.ts.map
@@ -1,24 +1,26 @@
1
- import dependencyTree from 'dependency-tree';
2
- import { debug } from '../../output.js';
3
- import slash from '../../packages/slash.js';
4
- export function buildDependencyTree(config, filename) {
5
- filename = slash(filename);
6
- const dependencyTreeConfig = {
7
- filename,
8
- isListForm: true,
9
- directory: config.watch.watchFilesBase,
10
- filter: (path) => path.indexOf('node_modules') === -1
11
- };
12
- // isListForm:true makes dependency-tree return a flat string[] at runtime,
13
- // which its shipped types don't model (they only type the tree-object form).
14
- const dt = dependencyTree(dependencyTreeConfig);
15
- // process the results into final format
16
- // (forward slashes and convert full path to rel path)
17
- const cwd = process.cwd();
18
- const processed = dt
19
- .map(file => slash(file).slice(cwd.length + 1))
20
- .filter(file => file !== filename); // remove the current file
21
- debug('found dep tree', processed);
22
- return processed;
23
- }
1
+ // import dependencyTree from 'dependency-tree'
2
+ export {};
3
+ // import type { FinalConfig } from '../../types.js'
4
+ // import { debug } from '../../output.js'
5
+ // import slash from '../../packages/slash.js'
6
+ // export function buildDependencyTree(config: FinalConfig, filename: string): string[] {
7
+ // filename = slash(filename)
8
+ // const dependencyTreeConfig = {
9
+ // filename,
10
+ // isListForm: true,
11
+ // directory: config.watch.watchFilesBase,
12
+ // filter: (path: string) => path.indexOf('node_modules') === -1
13
+ // }
14
+ // // isListForm:true makes dependency-tree return a flat string[] at runtime,
15
+ // // which its shipped types don't model (they only type the tree-object form).
16
+ // const dt = dependencyTree(dependencyTreeConfig) as unknown as string[]
17
+ // // process the results into final format
18
+ // // (forward slashes and convert full path to rel path)
19
+ // const cwd = process.cwd()
20
+ // const processed = dt
21
+ // .map(file => slash(file).slice(cwd.length + 1))
22
+ // .filter(file => file !== filename) // remove the current file
23
+ // debug('found dep tree', processed)
24
+ // return processed
25
+ // }
24
26
  //# sourceMappingURL=buildDependencyTree.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildDependencyTree.js","sourceRoot":"","sources":["../../../src/cli/lib/buildDependencyTree.ts"],"names":[],"mappings":"AACA,OAAO,cAAc,MAAM,iBAAiB,CAAA;AAG5C,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AACvC,OAAO,KAAK,MAAM,yBAAyB,CAAA;AAG3C,MAAM,UAAU,mBAAmB,CAAC,MAAmB,EAAE,QAAgB;IACrE,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAA;IAE1B,MAAM,oBAAoB,GAAG;QACzB,QAAQ;QACR,UAAU,EAAE,IAAI;QAChB,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,cAAc;QACtC,MAAM,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;KAChE,CAAA;IAED,2EAA2E;IAC3E,6EAA6E;IAC7E,MAAM,EAAE,GAAG,cAAc,CAAC,oBAAoB,CAAwB,CAAA;IAEtE,wCAAwC;IACxC,sDAAsD;IACtD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAA;IACzB,MAAM,SAAS,GAAG,EAAE;SACf,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;SAC9C,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAA,CAAC,0BAA0B;IAEjE,KAAK,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAA;IAElC,OAAO,SAAS,CAAA;AACpB,CAAC","sourcesContent":["\nimport dependencyTree from 'dependency-tree'\n\nimport type { FinalConfig } from '../../types.js'\nimport { debug } from '../../output.js'\nimport slash from '../../packages/slash.js'\n\n\nexport function buildDependencyTree(config: FinalConfig, filename: string): string[] {\n filename = slash(filename)\n\n const dependencyTreeConfig = {\n filename,\n isListForm: true,\n directory: config.watch.watchFilesBase,\n filter: (path: string) => path.indexOf('node_modules') === -1\n }\n\n // isListForm:true makes dependency-tree return a flat string[] at runtime,\n // which its shipped types don't model (they only type the tree-object form).\n const dt = dependencyTree(dependencyTreeConfig) as unknown as string[]\n\n // process the results into final format\n // (forward slashes and convert full path to rel path)\n const cwd = process.cwd()\n const processed = dt\n .map(file => slash(file).slice(cwd.length + 1))\n .filter(file => file !== filename) // remove the current file\n\n debug('found dep tree', processed)\n\n return processed\n}\n"]}
1
+ {"version":3,"file":"buildDependencyTree.js","sourceRoot":"","sources":["../../../src/cli/lib/buildDependencyTree.ts"],"names":[],"mappings":"AACA,+CAA+C;;AAE/C,oDAAoD;AACpD,0CAA0C;AAC1C,8CAA8C;AAG9C,yFAAyF;AACzF,iCAAiC;AAEjC,qCAAqC;AACrC,oBAAoB;AACpB,4BAA4B;AAC5B,kDAAkD;AAClD,wEAAwE;AACxE,QAAQ;AAER,kFAAkF;AAClF,oFAAoF;AACpF,6EAA6E;AAE7E,+CAA+C;AAC/C,6DAA6D;AAC7D,gCAAgC;AAChC,2BAA2B;AAC3B,0DAA0D;AAC1D,wEAAwE;AAExE,yCAAyC;AAEzC,uBAAuB;AACvB,IAAI","sourcesContent":["\n// import dependencyTree from 'dependency-tree'\n\n// import type { FinalConfig } from '../../types.js'\n// import { debug } from '../../output.js'\n// import slash from '../../packages/slash.js'\n\n\n// export function buildDependencyTree(config: FinalConfig, filename: string): string[] {\n// filename = slash(filename)\n\n// const dependencyTreeConfig = {\n// filename,\n// isListForm: true,\n// directory: config.watch.watchFilesBase,\n// filter: (path: string) => path.indexOf('node_modules') === -1\n// }\n\n// // isListForm:true makes dependency-tree return a flat string[] at runtime,\n// // which its shipped types don't model (they only type the tree-object form).\n// const dt = dependencyTree(dependencyTreeConfig) as unknown as string[]\n\n// // process the results into final format\n// // (forward slashes and convert full path to rel path)\n// const cwd = process.cwd()\n// const processed = dt\n// .map(file => slash(file).slice(cwd.length + 1))\n// .filter(file => file !== filename) // remove the current file\n\n// debug('found dep tree', processed)\n\n// return processed\n// }\n"]}
@@ -1,3 +1,2 @@
1
- import type { FinalConfig, DependencyTrees } from '../../types.js';
2
- export declare function buildDependencyTrees(config: FinalConfig, testFiles: Set<string>): DependencyTrees;
1
+ export {};
3
2
  //# sourceMappingURL=buildDependencyTrees.d.ts.map
@@ -1,17 +1,19 @@
1
- import { createTimeProfiler } from '../../packages/timeProfiler.js';
2
- import { showTimeProfiling } from '../../config.js';
3
- import { debug } from '../../output.js';
4
- import { buildDependencyTree } from './buildDependencyTree.js';
5
- const { timeProfile } = createTimeProfiler(showTimeProfiling);
6
- export function buildDependencyTrees(config, testFiles) {
7
- return timeProfile('build dependency trees', () => {
8
- const dependencyTrees = {};
9
- for (const testFile of testFiles) {
10
- const dependencyTree = buildDependencyTree(config, testFile);
11
- dependencyTrees[testFile] = dependencyTree;
12
- debug('final dep tree', dependencyTrees[testFile]);
13
- }
14
- return dependencyTrees;
15
- });
16
- }
1
+ // import type { FinalConfig, DependencyTrees } from '../../types.js'
2
+ // import { createTimeProfiler } from '../../packages/timeProfiler.js'
3
+ // import { showTimeProfiling } from '../../config.js'
4
+ // import { debug } from '../../output.js'
5
+ // import { buildDependencyTree } from './buildDependencyTree.js'
6
+ export {};
7
+ // const { timeProfile } = createTimeProfiler(showTimeProfiling)
8
+ // export function buildDependencyTrees(config: FinalConfig, testFiles: Set<string>): DependencyTrees {
9
+ // return timeProfile('build dependency trees', () => {
10
+ // const dependencyTrees: DependencyTrees = {}
11
+ // for (const testFile of testFiles) {
12
+ // const dependencyTree = buildDependencyTree(config, testFile)
13
+ // dependencyTrees[testFile] = dependencyTree
14
+ // debug('final dep tree', dependencyTrees[testFile])
15
+ // }
16
+ // return dependencyTrees
17
+ // })
18
+ // }
17
19
  //# sourceMappingURL=buildDependencyTrees.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildDependencyTrees.js","sourceRoot":"","sources":["../../../src/cli/lib/buildDependencyTrees.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAA;AACvC,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAG9D,MAAM,EAAE,WAAW,EAAE,GAAG,kBAAkB,CAAC,iBAAiB,CAAC,CAAA;AAE7D,MAAM,UAAU,oBAAoB,CAAC,MAAmB,EAAE,SAAsB;IAC5E,OAAO,WAAW,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAC9C,MAAM,eAAe,GAAoB,EAAE,CAAA;QAE3C,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YAC/B,MAAM,cAAc,GAAG,mBAAmB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;YAC5D,eAAe,CAAC,QAAQ,CAAC,GAAG,cAAc,CAAA;YAC1C,KAAK,CAAC,gBAAgB,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAA;QACtD,CAAC;QAED,OAAO,eAAe,CAAA;IAC1B,CAAC,CAAC,CAAA;AACN,CAAC","sourcesContent":["\nimport type { FinalConfig, DependencyTrees } from '../../types.js'\nimport { createTimeProfiler } from '../../packages/timeProfiler.js'\nimport { showTimeProfiling } from '../../config.js'\nimport { debug } from '../../output.js'\nimport { buildDependencyTree } from './buildDependencyTree.js'\n\n\nconst { timeProfile } = createTimeProfiler(showTimeProfiling)\n\nexport function buildDependencyTrees(config: FinalConfig, testFiles: Set<string>): DependencyTrees {\n return timeProfile('build dependency trees', () => {\n const dependencyTrees: DependencyTrees = {}\n\n for (const testFile of testFiles) {\n const dependencyTree = buildDependencyTree(config, testFile)\n dependencyTrees[testFile] = dependencyTree\n debug('final dep tree', dependencyTrees[testFile])\n }\n\n return dependencyTrees\n })\n}\n"]}
1
+ {"version":3,"file":"buildDependencyTrees.js","sourceRoot":"","sources":["../../../src/cli/lib/buildDependencyTrees.ts"],"names":[],"mappings":"AACA,qEAAqE;AACrE,sEAAsE;AACtE,sDAAsD;AACtD,0CAA0C;AAC1C,iEAAiE;;AAGjE,gEAAgE;AAEhE,uGAAuG;AACvG,2DAA2D;AAC3D,sDAAsD;AAEtD,8CAA8C;AAC9C,2EAA2E;AAC3E,yDAAyD;AACzD,iEAAiE;AACjE,YAAY;AAEZ,iCAAiC;AACjC,SAAS;AACT,IAAI","sourcesContent":["\n// import type { FinalConfig, DependencyTrees } from '../../types.js'\n// import { createTimeProfiler } from '../../packages/timeProfiler.js'\n// import { showTimeProfiling } from '../../config.js'\n// import { debug } from '../../output.js'\n// import { buildDependencyTree } from './buildDependencyTree.js'\n\n\n// const { timeProfile } = createTimeProfiler(showTimeProfiling)\n\n// export function buildDependencyTrees(config: FinalConfig, testFiles: Set<string>): DependencyTrees {\n// return timeProfile('build dependency trees', () => {\n// const dependencyTrees: DependencyTrees = {}\n\n// for (const testFile of testFiles) {\n// const dependencyTree = buildDependencyTree(config, testFile)\n// dependencyTrees[testFile] = dependencyTree\n// debug('final dep tree', dependencyTrees[testFile])\n// }\n\n// return dependencyTrees\n// })\n// }\n"]}
@@ -12,6 +12,7 @@ export const defaultConfig = {
12
12
  compact: false,
13
13
  restateFailures: true,
14
14
  agent: false,
15
+ agentLineLengthLimit: 500,
15
16
  failFast: false,
16
17
  execution: {
17
18
  files: 'sequential',
@@ -1 +1 @@
1
- {"version":3,"file":"defaultConfig.js","sourceRoot":"","sources":["../../../src/cli/lib/defaultConfig.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,aAAa,GAAgB;IACtC,SAAS,EAAE,GAAG;IACd,KAAK,EAAE;QACH,wBAAwB;QACxB,wBAAwB;QACxB,wBAAwB;QACxB,0BAA0B;QAC1B,8BAA8B;KACjC;IACD,WAAW,EAAE,CAAC,oBAAoB,CAAC;IACnC,cAAc,EAAE,KAAK;IACrB,OAAO,EAAE,KAAK;IACd,eAAe,EAAE,IAAI;IACrB,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE,KAAK;IACf,SAAS,EAAE;QACP,KAAK,EAAE,YAAY;QACnB,SAAS,EAAE,YAAY;QACvB,KAAK,EAAE,YAAY;KACtB;IACD,KAAK,EAAE;QACH,cAAc,EAAE,GAAG;QACnB,UAAU,EAAE,SAAS;QACrB,gBAAgB,EAAE,oBAAoB;QACtC,eAAe,EAAE,IAAI;QACrB,iBAAiB,EAAE,IAAI;QACvB,oBAAoB,EAAE,IAAI;QAC1B,oBAAoB,EAAE,KAAK;QAC3B,UAAU,EAAE,KAAK;KACpB;CACJ,CAAA","sourcesContent":["\nimport type { FinalConfig } from '../../types.js'\n\n\nexport const defaultConfig: FinalConfig = {\n testsBase: '.',\n tests: [\n '**/*.sxyt.{js,mjs,jsx}',\n '**/*.test.{js,mjs,jsx}',\n '**/*.spec.{js,mjs,jsx}',\n '**/tests?/*.{js,mjs,jsx}',\n '**/__tests?__/*.{js,mjs,jsx}'\n ],\n testsIgnore: ['**/node_modules/**'],\n showErrorTrace: false,\n compact: false,\n restateFailures: true,\n agent: false,\n failFast: false,\n execution: {\n files: 'sequential',\n describes: 'sequential',\n tests: 'sequential'\n },\n watch: {\n watchFilesBase: '.',\n watchFiles: '**/*.js',\n watchFilesIgnore: '**/node_modules/**',\n runAllOnStartup: true,\n reRunFailingTests: true,\n dependencyEvaluation: true,\n ttyActionsOnKeypress: false,\n statusFile: false\n }\n}\n"]}
1
+ {"version":3,"file":"defaultConfig.js","sourceRoot":"","sources":["../../../src/cli/lib/defaultConfig.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,MAAM,aAAa,GAAgB;IACtC,SAAS,EAAE,GAAG;IACd,KAAK,EAAE;QACH,wBAAwB;QACxB,wBAAwB;QACxB,wBAAwB;QACxB,0BAA0B;QAC1B,8BAA8B;KACjC;IACD,WAAW,EAAE,CAAC,oBAAoB,CAAC;IACnC,cAAc,EAAE,KAAK;IACrB,OAAO,EAAE,KAAK;IACd,eAAe,EAAE,IAAI;IACrB,KAAK,EAAE,KAAK;IACZ,oBAAoB,EAAE,GAAG;IACzB,QAAQ,EAAE,KAAK;IACf,SAAS,EAAE;QACP,KAAK,EAAE,YAAY;QACnB,SAAS,EAAE,YAAY;QACvB,KAAK,EAAE,YAAY;KACtB;IACD,KAAK,EAAE;QACH,cAAc,EAAE,GAAG;QACnB,UAAU,EAAE,SAAS;QACrB,gBAAgB,EAAE,oBAAoB;QACtC,eAAe,EAAE,IAAI;QACrB,iBAAiB,EAAE,IAAI;QACvB,oBAAoB,EAAE,IAAI;QAC1B,oBAAoB,EAAE,KAAK;QAC3B,UAAU,EAAE,KAAK;KACpB;CACJ,CAAA","sourcesContent":["\nimport type { FinalConfig } from '../../types.js'\n\n\nexport const defaultConfig: FinalConfig = {\n testsBase: '.',\n tests: [\n '**/*.sxyt.{js,mjs,jsx}',\n '**/*.test.{js,mjs,jsx}',\n '**/*.spec.{js,mjs,jsx}',\n '**/tests?/*.{js,mjs,jsx}',\n '**/__tests?__/*.{js,mjs,jsx}'\n ],\n testsIgnore: ['**/node_modules/**'],\n showErrorTrace: false,\n compact: false,\n restateFailures: true,\n agent: false,\n agentLineLengthLimit: 500,\n failFast: false,\n execution: {\n files: 'sequential',\n describes: 'sequential',\n tests: 'sequential'\n },\n watch: {\n watchFilesBase: '.',\n watchFiles: '**/*.js',\n watchFilesIgnore: '**/node_modules/**',\n runAllOnStartup: true,\n reRunFailingTests: true,\n dependencyEvaluation: true,\n ttyActionsOnKeypress: false,\n statusFile: false\n }\n}\n"]}
@@ -6,5 +6,5 @@ export interface FailureEntry {
6
6
  test?: string;
7
7
  error: string;
8
8
  }
9
- export declare function failureEntries(testResults: TestResult[], erroringTests?: Record<string, TestFile>): FailureEntry[];
9
+ export declare function failureEntries(testResults: TestResult[], erroringTests?: Record<string, TestFile>, messageLimit?: number): FailureEntry[];
10
10
  //# sourceMappingURL=failureEntries.d.ts.map
@@ -1,5 +1,6 @@
1
1
  import { inspect } from 'node:util';
2
- export function failureEntries(testResults, erroringTests = {}) {
2
+ export function failureEntries(testResults, erroringTests = {}, messageLimit = 500) {
3
+ const simplifyError = (error) => simplifyErrorTo(error, messageLimit);
3
4
  const entries = [];
4
5
  for (const [file, test] of Object.entries(erroringTests)) {
5
6
  entries.push({ file, error: `did not load: ${loadError(test.error)}` });
@@ -32,28 +33,27 @@ export function failureEntries(testResults, erroringTests = {}) {
32
33
  // preamble and keep the cause, flattened: an import error can run to several lines.
33
34
  function loadError(error) {
34
35
  if (typeof error !== 'string')
35
- return simplifyError(error);
36
+ return simplifyErrorTo(error, 500);
36
37
  const cause = error.replace(/^Error loading file\r?\n/, '').replace(/\s*[\r\n]+\s*/g, ' ').trim();
37
38
  if (cause === '')
38
39
  return 'Error loading file';
39
40
  return (cause.length > 200) ? `${cause.slice(0, 200)}...` : cause;
40
41
  }
41
- function simplifyError(error) {
42
+ function simplifyErrorTo(error, messageLimit) {
42
43
  if (error === undefined || error === null || error === false)
43
44
  return 'unknown error';
44
45
  if (typeof error === 'object' && 'message' in error && typeof error.message === 'string') {
45
46
  const name = ('name' in error && typeof error.name === 'string') ? error.name : 'Error';
46
- return `${name}: ${joinLines(error.message)}`;
47
+ return `${name}: ${joinLines(error.message, messageLimit)}`;
47
48
  }
48
49
  if (typeof error === 'string')
49
- return joinLines(error);
50
+ return joinLines(error, messageLimit);
50
51
  // breakLength keeps inspect from wrapping, so the result stays one line
51
- return joinLines(inspect(error, { breakLength: Infinity }));
52
+ return joinLines(inspect(error, { breakLength: Infinity }), messageLimit);
52
53
  }
53
- const messageLimit = 500;
54
54
  // lines after the first often carry the point - node:assert's values, ensure's sightings - so
55
55
  // all are kept, joined to stay one line for the status file and the agent stream
56
- function joinLines(text) {
56
+ function joinLines(text, messageLimit) {
57
57
  const joined = text.split(/\r?\n/).map(line => line.trim()).filter(line => line !== '')
58
58
  .reduce((previous, line) => (previous === '')
59
59
  ? line
@@ -1 +1 @@
1
- {"version":3,"file":"failureEntries.js","sourceRoot":"","sources":["../../../src/cli/lib/failureEntries.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport { inspect } from 'node:util'\n\nimport type { TestResult } from './runTest.js'\nimport type { TestFile } from './loadTestFile.js'\n\n\n// One failure, flattened. `describe` is absent when a file would not load, and `test` is\n// absent when the describe itself threw. Shared by the status file's nested tree and the\n// agent reporter's flat lines, so both describe a failure the same way.\nexport interface FailureEntry {\n file: string\n describe?: string\n test?: string\n error: string\n}\n\nexport function failureEntries(\n testResults: TestResult[],\n erroringTests: Record<string, TestFile> = {}\n): FailureEntry[] {\n const entries: FailureEntry[] = []\n\n for (const [file, test] of Object.entries(erroringTests)) {\n entries.push({ file, error: `did not load: ${loadError(test.error)}` })\n }\n\n for (const testResult of testResults) {\n for (const describeResult of testResult.describeResults) {\n if (describeResult.error !== undefined) {\n entries.push({\n file: testResult.file,\n describe: describeResult.thing,\n error: `threw: ${simplifyError(describeResult.error)}`\n })\n }\n\n for (const itResult of describeResult.itResults) {\n if (itResult.success !== false) continue\n entries.push({\n file: testResult.file,\n describe: describeResult.thing,\n test: itResult.should,\n error: simplifyError(itResult.error)\n })\n }\n }\n }\n\n return entries\n}\n\n// the loader stores its own preamble with the real cause on the line below, so the first\n// line alone says only that loading failed - which `did not load` has already said. Drop the\n// preamble and keep the cause, flattened: an import error can run to several lines.\nfunction loadError(error: unknown): string {\n if (typeof error !== 'string') return simplifyError(error)\n\n const cause = error.replace(/^Error loading file\\r?\\n/, '').replace(/\\s*[\\r\\n]+\\s*/g, ' ').trim()\n if (cause === '') return 'Error loading file'\n return (cause.length > 200) ? `${cause.slice(0, 200)}...` : cause\n}\n\nfunction simplifyError(error: unknown): string {\n if (error === undefined || error === null || error === false) return 'unknown error'\n\n if (typeof error === 'object' && 'message' in error && typeof error.message === 'string') {\n const name = ('name' in error && typeof error.name === 'string') ? error.name : 'Error'\n return `${name}: ${joinLines(error.message)}`\n }\n\n if (typeof error === 'string') return joinLines(error)\n // breakLength keeps inspect from wrapping, so the result stays one line\n return joinLines(inspect(error, { breakLength: Infinity }))\n}\n\nconst messageLimit = 500\n\n// lines after the first often carry the point - node:assert's values, ensure's sightings - so\n// all are kept, joined to stay one line for the status file and the agent stream\nfunction joinLines(text: string): string {\n const joined = text.split(/\\r?\\n/).map(line => line.trim()).filter(line => line !== '')\n .reduce((previous, line) => (previous === '')\n ? line\n : previous + (previous.endsWith(':') ? ' ' : ' / ') + line, '')\n return (joined.length > messageLimit) ? `${joined.slice(0, messageLimit)}...` : joined\n}\n"]}
1
+ {"version":3,"file":"failureEntries.js","sourceRoot":"","sources":["../../../src/cli/lib/failureEntries.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport { inspect } from 'node:util'\n\nimport type { TestResult } from './runTest.js'\nimport type { TestFile } from './loadTestFile.js'\n\n\n// One failure, flattened. `describe` is absent when a file would not load, and `test` is\n// absent when the describe itself threw. Shared by the status file's nested tree and the\n// agent reporter's flat lines, so both describe a failure the same way.\nexport interface FailureEntry {\n file: string\n describe?: string\n test?: string\n error: string\n}\n\nexport function failureEntries(\n testResults: TestResult[],\n erroringTests: Record<string, TestFile> = {},\n messageLimit = 500\n): FailureEntry[] {\n const simplifyError = (error: unknown) => simplifyErrorTo(error, messageLimit)\n const entries: FailureEntry[] = []\n\n for (const [file, test] of Object.entries(erroringTests)) {\n entries.push({ file, error: `did not load: ${loadError(test.error)}` })\n }\n\n for (const testResult of testResults) {\n for (const describeResult of testResult.describeResults) {\n if (describeResult.error !== undefined) {\n entries.push({\n file: testResult.file,\n describe: describeResult.thing,\n error: `threw: ${simplifyError(describeResult.error)}`\n })\n }\n\n for (const itResult of describeResult.itResults) {\n if (itResult.success !== false) continue\n entries.push({\n file: testResult.file,\n describe: describeResult.thing,\n test: itResult.should,\n error: simplifyError(itResult.error)\n })\n }\n }\n }\n\n return entries\n}\n\n// the loader stores its own preamble with the real cause on the line below, so the first\n// line alone says only that loading failed - which `did not load` has already said. Drop the\n// preamble and keep the cause, flattened: an import error can run to several lines.\nfunction loadError(error: unknown): string {\n if (typeof error !== 'string') return simplifyErrorTo(error, 500)\n\n const cause = error.replace(/^Error loading file\\r?\\n/, '').replace(/\\s*[\\r\\n]+\\s*/g, ' ').trim()\n if (cause === '') return 'Error loading file'\n return (cause.length > 200) ? `${cause.slice(0, 200)}...` : cause\n}\n\nfunction simplifyErrorTo(error: unknown, messageLimit: number): string {\n if (error === undefined || error === null || error === false) return 'unknown error'\n\n if (typeof error === 'object' && 'message' in error && typeof error.message === 'string') {\n const name = ('name' in error && typeof error.name === 'string') ? error.name : 'Error'\n return `${name}: ${joinLines(error.message, messageLimit)}`\n }\n\n if (typeof error === 'string') return joinLines(error, messageLimit)\n // breakLength keeps inspect from wrapping, so the result stays one line\n return joinLines(inspect(error, { breakLength: Infinity }), messageLimit)\n}\n\n// lines after the first often carry the point - node:assert's values, ensure's sightings - so\n// all are kept, joined to stay one line for the status file and the agent stream\nfunction joinLines(text: string, messageLimit: number): string {\n const joined = text.split(/\\r?\\n/).map(line => line.trim()).filter(line => line !== '')\n .reduce((previous, line) => (previous === '')\n ? line\n : previous + (previous.endsWith(':') ? ' ' : ' / ') + line, '')\n return (joined.length > messageLimit) ? `${joined.slice(0, messageLimit)}...` : joined\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import type { FinalConfig } from '../../types.js';
2
+ export interface DependencyGraph {
3
+ addTest(file: string): void;
4
+ update(file: string): void;
5
+ remove(file: string): void;
6
+ testsAffectedBy(file: string): Set<string>;
7
+ }
8
+ export declare function makeDependencyGraph(config: FinalConfig, testFiles: Iterable<string>): DependencyGraph;
9
+ //# sourceMappingURL=makeDependencyGraph.d.ts.map
@@ -0,0 +1,191 @@
1
+ import { existsSync, readFileSync, realpathSync } from 'node:fs';
2
+ import { isBuiltin } from 'node:module';
3
+ import { basename, dirname, extname, isAbsolute, join, relative, resolve } from 'node:path';
4
+ import cabinet from 'filing-cabinet';
5
+ import precinct from 'sxy-precinct';
6
+ import slash from '../../packages/slash.js';
7
+ import { createTimeProfiler } from '../../packages/timeProfiler.js';
8
+ import { showTimeProfiling } from '../../config.js';
9
+ const { timeProfile } = createTimeProfiler(showTimeProfiling);
10
+ // precinct type per extension; undefined sniffs ESM vs CommonJS from the source
11
+ const precinctTypes = {
12
+ '.js': undefined, '.jsx': undefined,
13
+ '.mjs': 'es6', '.cjs': 'commonjs',
14
+ '.ts': 'ts', '.mts': 'ts', '.cts': 'ts', '.tsx': 'tsx'
15
+ };
16
+ for (const [extension, from] of [['.mjs', '.js'], ['.cjs', '.js'], ['.mts', '.ts'], ['.cts', '.ts']]) {
17
+ const lookup = cabinet.getLookup(from);
18
+ if (cabinet.getLookup(extension) === undefined && lookup !== undefined)
19
+ cabinet.register(extension, lookup);
20
+ }
21
+ function tryRealpath(path) {
22
+ try {
23
+ return realpathSync.native(path);
24
+ }
25
+ catch {
26
+ return undefined;
27
+ }
28
+ }
29
+ // paths in and out are relative to the cwd with forward slashes; edges are kept between real paths
30
+ export function makeDependencyGraph(config, testFiles) {
31
+ const cwd = realpathSync.native(process.cwd());
32
+ const directory = resolve(config.watch.watchFilesBase === '' ? '.' : config.watch.watchFilesBase);
33
+ const imports = new Map();
34
+ const importers = new Map();
35
+ const tests = new Set();
36
+ const toKey = (absolute) => slash(relative(cwd, absolute));
37
+ const toAbsolute = (key) => resolve(cwd, key);
38
+ // cleared by each operation, so a path created, deleted or relinked since is never served stale
39
+ const realCache = new Map();
40
+ const resolveCache = new Map();
41
+ const clearCaches = () => {
42
+ realCache.clear();
43
+ resolveCache.clear();
44
+ };
45
+ function realAbsolute(file) {
46
+ const absolute = resolve(file);
47
+ let real = realCache.get(absolute);
48
+ if (real === undefined) {
49
+ // a missing file's directory may still be reached through a link
50
+ real = tryRealpath(absolute)
51
+ ?? join(tryRealpath(dirname(absolute)) ?? dirname(absolute), basename(absolute));
52
+ realCache.set(absolute, real);
53
+ }
54
+ return real;
55
+ }
56
+ const realKey = (file) => toKey(realAbsolute(file));
57
+ // undefined when the file does not parse, so its last known imports stand
58
+ function readImports(key) {
59
+ const filename = toAbsolute(key);
60
+ const extension = extname(filename);
61
+ if (!existsSync(filename) || !(extension in precinctTypes))
62
+ return new Set();
63
+ let specifiers;
64
+ try {
65
+ specifiers = precinct(readFileSync(filename, 'utf8'), {
66
+ type: precinctTypes[extension],
67
+ throwOnParseError: true,
68
+ es6: { mixedImports: true },
69
+ ts: { mixedImports: true },
70
+ tsx: { mixedImports: true }
71
+ });
72
+ }
73
+ catch {
74
+ return undefined;
75
+ }
76
+ // precinct only leaves an ast for content it sniffed; filing-cabinet otherwise parses again
77
+ const ast = precinctTypes[extension] === undefined ? precinct.ast ?? undefined : undefined;
78
+ const found = new Set();
79
+ for (const specifier of specifiers) {
80
+ if (isBuiltin(specifier))
81
+ continue;
82
+ const resolved = resolveImport(specifier, filename, ast);
83
+ if (resolved === null || slash(resolved).includes('/node_modules/'))
84
+ continue;
85
+ found.add(toKey(resolved));
86
+ }
87
+ return found;
88
+ }
89
+ // resolution depends only on the importer's directory and kind, so its neighbours share it
90
+ function resolveImport(specifier, filename, ast) {
91
+ const cacheKey = `${dirname(filename)}\0${extname(filename)}\0${specifier}`;
92
+ let result = resolveCache.get(cacheKey);
93
+ if (result === undefined) {
94
+ result = resolveUncached(specifier, filename, ast);
95
+ resolveCache.set(cacheKey, result);
96
+ }
97
+ return result;
98
+ }
99
+ function resolveUncached(specifier, filename, ast) {
100
+ let resolved = '';
101
+ try {
102
+ resolved = cabinet({ partial: specifier, filename, directory, ...(ast === undefined ? {} : { ast }) });
103
+ }
104
+ catch {
105
+ // left unresolved
106
+ }
107
+ if (resolved !== '')
108
+ return realAbsolute(resolved);
109
+ // filing-cabinet resolves a missing file to '' - keep the edge so its creation is seen
110
+ if (specifier.startsWith('.') || isAbsolute(specifier)) {
111
+ return realAbsolute(resolve(dirname(filename), specifier));
112
+ }
113
+ return null;
114
+ }
115
+ function setImports(key, next) {
116
+ const previous = imports.get(key) ?? new Set();
117
+ for (const dep of previous.difference(next))
118
+ importers.get(dep)?.delete(key);
119
+ for (const dep of next.difference(previous)) {
120
+ let set = importers.get(dep);
121
+ if (set === undefined)
122
+ importers.set(dep, set = new Set());
123
+ set.add(key);
124
+ }
125
+ imports.set(key, next);
126
+ }
127
+ // re-reads `start`, then walks only into files never read before
128
+ function walk(start) {
129
+ const queue = [start];
130
+ const seen = new Set();
131
+ for (let key = queue.pop(); key !== undefined; key = queue.pop()) {
132
+ if (seen.has(key))
133
+ continue;
134
+ seen.add(key);
135
+ const found = readImports(key);
136
+ if (found === undefined) {
137
+ if (!imports.has(key))
138
+ imports.set(key, new Set());
139
+ continue;
140
+ }
141
+ setImports(key, found);
142
+ for (const dep of found)
143
+ if (!imports.has(dep))
144
+ queue.push(dep);
145
+ }
146
+ }
147
+ timeProfile('build dependency graph', () => {
148
+ for (const file of testFiles) {
149
+ const key = realKey(file);
150
+ tests.add(key);
151
+ if (!imports.has(key))
152
+ walk(key);
153
+ }
154
+ });
155
+ return {
156
+ addTest(file) {
157
+ clearCaches();
158
+ const key = realKey(file);
159
+ tests.add(key);
160
+ walk(key);
161
+ },
162
+ update(file) {
163
+ clearCaches();
164
+ walk(realKey(file));
165
+ },
166
+ remove(file) {
167
+ clearCaches();
168
+ const key = realKey(file);
169
+ tests.delete(key);
170
+ setImports(key, new Set());
171
+ },
172
+ testsAffectedBy(file) {
173
+ clearCaches();
174
+ const start = realKey(file);
175
+ const affected = new Set();
176
+ const queue = [start];
177
+ const seen = new Set();
178
+ for (let key = queue.pop(); key !== undefined; key = queue.pop()) {
179
+ if (seen.has(key))
180
+ continue;
181
+ seen.add(key);
182
+ if (tests.has(key))
183
+ affected.add(key);
184
+ for (const importer of importers.get(key) ?? [])
185
+ queue.push(importer);
186
+ }
187
+ return affected;
188
+ }
189
+ };
190
+ }
191
+ //# sourceMappingURL=makeDependencyGraph.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"makeDependencyGraph.js","sourceRoot":"","sources":["../../../src/cli/lib/makeDependencyGraph.ts"],"names":[],"mappings":"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","sourcesContent":["\nimport { existsSync, readFileSync, realpathSync } from 'node:fs'\nimport { isBuiltin } from 'node:module'\nimport { basename, dirname, extname, isAbsolute, join, relative, resolve } from 'node:path'\nimport cabinet from 'filing-cabinet'\nimport precinct from 'sxy-precinct'\n\nimport type { FinalConfig } from '../../types.js'\nimport slash from '../../packages/slash.js'\nimport { createTimeProfiler } from '../../packages/timeProfiler.js'\nimport { showTimeProfiling } from '../../config.js'\n\n\nconst { timeProfile } = createTimeProfiler(showTimeProfiling)\n\nexport interface DependencyGraph {\n addTest(file: string): void\n update(file: string): void\n remove(file: string): void\n testsAffectedBy(file: string): Set<string>\n}\n\n// precinct type per extension; undefined sniffs ESM vs CommonJS from the source\nconst precinctTypes: Record<string, string | undefined> = {\n '.js': undefined, '.jsx': undefined,\n '.mjs': 'es6', '.cjs': 'commonjs',\n '.ts': 'ts', '.mts': 'ts', '.cts': 'ts', '.tsx': 'tsx'\n}\n\nfor (const [extension, from] of [['.mjs', '.js'], ['.cjs', '.js'], ['.mts', '.ts'], ['.cts', '.ts']] as const) {\n const lookup = cabinet.getLookup(from)\n if (cabinet.getLookup(extension) === undefined && lookup !== undefined) cabinet.register(extension, lookup)\n}\n\nfunction tryRealpath(path: string): string | undefined {\n try {\n return realpathSync.native(path)\n } catch {\n return undefined\n }\n}\n\n// paths in and out are relative to the cwd with forward slashes; edges are kept between real paths\nexport function makeDependencyGraph(config: FinalConfig, testFiles: Iterable<string>): DependencyGraph {\n const cwd = realpathSync.native(process.cwd())\n const directory = resolve(config.watch.watchFilesBase === '' ? '.' : config.watch.watchFilesBase)\n\n const imports = new Map<string, Set<string>>()\n const importers = new Map<string, Set<string>>()\n const tests = new Set<string>()\n\n const toKey = (absolute: string): string => slash(relative(cwd, absolute))\n const toAbsolute = (key: string): string => resolve(cwd, key)\n\n // cleared by each operation, so a path created, deleted or relinked since is never served stale\n const realCache = new Map<string, string>()\n const resolveCache = new Map<string, string | null>()\n const clearCaches = (): void => {\n realCache.clear()\n resolveCache.clear()\n }\n function realAbsolute(file: string): string {\n const absolute = resolve(file)\n let real = realCache.get(absolute)\n if (real === undefined) {\n // a missing file's directory may still be reached through a link\n real = tryRealpath(absolute)\n ?? join(tryRealpath(dirname(absolute)) ?? dirname(absolute), basename(absolute))\n realCache.set(absolute, real)\n }\n return real\n }\n const realKey = (file: string): string => toKey(realAbsolute(file))\n\n // undefined when the file does not parse, so its last known imports stand\n function readImports(key: string): Set<string> | undefined {\n const filename = toAbsolute(key)\n const extension = extname(filename)\n if (!existsSync(filename) || !(extension in precinctTypes)) return new Set()\n\n let specifiers: string[]\n try {\n specifiers = precinct(readFileSync(filename, 'utf8'), {\n type: precinctTypes[extension],\n throwOnParseError: true,\n es6: { mixedImports: true },\n ts: { mixedImports: true },\n tsx: { mixedImports: true }\n })\n } catch {\n return undefined\n }\n // precinct only leaves an ast for content it sniffed; filing-cabinet otherwise parses again\n const ast = precinctTypes[extension] === undefined ? precinct.ast ?? undefined : undefined\n\n const found = new Set<string>()\n for (const specifier of specifiers) {\n if (isBuiltin(specifier)) continue\n const resolved = resolveImport(specifier, filename, ast)\n if (resolved === null || slash(resolved).includes('/node_modules/')) continue\n found.add(toKey(resolved))\n }\n return found\n }\n\n // resolution depends only on the importer's directory and kind, so its neighbours share it\n function resolveImport(specifier: string, filename: string, ast: object | undefined): string | null {\n const cacheKey = `${dirname(filename)}\\0${extname(filename)}\\0${specifier}`\n let result = resolveCache.get(cacheKey)\n if (result === undefined) {\n result = resolveUncached(specifier, filename, ast)\n resolveCache.set(cacheKey, result)\n }\n return result\n }\n\n function resolveUncached(specifier: string, filename: string, ast: object | undefined): string | null {\n let resolved = ''\n try {\n resolved = cabinet({ partial: specifier, filename, directory, ...(ast === undefined ? {} : { ast }) })\n } catch {\n // left unresolved\n }\n if (resolved !== '') return realAbsolute(resolved)\n // filing-cabinet resolves a missing file to '' - keep the edge so its creation is seen\n if (specifier.startsWith('.') || isAbsolute(specifier)) {\n return realAbsolute(resolve(dirname(filename), specifier))\n }\n return null\n }\n\n function setImports(key: string, next: Set<string>): void {\n const previous = imports.get(key) ?? new Set<string>()\n for (const dep of previous.difference(next)) importers.get(dep)?.delete(key)\n for (const dep of next.difference(previous)) {\n let set = importers.get(dep)\n if (set === undefined) importers.set(dep, set = new Set())\n set.add(key)\n }\n imports.set(key, next)\n }\n\n // re-reads `start`, then walks only into files never read before\n function walk(start: string): void {\n const queue = [start]\n const seen = new Set<string>()\n for (let key = queue.pop(); key !== undefined; key = queue.pop()) {\n if (seen.has(key)) continue\n seen.add(key)\n const found = readImports(key)\n if (found === undefined) {\n if (!imports.has(key)) imports.set(key, new Set())\n continue\n }\n setImports(key, found)\n for (const dep of found) if (!imports.has(dep)) queue.push(dep)\n }\n }\n\n timeProfile('build dependency graph', () => {\n for (const file of testFiles) {\n const key = realKey(file)\n tests.add(key)\n if (!imports.has(key)) walk(key)\n }\n })\n\n return {\n addTest(file) {\n clearCaches()\n const key = realKey(file)\n tests.add(key)\n walk(key)\n },\n update(file) {\n clearCaches()\n walk(realKey(file))\n },\n remove(file) {\n clearCaches()\n const key = realKey(file)\n tests.delete(key)\n setImports(key, new Set())\n },\n testsAffectedBy(file) {\n clearCaches()\n const start = realKey(file)\n const affected = new Set<string>()\n const queue = [start]\n const seen = new Set<string>()\n for (let key = queue.pop(); key !== undefined; key = queue.pop()) {\n if (seen.has(key)) continue\n seen.add(key)\n if (tests.has(key)) affected.add(key)\n for (const importer of importers.get(key) ?? []) queue.push(importer)\n }\n return affected\n }\n }\n}\n"]}
@@ -1,2 +1,3 @@
1
1
  export declare function setEnv(): void;
2
+ export declare function setAgentEnv(agent: boolean): void;
2
3
  //# sourceMappingURL=setEnv.d.ts.map
@@ -4,4 +4,15 @@ export function setEnv() {
4
4
  process.env.SXYT = 'true';
5
5
  process.env.SXY_TEST_RUNNER = 'true';
6
6
  }
7
+ // deleted when off, so a value inherited from a parent agent run is not taken as this run's
8
+ export function setAgentEnv(agent) {
9
+ if (agent) {
10
+ process.env.SXYT_AGENT = 'true';
11
+ process.env.SXY_TEST_RUNNER_AGENT = 'true';
12
+ }
13
+ else {
14
+ delete process.env.SXYT_AGENT;
15
+ delete process.env.SXY_TEST_RUNNER_AGENT;
16
+ }
17
+ }
7
18
  //# sourceMappingURL=setEnv.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"setEnv.js","sourceRoot":"","sources":["../../../src/cli/lib/setEnv.ts"],"names":[],"mappings":"AACA,uDAAuD;AACvD,MAAM,UAAU,MAAM;IAClB,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,CAAA;IAC/B,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,MAAM,CAAA;IACzB,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,MAAM,CAAA;AACxC,CAAC","sourcesContent":["\n// set some env vars when the test framework is running\nexport function setEnv() {\n process.env.NODE_ENV ??= 'test'\n process.env.SXYT = 'true'\n process.env.SXY_TEST_RUNNER = 'true'\n}\n"]}
1
+ {"version":3,"file":"setEnv.js","sourceRoot":"","sources":["../../../src/cli/lib/setEnv.ts"],"names":[],"mappings":"AACA,uDAAuD;AACvD,MAAM,UAAU,MAAM;IAClB,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,CAAA;IAC/B,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,MAAM,CAAA;IACzB,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,MAAM,CAAA;AACxC,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,WAAW,CAAC,KAAc;IACtC,IAAI,KAAK,EAAE,CAAC;QACR,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,MAAM,CAAA;QAC/B,OAAO,CAAC,GAAG,CAAC,qBAAqB,GAAG,MAAM,CAAA;IAC9C,CAAC;SAAM,CAAC;QACJ,OAAO,OAAO,CAAC,GAAG,CAAC,UAAU,CAAA;QAC7B,OAAO,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAA;IAC5C,CAAC;AACL,CAAC","sourcesContent":["\n// set some env vars when the test framework is running\nexport function setEnv() {\n process.env.NODE_ENV ??= 'test'\n process.env.SXYT = 'true'\n process.env.SXY_TEST_RUNNER = 'true'\n}\n\n// deleted when off, so a value inherited from a parent agent run is not taken as this run's\nexport function setAgentEnv(agent: boolean) {\n if (agent) {\n process.env.SXYT_AGENT = 'true'\n process.env.SXY_TEST_RUNNER_AGENT = 'true'\n } else {\n delete process.env.SXYT_AGENT\n delete process.env.SXY_TEST_RUNNER_AGENT\n }\n}\n"]}