wxt
Version:
⚡ Next-gen Web Extension Framework
107 lines (106 loc) • 4.44 kB
JavaScript
import { normalizePath } from "../paths.mjs";
import { every, some } from "../arrays.mjs";
import { wxt } from "../../wxt.mjs";
//#region src/core/utils/building/detect-dev-changes.ts
/**
* Compare the changed files vs the build output and determine what kind of
* reload needs to happen:
*
* - Do nothing
*
* - CSS or JS file associated with an HTML page is changed - this is handled
* automatically by the dev server
* - Change isn't used by any of the entrypoints
* - Reload Content script
*
* - CSS or JS file associated with a content script
* - Background script will be told to reload the content script
* - Reload HTML file
*
* - HTML file itself is saved - HMR doesn't handle this because the HTML pages
* are pre-rendered
* - Chrome is OK reloading the page when the HTML file is changed without
* reloading the whole extension. Not sure about firefox, this might need
* to change to an extension reload
* - Reload extension
*
* - Background script is changed
* - Manifest is different
* - Restart browser
*
* - Web-ext.config.ts (runner config changes)
* - Full dev server restart
*
* - Wxt.config.ts (main config file)
* - Modules/* (any file related to WXT modules)
* - .env (environment variable changed could effect build)
*/
function detectDevChanges(changedFiles, currentOutput) {
const relevantChangedFiles = getRelevantDevChangedFiles(changedFiles, currentOutput);
if (some(relevantChangedFiles, (file) => file === wxt.config.userConfigMetadata.configFile)) return { type: "full-restart" };
if (some(relevantChangedFiles, (file) => file.startsWith(wxt.config.modulesDir))) return { type: "full-restart" };
if (some(relevantChangedFiles, (file) => file === wxt.config.webExt.configFile)) return { type: "browser-restart" };
const changedSteps = new Set(relevantChangedFiles.flatMap((changedFile) => findEffectedSteps(changedFile, currentOutput)));
if (changedSteps.size === 0) if (some(relevantChangedFiles, (file) => file.startsWith(wxt.config.publicDir))) return {
type: "extension-reload",
rebuildGroups: [],
cachedOutput: currentOutput
};
else return { type: "no-change" };
const unchangedOutput = {
manifest: currentOutput.manifest,
steps: [],
publicAssets: [...currentOutput.publicAssets]
};
const changedOutput = {
manifest: currentOutput.manifest,
steps: [],
publicAssets: []
};
for (const step of currentOutput.steps) if (changedSteps.has(step)) changedOutput.steps.push(step);
else unchangedOutput.steps.push(step);
if (relevantChangedFiles.length > 0 && every(relevantChangedFiles, (file) => file.endsWith(".html"))) return {
type: "html-reload",
cachedOutput: unchangedOutput,
rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
};
if (changedOutput.steps.length > 0 && every(changedOutput.steps.flatMap((step) => step.entrypoints), (entry) => entry.type === "content-script")) return {
type: "content-script-reload",
cachedOutput: unchangedOutput,
changedSteps: changedOutput.steps,
rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
};
return {
type: "extension-reload",
cachedOutput: unchangedOutput,
rebuildGroups: changedOutput.steps.map((step) => step.entrypoints)
};
}
function getRelevantDevChangedFiles(changedFiles, currentOutput) {
return Array.from(new Set(changedFiles)).filter((changedFile) => {
if (changedFile === wxt.config.userConfigMetadata.configFile) return true;
if (changedFile.startsWith(wxt.config.modulesDir)) return true;
if (changedFile === wxt.config.webExt.configFile) return true;
if (changedFile.startsWith(wxt.config.publicDir)) return true;
return findEffectedSteps(changedFile, currentOutput).length > 0;
});
}
/**
* For a single change, return all the step of the build output that were
* effected by it.
*/
function findEffectedSteps(changedFile, currentOutput) {
const changes = [];
const changedPath = normalizePath(changedFile);
const isChunkEffected = (chunk) => {
switch (chunk.type) {
case "asset": return changedPath.replace("/index.html", ".html").endsWith(chunk.fileName);
case "chunk": return chunk.moduleIds.map((path) => path.split("?")[0]).includes(changedPath);
default: return false;
}
};
for (const step of currentOutput.steps) if (step.chunks.find((chunk) => isChunkEffected(chunk))) changes.push(step);
return changes;
}
//#endregion
export { detectDevChanges, getRelevantDevChangedFiles };