From d783e7e6eccaf4d95d8998758c99b879e13fcad5 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Mon, 4 Mar 2024 13:06:33 +0900 Subject: [PATCH] feat: accept assets to be specified as input --- packages/vite/src/node/plugins/asset.ts | 27 ++++++++++++++++-- .../__tests__/backend-integration.spec.ts | 15 ++++++++++ .../frontend/entrypoints/icon.png | Bin 0 -> 3395 bytes 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 playground/backend-integration/frontend/entrypoints/icon.png diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index ac18fb1772a4d6..9d6e3f9d1710d4 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -205,7 +205,15 @@ export function assetPlugin(config: ResolvedConfig): Plugin { } } - return `export default ${JSON.stringify(url)}` + return { + code: `export default ${JSON.stringify(url)}`, + // Force rollup to keep this module from being shared between other entry points if it's an entrypoint. + // If the resulting chunk is empty, it will be removed in generateBundle. + moduleSideEffects: + config.command === 'build' && this.getModuleInfo(id)?.isEntry + ? 'no-treeshake' + : false, + } }, renderChunk(code, chunk, opts) { @@ -224,6 +232,19 @@ export function assetPlugin(config: ResolvedConfig): Plugin { }, generateBundle(_, bundle) { + // Remove empty entry point file + for (const file in bundle) { + const chunk = bundle[file] + if ( + chunk.type === 'chunk' && + chunk.isEntry && + chunk.moduleIds.length === 1 && + config.assetsInclude(chunk.moduleIds[0]) + ) { + delete bundle[file] + } + } + // do not emit assets for SSR build if ( config.command === 'build' && @@ -340,7 +361,7 @@ async function fileToBuiltUrl( const content = await fsp.readFile(file) let url: string - if (shouldInline(config, file, id, content, forceInline)) { + if (shouldInline(config, file, id, content, pluginContext, forceInline)) { if (config.build.lib && isGitLfsPlaceholder(content)) { config.logger.warn( colors.yellow(`Inlined file ${id} was not downloaded via Git LFS`), @@ -405,9 +426,11 @@ const shouldInline = ( file: string, id: string, content: Buffer, + pluginContext: PluginContext, forceInline: boolean | undefined, ): boolean => { if (config.build.lib) return true + if (pluginContext.getModuleInfo(id)?.isEntry) return false if (forceInline !== undefined) return forceInline let limit: number if (typeof config.build.assetsInlineLimit === 'function') { diff --git a/playground/backend-integration/__tests__/backend-integration.spec.ts b/playground/backend-integration/__tests__/backend-integration.spec.ts index 563e03b5f4e7c9..669239af237846 100644 --- a/playground/backend-integration/__tests__/backend-integration.spec.ts +++ b/playground/backend-integration/__tests__/backend-integration.spec.ts @@ -6,8 +6,10 @@ import { getColor, isBuild, isServe, + listAssets, page, readManifest, + serverLogs, untilBrowserLogAfter, untilUpdated, } from '~utils' @@ -39,6 +41,7 @@ describe.runIf(isBuild)('build', () => { const scssAssetEntry = manifest['nested/blue.scss'] const imgAssetEntry = manifest['../images/logo.png'] const dirFooAssetEntry = manifest['../../dir/foo.css'] + const iconEntrypointEntry = manifest['icon.png'] expect(htmlEntry.css.length).toEqual(1) expect(htmlEntry.assets.length).toEqual(1) expect(cssAssetEntry?.file).not.toBeUndefined() @@ -53,6 +56,7 @@ describe.runIf(isBuild)('build', () => { expect(dirFooAssetEntry).not.toBeUndefined() // '\\' should not be used even on windows // use the entry name expect(dirFooAssetEntry.file).toMatch('assets/bar-') + expect(iconEntrypointEntry?.file).not.toBeUndefined() }) test('CSS imported from JS entry should have a non-nested chunk name', () => { @@ -61,6 +65,17 @@ describe.runIf(isBuild)('build', () => { expect(mainTsEntryCss.length).toBe(1) expect(mainTsEntryCss[0].replace('assets/', '')).not.toContain('/') }) + + test('entrypoint assets should not generate empty JS file', () => { + expect(serverLogs).not.toContainEqual( + 'Generated an empty chunk: "icon.png".', + ) + + const assets = listAssets('dev') + expect(assets).not.toContainEqual( + expect.stringMatching(/icon.png-[-\w]{8}\.js$/), + ) + }) }) describe.runIf(isServe)('serve', () => { diff --git a/playground/backend-integration/frontend/entrypoints/icon.png b/playground/backend-integration/frontend/entrypoints/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4388bfdca3d4d778d2f25a5861599f8f19a1177c GIT binary patch literal 3395 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 literal 0 HcmV?d00001