From ff99e7ebbeb5816b79698331dbe19dacdd55849a Mon Sep 17 00:00:00 2001 From: Luke Bennett Date: Fri, 25 Sep 2026 13:58:47 +1000 Subject: [PATCH] Keep generated assets out of dist (#656) * Keep generated assets out of dist * Consolidate generated asset comments --- docs/STYLING.md | 2 +- .../@luke-ui/react/scripts/build-icons.ts | 5 ++- .../@luke-ui/react/scripts/build-themes.ts | 4 +-- packages/@luke-ui/react/vite.config.ts | 35 ++++--------------- turbo.json | 2 +- 5 files changed, 12 insertions(+), 36 deletions(-) diff --git a/docs/STYLING.md b/docs/STYLING.md index 8dff1706..fa606b53 100644 --- a/docs/STYLING.md +++ b/docs/STYLING.md @@ -27,7 +27,7 @@ Paths below are rooted in `packages/@luke-ui/react/src/`. | `core/styles/` | Stylesheet graph, layers, reset, theme root, recipe engine, modules registry, utilities, and shared helpers that emit no CSS on their own | | Component and primitive folders under `core/` | Colocate `recipe.css.ts` (public) and `styles.css.ts` (private) beside the owner | | `theme/` | Token contract, `defineTheme`, foundations, bundles, and the build pipeline | -| `scripts/build-themes.ts` | Writes `dist/themes//stylesheet.css` | +| `scripts/build-themes.ts` | Writes `.generated/themes//stylesheet.css`, which `vp pack` copies into `dist/` | Stable entry points: diff --git a/packages/@luke-ui/react/scripts/build-icons.ts b/packages/@luke-ui/react/scripts/build-icons.ts index 582dedf5..283a3b4f 100644 --- a/packages/@luke-ui/react/scripts/build-icons.ts +++ b/packages/@luke-ui/react/scripts/build-icons.ts @@ -7,7 +7,7 @@ import { parse } from 'node-html-parser'; const DEFAULT_VIEWBOX = '0 0 24 24'; const SPRITE_NAME = 'spritesheet.svg'; const INPUT_DIR = path.resolve(process.cwd(), 'icons'); -const OUTPUT_DIR = path.resolve(process.cwd(), 'dist'); +// See `vite.config.ts`'s `copy` config for why this writes to `.generated/` instead of `dist/`. const GENERATED_DIR = path.resolve(process.cwd(), '.generated'); const ICON_DATA_TS_PATH = path.resolve(GENERATED_DIR, 'icon-data.ts'); const ICON_MASK_DATA_TS_PATH = path.resolve(GENERATED_DIR, 'icon-mask-data.ts'); @@ -24,7 +24,6 @@ async function writeIfChanged(filepath: string, content: string): Promise `\t'${n}': 'url("data:image/svg+xml,${encodeURIComponent( } as const; `; - const spritePath = path.join(OUTPUT_DIR, SPRITE_NAME); + const spritePath = path.join(GENERATED_DIR, SPRITE_NAME); const [spriteChanged, iconDataChanged, iconMaskDataChanged] = await Promise.all([ writeIfChanged(spritePath, sprite), writeIfChanged(ICON_DATA_TS_PATH, iconDataTs), diff --git a/packages/@luke-ui/react/scripts/build-themes.ts b/packages/@luke-ui/react/scripts/build-themes.ts index d7e40f5b..dac21cb1 100644 --- a/packages/@luke-ui/react/scripts/build-themes.ts +++ b/packages/@luke-ui/react/scripts/build-themes.ts @@ -13,11 +13,11 @@ async function main() { await Promise.all( themes.map(async (theme) => { const outputPath = fileURLToPath( - new URL(`../dist/themes/${theme.name}/stylesheet.css`, import.meta.url), + new URL(`../.generated/themes/${theme.name}/stylesheet.css`, import.meta.url), ); await mkdir(dirname(outputPath), { recursive: true }); await writeFile(outputPath, defineTheme(theme), 'utf8'); - process.stdout.write(`Generated dist/themes/${theme.name}/stylesheet.css\n`); + process.stdout.write(`Generated .generated/themes/${theme.name}/stylesheet.css\n`); }), ); } diff --git a/packages/@luke-ui/react/vite.config.ts b/packages/@luke-ui/react/vite.config.ts index 17a052a5..e5fe7df5 100644 --- a/packages/@luke-ui/react/vite.config.ts +++ b/packages/@luke-ui/react/vite.config.ts @@ -1,9 +1,7 @@ -import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { makeIdFiltersToMatchWithQuery } from '@rolldown/pluginutils'; import { vanillaExtractPlugin } from '@vanilla-extract/rollup-plugin'; import react from '@vitejs/plugin-react'; -import { readdir, rm } from 'node:fs/promises'; import { transformSync } from 'oxc-transform-react'; import type { Plugin } from 'vite-plus'; import { defineConfig } from 'vite-plus'; @@ -14,8 +12,6 @@ const recipeEngineSource = fileURLToPath( new URL('./src/core/styles/recipe-engine.ts', import.meta.url), ); const workspaceRoot = fileURLToPath(new URL('../../../', import.meta.url)); -const distDir = fileURLToPath(new URL('dist/', import.meta.url)); -const preservedDistFiles = new Set(['spritesheet.svg', 'docs', 'themes']); const assetExports = [ './stylesheet.css', './spritesheet.svg', @@ -49,7 +45,6 @@ export default defineConfig({ excludeEntrypoints: assetExports, profile: 'esm-only', }, - clean: false, deps: { neverBundle: Object.keys(packageJson.peerDependencies), }, @@ -67,10 +62,13 @@ export default defineConfig({ // Built for extraction; not consumer subpaths. exclude: ['stylesheet'], }, + // `generate` writes these to `.generated/`, not `dist/`, so a cache replay of one Turbo task + // can't overwrite the other's outputs by restoring over a shared directory. + copy: [ + { from: '.generated/spritesheet.svg', to: 'dist' }, + { from: '.generated/themes/*/stylesheet.css', to: 'dist', flatten: false }, + ], format: ['esm'], - hooks: { - 'build:prepare': cleanDistExceptPreservedFiles, - }, outputOptions: { assetFileNames: '[name][extname]', }, @@ -149,24 +147,3 @@ function reactCompilerPlugin(): Plugin { }, }; } - -async function cleanDistExceptPreservedFiles() { - let entries: Array; - - try { - entries = await readdir(distDir); - } catch (error) { - if (error instanceof Error && 'code' in error && error.code === 'ENOENT') { - return; - } - - throw error; - } - - await Promise.all( - entries.flatMap((entry) => { - if (preservedDistFiles.has(entry)) return []; - return [rm(join(distDir, entry), { force: true, recursive: true })]; - }), - ); -} diff --git a/turbo.json b/turbo.json index 608daf3c..e50fa927 100644 --- a/turbo.json +++ b/turbo.json @@ -131,7 +131,7 @@ "!**/*.test.ts", "!**/*.test.tsx" ], - "outputs": [".generated/**", "dist/spritesheet.svg", "dist/themes/**", "src/routeTree.gen.ts"] + "outputs": [".generated/**", "src/routeTree.gen.ts"] }, "test": { "dependsOn": ["^build", "generate"], -- 2.51.2