@remix-run/node-hmr 0.0.0 → 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/README.md +305 -2
- package/dist/index.d.ts +128 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +107 -0
- package/dist/lib/browser-events.d.ts +87 -0
- package/dist/lib/browser-events.d.ts.map +1 -0
- package/dist/lib/browser-events.js +11 -0
- package/dist/lib/events.d.ts +29 -0
- package/dist/lib/events.d.ts.map +1 -0
- package/dist/lib/events.js +32 -0
- package/dist/lib/hmr-analysis.d.ts +17 -0
- package/dist/lib/hmr-analysis.d.ts.map +1 -0
- package/dist/lib/hmr-analysis.js +130 -0
- package/dist/lib/module-store.d.ts +27 -0
- package/dist/lib/module-store.d.ts.map +1 -0
- package/dist/lib/module-store.js +161 -0
- package/dist/lib/process-state.d.ts +3 -0
- package/dist/lib/process-state.d.ts.map +1 -0
- package/dist/lib/process-state.js +7 -0
- package/dist/lib/runner.d.ts +62 -0
- package/dist/lib/runner.d.ts.map +1 -0
- package/dist/lib/runner.js +1050 -0
- package/dist/lib/runtime-api.d.ts +7 -0
- package/dist/lib/runtime-api.d.ts.map +1 -0
- package/dist/lib/runtime-api.js +1 -0
- package/dist/lib/runtime.d.ts +46 -0
- package/dist/lib/runtime.d.ts.map +1 -0
- package/dist/lib/runtime.js +374 -0
- package/dist/register.d.ts +2 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +317 -0
- package/dist/runtime.d.ts +26 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +32 -0
- package/dist/runtime.node-hmr.d.ts +27 -0
- package/dist/runtime.node-hmr.d.ts.map +1 -0
- package/dist/runtime.node-hmr.js +33 -0
- package/dist/types.d.ts +36 -0
- package/package.json +55 -5
- package/src/index.ts +244 -0
- package/src/lib/browser-events.ts +113 -0
- package/src/lib/events.ts +61 -0
- package/src/lib/hmr-analysis.ts +178 -0
- package/src/lib/module-store.ts +228 -0
- package/src/lib/process-state.ts +9 -0
- package/src/lib/runner.ts +1429 -0
- package/src/lib/runtime-api.ts +9 -0
- package/src/lib/runtime.ts +534 -0
- package/src/register.ts +401 -0
- package/src/runtime.node-hmr.ts +40 -0
- package/src/runtime.ts +40 -0
- package/src/types.d.ts +36 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
export interface ModuleRecord {
|
|
2
|
+
filePath: string
|
|
3
|
+
hmr: {
|
|
4
|
+
acceptedDeps: string[]
|
|
5
|
+
selfAccepting: boolean
|
|
6
|
+
usesImportMetaHot: boolean
|
|
7
|
+
}
|
|
8
|
+
url: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface HotUpdateBoundary {
|
|
12
|
+
acceptedDependencyUrl: string
|
|
13
|
+
invalidatedUrls: Record<string, number>
|
|
14
|
+
updateHandlerUrl: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ModuleStore {
|
|
18
|
+
addDependency(importerUrl: string, dependencyUrl: string): void
|
|
19
|
+
findHotUpdateBoundaries(url: string, timestamp: number): HotUpdateBoundary[] | null
|
|
20
|
+
findHotUpdateBoundariesFromImporters(url: string, timestamp: number): HotUpdateBoundary[] | null
|
|
21
|
+
getModule(url: string): ModuleRecord | undefined
|
|
22
|
+
getModulesForFile(filePath: string): readonly ModuleRecord[]
|
|
23
|
+
getReachableFilePaths(entryUrl: string): ReadonlySet<string>
|
|
24
|
+
reset(): void
|
|
25
|
+
setAcceptedDependencies(url: string, acceptedDependencies: string[]): void
|
|
26
|
+
setModule(module: ModuleRecord): void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function createModuleStore(): ModuleStore {
|
|
30
|
+
let modulesByUrl = new Map<string, ModuleRecord>()
|
|
31
|
+
let modulesByFilePath = new Map<string, Map<string, ModuleRecord>>()
|
|
32
|
+
let dependenciesByImporterUrl = new Map<string, Set<string>>()
|
|
33
|
+
let importersByDependencyUrl = new Map<string, Set<string>>()
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
addDependency(importerUrl, dependencyUrl) {
|
|
37
|
+
addToIndexedSet(dependenciesByImporterUrl, importerUrl, dependencyUrl)
|
|
38
|
+
addToIndexedSet(importersByDependencyUrl, dependencyUrl, importerUrl)
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
findHotUpdateBoundaries(url, timestamp) {
|
|
42
|
+
return findPropagation(url, timestamp)
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
findHotUpdateBoundariesFromImporters(url, timestamp) {
|
|
46
|
+
return findPropagationFromImporters(url, timestamp, new Set([url]), [url])
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
getModule(url) {
|
|
50
|
+
return modulesByUrl.get(url)
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
getModulesForFile(filePath) {
|
|
54
|
+
return [...(modulesByFilePath.get(filePath)?.values() ?? [])]
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
getReachableFilePaths(entryUrl) {
|
|
58
|
+
let reachableFilePaths = new Set<string>()
|
|
59
|
+
let visitedUrls = new Set<string>()
|
|
60
|
+
visit(entryUrl)
|
|
61
|
+
return reachableFilePaths
|
|
62
|
+
|
|
63
|
+
function visit(url: string): void {
|
|
64
|
+
if (visitedUrls.has(url)) return
|
|
65
|
+
|
|
66
|
+
let module = modulesByUrl.get(url)
|
|
67
|
+
if (module === undefined) return
|
|
68
|
+
|
|
69
|
+
visitedUrls.add(url)
|
|
70
|
+
reachableFilePaths.add(module.filePath)
|
|
71
|
+
for (let dependencyUrl of dependenciesByImporterUrl.get(url) ?? []) {
|
|
72
|
+
visit(dependencyUrl)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
reset() {
|
|
78
|
+
modulesByUrl = new Map()
|
|
79
|
+
modulesByFilePath = new Map()
|
|
80
|
+
dependenciesByImporterUrl = new Map()
|
|
81
|
+
importersByDependencyUrl = new Map()
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
setAcceptedDependencies(url, acceptedDependencies) {
|
|
85
|
+
let module = modulesByUrl.get(url)
|
|
86
|
+
if (module !== undefined) {
|
|
87
|
+
module.hmr.acceptedDeps = acceptedDependencies
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
setModule(module) {
|
|
92
|
+
let previousModule = modulesByUrl.get(module.url)
|
|
93
|
+
if (previousModule !== undefined) {
|
|
94
|
+
removeModuleFromFileIndex(previousModule)
|
|
95
|
+
}
|
|
96
|
+
removeImporterDependencies(module.url)
|
|
97
|
+
|
|
98
|
+
modulesByUrl.set(module.url, module)
|
|
99
|
+
let fileModules = modulesByFilePath.get(module.filePath) ?? new Map()
|
|
100
|
+
fileModules.set(module.url, module)
|
|
101
|
+
modulesByFilePath.set(module.filePath, fileModules)
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function removeModuleFromFileIndex(module: ModuleRecord): void {
|
|
106
|
+
let fileModules = modulesByFilePath.get(module.filePath)
|
|
107
|
+
if (fileModules === undefined) return
|
|
108
|
+
|
|
109
|
+
fileModules.delete(module.url)
|
|
110
|
+
if (fileModules.size === 0) {
|
|
111
|
+
modulesByFilePath.delete(module.filePath)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function removeImporterDependencies(importerUrl: string): void {
|
|
116
|
+
let dependencies = dependenciesByImporterUrl.get(importerUrl)
|
|
117
|
+
if (dependencies === undefined) return
|
|
118
|
+
|
|
119
|
+
for (let dependencyUrl of dependencies) {
|
|
120
|
+
removeFromIndexedSet(importersByDependencyUrl, dependencyUrl, importerUrl)
|
|
121
|
+
}
|
|
122
|
+
dependenciesByImporterUrl.delete(importerUrl)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function findPropagation(
|
|
126
|
+
url: string,
|
|
127
|
+
timestamp: number,
|
|
128
|
+
traversedUrls: ReadonlySet<string> = new Set(),
|
|
129
|
+
invalidatedUrls: string[] = [url],
|
|
130
|
+
): HotUpdateBoundary[] | null {
|
|
131
|
+
if (traversedUrls.has(url)) return null
|
|
132
|
+
|
|
133
|
+
let nextTraversedUrls = new Set(traversedUrls)
|
|
134
|
+
nextTraversedUrls.add(url)
|
|
135
|
+
|
|
136
|
+
let module = modulesByUrl.get(url)
|
|
137
|
+
if (module === undefined) return null
|
|
138
|
+
|
|
139
|
+
if (module.hmr.selfAccepting) {
|
|
140
|
+
return [createHotUpdateBoundary(url, url, invalidatedUrls, timestamp)]
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return findPropagationFromImporters(url, timestamp, nextTraversedUrls, invalidatedUrls)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function findPropagationFromImporters(
|
|
147
|
+
url: string,
|
|
148
|
+
timestamp: number,
|
|
149
|
+
traversedUrls: ReadonlySet<string>,
|
|
150
|
+
invalidatedUrls: string[],
|
|
151
|
+
): HotUpdateBoundary[] | null {
|
|
152
|
+
let importerUrls = importersByDependencyUrl.get(url)
|
|
153
|
+
if (importerUrls === undefined || importerUrls.size === 0) return null
|
|
154
|
+
|
|
155
|
+
let importers = [...importerUrls]
|
|
156
|
+
.map((importerUrl) => modulesByUrl.get(importerUrl))
|
|
157
|
+
.filter((importer): importer is ModuleRecord => importer !== undefined)
|
|
158
|
+
if (importers.length === 0) return null
|
|
159
|
+
|
|
160
|
+
let boundaries: HotUpdateBoundary[] = []
|
|
161
|
+
for (let importer of importers) {
|
|
162
|
+
if (importer.hmr.acceptedDeps.includes(url)) {
|
|
163
|
+
boundaries.push(createHotUpdateBoundary(url, importer.url, invalidatedUrls, timestamp))
|
|
164
|
+
continue
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
let importerUpdates = findPropagation(importer.url, timestamp, traversedUrls, [
|
|
168
|
+
...invalidatedUrls,
|
|
169
|
+
importer.url,
|
|
170
|
+
])
|
|
171
|
+
if (importerUpdates === null) return null
|
|
172
|
+
boundaries.push(...importerUpdates)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return deduplicateHotUpdateBoundaries(boundaries)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function createHotUpdateBoundary(
|
|
180
|
+
acceptedDependencyUrl: string,
|
|
181
|
+
updateHandlerUrl: string,
|
|
182
|
+
invalidatedUrls: readonly string[],
|
|
183
|
+
timestamp: number,
|
|
184
|
+
): HotUpdateBoundary {
|
|
185
|
+
let invalidationTimestamps: Record<string, number> = {}
|
|
186
|
+
for (let invalidatedUrl of invalidatedUrls) {
|
|
187
|
+
invalidationTimestamps[invalidatedUrl] = timestamp
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
acceptedDependencyUrl,
|
|
192
|
+
invalidatedUrls: invalidationTimestamps,
|
|
193
|
+
updateHandlerUrl,
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function deduplicateHotUpdateBoundaries(
|
|
198
|
+
boundaries: readonly HotUpdateBoundary[],
|
|
199
|
+
): HotUpdateBoundary[] {
|
|
200
|
+
let boundaryByKey = new Map<string, HotUpdateBoundary>()
|
|
201
|
+
let result: HotUpdateBoundary[] = []
|
|
202
|
+
for (let boundary of boundaries) {
|
|
203
|
+
let key = `${boundary.updateHandlerUrl}\0${boundary.acceptedDependencyUrl}`
|
|
204
|
+
let existingBoundary = boundaryByKey.get(key)
|
|
205
|
+
if (existingBoundary !== undefined) {
|
|
206
|
+
Object.assign(existingBoundary.invalidatedUrls, boundary.invalidatedUrls)
|
|
207
|
+
continue
|
|
208
|
+
}
|
|
209
|
+
boundaryByKey.set(key, boundary)
|
|
210
|
+
result.push(boundary)
|
|
211
|
+
}
|
|
212
|
+
return result
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function addToIndexedSet(map: Map<string, Set<string>>, key: string, value: string): void {
|
|
216
|
+
let values = map.get(key) ?? new Set()
|
|
217
|
+
values.add(value)
|
|
218
|
+
map.set(key, values)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function removeFromIndexedSet(map: Map<string, Set<string>>, key: string, value: string): void {
|
|
222
|
+
let values = map.get(key)
|
|
223
|
+
if (values === undefined) return
|
|
224
|
+
values.delete(value)
|
|
225
|
+
if (values.size === 0) {
|
|
226
|
+
map.delete(key)
|
|
227
|
+
}
|
|
228
|
+
}
|