@pikacss/integration 0.0.47 → 0.0.49
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 +27 -0
- package/dist/index.d.mts +131 -0
- package/dist/index.mjs +281 -174
- package/package.json +12 -11
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @pikacss/integration
|
|
2
|
+
|
|
3
|
+
Integration layer between the PikaCSS core engine and bundler plugins. Handles config loading, file scanning, source transformation, and code generation.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @pikacss/integration
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createCtx } from '@pikacss/integration'
|
|
15
|
+
|
|
16
|
+
const ctx = createCtx({
|
|
17
|
+
cwd: process.cwd(),
|
|
18
|
+
})
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Documentation
|
|
22
|
+
|
|
23
|
+
See the [full documentation](https://pikacss.com/guide/integrations/).
|
|
24
|
+
|
|
25
|
+
## License
|
|
26
|
+
|
|
27
|
+
MIT
|
package/dist/index.d.mts
CHANGED
|
@@ -3,41 +3,127 @@ import { SourceMap } from "magic-string";
|
|
|
3
3
|
export * from "@pikacss/core";
|
|
4
4
|
|
|
5
5
|
//#region src/eventHook.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* Callback function invoked when an event is triggered on an `EventHook`.
|
|
8
|
+
* @internal
|
|
9
|
+
*
|
|
10
|
+
* @typeParam EventPayload - The type of data passed to the listener when the event fires.
|
|
11
|
+
*/
|
|
6
12
|
type EventHookListener<EventPayload> = (payload: EventPayload) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Lightweight publish-subscribe event hook for internal reactive state notifications.
|
|
15
|
+
* @internal
|
|
16
|
+
*
|
|
17
|
+
* @typeParam EventPayload - The type of data passed to listeners when the event fires.
|
|
18
|
+
*
|
|
19
|
+
* @remarks
|
|
20
|
+
* Used by the integration context to notify build-tool plugins when styles
|
|
21
|
+
* or TypeScript codegen content have changed and need to be regenerated.
|
|
22
|
+
*/
|
|
7
23
|
interface EventHook<EventPayload> {
|
|
24
|
+
/** The set of currently registered listener callbacks. */
|
|
8
25
|
listeners: Set<EventHookListener<EventPayload>>;
|
|
26
|
+
/** Invokes all registered listeners synchronously with the given payload. No-op when the listener set is empty. */
|
|
9
27
|
trigger: (payload: EventPayload) => void;
|
|
28
|
+
/** Registers a listener and returns an unsubscribe function that removes it. */
|
|
10
29
|
on: (listener: EventHookListener<EventPayload>) => () => void;
|
|
30
|
+
/** Removes a previously registered listener. No-op if the listener is not registered. */
|
|
11
31
|
off: (listener: EventHookListener<EventPayload>) => void;
|
|
12
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Creates a new event hook instance for publish-subscribe event dispatching.
|
|
35
|
+
* @internal
|
|
36
|
+
*
|
|
37
|
+
* @typeParam EventPayload - The type of data passed to listeners when the event fires.
|
|
38
|
+
* @returns A new `EventHook` with an empty listener set.
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* The returned hook can register listeners via `on`, remove them via `off`,
|
|
42
|
+
* and broadcast payloads to all listeners via `trigger`. Calling `trigger`
|
|
43
|
+
* with no registered listeners is a no-op.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* const hook = createEventHook<string>()
|
|
48
|
+
* const off = hook.on((msg) => console.log(msg))
|
|
49
|
+
* hook.trigger('hello') // logs "hello"
|
|
50
|
+
* off() // unsubscribes
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
13
53
|
declare function createEventHook<EventPayload>(): EventHook<EventPayload>;
|
|
14
54
|
//#endregion
|
|
15
55
|
//#region src/types.d.ts
|
|
56
|
+
/**
|
|
57
|
+
* Records a single `pika()` call result, pairing the resolved atomic style IDs with the original call arguments.
|
|
58
|
+
*
|
|
59
|
+
* @remarks
|
|
60
|
+
* Each source file may produce multiple `UsageRecord` entries — one per `pika()` call site.
|
|
61
|
+
* These records drive both CSS output (via `atomicStyleIds`) and IDE preview overloads (via `params`).
|
|
62
|
+
*/
|
|
16
63
|
interface UsageRecord {
|
|
64
|
+
/** The list of atomic CSS class names generated by the engine for this call. */
|
|
17
65
|
atomicStyleIds: string[];
|
|
66
|
+
/** The original arguments passed to `engine.use()`, preserved for TypeScript codegen overload generation. */
|
|
18
67
|
params: Parameters<Engine['use']>;
|
|
19
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Classifier and regex utilities for recognizing all `pika()` function call variants in source code.
|
|
71
|
+
*
|
|
72
|
+
* @remarks
|
|
73
|
+
* The function name is configurable via `IntegrationContextOptions.fnName`, so all
|
|
74
|
+
* variants (`.str`, `.arr`, preview `p` suffix, bracket notation) are derived
|
|
75
|
+
* dynamically from that base name.
|
|
76
|
+
*/
|
|
20
77
|
interface FnUtils {
|
|
78
|
+
/** Returns `true` if the given function name is a normal call (output format determined by `transformedFormat`). */
|
|
21
79
|
isNormal: (fnName: string) => boolean;
|
|
80
|
+
/** Returns `true` if the given function name forces string output (e.g., `pika.str`). */
|
|
22
81
|
isForceString: (fnName: string) => boolean;
|
|
82
|
+
/** Returns `true` if the given function name forces array output (e.g., `pika.arr`). */
|
|
23
83
|
isForceArray: (fnName: string) => boolean;
|
|
84
|
+
/** Returns `true` if the given function name is a preview variant (e.g., `pikap`, `pikap.str`). */
|
|
24
85
|
isPreview: (fnName: string) => boolean;
|
|
86
|
+
/** A compiled global regex that matches all recognized function call variants, including bracket-notation accessors. */
|
|
25
87
|
RE: RegExp;
|
|
26
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Configuration options for creating an integration context.
|
|
91
|
+
*
|
|
92
|
+
* @remarks
|
|
93
|
+
* These options are set by bundler plugin adapters (Vite, webpack, Nuxt) and are
|
|
94
|
+
* not typically configured by end users directly.
|
|
95
|
+
*/
|
|
27
96
|
interface IntegrationContextOptions {
|
|
97
|
+
/** The working directory used to resolve relative paths for config files, codegen outputs, and source scanning. */
|
|
28
98
|
cwd: string;
|
|
99
|
+
/** The npm package name of the integration consumer (e.g., `'@pikacss/unplugin'`), embedded in generated file headers and import paths. */
|
|
29
100
|
currentPackageName: string;
|
|
101
|
+
/** Glob patterns controlling which source files are scanned for `pika()` calls. `include` specifies files to process; `exclude` specifies files to skip. */
|
|
30
102
|
scan: {
|
|
31
103
|
include: string[];
|
|
32
104
|
exclude: string[];
|
|
33
105
|
};
|
|
106
|
+
/** The engine configuration object, a path to a config file, or `null`/`undefined` to trigger auto-discovery of `pika.config.*` files. */
|
|
34
107
|
configOrPath: EngineConfig | string | Nullish;
|
|
108
|
+
/** The base function name to recognize in source code (e.g., `'pika'`). All variants (`.str`, `.arr`, preview) are derived from this name. */
|
|
35
109
|
fnName: string;
|
|
110
|
+
/** Controls the default output format of normal `pika()` calls: `'string'` produces a space-joined class string, `'array'` produces a string array. */
|
|
36
111
|
transformedFormat: 'string' | 'array';
|
|
112
|
+
/** Path to the generated TypeScript declaration file (`pika.gen.ts`), or `false` to disable TypeScript codegen entirely. */
|
|
37
113
|
tsCodegen: false | string;
|
|
114
|
+
/** Path to the generated CSS output file (e.g., `'pika.gen.css'`). */
|
|
38
115
|
cssCodegen: string;
|
|
116
|
+
/** When `true`, automatically scaffolds a default `pika.config.js` file if no config file is found. */
|
|
39
117
|
autoCreateConfig: boolean;
|
|
40
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Discriminated union representing the outcome of loading an engine configuration file.
|
|
121
|
+
*
|
|
122
|
+
* @remarks
|
|
123
|
+
* Three shapes are possible: an inline config (no file), a successfully loaded file-based
|
|
124
|
+
* config, or a failed/missing load (all fields `null`). The `file` and `content` fields are
|
|
125
|
+
* populated only when the config was loaded from disk, enabling hot-reload detection.
|
|
126
|
+
*/
|
|
41
127
|
type LoadedConfigResult = {
|
|
42
128
|
config: EngineConfig;
|
|
43
129
|
file: null;
|
|
@@ -51,42 +137,87 @@ type LoadedConfigResult = {
|
|
|
51
137
|
file: string;
|
|
52
138
|
content: string;
|
|
53
139
|
};
|
|
140
|
+
/**
|
|
141
|
+
* The main build-tool integration context that bridges the PikaCSS engine with bundler plugins.
|
|
142
|
+
*
|
|
143
|
+
* @remarks
|
|
144
|
+
* Created via `createCtx()`. The context manages the full build lifecycle: config loading,
|
|
145
|
+
* engine initialization, source file transformation, usage tracking, and output file generation.
|
|
146
|
+
* All transform and codegen calls automatically await `setup()` completion before proceeding.
|
|
147
|
+
*/
|
|
54
148
|
interface IntegrationContext {
|
|
149
|
+
/** The current working directory. Can be updated at runtime (e.g., when the project root changes). */
|
|
55
150
|
cwd: string;
|
|
151
|
+
/** The npm package name of the integration consumer, used in generated file headers and module declarations. */
|
|
56
152
|
currentPackageName: string;
|
|
153
|
+
/** The base function name recognized in source transforms (e.g., `'pika'`). */
|
|
57
154
|
fnName: string;
|
|
155
|
+
/** The default output format for normal `pika()` calls: `'string'` or `'array'`. */
|
|
58
156
|
transformedFormat: 'string' | 'array';
|
|
157
|
+
/** Absolute path to the generated CSS output file, computed from `cwd` and the configured relative path. */
|
|
59
158
|
cssCodegenFilepath: string;
|
|
159
|
+
/** Absolute path to the generated TypeScript declaration file, or `null` if TypeScript codegen is disabled. */
|
|
60
160
|
tsCodegenFilepath: string | Nullish;
|
|
161
|
+
/** Whether the `vue` package is installed in the project, used to include Vue-specific type declarations in codegen. */
|
|
61
162
|
hasVue: boolean;
|
|
163
|
+
/** The loaded engine configuration object, or `null` if loading failed or no config was found. */
|
|
62
164
|
resolvedConfig: EngineConfig | Nullish;
|
|
165
|
+
/** Absolute path to the resolved config file on disk, or `null` for inline configs or when no config was loaded. */
|
|
63
166
|
resolvedConfigPath: string | Nullish;
|
|
167
|
+
/** Raw string content of the config file, or `null` for inline configs or when no config was loaded. */
|
|
64
168
|
resolvedConfigContent: string | Nullish;
|
|
169
|
+
/** Loads (or reloads) the engine configuration from disk or inline source, updating `resolvedConfig`, `resolvedConfigPath`, and `resolvedConfigContent`. */
|
|
65
170
|
loadConfig: () => Promise<LoadedConfigResult>;
|
|
171
|
+
/** Map from source file ID to the list of `UsageRecord` entries extracted during transforms. Keyed by the file path relative to `cwd`. */
|
|
66
172
|
usages: Map<string, UsageRecord[]>;
|
|
173
|
+
/** Map from source file ID to preview-only `UsageRecord` entries (from `pikap()` calls). Only these drive TypeScript preview overload generation. */
|
|
174
|
+
previewUsages: Map<string, UsageRecord[]>;
|
|
175
|
+
/** Event hooks for notifying plugins when generated outputs need refreshing. `styleUpdated` fires on CSS changes; `tsCodegenUpdated` fires on TypeScript declaration changes. */
|
|
67
176
|
hooks: {
|
|
68
177
|
styleUpdated: ReturnType<typeof createEventHook<void>>;
|
|
69
178
|
tsCodegenUpdated: ReturnType<typeof createEventHook<void>>;
|
|
70
179
|
};
|
|
180
|
+
/** The initialized PikaCSS engine instance. Throws if accessed before `setup()` completes. */
|
|
71
181
|
engine: Engine;
|
|
182
|
+
/** Glob patterns for the bundler's transform pipeline, derived from the scan config with codegen files excluded. */
|
|
72
183
|
transformFilter: {
|
|
73
184
|
include: string[];
|
|
74
185
|
exclude: string[];
|
|
75
186
|
};
|
|
187
|
+
/** Processes a source file by extracting `pika()` calls, resolving them through the engine, and replacing them with computed output. Returns the transformed code and source map, or `null` if no calls were found. */
|
|
76
188
|
transform: (code: string, id: string) => Promise<{
|
|
77
189
|
code: string;
|
|
78
190
|
map: SourceMap;
|
|
79
191
|
} | Nullish>;
|
|
192
|
+
/** Generates the full CSS output string, including layer declarations, preflights, and all atomic styles collected from transforms. */
|
|
80
193
|
getCssCodegenContent: () => Promise<string | Nullish>;
|
|
194
|
+
/** Generates the full TypeScript declaration content for `pika.gen.ts`, or `null` if TypeScript codegen is disabled. */
|
|
81
195
|
getTsCodegenContent: () => Promise<string | Nullish>;
|
|
196
|
+
/** Generates and writes the CSS codegen file to disk at `cssCodegenFilepath`. */
|
|
82
197
|
writeCssCodegenFile: () => Promise<void>;
|
|
198
|
+
/** Generates and writes the TypeScript codegen file to disk at `tsCodegenFilepath`. No-op if TypeScript codegen is disabled. */
|
|
83
199
|
writeTsCodegenFile: () => Promise<void>;
|
|
200
|
+
/** Scans all matching source files, collects usages via transform, then writes the CSS codegen file. Used for full rebuilds. */
|
|
84
201
|
fullyCssCodegen: () => Promise<void>;
|
|
202
|
+
/** The pending setup promise while initialization is in progress, or `null` when idle. Transform calls await this before proceeding. */
|
|
85
203
|
setupPromise: Promise<void> | null;
|
|
204
|
+
/** Initializes (or reinitializes) the context by clearing state, loading config, creating the engine, and wiring up dev hooks. Returns a promise that resolves when setup is complete. */
|
|
86
205
|
setup: () => Promise<void>;
|
|
87
206
|
}
|
|
88
207
|
//#endregion
|
|
89
208
|
//#region src/ctx.d.ts
|
|
209
|
+
/**
|
|
210
|
+
* Creates an `IntegrationContext` that wires together config loading, engine initialization, source file transformation, and codegen output.
|
|
211
|
+
*
|
|
212
|
+
* @param options - The integration configuration including paths, function name, scan globs, and codegen settings.
|
|
213
|
+
* @returns A fully constructed `IntegrationContext`. Call `setup()` on the returned context before using transforms.
|
|
214
|
+
*
|
|
215
|
+
* @remarks
|
|
216
|
+
* The context uses reactive signals internally so that computed paths (CSS and TS codegen
|
|
217
|
+
* file paths) automatically update when `cwd` changes. The `setup()` method must be called
|
|
218
|
+
* before any transform or codegen operations - transform calls automatically await the
|
|
219
|
+
* pending setup promise.
|
|
220
|
+
*/
|
|
90
221
|
declare function createCtx(options: IntegrationContextOptions): IntegrationContext;
|
|
91
222
|
//#endregion
|
|
92
223
|
export { FnUtils, IntegrationContext, IntegrationContextOptions, LoadedConfigResult, UsageRecord, createCtx };
|
package/dist/index.mjs
CHANGED
|
@@ -7,10 +7,226 @@ import { klona } from "klona";
|
|
|
7
7
|
import { isPackageExists } from "local-pkg";
|
|
8
8
|
import MagicString from "magic-string";
|
|
9
9
|
import { dirname, isAbsolute, join, relative, resolve } from "pathe";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
export * from "@pikacss/core";
|
|
11
|
+
//#region src/ctx.transform-utils.ts
|
|
12
|
+
const ESCAPE_REPLACE_RE = /[.*+?^${}()|[\]\\/]/g;
|
|
13
|
+
/**
|
|
14
|
+
* Builds classifier functions and a compiled regex for all `pika()` function call variants derived from the given base name.
|
|
15
|
+
* @internal
|
|
16
|
+
*
|
|
17
|
+
* @param fnName - The base function name (e.g., `'pika'`). All variants (`.str`, `.arr`, `p` suffix, bracket notation) are derived from this.
|
|
18
|
+
* @returns An `FnUtils` object with classifier methods and a global regex for matching all call variants.
|
|
19
|
+
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* The generated regex handles bracket-notation property access (e.g., `pika['str']`)
|
|
22
|
+
* in addition to dot notation, and includes word-boundary anchors to avoid false
|
|
23
|
+
* matches within longer identifiers.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const fnUtils = createFnUtils('pika')
|
|
28
|
+
* fnUtils.isNormal('pika') // true
|
|
29
|
+
* fnUtils.isForceString('pika.str') // true
|
|
30
|
+
* fnUtils.RE.test('pika(') // true
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
function createFnUtils(fnName) {
|
|
34
|
+
const available = {
|
|
35
|
+
normal: new Set([fnName]),
|
|
36
|
+
forceString: new Set([
|
|
37
|
+
`${fnName}.str`,
|
|
38
|
+
`${fnName}['str']`,
|
|
39
|
+
`${fnName}["str"]`,
|
|
40
|
+
`${fnName}[\`str\`]`
|
|
41
|
+
]),
|
|
42
|
+
forceArray: new Set([
|
|
43
|
+
`${fnName}.arr`,
|
|
44
|
+
`${fnName}['arr']`,
|
|
45
|
+
`${fnName}["arr"]`,
|
|
46
|
+
`${fnName}[\`arr\`]`
|
|
47
|
+
]),
|
|
48
|
+
normalPreview: new Set([`${fnName}p`]),
|
|
49
|
+
forceStringPreview: new Set([
|
|
50
|
+
`${fnName}p.str`,
|
|
51
|
+
`${fnName}p['str']`,
|
|
52
|
+
`${fnName}p["str"]`,
|
|
53
|
+
`${fnName}p[\`str\`]`
|
|
54
|
+
]),
|
|
55
|
+
forceArrayPreview: new Set([
|
|
56
|
+
`${fnName}p.arr`,
|
|
57
|
+
`${fnName}p['arr']`,
|
|
58
|
+
`${fnName}p["arr"]`,
|
|
59
|
+
`${fnName}p[\`arr\`]`
|
|
60
|
+
])
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
isNormal: (name) => available.normal.has(name) || available.normalPreview.has(name),
|
|
64
|
+
isForceString: (name) => available.forceString.has(name) || available.forceStringPreview.has(name),
|
|
65
|
+
isForceArray: (name) => available.forceArray.has(name) || available.forceArrayPreview.has(name),
|
|
66
|
+
isPreview: (name) => available.normalPreview.has(name) || available.forceStringPreview.has(name) || available.forceArrayPreview.has(name),
|
|
67
|
+
RE: new RegExp(`\\b(${Object.values(available).flatMap((set) => Array.from(set, (name) => `(${name.replace(ESCAPE_REPLACE_RE, "\\$&")})`)).join("|")})\\(`, "g")
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Finds the index of the closing `}` that terminates a template literal `${...}` expression.
|
|
72
|
+
* @internal
|
|
73
|
+
*
|
|
74
|
+
* @param code - The full source code string to search within.
|
|
75
|
+
* @param start - The index of the opening `{` of the template expression.
|
|
76
|
+
* @returns The index of the matching closing `}`, or `-1` if the expression is malformed or unterminated.
|
|
77
|
+
*
|
|
78
|
+
* @remarks
|
|
79
|
+
* Tracks nested braces, string literals (single, double, and backtick), escape sequences,
|
|
80
|
+
* line comments, and block comments. Recursively handles nested template expressions
|
|
81
|
+
* within backtick strings.
|
|
82
|
+
*/
|
|
83
|
+
function findTemplateExpressionEnd(code, start) {
|
|
84
|
+
let end = start;
|
|
85
|
+
let depth = 1;
|
|
86
|
+
let inString = false;
|
|
87
|
+
let isEscaped = false;
|
|
88
|
+
while (depth > 0 && end < code.length - 1) {
|
|
89
|
+
end++;
|
|
90
|
+
const char = code[end];
|
|
91
|
+
if (isEscaped) {
|
|
92
|
+
isEscaped = false;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (char === "\\") {
|
|
96
|
+
isEscaped = true;
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (inString !== false) {
|
|
100
|
+
if (char === inString) inString = false;
|
|
101
|
+
else if (inString === "`" && char === "$" && code[end + 1] === "{") {
|
|
102
|
+
if ((end = findTemplateExpressionEnd(code, end + 1)) < 0) return -1;
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (char === "{") depth++;
|
|
107
|
+
else if (char === "}") depth--;
|
|
108
|
+
else if (char === "'" || char === "\"" || char === "`") inString = char;
|
|
109
|
+
else if (char === "/" && code[end + 1] === "/") {
|
|
110
|
+
const lineEnd = code.indexOf("\n", end);
|
|
111
|
+
if (lineEnd === -1) return -1;
|
|
112
|
+
end = lineEnd;
|
|
113
|
+
} else if (char === "/" && code[end + 1] === "*") {
|
|
114
|
+
if ((end = code.indexOf("*/", end + 2) + 1) === 0) return -1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return depth === 0 ? end : -1;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Scans source code and returns all `pika()` function call matches found by the provided regex.
|
|
121
|
+
* @internal
|
|
122
|
+
*
|
|
123
|
+
* @param code - The full source code string to scan for function calls.
|
|
124
|
+
* @param fnUtils - An object providing the `RE` regex used to locate function call start positions.
|
|
125
|
+
* @returns An array of `FunctionCallMatch` objects, one per matched call. Malformed calls (unbalanced parentheses) are logged and skipped.
|
|
126
|
+
*
|
|
127
|
+
* @remarks
|
|
128
|
+
* Correctly handles nested parentheses, string literals, template expressions,
|
|
129
|
+
* line comments, and block comments within function arguments. Each match
|
|
130
|
+
* includes the full call snippet for later evaluation.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```ts
|
|
134
|
+
* const fnUtils = createFnUtils('pika')
|
|
135
|
+
* const matches = findFunctionCalls(`pika('bg:red', 'c:white')`, fnUtils)
|
|
136
|
+
* // [{ fnName: 'pika', start: 0, end: 25, snippet: "pika('bg:red', 'c:white')" }]
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
function findFunctionCalls(code, fnUtils) {
|
|
140
|
+
const RE = fnUtils.RE;
|
|
141
|
+
const result = [];
|
|
142
|
+
let matched = RE.exec(code);
|
|
143
|
+
while (matched != null) {
|
|
144
|
+
const fnName = matched[1];
|
|
145
|
+
const start = matched.index;
|
|
146
|
+
let end = start + fnName.length;
|
|
147
|
+
let depth = 1;
|
|
148
|
+
let inString = false;
|
|
149
|
+
let isEscaped = false;
|
|
150
|
+
while (depth > 0 && end < code.length) {
|
|
151
|
+
end++;
|
|
152
|
+
const char = code[end];
|
|
153
|
+
if (isEscaped) {
|
|
154
|
+
isEscaped = false;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (char === "\\") {
|
|
158
|
+
isEscaped = true;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (inString !== false) {
|
|
162
|
+
if (char === inString) inString = false;
|
|
163
|
+
else if (inString === "`" && char === "$" && code[end + 1] === "{") {
|
|
164
|
+
const templateExpressionEnd = findTemplateExpressionEnd(code, end + 1);
|
|
165
|
+
if (templateExpressionEnd === -1) {
|
|
166
|
+
log.warn(`Malformed template literal expression in function call at position ${start}`);
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
end = templateExpressionEnd;
|
|
170
|
+
}
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (char === "(") depth++;
|
|
174
|
+
else if (char === ")") depth--;
|
|
175
|
+
else if (char === "'" || char === "\"" || char === "`") inString = char;
|
|
176
|
+
else if (char === "/" && code[end + 1] === "/") {
|
|
177
|
+
const lineEnd = code.indexOf("\n", end);
|
|
178
|
+
if (lineEnd === -1) {
|
|
179
|
+
log.warn(`Unclosed function call at position ${start}`);
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
end = lineEnd;
|
|
183
|
+
} else if (char === "/" && code[end + 1] === "*") {
|
|
184
|
+
const commentEnd = code.indexOf("*/", end + 2);
|
|
185
|
+
if (commentEnd === -1) {
|
|
186
|
+
log.warn(`Unclosed comment in function call at position ${start}`);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
end = commentEnd + 1;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (depth !== 0) {
|
|
193
|
+
log.warn(`Malformed function call at position ${start}, skipping`);
|
|
194
|
+
matched = RE.exec(code);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const snippet = code.slice(start, end + 1);
|
|
198
|
+
result.push({
|
|
199
|
+
fnName,
|
|
200
|
+
start,
|
|
201
|
+
end,
|
|
202
|
+
snippet
|
|
203
|
+
});
|
|
204
|
+
matched = RE.exec(code);
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
13
209
|
//#region src/eventHook.ts
|
|
210
|
+
/**
|
|
211
|
+
* Creates a new event hook instance for publish-subscribe event dispatching.
|
|
212
|
+
* @internal
|
|
213
|
+
*
|
|
214
|
+
* @typeParam EventPayload - The type of data passed to listeners when the event fires.
|
|
215
|
+
* @returns A new `EventHook` with an empty listener set.
|
|
216
|
+
*
|
|
217
|
+
* @remarks
|
|
218
|
+
* The returned hook can register listeners via `on`, remove them via `off`,
|
|
219
|
+
* and broadcast payloads to all listeners via `trigger`. Calling `trigger`
|
|
220
|
+
* with no registered listeners is a no-op.
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* ```ts
|
|
224
|
+
* const hook = createEventHook<string>()
|
|
225
|
+
* const off = hook.on((msg) => console.log(msg))
|
|
226
|
+
* hook.trigger('hello') // logs "hello"
|
|
227
|
+
* off() // unsubscribes
|
|
228
|
+
* ```
|
|
229
|
+
*/
|
|
14
230
|
function createEventHook() {
|
|
15
231
|
const listeners = /* @__PURE__ */ new Set();
|
|
16
232
|
function trigger(payload) {
|
|
@@ -32,9 +248,9 @@ function createEventHook() {
|
|
|
32
248
|
off
|
|
33
249
|
};
|
|
34
250
|
}
|
|
35
|
-
|
|
36
251
|
//#endregion
|
|
37
252
|
//#region src/tsCodegen.ts
|
|
253
|
+
const RE_LEADING_INDENT = /^(\s*)/;
|
|
38
254
|
function formatUnionType(parts) {
|
|
39
255
|
return parts.length > 0 ? parts.join(" | ") : "never";
|
|
40
256
|
}
|
|
@@ -42,7 +258,7 @@ function formatUnionStringType(list) {
|
|
|
42
258
|
return formatUnionType(list.map((i) => JSON.stringify(i)));
|
|
43
259
|
}
|
|
44
260
|
function formatAutocompleteUnion(literals, patterns) {
|
|
45
|
-
return formatUnionType([...Array.from(literals, (value) => JSON.stringify(value)), ...patterns
|
|
261
|
+
return formatUnionType([...Array.from(literals, (value) => JSON.stringify(value)), ...patterns]);
|
|
46
262
|
}
|
|
47
263
|
function formatAutocompleteValueMap(keys, entries, patternEntries, formatValue) {
|
|
48
264
|
const mergedKeys = new Set(keys);
|
|
@@ -54,20 +270,20 @@ function generateAutocomplete(ctx) {
|
|
|
54
270
|
const autocomplete = ctx.engine.config.autocomplete;
|
|
55
271
|
const patterns = autocomplete.patterns ?? {
|
|
56
272
|
selectors: /* @__PURE__ */ new Set(),
|
|
57
|
-
|
|
273
|
+
shortcuts: /* @__PURE__ */ new Set(),
|
|
58
274
|
properties: /* @__PURE__ */ new Map(),
|
|
59
275
|
cssProperties: /* @__PURE__ */ new Map()
|
|
60
276
|
};
|
|
61
277
|
const { layers } = ctx.engine.config;
|
|
62
278
|
const layerNames = sortLayerNames(layers);
|
|
63
279
|
return [
|
|
64
|
-
"export type Autocomplete =
|
|
280
|
+
"export type Autocomplete = {",
|
|
65
281
|
` Selector: ${formatAutocompleteUnion(autocomplete.selectors, patterns.selectors)}`,
|
|
66
|
-
`
|
|
282
|
+
` Shortcut: ${formatAutocompleteUnion(autocomplete.shortcuts, patterns.shortcuts)}`,
|
|
67
283
|
` PropertyValue: ${formatAutocompleteValueMap(autocomplete.extraProperties, autocomplete.properties, patterns.properties, (values, patterns) => formatUnionType([...values, ...patterns]))}`,
|
|
68
284
|
` CSSPropertyValue: ${formatAutocompleteValueMap(autocomplete.extraCssProperties, autocomplete.cssProperties, patterns.cssProperties, (values, patterns) => formatAutocompleteUnion(values, patterns))}`,
|
|
69
285
|
` Layer: ${formatUnionStringType(layerNames)}`,
|
|
70
|
-
"}
|
|
286
|
+
"}",
|
|
71
287
|
""
|
|
72
288
|
];
|
|
73
289
|
}
|
|
@@ -120,8 +336,8 @@ async function generateOverloadContent(ctx) {
|
|
|
120
336
|
log.debug("Generating TypeScript overload content");
|
|
121
337
|
const paramsLines = [];
|
|
122
338
|
const fnsLines = [];
|
|
123
|
-
const usages = [...ctx.
|
|
124
|
-
log.debug(`Processing ${usages.length}
|
|
339
|
+
const usages = [...ctx.previewUsages.values()].flat();
|
|
340
|
+
log.debug(`Processing ${usages.length} preview usages for overload generation`);
|
|
125
341
|
for (let i = 0; i < usages.length; i++) {
|
|
126
342
|
const usage = usages[i];
|
|
127
343
|
try {
|
|
@@ -133,7 +349,7 @@ async function generateOverloadContent(ctx) {
|
|
|
133
349
|
...(await ctx.engine.renderAtomicStyles(true, {
|
|
134
350
|
atomicStyleIds: usage.atomicStyleIds,
|
|
135
351
|
isPreview: true
|
|
136
|
-
})).trim().split("\n").map((line) => ` * ${line.replace(
|
|
352
|
+
})).trim().split("\n").map((line) => ` * ${line.replace(RE_LEADING_INDENT, "$1")}`),
|
|
137
353
|
" * ```",
|
|
138
354
|
" */",
|
|
139
355
|
` fn(...params: [${usage.params.map((_, index) => `p${index}: P${i}_${index}`).join(", ")}]): ReturnType<StyleFn>`
|
|
@@ -154,11 +370,24 @@ async function generateOverloadContent(ctx) {
|
|
|
154
370
|
...paramsLines
|
|
155
371
|
];
|
|
156
372
|
}
|
|
373
|
+
/**
|
|
374
|
+
* Generates the full content of the `pika.gen.ts` TypeScript declaration file from the current engine and usage state.
|
|
375
|
+
* @internal
|
|
376
|
+
*
|
|
377
|
+
* @param ctx - The integration context providing engine config, usage records, and codegen settings.
|
|
378
|
+
* @returns The complete TypeScript source string for the generated declaration file.
|
|
379
|
+
*
|
|
380
|
+
* @remarks
|
|
381
|
+
* The output includes module augmentation for `PikaAugment`, autocomplete type literals
|
|
382
|
+
* derived from selectors/shortcuts/properties, style function type overloads (respecting
|
|
383
|
+
* `transformedFormat`), global declarations, optional Vue component property declarations,
|
|
384
|
+
* and per-usage preview overloads with inline CSS previews.
|
|
385
|
+
*/
|
|
157
386
|
async function generateTsCodegenContent(ctx) {
|
|
158
387
|
log.debug("Generating TypeScript code generation content");
|
|
159
388
|
const lines = [
|
|
160
389
|
`// Auto-generated by ${ctx.currentPackageName}`,
|
|
161
|
-
`import type { CSSProperty, CSSSelector,
|
|
390
|
+
`import type { CSSProperty, CSSSelector, Properties, StyleDefinition, StyleItem } from \'${ctx.currentPackageName}\'`,
|
|
162
391
|
"",
|
|
163
392
|
`declare module \'${ctx.currentPackageName}\' {`,
|
|
164
393
|
" interface PikaAugment {",
|
|
@@ -180,9 +409,9 @@ async function generateTsCodegenContent(ctx) {
|
|
|
180
409
|
log.debug("TypeScript code generation content completed");
|
|
181
410
|
return lines.join("\n");
|
|
182
411
|
}
|
|
183
|
-
|
|
184
412
|
//#endregion
|
|
185
413
|
//#region src/ctx.ts
|
|
414
|
+
const RE_VALID_CONFIG_EXT = /\.(?:js|cjs|mjs|ts|cts|mts)$/;
|
|
186
415
|
function createConfigScaffoldContent({ currentPackageName, resolvedConfigPath, tsCodegenFilepath }) {
|
|
187
416
|
const relativeTsCodegenFilepath = tsCodegenFilepath == null ? null : `./${relative(dirname(resolvedConfigPath), tsCodegenFilepath)}`;
|
|
188
417
|
return [
|
|
@@ -222,7 +451,6 @@ function usePaths({ cwd: _cwd, cssCodegen, tsCodegen }) {
|
|
|
222
451
|
};
|
|
223
452
|
}
|
|
224
453
|
function useConfig({ cwd, tsCodegenFilepath, currentPackageName, autoCreateConfig, configOrPath, scan }) {
|
|
225
|
-
const RE_VALID_CONFIG_EXT = /\.(?:js|cjs|mjs|ts|cts|mts)$/;
|
|
226
454
|
const specificConfigPath = computed(() => {
|
|
227
455
|
if (typeof configOrPath === "string" && RE_VALID_CONFIG_EXT.test(configOrPath)) return isAbsolute(configOrPath) ? configOrPath : join(cwd(), configOrPath);
|
|
228
456
|
return null;
|
|
@@ -231,7 +459,10 @@ function useConfig({ cwd, tsCodegenFilepath, currentPackageName, autoCreateConfi
|
|
|
231
459
|
const _cwd = cwd();
|
|
232
460
|
const _specificConfigPath = specificConfigPath();
|
|
233
461
|
if (_specificConfigPath != null && statSync(_specificConfigPath, { throwIfNoEntry: false })?.isFile()) return _specificConfigPath;
|
|
234
|
-
const stream = globbyStream("**/{pika,pikacss}.config.{js,cjs,mjs,ts,cts,mts}", {
|
|
462
|
+
const stream = globbyStream("**/{pika,pikacss}.config.{js,cjs,mjs,ts,cts,mts}", {
|
|
463
|
+
cwd: _cwd,
|
|
464
|
+
ignore: scan.exclude
|
|
465
|
+
});
|
|
235
466
|
for await (const entry of stream) return join(_cwd, entry);
|
|
236
467
|
return null;
|
|
237
468
|
}
|
|
@@ -294,165 +525,20 @@ function useConfig({ cwd, tsCodegenFilepath, currentPackageName, autoCreateConfi
|
|
|
294
525
|
loadConfig
|
|
295
526
|
};
|
|
296
527
|
}
|
|
297
|
-
function useTransform({ cwd, cssCodegenFilepath, tsCodegenFilepath, scan, fnName, usages, engine, transformedFormat, triggerStyleUpdated, triggerTsCodegenUpdated }) {
|
|
298
|
-
const ESCAPE_REPLACE_RE = /[.*+?^${}()|[\]\\/]/g;
|
|
299
|
-
function createFnUtils(fnName) {
|
|
300
|
-
const available = {
|
|
301
|
-
normal: new Set([fnName]),
|
|
302
|
-
forceString: new Set([
|
|
303
|
-
`${fnName}.str`,
|
|
304
|
-
`${fnName}['str']`,
|
|
305
|
-
`${fnName}["str"]`,
|
|
306
|
-
`${fnName}[\`str\`]`
|
|
307
|
-
]),
|
|
308
|
-
forceArray: new Set([
|
|
309
|
-
`${fnName}.arr`,
|
|
310
|
-
`${fnName}['arr']`,
|
|
311
|
-
`${fnName}["arr"]`,
|
|
312
|
-
`${fnName}[\`arr\`]`
|
|
313
|
-
]),
|
|
314
|
-
normalPreview: new Set([`${fnName}p`]),
|
|
315
|
-
forceStringPreview: new Set([
|
|
316
|
-
`${fnName}p.str`,
|
|
317
|
-
`${fnName}p['str']`,
|
|
318
|
-
`${fnName}p["str"]`,
|
|
319
|
-
`${fnName}p[\`str\`]`
|
|
320
|
-
]),
|
|
321
|
-
forceArrayPreview: new Set([
|
|
322
|
-
`${fnName}p.arr`,
|
|
323
|
-
`${fnName}p['arr']`,
|
|
324
|
-
`${fnName}p["arr"]`,
|
|
325
|
-
`${fnName}p[\`arr\`]`
|
|
326
|
-
])
|
|
327
|
-
};
|
|
328
|
-
return {
|
|
329
|
-
isNormal: (fnName) => available.normal.has(fnName) || available.normalPreview.has(fnName),
|
|
330
|
-
isForceString: (fnName) => available.forceString.has(fnName) || available.forceStringPreview.has(fnName),
|
|
331
|
-
isForceArray: (fnName) => available.forceArray.has(fnName) || available.forceArrayPreview.has(fnName),
|
|
332
|
-
isPreview: (fnName) => available.normalPreview.has(fnName) || available.forceStringPreview.has(fnName) || available.forceArrayPreview.has(fnName),
|
|
333
|
-
RE: new RegExp(`\\b(${Object.values(available).flatMap((s) => [...s].map((f) => `(${f.replace(ESCAPE_REPLACE_RE, "\\$&")})`)).join("|")})\\(`, "g")
|
|
334
|
-
};
|
|
335
|
-
}
|
|
528
|
+
function useTransform({ cwd, cssCodegenFilepath, tsCodegenFilepath, scan, fnName, usages, previewUsages, engine, transformedFormat, triggerStyleUpdated, triggerTsCodegenUpdated }) {
|
|
336
529
|
const fnUtils = createFnUtils(fnName);
|
|
337
|
-
function findTemplateExpressionEnd(code, start) {
|
|
338
|
-
let end = start;
|
|
339
|
-
let depth = 1;
|
|
340
|
-
let inString = false;
|
|
341
|
-
let isEscaped = false;
|
|
342
|
-
while (depth > 0 && end < code.length - 1) {
|
|
343
|
-
end++;
|
|
344
|
-
const char = code[end];
|
|
345
|
-
if (isEscaped) {
|
|
346
|
-
isEscaped = false;
|
|
347
|
-
continue;
|
|
348
|
-
}
|
|
349
|
-
if (char === "\\") {
|
|
350
|
-
isEscaped = true;
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
if (inString !== false) {
|
|
354
|
-
if (char === inString) inString = false;
|
|
355
|
-
else if (inString === "`" && char === "$" && code[end + 1] === "{") {
|
|
356
|
-
const nestedExpressionEnd = findTemplateExpressionEnd(code, end + 1);
|
|
357
|
-
if (nestedExpressionEnd === -1) return -1;
|
|
358
|
-
end = nestedExpressionEnd;
|
|
359
|
-
}
|
|
360
|
-
continue;
|
|
361
|
-
}
|
|
362
|
-
if (char === "{") depth++;
|
|
363
|
-
else if (char === "}") depth--;
|
|
364
|
-
else if (char === "'" || char === "\"" || char === "`") inString = char;
|
|
365
|
-
else if (char === "/" && code[end + 1] === "/") {
|
|
366
|
-
const lineEnd = code.indexOf("\n", end);
|
|
367
|
-
if (lineEnd === -1) return -1;
|
|
368
|
-
end = lineEnd;
|
|
369
|
-
} else if (char === "/" && code[end + 1] === "*") {
|
|
370
|
-
const commentEnd = code.indexOf("*/", end + 2);
|
|
371
|
-
if (commentEnd === -1) return -1;
|
|
372
|
-
end = commentEnd + 1;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
return depth === 0 ? end : -1;
|
|
376
|
-
}
|
|
377
|
-
function findFunctionCalls(code) {
|
|
378
|
-
const RE = fnUtils.RE;
|
|
379
|
-
const result = [];
|
|
380
|
-
let matched = RE.exec(code);
|
|
381
|
-
while (matched != null) {
|
|
382
|
-
const fnName = matched[1];
|
|
383
|
-
const start = matched.index;
|
|
384
|
-
let end = start + fnName.length;
|
|
385
|
-
let depth = 1;
|
|
386
|
-
let inString = false;
|
|
387
|
-
let isEscaped = false;
|
|
388
|
-
while (depth > 0 && end < code.length) {
|
|
389
|
-
end++;
|
|
390
|
-
const char = code[end];
|
|
391
|
-
if (isEscaped) {
|
|
392
|
-
isEscaped = false;
|
|
393
|
-
continue;
|
|
394
|
-
}
|
|
395
|
-
if (char === "\\") {
|
|
396
|
-
isEscaped = true;
|
|
397
|
-
continue;
|
|
398
|
-
}
|
|
399
|
-
if (inString !== false) {
|
|
400
|
-
if (char === inString) inString = false;
|
|
401
|
-
else if (inString === "`" && char === "$" && code[end + 1] === "{") {
|
|
402
|
-
const templateExpressionEnd = findTemplateExpressionEnd(code, end + 1);
|
|
403
|
-
if (templateExpressionEnd === -1) {
|
|
404
|
-
log.warn(`Malformed template literal expression in function call at position ${start}`);
|
|
405
|
-
break;
|
|
406
|
-
}
|
|
407
|
-
end = templateExpressionEnd;
|
|
408
|
-
}
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
if (char === "(") depth++;
|
|
412
|
-
else if (char === ")") depth--;
|
|
413
|
-
else if (char === "'" || char === "\"" || char === "`") inString = char;
|
|
414
|
-
else if (char === "/" && code[end + 1] === "/") {
|
|
415
|
-
const lineEnd = code.indexOf("\n", end);
|
|
416
|
-
if (lineEnd === -1) {
|
|
417
|
-
log.warn(`Unclosed function call at position ${start}`);
|
|
418
|
-
break;
|
|
419
|
-
}
|
|
420
|
-
end = lineEnd;
|
|
421
|
-
} else if (char === "/" && code[end + 1] === "*") {
|
|
422
|
-
const commentEnd = code.indexOf("*/", end + 2);
|
|
423
|
-
if (commentEnd === -1) {
|
|
424
|
-
log.warn(`Unclosed comment in function call at position ${start}`);
|
|
425
|
-
break;
|
|
426
|
-
}
|
|
427
|
-
end = commentEnd + 1;
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
if (depth !== 0) {
|
|
431
|
-
log.warn(`Malformed function call at position ${start}, skipping`);
|
|
432
|
-
matched = RE.exec(code);
|
|
433
|
-
continue;
|
|
434
|
-
}
|
|
435
|
-
const snippet = code.slice(start, end + 1);
|
|
436
|
-
result.push({
|
|
437
|
-
fnName,
|
|
438
|
-
start,
|
|
439
|
-
end,
|
|
440
|
-
snippet
|
|
441
|
-
});
|
|
442
|
-
matched = RE.exec(code);
|
|
443
|
-
}
|
|
444
|
-
return result;
|
|
445
|
-
}
|
|
446
530
|
async function transform(code, id) {
|
|
447
531
|
const _engine = engine();
|
|
448
532
|
if (_engine == null) return null;
|
|
449
533
|
try {
|
|
450
534
|
log.debug(`Transforming file: ${id}`);
|
|
451
535
|
usages.delete(id);
|
|
452
|
-
|
|
536
|
+
previewUsages.delete(id);
|
|
537
|
+
const functionCalls = findFunctionCalls(code, fnUtils);
|
|
453
538
|
if (functionCalls.length === 0) return;
|
|
454
539
|
log.debug(`Found ${functionCalls.length} style function calls in ${id}`);
|
|
455
540
|
const usageList = [];
|
|
541
|
+
const previewUsageList = [];
|
|
456
542
|
const transformed = new MagicString(code);
|
|
457
543
|
for (const fnCall of functionCalls) {
|
|
458
544
|
const argsStr = `[${fnCall.snippet.slice(fnCall.fnName.length + 1, -1)}]`;
|
|
@@ -463,6 +549,7 @@ function useTransform({ cwd, cssCodegenFilepath, tsCodegenFilepath, scan, fnName
|
|
|
463
549
|
params: args
|
|
464
550
|
};
|
|
465
551
|
usageList.push(usage);
|
|
552
|
+
if (fnUtils.isPreview(fnCall.fnName)) previewUsageList.push(usage);
|
|
466
553
|
let transformedContent;
|
|
467
554
|
if (fnUtils.isNormal(fnCall.fnName)) transformedContent = transformedFormat === "array" ? `[${names.map((n) => `'${n}'`).join(", ")}]` : `'${names.join(" ")}'`;
|
|
468
555
|
else if (fnUtils.isForceString(fnCall.fnName)) transformedContent = `'${names.join(" ")}'`;
|
|
@@ -471,6 +558,7 @@ function useTransform({ cwd, cssCodegenFilepath, tsCodegenFilepath, scan, fnName
|
|
|
471
558
|
transformed.update(fnCall.start, fnCall.end + 1, transformedContent);
|
|
472
559
|
}
|
|
473
560
|
usages.set(id, usageList);
|
|
561
|
+
if (previewUsageList.length > 0) previewUsages.set(id, previewUsageList);
|
|
474
562
|
triggerStyleUpdated();
|
|
475
563
|
triggerTsCodegenUpdated();
|
|
476
564
|
log.debug(`Transformed ${usageList.length} style usages in ${id}`);
|
|
@@ -479,7 +567,7 @@ function useTransform({ cwd, cssCodegenFilepath, tsCodegenFilepath, scan, fnName
|
|
|
479
567
|
map: transformed.generateMap({ hires: true })
|
|
480
568
|
};
|
|
481
569
|
} catch (error) {
|
|
482
|
-
log.error(`Failed to transform code (${join(cwd(), id)}): ${error.message}`, error);
|
|
570
|
+
log.error(`Failed to transform code (${isAbsolute(id) ? id : join(cwd(), id)}): ${error.message}`, error);
|
|
483
571
|
return;
|
|
484
572
|
}
|
|
485
573
|
}
|
|
@@ -488,13 +576,25 @@ function useTransform({ cwd, cssCodegenFilepath, tsCodegenFilepath, scan, fnName
|
|
|
488
576
|
include: scan.include,
|
|
489
577
|
exclude: [
|
|
490
578
|
...scan.exclude,
|
|
491
|
-
cssCodegenFilepath(),
|
|
492
|
-
...tsCodegenFilepath() ? [tsCodegenFilepath()] : []
|
|
579
|
+
relative(cwd(), cssCodegenFilepath()),
|
|
580
|
+
...tsCodegenFilepath() ? [relative(cwd(), tsCodegenFilepath())] : []
|
|
493
581
|
]
|
|
494
582
|
},
|
|
495
583
|
transform
|
|
496
584
|
};
|
|
497
585
|
}
|
|
586
|
+
/**
|
|
587
|
+
* Creates an `IntegrationContext` that wires together config loading, engine initialization, source file transformation, and codegen output.
|
|
588
|
+
*
|
|
589
|
+
* @param options - The integration configuration including paths, function name, scan globs, and codegen settings.
|
|
590
|
+
* @returns A fully constructed `IntegrationContext`. Call `setup()` on the returned context before using transforms.
|
|
591
|
+
*
|
|
592
|
+
* @remarks
|
|
593
|
+
* The context uses reactive signals internally so that computed paths (CSS and TS codegen
|
|
594
|
+
* file paths) automatically update when `cwd` changes. The `setup()` method must be called
|
|
595
|
+
* before any transform or codegen operations - transform calls automatically await the
|
|
596
|
+
* pending setup promise.
|
|
597
|
+
*/
|
|
498
598
|
function createCtx(options) {
|
|
499
599
|
const { cwd, cssCodegenFilepath, tsCodegenFilepath } = usePaths(options);
|
|
500
600
|
const { resolvedConfig, resolvedConfigPath, resolvedConfigContent, loadConfig } = useConfig({
|
|
@@ -503,6 +603,7 @@ function createCtx(options) {
|
|
|
503
603
|
tsCodegenFilepath
|
|
504
604
|
});
|
|
505
605
|
const usages = /* @__PURE__ */ new Map();
|
|
606
|
+
const previewUsages = /* @__PURE__ */ new Map();
|
|
506
607
|
const engine = signal(null);
|
|
507
608
|
const hooks = {
|
|
508
609
|
styleUpdated: createEventHook(),
|
|
@@ -514,6 +615,7 @@ function createCtx(options) {
|
|
|
514
615
|
cssCodegenFilepath,
|
|
515
616
|
tsCodegenFilepath,
|
|
516
617
|
usages,
|
|
618
|
+
previewUsages,
|
|
517
619
|
engine,
|
|
518
620
|
triggerStyleUpdated: () => hooks.styleUpdated.trigger(),
|
|
519
621
|
triggerTsCodegenUpdated: () => hooks.tsCodegenUpdated.trigger()
|
|
@@ -548,6 +650,7 @@ function createCtx(options) {
|
|
|
548
650
|
},
|
|
549
651
|
loadConfig,
|
|
550
652
|
usages,
|
|
653
|
+
previewUsages,
|
|
551
654
|
hooks,
|
|
552
655
|
get engine() {
|
|
553
656
|
const _engine = engine();
|
|
@@ -597,12 +700,16 @@ function createCtx(options) {
|
|
|
597
700
|
fullyCssCodegen: async () => {
|
|
598
701
|
await ctx.setupPromise;
|
|
599
702
|
log.debug("Starting full CSS code generation scan");
|
|
600
|
-
const stream = globbyStream(options.scan.include, { ignore: options.scan.exclude });
|
|
601
|
-
let fileCount = 0;
|
|
602
703
|
const _cwd = cwd();
|
|
704
|
+
const stream = globbyStream(options.scan.include, {
|
|
705
|
+
cwd: _cwd,
|
|
706
|
+
ignore: options.scan.exclude
|
|
707
|
+
});
|
|
708
|
+
let fileCount = 0;
|
|
603
709
|
for await (const entry of stream) {
|
|
604
|
-
const
|
|
605
|
-
await
|
|
710
|
+
const filePath = join(_cwd, entry);
|
|
711
|
+
const code = await readFile(filePath, "utf-8");
|
|
712
|
+
await ctx.transform(code, filePath);
|
|
606
713
|
fileCount++;
|
|
607
714
|
}
|
|
608
715
|
log.debug(`Scanned ${fileCount} files for style collection`);
|
|
@@ -621,6 +728,7 @@ function createCtx(options) {
|
|
|
621
728
|
async function setup() {
|
|
622
729
|
log.debug("Setting up integration context");
|
|
623
730
|
usages.clear();
|
|
731
|
+
previewUsages.clear();
|
|
624
732
|
hooks.styleUpdated.listeners.clear();
|
|
625
733
|
hooks.tsCodegenUpdated.listeners.clear();
|
|
626
734
|
engine(null);
|
|
@@ -645,6 +753,5 @@ function createCtx(options) {
|
|
|
645
753
|
}
|
|
646
754
|
return ctx;
|
|
647
755
|
}
|
|
648
|
-
|
|
649
756
|
//#endregion
|
|
650
|
-
export { createCtx };
|
|
757
|
+
export { createCtx };
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikacss/integration",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.49",
|
|
5
5
|
"author": "DevilTea <ch19980814@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"homepage": "https://pikacss.com",
|
|
7
8
|
"repository": {
|
|
8
9
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/pikacss/pikacss.git",
|
|
10
|
+
"url": "git+https://github.com/pikacss/pikacss.git",
|
|
10
11
|
"directory": "packages/integration"
|
|
11
12
|
},
|
|
12
13
|
"bugs": {
|
|
@@ -18,6 +19,7 @@
|
|
|
18
19
|
"css-in-js",
|
|
19
20
|
"atomic-css-in-js-engine"
|
|
20
21
|
],
|
|
22
|
+
"sideEffects": false,
|
|
21
23
|
"exports": {
|
|
22
24
|
".": {
|
|
23
25
|
"import": {
|
|
@@ -34,28 +36,27 @@
|
|
|
34
36
|
"files": [
|
|
35
37
|
"dist"
|
|
36
38
|
],
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=22"
|
|
41
|
+
},
|
|
37
42
|
"dependencies": {
|
|
38
43
|
"alien-signals": "^3.1.2",
|
|
39
|
-
"globby": "^16.
|
|
44
|
+
"globby": "^16.2.0",
|
|
40
45
|
"jiti": "^2.6.1",
|
|
41
46
|
"klona": "^2.0.6",
|
|
42
47
|
"local-pkg": "^1.1.2",
|
|
43
48
|
"magic-string": "^0.30.21",
|
|
44
|
-
"micromatch": "^4.0.8",
|
|
45
49
|
"pathe": "^2.0.3",
|
|
46
50
|
"perfect-debounce": "^2.1.0",
|
|
47
|
-
"@pikacss/core": "0.0.
|
|
48
|
-
},
|
|
49
|
-
"devDependencies": {
|
|
50
|
-
"@types/micromatch": "^4.0.10"
|
|
51
|
+
"@pikacss/core": "0.0.49"
|
|
51
52
|
},
|
|
52
53
|
"scripts": {
|
|
53
|
-
"build": "tsdown
|
|
54
|
+
"build": "tsdown",
|
|
54
55
|
"build:watch": "tsdown --watch",
|
|
55
56
|
"typecheck": "pnpm typecheck:package && pnpm typecheck:test",
|
|
56
57
|
"typecheck:package": "tsc --project ./tsconfig.package.json --noEmit",
|
|
57
58
|
"typecheck:test": "tsc --project ./tsconfig.tests.json --noEmit",
|
|
58
|
-
"test": "vitest run",
|
|
59
|
-
"test:watch": "vitest"
|
|
59
|
+
"test": "vitest run --config ./vitest.config.ts",
|
|
60
|
+
"test:watch": "vitest --config ./vitest.config.ts"
|
|
60
61
|
}
|
|
61
62
|
}
|