@animus-ui/vite-plugin 0.1.32 → 0.1.36
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 +71 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/context.d.ts +19 -2
- package/dist/context.d.ts.map +1 -1
- package/dist/index-html.d.ts +19 -3
- package/dist/index-html.d.ts.map +1 -1
- package/dist/index.cjs +304 -30
- package/dist/index.d.ts +16 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/quarantine-plugin.d.ts +10 -0
- package/dist/quarantine-plugin.d.ts.map +1 -0
- package/dist/watch-ignored.d.ts +11 -0
- package/dist/watch-ignored.d.ts.map +1 -0
- package/package.json +10 -3
package/README.md
CHANGED
|
@@ -60,7 +60,8 @@ export default defineConfig({
|
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
The plugin is **delivery-only**: it injects `artifact.code` verbatim as an inline
|
|
63
|
-
`<script data-animus-bootstrap>`
|
|
63
|
+
`<script data-animus-bootstrap>` in `<head>`, after the charset and viewport
|
|
64
|
+
metas (so the charset stays within the first 1024 bytes) and ahead of every
|
|
64
65
|
stylesheet reference, and interprets no appearance semantics. Omit the option and
|
|
65
66
|
the built HTML is byte-for-byte what it was before.
|
|
66
67
|
|
|
@@ -96,6 +97,75 @@ supplies either `cspHash` or a per-request nonce. Worked App Router and Pages
|
|
|
96
97
|
Router examples live in the
|
|
97
98
|
[`@animus-ui/next-plugin` README](https://github.com/codecaaron/animus/tree/main/packages/next-plugin#appearance-bootstrap-no-fouc-color-mode).
|
|
98
99
|
|
|
100
|
+
## Font preloads
|
|
101
|
+
|
|
102
|
+
A font face declared with `preload: true` gets a
|
|
103
|
+
`<link rel="preload" as="font" crossorigin data-animus-preload>` in `<head>`,
|
|
104
|
+
after the charset and viewport metas and ahead of every stylesheet link. Its
|
|
105
|
+
href is the URL the stylesheet's `url()` loads: the hashed asset in a build,
|
|
106
|
+
the dev server's URL in development. A root-relative or absolute URL that
|
|
107
|
+
`index.html` already preloads gets no second preload. A build warns, naming
|
|
108
|
+
the HTML page, when the finished page preloads the same file through another
|
|
109
|
+
tag: one written in the page for an `asset()` file, or one a plugin adds from
|
|
110
|
+
an `index.html` hook that runs after Animus's.
|
|
111
|
+
|
|
112
|
+
`fontPreloads` selects the faces that preload in place of their `preload`
|
|
113
|
+
flags, for an app that renders only some of a system's faces. A family name
|
|
114
|
+
selects every face of the family; `{ family, weight?, style? }` selects the
|
|
115
|
+
faces whose declared weight contains `weight` (a variable face's range
|
|
116
|
+
`'100 900'` contains 600) and whose style is `style` (`'normal'` when
|
|
117
|
+
omitted):
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
animusExtract({
|
|
121
|
+
system: './src/ds.ts',
|
|
122
|
+
fontPreloads: [
|
|
123
|
+
{ family: 'Apercu', weight: 400 },
|
|
124
|
+
{ family: 'Apercu', weight: 'bold' },
|
|
125
|
+
{ family: 'Suisse', weight: 700 },
|
|
126
|
+
],
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`[]` preloads none. An entry no face matches reports
|
|
131
|
+
`animus.style.font-preload-unmatched`; a malformed one, such as a weight
|
|
132
|
+
range, reports `animus.style.font-preload-invalid`.
|
|
133
|
+
|
|
134
|
+
A preload competes with the critical CSS and JavaScript for first paint, so
|
|
135
|
+
preload WOFF2 and only the faces the first screen needs: a large TrueType
|
|
136
|
+
file can delay first paint by seconds on a slow link. A preloaded file that
|
|
137
|
+
is not WOFF2 or WOFF, or an `asset()` file over 150 KB, reports
|
|
138
|
+
`animus.style.font-face-preload-costly`.
|
|
139
|
+
|
|
140
|
+
## Third-party stylesheets
|
|
141
|
+
|
|
142
|
+
A library's plain CSS outside any `@layer` beats every Animus layer. With
|
|
143
|
+
`quarantine`, [`@animus-ui/layergate`](../layergate/README.md) loads it into
|
|
144
|
+
`anm-vendor`, a layer below all of Animus's, so Animus's styles win and the
|
|
145
|
+
library's still apply where Animus sets nothing:
|
|
146
|
+
|
|
147
|
+
```sh
|
|
148
|
+
bun add -D @animus-ui/layergate
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
animusExtract({
|
|
153
|
+
system: './src/ds.ts',
|
|
154
|
+
quarantine: {
|
|
155
|
+
sheets: ['react-grid-layout/css/styles.css'], // delivered ahead of Animus's own CSS
|
|
156
|
+
dependencies: ['react-datepicker'], // CSS these packages' modules import
|
|
157
|
+
optOut: [], // exact files whose own rules stay unlayered
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`sheets` arrive at the head of Animus's own stylesheet, wherever it is
|
|
163
|
+
imported, so no extra import is needed. Their fonts and images keep
|
|
164
|
+
resolving, files outside the project root included.
|
|
165
|
+
`!important` declarations and animations still win, and are reported. App
|
|
166
|
+
CSS keeps its own rules unlayered; where it imports a vendor file, that file
|
|
167
|
+
is layered. CSS Modules are left alone.
|
|
168
|
+
|
|
99
169
|
## MDX
|
|
100
170
|
|
|
101
171
|
Animus reads each `.mdx` file as the JSX it renders: its imports, exports,
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAE3C,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,cAAc,GACrB,IAAI,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAE3C,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,cAAc,GACrB,IAAI,CA+EN"}
|
package/dist/context.d.ts
CHANGED
|
@@ -29,9 +29,11 @@ export declare class PluginContext {
|
|
|
29
29
|
pathAliasesJson: string | null;
|
|
30
30
|
/** Vite's export conditions, so the loader reads the files Vite serves. */
|
|
31
31
|
resolveConditions: string[];
|
|
32
|
+
private viteExtensionPolicy;
|
|
32
33
|
/** Vite's resolved extension order; the built-in policy until
|
|
33
34
|
* `configResolved`. */
|
|
34
|
-
extensionPolicy: HostExtensionPolicy;
|
|
35
|
+
get extensionPolicy(): HostExtensionPolicy;
|
|
36
|
+
set extensionPolicy(policy: HostExtensionPolicy);
|
|
35
37
|
/** Vite's asset classification, captured at `configResolved`. */
|
|
36
38
|
assetClassification: HostAssetClassification;
|
|
37
39
|
/** How Vite's build expands computed loads, captured at
|
|
@@ -85,6 +87,12 @@ export declare class PluginContext {
|
|
|
85
87
|
/** Coalesces a burst of invalidations into one full reload. */
|
|
86
88
|
private readonly reloadWindow;
|
|
87
89
|
base: string;
|
|
90
|
+
/** Vite's root and `server.origin`, which a dev asset URL is served
|
|
91
|
+
* under. */
|
|
92
|
+
viteRoot: string;
|
|
93
|
+
serverOrigin: string;
|
|
94
|
+
/** Vite's resolved public directory; empty when it serves none. */
|
|
95
|
+
publicDir: string;
|
|
88
96
|
assetUrlBySpecifier: Map<string, string>;
|
|
89
97
|
/** Where dev asset() copies live, immutable and content-named; null in a
|
|
90
98
|
* build, or when the dev server does not serve the directory, which
|
|
@@ -162,7 +170,16 @@ export declare class PluginContext {
|
|
|
162
170
|
/** A build-strictness check that failed: a build throws `message`, and
|
|
163
171
|
* development reports it at error level and takes the non-strict path. */
|
|
164
172
|
strictFailure(message: string, cause?: unknown): void;
|
|
165
|
-
|
|
173
|
+
/** The URL Vite's dev server serves `absPath` at, the form its dev CSS
|
|
174
|
+
* pass writes (`fileToDevUrl`): root-relative inside its root, `/@fs/`
|
|
175
|
+
* outside, under `server.origin` and the base. Every consumer, the
|
|
176
|
+
* stylesheet that pass rewrites, component CSS it does not, and a font
|
|
177
|
+
* preload, then names one URL, and the browser fetches the file once. */
|
|
178
|
+
devServedUrl(absPath: string): string;
|
|
179
|
+
/** A root-relative URL a face names as written, as Vite's dev CSS pass
|
|
180
|
+
* writes it: under `server.origin` and the base where it names a public
|
|
181
|
+
* file or a file under the root, else unchanged. */
|
|
182
|
+
devRootRelativeUrl(href: string): string;
|
|
166
183
|
/** A dev asset() URL: an immutable content-named copy of `absPath`, so an
|
|
167
184
|
* edit gets a new URL, or the live file where copies are not served. */
|
|
168
185
|
devAssetUrl(specifier: string, absPath: string): string;
|
package/dist/context.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAeA,OAAO,EAGL,iBAAiB,EAGjB,iBAAiB,EAGlB,MAAM,4BAA4B,CAAC;AAWpC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,KAAK,EAEV,cAAc,EACd,sBAAsB,EACtB,uBAAuB,EACvB,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,YAAY,EACb,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAEV,cAAc,EACd,kBAAkB,EAClB,YAAY,EACb,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAgBnC;2BAC2B;AAC3B,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,cAAc,CAAC;IACxB,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAiBD,qBAAa,aAAa;IACxB,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAE1B,MAAM,UAAS;IACf;uCACmC;IACnC,WAAW,UAAS;IACpB;8EAC0E;IAC1E,YAAY,UAAS;IACrB,OAAO,SAAM;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE7B,WAAW,EAAE,gBAAgB,CAAM;IAEnC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEtC,2EAA2E;IAC3E,iBAAiB,EAAE,MAAM,EAAE,CAAM;IACjC;4BACwB;IACxB,eAAe,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
|
package/dist/index-html.d.ts
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import type { PluginContext } from './context';
|
|
2
2
|
import type { HtmlTagDescriptor } from 'vite';
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* The hrefs a finished document preloads both through Animus's tag and
|
|
5
|
+
* through another, which the page wrote or a hook running after Animus's
|
|
6
|
+
* added: the browser may then fetch the file twice. Each href is compared
|
|
7
|
+
* as the browser resolves it against `pageUrl`, the URL the page is served
|
|
8
|
+
* at, and reported as Animus wrote it.
|
|
6
9
|
*/
|
|
7
|
-
export declare function
|
|
10
|
+
export declare function duplicatedFontPreloads(html: string, pageUrl: string): string[];
|
|
11
|
+
/**
|
|
12
|
+
* The hook's result: the tags written after the charset and viewport metas,
|
|
13
|
+
* each on its own line indented as the element it follows, so the charset
|
|
14
|
+
* stays in the first 1024 bytes however long the tags are; as `head-prepend`
|
|
15
|
+
* tags for a document without a `<head>`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function injectHeadTags(html: string, tags: HtmlTagDescriptor[]): string | HtmlTagDescriptor[];
|
|
18
|
+
/**
|
|
19
|
+
* Animus's head tags in document order: the bootstrap script, so it runs
|
|
20
|
+
* before first paint and ahead of every stylesheet, the layer declaration,
|
|
21
|
+
* the font preloads, then the development bridge.
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildIndexHtmlTags(ctx: PluginContext, html?: string): HtmlTagDescriptor[];
|
|
8
24
|
//# sourceMappingURL=index-html.d.ts.map
|
package/dist/index-html.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-html.d.ts","sourceRoot":"","sources":["../src/index-html.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index-html.d.ts","sourceRoot":"","sources":["../src/index-html.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AA0C9C;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,MAAM,EAAE,CAeV;AAsED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,iBAAiB,EAAE,GACxB,MAAM,GAAG,iBAAiB,EAAE,CAU9B;AAkCD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,aAAa,EAClB,IAAI,SAAK,GACR,iBAAiB,EAAE,CAuCrB"}
|
package/dist/index.cjs
CHANGED
|
@@ -4,10 +4,10 @@ Object.defineProperties(exports, {
|
|
|
4
4
|
});
|
|
5
5
|
let _animus_ui_extract_pipeline = require("@animus-ui/extract/pipeline");
|
|
6
6
|
let path = require("path");
|
|
7
|
+
let vite = require("vite");
|
|
7
8
|
let fs = require("fs");
|
|
8
9
|
let _animus_ui_extract_session = require("@animus-ui/extract/session");
|
|
9
10
|
let child_process = require("child_process");
|
|
10
|
-
let vite = require("vite");
|
|
11
11
|
let crypto = require("crypto");
|
|
12
12
|
//#region src/asset-copy-dirs.ts
|
|
13
13
|
/**
|
|
@@ -192,7 +192,7 @@ function applyResolvedConfig(ctx, config) {
|
|
|
192
192
|
const copyDir = (0, path.resolve)(config.cacheDir, "animus-assets", processCopyDir());
|
|
193
193
|
const served = (0, vite.isFileLoadingAllowed)(config, (0, path.join)(copyDir, "asset"));
|
|
194
194
|
ctx.assetCopyDir = served ? copyDir : null;
|
|
195
|
-
ctx.servesFile = (path$
|
|
195
|
+
ctx.servesFile = (path$11) => (0, vite.isFileLoadingAllowed)(config, path$11);
|
|
196
196
|
if (!served) ctx.warn(`[animus-extract] asset() copies in ${copyDir} are not served under server.fs; serving the live files instead, so an edited asset keeps its URL`);
|
|
197
197
|
}
|
|
198
198
|
ctx.outputExcludes = (0, _animus_ui_extract_pipeline.outputExclusions)(config.root, (0, _animus_ui_extract_pipeline.viteOutputTargets)(config));
|
|
@@ -204,6 +204,9 @@ function applyResolvedConfig(ctx, config) {
|
|
|
204
204
|
ctx.log(`extension order: ${ctx.extensionPolicy.resolverVersion}`);
|
|
205
205
|
ctx.logger = config.logger;
|
|
206
206
|
ctx.base = config.base ?? "/";
|
|
207
|
+
ctx.viteRoot = config.root;
|
|
208
|
+
ctx.serverOrigin = config.server.origin ?? "";
|
|
209
|
+
ctx.publicDir = config.publicDir;
|
|
207
210
|
ctx.lcssTargets = (0, _animus_ui_extract_pipeline.resolveLightningTargets)(ctx.options.targets, ctx.rootDir);
|
|
208
211
|
ctx.log(`Lightning CSS targets resolved (${Object.keys(ctx.lcssTargets).length} browsers)`);
|
|
209
212
|
const pairs = [];
|
|
@@ -348,9 +351,15 @@ var PluginContext = class {
|
|
|
348
351
|
pathAliasesJson = null;
|
|
349
352
|
/** Vite's export conditions, so the loader reads the files Vite serves. */
|
|
350
353
|
resolveConditions = [];
|
|
354
|
+
viteExtensionPolicy;
|
|
351
355
|
/** Vite's resolved extension order; the built-in policy until
|
|
352
356
|
* `configResolved`. */
|
|
353
|
-
extensionPolicy
|
|
357
|
+
get extensionPolicy() {
|
|
358
|
+
return this.viteExtensionPolicy ?? (0, _animus_ui_extract_pipeline.builtInExtensionPolicy)();
|
|
359
|
+
}
|
|
360
|
+
set extensionPolicy(policy) {
|
|
361
|
+
this.viteExtensionPolicy = policy;
|
|
362
|
+
}
|
|
354
363
|
/** Vite's asset classification, captured at `configResolved`. */
|
|
355
364
|
assetClassification = (0, _animus_ui_extract_pipeline.builtInAssetClassification)("noHost");
|
|
356
365
|
/** How Vite's build expands computed loads, captured at
|
|
@@ -436,6 +445,12 @@ var PluginContext = class {
|
|
|
436
445
|
this.devServer?.hot?.send({ type: "full-reload" });
|
|
437
446
|
});
|
|
438
447
|
base = "/";
|
|
448
|
+
/** Vite's root and `server.origin`, which a dev asset URL is served
|
|
449
|
+
* under. */
|
|
450
|
+
viteRoot = "";
|
|
451
|
+
serverOrigin = "";
|
|
452
|
+
/** Vite's resolved public directory; empty when it serves none. */
|
|
453
|
+
publicDir = "";
|
|
439
454
|
assetUrlBySpecifier = /* @__PURE__ */ new Map();
|
|
440
455
|
/** Where dev asset() copies live, immutable and content-named; null in a
|
|
441
456
|
* build, or when the dev server does not serve the directory, which
|
|
@@ -520,6 +535,7 @@ var PluginContext = class {
|
|
|
520
535
|
engine: options.engine,
|
|
521
536
|
runtimeImport: options.runtimeImport,
|
|
522
537
|
closedDynamicImports: options.closedDynamicImports,
|
|
538
|
+
fontPreloads: options.fontPreloads,
|
|
523
539
|
mode: this.emissionProd ? "production" : "development"
|
|
524
540
|
}, {
|
|
525
541
|
build: this.build,
|
|
@@ -555,7 +571,7 @@ var PluginContext = class {
|
|
|
555
571
|
* between sees one generation. */
|
|
556
572
|
observeOutcome(session, outcome) {
|
|
557
573
|
let systemPropsChanged = false;
|
|
558
|
-
if (outcome.kind === "held") for (const [path$
|
|
574
|
+
if (outcome.kind === "held") for (const [path$7, hash] of outcome.abortedOriginals) this.abortedParseHashes.set(path$7, hash);
|
|
559
575
|
else {
|
|
560
576
|
this.abortedParseHashes.clear();
|
|
561
577
|
const before = this.systemPropsModule;
|
|
@@ -646,13 +662,36 @@ var PluginContext = class {
|
|
|
646
662
|
if (!report) throw new Error(message, cause === void 0 ? void 0 : { cause });
|
|
647
663
|
report(message);
|
|
648
664
|
}
|
|
649
|
-
|
|
650
|
-
|
|
665
|
+
/** The URL Vite's dev server serves `absPath` at, the form its dev CSS
|
|
666
|
+
* pass writes (`fileToDevUrl`): root-relative inside its root, `/@fs/`
|
|
667
|
+
* outside, under `server.origin` and the base. Every consumer, the
|
|
668
|
+
* stylesheet that pass rewrites, component CSS it does not, and a font
|
|
669
|
+
* preload, then names one URL, and the browser fetches the file once. */
|
|
670
|
+
devServedUrl(absPath) {
|
|
671
|
+
const base = this.base.endsWith("/") ? this.base.slice(0, -1) : this.base;
|
|
672
|
+
const root = this.viteRoot.endsWith("/") ? this.viteRoot : `${this.viteRoot}/`;
|
|
673
|
+
const path$8 = this.viteRoot !== "" && absPath.startsWith(root) ? `/${absPath.slice(root.length)}` : `/@fs${absPath}`;
|
|
674
|
+
return `${this.serverOrigin}${base}${path$8}`;
|
|
675
|
+
}
|
|
676
|
+
/** A root-relative URL a face names as written, as Vite's dev CSS pass
|
|
677
|
+
* writes it: under `server.origin` and the base where it names a public
|
|
678
|
+
* file or a file under the root, else unchanged. */
|
|
679
|
+
devRootRelativeUrl(href) {
|
|
680
|
+
if (!href.startsWith("/") || href.startsWith("//")) return href;
|
|
681
|
+
let path$6;
|
|
682
|
+
try {
|
|
683
|
+
path$6 = decodeURI(href.split(/[?#]/)[0]);
|
|
684
|
+
} catch {
|
|
685
|
+
return href;
|
|
686
|
+
}
|
|
687
|
+
if (![this.publicDir, this.viteRoot].some((dir) => dir !== "" && (0, fs.existsSync)((0, path.join)(dir, path$6)))) return href;
|
|
688
|
+
const base = this.base.endsWith("/") ? this.base.slice(0, -1) : this.base;
|
|
689
|
+
return `${this.serverOrigin}${base}${href}`;
|
|
651
690
|
}
|
|
652
691
|
/** A dev asset() URL: an immutable content-named copy of `absPath`, so an
|
|
653
692
|
* edit gets a new URL, or the live file where copies are not served. */
|
|
654
693
|
devAssetUrl(specifier, absPath) {
|
|
655
|
-
if (this.assetCopyDir === null) return this.
|
|
694
|
+
if (this.assetCopyDir === null) return this.devServedUrl(absPath);
|
|
656
695
|
const { fileName } = (0, _animus_ui_extract_pipeline.writeAssetCopy)(absPath, this.assetCopyDir);
|
|
657
696
|
const copyPath = (0, path.join)(this.assetCopyDir, fileName);
|
|
658
697
|
if (!this.servesFile(copyPath)) {
|
|
@@ -660,12 +699,12 @@ var PluginContext = class {
|
|
|
660
699
|
this.unservedAssetCopies.add(specifier);
|
|
661
700
|
this.warn(`[animus-extract] the copy of asset() ${specifier} is not served under server.fs; serving the live file instead, so an edit keeps its URL`);
|
|
662
701
|
}
|
|
663
|
-
return this.
|
|
702
|
+
return this.devServedUrl(absPath);
|
|
664
703
|
}
|
|
665
704
|
if (this.assetSourceBySpecifier.get(specifier) !== absPath) this.devServer?.watcher?.add(absPath);
|
|
666
705
|
this.assetSourceBySpecifier.set(specifier, absPath);
|
|
667
706
|
this.retainAssetCopy(absPath, fileName);
|
|
668
|
-
return this.
|
|
707
|
+
return this.devServedUrl(copyPath);
|
|
669
708
|
}
|
|
670
709
|
/** Keeps each source's current copy and the one before it; an asset
|
|
671
710
|
* edit reloads the page, so an older URL is never fetched again. A copy
|
|
@@ -773,8 +812,8 @@ var PluginContext = class {
|
|
|
773
812
|
plan.missing,
|
|
774
813
|
directories
|
|
775
814
|
]) {
|
|
776
|
-
const added = paths.filter((path$
|
|
777
|
-
for (const path$
|
|
815
|
+
const added = paths.filter((path$9) => !registered.has(path$9));
|
|
816
|
+
for (const path$10 of added) registered.add(path$10);
|
|
778
817
|
if (added.length > 0) watcher.add(added);
|
|
779
818
|
}
|
|
780
819
|
}
|
|
@@ -1283,11 +1322,145 @@ function invalidateStaleModules(ctx, environment, modules, analyzed) {
|
|
|
1283
1322
|
}
|
|
1284
1323
|
//#endregion
|
|
1285
1324
|
//#region src/index-html.ts
|
|
1325
|
+
const LINK_TAG_RE = /<link\b[^>]*>/gi;
|
|
1326
|
+
/** Marks the preloads Animus writes, so another preload of the same file
|
|
1327
|
+
* can be told from Animus's own. */
|
|
1328
|
+
const ANIMUS_PRELOAD_MARKER = "data-animus-preload";
|
|
1329
|
+
const MARKER_RE = /\sdata-animus-preload(?=[\s=/>])/i;
|
|
1330
|
+
const REL_RE = /\srel\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i;
|
|
1331
|
+
const HREF_RE = /\shref\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i;
|
|
1332
|
+
function attribute(tag, re) {
|
|
1333
|
+
const match = re.exec(tag);
|
|
1334
|
+
return match ? match[1] ?? match[2] ?? match[3] : void 0;
|
|
1335
|
+
}
|
|
1336
|
+
/** Each `<link rel="preload">` in `html`, with its href. */
|
|
1337
|
+
function* preloadLinks(html) {
|
|
1338
|
+
for (const [tag] of html.matchAll(LINK_TAG_RE)) {
|
|
1339
|
+
const rel = attribute(tag, REL_RE)?.toLowerCase().split(/\s+/) ?? [];
|
|
1340
|
+
const href = attribute(tag, HREF_RE);
|
|
1341
|
+
if (rel.includes("preload") && href !== void 0) yield [tag, href];
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
const ENTITY_RE = /&(?:#x([0-9a-f]+)|#(\d+)|(amp|lt|gt|quot|apos));/gi;
|
|
1345
|
+
const NAMED_ENTITIES = /* @__PURE__ */ new Map([
|
|
1346
|
+
["amp", "&"],
|
|
1347
|
+
["lt", "<"],
|
|
1348
|
+
["gt", ">"],
|
|
1349
|
+
["quot", "\""],
|
|
1350
|
+
["apos", "'"]
|
|
1351
|
+
]);
|
|
1352
|
+
/** An attribute value as the browser reads it: character references decoded. */
|
|
1353
|
+
function attributeText(value) {
|
|
1354
|
+
return value.replace(ENTITY_RE, (_, hex, decimal, named) => named === void 0 ? String.fromCodePoint(Number.parseInt(hex ?? decimal, hex ? 16 : 10)) : NAMED_ENTITIES.get(named.toLowerCase()));
|
|
1355
|
+
}
|
|
1356
|
+
/**
|
|
1357
|
+
* The hrefs a finished document preloads both through Animus's tag and
|
|
1358
|
+
* through another, which the page wrote or a hook running after Animus's
|
|
1359
|
+
* added: the browser may then fetch the file twice. Each href is compared
|
|
1360
|
+
* as the browser resolves it against `pageUrl`, the URL the page is served
|
|
1361
|
+
* at, and reported as Animus wrote it.
|
|
1362
|
+
*/
|
|
1363
|
+
function duplicatedFontPreloads(html, pageUrl) {
|
|
1364
|
+
const resolved = (href) => {
|
|
1365
|
+
try {
|
|
1366
|
+
return new URL(attributeText(href), pageUrl).href;
|
|
1367
|
+
} catch {
|
|
1368
|
+
return href;
|
|
1369
|
+
}
|
|
1370
|
+
};
|
|
1371
|
+
const ours = /* @__PURE__ */ new Map();
|
|
1372
|
+
const others = /* @__PURE__ */ new Set();
|
|
1373
|
+
for (const [tag, href] of preloadLinks(html)) if (MARKER_RE.test(tag)) ours.set(resolved(href), href);
|
|
1374
|
+
else others.add(resolved(href));
|
|
1375
|
+
return [...ours].filter(([url]) => others.has(url)).map(([, href]) => href);
|
|
1376
|
+
}
|
|
1377
|
+
/** The hrefs the document already preloads. */
|
|
1378
|
+
function preloadedHrefs(html) {
|
|
1379
|
+
return new Set([...preloadLinks(html)].map(([, href]) => href));
|
|
1380
|
+
}
|
|
1381
|
+
/** The head elements Animus's tags go after (the charset and viewport
|
|
1382
|
+
* metas), the stylesheets they go before, and what holds neither. */
|
|
1383
|
+
const HEAD_SCAN_RE = /<!--[\s\S]*?-->|<script\b[\s\S]*?<\/script\s*>|<\/head\s*>|<style\b|<(?:meta|link)\b[^>]*>/gi;
|
|
1384
|
+
/** Where Animus's head tags go: after the head's charset and viewport
|
|
1385
|
+
* metas, so they cannot push the charset past the 1024 bytes a browser
|
|
1386
|
+
* sniffs for it, and before its first stylesheet; -1 without a `<head>`. */
|
|
1387
|
+
function headTagOffset(html) {
|
|
1388
|
+
const open = /<head\b[^>]*>/i.exec(html);
|
|
1389
|
+
if (open === null) return -1;
|
|
1390
|
+
const start = open.index + open[0].length;
|
|
1391
|
+
let offset = start;
|
|
1392
|
+
for (const match of html.slice(start).matchAll(HEAD_SCAN_RE)) {
|
|
1393
|
+
const [element] = match;
|
|
1394
|
+
const lower = element.toLowerCase();
|
|
1395
|
+
if (lower.startsWith("</head") || lower.startsWith("<style")) break;
|
|
1396
|
+
if (lower.startsWith("<link")) {
|
|
1397
|
+
if ((attribute(element, REL_RE)?.toLowerCase().split(/\s+/))?.includes("stylesheet")) break;
|
|
1398
|
+
} else if (lower.startsWith("<meta") && (/\scharset\s*=/i.test(element) || /\shttp-equiv\s*=\s*["']?content-type\b/i.test(element) || /\sname\s*=\s*["']?viewport\b/i.test(element))) offset = start + match.index + element.length;
|
|
1399
|
+
}
|
|
1400
|
+
return offset;
|
|
1401
|
+
}
|
|
1402
|
+
/** As `escape-html` escapes, which Vite applies to attribute values. */
|
|
1403
|
+
function escapeHtml(value) {
|
|
1404
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1405
|
+
}
|
|
1406
|
+
/** A tag as Vite serializes it, `children` as written, except that an
|
|
1407
|
+
* undefined attribute is left out rather than written "undefined". */
|
|
1408
|
+
function serializeTag({ tag, attrs = {}, children }) {
|
|
1409
|
+
const serialized = Object.entries(attrs).map(([key, value]) => {
|
|
1410
|
+
if (value === true) return ` ${key}`;
|
|
1411
|
+
if (value === false || value === void 0) return "";
|
|
1412
|
+
return ` ${key}="${escapeHtml(value)}"`;
|
|
1413
|
+
}).join("");
|
|
1414
|
+
if (tag === "link" || tag === "meta" || tag === "base") return `<${tag}${serialized}>`;
|
|
1415
|
+
return `<${tag}${serialized}>${Array.isArray(children) ? "" : children ?? ""}</${tag}>`;
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1418
|
+
* The hook's result: the tags written after the charset and viewport metas,
|
|
1419
|
+
* each on its own line indented as the element it follows, so the charset
|
|
1420
|
+
* stays in the first 1024 bytes however long the tags are; as `head-prepend`
|
|
1421
|
+
* tags for a document without a `<head>`.
|
|
1422
|
+
*/
|
|
1423
|
+
function injectHeadTags(html, tags) {
|
|
1424
|
+
const offset = headTagOffset(html);
|
|
1425
|
+
if (offset === -1) return tags;
|
|
1426
|
+
if (tags.length === 0) return html;
|
|
1427
|
+
const lineStart = html.lastIndexOf("\n", offset - 1) + 1;
|
|
1428
|
+
const indent = /^[ \t]*/.exec(html.slice(lineStart))[0];
|
|
1429
|
+
const inserted = tags.map((tag) => `\n${indent}${serializeTag(tag)}`).join("");
|
|
1430
|
+
return html.slice(0, offset) + inserted + html.slice(offset);
|
|
1431
|
+
}
|
|
1432
|
+
/** The preloads for the faces that ask for one, by the URL the stylesheet's
|
|
1433
|
+
* own `url()` gets, each marked as Animus's; one the document already
|
|
1434
|
+
* preloads is left out. */
|
|
1435
|
+
function fontPreloadTags(ctx, html) {
|
|
1436
|
+
const published = ctx.served?.fontPreloads ?? [];
|
|
1437
|
+
if (published.length === 0) return [];
|
|
1438
|
+
const existing = preloadedHrefs(html);
|
|
1439
|
+
return (0, _animus_ui_extract_pipeline.substituteFontPreloads)(published, ctx.assetUrlBySpecifier).map((preload) => ctx.isProd ? preload : {
|
|
1440
|
+
...preload,
|
|
1441
|
+
href: ctx.devRootRelativeUrl(preload.href)
|
|
1442
|
+
}).filter(({ href }) => !existing.has(href)).map(({ href, type }) => {
|
|
1443
|
+
const attrs = {
|
|
1444
|
+
rel: "preload",
|
|
1445
|
+
href,
|
|
1446
|
+
as: "font"
|
|
1447
|
+
};
|
|
1448
|
+
if (type !== void 0) attrs.type = type;
|
|
1449
|
+
attrs.crossorigin = true;
|
|
1450
|
+
attrs[ANIMUS_PRELOAD_MARKER] = true;
|
|
1451
|
+
return {
|
|
1452
|
+
tag: "link",
|
|
1453
|
+
attrs,
|
|
1454
|
+
injectTo: "head-prepend"
|
|
1455
|
+
};
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1286
1458
|
/**
|
|
1287
|
-
*
|
|
1288
|
-
*
|
|
1459
|
+
* Animus's head tags in document order: the bootstrap script, so it runs
|
|
1460
|
+
* before first paint and ahead of every stylesheet, the layer declaration,
|
|
1461
|
+
* the font preloads, then the development bridge.
|
|
1289
1462
|
*/
|
|
1290
|
-
function buildIndexHtmlTags(ctx) {
|
|
1463
|
+
function buildIndexHtmlTags(ctx, html = "") {
|
|
1291
1464
|
const tags = [];
|
|
1292
1465
|
const bootstrap = ctx.options.appearanceBootstrap;
|
|
1293
1466
|
if (bootstrap?.code) tags.push({
|
|
@@ -1302,6 +1475,7 @@ function buildIndexHtmlTags(ctx) {
|
|
|
1302
1475
|
children: ctx.layerDeclaration,
|
|
1303
1476
|
injectTo: "head-prepend"
|
|
1304
1477
|
});
|
|
1478
|
+
tags.push(...fontPreloadTags(ctx, html));
|
|
1305
1479
|
if (!ctx.isProd) tags.push({
|
|
1306
1480
|
tag: "script",
|
|
1307
1481
|
attrs: {
|
|
@@ -1314,6 +1488,32 @@ function buildIndexHtmlTags(ctx) {
|
|
|
1314
1488
|
return tags;
|
|
1315
1489
|
}
|
|
1316
1490
|
//#endregion
|
|
1491
|
+
//#region src/quarantine-plugin.ts
|
|
1492
|
+
/**
|
|
1493
|
+
* layergate's Vite plugin for the `quarantine` option, loaded on first use:
|
|
1494
|
+
* layergate is an optional peer.
|
|
1495
|
+
*/
|
|
1496
|
+
function quarantinePlugin(options, layers) {
|
|
1497
|
+
const order = (0, _animus_ui_extract_pipeline.withVendorLayer)(layers);
|
|
1498
|
+
let plugin;
|
|
1499
|
+
return {
|
|
1500
|
+
order,
|
|
1501
|
+
plugin: () => plugin ??= load(options, order)
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
async function load(options, order) {
|
|
1505
|
+
let layergate;
|
|
1506
|
+
try {
|
|
1507
|
+
({layergate} = await import("@animus-ui/layergate/vite"));
|
|
1508
|
+
} catch (cause) {
|
|
1509
|
+
throw (0, _animus_ui_extract_pipeline.layergateMissing)("@animus-ui/vite-plugin", cause);
|
|
1510
|
+
}
|
|
1511
|
+
return layergate({
|
|
1512
|
+
...(0, _animus_ui_extract_pipeline.layergateOptions)(options, order),
|
|
1513
|
+
declaredBy: VIRTUAL_CSS_ID
|
|
1514
|
+
});
|
|
1515
|
+
}
|
|
1516
|
+
//#endregion
|
|
1317
1517
|
//#region src/virtual-modules.ts
|
|
1318
1518
|
/**
|
|
1319
1519
|
* Keyed on the whole option record: two instances sharing a key means the
|
|
@@ -1427,6 +1627,27 @@ if (import.meta.hot) {
|
|
|
1427
1627
|
return null;
|
|
1428
1628
|
}
|
|
1429
1629
|
//#endregion
|
|
1630
|
+
//#region src/watch-ignored.ts
|
|
1631
|
+
/** A path as a glob that matches only itself. Each special character
|
|
1632
|
+
* goes in a bracket: the watcher turns every backslash in an ignored
|
|
1633
|
+
* glob into a slash, so a backslash escape would split the path. */
|
|
1634
|
+
function literal(path) {
|
|
1635
|
+
return path.replace(/\\/g, "/").replace(/[()[\]{}*?!+@|]/g, "[$&]");
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* `server.watch.ignored` entries that watch installed kits. Vite always
|
|
1639
|
+
* ignores every `node_modules` path, where an installed kit lives, and
|
|
1640
|
+
* watches what a negated glob names. Each glob names a kit root and the
|
|
1641
|
+
* paths under it that ingestion would read: a segment holding an excluded
|
|
1642
|
+
* name, the kit's own `node_modules` among them, stays ignored, so the
|
|
1643
|
+
* watcher never walks into it. The segment test is a regex lookahead,
|
|
1644
|
+
* which picomatch keeps as written.
|
|
1645
|
+
*/
|
|
1646
|
+
function kitWatchNegations(roots) {
|
|
1647
|
+
const excluded = _animus_ui_extract_pipeline.PACKAGE_SRC_EXCLUDES.map((name) => `*${literal(name)}`).join("|");
|
|
1648
|
+
return roots.map((root) => `!${literal(root)}*(/(?!${excluded})*)`);
|
|
1649
|
+
}
|
|
1650
|
+
//#endregion
|
|
1430
1651
|
//#region src/index.ts
|
|
1431
1652
|
async function sourceKitConfig(rootDir, systemPath, engine) {
|
|
1432
1653
|
const kits = /* @__PURE__ */ new Map();
|
|
@@ -1438,45 +1659,73 @@ async function sourceKitConfig(rootDir, systemPath, engine) {
|
|
|
1438
1659
|
exclude: names,
|
|
1439
1660
|
include: [...kits.values()].flatMap((kit) => kit.dependencies.map((dependency) => `${kit.name} > ${dependency}`))
|
|
1440
1661
|
},
|
|
1441
|
-
ssr: { noExternal: names }
|
|
1662
|
+
ssr: { noExternal: names },
|
|
1663
|
+
roots: [...kits.values()].map((kit) => kit.root)
|
|
1442
1664
|
};
|
|
1443
1665
|
}
|
|
1666
|
+
/** The URL a build's root is served at, for resolving a page's hrefs: an
|
|
1667
|
+
* absolute `base`, a root-relative one on a stand-in origin, or the root
|
|
1668
|
+
* for a relative one, which every page resolves from its own location. */
|
|
1669
|
+
function servedBase(base) {
|
|
1670
|
+
const origin = "https://animus.invalid/";
|
|
1671
|
+
if (/^[a-z][a-z\d+.-]*:/i.test(base)) return new URL(base.replace(/\/?$/, "/"));
|
|
1672
|
+
if (base.startsWith("/")) return new URL(base.replace(/\/?$/, "/"), origin);
|
|
1673
|
+
return new URL(origin);
|
|
1674
|
+
}
|
|
1444
1675
|
function animusExtract(options) {
|
|
1445
1676
|
(0, _animus_ui_extract_pipeline.assertSupportedEngine)(options.engine);
|
|
1446
|
-
(0, _animus_ui_extract_pipeline.assertKnownOptionKeys)(options, [
|
|
1677
|
+
(0, _animus_ui_extract_pipeline.assertKnownOptionKeys)(options, [
|
|
1678
|
+
"verify",
|
|
1679
|
+
"appearanceBootstrap",
|
|
1680
|
+
"quarantine"
|
|
1681
|
+
], [{
|
|
1447
1682
|
key: "root",
|
|
1448
1683
|
reason: "the Vite driver derives its root from the resolved Vite config"
|
|
1449
1684
|
}], {
|
|
1450
1685
|
onUnknownKey: "warn",
|
|
1451
1686
|
warn: (message) => console.warn(`[animus-extract] ${message}`)
|
|
1452
1687
|
});
|
|
1453
|
-
const
|
|
1688
|
+
const vendor = options.quarantine ? quarantinePlugin(options.quarantine, options.layers) : null;
|
|
1689
|
+
const ctx = new PluginContext(vendor ? {
|
|
1690
|
+
...options,
|
|
1691
|
+
layers: vendor.order
|
|
1692
|
+
} : options);
|
|
1454
1693
|
return {
|
|
1455
1694
|
name: "animus-extract",
|
|
1456
1695
|
enforce: "pre",
|
|
1457
1696
|
async config(userConfig, env) {
|
|
1697
|
+
const vendorConfig = await (await vendor?.plugin())?.config.call(this, userConfig, env);
|
|
1698
|
+
const withVendor = (own) => vendorConfig ? (0, vite.mergeConfig)(own, vendorConfig) : own;
|
|
1458
1699
|
const { mode } = (0, _animus_ui_extract_pipeline.resolveMode)(options.mode, () => env.command === "build" ? "production" : "development");
|
|
1459
1700
|
const define = { __ANIMUS_DEV__: mode === "development" };
|
|
1460
1701
|
const root = (0, path.resolve)(userConfig.root ?? process.cwd());
|
|
1461
1702
|
const kits = await sourceKitConfig(root, (0, path.resolve)(root, options.system), ctx.kitEngine());
|
|
1462
|
-
|
|
1703
|
+
if (!kits) return withVendor({ define });
|
|
1704
|
+
const { roots, ...config } = kits;
|
|
1705
|
+
const watched = env.command === "serve" && userConfig.server?.watch !== null ? { server: { watch: { ignored: kitWatchNegations(roots) } } } : {};
|
|
1706
|
+
return withVendor({
|
|
1463
1707
|
define,
|
|
1464
|
-
...
|
|
1465
|
-
|
|
1708
|
+
...config,
|
|
1709
|
+
...watched
|
|
1710
|
+
});
|
|
1466
1711
|
},
|
|
1467
|
-
configureServer(server) {
|
|
1712
|
+
async configureServer(server) {
|
|
1713
|
+
await (await vendor?.plugin())?.configureServer.call(this, server);
|
|
1468
1714
|
ctx.devServer = server;
|
|
1469
1715
|
ctx.registerSystemWatchPaths();
|
|
1470
1716
|
},
|
|
1471
|
-
configResolved(config) {
|
|
1717
|
+
async configResolved(config) {
|
|
1472
1718
|
applyResolvedConfig(ctx, config);
|
|
1719
|
+
(await vendor?.plugin())?.configResolved.call(this, config);
|
|
1473
1720
|
},
|
|
1474
|
-
buildEnd() {
|
|
1721
|
+
async buildEnd() {
|
|
1722
|
+
(await vendor?.plugin())?.buildEnd.call(this);
|
|
1475
1723
|
if (!ctx.builds.end(this.environment ?? ctx.builds.single)) return;
|
|
1476
1724
|
cancelSystemReload(ctx);
|
|
1477
1725
|
ctx.close();
|
|
1478
1726
|
},
|
|
1479
1727
|
async buildStart() {
|
|
1728
|
+
await (await vendor?.plugin())?.buildStart.call(this);
|
|
1480
1729
|
await ctx.builds.open(this.environment ?? ctx.builds.single);
|
|
1481
1730
|
await runBuildStart(ctx, async (specifier, importer) => {
|
|
1482
1731
|
return (await this.resolve(specifier, importer))?.id ?? null;
|
|
@@ -1486,7 +1735,9 @@ function animusExtract(options) {
|
|
|
1486
1735
|
source
|
|
1487
1736
|
}) : void 0);
|
|
1488
1737
|
},
|
|
1489
|
-
async resolveId(id, importer) {
|
|
1738
|
+
async resolveId(id, importer, resolveOptions) {
|
|
1739
|
+
const owned = await (await vendor?.plugin())?.resolveId.call(this, id, importer, resolveOptions);
|
|
1740
|
+
if (owned) return owned;
|
|
1490
1741
|
const virtual = resolveVirtualId(ctx, id);
|
|
1491
1742
|
if (virtual !== null || !id.startsWith(".") || importer === void 0) return virtual;
|
|
1492
1743
|
const importerPath = importer.split("?")[0];
|
|
@@ -1498,20 +1749,43 @@ function animusExtract(options) {
|
|
|
1498
1749
|
moduleSideEffects: true
|
|
1499
1750
|
};
|
|
1500
1751
|
},
|
|
1501
|
-
load(id) {
|
|
1502
|
-
|
|
1752
|
+
async load(id) {
|
|
1753
|
+
const layergate = await vendor?.plugin();
|
|
1754
|
+
const loaded = await layergate?.load.call(this, id);
|
|
1755
|
+
if (loaded) return loaded;
|
|
1756
|
+
const own = loadVirtualModule(ctx, id);
|
|
1757
|
+
if (layergate && id === RESOLVED_CSS_ID && own !== null) return await layergate.stylesheetHead.call(this, id) + own;
|
|
1758
|
+
return own;
|
|
1503
1759
|
},
|
|
1504
|
-
transform(code, id) {
|
|
1760
|
+
async transform(code, id) {
|
|
1761
|
+
if (vendor && /\.css(?:$|\?)/.test(id)) return (await vendor.plugin()).transform.call(this, code, id);
|
|
1505
1762
|
return transformSource(ctx, code, id);
|
|
1506
1763
|
},
|
|
1764
|
+
generateBundle: {
|
|
1765
|
+
order: "post",
|
|
1766
|
+
handler(_output, bundle) {
|
|
1767
|
+
if (!ctx.served?.fontPreloads.length) return;
|
|
1768
|
+
for (const file of Object.values(bundle)) {
|
|
1769
|
+
if (file.type !== "asset" || !file.fileName.endsWith(".html")) continue;
|
|
1770
|
+
const html = Buffer.from(file.source).toString("utf8");
|
|
1771
|
+
if (!html.includes("data-animus-preload")) continue;
|
|
1772
|
+
for (const href of duplicatedFontPreloads(html, new URL(file.fileName, servedBase(ctx.base)).href)) ctx.warn(`[animus-extract] ${file.fileName} preloads ${href} twice: Animus's font preload and another preload tag, written in the page or added by a plugin whose index.html hook runs after Animus's, so the browser may fetch it twice. Remove the other preload; Animus's names the URL the stylesheet's url() loads.`);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
},
|
|
1507
1776
|
transformIndexHtml: {
|
|
1508
1777
|
order: "pre",
|
|
1509
|
-
handler() {
|
|
1510
|
-
return buildIndexHtmlTags(ctx);
|
|
1778
|
+
handler(html) {
|
|
1779
|
+
return injectHeadTags(html, buildIndexHtmlTags(ctx, html));
|
|
1511
1780
|
}
|
|
1512
1781
|
},
|
|
1513
1782
|
async hotUpdate(hmr) {
|
|
1514
|
-
|
|
1783
|
+
const vendorModules = (await vendor?.plugin())?.hotUpdate.call(this, hmr);
|
|
1784
|
+
const next = vendorModules ? {
|
|
1785
|
+
...hmr,
|
|
1786
|
+
modules: vendorModules
|
|
1787
|
+
} : hmr;
|
|
1788
|
+
return await handleHotUpdate(ctx, this.environment, next) ?? vendorModules;
|
|
1515
1789
|
}
|
|
1516
1790
|
};
|
|
1517
1791
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { ClosedDynamicImport, DiagnosticLevels, StaticCssConfig } from '@animus-ui/extract/pipeline';
|
|
1
|
+
import type { AnimusQuarantineOptions, ClosedDynamicImport, FontPreloadEntry, DiagnosticLevels, StaticCssConfig } from '@animus-ui/extract/pipeline';
|
|
2
2
|
import type { Plugin } from 'vite';
|
|
3
3
|
export { discoverFiles } from '@animus-ui/extract/pipeline';
|
|
4
|
+
export type { AnimusQuarantineOptions } from '@animus-ui/extract/pipeline';
|
|
4
5
|
export interface AnimusExtractOptions {
|
|
5
6
|
/** Path to a module exporting a SystemInstance from `@animus-ui/system`. */
|
|
6
7
|
system: string;
|
|
@@ -25,6 +26,11 @@ export interface AnimusExtractOptions {
|
|
|
25
26
|
* still load any module.
|
|
26
27
|
*/
|
|
27
28
|
closedDynamicImports?: ClosedDynamicImport[];
|
|
29
|
+
/** The font faces this app preloads, in place of each typed face's own
|
|
30
|
+
* `preload` flag, so a face declared once for every product preloads only
|
|
31
|
+
* where the app renders it: family names, or `{ family, weight?, style? }`
|
|
32
|
+
* (see `FontPreloadEntry`). `[]` preloads none. */
|
|
33
|
+
fontPreloads?: FontPreloadEntry[];
|
|
28
34
|
/**
|
|
29
35
|
* Substrings, or globs when `*`/`?` present. Replaces the defaults `dist`,
|
|
30
36
|
* `.test.`, `.spec.`; `node_modules`, `.next`, `.animus` always apply.
|
|
@@ -107,6 +113,15 @@ export interface AnimusExtractOptions {
|
|
|
107
113
|
code: string;
|
|
108
114
|
cspHash: string;
|
|
109
115
|
};
|
|
116
|
+
/**
|
|
117
|
+
* Load third-party stylesheets into `anm-vendor`, a layer below every
|
|
118
|
+
* Animus layer, so their normal rules never beat Animus's. Needs
|
|
119
|
+
* `@animus-ui/layergate`. `sheets` are delivered with Animus's stylesheet,
|
|
120
|
+
* ahead of it; `dependencies` also covers `.css` your modules import from
|
|
121
|
+
* packages; `optOut` keeps a file's own rules outside the layer. App CSS
|
|
122
|
+
* and CSS Modules are left alone.
|
|
123
|
+
*/
|
|
124
|
+
quarantine?: AnimusQuarantineOptions;
|
|
110
125
|
}
|
|
111
126
|
export declare function animusExtract(options: AnimusExtractOptions): Plugin;
|
|
112
127
|
export default animusExtract;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EACV,uBAAuB,EACvB,mBAAmB,EACnB,gBAAgB,EAChB,gBAAgB,EAEhB,eAAe,EAChB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,MAAM,EAAc,MAAM,MAAM,CAAC;AAE/C,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,YAAY,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AA+C3E,MAAM,WAAW,oBAAoB;IACnC,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf;;;0DAGsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC7C;;;wDAGoD;IACpD,YAAY,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAClC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;;;;;;OAQG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;+CAE2C;IAC3C,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAC5B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,sEAAsE;IACtE,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,aAAa,GAAG,YAAY,CAAC;IACpC;;sDAEkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;gBAEY;IACZ,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,IAAI,CAAC;IACd;;;OAGG;IACH,mBAAmB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IACxD;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,uBAAuB,CAAC;CACtC;AAiBD,wBAAgB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM,CA4MnE;eAEc,aAAa"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AnimusQuarantineOptions } from '@animus-ui/extract/pipeline';
|
|
2
|
+
import type { LayergatePlugin } from '@animus-ui/layergate/vite';
|
|
3
|
+
export interface Quarantine {
|
|
4
|
+
/** The layer order, `anm-vendor` first, Animus's stylesheet declares. */
|
|
5
|
+
order: string[];
|
|
6
|
+
/** layergate's plugin, whose hooks Animus's own run. */
|
|
7
|
+
plugin(): Promise<LayergatePlugin>;
|
|
8
|
+
}
|
|
9
|
+
export declare function quarantinePlugin(options: AnimusQuarantineOptions, layers: readonly string[] | undefined): Quarantine;
|
|
10
|
+
//# sourceMappingURL=quarantine-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quarantine-plugin.d.ts","sourceRoot":"","sources":["../src/quarantine-plugin.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAC3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAEjE,MAAM,WAAW,UAAU;IACzB,yEAAyE;IACzE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,wDAAwD;IACxD,MAAM,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;CACpC;AAED,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,uBAAuB,EAChC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GACpC,UAAU,CAIZ"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `server.watch.ignored` entries that watch installed kits. Vite always
|
|
3
|
+
* ignores every `node_modules` path, where an installed kit lives, and
|
|
4
|
+
* watches what a negated glob names. Each glob names a kit root and the
|
|
5
|
+
* paths under it that ingestion would read: a segment holding an excluded
|
|
6
|
+
* name, the kit's own `node_modules` among them, stays ignored, so the
|
|
7
|
+
* watcher never walks into it. The segment test is a regex lookahead,
|
|
8
|
+
* which picomatch keeps as written.
|
|
9
|
+
*/
|
|
10
|
+
export declare function kitWatchNegations(roots: readonly string[]): string[];
|
|
11
|
+
//# sourceMappingURL=watch-ignored.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watch-ignored.d.ts","sourceRoot":"","sources":["../src/watch-ignored.ts"],"names":[],"mappings":"AASA;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAKpE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@animus-ui/vite-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.36",
|
|
4
4
|
"description": "Animus static CSS extraction Vite plugin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"animus",
|
|
@@ -39,16 +39,23 @@
|
|
|
39
39
|
"compile": "bun check"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@animus-ui/extract": "0.1.
|
|
42
|
+
"@animus-ui/extract": "0.1.36",
|
|
43
43
|
"browserslist": "^4.28.8",
|
|
44
44
|
"lightningcss": "^1.33.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@animus-ui/assertions": "0.1.
|
|
47
|
+
"@animus-ui/assertions": "0.1.36",
|
|
48
|
+
"@animus-ui/layergate": "0.1.36"
|
|
48
49
|
},
|
|
49
50
|
"peerDependencies": {
|
|
51
|
+
"@animus-ui/layergate": "0.1.36",
|
|
50
52
|
"vite": ">=8 <9"
|
|
51
53
|
},
|
|
54
|
+
"peerDependenciesMeta": {
|
|
55
|
+
"@animus-ui/layergate": {
|
|
56
|
+
"optional": true
|
|
57
|
+
}
|
|
58
|
+
},
|
|
52
59
|
"engines": {
|
|
53
60
|
"node": ">=22.12.0"
|
|
54
61
|
}
|