@isentinel/jest-roblox 0.3.0 → 0.3.1
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/README.md +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +36 -0
- package/dist/index.mjs +1 -1
- package/dist/{run-BEUPi80L.mjs → run-Cl5gYSQr.mjs} +339 -228
- package/dist/sea-entry.cjs +343 -227
- package/package.json +4 -4
|
@@ -6,7 +6,7 @@ import color from "tinyrainbow";
|
|
|
6
6
|
import { createDefineConfig, loadConfig } from "c12";
|
|
7
7
|
import { defuFn } from "defu";
|
|
8
8
|
import * as fs$1 from "node:fs";
|
|
9
|
-
import fs, { existsSync, readFileSync
|
|
9
|
+
import fs, { existsSync, readFileSync } from "node:fs";
|
|
10
10
|
import * as path$1 from "node:path";
|
|
11
11
|
import path, { dirname, join, relative, resolve } from "node:path";
|
|
12
12
|
import { type } from "arktype";
|
|
@@ -20,6 +20,7 @@ import picomatch from "picomatch";
|
|
|
20
20
|
import { getTsconfig } from "get-tsconfig";
|
|
21
21
|
import hljs from "highlight.js/lib/core";
|
|
22
22
|
import typescript from "highlight.js/lib/languages/typescript";
|
|
23
|
+
import { performance } from "node:perf_hooks";
|
|
23
24
|
import { LuauExecutionClient } from "@bedrock-rbx/ocale/luau-execution";
|
|
24
25
|
import { PlacesClient } from "@bedrock-rbx/ocale/places";
|
|
25
26
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -29,11 +30,10 @@ import * as cp from "node:child_process";
|
|
|
29
30
|
import { execFileSync } from "node:child_process";
|
|
30
31
|
import * as os from "node:os";
|
|
31
32
|
import { Buffer } from "node:buffer";
|
|
32
|
-
import { performance } from "node:perf_hooks";
|
|
33
33
|
import { parseJSONC, parseYAML } from "confbox";
|
|
34
34
|
import { Visitor, parseSync } from "oxc-parser";
|
|
35
35
|
//#region package.json
|
|
36
|
-
var version = "0.3.
|
|
36
|
+
var version = "0.3.1";
|
|
37
37
|
//#endregion
|
|
38
38
|
//#region src/config/errors.ts
|
|
39
39
|
var ConfigError = class extends Error {
|
|
@@ -1449,6 +1449,7 @@ function convertToLuau(filePath) {
|
|
|
1449
1449
|
const RbxPathParent = Symbol("Parent");
|
|
1450
1450
|
var RojoResolver = class RojoResolver {
|
|
1451
1451
|
rbxPath = new Array();
|
|
1452
|
+
realpathCache = /* @__PURE__ */ new Map();
|
|
1452
1453
|
walkedConfigFilesInternal = /* @__PURE__ */ new Set();
|
|
1453
1454
|
walkedDirectoriesInternal = /* @__PURE__ */ new Set();
|
|
1454
1455
|
filePathToRbxPathMap = /* @__PURE__ */ new Map();
|
|
@@ -1459,12 +1460,12 @@ var RojoResolver = class RojoResolver {
|
|
|
1459
1460
|
static findRojoConfigFilePath(projectPath) {
|
|
1460
1461
|
const warnings = new Array();
|
|
1461
1462
|
const defaultPath = path.join(projectPath, ROJO_DEFAULT_NAME);
|
|
1462
|
-
if (existsSync(defaultPath)) return {
|
|
1463
|
+
if (fs$1.existsSync(defaultPath)) return {
|
|
1463
1464
|
path: defaultPath,
|
|
1464
1465
|
warnings
|
|
1465
1466
|
};
|
|
1466
1467
|
const candidates = new Array();
|
|
1467
|
-
for (const fileName of readdirSync(projectPath)) if (fileName !== ROJO_DEFAULT_NAME && (fileName === ROJO_OLD_NAME || ROJO_FILE_REGEX.test(fileName))) candidates.push(path.join(projectPath, fileName));
|
|
1468
|
+
for (const fileName of fs$1.readdirSync(projectPath)) if (fileName !== ROJO_DEFAULT_NAME && (fileName === ROJO_OLD_NAME || ROJO_FILE_REGEX.test(fileName))) candidates.push(path.join(projectPath, fileName));
|
|
1468
1469
|
if (candidates.length > 1) warnings.push(`Multiple *.project.json files found, using ${candidates[0]}`);
|
|
1469
1470
|
return {
|
|
1470
1471
|
path: candidates[0],
|
|
@@ -1600,34 +1601,42 @@ var RojoResolver = class RojoResolver {
|
|
|
1600
1601
|
get walkedDirectories() {
|
|
1601
1602
|
return this.walkedDirectoriesInternal;
|
|
1602
1603
|
}
|
|
1604
|
+
cachedRealpath(targetPath) {
|
|
1605
|
+
let resolved = this.realpathCache.get(targetPath);
|
|
1606
|
+
if (resolved === void 0) {
|
|
1607
|
+
resolved = fs$1.realpathSync(targetPath);
|
|
1608
|
+
this.realpathCache.set(targetPath, resolved);
|
|
1609
|
+
}
|
|
1610
|
+
return resolved;
|
|
1611
|
+
}
|
|
1603
1612
|
getContainer(from, rbxPath) {
|
|
1604
1613
|
if (this.isGame && rbxPath) {
|
|
1605
1614
|
for (const container of from) if (arrayStartsWith(rbxPath, container)) return container;
|
|
1606
1615
|
}
|
|
1607
1616
|
}
|
|
1608
1617
|
parseConfig(rojoConfigFilePath, doNotPush = false) {
|
|
1609
|
-
if (!existsSync(rojoConfigFilePath)) {
|
|
1618
|
+
if (!fs$1.existsSync(rojoConfigFilePath)) {
|
|
1610
1619
|
this.warn(`RojoResolver: Path does not exist "${rojoConfigFilePath}"`);
|
|
1611
1620
|
return;
|
|
1612
1621
|
}
|
|
1613
|
-
const realPath =
|
|
1622
|
+
const realPath = this.cachedRealpath(rojoConfigFilePath);
|
|
1614
1623
|
this.walkedConfigFilesInternal.add(realPath);
|
|
1615
1624
|
let configJson;
|
|
1616
1625
|
try {
|
|
1617
|
-
configJson = JSON.parse(readFileSync(realPath, "utf8"));
|
|
1626
|
+
configJson = JSON.parse(fs$1.readFileSync(realPath, "utf8"));
|
|
1618
1627
|
} catch {}
|
|
1619
1628
|
if (isValidRojoConfig(configJson)) this.parseTree(path.dirname(rojoConfigFilePath), configJson.name, configJson.tree, doNotPush);
|
|
1620
1629
|
else this.warn("RojoResolver: Invalid configuration!");
|
|
1621
1630
|
}
|
|
1622
1631
|
parsePath(itemPath) {
|
|
1623
1632
|
const luauPath = convertToLuau(itemPath);
|
|
1624
|
-
const realPath = existsSync(luauPath) ?
|
|
1633
|
+
const realPath = fs$1.existsSync(luauPath) ? this.cachedRealpath(luauPath) : luauPath;
|
|
1625
1634
|
const extension = path.extname(luauPath);
|
|
1626
1635
|
if (ROJO_MODULE_EXTS.has(extension)) this.filePathToRbxPathMap.set(luauPath, [...this.rbxPath]);
|
|
1627
1636
|
else {
|
|
1628
|
-
const isDirectory = existsSync(realPath) && statSync(realPath).isDirectory();
|
|
1637
|
+
const isDirectory = fs$1.existsSync(realPath) && fs$1.statSync(realPath).isDirectory();
|
|
1629
1638
|
if (isDirectory) this.walkedDirectoriesInternal.add(realPath);
|
|
1630
|
-
if (isDirectory && readdirSync(realPath).includes(ROJO_DEFAULT_NAME)) this.parseConfig(path.join(luauPath, ROJO_DEFAULT_NAME), true);
|
|
1639
|
+
if (isDirectory && fs$1.readdirSync(realPath).includes(ROJO_DEFAULT_NAME)) this.parseConfig(path.join(luauPath, ROJO_DEFAULT_NAME), true);
|
|
1631
1640
|
else {
|
|
1632
1641
|
this.partitions.unshift({
|
|
1633
1642
|
fsPath: luauPath,
|
|
@@ -1644,26 +1653,40 @@ var RojoResolver = class RojoResolver {
|
|
|
1644
1653
|
for (const childName of Object.keys(tree).filter((value) => !value.startsWith("$"))) this.parseTree(basePath, childName, tree[childName]);
|
|
1645
1654
|
if (!doNotPush) this.rbxPath.pop();
|
|
1646
1655
|
}
|
|
1647
|
-
searchChildren(directory,
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
if (
|
|
1656
|
+
searchChildren(directory, directoryEntries) {
|
|
1657
|
+
const projectFiles = new Array();
|
|
1658
|
+
const subDirectories = new Array();
|
|
1659
|
+
for (const entry of directoryEntries) {
|
|
1660
|
+
const childPath = path.join(directory, entry.name);
|
|
1661
|
+
let isFile = entry.isFile();
|
|
1662
|
+
let isDirectory = entry.isDirectory();
|
|
1663
|
+
if (!isFile && !isDirectory) try {
|
|
1664
|
+
const stat = fs$1.statSync(this.cachedRealpath(childPath));
|
|
1665
|
+
isFile = stat.isFile();
|
|
1666
|
+
isDirectory = stat.isDirectory();
|
|
1667
|
+
} catch (err) {
|
|
1668
|
+
this.warn(`RojoResolver: Failed to resolve "${childPath}" (${err.message})`);
|
|
1669
|
+
continue;
|
|
1670
|
+
}
|
|
1671
|
+
if (isFile && ROJO_FILE_REGEX.test(entry.name)) projectFiles.push(childPath);
|
|
1672
|
+
else if (isDirectory) subDirectories.push({
|
|
1673
|
+
name: entry.name,
|
|
1674
|
+
path: childPath
|
|
1675
|
+
});
|
|
1655
1676
|
}
|
|
1677
|
+
for (const childPath of projectFiles) this.parseConfig(childPath);
|
|
1678
|
+
for (const { name, path: childPath } of subDirectories) this.searchDirectory(childPath, name);
|
|
1656
1679
|
}
|
|
1657
1680
|
searchDirectory(directory, item) {
|
|
1658
|
-
const realPath =
|
|
1681
|
+
const realPath = this.cachedRealpath(directory);
|
|
1659
1682
|
this.walkedDirectoriesInternal.add(realPath);
|
|
1660
|
-
const
|
|
1661
|
-
if (
|
|
1683
|
+
const directoryEntries = fs$1.readdirSync(directory, { withFileTypes: true });
|
|
1684
|
+
if (directoryEntries.some((entry) => entry.name === ROJO_DEFAULT_NAME)) {
|
|
1662
1685
|
this.parseConfig(path.join(directory, ROJO_DEFAULT_NAME));
|
|
1663
1686
|
return;
|
|
1664
1687
|
}
|
|
1665
1688
|
if (item !== void 0) this.rbxPath.push(item);
|
|
1666
|
-
this.searchChildren(directory,
|
|
1689
|
+
this.searchChildren(directory, directoryEntries);
|
|
1667
1690
|
if (item !== void 0) this.rbxPath.pop();
|
|
1668
1691
|
}
|
|
1669
1692
|
warn(str) {
|
|
@@ -3687,7 +3710,7 @@ function createSnapshotPathResolver(config) {
|
|
|
3687
3710
|
const result = `${basePath}/${normalized.slice(prefix.length + 1)}`;
|
|
3688
3711
|
const mapping = findMapping(result, tsconfigMappings);
|
|
3689
3712
|
if (mapping !== void 0) return {
|
|
3690
|
-
filePath: replacePrefix(result, mapping.outDir, mapping.rootDir)
|
|
3713
|
+
filePath: replacePrefix(result, mapping.outDir, mapping.rootDir),
|
|
3691
3714
|
mapping
|
|
3692
3715
|
};
|
|
3693
3716
|
return { filePath: result };
|
|
@@ -3707,6 +3730,96 @@ function buildMappings(tree, prefix) {
|
|
|
3707
3730
|
return mappings;
|
|
3708
3731
|
}
|
|
3709
3732
|
//#endregion
|
|
3733
|
+
//#region src/timing/orchestration-collector.ts
|
|
3734
|
+
/**
|
|
3735
|
+
* A buffered span-tree profiler for a single, sequential host run. Nesting is
|
|
3736
|
+
* tracked with one shared stack, so spans must open and close in LIFO order:
|
|
3737
|
+
* profile a phase, and any spans it opens nest under it. It is NOT safe to run
|
|
3738
|
+
* two `profile` / `profileAsync` calls concurrently on the same collector (e.g.
|
|
3739
|
+
* `Promise.all`) — interleaved opens/closes would corrupt the stack. Create one
|
|
3740
|
+
* collector per run; `flushTimingReport` empties it so a second flush is a
|
|
3741
|
+
* no-op.
|
|
3742
|
+
*/
|
|
3743
|
+
function createTimingCollector(options = {}) {
|
|
3744
|
+
const clock = options.clock ?? { now: () => performance.now() };
|
|
3745
|
+
const sink = options.sink ?? ((line) => void process.stderr.write(`${line}\n`));
|
|
3746
|
+
const enabled = options.enabled ?? process.env["TIMING"] !== void 0;
|
|
3747
|
+
const roots = /* @__PURE__ */ new Map();
|
|
3748
|
+
const stack = [];
|
|
3749
|
+
function open(name) {
|
|
3750
|
+
const top = stack.at(-1);
|
|
3751
|
+
const node = childOf(top === void 0 ? roots : top.children, name);
|
|
3752
|
+
stack.push(node);
|
|
3753
|
+
const start = clock.now();
|
|
3754
|
+
return () => {
|
|
3755
|
+
node.elapsedMs += clock.now() - start;
|
|
3756
|
+
stack.pop();
|
|
3757
|
+
};
|
|
3758
|
+
}
|
|
3759
|
+
function profile(name, func) {
|
|
3760
|
+
if (!enabled) return func();
|
|
3761
|
+
const close = open(name);
|
|
3762
|
+
try {
|
|
3763
|
+
return func();
|
|
3764
|
+
} finally {
|
|
3765
|
+
close();
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
async function profileAsync(name, func) {
|
|
3769
|
+
if (!enabled) return func();
|
|
3770
|
+
const close = open(name);
|
|
3771
|
+
try {
|
|
3772
|
+
return await func();
|
|
3773
|
+
} finally {
|
|
3774
|
+
close();
|
|
3775
|
+
}
|
|
3776
|
+
}
|
|
3777
|
+
function record(name, elapsedMs) {
|
|
3778
|
+
if (!enabled) return;
|
|
3779
|
+
const top = stack.at(-1);
|
|
3780
|
+
const node = childOf(top === void 0 ? roots : top.children, name);
|
|
3781
|
+
node.elapsedMs += elapsedMs;
|
|
3782
|
+
}
|
|
3783
|
+
function emit(node, depth) {
|
|
3784
|
+
sink(`[TIMING] ${" ".repeat(depth)}${node.name}: ${String(Math.round(node.elapsedMs))}ms`);
|
|
3785
|
+
for (const child of node.children.values()) emit(child, depth + 1);
|
|
3786
|
+
}
|
|
3787
|
+
function flushTimingReport() {
|
|
3788
|
+
if (!enabled || roots.size === 0) return;
|
|
3789
|
+
let total = 0;
|
|
3790
|
+
for (const node of roots.values()) {
|
|
3791
|
+
emit(node, 0);
|
|
3792
|
+
total += Math.round(node.elapsedMs);
|
|
3793
|
+
}
|
|
3794
|
+
sink(`[TIMING] TOTAL (host): ${String(total)}ms`);
|
|
3795
|
+
roots.clear();
|
|
3796
|
+
}
|
|
3797
|
+
return {
|
|
3798
|
+
flushTimingReport,
|
|
3799
|
+
profile,
|
|
3800
|
+
profileAsync,
|
|
3801
|
+
record
|
|
3802
|
+
};
|
|
3803
|
+
}
|
|
3804
|
+
/**
|
|
3805
|
+
* Shared disabled collector for callers that thread a profiler through their
|
|
3806
|
+
* signatures but are invoked outside a profiled workspace run (single-mode
|
|
3807
|
+
* coverage, the `instrument` subcommand, tests). Every method is a no-op.
|
|
3808
|
+
*/
|
|
3809
|
+
const NOOP_TIMING_COLLECTOR = createTimingCollector({ enabled: false });
|
|
3810
|
+
function childOf(parent, name) {
|
|
3811
|
+
let node = parent.get(name);
|
|
3812
|
+
if (node === void 0) {
|
|
3813
|
+
node = {
|
|
3814
|
+
name,
|
|
3815
|
+
children: /* @__PURE__ */ new Map(),
|
|
3816
|
+
elapsedMs: 0
|
|
3817
|
+
};
|
|
3818
|
+
parent.set(name, node);
|
|
3819
|
+
}
|
|
3820
|
+
return node;
|
|
3821
|
+
}
|
|
3822
|
+
//#endregion
|
|
3710
3823
|
//#region src/types/rojo.ts
|
|
3711
3824
|
const rojoProjectSchema = type({
|
|
3712
3825
|
"name": "string",
|
|
@@ -3856,38 +3969,48 @@ function formatExecuteOutput(options) {
|
|
|
3856
3969
|
* config.
|
|
3857
3970
|
*/
|
|
3858
3971
|
async function runProjects(options) {
|
|
3859
|
-
const
|
|
3860
|
-
const
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3972
|
+
const timing = options.timing ?? NOOP_TIMING_COLLECTOR;
|
|
3973
|
+
const jobs = timing.profile("buildJobs", () => {
|
|
3974
|
+
return options.projects.map((project) => buildProjectJob(project, timing));
|
|
3975
|
+
});
|
|
3976
|
+
const { rawResults, timing: backendTiming } = await timing.profileAsync("backend.runTests", async () => {
|
|
3977
|
+
const result = await options.backend.runTests({
|
|
3978
|
+
jobs,
|
|
3979
|
+
parallel: options.parallel,
|
|
3980
|
+
scriptOverride: options.scriptOverride,
|
|
3981
|
+
streaming: options.streaming,
|
|
3982
|
+
workStealing: options.workStealing
|
|
3983
|
+
});
|
|
3984
|
+
recordBackendTimingSpans(timing, result.timing);
|
|
3985
|
+
return result;
|
|
3866
3986
|
});
|
|
3867
3987
|
if (rawResults.length !== jobs.length) throw new Error(`Backend returned ${rawResults.length.toString()} results for ${jobs.length.toString()} jobs — rawResults must be parallel to jobs`);
|
|
3868
3988
|
return {
|
|
3869
3989
|
backendTiming,
|
|
3870
|
-
results:
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
|
|
3884
|
-
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3990
|
+
results: timing.profile("processResults", () => {
|
|
3991
|
+
return rawResults.map((raw, index) => {
|
|
3992
|
+
const job = jobs[index];
|
|
3993
|
+
try {
|
|
3994
|
+
return processProjectResult(buildProjectResult(raw.entry, job, raw.fallbackGameOutput), {
|
|
3995
|
+
backendTiming,
|
|
3996
|
+
config: job.config,
|
|
3997
|
+
deferFormatting: options.deferFormatting,
|
|
3998
|
+
startTime: options.startTime,
|
|
3999
|
+
timing,
|
|
4000
|
+
version: options.version
|
|
4001
|
+
});
|
|
4002
|
+
} catch (err) {
|
|
4003
|
+
if (!(err instanceof LuauScriptError)) throw err;
|
|
4004
|
+
return buildExecutionErrorResult({
|
|
4005
|
+
backendTiming,
|
|
4006
|
+
config: job.config,
|
|
4007
|
+
deferFormatting: options.deferFormatting,
|
|
4008
|
+
error: err,
|
|
4009
|
+
startTime: options.startTime,
|
|
4010
|
+
version: options.version
|
|
4011
|
+
});
|
|
4012
|
+
}
|
|
4013
|
+
});
|
|
3891
4014
|
})
|
|
3892
4015
|
};
|
|
3893
4016
|
}
|
|
@@ -3928,6 +4051,10 @@ function parseTsconfigMappings(options) {
|
|
|
3928
4051
|
rootDir: normalizeDirectoryPath(options.rootDir ?? "src")
|
|
3929
4052
|
}];
|
|
3930
4053
|
}
|
|
4054
|
+
function recordBackendTimingSpans(timing, backendTiming) {
|
|
4055
|
+
if (backendTiming.uploadMs !== void 0) timing.record("uploadMs", backendTiming.uploadMs);
|
|
4056
|
+
timing.record("executionMs", backendTiming.executionMs);
|
|
4057
|
+
}
|
|
3931
4058
|
const EXIT_CODE_MESSAGE = /^Exited with code: \d+$/;
|
|
3932
4059
|
/**
|
|
3933
4060
|
* Compose the human-readable failure message for an exec-error file
|
|
@@ -4128,7 +4255,7 @@ function writeSnapshots(snapshotWrites, config, tsconfigMappings) {
|
|
|
4128
4255
|
fs$1.writeFileSync(absolutePath, content);
|
|
4129
4256
|
const { filePath, mapping } = resolved;
|
|
4130
4257
|
if (mapping !== void 0) {
|
|
4131
|
-
const outPath = mapping.
|
|
4258
|
+
const outPath = replacePrefix(filePath, mapping.rootDir, mapping.outDir);
|
|
4132
4259
|
const absoluteOutPath = path$1.resolve(config.rootDir, outPath);
|
|
4133
4260
|
fs$1.mkdirSync(path$1.dirname(absoluteOutPath), { recursive: true });
|
|
4134
4261
|
fs$1.writeFileSync(absoluteOutPath, content);
|
|
@@ -4157,19 +4284,23 @@ function writeSnapshots(snapshotWrites, config, tsconfigMappings) {
|
|
|
4157
4284
|
* formatter output. Called once per job.
|
|
4158
4285
|
*/
|
|
4159
4286
|
function processProjectResult(entry, options) {
|
|
4160
|
-
const { backendTiming, config, deferFormatting, startTime, version } = options;
|
|
4287
|
+
const { backendTiming, config, deferFormatting, startTime, timing, version } = options;
|
|
4161
4288
|
const { coverageData, gameOutput, luauTiming, result, setupMs, snapshotWrites } = entry;
|
|
4162
|
-
const tsconfigMappings =
|
|
4163
|
-
|
|
4289
|
+
const tsconfigMappings = timing.profile("resolveTsconfigMappings", () => {
|
|
4290
|
+
return resolveAllTsconfigMappings(config.rootDir);
|
|
4291
|
+
});
|
|
4292
|
+
const writeCounts = snapshotWrites !== void 0 ? timing.profile("writeSnapshots", () => {
|
|
4293
|
+
return writeSnapshots(snapshotWrites, config, tsconfigMappings);
|
|
4294
|
+
}) : {
|
|
4164
4295
|
attempted: 0,
|
|
4165
4296
|
failed: 0,
|
|
4166
4297
|
written: 0
|
|
4167
4298
|
};
|
|
4168
4299
|
const testsMs = calculateTestsMs(result.testResults);
|
|
4169
|
-
const sourceMapper = config.sourceMap ? buildSourceMapper(config, tsconfigMappings) : void 0;
|
|
4300
|
+
const sourceMapper = config.sourceMap ? timing.profile("buildSourceMapper", () => buildSourceMapper(config, tsconfigMappings)) : void 0;
|
|
4170
4301
|
resolveTestFilePaths(result, sourceMapper);
|
|
4171
4302
|
const totalMs = Date.now() - startTime;
|
|
4172
|
-
const
|
|
4303
|
+
const resultTiming = {
|
|
4173
4304
|
executionMs: backendTiming.executionMs,
|
|
4174
4305
|
setupMs,
|
|
4175
4306
|
startTime,
|
|
@@ -4182,7 +4313,7 @@ function processProjectResult(entry, options) {
|
|
|
4182
4313
|
result,
|
|
4183
4314
|
snapshotWriteFailures: writeCounts.failed,
|
|
4184
4315
|
sourceMapper,
|
|
4185
|
-
timing,
|
|
4316
|
+
timing: resultTiming,
|
|
4186
4317
|
version
|
|
4187
4318
|
}) : "";
|
|
4188
4319
|
if (luauTiming !== void 0) printLuauTiming(luauTiming);
|
|
@@ -4194,7 +4325,7 @@ function processProjectResult(entry, options) {
|
|
|
4194
4325
|
result,
|
|
4195
4326
|
snapshotWriteFailures: writeCounts.failed > 0 ? writeCounts.failed : void 0,
|
|
4196
4327
|
sourceMapper,
|
|
4197
|
-
timing
|
|
4328
|
+
timing: resultTiming
|
|
4198
4329
|
};
|
|
4199
4330
|
}
|
|
4200
4331
|
/**
|
|
@@ -4202,8 +4333,10 @@ function processProjectResult(entry, options) {
|
|
|
4202
4333
|
* carries its own config so the Luau runner never re-resolves or shares format
|
|
4203
4334
|
* state across projects (fixes the spike's snapshot-diff regression — C1).
|
|
4204
4335
|
*/
|
|
4205
|
-
function buildProjectJob(parameters) {
|
|
4206
|
-
const tsconfigMappings =
|
|
4336
|
+
function buildProjectJob(parameters, timing) {
|
|
4337
|
+
const tsconfigMappings = timing.profile("resolveTsconfigMappings", () => {
|
|
4338
|
+
return resolveAllTsconfigMappings(parameters.config.rootDir);
|
|
4339
|
+
});
|
|
4207
4340
|
const luauProject = isLuauProject(parameters.testFiles, tsconfigMappings);
|
|
4208
4341
|
return {
|
|
4209
4342
|
config: applySnapshotFormatDefaults(parameters.config, luauProject),
|
|
@@ -6347,89 +6480,6 @@ function buildWithRojo(projectPath, outputPath) {
|
|
|
6347
6480
|
}
|
|
6348
6481
|
}
|
|
6349
6482
|
//#endregion
|
|
6350
|
-
//#region src/timing/orchestration-collector.ts
|
|
6351
|
-
/**
|
|
6352
|
-
* A buffered span-tree profiler for a single, sequential host run. Nesting is
|
|
6353
|
-
* tracked with one shared stack, so spans must open and close in LIFO order:
|
|
6354
|
-
* profile a phase, and any spans it opens nest under it. It is NOT safe to run
|
|
6355
|
-
* two `profile` / `profileAsync` calls concurrently on the same collector (e.g.
|
|
6356
|
-
* `Promise.all`) — interleaved opens/closes would corrupt the stack. Create one
|
|
6357
|
-
* collector per run; `flushTimingReport` empties it so a second flush is a
|
|
6358
|
-
* no-op.
|
|
6359
|
-
*/
|
|
6360
|
-
function createTimingCollector(options = {}) {
|
|
6361
|
-
const clock = options.clock ?? { now: () => performance.now() };
|
|
6362
|
-
const sink = options.sink ?? ((line) => void process.stderr.write(`${line}\n`));
|
|
6363
|
-
const enabled = options.enabled ?? process.env["TIMING"] !== void 0;
|
|
6364
|
-
const roots = /* @__PURE__ */ new Map();
|
|
6365
|
-
const stack = [];
|
|
6366
|
-
function open(name) {
|
|
6367
|
-
const top = stack.at(-1);
|
|
6368
|
-
const node = childOf(top === void 0 ? roots : top.children, name);
|
|
6369
|
-
stack.push(node);
|
|
6370
|
-
const start = clock.now();
|
|
6371
|
-
return () => {
|
|
6372
|
-
node.elapsedMs += clock.now() - start;
|
|
6373
|
-
stack.pop();
|
|
6374
|
-
};
|
|
6375
|
-
}
|
|
6376
|
-
function profile(name, func) {
|
|
6377
|
-
if (!enabled) return func();
|
|
6378
|
-
const close = open(name);
|
|
6379
|
-
try {
|
|
6380
|
-
return func();
|
|
6381
|
-
} finally {
|
|
6382
|
-
close();
|
|
6383
|
-
}
|
|
6384
|
-
}
|
|
6385
|
-
async function profileAsync(name, func) {
|
|
6386
|
-
if (!enabled) return func();
|
|
6387
|
-
const close = open(name);
|
|
6388
|
-
try {
|
|
6389
|
-
return await func();
|
|
6390
|
-
} finally {
|
|
6391
|
-
close();
|
|
6392
|
-
}
|
|
6393
|
-
}
|
|
6394
|
-
function emit(node, depth) {
|
|
6395
|
-
sink(`[TIMING] ${" ".repeat(depth)}${node.name}: ${String(Math.round(node.elapsedMs))}ms`);
|
|
6396
|
-
for (const child of node.children.values()) emit(child, depth + 1);
|
|
6397
|
-
}
|
|
6398
|
-
function flushTimingReport() {
|
|
6399
|
-
if (!enabled || roots.size === 0) return;
|
|
6400
|
-
let total = 0;
|
|
6401
|
-
for (const node of roots.values()) {
|
|
6402
|
-
emit(node, 0);
|
|
6403
|
-
total += Math.round(node.elapsedMs);
|
|
6404
|
-
}
|
|
6405
|
-
sink(`[TIMING] TOTAL (host): ${String(total)}ms`);
|
|
6406
|
-
roots.clear();
|
|
6407
|
-
}
|
|
6408
|
-
return {
|
|
6409
|
-
flushTimingReport,
|
|
6410
|
-
profile,
|
|
6411
|
-
profileAsync
|
|
6412
|
-
};
|
|
6413
|
-
}
|
|
6414
|
-
/**
|
|
6415
|
-
* Shared disabled collector for callers that thread a profiler through their
|
|
6416
|
-
* signatures but are invoked outside a profiled workspace run (single-mode
|
|
6417
|
-
* coverage, the `instrument` subcommand, tests). Every method is a no-op.
|
|
6418
|
-
*/
|
|
6419
|
-
const NOOP_TIMING_COLLECTOR = createTimingCollector({ enabled: false });
|
|
6420
|
-
function childOf(parent, name) {
|
|
6421
|
-
let node = parent.get(name);
|
|
6422
|
-
if (node === void 0) {
|
|
6423
|
-
node = {
|
|
6424
|
-
name,
|
|
6425
|
-
children: /* @__PURE__ */ new Map(),
|
|
6426
|
-
elapsedMs: 0
|
|
6427
|
-
};
|
|
6428
|
-
parent.set(name, node);
|
|
6429
|
-
}
|
|
6430
|
-
return node;
|
|
6431
|
-
}
|
|
6432
|
-
//#endregion
|
|
6433
6483
|
//#region src/utils/hash.ts
|
|
6434
6484
|
function hashBuffer(data) {
|
|
6435
6485
|
return createHash("sha256").update(data).digest("hex");
|
|
@@ -7900,15 +7950,26 @@ function classifyTestFiles(files, config) {
|
|
|
7900
7950
|
typeTestFiles
|
|
7901
7951
|
};
|
|
7902
7952
|
}
|
|
7953
|
+
function resolveAllSetupFilePaths(configs) {
|
|
7954
|
+
const resolvers = /* @__PURE__ */ new Map();
|
|
7955
|
+
for (const config of configs) {
|
|
7956
|
+
if (config.setupFiles === void 0 && config.setupFilesAfterEnv === void 0) continue;
|
|
7957
|
+
const rojoConfigPath = path$1.resolve(config.rootDir, config.rojoProject ?? DEFAULT_ROJO_PROJECT$1);
|
|
7958
|
+
const key = JSON.stringify([config.rootDir, rojoConfigPath]);
|
|
7959
|
+
let resolve = resolvers.get(key);
|
|
7960
|
+
if (resolve === void 0) {
|
|
7961
|
+
resolve = createSetupResolver({
|
|
7962
|
+
configDirectory: config.rootDir,
|
|
7963
|
+
rojoConfigPath
|
|
7964
|
+
});
|
|
7965
|
+
resolvers.set(key, resolve);
|
|
7966
|
+
}
|
|
7967
|
+
if (config.setupFiles !== void 0) config.setupFiles = config.setupFiles.map(resolve);
|
|
7968
|
+
if (config.setupFilesAfterEnv !== void 0) config.setupFilesAfterEnv = config.setupFilesAfterEnv.map(resolve);
|
|
7969
|
+
}
|
|
7970
|
+
}
|
|
7903
7971
|
function resolveSetupFilePaths(config) {
|
|
7904
|
-
|
|
7905
|
-
const rojoConfigPath = path$1.resolve(config.rootDir, config.rojoProject ?? DEFAULT_ROJO_PROJECT$1);
|
|
7906
|
-
const resolve = createSetupResolver({
|
|
7907
|
-
configDirectory: config.rootDir,
|
|
7908
|
-
rojoConfigPath
|
|
7909
|
-
});
|
|
7910
|
-
if (config.setupFiles !== void 0) config.setupFiles = config.setupFiles.map(resolve);
|
|
7911
|
-
if (config.setupFilesAfterEnv !== void 0) config.setupFilesAfterEnv = config.setupFilesAfterEnv.map(resolve);
|
|
7972
|
+
resolveAllSetupFilePaths([config]);
|
|
7912
7973
|
}
|
|
7913
7974
|
//#endregion
|
|
7914
7975
|
//#region src/run/multi.ts
|
|
@@ -7916,30 +7977,52 @@ const DEFAULT_ROJO_PROJECT = "default.project.json";
|
|
|
7916
7977
|
const VERSION$2 = version;
|
|
7917
7978
|
async function runMultiProject(options) {
|
|
7918
7979
|
const { cli, config: rootConfig, rawProjects } = options;
|
|
7919
|
-
const
|
|
7920
|
-
|
|
7921
|
-
const
|
|
7980
|
+
const timing = options.timing ?? NOOP_TIMING_COLLECTOR;
|
|
7981
|
+
const rojoTree = timing.profile("loadRojoTree", () => loadRojoTree(rootConfig));
|
|
7982
|
+
const allProjects = await timing.profileAsync("resolveAllProjects", async () => {
|
|
7983
|
+
return resolveAllProjects(rawProjects, rootConfig, rojoTree, rootConfig.rootDir);
|
|
7984
|
+
});
|
|
7985
|
+
timing.profile("resolveSetupFilePaths", () => {
|
|
7986
|
+
resolveAllSetupFilePaths(allProjects.map((project) => project.config));
|
|
7987
|
+
});
|
|
7988
|
+
const { filesByProject, projects } = timing.profile("selectProjects", () => {
|
|
7989
|
+
return selectProjects(allProjects, cli.project, cli.files, rootConfig.rootDir);
|
|
7990
|
+
});
|
|
7922
7991
|
const cacheRoot = path$1.resolve(rootConfig.rootDir, ".jest-roblox", "cache");
|
|
7923
|
-
const cleaned = cleanLeftoverStubs
|
|
7992
|
+
const cleaned = timing.profile("cleanLeftoverStubs", () => {
|
|
7993
|
+
return cleanLeftoverStubs(projects, rootConfig.rootDir);
|
|
7994
|
+
});
|
|
7924
7995
|
if (cleaned.length > 0) process.stderr.write(`jest-roblox: cleaned ${String(cleaned.length)} leftover stub(s):\n${cleaned.map((stubPath) => ` ${stubPath}\n`).join("")}`);
|
|
7925
|
-
generateProjectStubs
|
|
7926
|
-
|
|
7927
|
-
|
|
7996
|
+
timing.profile("generateProjectStubs", () => {
|
|
7997
|
+
generateProjectStubs(projects, rootConfig.rootDir, cacheRoot);
|
|
7998
|
+
});
|
|
7999
|
+
const { effectiveConfig, preCoverageMs } = timing.profile("prepareCoverage", () => {
|
|
8000
|
+
return prepareMultiProjectCoverage(rootConfig, projects, cacheRoot);
|
|
8001
|
+
});
|
|
8002
|
+
const backend = await timing.profileAsync("resolveBackend", async () => {
|
|
8003
|
+
return resolveBackend(cli, effectiveConfig);
|
|
8004
|
+
});
|
|
7928
8005
|
const parallel = effectiveParallelForBackend(effectiveConfig.parallel, backend);
|
|
7929
|
-
if (!rootConfig.collectCoverage && backend.kind === "open-cloud") buildOpenCloudPlace
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
8006
|
+
if (!rootConfig.collectCoverage && backend.kind === "open-cloud") timing.profile("buildOpenCloudPlace", () => {
|
|
8007
|
+
buildOpenCloudPlace(rootConfig, projects, cacheRoot);
|
|
8008
|
+
});
|
|
8009
|
+
const { allTypeTestFiles, pendingJobs } = timing.profile("collectPendingJobs", () => {
|
|
8010
|
+
return collectPendingJobs({
|
|
8011
|
+
cliFiles: cli.files,
|
|
8012
|
+
effectivePlaceFile: effectiveConfig.placeFile,
|
|
8013
|
+
filesByProject,
|
|
8014
|
+
projects,
|
|
8015
|
+
rootConfig
|
|
8016
|
+
});
|
|
7936
8017
|
});
|
|
7937
|
-
const projectResults = await runJobs(backend, pendingJobs, parallel);
|
|
8018
|
+
const projectResults = await runJobs(backend, pendingJobs, parallel, timing);
|
|
7938
8019
|
const uniqueTypeTestFiles = [...new Set(allTypeTestFiles)];
|
|
7939
|
-
const typecheckResult = uniqueTypeTestFiles.length > 0 ? runTypecheck({
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
8020
|
+
const typecheckResult = uniqueTypeTestFiles.length > 0 ? timing.profile("runTypecheck", () => {
|
|
8021
|
+
return runTypecheck({
|
|
8022
|
+
files: uniqueTypeTestFiles,
|
|
8023
|
+
rootDir: rootConfig.rootDir,
|
|
8024
|
+
tsconfig: rootConfig.typecheckTsconfig
|
|
8025
|
+
});
|
|
7943
8026
|
}) : void 0;
|
|
7944
8027
|
if (projectResults.length === 0 && typecheckResult === void 0) {
|
|
7945
8028
|
if (rootConfig.passWithNoTests) return {
|
|
@@ -8027,28 +8110,31 @@ function collectPendingJobs(arguments_) {
|
|
|
8027
8110
|
pendingJobs
|
|
8028
8111
|
};
|
|
8029
8112
|
}
|
|
8030
|
-
async function runJobs(backend, pendingJobs, parallel) {
|
|
8113
|
+
async function runJobs(backend, pendingJobs, parallel, timing) {
|
|
8031
8114
|
if (pendingJobs.length === 0) {
|
|
8032
8115
|
await backend.close?.();
|
|
8033
8116
|
return [];
|
|
8034
8117
|
}
|
|
8035
8118
|
let runResult;
|
|
8036
8119
|
try {
|
|
8037
|
-
runResult = await runProjects({
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8045
|
-
|
|
8046
|
-
|
|
8047
|
-
|
|
8048
|
-
|
|
8049
|
-
|
|
8050
|
-
|
|
8051
|
-
|
|
8120
|
+
runResult = await timing.profileAsync("runProjects", async () => {
|
|
8121
|
+
return runProjects({
|
|
8122
|
+
backend,
|
|
8123
|
+
deferFormatting: true,
|
|
8124
|
+
parallel,
|
|
8125
|
+
projects: pendingJobs.map((pending) => {
|
|
8126
|
+
return {
|
|
8127
|
+
config: pending.config,
|
|
8128
|
+
displayColor: pending.displayColor,
|
|
8129
|
+
displayName: pending.displayName,
|
|
8130
|
+
runtimeInjectionPaths: pending.runtimeInjectionPaths,
|
|
8131
|
+
testFiles: pending.runtimeFiles
|
|
8132
|
+
};
|
|
8133
|
+
}),
|
|
8134
|
+
startTime: Date.now(),
|
|
8135
|
+
timing,
|
|
8136
|
+
version: VERSION$2
|
|
8137
|
+
});
|
|
8052
8138
|
});
|
|
8053
8139
|
} finally {
|
|
8054
8140
|
await backend.close?.();
|
|
@@ -8125,9 +8211,14 @@ function selectProjects(allProjects, projectNames, cliFiles, rootDirectory) {
|
|
|
8125
8211
|
const VERSION$1 = version;
|
|
8126
8212
|
async function runSingleProject(options) {
|
|
8127
8213
|
const { cli } = options;
|
|
8128
|
-
const
|
|
8129
|
-
|
|
8130
|
-
|
|
8214
|
+
const timing = options.timing ?? NOOP_TIMING_COLLECTOR;
|
|
8215
|
+
const config = timing.profile("narrowConfigByFiles", () => {
|
|
8216
|
+
return narrowConfigByFiles(options.config, cli.files ?? []);
|
|
8217
|
+
});
|
|
8218
|
+
timing.profile("resolveSetupFilePaths", () => {
|
|
8219
|
+
resolveSetupFilePaths(config);
|
|
8220
|
+
});
|
|
8221
|
+
const discovery = timing.profile("discoverTestFiles", () => discoverTestFiles(config, cli.files));
|
|
8131
8222
|
if (discovery.files.length === 0) {
|
|
8132
8223
|
if (config.passWithNoTests) return {
|
|
8133
8224
|
mode: "single",
|
|
@@ -8140,7 +8231,9 @@ async function runSingleProject(options) {
|
|
|
8140
8231
|
validationExitCode: 2
|
|
8141
8232
|
};
|
|
8142
8233
|
}
|
|
8143
|
-
const { runtimeFiles, typeTestFiles } = classifyTestFiles
|
|
8234
|
+
const { runtimeFiles, typeTestFiles } = timing.profile("classifyTestFiles", () => {
|
|
8235
|
+
return classifyTestFiles(discovery.files, config);
|
|
8236
|
+
});
|
|
8144
8237
|
if (typeTestFiles.length === 0 && runtimeFiles.length === 0) {
|
|
8145
8238
|
if (config.passWithNoTests) return {
|
|
8146
8239
|
mode: "single",
|
|
@@ -8157,19 +8250,27 @@ async function runSingleProject(options) {
|
|
|
8157
8250
|
let effectiveConfig = config;
|
|
8158
8251
|
if (config.collectCoverage && !config.typecheckOnly && runtimeFiles.length > 0) {
|
|
8159
8252
|
const preCoverageStart = Date.now();
|
|
8160
|
-
const { placeFile } = prepareCoverage(config);
|
|
8253
|
+
const { placeFile } = timing.profile("prepareCoverage", () => prepareCoverage(config));
|
|
8161
8254
|
preCoverageMs = Date.now() - preCoverageStart;
|
|
8162
8255
|
effectiveConfig = {
|
|
8163
8256
|
...config,
|
|
8164
8257
|
placeFile
|
|
8165
8258
|
};
|
|
8166
8259
|
}
|
|
8167
|
-
const typecheckResult = typeTestFiles.length > 0 ? runTypecheck({
|
|
8168
|
-
|
|
8169
|
-
|
|
8170
|
-
|
|
8260
|
+
const typecheckResult = typeTestFiles.length > 0 ? timing.profile("runTypecheck", () => {
|
|
8261
|
+
return runTypecheck({
|
|
8262
|
+
files: typeTestFiles,
|
|
8263
|
+
rootDir: effectiveConfig.rootDir,
|
|
8264
|
+
tsconfig: effectiveConfig.typecheckTsconfig
|
|
8265
|
+
});
|
|
8266
|
+
}) : void 0;
|
|
8267
|
+
const runtimeResult = runtimeFiles.length > 0 ? await executeRuntimeTests({
|
|
8268
|
+
cli,
|
|
8269
|
+
config: effectiveConfig,
|
|
8270
|
+
testFiles: runtimeFiles,
|
|
8271
|
+
timing,
|
|
8272
|
+
totalFiles: discovery.totalFiles
|
|
8171
8273
|
}) : void 0;
|
|
8172
|
-
const runtimeResult = runtimeFiles.length > 0 ? await executeRuntimeTests(options, effectiveConfig, runtimeFiles, discovery.totalFiles) : void 0;
|
|
8173
8274
|
return {
|
|
8174
8275
|
mode: "single",
|
|
8175
8276
|
preCoverageMs,
|
|
@@ -8177,19 +8278,25 @@ async function runSingleProject(options) {
|
|
|
8177
8278
|
typecheckResult
|
|
8178
8279
|
};
|
|
8179
8280
|
}
|
|
8180
|
-
async function executeRuntimeTests(options
|
|
8281
|
+
async function executeRuntimeTests(options) {
|
|
8282
|
+
const { cli, config, testFiles, timing, totalFiles } = options;
|
|
8181
8283
|
if (!config.silent && !usesAgentFormatter(config.formatters, config.verbose) && !hasFormatter(config.formatters, "json") && testFiles.length !== totalFiles) process.stderr.write(`Running ${String(testFiles.length)} of ${String(totalFiles)} test files\n`);
|
|
8182
|
-
const backend = await resolveBackend
|
|
8284
|
+
const backend = await timing.profileAsync("resolveBackend", async () => {
|
|
8285
|
+
return resolveBackend(cli, config);
|
|
8286
|
+
});
|
|
8183
8287
|
try {
|
|
8184
|
-
const { results } = await runProjects({
|
|
8185
|
-
|
|
8186
|
-
|
|
8187
|
-
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
|
|
8191
|
-
|
|
8192
|
-
|
|
8288
|
+
const { results } = await timing.profileAsync("runProjects", async () => {
|
|
8289
|
+
return runProjects({
|
|
8290
|
+
backend,
|
|
8291
|
+
deferFormatting: true,
|
|
8292
|
+
projects: [{
|
|
8293
|
+
config,
|
|
8294
|
+
testFiles
|
|
8295
|
+
}],
|
|
8296
|
+
startTime: Date.now(),
|
|
8297
|
+
timing,
|
|
8298
|
+
version: VERSION$1
|
|
8299
|
+
});
|
|
8193
8300
|
});
|
|
8194
8301
|
return results[0];
|
|
8195
8302
|
} finally {
|
|
@@ -8925,12 +9032,7 @@ const SYNTHESIZED_PLACE_FILE = "synthesized.rbxl";
|
|
|
8925
9032
|
const WORKSPACE_CACHE_DIRECTORY = path$1.join(".jest-roblox", "workspace");
|
|
8926
9033
|
const ROJO_PROJECT_DEFAULT = "test.project.json";
|
|
8927
9034
|
async function runWorkspace(options) {
|
|
8928
|
-
|
|
8929
|
-
try {
|
|
8930
|
-
return await runWorkspaceProfiled(options, timing);
|
|
8931
|
-
} finally {
|
|
8932
|
-
timing.flushTimingReport();
|
|
8933
|
-
}
|
|
9035
|
+
return runWorkspaceProfiled(options, options.timing ?? NOOP_TIMING_COLLECTOR);
|
|
8934
9036
|
}
|
|
8935
9037
|
function buildCoverageMap(entries) {
|
|
8936
9038
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -9044,6 +9146,7 @@ async function runWorkspaceProfiled(options, timing) {
|
|
|
9044
9146
|
};
|
|
9045
9147
|
}),
|
|
9046
9148
|
startTime,
|
|
9149
|
+
timing,
|
|
9047
9150
|
version,
|
|
9048
9151
|
...dispatchSpec
|
|
9049
9152
|
});
|
|
@@ -9642,7 +9745,7 @@ const EMPTY_RESULT = {
|
|
|
9642
9745
|
preCoverageMs: 0,
|
|
9643
9746
|
projectResults: []
|
|
9644
9747
|
};
|
|
9645
|
-
async function runWorkspaceMode(cli, workspace) {
|
|
9748
|
+
async function runWorkspaceMode(cli, workspace, timing) {
|
|
9646
9749
|
const basicValidation = validateBasicWorkspaceFlags(cli);
|
|
9647
9750
|
if (!basicValidation.ok) return {
|
|
9648
9751
|
...EMPTY_RESULT,
|
|
@@ -9715,6 +9818,7 @@ async function runWorkspaceMode(cli, workspace) {
|
|
|
9715
9818
|
...onStreamingResult !== void 0 ? { onStreamingResult } : {},
|
|
9716
9819
|
packageInfos,
|
|
9717
9820
|
runOptions,
|
|
9821
|
+
timing,
|
|
9718
9822
|
version: VERSION,
|
|
9719
9823
|
workspaceRoot,
|
|
9720
9824
|
workStealingCredentials
|
|
@@ -9817,18 +9921,25 @@ function isWorkspaceInvocation(cli) {
|
|
|
9817
9921
|
return cli.workspace === true || cli.packages !== void 0 || cli.affectedSince !== void 0;
|
|
9818
9922
|
}
|
|
9819
9923
|
async function runJestRoblox(cli, config) {
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
9826
|
-
|
|
9827
|
-
|
|
9828
|
-
|
|
9829
|
-
|
|
9830
|
-
|
|
9831
|
-
|
|
9924
|
+
const timing = createTimingCollector();
|
|
9925
|
+
try {
|
|
9926
|
+
if (isWorkspaceInvocation(cli)) return await runWorkspaceMode(cli, config.workspace, timing);
|
|
9927
|
+
const merged = mergeCliWithConfig(cli, config);
|
|
9928
|
+
const rawProjects = merged.projects;
|
|
9929
|
+
if (rawProjects !== void 0 && rawProjects.length > 0) return await runMultiProject({
|
|
9930
|
+
cli,
|
|
9931
|
+
config: merged,
|
|
9932
|
+
rawProjects,
|
|
9933
|
+
timing
|
|
9934
|
+
});
|
|
9935
|
+
return await runSingleProject({
|
|
9936
|
+
cli,
|
|
9937
|
+
config: merged,
|
|
9938
|
+
timing
|
|
9939
|
+
});
|
|
9940
|
+
} finally {
|
|
9941
|
+
timing.flushTimingReport();
|
|
9942
|
+
}
|
|
9832
9943
|
}
|
|
9833
9944
|
//#endregion
|
|
9834
9945
|
export { mergeCliWithConfig as A, defineProject as B, formatFailure as C, LuauScriptError as D, formatBanner as E, JEST_ARGV_EXCLUDED_KEYS as F, ConfigError as H, ROOT_CLI_KEYS as I, SHARED_TEST_KEYS as L, resolveConfig as M, DEFAULT_CONFIG as N, extractJsonFromOutput as O, GLOBAL_TEST_KEYS as P, VALID_BACKENDS as R, writeJsonFile$1 as S, formatTestSummary as T, version as U, isValidBackend as V, runProjects as _, visitStatement as a, writeGameOutput as b, OpenCloudBackend as c, generateTestScript as d, outputMultiResult as f, formatExecuteOutput as g, formatJobSummary as h, visitExpression as i, loadConfig$1 as j, parseJestOutput as k, createOpenCloudBackend as l, formatAnnotations as m, runTypecheck as n, StudioBackend as o, outputSingleResult as p, visitBlock as r, createStudioBackend as s, runJestRoblox as t, buildJestArgv as u, formatGameOutputNotice as v, formatResult as w, formatJson as x, parseGameOutput as y, defineConfig as z };
|