@pikacss/integration 0.0.45 → 0.0.47

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/dist/index.d.mts CHANGED
@@ -3,7 +3,7 @@ import { SourceMap } from "magic-string";
3
3
  export * from "@pikacss/core";
4
4
 
5
5
  //#region src/eventHook.d.ts
6
- type EventHookListener<EventPayload> = (payload: EventPayload) => void | Promise<void>;
6
+ type EventHookListener<EventPayload> = (payload: EventPayload) => void;
7
7
  interface EventHook<EventPayload> {
8
8
  listeners: Set<EventHookListener<EventPayload>>;
9
9
  trigger: (payload: EventPayload) => void;
@@ -33,32 +33,36 @@ interface IntegrationContextOptions {
33
33
  };
34
34
  configOrPath: EngineConfig | string | Nullish;
35
35
  fnName: string;
36
- transformedFormat: 'string' | 'array' | 'inline';
36
+ transformedFormat: 'string' | 'array';
37
37
  tsCodegen: false | string;
38
38
  cssCodegen: string;
39
39
  autoCreateConfig: boolean;
40
40
  }
41
+ type LoadedConfigResult = {
42
+ config: EngineConfig;
43
+ file: null;
44
+ content: null;
45
+ } | {
46
+ config: null;
47
+ file: null;
48
+ content: null;
49
+ } | {
50
+ config: EngineConfig;
51
+ file: string;
52
+ content: string;
53
+ };
41
54
  interface IntegrationContext {
42
55
  cwd: string;
43
56
  currentPackageName: string;
44
57
  fnName: string;
45
- transformedFormat: 'string' | 'array' | 'inline';
58
+ transformedFormat: 'string' | 'array';
46
59
  cssCodegenFilepath: string;
47
60
  tsCodegenFilepath: string | Nullish;
48
61
  hasVue: boolean;
49
62
  resolvedConfig: EngineConfig | Nullish;
50
63
  resolvedConfigPath: string | Nullish;
51
64
  resolvedConfigContent: string | Nullish;
52
- loadConfig: () => Promise<{
53
- config: EngineConfig;
54
- file: null;
55
- } | {
56
- config: null;
57
- file: null;
58
- } | {
59
- config: EngineConfig;
60
- file: string;
61
- }>;
65
+ loadConfig: () => Promise<LoadedConfigResult>;
62
66
  usages: Map<string, UsageRecord[]>;
63
67
  hooks: {
64
68
  styleUpdated: ReturnType<typeof createEventHook<void>>;
@@ -85,4 +89,4 @@ interface IntegrationContext {
85
89
  //#region src/ctx.d.ts
86
90
  declare function createCtx(options: IntegrationContextOptions): IntegrationContext;
87
91
  //#endregion
88
- export { FnUtils, IntegrationContext, IntegrationContextOptions, UsageRecord, createCtx };
92
+ export { FnUtils, IntegrationContext, IntegrationContextOptions, LoadedConfigResult, UsageRecord, createCtx };
package/dist/index.mjs CHANGED
@@ -35,21 +35,37 @@ function createEventHook() {
35
35
 
36
36
  //#endregion
37
37
  //#region src/tsCodegen.ts
38
+ function formatUnionType(parts) {
39
+ return parts.length > 0 ? parts.join(" | ") : "never";
40
+ }
38
41
  function formatUnionStringType(list) {
39
- return list.length > 0 ? list.map((i) => JSON.stringify(i)).join(" | ") : "never";
42
+ return formatUnionType(list.map((i) => JSON.stringify(i)));
43
+ }
44
+ function formatAutocompleteUnion(literals, patterns) {
45
+ return formatUnionType([...Array.from(literals, (value) => JSON.stringify(value)), ...patterns == null ? [] : [...patterns]]);
46
+ }
47
+ function formatAutocompleteValueMap(keys, entries, patternEntries, formatValue) {
48
+ const mergedKeys = new Set(keys);
49
+ for (const key of entries.keys()) mergedKeys.add(key);
50
+ for (const key of patternEntries.keys()) mergedKeys.add(key);
51
+ return mergedKeys.size > 0 ? `{ ${Array.from(mergedKeys, (key) => `${JSON.stringify(key)}: ${formatValue(entries.get(key) || [], patternEntries.get(key) || [])}`).join(", ")} }` : "never";
40
52
  }
41
53
  function generateAutocomplete(ctx) {
42
54
  const autocomplete = ctx.engine.config.autocomplete;
55
+ const patterns = autocomplete.patterns ?? {
56
+ selectors: /* @__PURE__ */ new Set(),
57
+ styleItemStrings: /* @__PURE__ */ new Set(),
58
+ properties: /* @__PURE__ */ new Map(),
59
+ cssProperties: /* @__PURE__ */ new Map()
60
+ };
43
61
  const { layers } = ctx.engine.config;
44
62
  const layerNames = sortLayerNames(layers);
45
63
  return [
46
64
  "export type Autocomplete = DefineAutocomplete<{",
47
- ` Selector: ${formatUnionStringType([...autocomplete.selectors])}`,
48
- ` StyleItemString: ${formatUnionStringType([...autocomplete.styleItemStrings])}`,
49
- ` ExtraProperty: ${formatUnionStringType([...autocomplete.extraProperties])}`,
50
- ` ExtraCssProperty: ${formatUnionStringType([...autocomplete.extraCssProperties])}`,
51
- ` PropertiesValue: { ${Array.from(autocomplete.properties.entries(), ([k, v]) => `${JSON.stringify(k)}: ${v.length > 0 ? v.join(" | ") : "never"}`).join(", ")} }`,
52
- ` CssPropertiesValue: { ${Array.from(autocomplete.cssProperties.entries(), ([k, v]) => `${JSON.stringify(k)}: ${formatUnionStringType(v)}`).join(", ")} }`,
65
+ ` Selector: ${formatAutocompleteUnion(autocomplete.selectors, patterns.selectors)}`,
66
+ ` StyleItemString: ${formatAutocompleteUnion(autocomplete.styleItemStrings, patterns.styleItemStrings)}`,
67
+ ` PropertyValue: ${formatAutocompleteValueMap(autocomplete.extraProperties, autocomplete.properties, patterns.properties, (values, patterns) => formatUnionType([...values, ...patterns]))}`,
68
+ ` CSSPropertyValue: ${formatAutocompleteValueMap(autocomplete.extraCssProperties, autocomplete.cssProperties, patterns.cssProperties, (values, patterns) => formatAutocompleteUnion(values, patterns))}`,
53
69
  ` Layer: ${formatUnionStringType(layerNames)}`,
54
70
  "}>",
55
71
  ""
@@ -57,15 +73,10 @@ function generateAutocomplete(ctx) {
57
73
  }
58
74
  function generateStyleFn(ctx) {
59
75
  const { transformedFormat } = ctx;
60
- const lines = [
61
- "type StyleFn_Array = (...params: StyleItem[]) => string[]",
62
- "type StyleFn_String = (...params: StyleItem[]) => string",
63
- "type StyleFn_Inline = (...params: StyleItem[]) => void"
64
- ];
76
+ const lines = ["type StyleFn_Array = (...params: StyleItem[]) => string[]", "type StyleFn_String = (...params: StyleItem[]) => string"];
65
77
  if (transformedFormat === "array") lines.push("type StyleFn_Normal = StyleFn_Array");
66
78
  else if (transformedFormat === "string") lines.push("type StyleFn_Normal = StyleFn_String");
67
- else if (transformedFormat === "inline") lines.push("type StyleFn_Normal = StyleFn_Inline");
68
- lines.push("type StyleFn = StyleFn_Normal & {", " str: StyleFn_String", " arr: StyleFn_Array", " inl: StyleFn_Inline", "}", `type StyleFnWithPreview = PreviewOverloads<StyleFn_Normal>[\'fn\'] & {`, ` str: PreviewOverloads<StyleFn_String>[\'fn\']`, ` arr: PreviewOverloads<StyleFn_Array>[\'fn\']`, ` inl: PreviewOverloads<StyleFn_Inline>[\'fn\']`, "}", "");
79
+ lines.push("type StyleFn = StyleFn_Normal & {", " str: StyleFn_String", " arr: StyleFn_Array", "}", `type StyleFnWithPreview = PreviewOverloads<StyleFn_Normal>[\'fn\'] & {`, ` str: PreviewOverloads<StyleFn_String>[\'fn\']`, ` arr: PreviewOverloads<StyleFn_Array>[\'fn\']`, "}", "");
69
80
  return lines;
70
81
  }
71
82
  function generateGlobalDeclaration(ctx) {
@@ -132,7 +143,7 @@ async function generateOverloadContent(ctx) {
132
143
  } catch {}
133
144
  }
134
145
  return [
135
- "interface PreviewOverloads<StyleFn extends (StyleFn_Array | StyleFn_String | StyleFn_Inline)> {",
146
+ "interface PreviewOverloads<StyleFn extends (StyleFn_Array | StyleFn_String)> {",
136
147
  ...fnsLines,
137
148
  " /**",
138
149
  " * PikaCSS Preview",
@@ -153,7 +164,7 @@ async function generateTsCodegenContent(ctx) {
153
164
  " interface PikaAugment {",
154
165
  " Autocomplete: Autocomplete",
155
166
  " Selector: Autocomplete['Selector'] | CSSSelector",
156
- " CSSProperty: Autocomplete['ExtraCssProperty'] | CSSProperty",
167
+ " CSSProperty: ([Autocomplete['CSSPropertyValue']] extends [never] ? never : Extract<keyof Autocomplete['CSSPropertyValue'], string>) | CSSProperty",
157
168
  " Properties: Properties",
158
169
  " StyleDefinition: StyleDefinition",
159
170
  " StyleItem: StyleItem",
@@ -172,6 +183,36 @@ async function generateTsCodegenContent(ctx) {
172
183
 
173
184
  //#endregion
174
185
  //#region src/ctx.ts
186
+ function createConfigScaffoldContent({ currentPackageName, resolvedConfigPath, tsCodegenFilepath }) {
187
+ const relativeTsCodegenFilepath = tsCodegenFilepath == null ? null : `./${relative(dirname(resolvedConfigPath), tsCodegenFilepath)}`;
188
+ return [
189
+ ...relativeTsCodegenFilepath == null ? [] : [`/// <reference path="${relativeTsCodegenFilepath}" />`],
190
+ `import { defineEngineConfig } from '${currentPackageName}'`,
191
+ "",
192
+ "export default defineEngineConfig({",
193
+ " // Add your PikaCSS engine config here",
194
+ "})"
195
+ ].join("\n");
196
+ }
197
+ async function writeGeneratedFile(filepath, content) {
198
+ await mkdir(dirname(filepath), { recursive: true }).catch(() => {});
199
+ await writeFile(filepath, content);
200
+ }
201
+ async function evaluateConfigModule(resolvedConfigPath) {
202
+ log.info(`Using config file: ${resolvedConfigPath}`);
203
+ const { createJiti } = await import("jiti");
204
+ const jiti = createJiti(import.meta.url, { interopDefault: true });
205
+ const content = await readFile(resolvedConfigPath, "utf-8");
206
+ const config = (await jiti.evalModule(content, {
207
+ id: resolvedConfigPath,
208
+ forceTranspile: true
209
+ })).default;
210
+ return {
211
+ config: klona(config),
212
+ file: resolvedConfigPath,
213
+ content
214
+ };
215
+ }
175
216
  function usePaths({ cwd: _cwd, cssCodegen, tsCodegen }) {
176
217
  const cwd = signal(_cwd);
177
218
  return {
@@ -194,6 +235,20 @@ function useConfig({ cwd, tsCodegenFilepath, currentPackageName, autoCreateConfi
194
235
  for await (const entry of stream) return join(_cwd, entry);
195
236
  return null;
196
237
  }
238
+ async function ensureConfigPath(candidatePath) {
239
+ if (candidatePath != null) return candidatePath;
240
+ if (autoCreateConfig === false) {
241
+ log.warn("Config file not found and autoCreateConfig is false");
242
+ return null;
243
+ }
244
+ const resolvedConfigPath = specificConfigPath() ?? join(cwd(), "pika.config.js");
245
+ await writeGeneratedFile(resolvedConfigPath, createConfigScaffoldContent({
246
+ currentPackageName,
247
+ resolvedConfigPath,
248
+ tsCodegenFilepath: tsCodegenFilepath()
249
+ }));
250
+ return resolvedConfigPath;
251
+ }
197
252
  const inlineConfig = typeof configOrPath === "object" ? configOrPath : null;
198
253
  async function _loadConfig() {
199
254
  try {
@@ -206,43 +261,13 @@ function useConfig({ cwd, tsCodegenFilepath, currentPackageName, autoCreateConfi
206
261
  content: null
207
262
  };
208
263
  }
209
- let resolvedConfigPath = await findFirstExistingConfigPath();
210
- const _cwd = cwd();
211
- if (resolvedConfigPath == null) {
212
- if (autoCreateConfig === false) {
213
- log.warn("Config file not found and autoCreateConfig is false");
214
- return {
215
- config: null,
216
- file: null,
217
- content: null
218
- };
219
- }
220
- resolvedConfigPath = join(_cwd, specificConfigPath() ?? "pika.config.js");
221
- await mkdir(dirname(resolvedConfigPath), { recursive: true }).catch(() => {});
222
- const _tsCodegenFilepath = tsCodegenFilepath();
223
- const relativeTsCodegenFilepath = _tsCodegenFilepath == null ? null : `./${relative(dirname(resolvedConfigPath), _tsCodegenFilepath)}`;
224
- await writeFile(resolvedConfigPath, [
225
- ...relativeTsCodegenFilepath == null ? [] : [`/// <reference path="${relativeTsCodegenFilepath}" />`],
226
- `import { defineEngineConfig } from '${currentPackageName}'`,
227
- "",
228
- "export default defineEngineConfig({",
229
- " // Add your PikaCSS engine config here",
230
- "})"
231
- ].join("\n"));
232
- }
233
- log.info(`Using config file: ${resolvedConfigPath}`);
234
- const { createJiti } = await import("jiti");
235
- const jiti = createJiti(import.meta.url, { interopDefault: true });
236
- const content = await readFile(resolvedConfigPath, "utf-8");
237
- const config = (await jiti.evalModule(content, {
238
- id: resolvedConfigPath,
239
- forceTranspile: true
240
- })).default;
241
- return {
242
- config: klona(config),
243
- file: resolvedConfigPath,
244
- content
264
+ const resolvedConfigPath = await ensureConfigPath(await findFirstExistingConfigPath());
265
+ if (resolvedConfigPath == null) return {
266
+ config: null,
267
+ file: null,
268
+ content: null
245
269
  };
270
+ return await evaluateConfigModule(resolvedConfigPath);
246
271
  } catch (error) {
247
272
  log.error(`Failed to load config file: ${error.message}`, error);
248
273
  return {
@@ -309,6 +334,46 @@ function useTransform({ cwd, cssCodegenFilepath, tsCodegenFilepath, scan, fnName
309
334
  };
310
335
  }
311
336
  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
+ }
312
377
  function findFunctionCalls(code) {
313
378
  const RE = fnUtils.RE;
314
379
  const result = [];
@@ -334,8 +399,12 @@ function useTransform({ cwd, cssCodegenFilepath, tsCodegenFilepath, scan, fnName
334
399
  if (inString !== false) {
335
400
  if (char === inString) inString = false;
336
401
  else if (inString === "`" && char === "$" && code[end + 1] === "{") {
337
- end++;
338
- depth++;
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;
339
408
  }
340
409
  continue;
341
410
  }
@@ -395,7 +464,7 @@ function useTransform({ cwd, cssCodegenFilepath, tsCodegenFilepath, scan, fnName
395
464
  };
396
465
  usageList.push(usage);
397
466
  let transformedContent;
398
- if (fnUtils.isNormal(fnCall.fnName)) transformedContent = transformedFormat === "array" ? `[${names.map((n) => `'${n}'`).join(", ")}]` : transformedFormat === "string" ? `'${names.join(" ")}'` : names.join(" ");
467
+ if (fnUtils.isNormal(fnCall.fnName)) transformedContent = transformedFormat === "array" ? `[${names.map((n) => `'${n}'`).join(", ")}]` : `'${names.join(" ")}'`;
399
468
  else if (fnUtils.isForceString(fnCall.fnName)) transformedContent = `'${names.join(" ")}'`;
400
469
  else if (fnUtils.isForceArray(fnCall.fnName)) transformedContent = `[${names.map((n) => `'${n}'`).join(", ")}]`;
401
470
  else throw new Error(`Unexpected function name: ${fnCall.fnName}`);
@@ -514,18 +583,16 @@ function createCtx(options) {
514
583
  await ctx.setupPromise;
515
584
  const content = await ctx.getCssCodegenContent();
516
585
  if (content == null) return;
517
- await mkdir(dirname(ctx.cssCodegenFilepath), { recursive: true }).catch(() => {});
518
586
  log.debug(`Writing CSS code generation file: ${ctx.cssCodegenFilepath}`);
519
- await writeFile(ctx.cssCodegenFilepath, content);
587
+ await writeGeneratedFile(ctx.cssCodegenFilepath, content);
520
588
  },
521
589
  writeTsCodegenFile: async () => {
522
590
  await ctx.setupPromise;
523
591
  if (ctx.tsCodegenFilepath == null) return;
524
592
  const content = await ctx.getTsCodegenContent();
525
593
  if (content == null) return;
526
- await mkdir(dirname(ctx.tsCodegenFilepath), { recursive: true }).catch(() => {});
527
594
  log.debug(`Writing TypeScript code generation file: ${ctx.tsCodegenFilepath}`);
528
- await writeFile(ctx.tsCodegenFilepath, content);
595
+ await writeGeneratedFile(ctx.tsCodegenFilepath, content);
529
596
  },
530
597
  fullyCssCodegen: async () => {
531
598
  await ctx.setupPromise;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pikacss/integration",
3
3
  "type": "module",
4
- "version": "0.0.45",
4
+ "version": "0.0.47",
5
5
  "author": "DevilTea <ch19980814@gmail.com>",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -44,7 +44,7 @@
44
44
  "micromatch": "^4.0.8",
45
45
  "pathe": "^2.0.3",
46
46
  "perfect-debounce": "^2.1.0",
47
- "@pikacss/core": "0.0.45"
47
+ "@pikacss/core": "0.0.47"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/micromatch": "^4.0.10"