From 4ad42723e6de6d26d3d2a68fe893126f04e14e43 Mon Sep 17 00:00:00 2001 From: Bret Comnes Date: Fri, 26 Jun 2026 18:40:13 -0700 Subject: [PATCH] Add default asset loaders to esbuild config Configure default esbuild loaders for common image, icon, and font file types so CSS and client bundles can reference assets without custom settings. Add integration coverage for CSS asset imports, including inlined GIF data URLs and emitted WOFF2 font files. Document how to extend DOMStack's esbuild defaults and how to reset convenience defaults for full customization. Closes #230 --- README.md | 38 ++++++++++++++++++ lib/build-esbuild/index.js | 17 +++++++- test-cases/general-features/index.test.js | 14 +++++++ .../general-features/src/globals/global.css | 3 ++ .../src/globals/test-font.woff2 | 1 + .../src/globals/test-icon.gif | Bin 0 -> 43 bytes 6 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 test-cases/general-features/src/globals/test-font.woff2 create mode 100644 test-cases/general-features/src/globals/test-icon.gif diff --git a/README.md b/README.md index f01d8e19..5e111a07 100644 --- a/README.md +++ b/README.md @@ -1176,6 +1176,44 @@ const esbuildSettingsOverride = async (esbuildSettings: BuildOptions): Promise => { + return { + ...esbuildSettings, + loader: { + ...esbuildSettings.loader, + '.wasm': 'file', + }, + } +} + +export default esbuildSettingsOverride +``` + +If you want full control, reset DOMStack's convenience defaults back to esbuild's defaults while preserving the required DOMStack build wiring (`entryPoints`, `outdir`, `outbase`, etc.). From there, define only the settings you want: + +```typescript +import type { BuildOptions } from '@domstack/static' + +const esbuildSettingsOverride = async (esbuildSettings: BuildOptions): Promise => { + return { + ...esbuildSettings, + jsx: undefined, + jsxImportSource: undefined, + loader: { + '.png': 'file', + '.svg': 'text', + }, + } +} + +export default esbuildSettingsOverride +``` + Important esbuild settings you may want to set here are: - [target](https://esbuild.github.io/api/#target) - Set the `target` to make `esbuild` run a few small transforms on your CSS and JS code. diff --git a/lib/build-esbuild/index.js b/lib/build-esbuild/index.js index e46f868d..636726a6 100644 --- a/lib/build-esbuild/index.js +++ b/lib/build-esbuild/index.js @@ -204,7 +204,22 @@ async function assembleBuildOpts (src, dest, siteData, opts, modeOpts = {}) { entryNames: watch ? '[dir]/[name]' : '[dir]/[name]-[hash]', chunkNames: 'chunks/[ext]/[name]-[hash]', jsx: 'automatic', - jsxImportSource: 'preact' + jsxImportSource: 'preact', + loader: { + '.png': 'dataurl', + '.jpg': 'dataurl', + '.jpeg': 'dataurl', + '.gif': 'dataurl', + '.svg': 'dataurl', + '.webp': 'dataurl', + '.avif': 'dataurl', + '.ico': 'file', + '.woff': 'file', + '.woff2': 'file', + '.ttf': 'file', + '.eot': 'file', + '.otf': 'file', + } } const esbuildSettingsExtends = siteData.esbuildSettings diff --git a/test-cases/general-features/index.test.js b/test-cases/general-features/index.test.js index 616ba4dc..55d6dd9f 100644 --- a/test-cases/general-features/index.test.js +++ b/test-cases/general-features/index.test.js @@ -119,6 +119,20 @@ test.describe('general-features', () => { assert.fail('Failed to verify global.data.js output in template vars: ' + error.message) } + // Verify that CSS asset loaders work: images inline as data URLs, fonts are emitted as files + const globalCssFile = files.find(f => f.relname.match(/global-([A-Z0-9])\w+\.css$/)) + if (globalCssFile) { + const cssContent = await readFile(path.join(dest, globalCssFile.relname), 'utf8') + assert.ok( + cssContent.includes('data:image/gif;base64,'), + 'global CSS inlines GIF image as a base64 data URL' + ) + } else { + assert.fail('Could not find global CSS output file to verify asset loaders') + } + + const woff2Files = files.filter(f => f.relname.endsWith('.woff2')) + assert.ok(woff2Files.length > 0, 'woff2 font file was emitted to the output directory') // Special test for global.data.js blogPostsHtml const indexPath = path.join(dest, 'index.html') try { diff --git a/test-cases/general-features/src/globals/global.css b/test-cases/general-features/src/globals/global.css index ccb97b21..45e5274e 100644 --- a/test-cases/general-features/src/globals/global.css +++ b/test-cases/general-features/src/globals/global.css @@ -1,2 +1,5 @@ @import 'mine.css/dist/mine.css'; @import 'mine.css/dist/layout.css'; + +.test-asset-icon { background-image: url('./test-icon.gif'); } +@font-face { font-family: 'TestFont'; src: url('./test-font.woff2'); } diff --git a/test-cases/general-features/src/globals/test-font.woff2 b/test-cases/general-features/src/globals/test-font.woff2 new file mode 100644 index 00000000..db76e17b --- /dev/null +++ b/test-cases/general-features/src/globals/test-font.woff2 @@ -0,0 +1 @@ +wOF2placeholder \ No newline at end of file diff --git a/test-cases/general-features/src/globals/test-icon.gif b/test-cases/general-features/src/globals/test-icon.gif new file mode 100644 index 0000000000000000000000000000000000000000..09e3508f57d15f994ffe55a17c9aa8d3b6779229 GIT binary patch literal 43 scmZ?wbhEHbWMp7uXkcLY|NlP&1B2pE7Dgb&paUX6G7L;iK8y_30LaP*0RR91 literal 0 HcmV?d00001