@lithent/hmr-parser 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/helper/calculator.d.ts +1 -1
- package/dist/lithentHmrParser.mjs +788 -803
- package/dist/lithentHmrParser.mjs.map +1 -1
- package/dist/lithentHmrParser.umd.js +45 -57
- package/dist/lithentHmrParser.umd.js.map +1 -1
- package/package.json +15 -12
- package/src/helper/calculator.ts +5 -21
- package/src/strings.ts +19 -31
- package/src/tests/mark.test.ts +9 -10
- package/src/tests/nomark.test.tsx +5 -7
- package/src/transform/componentRegister.ts +1 -1
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@lithent/hmr-parser",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"description": "Lithent HMR parser utilities for build tools",
|
|
6
|
-
"packageManager": "pnpm@9.12.3",
|
|
7
6
|
"main": "./dist/lithentHmrParser.umd.js",
|
|
8
7
|
"module": "./dist/lithentHmrParser.mjs",
|
|
9
8
|
"types": "./dist/index.d.ts",
|
|
@@ -14,12 +13,6 @@
|
|
|
14
13
|
"require": "./dist/lithentHmrParser.umd.js"
|
|
15
14
|
}
|
|
16
15
|
},
|
|
17
|
-
"scripts": {
|
|
18
|
-
"build": "vite build",
|
|
19
|
-
"watch": "vite build --watch",
|
|
20
|
-
"test": "vitest run --reporter verbose",
|
|
21
|
-
"test:dev": "vitest"
|
|
22
|
-
},
|
|
23
16
|
"author": "superlucky84",
|
|
24
17
|
"repository": {
|
|
25
18
|
"type": "git",
|
|
@@ -27,7 +20,11 @@
|
|
|
27
20
|
},
|
|
28
21
|
"homepage": "https://github.com/superlucky84/lithent",
|
|
29
22
|
"license": "MIT",
|
|
30
|
-
"keywords": [
|
|
23
|
+
"keywords": [
|
|
24
|
+
"lithent",
|
|
25
|
+
"hmr",
|
|
26
|
+
"parser"
|
|
27
|
+
],
|
|
31
28
|
"dependencies": {
|
|
32
29
|
"@babel/parser": "^7.26.2",
|
|
33
30
|
"@babel/types": "^7.26.0",
|
|
@@ -38,15 +35,21 @@
|
|
|
38
35
|
"src"
|
|
39
36
|
],
|
|
40
37
|
"devDependencies": {
|
|
41
|
-
"lithent": "^1.20.0",
|
|
42
38
|
"@types/babel__traverse": "^7.20.6",
|
|
43
39
|
"vite": "^5.4.8",
|
|
44
40
|
"vite-plugin-checker": "^0.8.0",
|
|
45
41
|
"vite-plugin-dts": "^4.2.1",
|
|
46
|
-
"vitest": "^2.1.5"
|
|
42
|
+
"vitest": "^2.1.5",
|
|
43
|
+
"lithent": "1.20.0"
|
|
47
44
|
},
|
|
48
45
|
"volta": {
|
|
49
46
|
"node": "20.3.0",
|
|
50
47
|
"pnpm": "9.12.3"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "vite build",
|
|
51
|
+
"watch": "vite build --watch",
|
|
52
|
+
"test": "vitest run --reporter verbose",
|
|
53
|
+
"test:dev": "vitest"
|
|
51
54
|
}
|
|
52
|
-
}
|
|
55
|
+
}
|
package/src/helper/calculator.ts
CHANGED
|
@@ -86,11 +86,10 @@ export const collectHeaderInsert = (
|
|
|
86
86
|
ast: File,
|
|
87
87
|
sourceCode: string,
|
|
88
88
|
boundaryImportSpecifier: string,
|
|
89
|
-
|
|
89
|
+
_tagFunctionImportSpecifier: string
|
|
90
90
|
) => {
|
|
91
91
|
const importsToPrepend: string[] = [];
|
|
92
92
|
let hasBoundaryImport = false;
|
|
93
|
-
let hasTagImport = false;
|
|
94
93
|
let lastImportEnd = 0;
|
|
95
94
|
let directiveEnd = 0;
|
|
96
95
|
let firstImportStart: number | null = null;
|
|
@@ -106,20 +105,6 @@ export const collectHeaderInsert = (
|
|
|
106
105
|
hasBoundaryImport = true;
|
|
107
106
|
}
|
|
108
107
|
|
|
109
|
-
if (node.source.value === tagFunctionImportSpecifier) {
|
|
110
|
-
const hasTag = node.specifiers.some(specifier => {
|
|
111
|
-
return (
|
|
112
|
-
specifier.type === 'ImportSpecifier' &&
|
|
113
|
-
specifier.imported.type === 'Identifier' &&
|
|
114
|
-
specifier.imported.name === 'TagFunction'
|
|
115
|
-
);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
if (hasTag) {
|
|
119
|
-
hasTagImport = true;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
108
|
continue;
|
|
124
109
|
}
|
|
125
110
|
|
|
@@ -145,11 +130,10 @@ export const collectHeaderInsert = (
|
|
|
145
130
|
);
|
|
146
131
|
}
|
|
147
132
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
133
|
+
// Note: We don't add TagFunction import anymore since:
|
|
134
|
+
// 1. In enforce:'post', esbuild already removed type imports
|
|
135
|
+
// 2. The type is only used for TypeScript casting, not at runtime
|
|
136
|
+
// 3. Adding "import type" in post-transform breaks browser execution
|
|
153
137
|
|
|
154
138
|
const importInsertionPos = firstImportStart ?? directiveEnd ?? 0;
|
|
155
139
|
const blockInsertionPos = lastImportEnd || directiveEnd || 0;
|
package/src/strings.ts
CHANGED
|
@@ -14,17 +14,11 @@ const __lithentBoundaryStoreKey = \`__lithent_hmr_boundary__\${__lithentModuleId
|
|
|
14
14
|
const __lithentDisposeStoreKey = \`__lithent_hmr_dispose__\${__lithentModuleId}\`;
|
|
15
15
|
const __lithentGlobalStore =
|
|
16
16
|
typeof globalThis === 'object'
|
|
17
|
-
?
|
|
17
|
+
? globalThis
|
|
18
18
|
: undefined;
|
|
19
19
|
|
|
20
|
-
const __lithentEnsureHotData = ()
|
|
21
|
-
const hot =
|
|
22
|
-
(import.meta.hot as
|
|
23
|
-
| {
|
|
24
|
-
data?: Record<string, unknown>;
|
|
25
|
-
__lithentData?: Record<string, unknown>;
|
|
26
|
-
}
|
|
27
|
-
| undefined) ?? undefined;
|
|
20
|
+
const __lithentEnsureHotData = () => {
|
|
21
|
+
const hot = import.meta.hot ?? undefined;
|
|
28
22
|
if (!hot) return undefined;
|
|
29
23
|
|
|
30
24
|
const existing = hot.data;
|
|
@@ -33,7 +27,7 @@ const __lithentEnsureHotData = (): Record<string, unknown> | undefined => {
|
|
|
33
27
|
}
|
|
34
28
|
|
|
35
29
|
try {
|
|
36
|
-
const target
|
|
30
|
+
const target = {};
|
|
37
31
|
hot.data = target;
|
|
38
32
|
return target;
|
|
39
33
|
} catch {
|
|
@@ -43,11 +37,10 @@ const __lithentEnsureHotData = (): Record<string, unknown> | undefined => {
|
|
|
43
37
|
};
|
|
44
38
|
|
|
45
39
|
const __lithentHotData = __lithentEnsureHotData();
|
|
46
|
-
type __LithentBoundaryController = ReturnType<typeof createBoundary>;
|
|
47
40
|
|
|
48
|
-
const counterBoundary
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
const counterBoundary =
|
|
42
|
+
__lithentHotData?.counterBoundary ||
|
|
43
|
+
__lithentGlobalStore?.[__lithentBoundaryStoreKey] ||
|
|
51
44
|
createBoundary(__lithentModuleId);
|
|
52
45
|
|
|
53
46
|
if (__lithentHotData) {
|
|
@@ -59,18 +52,18 @@ if (__lithentGlobalStore) {
|
|
|
59
52
|
}
|
|
60
53
|
|
|
61
54
|
let disposeApp =
|
|
62
|
-
|
|
63
|
-
|
|
55
|
+
__lithentHotData?.disposeApp ||
|
|
56
|
+
__lithentGlobalStore?.[__lithentDisposeStoreKey];
|
|
64
57
|
|
|
65
|
-
const __lithentRenderOnce =
|
|
58
|
+
const __lithentRenderOnce = (factory) => {
|
|
66
59
|
if (disposeApp) {
|
|
67
|
-
return disposeApp
|
|
60
|
+
return disposeApp;
|
|
68
61
|
}
|
|
69
62
|
|
|
70
63
|
const result = factory();
|
|
71
64
|
|
|
72
65
|
if (typeof result === 'function') {
|
|
73
|
-
disposeApp = result
|
|
66
|
+
disposeApp = result;
|
|
74
67
|
|
|
75
68
|
if (__lithentHotData) {
|
|
76
69
|
__lithentHotData.disposeApp = disposeApp;
|
|
@@ -87,10 +80,7 @@ const __lithentRenderOnce = <T,>(factory: () => T) => {
|
|
|
87
80
|
const __lithentModuleHotStore =
|
|
88
81
|
__lithentHotData
|
|
89
82
|
? ((__lithentHotData.components =
|
|
90
|
-
|
|
91
|
-
string,
|
|
92
|
-
TagFunction | undefined
|
|
93
|
-
> | undefined) ?? {}) as Record<string, TagFunction | undefined>)
|
|
83
|
+
__lithentHotData.components ?? {}))
|
|
94
84
|
: undefined;
|
|
95
85
|
|
|
96
86
|
const __lithentHmrTargets = ${serializedTargets};
|
|
@@ -102,8 +92,8 @@ const __lithentSetupHmrHooks = () => {
|
|
|
102
92
|
|
|
103
93
|
import.meta.hot.accept(mod => {
|
|
104
94
|
let applied = false;
|
|
105
|
-
const nextModule =
|
|
106
|
-
const missing
|
|
95
|
+
const nextModule = mod ?? {};
|
|
96
|
+
const missing = [];
|
|
107
97
|
const knownNames = new Set([
|
|
108
98
|
...${JSON.stringify(Array.from(new Set(componentNames)))},
|
|
109
99
|
...__lithentHmrTargets,
|
|
@@ -112,9 +102,9 @@ const __lithentSetupHmrHooks = () => {
|
|
|
112
102
|
for (const name of knownNames) {
|
|
113
103
|
const nextCtor =
|
|
114
104
|
name === 'default'
|
|
115
|
-
?
|
|
116
|
-
:
|
|
117
|
-
|
|
105
|
+
? nextModule.default
|
|
106
|
+
: nextModule[name] ||
|
|
107
|
+
__lithentModuleHotStore?.[name];
|
|
118
108
|
|
|
119
109
|
if (!nextCtor) {
|
|
120
110
|
missing.push(name);
|
|
@@ -131,9 +121,7 @@ const __lithentSetupHmrHooks = () => {
|
|
|
131
121
|
queueMicrotask(() => {
|
|
132
122
|
let retried = false;
|
|
133
123
|
for (const name of missing) {
|
|
134
|
-
const retryCtor = __lithentModuleHotStore?.[name]
|
|
135
|
-
| TagFunction
|
|
136
|
-
| undefined;
|
|
124
|
+
const retryCtor = __lithentModuleHotStore?.[name];
|
|
137
125
|
if (retryCtor && counterBoundary.update(retryCtor)) {
|
|
138
126
|
retried = true;
|
|
139
127
|
}
|
package/src/tests/mark.test.ts
CHANGED
|
@@ -84,10 +84,7 @@ if (import.meta.vitest) {
|
|
|
84
84
|
expect(defaultResult.code).toContain(
|
|
85
85
|
"import { createBoundary } from 'lithent/devHelper';"
|
|
86
86
|
);
|
|
87
|
-
expect(defaultResult.code).toContain(
|
|
88
|
-
"import type { TagFunction } from 'lithent';"
|
|
89
|
-
);
|
|
90
|
-
expect(defaultResult.code).toContain('import.meta.hot as');
|
|
87
|
+
expect(defaultResult.code).toContain('import.meta.hot');
|
|
91
88
|
expect(defaultResult.code).toContain(
|
|
92
89
|
"import { mountCallback, getComponentKey } from 'lithent';"
|
|
93
90
|
);
|
|
@@ -101,18 +98,20 @@ if (import.meta.vitest) {
|
|
|
101
98
|
expect(defaultResult.code).toContain('...["App","Secondary"]');
|
|
102
99
|
expect(defaultResult.code).toContain('...__lithentHmrTargets');
|
|
103
100
|
expect(defaultResult.code).toContain(
|
|
104
|
-
'const __lithentHotComponent_App = App
|
|
101
|
+
'const __lithentHotComponent_App = App;'
|
|
105
102
|
);
|
|
106
103
|
expect(defaultResult.code).toContain(
|
|
107
104
|
'__lithentModuleHotStore["App"] = __lithentHotComponent_App;'
|
|
108
105
|
);
|
|
109
106
|
expect(defaultResult.code).toContain(
|
|
110
|
-
'const __lithentHotComponent_Secondary = Secondary
|
|
107
|
+
'const __lithentHotComponent_Secondary = Secondary;'
|
|
111
108
|
);
|
|
112
109
|
expect(defaultResult.code).toContain(
|
|
113
110
|
'__lithentModuleHotStore["Secondary"] = __lithentHotComponent_Secondary;'
|
|
114
111
|
);
|
|
115
|
-
expect(defaultResult.code).toContain(
|
|
112
|
+
expect(defaultResult.code).toContain(
|
|
113
|
+
'const __lithentRenderOnce = (factory)'
|
|
114
|
+
);
|
|
116
115
|
const registerMatches =
|
|
117
116
|
defaultResult.code.match(/counterBoundary\.register\(compKey\)/g) ?? [];
|
|
118
117
|
const unregisterMatches =
|
|
@@ -133,18 +132,18 @@ if (import.meta.vitest) {
|
|
|
133
132
|
expect(namedResult.code).toContain(
|
|
134
133
|
'const __lithentHmrTargets = ["Counter","Another"];'
|
|
135
134
|
);
|
|
136
|
-
expect(namedResult.code).toContain('import.meta.hot
|
|
135
|
+
expect(namedResult.code).toContain('import.meta.hot');
|
|
137
136
|
expect(namedResult.code).toContain('const knownNames = new Set([');
|
|
138
137
|
expect(namedResult.code).toContain('...["Counter","Another"]');
|
|
139
138
|
expect(namedResult.code).toContain('...__lithentHmrTargets');
|
|
140
139
|
expect(namedResult.code).toContain(
|
|
141
|
-
'const __lithentHotComponent_Counter = Counter
|
|
140
|
+
'const __lithentHotComponent_Counter = Counter;'
|
|
142
141
|
);
|
|
143
142
|
expect(namedResult.code).toContain(
|
|
144
143
|
'__lithentModuleHotStore["Counter"] = __lithentHotComponent_Counter;'
|
|
145
144
|
);
|
|
146
145
|
expect(namedResult.code).toContain(
|
|
147
|
-
'const __lithentHotComponent_Another = Another
|
|
146
|
+
'const __lithentHotComponent_Another = Another;'
|
|
148
147
|
);
|
|
149
148
|
expect(namedResult.code).toContain(
|
|
150
149
|
'__lithentModuleHotStore["Another"] = __lithentHotComponent_Another;'
|
|
@@ -65,9 +65,7 @@ if (import.meta.vitest) {
|
|
|
65
65
|
expect(result.code.includes('...["Demo","Another"]')).toBe(true);
|
|
66
66
|
expect(result.code.includes('...__lithentHmrTargets')).toBe(true);
|
|
67
67
|
expect(
|
|
68
|
-
result.code.includes(
|
|
69
|
-
'const __lithentHotComponent_Demo = Demo as unknown as TagFunction;'
|
|
70
|
-
)
|
|
68
|
+
result.code.includes('const __lithentHotComponent_Demo = Demo;')
|
|
71
69
|
).toBe(true);
|
|
72
70
|
expect(
|
|
73
71
|
result.code.includes(
|
|
@@ -75,16 +73,16 @@ if (import.meta.vitest) {
|
|
|
75
73
|
)
|
|
76
74
|
).toBe(true);
|
|
77
75
|
expect(
|
|
78
|
-
result.code.includes(
|
|
79
|
-
'const __lithentHotComponent_Another = Another as unknown as TagFunction;'
|
|
80
|
-
)
|
|
76
|
+
result.code.includes('const __lithentHotComponent_Another = Another;')
|
|
81
77
|
).toBe(true);
|
|
82
78
|
expect(
|
|
83
79
|
result.code.includes(
|
|
84
80
|
'__lithentModuleHotStore["Another"] = __lithentHotComponent_Another;'
|
|
85
81
|
)
|
|
86
82
|
).toBe(true);
|
|
87
|
-
expect(
|
|
83
|
+
expect(
|
|
84
|
+
result.code.includes('const __lithentRenderOnce = (factory)')
|
|
85
|
+
).toBe(true);
|
|
88
86
|
expect(/__lithentRenderOnce\(\(\) =>\s*render\(/.test(result.code)).toBe(
|
|
89
87
|
true
|
|
90
88
|
);
|
|
@@ -39,7 +39,7 @@ export const stitchComponentRegistration = (
|
|
|
39
39
|
if (componentName) {
|
|
40
40
|
const hotStoreVar = `__lithentHotComponent_${componentName}`;
|
|
41
41
|
const storeSnippet = `
|
|
42
|
-
const ${hotStoreVar} = ${componentName}
|
|
42
|
+
const ${hotStoreVar} = ${componentName};
|
|
43
43
|
if (__lithentModuleHotStore) {
|
|
44
44
|
__lithentModuleHotStore["${componentName}"] = ${hotStoreVar};
|
|
45
45
|
}
|