feat: Reload HTML files individually (#23)
This commit is contained in:
@@ -56,16 +56,12 @@ describe('Output Directory Structure', () => {
|
||||
================================================================================
|
||||
.output/chrome-mv3/content-scripts/one.js
|
||||
----------------------------------------
|
||||
(function(){\\"use strict\\";function c(n){return n}const e=\\"\\",t={matches:[\\"*://*/*\\"],main:()=>{}};(async()=>{try{await t.main()}catch(n){console.error(\`The content script crashed on startup!
|
||||
|
||||
\`,n)}})()})();
|
||||
(function(){\\"use strict\\";function i(n){return n}const s=\\"\\",o={matches:[\\"*://*/*\\"],main:()=>{}};function t(n,...e){if(typeof e[0]==\\"string\\"){const c=e.shift();n(\`[wxt] \${c}\`,...e)}else n(\\"[wxt]\\",...e)}var r={debug:(...n)=>t(console.debug,...n),log:(...n)=>t(console.log,...n),warn:(...n)=>t(console.warn,...n),error:(...n)=>t(console.error,...n)};(async()=>{try{await o.main()}catch(n){r.error(\\"The content script crashed on startup!\\",n)}})()})();
|
||||
|
||||
================================================================================
|
||||
.output/chrome-mv3/content-scripts/two.js
|
||||
----------------------------------------
|
||||
(function(){\\"use strict\\";function c(n){return n}const e=\\"\\",t={matches:[\\"*://*/*\\"],main:()=>{}};(async()=>{try{await t.main()}catch(n){console.error(\`The content script crashed on startup!
|
||||
|
||||
\`,n)}})()})();
|
||||
(function(){\\"use strict\\";function i(n){return n}const s=\\"\\",o={matches:[\\"*://*/*\\"],main:()=>{}};function t(n,...e){if(typeof e[0]==\\"string\\"){const c=e.shift();n(\`[wxt] \${c}\`,...e)}else n(\\"[wxt]\\",...e)}var r={debug:(...n)=>t(console.debug,...n),log:(...n)=>t(console.log,...n),warn:(...n)=>t(console.warn,...n),error:(...n)=>t(console.error,...n)};(async()=>{try{await o.main()}catch(n){r.error(\\"The content script crashed on startup!\\",n)}})()})();
|
||||
|
||||
================================================================================
|
||||
.output/chrome-mv3/manifest.json
|
||||
|
||||
+12
-4
@@ -39,16 +39,24 @@ await Promise.all([
|
||||
silent: true,
|
||||
}),
|
||||
...clientTemplates.map((templateName) =>
|
||||
tsup.build({
|
||||
tsup.build({
|
||||
entry: {
|
||||
[`templates/virtual-${templateName}`]: `src/client/templates/virtual-${templateName}.ts`,
|
||||
},
|
||||
format: ['esm'],
|
||||
sourcemap: true,
|
||||
silent: true,
|
||||
external: [`virtual:user-${templateName}`],
|
||||
}),
|
||||
),
|
||||
tsup.build({
|
||||
entry: {
|
||||
'templates/reload-html': `src/client/templates/reload-html.ts`,
|
||||
},
|
||||
format: ['esm'],
|
||||
sourcemap: true,
|
||||
silent: true,
|
||||
external: [`virtual:user-${templateName}`],
|
||||
}),
|
||||
),
|
||||
}),
|
||||
]).catch((err) => {
|
||||
spinner.fail();
|
||||
console.error(err);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
import { logger } from '../utils/logger';
|
||||
import { setupWebSocket } from '../utils/setupWebSocket';
|
||||
|
||||
if (__COMMAND__ === 'serve') {
|
||||
try {
|
||||
setupWebSocket((message) => {
|
||||
if (message.event === 'wxt:reload-page') {
|
||||
// We need to remove the initial slash from the path to compare correctly
|
||||
// "popup.html" === "/popup.html".substring(1)
|
||||
if (message.data === location.pathname.substring(1)) {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error('Failed to setup web socket connection with dev server', err);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
import definition from 'virtual:user-content-script';
|
||||
import { logger } from '../utils/logger';
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
await definition.main();
|
||||
} catch (err) {
|
||||
console.error('The content script crashed on startup!\n\n', err);
|
||||
logger.error('The content script crashed on startup!', err);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -33,7 +33,6 @@ export function setupWebSocket(
|
||||
try {
|
||||
const message = JSON.parse(e.data) as WebSocketMessage;
|
||||
if (message.type === 'custom' && message.event?.startsWith?.('wxt:')) {
|
||||
logger.debug(`Recieved message: ${message.event}`, message);
|
||||
onMessage?.(message);
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
@@ -58,6 +58,18 @@ export interface WxtDevServer extends vite.ViteDevServer {
|
||||
* Tell the extension to reload by running `browser.runtime.reload`.
|
||||
*/
|
||||
reloadExtension: () => void;
|
||||
/**
|
||||
* Tell an extension page to reload.
|
||||
*
|
||||
* The path is the bundle path, not the input paths, so if the input paths is
|
||||
* "src/options/index.html", you would pass "options.html" because that's where it is written to
|
||||
* in the dist directory, and where it's available at in the actual extension.
|
||||
*
|
||||
* @example
|
||||
* server.reloadPage("popup.html")
|
||||
* server.reloadPage("sandbox.html")
|
||||
*/
|
||||
reloadPage: (path: string) => void;
|
||||
}
|
||||
|
||||
export type TargetBrowser = 'chrome' | 'firefox' | 'safari' | 'edge' | 'opera';
|
||||
|
||||
@@ -60,6 +60,17 @@ export function detectDevChanges(
|
||||
}
|
||||
}
|
||||
|
||||
const isOnlyHtmlChanges = !changedFiles.find(
|
||||
([_, file]) => !file.endsWith('.html'),
|
||||
);
|
||||
if (isOnlyHtmlChanges) {
|
||||
return {
|
||||
type: 'html-reload',
|
||||
cachedOutput: unchangedOutput,
|
||||
rebuildGroups: changedOutput.steps.map((step) => step.entrypoints),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'extension-reload',
|
||||
cachedOutput: unchangedOutput,
|
||||
@@ -103,10 +114,7 @@ function findEffectedSteps(
|
||||
* Contains information about what files changed, what needs rebuilt, and the type of reload that is
|
||||
* required.
|
||||
*/
|
||||
export type DevModeChange =
|
||||
| NoChange
|
||||
// | HtmlReload
|
||||
| ExtensionReload;
|
||||
export type DevModeChange = NoChange | HtmlReload | ExtensionReload;
|
||||
// | BrowserRestart
|
||||
// | ContentScriptReload
|
||||
|
||||
@@ -125,15 +133,9 @@ interface RebuildChange {
|
||||
cachedOutput: BuildOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is separate from `ExtensionReload` because if an HTML file changes, for some browsers,
|
||||
* reloading the current page is enough to pull from the latest change. Simply reloading the page is
|
||||
* less destructive then reloading the entire extension (content scripts aren't invalidated, tabs to
|
||||
* extension pages aren't closed, etc).
|
||||
*/
|
||||
// interface HtmlReload extends RebuildChange {
|
||||
// type: 'html-reload';
|
||||
// }
|
||||
interface HtmlReload extends RebuildChange {
|
||||
type: 'html-reload';
|
||||
}
|
||||
|
||||
interface ExtensionReload extends RebuildChange {
|
||||
type: 'extension-reload';
|
||||
|
||||
@@ -11,6 +11,21 @@ export function devHtmlPrerender(config: InternalConfig): vite.Plugin {
|
||||
return {
|
||||
apply: 'build',
|
||||
name: 'wxt:dev-html-prerender',
|
||||
config(userConfig) {
|
||||
return vite.mergeConfig(
|
||||
{
|
||||
resolve: {
|
||||
alias: {
|
||||
'@wxt/reload-html': resolve(
|
||||
config.root,
|
||||
'node_modules/wxt/dist/templates/reload-html.js',
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
userConfig,
|
||||
);
|
||||
},
|
||||
async transform(html, id) {
|
||||
const server = config.server;
|
||||
if (config.command !== 'serve' || server == null || !id.endsWith('.html'))
|
||||
@@ -43,6 +58,12 @@ export function devHtmlPrerender(config: InternalConfig): vite.Plugin {
|
||||
pointToDevServer('script[type=module]', 'src');
|
||||
pointToDevServer('link[rel=stylesheet]', 'href');
|
||||
|
||||
// Add a script to add page reloading
|
||||
const reloader = document.createElement('script');
|
||||
reloader.src = '@wxt/reload-html';
|
||||
reloader.type = 'module';
|
||||
document.head.appendChild(reloader);
|
||||
|
||||
const newHtml = document.toString();
|
||||
config.logger.debug('Transformed ' + id);
|
||||
config.logger.debug('Old HTML:\n' + html);
|
||||
|
||||
+20
-1
@@ -23,7 +23,10 @@ import { detectDevChanges } from './core/utils/detectDevChanges';
|
||||
import { Mutex } from 'async-mutex';
|
||||
import { consola } from 'consola';
|
||||
import { relative } from 'node:path';
|
||||
import { getEntrypointOutputFile } from './core/utils/entrypoints';
|
||||
import {
|
||||
getEntrypointBundlePath,
|
||||
getEntrypointOutputFile,
|
||||
} from './core/utils/entrypoints';
|
||||
|
||||
export { version } from '../package.json';
|
||||
export * from './core/types/external';
|
||||
@@ -106,6 +109,17 @@ export async function createServer(
|
||||
server.reloadExtension();
|
||||
consola.success(`Reloaded extension: ${rebuiltNames}`);
|
||||
break;
|
||||
case 'html-reload':
|
||||
changes.rebuildGroups.flat().forEach((entry) => {
|
||||
const path = getEntrypointBundlePath(
|
||||
entry,
|
||||
internalConfig.outDir,
|
||||
'.html',
|
||||
);
|
||||
server.reloadPage(path);
|
||||
});
|
||||
consola.success(`Reloaded pages: ${rebuiltNames}`);
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -130,6 +144,11 @@ export async function createServer(
|
||||
reloadExtension: () => {
|
||||
server.ws.send('wxt:reload-extension');
|
||||
},
|
||||
reloadPage: (path) => {
|
||||
// Can't use Vite's built-in "full-reload" event because it doesn't like our paths, it expects
|
||||
// paths ending in "/index.html"
|
||||
server.ws.send('wxt:reload-page', path);
|
||||
},
|
||||
};
|
||||
internalConfig.logger.info('Created dev server');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user