@teacss/vite 0.10.4 → 0.10.6
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/README.md +3 -1
- package/dist/finalize.d.ts +5 -0
- package/dist/finalize.js +104 -0
- package/dist/plugin.d.ts +10 -0
- package/dist/plugin.js +119 -44
- package/dist/virtual.d.ts +3 -1
- package/dist/virtual.js +6 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -33,7 +33,9 @@ Import the generated CSS once from the application entry:
|
|
|
33
33
|
import "virtual:teacss.css";
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
The dev server also serves the inspector at `/__teacss`; pass `inspector: false` to turn it off.
|
|
36
|
+
The dev server also serves the inspector at `/__teacss`; pass `inspector: false` to turn it off. In dev, the dev server serves the Standard preset's fonts under `<base>__teacss_fonts/`, prefixed with `server.origin` when it is set; a build emits them as assets.
|
|
37
|
+
|
|
38
|
+
Other plugins reach the project through the `api` of the plugin named `teacss:api` (`TeacssVitePluginApi`): `getProject()` returns it, and `invalidate()` updates the generated CSS on the dev client after a module was registered through `getProject().transform()`.
|
|
37
39
|
|
|
38
40
|
## Documentation
|
|
39
41
|
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ResolvedBuildEnvironmentOptions, type ResolvedConfig } from "vite";
|
|
2
|
+
type BuildOptions = Pick<ResolvedBuildEnvironmentOptions, "cssMinify" | "cssTarget">;
|
|
3
|
+
export declare function finalizeBuildCss(css: string, config: ResolvedConfig, build: BuildOptions, viteVersion: string): Promise<string>;
|
|
4
|
+
export {};
|
|
5
|
+
//# sourceMappingURL=finalize.d.ts.map
|
package/dist/finalize.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { preprocessCSS } from "vite";
|
|
4
|
+
import { RESOLVED_ENTRY } from "./virtual.js";
|
|
5
|
+
// esbuild and lightningcss are Vite's (a dependency or an optional peer, by version), not this package's: load them as Vite does.
|
|
6
|
+
const fromVite = createRequire(import.meta.resolve("vite"));
|
|
7
|
+
export async function finalizeBuildCss(css, config, build, viteVersion) {
|
|
8
|
+
const filename = join(config.root, RESOLVED_ENTRY.slice(1));
|
|
9
|
+
const minifier = minifierOf(build.cssMinify, viteVersion);
|
|
10
|
+
const lightningcss = config.css.transformer === "lightningcss";
|
|
11
|
+
// One pass: a second Lightning CSS pass repeats the prefixes the first one added.
|
|
12
|
+
if (lightningcss && minifier === "lightningcss")
|
|
13
|
+
return minifyWithLightningcss(css, filename, config, build.cssTarget);
|
|
14
|
+
const { code } = await preprocessCSS(css, filename, lightningcss ? withoutCssMinify(config) : config);
|
|
15
|
+
if (minifier === "esbuild")
|
|
16
|
+
return minifyWithEsbuild(code, filename, config, build.cssTarget);
|
|
17
|
+
if (minifier === "lightningcss")
|
|
18
|
+
return minifyWithLightningcss(code, filename, config, build.cssTarget);
|
|
19
|
+
return code;
|
|
20
|
+
}
|
|
21
|
+
/** `preprocessCSS` runs Lightning CSS with the client environment's `build`, minifying wherever its `cssMinify` is set. */
|
|
22
|
+
function withoutCssMinify(config) {
|
|
23
|
+
const environments = Object.fromEntries(Object.entries(config.environments).map(([name, options]) => [name, { ...options, build: { ...options.build, cssMinify: false } }]));
|
|
24
|
+
return { ...config, build: { ...config.build, cssMinify: false }, environments };
|
|
25
|
+
}
|
|
26
|
+
function minifierOf(cssMinify, viteVersion) {
|
|
27
|
+
if (cssMinify !== true)
|
|
28
|
+
return cssMinify;
|
|
29
|
+
// Vite 7 minifies with esbuild for `cssMinify: true`, Vite 8 with Lightning CSS.
|
|
30
|
+
return Number.parseInt(viteVersion, 10) >= 8 ? "lightningcss" : "esbuild";
|
|
31
|
+
}
|
|
32
|
+
async function minifyWithEsbuild(css, filename, config, cssTarget) {
|
|
33
|
+
const esbuild = fromVite("esbuild");
|
|
34
|
+
const result = await esbuild.transform(css, { ...resolveMinifyCssEsbuildOptions(config.esbuild || {}), loader: "css", sourcefile: filename, target: cssTarget || undefined });
|
|
35
|
+
for (const warning of result.warnings)
|
|
36
|
+
config.logger.warn(`[esbuild css minify] ${warning.text}`);
|
|
37
|
+
return result.code;
|
|
38
|
+
}
|
|
39
|
+
function resolveMinifyCssEsbuildOptions(options) {
|
|
40
|
+
const { charset, legalComments, logLevel, logLimit, logOverride, minifyIdentifiers, minifySyntax, minifyWhitespace } = options;
|
|
41
|
+
const base = { charset, legalComments, logLevel, logLimit, logOverride };
|
|
42
|
+
if (minifyIdentifiers == null && minifySyntax == null && minifyWhitespace == null)
|
|
43
|
+
return { ...base, minify: true };
|
|
44
|
+
return { ...base, minifyIdentifiers: minifyIdentifiers ?? true, minifySyntax: minifySyntax ?? true, minifyWhitespace: minifyWhitespace ?? true };
|
|
45
|
+
}
|
|
46
|
+
function minifyWithLightningcss(css, filename, config, cssTarget) {
|
|
47
|
+
const lightningcss = fromVite("lightningcss");
|
|
48
|
+
const result = lightningcss.transform({ ...config.css.lightningcss, filename, code: Buffer.from(css), minify: true, targets: convertTargets(cssTarget), cssModules: undefined });
|
|
49
|
+
for (const warning of result.warnings)
|
|
50
|
+
config.logger.warn(`[lightningcss minify] ${warning.message}`);
|
|
51
|
+
return new TextDecoder().decode(result.code);
|
|
52
|
+
}
|
|
53
|
+
const CSS_TARGET_BROWSERS = new Map([
|
|
54
|
+
["chrome", "chrome"],
|
|
55
|
+
["edge", "edge"],
|
|
56
|
+
["firefox", "firefox"],
|
|
57
|
+
["hermes", false],
|
|
58
|
+
["ie", "ie"],
|
|
59
|
+
["ios", "ios_saf"],
|
|
60
|
+
["node", false],
|
|
61
|
+
["opera", "opera"],
|
|
62
|
+
["rhino", false],
|
|
63
|
+
["safari", "safari"],
|
|
64
|
+
]);
|
|
65
|
+
const ES_TARGETS = new Map([
|
|
66
|
+
[2015, ["chrome49", "edge13", "safari10", "ios10", "firefox44", "opera36"]],
|
|
67
|
+
[2016, ["chrome50", "edge13", "safari10", "ios10", "firefox43", "opera37"]],
|
|
68
|
+
[2017, ["chrome58", "edge15", "safari11", "ios11", "firefox52", "opera45"]],
|
|
69
|
+
[2018, ["chrome63", "edge79", "safari12", "ios12", "firefox58", "opera50"]],
|
|
70
|
+
[2019, ["chrome73", "edge79", "safari12.1", "ios12.1", "firefox64", "opera60"]],
|
|
71
|
+
[2020, ["chrome80", "edge80", "safari14.1", "ios14.5", "firefox80", "opera67"]],
|
|
72
|
+
[2021, ["chrome85", "edge85", "safari14.1", "ios14.5", "firefox80", "opera71"]],
|
|
73
|
+
[2022, ["chrome94", "edge94", "safari16.4", "ios16.4", "firefox93", "opera80"]],
|
|
74
|
+
[2023, ["chrome110", "edge110", "safari16.4", "ios16.4", "firefox146", "opera96"]],
|
|
75
|
+
[2024, ["chrome119", "edge119", "safari17.4", "ios17.4", "firefox145", "opera105"]],
|
|
76
|
+
[2025, ["chrome136", "edge136", "safari26.0", "ios26.0", "firefox138", "opera121"]],
|
|
77
|
+
]);
|
|
78
|
+
function convertTargets(cssTarget) {
|
|
79
|
+
const targets = {};
|
|
80
|
+
for (const entry of [cssTarget || []].flat().flatMap(esBrowsers)) {
|
|
81
|
+
if (entry === "esnext")
|
|
82
|
+
continue;
|
|
83
|
+
const [, name = "", major, minor = "0"] = /^([a-z_]+)(\d+)(?:\.(\d+))?$/i.exec(entry) ?? [];
|
|
84
|
+
const browser = CSS_TARGET_BROWSERS.get(name.toLowerCase());
|
|
85
|
+
if (browser === false)
|
|
86
|
+
continue;
|
|
87
|
+
if (browser === undefined)
|
|
88
|
+
throw new Error(`Unsupported target "${entry}"`);
|
|
89
|
+
const version = (Number(major) << 16) | (Number(minor) << 8);
|
|
90
|
+
if (version < (targets[browser] ?? Number.POSITIVE_INFINITY))
|
|
91
|
+
targets[browser] = version;
|
|
92
|
+
}
|
|
93
|
+
return targets;
|
|
94
|
+
}
|
|
95
|
+
function esBrowsers(entry) {
|
|
96
|
+
const year = /^es(6|\d{4})$/.exec(entry)?.[1];
|
|
97
|
+
if (year === undefined)
|
|
98
|
+
return [entry];
|
|
99
|
+
const browsers = ES_TARGETS.get(year === "6" ? 2015 : Number(year));
|
|
100
|
+
if (browsers === undefined)
|
|
101
|
+
throw new Error(`Unsupported target "${entry}"`);
|
|
102
|
+
return browsers;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=finalize.js.map
|
package/dist/plugin.d.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { type Project, type ShellOptions } from "@teacss/engine";
|
|
2
2
|
import type { Plugin } from "vite";
|
|
3
3
|
export interface TeacssViteOptions extends ShellOptions {
|
|
4
|
+
/**
|
|
5
|
+
* The engine's own minify of the generated CSS, which drops the layer comments and line breaks. Default false:
|
|
6
|
+
* a build hands the generated CSS to Vite's minifier, `build.cssMinify`, whatever this option says.
|
|
7
|
+
*/
|
|
8
|
+
minify?: boolean;
|
|
4
9
|
/** v10.2 §3.6: serve `@teacss/inspector` at `INSPECTOR_BASE` on the dev server. Default true; a build never mounts it. */
|
|
5
10
|
inspector?: boolean;
|
|
6
11
|
/**
|
|
@@ -12,6 +17,11 @@ export interface TeacssViteOptions extends ShellOptions {
|
|
|
12
17
|
}
|
|
13
18
|
export interface TeacssVitePluginApi {
|
|
14
19
|
getProject(): Project;
|
|
20
|
+
/**
|
|
21
|
+
* Sends the dev client an update of the generated CSS, as a changed module does: for the modules another plugin
|
|
22
|
+
* registers through `getProject().transform()`. A build has no client to update.
|
|
23
|
+
*/
|
|
24
|
+
invalidate(): void;
|
|
15
25
|
}
|
|
16
26
|
/** The dev-server path the inspector's UI and `/api` routes live under; the UI's built asset URLs assume it. */
|
|
17
27
|
export declare const INSPECTOR_BASE = "/__teacss";
|
package/dist/plugin.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
1
2
|
import { readFileSync } from "node:fs";
|
|
2
3
|
import { createRequire } from "node:module";
|
|
3
4
|
import { join, posix, resolve } from "node:path";
|
|
4
5
|
import { createProject, extractBundledFonts, isCssId, markers, replaceEntryWithMap } from "@teacss/engine";
|
|
5
6
|
import { createInspectorMiddleware } from "@teacss/inspector";
|
|
6
|
-
import {
|
|
7
|
+
import { finalizeBuildCss } from "./finalize.js";
|
|
8
|
+
import { BUILD_PLACEHOLDER, BUILD_PLACEHOLDER_PATTERN, digestPlaceholder, hmrPath, isResolvedEntry, resolveEntryId, shouldTransform } from "./virtual.js";
|
|
7
9
|
const INCLUDE_COMMENT = markers().includeComment;
|
|
8
10
|
/** architecture §5: the files `loadProjectFromRoot` discovers the entry through. */
|
|
9
11
|
const ENTRY_CANDIDATES = ["package.json", "index.css", "src/index.css"];
|
|
@@ -11,7 +13,30 @@ const ENTRY_CANDIDATES = ["package.json", "index.css", "src/index.css"];
|
|
|
11
13
|
const INVALIDATION_DELAY_MS = 10;
|
|
12
14
|
/** The dev-server path the inspector's UI and `/api` routes live under; the UI's built asset URLs assume it. */
|
|
13
15
|
export const INSPECTOR_BASE = "/__teacss";
|
|
16
|
+
/** Where, under Vite's `base`, the dev server serves the fonts of the generated CSS; the path teacss-ts uses. */
|
|
17
|
+
const DEV_FONTS = "__teacss_fonts";
|
|
14
18
|
const HTML_ENTRY = /\.html$/;
|
|
19
|
+
/** `vite:css-post`'s transform, and the stylesheets holding the placeholder it cached, by its hook object. */
|
|
20
|
+
const cssPostCaches = new WeakMap();
|
|
21
|
+
/**
|
|
22
|
+
* Vite names a CSS file by the hash of what `vite:css-post` cached for its stylesheets, when it renders a chunk,
|
|
23
|
+
* and the generated CSS replaces the placeholder only in `generateBundle`. So the stylesheets that hold the
|
|
24
|
+
* placeholder are recorded here, and `renderStart` caches them again with the generated CSS's digest in it.
|
|
25
|
+
*/
|
|
26
|
+
function recordPlaceholderStylesheets(plugins) {
|
|
27
|
+
const hook = plugins.find((plugin) => plugin.name === "vite:css-post")?.transform;
|
|
28
|
+
if (typeof hook !== "object" || hook === null || cssPostCaches.has(hook))
|
|
29
|
+
return;
|
|
30
|
+
const transform = hook.handler;
|
|
31
|
+
const cached = new Map();
|
|
32
|
+
cssPostCaches.set(hook, { transform, cached });
|
|
33
|
+
const record = function (css, id, ...rest) {
|
|
34
|
+
if (typeof css === "string" && css.search(BUILD_PLACEHOLDER_PATTERN) !== -1)
|
|
35
|
+
cached.set(id, css);
|
|
36
|
+
return transform.call(this, css, id, ...rest);
|
|
37
|
+
};
|
|
38
|
+
hook.handler = record;
|
|
39
|
+
}
|
|
15
40
|
export function teacss(options = {}) {
|
|
16
41
|
const { inspector = true, lightningcss, ...shell } = options;
|
|
17
42
|
let project;
|
|
@@ -31,6 +56,8 @@ export function teacss(options = {}) {
|
|
|
31
56
|
const entries = new Set();
|
|
32
57
|
/** `@source` directories already reported as unreadable: Vite's builder resolves the config once per environment. */
|
|
33
58
|
const unreadable = new Set();
|
|
59
|
+
/** The bytes of each font the dev CSS links to, by its file name under `DEV_FONTS`, which carries its sha256. */
|
|
60
|
+
const devFonts = new Map();
|
|
34
61
|
function warnUnreadable() {
|
|
35
62
|
for (const path of project.info.unreadableDirectories) {
|
|
36
63
|
if (unreadable.has(path))
|
|
@@ -170,7 +197,7 @@ export function teacss(options = {}) {
|
|
|
170
197
|
...shell,
|
|
171
198
|
root: shell.root ?? config.root,
|
|
172
199
|
envMode: shell.envMode ?? (isBuild ? "build" : "dev"),
|
|
173
|
-
minify: shell.minify ??
|
|
200
|
+
minify: shell.minify ?? false,
|
|
174
201
|
});
|
|
175
202
|
disposed = false;
|
|
176
203
|
sources.clear();
|
|
@@ -179,9 +206,9 @@ export function teacss(options = {}) {
|
|
|
179
206
|
syncSources();
|
|
180
207
|
}
|
|
181
208
|
/**
|
|
182
|
-
* What stands for the generated CSS: the placeholder `generateBundle` replaces in a build, the CSS itself in dev
|
|
183
|
-
* While a watcher event has failed, the error goes out again: Vite's
|
|
184
|
-
* clears the overlay before the client requests it.
|
|
209
|
+
* What stands for the generated CSS: the placeholder `generateBundle` replaces in a build, the CSS itself in dev,
|
|
210
|
+
* with its fonts linked from the dev server. While a watcher event has failed, the error goes out again: Vite's
|
|
211
|
+
* own update of a changed entry stylesheet clears the overlay before the client requests it.
|
|
185
212
|
*/
|
|
186
213
|
function generated(warn) {
|
|
187
214
|
if (config.command === "build")
|
|
@@ -190,7 +217,22 @@ export function teacss(options = {}) {
|
|
|
190
217
|
server?.hot.send({ type: "error", err: failure });
|
|
191
218
|
const result = project.css();
|
|
192
219
|
project.reportUnmatched(result, { warn });
|
|
193
|
-
return result.css;
|
|
220
|
+
return linkDevFonts(result.css);
|
|
221
|
+
}
|
|
222
|
+
/** `css` with the fonts it inlines linked from the dev server, which serves the bytes recorded here. */
|
|
223
|
+
function linkDevFonts(css) {
|
|
224
|
+
return extractBundledFonts(css, (digest, bytes) => {
|
|
225
|
+
const name = `teacss-${digest}.woff2`;
|
|
226
|
+
devFonts.set(name, bytes);
|
|
227
|
+
// As Vite's own dev asset URLs: a page served by another server (`server.origin`) loads them from Vite.
|
|
228
|
+
return `${config.server.origin ?? ""}${config.base}${DEV_FONTS}/${name}`;
|
|
229
|
+
}).css;
|
|
230
|
+
}
|
|
231
|
+
/** A font this instance has not linked yet, as in CSS another process generated, is looked up in the project's CSS. */
|
|
232
|
+
function devFont(name) {
|
|
233
|
+
if (!devFonts.has(name) && !disposed)
|
|
234
|
+
linkDevFonts(project.css().css);
|
|
235
|
+
return devFonts.get(name);
|
|
194
236
|
}
|
|
195
237
|
function dispose() {
|
|
196
238
|
if (invalidation)
|
|
@@ -202,6 +244,47 @@ export function teacss(options = {}) {
|
|
|
202
244
|
disposed = true;
|
|
203
245
|
project.dispose();
|
|
204
246
|
}
|
|
247
|
+
/**
|
|
248
|
+
* Splices the generated CSS into every CSS file that holds the placeholder. Vite writes a chunk's CSS file
|
|
249
|
+
* when it renders the chunk, and with `build.cssCodeSplit: false` its single CSS file only in its own
|
|
250
|
+
* `generateBundle`, so `teacss:api` runs this again after it.
|
|
251
|
+
*/
|
|
252
|
+
const spliceGenerated = async function (_options, bundle) {
|
|
253
|
+
let css;
|
|
254
|
+
const fonts = new Map();
|
|
255
|
+
for (const file of Object.values(bundle)) {
|
|
256
|
+
if (file.type !== "asset" || !file.fileName.endsWith(".css"))
|
|
257
|
+
continue;
|
|
258
|
+
const source = typeof file.source === "string" ? file.source : new TextDecoder().decode(file.source);
|
|
259
|
+
if (source.search(BUILD_PLACEHOLDER_PATTERN) === -1)
|
|
260
|
+
continue;
|
|
261
|
+
if (css === undefined) {
|
|
262
|
+
const result = project.css();
|
|
263
|
+
project.reportUnmatched(result, { warn: (message) => this.warn(message) });
|
|
264
|
+
css = await finalizeBuildCss(result.css, this.environment.getTopLevelConfig(), this.environment.config.build, viteVersion);
|
|
265
|
+
}
|
|
266
|
+
const emitted = new Set();
|
|
267
|
+
const spliced = extractBundledFonts(css, (digest, bytes) => {
|
|
268
|
+
let reference = fonts.get(digest);
|
|
269
|
+
if (reference === undefined) {
|
|
270
|
+
reference = this.emitFile({ type: "asset", name: `teacss-${digest}.woff2`, source: bytes });
|
|
271
|
+
fonts.set(digest, reference);
|
|
272
|
+
}
|
|
273
|
+
const fileName = this.getFileName(reference);
|
|
274
|
+
emitted.add(fileName);
|
|
275
|
+
return assetUrl(config.base, file.fileName, fileName);
|
|
276
|
+
}).css;
|
|
277
|
+
file.source = source.replace(BUILD_PLACEHOLDER_PATTERN, () => spliced);
|
|
278
|
+
// Vite's manifest lists a chunk's assets from `viteMetadata.importedAssets`; Astro moves the
|
|
279
|
+
// fonts of its server build to the client output from that list.
|
|
280
|
+
for (const chunk of Object.values(bundle)) {
|
|
281
|
+
if (chunk.type !== "chunk" || !chunk.viteMetadata?.importedCss.has(file.fileName))
|
|
282
|
+
continue;
|
|
283
|
+
for (const fileName of emitted)
|
|
284
|
+
chunk.viteMetadata.importedAssets.add(fileName);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
};
|
|
205
288
|
const main = {
|
|
206
289
|
name: "teacss",
|
|
207
290
|
enforce: "pre",
|
|
@@ -239,8 +322,21 @@ export function teacss(options = {}) {
|
|
|
239
322
|
dispose();
|
|
240
323
|
config = resolved;
|
|
241
324
|
viteVersion = this.meta.viteVersion;
|
|
325
|
+
if (resolved.command === "build")
|
|
326
|
+
recordPlaceholderStylesheets(resolved.plugins);
|
|
242
327
|
open();
|
|
243
328
|
},
|
|
329
|
+
async renderStart() {
|
|
330
|
+
if (config.command !== "build")
|
|
331
|
+
return;
|
|
332
|
+
const hook = this.environment.config.plugins.find((plugin) => plugin.name === "vite:css-post")?.transform;
|
|
333
|
+
const cache = typeof hook === "object" && hook !== null ? cssPostCaches.get(hook) : undefined;
|
|
334
|
+
if (!cache || cache.cached.size === 0)
|
|
335
|
+
return;
|
|
336
|
+
const digest = createHash("sha256").update(project.css().css).digest("hex").slice(0, 16);
|
|
337
|
+
for (const [id, css] of cache.cached)
|
|
338
|
+
await cache.transform.call(this, css.replace(BUILD_PLACEHOLDER_PATTERN, () => digestPlaceholder(digest)), id);
|
|
339
|
+
},
|
|
244
340
|
buildStart() {
|
|
245
341
|
if (disposed)
|
|
246
342
|
open();
|
|
@@ -259,6 +355,19 @@ export function teacss(options = {}) {
|
|
|
259
355
|
if (failure)
|
|
260
356
|
client.send({ type: "error", err: failure });
|
|
261
357
|
});
|
|
358
|
+
const serveFont = (request, response, next) => {
|
|
359
|
+
const bytes = devFont(request.url?.split("?", 1)[0]?.slice(1) ?? "");
|
|
360
|
+
if (!bytes)
|
|
361
|
+
return next();
|
|
362
|
+
response.setHeader("Content-Type", "font/woff2");
|
|
363
|
+
response.setHeader("Cache-Control", "public, max-age=31536000, immutable");
|
|
364
|
+
response.end(bytes);
|
|
365
|
+
};
|
|
366
|
+
// After Vite's CORS and host check, ahead of the middleware that strips `base`.
|
|
367
|
+
devServer.middlewares.use(`${config.base}${DEV_FONTS}`, serveFont);
|
|
368
|
+
// A parent app that mounts Vite's middlewares under `base` strips it before they run.
|
|
369
|
+
if (config.server.middlewareMode && config.base !== "/")
|
|
370
|
+
devServer.middlewares.use(`/${DEV_FONTS}`, serveFont);
|
|
262
371
|
if (inspector)
|
|
263
372
|
devServer.middlewares.use(INSPECTOR_BASE, createInspectorMiddleware(project));
|
|
264
373
|
},
|
|
@@ -281,7 +390,7 @@ export function teacss(options = {}) {
|
|
|
281
390
|
return null;
|
|
282
391
|
const result = project.transform(id, code);
|
|
283
392
|
const stylesheet = isCssId(id);
|
|
284
|
-
const entry = stylesheet ? replaceEntryWithMap(result.code, () => generated((message) => this.warn(message)), { id
|
|
393
|
+
const entry = stylesheet ? replaceEntryWithMap(result.code, () => generated((message) => this.warn(message)), { id, original: code, map: result.map }) : null;
|
|
285
394
|
if (entry !== null) {
|
|
286
395
|
entries.add(id);
|
|
287
396
|
return { code: entry.code, map: { ...entry.map, file: entry.map.file ?? undefined } };
|
|
@@ -303,42 +412,7 @@ export function teacss(options = {}) {
|
|
|
303
412
|
return result.code;
|
|
304
413
|
},
|
|
305
414
|
},
|
|
306
|
-
generateBundle
|
|
307
|
-
let css;
|
|
308
|
-
const fonts = new Map();
|
|
309
|
-
for (const file of Object.values(bundle)) {
|
|
310
|
-
if (file.type !== "asset" || !file.fileName.endsWith(".css"))
|
|
311
|
-
continue;
|
|
312
|
-
const source = typeof file.source === "string" ? file.source : new TextDecoder().decode(file.source);
|
|
313
|
-
if (source.search(BUILD_PLACEHOLDER_PATTERN) === -1)
|
|
314
|
-
continue;
|
|
315
|
-
if (css === undefined) {
|
|
316
|
-
const result = project.css();
|
|
317
|
-
project.reportUnmatched(result, { warn: (message) => this.warn(message) });
|
|
318
|
-
css = result.css;
|
|
319
|
-
}
|
|
320
|
-
const emitted = new Set();
|
|
321
|
-
const spliced = extractBundledFonts(css, (digest, bytes) => {
|
|
322
|
-
let reference = fonts.get(digest);
|
|
323
|
-
if (reference === undefined) {
|
|
324
|
-
reference = this.emitFile({ type: "asset", name: `teacss-${digest}.woff2`, source: bytes });
|
|
325
|
-
fonts.set(digest, reference);
|
|
326
|
-
}
|
|
327
|
-
const fileName = this.getFileName(reference);
|
|
328
|
-
emitted.add(fileName);
|
|
329
|
-
return assetUrl(config.base, file.fileName, fileName);
|
|
330
|
-
}).css;
|
|
331
|
-
file.source = source.replace(BUILD_PLACEHOLDER_PATTERN, () => spliced);
|
|
332
|
-
// Vite's manifest lists a chunk's assets from `viteMetadata.importedAssets`; Astro moves the
|
|
333
|
-
// fonts of its server build to the client output from that list.
|
|
334
|
-
for (const chunk of Object.values(bundle)) {
|
|
335
|
-
if (chunk.type !== "chunk" || !chunk.viteMetadata?.importedCss.has(file.fileName))
|
|
336
|
-
continue;
|
|
337
|
-
for (const fileName of emitted)
|
|
338
|
-
chunk.viteMetadata.importedAssets.add(fileName);
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
},
|
|
415
|
+
generateBundle: spliceGenerated,
|
|
342
416
|
closeBundle() {
|
|
343
417
|
if (!config.build.watch)
|
|
344
418
|
dispose();
|
|
@@ -350,7 +424,8 @@ export function teacss(options = {}) {
|
|
|
350
424
|
const api = {
|
|
351
425
|
name: "teacss:api",
|
|
352
426
|
enforce: "post",
|
|
353
|
-
|
|
427
|
+
generateBundle: spliceGenerated,
|
|
428
|
+
api: { getProject: () => project, invalidate: invalidateEntry },
|
|
354
429
|
};
|
|
355
430
|
return [main, api];
|
|
356
431
|
}
|
package/dist/virtual.d.ts
CHANGED
|
@@ -8,7 +8,9 @@ export declare const RESOLVED_ENTRY = "\0virtual:teacss.css";
|
|
|
8
8
|
* warns about an unknown at-rule, esbuild drops the comment, and other CSS can precede the entry in a chunk.
|
|
9
9
|
*/
|
|
10
10
|
export declare const BUILD_PLACEHOLDER: string;
|
|
11
|
-
/** `BUILD_PLACEHOLDER`
|
|
11
|
+
/** `BUILD_PLACEHOLDER` with the digest of the CSS that will replace it, which goes into the CSS file's name. */
|
|
12
|
+
export declare function digestPlaceholder(digest: string): string;
|
|
13
|
+
/** `BUILD_PLACEHOLDER` or a `digestPlaceholder` as any minifier prints it. */
|
|
12
14
|
export declare const BUILD_PLACEHOLDER_PATTERN: RegExp;
|
|
13
15
|
/** Resolves `virtual:teacss.css`, `teacss.css`, their `/`-prefixed forms and the resolved id itself, keeping a `?query`. */
|
|
14
16
|
export declare function resolveEntryId(id: string): string | null;
|
package/dist/virtual.js
CHANGED
|
@@ -10,8 +10,12 @@ const PLACEHOLDER_NAME = CSS_PLACEHOLDER.slice(1);
|
|
|
10
10
|
* warns about an unknown at-rule, esbuild drops the comment, and other CSS can precede the entry in a chunk.
|
|
11
11
|
*/
|
|
12
12
|
export const BUILD_PLACEHOLDER = `#${PLACEHOLDER_NAME}{--${PLACEHOLDER_NAME}:0}`;
|
|
13
|
-
/** `BUILD_PLACEHOLDER`
|
|
14
|
-
export
|
|
13
|
+
/** `BUILD_PLACEHOLDER` with the digest of the CSS that will replace it, which goes into the CSS file's name. */
|
|
14
|
+
export function digestPlaceholder(digest) {
|
|
15
|
+
return `#${PLACEHOLDER_NAME}{--${PLACEHOLDER_NAME}:t${digest}}`;
|
|
16
|
+
}
|
|
17
|
+
/** `BUILD_PLACEHOLDER` or a `digestPlaceholder` as any minifier prints it. */
|
|
18
|
+
export const BUILD_PLACEHOLDER_PATTERN = new RegExp(`#${PLACEHOLDER_NAME}\\s*\\{\\s*--${PLACEHOLDER_NAME}\\s*:\\s*[\\w-]+\\s*;?\\s*\\}`, "g");
|
|
15
19
|
/** Resolves `virtual:teacss.css`, `teacss.css`, their `/`-prefixed forms and the resolved id itself, keeping a `?query`. */
|
|
16
20
|
export function resolveEntryId(id) {
|
|
17
21
|
const [bare = "", query] = splitQuery(id);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teacss/vite",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.6",
|
|
4
4
|
"description": "Vite plugin for TeaCSS on the Rust engine (@teacss/engine)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"vite": ">=7"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@teacss/engine": "0.10.
|
|
40
|
-
"@teacss/inspector": "0.10.
|
|
39
|
+
"@teacss/engine": "0.10.6",
|
|
40
|
+
"@teacss/inspector": "0.10.6"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^22.20.5",
|