@remix-run/assets 0.4.4 → 0.6.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.
Files changed (83) hide show
  1. package/README.md +331 -72
  2. package/dist/assets.d.ts +4 -1
  3. package/dist/assets.d.ts.map +1 -1
  4. package/dist/assets.js +2 -2
  5. package/dist/lib/access.d.ts +35 -4
  6. package/dist/lib/access.d.ts.map +1 -1
  7. package/dist/lib/access.js +332 -11
  8. package/dist/lib/asset-server.d.ts +138 -11
  9. package/dist/lib/asset-server.d.ts.map +1 -1
  10. package/dist/lib/asset-server.js +281 -30
  11. package/dist/lib/compilation-error.d.ts +1 -1
  12. package/dist/lib/compilation-error.d.ts.map +1 -1
  13. package/dist/lib/file-matcher.js +1 -1
  14. package/dist/lib/files/compiler.d.ts.map +1 -1
  15. package/dist/lib/files/compiler.js +9 -8
  16. package/dist/lib/files/config.js +1 -1
  17. package/dist/lib/hmr.d.ts +37 -0
  18. package/dist/lib/hmr.d.ts.map +1 -0
  19. package/dist/lib/hmr.js +357 -0
  20. package/dist/lib/injected-packages.d.ts +3 -2
  21. package/dist/lib/injected-packages.d.ts.map +1 -1
  22. package/dist/lib/injected-packages.js +43 -17
  23. package/dist/lib/inspection.d.ts +39 -0
  24. package/dist/lib/inspection.d.ts.map +1 -0
  25. package/dist/lib/inspection.js +160 -0
  26. package/dist/lib/loaders.d.ts +57 -0
  27. package/dist/lib/loaders.d.ts.map +1 -0
  28. package/dist/lib/loaders.js +1 -0
  29. package/dist/lib/module-store.d.ts +24 -0
  30. package/dist/lib/module-store.d.ts.map +1 -1
  31. package/dist/lib/module-store.js +136 -11
  32. package/dist/lib/routes.d.ts +11 -3
  33. package/dist/lib/routes.d.ts.map +1 -1
  34. package/dist/lib/routes.js +124 -80
  35. package/dist/lib/scripts/compiler.d.ts +24 -1
  36. package/dist/lib/scripts/compiler.d.ts.map +1 -1
  37. package/dist/lib/scripts/compiler.js +183 -29
  38. package/dist/lib/scripts/conditions.d.ts +2 -0
  39. package/dist/lib/scripts/conditions.d.ts.map +1 -0
  40. package/dist/lib/scripts/conditions.js +1 -0
  41. package/dist/lib/scripts/emit.d.ts +3 -0
  42. package/dist/lib/scripts/emit.d.ts.map +1 -1
  43. package/dist/lib/scripts/emit.js +24 -5
  44. package/dist/lib/scripts/resolve.d.ts +4 -0
  45. package/dist/lib/scripts/resolve.d.ts.map +1 -1
  46. package/dist/lib/scripts/resolve.js +75 -10
  47. package/dist/lib/scripts/transform.d.ts +9 -0
  48. package/dist/lib/scripts/transform.d.ts.map +1 -1
  49. package/dist/lib/scripts/transform.js +222 -18
  50. package/dist/lib/source-maps.js +1 -1
  51. package/dist/lib/styles/compiler.d.ts +14 -1
  52. package/dist/lib/styles/compiler.d.ts.map +1 -1
  53. package/dist/lib/styles/compiler.js +78 -14
  54. package/dist/lib/styles/emit.js +4 -4
  55. package/dist/lib/styles/resolve.d.ts.map +1 -1
  56. package/dist/lib/styles/resolve.js +16 -15
  57. package/dist/lib/styles/transform.js +5 -5
  58. package/dist/lib/target.d.ts +1 -1
  59. package/dist/lib/target.d.ts.map +1 -1
  60. package/dist/lib/watch.js +1 -1
  61. package/dist/types/hmr.d.ts +36 -0
  62. package/package.json +16 -11
  63. package/src/assets.ts +4 -1
  64. package/src/lib/access.ts +445 -11
  65. package/src/lib/asset-server.ts +466 -27
  66. package/src/lib/compilation-error.ts +4 -3
  67. package/src/lib/files/compiler.ts +9 -5
  68. package/src/lib/hmr.ts +397 -0
  69. package/src/lib/injected-packages.ts +52 -16
  70. package/src/lib/inspection.ts +229 -0
  71. package/src/lib/loaders.ts +80 -0
  72. package/src/lib/module-store.ts +190 -9
  73. package/src/lib/routes.ts +158 -126
  74. package/src/lib/scripts/compiler.ts +248 -24
  75. package/src/lib/scripts/conditions.ts +1 -0
  76. package/src/lib/scripts/emit.ts +50 -5
  77. package/src/lib/scripts/resolve.ts +129 -7
  78. package/src/lib/scripts/transform.ts +290 -19
  79. package/src/lib/styles/compiler.ts +108 -8
  80. package/src/lib/styles/emit.ts +1 -1
  81. package/src/lib/styles/resolve.ts +19 -15
  82. package/src/lib/styles/transform.ts +2 -2
  83. package/src/types/hmr.d.ts +36 -0
@@ -2,22 +2,23 @@ type AssetServerCompilationErrorCode =
2
2
  | 'FILE_NOT_FOUND'
3
3
  | 'FILE_NOT_ALLOWED'
4
4
  | 'FILE_NOT_SUPPORTED'
5
- | 'FILE_OUTSIDE_FILE_MAP'
5
+ | 'FILE_OUTSIDE_MOUNTS'
6
6
  | 'FILE_TRANSFORM_QUERY_INVALID'
7
7
  | 'FILE_TRANSFORM_NOT_SUPPORTED'
8
8
  | 'FILE_TRANSFORM_RESULT_INVALID'
9
9
  | 'FILE_TRANSFORM_FAILED'
10
10
  | 'COMMONJS_NOT_SUPPORTED'
11
11
  | 'TRANSFORM_FAILED'
12
+ | 'LOADER_FAILED'
12
13
  | 'EMIT_FAILED'
13
14
  | 'IMPORT_RESOLUTION_FAILED'
14
15
  | 'IMPORT_NOT_SUPPORTED'
15
16
  | 'IMPORT_NOT_ALLOWED'
16
- | 'IMPORT_OUTSIDE_FILE_MAP'
17
+ | 'IMPORT_OUTSIDE_MOUNTS'
17
18
  | 'URL_RESOLUTION_FAILED'
18
19
  | 'URL_NOT_SUPPORTED'
19
20
  | 'URL_NOT_ALLOWED'
20
- | 'URL_OUTSIDE_FILE_MAP'
21
+ | 'URL_OUTSIDE_MOUNTS'
21
22
 
22
23
  /**
23
24
  * Internal error used by the request-time asset compilation pipeline.
@@ -554,17 +554,21 @@ export function resolveServedFileOrThrow(filePath: string, args: ResolveArgs): R
554
554
  }
555
555
 
556
556
  if (!args.isAllowed(identityPath)) {
557
- throw createAssetServerCompilationError(`File is not allowed: ${identityPath}`, {
558
- code: 'FILE_NOT_ALLOWED',
559
- })
557
+ throw createAssetServerCompilationError(
558
+ `File "${identityPath}" is not allowed by the asset server access configuration. ` +
559
+ `Add a matching allowFiles or allowPackages rule, or remove a conflicting denyFiles rule.`,
560
+ {
561
+ code: 'FILE_NOT_ALLOWED',
562
+ },
563
+ )
560
564
  }
561
565
 
562
566
  let stableUrlPathname = args.routes.toUrlPathname(identityPath)
563
567
  if (!stableUrlPathname) {
564
568
  throw createAssetServerCompilationError(
565
- `File ${identityPath} is outside all configured fileMap entries.`,
569
+ `File ${identityPath} is outside all configured mounts.`,
566
570
  {
567
- code: 'FILE_OUTSIDE_FILE_MAP',
571
+ code: 'FILE_OUTSIDE_MOUNTS',
568
572
  },
569
573
  )
570
574
  }
package/src/lib/hmr.ts ADDED
@@ -0,0 +1,397 @@
1
+ /**
2
+ * Messages consumed by the browser HMR client.
3
+ *
4
+ * Server runtimes use `server:update` to request reconciliation with freshly rendered server
5
+ * output. Asset tooling uses `browser:update` for updates accepted by JavaScript or CSS boundaries,
6
+ * and falls back to `browser:reload` when the page must reload.
7
+ */
8
+ export type HmrPayload =
9
+ | {
10
+ /** Indicates that server-rendered output may have changed. */
11
+ type: 'server:update'
12
+ }
13
+ | {
14
+ /** JavaScript and CSS modules that the browser should update. */
15
+ updates: Array<
16
+ | {
17
+ /** Importing module whose dependency-accept handler accepts this update. */
18
+ acceptedPath?: string
19
+ /** Public URL of the changed JavaScript module. */
20
+ path: string
21
+ /** Identifies a JavaScript module update. */
22
+ type: 'js'
23
+ }
24
+ | {
25
+ /** Public URL of the changed stylesheet. */
26
+ path: string
27
+ /** Identifies a stylesheet update. */
28
+ type: 'css'
29
+ }
30
+ >
31
+ /** Update time used to bypass browser module and stylesheet caches. */
32
+ timestamp: number
33
+ /** Indicates that the listed browser modules should update in place. */
34
+ type: 'browser:update'
35
+ }
36
+ | {
37
+ /** Indicates that no HMR boundary accepted the change and the page must reload. */
38
+ type: 'browser:reload'
39
+ }
40
+
41
+ export function createHmrClientSource(options: { eventPathname: string }): string {
42
+ return `
43
+ const contexts = new Map()
44
+ const dataByPath = new Map()
45
+
46
+ class RemixHmrContext {
47
+ constructor(path) {
48
+ this.path = path
49
+ this.data = dataByPath.get(path) ?? {}
50
+ this.acceptCallbacks = []
51
+ this.acceptDependencyCallbacks = []
52
+ this.disposeCallbacks = []
53
+ this.customEventCallbacks = new Map()
54
+ this.invalidated = false
55
+ this.updating = false
56
+ dataByPath.set(path, this.data)
57
+ }
58
+
59
+ accept(deps, callback) {
60
+ if (typeof deps === 'string') {
61
+ this.acceptDependencyCallbacks.push({
62
+ deps: [normalizeAcceptedDependency(this.path, deps)],
63
+ callback: callback ?? (() => {}),
64
+ })
65
+ return
66
+ }
67
+
68
+ if (Array.isArray(deps)) {
69
+ this.acceptDependencyCallbacks.push({
70
+ deps: deps.map((dep) => normalizeAcceptedDependency(this.path, dep)),
71
+ callback: callback ?? (() => {}),
72
+ })
73
+ return
74
+ }
75
+
76
+ this.acceptCallbacks.push(deps ?? (() => {}))
77
+ }
78
+
79
+ dispose(callback) {
80
+ this.disposeCallbacks.push(callback)
81
+ }
82
+
83
+ invalidate(message) {
84
+ this.invalidated = true
85
+ if (this.updating) {
86
+ if (message) console.debug(message)
87
+ return
88
+ }
89
+ if (message) console.debug(message)
90
+ reloadPage()
91
+ }
92
+
93
+ on(event, callback) {
94
+ let callbacks = this.customEventCallbacks.get(event)
95
+ if (!callbacks) {
96
+ callbacks = []
97
+ this.customEventCallbacks.set(event, callbacks)
98
+ }
99
+ callbacks.push(callback)
100
+ }
101
+ }
102
+
103
+ export function createHotContext(path) {
104
+ let context = new RemixHmrContext(path)
105
+ contexts.set(path, context)
106
+ return context
107
+ }
108
+
109
+ let connected = false
110
+ let reconnectPending = false
111
+ let pageReloadTimer
112
+ let failedJavaScriptUpdates = new Map()
113
+ let stylesheetUpdatePromise = Promise.resolve()
114
+
115
+ let events = new EventSource(${JSON.stringify(options.eventPathname)})
116
+
117
+ events.onopen = () => {
118
+ console.debug('[remix] HMR connected')
119
+ if (reconnectPending) {
120
+ reconnectPending = false
121
+ console.log('[remix] HMR reconnected')
122
+ handleReconnect().catch((error) => {
123
+ console.error('[remix] HMR reconnect recovery failed', error)
124
+ reloadPage()
125
+ })
126
+ }
127
+ connected = true
128
+ }
129
+
130
+ events.onerror = () => {
131
+ if (!connected) return
132
+ connected = false
133
+ reconnectPending = true
134
+ console.log('[remix] HMR connection lost, retrying...')
135
+ }
136
+
137
+ events.onmessage = (event) => {
138
+ let payload = JSON.parse(event.data)
139
+ handlePayload(payload).catch((error) => {
140
+ console.error('[remix] HMR update failed', error)
141
+ if (payload.type !== 'browser:update' || payload.updates.some((update) => update.type !== 'js')) {
142
+ reloadPage()
143
+ }
144
+ })
145
+ }
146
+
147
+ async function handlePayload(payload) {
148
+ if (payload.type === 'browser:reload') {
149
+ reloadPage()
150
+ return
151
+ }
152
+
153
+ if (payload.type === 'server:update') {
154
+ await retryFailedJavaScriptUpdates(payload)
155
+ await dispatchCustomEvent(payload.type, payload)
156
+ return
157
+ }
158
+
159
+ if (payload.type === 'browser:update') {
160
+ for (let update of payload.updates) {
161
+ if (update.type === 'css') {
162
+ let updated = await queueStylesheetUpdate(update.path, payload.timestamp)
163
+ if (updated) console.debug('[remix] HMR updated stylesheet', update.path)
164
+ continue
165
+ }
166
+
167
+ try {
168
+ let updated = await updateJavaScriptModule(
169
+ update.path,
170
+ update.acceptedPath ?? update.path,
171
+ payload.timestamp,
172
+ )
173
+ failedJavaScriptUpdates.delete(update.path)
174
+ if (updated) console.debug('[remix] HMR accepted update', update.path)
175
+ } catch (error) {
176
+ failedJavaScriptUpdates.set(update.path, update.acceptedPath ?? update.path)
177
+ throw error
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+ async function handleReconnect() {
184
+ let data = { timestamp: Date.now() }
185
+ await reloadCurrentStylesheets(data)
186
+ await retryFailedJavaScriptUpdates(data)
187
+ }
188
+
189
+ async function retryFailedJavaScriptUpdates(data) {
190
+ if (failedJavaScriptUpdates.size === 0) return
191
+
192
+ let timestamp = getTimestamp(data)
193
+ for (let [path, acceptedPath] of Array.from(failedJavaScriptUpdates)) {
194
+ try {
195
+ let updated = await updateJavaScriptModule(path, acceptedPath, timestamp)
196
+ failedJavaScriptUpdates.delete(path)
197
+ if (updated) console.debug('[remix] HMR recovered update', path)
198
+ } catch (error) {
199
+ console.error('[remix] HMR recovery update failed', error)
200
+ }
201
+ }
202
+ }
203
+
204
+ async function reloadCurrentStylesheets(data) {
205
+ let timestamp = getTimestamp(data)
206
+ let paths = new Set()
207
+ for (let link of document.querySelectorAll('link[rel="stylesheet"]')) {
208
+ if (link.dataset.remixHmrStylesheet === 'true') continue
209
+ let url = new URL(link.href)
210
+ if (url.origin !== location.origin) continue
211
+ paths.add(url.pathname)
212
+ }
213
+
214
+ for (let path of paths) {
215
+ await queueStylesheetUpdate(path, timestamp)
216
+ }
217
+ }
218
+
219
+ function getTimestamp(data) {
220
+ if (data && typeof data === 'object' && typeof data.timestamp === 'number') {
221
+ return data.timestamp
222
+ }
223
+
224
+ return Date.now()
225
+ }
226
+
227
+ async function updateJavaScriptModule(path, acceptedPath, timestamp) {
228
+ let previousContext = contexts.get(path)
229
+ if (!previousContext) return false
230
+
231
+ let isSelfUpdate = path === acceptedPath
232
+ let dependencyCallbacks = getAcceptDependencyCallbacks(previousContext, acceptedPath)
233
+
234
+ if (
235
+ isSelfUpdate ? previousContext.acceptCallbacks.length === 0 : dependencyCallbacks.length === 0
236
+ ) {
237
+ console.log('[remix] HMR no accept handler, reloading page', path)
238
+ reloadPage()
239
+ return false
240
+ }
241
+
242
+ if (isSelfUpdate) {
243
+ for (let callback of previousContext.disposeCallbacks) {
244
+ await callback(previousContext.data)
245
+ }
246
+
247
+ let updatedModule = await import(withTimestamp(path, timestamp))
248
+ previousContext.invalidated = false
249
+ previousContext.updating = true
250
+ try {
251
+ for (let callback of previousContext.acceptCallbacks) {
252
+ await callback(updatedModule)
253
+ }
254
+ } finally {
255
+ previousContext.updating = false
256
+ }
257
+ if (previousContext.invalidated) {
258
+ await propagateInvalidatedJavaScriptModule(path, timestamp)
259
+ }
260
+ return true
261
+ }
262
+
263
+ let acceptedContext = contexts.get(acceptedPath)
264
+ if (acceptedContext) {
265
+ for (let callback of acceptedContext.disposeCallbacks) {
266
+ await callback(acceptedContext.data)
267
+ }
268
+ }
269
+
270
+ let updatedModule = await import(withTimestamp(acceptedPath, timestamp))
271
+ previousContext.invalidated = false
272
+ previousContext.updating = true
273
+ try {
274
+ for (let { deps, callback } of dependencyCallbacks) {
275
+ if (deps.length === 1) {
276
+ await callback(updatedModule)
277
+ } else {
278
+ await callback(deps.map((dep) => (dep === acceptedPath ? updatedModule : undefined)))
279
+ }
280
+ }
281
+ } finally {
282
+ previousContext.updating = false
283
+ }
284
+ if (previousContext.invalidated) {
285
+ await propagateInvalidatedJavaScriptModule(path, timestamp)
286
+ }
287
+ return true
288
+ }
289
+
290
+ async function propagateInvalidatedJavaScriptModule(path, timestamp) {
291
+ let updated = false
292
+ for (let [importerPath, importerContext] of contexts) {
293
+ if (importerPath === path) continue
294
+ let callbacks = getAcceptDependencyCallbacks(importerContext, path)
295
+ if (callbacks.length === 0) continue
296
+
297
+ for (let callback of importerContext.disposeCallbacks) {
298
+ await callback(importerContext.data)
299
+ }
300
+
301
+ let updatedModule = await import(withTimestamp(path, timestamp))
302
+ importerContext.invalidated = false
303
+ importerContext.updating = true
304
+ try {
305
+ for (let { deps, callback } of callbacks) {
306
+ if (deps.length === 1) {
307
+ await callback(updatedModule)
308
+ } else {
309
+ await callback(deps.map((dep) => (dep === path ? updatedModule : undefined)))
310
+ }
311
+ }
312
+ } finally {
313
+ importerContext.updating = false
314
+ }
315
+ updated = true
316
+
317
+ if (importerContext.invalidated) {
318
+ reloadPage()
319
+ return
320
+ }
321
+ }
322
+
323
+ if (!updated) reloadPage()
324
+ }
325
+
326
+ function getAcceptDependencyCallbacks(context, acceptedPath) {
327
+ return context.acceptDependencyCallbacks.filter(({ deps }) => deps.includes(acceptedPath))
328
+ }
329
+
330
+ function normalizeAcceptedDependency(importerPath, dep) {
331
+ if (dep.startsWith('/')) return dep
332
+ return new URL(dep, new URL(importerPath, window.location.href)).pathname
333
+ }
334
+
335
+ async function dispatchCustomEvent(event, data) {
336
+ for (let context of contexts.values()) {
337
+ let callbacks = context.customEventCallbacks.get(event) ?? []
338
+ for (let callback of callbacks) {
339
+ await callback(data)
340
+ }
341
+ }
342
+ }
343
+
344
+ async function reloadStylesheet(path, timestamp) {
345
+ let links = document.querySelectorAll('link[rel="stylesheet"]')
346
+ let updates = []
347
+ for (let link of links) {
348
+ let url = new URL(link.href)
349
+ if (url.pathname !== path) continue
350
+ if (link.dataset.remixHmrStylesheet === 'true') continue
351
+
352
+ updates.push(loadStylesheet(link, path, timestamp))
353
+ }
354
+
355
+ if (updates.length === 0) return false
356
+ return (await Promise.all(updates)).some(Boolean)
357
+ }
358
+
359
+ async function queueStylesheetUpdate(path, timestamp) {
360
+ let update = stylesheetUpdatePromise.then(() => reloadStylesheet(path, timestamp))
361
+ stylesheetUpdatePromise = update.catch(() => {})
362
+ return update
363
+ }
364
+
365
+ function loadStylesheet(link, path, timestamp) {
366
+ return new Promise((resolve) => {
367
+ let next = link.cloneNode()
368
+ next.dataset.remixHmrStylesheet = 'true'
369
+ next.href = withTimestamp(path, timestamp)
370
+ next.onload = () => {
371
+ delete next.dataset.remixHmrStylesheet
372
+ link.remove()
373
+ resolve(true)
374
+ }
375
+ next.onerror = () => {
376
+ next.remove()
377
+ resolve(false)
378
+ }
379
+ link.after(next)
380
+ })
381
+ }
382
+
383
+ function withTimestamp(path, timestamp) {
384
+ let url = new URL(path, location.href)
385
+ url.searchParams.set('t', String(timestamp))
386
+ return url.pathname + url.search
387
+ }
388
+
389
+ function reloadPage() {
390
+ if (pageReloadTimer) clearTimeout(pageReloadTimer)
391
+ pageReloadTimer = setTimeout(() => {
392
+ console.debug('[remix] HMR reloading page')
393
+ window.location.href = window.location.href
394
+ }, 20)
395
+ }
396
+ `.trimStart()
397
+ }
@@ -9,6 +9,8 @@ type ResolvedInjectedPackage = {
9
9
  }
10
10
 
11
11
  const injectedPackageNames = ['@oxc-project/runtime'] as const
12
+ const authoredInjectedPackageNames = ['@oxc-project/runtime'] as const
13
+ const generatedInjectedPackageSpecifiers = [] as const
12
14
  const injectedPackagesBasePath = '/__@remix/injected'
13
15
 
14
16
  const resolvedInjectedPackages = new Map<string, ResolvedInjectedPackage>()
@@ -26,24 +28,37 @@ export function isInjectedPackageFilePath(filePath: string): boolean {
26
28
  return false
27
29
  }
28
30
 
29
- export function getInjectedPackageRouteConfigs(): {
30
- fileMap: Record<string, string>
31
+ export function getInjectedPackageMountConfigs(): {
32
+ mounts: Record<string, string>
31
33
  rootDir: string
32
34
  }[] {
33
35
  return injectedPackageNames.map((packageName) => {
34
36
  let { packageRoot } = getResolvedInjectedPackage(packageName)
37
+ let { fileRoot, routeRoot } = getInjectedPackageRoute(packageRoot, packageName)
35
38
 
36
39
  return {
37
- fileMap: {
38
- [getInjectedPackageRoutePattern(packageName)]: `${packageName}/*path`,
40
+ mounts: {
41
+ [getInjectedPackageMountPath(packageName)]: fileRoot,
39
42
  },
40
- rootDir: getInjectedPackageRouteRoot(packageRoot, packageName),
43
+ rootDir: routeRoot,
41
44
  }
42
45
  })
43
46
  }
44
47
 
48
+ export function getInjectedPackageRoots(): readonly string[] {
49
+ return injectedPackageNames.map(
50
+ (packageName) => getResolvedInjectedPackage(packageName).packageRoot,
51
+ )
52
+ }
53
+
45
54
  export function getInjectedPackageNameForSpecifier(specifier: string): string | null {
46
- for (let packageName of injectedPackageNames) {
55
+ for (let injectedSpecifier of generatedInjectedPackageSpecifiers) {
56
+ if (specifier === injectedSpecifier) {
57
+ return getPackageName(injectedSpecifier)
58
+ }
59
+ }
60
+
61
+ for (let packageName of authoredInjectedPackageNames) {
47
62
  if (specifier === packageName || specifier.startsWith(`${packageName}/`)) {
48
63
  return packageName
49
64
  }
@@ -53,19 +68,22 @@ export function getInjectedPackageNameForSpecifier(specifier: string): string |
53
68
  }
54
69
 
55
70
  export function mayContainInjectedPackageSpecifier(sourceText: string): boolean {
56
- return injectedPackageNames.some((packageName) => sourceText.includes(packageName))
71
+ return authoredInjectedPackageNames.some((packageName) => sourceText.includes(packageName))
57
72
  }
58
73
 
59
74
  export function maskAuthoredInjectedPackageSpecifier(specifier: string): string | null {
60
- let packageName = getInjectedPackageNameForSpecifier(specifier)
61
- if (!packageName) return null
75
+ for (let packageName of authoredInjectedPackageNames) {
76
+ if (specifier !== packageName && !specifier.startsWith(`${packageName}/`)) continue
77
+
78
+ let maskedPackageName = getMaskedInjectedPackageName(packageName)
79
+ return `${maskedPackageName}${specifier.slice(packageName.length)}`
80
+ }
62
81
 
63
- let maskedPackageName = getMaskedInjectedPackageName(packageName)
64
- return `${maskedPackageName}${specifier.slice(packageName.length)}`
82
+ return null
65
83
  }
66
84
 
67
85
  export function restoreAuthoredInjectedPackageSpecifier(specifier: string): string | null {
68
- for (let packageName of injectedPackageNames) {
86
+ for (let packageName of authoredInjectedPackageNames) {
69
87
  let maskedPackageName = getMaskedInjectedPackageName(packageName)
70
88
  if (specifier === maskedPackageName) {
71
89
  return packageName
@@ -82,6 +100,11 @@ function getMaskedInjectedPackageName(packageName: string): string {
82
100
  return `~${packageName.slice(1)}`
83
101
  }
84
102
 
103
+ function getPackageName(specifier: string): string {
104
+ let parts = specifier.split('/')
105
+ return parts[0]?.startsWith('@') ? `${parts[0]}/${parts[1]}` : (parts[0] ?? specifier)
106
+ }
107
+
85
108
  export function getInjectedPackageImporterPath(): string {
86
109
  return normalizeFilePath(fileURLToPath(import.meta.url))
87
110
  }
@@ -102,16 +125,29 @@ function getResolvedInjectedPackage(packageName: string): ResolvedInjectedPackag
102
125
  return resolvedInjectedPackage
103
126
  }
104
127
 
105
- function getInjectedPackageRoutePattern(packageName: string): string {
106
- return `${injectedPackagesBasePath}/${packageName}/*path`
128
+ function getInjectedPackageMountPath(packageName: string): string {
129
+ return `${injectedPackagesBasePath}/${packageName}`
107
130
  }
108
131
 
109
- function getInjectedPackageRouteRoot(packageRoot: string, packageName: string): string {
132
+ function getInjectedPackageRoute(
133
+ packageRoot: string,
134
+ packageName: string,
135
+ ): { fileRoot: string; routeRoot: string } {
136
+ if (!packageRoot.endsWith(`/${packageName}`)) {
137
+ return {
138
+ fileRoot: packageRoot.slice(getFilePathDirectory(packageRoot).length + 1),
139
+ routeRoot: getFilePathDirectory(packageRoot),
140
+ }
141
+ }
142
+
110
143
  let routeRoot = packageRoot
111
144
 
112
145
  for (let _segment of packageName.split('/')) {
113
146
  routeRoot = getFilePathDirectory(routeRoot)
114
147
  }
115
148
 
116
- return routeRoot
149
+ return {
150
+ fileRoot: packageName,
151
+ routeRoot,
152
+ }
117
153
  }