@htmlplus/element 3.3.0 → 3.4.0

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
 
@@ -770,15 +754,24 @@ const customElement = (options) => {
770
754
  preserveComments: true
771
755
  });
772
756
  path.node.body.push(...ast);
773
- },
774
- // TODO
757
+ }
758
+ });
759
+ // TODO
760
+ visitor(ast, {
775
761
  TSTypeReference(path) {
776
- if (path.node.typeName?.name != 'OverridesConfig')
762
+ if (!t.isIdentifier(path.node.typeName))
763
+ return;
764
+ if (path.node.typeName.name !== 'OverridesConfig')
777
765
  return;
778
- const property = path.findParent((path) => path.isTSPropertySignature());
766
+ const property = path.findParent((p) => p.isTSPropertySignature());
779
767
  if (!property)
780
768
  return;
769
+ if (!t.isTSPropertySignature(property.node))
770
+ return;
771
+ // biome-ignore lint: TODO
781
772
  const name = property.node.key.name || property.node.key.extra.rawValue;
773
+ if (!name)
774
+ return;
782
775
  if (!path.node.typeParameters?.params)
783
776
  return;
784
777
  path.node.typeParameters.params[1] = t.tsTypeReference(t.identifier('Omit'), t.tsTypeParameterInstantiation([
@@ -788,18 +781,51 @@ const customElement = (options) => {
788
781
  path.skip();
789
782
  }
790
783
  });
784
+ // TODO
785
+ visitor(ast, {
786
+ TSTypeReference(path) {
787
+ if (!t.isIdentifier(path.node.typeName))
788
+ return;
789
+ if (path.node.typeName.name !== 'OverridableValue')
790
+ return;
791
+ const property = path.findParent((p) => p.isTSPropertySignature());
792
+ if (!property)
793
+ return;
794
+ if (!t.isTSPropertySignature(property.node))
795
+ return;
796
+ // biome-ignore lint: TODO
797
+ const name = property.node.key.name || property.node.key.extra.rawValue;
798
+ if (!name)
799
+ return;
800
+ if (!path.node.typeParameters?.params)
801
+ return;
802
+ if (path.node.typeParameters.params.length > 1)
803
+ return;
804
+ const interfaceName = pascalCase(`${context.className}-${name}-Overrides`);
805
+ path.node.typeParameters.params[1] = t.identifier(interfaceName);
806
+ path.skip();
807
+ const has = ast.program.body.some((child) => t.isExportNamedDeclaration(child) &&
808
+ t.isInterfaceDeclaration(child.declaration) &&
809
+ child.declaration.id.name === interfaceName);
810
+ if (has)
811
+ return;
812
+ ast.program.body.push(t.exportNamedDeclaration(t.interfaceDeclaration(t.identifier(interfaceName), undefined, undefined, t.objectTypeAnnotation([]))));
813
+ }
814
+ });
791
815
  }
792
816
  context.script = print(ast);
793
817
  };
794
818
  return { name, run };
795
819
  };
796
820
 
821
+ // biome-ignore-all lint: TODO
797
822
  const DOCUMENT_OPTIONS = {
798
- destination: path.join('dist', 'document.json')
823
+ destination: path.join('dist', 'document.json'),
824
+ transformer: (_context, element) => element
799
825
  };
800
- const document = (options) => {
826
+ const document = (userOptions) => {
801
827
  const name = 'document';
802
- options = Object.assign({}, DOCUMENT_OPTIONS, options);
828
+ const options = Object.assign({}, DOCUMENT_OPTIONS, userOptions);
803
829
  const finish = (global) => {
804
830
  const json = {
805
831
  elements: []
@@ -813,9 +839,9 @@ const document = (options) => {
813
839
  for (const decorator of event.decorators) {
814
840
  for (const argument of decorator.expression['arguments']) {
815
841
  for (const property of argument.properties) {
816
- if (property.key.name != 'cancelable')
842
+ if (property.key.name !== 'cancelable')
817
843
  continue;
818
- if (property.value.type != 'BooleanLiteral')
844
+ if (property.value.type !== 'BooleanLiteral')
819
845
  continue;
820
846
  if (!property.value.value)
821
847
  continue;
@@ -848,7 +874,8 @@ const document = (options) => {
848
874
  const comments = extractFromComment(method);
849
875
  // TODO
850
876
  const parameters = method.params.map((param) => ({
851
- description: comments.params?.find((item) => item.name == param['name'])?.description,
877
+ description: comments.params?.find((item) => item.name === param['name'])
878
+ ?.description,
852
879
  required: !param['optional'],
853
880
  name: param['name'],
854
881
  type: print(param?.['typeAnnotation']?.typeAnnotation) || undefined,
@@ -888,7 +915,7 @@ const document = (options) => {
888
915
  returns
889
916
  },
890
917
  // TODO
891
- returns != 'void' &&
918
+ returns !== 'void' &&
892
919
  comments.returns && {
893
920
  tags: [
894
921
  {
@@ -903,7 +930,7 @@ const document = (options) => {
903
930
  // TODO
904
931
  const initializer = getInitializer(property.value);
905
932
  const name = property.key['name'];
906
- const readonly = property['kind'] == 'get';
933
+ const readonly = property['kind'] === 'get';
907
934
  // TODO
908
935
  const reflects = (() => {
909
936
  if (!property.decorators)
@@ -912,9 +939,9 @@ const document = (options) => {
912
939
  for (const decorator of property.decorators) {
913
940
  for (const argument of decorator.expression['arguments']) {
914
941
  for (const property of argument.properties) {
915
- if (property.key.name != 'reflect')
942
+ if (property.key.name !== 'reflect')
916
943
  continue;
917
- if (property.value.type != 'BooleanLiteral')
944
+ if (property.value.type !== 'BooleanLiteral')
918
945
  continue;
919
946
  if (!property.value.value)
920
947
  continue;
@@ -990,7 +1017,10 @@ const document = (options) => {
990
1017
  json.elements = json.elements.sort((a, b) => (a.title > b.title ? 1 : -1));
991
1018
  const dirname = path.dirname(options.destination);
992
1019
  fs.ensureDirSync(dirname);
993
- fs.writeJSONSync(options.destination, json, { encoding: 'utf8', spaces: 2 });
1020
+ fs.writeJSONSync(options.destination, json, {
1021
+ encoding: 'utf8',
1022
+ spaces: 2
1023
+ });
994
1024
  };
995
1025
  return { name, finish };
996
1026
  };
@@ -998,11 +1028,13 @@ const document = (options) => {
998
1028
  const extract = () => {
999
1029
  const name = 'extract';
1000
1030
  const run = (context) => {
1001
- const { declaration, leadingComments } = context.fileAST?.program.body.find((child) => {
1031
+ const body = context.fileAST?.program.body.find((child) => {
1002
1032
  return t.isExportNamedDeclaration(child);
1003
1033
  });
1004
- context.class = declaration;
1005
- context.class.leadingComments = leadingComments; // TODO
1034
+ context.class = body?.declaration;
1035
+ if (context.class) {
1036
+ context.class.leadingComments = body?.leadingComments; // TODO
1037
+ }
1006
1038
  context.classMembers = context.class?.body?.body || [];
1007
1039
  context.className = context.class?.id?.name;
1008
1040
  context.elementKey = kebabCase(context.className || '');
@@ -1018,11 +1050,11 @@ const PARSE_OPTIONS = {
1018
1050
  sourceType: 'module',
1019
1051
  plugins: [['decorators', { decoratorsBeforeExport: true }], 'jsx', 'typescript']
1020
1052
  };
1021
- const parse = (options) => {
1053
+ const parse = (userOptions) => {
1022
1054
  const name = 'parse';
1023
- options = Object.assign({}, PARSE_OPTIONS, options);
1055
+ const options = Object.assign({}, PARSE_OPTIONS, userOptions);
1024
1056
  const run = (context) => {
1025
- context.fileAST = parse$1(context.fileContent, options);
1057
+ context.fileAST = parse$1(context.fileContent || '', options);
1026
1058
  };
1027
1059
  return { name, run };
1028
1060
  };
@@ -1043,12 +1075,12 @@ const read = () => {
1043
1075
 
1044
1076
  const README_OPTIONS = {
1045
1077
  source(context) {
1046
- return path.join(context.directoryPath, `${context.fileName}.md`);
1078
+ return path.join(context.directoryPath || '', `${context.fileName}.md`);
1047
1079
  }
1048
1080
  };
1049
- const readme = (options) => {
1081
+ const readme = (userOptions) => {
1050
1082
  const name = 'readme';
1051
- options = Object.assign({}, README_OPTIONS, options);
1083
+ const options = Object.assign({}, README_OPTIONS, userOptions);
1052
1084
  const finish = (global) => {
1053
1085
  for (const context of global.contexts) {
1054
1086
  context.readmePath = options.source(context);
@@ -1066,18 +1098,14 @@ const readme = (options) => {
1066
1098
 
1067
1099
  const STYLE_OPTIONS = {
1068
1100
  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
- ];
1101
+ return ['css', 'less', 'sass', 'scss', 'styl'].map((key) => {
1102
+ return path.join(context.directoryPath || '', `${context.fileName}.${key}`);
1103
+ });
1076
1104
  }
1077
1105
  };
1078
- const style = (options) => {
1106
+ const style = (userOptions) => {
1079
1107
  const name = 'style';
1080
- options = Object.assign({}, STYLE_OPTIONS, options);
1108
+ const options = Object.assign({}, STYLE_OPTIONS, userOptions);
1081
1109
  const run = (context) => {
1082
1110
  const sources = [options.source(context)].flat();
1083
1111
  for (const source of sources) {
@@ -1093,11 +1121,12 @@ const style = (options) => {
1093
1121
  context.styleContent = fs.readFileSync(context.stylePath, 'utf8');
1094
1122
  context.styleExtension = path.extname(context.stylePath);
1095
1123
  context.styleName = path.basename(context.stylePath, context.styleExtension);
1124
+ if (!context.fileAST)
1125
+ return;
1096
1126
  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);
1127
+ const property = t.classProperty(t.identifier(STATIC_STYLE), t.identifier(local || ''), undefined, null, undefined, true);
1099
1128
  t.addComment(property, 'leading', COMMENT_AUTO_ADDED, true);
1100
- context.class.body.body.unshift(property);
1129
+ context.class?.body.body.unshift(property);
1101
1130
  };
1102
1131
  return { name, run };
1103
1132
  };
@@ -1106,30 +1135,36 @@ const validate = () => {
1106
1135
  const name = 'validate';
1107
1136
  const run = (context) => {
1108
1137
  context.skipped = true;
1138
+ if (!context.fileAST)
1139
+ return;
1109
1140
  visitor(context.fileAST, {
1110
1141
  ImportDeclaration(path) {
1111
1142
  if (path.node.source?.value !== PACKAGE_NAME)
1112
1143
  return;
1113
1144
  for (const specifier of path.node.specifiers) {
1114
- if (specifier.imported.name !== DECORATOR_ELEMENT)
1145
+ if (!t.isImportSpecifier(specifier) ||
1146
+ !t.isIdentifier(specifier.imported) ||
1147
+ specifier.imported.name !== DECORATOR_ELEMENT) {
1115
1148
  continue;
1149
+ }
1116
1150
  const binding = path.scope.getBinding(specifier.imported.name);
1117
- if (binding.references == 0)
1118
- break;
1151
+ if (!binding || binding.references === 0) {
1152
+ continue;
1153
+ }
1119
1154
  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;
1155
+ return (t.isCallExpression(referencePath.parent) &&
1156
+ t.isDecorator(referencePath.parentPath?.parent) &&
1157
+ t.isClassDeclaration(referencePath.parentPath.parentPath?.parent) &&
1158
+ t.isExportNamedDeclaration(referencePath.parentPath.parentPath.parentPath?.parent));
1125
1159
  });
1126
1160
  if (referencePaths.length > 1) {
1127
1161
  throw new Error('In each file, only one custom element can be defined. \n' +
1128
1162
  'If more than one @Element() decorator is used in the file, it will result in an error.\n');
1129
1163
  }
1130
1164
  context.skipped = false;
1131
- if (referencePaths.length == 1)
1165
+ if (referencePaths.length === 1) {
1132
1166
  break;
1167
+ }
1133
1168
  throw new Error('It appears that the class annotated with the @Element() decorator is not being exported correctly.');
1134
1169
  }
1135
1170
  path.stop();
@@ -1140,12 +1175,15 @@ const validate = () => {
1140
1175
  return { name, run };
1141
1176
  };
1142
1177
 
1178
+ // biome-ignore-all lint: TODO
1143
1179
  const VISUAL_STUDIO_CODE_OPTIONS = {
1144
- destination: path.join('dist', 'visual-studio-code.json')
1180
+ destination: path.join('dist', 'visual-studio-code.json'),
1181
+ reference: () => '',
1182
+ transformer: (_context, element) => element
1145
1183
  };
1146
- const visualStudioCode = (options) => {
1184
+ const visualStudioCode = (userOptions) => {
1147
1185
  const name = 'visualStudioCode';
1148
- options = Object.assign({}, VISUAL_STUDIO_CODE_OPTIONS, options);
1186
+ const options = Object.assign({}, VISUAL_STUDIO_CODE_OPTIONS, userOptions);
1149
1187
  const finish = (global) => {
1150
1188
  const contexts = global.contexts.sort((a, b) => {
1151
1189
  return a.elementKey.toUpperCase() > b.elementKey.toUpperCase() ? 1 : -1;
@@ -1205,34 +1243,40 @@ const visualStudioCode = (options) => {
1205
1243
  }
1206
1244
  const dirname = path.dirname(options.destination);
1207
1245
  fs.ensureDirSync(dirname);
1208
- fs.writeJSONSync(options.destination, json, { encoding: 'utf8', spaces: 2 });
1246
+ fs.writeJSONSync(options.destination, json, {
1247
+ encoding: 'utf8',
1248
+ spaces: 2
1249
+ });
1209
1250
  };
1210
1251
  return { name, finish };
1211
1252
  };
1212
1253
 
1254
+ // biome-ignore-all lint: TODO
1213
1255
  const WEB_TYPES_OPTIONS = {
1214
1256
  destination: path.join('dist', 'web-types.json'),
1215
1257
  packageName: '',
1216
- packageVersion: ''
1258
+ packageVersion: '',
1259
+ reference: () => '',
1260
+ transformer: (_context, element) => element
1217
1261
  };
1218
- const webTypes = (options) => {
1262
+ const webTypes = (userOptions) => {
1219
1263
  const name = 'webTypes';
1220
- options = Object.assign({}, WEB_TYPES_OPTIONS, options);
1264
+ const options = Object.assign({}, WEB_TYPES_OPTIONS, userOptions);
1221
1265
  const finish = (global) => {
1222
1266
  const contexts = global.contexts.sort((a, b) => {
1223
- return a.elementKey.toUpperCase() > b.elementKey.toUpperCase() ? 1 : -1;
1267
+ return (a.elementKey ?? '').toUpperCase().localeCompare((b.elementKey ?? '').toUpperCase());
1224
1268
  });
1225
1269
  const json = {
1226
- '$schema': 'http://json.schemastore.org/web-types',
1227
- 'name': options.packageName,
1228
- 'version': options.packageVersion,
1270
+ $schema: 'http://json.schemastore.org/web-types',
1271
+ name: options.packageName,
1272
+ version: options.packageVersion,
1229
1273
  'description-markup': 'markdown',
1230
1274
  'framework-config': {
1231
1275
  'enable-when': {
1232
1276
  'node-packages': [options.packageName]
1233
1277
  }
1234
1278
  },
1235
- 'contributions': {
1279
+ contributions: {
1236
1280
  html: {
1237
1281
  elements: []
1238
1282
  }
@@ -1263,9 +1307,9 @@ const webTypes = (options) => {
1263
1307
  default: getInitializer(property.value)
1264
1308
  }, extractFromComment(property, ['description', 'deprecated', 'experimental'])));
1265
1309
  const element = Object.assign({
1266
- 'name': context.elementKey,
1310
+ name: context.elementKey,
1267
1311
  'doc-url': options.reference?.(context),
1268
- 'js': {
1312
+ js: {
1269
1313
  events,
1270
1314
  properties: [].concat(properties, methods)
1271
1315
  },
@@ -1276,9 +1320,81 @@ const webTypes = (options) => {
1276
1320
  }
1277
1321
  const dirname = path.dirname(options.destination);
1278
1322
  fs.ensureDirSync(dirname);
1279
- fs.writeJSONSync(options.destination, json, { encoding: 'utf8', spaces: 2 });
1323
+ fs.writeJSONSync(options.destination, json, {
1324
+ encoding: 'utf8',
1325
+ spaces: 2
1326
+ });
1280
1327
  };
1281
1328
  return { name, finish };
1282
1329
  };
1283
1330
 
1331
+ const logger = ora({
1332
+ color: 'yellow'
1333
+ });
1334
+ const log = (message, persist) => {
1335
+ const content = `${new Date().toLocaleTimeString()} [${KEY}] ${message}`;
1336
+ const log = logger.start(content);
1337
+ if (!persist)
1338
+ return;
1339
+ log.succeed();
1340
+ };
1341
+ const transformer = (...plugins) => {
1342
+ let global = {
1343
+ contexts: []
1344
+ };
1345
+ const start = async () => {
1346
+ log(`Started.`, true);
1347
+ log(`${plugins.length} plugins detected.`, true);
1348
+ log(`Plugins are starting.`, true);
1349
+ for (const plugin of plugins) {
1350
+ if (!plugin.start)
1351
+ continue;
1352
+ log(`Plugin '${plugin.name}' is starting.`);
1353
+ global = (await plugin.start(global)) || global;
1354
+ log(`Plugin '${plugin.name}' started successfully.`);
1355
+ }
1356
+ log(`Plugins have been successfully started.`, true);
1357
+ };
1358
+ const run = async (filePath) => {
1359
+ let context = {
1360
+ filePath
1361
+ };
1362
+ const parsed = path.parse(filePath);
1363
+ for (const plugin of plugins) {
1364
+ if (!plugin.run)
1365
+ continue;
1366
+ const source = path.join(parsed.dir).split(path.sep).slice(-2).concat(parsed.base).join('/');
1367
+ log(`Plugin '${plugin.name}' is executing on '${source}' file.`);
1368
+ try {
1369
+ context = (await plugin.run(context, global)) || context;
1370
+ }
1371
+ catch (error) {
1372
+ log(`Error in '${plugin.name}' plugin on '${source}' file.\n`, true);
1373
+ throw error;
1374
+ }
1375
+ global.contexts = global.contexts
1376
+ .filter((current) => {
1377
+ return current.filePath !== context.filePath;
1378
+ })
1379
+ .concat(context);
1380
+ log(`Plugin '${plugin.name}' executed successfully on '${source}' file.`);
1381
+ }
1382
+ logger.stop();
1383
+ return context;
1384
+ };
1385
+ const finish = async () => {
1386
+ log(`Plugins are finishing.`, true);
1387
+ for (const plugin of plugins) {
1388
+ if (!plugin.finish)
1389
+ continue;
1390
+ log(`Plugin '${plugin.name}' is finishing.`);
1391
+ global = (await plugin.finish(global)) || global;
1392
+ log(`Plugin '${plugin.name}' finished successfully.`);
1393
+ }
1394
+ log(`Plugins have been successfully finished.`, true);
1395
+ log(`Finished.`, true);
1396
+ };
1397
+ return { global, start, run, finish };
1398
+ };
1399
+
1284
1400
  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 };