@lithent/hmr-parser 0.2.2 → 0.2.3

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 (42) hide show
  1. package/dist/helper/calculator.d.cts +8 -0
  2. package/dist/helper/calculator.d.ts +1 -1
  3. package/dist/index.d.cts +8 -0
  4. package/dist/index.d.ts +5 -5
  5. package/dist/lithentHmrParser.mjs +4214 -4370
  6. package/dist/lithentHmrParser.mjs.map +1 -1
  7. package/dist/lithentHmrParser.umd.js +137 -216
  8. package/dist/lithentHmrParser.umd.js.map +1 -1
  9. package/dist/markerParse.d.cts +2 -0
  10. package/dist/markerParse.d.ts +1 -1
  11. package/dist/noMarkerParse.d.cts +2 -0
  12. package/dist/noMarkerParse.d.ts +1 -1
  13. package/dist/shared.d.cts +4 -0
  14. package/dist/shared.d.ts +1 -1
  15. package/dist/strings.d.cts +1 -0
  16. package/dist/strings.d.ts +1 -1
  17. package/dist/tests/bootstrap.test.d.cts +1 -0
  18. package/dist/tests/bootstrap.test.d.ts +1 -0
  19. package/dist/tests/mark.test.d.cts +1 -0
  20. package/dist/tests/nomark.test.d.cts +1 -0
  21. package/dist/transform/componentRegister.d.cts +3 -0
  22. package/dist/transform/componentRegister.d.ts +2 -2
  23. package/dist/transform/exportSnapshot.d.cts +3 -0
  24. package/dist/transform/exportSnapshot.d.ts +3 -0
  25. package/dist/transform/renderGuard.d.cts +3 -0
  26. package/dist/transform/renderGuard.d.ts +1 -1
  27. package/dist/types.d.cts +28 -0
  28. package/dist/utils/ast/componentCollector.d.cts +9 -0
  29. package/dist/utils/ast/componentCollector.d.ts +5 -4
  30. package/package.json +2 -2
  31. package/src/helper/calculator.ts +27 -7
  32. package/src/markerParse.ts +30 -14
  33. package/src/noMarkerParse.ts +16 -4
  34. package/src/shared.ts +8 -2
  35. package/src/strings.ts +105 -177
  36. package/src/tests/bootstrap.test.ts +312 -0
  37. package/src/tests/mark.test.ts +61 -181
  38. package/src/tests/nomark.test.tsx +110 -89
  39. package/src/transform/componentRegister.ts +13 -304
  40. package/src/transform/exportSnapshot.ts +77 -0
  41. package/src/transform/renderGuard.ts +59 -49
  42. package/src/utils/ast/componentCollector.ts +195 -96
@@ -0,0 +1,2 @@
1
+ import { MarkerTransformOptions, HmrTransformResult } from './types.cjs';
2
+ export declare const transformWithMarker: (options: MarkerTransformOptions) => HmrTransformResult;
@@ -1,2 +1,2 @@
1
- import { MarkerTransformOptions, HmrTransformResult } from './types';
1
+ import { MarkerTransformOptions, HmrTransformResult } from './types.js';
2
2
  export declare const transformWithMarker: (options: MarkerTransformOptions) => HmrTransformResult;
@@ -0,0 +1,2 @@
1
+ import { BaseTransformOptions, HmrTransformResult } from './types.cjs';
2
+ export declare const transformWithoutMarker: (options: BaseTransformOptions) => HmrTransformResult;
@@ -1,2 +1,2 @@
1
- import { BaseTransformOptions, HmrTransformResult } from './types';
1
+ import { BaseTransformOptions, HmrTransformResult } from './types.js';
2
2
  export declare const transformWithoutMarker: (options: BaseTransformOptions) => HmrTransformResult;
@@ -0,0 +1,4 @@
1
+ import { MarkerTransformOptions, BaseTransformOptions, MarkerAnalysisResult, BaseAnalysisResult } from './types.cjs';
2
+ export declare const analyzeMarker: (options: MarkerTransformOptions) => MarkerAnalysisResult;
3
+ export declare const analyzeNoMarker: (options: BaseTransformOptions) => BaseAnalysisResult;
4
+ export declare const shouldSkipTransform: (code: string) => boolean;
package/dist/shared.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { MarkerTransformOptions, BaseTransformOptions, MarkerAnalysisResult, BaseAnalysisResult } from './types';
1
+ import { MarkerTransformOptions, BaseTransformOptions, MarkerAnalysisResult, BaseAnalysisResult } from './types.js';
2
2
  export declare const analyzeMarker: (options: MarkerTransformOptions) => MarkerAnalysisResult;
3
3
  export declare const analyzeNoMarker: (options: BaseTransformOptions) => BaseAnalysisResult;
4
4
  export declare const shouldSkipTransform: (code: string) => boolean;
@@ -0,0 +1 @@
1
+ export declare const createHmrBootstrapBlock: (_targetExports: string[], componentNames: string[]) => string;
package/dist/strings.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const createHmrBootstrapBlock: (targetExports: string[], componentNames: string[]) => string;
1
+ export declare const createHmrBootstrapBlock: (_targetExports: string[], componentNames: string[]) => string;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import { default as MagicString } from 'magic-string';
2
+ import { MountInfo } from '../utils/ast/componentCollector.cjs';
3
+ export declare const stitchComponentRegistration: (ms: MagicString, mounts: MountInfo[], _code: string, _importInsertionPos: number) => string;
@@ -1,3 +1,3 @@
1
1
  import { default as MagicString } from 'magic-string';
2
- import { MountInfo } from '../utils/ast/componentCollector';
3
- export declare const stitchComponentRegistration: (ms: MagicString, mounts: MountInfo[], code: string, importInsertionPos: number) => void;
2
+ import { MountInfo } from '../utils/ast/componentCollector.js';
3
+ export declare const stitchComponentRegistration: (ms: MagicString, mounts: MountInfo[], _code: string, _importInsertionPos: number) => string;
@@ -0,0 +1,3 @@
1
+ import { File } from '@babel/types';
2
+ import { MountInfo } from '../utils/ast/componentCollector.cjs';
3
+ export declare const createExportSnapshot: (ast: File, components: MountInfo[]) => string;
@@ -0,0 +1,3 @@
1
+ import { File } from '@babel/types';
2
+ import { MountInfo } from '../utils/ast/componentCollector.js';
3
+ export declare const createExportSnapshot: (ast: File, components: MountInfo[]) => string;
@@ -0,0 +1,3 @@
1
+ import { default as MagicString } from 'magic-string';
2
+ import { File } from '@babel/types';
3
+ export declare const wrapRenderCalls: (ms: MagicString, ast: File, _code: string, importSpecifier?: string) => boolean;
@@ -1,3 +1,3 @@
1
1
  import { default as MagicString } from 'magic-string';
2
2
  import { File } from '@babel/types';
3
- export declare const wrapRenderCalls: (ms: MagicString, ast: File, code: string) => boolean;
3
+ export declare const wrapRenderCalls: (ms: MagicString, ast: File, _code: string, importSpecifier?: string) => boolean;
@@ -0,0 +1,28 @@
1
+ import { default as MagicString } from 'magic-string';
2
+ import { File } from '@babel/types';
3
+ export interface BaseTransformOptions {
4
+ code: string;
5
+ boundaryImportSpecifier: string;
6
+ tagFunctionImportSpecifier: string;
7
+ }
8
+ export interface MarkerTransformOptions extends BaseTransformOptions {
9
+ markerRegex: RegExp;
10
+ }
11
+ export interface BaseAnalysisResult {
12
+ targetExports: string[];
13
+ shouldTransform: boolean;
14
+ importInsertionPos: number;
15
+ blockInsertionPos: number;
16
+ headerSnippet: string;
17
+ ast: File;
18
+ }
19
+ export interface MarkerAnalysisResult extends BaseAnalysisResult {
20
+ match: RegExpExecArray | null;
21
+ }
22
+ export interface HmrTransformOptions extends MarkerTransformOptions {
23
+ }
24
+ export interface HmrTransformResult {
25
+ transformed: boolean;
26
+ code: string;
27
+ map: ReturnType<MagicString['generateMap']> | null;
28
+ }
@@ -0,0 +1,9 @@
1
+ import { File } from '@babel/types';
2
+ export type MountInfo = {
3
+ componentName: string;
4
+ declarationEnd: number;
5
+ start: number;
6
+ end: number;
7
+ declaration: boolean;
8
+ };
9
+ export declare const collectComponentMounts: (ast: File, _code: string, importSpecifier?: string) => MountInfo[];
@@ -1,8 +1,9 @@
1
1
  import { File } from '@babel/types';
2
2
  export type MountInfo = {
3
- insertPos: number;
4
- propsExpression: string;
5
- componentName: string | null;
3
+ componentName: string;
6
4
  declarationEnd: number;
5
+ start: number;
6
+ end: number;
7
+ declaration: boolean;
7
8
  };
8
- export declare const collectComponentMounts: (ast: File, code: string) => MountInfo[];
9
+ export declare const collectComponentMounts: (ast: File, _code: string, importSpecifier?: string) => MountInfo[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@lithent/hmr-parser",
4
- "version": "0.2.2",
4
+ "version": "0.2.3",
5
5
  "description": "Lithent HMR parser utilities for build tools",
6
6
  "main": "./dist/lithentHmrParser.umd.js",
7
7
  "module": "./dist/lithentHmrParser.mjs",
@@ -40,7 +40,7 @@
40
40
  "vite-plugin-checker": "^0.8.0",
41
41
  "vite-plugin-dts": "^4.2.1",
42
42
  "vitest": "^2.1.5",
43
- "lithent": "1.20.2"
43
+ "lithent": "1.25.0"
44
44
  },
45
45
  "volta": {
46
46
  "node": "20.3.0",
@@ -49,7 +49,13 @@ export const collectExportNames = (ast: File): string[] => {
49
49
  };
50
50
 
51
51
  export const detectLithentUsage = (ast: File): boolean => {
52
- const watchedNames = new Set(['render', 'mount', 'createApp', 'createRoot']);
52
+ const watchedNames = new Set([
53
+ 'render',
54
+ 'mount',
55
+ 'lmount',
56
+ 'createApp',
57
+ 'createRoot',
58
+ ]);
53
59
 
54
60
  for (const node of ast.program.body) {
55
61
  if (node.type !== 'ImportDeclaration') continue;
@@ -86,12 +92,15 @@ export const collectHeaderInsert = (
86
92
  ast: File,
87
93
  sourceCode: string,
88
94
  boundaryImportSpecifier: string,
89
- _tagFunctionImportSpecifier: string
95
+ tagFunctionImportSpecifier: string
90
96
  ) => {
91
- const importsToPrepend: string[] = [];
97
+ const importsToPrepend: string[] = [
98
+ `import { getComponentKey as __lithentGetComponentKey, mountCallback as __lithentMountCallback, componentMap as __lithentComponentMap } from '${tagFunctionImportSpecifier}';`,
99
+ ];
92
100
  let hasBoundaryImport = false;
93
101
  let lastImportEnd = 0;
94
- let directiveEnd = 0;
102
+ let directiveEnd =
103
+ ast.program.directives.at(-1)?.end ?? ast.program.interpreter?.end ?? 0;
95
104
  let firstImportStart: number | null = null;
96
105
 
97
106
  for (const node of ast.program.body) {
@@ -101,8 +110,19 @@ export const collectHeaderInsert = (
101
110
  firstImportStart = node.start ?? null;
102
111
  }
103
112
 
104
- if (node.source.value === boundaryImportSpecifier) {
105
- hasBoundaryImport = true;
113
+ if (
114
+ node.source.value === boundaryImportSpecifier &&
115
+ node.importKind !== 'type'
116
+ ) {
117
+ hasBoundaryImport =
118
+ node.specifiers.some(
119
+ specifier =>
120
+ specifier.type === 'ImportSpecifier' &&
121
+ specifier.importKind !== 'type' &&
122
+ specifier.imported.type === 'Identifier' &&
123
+ specifier.imported.name === 'createBoundary' &&
124
+ specifier.local.name === '__lithentCreateBoundary'
125
+ ) || hasBoundaryImport;
106
126
  }
107
127
 
108
128
  continue;
@@ -126,7 +146,7 @@ export const collectHeaderInsert = (
126
146
 
127
147
  if (!hasBoundaryImport) {
128
148
  importsToPrepend.push(
129
- `import { createBoundary } from '${boundaryImportSpecifier}';`
149
+ `import { createBoundary as __lithentCreateBoundary } from '${boundaryImportSpecifier}';`
130
150
  );
131
151
  }
132
152
 
@@ -5,6 +5,7 @@ import { analyzeMarker } from './shared';
5
5
  import { stitchComponentRegistration } from './transform/componentRegister';
6
6
  import { wrapRenderCalls } from './transform/renderGuard';
7
7
  import { collectComponentMounts } from './utils/ast/componentCollector';
8
+ import { createExportSnapshot } from './transform/exportSnapshot';
8
9
 
9
10
  export const transformWithMarker = (
10
11
  options: MarkerTransformOptions
@@ -15,6 +16,7 @@ export const transformWithMarker = (
15
16
  targetExports,
16
17
  shouldTransform,
17
18
  importInsertionPos,
19
+ blockInsertionPos,
18
20
  headerSnippet,
19
21
  } = analysis;
20
22
 
@@ -22,7 +24,11 @@ export const transformWithMarker = (
22
24
  return { transformed: false, code: options.code, map: null };
23
25
  }
24
26
 
25
- const mounts = collectComponentMounts(analysis.ast, options.code);
27
+ const mounts = collectComponentMounts(
28
+ analysis.ast,
29
+ options.code,
30
+ options.tagFunctionImportSpecifier
31
+ );
26
32
  const componentNames = Array.from(
27
33
  new Set(
28
34
  mounts
@@ -32,26 +38,36 @@ export const transformWithMarker = (
32
38
  );
33
39
 
34
40
  const ms = new MagicString(options.code);
35
- wrapRenderCalls(ms, analysis.ast, options.code);
41
+ if (mounts.length)
42
+ wrapRenderCalls(
43
+ ms,
44
+ analysis.ast,
45
+ options.code,
46
+ options.tagFunctionImportSpecifier
47
+ );
48
+ const hoisted = stitchComponentRegistration(
49
+ ms,
50
+ mounts,
51
+ options.code,
52
+ importInsertionPos
53
+ );
36
54
  const transformBlock = createHmrBootstrapBlock(
37
55
  targetExports,
38
56
  componentNames
39
57
  ).trimStart();
40
58
 
41
- if (headerSnippet) {
59
+ const precedingChar =
60
+ blockInsertionPos > 0 ? options.code[blockInsertionPos - 1] : undefined;
61
+ const needsLeadingNewline = blockInsertionPos > 0 && precedingChar !== '\n';
62
+ const blockSnippet = `${needsLeadingNewline ? '\n' : ''}${transformBlock}\n${hoisted}\n\n`;
63
+ if (blockInsertionPos === importInsertionPos) {
64
+ ms.appendLeft(importInsertionPos, `${headerSnippet}${blockSnippet}`);
65
+ } else {
42
66
  ms.appendLeft(importInsertionPos, headerSnippet);
67
+ ms.appendLeft(blockInsertionPos, blockSnippet);
43
68
  }
44
-
45
- const precedingChar =
46
- match.index > 0 ? options.code[match.index - 1] : undefined;
47
- const needsLeadingNewline = match.index > 0 && precedingChar !== '\n';
48
- const followingChar = options.code[match.index + match[0].length] ?? null;
49
- const trailingNewline = followingChar === '\n' ? '\n' : '\n\n';
50
- const blockSnippet = `${needsLeadingNewline ? '\n' : ''}${transformBlock}${trailingNewline}`;
51
-
52
- ms.overwrite(match.index, match.index + match[0].length, blockSnippet);
53
-
54
- stitchComponentRegistration(ms, mounts, options.code, importInsertionPos);
69
+ ms.overwrite(match.index, match.index + match[0].length, '');
70
+ ms.append(createExportSnapshot(analysis.ast, mounts));
55
71
 
56
72
  return {
57
73
  transformed: true,
@@ -5,6 +5,7 @@ import { analyzeNoMarker } from './shared';
5
5
  import { stitchComponentRegistration } from './transform/componentRegister';
6
6
  import { wrapRenderCalls } from './transform/renderGuard';
7
7
  import { collectComponentMounts } from './utils/ast/componentCollector';
8
+ import { createExportSnapshot } from './transform/exportSnapshot';
8
9
 
9
10
  export const transformWithoutMarker = (
10
11
  options: BaseTransformOptions
@@ -23,7 +24,11 @@ export const transformWithoutMarker = (
23
24
  return { transformed: false, code: options.code, map: null };
24
25
  }
25
26
 
26
- const mounts = collectComponentMounts(ast, options.code);
27
+ const mounts = collectComponentMounts(
28
+ ast,
29
+ options.code,
30
+ options.tagFunctionImportSpecifier
31
+ );
27
32
  const componentNames = Array.from(
28
33
  new Set(
29
34
  mounts
@@ -33,7 +38,14 @@ export const transformWithoutMarker = (
33
38
  );
34
39
 
35
40
  const ms = new MagicString(options.code);
36
- wrapRenderCalls(ms, ast, options.code);
41
+ if (mounts.length)
42
+ wrapRenderCalls(ms, ast, options.code, options.tagFunctionImportSpecifier);
43
+ const hoisted = stitchComponentRegistration(
44
+ ms,
45
+ mounts,
46
+ options.code,
47
+ importInsertionPos
48
+ );
37
49
  const transformBlock = createHmrBootstrapBlock(
38
50
  targetExports,
39
51
  componentNames
@@ -43,7 +55,7 @@ export const transformWithoutMarker = (
43
55
  blockInsertionPos > 0 ? options.code[blockInsertionPos - 1] : undefined;
44
56
  const needsLeadingNewlineForBlock =
45
57
  blockInsertionPos > 0 && blockPrecedingChar !== '\n';
46
- const blockSnippet = `${needsLeadingNewlineForBlock ? '\n' : ''}${transformBlock}\n\n`;
58
+ const blockSnippet = `${needsLeadingNewlineForBlock ? '\n' : ''}${transformBlock}\n${hoisted}\n\n`;
47
59
 
48
60
  if (!headerSnippet && !blockSnippet.trim().length) {
49
61
  return { transformed: false, code: options.code, map: null };
@@ -60,7 +72,7 @@ export const transformWithoutMarker = (
60
72
  ms.appendLeft(blockInsertionPos, blockSnippet);
61
73
  }
62
74
 
63
- stitchComponentRegistration(ms, mounts, options.code, importInsertionPos);
75
+ ms.append(createExportSnapshot(ast, mounts));
64
76
 
65
77
  return {
66
78
  transformed: true,
package/src/shared.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { parse } from '@babel/parser';
2
+ import { collectComponentMounts } from './utils/ast/componentCollector';
2
3
  import type { File } from '@babel/types';
3
4
  import type {
4
5
  MarkerTransformOptions,
@@ -73,7 +74,10 @@ export const analyzeMarker = (
73
74
 
74
75
  return {
75
76
  targetExports: finalTargets,
76
- shouldTransform: !!match || detectLithentUsage(ast),
77
+ shouldTransform:
78
+ !!match ||
79
+ detectLithentUsage(ast) ||
80
+ collectComponentMounts(ast, code, tagFunctionImportSpecifier).length > 0,
77
81
  importInsertionPos,
78
82
  blockInsertionPos,
79
83
  headerSnippet,
@@ -97,7 +101,9 @@ export const analyzeNoMarker = (
97
101
 
98
102
  return {
99
103
  targetExports,
100
- shouldTransform: detectLithentUsage(ast),
104
+ shouldTransform:
105
+ detectLithentUsage(ast) ||
106
+ collectComponentMounts(ast, code, tagFunctionImportSpecifier).length > 0,
101
107
  importInsertionPos,
102
108
  blockInsertionPos,
103
109
  headerSnippet,