@htmlplus/element 2.12.0 → 2.13.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.
Files changed (176) hide show
  1. package/README.md +2 -2
  2. package/bundlers.d.ts +19 -0
  3. package/bundlers.js +75 -0
  4. package/client-5FqNUiuz.d.ts +309 -0
  5. package/client-By_MTZkm.js +1796 -0
  6. package/client.d.ts +1 -0
  7. package/client.js +3 -0
  8. package/constants.d.ts +50 -0
  9. package/constants.js +62 -0
  10. package/internal.d.ts +1 -0
  11. package/internal.js +3 -0
  12. package/package.json +9 -4
  13. package/transformer.d.ts +137 -0
  14. package/transformer.js +1253 -0
  15. package/bundlers/rollup.d.ts +0 -7
  16. package/bundlers/rollup.js +0 -23
  17. package/bundlers/vite.d.ts +0 -8
  18. package/bundlers/vite.js +0 -52
  19. package/client/decorators/bind.d.ts +0 -8
  20. package/client/decorators/bind.js +0 -21
  21. package/client/decorators/context.d.ts +0 -3
  22. package/client/decorators/context.js +0 -121
  23. package/client/decorators/direction.d.ts +0 -5
  24. package/client/decorators/direction.js +0 -8
  25. package/client/decorators/element.d.ts +0 -7
  26. package/client/decorators/element.js +0 -65
  27. package/client/decorators/event.d.ts +0 -35
  28. package/client/decorators/event.js +0 -53
  29. package/client/decorators/host.d.ts +0 -4
  30. package/client/decorators/host.js +0 -7
  31. package/client/decorators/index.d.ts +0 -15
  32. package/client/decorators/index.js +0 -15
  33. package/client/decorators/isRTL.d.ts +0 -4
  34. package/client/decorators/isRTL.js +0 -7
  35. package/client/decorators/listen.d.ts +0 -52
  36. package/client/decorators/listen.js +0 -37
  37. package/client/decorators/method.d.ts +0 -6
  38. package/client/decorators/method.js +0 -15
  39. package/client/decorators/property.d.ts +0 -25
  40. package/client/decorators/property.js +0 -94
  41. package/client/decorators/query.d.ts +0 -9
  42. package/client/decorators/query.js +0 -12
  43. package/client/decorators/queryAll.d.ts +0 -12
  44. package/client/decorators/queryAll.js +0 -15
  45. package/client/decorators/slots.d.ts +0 -4
  46. package/client/decorators/slots.js +0 -7
  47. package/client/decorators/state.d.ts +0 -6
  48. package/client/decorators/state.js +0 -23
  49. package/client/decorators/watch.d.ts +0 -11
  50. package/client/decorators/watch.js +0 -31
  51. package/client/index.d.ts +0 -2
  52. package/client/index.js +0 -2
  53. package/client/utils/appendToMethod.d.ts +0 -1
  54. package/client/utils/appendToMethod.js +0 -15
  55. package/client/utils/attributes.d.ts +0 -2
  56. package/client/utils/attributes.js +0 -33
  57. package/client/utils/call.d.ts +0 -1
  58. package/client/utils/call.js +0 -3
  59. package/client/utils/classes.d.ts +0 -4
  60. package/client/utils/classes.js +0 -45
  61. package/client/utils/config.d.ts +0 -39
  62. package/client/utils/config.js +0 -28
  63. package/client/utils/defineProperty.d.ts +0 -1
  64. package/client/utils/defineProperty.js +0 -1
  65. package/client/utils/direction.d.ts +0 -6
  66. package/client/utils/direction.js +0 -8
  67. package/client/utils/event.d.ts +0 -13
  68. package/client/utils/event.js +0 -48
  69. package/client/utils/getFramework.d.ts +0 -2
  70. package/client/utils/getFramework.js +0 -20
  71. package/client/utils/getStyles.d.ts +0 -2
  72. package/client/utils/getStyles.js +0 -4
  73. package/client/utils/getTag.d.ts +0 -2
  74. package/client/utils/getTag.js +0 -4
  75. package/client/utils/host.d.ts +0 -5
  76. package/client/utils/host.js +0 -12
  77. package/client/utils/index.d.ts +0 -32
  78. package/client/utils/index.js +0 -32
  79. package/client/utils/isCSSColor.d.ts +0 -5
  80. package/client/utils/isCSSColor.js +0 -9
  81. package/client/utils/isEvent.d.ts +0 -1
  82. package/client/utils/isEvent.js +0 -3
  83. package/client/utils/isRTL.d.ts +0 -5
  84. package/client/utils/isRTL.js +0 -5
  85. package/client/utils/isServer.d.ts +0 -4
  86. package/client/utils/isServer.js +0 -6
  87. package/client/utils/merge.d.ts +0 -1
  88. package/client/utils/merge.js +0 -22
  89. package/client/utils/query.d.ts +0 -5
  90. package/client/utils/query.js +0 -7
  91. package/client/utils/queryAll.d.ts +0 -5
  92. package/client/utils/queryAll.js +0 -7
  93. package/client/utils/request.d.ts +0 -9
  94. package/client/utils/request.js +0 -106
  95. package/client/utils/shadowRoot.d.ts +0 -2
  96. package/client/utils/shadowRoot.js +0 -4
  97. package/client/utils/slots.d.ts +0 -9
  98. package/client/utils/slots.js +0 -18
  99. package/client/utils/styles.d.ts +0 -4
  100. package/client/utils/styles.js +0 -10
  101. package/client/utils/task.d.ts +0 -6
  102. package/client/utils/task.js +0 -34
  103. package/client/utils/toBoolean.d.ts +0 -1
  104. package/client/utils/toBoolean.js +0 -3
  105. package/client/utils/toDecorator.d.ts +0 -2
  106. package/client/utils/toDecorator.js +0 -10
  107. package/client/utils/toEvent.d.ts +0 -1
  108. package/client/utils/toEvent.js +0 -3
  109. package/client/utils/toProperty.d.ts +0 -1
  110. package/client/utils/toProperty.js +0 -55
  111. package/client/utils/toUnit.d.ts +0 -4
  112. package/client/utils/toUnit.js +0 -10
  113. package/client/utils/typeOf.d.ts +0 -3
  114. package/client/utils/typeOf.js +0 -6
  115. package/client/utils/uhtml.d.ts +0 -22
  116. package/client/utils/uhtml.js +0 -703
  117. package/client/utils/updateAttribute.d.ts +0 -2
  118. package/client/utils/updateAttribute.js +0 -10
  119. package/constants/index.d.ts +0 -48
  120. package/constants/index.js +0 -60
  121. package/transformer/index.d.ts +0 -3
  122. package/transformer/index.js +0 -3
  123. package/transformer/plugins/assets.d.ts +0 -7
  124. package/transformer/plugins/assets.js +0 -26
  125. package/transformer/plugins/copy.d.ts +0 -9
  126. package/transformer/plugins/copy.js +0 -29
  127. package/transformer/plugins/customElement.d.ts +0 -7
  128. package/transformer/plugins/customElement.js +0 -392
  129. package/transformer/plugins/document.d.ts +0 -7
  130. package/transformer/plugins/document.js +0 -206
  131. package/transformer/plugins/extract.d.ts +0 -2
  132. package/transformer/plugins/extract.js +0 -22
  133. package/transformer/plugins/index.d.ts +0 -12
  134. package/transformer/plugins/index.js +0 -12
  135. package/transformer/plugins/parse.d.ts +0 -6
  136. package/transformer/plugins/parse.js +0 -13
  137. package/transformer/plugins/read.d.ts +0 -7
  138. package/transformer/plugins/read.js +0 -19
  139. package/transformer/plugins/readme.d.ts +0 -6
  140. package/transformer/plugins/readme.js +0 -24
  141. package/transformer/plugins/style.d.ts +0 -6
  142. package/transformer/plugins/style.js +0 -42
  143. package/transformer/plugins/validate.d.ts +0 -2
  144. package/transformer/plugins/validate.js +0 -40
  145. package/transformer/plugins/visualStudioCode.d.ts +0 -8
  146. package/transformer/plugins/visualStudioCode.js +0 -73
  147. package/transformer/plugins/webTypes.d.ts +0 -10
  148. package/transformer/plugins/webTypes.js +0 -74
  149. package/transformer/transformer.d.ts +0 -8
  150. package/transformer/transformer.js +0 -78
  151. package/transformer/transformer.types.d.ts +0 -52
  152. package/transformer/transformer.types.js +0 -1
  153. package/transformer/utils/addDependency.d.ts +0 -7
  154. package/transformer/utils/addDependency.js +0 -64
  155. package/transformer/utils/extractAttribute.d.ts +0 -2
  156. package/transformer/utils/extractAttribute.js +0 -10
  157. package/transformer/utils/extractFromComment.d.ts +0 -4
  158. package/transformer/utils/extractFromComment.js +0 -51
  159. package/transformer/utils/getInitializer.d.ts +0 -2
  160. package/transformer/utils/getInitializer.js +0 -3
  161. package/transformer/utils/getType.d.ts +0 -2
  162. package/transformer/utils/getType.js +0 -89
  163. package/transformer/utils/getTypeReference.d.ts +0 -2
  164. package/transformer/utils/getTypeReference.js +0 -33
  165. package/transformer/utils/hasDecorator.d.ts +0 -1
  166. package/transformer/utils/hasDecorator.js +0 -5
  167. package/transformer/utils/index.d.ts +0 -10
  168. package/transformer/utils/index.js +0 -10
  169. package/transformer/utils/print.d.ts +0 -2
  170. package/transformer/utils/print.js +0 -5
  171. package/transformer/utils/printType.d.ts +0 -1
  172. package/transformer/utils/printType.js +0 -69
  173. package/transformer/utils/visitor.d.ts +0 -2
  174. package/transformer/utils/visitor.js +0 -5
  175. package/types/index.d.ts +0 -2
  176. package/types/index.js +0 -1
package/transformer.js ADDED
@@ -0,0 +1,1253 @@
1
+ import generator from '@babel/generator';
2
+ import t from '@babel/types';
3
+ import traverse from '@babel/traverse';
4
+ import { parse as parse$1 } from '@babel/parser';
5
+ import fs from 'fs-extra';
6
+ import { glob } from 'glob';
7
+ import template from '@babel/template';
8
+ import { pascalCase, kebabCase, camelCase, capitalCase } from 'change-case';
9
+ import ora from 'ora';
10
+ import path, { join, resolve, dirname } from 'node:path';
11
+ import { KEY, COMMENT_AUTO_ADDED, DECORATOR_PROPERTY, STATIC_TAG, UTILS_STYLES_LOCAL, UTILS_PATH, DECORATOR_PROPERTY_TYPE, UTILS_STYLES_IMPORTED, ELEMENT_HOST_NAME, UTILS_HTML_IMPORTED, UTILS_HTML_LOCAL, TYPE_OBJECT, TYPE_ARRAY, TYPE_NULL, 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 } 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
+ };
82
+
83
+ const ASSETS_OPTIONS = {
84
+ destination(context) {
85
+ return path.join('dist', 'assets', context.fileName);
86
+ },
87
+ source(context) {
88
+ return path.join(context.directoryPath, 'assets');
89
+ }
90
+ };
91
+ const assets = (options) => {
92
+ const name = 'assets';
93
+ options = Object.assign({}, ASSETS_OPTIONS, options);
94
+ const finish = (global) => {
95
+ for (const context of global.contexts) {
96
+ context.assetsDestination = options.destination(context);
97
+ context.assetsSource = options.source(context);
98
+ if (!context.assetsSource)
99
+ continue;
100
+ if (!fs.existsSync(context.assetsSource))
101
+ continue;
102
+ fs.copySync(context.assetsSource, context.assetsDestination);
103
+ }
104
+ };
105
+ return { name, finish };
106
+ };
107
+
108
+ const COPY_OPTIONS = {
109
+ at: 'start'
110
+ };
111
+ const copy = (options) => {
112
+ const name = 'copy';
113
+ options = Object.assign({}, COPY_OPTIONS, options);
114
+ const copy = (caller) => {
115
+ if (options.at != caller)
116
+ return;
117
+ let content;
118
+ content = fs.readFileSync(options.source, 'utf8');
119
+ if (options.transformer)
120
+ content = options.transformer(content);
121
+ fs.ensureDirSync(path.dirname(options.destination));
122
+ fs.writeFileSync(options.destination, content, 'utf8');
123
+ };
124
+ const start = () => {
125
+ copy('start');
126
+ };
127
+ const run = () => {
128
+ copy('run');
129
+ };
130
+ const finish = () => {
131
+ copy('finish');
132
+ };
133
+ return { name, start, run, finish };
134
+ };
135
+
136
+ // TODO: options type
137
+ const visitor = (ast, options) => {
138
+ (traverse.default || traverse)(ast, options);
139
+ };
140
+
141
+ function addDependency(path, source, local, imported, comment) {
142
+ const isDefault = local && !imported;
143
+ const isImport = local && imported;
144
+ const isNormal = !local && !imported;
145
+ let declaration;
146
+ let file = path;
147
+ while (file.parentPath)
148
+ file = file.parentPath;
149
+ file = file.node || file;
150
+ visitor(file, {
151
+ ImportDeclaration(path) {
152
+ if (path.node.source.value != source)
153
+ return;
154
+ declaration = path.node;
155
+ }
156
+ });
157
+ if (isNormal && declaration)
158
+ return {
159
+ node: declaration
160
+ };
161
+ let specifier = declaration?.specifiers.find((specifier) => {
162
+ if (isDefault) {
163
+ return specifier.type == 'ImportDefaultSpecifier';
164
+ }
165
+ else if (isImport) {
166
+ return specifier.imported?.name == imported;
167
+ }
168
+ });
169
+ if (specifier)
170
+ return {
171
+ local: specifier.local.name,
172
+ node: declaration
173
+ };
174
+ if (isDefault) {
175
+ specifier = t.importDefaultSpecifier(t.identifier(local));
176
+ }
177
+ else if (isImport) {
178
+ specifier = t.importSpecifier(t.identifier(local), t.identifier(imported));
179
+ }
180
+ if (declaration) {
181
+ if (isDefault) {
182
+ declaration.specifiers.unshift(specifier);
183
+ }
184
+ else if (isImport) {
185
+ declaration.specifiers.push(specifier);
186
+ }
187
+ }
188
+ else {
189
+ declaration = t.importDeclaration(specifier ? [specifier] : [], t.stringLiteral(source));
190
+ // TODO
191
+ (file.program || file).body.unshift(declaration);
192
+ // TODO
193
+ if (comment) {
194
+ t.addComment(declaration, 'leading', COMMENT_AUTO_ADDED, true);
195
+ }
196
+ }
197
+ return {
198
+ local,
199
+ node: declaration
200
+ };
201
+ }
202
+
203
+ const extractAttribute = (property) => {
204
+ try {
205
+ return property.decorators
206
+ .find((decorator) => decorator.expression.callee.name == DECORATOR_PROPERTY)
207
+ .expression.arguments[0].properties.find((property) => property.key.name == 'attribute').value
208
+ .value;
209
+ }
210
+ catch { }
211
+ };
212
+
213
+ const extractFromComment = (node, whitelist) => {
214
+ const normalized = [];
215
+ const result = {
216
+ description: ''
217
+ };
218
+ const lines = node.leadingComments
219
+ ?.map((comment) => {
220
+ if (comment.type == 'CommentLine')
221
+ return comment.value;
222
+ return comment.value.split('\n');
223
+ })
224
+ ?.flat()
225
+ ?.map((line) => line.trim().replace(/^\*/, '').trim())
226
+ ?.filter((line) => line.trim());
227
+ for (const line of lines || []) {
228
+ if (line.startsWith('@')) {
229
+ normalized.push(line);
230
+ continue;
231
+ }
232
+ if (!normalized.length)
233
+ normalized.push('');
234
+ normalized[normalized.length - 1] += ' ' + line;
235
+ }
236
+ for (const line of normalized) {
237
+ if (!line.startsWith('@')) {
238
+ result.description = line.trim();
239
+ continue;
240
+ }
241
+ const regex = /@(\w+)(?:\s*({\w+})\s*)?(?:\s*([-a-zA-Z\s]+)\s*-\s*)?(.*)/;
242
+ const groups = regex.exec(line);
243
+ if (!groups)
244
+ continue;
245
+ const tag = groups[1]?.trim();
246
+ const type = groups[2]?.trim().slice(1, -1);
247
+ const name = groups[3]?.trim();
248
+ const description = groups[4]?.trim();
249
+ if (name && description) {
250
+ const key = tag + 's';
251
+ if (whitelist && !whitelist.includes(key))
252
+ continue;
253
+ (result[key] ||= []).push({ name, type, description });
254
+ }
255
+ else {
256
+ const key = tag;
257
+ if (whitelist && !whitelist.includes(key))
258
+ continue;
259
+ result[key] = description || true;
260
+ }
261
+ }
262
+ return result;
263
+ };
264
+
265
+ const getInitializer = (node) => {
266
+ return node?.extra?.raw || node?.['value'];
267
+ };
268
+
269
+ const getType = (directory, file, node) => {
270
+ if (!node)
271
+ return node;
272
+ if (node.type != 'TSTypeReference')
273
+ return node;
274
+ let result;
275
+ visitor(file, {
276
+ ClassDeclaration(path) {
277
+ if (path.node.id.name != node.typeName['name'])
278
+ return;
279
+ result = path.node;
280
+ path.stop();
281
+ },
282
+ ImportDeclaration(path) {
283
+ for (const specifier of path.node.specifiers) {
284
+ const alias = specifier.local.name;
285
+ if (alias != node.typeName['name'])
286
+ continue;
287
+ switch (specifier.type) {
288
+ case 'ImportNamespaceSpecifier':
289
+ break;
290
+ case 'ImportDefaultSpecifier':
291
+ break;
292
+ case 'ImportSpecifier':
293
+ specifier.imported.name;
294
+ break;
295
+ }
296
+ try {
297
+ const reference = glob
298
+ .sync(['.ts*', '/index.ts*'].map((key) => {
299
+ return join(directory, path.node.source.value).replace(/\\/g, '/') + key;
300
+ }))
301
+ .find((reference) => fs.existsSync(reference));
302
+ const content = fs.readFileSync(reference, 'utf8');
303
+ const filePath = resolve(directory, path.node.source.value + '.ts');
304
+ path.$ast ||= parse$1(content, {
305
+ allowImportExportEverywhere: true,
306
+ plugins: ['typescript'],
307
+ ranges: false
308
+ });
309
+ result = getType(dirname(filePath), path.$ast, node);
310
+ }
311
+ catch { }
312
+ path.stop();
313
+ break;
314
+ }
315
+ },
316
+ TSInterfaceDeclaration(path) {
317
+ if (path.node.id.name != node.typeName['name'])
318
+ return;
319
+ result = path.node;
320
+ path.stop();
321
+ },
322
+ TSTypeAliasDeclaration(path) {
323
+ if (path.node.id.name != node.typeName['name'])
324
+ return;
325
+ result = path.node.typeAnnotation;
326
+ switch (result.type) {
327
+ case 'TSUnionType':
328
+ const types = [];
329
+ for (const prev of result.types) {
330
+ const next = getType(directory, file, prev);
331
+ if (next.type == 'TSUnionType') {
332
+ next.types.forEach((type) => types.push(type));
333
+ }
334
+ else {
335
+ types.push(next);
336
+ }
337
+ }
338
+ result.types = types;
339
+ break;
340
+ default:
341
+ result = getType(directory, file, result);
342
+ break;
343
+ }
344
+ path.stop();
345
+ }
346
+ });
347
+ return result || node;
348
+ };
349
+
350
+ const getTypeReference = (file, node) => {
351
+ if (!node)
352
+ return;
353
+ if (node.type != 'TSTypeReference')
354
+ return;
355
+ let result;
356
+ visitor(file, {
357
+ ImportDeclaration(path) {
358
+ for (const specifier of path.node.specifiers) {
359
+ const alias = specifier.local.name;
360
+ if (alias != node.typeName['name'])
361
+ continue;
362
+ switch (specifier.type) {
363
+ case 'ImportNamespaceSpecifier':
364
+ break;
365
+ case 'ImportDefaultSpecifier':
366
+ break;
367
+ case 'ImportSpecifier':
368
+ specifier.imported.name;
369
+ break;
370
+ }
371
+ result = path.node.source.value;
372
+ path.stop();
373
+ break;
374
+ }
375
+ }
376
+ });
377
+ return result;
378
+ };
379
+
380
+ const hasDecorator = (node, name) => {
381
+ if (!node.decorators)
382
+ return false;
383
+ return !!node.decorators.some((decorator) => decorator.expression.callee?.name == name);
384
+ };
385
+
386
+ // TODO: add options
387
+ const print = (ast) => {
388
+ return (generator.default || generator)(ast, { decoratorsBeforeExport: true }).code;
389
+ };
390
+
391
+ const CUSTOM_ELEMENT_OPTIONS = {
392
+ prefix: undefined,
393
+ typings: true
394
+ };
395
+ // TODO: support {variable && jsxElement}
396
+ const customElement = (options) => {
397
+ const name = 'customElement';
398
+ options = Object.assign({}, CUSTOM_ELEMENT_OPTIONS, options);
399
+ const run = (context) => {
400
+ const ast = t.cloneNode(context.fileAST, true);
401
+ context.elementTagName = `${options.prefix || ''}${context.elementKey}`;
402
+ context.elementInterfaceName = `HTML${pascalCase(context.elementTagName)}Element`;
403
+ // attach tag name
404
+ visitor(ast, {
405
+ ClassDeclaration(path) {
406
+ const { body, id } = path.node;
407
+ if (id.name != context.className)
408
+ return;
409
+ const node = t.classProperty(t.identifier(STATIC_TAG), t.stringLiteral(context.elementTagName), undefined, undefined, undefined, true);
410
+ t.addComment(node, 'leading', COMMENT_AUTO_ADDED, true);
411
+ body.body.unshift(node);
412
+ }
413
+ });
414
+ // attach style mapper for 'style' attribute
415
+ visitor(ast, {
416
+ JSXAttribute(path) {
417
+ const { name, value } = path.node;
418
+ if (name.name != 'style')
419
+ return;
420
+ if (!value)
421
+ return;
422
+ if (value.type != 'JSXExpressionContainer')
423
+ return;
424
+ const { local } = addDependency(path, UTILS_PATH, UTILS_STYLES_LOCAL, UTILS_STYLES_IMPORTED);
425
+ // TODO: remove 'local!'
426
+ path.replaceWith(t.jsxAttribute(t.jsxIdentifier('style'), t.jsxExpressionContainer(t.callExpression(t.identifier(local), [value.expression]))));
427
+ path.skip();
428
+ }
429
+ });
430
+ // replace 'className' attribute with 'class'
431
+ visitor(ast, {
432
+ JSXAttribute(path) {
433
+ const { name, value } = path.node;
434
+ if (name.name != 'className')
435
+ return;
436
+ const hasClass = path.parentPath.node.attributes.some((attribute) => {
437
+ return attribute.name?.name == 'class';
438
+ });
439
+ if (hasClass)
440
+ return path.remove();
441
+ path.replaceWith(t.jsxAttribute(t.jsxIdentifier('class'), value));
442
+ }
443
+ });
444
+ // TODO
445
+ visitor(ast, {
446
+ JSXAttribute(path) {
447
+ const { name, value } = path.node;
448
+ if (name.name == 'value') {
449
+ name.name = '.' + name.name;
450
+ return;
451
+ }
452
+ const key = ['tabIndex', 'viewBox'];
453
+ if (!key.includes(name.name))
454
+ return;
455
+ path.replaceWith(t.jsxAttribute(t.jsxIdentifier(name.name.toLowerCase()), value));
456
+ }
457
+ });
458
+ // TODO
459
+ // convert 'jsx' to 'uhtml' syntax
460
+ visitor(ast, {
461
+ enter(path) {
462
+ const { type } = path.node;
463
+ if (!['JSXElement', 'JSXFragment'].includes(type))
464
+ return;
465
+ const TODO = (node, attributes) => {
466
+ const { local } = addDependency(path, UTILS_PATH, UTILS_ATTRIBUTES_LOCAL, UTILS_ATTRIBUTES_IMPORTED);
467
+ return t.callExpression(t.identifier(local), [
468
+ node,
469
+ t.arrayExpression(attributes.map((attribute) => {
470
+ switch (attribute.type) {
471
+ case 'JSXSpreadAttribute':
472
+ return attribute.argument;
473
+ default:
474
+ return t.objectExpression([
475
+ t.objectProperty(t.stringLiteral(attribute.name.name), attribute.value?.type == 'JSXExpressionContainer'
476
+ ? attribute.value.expression
477
+ : attribute.value || t.booleanLiteral(true))
478
+ ]);
479
+ }
480
+ }))
481
+ ]);
482
+ };
483
+ const render = (node) => {
484
+ switch (node.type) {
485
+ case 'JSXElement':
486
+ const attributes = node.openingElement.attributes;
487
+ const isHost = node.openingElement.name.name == ELEMENT_HOST_NAME;
488
+ // TODO
489
+ if (isHost) {
490
+ const children = node.children.map(render).flat();
491
+ if (!attributes.length)
492
+ return children;
493
+ return [TODO(t.thisExpression(), attributes), ...children];
494
+ }
495
+ const name = node.openingElement.name.name;
496
+ const children = node.children.map(render).flat();
497
+ const parts = [];
498
+ parts.push('<', name);
499
+ const hasSpreadAttribute = attributes.some((attribute) => {
500
+ return attribute.type == 'JSXSpreadAttribute';
501
+ });
502
+ if (hasSpreadAttribute) {
503
+ parts.push(' ', TODO(t.identifier('TODO'), attributes));
504
+ }
505
+ else {
506
+ for (const attribute of attributes) {
507
+ switch (attribute.type) {
508
+ case 'JSXAttribute':
509
+ if (attribute.name.name == 'dangerouslySetInnerHTML') {
510
+ try {
511
+ parts.push(' ', '.innerHTML');
512
+ parts.push('=');
513
+ parts.push(attribute.value.expression.properties.at(0).value);
514
+ }
515
+ catch { }
516
+ continue;
517
+ }
518
+ parts.push(' ', attribute.name.name);
519
+ if (!attribute.value)
520
+ continue;
521
+ parts.push('=');
522
+ switch (attribute.value.type) {
523
+ case 'JSXExpressionContainer':
524
+ parts.push(attribute.value.expression);
525
+ break;
526
+ default:
527
+ parts.push(attribute.value.extra.raw);
528
+ break;
529
+ }
530
+ break;
531
+ default:
532
+ parts.push(' ', attribute);
533
+ break;
534
+ }
535
+ }
536
+ }
537
+ parts.push(node.closingElement ? '>' : ' />');
538
+ parts.push(...children);
539
+ if (node.closingElement) {
540
+ parts.push('<', '/', name, '>');
541
+ }
542
+ return parts;
543
+ case 'JSXFragment':
544
+ return node.children.map(render).flat();
545
+ case 'JSXText':
546
+ return [node.extra.raw];
547
+ case 'JSXExpressionContainer':
548
+ if (node.expression.type == 'JSXEmptyExpression')
549
+ return [];
550
+ return [node.expression];
551
+ }
552
+ };
553
+ const transform = (parts) => {
554
+ const quasis = [];
555
+ const expressions = [];
556
+ let i = 0;
557
+ while (i < parts.length + 1) {
558
+ let quasi = '';
559
+ while (typeof parts[i] == 'string') {
560
+ quasi += parts[i].replace(/[\\`]/g, (s) => `\\${s}`);
561
+ i += 1;
562
+ }
563
+ quasis.push(t.templateElement({ raw: quasi }));
564
+ if (parts[i] != null)
565
+ expressions.push(parts[i]);
566
+ i += 1;
567
+ }
568
+ const templateLiteral = t.templateLiteral(quasis, expressions);
569
+ // TODO
570
+ // if (!expressions.length) return template;
571
+ const { local } = addDependency(path, UTILS_PATH, UTILS_HTML_LOCAL, UTILS_HTML_IMPORTED, true);
572
+ return t.taggedTemplateExpression(t.identifier(local), templateLiteral);
573
+ };
574
+ path.replaceWith(transform(render(path.node)));
575
+ }
576
+ });
577
+ // add type to properties
578
+ visitor(ast, {
579
+ Decorator(path) {
580
+ const { expression } = path.node;
581
+ if (expression.callee?.name != DECORATOR_PROPERTY)
582
+ return;
583
+ if (!expression.arguments.length) {
584
+ expression.arguments.push(t.objectExpression([]));
585
+ }
586
+ const [argument] = expression.arguments;
587
+ const property = argument.properties.find((property) => {
588
+ return property.key.name == DECORATOR_PROPERTY_TYPE;
589
+ });
590
+ if (property)
591
+ return;
592
+ let type = 0;
593
+ const extract = (input) => {
594
+ switch (input?.type) {
595
+ case 'bool':
596
+ case 'Boolean':
597
+ case 'BooleanLiteral':
598
+ case 'TSBooleanKeyword':
599
+ type |= TYPE_BOOLEAN;
600
+ break;
601
+ case 'Date':
602
+ type |= TYPE_DATE;
603
+ break;
604
+ case 'Number':
605
+ case 'NumericLiteral':
606
+ case 'TSNumberKeyword':
607
+ type |= TYPE_NUMBER;
608
+ break;
609
+ case 'StringLiteral':
610
+ type |= TYPE_ENUM;
611
+ break;
612
+ case 'TSStringKeyword':
613
+ type |= TYPE_STRING;
614
+ break;
615
+ case 'TSArrayType':
616
+ type |= TYPE_ARRAY;
617
+ break;
618
+ case 'TSLiteralType':
619
+ extract(input.literal);
620
+ break;
621
+ case 'TSNullKeyword':
622
+ type |= TYPE_NULL;
623
+ break;
624
+ case 'Object':
625
+ case 'TSObjectKeyword':
626
+ type |= TYPE_OBJECT;
627
+ break;
628
+ case 'Array':
629
+ case 'TSTupleType':
630
+ type |= TYPE_ARRAY;
631
+ break;
632
+ case 'TSTypeLiteral':
633
+ type |= TYPE_OBJECT;
634
+ break;
635
+ case 'TSTypeReference':
636
+ extract({ type: input?.typeName?.name });
637
+ break;
638
+ case 'TSUnionType':
639
+ input.types.forEach(extract);
640
+ break;
641
+ // TODO
642
+ case 'TSParenthesizedType':
643
+ if (input?.typeAnnotation?.type != 'TSIntersectionType')
644
+ break;
645
+ let types = input.types || input.typeAnnotation.types;
646
+ if (types.length != 2)
647
+ return;
648
+ types = types.filter((type) => type.type != 'TSTypeLiteral');
649
+ if (types.length != 1)
650
+ return;
651
+ extract(types[0]);
652
+ break;
653
+ }
654
+ };
655
+ extract(getType(context.directoryPath, ast, path.parentPath.node.typeAnnotation?.typeAnnotation));
656
+ argument.properties.push(t.objectProperty(t.identifier(DECORATOR_PROPERTY_TYPE), t.numericLiteral(type)));
657
+ }
658
+ });
659
+ // attach typings
660
+ if (options.typings) {
661
+ visitor(ast, {
662
+ Program(path) {
663
+ const attributes = context
664
+ .classProperties.filter((property) => !t.isClassMethod(property))
665
+ .map((property) => {
666
+ const key = extractAttribute(property) || kebabCase(property.key['name']);
667
+ const typeAnnotation = property.typeAnnotation;
668
+ return Object.assign(t.tSPropertySignature(t.stringLiteral(kebabCase(key)), typeAnnotation), {
669
+ optional: property.optional,
670
+ leadingComments: t.cloneNode(property, true).leadingComments
671
+ });
672
+ });
673
+ const events = context.classEvents.map((event) => {
674
+ const key = event.key;
675
+ const typeAnnotation = event.typeAnnotation;
676
+ return Object.assign(t.tSPropertySignature(t.identifier(camelCase('on-' + key.name)), t.tsTypeAnnotation(t.tsFunctionType(undefined, [
677
+ Object.assign({}, t.identifier('event'), {
678
+ typeAnnotation: t.tsTypeAnnotation(t.tsTypeReference(t.identifier('CustomEvent'), typeAnnotation?.['typeAnnotation']?.typeParameters))
679
+ })
680
+ ], t.tsTypeAnnotation(t.tsVoidKeyword())))), {
681
+ optional: true,
682
+ leadingComments: t.cloneNode(event, true).leadingComments
683
+ });
684
+ });
685
+ const methods = context.classMethods.map((method) => {
686
+ return Object.assign(t.tsMethodSignature(method.key, undefined, method.params, // TODO
687
+ method.returnType // TODO
688
+ ), {
689
+ leadingComments: t.cloneNode(method, true).leadingComments
690
+ });
691
+ });
692
+ const properties = context.classProperties.map((property) => {
693
+ const key = property.key;
694
+ // TODO
695
+ const readonly = property.readonly || !!property['returnType'];
696
+ // TODO
697
+ const typeAnnotation = (property.typeAnnotation ||
698
+ property['returnType']);
699
+ return Object.assign(t.tsPropertySignature(t.identifier(key.name), typeAnnotation), {
700
+ readonly,
701
+ optional: property.optional,
702
+ leadingComments: t.cloneNode(property, true).leadingComments
703
+ });
704
+ });
705
+ // prettier-ignore
706
+ const ast = template.default.ast(`
707
+ // THE FOLLOWING TYPES HAVE BEEN ADDED AUTOMATICALLY
708
+
709
+ export interface ${context.className}Attributes {
710
+ ${attributes.map(print).join('')}
711
+ }
712
+
713
+ export interface ${context.className}Events {
714
+ ${events.map(print).join('')}
715
+ }
716
+
717
+ export interface ${context.className}Methods {
718
+ ${methods.map(print).join('')}
719
+ }
720
+
721
+ export interface ${context.className}Properties {
722
+ ${properties.map(print).join('')}
723
+ }
724
+
725
+ export interface ${context.className}JSX extends ${context.className}Events, ${context.className}Properties { }
726
+
727
+ declare global {
728
+ interface ${context.elementInterfaceName} extends HTMLElement, ${context.className}Methods, ${context.className}Properties { }
729
+
730
+ var ${context.elementInterfaceName}: {
731
+ prototype: ${context.elementInterfaceName};
732
+ new (): ${context.elementInterfaceName};
733
+ };
734
+
735
+ interface HTMLElementTagNameMap {
736
+ "${context.elementTagName}": ${context.elementInterfaceName};
737
+ }
738
+
739
+ namespace JSX {
740
+ interface IntrinsicElements {
741
+ "${context.elementTagName}": ${context.className}Events & ${context.className}Attributes & {
742
+ [key: string]: any;
743
+ };
744
+ }
745
+ }
746
+ }
747
+
748
+ export type ${context.className}Element = globalThis.${context.elementInterfaceName}
749
+ `, {
750
+ plugins: ['typescript'],
751
+ preserveComments: true
752
+ });
753
+ path.node.body.push(...ast);
754
+ }
755
+ });
756
+ }
757
+ context.script = print(ast);
758
+ };
759
+ return { name, run };
760
+ };
761
+
762
+ const DOCUMENT_OPTIONS = {
763
+ destination: path.join('dist', 'document.json')
764
+ };
765
+ const document = (options) => {
766
+ const name = 'document';
767
+ options = Object.assign({}, DOCUMENT_OPTIONS, options);
768
+ const finish = (global) => {
769
+ const json = {
770
+ elements: []
771
+ };
772
+ for (const context of global.contexts) {
773
+ const events = context.classEvents.map((event) => {
774
+ const cancelable = (() => {
775
+ if (!event.decorators)
776
+ return false;
777
+ try {
778
+ for (const decorator of event.decorators) {
779
+ for (const argument of decorator.expression['arguments']) {
780
+ for (const property of argument.properties) {
781
+ if (property.key.name != 'cancelable')
782
+ continue;
783
+ if (property.value.type != 'BooleanLiteral')
784
+ continue;
785
+ if (!property.value.value)
786
+ continue;
787
+ return true;
788
+ }
789
+ }
790
+ }
791
+ }
792
+ catch { }
793
+ return false;
794
+ })();
795
+ const detail = print(event.typeAnnotation?.['typeAnnotation']);
796
+ const detailReference = getTypeReference(context.fileAST, event.typeAnnotation?.['typeAnnotation'].typeParameters.params[0]);
797
+ const name = event.key['name'];
798
+ return Object.assign({
799
+ cancelable,
800
+ detail,
801
+ detailReference,
802
+ name
803
+ }, extractFromComment(event));
804
+ });
805
+ const lastModified = glob
806
+ .sync('**/*.*', { cwd: context.directoryPath })
807
+ .map((file) => fs.statSync(path.join(context.directoryPath, file)).mtime)
808
+ .sort((a, b) => (a > b ? 1 : -1))
809
+ .pop();
810
+ const methods = context.classMethods.map((method) => {
811
+ const async = method.async;
812
+ const name = method.key['name'];
813
+ const comments = extractFromComment(method);
814
+ // TODO
815
+ const parameters = method.params.map((param) => ({
816
+ description: comments.params?.find((item) => item.name == param['name'])?.description,
817
+ required: !param['optional'],
818
+ name: param['name'],
819
+ type: print(param?.['typeAnnotation']?.typeAnnotation) || undefined,
820
+ typeReference: getTypeReference(context.fileAST, param?.['typeAnnotation']?.typeAnnotation)
821
+ }));
822
+ // TODO
823
+ delete comments.params;
824
+ const returns = print(method.returnType?.['typeAnnotation']) || 'void';
825
+ const returnsReference = getTypeReference(context.fileAST, method.returnType?.['typeAnnotation']);
826
+ const signature = [
827
+ method.key['name'],
828
+ '(',
829
+ parameters
830
+ .map((parameter) => {
831
+ let string = '';
832
+ string += parameter.name;
833
+ string += parameter.required ? '' : '?';
834
+ string += parameter.type ? ': ' : '';
835
+ string += parameter.type ?? '';
836
+ return string;
837
+ })
838
+ .join(', '),
839
+ ')',
840
+ ' => ',
841
+ returns
842
+ ].join('');
843
+ return Object.assign({
844
+ async,
845
+ name,
846
+ parameters,
847
+ returns,
848
+ returnsReference,
849
+ signature
850
+ }, comments,
851
+ // TODO
852
+ {
853
+ returns
854
+ },
855
+ // TODO
856
+ returns != 'void' &&
857
+ comments.returns && {
858
+ tags: [
859
+ {
860
+ key: 'returns',
861
+ value: `${comments.returns}`
862
+ }
863
+ ]
864
+ });
865
+ });
866
+ const properties = context.classProperties.map((property) => {
867
+ const attribute = extractAttribute(property) || kebabCase(property.key['name']);
868
+ // TODO
869
+ const initializer = getInitializer(property.value);
870
+ const name = property.key['name'];
871
+ const readonly = property['kind'] == 'get';
872
+ // TODO
873
+ const reflects = (() => {
874
+ if (!property.decorators)
875
+ return false;
876
+ try {
877
+ for (const decorator of property.decorators) {
878
+ for (const argument of decorator.expression['arguments']) {
879
+ for (const property of argument.properties) {
880
+ if (property.key.name != 'reflect')
881
+ continue;
882
+ if (property.value.type != 'BooleanLiteral')
883
+ continue;
884
+ if (!property.value.value)
885
+ continue;
886
+ return true;
887
+ }
888
+ }
889
+ }
890
+ }
891
+ catch { }
892
+ return false;
893
+ })();
894
+ const required = 'optional' in property && !property.optional;
895
+ // TODO
896
+ const type = property['returnType']
897
+ ? print(property['returnType']?.['typeAnnotation'])
898
+ : print(property.typeAnnotation?.['typeAnnotation']);
899
+ const typeReference = getTypeReference(context.fileAST, property.typeAnnotation?.['typeAnnotation']);
900
+ return Object.assign({
901
+ attribute,
902
+ initializer,
903
+ name,
904
+ readonly,
905
+ reflects,
906
+ required,
907
+ type,
908
+ typeReference
909
+ }, extractFromComment(property));
910
+ });
911
+ // TODO
912
+ const styles = (() => {
913
+ if (!context.styleContent)
914
+ return [];
915
+ return context.styleContent
916
+ .split(DECORATOR_CSS_VARIABLE)
917
+ .slice(1)
918
+ .map((section) => {
919
+ const [first, second] = section.split(/\n/);
920
+ const description = first.replace('*/', '').trim();
921
+ const name = second.split(':')[0].trim();
922
+ const initializerDefault = second.split(':').slice(1).join(':').replace(';', '').trim();
923
+ // TODO
924
+ const initializerTransformed = context.styleContentTransformed
925
+ ?.split(name)
926
+ ?.at(1)
927
+ ?.split(':')
928
+ ?.filter((section) => !!section)
929
+ ?.at(0)
930
+ ?.split(/;|}/)
931
+ ?.at(0)
932
+ ?.trim();
933
+ const initializer = initializerTransformed || initializerDefault;
934
+ return {
935
+ description,
936
+ initializer,
937
+ name
938
+ };
939
+ });
940
+ })();
941
+ const title = capitalCase(context.elementKey);
942
+ const element = Object.assign({
943
+ events,
944
+ key: context.elementKey,
945
+ lastModified,
946
+ methods,
947
+ properties,
948
+ readmeContent: context.readmeContent,
949
+ styles,
950
+ title
951
+ }, extractFromComment(context.class));
952
+ const transformed = options.transformer?.(context, element) || element;
953
+ json.elements.push(transformed);
954
+ }
955
+ json.elements = json.elements.sort((a, b) => (a.title > b.title ? 1 : -1));
956
+ const dirname = path.dirname(options.destination);
957
+ fs.ensureDirSync(dirname);
958
+ fs.writeJSONSync(options.destination, json, { encoding: 'utf8', spaces: 2 });
959
+ };
960
+ return { name, finish };
961
+ };
962
+
963
+ const extract = () => {
964
+ const name = 'extract';
965
+ const run = (context) => {
966
+ const { declaration, leadingComments } = context.fileAST?.program.body.find((child) => {
967
+ return t.isExportNamedDeclaration(child);
968
+ });
969
+ context.class = declaration;
970
+ context.class.leadingComments = leadingComments; // TODO
971
+ context.classMembers = context.class?.body?.body || [];
972
+ context.className = context.class?.id?.name;
973
+ context.elementKey = kebabCase(context.className || '');
974
+ context.classEvents = context.classMembers.filter((member) => hasDecorator(member, DECORATOR_EVENT));
975
+ context.classMethods = context.classMembers.filter((member) => hasDecorator(member, DECORATOR_METHOD));
976
+ context.classProperties = context.classMembers.filter((member) => hasDecorator(member, DECORATOR_PROPERTY));
977
+ context.classStates = context.classMembers.filter((member) => hasDecorator(member, DECORATOR_STATE));
978
+ };
979
+ return { name, run };
980
+ };
981
+
982
+ const PARSE_OPTIONS = {
983
+ sourceType: 'module',
984
+ plugins: [['decorators', { decoratorsBeforeExport: true }], 'jsx', 'typescript']
985
+ };
986
+ const parse = (options) => {
987
+ const name = 'parse';
988
+ options = Object.assign({}, PARSE_OPTIONS, options);
989
+ const run = (context) => {
990
+ context.fileAST = parse$1(context.fileContent, options);
991
+ };
992
+ return { name, run };
993
+ };
994
+
995
+ const READ_OPTIONS = {
996
+ encoding: 'utf8'
997
+ };
998
+ const read = (options) => {
999
+ const name = 'read';
1000
+ options = Object.assign({}, READ_OPTIONS, options);
1001
+ const run = (context) => {
1002
+ if (!context.filePath)
1003
+ return;
1004
+ context.fileContent = fs.readFileSync(context.filePath, options);
1005
+ context.fileExtension = path.extname(context.filePath);
1006
+ context.fileName = path.basename(context.filePath, context.fileExtension);
1007
+ context.directoryPath = path.dirname(context.filePath);
1008
+ context.directoryName = path.basename(context.directoryPath);
1009
+ };
1010
+ return { name, run };
1011
+ };
1012
+
1013
+ const README_OPTIONS = {
1014
+ source(context) {
1015
+ return path.join(context.directoryPath, `${context.fileName}.md`);
1016
+ }
1017
+ };
1018
+ const readme = (options) => {
1019
+ const name = 'readme';
1020
+ options = Object.assign({}, README_OPTIONS, options);
1021
+ const finish = (global) => {
1022
+ for (const context of global.contexts) {
1023
+ context.readmePath = options.source(context);
1024
+ if (!context.readmePath)
1025
+ continue;
1026
+ if (!fs.existsSync(context.readmePath))
1027
+ continue;
1028
+ context.readmeContent = fs.readFileSync(context.readmePath, 'utf8');
1029
+ context.readmeExtension = path.extname(context.readmePath);
1030
+ context.readmeName = path.basename(context.readmePath, context.readmeExtension);
1031
+ }
1032
+ };
1033
+ return { name, finish };
1034
+ };
1035
+
1036
+ const STYLE_OPTIONS = {
1037
+ source(context) {
1038
+ return [
1039
+ path.join(context.directoryPath, `${context.fileName}.css`),
1040
+ path.join(context.directoryPath, `${context.fileName}.less`),
1041
+ path.join(context.directoryPath, `${context.fileName}.sass`),
1042
+ path.join(context.directoryPath, `${context.fileName}.scss`),
1043
+ path.join(context.directoryPath, `${context.fileName}.styl`)
1044
+ ];
1045
+ }
1046
+ };
1047
+ const style = (options) => {
1048
+ const name = 'style';
1049
+ options = Object.assign({}, STYLE_OPTIONS, options);
1050
+ const run = (context) => {
1051
+ const sources = [options.source(context)].flat();
1052
+ for (const source of sources) {
1053
+ if (!source)
1054
+ continue;
1055
+ if (!fs.existsSync(source))
1056
+ continue;
1057
+ context.stylePath = source;
1058
+ break;
1059
+ }
1060
+ if (!context.stylePath)
1061
+ return;
1062
+ context.styleContent = fs.readFileSync(context.stylePath, 'utf8');
1063
+ context.styleExtension = path.extname(context.stylePath);
1064
+ context.styleName = path.basename(context.stylePath, context.styleExtension);
1065
+ const { local } = addDependency(context.fileAST, context.stylePath, STYLE_IMPORTED, undefined, true);
1066
+ // TODO: remove 'local!'
1067
+ const property = t.classProperty(t.identifier(STATIC_STYLE), t.identifier(local), undefined, null, undefined, true);
1068
+ t.addComment(property, 'leading', COMMENT_AUTO_ADDED, true);
1069
+ context.class.body.body.unshift(property);
1070
+ };
1071
+ return { name, run };
1072
+ };
1073
+
1074
+ const validate = () => {
1075
+ const name = 'validate';
1076
+ const run = (context) => {
1077
+ context.skipped = true;
1078
+ visitor(context.fileAST, {
1079
+ ImportDeclaration(path) {
1080
+ if (path.node.source?.value !== PACKAGE_NAME)
1081
+ return;
1082
+ for (const specifier of path.node.specifiers) {
1083
+ if (specifier.imported.name !== DECORATOR_ELEMENT)
1084
+ continue;
1085
+ const binding = path.scope.getBinding(specifier.imported.name);
1086
+ if (binding.references == 0)
1087
+ break;
1088
+ const referencePaths = binding.referencePaths.filter((referencePath) => {
1089
+ if (t.isCallExpression(referencePath.parent) &&
1090
+ t.isDecorator(referencePath.parentPath.parent) &&
1091
+ t.isClassDeclaration(referencePath.parentPath.parentPath.parent) &&
1092
+ t.isExportNamedDeclaration(referencePath.parentPath.parentPath.parentPath.parent))
1093
+ return true;
1094
+ });
1095
+ if (referencePaths.length > 1) {
1096
+ throw new Error('In each file, only one custom element can be defined. \n' +
1097
+ 'If more than one @Element() decorator is used in the file, it will result in an error.\n');
1098
+ }
1099
+ context.skipped = false;
1100
+ if (referencePaths.length == 1)
1101
+ break;
1102
+ throw new Error('It appears that the class annotated with the @Element() decorator is not being exported correctly.');
1103
+ }
1104
+ path.stop();
1105
+ }
1106
+ });
1107
+ context.skipped;
1108
+ };
1109
+ return { name, run };
1110
+ };
1111
+
1112
+ const VISUAL_STUDIO_CODE_OPTIONS = {
1113
+ destination: path.join('dist', 'visual-studio-code.json')
1114
+ };
1115
+ const visualStudioCode = (options) => {
1116
+ const name = 'visualStudioCode';
1117
+ options = Object.assign({}, VISUAL_STUDIO_CODE_OPTIONS, options);
1118
+ const finish = (global) => {
1119
+ const contexts = global.contexts.sort((a, b) => {
1120
+ return a.elementKey.toUpperCase() > b.elementKey.toUpperCase() ? +1 : -1;
1121
+ });
1122
+ const json = {
1123
+ $schema: 'TODO',
1124
+ version: 1.1,
1125
+ tags: []
1126
+ };
1127
+ for (const context of contexts) {
1128
+ const tag = Object.assign({
1129
+ name: context.elementKey,
1130
+ attributes: [],
1131
+ references: [
1132
+ {
1133
+ name: 'Source code',
1134
+ url: options.reference?.(context)
1135
+ }
1136
+ ]
1137
+ }, extractFromComment(context.class, ['description']));
1138
+ for (const property of context.classProperties || []) {
1139
+ const attribute = Object.assign({
1140
+ name: extractAttribute(property) || kebabCase(property.key['name']),
1141
+ values: []
1142
+ }, extractFromComment(property, ['description']));
1143
+ const type = print(getType(context.directoryPath, context.fileAST, property.typeAnnotation?.['typeAnnotation']));
1144
+ const sections = type.split('|');
1145
+ for (const section of sections) {
1146
+ const trimmed = section.trim();
1147
+ if (!trimmed)
1148
+ continue;
1149
+ const isBoolean = /bool|boolean|Boolean/.test(trimmed);
1150
+ const isNumber = !isNaN(trimmed);
1151
+ const isString = /^("|'|`)/.test(trimmed);
1152
+ if (isBoolean) {
1153
+ attribute.values.push({
1154
+ name: 'false'
1155
+ }, {
1156
+ name: 'true'
1157
+ });
1158
+ }
1159
+ else if (isNumber) {
1160
+ attribute.values.push({
1161
+ name: trimmed
1162
+ });
1163
+ }
1164
+ else if (isString) {
1165
+ attribute.values.push({
1166
+ name: trimmed.slice(1, -1)
1167
+ });
1168
+ }
1169
+ }
1170
+ tag.attributes.push(attribute);
1171
+ }
1172
+ const transformed = options.transformer?.(context, tag) || tag;
1173
+ json.tags.push(transformed);
1174
+ }
1175
+ const dirname = path.dirname(options.destination);
1176
+ fs.ensureDirSync(dirname);
1177
+ fs.writeJSONSync(options.destination, json, { encoding: 'utf8', spaces: 2 });
1178
+ };
1179
+ return { name, finish };
1180
+ };
1181
+
1182
+ const WEB_TYPES_OPTIONS = {
1183
+ destination: path.join('dist', 'web-types.json'),
1184
+ packageName: '',
1185
+ packageVersion: ''
1186
+ };
1187
+ const webTypes = (options) => {
1188
+ const name = 'webTypes';
1189
+ options = Object.assign({}, WEB_TYPES_OPTIONS, options);
1190
+ const finish = (global) => {
1191
+ const contexts = global.contexts.sort((a, b) => {
1192
+ return a.elementKey.toUpperCase() > b.elementKey.toUpperCase() ? +1 : -1;
1193
+ });
1194
+ const json = {
1195
+ '$schema': 'http://json.schemastore.org/web-types',
1196
+ 'name': options.packageName,
1197
+ 'version': options.packageVersion,
1198
+ 'description-markup': 'markdown',
1199
+ 'framework-config': {
1200
+ 'enable-when': {
1201
+ 'node-packages': [options.packageName]
1202
+ }
1203
+ },
1204
+ 'contributions': {
1205
+ html: {
1206
+ elements: []
1207
+ }
1208
+ }
1209
+ };
1210
+ for (const context of contexts) {
1211
+ const attributes = context.classProperties?.map((property) => Object.assign({
1212
+ name: extractAttribute(property) || kebabCase(property.key['name']),
1213
+ value: {
1214
+ // kind: TODO
1215
+ type: print(getType(context.directoryPath, context.fileAST, property.typeAnnotation?.['typeAnnotation']))
1216
+ // required: TODO
1217
+ // default: TODO
1218
+ },
1219
+ default: getInitializer(property.value)
1220
+ }, extractFromComment(property, ['description', 'deprecated', 'experimental'])));
1221
+ const events = context.classEvents?.map((event) => Object.assign({
1222
+ name: kebabCase(event.key['name']) // TODO
1223
+ // 'value': TODO
1224
+ }, extractFromComment(event, ['description', 'deprecated', 'experimental'])));
1225
+ const methods = context.classMethods?.map((method) => Object.assign({
1226
+ name: method.key['name']
1227
+ // 'value': TODO
1228
+ }, extractFromComment(method, ['description', 'deprecated', 'experimental'])));
1229
+ const properties = context.classProperties?.map((property) => Object.assign({
1230
+ name: property.key['name'],
1231
+ // 'value': TODO
1232
+ default: getInitializer(property.value)
1233
+ }, extractFromComment(property, ['description', 'deprecated', 'experimental'])));
1234
+ const element = Object.assign({
1235
+ 'name': context.elementKey,
1236
+ 'doc-url': options.reference?.(context),
1237
+ 'js': {
1238
+ events,
1239
+ properties: [].concat(properties, methods)
1240
+ },
1241
+ attributes
1242
+ }, extractFromComment(context.class, ['description', 'deprecated', 'experimental', 'slots']));
1243
+ const transformed = options.transformer?.(context, element) || element;
1244
+ json.contributions.html.elements.push(transformed);
1245
+ }
1246
+ const dirname = path.dirname(options.destination);
1247
+ fs.ensureDirSync(dirname);
1248
+ fs.writeJSONSync(options.destination, json, { encoding: 'utf8', spaces: 2 });
1249
+ };
1250
+ return { name, finish };
1251
+ };
1252
+
1253
+ export { ASSETS_OPTIONS, COPY_OPTIONS, CUSTOM_ELEMENT_OPTIONS, DOCUMENT_OPTIONS, PARSE_OPTIONS, README_OPTIONS, READ_OPTIONS, STYLE_OPTIONS, VISUAL_STUDIO_CODE_OPTIONS, WEB_TYPES_OPTIONS, assets, copy, customElement, document, extract, parse, read, readme, style, transformer, validate, visualStudioCode, webTypes };