@bestcss/vite-plugin 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yuta Ura
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/client.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ // 利用側プロジェクト向けの仮想モジュール型宣言。
2
+ // tsconfig の "types" に "@bestcss/vite-plugin/client" を追加して使う
3
+ declare module "virtual:bestcss/dev-styles" {}
4
+
5
+ declare module "virtual:bestcss/route-css" {
6
+ const manifest: Record<string, string[]> | null;
7
+ export default manifest;
8
+ }
@@ -0,0 +1,31 @@
1
+ import type { Plugin } from "vite";
2
+ export interface BestCssSsrOptions {
3
+ /**
4
+ * ルートファイルのディレクトリ(root からの相対パス。例: "app/routes")。
5
+ * 指定すると各ルートの import グラフから CSS を集めてルート単位に分割し、
6
+ * ルート → CSS の対応表を出力する。renderer 側は
7
+ * `@bestcss/vite-plugin/route-css` の routeCssHrefs で <link> を注入できる
8
+ */
9
+ routesDir?: string;
10
+ }
11
+ export interface BestCssOptions {
12
+ /**
13
+ * ビルド時にクラス名を使用頻度順の短い名前(a, b, ...)へ振り直す。
14
+ * 無効化は、SSR した HTML を長期キャッシュする等でクラス名の
15
+ * ビルド間安定性を優先したい場合を想定している
16
+ *
17
+ * @default true
18
+ */
19
+ minifyClassNames?: boolean;
20
+ /**
21
+ * SSR プロジェクト(client / server の 2 パスビルド)であることを宣言する。
22
+ * client / server どちらの設定にも同じ値を渡せばよい。
23
+ *
24
+ * - クラス名短縮のリネーム表をビルド間で自動共有し、SSR された HTML と
25
+ * 配信 CSS の短縮名を一致させる(ビルドは client → server の順)
26
+ * - routesDir を指定するとルート単位の CSS 分割も有効になる
27
+ */
28
+ ssr?: boolean | BestCssSsrOptions;
29
+ }
30
+ export declare function bestCss(options?: BestCssOptions): Plugin;
31
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAiDnC,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,OAAO,GAAG,iBAAiB,CAAC;CACnC;AAED,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,MAAM,CA0f5D"}
package/dist/index.js ADDED
@@ -0,0 +1,457 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { applyRename, collectImportSources, createRenameMap, dedupeCss, generateClassName, transform, } from "@bestcss/core";
4
+ const TRANSFORM_TARGET_RE = /\.[jt]sx?$/;
5
+ /**
6
+ * 抽出した CSS を「元ファイル名 + この接尾辞」の仮想モジュールとして登録する。
7
+ * .css で終わる id にしておくことで、Vite 自身の CSS パイプライン
8
+ * (postcss / minify / コード分割)にそのまま処理を委ねられる
9
+ */
10
+ const VIRTUAL_CSS_SUFFIX = ".bestcss.css";
11
+ /**
12
+ * ルート単位のスタイル収集エントリ(仮想モジュール)の接頭辞。
13
+ * ルートファイルの import グラフ上の css`` を side-effect import として
14
+ * 集めた「スタイルだけのエントリ」を表す
15
+ */
16
+ const VIRTUAL_ROUTE_PREFIX = "\0bestcss-route:";
17
+ /** ルート → CSS ファイル一覧の対応表(ビルド時にインラインされる) */
18
+ const VIRTUAL_ROUTE_MANIFEST = "virtual:bestcss/route-css";
19
+ const RESOLVED_ROUTE_MANIFEST = "\0virtual:bestcss/route-css";
20
+ /** dev で全ルートのスタイルを HMR 付きで読み込むための仮想モジュール */
21
+ const VIRTUAL_DEV_STYLES = "virtual:bestcss/dev-styles";
22
+ const RESOLVED_DEV_STYLES = "\0virtual:bestcss/dev-styles";
23
+ /**
24
+ * client / server ビルド間で共有する中間生成物の置き場所(root 相対)。
25
+ * node_modules 配下にするのは、VCS に入らず outDir 設定にも依存しないため
26
+ */
27
+ const SHARE_DIR = "node_modules/.bestcss";
28
+ /** 仮想 CSS モジュールの id からハッシュクエリを外し、Map のキーに揃える */
29
+ const stripQuery = (id) => id.split("?")[0] ?? id;
30
+ /** ディレクトリ配下の .ts / .tsx / .js / .jsx を再帰的に列挙する */
31
+ function walkDir(dir) {
32
+ const files = [];
33
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
34
+ const fullPath = path.join(dir, entry.name);
35
+ if (entry.isDirectory()) {
36
+ files.push(...walkDir(fullPath));
37
+ }
38
+ else if (/\.[jt]sx?$/.test(entry.name)) {
39
+ files.push(fullPath);
40
+ }
41
+ }
42
+ return files;
43
+ }
44
+ export function bestCss(options = {}) {
45
+ const minifyClassNames = options.minifyClassNames ?? true;
46
+ const ssr = options.ssr === undefined || options.ssr === false
47
+ ? null
48
+ : options.ssr === true
49
+ ? {}
50
+ : options.ssr;
51
+ const extractedCss = new Map();
52
+ /** 遅延ロードした仮想 CSS の鮮度管理(ソースの mtime) */
53
+ const sourceMtimes = new Map();
54
+ const generatedClassNames = new Set();
55
+ let root = process.cwd();
56
+ let isProduction = false;
57
+ let sharedRenameMap = null;
58
+ const renameMapPath = () => ssr === null ? null : path.resolve(root, SHARE_DIR, "rename-map.json");
59
+ const routeManifestPath = () => path.resolve(root, SHARE_DIR, "route-css.json");
60
+ const routesDirPath = () => ssr?.routesDir === undefined ? null : path.resolve(root, ssr.routesDir);
61
+ const loadSharedRenameMap = () => {
62
+ if (sharedRenameMap !== null) {
63
+ return sharedRenameMap;
64
+ }
65
+ const mapPath = renameMapPath();
66
+ if (mapPath === null || !fs.existsSync(mapPath)) {
67
+ throw new Error(`bestcss: リネーム表 ${mapPath} が見つかりません。` +
68
+ `表はクライアントビルドが書き出すため、` +
69
+ `クライアントビルドを先に実行してください。`);
70
+ }
71
+ sharedRenameMap = new Map(Object.entries(JSON.parse(fs.readFileSync(mapPath, "utf8"))));
72
+ return sharedRenameMap;
73
+ };
74
+ /** ルートファイルの import グラフを辿り、css`` を含むファイルを集める */
75
+ const collectStyledFiles = async (ctx, entryFile) => {
76
+ const visited = new Set();
77
+ const styledFiles = [];
78
+ const walk = async (file) => {
79
+ if (visited.has(file)) {
80
+ return;
81
+ }
82
+ visited.add(file);
83
+ let code;
84
+ try {
85
+ code = fs.readFileSync(file, "utf8");
86
+ }
87
+ catch {
88
+ return;
89
+ }
90
+ const importSources = collectImportSources(code, file);
91
+ // 文字列やコメントに "@bestcss/core" を含むだけのファイル(core 自身の
92
+ // 実装など)を拾わないよう、AST 上の import 指定子で判定する
93
+ if (importSources.includes("@bestcss/core")) {
94
+ styledFiles.push(file);
95
+ }
96
+ for (const spec of importSources) {
97
+ const resolved = await ctx.resolve(spec, file);
98
+ if (resolved === null) {
99
+ continue;
100
+ }
101
+ const resolvedId = stripQuery(resolved.id);
102
+ if (resolvedId.includes("/node_modules/") ||
103
+ !/\.[jt]sx?$/.test(resolvedId) ||
104
+ !fs.existsSync(resolvedId)) {
105
+ continue;
106
+ }
107
+ await walk(resolvedId);
108
+ }
109
+ };
110
+ await walk(entryFile);
111
+ return styledFiles;
112
+ };
113
+ /** チャンクとその static import 先が参照する CSS アセット名を集める */
114
+ const collectChunkCss = (fileName, bundle, seen) => {
115
+ if (seen.has(fileName)) {
116
+ return [];
117
+ }
118
+ seen.add(fileName);
119
+ const output = bundle[fileName];
120
+ if (output === undefined || output.type !== "chunk") {
121
+ return [];
122
+ }
123
+ const css = [...(output.viteMetadata?.importedCss ?? [])];
124
+ for (const imported of output.imports ?? []) {
125
+ css.push(...collectChunkCss(imported, bundle, seen));
126
+ }
127
+ return css;
128
+ };
129
+ /**
130
+ * ルート → CSS ファイル一覧の対応表を書き出し、スタイル収集用の
131
+ * 仮想エントリ(空の JS チャンク)を成果物から取り除く
132
+ */
133
+ const writeRouteCssManifest = (bundle) => {
134
+ const routesDir = routesDirPath();
135
+ if (routesDir === null) {
136
+ return;
137
+ }
138
+ const manifest = {};
139
+ let found = false;
140
+ for (const [fileName, output] of Object.entries(bundle)) {
141
+ const chunk = output;
142
+ if (chunk.type !== "chunk" ||
143
+ !chunk.facadeModuleId?.startsWith(VIRTUAL_ROUTE_PREFIX)) {
144
+ continue;
145
+ }
146
+ found = true;
147
+ const routeFile = chunk.facadeModuleId.slice(VIRTUAL_ROUTE_PREFIX.length);
148
+ const routeKey = path
149
+ .relative(routesDir, routeFile)
150
+ .replace(/\.[jt]sx?$/, "");
151
+ manifest[routeKey] = [
152
+ ...new Set(collectChunkCss(fileName, bundle, new Set())),
153
+ ];
154
+ delete bundle[fileName];
155
+ }
156
+ if (found) {
157
+ const manifestPath = routeManifestPath();
158
+ fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
159
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
160
+ }
161
+ };
162
+ return {
163
+ name: "bestcss",
164
+ // enforce: "pre" にする理由: JSX 変換(@vitejs/plugin-react や esbuild)より
165
+ // 前にユーザーが書いた元ソースを受け取り、css`` の位置情報を保つため
166
+ enforce: "pre",
167
+ config() {
168
+ // route-css ヘルパー(@bestcss/vite-plugin/route-css)は仮想モジュールを
169
+ // import するため、SSR で externalize されると Node がそのまま実行して
170
+ // 解決に失敗する。プラグイン側で noExternal を設定し、利用側の設定を不要にする
171
+ return {
172
+ ssr: { noExternal: ["@bestcss/vite-plugin"] },
173
+ };
174
+ },
175
+ configResolved(config) {
176
+ root = config.root;
177
+ // command ではなく isProduction で判定する理由: @hono/vite-ssg は
178
+ // ビルド中に内部のモジュールランナー(command=serve の設定)で
179
+ // configResolved を再度呼ぶため、command はビルド中でも serve に
180
+ // 上書きされ得る。isProduction はその場合も true を保つ
181
+ isProduction = config.isProduction;
182
+ },
183
+ buildStart() {
184
+ const routesDir = routesDirPath();
185
+ if (routesDir === null) {
186
+ return;
187
+ }
188
+ const environment = this.environment;
189
+ // スタイルエントリは「CSS を出力するクライアントの本番ビルド」にだけ
190
+ // 注入する。input 未定義の環境を除外するのは、SSG 等がビルド中に
191
+ // 走らせる空のクライアント環境(成果物と manifest を汚す)を避けるため
192
+ const input = environment?.config?.build?.rollupOptions?.input;
193
+ const hasInput = Array.isArray(input)
194
+ ? input.length > 0
195
+ : typeof input === "string"
196
+ ? true
197
+ : input !== undefined &&
198
+ input !== null &&
199
+ Object.keys(input).length > 0;
200
+ if (environment?.mode !== "build" ||
201
+ environment.config?.consumer === "server" ||
202
+ !hasInput) {
203
+ return;
204
+ }
205
+ for (const routeFile of walkDir(routesDir)) {
206
+ const routeKey = path
207
+ .relative(routesDir, routeFile)
208
+ .replace(/\.[jt]sx?$/, "");
209
+ this.emitFile({
210
+ type: "chunk",
211
+ id: VIRTUAL_ROUTE_PREFIX + routeFile,
212
+ name: `bestcss-route/${routeKey}`,
213
+ });
214
+ }
215
+ },
216
+ resolveId(source, importer) {
217
+ // moduleSideEffects: true を明示する理由: 注入する CSS import は
218
+ // side-effect import であり、"sideEffects": false を宣言した
219
+ // パッケージ(コンポーネントライブラリ等)の中ではツリーシェイクで
220
+ // 静かに落とされてしまう。利用者の設定に依存せず保持させる
221
+ const asSideEffect = (id) => ({
222
+ id,
223
+ moduleSideEffects: true,
224
+ });
225
+ if (source === VIRTUAL_ROUTE_MANIFEST) {
226
+ return RESOLVED_ROUTE_MANIFEST;
227
+ }
228
+ if (source === VIRTUAL_DEV_STYLES) {
229
+ return asSideEffect(RESOLVED_DEV_STYLES);
230
+ }
231
+ if (source.startsWith(VIRTUAL_ROUTE_PREFIX)) {
232
+ return source;
233
+ }
234
+ // 仮想 CSS モジュールはファイルシステムに存在しないため、
235
+ // 他のリゾルバに渡さずここで解決を確定させる
236
+ const base = stripQuery(source);
237
+ if (!base.endsWith(VIRTUAL_CSS_SUFFIX)) {
238
+ return null;
239
+ }
240
+ if (extractedCss.has(base)) {
241
+ return asSideEffect(source);
242
+ }
243
+ // まだ変換していないソースの仮想 CSS も、元ファイルが実在するなら
244
+ // 解決する(load 側でオンデマンドに変換する)。相対指定は
245
+ // importer 基準で絶対パスに直す
246
+ const absolute = base.startsWith(".") && importer !== undefined
247
+ ? path.resolve(path.dirname(stripQuery(importer)), base)
248
+ : base;
249
+ const sourceFile = absolute.slice(0, -VIRTUAL_CSS_SUFFIX.length);
250
+ if (fs.existsSync(sourceFile)) {
251
+ return asSideEffect(absolute);
252
+ }
253
+ return null;
254
+ },
255
+ async load(id) {
256
+ if (id === RESOLVED_ROUTE_MANIFEST) {
257
+ // 本番はビルド時に対応表をインラインする(実行時 fs アクセス不要)。
258
+ // dev は null(スタイルは dev-styles 経由で注入されるため)
259
+ const manifestPath = routeManifestPath();
260
+ if (isProduction && fs.existsSync(manifestPath)) {
261
+ return `export default ${fs.readFileSync(manifestPath, "utf8")};`;
262
+ }
263
+ return "export default null;";
264
+ }
265
+ if (id === RESOLVED_DEV_STYLES) {
266
+ // dev 専用: 全ルートの import グラフからスタイルを収集して読み込む。
267
+ // 本番ビルドでは空になり、ルート単位のスタイルエントリに置き換わる
268
+ const routesDir = routesDirPath();
269
+ if (isProduction || routesDir === null || !fs.existsSync(routesDir)) {
270
+ return "export {};";
271
+ }
272
+ const styled = new Set();
273
+ for (const routeFile of walkDir(routesDir)) {
274
+ for (const file of await collectStyledFiles(this, routeFile)) {
275
+ styled.add(file);
276
+ }
277
+ }
278
+ return ([...styled]
279
+ .map((f) => `import ${JSON.stringify(f + VIRTUAL_CSS_SUFFIX)};`)
280
+ .join("\n") + "\nexport {};\n");
281
+ }
282
+ if (id.startsWith(VIRTUAL_ROUTE_PREFIX)) {
283
+ // ルートの import グラフを辿り、css`` を含むファイルの仮想 CSS を
284
+ // side-effect import するだけのモジュールを生成する。
285
+ // ルート専用/共有の分割判断は Vite のチャンク分割に委ねる
286
+ const routeFile = id.slice(VIRTUAL_ROUTE_PREFIX.length);
287
+ const styledFiles = await collectStyledFiles(this, routeFile);
288
+ return (styledFiles
289
+ .map((f) => `import ${JSON.stringify(f + VIRTUAL_CSS_SUFFIX)};`)
290
+ .join("\n") + "\nexport {};\n");
291
+ }
292
+ const base = stripQuery(id);
293
+ if (!base.endsWith(VIRTUAL_CSS_SUFFIX)) {
294
+ return null;
295
+ }
296
+ const sourceFile = base.slice(0, -VIRTUAL_CSS_SUFFIX.length);
297
+ let entry = extractedCss.get(base);
298
+ // ソースを JS として import しない利用(routeStyles / dev-styles)でも
299
+ // 内容が新しくなるよう、mtime で鮮度を確認してオンデマンドに変換する
300
+ if (fs.existsSync(sourceFile)) {
301
+ const mtime = fs.statSync(sourceFile).mtimeMs;
302
+ if (entry === undefined || sourceMtimes.get(base) !== mtime) {
303
+ const result = transform(fs.readFileSync(sourceFile, "utf8"), {
304
+ filename: sourceFile,
305
+ });
306
+ sourceMtimes.set(base, mtime);
307
+ if (result === null) {
308
+ // @bestcss/core を import していても css`` がないファイルは
309
+ // 空の CSS として扱う(ENOENT でビルドを壊さない)
310
+ return "";
311
+ }
312
+ entry = { css: result.css, map: result.cssMap };
313
+ extractedCss.set(base, entry);
314
+ for (const className of result.classNames) {
315
+ generatedClassNames.add(className);
316
+ }
317
+ }
318
+ }
319
+ if (entry === undefined) {
320
+ return null;
321
+ }
322
+ // map を添えることで、css.devSourcemap 有効時に DevTools の Styles
323
+ // ペインから元の tsx(css`` の位置)へ辿れるようになる。
324
+ // moduleSideEffects は resolveId に加えてここでも明示する
325
+ // (sideEffects: false 宣言下でのツリーシェイク耐性)
326
+ return { code: entry.css, map: entry.map, moduleSideEffects: true };
327
+ },
328
+ transform(code, id) {
329
+ if (!TRANSFORM_TARGET_RE.test(id) || id.includes("/node_modules/")) {
330
+ return null;
331
+ }
332
+ const result = transform(code, { filename: id });
333
+ if (result === null) {
334
+ // css`` が全て削除された場合、新しいコードに import が残らないため
335
+ // Vite の HMR prune が古い style 要素を除去する。ここでの後始末は不要
336
+ return null;
337
+ }
338
+ const cssId = id + VIRTUAL_CSS_SUFFIX;
339
+ extractedCss.set(cssId, { css: result.css, map: result.cssMap });
340
+ for (const className of result.classNames) {
341
+ generatedClassNames.add(className);
342
+ }
343
+ // サーバー(SSR)向けの変換では CSS import を付与しない。
344
+ // SSR バンドルに必要なのはクラス名だけで、CSS の配信は
345
+ // クライアントビルドの責務のため(サーバーバンドルも汚さない)
346
+ const consumer = this.environment?.config?.consumer;
347
+ if (consumer === "server") {
348
+ let serverCode = result.code;
349
+ // リネーム表の適用を generateBundle ではなく変換時に行う理由:
350
+ // @hono/vite-ssg のように、バンドルを作らずモジュールランナーで
351
+ // サーバーコードを実行して HTML を書き出すツールでは
352
+ // generateBundle が HTML 生成経路を通らないため。
353
+ // dev(vite dev)は表を使わない(クライアント側も bc 名のため)
354
+ if (isProduction && ssr !== null && minifyClassNames) {
355
+ serverCode = applyRename(serverCode, loadSharedRenameMap());
356
+ }
357
+ return { code: serverCode, map: result.map };
358
+ }
359
+ // import URL に CSS の内容ハッシュを付ける理由: ブラウザは ESM モジュールを
360
+ // URL 単位でキャッシュするため、サーバー側の内容更新だけでは再取得されない。
361
+ // モジュールグラフの invalidate(?t= 方式)はグラフの内部状態に依存して
362
+ // 空振りし得たため、内容が変われば URL が必ず変わるこの方式にした
363
+ const versionedCssId = `${cssId}?hash=${generateClassName(result.css)}`;
364
+ // import 行は map 生成後の末尾追記だが、行の追加は既存行の
365
+ // マッピングをずらさないためソースマップはそのまま有効
366
+ return {
367
+ code: `${result.code}\nimport ${JSON.stringify(versionedCssId)};\n`,
368
+ map: result.map,
369
+ };
370
+ },
371
+ hotUpdate(ctx) {
372
+ // dev-styles 経由でのみ読み込まれている仮想 CSS は、元ファイルの
373
+ // 変更を watcher が関連付けられないため、ここで明示的に更新対象に加える
374
+ const cssId = ctx.file + VIRTUAL_CSS_SUFFIX;
375
+ const graph = this.environment?.moduleGraph;
376
+ const mod = graph?.getModuleById?.(cssId);
377
+ if (mod === undefined || mod === null) {
378
+ return;
379
+ }
380
+ return [...ctx.modules, mod];
381
+ },
382
+ generateBundle: {
383
+ // プラグイン全体は enforce: "pre" のため、order: "post" を付けないと
384
+ // Vite 内部(css-post)が CSS アセットを bundle に追加する前に走ってしまう
385
+ order: "post",
386
+ handler(_options, bundle) {
387
+ // 同一内容の css`` が複数ファイルにあると、クラス名は内容ハッシュで
388
+ // 同一に収束する一方、CSS 本文は各仮想モジュールから重複して出力される。
389
+ // Vite の cssMinify(Lightning CSS)も重複をマージするが、minify を
390
+ // 無効にした構成でも「サイズ最適化」の保証が消えないよう自前でも行う
391
+ for (const [fileName, output] of Object.entries(bundle)) {
392
+ if (output.type === "asset" && fileName.endsWith(".css")) {
393
+ output.source = dedupeCss(String(output.source));
394
+ }
395
+ }
396
+ writeRouteCssManifest(bundle);
397
+ if (!minifyClassNames) {
398
+ return;
399
+ }
400
+ const consumer = this.environment?.config?.consumer;
401
+ if (consumer === "server") {
402
+ // サーバービルドは自分の頻度で短縮しない。CSS を持つのは
403
+ // クライアントビルドであり、独立に計算した短縮名は一致しないため。
404
+ // ssr 設定時のみ、共有された表に従って書き換える(transform 時に
405
+ // 適用済みだが、バンドル型 SSR での取りこぼしをここで拾う)
406
+ if (ssr === null) {
407
+ return;
408
+ }
409
+ const sharedMap = loadSharedRenameMap();
410
+ for (const output of Object.values(bundle)) {
411
+ if (output.type === "chunk") {
412
+ output.code = applyRename(output.code, sharedMap);
413
+ }
414
+ }
415
+ return;
416
+ }
417
+ if (generatedClassNames.size === 0) {
418
+ return;
419
+ }
420
+ // 使用頻度は JS チャンク内の静的な出現回数を代理指標にする。
421
+ // 実行時の描画回数は分からないが、全クラスが 1〜3 文字になるため
422
+ // 順位の精度がサイズに与える影響は小さい
423
+ const frequencies = new Map([...generatedClassNames].map((name) => [name, 0]));
424
+ for (const output of Object.values(bundle)) {
425
+ if (output.type !== "chunk") {
426
+ continue;
427
+ }
428
+ for (const matched of output.code.matchAll(/\bbc[a-z0-9]+\b/g)) {
429
+ const count = frequencies.get(matched[0]);
430
+ if (count !== undefined) {
431
+ frequencies.set(matched[0], count + 1);
432
+ }
433
+ }
434
+ }
435
+ const renameMap = createRenameMap(frequencies);
436
+ for (const [fileName, output] of Object.entries(bundle)) {
437
+ if (output.type === "chunk") {
438
+ output.code = applyRename(output.code, renameMap);
439
+ }
440
+ else if (fileName.endsWith(".css")) {
441
+ output.source = applyRename(String(output.source), renameMap);
442
+ }
443
+ }
444
+ // 確定した表を書き出し、後続のサーバービルドに共有する。
445
+ // CSS アセットを持つ環境に限定する理由: SSG 等が走らせる空の
446
+ // クライアント環境が、確定済みの表を上書きするのを防ぐため
447
+ const mapPath = renameMapPath();
448
+ const hasCssAsset = Object.keys(bundle).some((fileName) => fileName.endsWith(".css"));
449
+ if (mapPath !== null && hasCssAsset) {
450
+ fs.mkdirSync(path.dirname(mapPath), { recursive: true });
451
+ fs.writeFileSync(mapPath, JSON.stringify(Object.fromEntries(renameMap), null, 2));
452
+ }
453
+ },
454
+ },
455
+ };
456
+ }
457
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * リクエストパスから、そのルートに必要な CSS ファイル一覧を引く。
3
+ *
4
+ * manifest のキーはルートファイルの相対パス(拡張子なし)。
5
+ * "_" 始まりのキー(_renderer 等の共通ファイル)は全ページに含める。
6
+ * 動的セグメント($id 等)のマッチングは未対応(ADR-0007 参照)
7
+ */
8
+ export declare function matchRouteCss(manifest: Record<string, string[]>, requestPath: string): string[];
9
+ //# sourceMappingURL=route-css-match.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-css-match.d.ts","sourceRoot":"","sources":["../src/route-css-match.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAClC,WAAW,EAAE,MAAM,GAClB,MAAM,EAAE,CAcV"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * リクエストパスから、そのルートに必要な CSS ファイル一覧を引く。
3
+ *
4
+ * manifest のキーはルートファイルの相対パス(拡張子なし)。
5
+ * "_" 始まりのキー(_renderer 等の共通ファイル)は全ページに含める。
6
+ * 動的セグメント($id 等)のマッチングは未対応(ADR-0007 参照)
7
+ */
8
+ export function matchRouteCss(manifest, requestPath) {
9
+ const trimmed = requestPath.replace(/^\/+|\/+$/g, "");
10
+ const candidates = trimmed === "" ? ["index"] : [trimmed, `${trimmed}/index`];
11
+ const files = new Set();
12
+ for (const [routeKey, cssFiles] of Object.entries(manifest)) {
13
+ if (routeKey.startsWith("_") || candidates.includes(routeKey)) {
14
+ for (const file of cssFiles) {
15
+ files.add(file);
16
+ }
17
+ }
18
+ }
19
+ return [...files];
20
+ }
21
+ //# sourceMappingURL=route-css-match.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-css-match.js","sourceRoot":"","sources":["../src/route-css-match.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,QAAkC,EAClC,WAAmB;IAEnB,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IACtD,MAAM,UAAU,GACd,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,OAAO,QAAQ,CAAC,CAAC;IAE7D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5D,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9D,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;gBAC5B,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC;AACpB,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * SSR の renderer から、現在のルートに必要な CSS の href 一覧を得る。
3
+ *
4
+ * manifest(ルート → CSS ファイル一覧)は仮想モジュールとして
5
+ * ビルド時にインラインされるため、実行時のファイルアクセスは発生しない
6
+ * (serverless 環境でも動く)。dev では空配列を返す
7
+ * (スタイルは virtual:bestcss/dev-styles 経由で注入される)
8
+ */
9
+ export declare function routeCssHrefs(requestPath: string): string[];
10
+ //# sourceMappingURL=route-css.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-css.d.ts","sourceRoot":"","sources":["../src/route-css.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAM3D"}
@@ -0,0 +1,18 @@
1
+ import manifestData from "virtual:bestcss/route-css";
2
+ import { matchRouteCss } from "./route-css-match.js";
3
+ /**
4
+ * SSR の renderer から、現在のルートに必要な CSS の href 一覧を得る。
5
+ *
6
+ * manifest(ルート → CSS ファイル一覧)は仮想モジュールとして
7
+ * ビルド時にインラインされるため、実行時のファイルアクセスは発生しない
8
+ * (serverless 環境でも動く)。dev では空配列を返す
9
+ * (スタイルは virtual:bestcss/dev-styles 経由で注入される)
10
+ */
11
+ export function routeCssHrefs(requestPath) {
12
+ const manifest = manifestData;
13
+ if (manifest === null) {
14
+ return [];
15
+ }
16
+ return matchRouteCss(manifest, requestPath).map((file) => `/${file}`);
17
+ }
18
+ //# sourceMappingURL=route-css.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-css.js","sourceRoot":"","sources":["../src/route-css.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,WAAmB;IAC/C,MAAM,QAAQ,GAAG,YAA+C,CAAC;IACjE,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,aAAa,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;AACxE,CAAC"}
package/docs/01-ssr.md ADDED
@@ -0,0 +1,54 @@
1
+ # SSR / MPA フレームワークとの統合
2
+
3
+ 対象: HonoX など、client / server の 2 パスビルドを行う Vite ベースのフレームワーク。
4
+
5
+ ## 設定
6
+
7
+ `ssr` オプションを **client / server どちらのビルド設定にも同じ値で**渡す:
8
+
9
+ ```ts
10
+ // vite.config.ts(client / server 共通)
11
+ bestCss({ ssr: { routesDir: "app/routes" } })
12
+ // ルート単位の CSS 分割が不要なら
13
+ bestCss({ ssr: true })
14
+ ```
15
+
16
+ これで内部的に次が有効になる:
17
+
18
+ 1. **クラス名短縮のリネーム表をビルド間で自動共有** — SSR された HTML と配信 CSS の短縮名が一致する。ビルドは **client → server の順**(順序違反は明示的なエラーで検出される)
19
+ 2. **SSR ビルドに CSS import を付与しない** — サーバーバンドルに必要なのはクラス名だけ
20
+ 3. **ルート単位の CSS 分割**(`routesDir` 指定時) — ルート専用 CSS はそのルートにのみ、共有 CSS は共有ファイルとして配信される
21
+
22
+ ## renderer への `<link>` 注入
23
+
24
+ ```tsx
25
+ // app/routes/_renderer.tsx
26
+ import { routeCssHrefs } from "@bestcss/vite-plugin/route-css";
27
+
28
+ {routeCssHrefs(c.req.path).map((href) => (
29
+ <link href={href} rel="stylesheet" />
30
+ ))}
31
+ ```
32
+
33
+ ルート → CSS の対応表はビルド時にインラインされるため、実行時のファイルアクセスは不要(serverless でも動く)。dev では空配列を返す。
34
+
35
+ 制限: 動的セグメント(`$id` 等)のパスマッチングは未対応。
36
+
37
+ ## dev のスタイル読み込み
38
+
39
+ クライアントエントリで仮想モジュールを 1 行 import する。dev では全ルートのスタイルを HMR 付きで収集し、本番ビルドでは空になる:
40
+
41
+ ```ts
42
+ // app/client.ts
43
+ import "virtual:bestcss/dev-styles";
44
+ ```
45
+
46
+ 仮想モジュールの型は tsconfig に追加する:
47
+
48
+ ```json
49
+ { "compilerOptions": { "types": ["vite/client", "@bestcss/vite-plugin/client"] } }
50
+ ```
51
+
52
+ ## リネーム表を使わない選択肢
53
+
54
+ `minifyClassNames: false` にすると内容ハッシュ名(`bc...`)のまま出力される。内容ハッシュは独立したビルド間でも決定的に一致するため、表の共有なしで HTML と CSS が一致する([core: 内部のしくみ](../../core/docs/02-how-it-works.md) 参照)。
package/docs/index.md ADDED
@@ -0,0 +1,84 @@
1
+ # @bestcss/vite-plugin ドキュメント
2
+
3
+ このディレクトリはパッケージに同梱されており、インストールされているバージョンと常に一致する。**css`` の文法や内部のしくみは [core のドキュメント](../../core/docs/index.md)**(`node_modules/@bestcss/core/docs/`)を参照。
4
+
5
+ ## セットアップ
6
+
7
+ ```sh
8
+ pnpm add @bestcss/core
9
+ pnpm add -D @bestcss/vite-plugin
10
+ ```
11
+
12
+ ```ts
13
+ // vite.config.ts
14
+ import { bestCss } from "@bestcss/vite-plugin";
15
+ import react from "@vitejs/plugin-react";
16
+ import { defineConfig } from "vite";
17
+
18
+ export default defineConfig({
19
+ plugins: [react(), bestCss()],
20
+ css: { devSourcemap: true }, // 任意: DevTools から css`` の元位置へ辿れる
21
+ });
22
+ ```
23
+
24
+ ```tsx
25
+ import { css } from "@bestcss/core";
26
+
27
+ const button = css`
28
+ padding: 8px 16px;
29
+
30
+ &:hover {
31
+ opacity: 0.8;
32
+ }
33
+ `;
34
+
35
+ export const Button = () => <button className={button}>Click</button>;
36
+ ```
37
+
38
+ 書ける文法の詳細は [core: css`` の文法](../../core/docs/01-syntax.md)。
39
+
40
+ ## オプション
41
+
42
+ ```ts
43
+ bestCss({
44
+ minifyClassNames?: boolean, // デフォルト true
45
+ ssr?: boolean | { routesDir?: string },
46
+ })
47
+ ```
48
+
49
+ - **minifyClassNames** — 本番ビルドでクラス名を使用頻度順の短い名前(`a`, `b`, ...)へ振り直す。dev では常に内容ハッシュ名(`bc...`)。`false` は SSR した HTML を長期キャッシュする等、ビルド間の名前安定性を優先したい場合
50
+ - **ssr** — SSR プロジェクトの宣言。[SSR / MPA 統合](./01-ssr.md) を参照
51
+
52
+ ## テスト(Vitest)
53
+
54
+ css`` はビルド時変換が前提のため、プラグインなしでテストを実行すると実行時エラーになる。`vitest.config.ts` に同じプラグインを並べる:
55
+
56
+ ```ts
57
+ import { bestCss } from "@bestcss/vite-plugin";
58
+ import { defineConfig } from "vitest/config";
59
+
60
+ export default defineConfig({
61
+ plugins: [bestCss()],
62
+ });
63
+ ```
64
+
65
+ ## CSS のコード分割
66
+
67
+ 抽出した CSS は Vite のチャンクグラフに乗るため、Vite 標準のチャンク制御がそのまま使える:
68
+
69
+ - `build.cssCodeSplit: false` — 全 CSS を 1 ファイルに集約
70
+ - `build.rollupOptions.output.codeSplitting` — 仮想 CSS の id は「元ファイルパス + `.bestcss.css`」なので、`test` 正規表現でディレクトリ・ファイル名単位のグループ化ができる
71
+ - 動的 `import()` 境界で CSS も遅延ロードされる
72
+
73
+ ## トラブルシューティング
74
+
75
+ - 変換対象は `@bestcss/core` から `css` を import しているファイルのみ
76
+ - 「css`` が実行時に呼ばれました」エラー = プラグイン未設定の環境でコードが実行された(素の Vitest 等)。上記のテスト設定を参照
77
+
78
+ ## 制限: sideEffects: false のパッケージ内で使う場合
79
+
80
+ 注入される CSS import は side-effect import のため、`"sideEffects": false` を宣言したパッケージ(コンポーネントライブラリ等)の中ではツリーシェイクで落とされる。CSS を side effect として明示すること:
81
+
82
+ ```json
83
+ { "sideEffects": ["**/*.css"] }
84
+ ```
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@bestcss/vite-plugin",
3
+ "version": "0.1.0",
4
+ "description": "bestcss の Vite プラグイン。css`` の抽出・HMR・クラス名短縮・SSR / ルート単位 CSS 分割",
5
+ "keywords": [
6
+ "css",
7
+ "css-in-js",
8
+ "zero-runtime",
9
+ "bestcss"
10
+ ],
11
+ "license": "MIT",
12
+ "author": "Yuta Ura",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/YutaUra/bestcss.git",
16
+ "directory": "packages/vite-plugin"
17
+ },
18
+ "type": "module",
19
+ "engines": {
20
+ "node": ">=20"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "exports": {
26
+ ".": {
27
+ "types": "./dist/index.d.ts",
28
+ "default": "./dist/index.js"
29
+ },
30
+ "./route-css": {
31
+ "types": "./dist/route-css.d.ts",
32
+ "default": "./dist/route-css.js"
33
+ },
34
+ "./client": {
35
+ "types": "./client.d.ts"
36
+ }
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "client.d.ts",
41
+ "docs"
42
+ ],
43
+ "dependencies": {
44
+ "@bestcss/core": "0.1.0"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^26.1.0",
48
+ "vite": "^8.1.3"
49
+ },
50
+ "scripts": {
51
+ "build": "tsc -p tsconfig.build.json",
52
+ "typecheck": "tsc --noEmit"
53
+ }
54
+ }