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.
- package/AGENTS.md +14 -6
- package/dist/cli/init.js +2 -0
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/lib/buildDependencyTree.d.ts +1 -2
- package/dist/cli/lib/buildDependencyTree.js +25 -23
- package/dist/cli/lib/buildDependencyTree.js.map +1 -1
- package/dist/cli/lib/buildDependencyTrees.d.ts +1 -2
- package/dist/cli/lib/buildDependencyTrees.js +18 -16
- package/dist/cli/lib/buildDependencyTrees.js.map +1 -1
- package/dist/cli/lib/defaultConfig.js +1 -0
- package/dist/cli/lib/defaultConfig.js.map +1 -1
- package/dist/cli/lib/failureEntries.d.ts +1 -1
- package/dist/cli/lib/failureEntries.js +8 -8
- package/dist/cli/lib/failureEntries.js.map +1 -1
- package/dist/cli/lib/makeDependencyGraph.d.ts +9 -0
- package/dist/cli/lib/makeDependencyGraph.js +191 -0
- package/dist/cli/lib/makeDependencyGraph.js.map +1 -0
- package/dist/cli/lib/setEnv.d.ts +1 -0
- package/dist/cli/lib/setEnv.js +11 -0
- package/dist/cli/lib/setEnv.js.map +1 -1
- package/dist/cli/lib/showTestResultsSummary.js +1 -1
- package/dist/cli/lib/showTestResultsSummary.js.map +1 -1
- package/dist/cli/lib/validateConfig.js +4 -0
- package/dist/cli/lib/validateConfig.js.map +1 -1
- package/dist/cli/lib/watchFilesAndRunTestsNodeWatch.d.ts +3 -2
- package/dist/cli/lib/watchFilesAndRunTestsNodeWatch.js +21 -44
- package/dist/cli/lib/watchFilesAndRunTestsNodeWatch.js.map +1 -1
- package/dist/cli/once.js +2 -1
- package/dist/cli/once.js.map +1 -1
- package/dist/cli/watch.js +13 -12
- package/dist/cli/watch.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.js.map +1 -1
- package/package.json +7 -5
- 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
|
-
-
|
|
339
|
-
|
|
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
|
|
473
|
-
that was neither changed nor already failing is not
|
|
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
|
|
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
|
package/dist/cli/init.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAC3B,OAAO,EAAE,MAAM,IAAI,CAAA;AAEnB,OAAO,KAAK,MAAM,sBAAsB,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AACrF,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAG9C,MAAM,UAAU,IAAI;IAChB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,CAAA;IAEhC,KAAK,MAAM,cAAc,IAAI,eAAe,EAAE,CAAC;QAC3C,KAAK,MAAM,cAAc,IAAI,eAAe,EAAE,CAAC;YAC3C,IAAI,GAAG,CAAA;YACP,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,cAAc,GAAG,KAAK,CAAC,CAAA;YAC9D,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,mBAAmB,CAAC,GAAG,CAAC,CAAA;YAC5B,CAAC;YACD,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,cAAc,GAAG,MAAM,CAAC,CAAA;YAC/D,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,mBAAmB,CAAC,GAAG,CAAC,CAAA;YAC5B,CAAC;YACD,GAAG,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,EAAE,cAAc,GAAG,MAAM,CAAC,CAAA;YAC/D,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrB,mBAAmB,CAAC,GAAG,CAAC,CAAA;YAC5B,CAAC;QACL,CAAC;IACL,CAAC;IAED,SAAS,mBAAmB,CAAC,GAAW;QACpC,aAAa,CAAC,IAAI,WAAW,yCAAyC,GAAG,EAAE;cACrE,kEAAkE,OAAO,SAAS,CAAC,CAAA;IAC7F,CAAC;IAED,MAAM,0BAA0B,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAA;IACnE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,0BAA0B,CAAC,EAAE,CAAC;QAC7C,aAAa,CAAC,IAAI,WAAW,2CAA2C;cAClE,gEAAgE;cAChE,iFAAiF,CAAC,CAAA;IAC5F,CAAC;IAED,IAAI,WAA8B,CAAA;IAClC,IAAI,CAAC;QACD,mEAAmE;QACnE,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,0BAA0B,EAAE,MAAM,CAAC,CAAC,CAAA;IACjF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,aAAa,CAAC,IAAI,WAAW,wCAAwC,0BAA0B,EAAE;cAC3F,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;cAC7D,iFAAiF,CAAC,CAAA;IAC5F,CAAC;IAED,MAAM,gBAAgB,GAAG,CAAC,WAAW,CAAC,IAAI,KAAK,QAAQ,CAAC;QACpD,aAAa;QACb,CAAC,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;QACxE,SAAS;QACT,CAAC,CAAC,IAAI,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAA;IAE7E,MAAM,aAAa,GACf
|
|
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,24 +1,26 @@
|
|
|
1
|
-
import dependencyTree from 'dependency-tree'
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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,
|
|
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,17 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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":"
|
|
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"]}
|
|
@@ -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
|
|
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
|
|
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
|
|
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":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAgBnC,MAAM,UAAU,cAAc,CAC1B,WAAyB,EACzB,gBAA0C,EAAE;
|
|
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"]}
|
package/dist/cli/lib/setEnv.d.ts
CHANGED
package/dist/cli/lib/setEnv.js
CHANGED
|
@@ -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"]}
|