@czap/vite 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 +21 -0
- package/README.md +19 -0
- package/dist/css-quantize.d.ts +53 -0
- package/dist/css-quantize.d.ts.map +1 -0
- package/dist/css-quantize.js +247 -0
- package/dist/css-quantize.js.map +1 -0
- package/dist/environments.d.ts +36 -0
- package/dist/environments.d.ts.map +1 -0
- package/dist/environments.js +67 -0
- package/dist/environments.js.map +1 -0
- package/dist/hmr.d.ts +37 -0
- package/dist/hmr.d.ts.map +1 -0
- package/dist/hmr.js +84 -0
- package/dist/hmr.js.map +1 -0
- package/dist/html-transform.d.ts +19 -0
- package/dist/html-transform.d.ts.map +1 -0
- package/dist/html-transform.js +54 -0
- package/dist/html-transform.js.map +1 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/normalize-css-eol.d.ts +7 -0
- package/dist/normalize-css-eol.d.ts.map +1 -0
- package/dist/normalize-css-eol.js +9 -0
- package/dist/normalize-css-eol.js.map +1 -0
- package/dist/plugin.d.ts +48 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +404 -0
- package/dist/plugin.js.map +1 -0
- package/dist/primitive-resolve.d.ts +56 -0
- package/dist/primitive-resolve.d.ts.map +1 -0
- package/dist/primitive-resolve.js +71 -0
- package/dist/primitive-resolve.js.map +1 -0
- package/dist/resolve-fs.d.ts +13 -0
- package/dist/resolve-fs.d.ts.map +1 -0
- package/dist/resolve-fs.js +80 -0
- package/dist/resolve-fs.js.map +1 -0
- package/dist/resolve-utils.d.ts +20 -0
- package/dist/resolve-utils.d.ts.map +1 -0
- package/dist/resolve-utils.js +45 -0
- package/dist/resolve-utils.js.map +1 -0
- package/dist/style-transform.d.ts +49 -0
- package/dist/style-transform.d.ts.map +1 -0
- package/dist/style-transform.js +122 -0
- package/dist/style-transform.js.map +1 -0
- package/dist/theme-transform.d.ts +44 -0
- package/dist/theme-transform.d.ts.map +1 -0
- package/dist/theme-transform.js +85 -0
- package/dist/theme-transform.js.map +1 -0
- package/dist/token-transform.d.ts +42 -0
- package/dist/token-transform.d.ts.map +1 -0
- package/dist/token-transform.js +84 -0
- package/dist/token-transform.js.map +1 -0
- package/dist/virtual-modules.d.ts +55 -0
- package/dist/virtual-modules.d.ts.map +1 -0
- package/dist/virtual-modules.js +141 -0
- package/dist/virtual-modules.js.map +1 -0
- package/dist/wasm-resolve.d.ts +25 -0
- package/dist/wasm-resolve.d.ts.map +1 -0
- package/dist/wasm-resolve.js +36 -0
- package/dist/wasm-resolve.js.map +1 -0
- package/package.json +63 -0
- package/src/css-quantize.ts +294 -0
- package/src/environments.ts +98 -0
- package/src/hmr.ts +121 -0
- package/src/html-transform.ts +61 -0
- package/src/index.ts +71 -0
- package/src/normalize-css-eol.ts +8 -0
- package/src/plugin.ts +492 -0
- package/src/primitive-resolve.ts +106 -0
- package/src/resolve-fs.ts +82 -0
- package/src/resolve-utils.ts +54 -0
- package/src/style-transform.ts +157 -0
- package/src/theme-transform.ts +119 -0
- package/src/token-transform.ts +117 -0
- package/src/virtual-modules.ts +160 -0
- package/src/wasm-resolve.ts +54 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML transform -- resolves `data-czap="name"` to boundary JSON.
|
|
3
|
+
*
|
|
4
|
+
* Scans HTML/Astro source for `data-czap="..."` attributes, resolves
|
|
5
|
+
* the named boundary via the existing boundary resolution infrastructure,
|
|
6
|
+
* and replaces with `data-czap-boundary='...'` containing serialized JSON.
|
|
7
|
+
*
|
|
8
|
+
* @module
|
|
9
|
+
*/
|
|
10
|
+
import { Diagnostics } from '@czap/core';
|
|
11
|
+
import { resolvePrimitive } from './primitive-resolve.js';
|
|
12
|
+
// Match data-czap="boundaryName" (not data-czap-* which are other attrs)
|
|
13
|
+
const DATA_CZAP_PATTERN = /data-czap="([^"]+)"/g;
|
|
14
|
+
/**
|
|
15
|
+
* Transform HTML source, replacing `data-czap="name"` with resolved boundary JSON.
|
|
16
|
+
*
|
|
17
|
+
* @param source - The HTML/Astro source string
|
|
18
|
+
* @param fromFile - The file path of the source (for resolution context)
|
|
19
|
+
* @param projectRoot - The project root directory
|
|
20
|
+
* @returns The transformed source, or the original if no transforms needed
|
|
21
|
+
*/
|
|
22
|
+
export async function transformHTML(source, fromFile, projectRoot) {
|
|
23
|
+
const matches = [...source.matchAll(DATA_CZAP_PATTERN)];
|
|
24
|
+
if (matches.length === 0)
|
|
25
|
+
return source;
|
|
26
|
+
let result = source;
|
|
27
|
+
for (const match of matches) {
|
|
28
|
+
const fullMatch = match[0];
|
|
29
|
+
const boundaryName = match[1];
|
|
30
|
+
const resolution = await resolvePrimitive('boundary', boundaryName, fromFile, projectRoot);
|
|
31
|
+
if (!resolution) {
|
|
32
|
+
Diagnostics.warn({
|
|
33
|
+
source: 'czap/vite.html-transform',
|
|
34
|
+
code: 'boundary-not-found',
|
|
35
|
+
message: `Boundary "${boundaryName}" could not be resolved for HTML transform.`,
|
|
36
|
+
detail: { fromFile },
|
|
37
|
+
});
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const boundary = resolution.primitive;
|
|
41
|
+
const serialized = JSON.stringify({
|
|
42
|
+
id: boundary.id,
|
|
43
|
+
input: boundary.input,
|
|
44
|
+
thresholds: boundary.thresholds,
|
|
45
|
+
states: boundary.states,
|
|
46
|
+
hysteresis: boundary.hysteresis,
|
|
47
|
+
});
|
|
48
|
+
// Replace data-czap="name" with data-czap-boundary='...'
|
|
49
|
+
const replacement = `data-czap-boundary='${serialized.replace(/'/g, ''')}'`;
|
|
50
|
+
result = result.replace(fullMatch, replacement);
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=html-transform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"html-transform.js","sourceRoot":"","sources":["../src/html-transform.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE1D,yEAAyE;AACzE,MAAM,iBAAiB,GAAG,sBAAsB,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,MAAc,EAAE,QAAgB,EAAE,WAAmB;IACvF,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC;IACxD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC;IAExC,IAAI,MAAM,GAAG,MAAM,CAAC;IAEpB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QAC5B,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QAE/B,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;QAC3F,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,WAAW,CAAC,IAAI,CAAC;gBACf,MAAM,EAAE,0BAA0B;gBAClC,IAAI,EAAE,oBAAoB;gBAC1B,OAAO,EAAE,aAAa,YAAY,6CAA6C;gBAC/E,MAAM,EAAE,EAAE,QAAQ,EAAE;aACrB,CAAC,CAAC;YACH,SAAS;QACX,CAAC;QAED,MAAM,QAAQ,GAAG,UAAU,CAAC,SAAS,CAAC;QACtC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;YAChC,EAAE,EAAE,QAAQ,CAAC,EAAE;YACf,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;SAChC,CAAC,CAAC;QAEH,yDAAyD;QACzD,MAAM,WAAW,GAAG,uBAAuB,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;QAChF,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@czap/vite` — **LiteShip** Vite 8 plugin: turns `@token` / `@theme` /
|
|
3
|
+
* `@style` / `@quantize` at-rule blocks into native CSS and **rigs** HMR for
|
|
4
|
+
* `@czap/*` definitions.
|
|
5
|
+
*
|
|
6
|
+
* The plugin hooks into Vite's `resolveId`, `load`, `transform`, and
|
|
7
|
+
* `handleHotUpdate` phases:
|
|
8
|
+
*
|
|
9
|
+
* - `resolveId` + `load`: map `virtual:czap/*` specifiers to generated
|
|
10
|
+
* modules (device capabilities, WASM URL, ...).
|
|
11
|
+
* - `transform`: rewrite `@token`, `@theme`, `@style`, and `@quantize`
|
|
12
|
+
* at-rule blocks into native CSS (custom properties,
|
|
13
|
+
* `html[data-theme]` selectors, scoped `@layer` / `@scope` rules,
|
|
14
|
+
* and `@container` queries).
|
|
15
|
+
* - `handleHotUpdate`: emit surgical HMR payloads so CSS variables,
|
|
16
|
+
* shader uniforms, and boundary definitions update without a full
|
|
17
|
+
* page reload.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* // vite.config.ts
|
|
22
|
+
* import { defineConfig } from 'vite';
|
|
23
|
+
* import { plugin as czap } from '@czap/vite';
|
|
24
|
+
*
|
|
25
|
+
* const config = defineConfig({
|
|
26
|
+
* plugins: [czap({ themes: ['./themes/default.ts'] })],
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @module
|
|
31
|
+
*/
|
|
32
|
+
export type { PluginConfig } from './plugin.js';
|
|
33
|
+
export { plugin } from './plugin.js';
|
|
34
|
+
export { resolveWASM } from './wasm-resolve.js';
|
|
35
|
+
export type { WASMResolution } from './wasm-resolve.js';
|
|
36
|
+
export type { QuantizeBlock } from './css-quantize.js';
|
|
37
|
+
export { parseQuantizeBlocks, compileQuantizeBlock } from './css-quantize.js';
|
|
38
|
+
export type { TokenBlock } from './token-transform.js';
|
|
39
|
+
export { parseTokenBlocks, compileTokenBlock } from './token-transform.js';
|
|
40
|
+
export type { ThemeBlock } from './theme-transform.js';
|
|
41
|
+
export { parseThemeBlocks, compileThemeBlock } from './theme-transform.js';
|
|
42
|
+
export type { StyleBlock } from './style-transform.js';
|
|
43
|
+
export { parseStyleBlocks, compileStyleBlock } from './style-transform.js';
|
|
44
|
+
export { transformHTML } from './html-transform.js';
|
|
45
|
+
export type { VirtualModuleId } from './virtual-modules.js';
|
|
46
|
+
export { resolveVirtualId, isVirtualId, loadVirtualModule } from './virtual-modules.js';
|
|
47
|
+
export type { HMRPayload } from './hmr.js';
|
|
48
|
+
export { handleHMR } from './hmr.js';
|
|
49
|
+
export type { PrimitiveKind, PrimitiveResolution, PrimitiveShape } from './primitive-resolve.js';
|
|
50
|
+
export { resolvePrimitive } from './primitive-resolve.js';
|
|
51
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAGH,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAG9E,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAG3E,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAG3E,YAAY,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAG3E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAGxF,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAMrC,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACjG,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@czap/vite` — **LiteShip** Vite 8 plugin: turns `@token` / `@theme` /
|
|
3
|
+
* `@style` / `@quantize` at-rule blocks into native CSS and **rigs** HMR for
|
|
4
|
+
* `@czap/*` definitions.
|
|
5
|
+
*
|
|
6
|
+
* The plugin hooks into Vite's `resolveId`, `load`, `transform`, and
|
|
7
|
+
* `handleHotUpdate` phases:
|
|
8
|
+
*
|
|
9
|
+
* - `resolveId` + `load`: map `virtual:czap/*` specifiers to generated
|
|
10
|
+
* modules (device capabilities, WASM URL, ...).
|
|
11
|
+
* - `transform`: rewrite `@token`, `@theme`, `@style`, and `@quantize`
|
|
12
|
+
* at-rule blocks into native CSS (custom properties,
|
|
13
|
+
* `html[data-theme]` selectors, scoped `@layer` / `@scope` rules,
|
|
14
|
+
* and `@container` queries).
|
|
15
|
+
* - `handleHotUpdate`: emit surgical HMR payloads so CSS variables,
|
|
16
|
+
* shader uniforms, and boundary definitions update without a full
|
|
17
|
+
* page reload.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* // vite.config.ts
|
|
22
|
+
* import { defineConfig } from 'vite';
|
|
23
|
+
* import { plugin as czap } from '@czap/vite';
|
|
24
|
+
*
|
|
25
|
+
* const config = defineConfig({
|
|
26
|
+
* plugins: [czap({ themes: ['./themes/default.ts'] })],
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @module
|
|
31
|
+
*/
|
|
32
|
+
export { plugin } from './plugin.js';
|
|
33
|
+
export { resolveWASM } from './wasm-resolve.js';
|
|
34
|
+
export { parseQuantizeBlocks, compileQuantizeBlock } from './css-quantize.js';
|
|
35
|
+
export { parseTokenBlocks, compileTokenBlock } from './token-transform.js';
|
|
36
|
+
export { parseThemeBlocks, compileThemeBlock } from './theme-transform.js';
|
|
37
|
+
export { parseStyleBlocks, compileStyleBlock } from './style-transform.js';
|
|
38
|
+
// HTML transform
|
|
39
|
+
export { transformHTML } from './html-transform.js';
|
|
40
|
+
export { resolveVirtualId, isVirtualId, loadVirtualModule } from './virtual-modules.js';
|
|
41
|
+
export { handleHMR } from './hmr.js';
|
|
42
|
+
export { resolvePrimitive } from './primitive-resolve.js';
|
|
43
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAIH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKhD,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAI9E,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAI3E,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAI3E,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE3E,iBAAiB;AACjB,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAIxF,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAOrC,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize-css-eol.d.ts","sourceRoot":"","sources":["../src/normalize-css-eol.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize-css-eol.js","sourceRoot":"","sources":["../src/normalize-css-eol.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,GAAW;IACjD,OAAO,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AACzD,CAAC"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main Vite 8 plugin for czap -- processes `@token`, `@theme`,
|
|
3
|
+
* `@style`, and `@quantize` CSS blocks, handles HMR, serves virtual
|
|
4
|
+
* modules, and configures build environments.
|
|
5
|
+
*
|
|
6
|
+
* Transform pipeline order: tokens -- themes -- styles -- quantize.
|
|
7
|
+
* This ordering ensures themes / styles can reference token custom
|
|
8
|
+
* properties that were already compiled earlier in the pipeline.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import type { Plugin } from 'vite';
|
|
13
|
+
/**
|
|
14
|
+
* Configuration options for the {@link plugin} factory. Every field
|
|
15
|
+
* is optional; omitted values use convention-based defaults.
|
|
16
|
+
*/
|
|
17
|
+
export interface PluginConfig {
|
|
18
|
+
/** Override source directories for each primitive kind. */
|
|
19
|
+
readonly dirs?: Partial<Record<'boundary' | 'token' | 'theme' | 'style', string>>;
|
|
20
|
+
/** Toggle surgical HMR emission (default `true`). */
|
|
21
|
+
readonly hmr?: boolean;
|
|
22
|
+
/** Named Vite environments to configure (browser / server / shader). */
|
|
23
|
+
readonly environments?: readonly ('browser' | 'server' | 'shader')[];
|
|
24
|
+
/** Opt-in WASM runtime configuration. */
|
|
25
|
+
readonly wasm?: {
|
|
26
|
+
readonly enabled?: boolean;
|
|
27
|
+
readonly path?: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Create the czap Vite plugin.
|
|
32
|
+
*
|
|
33
|
+
* Transforms CSS files containing `@token`, `@theme`, `@style`, and
|
|
34
|
+
* `@quantize` blocks into native CSS custom properties,
|
|
35
|
+
* `html[data-theme]` selectors, scoped `@layer` / `@scope` rules, and
|
|
36
|
+
* `@container` queries respectively. Uses convention-based definition
|
|
37
|
+
* resolution and provides HMR support for surgical CSS and shader
|
|
38
|
+
* uniform updates.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* // vite.config.ts
|
|
43
|
+
* import { plugin as czap } from '@czap/vite';
|
|
44
|
+
* const config = { plugins: [czap()] };
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function plugin(config?: PluginConfig): Plugin;
|
|
48
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAiBnC;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;IAClF,qDAAqD;IACrD,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC;IACvB,wEAAwE;IACxE,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,CAAC,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAC;IACrE,yCAAyC;IACzC,QAAQ,CAAC,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACxE;AAMD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,MAAM,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM,CAwTpD"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main Vite 8 plugin for czap -- processes `@token`, `@theme`,
|
|
3
|
+
* `@style`, and `@quantize` CSS blocks, handles HMR, serves virtual
|
|
4
|
+
* modules, and configures build environments.
|
|
5
|
+
*
|
|
6
|
+
* Transform pipeline order: tokens -- themes -- styles -- quantize.
|
|
7
|
+
* This ordering ensures themes / styles can reference token custom
|
|
8
|
+
* properties that were already compiled earlier in the pipeline.
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync } from 'node:fs';
|
|
13
|
+
import { parseQuantizeBlocks, compileQuantizeBlock } from './css-quantize.js';
|
|
14
|
+
import { resolvePrimitive } from './primitive-resolve.js';
|
|
15
|
+
import { transformHTML } from './html-transform.js';
|
|
16
|
+
import { parseTokenBlocks, compileTokenBlock } from './token-transform.js';
|
|
17
|
+
import { parseThemeBlocks, compileThemeBlock } from './theme-transform.js';
|
|
18
|
+
import { parseStyleBlocks, compileStyleBlock } from './style-transform.js';
|
|
19
|
+
import { resolveVirtualId, loadVirtualModule } from './virtual-modules.js';
|
|
20
|
+
import { buildEnvironments } from './environments.js';
|
|
21
|
+
import { resolveWASM } from './wasm-resolve.js';
|
|
22
|
+
import { normalizeCssLineEndings } from './normalize-css-eol.js';
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Plugin
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/**
|
|
27
|
+
* Create the czap Vite plugin.
|
|
28
|
+
*
|
|
29
|
+
* Transforms CSS files containing `@token`, `@theme`, `@style`, and
|
|
30
|
+
* `@quantize` blocks into native CSS custom properties,
|
|
31
|
+
* `html[data-theme]` selectors, scoped `@layer` / `@scope` rules, and
|
|
32
|
+
* `@container` queries respectively. Uses convention-based definition
|
|
33
|
+
* resolution and provides HMR support for surgical CSS and shader
|
|
34
|
+
* uniform updates.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* // vite.config.ts
|
|
39
|
+
* import { plugin as czap } from '@czap/vite';
|
|
40
|
+
* const config = { plugins: [czap()] };
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export function plugin(config) {
|
|
44
|
+
const hmrEnabled = config?.hmr !== false;
|
|
45
|
+
const wasmEnabled = config?.wasm?.enabled === true;
|
|
46
|
+
let projectRoot = process.cwd();
|
|
47
|
+
let isBuild = false;
|
|
48
|
+
let resolvedWasm = null;
|
|
49
|
+
if (wasmEnabled) {
|
|
50
|
+
resolvedWasm = resolveWASM(projectRoot, config?.wasm?.path);
|
|
51
|
+
}
|
|
52
|
+
let emittedWasmRefId = null;
|
|
53
|
+
// Caches for resolved definitions to avoid re-importing on every transform
|
|
54
|
+
const boundaryCache = new Map();
|
|
55
|
+
const tokenCache = new Map();
|
|
56
|
+
const themeCache = new Map();
|
|
57
|
+
const styleCache = new Map();
|
|
58
|
+
return {
|
|
59
|
+
name: '@czap/vite',
|
|
60
|
+
enforce: 'pre',
|
|
61
|
+
configResolved(resolvedConfig) {
|
|
62
|
+
projectRoot = resolvedConfig.root;
|
|
63
|
+
isBuild = resolvedConfig.command === 'build';
|
|
64
|
+
resolvedWasm = null;
|
|
65
|
+
if (wasmEnabled) {
|
|
66
|
+
resolvedWasm = resolveWASM(projectRoot, config?.wasm?.path);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
buildStart() {
|
|
70
|
+
if (!wasmEnabled) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
resolvedWasm = resolveWASM(projectRoot, config?.wasm?.path);
|
|
74
|
+
if (!resolvedWasm) {
|
|
75
|
+
this.warn('WASM support was enabled, but no czap-compute binary could be resolved. Runtime will fall back to TypeScript kernels.');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (isBuild) {
|
|
79
|
+
emittedWasmRefId = this.emitFile({
|
|
80
|
+
type: 'asset',
|
|
81
|
+
name: 'czap-compute.wasm',
|
|
82
|
+
source: readFileSync(resolvedWasm.filePath),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
// -----------------------------------------------------------------------
|
|
87
|
+
// HMR client script injection
|
|
88
|
+
// -----------------------------------------------------------------------
|
|
89
|
+
transformIndexHtml() {
|
|
90
|
+
if (!hmrEnabled)
|
|
91
|
+
return [];
|
|
92
|
+
return [
|
|
93
|
+
{
|
|
94
|
+
tag: 'script',
|
|
95
|
+
attrs: { type: 'module' },
|
|
96
|
+
children: `import 'virtual:czap/hmr-client';`,
|
|
97
|
+
injectTo: 'head',
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
},
|
|
101
|
+
// -----------------------------------------------------------------------
|
|
102
|
+
// Virtual module resolution
|
|
103
|
+
// -----------------------------------------------------------------------
|
|
104
|
+
resolveId(id) {
|
|
105
|
+
return resolveVirtualId(id);
|
|
106
|
+
},
|
|
107
|
+
load(id) {
|
|
108
|
+
if (id === '\0virtual:czap/wasm-url') {
|
|
109
|
+
if (!wasmEnabled) {
|
|
110
|
+
return 'export const wasmUrl = null;';
|
|
111
|
+
}
|
|
112
|
+
if (!resolvedWasm) {
|
|
113
|
+
return 'export const wasmUrl = null;';
|
|
114
|
+
}
|
|
115
|
+
if (isBuild && emittedWasmRefId) {
|
|
116
|
+
return `export const wasmUrl = import.meta.ROLLUP_FILE_URL_${emittedWasmRefId};`;
|
|
117
|
+
}
|
|
118
|
+
const browserUrl = resolvedWasm.source === 'public' ? '/czap-compute.wasm' : `/@fs/${resolvedWasm.filePath.replace(/\\/g, '/')}`;
|
|
119
|
+
return `export const wasmUrl = ${JSON.stringify(browserUrl)};`;
|
|
120
|
+
}
|
|
121
|
+
return loadVirtualModule(id);
|
|
122
|
+
},
|
|
123
|
+
// -----------------------------------------------------------------------
|
|
124
|
+
// CSS transform pipeline: tokens -> themes -> styles -> quantize
|
|
125
|
+
// -----------------------------------------------------------------------
|
|
126
|
+
async transform(code, id) {
|
|
127
|
+
if (id.endsWith('.html') || id.endsWith('.astro')) {
|
|
128
|
+
const transformed = await transformHTML(code, id, projectRoot);
|
|
129
|
+
if (transformed === code) {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
code: transformed,
|
|
134
|
+
map: null,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// Only process CSS files
|
|
138
|
+
if (!id.endsWith('.css'))
|
|
139
|
+
return null;
|
|
140
|
+
// Quick check -- skip files with no @czap at-rules
|
|
141
|
+
const hasToken = code.includes('@token');
|
|
142
|
+
const hasTheme = code.includes('@theme');
|
|
143
|
+
const hasStyle = code.includes('@style');
|
|
144
|
+
const hasQuantize = code.includes('@quantize');
|
|
145
|
+
if (!hasToken && !hasTheme && !hasStyle && !hasQuantize)
|
|
146
|
+
return null;
|
|
147
|
+
let transformed = normalizeCssLineEndings(code);
|
|
148
|
+
// ---- Phase 1: @token -> CSS custom properties + @property ----
|
|
149
|
+
if (hasToken) {
|
|
150
|
+
const tokenBlocks = parseTokenBlocks(transformed, id);
|
|
151
|
+
for (const block of tokenBlocks) {
|
|
152
|
+
const cacheKey = `${block.tokenName}:${id}`;
|
|
153
|
+
let token = tokenCache.get(cacheKey);
|
|
154
|
+
if (token === undefined) {
|
|
155
|
+
const resolution = await resolvePrimitive('token', block.tokenName, id, projectRoot, config?.dirs?.token);
|
|
156
|
+
token = resolution?.primitive ?? null;
|
|
157
|
+
tokenCache.set(cacheKey, token);
|
|
158
|
+
}
|
|
159
|
+
if (token === null) {
|
|
160
|
+
this.warn(`Could not resolve token "${block.tokenName}" referenced in ${id}:${block.line}`);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const compiled = compileTokenBlock(block, token);
|
|
164
|
+
const blockSpan = findAtRuleBlock(transformed, '@token', block.tokenName);
|
|
165
|
+
if (blockSpan) {
|
|
166
|
+
transformed = transformed.substring(0, blockSpan.start) + compiled + transformed.substring(blockSpan.end);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// ---- Phase 2: @theme -> html[data-theme] selectors + transitions ----
|
|
171
|
+
if (hasTheme) {
|
|
172
|
+
const themeBlocks = parseThemeBlocks(transformed, id);
|
|
173
|
+
for (const block of themeBlocks) {
|
|
174
|
+
const cacheKey = `${block.themeName}:${id}`;
|
|
175
|
+
let theme = themeCache.get(cacheKey);
|
|
176
|
+
if (theme === undefined) {
|
|
177
|
+
const resolution = await resolvePrimitive('theme', block.themeName, id, projectRoot, config?.dirs?.theme);
|
|
178
|
+
theme = resolution?.primitive ?? null;
|
|
179
|
+
themeCache.set(cacheKey, theme);
|
|
180
|
+
}
|
|
181
|
+
if (theme === null) {
|
|
182
|
+
this.warn(`Could not resolve theme "${block.themeName}" referenced in ${id}:${block.line}`);
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const compiled = compileThemeBlock(block, theme);
|
|
186
|
+
const blockSpan = findAtRuleBlock(transformed, '@theme', block.themeName);
|
|
187
|
+
if (blockSpan) {
|
|
188
|
+
transformed = transformed.substring(0, blockSpan.start) + compiled + transformed.substring(blockSpan.end);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// ---- Phase 3: @style -> scoped CSS with @layer/@scope/@starting-style ----
|
|
193
|
+
if (hasStyle) {
|
|
194
|
+
const styleBlocks = parseStyleBlocks(transformed, id);
|
|
195
|
+
for (const block of styleBlocks) {
|
|
196
|
+
const cacheKey = `${block.styleName}:${id}`;
|
|
197
|
+
let style = styleCache.get(cacheKey);
|
|
198
|
+
if (style === undefined) {
|
|
199
|
+
const resolution = await resolvePrimitive('style', block.styleName, id, projectRoot, config?.dirs?.style);
|
|
200
|
+
style = resolution?.primitive ?? null;
|
|
201
|
+
styleCache.set(cacheKey, style);
|
|
202
|
+
}
|
|
203
|
+
if (style === null) {
|
|
204
|
+
this.warn(`Could not resolve style "${block.styleName}" referenced in ${id}:${block.line}`);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const compiled = compileStyleBlock(block, style);
|
|
208
|
+
const blockSpan = findAtRuleBlock(transformed, '@style', block.styleName);
|
|
209
|
+
if (blockSpan) {
|
|
210
|
+
transformed = transformed.substring(0, blockSpan.start) + compiled + transformed.substring(blockSpan.end);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// ---- Phase 4: @quantize -> @container queries (existing) ----
|
|
215
|
+
if (hasQuantize) {
|
|
216
|
+
const quantizeBlocks = parseQuantizeBlocks(transformed, id);
|
|
217
|
+
for (const block of quantizeBlocks) {
|
|
218
|
+
const cacheKey = `${block.boundaryName}:${id}`;
|
|
219
|
+
let boundary = boundaryCache.get(cacheKey);
|
|
220
|
+
if (boundary === undefined) {
|
|
221
|
+
const resolution = await resolvePrimitive('boundary', block.boundaryName, id, projectRoot, config?.dirs?.boundary);
|
|
222
|
+
boundary = resolution?.primitive ?? null;
|
|
223
|
+
boundaryCache.set(cacheKey, boundary);
|
|
224
|
+
}
|
|
225
|
+
if (boundary === null) {
|
|
226
|
+
this.warn(`Could not resolve boundary "${block.boundaryName}" referenced in ${id}:${block.line}`);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
const compiled = compileQuantizeBlock(block, boundary);
|
|
230
|
+
const blockSpan = findAtRuleBlock(transformed, '@quantize', block.boundaryName);
|
|
231
|
+
if (blockSpan) {
|
|
232
|
+
transformed = transformed.substring(0, blockSpan.start) + compiled + transformed.substring(blockSpan.end);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (transformed === code)
|
|
237
|
+
return null;
|
|
238
|
+
return {
|
|
239
|
+
code: transformed,
|
|
240
|
+
map: null,
|
|
241
|
+
};
|
|
242
|
+
},
|
|
243
|
+
// -----------------------------------------------------------------------
|
|
244
|
+
// HMR: invalidate caches + re-transform on definition file changes
|
|
245
|
+
// -----------------------------------------------------------------------
|
|
246
|
+
hotUpdate(options) {
|
|
247
|
+
if (!hmrEnabled)
|
|
248
|
+
return;
|
|
249
|
+
const file = options.file;
|
|
250
|
+
// Invalidate definition caches when source files change
|
|
251
|
+
const isDefFile = file.endsWith('.boundaries.ts') ||
|
|
252
|
+
file.endsWith('/boundaries.ts') ||
|
|
253
|
+
file.endsWith('.tokens.ts') ||
|
|
254
|
+
file.endsWith('/tokens.ts') ||
|
|
255
|
+
file.endsWith('.themes.ts') ||
|
|
256
|
+
file.endsWith('/themes.ts') ||
|
|
257
|
+
file.endsWith('.styles.ts') ||
|
|
258
|
+
file.endsWith('/styles.ts');
|
|
259
|
+
if (isDefFile) {
|
|
260
|
+
// Clear all caches since definitions may cross-reference
|
|
261
|
+
boundaryCache.clear();
|
|
262
|
+
tokenCache.clear();
|
|
263
|
+
themeCache.clear();
|
|
264
|
+
styleCache.clear();
|
|
265
|
+
const moduleGraph = this.environment.moduleGraph;
|
|
266
|
+
const transformModules = Array.from(moduleGraph.idToModuleMap.values()).filter((mod) => {
|
|
267
|
+
const moduleId = mod.id;
|
|
268
|
+
return (typeof moduleId === 'string' &&
|
|
269
|
+
(moduleId.endsWith('.css') || moduleId.endsWith('.astro') || moduleId.endsWith('.html')));
|
|
270
|
+
});
|
|
271
|
+
if (transformModules.length > 0) {
|
|
272
|
+
return transformModules;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (file.endsWith('.css') || file.endsWith('.astro') || file.endsWith('.html')) {
|
|
276
|
+
const moduleGraph = this.environment.moduleGraph;
|
|
277
|
+
const mod = moduleGraph.getModuleById(file);
|
|
278
|
+
if (mod) {
|
|
279
|
+
return [mod];
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return;
|
|
283
|
+
},
|
|
284
|
+
config() {
|
|
285
|
+
if (!config?.environments || config.environments.length === 0)
|
|
286
|
+
return {};
|
|
287
|
+
const envNames = config.environments;
|
|
288
|
+
const envs = buildEnvironments(envNames);
|
|
289
|
+
return {
|
|
290
|
+
environments: envs,
|
|
291
|
+
};
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
// ---------------------------------------------------------------------------
|
|
296
|
+
// Helpers
|
|
297
|
+
// ---------------------------------------------------------------------------
|
|
298
|
+
/**
|
|
299
|
+
* Find the full span of an at-rule block in CSS source.
|
|
300
|
+
* Returns the start/end character offsets, or null if not found.
|
|
301
|
+
*
|
|
302
|
+
* Works for any at-rule pattern: `@token`, `@theme`, `@style`,
|
|
303
|
+
* `@quantize`. Uses a state machine to correctly skip block comments,
|
|
304
|
+
* quoted strings, and `url(...)` tokens (which may contain braces in
|
|
305
|
+
* data URIs) before counting brace depth.
|
|
306
|
+
*/
|
|
307
|
+
function findAtRuleBlock(css, marker, name) {
|
|
308
|
+
let searchFrom = 0;
|
|
309
|
+
while (searchFrom < css.length) {
|
|
310
|
+
const idx = css.indexOf(marker, searchFrom);
|
|
311
|
+
if (idx === -1)
|
|
312
|
+
return null;
|
|
313
|
+
// Verify this at-rule is followed by the target name
|
|
314
|
+
const afterMarker = css.substring(idx + marker.length).trimStart();
|
|
315
|
+
if (!afterMarker.startsWith(name)) {
|
|
316
|
+
searchFrom = idx + marker.length;
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
// Ensure the name isn't just a prefix of a longer identifier
|
|
320
|
+
const charAfterName = afterMarker[name.length];
|
|
321
|
+
if (charAfterName !== undefined && /[a-zA-Z0-9_-]/.test(charAfterName)) {
|
|
322
|
+
searchFrom = idx + marker.length;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
// Find the opening brace
|
|
326
|
+
const braceStart = css.indexOf('{', idx);
|
|
327
|
+
/* v8 ignore next — unreachable under real call sites: `findAtRuleBlock` runs only
|
|
328
|
+
after `parseTokenBlocks`/etc. matched a `@marker name { ... }` block with braces,
|
|
329
|
+
so the `{` is always still present in the transformed source. Defensive against
|
|
330
|
+
future multi-phase edits that strip braces between parse and lookup. */
|
|
331
|
+
if (braceStart === -1)
|
|
332
|
+
return null;
|
|
333
|
+
// Walk forward tracking depth with full comment/string/url awareness
|
|
334
|
+
let depth = 1;
|
|
335
|
+
let pos = braceStart + 1;
|
|
336
|
+
while (pos < css.length && depth > 0) {
|
|
337
|
+
const ch = css[pos];
|
|
338
|
+
// Skip block comments: /* ... */
|
|
339
|
+
if (ch === '/' && css[pos + 1] === '*') {
|
|
340
|
+
pos += 2;
|
|
341
|
+
while (pos < css.length - 1 && !(css[pos] === '*' && css[pos + 1] === '/')) {
|
|
342
|
+
pos++;
|
|
343
|
+
}
|
|
344
|
+
pos += 2;
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
// Skip quoted strings: "..." and '...' (with backslash escapes)
|
|
348
|
+
if (ch === '"' || ch === "'") {
|
|
349
|
+
const quote = ch;
|
|
350
|
+
pos++;
|
|
351
|
+
while (pos < css.length && css[pos] !== quote) {
|
|
352
|
+
if (css[pos] === '\\')
|
|
353
|
+
pos++;
|
|
354
|
+
pos++;
|
|
355
|
+
}
|
|
356
|
+
pos++;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
// Skip url(...) tokens: may contain unquoted data URIs with braces
|
|
360
|
+
if (ch === 'u' && css.slice(pos, pos + 4).toLowerCase() === 'url(') {
|
|
361
|
+
pos += 4;
|
|
362
|
+
// url() may use a quoted or unquoted value
|
|
363
|
+
if (css[pos] === '"' || css[pos] === "'") {
|
|
364
|
+
const quote = css[pos];
|
|
365
|
+
pos++;
|
|
366
|
+
while (pos < css.length && css[pos] !== quote) {
|
|
367
|
+
if (css[pos] === '\\')
|
|
368
|
+
pos++;
|
|
369
|
+
pos++;
|
|
370
|
+
}
|
|
371
|
+
pos++; // closing quote
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
// unquoted -- scan until the matching ')'
|
|
375
|
+
let parenDepth = 1;
|
|
376
|
+
while (pos < css.length && parenDepth > 0) {
|
|
377
|
+
if (css[pos] === '(')
|
|
378
|
+
parenDepth++;
|
|
379
|
+
else if (css[pos] === ')')
|
|
380
|
+
parenDepth--;
|
|
381
|
+
pos++;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
continue;
|
|
385
|
+
}
|
|
386
|
+
if (ch === '{')
|
|
387
|
+
depth++;
|
|
388
|
+
else if (ch === '}')
|
|
389
|
+
depth--;
|
|
390
|
+
pos++;
|
|
391
|
+
}
|
|
392
|
+
if (depth === 0) {
|
|
393
|
+
return { start: idx, end: pos };
|
|
394
|
+
}
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
/* v8 ignore next — unreachable under real call sites: the inner `while` only runs
|
|
398
|
+
when `parseTokenBlocks` has already matched a `@marker name { ... }` block, so the
|
|
399
|
+
first indexOf hit returns either a `{start,end}` span or null inside the loop.
|
|
400
|
+
This terminal `return null` is a defense against pathological CSS where the
|
|
401
|
+
marker+name hits but searchFrom exhausts without a `{` match. */
|
|
402
|
+
return null;
|
|
403
|
+
}
|
|
404
|
+
//# sourceMappingURL=plugin.js.map
|