sxy-test-runner 2.5.3 → 2.5.5
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 +47 -21
- package/dist/cli/lib/agentReport.d.ts +1 -0
- package/dist/cli/lib/agentReport.js +3 -0
- package/dist/cli/lib/agentReport.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/esmGraphReload.d.ts +5 -0
- package/dist/cli/lib/esmGraphReload.js +24 -0
- package/dist/cli/lib/esmGraphReload.js.map +1 -0
- package/dist/cli/lib/esmTreeReload.d.ts +4 -0
- package/dist/cli/lib/esmTreeReload.js +62 -0
- package/dist/cli/lib/esmTreeReload.js.map +1 -0
- package/dist/cli/lib/handleUserInput.d.ts +2 -2
- package/dist/cli/lib/handleUserInput.js +13 -6
- package/dist/cli/lib/handleUserInput.js.map +1 -1
- package/dist/cli/lib/listenToUser.d.ts +2 -2
- package/dist/cli/lib/listenToUser.js.map +1 -1
- package/dist/cli/lib/loadTestFile.d.ts +0 -1
- package/dist/cli/lib/loadTestFile.js +23 -8
- package/dist/cli/lib/loadTestFile.js.map +1 -1
- package/dist/cli/lib/makeDependencyGraph.d.ts +10 -0
- package/dist/cli/lib/makeDependencyGraph.js +210 -0
- package/dist/cli/lib/makeDependencyGraph.js.map +1 -0
- package/dist/cli/lib/reRunManager.js +4 -6
- package/dist/cli/lib/reRunManager.js.map +1 -1
- package/dist/cli/lib/runTests.js +7 -2
- package/dist/cli/lib/runTests.js.map +1 -1
- package/dist/cli/lib/showTestResultsSummary.d.ts +1 -1
- package/dist/cli/lib/showTestResultsSummary.js +5 -1
- package/dist/cli/lib/showTestResultsSummary.js.map +1 -1
- package/dist/cli/lib/statusFile.js +20 -1
- package/dist/cli/lib/statusFile.js.map +1 -1
- package/dist/cli/lib/watchFilesAndRunTestsNodeWatch.d.ts +3 -2
- package/dist/cli/lib/watchFilesAndRunTestsNodeWatch.js +24 -48
- package/dist/cli/lib/watchFilesAndRunTestsNodeWatch.js.map +1 -1
- package/dist/cli/watch.js +25 -12
- package/dist/cli/watch.js.map +1 -1
- package/dist/describe.js +5 -3
- package/dist/describe.js.map +1 -1
- package/dist/meta.js +2 -1
- package/dist/meta.js.map +1 -1
- package/dist/registration.d.ts +4 -0
- package/dist/registration.js +34 -0
- package/dist/registration.js.map +1 -0
- package/dist/types.d.ts +8 -0
- package/dist/types.js.map +1 -1
- package/package.json +11 -6
- package/readme.md +16 -123
- package/dist/packages/esmReload.d.ts +0 -11
- package/dist/packages/esmReload.js +0 -43
- package/dist/packages/esmReload.js.map +0 -1
package/dist/describe.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe.js","sourceRoot":"","sources":["../src/describe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,qBAAqB,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"describe.js","sourceRoot":"","sources":["../src/describe.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,qBAAqB,CAAA;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAOnD,MAAM,UAAU,QAAQ,CACpB,QAAgB,EAAE,WAA4C,EAAE,aAAgC;IAEhG,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACX,oFAAoF;cAClF,0DAA0D;cAC1D,+CAA+C,CACpD,CAAA;QACD,gFAAgF;IACpF,CAAC;IAED,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAA;IAChE,MAAM,mBAAmB,GAAG,YAAY,CAAC,eAAe,CAAA;IACxD,YAAY,CAAC,eAAe,EAAE,CAAA;IAE9B,IAAI,IAAI,GAAG,SAAS,CAAA;IACpB,IAAI,KAAK,CAAA;IACT,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,2CAA2C,QAAQ,IAAI,CAAC,CAAA;IAC5E,CAAC;SAAM,IAAI,OAAO,WAAW,KAAK,UAAU,EAAE,CAAC;QAC3C,KAAK,GAAG,WAAW,CAAA;IACvB,CAAC;SAAM,CAAC;QACJ,SAAS,CAAC,aAAa,QAAQ,IAAI,EAAE,WAAW,EAAE,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC,CAAA;QACvF,IAAI,GAAG,WAAW,CAAA;QAClB,KAAK,GAAG,aAAiC,CAAA,CAAC,kEAAkE;IAChH,CAAC;IACD,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC;QACxB,OAAO,EAAE,mBAAmB;QAC5B,KAAK,EAAE,QAAQ;QACf,IAAI;QACJ,KAAK;KACR,CAAC,CAAA;IAEF,KAAK,CACD,KAAK,CAAC,SAAS,CAAA,GAAG,mBAAmB,GAAG;UAClC,KAAK,CAAC,SAAS,CAAA,aAAa,GAAG,KAAK,CAAC,UAAU,CAAA,GAAG,QAAQ,IAAI,CACvE,CAAA;AACL,CAAC","sourcesContent":["\nimport type { DescribeCallback } from './types.js'\nimport chalk from './packages/chalk.js'\nimport { checkMeta } from './checkMeta.js'\nimport { debug } from './output.js'\nimport { registrationFor } from './registration.js'\n\n\nexport type DescribeMeta = { parallelTests: boolean } | { sequentialTests: boolean }\n\nexport function describe(theThing: string, tests: DescribeCallback): void\nexport function describe(theThing: string, meta: DescribeMeta, tests: DescribeCallback): void\nexport function describe(\n theThing: string, testsOrMeta: DescribeCallback | DescribeMeta, possiblyTests?: DescribeCallback\n): void {\n if (!global.__sxy_test_runner__) {\n throw new Error(\n 'sxy-test-runner describe() was called, but the test framework has not initialized.'\n + ' This might be because you have run a test file directly'\n + ', instead of running \\'pnpm sxy-test-runner\\''\n )\n // todo - maybe we can initialise the framework and just run only this test file\n }\n\n const registration = registrationFor(global.__sxy_test_runner__)\n const thisDescribeCounter = registration.describeCounter\n registration.describeCounter++\n\n let meta = undefined\n let tests\n if (testsOrMeta === undefined) {\n throw new Error(`callback argument missing for describe('${theThing}')`)\n } else if (typeof testsOrMeta === 'function') {\n tests = testsOrMeta\n } else {\n checkMeta(`describe('${theThing}')`, testsOrMeta, ['parallelTests', 'sequentialTests'])\n meta = testsOrMeta\n tests = possiblyTests as DescribeCallback // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion\n }\n registration.describes.push({\n counter: thisDescribeCounter,\n thing: theThing,\n meta,\n tests\n })\n\n debug(\n chalk.lightgrey`${thisDescribeCounter})`\n + chalk.lightgrey` Done with ` + chalk.brightblue`${theThing}\\n`\n )\n}\n"]}
|
package/dist/meta.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { checkMeta } from './checkMeta.js';
|
|
2
|
+
import { registrationFor } from './registration.js';
|
|
2
3
|
export function meta(theMeta) {
|
|
3
4
|
if (!global.__sxy_test_runner__) {
|
|
4
5
|
throw new Error('sxy-test-runner meta() was called, but the test framework has not initialized.'
|
|
@@ -6,6 +7,6 @@ export function meta(theMeta) {
|
|
|
6
7
|
+ ', instead of running \'pnpm sxy-test-runner\'');
|
|
7
8
|
}
|
|
8
9
|
checkMeta('meta()', theMeta, ['parallelDescribes', 'sequentialDescribes']);
|
|
9
|
-
global.__sxy_test_runner__.fileMeta = theMeta;
|
|
10
|
+
registrationFor(global.__sxy_test_runner__).fileMeta = theMeta;
|
|
10
11
|
}
|
|
11
12
|
//# sourceMappingURL=meta.js.map
|
package/dist/meta.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meta.js","sourceRoot":"","sources":["../src/meta.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"meta.js","sourceRoot":"","sources":["../src/meta.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAOnD,MAAM,UAAU,IAAI,CAAC,OAAiB;IAClC,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACX,gFAAgF;cAC9E,0DAA0D;cAC1D,+CAA+C,CACpD,CAAA;IACL,CAAC;IAED,SAAS,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,CAAC,CAAA;IAE1E,eAAe,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,QAAQ,GAAG,OAAO,CAAA;AAClE,CAAC","sourcesContent":["\nimport { checkMeta } from './checkMeta.js'\nimport { registrationFor } from './registration.js'\n\n\nexport type FileMeta\n = { parallelDescribes: boolean }\n | { sequentialDescribes: boolean }\n\nexport function meta(theMeta: FileMeta): void {\n if (!global.__sxy_test_runner__) {\n throw new Error(\n 'sxy-test-runner meta() was called, but the test framework has not initialized.'\n + ' This might be because you have run a test file directly'\n + ', instead of running \\'pnpm sxy-test-runner\\''\n )\n }\n\n checkMeta('meta()', theMeta, ['parallelDescribes', 'sequentialDescribes'])\n\n registrationFor(global.__sxy_test_runner__).fileMeta = theMeta\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { FileRegistration, SxyTestRunnerGlobal } from './types.js';
|
|
2
|
+
export declare function withoutQuery(url: string): string;
|
|
3
|
+
export declare function registrationFor(registry: SxyTestRunnerGlobal): SxyTestRunnerGlobal | FileRegistration;
|
|
4
|
+
//# sourceMappingURL=registration.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { getCallSites } from 'node:util';
|
|
2
|
+
const maxCallSites = 200;
|
|
3
|
+
export function withoutQuery(url) {
|
|
4
|
+
const parsed = new URL(url);
|
|
5
|
+
parsed.search = '';
|
|
6
|
+
parsed.hash = '';
|
|
7
|
+
return parsed.href;
|
|
8
|
+
}
|
|
9
|
+
// the URL of the module whose top level this call comes from - each module evaluates on a stack
|
|
10
|
+
// of its own, so a module imported by the test file never has the test file beneath it
|
|
11
|
+
function topLevelUrl(fallback) {
|
|
12
|
+
// the module URLs as loaded - with --enable-source-maps they would otherwise name the source
|
|
13
|
+
const sites = getCallSites(maxCallSites, { sourceMap: false });
|
|
14
|
+
// a stack this deep may have lost its outermost frames
|
|
15
|
+
if (sites.length >= maxCallSites)
|
|
16
|
+
return fallback;
|
|
17
|
+
return sites.findLast(site => site.scriptName.startsWith('file:'))?.scriptName ?? fallback;
|
|
18
|
+
}
|
|
19
|
+
// where a describe or meta call registers: the registry itself outside a test file load, as
|
|
20
|
+
// when describe is called directly, otherwise the registration of the module that made it
|
|
21
|
+
export function registrationFor(registry) {
|
|
22
|
+
if (registry.files === undefined || registry.loadingUrl === undefined)
|
|
23
|
+
return registry;
|
|
24
|
+
const url = topLevelUrl(registry.loadingUrl);
|
|
25
|
+
const key = withoutQuery(url);
|
|
26
|
+
let registration = registry.files.get(key);
|
|
27
|
+
// a newer evaluation of the same file replaces what an older one registered
|
|
28
|
+
if (registration?.url !== url) {
|
|
29
|
+
registration = { url, describes: [], describeCounter: 1 };
|
|
30
|
+
registry.files.set(key, registration);
|
|
31
|
+
}
|
|
32
|
+
return registration;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=registration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registration.js","sourceRoot":"","sources":["../src/registration.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAKxC,MAAM,YAAY,GAAG,GAAG,CAAA;AAExB,MAAM,UAAU,YAAY,CAAC,GAAW;IACpC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;IAC3B,MAAM,CAAC,MAAM,GAAG,EAAE,CAAA;IAClB,MAAM,CAAC,IAAI,GAAG,EAAE,CAAA;IAChB,OAAO,MAAM,CAAC,IAAI,CAAA;AACtB,CAAC;AAED,gGAAgG;AAChG,uFAAuF;AACvF,SAAS,WAAW,CAAC,QAAgB;IACjC,6FAA6F;IAC7F,MAAM,KAAK,GAAG,YAAY,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAA;IAC9D,uDAAuD;IACvD,IAAI,KAAK,CAAC,MAAM,IAAI,YAAY;QAAE,OAAO,QAAQ,CAAA;IACjD,OAAO,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,EAAE,UAAU,IAAI,QAAQ,CAAA;AAC9F,CAAC;AAED,4FAA4F;AAC5F,0FAA0F;AAC1F,MAAM,UAAU,eAAe,CAAC,QAA6B;IACzD,IAAI,QAAQ,CAAC,KAAK,KAAK,SAAS,IAAI,QAAQ,CAAC,UAAU,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAA;IAEtF,MAAM,GAAG,GAAG,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;IAC5C,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,CAAA;IAC7B,IAAI,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;IAC1C,4EAA4E;IAC5E,IAAI,YAAY,EAAE,GAAG,KAAK,GAAG,EAAE,CAAC;QAC5B,YAAY,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,EAAE,eAAe,EAAE,CAAC,EAAE,CAAA;QACzD,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAA;IACzC,CAAC;IACD,OAAO,YAAY,CAAA;AACvB,CAAC","sourcesContent":["\nimport { getCallSites } from 'node:util'\n\nimport type { FileRegistration, SxyTestRunnerGlobal } from './types.js'\n\n\nconst maxCallSites = 200\n\nexport function withoutQuery(url: string): string {\n const parsed = new URL(url)\n parsed.search = ''\n parsed.hash = ''\n return parsed.href\n}\n\n// the URL of the module whose top level this call comes from - each module evaluates on a stack\n// of its own, so a module imported by the test file never has the test file beneath it\nfunction topLevelUrl(fallback: string): string {\n // the module URLs as loaded - with --enable-source-maps they would otherwise name the source\n const sites = getCallSites(maxCallSites, { sourceMap: false })\n // a stack this deep may have lost its outermost frames\n if (sites.length >= maxCallSites) return fallback\n return sites.findLast(site => site.scriptName.startsWith('file:'))?.scriptName ?? fallback\n}\n\n// where a describe or meta call registers: the registry itself outside a test file load, as\n// when describe is called directly, otherwise the registration of the module that made it\nexport function registrationFor(registry: SxyTestRunnerGlobal): SxyTestRunnerGlobal | FileRegistration {\n if (registry.files === undefined || registry.loadingUrl === undefined) return registry\n\n const url = topLevelUrl(registry.loadingUrl)\n const key = withoutQuery(url)\n let registration = registry.files.get(key)\n // a newer evaluation of the same file replaces what an older one registered\n if (registration?.url !== url) {\n registration = { url, describes: [], describeCounter: 1 }\n registry.files.set(key, registration)\n }\n return registration\n}\n"]}
|
package/dist/types.d.ts
CHANGED
|
@@ -79,10 +79,18 @@ export type It = {
|
|
|
79
79
|
should: string;
|
|
80
80
|
test: (parcel: TestParcel) => void | Promise<void>;
|
|
81
81
|
};
|
|
82
|
+
export interface FileRegistration {
|
|
83
|
+
url: string;
|
|
84
|
+
describes: Describe[];
|
|
85
|
+
describeCounter: number;
|
|
86
|
+
fileMeta?: FileMeta;
|
|
87
|
+
}
|
|
82
88
|
export interface SxyTestRunnerGlobal {
|
|
83
89
|
describes: Describe[];
|
|
84
90
|
describeCounter: number;
|
|
85
91
|
fileMeta?: FileMeta;
|
|
92
|
+
files?: Map<string, FileRegistration>;
|
|
93
|
+
loadingUrl?: string;
|
|
86
94
|
}
|
|
87
95
|
declare global {
|
|
88
96
|
var __sxy_test_runner__: SxyTestRunnerGlobal | undefined;
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,2EAA2E;AAC3E,2EAA2E;AAC3E,2EAA2E;AAC3E,+EAA+E","sourcesContent":["\n// Shared types for the test-run data model, user config, and CLI plumbing.\n// The config/result shapes are inferred from usage across the old awaitful\n// source (defaultConfig.js, validateConfig.js, init.js's template, and the\n// run*/parseDescribe/showTestResult modules) - there was no prior type source.\n\nimport type { FileMeta } from './meta.js'\nimport type { DescribeMeta } from './describe.js'\n\n\nexport type ExecutionMode = 'parallel' | 'sequential'\nexport type GlobPattern = string | string[]\n\nexport interface ExecutionConfig {\n files: ExecutionMode\n describes: ExecutionMode\n tests: ExecutionMode\n}\n\n// path to write the latest watch run status to, or false to write none\nexport type StatusFileConfig = string | false\n\nexport interface WatchConfig {\n watchFilesBase: string\n watchFiles: GlobPattern\n watchFilesIgnore: GlobPattern\n runAllOnStartup: boolean\n reRunFailingTests: boolean\n // map a changed file to the tests that import it. Off, any watched change runs everything\n dependencyEvaluation: boolean\n ttyActionsOnKeypress: boolean\n statusFile: StatusFileConfig\n}\n\nexport type TaskExports = Record<string, unknown>\nexport type ConfigTaskFn = (parcel?: TaskExports) => TaskExports | void | Promise<TaskExports | void>\n// a task may also be a file path (relative to the project root) to dynamically import\nexport type ConfigTask = ConfigTaskFn | string\nexport type ConfigTasks = ConfigTask | ConfigTask[]\n\nexport interface FinalConfig {\n testsBase: string\n tests: GlobPattern\n testsFilter?: string\n testsIgnore: GlobPattern\n setup?: ConfigTasks\n beforeEachFile?: ConfigTasks\n afterEachFile?: ConfigTasks\n beforeEachDescribe?: ConfigTasks\n afterEachDescribe?: ConfigTasks\n beforeEachTest?: ConfigTasks\n afterEachTest?: ConfigTasks\n teardown?: ConfigTasks\n showErrorTrace: boolean\n compact: boolean\n // repeat a failed run's failures above the tallies, where scrolling has not buried them\n restateFailures: boolean\n // report the run as single agent readable lines instead of the usual output\n agent: boolean\n // the most characters of error text on each agent fail line; Infinity for no limit\n agentLineLengthLimit: number\n // stop the run at the first failing test, erroring describe or unloadable test file.\n // Set by `once --fail-fast` only: it describes one invocation, not a project, and in\n // watch mode it would silently truncate runs that report themselves as covering\n // everything\n failFast: boolean\n execution: ExecutionConfig\n watch: WatchConfig\n}\n\nexport type UserConfig = Partial<\n Omit<FinalConfig, 'testsFilter' | 'failFast' | 'execution' | 'watch'>\n> & {\n execution?: Partial<ExecutionConfig>\n watch?: Partial<WatchConfig>\n}\n\nexport type UserConfigInternal = UserConfig\n & Partial<Pick<FinalConfig, 'testsFilter' | 'failFast'>>\n\n\n// -- test-run data model --------------------------------------------------\n\nexport type TestParcel = TaskExports\n\nexport interface DescribeParcel extends TaskExports {\n it: (should: string, test: (parcel: TestParcel) => void | Promise<void>) => void\n test: (should: string, test: (parcel: TestParcel) => void | Promise<void>) => void\n afterDescribe: (func: () => void | Promise<void>) => void\n beforeEachTest: (func: () => TaskExports | void | Promise<TaskExports | void>) => void\n afterEachTest: (func: () => void | Promise<void>) => void\n}\n\nexport type DescribeCallback = (parcel: DescribeParcel) => void | Promise<void>\n\n\n\nexport interface ParsedDescribe {\n its: It[]\n beforeDescribeExports: TaskExports\n afterDescribeFunc?: () => void | Promise<void>\n beforeEachTestFunc?: () => TaskExports | void | Promise<TaskExports | void>\n afterEachTestFunc?: () => void | Promise<void>\n error?: unknown\n}\n\n// export interface TestError extends Error {\n// actual?: unknown\n// expected?: unknown\n// }\n\n\n\n\n\n\n\n\n\n\nexport type DependencyTrees = Record<string, string[]>\n\nexport type OutFn = (...texts: unknown[]) => void\n\n\n\n// -- globals ----------------------------------------------------------------\n\nexport type Describe = {\n counter: number\n thing: string\n meta?: DescribeMeta\n tests: DescribeCallback\n}\n\nexport type It = {\n counter: number\n should: string\n test: (parcel: TestParcel) => void | Promise<void>\n}\n\nexport interface SxyTestRunnerGlobal {\n describes: Describe[]\n describeCounter: number\n fileMeta?: FileMeta\n}\ndeclare global {\n var __sxy_test_runner__: SxyTestRunnerGlobal | undefined\n var __sxyt_commandLineArguments: string[]\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,2EAA2E;AAC3E,2EAA2E;AAC3E,2EAA2E;AAC3E,+EAA+E","sourcesContent":["\n// Shared types for the test-run data model, user config, and CLI plumbing.\n// The config/result shapes are inferred from usage across the old awaitful\n// source (defaultConfig.js, validateConfig.js, init.js's template, and the\n// run*/parseDescribe/showTestResult modules) - there was no prior type source.\n\nimport type { FileMeta } from './meta.js'\nimport type { DescribeMeta } from './describe.js'\n\n\nexport type ExecutionMode = 'parallel' | 'sequential'\nexport type GlobPattern = string | string[]\n\nexport interface ExecutionConfig {\n files: ExecutionMode\n describes: ExecutionMode\n tests: ExecutionMode\n}\n\n// path to write the latest watch run status to, or false to write none\nexport type StatusFileConfig = string | false\n\nexport interface WatchConfig {\n watchFilesBase: string\n watchFiles: GlobPattern\n watchFilesIgnore: GlobPattern\n runAllOnStartup: boolean\n reRunFailingTests: boolean\n // map a changed file to the tests that import it. Off, any watched change runs everything\n dependencyEvaluation: boolean\n ttyActionsOnKeypress: boolean\n statusFile: StatusFileConfig\n}\n\nexport type TaskExports = Record<string, unknown>\nexport type ConfigTaskFn = (parcel?: TaskExports) => TaskExports | void | Promise<TaskExports | void>\n// a task may also be a file path (relative to the project root) to dynamically import\nexport type ConfigTask = ConfigTaskFn | string\nexport type ConfigTasks = ConfigTask | ConfigTask[]\n\nexport interface FinalConfig {\n testsBase: string\n tests: GlobPattern\n testsFilter?: string\n testsIgnore: GlobPattern\n setup?: ConfigTasks\n beforeEachFile?: ConfigTasks\n afterEachFile?: ConfigTasks\n beforeEachDescribe?: ConfigTasks\n afterEachDescribe?: ConfigTasks\n beforeEachTest?: ConfigTasks\n afterEachTest?: ConfigTasks\n teardown?: ConfigTasks\n showErrorTrace: boolean\n compact: boolean\n // repeat a failed run's failures above the tallies, where scrolling has not buried them\n restateFailures: boolean\n // report the run as single agent readable lines instead of the usual output\n agent: boolean\n // the most characters of error text on each agent fail line; Infinity for no limit\n agentLineLengthLimit: number\n // stop the run at the first failing test, erroring describe or unloadable test file.\n // Set by `once --fail-fast` only: it describes one invocation, not a project, and in\n // watch mode it would silently truncate runs that report themselves as covering\n // everything\n failFast: boolean\n execution: ExecutionConfig\n watch: WatchConfig\n}\n\nexport type UserConfig = Partial<\n Omit<FinalConfig, 'testsFilter' | 'failFast' | 'execution' | 'watch'>\n> & {\n execution?: Partial<ExecutionConfig>\n watch?: Partial<WatchConfig>\n}\n\nexport type UserConfigInternal = UserConfig\n & Partial<Pick<FinalConfig, 'testsFilter' | 'failFast'>>\n\n\n// -- test-run data model --------------------------------------------------\n\nexport type TestParcel = TaskExports\n\nexport interface DescribeParcel extends TaskExports {\n it: (should: string, test: (parcel: TestParcel) => void | Promise<void>) => void\n test: (should: string, test: (parcel: TestParcel) => void | Promise<void>) => void\n afterDescribe: (func: () => void | Promise<void>) => void\n beforeEachTest: (func: () => TaskExports | void | Promise<TaskExports | void>) => void\n afterEachTest: (func: () => void | Promise<void>) => void\n}\n\nexport type DescribeCallback = (parcel: DescribeParcel) => void | Promise<void>\n\n\n\nexport interface ParsedDescribe {\n its: It[]\n beforeDescribeExports: TaskExports\n afterDescribeFunc?: () => void | Promise<void>\n beforeEachTestFunc?: () => TaskExports | void | Promise<TaskExports | void>\n afterEachTestFunc?: () => void | Promise<void>\n error?: unknown\n}\n\n// export interface TestError extends Error {\n// actual?: unknown\n// expected?: unknown\n// }\n\n\n\n\n\n\n\n\n\n\nexport type DependencyTrees = Record<string, string[]>\n\nexport type OutFn = (...texts: unknown[]) => void\n\n\n\n// -- globals ----------------------------------------------------------------\n\nexport type Describe = {\n counter: number\n thing: string\n meta?: DescribeMeta\n tests: DescribeCallback\n}\n\nexport type It = {\n counter: number\n should: string\n test: (parcel: TestParcel) => void | Promise<void>\n}\n\n// what one evaluation of a module registered, from the exact URL it was evaluated as\nexport interface FileRegistration {\n url: string\n describes: Describe[]\n describeCounter: number\n fileMeta?: FileMeta\n}\n\nexport interface SxyTestRunnerGlobal {\n describes: Describe[]\n describeCounter: number\n fileMeta?: FileMeta\n // set while loading test files: registrations filed by the module whose top level made them,\n // so a test file imported by another is credited to itself, keyed by URL without its query\n files?: Map<string, FileRegistration>\n // the URL the test file is being imported as, for a call whose top level cannot be found\n loadingUrl?: string\n}\ndeclare global {\n var __sxy_test_runner__: SxyTestRunnerGlobal | undefined\n var __sxyt_commandLineArguments: string[]\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sxy-test-runner",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.5",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"homepage": "https://github.com/RobertSandiford/sxy-test-runner",
|
|
6
6
|
"repository": {
|
|
@@ -41,11 +41,12 @@
|
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"chalk": "^4.1.2",
|
|
43
43
|
"cross-env": "^7.0.3",
|
|
44
|
-
"dependency-tree": "^12.0.1",
|
|
45
44
|
"esm-reload": "^1.0.1",
|
|
45
|
+
"filing-cabinet": "^6.0.0",
|
|
46
46
|
"node-watch": "^0.7.4",
|
|
47
47
|
"rimraf": "^6.1.3",
|
|
48
|
-
"sxy-lib": "^3.0.7"
|
|
48
|
+
"sxy-lib": "^3.0.7",
|
|
49
|
+
"sxy-precinct": "^13.1.1"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
52
|
"@types/chai": "^5.2.3",
|
|
@@ -63,16 +64,20 @@
|
|
|
63
64
|
"eslint-config-tungsten-flat": "^0.3.4",
|
|
64
65
|
"eslint-plugin-imports-regulation": "0.2.4",
|
|
65
66
|
"eslint-plugin-start-end-lines": "^0.1.0",
|
|
67
|
+
"eslint-plugin-use-disposables": "^0.1.1",
|
|
66
68
|
"mocha": "^9.2.2",
|
|
67
69
|
"mocha-steps": "^1.3.0",
|
|
68
70
|
"nodemon": "^2.0.22",
|
|
69
71
|
"sinon": "^12.0.1",
|
|
70
72
|
"sinon-chai": "^3.7.0",
|
|
73
|
+
"sxy-assert": "^0.1.13",
|
|
71
74
|
"typescript": "^6.0.3"
|
|
72
75
|
},
|
|
73
76
|
"scripts": {
|
|
74
|
-
"build": "
|
|
75
|
-
"build-once": "
|
|
77
|
+
"build": "tscmon",
|
|
78
|
+
"build-once": "tscmon --once",
|
|
79
|
+
"time-dep-graph": "node bench/depGraph.ts",
|
|
80
|
+
"time-reload": "node bench/reload.ts",
|
|
76
81
|
"dev-mon": "cross-env NODE_OPTIONS=\"--experimental-specifier-resolution=node\" nodemon --experimental-specifier-resolution=node --watch dist/cli --watch dist/cli/lib --watch sxy-test-runner.config.js --watch sxy-loader.config.js dist/cli/index.js",
|
|
77
82
|
"dev-mon-input": "cross-env NODE_OPTIONS=\"--experimental-specifier-resolution=node\" nodemon --no-stdin --experimental-specifier-resolution=node --watch dist/cli --watch dist/cli/lib --watch sxy-test-runner.config.js --watch sxy-loader.config.js dist/cli/index.js",
|
|
78
83
|
"dev": "cross-env NODE_OPTIONS=\"--experimental-specifier-resolution=node\" node --experimental-specifier-resolution=node dist/cli/index.js",
|
|
@@ -114,7 +119,7 @@
|
|
|
114
119
|
"unit-test-add-exports": "mocha --watch --parallel --config unitTesting/.mocharc.cjs --watch-files dist,unitTesting dist/**/addExports.unitTest.js",
|
|
115
120
|
"unit-test-describe": "mocha --watch --parallel --config unitTesting/.mocharc.cjs --watch-files dist,unitTesting dist/**/describe.unitTest.js",
|
|
116
121
|
"unit-test-run-tests": "mocha --watch --parallel --config unitTesting/.mocharc.cjs --watch-files dist,unitTesting dist/**/runTests.unitTest.js",
|
|
117
|
-
"unit-test-esm-reload": "mocha --watch --parallel --config unitTesting/.mocharc.cjs --watch-files dist,unitTesting dist/**/
|
|
122
|
+
"unit-test-esm-reload": "mocha --watch --parallel --config unitTesting/.mocharc.cjs --watch-files dist,unitTesting dist/**/esmTreeReload.unitTest.js",
|
|
118
123
|
"unit-test-user-input": "mocha --watch --parallel --config unitTesting/.mocharc.cjs --watch-files dist,unitTesting dist/**/handleUserInput.unitTest.js dist/**/listenToUser.unitTest.js",
|
|
119
124
|
"mocha-trial": "mocha --watch --parallel --config unitTesting/.mocharc.cjs --watch-files dist,unitTesting dist/**/runTest.unitTest.js"
|
|
120
125
|
}
|
package/readme.md
CHANGED
|
@@ -74,18 +74,18 @@ This will create a config in your project folder. It can be moved to a folder na
|
|
|
74
74
|
}
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
The other config options
|
|
77
|
+
The other config options are fairly well explained in the config. More info on them later.
|
|
78
78
|
|
|
79
79
|
### 4. Run
|
|
80
80
|
|
|
81
81
|
```
|
|
82
82
|
npx sxy-test-runner
|
|
83
83
|
```
|
|
84
|
-
|
|
84
|
+
*`sxyt` and `sxy-test` are aliases for sxy-test-runner and can be used instead*
|
|
85
85
|
|
|
86
86
|
("npx" can be omitted in package.json scripts)
|
|
87
87
|
|
|
88
|
-
sxy-test-runner runs watch mode, re-running only relevant tests, by default.
|
|
88
|
+
`sxy-test-runner` runs watch mode, re-running only relevant tests, by default.
|
|
89
89
|
|
|
90
90
|
While watch mode is idle, enter `r` or `l` to re-run the last set of tests, `a` to run
|
|
91
91
|
all tests again, or `f` to re-run the currently failing tests.
|
|
@@ -115,70 +115,16 @@ npx sxy-test-runner once --filter "**/users/**"
|
|
|
115
115
|
`--test-files` and `--filter` can be combined: the former replaces the configured test
|
|
116
116
|
pattern, then the latter filters the discovered files.
|
|
117
117
|
|
|
118
|
-
`--filter-describe` and `--filter-it` filter
|
|
119
|
-
have loaded. sxy-test-runner gives each test file a fresh ESM instance and propagates it
|
|
120
|
-
through that file's static dependency graph, bypassing the ordinary ESM cache. Loading a
|
|
121
|
-
large set of files and filtering their describes or tests afterward can therefore be much
|
|
122
|
-
slower than filtering files first. Prefer `--filter` when a file glob can select the work
|
|
123
|
-
you need, and use the name filters for further narrowing within those files.
|
|
118
|
+
`--filter-describe` and `--filter-it` filter describes and tests by name.
|
|
124
119
|
|
|
125
|
-
To
|
|
126
|
-
`watch.statusFile` in the config. This is for anything watching the suite from outside - a
|
|
127
|
-
script, a notifier, an agent - that would otherwise have to read the terminal:
|
|
128
|
-
|
|
129
|
-
```
|
|
130
|
-
npx sxy-test-runner --status-file .sxyt-status
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
The first line is a status code, a `-`, and a short description. The second is the time the
|
|
134
|
-
run finished, then the summary, then any failures, and the run's duration last:
|
|
135
|
-
|
|
136
|
-
```
|
|
137
|
-
1 - 2 tests failed
|
|
138
|
-
2026-08-03 20:14:52 +01:00
|
|
139
|
-
Tests: 8/10
|
|
140
|
-
Items: 4/5
|
|
141
|
-
Test Files: 1/2
|
|
142
|
-
Failures:
|
|
143
|
-
f: tests/users.test.js
|
|
144
|
-
d: creating a user
|
|
145
|
-
t: rejects a duplicate email
|
|
146
|
-
e: AssertionError: expected 409 to equal 200
|
|
147
|
-
t: sends a welcome email
|
|
148
|
-
e: Error: connect ECONNREFUSED 127.0.0.1:2525
|
|
149
|
-
1,204ms
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
Failures nest as `f:` file, `d:` describe, `t:` test, `e:` error - one line each. Diffs, logs
|
|
153
|
-
and stack traces stay in the terminal.
|
|
154
|
-
|
|
155
|
-
| code | meaning |
|
|
156
|
-
| --- | --- |
|
|
157
|
-
| -1 | watch mode has started, but no run has finished |
|
|
158
|
-
| 0 | everything passed |
|
|
159
|
-
| 1 | tests ran and failed |
|
|
160
|
-
| 2 | the run died partway - a test file would not load, or a describe threw |
|
|
161
|
-
| 3 | nothing ran - no test files were found, or the filters matched nothing |
|
|
162
|
-
|
|
163
|
-
Watch mode writes the `-1` status on startup, so a status left over from a previous session
|
|
164
|
-
is never read as the current one. Each write goes to a temporary file and is renamed into
|
|
165
|
-
place, so a poller only ever sees a complete status.
|
|
166
|
-
|
|
167
|
-
Name the file `.sxys` and the VS Code extension in
|
|
168
|
-
[`vscode-sxyt-status/`](vscode-sxyt-status) will colour it - green when the run passed, red
|
|
169
|
-
where it did not.
|
|
170
|
-
|
|
171
|
-
To specific an alternate config, use -c or --config
|
|
120
|
+
To specify an alternate config, use -c or --config
|
|
172
121
|
```
|
|
173
|
-
npx sxy-test-runner --config
|
|
122
|
+
npx sxy-test-runner --config sxyt.alternate.config.js
|
|
174
123
|
```
|
|
175
124
|
|
|
176
125
|
## Reporting for an agent
|
|
177
126
|
|
|
178
|
-
`--agent` replaces the usual output with
|
|
179
|
-
process's stdout rather than a terminal. It works in both modes. In `once` it leaves only
|
|
180
|
-
the failures and the summary - passing tests are not listed - and the exit code is
|
|
181
|
-
unchanged, so the run stays usable in CI:
|
|
127
|
+
`--agent` replaces the usual (verbose) output with lines for key events - e.g. startup, failures, and completion.
|
|
182
128
|
|
|
183
129
|
```
|
|
184
130
|
npx sxy-test-runner once --agent
|
|
@@ -192,77 +138,28 @@ npx sxy-test-runner once --agent
|
|
|
192
138
|
[sxyt] done | n=1 | status=failed | desc=1 test failed | tests=2/3 | items=1/2 | files=1/2 | failing=1 | ms=150
|
|
193
139
|
```
|
|
194
140
|
|
|
195
|
-
Every line is one complete event: `[sxyt] <event>` followed by ` | <key>=<value>` pairs.
|
|
196
|
-
Newlines in free text are collapsed and any `|` becomes `/`, so one event never spans two
|
|
197
|
-
lines. In watch mode the stream continues with `change`, further `run`/`done` pairs, and a
|
|
198
|
-
`watching` line once the watcher is live.
|
|
199
|
-
|
|
200
|
-
`failing` counts the test files still known to be failing across the whole session, and
|
|
201
|
-
`covers` says what the run was built from, so a partial re-run cannot be mistaken for a
|
|
202
|
-
clean suite. `status` is one of `passed`, `failed`, `aborted` (a test file would not load, or
|
|
203
|
-
a describe threw) or `could-not-run` - the same states as the status file table above, named
|
|
204
|
-
rather than numbered. It is not the process exit code: read that from the process.
|
|
205
|
-
|
|
206
|
-
Each failure's error message is cut at 500 characters. Set `agentLineLengthLimit` in the
|
|
207
|
-
config to change that, or to `Infinity` to keep messages whole.
|
|
208
|
-
|
|
209
141
|
Agent mode also sets the environment variables `SXYT_AGENT` and `SXY_TEST_RUNNER_AGENT` to
|
|
210
142
|
`true`, so setup code and tests can tell they are running under it.
|
|
211
143
|
|
|
212
|
-
## Stopping at the first failure
|
|
213
|
-
|
|
214
|
-
`-x` or `--fail-fast` stops a `once` run at the first failing test, erroring describe, or
|
|
215
|
-
test file that will not load. The failure is reported in full and nothing further starts;
|
|
216
|
-
`afterEach*` hooks and `teardown` still run:
|
|
217
|
-
|
|
218
|
-
```
|
|
219
|
-
npx sxy-test-runner once --fail-fast
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
The tallies then count only what ran, so the summary ends with `stopped at the first
|
|
223
|
-
failure (failFast)`. There is no watch equivalent, and no config option - it describes a
|
|
224
|
-
single run rather than a project.
|
|
225
|
-
|
|
226
|
-
## Unrecognised options
|
|
227
|
-
|
|
228
|
-
`--help` (`-h`) and `--version` (`-v`) work on any command and take over from it, so
|
|
229
|
-
`once --help` describes `once` rather than running the suite.
|
|
230
|
-
|
|
231
|
-
An option that the command does not accept is an error: the command exits without running
|
|
232
|
-
anything, rather than warning and carrying on. A typo like `--fitler-it` would otherwise
|
|
233
|
-
quietly run the whole suite and report a pass. Options are per-command, so `--fail-fast`
|
|
234
|
-
belongs to `once` and `--status-file` to `watch`. Run `npx sxy-test-runner help`, or add
|
|
235
|
-
`--help` to any command, for the current list.
|
|
236
|
-
|
|
237
144
|
# How it works
|
|
238
145
|
|
|
239
|
-
sxy-test-runner runs all tests inside one node process
|
|
240
|
-
|
|
241
|
-
Because of this, be careful with globals, which will be shared.
|
|
146
|
+
sxy-test-runner runs all tests inside one node process. Be careful with globals, which will be shared.
|
|
242
147
|
|
|
243
148
|
While tests are running, `NODE_ENV` defaults to `test` when it was not already set.
|
|
244
149
|
`SXYT` and `SXY_TEST_RUNNER` are both set to `true`, allowing configuration and application
|
|
245
150
|
code to detect sxy-test-runner specifically. In agent mode, `SXYT_AGENT` and
|
|
246
151
|
`SXY_TEST_RUNNER_AGENT` are also set to `true`.
|
|
247
152
|
|
|
248
|
-
###
|
|
153
|
+
### Caching modules
|
|
249
154
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
`._cached_.`
|
|
155
|
+
In watch mode modules will be reloaded as needed. With `watch.dependencyEvaluation: false` everything will be reloaded on each run.
|
|
156
|
+
|
|
157
|
+
To force a file never to be reloaded, insert `._cached_.` into its filename, like:
|
|
253
158
|
|
|
254
159
|
```js
|
|
255
160
|
import { database } from './fixtures/database._cached_.js'
|
|
256
161
|
```
|
|
257
162
|
|
|
258
|
-
The module then uses Node's normal process-wide module cache, so every test file receives
|
|
259
|
-
the same exported values. JavaScript and TypeScript module extensions are supported; in
|
|
260
|
-
TypeScript `NodeNext` source, import the emitted `._cached_.js` name as usual.
|
|
261
|
-
|
|
262
|
-
Cached modules remain loaded for the full one-off run or watch process. Avoid unsynchronised
|
|
263
|
-
mutable state when tests run in parallel, and explicitly clean up shared resources such as
|
|
264
|
-
servers or database connections.
|
|
265
|
-
|
|
266
163
|
## Execution modes
|
|
267
164
|
|
|
268
165
|
**execution** settings let you choose whether to run test files, describe blocks, and individual tests sequentially or in parallel.
|
|
@@ -271,17 +168,13 @@ If working with globals you may want to use sequential only, to avoid conflicts
|
|
|
271
168
|
|
|
272
169
|
Sequential mode also allows console log output to be displayed correctly with each test. Running in parallel mode does not allow log output to be matched up to each test, due to the use of a global override on console.log (other console output methods such as console.trace() are not covered currently)
|
|
273
170
|
|
|
274
|
-
If your code is isolated, and
|
|
171
|
+
If your code is isolated, and asynchronous, you can use parallel execution modes to radically speed up your tests, at the expense of the points mentioned above.
|
|
275
172
|
|
|
276
173
|
## Dependencies
|
|
277
174
|
|
|
278
|
-
|
|
175
|
+
Dynamic imports will not be detected and will not trigger test re-runs.
|
|
279
176
|
|
|
280
|
-
To re-run every test on any watched
|
|
281
|
-
to `false`. The dependency analysis is then skipped entirely - no trees are built at startup,
|
|
282
|
-
which is the slowest part of it on a large suite - and the blind spot above stops mattering,
|
|
283
|
-
because nothing is being predicted. On a small or fast suite this is often the better
|
|
284
|
-
setting; on a large one it trades a slower re-run for a complete one.
|
|
177
|
+
To re-run every test on any change in the watched files instead set `watch.dependencyEvaluation: false`
|
|
285
178
|
|
|
286
179
|
## Failing test re-running
|
|
287
180
|
|
|
@@ -372,5 +265,5 @@ describe('something', ({it, beforeEachTest, afterEachTest, afterDescribe}) => {
|
|
|
372
265
|
These start at 20 to stay clear of Node's reserved codes (1-14) and of signal exits
|
|
373
266
|
(over 128), so a non-zero code is always sxy-test's own.
|
|
374
267
|
|
|
375
|
-
It
|
|
268
|
+
It could also crash due to exceptions without giving any of these codes, in which case Node
|
|
376
269
|
supplies the code - usually `1` for an uncaught exception.
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
interface ResolveContext {
|
|
2
|
-
parentURL?: string;
|
|
3
|
-
}
|
|
4
|
-
interface ResolveResult {
|
|
5
|
-
url: string;
|
|
6
|
-
[key: string]: unknown;
|
|
7
|
-
}
|
|
8
|
-
type NextResolve = (specifier: string, context: ResolveContext) => ResolveResult | Promise<ResolveResult>;
|
|
9
|
-
export declare function resolve(specifier: string, context: ResolveContext, nextResolve: NextResolve): Promise<ResolveResult>;
|
|
10
|
-
export {};
|
|
11
|
-
//# sourceMappingURL=esmReload.d.ts.map
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { isBuiltin, register } from 'node:module';
|
|
2
|
-
const cachedModulePattern = /\._cached_\.(?:[cm]?js|[cm]?ts|jsx|tsx)$/i;
|
|
3
|
-
let reloadId = 0;
|
|
4
|
-
function isLoaderInstance() {
|
|
5
|
-
return new URL(import.meta.url).searchParams.has('loader');
|
|
6
|
-
}
|
|
7
|
-
function isThisModule(url) {
|
|
8
|
-
const candidate = new URL(url);
|
|
9
|
-
const current = new URL(import.meta.url);
|
|
10
|
-
candidate.search = '';
|
|
11
|
-
current.search = '';
|
|
12
|
-
return candidate.href === current.href;
|
|
13
|
-
}
|
|
14
|
-
function isCachedModule(url) {
|
|
15
|
-
return cachedModulePattern.test(url.pathname);
|
|
16
|
-
}
|
|
17
|
-
export async function resolve(specifier, context, nextResolve) {
|
|
18
|
-
const result = await nextResolve(specifier, context);
|
|
19
|
-
if (isThisModule(result.url) || isBuiltin(result.url) || context.parentURL === undefined) {
|
|
20
|
-
return result;
|
|
21
|
-
}
|
|
22
|
-
const url = new URL(result.url);
|
|
23
|
-
// A *._cached_.js/ts module and its dependency graph use Node's ordinary
|
|
24
|
-
// process-wide module cache instead of inheriting the test file instance.
|
|
25
|
-
if (isCachedModule(url))
|
|
26
|
-
return result;
|
|
27
|
-
const parentUrl = new URL(context.parentURL);
|
|
28
|
-
const instance = url.searchParams.get('reload') === ''
|
|
29
|
-
? `sxy-test-runner-reload-${reloadId++}`
|
|
30
|
-
: parentUrl.searchParams.get('sxyt-instance');
|
|
31
|
-
if (instance === null)
|
|
32
|
-
return result;
|
|
33
|
-
url.searchParams.delete('reload');
|
|
34
|
-
url.searchParams.set('sxyt-instance', instance);
|
|
35
|
-
return {
|
|
36
|
-
...result,
|
|
37
|
-
url: url.href
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
if (!isLoaderInstance()) {
|
|
41
|
-
register(`${import.meta.url}?loader`);
|
|
42
|
-
}
|
|
43
|
-
//# sourceMappingURL=esmReload.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"esmReload.js","sourceRoot":"","sources":["../../src/packages/esmReload.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAqCjD,MAAM,mBAAmB,GAAG,2CAA2C,CAAA;AAEvE,IAAI,QAAQ,GAAG,CAAC,CAAA;AAEhB,SAAS,gBAAgB;IACrB,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AAC9D,CAAC;AAED,SAAS,YAAY,CAAC,GAAW;IAC7B,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAA;IAC9B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACxC,SAAS,CAAC,MAAM,GAAG,EAAE,CAAA;IACrB,OAAO,CAAC,MAAM,GAAG,EAAE,CAAA;IACnB,OAAO,SAAS,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,CAAA;AAC1C,CAAC;AAED,SAAS,cAAc,CAAC,GAAQ;IAC5B,OAAO,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CACzB,SAAiB,EACjB,OAAuB,EACvB,WAAwB;IAExB,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;IAEpD,IAAI,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QACvF,OAAO,MAAM,CAAA;IACjB,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IAE/B,yEAAyE;IACzE,0EAA0E;IAC1E,IAAI,cAAc,CAAC,GAAG,CAAC;QAAE,OAAO,MAAM,CAAA;IAEtC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAC5C,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,EAAE;QAClD,CAAC,CAAC,0BAA0B,QAAQ,EAAE,EAAE;QACxC,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;IAEjD,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,MAAM,CAAA;IAEpC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IACjC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,eAAe,EAAE,QAAQ,CAAC,CAAA;IAE/C,OAAO;QACH,GAAG,MAAM;QACT,GAAG,EAAE,GAAG,CAAC,IAAI;KAChB,CAAA;AACL,CAAC;AAED,IAAI,CAAC,gBAAgB,EAAE,EAAE,CAAC;IACtB,QAAQ,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,CAAA;AACzC,CAAC","sourcesContent":["\nimport { isBuiltin, register } from 'node:module'\n\n\n/*\n * Based on esm-reload by Marcel Laverdet and Pierre-Yves Gérardy.\n *\n * ISC License\n *\n * Copyright 2024 Marcel Laverdet & Pierre-Yves Gérardy\n *\n * Permission to use, copy, modify, and/or distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\ninterface ResolveContext {\n parentURL?: string\n}\n\ninterface ResolveResult {\n url: string\n [key: string]: unknown\n}\n\ntype NextResolve = (\n specifier: string,\n context: ResolveContext\n) => ResolveResult | Promise<ResolveResult>\n\nconst cachedModulePattern = /\\._cached_\\.(?:[cm]?js|[cm]?ts|jsx|tsx)$/i\n\nlet reloadId = 0\n\nfunction isLoaderInstance(): boolean {\n return new URL(import.meta.url).searchParams.has('loader')\n}\n\nfunction isThisModule(url: string): boolean {\n const candidate = new URL(url)\n const current = new URL(import.meta.url)\n candidate.search = ''\n current.search = ''\n return candidate.href === current.href\n}\n\nfunction isCachedModule(url: URL): boolean {\n return cachedModulePattern.test(url.pathname)\n}\n\nexport async function resolve(\n specifier: string,\n context: ResolveContext,\n nextResolve: NextResolve\n): Promise<ResolveResult> {\n const result = await nextResolve(specifier, context)\n\n if (isThisModule(result.url) || isBuiltin(result.url) || context.parentURL === undefined) {\n return result\n }\n\n const url = new URL(result.url)\n\n // A *._cached_.js/ts module and its dependency graph use Node's ordinary\n // process-wide module cache instead of inheriting the test file instance.\n if (isCachedModule(url)) return result\n\n const parentUrl = new URL(context.parentURL)\n const instance = url.searchParams.get('reload') === ''\n ? `sxy-test-runner-reload-${reloadId++}`\n : parentUrl.searchParams.get('sxyt-instance')\n\n if (instance === null) return result\n\n url.searchParams.delete('reload')\n url.searchParams.set('sxyt-instance', instance)\n\n return {\n ...result,\n url: url.href\n }\n}\n\nif (!isLoaderInstance()) {\n register(`${import.meta.url}?loader`)\n}\n"]}
|