@htmlplus/element 3.3.0 → 3.3.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.
@@ -1,99 +1,29 @@
1
- import generator from '@babel/generator';
2
1
  import t from '@babel/types';
3
- import traverse from '@babel/traverse';
4
2
  import { parse as parse$1 } from '@babel/parser';
5
3
  import fs from 'fs-extra';
6
4
  import { glob } from 'glob';
7
5
  import template from '@babel/template';
8
6
  import { pascalCase, kebabCase, camelCase, capitalCase } from 'change-case';
9
- import ora from 'ora';
10
7
  import path, { join, resolve, dirname } from 'node:path';
11
- import { KEY, COMMENT_AUTO_ADDED, DECORATOR_PROPERTY, STATIC_TAG, UTILS_STYLES_LOCAL, UTILS_PATH, ELEMENT_HOST_NAME, UTILS_HTML_LOCAL, DECORATOR_PROPERTY_TYPE, TYPE_OBJECT, TYPE_NULL, TYPE_ARRAY, TYPE_STRING, TYPE_ENUM, TYPE_NUMBER, TYPE_DATE, TYPE_BOOLEAN, UTILS_ATTRIBUTES_LOCAL, UTILS_STYLES_IMPORTED, UTILS_ATTRIBUTES_IMPORTED, UTILS_HTML_IMPORTED, DECORATOR_CSS_VARIABLE, DECORATOR_EVENT, DECORATOR_METHOD, DECORATOR_STATE, STATIC_STYLE, STYLE_IMPORTED, PACKAGE_NAME, DECORATOR_ELEMENT } from './constants.js';
12
-
13
- const logger = ora({
14
- color: 'yellow'
15
- });
16
- const log = (message, persist) => {
17
- const content = `${new Date().toLocaleTimeString()} [${KEY}] ${message}`;
18
- const log = logger.start(content);
19
- if (!persist)
20
- return;
21
- log.succeed();
22
- };
23
- const transformer = (...plugins) => {
24
- let global = {
25
- contexts: []
26
- };
27
- const start = async () => {
28
- log(`Started.`, true);
29
- log(`${plugins.length} plugins detected.`, true);
30
- log(`Plugins are starting.`, true);
31
- for (const plugin of plugins) {
32
- if (!plugin.start)
33
- continue;
34
- log(`Plugin '${plugin.name}' is starting.`);
35
- global = (await plugin.start(global)) || global;
36
- log(`Plugin '${plugin.name}' started successfully.`);
37
- }
38
- log(`Plugins have been successfully started.`, true);
39
- };
40
- const run = async (filePath) => {
41
- path.join(filePath).split(path.sep).pop();
42
- let context = {
43
- filePath
44
- };
45
- const parsed = path.parse(filePath);
46
- for (const plugin of plugins) {
47
- if (!plugin.run)
48
- continue;
49
- const source = path.join(parsed.dir).split(path.sep).slice(-2).concat(parsed.base).join('/');
50
- log(`Plugin '${plugin.name}' is executing on '${source}' file.`);
51
- try {
52
- context = (await plugin.run(context, global)) || context;
53
- }
54
- catch (error) {
55
- log(`Error in '${plugin.name}' plugin on '${source}' file.\n`, true);
56
- throw error;
57
- }
58
- global.contexts = global.contexts
59
- .filter((current) => {
60
- return current.filePath != context.filePath;
61
- })
62
- .concat(context);
63
- log(`Plugin '${plugin.name}' executed successfully on '${source}' file.`);
64
- }
65
- logger.stop();
66
- return context;
67
- };
68
- const finish = async () => {
69
- log(`Plugins are finishing.`, true);
70
- for (const plugin of plugins) {
71
- if (!plugin.finish)
72
- continue;
73
- log(`Plugin '${plugin.name}' is finishing.`);
74
- global = (await plugin.finish(global)) || global;
75
- log(`Plugin '${plugin.name}' finished successfully.`);
76
- }
77
- log(`Plugins have been successfully finished.`, true);
78
- log(`Finished.`, true);
79
- };
80
- return { global, start, run, finish };
81
- };
8
+ import { COMMENT_AUTO_ADDED, DECORATOR_PROPERTY, STATIC_TAG, DECORATOR_PROPERTY_TYPE, UTILS_STYLES_IMPORTED, UTILS_STYLES_LOCAL, UTILS_PATH, UTILS_HTML_IMPORTED, UTILS_HTML_LOCAL, ELEMENT_HOST_NAME, TYPE_OBJECT, TYPE_NULL, TYPE_ARRAY, TYPE_STRING, TYPE_ENUM, TYPE_NUMBER, TYPE_DATE, TYPE_BOOLEAN, UTILS_ATTRIBUTES_IMPORTED, UTILS_ATTRIBUTES_LOCAL, DECORATOR_CSS_VARIABLE, DECORATOR_EVENT, DECORATOR_METHOD, DECORATOR_STATE, STATIC_STYLE, STYLE_IMPORTED, PACKAGE_NAME, DECORATOR_ELEMENT, KEY } from './constants.js';
9
+ import core from '@babel/traverse';
10
+ import core$1 from '@babel/generator';
11
+ import ora from 'ora';
82
12
 
83
13
  const ASSETS_OPTIONS = {
84
14
  destination(context) {
85
- return path.join('dist', 'assets', context.fileName);
15
+ return path.join('dist', 'assets', context.fileName || '');
86
16
  },
87
17
  source(context) {
88
- return path.join(context.directoryPath, 'assets');
18
+ return path.join(context.directoryPath || '', 'assets');
89
19
  },
90
20
  json(context) {
91
- return path.join('dist', 'assets', context.fileName + '.json');
21
+ return path.join('dist', 'assets', `${context.fileName || ''}.json`);
92
22
  }
93
23
  };
94
- const assets = (options) => {
24
+ const assets = (userOptions) => {
95
25
  const name = 'assets';
96
- options = Object.assign({}, ASSETS_OPTIONS, options);
26
+ const options = Object.assign({}, ASSETS_OPTIONS, userOptions);
97
27
  const finish = (global) => {
98
28
  for (const context of global.contexts) {
99
29
  context.assetsDestination = options.destination(context);
@@ -115,16 +45,16 @@ const assets = (options) => {
115
45
  };
116
46
 
117
47
  const COPY_OPTIONS = {
118
- at: 'start'
48
+ at: 'start',
49
+ transformer: (content) => content
119
50
  };
120
- const copy = (options) => {
51
+ const copy = (userOptions) => {
121
52
  const name = 'copy';
122
- options = Object.assign({}, COPY_OPTIONS, options);
53
+ const options = Object.assign({}, COPY_OPTIONS, userOptions);
123
54
  const copy = (caller) => {
124
- if (options.at != caller)
55
+ if (options.at !== caller)
125
56
  return;
126
- let content;
127
- content = fs.readFileSync(options.source, 'utf8');
57
+ let content = fs.readFileSync(options.source, 'utf8');
128
58
  if (options.transformer)
129
59
  content = options.transformer(content);
130
60
  fs.ensureDirSync(path.dirname(options.destination));
@@ -142,11 +72,10 @@ const copy = (options) => {
142
72
  return { name, start, run, finish };
143
73
  };
144
74
 
145
- // TODO: options type
146
- const visitor = (ast, options) => {
147
- (traverse.default || traverse)(ast, options);
148
- };
75
+ const traverse = (core.default || core);
76
+ const visitor = traverse;
149
77
 
78
+ // biome-ignore-all lint: TODO
150
79
  function addDependency(path, source, local, imported, comment) {
151
80
  const isDefault = local && !imported;
152
81
  const isImport = local && imported;
@@ -158,7 +87,7 @@ function addDependency(path, source, local, imported, comment) {
158
87
  file = file.node || file;
159
88
  visitor(file, {
160
89
  ImportDeclaration(path) {
161
- if (path.node.source.value != source)
90
+ if (path.node.source.value !== source)
162
91
  return;
163
92
  declaration = path.node;
164
93
  }
@@ -169,10 +98,10 @@ function addDependency(path, source, local, imported, comment) {
169
98
  };
170
99
  let specifier = declaration?.specifiers.find((specifier) => {
171
100
  if (isDefault) {
172
- return specifier.type == 'ImportDefaultSpecifier';
101
+ return specifier.type === 'ImportDefaultSpecifier';
173
102
  }
174
103
  else if (isImport) {
175
- return specifier.imported?.name == imported;
104
+ return specifier.imported?.name === imported;
176
105
  }
177
106
  });
178
107
  if (specifier)
@@ -211,10 +140,11 @@ function addDependency(path, source, local, imported, comment) {
211
140
 
212
141
  const extractAttribute = (property) => {
213
142
  try {
143
+ // biome-ignore lint: Keep using `any` type because of complexity
214
144
  return property.decorators
215
- .find((decorator) => decorator.expression.callee.name == DECORATOR_PROPERTY)
216
- .expression.arguments[0].properties.find((property) => property.key.name == 'attribute').value
217
- .value;
145
+ .find((decorator) => decorator.expression.callee.name === DECORATOR_PROPERTY)
146
+ .expression.arguments.at(0)
147
+ .properties.find((property) => property.key.name === 'attribute').value.value;
218
148
  }
219
149
  catch { }
220
150
  };
@@ -225,12 +155,12 @@ const extractFromComment = (node, whitelist) => {
225
155
  description: ''
226
156
  };
227
157
  const lines = node.leadingComments
228
- ?.map((comment) => {
229
- if (comment.type == 'CommentLine')
158
+ ?.flatMap((comment) => {
159
+ if (comment.type === 'CommentLine') {
230
160
  return comment.value;
161
+ }
231
162
  return comment.value.split('\n');
232
163
  })
233
- ?.flat()
234
164
  ?.map((line) => line.trim().replace(/^\*/, '').trim())
235
165
  ?.filter((line) => line.trim());
236
166
  for (const line of lines || []) {
@@ -240,7 +170,7 @@ const extractFromComment = (node, whitelist) => {
240
170
  }
241
171
  if (!normalized.length)
242
172
  normalized.push('');
243
- normalized[normalized.length - 1] += ' ' + line;
173
+ normalized[normalized.length - 1] += ` ${line}`;
244
174
  }
245
175
  for (const line of normalized) {
246
176
  if (!line.startsWith('@')) {
@@ -255,11 +185,14 @@ const extractFromComment = (node, whitelist) => {
255
185
  const type = groups[2]?.trim().slice(1, -1);
256
186
  const name = groups[3]?.trim();
257
187
  const description = groups[4]?.trim();
188
+ // TODO
189
+ // const [, tag, type, name, description] = groups.map((g) => g?.trim() || '');
258
190
  if (name && description) {
259
- const key = tag + 's';
191
+ const key = `${tag}s`;
260
192
  if (whitelist && !whitelist.includes(key))
261
193
  continue;
262
- (result[key] ||= []).push({ name, type, description });
194
+ result[key] ||= [];
195
+ result[key].push({ name, type, description });
263
196
  }
264
197
  else {
265
198
  const key = tag;
@@ -272,18 +205,30 @@ const extractFromComment = (node, whitelist) => {
272
205
  };
273
206
 
274
207
  const getInitializer = (node) => {
208
+ // biome-ignore lint: Keep using `any` type because of complexity
275
209
  return node?.extra?.raw || node?.['value'];
276
210
  };
277
211
 
212
+ const getTypeReferenceName = (ref) => {
213
+ switch (ref.typeName.type) {
214
+ case 'Identifier':
215
+ return ref.typeName.name;
216
+ default:
217
+ return undefined;
218
+ }
219
+ };
278
220
  const getType = (directory, file, node) => {
279
221
  if (!node)
280
222
  return node;
281
- if (node.type != 'TSTypeReference')
223
+ if (node.type !== 'TSTypeReference')
282
224
  return node;
283
225
  let result;
226
+ const typeName = getTypeReferenceName(node);
227
+ if (!typeName)
228
+ return node;
284
229
  visitor(file, {
285
230
  ClassDeclaration(path) {
286
- if (path.node.id.name != node.typeName['name'])
231
+ if (path.node.id?.name !== typeName)
287
232
  return;
288
233
  result = path.node;
289
234
  path.stop();
@@ -291,31 +236,25 @@ const getType = (directory, file, node) => {
291
236
  ImportDeclaration(path) {
292
237
  for (const specifier of path.node.specifiers) {
293
238
  const alias = specifier.local.name;
294
- if (alias != node.typeName['name'])
239
+ if (alias !== typeName)
295
240
  continue;
296
- switch (specifier.type) {
297
- case 'ImportNamespaceSpecifier':
298
- break;
299
- case 'ImportDefaultSpecifier':
300
- break;
301
- case 'ImportSpecifier':
302
- specifier.imported.name;
303
- break;
304
- }
305
241
  try {
306
242
  const reference = glob
307
243
  .sync(['.ts*', '/index.ts*'].map((key) => {
308
244
  return join(directory, path.node.source.value).replace(/\\/g, '/') + key;
309
245
  }))
310
- .find((reference) => fs.existsSync(reference));
246
+ .find((reference) => reference && fs.existsSync(reference));
247
+ if (!reference)
248
+ continue;
311
249
  const content = fs.readFileSync(reference, 'utf8');
312
- const filePath = resolve(directory, path.node.source.value + '.ts');
313
- path.$ast ||= parse$1(content, {
250
+ const filePath = resolve(directory, `${path.node.source.value}.ts`);
251
+ const pathWithAst = path;
252
+ pathWithAst.$ast ||= parse$1(content, {
314
253
  allowImportExportEverywhere: true,
315
254
  plugins: ['typescript'],
316
255
  ranges: false
317
256
  });
318
- result = getType(dirname(filePath), path.$ast, node);
257
+ result = getType(dirname(filePath), pathWithAst.$ast, node);
319
258
  }
320
259
  catch { }
321
260
  path.stop();
@@ -323,32 +262,35 @@ const getType = (directory, file, node) => {
323
262
  }
324
263
  },
325
264
  TSInterfaceDeclaration(path) {
326
- if (path.node.id.name != node.typeName['name'])
265
+ if (path.node.id.name !== typeName)
327
266
  return;
328
267
  result = path.node;
329
268
  path.stop();
330
269
  },
331
270
  TSTypeAliasDeclaration(path) {
332
- if (path.node.id.name != node.typeName['name'])
271
+ if (path.node.id.name !== typeName)
333
272
  return;
334
- result = path.node.typeAnnotation;
335
- switch (result.type) {
336
- case 'TSUnionType':
273
+ const typeAnnotation = path.node.typeAnnotation;
274
+ switch (typeAnnotation.type) {
275
+ case 'TSUnionType': {
337
276
  const types = [];
338
- for (const prev of result.types) {
277
+ for (const prev of typeAnnotation.types) {
339
278
  const next = getType(directory, file, prev);
340
- if (next.type == 'TSUnionType') {
341
- next.types.forEach((type) => types.push(type));
279
+ if (next.type === 'TSUnionType') {
280
+ types.push(...next.types);
342
281
  }
343
282
  else {
344
283
  types.push(next);
345
284
  }
346
285
  }
347
- result.types = types;
286
+ typeAnnotation.types = types;
287
+ result = typeAnnotation;
348
288
  break;
349
- default:
350
- result = getType(directory, file, result);
289
+ }
290
+ default: {
291
+ result = getType(directory, file, typeAnnotation);
351
292
  break;
293
+ }
352
294
  }
353
295
  path.stop();
354
296
  }
@@ -359,24 +301,17 @@ const getType = (directory, file, node) => {
359
301
  const getTypeReference = (file, node) => {
360
302
  if (!node)
361
303
  return;
362
- if (node.type != 'TSTypeReference')
304
+ if (node.type !== 'TSTypeReference')
363
305
  return;
364
306
  let result;
365
307
  visitor(file, {
366
308
  ImportDeclaration(path) {
367
309
  for (const specifier of path.node.specifiers) {
368
310
  const alias = specifier.local.name;
369
- if (alias != node.typeName['name'])
311
+ if (node.typeName.type !== 'Identifier')
312
+ continue;
313
+ if (alias !== node.typeName.name)
370
314
  continue;
371
- switch (specifier.type) {
372
- case 'ImportNamespaceSpecifier':
373
- break;
374
- case 'ImportDefaultSpecifier':
375
- break;
376
- case 'ImportSpecifier':
377
- specifier.imported.name;
378
- break;
379
- }
380
315
  result = path.node.source.value;
381
316
  path.stop();
382
317
  break;
@@ -387,38 +322,51 @@ const getTypeReference = (file, node) => {
387
322
  };
388
323
 
389
324
  const hasDecorator = (node, name) => {
325
+ if ('decorators' in node === false)
326
+ return false;
390
327
  if (!node.decorators)
391
328
  return false;
392
- return !!node.decorators.some((decorator) => decorator.expression.callee?.name == name);
329
+ for (const decorator of node.decorators) {
330
+ const expression = decorator.expression;
331
+ if (!t.isCallExpression(expression))
332
+ continue;
333
+ if (!t.isIdentifier(expression.callee))
334
+ continue;
335
+ if (expression.callee.name === name) {
336
+ return true;
337
+ }
338
+ }
339
+ return false;
393
340
  };
394
341
 
395
- // TODO: add options
342
+ const generator = (core$1.default || core$1);
396
343
  const print = (ast) => {
397
- // TODO: the `ast` should not be undefined
398
344
  if (!ast)
399
345
  return '';
400
- return (generator.default || generator)(ast, { decoratorsBeforeExport: true }).code;
346
+ return generator(ast, { decoratorsBeforeExport: true }).code;
401
347
  };
402
348
 
403
349
  const CUSTOM_ELEMENT_OPTIONS = {
404
- prefix: undefined,
350
+ prefix: '',
405
351
  typings: true
406
352
  };
407
353
  // TODO: support {variable && jsxElement}
408
- const customElement = (options) => {
354
+ const customElement = (userOptions) => {
409
355
  const name = 'customElement';
410
- options = Object.assign({}, CUSTOM_ELEMENT_OPTIONS, options);
356
+ const options = Object.assign({}, CUSTOM_ELEMENT_OPTIONS, userOptions);
411
357
  const run = (context) => {
358
+ if (!context.fileAST)
359
+ return;
412
360
  const ast = t.cloneNode(context.fileAST, true);
413
- context.elementTagName = `${options.prefix || ''}${context.elementKey}`;
361
+ context.elementTagName = `${options.prefix}${context.elementKey}`;
414
362
  context.elementInterfaceName = `HTML${pascalCase(context.elementTagName)}Element`;
415
363
  // attach tag name
416
364
  visitor(ast, {
417
365
  ClassDeclaration(path) {
418
366
  const { body, id } = path.node;
419
- if (id.name != context.className)
367
+ if (id?.name !== context.className)
420
368
  return;
421
- const node = t.classProperty(t.identifier(STATIC_TAG), t.stringLiteral(context.elementTagName), undefined, undefined, undefined, true);
369
+ const node = t.classProperty(t.identifier(STATIC_TAG), t.stringLiteral(context.elementTagName || ''), undefined, undefined, undefined, true);
422
370
  t.addComment(node, 'leading', COMMENT_AUTO_ADDED, true);
423
371
  body.body.unshift(node);
424
372
  }
@@ -427,15 +375,18 @@ const customElement = (options) => {
427
375
  visitor(ast, {
428
376
  JSXAttribute(path) {
429
377
  const { name, value } = path.node;
430
- if (name.name != 'style')
378
+ if (name.name !== 'style')
431
379
  return;
432
380
  if (!value)
433
381
  return;
434
- if (value.type != 'JSXExpressionContainer')
382
+ if (value.type !== 'JSXExpressionContainer')
383
+ return;
384
+ if (!value.expression)
385
+ return;
386
+ if (value.expression.type === 'JSXEmptyExpression')
435
387
  return;
436
388
  const { local } = addDependency(path, UTILS_PATH, UTILS_STYLES_LOCAL, UTILS_STYLES_IMPORTED);
437
- // TODO: remove 'local!'
438
- path.replaceWith(t.jsxAttribute(t.jsxIdentifier('style'), t.jsxExpressionContainer(t.callExpression(t.identifier(local), [value.expression]))));
389
+ path.replaceWith(t.jsxAttribute(t.jsxIdentifier('style'), t.jsxExpressionContainer(t.callExpression(t.identifier(local || ''), [value.expression]))));
439
390
  path.skip();
440
391
  }
441
392
  });
@@ -443,10 +394,14 @@ const customElement = (options) => {
443
394
  visitor(ast, {
444
395
  JSXAttribute(path) {
445
396
  const { name, value } = path.node;
446
- if (name.name != 'className')
397
+ if (name.name !== 'className')
447
398
  return;
448
- const hasClass = path.parentPath.node.attributes.some((attribute) => {
449
- return attribute.name?.name == 'class';
399
+ if (!value)
400
+ return;
401
+ if (!t.isJSXOpeningElement(path.parent))
402
+ return;
403
+ const hasClass = path.parent.attributes.some((attribute) => {
404
+ return t.isJSXAttribute(attribute) && attribute.name.name === 'class';
450
405
  });
451
406
  if (hasClass)
452
407
  return path.remove();
@@ -457,12 +412,14 @@ const customElement = (options) => {
457
412
  visitor(ast, {
458
413
  JSXAttribute(path) {
459
414
  const { name, value } = path.node;
460
- if (name.name == 'value') {
461
- name.name = '.' + name.name;
415
+ if (!t.isJSXIdentifier(name))
416
+ return;
417
+ if (name.name === 'value') {
418
+ name.name = `.${name.name}`;
462
419
  return;
463
420
  }
464
- if (name.name == 'disabled') {
465
- name.name = '.' + name.name;
421
+ if (name.name === 'disabled') {
422
+ name.name = `.${name.name}`;
466
423
  return;
467
424
  }
468
425
  const key = ['tabIndex', 'viewBox'];
@@ -480,7 +437,7 @@ const customElement = (options) => {
480
437
  return;
481
438
  const TODO = (node, attributes) => {
482
439
  const { local } = addDependency(path, UTILS_PATH, UTILS_ATTRIBUTES_LOCAL, UTILS_ATTRIBUTES_IMPORTED);
483
- return t.callExpression(t.identifier(local), [
440
+ return t.callExpression(t.identifier(local || ''), [
484
441
  node,
485
442
  t.arrayExpression(attributes.map((attribute) => {
486
443
  switch (attribute.type) {
@@ -488,7 +445,7 @@ const customElement = (options) => {
488
445
  return attribute.argument;
489
446
  default:
490
447
  return t.objectExpression([
491
- t.objectProperty(t.stringLiteral(attribute.name.name), attribute.value?.type == 'JSXExpressionContainer'
448
+ t.objectProperty(t.stringLiteral(attribute.name.name), attribute.value?.type === 'JSXExpressionContainer'
492
449
  ? attribute.value.expression
493
450
  : attribute.value || t.booleanLiteral(true))
494
451
  ]);
@@ -498,22 +455,22 @@ const customElement = (options) => {
498
455
  };
499
456
  const render = (node) => {
500
457
  switch (node.type) {
501
- case 'JSXElement':
458
+ case 'JSXElement': {
502
459
  const attributes = node.openingElement.attributes;
503
- const isHost = node.openingElement.name.name == ELEMENT_HOST_NAME;
460
+ const isHost = node.openingElement.name.name === ELEMENT_HOST_NAME;
504
461
  // TODO
505
462
  if (isHost) {
506
- const children = node.children.map(render).flat();
463
+ const children = node.children.flatMap(render);
507
464
  if (!attributes.length)
508
465
  return children;
509
466
  return [TODO(t.thisExpression(), attributes), ...children];
510
467
  }
511
468
  const name = node.openingElement.name.name;
512
- const children = node.children.map(render).flat();
469
+ const children = node.children.flatMap(render);
513
470
  const parts = [];
514
471
  parts.push('<', name);
515
472
  const hasSpreadAttribute = attributes.some((attribute) => {
516
- return attribute.type == 'JSXSpreadAttribute';
473
+ return attribute.type === 'JSXSpreadAttribute';
517
474
  });
518
475
  if (hasSpreadAttribute) {
519
476
  parts.push(' ', 'ref=', t.arrowFunctionExpression([t.identifier('$element')], TODO(t.identifier('$element'), attributes)));
@@ -522,7 +479,7 @@ const customElement = (options) => {
522
479
  for (const attribute of attributes) {
523
480
  switch (attribute.type) {
524
481
  case 'JSXAttribute':
525
- if (attribute.name.name == 'dangerouslySetInnerHTML') {
482
+ if (attribute.name.name === 'dangerouslySetInnerHTML') {
526
483
  try {
527
484
  parts.push(' ', '.innerHTML');
528
485
  parts.push('=');
@@ -556,12 +513,13 @@ const customElement = (options) => {
556
513
  parts.push('<', '/', name, '>');
557
514
  }
558
515
  return parts;
516
+ }
559
517
  case 'JSXFragment':
560
- return node.children.map(render).flat();
518
+ return node.children.flatMap(render);
561
519
  case 'JSXText':
562
520
  return [node.extra.raw];
563
521
  case 'JSXExpressionContainer':
564
- if (node.expression.type == 'JSXEmptyExpression')
522
+ if (node.expression.type === 'JSXEmptyExpression')
565
523
  return [];
566
524
  return [node.expression];
567
525
  }
@@ -572,7 +530,7 @@ const customElement = (options) => {
572
530
  let i = 0;
573
531
  while (i < parts.length + 1) {
574
532
  let quasi = '';
575
- while (typeof parts[i] == 'string') {
533
+ while (typeof parts[i] === 'string') {
576
534
  quasi += parts[i].replace(/[\\`]/g, (s) => `\\${s}`);
577
535
  i += 1;
578
536
  }
@@ -585,7 +543,7 @@ const customElement = (options) => {
585
543
  // TODO
586
544
  // if (!expressions.length) return template;
587
545
  const { local } = addDependency(path, UTILS_PATH, UTILS_HTML_LOCAL, UTILS_HTML_IMPORTED, true);
588
- return t.taggedTemplateExpression(t.identifier(local), templateLiteral);
546
+ return t.taggedTemplateExpression(t.identifier(local || ''), templateLiteral);
589
547
  };
590
548
  path.replaceWith(transform(render(path.node)));
591
549
  }
@@ -594,14 +552,22 @@ const customElement = (options) => {
594
552
  visitor(ast, {
595
553
  Decorator(path) {
596
554
  const { expression } = path.node;
597
- if (expression.callee?.name != DECORATOR_PROPERTY)
555
+ if (!t.isCallExpression(expression))
556
+ return;
557
+ if (!t.isIdentifier(expression.callee))
558
+ return;
559
+ if (expression.callee.name !== DECORATOR_PROPERTY)
598
560
  return;
599
561
  if (!expression.arguments.length) {
600
562
  expression.arguments.push(t.objectExpression([]));
601
563
  }
602
564
  const [argument] = expression.arguments;
565
+ if (!t.isObjectExpression(argument))
566
+ return;
603
567
  const property = argument.properties.find((property) => {
604
- return property.key.name == DECORATOR_PROPERTY_TYPE;
568
+ return (t.isObjectProperty(property) &&
569
+ t.isIdentifier(property.key) &&
570
+ property.key.name === DECORATOR_PROPERTY_TYPE);
605
571
  });
606
572
  if (property)
607
573
  return;
@@ -652,20 +618,25 @@ const customElement = (options) => {
652
618
  input.types.forEach(extract);
653
619
  break;
654
620
  // TODO
655
- case 'TSParenthesizedType':
656
- if (input?.typeAnnotation?.type != 'TSIntersectionType')
621
+ case 'TSParenthesizedType': {
622
+ if (input?.typeAnnotation?.type !== 'TSIntersectionType')
657
623
  break;
658
624
  let types = input.types || input.typeAnnotation.types;
659
- if (types.length != 2)
625
+ if (types.length !== 2)
660
626
  return;
661
- types = types.filter((type) => type.type != 'TSTypeLiteral');
662
- if (types.length != 1)
627
+ types = types.filter((type) => type.type !== 'TSTypeLiteral');
628
+ if (types.length !== 1)
663
629
  return;
664
630
  extract(types[0]);
665
631
  break;
632
+ }
666
633
  }
667
634
  };
668
- extract(getType(context.directoryPath, ast, path.parentPath.node.typeAnnotation?.typeAnnotation));
635
+ if (context.directoryPath) {
636
+ extract(
637
+ // biome-ignore lint: TODO
638
+ getType(context.directoryPath, ast, path.parent['typeAnnotation']?.typeAnnotation));
639
+ }
669
640
  argument.properties.push(t.objectProperty(t.identifier(DECORATOR_PROPERTY_TYPE), t.numericLiteral(type)));
670
641
  }
671
642
  });
@@ -673,49 +644,62 @@ const customElement = (options) => {
673
644
  if (options.typings) {
674
645
  visitor(ast, {
675
646
  Program(path) {
676
- const attributes = context
677
- .classProperties.filter((property) => !t.isClassMethod(property))
647
+ const attributes = (context.classProperties || [])
648
+ .filter((property) => !t.isClassMethod(property))
678
649
  .map((property) => {
679
- const key = extractAttribute(property) || kebabCase(property.key['name']);
680
- const typeAnnotation = property.typeAnnotation;
681
- return Object.assign(t.tSPropertySignature(t.stringLiteral(kebabCase(key)), typeAnnotation), {
682
- optional: property.optional,
683
- leadingComments: t.cloneNode(property, true).leadingComments
684
- });
650
+ const keyName = extractAttribute(property) ??
651
+ (t.isIdentifier(property.key) ? kebabCase(property.key.name) : '');
652
+ const typeAnnotation = property.typeAnnotation
653
+ ? property.typeAnnotation
654
+ : undefined;
655
+ const signature = t.tSPropertySignature(t.stringLiteral(kebabCase(keyName)), typeAnnotation);
656
+ signature.optional = property.optional ?? false;
657
+ signature.leadingComments = t.cloneNode(property, true).leadingComments;
658
+ return signature;
685
659
  });
686
- const events = context.classEvents.map((event) => {
660
+ const events = (context.classEvents ?? [])
661
+ .map((event) => {
662
+ if (!t.isIdentifier(event.key))
663
+ return null;
687
664
  const key = event.key;
688
- const typeAnnotation = event.typeAnnotation;
689
- return Object.assign(t.tSPropertySignature(t.identifier(camelCase('on-' + key.name)), t.tsTypeAnnotation(t.tsFunctionType(undefined, [
690
- Object.assign({}, t.identifier('event'), {
691
- typeAnnotation: t.tsTypeAnnotation(t.tsTypeReference(t.identifier('CustomEvent'), typeAnnotation?.['typeAnnotation']?.typeParameters))
692
- })
693
- ], t.tsTypeAnnotation(t.tsVoidKeyword())))), {
694
- optional: true,
695
- leadingComments: t.cloneNode(event, true).leadingComments
696
- });
697
- });
698
- const methods = context.classMethods.map((method) => {
699
- return Object.assign(t.tsMethodSignature(method.key, undefined, method.params, // TODO
700
- method.returnType // TODO
701
- ), {
702
- leadingComments: t.cloneNode(method, true).leadingComments
703
- });
704
- });
705
- const properties = context.classProperties.map((property) => {
665
+ const parameter = t.identifier('event');
666
+ parameter.typeAnnotation = t.tsTypeAnnotation(t.tsTypeReference(t.identifier('CustomEvent'),
667
+ // biome-ignore lint: TODO
668
+ event.typeAnnotation?.['typeAnnotation']?.typeParameters));
669
+ const functionType = t.tsFunctionType(undefined, [parameter], t.tsTypeAnnotation(t.tsVoidKeyword()));
670
+ const signature = t.tSPropertySignature(t.identifier(camelCase(`on-${key.name}`)), t.tsTypeAnnotation(functionType));
671
+ signature.optional = true;
672
+ signature.leadingComments = t.cloneNode(event, true).leadingComments;
673
+ return signature;
674
+ })
675
+ .filter((event) => !!event);
676
+ const methods = (context.classMethods ?? [])
677
+ .map((method) => {
678
+ if (!t.isIdentifier(method.key))
679
+ return null;
680
+ const parameters = (method.params ?? []);
681
+ const returnType = method.returnType;
682
+ const signature = t.tsMethodSignature(method.key, undefined, parameters, returnType);
683
+ signature.leadingComments = t.cloneNode(method, true).leadingComments;
684
+ return signature;
685
+ })
686
+ .filter((method) => !!method);
687
+ const properties = (context.classProperties ?? [])
688
+ .map((property) => {
689
+ if (!t.isIdentifier(property.key))
690
+ return null;
706
691
  const key = property.key;
707
- // TODO
692
+ // biome-ignore lint: TODO
708
693
  const readonly = property.readonly || !!property['returnType'];
709
- // TODO
710
- const typeAnnotation = (property.typeAnnotation ||
711
- property['returnType']);
712
- return Object.assign(t.tsPropertySignature(t.identifier(key.name), typeAnnotation), {
713
- readonly,
714
- optional: property.optional,
715
- leadingComments: t.cloneNode(property, true).leadingComments
716
- });
717
- });
718
- // prettier-ignore
694
+ // biome-ignore lint: TODO
695
+ const typeAnnotation = property.typeAnnotation || property['returnType'];
696
+ const signature = t.tsPropertySignature(t.identifier(key.name), typeAnnotation);
697
+ signature.readonly = readonly;
698
+ signature.optional = property.optional ?? false;
699
+ signature.leadingComments = t.cloneNode(property, true).leadingComments;
700
+ return signature;
701
+ })
702
+ .filter((property) => !!property);
719
703
  const ast = template.default.ast(`
720
704
  // THE FOLLOWING TYPES HAVE BEEN ADDED AUTOMATICALLY
721
705
 
@@ -773,12 +757,19 @@ const customElement = (options) => {
773
757
  },
774
758
  // TODO
775
759
  TSTypeReference(path) {
776
- if (path.node.typeName?.name != 'OverridesConfig')
760
+ if (!t.isIdentifier(path.node.typeName))
777
761
  return;
778
- const property = path.findParent((path) => path.isTSPropertySignature());
762
+ if (path.node.typeName.name !== 'OverridesConfig')
763
+ return;
764
+ const property = path.findParent((p) => p.isTSPropertySignature());
779
765
  if (!property)
780
766
  return;
767
+ if (!t.isTSPropertySignature(property.node))
768
+ return;
769
+ // biome-ignore lint: TODO
781
770
  const name = property.node.key.name || property.node.key.extra.rawValue;
771
+ if (!name)
772
+ return;
782
773
  if (!path.node.typeParameters?.params)
783
774
  return;
784
775
  path.node.typeParameters.params[1] = t.tsTypeReference(t.identifier('Omit'), t.tsTypeParameterInstantiation([
@@ -794,12 +785,14 @@ const customElement = (options) => {
794
785
  return { name, run };
795
786
  };
796
787
 
788
+ // biome-ignore-all lint: TODO
797
789
  const DOCUMENT_OPTIONS = {
798
- destination: path.join('dist', 'document.json')
790
+ destination: path.join('dist', 'document.json'),
791
+ transformer: (_context, element) => element
799
792
  };
800
- const document = (options) => {
793
+ const document = (userOptions) => {
801
794
  const name = 'document';
802
- options = Object.assign({}, DOCUMENT_OPTIONS, options);
795
+ const options = Object.assign({}, DOCUMENT_OPTIONS, userOptions);
803
796
  const finish = (global) => {
804
797
  const json = {
805
798
  elements: []
@@ -813,9 +806,9 @@ const document = (options) => {
813
806
  for (const decorator of event.decorators) {
814
807
  for (const argument of decorator.expression['arguments']) {
815
808
  for (const property of argument.properties) {
816
- if (property.key.name != 'cancelable')
809
+ if (property.key.name !== 'cancelable')
817
810
  continue;
818
- if (property.value.type != 'BooleanLiteral')
811
+ if (property.value.type !== 'BooleanLiteral')
819
812
  continue;
820
813
  if (!property.value.value)
821
814
  continue;
@@ -848,7 +841,8 @@ const document = (options) => {
848
841
  const comments = extractFromComment(method);
849
842
  // TODO
850
843
  const parameters = method.params.map((param) => ({
851
- description: comments.params?.find((item) => item.name == param['name'])?.description,
844
+ description: comments.params?.find((item) => item.name === param['name'])
845
+ ?.description,
852
846
  required: !param['optional'],
853
847
  name: param['name'],
854
848
  type: print(param?.['typeAnnotation']?.typeAnnotation) || undefined,
@@ -888,7 +882,7 @@ const document = (options) => {
888
882
  returns
889
883
  },
890
884
  // TODO
891
- returns != 'void' &&
885
+ returns !== 'void' &&
892
886
  comments.returns && {
893
887
  tags: [
894
888
  {
@@ -903,7 +897,7 @@ const document = (options) => {
903
897
  // TODO
904
898
  const initializer = getInitializer(property.value);
905
899
  const name = property.key['name'];
906
- const readonly = property['kind'] == 'get';
900
+ const readonly = property['kind'] === 'get';
907
901
  // TODO
908
902
  const reflects = (() => {
909
903
  if (!property.decorators)
@@ -912,9 +906,9 @@ const document = (options) => {
912
906
  for (const decorator of property.decorators) {
913
907
  for (const argument of decorator.expression['arguments']) {
914
908
  for (const property of argument.properties) {
915
- if (property.key.name != 'reflect')
909
+ if (property.key.name !== 'reflect')
916
910
  continue;
917
- if (property.value.type != 'BooleanLiteral')
911
+ if (property.value.type !== 'BooleanLiteral')
918
912
  continue;
919
913
  if (!property.value.value)
920
914
  continue;
@@ -990,7 +984,10 @@ const document = (options) => {
990
984
  json.elements = json.elements.sort((a, b) => (a.title > b.title ? 1 : -1));
991
985
  const dirname = path.dirname(options.destination);
992
986
  fs.ensureDirSync(dirname);
993
- fs.writeJSONSync(options.destination, json, { encoding: 'utf8', spaces: 2 });
987
+ fs.writeJSONSync(options.destination, json, {
988
+ encoding: 'utf8',
989
+ spaces: 2
990
+ });
994
991
  };
995
992
  return { name, finish };
996
993
  };
@@ -998,11 +995,13 @@ const document = (options) => {
998
995
  const extract = () => {
999
996
  const name = 'extract';
1000
997
  const run = (context) => {
1001
- const { declaration, leadingComments } = context.fileAST?.program.body.find((child) => {
998
+ const body = context.fileAST?.program.body.find((child) => {
1002
999
  return t.isExportNamedDeclaration(child);
1003
1000
  });
1004
- context.class = declaration;
1005
- context.class.leadingComments = leadingComments; // TODO
1001
+ context.class = body?.declaration;
1002
+ if (context.class) {
1003
+ context.class.leadingComments = body?.leadingComments; // TODO
1004
+ }
1006
1005
  context.classMembers = context.class?.body?.body || [];
1007
1006
  context.className = context.class?.id?.name;
1008
1007
  context.elementKey = kebabCase(context.className || '');
@@ -1018,11 +1017,11 @@ const PARSE_OPTIONS = {
1018
1017
  sourceType: 'module',
1019
1018
  plugins: [['decorators', { decoratorsBeforeExport: true }], 'jsx', 'typescript']
1020
1019
  };
1021
- const parse = (options) => {
1020
+ const parse = (userOptions) => {
1022
1021
  const name = 'parse';
1023
- options = Object.assign({}, PARSE_OPTIONS, options);
1022
+ const options = Object.assign({}, PARSE_OPTIONS, userOptions);
1024
1023
  const run = (context) => {
1025
- context.fileAST = parse$1(context.fileContent, options);
1024
+ context.fileAST = parse$1(context.fileContent || '', options);
1026
1025
  };
1027
1026
  return { name, run };
1028
1027
  };
@@ -1043,12 +1042,12 @@ const read = () => {
1043
1042
 
1044
1043
  const README_OPTIONS = {
1045
1044
  source(context) {
1046
- return path.join(context.directoryPath, `${context.fileName}.md`);
1045
+ return path.join(context.directoryPath || '', `${context.fileName}.md`);
1047
1046
  }
1048
1047
  };
1049
- const readme = (options) => {
1048
+ const readme = (userOptions) => {
1050
1049
  const name = 'readme';
1051
- options = Object.assign({}, README_OPTIONS, options);
1050
+ const options = Object.assign({}, README_OPTIONS, userOptions);
1052
1051
  const finish = (global) => {
1053
1052
  for (const context of global.contexts) {
1054
1053
  context.readmePath = options.source(context);
@@ -1066,18 +1065,14 @@ const readme = (options) => {
1066
1065
 
1067
1066
  const STYLE_OPTIONS = {
1068
1067
  source(context) {
1069
- return [
1070
- path.join(context.directoryPath, `${context.fileName}.css`),
1071
- path.join(context.directoryPath, `${context.fileName}.less`),
1072
- path.join(context.directoryPath, `${context.fileName}.sass`),
1073
- path.join(context.directoryPath, `${context.fileName}.scss`),
1074
- path.join(context.directoryPath, `${context.fileName}.styl`)
1075
- ];
1068
+ return ['css', 'less', 'sass', 'scss', 'styl'].map((key) => {
1069
+ return path.join(context.directoryPath || '', `${context.fileName}.${key}`);
1070
+ });
1076
1071
  }
1077
1072
  };
1078
- const style = (options) => {
1073
+ const style = (userOptions) => {
1079
1074
  const name = 'style';
1080
- options = Object.assign({}, STYLE_OPTIONS, options);
1075
+ const options = Object.assign({}, STYLE_OPTIONS, userOptions);
1081
1076
  const run = (context) => {
1082
1077
  const sources = [options.source(context)].flat();
1083
1078
  for (const source of sources) {
@@ -1093,11 +1088,12 @@ const style = (options) => {
1093
1088
  context.styleContent = fs.readFileSync(context.stylePath, 'utf8');
1094
1089
  context.styleExtension = path.extname(context.stylePath);
1095
1090
  context.styleName = path.basename(context.stylePath, context.styleExtension);
1091
+ if (!context.fileAST)
1092
+ return;
1096
1093
  const { local } = addDependency(context.fileAST, context.stylePath, STYLE_IMPORTED, undefined, true);
1097
- // TODO: remove 'local!'
1098
- const property = t.classProperty(t.identifier(STATIC_STYLE), t.identifier(local), undefined, null, undefined, true);
1094
+ const property = t.classProperty(t.identifier(STATIC_STYLE), t.identifier(local || ''), undefined, null, undefined, true);
1099
1095
  t.addComment(property, 'leading', COMMENT_AUTO_ADDED, true);
1100
- context.class.body.body.unshift(property);
1096
+ context.class?.body.body.unshift(property);
1101
1097
  };
1102
1098
  return { name, run };
1103
1099
  };
@@ -1106,30 +1102,36 @@ const validate = () => {
1106
1102
  const name = 'validate';
1107
1103
  const run = (context) => {
1108
1104
  context.skipped = true;
1105
+ if (!context.fileAST)
1106
+ return;
1109
1107
  visitor(context.fileAST, {
1110
1108
  ImportDeclaration(path) {
1111
1109
  if (path.node.source?.value !== PACKAGE_NAME)
1112
1110
  return;
1113
1111
  for (const specifier of path.node.specifiers) {
1114
- if (specifier.imported.name !== DECORATOR_ELEMENT)
1112
+ if (!t.isImportSpecifier(specifier) ||
1113
+ !t.isIdentifier(specifier.imported) ||
1114
+ specifier.imported.name !== DECORATOR_ELEMENT) {
1115
1115
  continue;
1116
+ }
1116
1117
  const binding = path.scope.getBinding(specifier.imported.name);
1117
- if (binding.references == 0)
1118
- break;
1118
+ if (!binding || binding.references === 0) {
1119
+ continue;
1120
+ }
1119
1121
  const referencePaths = binding.referencePaths.filter((referencePath) => {
1120
- if (t.isCallExpression(referencePath.parent) &&
1121
- t.isDecorator(referencePath.parentPath.parent) &&
1122
- t.isClassDeclaration(referencePath.parentPath.parentPath.parent) &&
1123
- t.isExportNamedDeclaration(referencePath.parentPath.parentPath.parentPath.parent))
1124
- return true;
1122
+ return (t.isCallExpression(referencePath.parent) &&
1123
+ t.isDecorator(referencePath.parentPath?.parent) &&
1124
+ t.isClassDeclaration(referencePath.parentPath.parentPath?.parent) &&
1125
+ t.isExportNamedDeclaration(referencePath.parentPath.parentPath.parentPath?.parent));
1125
1126
  });
1126
1127
  if (referencePaths.length > 1) {
1127
1128
  throw new Error('In each file, only one custom element can be defined. \n' +
1128
1129
  'If more than one @Element() decorator is used in the file, it will result in an error.\n');
1129
1130
  }
1130
1131
  context.skipped = false;
1131
- if (referencePaths.length == 1)
1132
+ if (referencePaths.length === 1) {
1132
1133
  break;
1134
+ }
1133
1135
  throw new Error('It appears that the class annotated with the @Element() decorator is not being exported correctly.');
1134
1136
  }
1135
1137
  path.stop();
@@ -1140,12 +1142,15 @@ const validate = () => {
1140
1142
  return { name, run };
1141
1143
  };
1142
1144
 
1145
+ // biome-ignore-all lint: TODO
1143
1146
  const VISUAL_STUDIO_CODE_OPTIONS = {
1144
- destination: path.join('dist', 'visual-studio-code.json')
1147
+ destination: path.join('dist', 'visual-studio-code.json'),
1148
+ reference: () => '',
1149
+ transformer: (_context, element) => element
1145
1150
  };
1146
- const visualStudioCode = (options) => {
1151
+ const visualStudioCode = (userOptions) => {
1147
1152
  const name = 'visualStudioCode';
1148
- options = Object.assign({}, VISUAL_STUDIO_CODE_OPTIONS, options);
1153
+ const options = Object.assign({}, VISUAL_STUDIO_CODE_OPTIONS, userOptions);
1149
1154
  const finish = (global) => {
1150
1155
  const contexts = global.contexts.sort((a, b) => {
1151
1156
  return a.elementKey.toUpperCase() > b.elementKey.toUpperCase() ? 1 : -1;
@@ -1205,34 +1210,40 @@ const visualStudioCode = (options) => {
1205
1210
  }
1206
1211
  const dirname = path.dirname(options.destination);
1207
1212
  fs.ensureDirSync(dirname);
1208
- fs.writeJSONSync(options.destination, json, { encoding: 'utf8', spaces: 2 });
1213
+ fs.writeJSONSync(options.destination, json, {
1214
+ encoding: 'utf8',
1215
+ spaces: 2
1216
+ });
1209
1217
  };
1210
1218
  return { name, finish };
1211
1219
  };
1212
1220
 
1221
+ // biome-ignore-all lint: TODO
1213
1222
  const WEB_TYPES_OPTIONS = {
1214
1223
  destination: path.join('dist', 'web-types.json'),
1215
1224
  packageName: '',
1216
- packageVersion: ''
1225
+ packageVersion: '',
1226
+ reference: () => '',
1227
+ transformer: (_context, element) => element
1217
1228
  };
1218
- const webTypes = (options) => {
1229
+ const webTypes = (userOptions) => {
1219
1230
  const name = 'webTypes';
1220
- options = Object.assign({}, WEB_TYPES_OPTIONS, options);
1231
+ const options = Object.assign({}, WEB_TYPES_OPTIONS, userOptions);
1221
1232
  const finish = (global) => {
1222
1233
  const contexts = global.contexts.sort((a, b) => {
1223
- return a.elementKey.toUpperCase() > b.elementKey.toUpperCase() ? 1 : -1;
1234
+ return (a.elementKey ?? '').toUpperCase().localeCompare((b.elementKey ?? '').toUpperCase());
1224
1235
  });
1225
1236
  const json = {
1226
- '$schema': 'http://json.schemastore.org/web-types',
1227
- 'name': options.packageName,
1228
- 'version': options.packageVersion,
1237
+ $schema: 'http://json.schemastore.org/web-types',
1238
+ name: options.packageName,
1239
+ version: options.packageVersion,
1229
1240
  'description-markup': 'markdown',
1230
1241
  'framework-config': {
1231
1242
  'enable-when': {
1232
1243
  'node-packages': [options.packageName]
1233
1244
  }
1234
1245
  },
1235
- 'contributions': {
1246
+ contributions: {
1236
1247
  html: {
1237
1248
  elements: []
1238
1249
  }
@@ -1263,9 +1274,9 @@ const webTypes = (options) => {
1263
1274
  default: getInitializer(property.value)
1264
1275
  }, extractFromComment(property, ['description', 'deprecated', 'experimental'])));
1265
1276
  const element = Object.assign({
1266
- 'name': context.elementKey,
1277
+ name: context.elementKey,
1267
1278
  'doc-url': options.reference?.(context),
1268
- 'js': {
1279
+ js: {
1269
1280
  events,
1270
1281
  properties: [].concat(properties, methods)
1271
1282
  },
@@ -1276,9 +1287,81 @@ const webTypes = (options) => {
1276
1287
  }
1277
1288
  const dirname = path.dirname(options.destination);
1278
1289
  fs.ensureDirSync(dirname);
1279
- fs.writeJSONSync(options.destination, json, { encoding: 'utf8', spaces: 2 });
1290
+ fs.writeJSONSync(options.destination, json, {
1291
+ encoding: 'utf8',
1292
+ spaces: 2
1293
+ });
1280
1294
  };
1281
1295
  return { name, finish };
1282
1296
  };
1283
1297
 
1298
+ const logger = ora({
1299
+ color: 'yellow'
1300
+ });
1301
+ const log = (message, persist) => {
1302
+ const content = `${new Date().toLocaleTimeString()} [${KEY}] ${message}`;
1303
+ const log = logger.start(content);
1304
+ if (!persist)
1305
+ return;
1306
+ log.succeed();
1307
+ };
1308
+ const transformer = (...plugins) => {
1309
+ let global = {
1310
+ contexts: []
1311
+ };
1312
+ const start = async () => {
1313
+ log(`Started.`, true);
1314
+ log(`${plugins.length} plugins detected.`, true);
1315
+ log(`Plugins are starting.`, true);
1316
+ for (const plugin of plugins) {
1317
+ if (!plugin.start)
1318
+ continue;
1319
+ log(`Plugin '${plugin.name}' is starting.`);
1320
+ global = (await plugin.start(global)) || global;
1321
+ log(`Plugin '${plugin.name}' started successfully.`);
1322
+ }
1323
+ log(`Plugins have been successfully started.`, true);
1324
+ };
1325
+ const run = async (filePath) => {
1326
+ let context = {
1327
+ filePath
1328
+ };
1329
+ const parsed = path.parse(filePath);
1330
+ for (const plugin of plugins) {
1331
+ if (!plugin.run)
1332
+ continue;
1333
+ const source = path.join(parsed.dir).split(path.sep).slice(-2).concat(parsed.base).join('/');
1334
+ log(`Plugin '${plugin.name}' is executing on '${source}' file.`);
1335
+ try {
1336
+ context = (await plugin.run(context, global)) || context;
1337
+ }
1338
+ catch (error) {
1339
+ log(`Error in '${plugin.name}' plugin on '${source}' file.\n`, true);
1340
+ throw error;
1341
+ }
1342
+ global.contexts = global.contexts
1343
+ .filter((current) => {
1344
+ return current.filePath !== context.filePath;
1345
+ })
1346
+ .concat(context);
1347
+ log(`Plugin '${plugin.name}' executed successfully on '${source}' file.`);
1348
+ }
1349
+ logger.stop();
1350
+ return context;
1351
+ };
1352
+ const finish = async () => {
1353
+ log(`Plugins are finishing.`, true);
1354
+ for (const plugin of plugins) {
1355
+ if (!plugin.finish)
1356
+ continue;
1357
+ log(`Plugin '${plugin.name}' is finishing.`);
1358
+ global = (await plugin.finish(global)) || global;
1359
+ log(`Plugin '${plugin.name}' finished successfully.`);
1360
+ }
1361
+ log(`Plugins have been successfully finished.`, true);
1362
+ log(`Finished.`, true);
1363
+ };
1364
+ return { global, start, run, finish };
1365
+ };
1366
+
1284
1367
  export { ASSETS_OPTIONS, COPY_OPTIONS, CUSTOM_ELEMENT_OPTIONS, DOCUMENT_OPTIONS, PARSE_OPTIONS, README_OPTIONS, STYLE_OPTIONS, VISUAL_STUDIO_CODE_OPTIONS, WEB_TYPES_OPTIONS, assets, copy, customElement, document, extract, parse, read, readme, style, transformer, validate, visualStudioCode, webTypes };