@arcmantle/lit-jsx 1.0.5 → 1.0.7

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 (55) hide show
  1. package/README.md +22 -16
  2. package/dist/compiler/babel-plugin.d.ts +4 -0
  3. package/dist/compiler/babel-plugin.d.ts.map +1 -0
  4. package/dist/compiler/babel-plugin.js +20 -0
  5. package/dist/compiler/babel-plugin.js.map +1 -0
  6. package/dist/compiler/babel-traverse.d.ts +3 -0
  7. package/dist/compiler/babel-traverse.d.ts.map +1 -0
  8. package/dist/compiler/babel-traverse.js +7 -0
  9. package/dist/compiler/babel-traverse.js.map +1 -0
  10. package/dist/compiler/compiler-utils.d.ts +3 -2
  11. package/dist/compiler/compiler-utils.d.ts.map +1 -1
  12. package/dist/compiler/compiler-utils.js +25 -23
  13. package/dist/compiler/compiler-utils.js.map +1 -1
  14. package/dist/compiler/config.d.ts +5 -1
  15. package/dist/compiler/config.d.ts.map +1 -1
  16. package/dist/compiler/config.js +2 -2
  17. package/dist/compiler/config.js.map +1 -1
  18. package/dist/compiler/create-logger.d.ts +3 -0
  19. package/dist/compiler/create-logger.d.ts.map +1 -0
  20. package/dist/compiler/create-logger.js +32 -0
  21. package/dist/compiler/create-logger.js.map +1 -0
  22. package/dist/compiler/import-discovery.d.ts +51 -0
  23. package/dist/compiler/import-discovery.d.ts.map +1 -0
  24. package/dist/compiler/import-discovery.js +325 -0
  25. package/dist/compiler/import-discovery.js.map +1 -0
  26. package/dist/compiler/preprocess.d.ts +1 -1
  27. package/dist/compiler/preprocess.d.ts.map +1 -1
  28. package/dist/compiler/preprocess.js +5 -4
  29. package/dist/compiler/preprocess.js.map +1 -1
  30. package/dist/compiler/transpiler.js +4 -4
  31. package/dist/compiler/transpiler.js.map +1 -1
  32. package/dist/compiler/vite-plugin.d.ts +1 -0
  33. package/dist/compiler/vite-plugin.d.ts.map +1 -1
  34. package/dist/compiler/vite-plugin.js +9 -13
  35. package/dist/compiler/vite-plugin.js.map +1 -1
  36. package/dist/runtime/type-helpers.d.ts +24 -36
  37. package/dist/runtime/type-helpers.d.ts.map +1 -1
  38. package/dist/runtime/type-helpers.js +22 -34
  39. package/dist/runtime/type-helpers.js.map +1 -1
  40. package/package.json +5 -2
  41. package/src/compiler/babel-plugin.ts +23 -0
  42. package/src/compiler/babel-traverse.ts +7 -0
  43. package/src/compiler/compiler-utils.ts +35 -25
  44. package/src/compiler/config.ts +7 -2
  45. package/src/compiler/create-logger.ts +35 -0
  46. package/src/compiler/import-discovery.ts +460 -0
  47. package/src/compiler/preprocess.ts +9 -7
  48. package/src/compiler/transpiler.ts +4 -4
  49. package/src/compiler/vite-plugin.ts +16 -22
  50. package/src/runtime/type-helpers.ts +24 -36
  51. package/dist/compiler/babel-preset.d.ts +0 -6
  52. package/dist/compiler/babel-preset.d.ts.map +0 -1
  53. package/dist/compiler/babel-preset.js +0 -27
  54. package/dist/compiler/babel-preset.js.map +0 -1
  55. package/src/compiler/babel-preset.ts +0 -34
@@ -0,0 +1,460 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+
4
+ import * as babel from '@babel/core';
5
+ import type { Binding, NodePath, Scope } from '@babel/traverse';
6
+ import * as t from '@babel/types';
7
+ import oxcResolver from 'oxc-resolver';
8
+ import type { Logger } from 'pino';
9
+
10
+ import { traverse } from './babel-traverse.ts';
11
+ import { getPathFilename, isComponent } from './compiler-utils.ts';
12
+ import { babelPlugins, debugMode } from './config.ts';
13
+ import { createLogger } from './create-logger.ts';
14
+
15
+
16
+ export interface ElementDefinition {
17
+ type: 'custom-element' | 'import' | 'local-variable' | 'wildcard-export' | 'unknown';
18
+ source?: string;
19
+ originalName?: string;
20
+ localName?: string;
21
+ callExpression?: t.CallExpression;
22
+ resolvedPath?: string; // For lazy import resolution
23
+ referencedName?: string; // For lazy local reference resolution
24
+ }
25
+
26
+ interface FileSystemAdapter {
27
+ existsSync: (path: string) => boolean;
28
+ readFileSync: (path: string, encoding: 'utf-8') => string;
29
+ dirname: (path: string) => string;
30
+ }
31
+
32
+ interface ResolverAdapter {
33
+ sync: (basedir: string, module: string) => { path?: string; };
34
+ }
35
+
36
+
37
+ export class ImportDiscovery {
38
+
39
+ static readonly definitionCache: Map<string, Map<string, ElementDefinition>> = new Map();
40
+ static readonly fileBindingsCache: Map<string, ReadonlyMap<string, ElementDefinition>> = new Map();
41
+ static readonly fileDependencies: Map<string, Set<string>> = new Map();
42
+ static readonly EMPTY_BINDINGS: ReadonlyMap<string, ElementDefinition> = new Map();
43
+
44
+ static clearCacheForFileAndDependents(changedFilePath: string): void {
45
+ // Clear the changed file itself
46
+ ImportDiscovery.definitionCache.delete(changedFilePath);
47
+ ImportDiscovery.fileBindingsCache.delete(changedFilePath);
48
+ ImportDiscovery.fileDependencies.delete(changedFilePath);
49
+
50
+ // Find and clear all files that depend on the changed file in one pass
51
+ for (const [ file, dependencies ] of ImportDiscovery.fileDependencies) {
52
+ if (!dependencies.has(changedFilePath))
53
+ continue;
54
+
55
+ ImportDiscovery.definitionCache.delete(file);
56
+ ImportDiscovery.fileBindingsCache.delete(file);
57
+ ImportDiscovery.fileDependencies.delete(file);
58
+ }
59
+ }
60
+
61
+ protected readonly visitedFiles: Set<string> = new Set();
62
+ protected readonly resolver: ResolverAdapter;
63
+ protected readonly log: Logger<never, boolean>;
64
+ protected readonly fs: FileSystemAdapter;
65
+
66
+ constructor() {
67
+ this.resolver = oxcResolver;
68
+ this.log = createLogger('import-discovery', debugMode.value);
69
+ this.fs = { existsSync, readFileSync, dirname };
70
+ }
71
+
72
+ /**
73
+ * Finds the definition of a JSX element in the given path.
74
+ */
75
+ findElementDefinition(
76
+ path: NodePath<t.JSXOpeningElement>,
77
+ ): ElementDefinition {
78
+ this.visitedFiles.clear();
79
+
80
+ const filePath = getPathFilename(path);
81
+ const cacheKey = String(path.node.start);
82
+
83
+ const fileCache = ImportDiscovery.definitionCache.get(filePath);
84
+ if (fileCache) {
85
+ const cached = fileCache.get(cacheKey);
86
+ if (cached)
87
+ return cached;
88
+ }
89
+
90
+ if (!t.isJSXIdentifier(path.node.name))
91
+ return { type: 'unknown' };
92
+
93
+ const elementName = path.node.name.name;
94
+ if (!isComponent(elementName))
95
+ return { type: 'unknown' };
96
+
97
+ const currentFileName = getPathFilename(path);
98
+ const result = this.traceElementDefinition(elementName, path.scope, currentFileName);
99
+
100
+ // Store in file-specific cache
101
+ const definitionCache = ImportDiscovery.definitionCache.get(filePath)
102
+ ?? ImportDiscovery.definitionCache
103
+ .set(filePath, new Map())
104
+ .get(filePath)!;
105
+
106
+ definitionCache.set(cacheKey, result);
107
+
108
+ return result;
109
+ }
110
+
111
+ // Trace the element definition recursively
112
+ protected traceElementDefinition(
113
+ elementName: string,
114
+ scope: Scope,
115
+ currentFileName: string,
116
+ ): ElementDefinition {
117
+ const traceKey = `${ currentFileName }:${ elementName }`;
118
+
119
+ // Prevent infinite recursion
120
+ if (this.visitedFiles.has(traceKey))
121
+ return { type: 'unknown' };
122
+
123
+ this.visitedFiles.add(traceKey);
124
+
125
+ // Use batched file analysis
126
+ const fileBindings = this.analyzeFileBindings(currentFileName);
127
+
128
+ // Check if we have this element in our batch analysis
129
+ if (fileBindings.has(elementName)) {
130
+ const definition = fileBindings.get(elementName)!;
131
+
132
+ // Resolve any lazy references
133
+ return this.resolveLazyDefinition(definition);
134
+ }
135
+
136
+ // Fallback to scope-based lookup for dynamic cases
137
+ const binding = scope.getBinding(elementName);
138
+ if (!binding)
139
+ return { type: 'unknown' };
140
+
141
+ // Use the fast analysis methods
142
+ const result = this.analyzeBindingFast(binding, currentFileName);
143
+
144
+ // Resolve any lazy references (imports, local references)
145
+ return this.resolveLazyDefinition(result);
146
+ }
147
+
148
+ // Analyze all relevant bindings in a file at once
149
+ protected analyzeFileBindings(filePath: string): ReadonlyMap<string, ElementDefinition> {
150
+ const fileBinding = ImportDiscovery.fileBindingsCache.get(filePath);
151
+ if (fileBinding)
152
+ return fileBinding;
153
+
154
+ if (!this.fs.existsSync(filePath)) {
155
+ ImportDiscovery.fileBindingsCache.set(filePath, ImportDiscovery.EMPTY_BINDINGS);
156
+
157
+ return ImportDiscovery.EMPTY_BINDINGS;
158
+ }
159
+
160
+ const fileContent = this.fs.readFileSync(filePath, 'utf-8');
161
+ let ast: t.File;
162
+
163
+ try {
164
+ ast = babel.parseSync(fileContent, {
165
+ filename: filePath,
166
+ parserOpts: {
167
+ plugins: babelPlugins,
168
+ },
169
+ })!;
170
+ }
171
+ catch (error) {
172
+ // Failed to parse, cache empty result
173
+ ImportDiscovery.fileBindingsCache.set(filePath, ImportDiscovery.EMPTY_BINDINGS);
174
+
175
+ return ImportDiscovery.EMPTY_BINDINGS;
176
+ }
177
+
178
+ let programPath: babel.NodePath<babel.types.Program> = undefined as any;
179
+ traverse(ast, { Program(path) { programPath = path; path.stop(); } });
180
+
181
+ const bindings: Map<string, ElementDefinition> = new Map();
182
+
183
+ // 1. Analyze all relevant local bindings at once
184
+ this.analyzeFileDeclarations(programPath, filePath, bindings);
185
+
186
+ // 2. Analyze all exports at once
187
+ this.analyzeFileExports(programPath, filePath, bindings);
188
+
189
+ const readonlyBindings = new Map(bindings) as ReadonlyMap<string, ElementDefinition>;
190
+ ImportDiscovery.fileBindingsCache.set(filePath, readonlyBindings);
191
+
192
+ return readonlyBindings;
193
+ }
194
+
195
+ // Resolve lazy references in the definition
196
+ protected analyzeFileDeclarations(
197
+ programPath: babel.NodePath<babel.types.Program>,
198
+ filePath: string,
199
+ bindings: Map<string, ElementDefinition>,
200
+ ): void {
201
+ for (const [ name, binding ] of Object.entries(programPath.scope.bindings)) {
202
+ // Skip function/import bindings that are clearly not component-related
203
+ if (binding.kind === 'module' || binding.kind === 'hoisted') {
204
+ if (!isComponent(name))
205
+ continue;
206
+ }
207
+
208
+ const definition = this.analyzeBindingFast(binding, filePath);
209
+ if (definition.type !== 'unknown')
210
+ bindings.set(name, definition);
211
+ }
212
+ }
213
+
214
+ // Fast binding analysis without deep traversal
215
+ protected analyzeBindingFast(
216
+ binding: Binding,
217
+ filePath: string,
218
+ ): ElementDefinition {
219
+ // Handle imports
220
+ if (binding.kind === 'module' && t.isImportSpecifier(binding.path.node))
221
+ return this.analyzeImportBinding(binding, filePath);
222
+
223
+ // Handle local variables
224
+ if (binding.kind === 'const' || binding.kind === 'let' || binding.kind === 'var')
225
+ return this.analyzeLocalBinding(binding, filePath);
226
+
227
+ return { type: 'unknown' };
228
+ }
229
+
230
+ // Analyze import without deep file traversal
231
+ protected analyzeImportBinding(binding: Binding, currentFileName: string): ElementDefinition {
232
+ const importDeclaration = binding.path.parent;
233
+ if (!t.isImportDeclaration(importDeclaration))
234
+ return { type: 'unknown' };
235
+
236
+ const importSpecifier = binding.path.node;
237
+ if (!t.isImportSpecifier(importSpecifier))
238
+ return { type: 'unknown' };
239
+
240
+ const originalExportedName = t.isIdentifier(importSpecifier.imported)
241
+ ? importSpecifier.imported.name
242
+ : importSpecifier.imported.value;
243
+
244
+ const importSource = importDeclaration.source.value;
245
+ const currentDir = dirname(currentFileName);
246
+
247
+ const resolvedResult = this.resolver.sync(currentDir, importSource);
248
+ const resolvedPath = resolvedResult.path;
249
+
250
+ if (!resolvedPath)
251
+ return { type: 'unknown' };
252
+
253
+ // Instead of deep traversal, just mark as import and resolve lazily
254
+ return {
255
+ type: 'import',
256
+ source: importSource,
257
+ originalName: originalExportedName,
258
+ localName: binding.identifier.name,
259
+ // Store resolved path for later lookup
260
+ resolvedPath: resolvedPath,
261
+ };
262
+ }
263
+
264
+ // Analyze local binding without recursion
265
+ protected analyzeLocalBinding(binding: Binding, filePath: string): ElementDefinition {
266
+ if (!t.isVariableDeclarator(binding.path.node))
267
+ return { type: 'local-variable' };
268
+
269
+
270
+ const declarator = binding.path.node;
271
+ if (!declarator.init)
272
+ return { type: 'local-variable' };
273
+
274
+
275
+ // Check for toComponent/toTag calls
276
+ if (t.isCallExpression(declarator.init)) {
277
+ if (this.isToComponentOrTagCall(declarator.init, binding.path.scope)) {
278
+ return {
279
+ type: 'custom-element',
280
+ source: filePath,
281
+ callExpression: declarator.init,
282
+ };
283
+ }
284
+ }
285
+
286
+ // For identifier assignments, store reference for later resolution
287
+ if (t.isIdentifier(declarator.init)) {
288
+ return {
289
+ type: 'local-variable',
290
+ source: filePath,
291
+ referencedName: declarator.init.name,
292
+ };
293
+ }
294
+
295
+ return { type: 'local-variable' };
296
+ }
297
+
298
+ // Helper function to check if a call expression is a toComponent/toTag call
299
+ // even if the function has been renamed through imports
300
+ protected isToComponentOrTagCall(
301
+ callExpr: t.CallExpression,
302
+ scope: Scope,
303
+ ): boolean {
304
+ if (!t.isIdentifier(callExpr.callee))
305
+ return false;
306
+
307
+ const functionName = callExpr.callee.name;
308
+
309
+ // Check direct names first (fast path)
310
+ if (functionName === 'toComponent' || functionName === 'toTag')
311
+ return true;
312
+
313
+ // Check if this identifier is bound to an import that originally was toComponent/toTag
314
+ const binding = scope.getBinding(functionName);
315
+ if (!binding || binding.kind !== 'module')
316
+ return false;
317
+
318
+ if (!t.isImportSpecifier(binding.path.node))
319
+ return false;
320
+
321
+ const importSpecifier = binding.path.node;
322
+ const originalImportedName = t.isIdentifier(importSpecifier.imported)
323
+ ? importSpecifier.imported.name
324
+ : importSpecifier.imported.value;
325
+
326
+ // Check if the original imported name was toComponent or toTag
327
+ const isOriginallyToComponentOrTag =
328
+ originalImportedName === 'toComponent'
329
+ || originalImportedName === 'toTag';
330
+
331
+ return isOriginallyToComponentOrTag;
332
+ }
333
+
334
+ // Analyze all exports in one pass
335
+ protected analyzeFileExports(
336
+ programPath: babel.NodePath<babel.types.Program>,
337
+ filePath: string,
338
+ bindings: Map<string, ElementDefinition>,
339
+ ): void {
340
+ for (const statement of programPath.node.body) {
341
+ // Handle named exports: export { X } from './file' or export { X }
342
+ if (t.isExportNamedDeclaration(statement)) {
343
+ for (const specifier of statement.specifiers) {
344
+ if (!t.isExportSpecifier(specifier))
345
+ continue;
346
+
347
+ const exportedName = t.isIdentifier(specifier.exported)
348
+ ? specifier.exported.name
349
+ : specifier.exported.value;
350
+
351
+ const localName = specifier.local.name;
352
+
353
+ if (!isComponent(exportedName))
354
+ continue;
355
+
356
+ // For re-exports with source
357
+ if (statement.source) {
358
+ const definition = {
359
+ type: 'import' as const,
360
+ source: statement.source.value,
361
+ originalName: localName,
362
+ localName: exportedName,
363
+ };
364
+ bindings.set(exportedName, definition);
365
+ }
366
+ else {
367
+ // For local exports, reference the local binding
368
+ const definition = {
369
+ type: 'local-variable' as const,
370
+ source: filePath,
371
+ referencedName: localName,
372
+ localName: exportedName,
373
+ };
374
+ bindings.set(exportedName, definition);
375
+ }
376
+ }
377
+ }
378
+ // Handle wildcard exports: export * from './file'
379
+ else if (t.isExportAllDeclaration(statement) && statement.source) {
380
+ // For wildcard exports, we need to mark this as a wildcard re-export
381
+ // The actual resolution will happen in resolveLazyDefinition
382
+ const definition = {
383
+ type: 'wildcard-export' as const,
384
+ source: statement.source.value,
385
+ };
386
+ // Store with a special key to indicate wildcard export
387
+ bindings.set('*', definition);
388
+ }
389
+ }
390
+ }
391
+
392
+ // Resolve lazy definitions (imports, references)
393
+ protected resolveLazyDefinition(definition: ElementDefinition): ElementDefinition {
394
+ if (definition.type === 'import' && definition.resolvedPath && definition.originalName) {
395
+ if (definition.source) {
396
+ const currentFile = definition.source;
397
+ const dependsOn = definition.resolvedPath;
398
+
399
+ let fileDependencies = ImportDiscovery.fileDependencies.get(currentFile);
400
+ if (!fileDependencies) {
401
+ fileDependencies = new Set<string>();
402
+ ImportDiscovery.fileDependencies.set(currentFile, fileDependencies);
403
+ }
404
+
405
+ fileDependencies.add(dependsOn);
406
+ }
407
+
408
+ // Recursively analyze the imported file
409
+ const importedBindings = this.analyzeFileBindings(definition.resolvedPath);
410
+ const binding = importedBindings.get(definition.originalName);
411
+
412
+ if (binding) // Recursively resolve the found definition
413
+ return this.resolveLazyDefinition(binding);
414
+
415
+ // If specific export not found, check for wildcard exports
416
+ const wildcardExport = importedBindings.get('*');
417
+ if (wildcardExport && wildcardExport.type === 'wildcard-export') {
418
+ // Resolve the wildcard export by looking in the target file
419
+ const currentDir = this.fs.dirname(definition.resolvedPath);
420
+ const resolvedResult = this.resolver.sync(currentDir, wildcardExport.source!);
421
+ const resolvedPath = resolvedResult.path;
422
+
423
+ if (resolvedPath) {
424
+ // Create a new import definition for the wildcard target
425
+ const wildcardTargetDefinition: ElementDefinition = {
426
+ type: 'import',
427
+ source: wildcardExport.source,
428
+ originalName: definition.originalName,
429
+ localName: definition.localName,
430
+ resolvedPath: resolvedPath,
431
+ };
432
+
433
+ return this.resolveLazyDefinition(wildcardTargetDefinition);
434
+ }
435
+ }
436
+ }
437
+
438
+ if (definition.type === 'local-variable' && definition.referencedName && definition.source) {
439
+ // Resolve local references
440
+ const fileBindings = this.analyzeFileBindings(definition.source);
441
+ const binding = fileBindings.get(definition.referencedName);
442
+
443
+ if (binding)
444
+ return this.resolveLazyDefinition(binding);
445
+ }
446
+
447
+ return definition;
448
+ }
449
+
450
+ }
451
+
452
+
453
+ let discovery: ImportDiscovery;
454
+ export const findElementDefinition = (
455
+ ...args: Parameters<ImportDiscovery['findElementDefinition']>
456
+ ): ReturnType<ImportDiscovery['findElementDefinition']> => {
457
+ discovery ??= new ImportDiscovery();
458
+
459
+ return discovery.findElementDefinition(...args);
460
+ };
@@ -1,14 +1,16 @@
1
1
  import type { PluginPass } from '@babel/core';
2
- import type { VisitNodeFunction } from '@babel/traverse';
3
- import { isJSXElement, isJSXIdentifier, type Program } from '@babel/types';
2
+ import type { NodePath, VisitNodeFunction } from '@babel/traverse';
3
+ import type { JSXElement, Program } from '@babel/types';
4
+ import { isJSXElement, isJSXIdentifier } from '@babel/types';
4
5
  import { isValidHTMLNesting } from 'validate-html-nesting';
5
6
 
6
7
  import { isComponent } from './compiler-utils.js';
7
8
 
8
9
 
9
- // From https://github.com/MananTank/babel-plugin-validate-jsx-nesting/blob/main/src/index.js
10
- const JSXValidator = {
11
- JSXElement(path: any) {
10
+ const preprocessVisitors = {
11
+ // From https://github.com/MananTank/babel-plugin-validate-jsx-nesting/blob/main/src/index.js
12
+ // Validates JSX nesting based on HTML5 rules.
13
+ JSXElement(path: NodePath<JSXElement>) {
12
14
  const elName = path.node.openingElement.name;
13
15
  const parent = path.parent;
14
16
 
@@ -35,6 +37,6 @@ const JSXValidator = {
35
37
  };
36
38
 
37
39
 
38
- export const preprocess: VisitNodeFunction<PluginPass, Program> = (path, state): void => {
39
- path.traverse(JSXValidator);
40
+ export const preprocess: VisitNodeFunction<PluginPass, Program> = (path): void => {
41
+ path.traverse(preprocessVisitors);
40
42
  };
@@ -92,7 +92,7 @@ export class TemplateTranspiler extends JSXTranspiler<TemplateContext> {
92
92
 
93
93
  context.tagName = getJSXElementName(context.path.node);
94
94
 
95
- if (isJSXCustomElementComponent(context.tagName)) {
95
+ if (isJSXCustomElementComponent(context.path)) {
96
96
  this.openingTag(context);
97
97
  this.attributes(context);
98
98
  this.children(context);
@@ -101,7 +101,7 @@ export class TemplateTranspiler extends JSXTranspiler<TemplateContext> {
101
101
  return;
102
102
  }
103
103
 
104
- if (isJSXFunctionElementComponent(context.tagName)) {
104
+ if (isJSXFunctionElementComponent(context.path)) {
105
105
  // Process attributes and children into a props object
106
106
  if (!context.isInitialElement)
107
107
  return this.functionalComponent(context);
@@ -118,7 +118,7 @@ export class TemplateTranspiler extends JSXTranspiler<TemplateContext> {
118
118
  }
119
119
 
120
120
  override openingTag(context: TemplateContext): void {
121
- if (isJSXCustomElementComponent(context.tagName)) {
121
+ if (isJSXCustomElementComponent(context.path)) {
122
122
  const literalIdentifier = Ensure.componentLiteral(
123
123
  context.tagName,
124
124
  COMPONENT_LITERAL_PREFIX + context.tagName,
@@ -414,7 +414,7 @@ export class CompiledTranspiler extends JSXTranspiler<CompiledContext> {
414
414
 
415
415
  context.tagName = getJSXElementName(context.path.node);
416
416
 
417
- if (isJSXFunctionElementComponent(context.tagName)) {
417
+ if (isJSXFunctionElementComponent(context.path)) {
418
418
  // Process attributes and children into a props object
419
419
  if (!context.isInitialElement)
420
420
  return this.functionalComponent(context);
@@ -24,10 +24,9 @@ import * as babel from '@babel/core';
24
24
  import { mergeAndConcat } from 'merge-anything';
25
25
  import type { PluginOption } from 'vite';
26
26
 
27
- import { litJsxBabelPreset } from './babel-preset.js';
28
-
29
-
30
- type BabelPlugins = NonNullable<NonNullable<babel.TransformOptions['parserOpts']>['plugins']>;
27
+ import { litJsxBabelPlugin } from './babel-plugin.js';
28
+ import { babelPlugins, debugMode } from './config.js';
29
+ import { ImportDiscovery } from './import-discovery.js';
31
30
 
32
31
 
33
32
  /**
@@ -41,6 +40,7 @@ type BabelPlugins = NonNullable<NonNullable<babel.TransformOptions['parserOpts']
41
40
  * @returns Vite plugin configuration
42
41
  */
43
42
  export const litJsx = (options: {
43
+ debug?: boolean; // Enable debug mode for additional logging
44
44
  /** Options for the Babel transform */
45
45
  babel?:
46
46
  | babel.TransformOptions
@@ -48,6 +48,8 @@ export const litJsx = (options: {
48
48
  } = {}): PluginOption => {
49
49
  let projectRoot: string;
50
50
 
51
+ debugMode.value = !!options.debug;
52
+
51
53
  return {
52
54
  name: 'lit-jsx-preserve',
53
55
  config: {
@@ -63,10 +65,6 @@ export const litJsx = (options: {
63
65
  },
64
66
  order: 'pre',
65
67
  async handler(source, id) {
66
- const plugins: BabelPlugins = [ 'jsx', 'decorators', 'decoratorAutoAccessors' ];
67
- if (id.endsWith('.tsx'))
68
- plugins.push('typescript');
69
-
70
68
  // Default value for babel user options
71
69
  let babelUserOptions: babel.TransformOptions = {};
72
70
 
@@ -86,20 +84,13 @@ export const litJsx = (options: {
86
84
  root: projectRoot,
87
85
  filename: id,
88
86
  sourceFileName: id,
89
- presets: [
90
- [
91
- litJsxBabelPreset,
92
- /* merged into the metadata obj through state.opts */
93
- {},
94
- ],
95
- ],
96
- plugins: [],
97
- ast: false,
98
- sourceMaps: true,
99
- configFile: false,
100
- babelrc: false,
101
- parserOpts: {
102
- plugins,
87
+ plugins: [ litJsxBabelPlugin() ],
88
+ ast: false,
89
+ sourceMaps: true,
90
+ configFile: false,
91
+ babelrc: false,
92
+ parserOpts: {
93
+ plugins: babelPlugins,
103
94
  },
104
95
  };
105
96
 
@@ -110,5 +101,8 @@ export const litJsx = (options: {
110
101
  return { code: result.code, map: result.map };
111
102
  },
112
103
  },
104
+ hotUpdate(options) {
105
+ ImportDiscovery.clearCacheForFileAndDependents(options.file);
106
+ },
113
107
  };
114
108
  };