Skip to content

Commit 4d0062d

Browse files
ZerGo0PatrykKuniczak
authored andcommitted
fix: file change detection and reloading logic
- Introduced `getRelevantDevChangedFiles` to filter out unrelated file changes. - Updated `detectDevChanges` to utilize the new filtering function for improved accuracy. - Modified `createFileReloader` to work with relevant file changes, ensuring only necessary reloads occur. - Enhanced tests to validate the new behavior and ensure proper handling of HTML-only reloads.
1 parent 8767b01 commit 4d0062d

4 files changed

Lines changed: 120 additions & 32 deletions

File tree

packages/wxt/src/core/__tests__/create-server.test.ts

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
22
import { createFileReloader } from '../create-server';
3-
import { detectDevChanges, findEntrypoints, rebuild } from '../utils/building';
3+
import { findEntrypoints, rebuild } from '../utils/building';
44
import {
5+
fakeBackgroundEntrypoint,
56
fakeBuildOutput,
67
fakeDevServer,
8+
fakeOutputChunk,
79
setFakeWxt,
810
} from '../utils/testing/fake-objects';
911

10-
vi.mock('../utils/building', () => ({
11-
detectDevChanges: vi.fn(),
12-
findEntrypoints: vi.fn(),
13-
internalBuild: vi.fn(),
14-
rebuild: vi.fn(),
15-
}));
12+
vi.mock('../utils/building', async () => {
13+
const actual =
14+
await vi.importActual<typeof import('../utils/building')>(
15+
'../utils/building',
16+
);
17+
return {
18+
...actual,
19+
findEntrypoints: vi.fn(),
20+
rebuild: vi.fn(),
21+
};
22+
});
1623

1724
describe('createFileReloader', () => {
1825
beforeEach(() => {
@@ -39,25 +46,23 @@ describe('createFileReloader', () => {
3946
const relevantFile = '/root/src/entrypoints/background.ts';
4047
const noisyProfileFile =
4148
'/root/private/.dev-profile/Default/Cache/Cache_Data/d573fa6484e43cf9_0';
49+
const backgroundEntrypoint = fakeBackgroundEntrypoint({
50+
inputPath: relevantFile,
51+
});
4252
const currentOutput = fakeBuildOutput({
43-
steps: [],
53+
steps: [
54+
{
55+
entrypoints: backgroundEntrypoint,
56+
chunks: [fakeOutputChunk({ moduleIds: [relevantFile] })],
57+
},
58+
],
4459
publicAssets: [],
4560
});
4661
const server = fakeDevServer({
4762
currentOutput,
4863
reloadExtension: vi.fn(),
4964
});
5065

51-
vi.mocked(detectDevChanges).mockImplementation((fileChanges, output) => {
52-
if (fileChanges.includes(relevantFile)) {
53-
return {
54-
type: 'extension-reload',
55-
rebuildGroups: [],
56-
cachedOutput: output,
57-
};
58-
}
59-
return { type: 'no-change' };
60-
});
6166
vi.mocked(rebuild).mockResolvedValue({
6267
output: currentOutput,
6368
manifest: currentOutput.manifest,
@@ -72,11 +77,12 @@ describe('createFileReloader', () => {
7277
await vi.advanceTimersByTimeAsync(500);
7378
await Promise.all([fixedFirst, fixedSecond]);
7479

75-
const seenFiles = vi
76-
.mocked(detectDevChanges)
77-
.mock.calls.flatMap(([fileChanges]) => fileChanges);
78-
79-
expect(seenFiles).toContain(relevantFile);
80+
expect(rebuild).toBeCalledTimes(1);
81+
expect(rebuild).toBeCalledWith(
82+
[],
83+
[expect.objectContaining({ inputPath: relevantFile })],
84+
expect.anything(),
85+
);
8086
expect(server.reloadExtension).toBeCalledTimes(1);
8187
});
8288
});

packages/wxt/src/core/create-server.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
import {
1515
internalBuild,
1616
detectDevChanges,
17+
getRelevantDevChangedFiles,
1718
rebuild,
1819
findEntrypoints,
1920
} from './utils/building';
@@ -214,9 +215,18 @@ export function createFileReloader(server: WxtDevServer) {
214215
if (fileChanges.length === 0) return;
215216
if (server.currentOutput == null) return;
216217

218+
const relevantFileChanges = getRelevantDevChangedFiles(
219+
fileChanges,
220+
server.currentOutput,
221+
);
222+
if (relevantFileChanges.length === 0) return;
223+
217224
await wxt.reloadConfig();
218225

219-
const changes = detectDevChanges(fileChanges, server.currentOutput);
226+
const changes = detectDevChanges(
227+
relevantFileChanges,
228+
server.currentOutput,
229+
);
220230
if (changes.type === 'no-change') return;
221231

222232
if (changes.type === 'full-restart') {
@@ -233,7 +243,7 @@ export function createFileReloader(server: WxtDevServer) {
233243

234244
// Log the entrypoints that were effected
235245
wxt.logger.info(
236-
`Changed: ${Array.from(new Set(fileChanges))
246+
`Changed: ${relevantFileChanges
237247
.map((file) => pc.dim(relative(wxt.config.root, file)))
238248
.join(', ')}`,
239249
);

packages/wxt/src/core/utils/building/__tests__/detect-dev-changes.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,60 @@ describe('Detect Dev Changes', () => {
252252
expect(actual).toEqual(expected);
253253
});
254254

255+
it('should ignore unrelated changed files when checking html-only reloads', async () => {
256+
const changedPath = '/root/page1.html';
257+
const unrelatedPath =
258+
'/root/private/.dev-profile/Default/Cache/Cache_Data/1004_0';
259+
const htmlPage1 = fakePopupEntrypoint({
260+
inputPath: changedPath,
261+
});
262+
const htmlPage2 = fakeOptionsEntrypoint({
263+
inputPath: '/root/page2.html',
264+
});
265+
const htmlPage3 = fakeGenericEntrypoint({
266+
type: 'sandbox',
267+
inputPath: '/root/page3.html',
268+
});
269+
270+
const step1: BuildStepOutput = {
271+
entrypoints: [htmlPage1, htmlPage2],
272+
chunks: [
273+
fakeOutputAsset({
274+
fileName: 'page1.html',
275+
}),
276+
],
277+
};
278+
const step2: BuildStepOutput = {
279+
entrypoints: [htmlPage3],
280+
chunks: [
281+
fakeOutputAsset({
282+
fileName: 'page2.html',
283+
}),
284+
],
285+
};
286+
287+
const currentOutput: BuildOutput = {
288+
manifest: fakeManifest(),
289+
publicAssets: [],
290+
steps: [step1, step2],
291+
};
292+
const expected: DevModeChange = {
293+
type: 'html-reload',
294+
cachedOutput: {
295+
...currentOutput,
296+
steps: [step2],
297+
},
298+
rebuildGroups: [[htmlPage1, htmlPage2]],
299+
};
300+
301+
const actual = detectDevChanges(
302+
[unrelatedPath, changedPath],
303+
currentOutput,
304+
);
305+
306+
expect(actual).toEqual(expected);
307+
});
308+
255309
it('should detect changes to entrypoints/<name>/index.html files', async () => {
256310
const changedPath = '/root/page1/index.html';
257311
const htmlPage1 = fakePopupEntrypoint({

packages/wxt/src/core/utils/building/detect-dev-changes.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,30 +36,35 @@ export function detectDevChanges(
3636
changedFiles: string[],
3737
currentOutput: BuildOutput,
3838
): DevModeChange {
39-
const isConfigChange = some(
39+
const relevantChangedFiles = getRelevantDevChangedFiles(
4040
changedFiles,
41+
currentOutput,
42+
);
43+
44+
const isConfigChange = some(
45+
relevantChangedFiles,
4146
(file) => file === wxt.config.userConfigMetadata.configFile,
4247
);
4348
if (isConfigChange) return { type: 'full-restart' };
4449

45-
const isWxtModuleChange = some(changedFiles, (file) =>
50+
const isWxtModuleChange = some(relevantChangedFiles, (file) =>
4651
file.startsWith(wxt.config.modulesDir),
4752
);
4853
if (isWxtModuleChange) return { type: 'full-restart' };
4954

5055
const isRunnerChange = some(
51-
changedFiles,
56+
relevantChangedFiles,
5257
(file) => file === wxt.config.runnerConfig.configFile,
5358
);
5459
if (isRunnerChange) return { type: 'browser-restart' };
5560

5661
const changedSteps = new Set(
57-
changedFiles.flatMap((changedFile) =>
62+
relevantChangedFiles.flatMap((changedFile) =>
5863
findEffectedSteps(changedFile, currentOutput),
5964
),
6065
);
6166
if (changedSteps.size === 0) {
62-
const hasPublicChange = some(changedFiles, (file) =>
67+
const hasPublicChange = some(relevantChangedFiles, (file) =>
6368
file.startsWith(wxt.config.publicDir),
6469
);
6570
if (hasPublicChange) {
@@ -93,8 +98,8 @@ export function detectDevChanges(
9398
}
9499

95100
const isOnlyHtmlChanges =
96-
changedFiles.length > 0 &&
97-
every(changedFiles, (file) => file.endsWith('.html'));
101+
relevantChangedFiles.length > 0 &&
102+
every(relevantChangedFiles, (file) => file.endsWith('.html'));
98103
if (isOnlyHtmlChanges) {
99104
return {
100105
type: 'html-reload',
@@ -125,6 +130,19 @@ export function detectDevChanges(
125130
};
126131
}
127132

133+
export function getRelevantDevChangedFiles(
134+
changedFiles: string[],
135+
currentOutput: BuildOutput,
136+
): string[] {
137+
return Array.from(new Set(changedFiles)).filter((changedFile) => {
138+
if (changedFile === wxt.config.userConfigMetadata.configFile) return true;
139+
if (changedFile.startsWith(wxt.config.modulesDir)) return true;
140+
if (changedFile === wxt.config.runnerConfig.configFile) return true;
141+
if (changedFile.startsWith(wxt.config.publicDir)) return true;
142+
return findEffectedSteps(changedFile, currentOutput).length > 0;
143+
});
144+
}
145+
128146
/**
129147
* For a single change, return all the step of the build output that were effected by it.
130148
*/

0 commit comments

Comments
 (0)