From 8be53efd248a69875bf5aa7cfb27e084ac6b3624 Mon Sep 17 00:00:00 2001 From: Aaron Date: Sat, 1 Jul 2023 13:58:44 -0500 Subject: [PATCH] chore: Refactor build output type (#19) --- src/core/build/buildEntrypoints.ts | 55 +++++++++++++++++------------- src/core/log/printBuildSummary.ts | 5 ++- src/core/types/external.ts | 12 ++++++- src/core/utils/manifest.ts | 12 ++++--- src/index.ts | 44 ++++++++++++++++++++---- 5 files changed, 91 insertions(+), 37 deletions(-) diff --git a/src/core/build/buildEntrypoints.ts b/src/core/build/buildEntrypoints.ts index dc316d40..0160db8e 100644 --- a/src/core/build/buildEntrypoints.ts +++ b/src/core/build/buildEntrypoints.ts @@ -1,6 +1,11 @@ import * as vite from 'vite'; -import { BuildOutput, Entrypoint, InternalConfig } from '../types'; -import { groupEntrypoints } from '../utils/groupEntrypoints'; +import { + BuildOutput, + BuildStepOutput, + Entrypoint, + EntrypointGroup, + InternalConfig, +} from '../types'; import * as plugins from '../vite-plugins'; import { removeEmptyDirs } from '../utils/removeEmptyDirs'; import { getEntrypointBundlePath } from '../utils/entrypoints'; @@ -9,25 +14,22 @@ import fs from 'fs-extra'; import { dirname, resolve } from 'path'; export async function buildEntrypoints( - entrypoints: Entrypoint[], + groups: EntrypointGroup[], config: InternalConfig, -): Promise { - const groups = groupEntrypoints(entrypoints); - - const outputs: BuildOutput[] = []; +): Promise> { + const steps: BuildStepOutput[] = []; for (const group of groups) { - const output = Array.isArray(group) + const step = Array.isArray(group) ? await buildMultipleEntrypoints(group, config) : await buildSingleEntrypoint(group, config); - outputs.push(output); + steps.push(step); } - const publicOutput = await copyPublicDirectory(config); - outputs.push(publicOutput); + const publicAssets = await copyPublicDirectory(config); // Remove any empty directories from moving outputs around await removeEmptyDirs(config.outDir); - return outputs.flat(); + return { publicAssets, steps }; } /** @@ -36,7 +38,7 @@ export async function buildEntrypoints( async function buildSingleEntrypoint( entrypoint: Entrypoint, config: InternalConfig, -): Promise { +): Promise { // Should this entrypoint be wrapped by the vite-plugins/virtualEntrypoint plugin? const isVirtual = ['background', 'content-script'].includes(entrypoint.type); const entry = isVirtual @@ -74,7 +76,10 @@ async function buildSingleEntrypoint( ) as vite.InlineConfig; const result = await vite.build(entryConfig); - return getBuildOutput(result); + return { + entrypoints: entrypoint, + chunks: getBuildOutputChunks(result), + }; } /** @@ -83,7 +88,7 @@ async function buildSingleEntrypoint( async function buildMultipleEntrypoints( entrypoints: Entrypoint[], config: InternalConfig, -): Promise { +): Promise { const multiPage: vite.InlineConfig = { plugins: [plugins.multipageMove(entrypoints, config)], build: { @@ -110,12 +115,15 @@ async function buildMultipleEntrypoints( ) as vite.InlineConfig; const result = await vite.build(entryConfig); - return getBuildOutput(result); + return { + entrypoints, + chunks: getBuildOutputChunks(result), + }; } -function getBuildOutput( +function getBuildOutputChunks( result: Awaited>, -): BuildOutput { +): BuildStepOutput['chunks'] { if ('on' in result) throw Error('wxt does not support vite watch mode.'); if (Array.isArray(result)) return result.flatMap(({ output }) => output); return result.output; @@ -123,19 +131,19 @@ function getBuildOutput( async function copyPublicDirectory( config: InternalConfig, -): Promise { - if (!(await fs.exists(config.publicDir))) return []; +): Promise { + const publicAssets: BuildOutput['publicAssets'] = []; + if (!(await fs.exists(config.publicDir))) return publicAssets; const files = await glob('**/*', { cwd: config.publicDir }); - const outputs: BuildOutput = []; for (const file of files) { const srcPath = resolve(config.publicDir, file); const outPath = resolve(config.outDir, file); await fs.ensureDir(dirname(outPath)); await fs.copyFile(srcPath, outPath); - outputs.push({ + publicAssets.push({ type: 'asset', fileName: file, name: file, @@ -143,5 +151,6 @@ async function copyPublicDirectory( source: await fs.readFile(srcPath), }); } - return outputs; + + return publicAssets; } diff --git a/src/core/log/printBuildSummary.ts b/src/core/log/printBuildSummary.ts index 81f97ec9..b2c877cf 100644 --- a/src/core/log/printBuildSummary.ts +++ b/src/core/log/printBuildSummary.ts @@ -9,7 +9,10 @@ export async function printBuildSummary( output: BuildOutput, config: InternalConfig, ) { - const chunks = output.sort((l, r) => { + const chunks = [ + ...output.steps.flatMap((step) => step.chunks), + ...output.publicAssets, + ].sort((l, r) => { const lWeight = CHUNK_SORT_WEIGHTS[l.fileName] ?? CHUNK_SORT_WEIGHTS[extname(l.fileName)] ?? diff --git a/src/core/types/external.ts b/src/core/types/external.ts index c4c9d8d2..5ead517e 100644 --- a/src/core/types/external.ts +++ b/src/core/types/external.ts @@ -1,6 +1,7 @@ import * as vite from 'vite'; import { Manifest } from 'webextension-polyfill'; import { UnimportOptions } from 'unimport'; +import { EntrypointGroup } from '.'; export interface InlineConfig { root?: string; @@ -29,7 +30,16 @@ export interface WxtInlineViteConfig build?: Omit; } -export type BuildOutput = (vite.Rollup.OutputChunk | vite.Rollup.OutputAsset)[]; +export interface BuildOutput { + manifest: Manifest.WebExtensionManifest; + publicAssets: vite.Rollup.OutputAsset[]; + steps: BuildStepOutput[]; +} + +export interface BuildStepOutput { + entrypoints: EntrypointGroup; + chunks: (vite.Rollup.OutputChunk | vite.Rollup.OutputAsset)[]; +} export interface WxtDevServer extends vite.ViteDevServer { logger: Logger; diff --git a/src/core/utils/manifest.ts b/src/core/utils/manifest.ts index 3da3915c..ee7b1db6 100644 --- a/src/core/utils/manifest.ts +++ b/src/core/utils/manifest.ts @@ -29,7 +29,7 @@ export async function writeManifest( await fs.ensureDir(config.outDir); await fs.writeFile(resolve(config.outDir, 'manifest.json'), str, 'utf-8'); - output.unshift({ + output.publicAssets.unshift({ type: 'asset', fileName: 'manifest.json', name: 'manifest', @@ -43,7 +43,7 @@ export async function writeManifest( */ export async function generateMainfest( entrypoints: Entrypoint[], - buildOutput: BuildOutput, + buildOutput: Omit, config: InternalConfig, ): Promise { const pkg = await getPackageJson(config); @@ -100,7 +100,7 @@ function simplifyVersion(versionName: string): string { function addEntrypoints( manifest: Manifest.WebExtensionManifest, entrypoints: Entrypoint[], - buildOutput: BuildOutput, + buildOutput: Omit, config: InternalConfig, ): void { const entriesByType = entrypoints.reduce< @@ -351,12 +351,14 @@ function addDevModeCsp( */ function getContentScriptCssFiles( contentScripts: ContentScriptEntrypoint[], - buildOutput: BuildOutput, + buildOutput: Omit, ): string[] | undefined { const css: string[] = []; + const allChunks = buildOutput.steps.flatMap((step) => step.chunks); + contentScripts.forEach((script) => { - const relatedCss = buildOutput.find( + const relatedCss = allChunks.find( (chunk) => chunk.fileName === `assets/${script.name}.css`, ); if (relatedCss) css.push(relatedCss.fileName); diff --git a/src/index.ts b/src/index.ts index ba0926d3..d86ff94a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ import { WxtDevServer, InlineConfig, InternalConfig, + EntrypointGroup, } from './core/types'; import { getInternalConfig } from './core/utils/getInternalConfig'; import { findEntrypoints } from './core/build/findEntrypoints'; @@ -16,6 +17,8 @@ import * as vite from 'vite'; import { findOpenPort } from './core/utils/findOpenPort'; import { formatDuration } from './core/utils/formatDuration'; import { createWebExtRunner } from './core/runners/createWebExtRunner'; +import { groupEntrypoints } from './core/utils/groupEntrypoints'; +import { Manifest } from 'webextension-polyfill'; export { version } from '../package.json'; export * from './core/types/external'; @@ -90,14 +93,9 @@ async function buildInternal(config: InternalConfig): Promise { await fs.rm(config.outDir, { recursive: true, force: true }); await fs.ensureDir(config.outDir); - // Build const entrypoints = await findEntrypoints(config); - await generateTypesDir(entrypoints, config); - const output = await buildEntrypoints(entrypoints, config); - - // Write manifest - const manifest = await generateMainfest(entrypoints, output, config); - await writeManifest(manifest, output, config); + const groups = groupEntrypoints(entrypoints); + const { output } = await rebuild(config, groups); // Post-build config.logger.success( @@ -107,3 +105,35 @@ async function buildInternal(config: InternalConfig): Promise { return output; } + +export async function rebuild( + config: InternalConfig, + entrypointGroups: EntrypointGroup[], + existingOutput: Omit = { + steps: [], + publicAssets: [], + }, +): Promise<{ output: BuildOutput; manifest: Manifest.WebExtensionManifest }> { + // Build + const allEntrypoints = await findEntrypoints(config); + await generateTypesDir(allEntrypoints, config); + const buildOutput = await buildEntrypoints(entrypointGroups, config); + + const manifest = await generateMainfest(allEntrypoints, buildOutput, config); + const output: BuildOutput = { + manifest, + ...buildOutput, + }; + + // Write manifest + await writeManifest(manifest, output, config); + + return { + output: { + manifest, + steps: [...existingOutput.steps, ...output.steps], + publicAssets: [...existingOutput.publicAssets, ...output.publicAssets], + }, + manifest, + }; +}