@pikacss/integration 0.0.1
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 +1 -0
- package/dist/index.cjs +402 -0
- package/dist/index.d.cts +74 -0
- package/dist/index.d.mts +74 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.mjs +373 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 DevilTea
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @pikacss/integration
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const node_fs = require('node:fs');
|
|
4
|
+
const promises = require('node:fs/promises');
|
|
5
|
+
const core = require('@pikacss/core');
|
|
6
|
+
const jiti = require('jiti');
|
|
7
|
+
const localPkg = require('local-pkg');
|
|
8
|
+
const MagicString = require('magic-string');
|
|
9
|
+
const micromatch = require('micromatch');
|
|
10
|
+
const pathe = require('pathe');
|
|
11
|
+
const prettier = require('prettier');
|
|
12
|
+
|
|
13
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
14
|
+
|
|
15
|
+
function _interopNamespaceCompat(e) {
|
|
16
|
+
if (e && typeof e === 'object' && 'default' in e) return e;
|
|
17
|
+
const n = Object.create(null);
|
|
18
|
+
if (e) {
|
|
19
|
+
for (const k in e) {
|
|
20
|
+
n[k] = e[k];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
n.default = e;
|
|
24
|
+
return n;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const MagicString__default = /*#__PURE__*/_interopDefaultCompat(MagicString);
|
|
28
|
+
const micromatch__default = /*#__PURE__*/_interopDefaultCompat(micromatch);
|
|
29
|
+
const prettier__namespace = /*#__PURE__*/_interopNamespaceCompat(prettier);
|
|
30
|
+
|
|
31
|
+
function createEventHook() {
|
|
32
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
33
|
+
function trigger(payload) {
|
|
34
|
+
listeners.forEach((listener) => listener(payload));
|
|
35
|
+
}
|
|
36
|
+
function off(listener) {
|
|
37
|
+
listeners.delete(listener);
|
|
38
|
+
}
|
|
39
|
+
function on(listener) {
|
|
40
|
+
listeners.add(listener);
|
|
41
|
+
const offListener = () => off(listener);
|
|
42
|
+
return offListener;
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
listeners,
|
|
46
|
+
trigger,
|
|
47
|
+
on,
|
|
48
|
+
off
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function formatUnionStringType(list) {
|
|
53
|
+
return list.length > 0 ? list.map((i) => typeof i === "number" ? i : `'${i}'`).join(" | ") : "never";
|
|
54
|
+
}
|
|
55
|
+
function generateAutocomplete(ctx) {
|
|
56
|
+
const autocomplete = ctx.engine.config.autocomplete;
|
|
57
|
+
return [
|
|
58
|
+
"export interface Autocomplete extends _Autocomplete {",
|
|
59
|
+
` Selector: ${formatUnionStringType([...autocomplete.selectors])}`,
|
|
60
|
+
` StyleItemString: ${formatUnionStringType([...autocomplete.styleItemStrings])}`,
|
|
61
|
+
` ExtraProperty: ${formatUnionStringType([...autocomplete.extraProperties])}`,
|
|
62
|
+
` ExtraCssProperty: ${formatUnionStringType([...autocomplete.extraCssProperties])}`,
|
|
63
|
+
` PropertiesValue: { ${Array.from(autocomplete.properties.entries(), ([k, v]) => `'${k}': ${v.join(" | ")}`).join(",")} }`,
|
|
64
|
+
` CssPropertiesValue: { ${Array.from(autocomplete.cssProperties.entries(), ([k, v]) => `'${k}': ${formatUnionStringType(v)}`).join(",")} }`,
|
|
65
|
+
"}",
|
|
66
|
+
""
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
function generateStyleFn(ctx) {
|
|
70
|
+
const { transformedFormat } = ctx;
|
|
71
|
+
const lines = [
|
|
72
|
+
"type StyleFn_Array = (...params: StyleItem<Autocomplete>[]) => string[]",
|
|
73
|
+
"type StyleFn_String = (...params: StyleItem<Autocomplete>[]) => string",
|
|
74
|
+
"type StyleFn_Inline = (...params: StyleItem<Autocomplete>[]) => void"
|
|
75
|
+
];
|
|
76
|
+
if (transformedFormat === "array")
|
|
77
|
+
lines.push("type StyleFn_Normal = StyleFn_Array");
|
|
78
|
+
else if (transformedFormat === "string")
|
|
79
|
+
lines.push("type StyleFn_Normal = StyleFn_String");
|
|
80
|
+
else if (transformedFormat === "inline")
|
|
81
|
+
lines.push("type StyleFn_Normal = StyleFn_Inline");
|
|
82
|
+
lines.push(
|
|
83
|
+
"type StyleFn = StyleFn_Normal & {",
|
|
84
|
+
" str: StyleFn_String",
|
|
85
|
+
" arr: StyleFn_Array",
|
|
86
|
+
" inl: StyleFn_Inline",
|
|
87
|
+
"}",
|
|
88
|
+
`type StyleFnWithPreview = PreviewOverloads<StyleFn_Normal>['fn'] & {`,
|
|
89
|
+
` str: PreviewOverloads<StyleFn_String>['fn']`,
|
|
90
|
+
` arr: PreviewOverloads<StyleFn_Array>['fn']`,
|
|
91
|
+
` inl: PreviewOverloads<StyleFn_Inline>['fn']`,
|
|
92
|
+
"}",
|
|
93
|
+
""
|
|
94
|
+
);
|
|
95
|
+
return lines;
|
|
96
|
+
}
|
|
97
|
+
function generateGlobalDeclaration(ctx) {
|
|
98
|
+
const { fnName } = ctx;
|
|
99
|
+
return [
|
|
100
|
+
"declare global {",
|
|
101
|
+
" /**",
|
|
102
|
+
" * PikaCSS",
|
|
103
|
+
" */",
|
|
104
|
+
` const ${fnName}: StyleFn`,
|
|
105
|
+
"",
|
|
106
|
+
" /**",
|
|
107
|
+
" * PikaCSS Preview",
|
|
108
|
+
" */",
|
|
109
|
+
` const ${fnName}p: StyleFnWithPreview`,
|
|
110
|
+
"}",
|
|
111
|
+
""
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
function generateVueDeclaration(ctx) {
|
|
115
|
+
const { hasVue, fnName } = ctx;
|
|
116
|
+
if (!hasVue)
|
|
117
|
+
return [];
|
|
118
|
+
return [
|
|
119
|
+
"declare module 'vue' {",
|
|
120
|
+
" interface ComponentCustomProperties {",
|
|
121
|
+
" /**",
|
|
122
|
+
" * PikaCSS",
|
|
123
|
+
" */",
|
|
124
|
+
` ${fnName}: StyleFn`,
|
|
125
|
+
"",
|
|
126
|
+
" /**",
|
|
127
|
+
" * PikaCSS Preview",
|
|
128
|
+
" */",
|
|
129
|
+
` ${fnName}p: StyleFnWithPreview`,
|
|
130
|
+
" }",
|
|
131
|
+
"}",
|
|
132
|
+
""
|
|
133
|
+
];
|
|
134
|
+
}
|
|
135
|
+
async function generateOverloadContent(ctx) {
|
|
136
|
+
const paramsLines = [];
|
|
137
|
+
const fnsLines = [];
|
|
138
|
+
const usages = [...ctx.usages.values()].flat();
|
|
139
|
+
for (let i = 0; i < usages.length; i++) {
|
|
140
|
+
const usage = usages[i];
|
|
141
|
+
try {
|
|
142
|
+
const addedParamsLines = usage.params.map((param, index) => `type P${i}_${index} = ${JSON.stringify(param)}`);
|
|
143
|
+
const addedFnLines = [
|
|
144
|
+
" /**",
|
|
145
|
+
" * ### PikaCSS Preview",
|
|
146
|
+
" * ```css",
|
|
147
|
+
// CSS Lines
|
|
148
|
+
...(await prettier__namespace.format(await ctx.engine.renderPreviewStyles(...usage.params), { parser: "css" })).split("\n").map((line) => ` * \u200E${line.replace(/^(\s*)/, "$1\u200E")}`),
|
|
149
|
+
" * ```",
|
|
150
|
+
" */",
|
|
151
|
+
` fn(...params: [${usage.params.map((_, index) => `p${index}: P${i}_${index}`).join(", ")}]): ReturnType<StyleFn>`
|
|
152
|
+
];
|
|
153
|
+
paramsLines.push(...addedParamsLines);
|
|
154
|
+
fnsLines.push(...addedFnLines);
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return [
|
|
159
|
+
"interface PreviewOverloads<StyleFn extends (StyleFn_Array | StyleFn_String | StyleFn_Inline)> {",
|
|
160
|
+
...fnsLines,
|
|
161
|
+
" /**",
|
|
162
|
+
" * PikaCSS Preview",
|
|
163
|
+
" * Save the current file to see the preview.",
|
|
164
|
+
" */",
|
|
165
|
+
` fn(...params: Parameters<StyleFn>): ReturnType<StyleFn>`,
|
|
166
|
+
"}",
|
|
167
|
+
...paramsLines
|
|
168
|
+
];
|
|
169
|
+
}
|
|
170
|
+
async function generateTsCodegenContent(ctx) {
|
|
171
|
+
const lines = [
|
|
172
|
+
`// Auto-generated by ${ctx.currentPackageName}`,
|
|
173
|
+
`import type { Autocomplete as _Autocomplete, StyleItem } from '${ctx.currentPackageName}'`,
|
|
174
|
+
`import { createDefineEngineConfigFn } from '${ctx.currentPackageName}'`,
|
|
175
|
+
""
|
|
176
|
+
];
|
|
177
|
+
lines.push(...generateAutocomplete(ctx));
|
|
178
|
+
lines.push(
|
|
179
|
+
"export const defineEngineConfig: ReturnType<typeof createDefineEngineConfigFn<Autocomplete>> = createDefineEngineConfigFn<Autocomplete>()",
|
|
180
|
+
""
|
|
181
|
+
);
|
|
182
|
+
lines.push(...generateStyleFn(ctx));
|
|
183
|
+
lines.push(...generateGlobalDeclaration(ctx));
|
|
184
|
+
lines.push(...generateVueDeclaration(ctx));
|
|
185
|
+
lines.push(...await generateOverloadContent(ctx));
|
|
186
|
+
return lines.join("\n");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function findFunctionCalls(code, RE) {
|
|
190
|
+
const result = [];
|
|
191
|
+
let matched = RE.exec(code);
|
|
192
|
+
while (matched != null) {
|
|
193
|
+
const fnName = matched[1];
|
|
194
|
+
const start = matched.index;
|
|
195
|
+
let end = start + fnName.length;
|
|
196
|
+
let depth = 1;
|
|
197
|
+
let inString = false;
|
|
198
|
+
while (depth > 0) {
|
|
199
|
+
end++;
|
|
200
|
+
if (inString === false && code[end] === "(")
|
|
201
|
+
depth++;
|
|
202
|
+
else if (inString === false && code[end] === ")")
|
|
203
|
+
depth--;
|
|
204
|
+
else if (inString === false && (code[end] === "'" || code[end] === '"'))
|
|
205
|
+
inString = code[end];
|
|
206
|
+
else if (inString === code[end])
|
|
207
|
+
inString = false;
|
|
208
|
+
}
|
|
209
|
+
const snippet = code.slice(start, end + 1);
|
|
210
|
+
result.push({ fnName, start, end, snippet });
|
|
211
|
+
matched = RE.exec(code);
|
|
212
|
+
}
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
const ESCAPE_REPLACE_RE = /[.*+?^${}()|[\]\\/]/g;
|
|
216
|
+
function createFnUtils(fnName) {
|
|
217
|
+
const available = {
|
|
218
|
+
normal: /* @__PURE__ */ new Set([fnName]),
|
|
219
|
+
forceString: /* @__PURE__ */ new Set([`${fnName}.str`, `${fnName}['str']`, `${fnName}["str"]`, `${fnName}[\`str\`]`]),
|
|
220
|
+
forceArray: /* @__PURE__ */ new Set([`${fnName}.arr`, `${fnName}['arr']`, `${fnName}["arr"]`, `${fnName}[\`arr\`]`]),
|
|
221
|
+
forceInline: /* @__PURE__ */ new Set([`${fnName}.inl`, `${fnName}['inl']`, `${fnName}["inl"]`, `${fnName}[\`inl\`]`]),
|
|
222
|
+
// preview
|
|
223
|
+
normalPreview: /* @__PURE__ */ new Set([`${fnName}p`]),
|
|
224
|
+
forceStringPreview: /* @__PURE__ */ new Set([`${fnName}p.str`, `${fnName}p['str']`, `${fnName}p["str"]`, `${fnName}p[\`str\`]`]),
|
|
225
|
+
forceArrayPreview: /* @__PURE__ */ new Set([`${fnName}p.arr`, `${fnName}p['arr']`, `${fnName}p["arr"]`, `${fnName}p[\`arr\`]`]),
|
|
226
|
+
forceInlinePreview: /* @__PURE__ */ new Set([`${fnName}p.inl`, `${fnName}p['inl']`, `${fnName}p["inl"]`, `${fnName}p[\`inl\`]`])
|
|
227
|
+
};
|
|
228
|
+
const RE = new RegExp(`\\b(${Object.values(available).flatMap((s) => [...s].map((f) => `(${f.replace(ESCAPE_REPLACE_RE, "\\$&")})`)).join("|")})\\(`, "g");
|
|
229
|
+
return {
|
|
230
|
+
isNormal: (fnName2) => available.normal.has(fnName2) || available.normalPreview.has(fnName2),
|
|
231
|
+
isForceString: (fnName2) => available.forceString.has(fnName2) || available.forceStringPreview.has(fnName2),
|
|
232
|
+
isForceArray: (fnName2) => available.forceArray.has(fnName2) || available.forceArrayPreview.has(fnName2),
|
|
233
|
+
isForceInline: (fnName2) => available.forceInline.has(fnName2) || available.forceInlinePreview.has(fnName2),
|
|
234
|
+
isPreview: (fnName2) => available.normalPreview.has(fnName2) || available.forceStringPreview.has(fnName2) || available.forceArrayPreview.has(fnName2) || available.forceInlinePreview.has(fnName2),
|
|
235
|
+
RE
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
async function createCtx(options) {
|
|
239
|
+
const {
|
|
240
|
+
cwd,
|
|
241
|
+
currentPackageName,
|
|
242
|
+
target,
|
|
243
|
+
configOrPath,
|
|
244
|
+
fnName,
|
|
245
|
+
transformedFormat,
|
|
246
|
+
tsCodegen,
|
|
247
|
+
devCss
|
|
248
|
+
} = options;
|
|
249
|
+
const devCssFilepath = pathe.isAbsolute(devCss) ? pathe.resolve(devCss) : pathe.join(cwd, devCss);
|
|
250
|
+
const tsCodegenFilepath = tsCodegen === false ? null : pathe.isAbsolute(tsCodegen) ? pathe.resolve(tsCodegen) : pathe.join(cwd, tsCodegen);
|
|
251
|
+
const inlineConfig = typeof configOrPath === "object" ? configOrPath : null;
|
|
252
|
+
const specificConfigPath = typeof configOrPath === "string" ? pathe.isAbsolute(configOrPath) ? configOrPath : pathe.join(cwd, configOrPath) : null;
|
|
253
|
+
const configSources = [
|
|
254
|
+
...specificConfigPath == null ? [] : [specificConfigPath],
|
|
255
|
+
...["pika", "pikacss"].flatMap((name) => ["js", "ts", "cjs", "cts", "mjs", "mts"].map((ext) => `${name}.config.${ext}`)).map((name) => pathe.join(cwd, name))
|
|
256
|
+
];
|
|
257
|
+
const targetREs = target.map((t) => micromatch__default.makeRe(t));
|
|
258
|
+
const needToTransform = (id) => targetREs.some((re) => re.test(id));
|
|
259
|
+
const ctx = {
|
|
260
|
+
cwd,
|
|
261
|
+
currentPackageName,
|
|
262
|
+
fnName,
|
|
263
|
+
fnUtils: createFnUtils(fnName),
|
|
264
|
+
transformedFormat,
|
|
265
|
+
devCssFilepath,
|
|
266
|
+
tsCodegenFilepath,
|
|
267
|
+
hasVue: localPkg.isPackageExists("vue", { paths: [cwd] }),
|
|
268
|
+
usages: /* @__PURE__ */ new Map(),
|
|
269
|
+
hooks: {
|
|
270
|
+
styleUpdated: createEventHook(),
|
|
271
|
+
tsCodegenUpdated: createEventHook()
|
|
272
|
+
},
|
|
273
|
+
loadConfig: async () => {
|
|
274
|
+
if (inlineConfig != null)
|
|
275
|
+
return { config: inlineConfig, file: null };
|
|
276
|
+
const resolvedConfigPath = configSources.find((path) => {
|
|
277
|
+
const stat = node_fs.statSync(path, { throwIfNoEntry: false });
|
|
278
|
+
return stat != null && stat.isFile();
|
|
279
|
+
});
|
|
280
|
+
if (resolvedConfigPath == null)
|
|
281
|
+
return { config: null, file: null };
|
|
282
|
+
const jiti$1 = jiti.createJiti(cwd, {
|
|
283
|
+
fsCache: false,
|
|
284
|
+
moduleCache: false
|
|
285
|
+
});
|
|
286
|
+
const config = await jiti$1.import(resolvedConfigPath, { default: true });
|
|
287
|
+
return { config, file: resolvedConfigPath };
|
|
288
|
+
},
|
|
289
|
+
init: async () => {
|
|
290
|
+
ctx.isReady = false;
|
|
291
|
+
await promises.mkdir(pathe.dirname(devCssFilepath), { recursive: true }).catch(() => {
|
|
292
|
+
});
|
|
293
|
+
await promises.writeFile(devCssFilepath, "");
|
|
294
|
+
if (tsCodegenFilepath != null) {
|
|
295
|
+
await promises.mkdir(pathe.dirname(tsCodegenFilepath), { recursive: true }).catch(() => {
|
|
296
|
+
});
|
|
297
|
+
await promises.writeFile(tsCodegenFilepath, "export function defineEngineConfig(config: any) { return config }");
|
|
298
|
+
}
|
|
299
|
+
ctx.usages.clear();
|
|
300
|
+
const { config, file } = await ctx.loadConfig().catch((error) => {
|
|
301
|
+
console.warn(`[${ctx.currentPackageName}] Failed to load config file: ${error}`);
|
|
302
|
+
return { config: null, file: null };
|
|
303
|
+
});
|
|
304
|
+
ctx.resolvedConfigPath = file;
|
|
305
|
+
ctx.engine = await core.createEngine(config ?? {});
|
|
306
|
+
ctx.engine.config.plugins.unshift({
|
|
307
|
+
name: "@pikacss/integration:dev",
|
|
308
|
+
preflightUpdated: () => ctx.hooks.styleUpdated.trigger(),
|
|
309
|
+
atomicRuleAdded: () => ctx.hooks.styleUpdated.trigger(),
|
|
310
|
+
autocompleteConfigUpdated: () => ctx.hooks.tsCodegenUpdated.trigger()
|
|
311
|
+
});
|
|
312
|
+
ctx.isReady = true;
|
|
313
|
+
},
|
|
314
|
+
isReady: false,
|
|
315
|
+
configSources,
|
|
316
|
+
resolvedConfigPath: null,
|
|
317
|
+
engine: null,
|
|
318
|
+
transform: async (code, id) => {
|
|
319
|
+
if (ctx.isReady === false || !needToTransform(id)) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
ctx.usages.delete(id);
|
|
323
|
+
const functionCalls = findFunctionCalls(code, ctx.fnUtils.RE);
|
|
324
|
+
if (functionCalls.length === 0)
|
|
325
|
+
return;
|
|
326
|
+
const usages = [];
|
|
327
|
+
ctx.usages.set(id, usages);
|
|
328
|
+
const transformed = new MagicString__default(code);
|
|
329
|
+
for (const fnCall of functionCalls) {
|
|
330
|
+
const functionCallStr = fnCall.snippet;
|
|
331
|
+
const argsStr = `[${functionCallStr.slice(fnCall.fnName.length + 1, -1)}]`;
|
|
332
|
+
const args = new Function(`return ${argsStr}`)();
|
|
333
|
+
const usage = {
|
|
334
|
+
params: args
|
|
335
|
+
};
|
|
336
|
+
usages.push(usage);
|
|
337
|
+
const names = await ctx.engine.use(...args);
|
|
338
|
+
ctx.hooks.tsCodegenUpdated.trigger();
|
|
339
|
+
let transformedContent;
|
|
340
|
+
if (ctx.fnUtils.isNormal(fnCall.fnName)) {
|
|
341
|
+
transformedContent = ctx.transformedFormat === "array" ? `[${names.map((n) => `'${n}'`).join(", ")}]` : ctx.transformedFormat === "string" ? `'${names.join(" ")}'` : names.join(" ");
|
|
342
|
+
} else if (ctx.fnUtils.isForceString(fnCall.fnName)) {
|
|
343
|
+
transformedContent = `'${names.join(" ")}'`;
|
|
344
|
+
} else if (ctx.fnUtils.isForceArray(fnCall.fnName)) {
|
|
345
|
+
transformedContent = `[${names.map((n) => `'${n}'`).join(", ")}]`;
|
|
346
|
+
} else if (ctx.fnUtils.isForceInline(fnCall.fnName)) {
|
|
347
|
+
transformedContent = names.join(" ");
|
|
348
|
+
} else {
|
|
349
|
+
throw new Error(`Unexpected function name: ${fnCall.fnName}`);
|
|
350
|
+
}
|
|
351
|
+
transformed.update(fnCall.start, fnCall.end + 1, transformedContent);
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
code: transformed.toString(),
|
|
355
|
+
map: transformed.generateMap({ hires: true })
|
|
356
|
+
};
|
|
357
|
+
},
|
|
358
|
+
writeDevCssFile: async () => {
|
|
359
|
+
if (ctx.isReady === false)
|
|
360
|
+
return;
|
|
361
|
+
let rawCss = "";
|
|
362
|
+
let css = "";
|
|
363
|
+
try {
|
|
364
|
+
rawCss = ctx.engine.renderStyles();
|
|
365
|
+
css = await prettier__namespace.format([
|
|
366
|
+
`/* Auto-generated by ${ctx.currentPackageName} */`,
|
|
367
|
+
rawCss
|
|
368
|
+
].join("\n"), { parser: "css" });
|
|
369
|
+
} catch (error) {
|
|
370
|
+
console.warn(`[${ctx.currentPackageName}] Failed to generate CSS: ${ctx.devCssFilepath}`);
|
|
371
|
+
css = [
|
|
372
|
+
`/* ${error.message.split("\n")[0]}`,
|
|
373
|
+
"",
|
|
374
|
+
rawCss,
|
|
375
|
+
"",
|
|
376
|
+
"*/"
|
|
377
|
+
].join("\n");
|
|
378
|
+
}
|
|
379
|
+
await promises.writeFile(ctx.devCssFilepath, css);
|
|
380
|
+
},
|
|
381
|
+
writeTsCodegenFile: async () => {
|
|
382
|
+
if (ctx.isReady === false || ctx.tsCodegenFilepath == null)
|
|
383
|
+
return;
|
|
384
|
+
const content = await generateTsCodegenContent(ctx);
|
|
385
|
+
await promises.writeFile(ctx.tsCodegenFilepath, content);
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
await ctx.init();
|
|
389
|
+
return ctx;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
exports.createCtx = createCtx;
|
|
393
|
+
Object.prototype.hasOwnProperty.call(core, '__proto__') &&
|
|
394
|
+
!Object.prototype.hasOwnProperty.call(exports, '__proto__') &&
|
|
395
|
+
Object.defineProperty(exports, '__proto__', {
|
|
396
|
+
enumerable: true,
|
|
397
|
+
value: core['__proto__']
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
Object.keys(core).forEach(function (k) {
|
|
401
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = core[k];
|
|
402
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { EngineConfig, Engine } from '@pikacss/core';
|
|
2
|
+
export * from '@pikacss/core';
|
|
3
|
+
import { SourceMap } from 'magic-string';
|
|
4
|
+
|
|
5
|
+
type EventHookListener<EventPayload> = (payload: EventPayload) => void | Promise<void>;
|
|
6
|
+
interface EventHook<EventPayload> {
|
|
7
|
+
listeners: Set<EventHookListener<EventPayload>>;
|
|
8
|
+
trigger: (payload: EventPayload) => void;
|
|
9
|
+
on: (listener: EventHookListener<EventPayload>) => () => void;
|
|
10
|
+
off: (listener: EventHookListener<EventPayload>) => void;
|
|
11
|
+
}
|
|
12
|
+
declare function createEventHook<EventPayload>(): EventHook<EventPayload>;
|
|
13
|
+
|
|
14
|
+
interface UsageRecord {
|
|
15
|
+
params: Parameters<Engine['use']>;
|
|
16
|
+
}
|
|
17
|
+
interface FnUtils {
|
|
18
|
+
isNormal: (fnName: string) => boolean;
|
|
19
|
+
isForceString: (fnName: string) => boolean;
|
|
20
|
+
isForceArray: (fnName: string) => boolean;
|
|
21
|
+
isForceInline: (fnName: string) => boolean;
|
|
22
|
+
isPreview: (fnName: string) => boolean;
|
|
23
|
+
RE: RegExp;
|
|
24
|
+
}
|
|
25
|
+
interface IntegrationContext {
|
|
26
|
+
cwd: string;
|
|
27
|
+
currentPackageName: string;
|
|
28
|
+
fnName: string;
|
|
29
|
+
fnUtils: FnUtils;
|
|
30
|
+
transformedFormat: 'string' | 'array' | 'inline';
|
|
31
|
+
devCssFilepath: string;
|
|
32
|
+
tsCodegenFilepath: string | null;
|
|
33
|
+
hasVue: boolean;
|
|
34
|
+
usages: Map<string, UsageRecord[]>;
|
|
35
|
+
hooks: {
|
|
36
|
+
styleUpdated: ReturnType<typeof createEventHook<void>>;
|
|
37
|
+
tsCodegenUpdated: ReturnType<typeof createEventHook<void>>;
|
|
38
|
+
};
|
|
39
|
+
loadConfig: () => Promise<{
|
|
40
|
+
config: EngineConfig;
|
|
41
|
+
file: null;
|
|
42
|
+
} | {
|
|
43
|
+
config: null;
|
|
44
|
+
file: null;
|
|
45
|
+
} | {
|
|
46
|
+
config: EngineConfig;
|
|
47
|
+
file: string;
|
|
48
|
+
}>;
|
|
49
|
+
init: () => Promise<any>;
|
|
50
|
+
isReady: boolean;
|
|
51
|
+
configSources: string[];
|
|
52
|
+
resolvedConfigPath: string | null;
|
|
53
|
+
engine: Engine;
|
|
54
|
+
transform: (code: string, id: string) => Promise<{
|
|
55
|
+
code: string;
|
|
56
|
+
map: SourceMap;
|
|
57
|
+
} | undefined>;
|
|
58
|
+
writeDevCssFile: () => Promise<void>;
|
|
59
|
+
writeTsCodegenFile: () => Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
interface IntegrationContextOptions {
|
|
62
|
+
cwd: string;
|
|
63
|
+
currentPackageName: string;
|
|
64
|
+
target: string[];
|
|
65
|
+
configOrPath: EngineConfig | string | null | undefined;
|
|
66
|
+
fnName: string;
|
|
67
|
+
transformedFormat: 'string' | 'array' | 'inline';
|
|
68
|
+
tsCodegen: false | string;
|
|
69
|
+
devCss: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare function createCtx(options: IntegrationContextOptions): Promise<IntegrationContext>;
|
|
73
|
+
|
|
74
|
+
export { type FnUtils, type IntegrationContext, type IntegrationContextOptions, type UsageRecord, createCtx };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { EngineConfig, Engine } from '@pikacss/core';
|
|
2
|
+
export * from '@pikacss/core';
|
|
3
|
+
import { SourceMap } from 'magic-string';
|
|
4
|
+
|
|
5
|
+
type EventHookListener<EventPayload> = (payload: EventPayload) => void | Promise<void>;
|
|
6
|
+
interface EventHook<EventPayload> {
|
|
7
|
+
listeners: Set<EventHookListener<EventPayload>>;
|
|
8
|
+
trigger: (payload: EventPayload) => void;
|
|
9
|
+
on: (listener: EventHookListener<EventPayload>) => () => void;
|
|
10
|
+
off: (listener: EventHookListener<EventPayload>) => void;
|
|
11
|
+
}
|
|
12
|
+
declare function createEventHook<EventPayload>(): EventHook<EventPayload>;
|
|
13
|
+
|
|
14
|
+
interface UsageRecord {
|
|
15
|
+
params: Parameters<Engine['use']>;
|
|
16
|
+
}
|
|
17
|
+
interface FnUtils {
|
|
18
|
+
isNormal: (fnName: string) => boolean;
|
|
19
|
+
isForceString: (fnName: string) => boolean;
|
|
20
|
+
isForceArray: (fnName: string) => boolean;
|
|
21
|
+
isForceInline: (fnName: string) => boolean;
|
|
22
|
+
isPreview: (fnName: string) => boolean;
|
|
23
|
+
RE: RegExp;
|
|
24
|
+
}
|
|
25
|
+
interface IntegrationContext {
|
|
26
|
+
cwd: string;
|
|
27
|
+
currentPackageName: string;
|
|
28
|
+
fnName: string;
|
|
29
|
+
fnUtils: FnUtils;
|
|
30
|
+
transformedFormat: 'string' | 'array' | 'inline';
|
|
31
|
+
devCssFilepath: string;
|
|
32
|
+
tsCodegenFilepath: string | null;
|
|
33
|
+
hasVue: boolean;
|
|
34
|
+
usages: Map<string, UsageRecord[]>;
|
|
35
|
+
hooks: {
|
|
36
|
+
styleUpdated: ReturnType<typeof createEventHook<void>>;
|
|
37
|
+
tsCodegenUpdated: ReturnType<typeof createEventHook<void>>;
|
|
38
|
+
};
|
|
39
|
+
loadConfig: () => Promise<{
|
|
40
|
+
config: EngineConfig;
|
|
41
|
+
file: null;
|
|
42
|
+
} | {
|
|
43
|
+
config: null;
|
|
44
|
+
file: null;
|
|
45
|
+
} | {
|
|
46
|
+
config: EngineConfig;
|
|
47
|
+
file: string;
|
|
48
|
+
}>;
|
|
49
|
+
init: () => Promise<any>;
|
|
50
|
+
isReady: boolean;
|
|
51
|
+
configSources: string[];
|
|
52
|
+
resolvedConfigPath: string | null;
|
|
53
|
+
engine: Engine;
|
|
54
|
+
transform: (code: string, id: string) => Promise<{
|
|
55
|
+
code: string;
|
|
56
|
+
map: SourceMap;
|
|
57
|
+
} | undefined>;
|
|
58
|
+
writeDevCssFile: () => Promise<void>;
|
|
59
|
+
writeTsCodegenFile: () => Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
interface IntegrationContextOptions {
|
|
62
|
+
cwd: string;
|
|
63
|
+
currentPackageName: string;
|
|
64
|
+
target: string[];
|
|
65
|
+
configOrPath: EngineConfig | string | null | undefined;
|
|
66
|
+
fnName: string;
|
|
67
|
+
transformedFormat: 'string' | 'array' | 'inline';
|
|
68
|
+
tsCodegen: false | string;
|
|
69
|
+
devCss: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare function createCtx(options: IntegrationContextOptions): Promise<IntegrationContext>;
|
|
73
|
+
|
|
74
|
+
export { type FnUtils, type IntegrationContext, type IntegrationContextOptions, type UsageRecord, createCtx };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { EngineConfig, Engine } from '@pikacss/core';
|
|
2
|
+
export * from '@pikacss/core';
|
|
3
|
+
import { SourceMap } from 'magic-string';
|
|
4
|
+
|
|
5
|
+
type EventHookListener<EventPayload> = (payload: EventPayload) => void | Promise<void>;
|
|
6
|
+
interface EventHook<EventPayload> {
|
|
7
|
+
listeners: Set<EventHookListener<EventPayload>>;
|
|
8
|
+
trigger: (payload: EventPayload) => void;
|
|
9
|
+
on: (listener: EventHookListener<EventPayload>) => () => void;
|
|
10
|
+
off: (listener: EventHookListener<EventPayload>) => void;
|
|
11
|
+
}
|
|
12
|
+
declare function createEventHook<EventPayload>(): EventHook<EventPayload>;
|
|
13
|
+
|
|
14
|
+
interface UsageRecord {
|
|
15
|
+
params: Parameters<Engine['use']>;
|
|
16
|
+
}
|
|
17
|
+
interface FnUtils {
|
|
18
|
+
isNormal: (fnName: string) => boolean;
|
|
19
|
+
isForceString: (fnName: string) => boolean;
|
|
20
|
+
isForceArray: (fnName: string) => boolean;
|
|
21
|
+
isForceInline: (fnName: string) => boolean;
|
|
22
|
+
isPreview: (fnName: string) => boolean;
|
|
23
|
+
RE: RegExp;
|
|
24
|
+
}
|
|
25
|
+
interface IntegrationContext {
|
|
26
|
+
cwd: string;
|
|
27
|
+
currentPackageName: string;
|
|
28
|
+
fnName: string;
|
|
29
|
+
fnUtils: FnUtils;
|
|
30
|
+
transformedFormat: 'string' | 'array' | 'inline';
|
|
31
|
+
devCssFilepath: string;
|
|
32
|
+
tsCodegenFilepath: string | null;
|
|
33
|
+
hasVue: boolean;
|
|
34
|
+
usages: Map<string, UsageRecord[]>;
|
|
35
|
+
hooks: {
|
|
36
|
+
styleUpdated: ReturnType<typeof createEventHook<void>>;
|
|
37
|
+
tsCodegenUpdated: ReturnType<typeof createEventHook<void>>;
|
|
38
|
+
};
|
|
39
|
+
loadConfig: () => Promise<{
|
|
40
|
+
config: EngineConfig;
|
|
41
|
+
file: null;
|
|
42
|
+
} | {
|
|
43
|
+
config: null;
|
|
44
|
+
file: null;
|
|
45
|
+
} | {
|
|
46
|
+
config: EngineConfig;
|
|
47
|
+
file: string;
|
|
48
|
+
}>;
|
|
49
|
+
init: () => Promise<any>;
|
|
50
|
+
isReady: boolean;
|
|
51
|
+
configSources: string[];
|
|
52
|
+
resolvedConfigPath: string | null;
|
|
53
|
+
engine: Engine;
|
|
54
|
+
transform: (code: string, id: string) => Promise<{
|
|
55
|
+
code: string;
|
|
56
|
+
map: SourceMap;
|
|
57
|
+
} | undefined>;
|
|
58
|
+
writeDevCssFile: () => Promise<void>;
|
|
59
|
+
writeTsCodegenFile: () => Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
interface IntegrationContextOptions {
|
|
62
|
+
cwd: string;
|
|
63
|
+
currentPackageName: string;
|
|
64
|
+
target: string[];
|
|
65
|
+
configOrPath: EngineConfig | string | null | undefined;
|
|
66
|
+
fnName: string;
|
|
67
|
+
transformedFormat: 'string' | 'array' | 'inline';
|
|
68
|
+
tsCodegen: false | string;
|
|
69
|
+
devCss: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare function createCtx(options: IntegrationContextOptions): Promise<IntegrationContext>;
|
|
73
|
+
|
|
74
|
+
export { type FnUtils, type IntegrationContext, type IntegrationContextOptions, type UsageRecord, createCtx };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
import { statSync } from 'node:fs';
|
|
2
|
+
import { writeFile, mkdir } from 'node:fs/promises';
|
|
3
|
+
import { createEngine } from '@pikacss/core';
|
|
4
|
+
export * from '@pikacss/core';
|
|
5
|
+
import { createJiti } from 'jiti';
|
|
6
|
+
import { isPackageExists } from 'local-pkg';
|
|
7
|
+
import MagicString from 'magic-string';
|
|
8
|
+
import micromatch from 'micromatch';
|
|
9
|
+
import { isAbsolute, resolve, join, dirname } from 'pathe';
|
|
10
|
+
import * as prettier from 'prettier';
|
|
11
|
+
|
|
12
|
+
function createEventHook() {
|
|
13
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
14
|
+
function trigger(payload) {
|
|
15
|
+
listeners.forEach((listener) => listener(payload));
|
|
16
|
+
}
|
|
17
|
+
function off(listener) {
|
|
18
|
+
listeners.delete(listener);
|
|
19
|
+
}
|
|
20
|
+
function on(listener) {
|
|
21
|
+
listeners.add(listener);
|
|
22
|
+
const offListener = () => off(listener);
|
|
23
|
+
return offListener;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
listeners,
|
|
27
|
+
trigger,
|
|
28
|
+
on,
|
|
29
|
+
off
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function formatUnionStringType(list) {
|
|
34
|
+
return list.length > 0 ? list.map((i) => typeof i === "number" ? i : `'${i}'`).join(" | ") : "never";
|
|
35
|
+
}
|
|
36
|
+
function generateAutocomplete(ctx) {
|
|
37
|
+
const autocomplete = ctx.engine.config.autocomplete;
|
|
38
|
+
return [
|
|
39
|
+
"export interface Autocomplete extends _Autocomplete {",
|
|
40
|
+
` Selector: ${formatUnionStringType([...autocomplete.selectors])}`,
|
|
41
|
+
` StyleItemString: ${formatUnionStringType([...autocomplete.styleItemStrings])}`,
|
|
42
|
+
` ExtraProperty: ${formatUnionStringType([...autocomplete.extraProperties])}`,
|
|
43
|
+
` ExtraCssProperty: ${formatUnionStringType([...autocomplete.extraCssProperties])}`,
|
|
44
|
+
` PropertiesValue: { ${Array.from(autocomplete.properties.entries(), ([k, v]) => `'${k}': ${v.join(" | ")}`).join(",")} }`,
|
|
45
|
+
` CssPropertiesValue: { ${Array.from(autocomplete.cssProperties.entries(), ([k, v]) => `'${k}': ${formatUnionStringType(v)}`).join(",")} }`,
|
|
46
|
+
"}",
|
|
47
|
+
""
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
function generateStyleFn(ctx) {
|
|
51
|
+
const { transformedFormat } = ctx;
|
|
52
|
+
const lines = [
|
|
53
|
+
"type StyleFn_Array = (...params: StyleItem<Autocomplete>[]) => string[]",
|
|
54
|
+
"type StyleFn_String = (...params: StyleItem<Autocomplete>[]) => string",
|
|
55
|
+
"type StyleFn_Inline = (...params: StyleItem<Autocomplete>[]) => void"
|
|
56
|
+
];
|
|
57
|
+
if (transformedFormat === "array")
|
|
58
|
+
lines.push("type StyleFn_Normal = StyleFn_Array");
|
|
59
|
+
else if (transformedFormat === "string")
|
|
60
|
+
lines.push("type StyleFn_Normal = StyleFn_String");
|
|
61
|
+
else if (transformedFormat === "inline")
|
|
62
|
+
lines.push("type StyleFn_Normal = StyleFn_Inline");
|
|
63
|
+
lines.push(
|
|
64
|
+
"type StyleFn = StyleFn_Normal & {",
|
|
65
|
+
" str: StyleFn_String",
|
|
66
|
+
" arr: StyleFn_Array",
|
|
67
|
+
" inl: StyleFn_Inline",
|
|
68
|
+
"}",
|
|
69
|
+
`type StyleFnWithPreview = PreviewOverloads<StyleFn_Normal>['fn'] & {`,
|
|
70
|
+
` str: PreviewOverloads<StyleFn_String>['fn']`,
|
|
71
|
+
` arr: PreviewOverloads<StyleFn_Array>['fn']`,
|
|
72
|
+
` inl: PreviewOverloads<StyleFn_Inline>['fn']`,
|
|
73
|
+
"}",
|
|
74
|
+
""
|
|
75
|
+
);
|
|
76
|
+
return lines;
|
|
77
|
+
}
|
|
78
|
+
function generateGlobalDeclaration(ctx) {
|
|
79
|
+
const { fnName } = ctx;
|
|
80
|
+
return [
|
|
81
|
+
"declare global {",
|
|
82
|
+
" /**",
|
|
83
|
+
" * PikaCSS",
|
|
84
|
+
" */",
|
|
85
|
+
` const ${fnName}: StyleFn`,
|
|
86
|
+
"",
|
|
87
|
+
" /**",
|
|
88
|
+
" * PikaCSS Preview",
|
|
89
|
+
" */",
|
|
90
|
+
` const ${fnName}p: StyleFnWithPreview`,
|
|
91
|
+
"}",
|
|
92
|
+
""
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
function generateVueDeclaration(ctx) {
|
|
96
|
+
const { hasVue, fnName } = ctx;
|
|
97
|
+
if (!hasVue)
|
|
98
|
+
return [];
|
|
99
|
+
return [
|
|
100
|
+
"declare module 'vue' {",
|
|
101
|
+
" interface ComponentCustomProperties {",
|
|
102
|
+
" /**",
|
|
103
|
+
" * PikaCSS",
|
|
104
|
+
" */",
|
|
105
|
+
` ${fnName}: StyleFn`,
|
|
106
|
+
"",
|
|
107
|
+
" /**",
|
|
108
|
+
" * PikaCSS Preview",
|
|
109
|
+
" */",
|
|
110
|
+
` ${fnName}p: StyleFnWithPreview`,
|
|
111
|
+
" }",
|
|
112
|
+
"}",
|
|
113
|
+
""
|
|
114
|
+
];
|
|
115
|
+
}
|
|
116
|
+
async function generateOverloadContent(ctx) {
|
|
117
|
+
const paramsLines = [];
|
|
118
|
+
const fnsLines = [];
|
|
119
|
+
const usages = [...ctx.usages.values()].flat();
|
|
120
|
+
for (let i = 0; i < usages.length; i++) {
|
|
121
|
+
const usage = usages[i];
|
|
122
|
+
try {
|
|
123
|
+
const addedParamsLines = usage.params.map((param, index) => `type P${i}_${index} = ${JSON.stringify(param)}`);
|
|
124
|
+
const addedFnLines = [
|
|
125
|
+
" /**",
|
|
126
|
+
" * ### PikaCSS Preview",
|
|
127
|
+
" * ```css",
|
|
128
|
+
// CSS Lines
|
|
129
|
+
...(await prettier.format(await ctx.engine.renderPreviewStyles(...usage.params), { parser: "css" })).split("\n").map((line) => ` * \u200E${line.replace(/^(\s*)/, "$1\u200E")}`),
|
|
130
|
+
" * ```",
|
|
131
|
+
" */",
|
|
132
|
+
` fn(...params: [${usage.params.map((_, index) => `p${index}: P${i}_${index}`).join(", ")}]): ReturnType<StyleFn>`
|
|
133
|
+
];
|
|
134
|
+
paramsLines.push(...addedParamsLines);
|
|
135
|
+
fnsLines.push(...addedFnLines);
|
|
136
|
+
} catch {
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return [
|
|
140
|
+
"interface PreviewOverloads<StyleFn extends (StyleFn_Array | StyleFn_String | StyleFn_Inline)> {",
|
|
141
|
+
...fnsLines,
|
|
142
|
+
" /**",
|
|
143
|
+
" * PikaCSS Preview",
|
|
144
|
+
" * Save the current file to see the preview.",
|
|
145
|
+
" */",
|
|
146
|
+
` fn(...params: Parameters<StyleFn>): ReturnType<StyleFn>`,
|
|
147
|
+
"}",
|
|
148
|
+
...paramsLines
|
|
149
|
+
];
|
|
150
|
+
}
|
|
151
|
+
async function generateTsCodegenContent(ctx) {
|
|
152
|
+
const lines = [
|
|
153
|
+
`// Auto-generated by ${ctx.currentPackageName}`,
|
|
154
|
+
`import type { Autocomplete as _Autocomplete, StyleItem } from '${ctx.currentPackageName}'`,
|
|
155
|
+
`import { createDefineEngineConfigFn } from '${ctx.currentPackageName}'`,
|
|
156
|
+
""
|
|
157
|
+
];
|
|
158
|
+
lines.push(...generateAutocomplete(ctx));
|
|
159
|
+
lines.push(
|
|
160
|
+
"export const defineEngineConfig: ReturnType<typeof createDefineEngineConfigFn<Autocomplete>> = createDefineEngineConfigFn<Autocomplete>()",
|
|
161
|
+
""
|
|
162
|
+
);
|
|
163
|
+
lines.push(...generateStyleFn(ctx));
|
|
164
|
+
lines.push(...generateGlobalDeclaration(ctx));
|
|
165
|
+
lines.push(...generateVueDeclaration(ctx));
|
|
166
|
+
lines.push(...await generateOverloadContent(ctx));
|
|
167
|
+
return lines.join("\n");
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function findFunctionCalls(code, RE) {
|
|
171
|
+
const result = [];
|
|
172
|
+
let matched = RE.exec(code);
|
|
173
|
+
while (matched != null) {
|
|
174
|
+
const fnName = matched[1];
|
|
175
|
+
const start = matched.index;
|
|
176
|
+
let end = start + fnName.length;
|
|
177
|
+
let depth = 1;
|
|
178
|
+
let inString = false;
|
|
179
|
+
while (depth > 0) {
|
|
180
|
+
end++;
|
|
181
|
+
if (inString === false && code[end] === "(")
|
|
182
|
+
depth++;
|
|
183
|
+
else if (inString === false && code[end] === ")")
|
|
184
|
+
depth--;
|
|
185
|
+
else if (inString === false && (code[end] === "'" || code[end] === '"'))
|
|
186
|
+
inString = code[end];
|
|
187
|
+
else if (inString === code[end])
|
|
188
|
+
inString = false;
|
|
189
|
+
}
|
|
190
|
+
const snippet = code.slice(start, end + 1);
|
|
191
|
+
result.push({ fnName, start, end, snippet });
|
|
192
|
+
matched = RE.exec(code);
|
|
193
|
+
}
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
196
|
+
const ESCAPE_REPLACE_RE = /[.*+?^${}()|[\]\\/]/g;
|
|
197
|
+
function createFnUtils(fnName) {
|
|
198
|
+
const available = {
|
|
199
|
+
normal: /* @__PURE__ */ new Set([fnName]),
|
|
200
|
+
forceString: /* @__PURE__ */ new Set([`${fnName}.str`, `${fnName}['str']`, `${fnName}["str"]`, `${fnName}[\`str\`]`]),
|
|
201
|
+
forceArray: /* @__PURE__ */ new Set([`${fnName}.arr`, `${fnName}['arr']`, `${fnName}["arr"]`, `${fnName}[\`arr\`]`]),
|
|
202
|
+
forceInline: /* @__PURE__ */ new Set([`${fnName}.inl`, `${fnName}['inl']`, `${fnName}["inl"]`, `${fnName}[\`inl\`]`]),
|
|
203
|
+
// preview
|
|
204
|
+
normalPreview: /* @__PURE__ */ new Set([`${fnName}p`]),
|
|
205
|
+
forceStringPreview: /* @__PURE__ */ new Set([`${fnName}p.str`, `${fnName}p['str']`, `${fnName}p["str"]`, `${fnName}p[\`str\`]`]),
|
|
206
|
+
forceArrayPreview: /* @__PURE__ */ new Set([`${fnName}p.arr`, `${fnName}p['arr']`, `${fnName}p["arr"]`, `${fnName}p[\`arr\`]`]),
|
|
207
|
+
forceInlinePreview: /* @__PURE__ */ new Set([`${fnName}p.inl`, `${fnName}p['inl']`, `${fnName}p["inl"]`, `${fnName}p[\`inl\`]`])
|
|
208
|
+
};
|
|
209
|
+
const RE = new RegExp(`\\b(${Object.values(available).flatMap((s) => [...s].map((f) => `(${f.replace(ESCAPE_REPLACE_RE, "\\$&")})`)).join("|")})\\(`, "g");
|
|
210
|
+
return {
|
|
211
|
+
isNormal: (fnName2) => available.normal.has(fnName2) || available.normalPreview.has(fnName2),
|
|
212
|
+
isForceString: (fnName2) => available.forceString.has(fnName2) || available.forceStringPreview.has(fnName2),
|
|
213
|
+
isForceArray: (fnName2) => available.forceArray.has(fnName2) || available.forceArrayPreview.has(fnName2),
|
|
214
|
+
isForceInline: (fnName2) => available.forceInline.has(fnName2) || available.forceInlinePreview.has(fnName2),
|
|
215
|
+
isPreview: (fnName2) => available.normalPreview.has(fnName2) || available.forceStringPreview.has(fnName2) || available.forceArrayPreview.has(fnName2) || available.forceInlinePreview.has(fnName2),
|
|
216
|
+
RE
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
async function createCtx(options) {
|
|
220
|
+
const {
|
|
221
|
+
cwd,
|
|
222
|
+
currentPackageName,
|
|
223
|
+
target,
|
|
224
|
+
configOrPath,
|
|
225
|
+
fnName,
|
|
226
|
+
transformedFormat,
|
|
227
|
+
tsCodegen,
|
|
228
|
+
devCss
|
|
229
|
+
} = options;
|
|
230
|
+
const devCssFilepath = isAbsolute(devCss) ? resolve(devCss) : join(cwd, devCss);
|
|
231
|
+
const tsCodegenFilepath = tsCodegen === false ? null : isAbsolute(tsCodegen) ? resolve(tsCodegen) : join(cwd, tsCodegen);
|
|
232
|
+
const inlineConfig = typeof configOrPath === "object" ? configOrPath : null;
|
|
233
|
+
const specificConfigPath = typeof configOrPath === "string" ? isAbsolute(configOrPath) ? configOrPath : join(cwd, configOrPath) : null;
|
|
234
|
+
const configSources = [
|
|
235
|
+
...specificConfigPath == null ? [] : [specificConfigPath],
|
|
236
|
+
...["pika", "pikacss"].flatMap((name) => ["js", "ts", "cjs", "cts", "mjs", "mts"].map((ext) => `${name}.config.${ext}`)).map((name) => join(cwd, name))
|
|
237
|
+
];
|
|
238
|
+
const targetREs = target.map((t) => micromatch.makeRe(t));
|
|
239
|
+
const needToTransform = (id) => targetREs.some((re) => re.test(id));
|
|
240
|
+
const ctx = {
|
|
241
|
+
cwd,
|
|
242
|
+
currentPackageName,
|
|
243
|
+
fnName,
|
|
244
|
+
fnUtils: createFnUtils(fnName),
|
|
245
|
+
transformedFormat,
|
|
246
|
+
devCssFilepath,
|
|
247
|
+
tsCodegenFilepath,
|
|
248
|
+
hasVue: isPackageExists("vue", { paths: [cwd] }),
|
|
249
|
+
usages: /* @__PURE__ */ new Map(),
|
|
250
|
+
hooks: {
|
|
251
|
+
styleUpdated: createEventHook(),
|
|
252
|
+
tsCodegenUpdated: createEventHook()
|
|
253
|
+
},
|
|
254
|
+
loadConfig: async () => {
|
|
255
|
+
if (inlineConfig != null)
|
|
256
|
+
return { config: inlineConfig, file: null };
|
|
257
|
+
const resolvedConfigPath = configSources.find((path) => {
|
|
258
|
+
const stat = statSync(path, { throwIfNoEntry: false });
|
|
259
|
+
return stat != null && stat.isFile();
|
|
260
|
+
});
|
|
261
|
+
if (resolvedConfigPath == null)
|
|
262
|
+
return { config: null, file: null };
|
|
263
|
+
const jiti = createJiti(cwd, {
|
|
264
|
+
fsCache: false,
|
|
265
|
+
moduleCache: false
|
|
266
|
+
});
|
|
267
|
+
const config = await jiti.import(resolvedConfigPath, { default: true });
|
|
268
|
+
return { config, file: resolvedConfigPath };
|
|
269
|
+
},
|
|
270
|
+
init: async () => {
|
|
271
|
+
ctx.isReady = false;
|
|
272
|
+
await mkdir(dirname(devCssFilepath), { recursive: true }).catch(() => {
|
|
273
|
+
});
|
|
274
|
+
await writeFile(devCssFilepath, "");
|
|
275
|
+
if (tsCodegenFilepath != null) {
|
|
276
|
+
await mkdir(dirname(tsCodegenFilepath), { recursive: true }).catch(() => {
|
|
277
|
+
});
|
|
278
|
+
await writeFile(tsCodegenFilepath, "export function defineEngineConfig(config: any) { return config }");
|
|
279
|
+
}
|
|
280
|
+
ctx.usages.clear();
|
|
281
|
+
const { config, file } = await ctx.loadConfig().catch((error) => {
|
|
282
|
+
console.warn(`[${ctx.currentPackageName}] Failed to load config file: ${error}`);
|
|
283
|
+
return { config: null, file: null };
|
|
284
|
+
});
|
|
285
|
+
ctx.resolvedConfigPath = file;
|
|
286
|
+
ctx.engine = await createEngine(config ?? {});
|
|
287
|
+
ctx.engine.config.plugins.unshift({
|
|
288
|
+
name: "@pikacss/integration:dev",
|
|
289
|
+
preflightUpdated: () => ctx.hooks.styleUpdated.trigger(),
|
|
290
|
+
atomicRuleAdded: () => ctx.hooks.styleUpdated.trigger(),
|
|
291
|
+
autocompleteConfigUpdated: () => ctx.hooks.tsCodegenUpdated.trigger()
|
|
292
|
+
});
|
|
293
|
+
ctx.isReady = true;
|
|
294
|
+
},
|
|
295
|
+
isReady: false,
|
|
296
|
+
configSources,
|
|
297
|
+
resolvedConfigPath: null,
|
|
298
|
+
engine: null,
|
|
299
|
+
transform: async (code, id) => {
|
|
300
|
+
if (ctx.isReady === false || !needToTransform(id)) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
ctx.usages.delete(id);
|
|
304
|
+
const functionCalls = findFunctionCalls(code, ctx.fnUtils.RE);
|
|
305
|
+
if (functionCalls.length === 0)
|
|
306
|
+
return;
|
|
307
|
+
const usages = [];
|
|
308
|
+
ctx.usages.set(id, usages);
|
|
309
|
+
const transformed = new MagicString(code);
|
|
310
|
+
for (const fnCall of functionCalls) {
|
|
311
|
+
const functionCallStr = fnCall.snippet;
|
|
312
|
+
const argsStr = `[${functionCallStr.slice(fnCall.fnName.length + 1, -1)}]`;
|
|
313
|
+
const args = new Function(`return ${argsStr}`)();
|
|
314
|
+
const usage = {
|
|
315
|
+
params: args
|
|
316
|
+
};
|
|
317
|
+
usages.push(usage);
|
|
318
|
+
const names = await ctx.engine.use(...args);
|
|
319
|
+
ctx.hooks.tsCodegenUpdated.trigger();
|
|
320
|
+
let transformedContent;
|
|
321
|
+
if (ctx.fnUtils.isNormal(fnCall.fnName)) {
|
|
322
|
+
transformedContent = ctx.transformedFormat === "array" ? `[${names.map((n) => `'${n}'`).join(", ")}]` : ctx.transformedFormat === "string" ? `'${names.join(" ")}'` : names.join(" ");
|
|
323
|
+
} else if (ctx.fnUtils.isForceString(fnCall.fnName)) {
|
|
324
|
+
transformedContent = `'${names.join(" ")}'`;
|
|
325
|
+
} else if (ctx.fnUtils.isForceArray(fnCall.fnName)) {
|
|
326
|
+
transformedContent = `[${names.map((n) => `'${n}'`).join(", ")}]`;
|
|
327
|
+
} else if (ctx.fnUtils.isForceInline(fnCall.fnName)) {
|
|
328
|
+
transformedContent = names.join(" ");
|
|
329
|
+
} else {
|
|
330
|
+
throw new Error(`Unexpected function name: ${fnCall.fnName}`);
|
|
331
|
+
}
|
|
332
|
+
transformed.update(fnCall.start, fnCall.end + 1, transformedContent);
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
code: transformed.toString(),
|
|
336
|
+
map: transformed.generateMap({ hires: true })
|
|
337
|
+
};
|
|
338
|
+
},
|
|
339
|
+
writeDevCssFile: async () => {
|
|
340
|
+
if (ctx.isReady === false)
|
|
341
|
+
return;
|
|
342
|
+
let rawCss = "";
|
|
343
|
+
let css = "";
|
|
344
|
+
try {
|
|
345
|
+
rawCss = ctx.engine.renderStyles();
|
|
346
|
+
css = await prettier.format([
|
|
347
|
+
`/* Auto-generated by ${ctx.currentPackageName} */`,
|
|
348
|
+
rawCss
|
|
349
|
+
].join("\n"), { parser: "css" });
|
|
350
|
+
} catch (error) {
|
|
351
|
+
console.warn(`[${ctx.currentPackageName}] Failed to generate CSS: ${ctx.devCssFilepath}`);
|
|
352
|
+
css = [
|
|
353
|
+
`/* ${error.message.split("\n")[0]}`,
|
|
354
|
+
"",
|
|
355
|
+
rawCss,
|
|
356
|
+
"",
|
|
357
|
+
"*/"
|
|
358
|
+
].join("\n");
|
|
359
|
+
}
|
|
360
|
+
await writeFile(ctx.devCssFilepath, css);
|
|
361
|
+
},
|
|
362
|
+
writeTsCodegenFile: async () => {
|
|
363
|
+
if (ctx.isReady === false || ctx.tsCodegenFilepath == null)
|
|
364
|
+
return;
|
|
365
|
+
const content = await generateTsCodegenContent(ctx);
|
|
366
|
+
await writeFile(ctx.tsCodegenFilepath, content);
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
await ctx.init();
|
|
370
|
+
return ctx;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export { createCtx };
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pikacss/integration",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"version": "0.0.1",
|
|
8
|
+
"author": "DevilTea <ch19980814@gmail.com>",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/DevilTea/pikacss.git",
|
|
13
|
+
"directory": "packages/integration"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/DevilTea/pikacss/issues"
|
|
17
|
+
},
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"import": {
|
|
21
|
+
"types": "./dist/index.d.mts",
|
|
22
|
+
"default": "./dist/index.mjs"
|
|
23
|
+
},
|
|
24
|
+
"require": {
|
|
25
|
+
"types": "./dist/index.d.cts",
|
|
26
|
+
"default": "./dist/index.cjs"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"main": "dist/index.cjs",
|
|
31
|
+
"module": "dist/index.mjs",
|
|
32
|
+
"types": "dist/index.d.ts",
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"fast-glob": "^3.3.2",
|
|
38
|
+
"jiti": "^2.4.0",
|
|
39
|
+
"local-pkg": "^1.1.1",
|
|
40
|
+
"magic-string": "^0.30.12",
|
|
41
|
+
"micromatch": "^4.0.8",
|
|
42
|
+
"pathe": "^2.0.3",
|
|
43
|
+
"prettier": "^3.2.5",
|
|
44
|
+
"@pikacss/core": "0.0.1"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@types/micromatch": "^4.0.9"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "unbuild",
|
|
51
|
+
"build:pack": "pnpm build && pnpm pack",
|
|
52
|
+
"stub": "unbuild --stub",
|
|
53
|
+
"typecheck": "pnpm typecheck:package && pnpm typecheck:test",
|
|
54
|
+
"typecheck:package": "tsc --project ./tsconfig.package.json --noEmit",
|
|
55
|
+
"typecheck:test": "tsc --project ./tsconfig.tests.json --noEmit",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:watch": "vitest"
|
|
58
|
+
}
|
|
59
|
+
}
|