Skip to content

Commit ce64185

Browse files
committed
up
1 parent 5b416e0 commit ce64185

File tree

6 files changed

+105
-110
lines changed

6 files changed

+105
-110
lines changed

e2e/watch/shortcuts.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ describe('CLI shortcuts', () => {
8989
cli.exec.process!.stdin!.write('f');
9090
await cli.waitForStdout('Duration');
9191
expect(cli.stdout).toMatch('Tests 1 failed');
92-
expect(cli.stdout).toMatch('Run all tests.');
92+
expect(cli.stdout).toMatch('Run filtered tests.');
9393

9494
cli.exec.kill();
9595
});

packages/core/src/core/plugins/basic.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import path from 'pathe';
33
import type { RstestContext } from '../../types';
44
import { TEMP_RSTEST_OUTPUT_DIR } from '../../utils';
55

6+
export const RUNTIME_CHUNK_NAME = 'runtime';
7+
68
export const pluginBasic: (context: RstestContext) => RsbuildPlugin = (
79
context,
810
) => ({
@@ -97,7 +99,7 @@ export const pluginBasic: (context: RstestContext) => RsbuildPlugin = (
9799
...(config.optimization || {}),
98100
// make sure setup file and test file share the runtime
99101
runtimeChunk: {
100-
name: 'runtime',
102+
name: RUNTIME_CHUNK_NAME,
101103
},
102104
};
103105
},

packages/core/src/core/rsbuild.ts

Lines changed: 70 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
import path from 'pathe';
1010
import type { EntryInfo, RstestContext, SourceMapInput } from '../types';
1111
import { isDebug } from '../utils';
12-
import { pluginBasic } from './plugins/basic';
12+
import { pluginBasic, RUNTIME_CHUNK_NAME } from './plugins/basic';
1313
import { pluginCSSFilter } from './plugins/css-filter';
1414
import { pluginEntryWatch } from './plugins/entry';
1515
import { pluginExternal } from './plugins/external';
@@ -18,7 +18,7 @@ import { pluginInspect } from './plugins/inspect';
1818
import { pluginMockRuntime } from './plugins/mockRuntime';
1919
import { pluginCacheControl } from './plugins/moduleCacheControl';
2020

21-
type EntryToChunkHashes = {
21+
type TestEntryToChunkHashes = {
2222
name: string;
2323
/** key is chunk name, value is chunk hash */
2424
chunks: Record<string, string>;
@@ -122,96 +122,88 @@ export const prepareRsbuild = async (
122122
export const calcEntriesToRerun = (
123123
entries: EntryInfo[],
124124
chunks: Rspack.StatsChunk[] | undefined,
125-
buildData: { entryToChunkHashes?: EntryToChunkHashes },
125+
buildData: { entryToChunkHashes?: TestEntryToChunkHashes },
126126
): {
127127
affectedEntries: EntryInfo[];
128128
deletedEntries: string[];
129129
} => {
130-
const entryToChunkHashes: EntryToChunkHashes = [];
130+
const entryToChunkHashesMap = new Map<string, Record<string, string>>();
131131

132-
for (const entry of entries || []) {
133-
for (const chunkName of entry.chunks || []) {
134-
// Treat runtime chunk as invariant, sometimes it will change but we don't care.
135-
if (chunkName === 'runtime') {
136-
continue;
137-
}
132+
// Build current chunk hashes map
133+
const buildChunkHashes = (entry: EntryInfo) => {
134+
const validChunks = (entry.chunks || []).filter(
135+
(chunk) => chunk !== RUNTIME_CHUNK_NAME,
136+
);
138137

139-
const chunkInfo = chunks!.find((c) =>
138+
validChunks.forEach((chunkName) => {
139+
const chunkInfo = chunks?.find((c) =>
140140
c.names?.includes(chunkName as string),
141141
);
142142
if (chunkInfo) {
143-
const current = entryToChunkHashes.find(
144-
(e) => e.name === entry.testPath,
145-
);
146-
if (current) {
147-
current.chunks[chunkName] = chunkInfo.hash ?? '';
148-
} else {
149-
entryToChunkHashes.push({
150-
name: entry.testPath,
151-
chunks: {
152-
[chunkName]: chunkInfo.hash ?? '',
153-
},
154-
});
155-
}
143+
const existing = entryToChunkHashesMap.get(entry.testPath) || {};
144+
existing[chunkName] = chunkInfo.hash ?? '';
145+
entryToChunkHashesMap.set(entry.testPath, existing);
156146
}
157-
}
158-
}
147+
});
148+
};
149+
150+
(entries || []).forEach(buildChunkHashes);
151+
152+
const entryToChunkHashes: TestEntryToChunkHashes = Array.from(
153+
entryToChunkHashesMap.entries(),
154+
).map(([name, chunks]) => ({ name, chunks }));
155+
156+
// Process changes if we have previous data
157+
const affectedTestPaths = new Set<string>();
158+
const deletedEntries: string[] = [];
159159

160-
let affectedEntries: EntryInfo[] = [];
161-
let deletedEntries: string[] = [];
162160
if (buildData.entryToChunkHashes) {
163-
const prev = buildData.entryToChunkHashes;
164-
const deleted = prev?.filter(
165-
(p) => !entryToChunkHashes.find((e) => e.name === p.name),
161+
const prevMap = new Map(
162+
buildData.entryToChunkHashes.map((e) => [e.name, e.chunks]),
166163
);
164+
const currentNames = new Set(entryToChunkHashesMap.keys());
167165

168-
// deleted
169-
if (deleted.length) {
170-
deletedEntries = deleted.map((entry) => entry.name);
171-
}
166+
// Find deleted entries
167+
deletedEntries.push(
168+
...Array.from(prevMap.keys()).filter((name) => !currentNames.has(name)),
169+
);
172170

173-
entryToChunkHashes.forEach((entryToChunk) => {
174-
const prevChunk = prev?.find((p) => p.name === entryToChunk.name);
175-
Object.entries(entryToChunk.chunks).forEach(([chunkName, chunkHash]) => {
176-
// modified
177-
if (prevChunk) {
178-
const prevHash = prevChunk.chunks[chunkName];
179-
if (prevHash !== chunkHash) {
180-
const entryInfo = entries.find(
181-
(e) => e.testPath === entryToChunk.name,
182-
);
183-
if (entryInfo) {
184-
affectedEntries ??= [];
185-
affectedEntries.push(entryInfo);
186-
}
187-
}
188-
} else {
189-
// added
190-
const entryInfo = entries.find(
191-
(e) => e.testPath === entryToChunk.name,
192-
);
193-
if (entryInfo) {
194-
affectedEntries ??= [];
195-
affectedEntries.push(entryInfo);
196-
}
197-
}
198-
});
171+
// Find modified or added entries
172+
const findAffectedEntry = (testPath: string) => {
173+
const currentChunks = entryToChunkHashesMap.get(testPath);
174+
const prevChunks = prevMap.get(testPath);
175+
176+
if (!currentChunks) return;
177+
178+
if (!prevChunks) {
179+
// New entry
180+
affectedTestPaths.add(testPath);
181+
return;
182+
}
183+
184+
// Check for modified chunks
185+
const hasChanges = Object.entries(currentChunks).some(
186+
([chunkName, hash]) => prevChunks[chunkName] !== hash,
187+
);
188+
189+
if (hasChanges) {
190+
affectedTestPaths.add(testPath);
191+
}
192+
};
193+
194+
entryToChunkHashesMap.forEach((_, testPath) => {
195+
findAffectedEntry(testPath);
199196
});
200197
}
201198

202199
buildData.entryToChunkHashes = entryToChunkHashes;
203-
let dedupeAffectedEntries: EntryInfo[] = [];
204200

205-
if (affectedEntries) {
206-
dedupeAffectedEntries = [];
207-
for (const entry of affectedEntries) {
208-
if (!dedupeAffectedEntries.some((e) => e.testPath === entry.testPath)) {
209-
dedupeAffectedEntries.push(entry);
210-
}
211-
}
212-
}
201+
// Convert affected test paths to EntryInfo objects
202+
const affectedEntries = Array.from(affectedTestPaths)
203+
.map((testPath) => entries.find((e) => e.testPath === testPath))
204+
.filter((entry): entry is EntryInfo => entry !== undefined);
213205

214-
return { affectedEntries: dedupeAffectedEntries, deletedEntries };
206+
return { affectedEntries, deletedEntries };
215207
};
216208

217209
export const createRsbuildServer = async ({
@@ -244,6 +236,7 @@ export const createRsbuildServer = async ({
244236
}> => {
245237
// Read files from memory via `rspackCompiler.outputFileSystem`
246238
let rspackCompiler: Rspack.Compiler | Rspack.MultiCompiler | undefined;
239+
let isFirstCompile = false;
247240

248241
const rstestCompilerPlugin: RsbuildPlugin = {
249242
name: 'rstest:compiler',
@@ -252,6 +245,10 @@ export const createRsbuildServer = async ({
252245
// outputFileSystem to be updated later by `rsbuild-dev-middleware`
253246
rspackCompiler = compiler;
254247
});
248+
249+
api.onAfterDevCompile(({ isFirstCompile: _isFirstCompile }) => {
250+
isFirstCompile = _isFirstCompile;
251+
});
255252
},
256253
};
257254

@@ -286,13 +283,11 @@ export const createRsbuildServer = async ({
286283
);
287284
}
288285

289-
let runCount = 0;
290-
const buildData: { entryToChunkHashes?: EntryToChunkHashes } = {};
286+
const buildData: { entryToChunkHashes?: TestEntryToChunkHashes } = {};
291287

292288
const getRsbuildStats = async ({
293289
fileFilters,
294290
}: { fileFilters?: string[] } | undefined = {}) => {
295-
runCount++;
296291
const stats = await devServer.environments[name]!.getStats();
297292

298293
const manifest = devServer.environments[name]!.context
@@ -316,9 +311,6 @@ export const createRsbuildServer = async ({
316311
// get the compilation time
317312
chunks: true,
318313
timings: true,
319-
modules: true,
320-
reasons: true,
321-
chunkModules: true,
322314
});
323315

324316
const readFile = async (fileName: string) => {
@@ -418,7 +410,7 @@ export const createRsbuildServer = async ({
418410
return {
419411
affectedEntries,
420412
deletedEntries,
421-
isFirstRun: runCount === 1,
413+
isFirstRun: isFirstCompile,
422414
hash,
423415
entries,
424416
setupEntries,

packages/core/src/core/rstest.ts

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
import { SnapshotManager } from '@vitest/snapshot/manager';
2+
import { isCI } from 'std-env';
3+
import { withDefaultConfig } from '../config';
4+
import { DefaultReporter } from '../reporter';
5+
import { GithubActionsReporter } from '../reporter/githubActions';
6+
import { VerboseReporter } from '../reporter/verbose';
27
import type {
38
NormalizedConfig,
49
Reporter,
@@ -8,12 +13,7 @@ import type {
813
Test,
914
TestFileResult,
1015
TestResult,
11-
} from 'src/types';
12-
import { isCI } from 'std-env';
13-
import { withDefaultConfig } from '../config';
14-
import { DefaultReporter } from '../reporter';
15-
import { GithubActionsReporter } from '../reporter/githubActions';
16-
import { VerboseReporter } from '../reporter/verbose';
16+
} from '../types';
1717
import { castArray, getAbsolutePath } from '../utils/helper';
1818

1919
type Options = {
@@ -75,14 +75,13 @@ export class Rstest implements RstestContext {
7575
this.normalizedConfig = rstestConfig;
7676
}
7777

78-
public updateReporter(
78+
public updateReporterResultState(
7979
results: TestFileResult[],
8080
testResults: TestResult[],
8181
deletedEntries: string[] = [],
8282
): void {
83-
// update results
84-
for (let i = 0; i < results.length; i++) {
85-
const item = results[i]!;
83+
// Update or add results
84+
results.forEach((item) => {
8685
const existingIndex = this.reporterResults.results.findIndex(
8786
(r) => r.testPath === item.testPath,
8887
);
@@ -91,27 +90,25 @@ export class Rstest implements RstestContext {
9190
} else {
9291
this.reporterResults.results.push(item);
9392
}
94-
}
95-
96-
// update test results
97-
const pathToClear = testResults.map((r) => r.testPath);
98-
for (const file of pathToClear) {
99-
this.reporterResults.testResults =
100-
this.reporterResults.testResults.filter((r) => r.testPath !== file);
101-
}
93+
});
10294

103-
for (let i = 0; i < testResults.length; i++) {
104-
const item = testResults[i]!;
105-
this.reporterResults.testResults.push(item);
106-
}
95+
// Clear existing test results for updated paths and add new ones
96+
const testPathsToUpdate = new Set(testResults.map((r) => r.testPath));
97+
this.reporterResults.testResults = this.reporterResults.testResults.filter(
98+
(r) => !testPathsToUpdate.has(r.testPath),
99+
);
100+
this.reporterResults.testResults.push(...testResults);
107101

108-
// deleted tests that are not in entires
109-
for (const entry of deletedEntries) {
102+
// Remove deleted entries
103+
if (deletedEntries.length > 0) {
104+
const deletedPathsSet = new Set(deletedEntries);
110105
this.reporterResults.results = this.reporterResults.results.filter(
111-
(r) => r.testPath !== entry,
106+
(r) => !deletedPathsSet.has(r.testPath),
112107
);
113108
this.reporterResults.testResults =
114-
this.reporterResults.testResults.filter((r) => r.testPath !== entry);
109+
this.reporterResults.testResults.filter(
110+
(r) => !deletedPathsSet.has(r.testPath),
111+
);
115112
}
116113
}
117114
}

packages/core/src/core/runTests.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,11 @@ export async function runTests(context: Rstest): Promise<void> {
145145
finalEntries = affectedEntries;
146146
}
147147
} else {
148-
logger.debug(color.yellow('Run all tests.\n'));
148+
logger.debug(
149+
color.yellow(
150+
fileFilters?.length ? 'Run filtered tests.\n' : 'Run all tests.\n',
151+
),
152+
);
149153
}
150154

151155
const { results, testResults } = await pool.runTests({
@@ -157,7 +161,7 @@ export async function runTests(context: Rstest): Promise<void> {
157161
updateSnapshot: snapshotManager.options.updateSnapshot,
158162
});
159163

160-
context.updateReporter(results, testResults, deletedEntries);
164+
context.updateReporterResultState(results, testResults, deletedEntries);
161165

162166
const actualBuildTime = buildHash === hash ? 0 : buildTime;
163167

@@ -182,7 +186,7 @@ export async function runTests(context: Rstest): Promise<void> {
182186
snapshotSummary: snapshotManager.summary,
183187
duration,
184188
getSourcemap,
185-
filterRerunTestPaths: affectedEntries
189+
filterRerunTestPaths: affectedEntries.length
186190
? affectedEntries.map((e) => e.testPath)
187191
: undefined,
188192
});

packages/core/src/runtime/runner/runtime.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { MaybePromise } from 'src/types/utils';
21
import type {
32
AfterAllListener,
43
AfterEachListener,
@@ -8,6 +7,7 @@ import type {
87
DescribeEachFn,
98
DescribeForFn,
109
Fixtures,
10+
MaybePromise,
1111
NormalizedFixtures,
1212
RunnerAPI,
1313
RuntimeConfig,

0 commit comments

Comments
 (0)