@plumeria/codemod 18.2.19 → 18.2.21

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.
@@ -1,8 +1,10 @@
1
1
  import type { Rule } from 'eslint';
2
2
  export interface ModuleMap {
3
3
  source: string;
4
+ target?: string;
4
5
  names: Record<string, string>;
5
6
  composes?: Record<string, string[]>;
7
+ functions?: Record<string, string[]>;
6
8
  }
7
9
  export interface AdoptStylesOptions {
8
10
  modules: Record<string, ModuleMap>;
@@ -1,6 +1,41 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.adoptStyles = void 0;
37
+ const path = __importStar(require("node:path"));
38
+ const resolve_1 = require("../resolve");
4
39
  const CORE = '@plumeria/core';
5
40
  const readOptions = (context) => {
6
41
  const raw = (context.options[0] ?? {});
@@ -22,6 +57,9 @@ exports.adoptStyles = {
22
57
  key: 'The generated key for "{{from}}" is "{{to}}".',
23
58
  prop: 'Styles are carried by the "{{prop}}" prop.',
24
59
  composes: '"{{key}}" composed other classes; pass them as an array.',
60
+ value: 'A style read as a value goes back through `css.use`.',
61
+ joined: 'A joined class list is written as an array.',
62
+ call: 'The custom properties are the arguments of a function style.',
25
63
  },
26
64
  schema: [
27
65
  {
@@ -39,9 +77,144 @@ exports.adoptStyles = {
39
77
  const bindings = new Map();
40
78
  let hasCore = false;
41
79
  let stylesTaken = false;
80
+ let coreLocal;
81
+ let readsValue = false;
82
+ let several = false;
83
+ const stylingPosition = (node) => {
84
+ let current = node;
85
+ while (current.parent &&
86
+ (current.parent.type === 'ArrayExpression' ||
87
+ current.parent.type === 'LogicalExpression' ||
88
+ current.parent.type === 'ConditionalExpression' ||
89
+ current.parent.type === 'JSXExpressionContainer'))
90
+ current = current.parent;
91
+ return (current.parent?.type === 'JSXAttribute' &&
92
+ (current.parent.name?.name === 'className' ||
93
+ current.parent.name?.name === styleProp));
94
+ };
95
+ const joinedElements = (node) => {
96
+ if (node?.type === 'TemplateLiteral' && node.expressions.length > 1) {
97
+ const spaced = node.quasis.every((quasi, index) => index === 0 || index === node.quasis.length - 1
98
+ ? quasi.value.cooked === ''
99
+ : quasi.value.cooked === ' ');
100
+ return spaced ? node.expressions : undefined;
101
+ }
102
+ if (node?.type !== 'CallExpression' ||
103
+ node.callee.type !== 'MemberExpression' ||
104
+ node.callee.property?.name !== 'join' ||
105
+ node.arguments.length !== 1 ||
106
+ node.arguments[0].value !== ' ')
107
+ return undefined;
108
+ const joined = node.callee.object;
109
+ if (joined?.type === 'ArrayExpression')
110
+ return joined.elements.filter(Boolean);
111
+ return joined?.type === 'CallExpression' &&
112
+ joined.callee.type === 'MemberExpression' &&
113
+ joined.callee.property?.name === 'filter' &&
114
+ joined.callee.object?.type === 'ArrayExpression'
115
+ ? joined.callee.object.elements.filter(Boolean)
116
+ : undefined;
117
+ };
118
+ const intrinsic = (element) => {
119
+ const name = element?.name;
120
+ return name?.type === 'JSXIdentifier' && /^[a-z]/.test(String(name.name));
121
+ };
122
+ const renamedText = (element) => {
123
+ const read = styleRead(element);
124
+ const binding = read?.object?.type === 'Identifier'
125
+ ? bindings.get(read.object.name)
126
+ : undefined;
127
+ if (!binding)
128
+ return context.sourceCode.getText(element);
129
+ const written = read.computed && read.property.type === 'Literal'
130
+ ? String(read.property.value)
131
+ : !read.computed && read.property.type === 'Identifier'
132
+ ? read.property.name
133
+ : undefined;
134
+ if (written === undefined)
135
+ return context.sourceCode.getText(element);
136
+ const access = `${binding.local}.${binding.entry.names[written] ?? written}`;
137
+ return element.type === 'LogicalExpression'
138
+ ? `${context.sourceCode.getText(element.left)} ${element.operator} ${access}`
139
+ : access;
140
+ };
141
+ const styleRead = (node) => node?.type === 'LogicalExpression'
142
+ ? styleRead(node.right)
143
+ : node?.type === 'MemberExpression'
144
+ ? node
145
+ : undefined;
146
+ const insideUse = (node) => {
147
+ let current = node;
148
+ while (current.parent &&
149
+ (current.parent.type === 'LogicalExpression' ||
150
+ current.parent.type === 'ConditionalExpression' ||
151
+ current.parent.type === 'ArrayExpression'))
152
+ current = current.parent;
153
+ return (current.parent?.type === 'CallExpression' &&
154
+ current.parent.callee?.type === 'MemberExpression' &&
155
+ current.parent.callee.property?.name === 'use');
156
+ };
42
157
  return {
43
158
  Program(program) {
44
159
  hasCore = program.body.some((n) => n.type === 'ImportDeclaration' && n.source.value === CORE);
160
+ const locals = new Set();
161
+ let seen = 0;
162
+ for (const statement of program.body) {
163
+ if (statement.type !== 'ImportDeclaration')
164
+ continue;
165
+ if (statement.source.value === CORE) {
166
+ for (const specifier of statement.specifiers)
167
+ if (specifier.type === 'ImportNamespaceSpecifier' ||
168
+ (specifier.type === 'ImportSpecifier' &&
169
+ (specifier.imported.name ?? specifier.imported.value) ===
170
+ 'css'))
171
+ coreLocal = specifier.local.name;
172
+ continue;
173
+ }
174
+ const target = (0, resolve_1.resolveSourcePath)(String(statement.source.value), context.filename);
175
+ if (!target || !modules[target])
176
+ continue;
177
+ for (const specifier of statement.specifiers)
178
+ if (specifier.type === 'ImportDefaultSpecifier') {
179
+ locals.add(specifier.local.name);
180
+ seen += 1;
181
+ }
182
+ }
183
+ several = seen > 1;
184
+ const visit = (node, styling) => {
185
+ if (!node || typeof node !== 'object')
186
+ return;
187
+ if (Array.isArray(node)) {
188
+ for (const child of node)
189
+ visit(child, styling);
190
+ return;
191
+ }
192
+ if (typeof node.type !== 'string')
193
+ return;
194
+ const inside = styling ||
195
+ (node.type === 'JSXAttribute' &&
196
+ (node.name?.name === 'className' ||
197
+ node.name?.name === styleProp));
198
+ if (!inside &&
199
+ node.type === 'MemberExpression' &&
200
+ locals.has(node.object?.name))
201
+ readsValue = true;
202
+ if (node.type === 'JSXAttribute' &&
203
+ node.name?.name === 'className' &&
204
+ node.parent?.name?.type === 'JSXIdentifier' &&
205
+ /^[A-Z]/.test(String(node.parent.name.name)) &&
206
+ node.value?.type === 'JSXExpressionContainer' &&
207
+ joinedElements(node.value.expression))
208
+ readsValue = true;
209
+ for (const key of Object.keys(node)) {
210
+ if (key === 'parent' || key === 'loc' || key === 'range')
211
+ continue;
212
+ visit(node[key], inside);
213
+ }
214
+ };
215
+ visit(program.body, false);
216
+ if (readsValue && !coreLocal)
217
+ coreLocal = 'css';
45
218
  stylesTaken = program.body.some((n) => {
46
219
  if (n.type === 'VariableDeclaration') {
47
220
  return n.declarations.some((d) => d.id?.name === 'styles');
@@ -56,28 +229,55 @@ exports.adoptStyles = {
56
229
  },
57
230
  ImportDeclaration(node) {
58
231
  const specifier = String(node.source.value);
59
- const entry = modules[specifier] ??
60
- Object.entries(modules).find(([key]) => key.includes('/') === false && specifier.endsWith(`/${key}`))?.[1];
61
- if (!entry)
232
+ const resolved = (0, resolve_1.resolveSourcePath)(specifier, context.filename);
233
+ const named = Object.entries(modules).filter(([key]) => key === specifier ||
234
+ path.basename(key) === path.basename(specifier));
235
+ const entry = (resolved ? modules[resolved] : undefined) ??
236
+ (named.length === 1 ? named[0][1] : undefined);
237
+ if (!entry) {
238
+ const adopted = resolved &&
239
+ Object.values(modules).find((candidate) => candidate.target === resolved);
240
+ if (!adopted)
241
+ return;
242
+ const already = node.specifiers.find((specifier) => specifier.type === 'ImportSpecifier' &&
243
+ (specifier.imported.name ?? specifier.imported.value) ===
244
+ 'styles');
245
+ if (already)
246
+ bindings.set(already.local.name, {
247
+ entry: adopted,
248
+ local: already.local.name,
249
+ });
62
250
  return;
251
+ }
252
+ const from = entry.target && path.isAbsolute(context.filename)
253
+ ? (() => {
254
+ const relative = path
255
+ .relative(path.dirname(context.filename), entry.target)
256
+ .split(path.sep)
257
+ .join('/')
258
+ .replace(/\.ts$/, '');
259
+ return relative.startsWith('.') ? relative : `./${relative}`;
260
+ })()
261
+ : entry.source;
63
262
  const local = node.specifiers.find((s) => s.type === 'ImportDefaultSpecifier');
64
263
  if (!local)
65
264
  return;
66
265
  const name = local.local.name;
67
- const renamed = stylesTaken && name !== 'styles' ? name : 'styles';
266
+ const renamed = several || (stylesTaken && name !== 'styles') ? name : 'styles';
68
267
  bindings.set(name, { entry, local: renamed });
69
268
  const imported = renamed === 'styles'
70
- ? `import { styles } from '${entry.source}';`
71
- : `import { styles as ${renamed} } from '${entry.source}';`;
72
- const replacement = hasCore
73
- ? imported
74
- : `import '${CORE}';\n${imported}`;
269
+ ? `import { styles } from '${from}';`
270
+ : `import { styles as ${renamed} } from '${from}';`;
271
+ const core = coreLocal
272
+ ? `import * as ${coreLocal} from '${CORE}';`
273
+ : `import '${CORE}';`;
274
+ const replacement = hasCore ? imported : `${core}\n${imported}`;
75
275
  if (!hasCore)
76
276
  hasCore = true;
77
277
  context.report({
78
278
  node,
79
279
  messageId: 'source',
80
- data: { source: entry.source },
280
+ data: { source: from },
81
281
  fix: (fixer) => fixer.replaceText(node, replacement),
82
282
  });
83
283
  },
@@ -96,6 +296,14 @@ exports.adoptStyles = {
96
296
  return;
97
297
  const key = binding.entry.names[written] ?? written;
98
298
  const local = binding.local;
299
+ if (coreLocal && !stylingPosition(node) && !insideUse(node)) {
300
+ context.report({
301
+ node,
302
+ messageId: 'value',
303
+ fix: (fixer) => fixer.replaceText(node, `${coreLocal}.use(${local}.${key})`),
304
+ });
305
+ return;
306
+ }
99
307
  if (key === written && local === node.object.name)
100
308
  return;
101
309
  context.report({
@@ -105,6 +313,82 @@ exports.adoptStyles = {
105
313
  fix: (fixer) => fixer.replaceText(node, `${local}.${key}`),
106
314
  });
107
315
  },
316
+ [`JSXAttribute[name.name='${styleProp}']`](node) {
317
+ if (node.value?.type !== 'JSXExpressionContainer')
318
+ return;
319
+ const style = node.parent.attributes.find((attribute) => attribute.type === 'JSXAttribute' &&
320
+ attribute.name?.name === 'style' &&
321
+ attribute.value?.type === 'JSXExpressionContainer' &&
322
+ attribute.value.expression.type === 'ObjectExpression');
323
+ if (!style)
324
+ return;
325
+ const properties = style.value.expression.properties;
326
+ const named = new Map();
327
+ for (const property of properties) {
328
+ if (property.type !== 'Property' || !property.computed)
329
+ continue;
330
+ const key = property.key.type === 'TSAsExpression'
331
+ ? property.key.expression
332
+ : property.key;
333
+ if (key.type === 'Literal' && typeof key.value === 'string')
334
+ named.set(key.value, property);
335
+ }
336
+ if (named.size === 0)
337
+ return;
338
+ const consumed = new Set();
339
+ const call = (read) => {
340
+ const binding = read?.type === 'MemberExpression' &&
341
+ read.object.type === 'Identifier'
342
+ ? bindings.get(read.object.name)
343
+ : undefined;
344
+ if (!binding)
345
+ return undefined;
346
+ const written = !read.computed && read.property.type === 'Identifier'
347
+ ? read.property.name
348
+ : read.computed && read.property.type === 'Literal'
349
+ ? String(read.property.value)
350
+ : undefined;
351
+ const key = written && (binding.entry.names[written] ?? written);
352
+ const variables = key ? binding.entry.functions?.[key] : undefined;
353
+ if (!variables || variables.some((v) => !named.has(v)))
354
+ return undefined;
355
+ for (const variable of variables)
356
+ consumed.add(named.get(variable));
357
+ return `${binding.local}.${key}(${variables
358
+ .map((variable) => context.sourceCode.getText(named.get(variable).value))
359
+ .join(', ')})`;
360
+ };
361
+ const expression = node.value.expression;
362
+ const elements = expression.type === 'ArrayExpression'
363
+ ? expression.elements.filter(Boolean)
364
+ : [expression];
365
+ const texts = elements.map((element) => {
366
+ const called = call(styleRead(element));
367
+ if (!called)
368
+ return context.sourceCode.getText(element);
369
+ return element.type === 'LogicalExpression'
370
+ ? `${context.sourceCode.getText(element.left)} ${element.operator} ${called}`
371
+ : called;
372
+ });
373
+ if (consumed.size === 0)
374
+ return;
375
+ const kept = properties.filter((property) => !consumed.has(property));
376
+ const replacement = expression.type === 'ArrayExpression'
377
+ ? `[${texts.join(', ')}]`
378
+ : texts[0];
379
+ context.report({
380
+ node,
381
+ messageId: 'call',
382
+ fix: (fixer) => [
383
+ fixer.replaceText(expression, replacement),
384
+ kept.length === 0
385
+ ? fixer.removeRange([style.range[0] - 1, style.range[1]])
386
+ : fixer.replaceText(style.value.expression, `{ ${kept
387
+ .map((property) => context.sourceCode.getText(property))
388
+ .join(', ')} }`),
389
+ ],
390
+ });
391
+ },
108
392
  JSXAttribute(node) {
109
393
  if (node.name.type !== 'JSXIdentifier')
110
394
  return;
@@ -114,6 +398,32 @@ exports.adoptStyles = {
114
398
  if (!value || value.type !== 'JSXExpressionContainer')
115
399
  return;
116
400
  const expression = value.expression;
401
+ const joined = joinedElements(expression);
402
+ if (joined) {
403
+ if (!joined.some((element) => bindings.has(styleRead(element)?.object?.name)))
404
+ return;
405
+ const list = joined.map(renamedText).join(', ');
406
+ if (!intrinsic(node.parent) && coreLocal) {
407
+ context.report({
408
+ node: expression,
409
+ messageId: 'value',
410
+ fix: (fixer) => fixer.replaceText(expression, `${coreLocal}.use(${list})`),
411
+ });
412
+ return;
413
+ }
414
+ context.report({
415
+ node: node.name,
416
+ messageId: 'prop',
417
+ data: { prop: styleProp },
418
+ fix: (fixer) => fixer.replaceText(node.name, styleProp),
419
+ });
420
+ context.report({
421
+ node: expression,
422
+ messageId: 'joined',
423
+ fix: (fixer) => fixer.replaceText(expression, `[${list}]`),
424
+ });
425
+ return;
426
+ }
117
427
  const root = expression.type === 'MemberExpression' &&
118
428
  expression.object.type === 'Identifier'
119
429
  ? expression.object.name
@@ -9,9 +9,13 @@ export interface Converted {
9
9
  code: string;
10
10
  names: Record<string, string>;
11
11
  composes: Record<string, string[]>;
12
+ functions: Record<string, string[]>;
12
13
  reports: Report[];
13
14
  }
14
- type Value = string | number;
15
+ interface Raw {
16
+ raw: string;
17
+ }
18
+ type Value = string | number | Raw;
15
19
  export declare const toProperty: (name: string) => string;
16
20
  export declare const toKey: (className: string) => string;
17
21
  export declare const toValue: (raw: string) => Value;
@@ -41,6 +41,7 @@ const nest = (node, key) => {
41
41
  node.nested.push([key, child]);
42
42
  return child;
43
43
  };
44
+ const isSpecificityBoost = (node) => node.type === 'pseudo' && /^:not\(#[\\_].*\)$/.test(node.toString());
44
45
  const atRuleKeys = (rule) => {
45
46
  const keys = [];
46
47
  let parent = rule.parent;
@@ -57,7 +58,7 @@ const readSelector = (selector) => {
57
58
  hint: 'Only a local class, or one local class under another, is converted.',
58
59
  };
59
60
  (0, postcss_selector_parser_1.default)((root) => {
60
- const nodes = root.first.nodes.filter((n) => n.type !== 'string');
61
+ const nodes = root.first.nodes.filter((n) => n.type !== 'string' && !isSpecificityBoost(n));
61
62
  const parts = [
62
63
  { pseudos: [] },
63
64
  ];
@@ -148,7 +149,16 @@ const serialise = (node, indent) => {
148
149
  lines.push(`${indent}...${marker},`);
149
150
  for (const [prop, value] of node.decls) {
150
151
  const key = /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(prop) ? prop : `'${prop}'`;
151
- lines.push(`${indent}${key}: ${typeof value === 'number' ? value : `'${value}'`},`);
152
+ if (typeof value === 'object' && value.raw === prop) {
153
+ lines.push(`${indent}${prop},`);
154
+ continue;
155
+ }
156
+ const written = typeof value === 'number'
157
+ ? value
158
+ : typeof value === 'object'
159
+ ? value.raw
160
+ : `'${value}'`;
161
+ lines.push(`${indent}${key}: ${written},`);
152
162
  }
153
163
  for (const [key, child] of node.nested) {
154
164
  const written = key.startsWith('css.extended(') ? `[${key}]` : `'${key}'`;
@@ -224,12 +234,56 @@ function convertStylesheet(css) {
224
234
  composes[target.key] = (composes[target.key] ?? []).concat(name.split(/\s+/).map(exports.toKey));
225
235
  return;
226
236
  }
227
- node.decls.push([(0, exports.toProperty)(child.prop), (0, exports.toValue)(child.value)]);
237
+ const property = (0, exports.toProperty)(child.prop);
238
+ const held = node.decls.findIndex(([name]) => name === property);
239
+ if (held !== -1)
240
+ node.decls.splice(held, 1);
241
+ node.decls.push([property, (0, exports.toValue)(child.value)]);
228
242
  });
229
243
  }
230
244
  });
245
+ const declared = new Set();
246
+ root.walkDecls((decl) => {
247
+ if (decl.prop.startsWith('--'))
248
+ declared.add(decl.prop);
249
+ });
250
+ const functions = {};
251
+ const VARIABLE = /^var\((--[A-Za-z0-9_-]+)\)$/;
252
+ const HASHED = /^x[0-9a-z]{7}$/;
253
+ const kebab = (name) => name.replace(/([A-Z])/g, '-$1').toLowerCase();
254
+ const parameterise = (node, key, variables) => {
255
+ const owned = new RegExp(`^--[A-Za-z0-9_-]+-${kebab(key)}-([a-z0-9-]+)$`);
256
+ node.decls = node.decls.map(([prop, value]) => {
257
+ const match = typeof value === 'string' ? VARIABLE.exec(value.trim()) : null;
258
+ const variable = match?.[1];
259
+ if (!variable || declared.has(variable))
260
+ return [prop, value];
261
+ const parameter = owned.exec(variable)?.[1];
262
+ if (!parameter || HASHED.test(variable.slice(2).split('-')[0]))
263
+ return [prop, value];
264
+ if (!variables.includes(variable))
265
+ variables.push(variable);
266
+ return [prop, { raw: (0, exports.toKey)(parameter) }];
267
+ });
268
+ for (const [, child] of node.nested)
269
+ parameterise(child, key, variables);
270
+ };
271
+ for (const [key, node] of keys) {
272
+ const variables = [];
273
+ parameterise(node, key, variables);
274
+ if (variables.length > 0)
275
+ functions[key] = variables;
276
+ }
231
277
  const body = [...keys.entries()]
232
- .map(([key, node]) => ` ${key}: {\n${serialise(node, ' ')}\n },`)
278
+ .map(([key, node]) => {
279
+ const variables = functions[key];
280
+ if (!variables)
281
+ return ` ${key}: {\n${serialise(node, ' ')}\n },`;
282
+ const params = variables
283
+ .map((variable) => `${(0, exports.toKey)(variable.split('-').pop())}: string | number`)
284
+ .join(', ');
285
+ return ` ${key}: (${params}) => ({\n${serialise(node, ' ')}\n }),`;
286
+ })
233
287
  .join('\n');
234
288
  const code = `import * as css from '@plumeria/core';
235
289
 
@@ -237,5 +291,5 @@ export const styles = css.create({
237
291
  ${body}
238
292
  });
239
293
  `;
240
- return { code, names, composes, reports };
294
+ return { code, names, composes, functions, reports };
241
295
  }
@@ -14,9 +14,25 @@ export interface PlumeriaModule {
14
14
  variables: string[];
15
15
  }>;
16
16
  aliases: Record<string, Record<string, string>>;
17
+ merges: Record<string, string>;
18
+ overrides: Record<string, Record<number, string>>;
17
19
  definitionOnly: boolean;
18
20
  reports: PlumeriaReport[];
19
21
  }
22
+ export interface PlumeriaComposition {
23
+ parts: {
24
+ binding: string;
25
+ key: string;
26
+ }[];
27
+ slots: {
28
+ binding: string;
29
+ key: string;
30
+ }[][];
31
+ file: string;
32
+ line: number;
33
+ column: number;
34
+ }
35
+ export declare const compositionSignature: (classNames: string[]) => string;
20
36
  export interface ThemeExtraction {
21
37
  bindings: Record<string, Record<string, string>>;
22
38
  globalCss: string;
@@ -42,4 +58,4 @@ export declare const __private: {
42
58
  export declare function extractPlumeriaThemes(source: string): ThemeExtraction;
43
59
  export declare function extractPlumeriaStatics(source: string, externalValues?: Record<string, unknown>): StaticExtraction;
44
60
  export declare function extractPlumeriaAnimations(source: string, externalValues?: Record<string, unknown>): AnimationExtraction;
45
- export declare function convertPlumeriaModule(source: string, externalValues?: Record<string, unknown>): PlumeriaModule | null;
61
+ export declare function convertPlumeriaModule(source: string, externalValues?: Record<string, unknown>, compositions?: PlumeriaComposition[]): PlumeriaModule | null;