Просмотр исходного кода

move font assets to /public/static/fonts

Yuki Takei 1 месяц назад
Родитель
Сommit
1f3e1f556e

+ 8 - 1
.kiro/specs/migrate-to-turbopack/design.md

@@ -366,11 +366,17 @@ interface ResolveAliasConfig {
 - Collects all `src/**/*.vendor-styles.ts` as entry points via `fs.globSync`
 - Outputs to `src/` preserving directory structure, with `.vendor-styles.prebuilt.js` suffix
 - Uses `rollupOptions.input` (not `lib.entry`) for multi-entry compilation
+- Includes `moveAssetsToPublic` Vite plugin that runs at `closeBundle`:
+  1. Moves emitted font/asset files from `src/assets/` to `public/static/fonts/`
+  2. Rewrites `/assets/` → `/static/fonts/` URL references in all `.vendor-styles.prebuilt.js` files
+  3. Removes the temporary `src/assets/` directory
+- Fonts are served at `/static/fonts/*` via the existing `express.static(crowi.publicDir)` route (which serves `public/` at `/`)
+- `/public/static/fonts` is git-ignored (build artifact, regenerated by `pre:styles-components`)
 
 **Turborepo Integration**
 - `pre:styles-components` / `dev:pre:styles-components` tasks added as dependencies of `build` and `dev`
 - Inputs: `vite.vendor-styles-components.ts`, `src/**/*.vendor-styles.ts`, `package.json`
-- Outputs: `src/**/*.vendor-styles.prebuilt.js`
+- Outputs: `src/**/*.vendor-styles.prebuilt.js`, `public/static/fonts/**`
 
 **Vendor-Styles Entry Points (8 files covering 13 CSS imports)**
 
@@ -392,6 +398,7 @@ interface ResolveAliasConfig {
 - Multiple CSS imports for the same component are combined in a single entry point (e.g., `Renderer.vendor-styles.ts` imports 3 CSS files)
 - Shared entry points (e.g., `GrowiEditor.vendor-styles.ts`) are referenced from multiple components via relative path
 - SSR note: CSS is injected at runtime via `<style>` tags, so it is not available during SSR. Most consuming components already use `next/dynamic` with `ssr: false`, avoiding FOUC
+- **Font/asset handling**: When vendor CSS references external assets (e.g., KaTeX's `@font-face` declarations referencing `fonts/KaTeX_*.woff2`), Vite emits them to `src/assets/` during build. The `moveAssetsToPublic` plugin then relocates these to `public/static/fonts/` and rewrites the URL references in prebuilt JS to `/static/fonts/...`. This ensures fonts are served by the existing Express static file middleware without additional server configuration.
 
 #### GlobalSyntaxConversion — `:global` Block-to-Function Form
 

+ 4 - 1
.kiro/specs/migrate-to-turbopack/tasks.md

@@ -44,9 +44,11 @@
 - [x] 4. Precompile vendor CSS via Vite for Turbopack compatibility
 - [x] 4.1 Create Vite config and Turborepo tasks for vendor CSS precompilation
   - Created `vite.vendor-styles-components.ts` — collects all `src/**/*.vendor-styles.ts` as entry points, precompiles via Vite `?inline` suffix
+  - Includes `moveAssetsToPublic` plugin: moves Vite-emitted font files from `src/assets/` to `public/static/fonts/` and rewrites URL references in prebuilt JS (`/assets/` → `/static/fonts/`)
+  - Fonts served at `/static/fonts/*` via existing `express.static(crowi.publicDir)` — no additional Express route needed
   - Added `pre:styles-components` / `dev:pre:styles-components` tasks to `turbo.json` as dependencies of `build` and `dev`
   - Added corresponding npm scripts to `package.json`
-  - Added `/src/**/*.vendor-styles.prebuilt.js` to `.gitignore`
+  - Added `/src/**/*.vendor-styles.prebuilt.js` and `/public/static/fonts` to `.gitignore`
   - _Requirements: 8.3, 8.4, 8.5_
 
 - [x] 4.2 Create vendor-styles entry points and migrate CSS imports from components
@@ -127,3 +129,4 @@
 - **Standalone `:local`**: Turbopack doesn't support standalone `:local` or `&:local` in CSS Modules. Inside `:global(...)` function form, properties are already locally scoped by default, so `&:local` wrappers can simply be removed.
 - **Sass `@extend` in CSS Modules**: `@extend .class` fails when the target is wrapped in `:global(.class)` — Sass doesn't match them as the same selector. Replace with shared selector groups (comma-separated selectors).
 - **handsontable CSS**: `handsontable.full.min.css` contains IE CSS star hacks (`*zoom:1`, `*display:inline`) and `filter:alpha()` that Turbopack's CSS parser (lightningcss) cannot parse. Use `handsontable/dist/handsontable.css` (non-full, non-minified) instead — the "full" variant includes Pikaday which is unused.
+- **Vendor CSS font handling**: When Vite precompiles CSS that references external assets (e.g., KaTeX `@font-face` with `url(fonts/KaTeX_*.woff2)`), it emits asset files to `src/assets/` and rewrites URLs to `/assets/...`. Since `src/assets/` is not served by Express, a `moveAssetsToPublic` Vite plugin was added to relocate fonts to `public/static/fonts/` and rewrite URL references to `/static/fonts/...` in prebuilt JS. This aligns with the existing `public/static/` convention (`/public/static/js`, `/public/static/styles`).

+ 1 - 0
apps/app/.gitignore

@@ -9,6 +9,7 @@
 /build/
 /dist/
 /transpiled/
+/public/static/fonts
 /public/static/js
 /public/static/styles
 /public/uploads

+ 37 - 0
apps/app/vite.vendor-styles-components.ts

@@ -1,5 +1,6 @@
 import fs from 'node:fs';
 import path from 'node:path';
+import type { Plugin } from 'vite';
 import { defineConfig } from 'vite';
 
 // Collect all src/**/*.vendor-styles.ts as entry points
@@ -16,8 +17,44 @@ const entries = fs
     {} as Record<string, string>,
   );
 
+// Move emitted font assets from src/assets/ to public/static/fonts/
+// and rewrite URL references in prebuilt JS files
+function moveAssetsToPublic(): Plugin {
+  return {
+    name: 'move-assets-to-public',
+    closeBundle() {
+      const srcDir = path.resolve(__dirname, 'src/assets');
+      const destDir = path.resolve(__dirname, 'public/static/fonts');
+      if (!fs.existsSync(srcDir)) return;
+
+      // Move font files
+      fs.mkdirSync(destDir, { recursive: true });
+      for (const file of fs.readdirSync(srcDir)) {
+        fs.renameSync(path.join(srcDir, file), path.join(destDir, file));
+      }
+      fs.rmdirSync(srcDir);
+
+      // Rewrite /assets/ -> /static/fonts/ in prebuilt JS files
+      const prebuiltFiles = fs.globSync('src/**/*.vendor-styles.prebuilt.js', {
+        cwd: __dirname,
+      });
+      for (const file of prebuiltFiles) {
+        const filePath = path.resolve(__dirname, file);
+        const content = fs.readFileSync(filePath, 'utf-8');
+        if (content.includes('/assets/')) {
+          fs.writeFileSync(
+            filePath,
+            content.replaceAll('/assets/', '/static/fonts/'),
+          );
+        }
+      }
+    },
+  };
+}
+
 export default defineConfig({
   publicDir: false,
+  plugins: [moveAssetsToPublic()],
   build: {
     outDir: 'src',
     emptyOutDir: false,