@htmlplus/element 3.2.6 → 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')
398
+ return;
399
+ if (!value)
400
+ return;
401
+ if (!t.isJSXOpeningElement(path.parent))
447
402
  return;
448
- const hasClass = path.parentPath.node.attributes.some((attribute) => {
449
- return attribute.name?.name == 'class';
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
 
@@ -751,9 +735,7 @@ const customElement = (options) => {
751
735
 
752
736
  namespace JSX {
753
737
  interface IntrinsicElements {
754
- "${context.elementTagName}": ${context.className}Events & ${context.className}Attributes & {
755
- [key: string]: any;
756
- };
738
+ "${context.elementTagName}": ${context.className}Events & ${context.className}Attributes & React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
757
739
  }
758
740
  }
759
741
  }
@@ -761,9 +743,7 @@ const customElement = (options) => {
761
743
  declare module "react" {
762
744
  namespace JSX {
763
745
  interface IntrinsicElements {
764
- "${context.elementTagName}": ${context.className}Events & ${context.className}Attributes & {
765
- [key: string]: any;
766
- };
746
+ "${context.elementTagName}": ${context.className}Events & ${context.className}Attributes & React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
767
747
  }
768
748
  }
769
749
  }
@@ -774,6 +754,29 @@ const customElement = (options) => {
774
754
  preserveComments: true
775
755
  });
776
756
  path.node.body.push(...ast);
757
+ },
758
+ // TODO
759
+ TSTypeReference(path) {
760
+ if (!t.isIdentifier(path.node.typeName))
761
+ return;
762
+ if (path.node.typeName.name !== 'OverridesConfig')
763
+ return;
764
+ const property = path.findParent((p) => p.isTSPropertySignature());
765
+ if (!property)
766
+ return;
767
+ if (!t.isTSPropertySignature(property.node))
768
+ return;
769
+ // biome-ignore lint: TODO
770
+ const name = property.node.key.name || property.node.key.extra.rawValue;
771
+ if (!name)
772
+ return;
773
+ if (!path.node.typeParameters?.params)
774
+ return;
775
+ path.node.typeParameters.params[1] = t.tsTypeReference(t.identifier('Omit'), t.tsTypeParameterInstantiation([
776
+ t.tsTypeReference(t.identifier(`${context.className}Properties`)),
777
+ t.tsLiteralType(t.stringLiteral(name))
778
+ ]));
779
+ path.skip();
777
780
  }
778
781
  });
779
782
  }
@@ -782,12 +785,14 @@ const customElement = (options) => {
782
785
  return { name, run };
783
786
  };
784
787
 
788
+ // biome-ignore-all lint: TODO
785
789
  const DOCUMENT_OPTIONS = {
786
- destination: path.join('dist', 'document.json')
790
+ destination: path.join('dist', 'document.json'),
791
+ transformer: (_context, element) => element
787
792
  };
788
- const document = (options) => {
793
+ const document = (userOptions) => {
789
794
  const name = 'document';
790
- options = Object.assign({}, DOCUMENT_OPTIONS, options);
795
+ const options = Object.assign({}, DOCUMENT_OPTIONS, userOptions);
791
796
  const finish = (global) => {
792
797
  const json = {
793
798
  elements: []
@@ -801,9 +806,9 @@ const document = (options) => {
801
806
  for (const decorator of event.decorators) {
802
807
  for (const argument of decorator.expression['arguments']) {
803
808
  for (const property of argument.properties) {
804
- if (property.key.name != 'cancelable')
809
+ if (property.key.name !== 'cancelable')
805
810
  continue;
806
- if (property.value.type != 'BooleanLiteral')
811
+ if (property.value.type !== 'BooleanLiteral')
807
812
  continue;
808
813
  if (!property.value.value)
809
814
  continue;
@@ -836,7 +841,8 @@ const document = (options) => {
836
841
  const comments = extractFromComment(method);
837
842
  // TODO
838
843
  const parameters = method.params.map((param) => ({
839
- description: comments.params?.find((item) => item.name == param['name'])?.description,
844
+ description: comments.params?.find((item) => item.name === param['name'])
845
+ ?.description,
840
846
  required: !param['optional'],
841
847
  name: param['name'],
842
848
  type: print(param?.['typeAnnotation']?.typeAnnotation) || undefined,
@@ -876,7 +882,7 @@ const document = (options) => {
876
882
  returns
877
883
  },
878
884
  // TODO
879
- returns != 'void' &&
885
+ returns !== 'void' &&
880
886
  comments.returns && {
881
887
  tags: [
882
888
  {
@@ -891,7 +897,7 @@ const document = (options) => {
891
897
  // TODO
892
898
  const initializer = getInitializer(property.value);
893
899
  const name = property.key['name'];
894
- const readonly = property['kind'] == 'get';
900
+ const readonly = property['kind'] === 'get';
895
901
  // TODO
896
902
  const reflects = (() => {
897
903
  if (!property.decorators)
@@ -900,9 +906,9 @@ const document = (options) => {
900
906
  for (const decorator of property.decorators) {
901
907
  for (const argument of decorator.expression['arguments']) {
902
908
  for (const property of argument.properties) {
903
- if (property.key.name != 'reflect')
909
+ if (property.key.name !== 'reflect')
904
910
  continue;
905
- if (property.value.type != 'BooleanLiteral')
911
+ if (property.value.type !== 'BooleanLiteral')
906
912
  continue;
907
913
  if (!property.value.value)
908
914
  continue;
@@ -978,7 +984,10 @@ const document = (options) => {
978
984
  json.elements = json.elements.sort((a, b) => (a.title > b.title ? 1 : -1));
979
985
  const dirname = path.dirname(options.destination);
980
986
  fs.ensureDirSync(dirname);
981
- fs.writeJSONSync(options.destination, json, { encoding: 'utf8', spaces: 2 });
987
+ fs.writeJSONSync(options.destination, json, {
988
+ encoding: 'utf8',
989
+ spaces: 2
990
+ });
982
991
  };
983
992
  return { name, finish };
984
993
  };
@@ -986,11 +995,13 @@ const document = (options) => {
986
995
  const extract = () => {
987
996
  const name = 'extract';
988
997
  const run = (context) => {
989
- const { declaration, leadingComments } = context.fileAST?.program.body.find((child) => {
998
+ const body = context.fileAST?.program.body.find((child) => {
990
999
  return t.isExportNamedDeclaration(child);
991
1000
  });
992
- context.class = declaration;
993
- context.class.leadingComments = leadingComments; // TODO
1001
+ context.class = body?.declaration;
1002
+ if (context.class) {
1003
+ context.class.leadingComments = body?.leadingComments; // TODO
1004
+ }
994
1005
  context.classMembers = context.class?.body?.body || [];
995
1006
  context.className = context.class?.id?.name;
996
1007
  context.elementKey = kebabCase(context.className || '');
@@ -1006,11 +1017,11 @@ const PARSE_OPTIONS = {
1006
1017
  sourceType: 'module',
1007
1018
  plugins: [['decorators', { decoratorsBeforeExport: true }], 'jsx', 'typescript']
1008
1019
  };
1009
- const parse = (options) => {
1020
+ const parse = (userOptions) => {
1010
1021
  const name = 'parse';
1011
- options = Object.assign({}, PARSE_OPTIONS, options);
1022
+ const options = Object.assign({}, PARSE_OPTIONS, userOptions);
1012
1023
  const run = (context) => {
1013
- context.fileAST = parse$1(context.fileContent, options);
1024
+ context.fileAST = parse$1(context.fileContent || '', options);
1014
1025
  };
1015
1026
  return { name, run };
1016
1027
  };
@@ -1031,12 +1042,12 @@ const read = () => {
1031
1042
 
1032
1043
  const README_OPTIONS = {
1033
1044
  source(context) {
1034
- return path.join(context.directoryPath, `${context.fileName}.md`);
1045
+ return path.join(context.directoryPath || '', `${context.fileName}.md`);
1035
1046
  }
1036
1047
  };
1037
- const readme = (options) => {
1048
+ const readme = (userOptions) => {
1038
1049
  const name = 'readme';
1039
- options = Object.assign({}, README_OPTIONS, options);
1050
+ const options = Object.assign({}, README_OPTIONS, userOptions);
1040
1051
  const finish = (global) => {
1041
1052
  for (const context of global.contexts) {
1042
1053
  context.readmePath = options.source(context);
@@ -1054,18 +1065,14 @@ const readme = (options) => {
1054
1065
 
1055
1066
  const STYLE_OPTIONS = {
1056
1067
  source(context) {
1057
- return [
1058
- path.join(context.directoryPath, `${context.fileName}.css`),
1059
- path.join(context.directoryPath, `${context.fileName}.less`),
1060
- path.join(context.directoryPath, `${context.fileName}.sass`),
1061
- path.join(context.directoryPath, `${context.fileName}.scss`),
1062
- path.join(context.directoryPath, `${context.fileName}.styl`)
1063
- ];
1068
+ return ['css', 'less', 'sass', 'scss', 'styl'].map((key) => {
1069
+ return path.join(context.directoryPath || '', `${context.fileName}.${key}`);
1070
+ });
1064
1071
  }
1065
1072
  };
1066
- const style = (options) => {
1073
+ const style = (userOptions) => {
1067
1074
  const name = 'style';
1068
- options = Object.assign({}, STYLE_OPTIONS, options);
1075
+ const options = Object.assign({}, STYLE_OPTIONS, userOptions);
1069
1076
  const run = (context) => {
1070
1077
  const sources = [options.source(context)].flat();
1071
1078
  for (const source of sources) {
@@ -1081,11 +1088,12 @@ const style = (options) => {
1081
1088
  context.styleContent = fs.readFileSync(context.stylePath, 'utf8');
1082
1089
  context.styleExtension = path.extname(context.stylePath);
1083
1090
  context.styleName = path.basename(context.stylePath, context.styleExtension);
1091
+ if (!context.fileAST)
1092
+ return;
1084
1093
  const { local } = addDependency(context.fileAST, context.stylePath, STYLE_IMPORTED, undefined, true);
1085
- // TODO: remove 'local!'
1086
- 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);
1087
1095
  t.addComment(property, 'leading', COMMENT_AUTO_ADDED, true);
1088
- context.class.body.body.unshift(property);
1096
+ context.class?.body.body.unshift(property);
1089
1097
  };
1090
1098
  return { name, run };
1091
1099
  };
@@ -1094,30 +1102,36 @@ const validate = () => {
1094
1102
  const name = 'validate';
1095
1103
  const run = (context) => {
1096
1104
  context.skipped = true;
1105
+ if (!context.fileAST)
1106
+ return;
1097
1107
  visitor(context.fileAST, {
1098
1108
  ImportDeclaration(path) {
1099
1109
  if (path.node.source?.value !== PACKAGE_NAME)
1100
1110
  return;
1101
1111
  for (const specifier of path.node.specifiers) {
1102
- if (specifier.imported.name !== DECORATOR_ELEMENT)
1112
+ if (!t.isImportSpecifier(specifier) ||
1113
+ !t.isIdentifier(specifier.imported) ||
1114
+ specifier.imported.name !== DECORATOR_ELEMENT) {
1103
1115
  continue;
1116
+ }
1104
1117
  const binding = path.scope.getBinding(specifier.imported.name);
1105
- if (binding.references == 0)
1106
- break;
1118
+ if (!binding || binding.references === 0) {
1119
+ continue;
1120
+ }
1107
1121
  const referencePaths = binding.referencePaths.filter((referencePath) => {
1108
- if (t.isCallExpression(referencePath.parent) &&
1109
- t.isDecorator(referencePath.parentPath.parent) &&
1110
- t.isClassDeclaration(referencePath.parentPath.parentPath.parent) &&
1111
- t.isExportNamedDeclaration(referencePath.parentPath.parentPath.parentPath.parent))
1112
- 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));
1113
1126
  });
1114
1127
  if (referencePaths.length > 1) {
1115
1128
  throw new Error('In each file, only one custom element can be defined. \n' +
1116
1129
  'If more than one @Element() decorator is used in the file, it will result in an error.\n');
1117
1130
  }
1118
1131
  context.skipped = false;
1119
- if (referencePaths.length == 1)
1132
+ if (referencePaths.length === 1) {
1120
1133
  break;
1134
+ }
1121
1135
  throw new Error('It appears that the class annotated with the @Element() decorator is not being exported correctly.');
1122
1136
  }
1123
1137
  path.stop();
@@ -1128,12 +1142,15 @@ const validate = () => {
1128
1142
  return { name, run };
1129
1143
  };
1130
1144
 
1145
+ // biome-ignore-all lint: TODO
1131
1146
  const VISUAL_STUDIO_CODE_OPTIONS = {
1132
- destination: path.join('dist', 'visual-studio-code.json')
1147
+ destination: path.join('dist', 'visual-studio-code.json'),
1148
+ reference: () => '',
1149
+ transformer: (_context, element) => element
1133
1150
  };
1134
- const visualStudioCode = (options) => {
1151
+ const visualStudioCode = (userOptions) => {
1135
1152
  const name = 'visualStudioCode';
1136
- options = Object.assign({}, VISUAL_STUDIO_CODE_OPTIONS, options);
1153
+ const options = Object.assign({}, VISUAL_STUDIO_CODE_OPTIONS, userOptions);
1137
1154
  const finish = (global) => {
1138
1155
  const contexts = global.contexts.sort((a, b) => {
1139
1156
  return a.elementKey.toUpperCase() > b.elementKey.toUpperCase() ? 1 : -1;
@@ -1193,34 +1210,40 @@ const visualStudioCode = (options) => {
1193
1210
  }
1194
1211
  const dirname = path.dirname(options.destination);
1195
1212
  fs.ensureDirSync(dirname);
1196
- fs.writeJSONSync(options.destination, json, { encoding: 'utf8', spaces: 2 });
1213
+ fs.writeJSONSync(options.destination, json, {
1214
+ encoding: 'utf8',
1215
+ spaces: 2
1216
+ });
1197
1217
  };
1198
1218
  return { name, finish };
1199
1219
  };
1200
1220
 
1221
+ // biome-ignore-all lint: TODO
1201
1222
  const WEB_TYPES_OPTIONS = {
1202
1223
  destination: path.join('dist', 'web-types.json'),
1203
1224
  packageName: '',
1204
- packageVersion: ''
1225
+ packageVersion: '',
1226
+ reference: () => '',
1227
+ transformer: (_context, element) => element
1205
1228
  };
1206
- const webTypes = (options) => {
1229
+ const webTypes = (userOptions) => {
1207
1230
  const name = 'webTypes';
1208
- options = Object.assign({}, WEB_TYPES_OPTIONS, options);
1231
+ const options = Object.assign({}, WEB_TYPES_OPTIONS, userOptions);
1209
1232
  const finish = (global) => {
1210
1233
  const contexts = global.contexts.sort((a, b) => {
1211
- return a.elementKey.toUpperCase() > b.elementKey.toUpperCase() ? 1 : -1;
1234
+ return (a.elementKey ?? '').toUpperCase().localeCompare((b.elementKey ?? '').toUpperCase());
1212
1235
  });
1213
1236
  const json = {
1214
- '$schema': 'http://json.schemastore.org/web-types',
1215
- 'name': options.packageName,
1216
- 'version': options.packageVersion,
1237
+ $schema: 'http://json.schemastore.org/web-types',
1238
+ name: options.packageName,
1239
+ version: options.packageVersion,
1217
1240
  'description-markup': 'markdown',
1218
1241
  'framework-config': {
1219
1242
  'enable-when': {
1220
1243
  'node-packages': [options.packageName]
1221
1244
  }
1222
1245
  },
1223
- 'contributions': {
1246
+ contributions: {
1224
1247
  html: {
1225
1248
  elements: []
1226
1249
  }
@@ -1251,9 +1274,9 @@ const webTypes = (options) => {
1251
1274
  default: getInitializer(property.value)
1252
1275
  }, extractFromComment(property, ['description', 'deprecated', 'experimental'])));
1253
1276
  const element = Object.assign({
1254
- 'name': context.elementKey,
1277
+ name: context.elementKey,
1255
1278
  'doc-url': options.reference?.(context),
1256
- 'js': {
1279
+ js: {
1257
1280
  events,
1258
1281
  properties: [].concat(properties, methods)
1259
1282
  },
@@ -1264,9 +1287,81 @@ const webTypes = (options) => {
1264
1287
  }
1265
1288
  const dirname = path.dirname(options.destination);
1266
1289
  fs.ensureDirSync(dirname);
1267
- fs.writeJSONSync(options.destination, json, { encoding: 'utf8', spaces: 2 });
1290
+ fs.writeJSONSync(options.destination, json, {
1291
+ encoding: 'utf8',
1292
+ spaces: 2
1293
+ });
1268
1294
  };
1269
1295
  return { name, finish };
1270
1296
  };
1271
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
+
1272
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 };