@backstage/cli-module-test-jest 0.0.0-nightly-20260317031259
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/CHANGELOG.md +13 -0
- package/README.md +15 -0
- package/bin/backstage-cli-module-test-jest +32 -0
- package/config/getJestEnvironment.js +49 -0
- package/config/jest-environment-jsdom/index.js +61 -0
- package/config/jest.js +422 -0
- package/config/jestCacheResultProcessor.cjs +23 -0
- package/config/jestCachingModuleLoader.js +35 -0
- package/config/jestFileTransform.js +44 -0
- package/config/jestRejectNetworkRequests.js +70 -0
- package/config/jestSucraseTransform.js +87 -0
- package/config/jestSwcTransform.js +44 -0
- package/config/jestYamlTransform.js +40 -0
- package/dist/commands/package/test.cjs.js +59 -0
- package/dist/commands/package/test.cjs.js.map +1 -0
- package/dist/commands/repo/test.cjs.js +299 -0
- package/dist/commands/repo/test.cjs.js.map +1 -0
- package/dist/index.cjs.js +25 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/package.json.cjs.js +110 -0
- package/dist/package.json.cjs.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const http = require('node:http');
|
|
18
|
+
const https = require('node:https');
|
|
19
|
+
|
|
20
|
+
const errorMessage = 'Network requests are not allowed in tests';
|
|
21
|
+
|
|
22
|
+
const origHttpAgent = http.globalAgent;
|
|
23
|
+
const origHttpsAgent = https.globalAgent;
|
|
24
|
+
const origFetch = global.fetch;
|
|
25
|
+
const origXMLHttpRequest = global.XMLHttpRequest;
|
|
26
|
+
|
|
27
|
+
http.globalAgent = new http.Agent({
|
|
28
|
+
lookup() {
|
|
29
|
+
throw new Error(errorMessage);
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
https.globalAgent = new https.Agent({
|
|
34
|
+
lookup() {
|
|
35
|
+
throw new Error(errorMessage);
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const BLOCKING_FETCH_SYMBOL = Symbol.for(
|
|
40
|
+
'backstage.jestRejectNetworkRequests.blockingFetch',
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
if (global.fetch) {
|
|
44
|
+
const blockingFetch = async (input, init) => {
|
|
45
|
+
// If global.fetch still has our marker, block the request
|
|
46
|
+
if (global.fetch[BLOCKING_FETCH_SYMBOL]) {
|
|
47
|
+
throw new Error(errorMessage);
|
|
48
|
+
}
|
|
49
|
+
// MSW (or something else) wrapped us - pass through
|
|
50
|
+
return origFetch(input, init);
|
|
51
|
+
};
|
|
52
|
+
blockingFetch[BLOCKING_FETCH_SYMBOL] = true;
|
|
53
|
+
global.fetch = blockingFetch;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (global.XMLHttpRequest) {
|
|
57
|
+
global.XMLHttpRequest = class {
|
|
58
|
+
constructor() {
|
|
59
|
+
throw new Error(errorMessage);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Reset overrides after each suite to make sure we don't pollute the test environment
|
|
65
|
+
afterAll(() => {
|
|
66
|
+
http.globalAgent = origHttpAgent;
|
|
67
|
+
https.globalAgent = origHttpsAgent;
|
|
68
|
+
global.fetch = origFetch;
|
|
69
|
+
global.XMLHttpRequest = origXMLHttpRequest;
|
|
70
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2021 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const { createHash } = require('node:crypto');
|
|
18
|
+
const { transform } = require('sucrase');
|
|
19
|
+
const sucrasePkg = require('sucrase/package.json');
|
|
20
|
+
|
|
21
|
+
const ESM_REGEX = /\b(?:import|export)\b/;
|
|
22
|
+
|
|
23
|
+
function createTransformer(config) {
|
|
24
|
+
const process = (source, filePath) => {
|
|
25
|
+
let transforms;
|
|
26
|
+
|
|
27
|
+
if (filePath.endsWith('.esm.js')) {
|
|
28
|
+
transforms = ['imports'];
|
|
29
|
+
} else if (filePath.endsWith('.js')) {
|
|
30
|
+
// This is a very rough filter to avoid transforming things that we quickly
|
|
31
|
+
// can be sure are definitely not ESM modules.
|
|
32
|
+
if (ESM_REGEX.test(source)) {
|
|
33
|
+
transforms = ['imports', 'jsx']; // JSX within .js is currently allowed
|
|
34
|
+
}
|
|
35
|
+
} else if (filePath.endsWith('.jsx')) {
|
|
36
|
+
transforms = ['jsx', 'imports'];
|
|
37
|
+
} else if (filePath.endsWith('.ts')) {
|
|
38
|
+
transforms = ['typescript', 'imports'];
|
|
39
|
+
} else if (filePath.endsWith('.tsx')) {
|
|
40
|
+
transforms = ['typescript', 'jsx', 'imports'];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Only apply the jest transform to the test files themselves
|
|
44
|
+
if (transforms && filePath.includes('.test.')) {
|
|
45
|
+
transforms.push('jest');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (transforms) {
|
|
49
|
+
const { code, sourceMap: map } = transform(source, {
|
|
50
|
+
transforms,
|
|
51
|
+
filePath,
|
|
52
|
+
disableESTransforms: true,
|
|
53
|
+
sourceMapOptions: {
|
|
54
|
+
compiledFilename: filePath,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
if (config.enableSourceMaps) {
|
|
58
|
+
const b64 = Buffer.from(JSON.stringify(map), 'utf8').toString('base64');
|
|
59
|
+
const suffix = `//# sourceMappingURL=data:application/json;charset=utf-8;base64,${b64}`;
|
|
60
|
+
// Include both inline and object source maps, as inline source maps are
|
|
61
|
+
// needed for support of some editor integrations.
|
|
62
|
+
return { code: `${code}\n${suffix}`, map };
|
|
63
|
+
}
|
|
64
|
+
// We only return the `map` result if source maps are enabled, as they
|
|
65
|
+
// have a negative impact on the coverage accuracy.
|
|
66
|
+
return { code };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return { code: source };
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const getCacheKey = sourceText => {
|
|
73
|
+
return createHash('sha256')
|
|
74
|
+
.update(sourceText)
|
|
75
|
+
.update(Buffer.alloc(1))
|
|
76
|
+
.update(sucrasePkg.version)
|
|
77
|
+
.update(Buffer.alloc(1))
|
|
78
|
+
.update(JSON.stringify(config))
|
|
79
|
+
.update(Buffer.alloc(1))
|
|
80
|
+
.update('1') // increment whenever the transform logic in this file changes
|
|
81
|
+
.digest('hex');
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return { process, getCacheKey };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = { createTransformer };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2022 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
const { createTransformer: createSwcTransformer } = require('@swc/jest');
|
|
17
|
+
|
|
18
|
+
const ESM_REGEX = /\b(?:import|export)\b/;
|
|
19
|
+
|
|
20
|
+
function createTransformer(config) {
|
|
21
|
+
const swcTransformer = createSwcTransformer({
|
|
22
|
+
inputSourceMap: false,
|
|
23
|
+
...config,
|
|
24
|
+
});
|
|
25
|
+
const process = (source, filePath, jestOptions) => {
|
|
26
|
+
// Skip transformation of .js files without ESM syntax, we never transform from CJS to ESM
|
|
27
|
+
if (filePath.endsWith('.js') && !ESM_REGEX.test(source)) {
|
|
28
|
+
return { code: source };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Skip transformation of .mjs files, they should only be used if ESM support is available
|
|
32
|
+
if (jestOptions.supportsStaticESM && filePath.endsWith('.mjs')) {
|
|
33
|
+
return { code: source };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return swcTransformer.process(source, filePath, jestOptions);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const getCacheKey = swcTransformer.getCacheKey;
|
|
40
|
+
|
|
41
|
+
return { process, getCacheKey };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = { createTransformer };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2022 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const yaml = require('yaml');
|
|
18
|
+
const crypto = require('node:crypto');
|
|
19
|
+
|
|
20
|
+
function createTransformer(config) {
|
|
21
|
+
const process = source => {
|
|
22
|
+
const json = JSON.stringify(yaml.parse(source), null, 2);
|
|
23
|
+
return { code: `module.exports = ${json}`, map: null };
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const getCacheKey = sourceText => {
|
|
27
|
+
return crypto
|
|
28
|
+
.createHash('sha256')
|
|
29
|
+
.update(sourceText)
|
|
30
|
+
.update(Buffer.alloc(1))
|
|
31
|
+
.update(JSON.stringify(config))
|
|
32
|
+
.update(Buffer.alloc(1))
|
|
33
|
+
.update('1') // increment whenever the transform logic in this file changes
|
|
34
|
+
.digest('hex');
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
return { process, getCacheKey };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { createTransformer };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var cliCommon = require('@backstage/cli-common');
|
|
6
|
+
|
|
7
|
+
function includesAnyOf(hayStack, ...needles) {
|
|
8
|
+
for (const needle of needles) {
|
|
9
|
+
if (hayStack.includes(needle)) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
var test = async ({ args }) => {
|
|
16
|
+
if (!includesAnyOf(args, "-c", "--config")) {
|
|
17
|
+
args.push("--config", cliCommon.findOwnPaths(__dirname).resolve("config/jest.js"));
|
|
18
|
+
}
|
|
19
|
+
if (!includesAnyOf(args, "--no-passWithNoTests", "--passWithNoTests=false")) {
|
|
20
|
+
args.push("--passWithNoTests");
|
|
21
|
+
}
|
|
22
|
+
if (!process.env.CI && !args.includes("--coverage") && // explicitly no watching
|
|
23
|
+
!includesAnyOf(args, "--no-watch", "--watch=false", "--watchAll=false") && // already watching
|
|
24
|
+
!includesAnyOf(args, "--watch", "--watchAll")) {
|
|
25
|
+
const isGitRepo = () => cliCommon.runCheck(["git", "rev-parse", "--is-inside-work-tree"]);
|
|
26
|
+
const isMercurialRepo = () => cliCommon.runCheck(["hg", "--cwd", ".", "root"]);
|
|
27
|
+
if (await isGitRepo() || await isMercurialRepo()) {
|
|
28
|
+
args.push("--watch");
|
|
29
|
+
} else {
|
|
30
|
+
args.push("--watchAll");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (!process.env.NODE_ENV) {
|
|
34
|
+
process.env.NODE_ENV = "test";
|
|
35
|
+
}
|
|
36
|
+
if (!process.env.TZ) {
|
|
37
|
+
process.env.TZ = "UTC";
|
|
38
|
+
}
|
|
39
|
+
if (!process.env.NODE_OPTIONS?.includes("--node-snapshot")) {
|
|
40
|
+
process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS ? `${process.env.NODE_OPTIONS} ` : ""}--no-node-snapshot`;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
require.resolve("jest");
|
|
44
|
+
} catch {
|
|
45
|
+
console.error(
|
|
46
|
+
[
|
|
47
|
+
"No Jest installation found in this project.",
|
|
48
|
+
"",
|
|
49
|
+
"To support opt-in migration to Jest v30, the Backstage CLI now expects Jest to be installed as a devDependency.",
|
|
50
|
+
"See the migration guide in the changelog for version options and their consequences."
|
|
51
|
+
].join("\n")
|
|
52
|
+
);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
await require("jest").run(args);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
exports.default = test;
|
|
59
|
+
//# sourceMappingURL=test.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.cjs.js","sources":["../../../src/commands/package/test.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { findOwnPaths, runCheck } from '@backstage/cli-common';\nimport type { CliCommandContext } from '@backstage/cli-node';\n\nfunction includesAnyOf(hayStack: string[], ...needles: string[]) {\n for (const needle of needles) {\n if (hayStack.includes(needle)) {\n return true;\n }\n }\n return false;\n}\n\nexport default async ({ args }: CliCommandContext) => {\n // Only include our config if caller isn't passing their own config\n if (!includesAnyOf(args, '-c', '--config')) {\n /* eslint-disable-next-line no-restricted-syntax */\n args.push('--config', findOwnPaths(__dirname).resolve('config/jest.js'));\n }\n\n if (!includesAnyOf(args, '--no-passWithNoTests', '--passWithNoTests=false')) {\n args.push('--passWithNoTests');\n }\n\n // Run in watch mode unless in CI, coverage mode, or running all tests\n if (\n !process.env.CI &&\n !args.includes('--coverage') &&\n // explicitly no watching\n !includesAnyOf(args, '--no-watch', '--watch=false', '--watchAll=false') &&\n // already watching\n !includesAnyOf(args, '--watch', '--watchAll')\n ) {\n const isGitRepo = () =>\n runCheck(['git', 'rev-parse', '--is-inside-work-tree']);\n const isMercurialRepo = () => runCheck(['hg', '--cwd', '.', 'root']);\n\n if ((await isGitRepo()) || (await isMercurialRepo())) {\n args.push('--watch');\n } else {\n args.push('--watchAll');\n }\n }\n\n // This is the only thing that is not implemented by jest.run(), so we do it here instead\n // https://github.com/facebook/jest/blob/cd8828f7bbec6e55b4df5e41e853a5133c4a3ee1/packages/jest-cli/bin/jest.js#L12\n if (!process.env.NODE_ENV) {\n (process.env as any).NODE_ENV = 'test';\n }\n\n // This is to have a consistent timezone for when running tests that involve checking\n // the formatting of date/times.\n // https://stackoverflow.com/questions/56261381/how-do-i-set-a-timezone-in-my-jest-config\n if (!process.env.TZ) {\n process.env.TZ = 'UTC';\n }\n\n // Unless the user explicitly toggles node-snapshot, default to provide --no-node-snapshot to reduce number of steps to run scaffolder\n // on Node LTS.\n if (!process.env.NODE_OPTIONS?.includes('--node-snapshot')) {\n process.env.NODE_OPTIONS = `${\n process.env.NODE_OPTIONS ? `${process.env.NODE_OPTIONS} ` : ''\n }--no-node-snapshot`;\n }\n\n // Because of the ongoing migration to v30 of jest, jest is no longer hard-depended to allow\n // opt-in migration. Users instead need to add jest as a devDependency themselves and specify\n // the version they want. This prints a helpful error message if jest is not found, i.e. they\n // forgot/didn't know they had to add jest themselves.\n try {\n require.resolve('jest');\n } catch {\n console.error(\n [\n 'No Jest installation found in this project.',\n '',\n 'To support opt-in migration to Jest v30, the Backstage CLI now expects Jest to be installed as a devDependency.',\n 'See the migration guide in the changelog for version options and their consequences.',\n ].join('\\n'),\n );\n process.exit(1);\n }\n\n // eslint-disable-next-line @backstage/no-undeclared-imports\n await require('jest').run(args);\n};\n"],"names":["findOwnPaths","runCheck"],"mappings":";;;;;;AAmBA,SAAS,aAAA,CAAc,aAAuB,OAAA,EAAmB;AAC/D,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,IAAI,QAAA,CAAS,QAAA,CAAS,MAAM,CAAA,EAAG;AAC7B,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AAEA,WAAe,OAAO,EAAE,IAAA,EAAK,KAAyB;AAEpD,EAAA,IAAI,CAAC,aAAA,CAAc,IAAA,EAAM,IAAA,EAAM,UAAU,CAAA,EAAG;AAE1C,IAAA,IAAA,CAAK,KAAK,UAAA,EAAYA,sBAAA,CAAa,SAAS,CAAA,CAAE,OAAA,CAAQ,gBAAgB,CAAC,CAAA;AAAA,EACzE;AAEA,EAAA,IAAI,CAAC,aAAA,CAAc,IAAA,EAAM,sBAAA,EAAwB,yBAAyB,CAAA,EAAG;AAC3E,IAAA,IAAA,CAAK,KAAK,mBAAmB,CAAA;AAAA,EAC/B;AAGA,EAAA,IACE,CAAC,OAAA,CAAQ,GAAA,CAAI,MACb,CAAC,IAAA,CAAK,SAAS,YAAY,CAAA;AAAA,EAE3B,CAAC,aAAA,CAAc,IAAA,EAAM,YAAA,EAAc,iBAAiB,kBAAkB,CAAA;AAAA,EAEtE,CAAC,aAAA,CAAc,IAAA,EAAM,SAAA,EAAW,YAAY,CAAA,EAC5C;AACA,IAAA,MAAM,YAAY,MAChBC,kBAAA,CAAS,CAAC,KAAA,EAAO,WAAA,EAAa,uBAAuB,CAAC,CAAA;AACxD,IAAA,MAAM,eAAA,GAAkB,MAAMA,kBAAA,CAAS,CAAC,MAAM,OAAA,EAAS,GAAA,EAAK,MAAM,CAAC,CAAA;AAEnE,IAAA,IAAK,MAAM,SAAA,EAAU,IAAO,MAAM,iBAAgB,EAAI;AACpD,MAAA,IAAA,CAAK,KAAK,SAAS,CAAA;AAAA,IACrB,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,KAAK,YAAY,CAAA;AAAA,IACxB;AAAA,EACF;AAIA,EAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,QAAA,EAAU;AACzB,IAAC,OAAA,CAAQ,IAAY,QAAA,GAAW,MAAA;AAAA,EAClC;AAKA,EAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,EAAA,EAAI;AACnB,IAAA,OAAA,CAAQ,IAAI,EAAA,GAAK,KAAA;AAAA,EACnB;AAIA,EAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,YAAA,EAAc,QAAA,CAAS,iBAAiB,CAAA,EAAG;AAC1D,IAAA,OAAA,CAAQ,GAAA,CAAI,YAAA,GAAe,CAAA,EACzB,OAAA,CAAQ,GAAA,CAAI,YAAA,GAAe,CAAA,EAAG,OAAA,CAAQ,GAAA,CAAI,YAAY,CAAA,CAAA,CAAA,GAAM,EAC9D,CAAA,kBAAA,CAAA;AAAA,EACF;AAMA,EAAA,IAAI;AACF,IAAA,OAAA,CAAQ,QAAQ,MAAM,CAAA;AAAA,EACxB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAA,CAAQ,KAAA;AAAA,MACN;AAAA,QACE,6CAAA;AAAA,QACA,EAAA;AAAA,QACA,iHAAA;AAAA,QACA;AAAA,OACF,CAAE,KAAK,IAAI;AAAA,KACb;AACA,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EAChB;AAGA,EAAA,MAAM,OAAA,CAAQ,MAAM,CAAA,CAAE,GAAA,CAAI,IAAI,CAAA;AAChC,CAAA;;;;"}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var os = require('node:os');
|
|
6
|
+
var crypto = require('node:crypto');
|
|
7
|
+
var cleye = require('cleye');
|
|
8
|
+
var yargs = require('yargs');
|
|
9
|
+
var jestCli = require('jest-cli');
|
|
10
|
+
var node_path = require('node:path');
|
|
11
|
+
var cliNode = require('@backstage/cli-node');
|
|
12
|
+
var cliCommon = require('@backstage/cli-common');
|
|
13
|
+
|
|
14
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
15
|
+
|
|
16
|
+
var os__default = /*#__PURE__*/_interopDefaultCompat(os);
|
|
17
|
+
var crypto__default = /*#__PURE__*/_interopDefaultCompat(crypto);
|
|
18
|
+
var yargs__default = /*#__PURE__*/_interopDefaultCompat(yargs);
|
|
19
|
+
|
|
20
|
+
async function readPackageTreeHashes(graph) {
|
|
21
|
+
const pkgs = Array.from(graph.values()).map((pkg) => ({
|
|
22
|
+
...pkg,
|
|
23
|
+
path: node_path.relative(cliCommon.targetPaths.rootDir, pkg.dir)
|
|
24
|
+
}));
|
|
25
|
+
const output = await cliCommon.runOutput([
|
|
26
|
+
"git",
|
|
27
|
+
"ls-tree",
|
|
28
|
+
"--format=%(objectname)=%(path)",
|
|
29
|
+
"HEAD",
|
|
30
|
+
"--",
|
|
31
|
+
...pkgs.map((pkg) => pkg.path)
|
|
32
|
+
]);
|
|
33
|
+
const map = new Map(
|
|
34
|
+
output.trim().split(/\r?\n/).map((line) => {
|
|
35
|
+
const [itemSha, ...itemPathParts] = line.split("=");
|
|
36
|
+
const itemPath = itemPathParts.join("=");
|
|
37
|
+
const pkg = pkgs.find((p) => p.path === itemPath);
|
|
38
|
+
if (!pkg) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Unexpectedly missing tree sha entry for path ${itemPath}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return [pkg.packageJson.name, itemSha];
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
return (pkgName) => {
|
|
47
|
+
const sha = map.get(pkgName);
|
|
48
|
+
if (!sha) {
|
|
49
|
+
throw new Error(`Tree sha not found for ${pkgName}`);
|
|
50
|
+
}
|
|
51
|
+
return sha;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function createFlagFinder(args) {
|
|
55
|
+
const flags = /* @__PURE__ */ new Set();
|
|
56
|
+
for (const arg of args) {
|
|
57
|
+
if (arg.startsWith("--no-")) {
|
|
58
|
+
flags.add(`--${arg.slice("--no-".length)}`);
|
|
59
|
+
} else if (arg.startsWith("--")) {
|
|
60
|
+
flags.add(arg.split("=")[0]);
|
|
61
|
+
} else if (arg.startsWith("-")) {
|
|
62
|
+
const shortFlags = arg.slice(1).split("");
|
|
63
|
+
for (const shortFlag of shortFlags) {
|
|
64
|
+
flags.add(`-${shortFlag}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return (...findFlags) => {
|
|
69
|
+
for (const flag of findFlags) {
|
|
70
|
+
if (flags.has(flag)) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
var test = async ({ args, info }) => {
|
|
78
|
+
const testGlobal = global;
|
|
79
|
+
for (const flag of ["successCache", "successCacheDir", "jestHelp"]) {
|
|
80
|
+
if (args.some((a) => a === `--${flag}` || a.startsWith(`--${flag}=`))) {
|
|
81
|
+
process.stderr.write(
|
|
82
|
+
`DEPRECATION WARNING: --${flag} is deprecated, use the kebab-case form instead
|
|
83
|
+
`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const { flags: opts } = cleye.cli(
|
|
88
|
+
{
|
|
89
|
+
help: info,
|
|
90
|
+
booleanFlagNegation: true,
|
|
91
|
+
flags: {
|
|
92
|
+
since: {
|
|
93
|
+
type: String,
|
|
94
|
+
description: "Only include test packages changed since the specified ref"
|
|
95
|
+
},
|
|
96
|
+
successCache: {
|
|
97
|
+
type: Boolean,
|
|
98
|
+
description: "Cache and skip tests for unchanged packages"
|
|
99
|
+
},
|
|
100
|
+
successCacheDir: {
|
|
101
|
+
type: String,
|
|
102
|
+
description: "Directory for the success cache"
|
|
103
|
+
},
|
|
104
|
+
jestHelp: {
|
|
105
|
+
type: Boolean,
|
|
106
|
+
description: "Show Jest's own help output"
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
ignoreArgv: (type) => type === "unknown-flag" || type === "argument"
|
|
110
|
+
},
|
|
111
|
+
void 0,
|
|
112
|
+
args
|
|
113
|
+
);
|
|
114
|
+
const hasFlags = createFlagFinder(args);
|
|
115
|
+
const sinceRef = opts.since || void 0;
|
|
116
|
+
const { _: parsedArgs } = await yargs__default.default(args).options(jestCli.yargsOptions).argv;
|
|
117
|
+
if (!hasFlags("-c", "--config")) {
|
|
118
|
+
args.push("--config", cliCommon.findOwnPaths(__dirname).resolve("config/jest.js"));
|
|
119
|
+
}
|
|
120
|
+
if (!hasFlags("--passWithNoTests")) {
|
|
121
|
+
args.push("--passWithNoTests");
|
|
122
|
+
}
|
|
123
|
+
let isSingleWatchMode = args.includes("--watch");
|
|
124
|
+
if (!sinceRef && !process.env.CI && !hasFlags("--coverage", "--watch", "--watchAll")) {
|
|
125
|
+
const isGitRepo = () => cliCommon.runCheck(["git", "rev-parse", "--is-inside-work-tree"]);
|
|
126
|
+
const isMercurialRepo = () => cliCommon.runCheck(["hg", "--cwd", ".", "root"]);
|
|
127
|
+
if (await isGitRepo() || await isMercurialRepo()) {
|
|
128
|
+
isSingleWatchMode = true;
|
|
129
|
+
args.push("--watch");
|
|
130
|
+
} else {
|
|
131
|
+
args.push("--watchAll");
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (isSingleWatchMode && parsedArgs.length > 0) {
|
|
135
|
+
testGlobal.__backstageCli_watchProjectFilter = {
|
|
136
|
+
async filter(projectConfigs) {
|
|
137
|
+
const selectedProjects2 = [];
|
|
138
|
+
const usedArgs = /* @__PURE__ */ new Set();
|
|
139
|
+
for (const project of projectConfigs) {
|
|
140
|
+
for (const arg of parsedArgs) {
|
|
141
|
+
if (cliCommon.isChildPath(project.rootDir, String(arg))) {
|
|
142
|
+
selectedProjects2.push(project);
|
|
143
|
+
usedArgs.add(arg);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (usedArgs.size !== parsedArgs.length) {
|
|
148
|
+
return projectConfigs;
|
|
149
|
+
}
|
|
150
|
+
return selectedProjects2;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
if (!hasFlags("--workerIdleMemoryLimit")) {
|
|
155
|
+
args.push("--workerIdleMemoryLimit=1000M");
|
|
156
|
+
}
|
|
157
|
+
if (os__default.default.cpus().length <= 3 && !hasFlags("-i", "--runInBand", "-w", "--maxWorkers")) {
|
|
158
|
+
args.push("--maxWorkers=2");
|
|
159
|
+
}
|
|
160
|
+
let packageGraph;
|
|
161
|
+
async function getPackageGraph() {
|
|
162
|
+
if (packageGraph) {
|
|
163
|
+
return packageGraph;
|
|
164
|
+
}
|
|
165
|
+
const packages = await cliNode.PackageGraph.listTargetPackages();
|
|
166
|
+
packageGraph = cliNode.PackageGraph.fromPackages(packages);
|
|
167
|
+
return packageGraph;
|
|
168
|
+
}
|
|
169
|
+
let selectedProjects = void 0;
|
|
170
|
+
if (sinceRef && !hasFlags("--selectProjects")) {
|
|
171
|
+
const graph = await getPackageGraph();
|
|
172
|
+
const changedPackages = await graph.listChangedPackages({
|
|
173
|
+
ref: sinceRef,
|
|
174
|
+
analyzeLockfile: true
|
|
175
|
+
});
|
|
176
|
+
selectedProjects = Array.from(
|
|
177
|
+
graph.collectPackageNames(
|
|
178
|
+
changedPackages.map((pkg) => pkg.name),
|
|
179
|
+
(pkg) => pkg.allLocalDependents.keys()
|
|
180
|
+
)
|
|
181
|
+
);
|
|
182
|
+
if (selectedProjects.length === 0) {
|
|
183
|
+
console.log(`No packages changed since ${opts.since}`);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
args.push("--selectProjects", ...selectedProjects);
|
|
187
|
+
}
|
|
188
|
+
if (!process.env.NODE_ENV) {
|
|
189
|
+
process.env.NODE_ENV = "test";
|
|
190
|
+
}
|
|
191
|
+
if (!process.env.TZ) {
|
|
192
|
+
process.env.TZ = "UTC";
|
|
193
|
+
}
|
|
194
|
+
if (!process.env.NODE_OPTIONS?.includes("--node-snapshot")) {
|
|
195
|
+
process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS ? `${process.env.NODE_OPTIONS} ` : ""}--no-node-snapshot`;
|
|
196
|
+
}
|
|
197
|
+
if (opts.jestHelp) {
|
|
198
|
+
args.push("--help");
|
|
199
|
+
}
|
|
200
|
+
if (opts.successCache) {
|
|
201
|
+
if (parsedArgs.length > 0) {
|
|
202
|
+
throw new Error(
|
|
203
|
+
`The --successCache flag can not be combined with the following arguments: ${parsedArgs.join(
|
|
204
|
+
", "
|
|
205
|
+
)}`
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
if (args.includes("--shard")) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
`The --successCache flag can not be combined with the --shard flag`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
const cache = cliNode.SuccessCache.create({
|
|
214
|
+
name: "test",
|
|
215
|
+
basePath: opts.successCacheDir
|
|
216
|
+
});
|
|
217
|
+
const graph = await getPackageGraph();
|
|
218
|
+
const projectHashes = /* @__PURE__ */ new Map();
|
|
219
|
+
const outputSuccessCache = new Array();
|
|
220
|
+
testGlobal.__backstageCli_jestSuccessCache = {
|
|
221
|
+
// This is called by `config/jest.js` after the project configs have been gathered
|
|
222
|
+
async filterConfigs(projectConfigs, globalRootConfig) {
|
|
223
|
+
const cacheEntries = await cache.read();
|
|
224
|
+
const lockfile = await cliNode.Lockfile.load(
|
|
225
|
+
cliCommon.targetPaths.resolveRoot("yarn.lock")
|
|
226
|
+
);
|
|
227
|
+
const getPackageTreeHash = await readPackageTreeHashes(graph);
|
|
228
|
+
const baseHash = crypto__default.default.createHash("sha1");
|
|
229
|
+
baseHash.update("v1");
|
|
230
|
+
baseHash.update("\0");
|
|
231
|
+
baseHash.update(process.version);
|
|
232
|
+
baseHash.update("\0");
|
|
233
|
+
baseHash.update(
|
|
234
|
+
cliNode.SuccessCache.trimPaths(JSON.stringify(globalRootConfig))
|
|
235
|
+
);
|
|
236
|
+
const baseSha = baseHash.digest("hex");
|
|
237
|
+
return projectConfigs.filter((project) => {
|
|
238
|
+
const packageName = project.displayName;
|
|
239
|
+
const pkg = graph.get(packageName);
|
|
240
|
+
if (!pkg) {
|
|
241
|
+
throw new Error(
|
|
242
|
+
`Package ${packageName} not found in package graph`
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
const hash = crypto__default.default.createHash("sha1");
|
|
246
|
+
hash.update(baseSha);
|
|
247
|
+
const packageTreeSha = getPackageTreeHash(packageName);
|
|
248
|
+
hash.update(packageTreeSha);
|
|
249
|
+
for (const [depName, depPkg] of pkg.allLocalDependencies) {
|
|
250
|
+
const depHash = getPackageTreeHash(depPkg.name);
|
|
251
|
+
hash.update(`${depName}:${depHash}`);
|
|
252
|
+
}
|
|
253
|
+
hash.update(cliNode.SuccessCache.trimPaths(JSON.stringify(project)));
|
|
254
|
+
hash.update(lockfile.getDependencyTreeHash(packageName));
|
|
255
|
+
const sha = hash.digest("hex");
|
|
256
|
+
projectHashes.set(packageName, sha);
|
|
257
|
+
if (cacheEntries.has(sha)) {
|
|
258
|
+
if (!selectedProjects || selectedProjects.includes(packageName)) {
|
|
259
|
+
console.log(`Skipped ${packageName} due to cache hit`);
|
|
260
|
+
}
|
|
261
|
+
outputSuccessCache.push(sha);
|
|
262
|
+
return void 0;
|
|
263
|
+
}
|
|
264
|
+
return project;
|
|
265
|
+
});
|
|
266
|
+
},
|
|
267
|
+
// This is called by `config/jestCacheResultProcess.cjs` after all tests have run
|
|
268
|
+
async reportResults(results) {
|
|
269
|
+
const successful = /* @__PURE__ */ new Set();
|
|
270
|
+
const failed = /* @__PURE__ */ new Set();
|
|
271
|
+
for (const testResult of results.testResults) {
|
|
272
|
+
for (const [pkgName, pkg] of graph) {
|
|
273
|
+
if (cliCommon.isChildPath(pkg.dir, testResult.testFilePath)) {
|
|
274
|
+
if (testResult.testExecError || testResult.failureMessage || testResult.numFailingTests > 0) {
|
|
275
|
+
failed.add(pkgName);
|
|
276
|
+
successful.delete(pkgName);
|
|
277
|
+
} else if (!failed.has(pkgName)) {
|
|
278
|
+
successful.add(pkgName);
|
|
279
|
+
}
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
for (const pkgName of successful) {
|
|
285
|
+
const sha = projectHashes.get(pkgName);
|
|
286
|
+
if (sha) {
|
|
287
|
+
outputSuccessCache.push(sha);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
await cache.write(outputSuccessCache);
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
await jestCli.run(args);
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
exports.createFlagFinder = createFlagFinder;
|
|
298
|
+
exports.default = test;
|
|
299
|
+
//# sourceMappingURL=test.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test.cjs.js","sources":["../../../src/commands/repo/test.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport os from 'node:os';\nimport crypto from 'node:crypto';\nimport { cli } from 'cleye';\nimport yargs from 'yargs';\n// 'jest-cli' is included with jest and should be kept in sync with the installed jest version\n// eslint-disable-next-line @backstage/no-undeclared-imports\nimport { run as runJest, yargsOptions as jestYargsOptions } from 'jest-cli';\nimport { relative as relativePath } from 'node:path';\nimport { Lockfile, PackageGraph, SuccessCache } from '@backstage/cli-node';\n\nimport {\n findOwnPaths,\n runCheck,\n runOutput,\n targetPaths,\n isChildPath,\n} from '@backstage/cli-common';\nimport type { CliCommandContext } from '@backstage/cli-node';\n\ntype JestProject = {\n displayName: string;\n rootDir: string;\n};\n\ninterface TestGlobal extends Global {\n __backstageCli_jestSuccessCache?: {\n filterConfigs(\n projectConfigs: JestProject[],\n globalConfig: unknown,\n ): Promise<JestProject[]>;\n reportResults(results: {\n testResults: Array<{\n displayName?: { name: string };\n numFailingTests: number;\n testFilePath: string;\n testExecError: {\n message: string;\n stack: string;\n };\n failureMessage: string;\n }>;\n }): Promise<void>;\n };\n __backstageCli_watchProjectFilter?: {\n filter(projectConfigs: JestProject[]): Promise<JestProject[]>;\n };\n}\n\n/**\n * Use git to get the HEAD tree hashes of each package in the project.\n */\nasync function readPackageTreeHashes(graph: PackageGraph) {\n const pkgs = Array.from(graph.values()).map(pkg => ({\n ...pkg,\n path: relativePath(targetPaths.rootDir, pkg.dir),\n }));\n const output = await runOutput([\n 'git',\n 'ls-tree',\n '--format=%(objectname)=%(path)',\n 'HEAD',\n '--',\n ...pkgs.map(pkg => pkg.path),\n ]);\n\n const map = new Map(\n output\n .trim()\n .split(/\\r?\\n/)\n .map(line => {\n const [itemSha, ...itemPathParts] = line.split('=');\n const itemPath = itemPathParts.join('=');\n const pkg = pkgs.find(p => p.path === itemPath);\n if (!pkg) {\n throw new Error(\n `Unexpectedly missing tree sha entry for path ${itemPath}`,\n );\n }\n return [pkg.packageJson.name, itemSha];\n }),\n );\n\n return (pkgName: string) => {\n const sha = map.get(pkgName);\n if (!sha) {\n throw new Error(`Tree sha not found for ${pkgName}`);\n }\n return sha;\n };\n}\n\nexport function createFlagFinder(args: string[]) {\n const flags = new Set<string>();\n\n for (const arg of args) {\n if (arg.startsWith('--no-')) {\n flags.add(`--${arg.slice('--no-'.length)}`);\n } else if (arg.startsWith('--')) {\n flags.add(arg.split('=')[0]);\n } else if (arg.startsWith('-')) {\n const shortFlags = arg.slice(1).split('');\n for (const shortFlag of shortFlags) {\n flags.add(`-${shortFlag}`);\n }\n }\n }\n\n return (...findFlags: string[]) => {\n for (const flag of findFlags) {\n if (flags.has(flag)) {\n return true;\n }\n }\n return false;\n };\n}\n\nexport default async ({ args, info }: CliCommandContext) => {\n const testGlobal = global as TestGlobal;\n\n for (const flag of ['successCache', 'successCacheDir', 'jestHelp']) {\n if (args.some(a => a === `--${flag}` || a.startsWith(`--${flag}=`))) {\n process.stderr.write(\n `DEPRECATION WARNING: --${flag} is deprecated, use the kebab-case form instead\\n`,\n );\n }\n }\n\n // Parse Backstage-specific flags; unknown flags and arguments are left in\n // args so they can be forwarded to Jest.\n const { flags: opts } = cli(\n {\n help: info,\n booleanFlagNegation: true,\n flags: {\n since: {\n type: String,\n description:\n 'Only include test packages changed since the specified ref',\n },\n successCache: {\n type: Boolean,\n description: 'Cache and skip tests for unchanged packages',\n },\n successCacheDir: {\n type: String,\n description: 'Directory for the success cache',\n },\n jestHelp: {\n type: Boolean,\n description: \"Show Jest's own help output\",\n },\n },\n ignoreArgv: type => type === 'unknown-flag' || type === 'argument',\n },\n undefined,\n args,\n );\n\n const hasFlags = createFlagFinder(args);\n const sinceRef = opts.since || undefined;\n\n // Parse the args to ensure that no file filters are provided, in which case we refuse to run\n const { _: parsedArgs } = await yargs(args).options(jestYargsOptions).argv;\n\n // Only include our config if caller isn't passing their own config\n if (!hasFlags('-c', '--config')) {\n /* eslint-disable-next-line no-restricted-syntax */\n args.push('--config', findOwnPaths(__dirname).resolve('config/jest.js'));\n }\n\n if (!hasFlags('--passWithNoTests')) {\n args.push('--passWithNoTests');\n }\n\n // Run in watch mode unless in CI, coverage mode, or running all tests\n let isSingleWatchMode = args.includes('--watch');\n if (\n !sinceRef &&\n !process.env.CI &&\n !hasFlags('--coverage', '--watch', '--watchAll')\n ) {\n const isGitRepo = () =>\n runCheck(['git', 'rev-parse', '--is-inside-work-tree']);\n const isMercurialRepo = () => runCheck(['hg', '--cwd', '.', 'root']);\n\n if ((await isGitRepo()) || (await isMercurialRepo())) {\n isSingleWatchMode = true;\n args.push('--watch');\n } else {\n args.push('--watchAll');\n }\n }\n\n // Due to our monorepo Jest project setup watch mode can be quite slow as it\n // will always scan all projects for matches. This is an optimization where if\n // the only provides filter paths from the repo root as args, we filter the\n // projects to only run tests for those.\n //\n // This does mean you're not able to edit the watch filters during the watch\n // session to point outside of the selected packages, but we consider that a\n // worthwhile tradeoff, and you can always avoid providing paths upfront.\n if (isSingleWatchMode && parsedArgs.length > 0) {\n testGlobal.__backstageCli_watchProjectFilter = {\n async filter(projectConfigs) {\n const selectedProjects = [];\n const usedArgs = new Set();\n\n for (const project of projectConfigs) {\n for (const arg of parsedArgs) {\n if (isChildPath(project.rootDir, String(arg))) {\n selectedProjects.push(project);\n usedArgs.add(arg);\n }\n }\n }\n\n // If we didn't end up using all args in the filtering we need to bail\n // and let Jest do the full filtering instead.\n if (usedArgs.size !== parsedArgs.length) {\n return projectConfigs;\n }\n\n return selectedProjects;\n },\n };\n }\n\n // When running tests from the repo root in large repos you can easily hit the heap limit.\n // This is because Jest workers leak a lot of memory, and the workaround is to limit worker memory.\n // We set a default memory limit, but if an explicit one is supplied it will be used instead\n if (!hasFlags('--workerIdleMemoryLimit')) {\n args.push('--workerIdleMemoryLimit=1000M');\n }\n\n // In order for the above worker memory limit to work we need to make sure the worker\n // count is set to at least 2, as the tests will otherwise run in-band.\n // Depending on the mode tests are run with the default count is either cpus-1, or cpus/2.\n // This means that if we've got at 4 or more cores we'll always get at least 2 workers, but\n // otherwise we need to set the worker count explicitly unless already done.\n if (\n os.cpus().length <= 3 &&\n !hasFlags('-i', '--runInBand', '-w', '--maxWorkers')\n ) {\n args.push('--maxWorkers=2');\n }\n\n let packageGraph: PackageGraph | undefined;\n async function getPackageGraph() {\n if (packageGraph) {\n return packageGraph;\n }\n const packages = await PackageGraph.listTargetPackages();\n packageGraph = PackageGraph.fromPackages(packages);\n return packageGraph;\n }\n\n let selectedProjects: string[] | undefined = undefined;\n if (sinceRef && !hasFlags('--selectProjects')) {\n const graph = await getPackageGraph();\n const changedPackages = await graph.listChangedPackages({\n ref: sinceRef,\n analyzeLockfile: true,\n });\n\n selectedProjects = Array.from(\n graph.collectPackageNames(\n changedPackages.map(pkg => pkg.name),\n pkg => pkg.allLocalDependents.keys(),\n ),\n );\n\n if (selectedProjects.length === 0) {\n console.log(`No packages changed since ${opts.since}`);\n return;\n }\n\n args.push('--selectProjects', ...selectedProjects);\n }\n\n // This is the only thing that is not implemented by jest.run(), so we do it here instead\n // https://github.com/facebook/jest/blob/cd8828f7bbec6e55b4df5e41e853a5133c4a3ee1/packages/jest-cli/bin/jest.js#L12\n if (!process.env.NODE_ENV) {\n (process.env as any).NODE_ENV = 'test';\n }\n\n // This is to have a consistent timezone for when running tests that involve checking\n // the formatting of date/times.\n // https://stackoverflow.com/questions/56261381/how-do-i-set-a-timezone-in-my-jest-config\n if (!process.env.TZ) {\n process.env.TZ = 'UTC';\n }\n\n // Unless the user explicitly toggles node-snapshot, default to provide --no-node-snapshot to reduce number of steps to run scaffolder\n // on Node LTS.\n if (!process.env.NODE_OPTIONS?.includes('--node-snapshot')) {\n process.env.NODE_OPTIONS = `${\n process.env.NODE_OPTIONS ? `${process.env.NODE_OPTIONS} ` : ''\n }--no-node-snapshot`;\n }\n\n if (opts.jestHelp) {\n args.push('--help');\n }\n\n // This code path is enabled by the --successCache flag, which is specific to\n // the `repo test` command in the Backstage CLI.\n if (opts.successCache) {\n // Refuse to run if file filters are provided\n if (parsedArgs.length > 0) {\n throw new Error(\n `The --successCache flag can not be combined with the following arguments: ${parsedArgs.join(\n ', ',\n )}`,\n );\n }\n // Likewise, it's not possible to combine sharding and the success cache\n if (args.includes('--shard')) {\n throw new Error(\n `The --successCache flag can not be combined with the --shard flag`,\n );\n }\n\n const cache = SuccessCache.create({\n name: 'test',\n basePath: opts.successCacheDir,\n });\n const graph = await getPackageGraph();\n\n // Shared state for the bridge\n const projectHashes = new Map<string, string>();\n const outputSuccessCache = new Array<string>();\n\n // Set up a bridge with the @backstage/cli/config/jest configuration file. These methods\n // are picked up by the config script itself, as well as the custom result processor.\n testGlobal.__backstageCli_jestSuccessCache = {\n // This is called by `config/jest.js` after the project configs have been gathered\n async filterConfigs(projectConfigs, globalRootConfig) {\n const cacheEntries = await cache.read();\n const lockfile = await Lockfile.load(\n targetPaths.resolveRoot('yarn.lock'),\n );\n const getPackageTreeHash = await readPackageTreeHashes(graph);\n\n // Base hash shared by all projects\n const baseHash = crypto.createHash('sha1');\n baseHash.update('v1'); // The version of this implementation\n baseHash.update('\\0');\n baseHash.update(process.version); // Node.js version\n baseHash.update('\\0');\n baseHash.update(\n SuccessCache.trimPaths(JSON.stringify(globalRootConfig)),\n ); // Variable global jest config\n const baseSha = baseHash.digest('hex');\n\n return projectConfigs.filter(project => {\n const packageName = project.displayName;\n const pkg = graph.get(packageName);\n if (!pkg) {\n throw new Error(\n `Package ${packageName} not found in package graph`,\n );\n }\n\n const hash = crypto.createHash('sha1');\n\n hash.update(baseSha); // Global base hash\n\n const packageTreeSha = getPackageTreeHash(packageName);\n hash.update(packageTreeSha); // Hash for target package contents\n\n for (const [depName, depPkg] of pkg.allLocalDependencies) {\n const depHash = getPackageTreeHash(depPkg.name);\n hash.update(`${depName}:${depHash}`); // Hash for each local monorepo dependency contents\n }\n\n // The project ID is a hash of the transform configuration, which helps\n // us bust the cache when any changes are made to the transform implementation.\n hash.update(SuccessCache.trimPaths(JSON.stringify(project)));\n hash.update(lockfile.getDependencyTreeHash(packageName));\n\n const sha = hash.digest('hex');\n\n projectHashes.set(packageName, sha);\n\n if (cacheEntries.has(sha)) {\n if (!selectedProjects || selectedProjects.includes(packageName)) {\n console.log(`Skipped ${packageName} due to cache hit`);\n }\n outputSuccessCache.push(sha);\n return undefined;\n }\n\n return project;\n });\n },\n // This is called by `config/jestCacheResultProcess.cjs` after all tests have run\n async reportResults(results) {\n const successful = new Set<string>();\n const failed = new Set<string>();\n\n for (const testResult of results.testResults) {\n for (const [pkgName, pkg] of graph) {\n if (isChildPath(pkg.dir, testResult.testFilePath)) {\n if (\n testResult.testExecError ||\n testResult.failureMessage ||\n testResult.numFailingTests > 0\n ) {\n failed.add(pkgName);\n successful.delete(pkgName);\n } else if (!failed.has(pkgName)) {\n successful.add(pkgName);\n }\n break;\n }\n }\n }\n\n for (const pkgName of successful) {\n const sha = projectHashes.get(pkgName);\n if (sha) {\n outputSuccessCache.push(sha);\n }\n }\n\n await cache.write(outputSuccessCache);\n },\n };\n }\n\n await runJest(args);\n};\n"],"names":["relativePath","targetPaths","runOutput","cli","yargs","jestYargsOptions","findOwnPaths","runCheck","selectedProjects","isChildPath","os","PackageGraph","SuccessCache","Lockfile","crypto","runJest"],"mappings":";;;;;;;;;;;;;;;;;;;AAmEA,eAAe,sBAAsB,KAAA,EAAqB;AACxD,EAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,KAAA,CAAM,QAAQ,CAAA,CAAE,IAAI,CAAA,GAAA,MAAQ;AAAA,IAClD,GAAG,GAAA;AAAA,IACH,IAAA,EAAMA,kBAAA,CAAaC,qBAAA,CAAY,OAAA,EAAS,IAAI,GAAG;AAAA,GACjD,CAAE,CAAA;AACF,EAAA,MAAM,MAAA,GAAS,MAAMC,mBAAA,CAAU;AAAA,IAC7B,KAAA;AAAA,IACA,SAAA;AAAA,IACA,gCAAA;AAAA,IACA,MAAA;AAAA,IACA,IAAA;AAAA,IACA,GAAG,IAAA,CAAK,GAAA,CAAI,CAAA,GAAA,KAAO,IAAI,IAAI;AAAA,GAC5B,CAAA;AAED,EAAA,MAAM,MAAM,IAAI,GAAA;AAAA,IACd,OACG,IAAA,EAAK,CACL,MAAM,OAAO,CAAA,CACb,IAAI,CAAA,IAAA,KAAQ;AACX,MAAA,MAAM,CAAC,OAAA,EAAS,GAAG,aAAa,CAAA,GAAI,IAAA,CAAK,MAAM,GAAG,CAAA;AAClD,MAAA,MAAM,QAAA,GAAW,aAAA,CAAc,IAAA,CAAK,GAAG,CAAA;AACvC,MAAA,MAAM,MAAM,IAAA,CAAK,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,CAAE,SAAS,QAAQ,CAAA;AAC9C,MAAA,IAAI,CAAC,GAAA,EAAK;AACR,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,gDAAgD,QAAQ,CAAA;AAAA,SAC1D;AAAA,MACF;AACA,MAAA,OAAO,CAAC,GAAA,CAAI,WAAA,CAAY,IAAA,EAAM,OAAO,CAAA;AAAA,IACvC,CAAC;AAAA,GACL;AAEA,EAAA,OAAO,CAAC,OAAA,KAAoB;AAC1B,IAAA,MAAM,GAAA,GAAM,GAAA,CAAI,GAAA,CAAI,OAAO,CAAA;AAC3B,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,uBAAA,EAA0B,OAAO,CAAA,CAAE,CAAA;AAAA,IACrD;AACA,IAAA,OAAO,GAAA;AAAA,EACT,CAAA;AACF;AAEO,SAAS,iBAAiB,IAAA,EAAgB;AAC/C,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAY;AAE9B,EAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AACtB,IAAA,IAAI,GAAA,CAAI,UAAA,CAAW,OAAO,CAAA,EAAG;AAC3B,MAAA,KAAA,CAAM,IAAI,CAAA,EAAA,EAAK,GAAA,CAAI,MAAM,OAAA,CAAQ,MAAM,CAAC,CAAA,CAAE,CAAA;AAAA,IAC5C,CAAA,MAAA,IAAW,GAAA,CAAI,UAAA,CAAW,IAAI,CAAA,EAAG;AAC/B,MAAA,KAAA,CAAM,IAAI,GAAA,CAAI,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,CAAC,CAAA;AAAA,IAC7B,CAAA,MAAA,IAAW,GAAA,CAAI,UAAA,CAAW,GAAG,CAAA,EAAG;AAC9B,MAAA,MAAM,aAAa,GAAA,CAAI,KAAA,CAAM,CAAC,CAAA,CAAE,MAAM,EAAE,CAAA;AACxC,MAAA,KAAA,MAAW,aAAa,UAAA,EAAY;AAClC,QAAA,KAAA,CAAM,GAAA,CAAI,CAAA,CAAA,EAAI,SAAS,CAAA,CAAE,CAAA;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,IAAI,SAAA,KAAwB;AACjC,IAAA,KAAA,MAAW,QAAQ,SAAA,EAAW;AAC5B,MAAA,IAAI,KAAA,CAAM,GAAA,CAAI,IAAI,CAAA,EAAG;AACnB,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,IACF;AACA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AACF;AAEA,WAAe,OAAO,EAAE,IAAA,EAAM,IAAA,EAAK,KAAyB;AAC1D,EAAA,MAAM,UAAA,GAAa,MAAA;AAEnB,EAAA,KAAA,MAAW,IAAA,IAAQ,CAAC,cAAA,EAAgB,iBAAA,EAAmB,UAAU,CAAA,EAAG;AAClE,IAAA,IAAI,IAAA,CAAK,IAAA,CAAK,CAAA,CAAA,KAAK,CAAA,KAAM,CAAA,EAAA,EAAK,IAAI,CAAA,CAAA,IAAM,CAAA,CAAE,UAAA,CAAW,CAAA,EAAA,EAAK,IAAI,CAAA,CAAA,CAAG,CAAC,CAAA,EAAG;AACnE,MAAA,OAAA,CAAQ,MAAA,CAAO,KAAA;AAAA,QACb,0BAA0B,IAAI,CAAA;AAAA;AAAA,OAChC;AAAA,IACF;AAAA,EACF;AAIA,EAAA,MAAM,EAAE,KAAA,EAAO,IAAA,EAAK,GAAIC,SAAA;AAAA,IACtB;AAAA,MACE,IAAA,EAAM,IAAA;AAAA,MACN,mBAAA,EAAqB,IAAA;AAAA,MACrB,KAAA,EAAO;AAAA,QACL,KAAA,EAAO;AAAA,UACL,IAAA,EAAM,MAAA;AAAA,UACN,WAAA,EACE;AAAA,SACJ;AAAA,QACA,YAAA,EAAc;AAAA,UACZ,IAAA,EAAM,OAAA;AAAA,UACN,WAAA,EAAa;AAAA,SACf;AAAA,QACA,eAAA,EAAiB;AAAA,UACf,IAAA,EAAM,MAAA;AAAA,UACN,WAAA,EAAa;AAAA,SACf;AAAA,QACA,QAAA,EAAU;AAAA,UACR,IAAA,EAAM,OAAA;AAAA,UACN,WAAA,EAAa;AAAA;AACf,OACF;AAAA,MACA,UAAA,EAAY,CAAA,IAAA,KAAQ,IAAA,KAAS,cAAA,IAAkB,IAAA,KAAS;AAAA,KAC1D;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACF;AAEA,EAAA,MAAM,QAAA,GAAW,iBAAiB,IAAI,CAAA;AACtC,EAAA,MAAM,QAAA,GAAW,KAAK,KAAA,IAAS,MAAA;AAG/B,EAAA,MAAM,EAAE,CAAA,EAAG,UAAA,EAAW,GAAI,MAAMC,uBAAM,IAAI,CAAA,CAAE,OAAA,CAAQC,oBAAgB,CAAA,CAAE,IAAA;AAGtE,EAAA,IAAI,CAAC,QAAA,CAAS,IAAA,EAAM,UAAU,CAAA,EAAG;AAE/B,IAAA,IAAA,CAAK,KAAK,UAAA,EAAYC,sBAAA,CAAa,SAAS,CAAA,CAAE,OAAA,CAAQ,gBAAgB,CAAC,CAAA;AAAA,EACzE;AAEA,EAAA,IAAI,CAAC,QAAA,CAAS,mBAAmB,CAAA,EAAG;AAClC,IAAA,IAAA,CAAK,KAAK,mBAAmB,CAAA;AAAA,EAC/B;AAGA,EAAA,IAAI,iBAAA,GAAoB,IAAA,CAAK,QAAA,CAAS,SAAS,CAAA;AAC/C,EAAA,IACE,CAAC,QAAA,IACD,CAAC,OAAA,CAAQ,GAAA,CAAI,EAAA,IACb,CAAC,QAAA,CAAS,YAAA,EAAc,SAAA,EAAW,YAAY,CAAA,EAC/C;AACA,IAAA,MAAM,YAAY,MAChBC,kBAAA,CAAS,CAAC,KAAA,EAAO,WAAA,EAAa,uBAAuB,CAAC,CAAA;AACxD,IAAA,MAAM,eAAA,GAAkB,MAAMA,kBAAA,CAAS,CAAC,MAAM,OAAA,EAAS,GAAA,EAAK,MAAM,CAAC,CAAA;AAEnE,IAAA,IAAK,MAAM,SAAA,EAAU,IAAO,MAAM,iBAAgB,EAAI;AACpD,MAAA,iBAAA,GAAoB,IAAA;AACpB,MAAA,IAAA,CAAK,KAAK,SAAS,CAAA;AAAA,IACrB,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,KAAK,YAAY,CAAA;AAAA,IACxB;AAAA,EACF;AAUA,EAAA,IAAI,iBAAA,IAAqB,UAAA,CAAW,MAAA,GAAS,CAAA,EAAG;AAC9C,IAAA,UAAA,CAAW,iCAAA,GAAoC;AAAA,MAC7C,MAAM,OAAO,cAAA,EAAgB;AAC3B,QAAA,MAAMC,oBAAmB,EAAC;AAC1B,QAAA,MAAM,QAAA,uBAAe,GAAA,EAAI;AAEzB,QAAA,KAAA,MAAW,WAAW,cAAA,EAAgB;AACpC,UAAA,KAAA,MAAW,OAAO,UAAA,EAAY;AAC5B,YAAA,IAAIC,sBAAY,OAAA,CAAQ,OAAA,EAAS,MAAA,CAAO,GAAG,CAAC,CAAA,EAAG;AAC7C,cAAAD,iBAAAA,CAAiB,KAAK,OAAO,CAAA;AAC7B,cAAA,QAAA,CAAS,IAAI,GAAG,CAAA;AAAA,YAClB;AAAA,UACF;AAAA,QACF;AAIA,QAAA,IAAI,QAAA,CAAS,IAAA,KAAS,UAAA,CAAW,MAAA,EAAQ;AACvC,UAAA,OAAO,cAAA;AAAA,QACT;AAEA,QAAA,OAAOA,iBAAAA;AAAA,MACT;AAAA,KACF;AAAA,EACF;AAKA,EAAA,IAAI,CAAC,QAAA,CAAS,yBAAyB,CAAA,EAAG;AACxC,IAAA,IAAA,CAAK,KAAK,+BAA+B,CAAA;AAAA,EAC3C;AAOA,EAAA,IACEE,mBAAA,CAAG,IAAA,EAAK,CAAE,MAAA,IAAU,CAAA,IACpB,CAAC,QAAA,CAAS,IAAA,EAAM,aAAA,EAAe,IAAA,EAAM,cAAc,CAAA,EACnD;AACA,IAAA,IAAA,CAAK,KAAK,gBAAgB,CAAA;AAAA,EAC5B;AAEA,EAAA,IAAI,YAAA;AACJ,EAAA,eAAe,eAAA,GAAkB;AAC/B,IAAA,IAAI,YAAA,EAAc;AAChB,MAAA,OAAO,YAAA;AAAA,IACT;AACA,IAAA,MAAM,QAAA,GAAW,MAAMC,oBAAA,CAAa,kBAAA,EAAmB;AACvD,IAAA,YAAA,GAAeA,oBAAA,CAAa,aAAa,QAAQ,CAAA;AACjD,IAAA,OAAO,YAAA;AAAA,EACT;AAEA,EAAA,IAAI,gBAAA,GAAyC,MAAA;AAC7C,EAAA,IAAI,QAAA,IAAY,CAAC,QAAA,CAAS,kBAAkB,CAAA,EAAG;AAC7C,IAAA,MAAM,KAAA,GAAQ,MAAM,eAAA,EAAgB;AACpC,IAAA,MAAM,eAAA,GAAkB,MAAM,KAAA,CAAM,mBAAA,CAAoB;AAAA,MACtD,GAAA,EAAK,QAAA;AAAA,MACL,eAAA,EAAiB;AAAA,KAClB,CAAA;AAED,IAAA,gBAAA,GAAmB,KAAA,CAAM,IAAA;AAAA,MACvB,KAAA,CAAM,mBAAA;AAAA,QACJ,eAAA,CAAgB,GAAA,CAAI,CAAA,GAAA,KAAO,GAAA,CAAI,IAAI,CAAA;AAAA,QACnC,CAAA,GAAA,KAAO,GAAA,CAAI,kBAAA,CAAmB,IAAA;AAAK;AACrC,KACF;AAEA,IAAA,IAAI,gBAAA,CAAiB,WAAW,CAAA,EAAG;AACjC,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,0BAAA,EAA6B,IAAA,CAAK,KAAK,CAAA,CAAE,CAAA;AACrD,MAAA;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,IAAA,CAAK,kBAAA,EAAoB,GAAG,gBAAgB,CAAA;AAAA,EACnD;AAIA,EAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,QAAA,EAAU;AACzB,IAAC,OAAA,CAAQ,IAAY,QAAA,GAAW,MAAA;AAAA,EAClC;AAKA,EAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,EAAA,EAAI;AACnB,IAAA,OAAA,CAAQ,IAAI,EAAA,GAAK,KAAA;AAAA,EACnB;AAIA,EAAA,IAAI,CAAC,OAAA,CAAQ,GAAA,CAAI,YAAA,EAAc,QAAA,CAAS,iBAAiB,CAAA,EAAG;AAC1D,IAAA,OAAA,CAAQ,GAAA,CAAI,YAAA,GAAe,CAAA,EACzB,OAAA,CAAQ,GAAA,CAAI,YAAA,GAAe,CAAA,EAAG,OAAA,CAAQ,GAAA,CAAI,YAAY,CAAA,CAAA,CAAA,GAAM,EAC9D,CAAA,kBAAA,CAAA;AAAA,EACF;AAEA,EAAA,IAAI,KAAK,QAAA,EAAU;AACjB,IAAA,IAAA,CAAK,KAAK,QAAQ,CAAA;AAAA,EACpB;AAIA,EAAA,IAAI,KAAK,YAAA,EAAc;AAErB,IAAA,IAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACzB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,6EAA6E,UAAA,CAAW,IAAA;AAAA,UACtF;AAAA,SACD,CAAA;AAAA,OACH;AAAA,IACF;AAEA,IAAA,IAAI,IAAA,CAAK,QAAA,CAAS,SAAS,CAAA,EAAG;AAC5B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,iEAAA;AAAA,OACF;AAAA,IACF;AAEA,IAAA,MAAM,KAAA,GAAQC,qBAAa,MAAA,CAAO;AAAA,MAChC,IAAA,EAAM,MAAA;AAAA,MACN,UAAU,IAAA,CAAK;AAAA,KAChB,CAAA;AACD,IAAA,MAAM,KAAA,GAAQ,MAAM,eAAA,EAAgB;AAGpC,IAAA,MAAM,aAAA,uBAAoB,GAAA,EAAoB;AAC9C,IAAA,MAAM,kBAAA,GAAqB,IAAI,KAAA,EAAc;AAI7C,IAAA,UAAA,CAAW,+BAAA,GAAkC;AAAA;AAAA,MAE3C,MAAM,aAAA,CAAc,cAAA,EAAgB,gBAAA,EAAkB;AACpD,QAAA,MAAM,YAAA,GAAe,MAAM,KAAA,CAAM,IAAA,EAAK;AACtC,QAAA,MAAM,QAAA,GAAW,MAAMC,gBAAA,CAAS,IAAA;AAAA,UAC9BZ,qBAAA,CAAY,YAAY,WAAW;AAAA,SACrC;AACA,QAAA,MAAM,kBAAA,GAAqB,MAAM,qBAAA,CAAsB,KAAK,CAAA;AAG5D,QAAA,MAAM,QAAA,GAAWa,uBAAA,CAAO,UAAA,CAAW,MAAM,CAAA;AACzC,QAAA,QAAA,CAAS,OAAO,IAAI,CAAA;AACpB,QAAA,QAAA,CAAS,OAAO,IAAI,CAAA;AACpB,QAAA,QAAA,CAAS,MAAA,CAAO,QAAQ,OAAO,CAAA;AAC/B,QAAA,QAAA,CAAS,OAAO,IAAI,CAAA;AACpB,QAAA,QAAA,CAAS,MAAA;AAAA,UACPF,oBAAA,CAAa,SAAA,CAAU,IAAA,CAAK,SAAA,CAAU,gBAAgB,CAAC;AAAA,SACzD;AACA,QAAA,MAAM,OAAA,GAAU,QAAA,CAAS,MAAA,CAAO,KAAK,CAAA;AAErC,QAAA,OAAO,cAAA,CAAe,OAAO,CAAA,OAAA,KAAW;AACtC,UAAA,MAAM,cAAc,OAAA,CAAQ,WAAA;AAC5B,UAAA,MAAM,GAAA,GAAM,KAAA,CAAM,GAAA,CAAI,WAAW,CAAA;AACjC,UAAA,IAAI,CAAC,GAAA,EAAK;AACR,YAAA,MAAM,IAAI,KAAA;AAAA,cACR,WAAW,WAAW,CAAA,2BAAA;AAAA,aACxB;AAAA,UACF;AAEA,UAAA,MAAM,IAAA,GAAOE,uBAAA,CAAO,UAAA,CAAW,MAAM,CAAA;AAErC,UAAA,IAAA,CAAK,OAAO,OAAO,CAAA;AAEnB,UAAA,MAAM,cAAA,GAAiB,mBAAmB,WAAW,CAAA;AACrD,UAAA,IAAA,CAAK,OAAO,cAAc,CAAA;AAE1B,UAAA,KAAA,MAAW,CAAC,OAAA,EAAS,MAAM,CAAA,IAAK,IAAI,oBAAA,EAAsB;AACxD,YAAA,MAAM,OAAA,GAAU,kBAAA,CAAmB,MAAA,CAAO,IAAI,CAAA;AAC9C,YAAA,IAAA,CAAK,MAAA,CAAO,CAAA,EAAG,OAAO,CAAA,CAAA,EAAI,OAAO,CAAA,CAAE,CAAA;AAAA,UACrC;AAIA,UAAA,IAAA,CAAK,OAAOF,oBAAA,CAAa,SAAA,CAAU,KAAK,SAAA,CAAU,OAAO,CAAC,CAAC,CAAA;AAC3D,UAAA,IAAA,CAAK,MAAA,CAAO,QAAA,CAAS,qBAAA,CAAsB,WAAW,CAAC,CAAA;AAEvD,UAAA,MAAM,GAAA,GAAM,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA;AAE7B,UAAA,aAAA,CAAc,GAAA,CAAI,aAAa,GAAG,CAAA;AAElC,UAAA,IAAI,YAAA,CAAa,GAAA,CAAI,GAAG,CAAA,EAAG;AACzB,YAAA,IAAI,CAAC,gBAAA,IAAoB,gBAAA,CAAiB,QAAA,CAAS,WAAW,CAAA,EAAG;AAC/D,cAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,QAAA,EAAW,WAAW,CAAA,iBAAA,CAAmB,CAAA;AAAA,YACvD;AACA,YAAA,kBAAA,CAAmB,KAAK,GAAG,CAAA;AAC3B,YAAA,OAAO,MAAA;AAAA,UACT;AAEA,UAAA,OAAO,OAAA;AAAA,QACT,CAAC,CAAA;AAAA,MACH,CAAA;AAAA;AAAA,MAEA,MAAM,cAAc,OAAA,EAAS;AAC3B,QAAA,MAAM,UAAA,uBAAiB,GAAA,EAAY;AACnC,QAAA,MAAM,MAAA,uBAAa,GAAA,EAAY;AAE/B,QAAA,KAAA,MAAW,UAAA,IAAc,QAAQ,WAAA,EAAa;AAC5C,UAAA,KAAA,MAAW,CAAC,OAAA,EAAS,GAAG,CAAA,IAAK,KAAA,EAAO;AAClC,YAAA,IAAIH,qBAAA,CAAY,GAAA,CAAI,GAAA,EAAK,UAAA,CAAW,YAAY,CAAA,EAAG;AACjD,cAAA,IACE,WAAW,aAAA,IACX,UAAA,CAAW,cAAA,IACX,UAAA,CAAW,kBAAkB,CAAA,EAC7B;AACA,gBAAA,MAAA,CAAO,IAAI,OAAO,CAAA;AAClB,gBAAA,UAAA,CAAW,OAAO,OAAO,CAAA;AAAA,cAC3B,CAAA,MAAA,IAAW,CAAC,MAAA,CAAO,GAAA,CAAI,OAAO,CAAA,EAAG;AAC/B,gBAAA,UAAA,CAAW,IAAI,OAAO,CAAA;AAAA,cACxB;AACA,cAAA;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,QAAA,KAAA,MAAW,WAAW,UAAA,EAAY;AAChC,UAAA,MAAM,GAAA,GAAM,aAAA,CAAc,GAAA,CAAI,OAAO,CAAA;AACrC,UAAA,IAAI,GAAA,EAAK;AACP,YAAA,kBAAA,CAAmB,KAAK,GAAG,CAAA;AAAA,UAC7B;AAAA,QACF;AAEA,QAAA,MAAM,KAAA,CAAM,MAAM,kBAAkB,CAAA;AAAA,MACtC;AAAA,KACF;AAAA,EACF;AAEA,EAAA,MAAMM,YAAQ,IAAI,CAAA;AACpB,CAAA;;;;;"}
|