@remix-run/assets 0.5.0 → 0.7.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 (84) hide show
  1. package/README.md +185 -104
  2. package/dist/assets.d.ts +4 -1
  3. package/dist/assets.d.ts.map +1 -1
  4. package/dist/lib/access.d.ts +29 -2
  5. package/dist/lib/access.d.ts.map +1 -1
  6. package/dist/lib/access.js +52 -26
  7. package/dist/lib/asset-server.d.ts +61 -25
  8. package/dist/lib/asset-server.d.ts.map +1 -1
  9. package/dist/lib/asset-server.js +127 -55
  10. package/dist/lib/compilation-error.d.ts +1 -1
  11. package/dist/lib/compilation-error.d.ts.map +1 -1
  12. package/dist/lib/files/compiler.d.ts +1 -1
  13. package/dist/lib/files/compiler.d.ts.map +1 -1
  14. package/dist/lib/files/compiler.js +28 -31
  15. package/dist/lib/files/config.d.ts +6 -0
  16. package/dist/lib/files/config.d.ts.map +1 -1
  17. package/dist/lib/files/config.js +9 -0
  18. package/dist/lib/fingerprint.d.ts +0 -4
  19. package/dist/lib/fingerprint.d.ts.map +1 -1
  20. package/dist/lib/fingerprint.js +0 -4
  21. package/dist/lib/hmr.d.ts +7 -0
  22. package/dist/lib/hmr.d.ts.map +1 -1
  23. package/dist/lib/hmr.js +198 -25
  24. package/dist/lib/injected-packages.d.ts +3 -2
  25. package/dist/lib/injected-packages.d.ts.map +1 -1
  26. package/dist/lib/injected-packages.js +11 -8
  27. package/dist/lib/inspection.d.ts +39 -0
  28. package/dist/lib/inspection.d.ts.map +1 -0
  29. package/dist/lib/inspection.js +160 -0
  30. package/dist/lib/routes.d.ts +12 -3
  31. package/dist/lib/routes.d.ts.map +1 -1
  32. package/dist/lib/routes.js +124 -80
  33. package/dist/lib/scripts/compiler.d.ts +8 -2
  34. package/dist/lib/scripts/compiler.d.ts.map +1 -1
  35. package/dist/lib/scripts/compiler.js +188 -38
  36. package/dist/lib/scripts/emit.d.ts +2 -1
  37. package/dist/lib/scripts/emit.d.ts.map +1 -1
  38. package/dist/lib/scripts/emit.js +25 -16
  39. package/dist/lib/scripts/resolve.d.ts +7 -1
  40. package/dist/lib/scripts/resolve.d.ts.map +1 -1
  41. package/dist/lib/scripts/resolve.js +119 -12
  42. package/dist/lib/scripts/specifiers.d.ts +2 -0
  43. package/dist/lib/scripts/specifiers.d.ts.map +1 -0
  44. package/dist/lib/scripts/specifiers.js +9 -0
  45. package/dist/lib/scripts/transform.d.ts +2 -3
  46. package/dist/lib/scripts/transform.d.ts.map +1 -1
  47. package/dist/lib/scripts/transform.js +4 -18
  48. package/dist/lib/styles/compiler.d.ts +0 -1
  49. package/dist/lib/styles/compiler.d.ts.map +1 -1
  50. package/dist/lib/styles/compiler.js +105 -25
  51. package/dist/lib/styles/emit.d.ts +2 -1
  52. package/dist/lib/styles/emit.d.ts.map +1 -1
  53. package/dist/lib/styles/emit.js +12 -10
  54. package/dist/lib/styles/resolve.d.ts +0 -1
  55. package/dist/lib/styles/resolve.d.ts.map +1 -1
  56. package/dist/lib/styles/resolve.js +8 -9
  57. package/dist/lib/styles/transform.d.ts +0 -2
  58. package/dist/lib/styles/transform.d.ts.map +1 -1
  59. package/dist/lib/styles/transform.js +2 -9
  60. package/dist/lib/virtual-store.d.ts +8 -0
  61. package/dist/lib/virtual-store.d.ts.map +1 -0
  62. package/dist/lib/virtual-store.js +100 -0
  63. package/package.json +6 -6
  64. package/src/assets.ts +9 -1
  65. package/src/lib/access.ts +83 -30
  66. package/src/lib/asset-server.ts +224 -94
  67. package/src/lib/compilation-error.ts +4 -3
  68. package/src/lib/files/compiler.ts +32 -42
  69. package/src/lib/files/config.ts +17 -0
  70. package/src/lib/fingerprint.ts +0 -9
  71. package/src/lib/hmr.ts +210 -26
  72. package/src/lib/injected-packages.ts +16 -10
  73. package/src/lib/inspection.ts +229 -0
  74. package/src/lib/routes.ts +158 -126
  75. package/src/lib/scripts/compiler.ts +240 -54
  76. package/src/lib/scripts/emit.ts +34 -19
  77. package/src/lib/scripts/resolve.ts +175 -13
  78. package/src/lib/scripts/specifiers.ts +11 -0
  79. package/src/lib/scripts/transform.ts +6 -25
  80. package/src/lib/styles/compiler.ts +137 -39
  81. package/src/lib/styles/emit.ts +18 -13
  82. package/src/lib/styles/resolve.ts +8 -10
  83. package/src/lib/styles/transform.ts +2 -12
  84. package/src/lib/virtual-store.ts +124 -0
@@ -1 +1 @@
1
- {"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../src/lib/fingerprint.ts"],"names":[],"mappings":"AAGA,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAIhG;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC,eAAe,CAAC,CAAA;CAC9C,GAAG,OAAO,CAAC,MAAM,CAAC,CAIlB;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG;IACxD,QAAQ,EAAE,MAAM,CAAA;IAChB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;CACpC,CAyBA;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAahG;AAED,wBAAgB,iCAAiC,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAE7F"}
1
+ {"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../src/lib/fingerprint.ts"],"names":[],"mappings":"AAGA,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAIhG;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG;IACxD,QAAQ,EAAE,MAAM,CAAA;IAChB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;CACpC,CAyBA;AAED,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAahG;AAED,wBAAgB,iCAAiC,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAE7F"}
@@ -5,10 +5,6 @@ export async function hashContent(content) {
5
5
  let hashBuffer = await crypto.subtle.digest('SHA-256', bytes);
6
6
  return Buffer.from(hashBuffer).toString('base64url').slice(0, 6);
7
7
  }
8
- export async function generateFingerprint(args) {
9
- let content = typeof args.content === 'string' ? args.content : Buffer.from(args.content).toString('base64');
10
- return hashContent(JSON.stringify([content, args.buildId]));
11
- }
12
8
  export function parseFingerprintSuffix(pathname) {
13
9
  let lastSlashIndex = pathname.lastIndexOf('/');
14
10
  let directory = lastSlashIndex >= 0 ? pathname.slice(0, lastSlashIndex + 1) : '';
package/dist/lib/hmr.d.ts CHANGED
@@ -13,6 +13,11 @@ export type HmrPayload = {
13
13
  updates: Array<{
14
14
  /** Importing module whose dependency-accept handler accepts this update. */
15
15
  acceptedPath?: string;
16
+ /** Import map entries required before this update can be evaluated. */
17
+ importMap?: {
18
+ imports: Record<string, string>;
19
+ scopes?: Record<string, Record<string, string>>;
20
+ };
16
21
  /** Public URL of the changed JavaScript module. */
17
22
  path: string;
18
23
  /** Identifies a JavaScript module update. */
@@ -32,6 +37,8 @@ export type HmrPayload = {
32
37
  type: 'browser:reload';
33
38
  };
34
39
  export declare function createHmrClientSource(options: {
40
+ dataKey: string;
35
41
  eventPathname: string;
42
+ moduleImporter: string | null;
36
43
  }): string;
37
44
  //# sourceMappingURL=hmr.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../../src/lib/hmr.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAClB;IACE,8DAA8D;IAC9D,IAAI,EAAE,eAAe,CAAA;CACtB,GACD;IACE,iEAAiE;IACjE,OAAO,EAAE,KAAK,CACV;QACE,4EAA4E;QAC5E,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,mDAAmD;QACnD,IAAI,EAAE,MAAM,CAAA;QACZ,6CAA6C;QAC7C,IAAI,EAAE,IAAI,CAAA;KACX,GACD;QACE,4CAA4C;QAC5C,IAAI,EAAE,MAAM,CAAA;QACZ,sCAAsC;QACtC,IAAI,EAAE,KAAK,CAAA;KACZ,CACJ,CAAA;IACD,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,IAAI,EAAE,gBAAgB,CAAA;CACvB,GACD;IACE,mFAAmF;IACnF,IAAI,EAAE,gBAAgB,CAAA;CACvB,CAAA;AAEL,wBAAgB,qBAAqB,CAAC,OAAO,EAAE;IAAE,aAAa,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAoWhF"}
1
+ {"version":3,"file":"hmr.d.ts","sourceRoot":"","sources":["../../src/lib/hmr.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAClB;IACE,8DAA8D;IAC9D,IAAI,EAAE,eAAe,CAAA;CACtB,GACD;IACE,iEAAiE;IACjE,OAAO,EAAE,KAAK,CACV;QACE,4EAA4E;QAC5E,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,uEAAuE;QACvE,SAAS,CAAC,EAAE;YACV,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;YAC/B,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;SAChD,CAAA;QACD,mDAAmD;QACnD,IAAI,EAAE,MAAM,CAAA;QACZ,6CAA6C;QAC7C,IAAI,EAAE,IAAI,CAAA;KACX,GACD;QACE,4CAA4C;QAC5C,IAAI,EAAE,MAAM,CAAA;QACZ,sCAAsC;QACtC,IAAI,EAAE,KAAK,CAAA;KACZ,CACJ,CAAA;IACD,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,IAAI,EAAE,gBAAgB,CAAA;CACvB,GACD;IACE,mFAAmF;IACnF,IAAI,EAAE,gBAAgB,CAAA;CACvB,CAAA;AAEL,wBAAgB,qBAAqB,CAAC,OAAO,EAAE;IAC7C,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B,GAAG,MAAM,CAmhBT"}
package/dist/lib/hmr.js CHANGED
@@ -1,5 +1,9 @@
1
1
  export function createHmrClientSource(options) {
2
2
  return `
3
+ ${options.moduleImporter
4
+ ? `import { importModule as __remixImport } from ${JSON.stringify(options.moduleImporter)}`
5
+ : 'const __remixImport = (specifier) => import(specifier)'}
6
+
3
7
  const contexts = new Map()
4
8
  const dataByPath = new Map()
5
9
 
@@ -71,6 +75,13 @@ let reconnectPending = false
71
75
  let pageReloadTimer
72
76
  let failedJavaScriptUpdates = new Map()
73
77
  let stylesheetUpdatePromise = Promise.resolve()
78
+ let installedImportMap = { imports: new Map(), scopes: new Map() }
79
+ let processedScripts = new WeakSet()
80
+
81
+ let observer = new MutationObserver(processMutations)
82
+ observer.observe(document, { childList: true })
83
+ observer.observe(document.head, { childList: true })
84
+ processImportMaps()
74
85
 
75
86
  let events = new EventSource(${JSON.stringify(options.eventPathname)})
76
87
 
@@ -96,11 +107,18 @@ events.onerror = () => {
96
107
 
97
108
  events.onmessage = (event) => {
98
109
  let payload = JSON.parse(event.data)
110
+ if (payload.type === 'browser:update') {
111
+ let update = payload.data?.[${JSON.stringify(options.dataKey)}]
112
+ if (!update) return
113
+ handleBrowserUpdate(update).catch((error) => {
114
+ console.error('[remix] HMR update failed', error)
115
+ if (update.updates.some((entry) => entry.type !== 'js')) reloadPage()
116
+ })
117
+ return
118
+ }
99
119
  handlePayload(payload).catch((error) => {
100
120
  console.error('[remix] HMR update failed', error)
101
- if (payload.type !== 'browser:update' || payload.updates.some((update) => update.type !== 'js')) {
102
- reloadPage()
103
- }
121
+ reloadPage()
104
122
  })
105
123
  }
106
124
 
@@ -115,27 +133,28 @@ async function handlePayload(payload) {
115
133
  await dispatchCustomEvent(payload.type, payload)
116
134
  return
117
135
  }
136
+ }
118
137
 
119
- if (payload.type === 'browser:update') {
120
- for (let update of payload.updates) {
121
- if (update.type === 'css') {
122
- let updated = await queueStylesheetUpdate(update.path, payload.timestamp)
123
- if (updated) console.debug('[remix] HMR updated stylesheet', update.path)
124
- continue
125
- }
138
+ async function handleBrowserUpdate(payload) {
139
+ for (let update of payload.updates) {
140
+ if (update.type === 'css') {
141
+ let updated = await queueStylesheetUpdate(update.path, payload.timestamp)
142
+ if (updated) console.debug('[remix] HMR updated stylesheet', update.path)
143
+ continue
144
+ }
126
145
 
127
- try {
128
- let updated = await updateJavaScriptModule(
129
- update.path,
130
- update.acceptedPath ?? update.path,
131
- payload.timestamp,
132
- )
133
- failedJavaScriptUpdates.delete(update.path)
134
- if (updated) console.debug('[remix] HMR accepted update', update.path)
135
- } catch (error) {
136
- failedJavaScriptUpdates.set(update.path, update.acceptedPath ?? update.path)
137
- throw error
138
- }
146
+ try {
147
+ if (update.importMap && !installImportMap(update.importMap)) return
148
+ let updated = await updateJavaScriptModule(
149
+ update.path,
150
+ update.acceptedPath ?? update.path,
151
+ payload.timestamp,
152
+ )
153
+ failedJavaScriptUpdates.delete(update.path)
154
+ if (updated) console.debug('[remix] HMR accepted update', update.path)
155
+ } catch (error) {
156
+ failedJavaScriptUpdates.set(update.path, update.acceptedPath ?? update.path)
157
+ throw error
139
158
  }
140
159
  }
141
160
  }
@@ -204,7 +223,7 @@ async function updateJavaScriptModule(path, acceptedPath, timestamp) {
204
223
  await callback(previousContext.data)
205
224
  }
206
225
 
207
- let updatedModule = await import(withTimestamp(path, timestamp))
226
+ let updatedModule = await __remixImport(withTimestamp(path, timestamp), import.meta.url)
208
227
  previousContext.invalidated = false
209
228
  previousContext.updating = true
210
229
  try {
@@ -227,7 +246,7 @@ async function updateJavaScriptModule(path, acceptedPath, timestamp) {
227
246
  }
228
247
  }
229
248
 
230
- let updatedModule = await import(withTimestamp(acceptedPath, timestamp))
249
+ let updatedModule = await __remixImport(withTimestamp(acceptedPath, timestamp), import.meta.url)
231
250
  previousContext.invalidated = false
232
251
  previousContext.updating = true
233
252
  try {
@@ -258,7 +277,7 @@ async function propagateInvalidatedJavaScriptModule(path, timestamp) {
258
277
  await callback(importerContext.data)
259
278
  }
260
279
 
261
- let updatedModule = await import(withTimestamp(path, timestamp))
280
+ let updatedModule = await __remixImport(withTimestamp(path, timestamp), import.meta.url)
262
281
  importerContext.invalidated = false
263
282
  importerContext.updating = true
264
283
  try {
@@ -346,6 +365,160 @@ function withTimestamp(path, timestamp) {
346
365
  return url.pathname + url.search
347
366
  }
348
367
 
368
+ function installImportMap(importMap) {
369
+ processMutations(observer.takeRecords())
370
+ processImportMaps()
371
+ let delta = getImportMapDelta(installedImportMap, importMap)
372
+ if (delta === null) {
373
+ reloadPage()
374
+ return false
375
+ }
376
+ if (!delta) return true
377
+
378
+ let script = document.createElement('script')
379
+ script.setAttribute('data-rmx-import-map', '')
380
+ script.type = 'importmap'
381
+ script.textContent = JSON.stringify(delta)
382
+ document.head.appendChild(script)
383
+ processedScripts.add(script)
384
+ mergeImportMap(installedImportMap, delta)
385
+ return true
386
+ }
387
+
388
+ function processImportMap(script) {
389
+ if (processedScripts.has(script)) return
390
+ processedScripts.add(script)
391
+
392
+ let importMap
393
+ try {
394
+ importMap = JSON.parse(script.textContent ?? '')
395
+ } catch {
396
+ return
397
+ }
398
+ if (!importMap || typeof importMap !== 'object') return
399
+ mergeImportMap(installedImportMap, importMap)
400
+ }
401
+
402
+ function processImportMaps() {
403
+ for (let script of document.querySelectorAll('script[type="importmap"]')) {
404
+ processImportMap(script)
405
+ }
406
+ }
407
+
408
+ function processMutations(mutations) {
409
+ for (let mutation of mutations) {
410
+ if (mutation.type !== 'childList') continue
411
+ for (let node of mutation.addedNodes) {
412
+ if (node instanceof HTMLScriptElement && node.matches('script[type="importmap"]')) {
413
+ processImportMap(node)
414
+ }
415
+ }
416
+ }
417
+ }
418
+
419
+ function getImportMapDelta(installed, importMap) {
420
+ let imports = getImportMapImportsDelta(installed.imports, importMap.imports)
421
+ if (imports === null) return null
422
+
423
+ let scopes
424
+ if (importMap.scopes && typeof importMap.scopes === 'object') {
425
+ scopes = {}
426
+ for (let [scope, entries] of Object.entries(importMap.scopes)) {
427
+ let normalizedScope = normalizeImportMapUrl(scope)
428
+ if (!normalizedScope || !entries || typeof entries !== 'object') continue
429
+ let installedEntries = installed.scopes.get(normalizedScope) ?? new Map()
430
+ let entriesDelta = getImportMapImportsDelta(installedEntries, entries, scope)
431
+ if (entriesDelta === null) return null
432
+ if (entriesDelta) scopes[scope] = entriesDelta
433
+ }
434
+ if (Object.keys(scopes).length === 0) scopes = undefined
435
+ }
436
+
437
+ if (!imports && !scopes) return undefined
438
+ return {
439
+ ...(imports ? { imports } : null),
440
+ ...(scopes ? { scopes } : null),
441
+ }
442
+ }
443
+
444
+ function getImportMapImportsDelta(installed, entries, scope) {
445
+ if (!entries || typeof entries !== 'object') return undefined
446
+ let delta = {}
447
+ for (let [specifier, href] of Object.entries(entries)) {
448
+ if (href !== null && typeof href !== 'string') continue
449
+ let normalizedSpecifier = normalizeImportMapSpecifier(specifier)
450
+ if (!normalizedSpecifier) continue
451
+ let normalizedHref = normalizeImportMapAddress(href)
452
+ let existing = installed.get(normalizedSpecifier)
453
+ if (existing === normalizedHref) continue
454
+ if (existing !== undefined) {
455
+ let scopeDescription = scope ? \` in scope "\${scope}"\` : ''
456
+ console.warn(
457
+ \`[remix] HMR reloading page after import map conflict for "\${specifier}"\${scopeDescription}: \` +
458
+ formatImportMapAddress(existing) +
459
+ ' is already installed, but the new map points to ' +
460
+ formatImportMapAddress(normalizedHref),
461
+ )
462
+ return null
463
+ }
464
+ delta[specifier] = href
465
+ }
466
+ return Object.keys(delta).length > 0 ? delta : undefined
467
+ }
468
+
469
+ function mergeImportMap(installed, importMap) {
470
+ mergeImportMapImports(installed.imports, importMap.imports)
471
+ if (!importMap.scopes || typeof importMap.scopes !== 'object') return
472
+ for (let [scope, entries] of Object.entries(importMap.scopes)) {
473
+ let normalizedScope = normalizeImportMapUrl(scope)
474
+ if (!normalizedScope || !entries || typeof entries !== 'object') continue
475
+ let installedEntries = installed.scopes.get(normalizedScope)
476
+ if (!installedEntries) {
477
+ installedEntries = new Map()
478
+ installed.scopes.set(normalizedScope, installedEntries)
479
+ }
480
+ mergeImportMapImports(installedEntries, entries)
481
+ }
482
+ }
483
+
484
+ function mergeImportMapImports(installed, entries) {
485
+ if (!entries || typeof entries !== 'object') return
486
+ for (let [specifier, href] of Object.entries(entries)) {
487
+ if (href !== null && typeof href !== 'string') continue
488
+ let normalizedSpecifier = normalizeImportMapSpecifier(specifier)
489
+ if (!normalizedSpecifier || installed.has(normalizedSpecifier)) continue
490
+ installed.set(normalizedSpecifier, normalizeImportMapAddress(href))
491
+ }
492
+ }
493
+
494
+ function normalizeImportMapSpecifier(specifier) {
495
+ if (
496
+ specifier.startsWith('/') ||
497
+ specifier.startsWith('./') ||
498
+ specifier.startsWith('../') ||
499
+ URL.canParse(specifier)
500
+ ) {
501
+ return normalizeImportMapUrl(specifier)
502
+ }
503
+ return specifier
504
+ }
505
+
506
+ function normalizeImportMapAddress(href) {
507
+ return href === null ? null : normalizeImportMapUrl(href)
508
+ }
509
+
510
+ function formatImportMapAddress(href) {
511
+ return href === null ? 'null' : '"' + href + '"'
512
+ }
513
+
514
+ function normalizeImportMapUrl(value) {
515
+ try {
516
+ return new URL(value, document.baseURI).href
517
+ } catch {
518
+ return null
519
+ }
520
+ }
521
+
349
522
  function reloadPage() {
350
523
  if (pageReloadTimer) clearTimeout(pageReloadTimer)
351
524
  pageReloadTimer = setTimeout(() => {
@@ -1,8 +1,9 @@
1
1
  export declare function isInjectedPackageFilePath(filePath: string): boolean;
2
- export declare function getInjectedPackageRouteConfigs(): {
3
- fileMap: Record<string, string>;
2
+ export declare function getInjectedPackageMountConfigs(): {
3
+ mounts: Record<string, string>;
4
4
  rootDir: string;
5
5
  }[];
6
+ export declare function getInjectedPackageRoots(): readonly string[];
6
7
  export declare function getInjectedPackageNameForSpecifier(specifier: string): string | null;
7
8
  export declare function mayContainInjectedPackageSpecifier(sourceText: string): boolean;
8
9
  export declare function maskAuthoredInjectedPackageSpecifier(specifier: string): string | null;
@@ -1 +1 @@
1
- {"version":3,"file":"injected-packages.d.ts","sourceRoot":"","sources":["../../src/lib/injected-packages.ts"],"names":[],"mappings":"AAiBA,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAWnE;AAED,wBAAgB,8BAA8B,IAAI;IAChD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,OAAO,EAAE,MAAM,CAAA;CAChB,EAAE,CAYF;AAED,wBAAgB,kCAAkC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAcnF;AAED,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE9E;AAED,wBAAgB,oCAAoC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASrF;AAED,wBAAgB,uCAAuC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYxF;AAWD,wBAAgB,8BAA8B,IAAI,MAAM,CAEvD"}
1
+ {"version":3,"file":"injected-packages.d.ts","sourceRoot":"","sources":["../../src/lib/injected-packages.ts"],"names":[],"mappings":"AAiBA,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAWnE;AAED,wBAAgB,8BAA8B,IAAI;IAChD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,OAAO,EAAE,MAAM,CAAA;CAChB,EAAE,CAYF;AAED,wBAAgB,uBAAuB,IAAI,SAAS,MAAM,EAAE,CAI3D;AAED,wBAAgB,kCAAkC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAcnF;AAED,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE9E;AAED,wBAAgB,oCAAoC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASrF;AAED,wBAAgB,uCAAuC,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYxF;AAWD,wBAAgB,8BAA8B,IAAI,MAAM,CAEvD"}
@@ -16,18 +16,21 @@ export function isInjectedPackageFilePath(filePath) {
16
16
  }
17
17
  return false;
18
18
  }
19
- export function getInjectedPackageRouteConfigs() {
19
+ export function getInjectedPackageMountConfigs() {
20
20
  return injectedPackageNames.map((packageName) => {
21
21
  let { packageRoot } = getResolvedInjectedPackage(packageName);
22
- let { filePattern, routeRoot } = getInjectedPackageRoute(packageRoot, packageName);
22
+ let { fileRoot, routeRoot } = getInjectedPackageRoute(packageRoot, packageName);
23
23
  return {
24
- fileMap: {
25
- [getInjectedPackageRoutePattern(packageName)]: filePattern,
24
+ mounts: {
25
+ [getInjectedPackageMountPath(packageName)]: fileRoot,
26
26
  },
27
27
  rootDir: routeRoot,
28
28
  };
29
29
  });
30
30
  }
31
+ export function getInjectedPackageRoots() {
32
+ return injectedPackageNames.map((packageName) => getResolvedInjectedPackage(packageName).packageRoot);
33
+ }
31
34
  export function getInjectedPackageNameForSpecifier(specifier) {
32
35
  for (let injectedSpecifier of generatedInjectedPackageSpecifiers) {
33
36
  if (specifier === injectedSpecifier) {
@@ -88,13 +91,13 @@ function getResolvedInjectedPackage(packageName) {
88
91
  resolvedInjectedPackages.set(packageName, resolvedInjectedPackage);
89
92
  return resolvedInjectedPackage;
90
93
  }
91
- function getInjectedPackageRoutePattern(packageName) {
92
- return `${injectedPackagesBasePath}/${packageName}/*path`;
94
+ function getInjectedPackageMountPath(packageName) {
95
+ return `${injectedPackagesBasePath}/${packageName}`;
93
96
  }
94
97
  function getInjectedPackageRoute(packageRoot, packageName) {
95
98
  if (!packageRoot.endsWith(`/${packageName}`)) {
96
99
  return {
97
- filePattern: `${packageRoot.slice(getFilePathDirectory(packageRoot).length + 1)}/*path`,
100
+ fileRoot: packageRoot.slice(getFilePathDirectory(packageRoot).length + 1),
98
101
  routeRoot: getFilePathDirectory(packageRoot),
99
102
  };
100
103
  }
@@ -103,7 +106,7 @@ function getInjectedPackageRoute(packageRoot, packageName) {
103
106
  routeRoot = getFilePathDirectory(routeRoot);
104
107
  }
105
108
  return {
106
- filePattern: `${packageName}/*path`,
109
+ fileRoot: packageName,
107
110
  routeRoot,
108
111
  };
109
112
  }
@@ -0,0 +1,39 @@
1
+ import type { AccessPolicy, AssetAccessDetails } from './access.ts';
2
+ import type { CompiledRoutes } from './routes.ts';
3
+ /** How the asset server handles an inspected file. */
4
+ export type AssetKind = 'file' | 'script' | 'style' | 'unsupported';
5
+ /** Browser-reachability result for an inspected asset. */
6
+ export type AssetStatus = 'denied' | 'missing' | 'not-allowed' | 'reachable' | 'unmapped' | 'unsupported';
7
+ /** Diagnostic information about a configured asset URL or file path. */
8
+ export interface AssetDetails {
9
+ /** Access-control decision and the rules responsible for it. */
10
+ access?: AssetAccessDetails;
11
+ /** Absolute mapped file path. */
12
+ filePath?: string;
13
+ /** Configured filesystem mount root that matched the asset. */
14
+ fileRoot?: string;
15
+ /** Browser-reachability result. */
16
+ status: AssetStatus;
17
+ /** How the asset server handles the file. */
18
+ type?: AssetKind;
19
+ /** Stable public URL pathname for the asset. */
20
+ url?: string;
21
+ /** Public mount root that matched the asset. */
22
+ urlRoot?: string;
23
+ }
24
+ interface AssetInspectorOptions {
25
+ accessPolicy: AccessPolicy;
26
+ allowFiles: readonly string[];
27
+ fileExtensions: readonly string[];
28
+ rootDir: string;
29
+ routes: CompiledRoutes;
30
+ }
31
+ export interface AssetInspector {
32
+ /** Returns diagnostic information for a public URL or file path. */
33
+ getAssetDetails(input: string): Promise<AssetDetails>;
34
+ /** Returns every file currently reachable through the configured asset server. */
35
+ getAssets(): Promise<AssetDetails[]>;
36
+ }
37
+ export declare function createAssetInspector(options: AssetInspectorOptions): AssetInspector;
38
+ export {};
39
+ //# sourceMappingURL=inspection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspection.d.ts","sourceRoot":"","sources":["../../src/lib/inspection.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAInE,OAAO,KAAK,EAAmB,cAAc,EAAE,MAAM,aAAa,CAAA;AAOlE,sDAAsD;AACtD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,aAAa,CAAA;AAEnE,0DAA0D;AAC1D,MAAM,MAAM,WAAW,GACnB,QAAQ,GACR,SAAS,GACT,aAAa,GACb,WAAW,GACX,UAAU,GACV,aAAa,CAAA;AAEjB,wEAAwE;AACxE,MAAM,WAAW,YAAY;IAC3B,gEAAgE;IAChE,MAAM,CAAC,EAAE,kBAAkB,CAAA;IAC3B,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,mCAAmC;IACnC,MAAM,EAAE,WAAW,CAAA;IACnB,6CAA6C;IAC7C,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,UAAU,qBAAqB;IAC7B,YAAY,EAAE,YAAY,CAAA;IAC1B,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IAC7B,cAAc,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,cAAc,CAAA;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,oEAAoE;IACpE,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IACrD,kFAAkF;IAClF,SAAS,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAAA;CACrC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CA4BnF"}
@@ -0,0 +1,160 @@
1
+ import * as fs from 'node:fs';
2
+ import * as fsPromises from 'node:fs/promises';
3
+ import * as path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { parseFingerprintSuffix } from './fingerprint.js';
6
+ import { getInjectedPackageRoots } from './injected-packages.js';
7
+ import { isAbsoluteFilePath, normalizeFilePath, resolveFilePath } from './paths.js';
8
+ import { supportedScriptExtensions } from './scripts/resolve.js';
9
+ import { isStyleFilePath } from './styles/compiler.js';
10
+ const scriptExtensions = new Set(supportedScriptExtensions);
11
+ const globSyntaxPattern = /[*?[\]{}()!+@]/;
12
+ export function createAssetInspector(options) {
13
+ let fileExtensions = new Set(options.fileExtensions.map((extension) => extension.toLowerCase()));
14
+ return {
15
+ async getAssetDetails(input) {
16
+ let routeMatch = await resolveInput(input, options);
17
+ if (routeMatch === null)
18
+ return { status: 'unmapped' };
19
+ return inspectRouteMatch(routeMatch, options, fileExtensions);
20
+ },
21
+ async getAssets() {
22
+ let filePaths = await discoverFilePaths(options);
23
+ let assets = [];
24
+ for (let filePath of filePaths) {
25
+ let routeMatch = options.routes.matchFilePath(filePath);
26
+ if (routeMatch === null)
27
+ continue;
28
+ let details = await inspectRouteMatch(routeMatch, options, fileExtensions);
29
+ if (details.status === 'reachable')
30
+ assets.push(details);
31
+ }
32
+ assets.sort((left, right) => {
33
+ let urlOrder = (left.url ?? '').localeCompare(right.url ?? '');
34
+ return urlOrder === 0 ? (left.filePath ?? '').localeCompare(right.filePath ?? '') : urlOrder;
35
+ });
36
+ return assets;
37
+ },
38
+ };
39
+ }
40
+ async function resolveInput(input, options) {
41
+ if (input.startsWith('file://')) {
42
+ return options.routes.matchFilePath(fileURLToPath(input));
43
+ }
44
+ if (/^[A-Za-z][A-Za-z\d+.-]*:\/\//.test(input)) {
45
+ let pathname = parseFingerprintSuffix(new URL(input).pathname).pathname;
46
+ return options.routes.matchUrlPathname(pathname);
47
+ }
48
+ let filePath = resolveFilePath(options.rootDir, input);
49
+ if (!input.startsWith('/') || isAbsoluteFilePath(input)) {
50
+ if (await pathExists(filePath))
51
+ return options.routes.matchFilePath(filePath);
52
+ if (!input.startsWith('/'))
53
+ return options.routes.matchFilePath(filePath);
54
+ }
55
+ let pathname = parseFingerprintSuffix(new URL(input, 'http://remix.run').pathname).pathname;
56
+ return options.routes.matchUrlPathname(pathname);
57
+ }
58
+ async function inspectRouteMatch(routeMatch, options, fileExtensions) {
59
+ let exists = await pathExists(routeMatch.filePath);
60
+ let identityPath = exists ? fs.realpathSync(routeMatch.filePath) : routeMatch.filePath;
61
+ let normalizedIdentityPath = normalizeFilePath(identityPath);
62
+ let access = options.accessPolicy.inspect(normalizedIdentityPath);
63
+ let type = getAssetKind(routeMatch.filePath, fileExtensions);
64
+ let details = {
65
+ access,
66
+ filePath: routeMatch.filePath,
67
+ fileRoot: routeMatch.fileRoot,
68
+ type,
69
+ url: routeMatch.urlPathname,
70
+ urlRoot: routeMatch.urlRoot,
71
+ };
72
+ if (!exists)
73
+ return { ...details, status: 'missing' };
74
+ if (!access.allowed) {
75
+ return { ...details, status: access.deniedBy === undefined ? 'not-allowed' : 'denied' };
76
+ }
77
+ if (type === 'unsupported')
78
+ return { ...details, status: 'unsupported' };
79
+ return { ...details, status: 'reachable' };
80
+ }
81
+ function getAssetKind(filePath, fileExtensions) {
82
+ let extension = path.extname(filePath).toLowerCase();
83
+ if (scriptExtensions.has(extension))
84
+ return 'script';
85
+ if (isStyleFilePath(filePath))
86
+ return 'style';
87
+ if (fileExtensions.has(extension))
88
+ return 'file';
89
+ return 'unsupported';
90
+ }
91
+ async function discoverFilePaths(options) {
92
+ let roots = new Set();
93
+ for (let pattern of options.allowFiles) {
94
+ roots.add(resolveDiscoveryRoot(options.rootDir, pattern));
95
+ }
96
+ for (let packageRoot of options.accessPolicy.getAllowedPackageRoots()) {
97
+ roots.add(packageRoot);
98
+ }
99
+ for (let packageRoot of getInjectedPackageRoots()) {
100
+ roots.add(packageRoot);
101
+ }
102
+ let filePaths = new Set();
103
+ for (let root of roots) {
104
+ await collectFiles(root, filePaths);
105
+ }
106
+ return [...filePaths];
107
+ }
108
+ function resolveDiscoveryRoot(rootDir, pattern) {
109
+ let dynamicIndex = pattern.search(globSyntaxPattern);
110
+ if (dynamicIndex === -1)
111
+ return resolveFilePath(rootDir, pattern);
112
+ let rawStaticPrefix = pattern.slice(0, dynamicIndex);
113
+ let staticPrefix = rawStaticPrefix.replace(/[/\\]+$/, '');
114
+ if (staticPrefix.length === 0)
115
+ return rootDir;
116
+ return resolveFilePath(rootDir, /[/\\]$/.test(rawStaticPrefix) ? staticPrefix : path.dirname(staticPrefix));
117
+ }
118
+ async function collectFiles(root, filePaths) {
119
+ let stat;
120
+ try {
121
+ stat = await fsPromises.stat(root);
122
+ }
123
+ catch (error) {
124
+ if (isPathNotFoundError(error))
125
+ return;
126
+ throw error;
127
+ }
128
+ if (stat.isFile()) {
129
+ filePaths.add(normalizeFilePath(root));
130
+ return;
131
+ }
132
+ if (!stat.isDirectory())
133
+ return;
134
+ let entries = await fsPromises.readdir(root, { withFileTypes: true });
135
+ for (let entry of entries) {
136
+ let entryPath = path.join(root, entry.name);
137
+ if (entry.isDirectory()) {
138
+ await collectFiles(entryPath, filePaths);
139
+ }
140
+ else if (entry.isFile()) {
141
+ filePaths.add(normalizeFilePath(entryPath));
142
+ }
143
+ }
144
+ }
145
+ async function pathExists(filePath) {
146
+ try {
147
+ await fsPromises.access(filePath);
148
+ return true;
149
+ }
150
+ catch (error) {
151
+ if (isPathNotFoundError(error))
152
+ return false;
153
+ throw error;
154
+ }
155
+ }
156
+ function isPathNotFoundError(error) {
157
+ return (error instanceof Error &&
158
+ 'code' in error &&
159
+ (error.code === 'ENOENT' || error.code === 'ENOTDIR'));
160
+ }
@@ -1,11 +1,20 @@
1
- interface RouteConfig {
2
- fileMap: Readonly<Record<string, string>>;
1
+ interface MountConfig {
2
+ mounts: Readonly<Record<string, string>>;
3
3
  rootDir: string;
4
4
  }
5
5
  export interface CompiledRoutes {
6
6
  resolveUrlPathname(pathname: string): string | null;
7
+ matchUrlPathname(pathname: string): AssetRouteMatch | null;
7
8
  toUrlPathname(filePath: string): string | null;
9
+ matchFilePath(filePath: string): AssetRouteMatch | null;
8
10
  }
9
- export declare function compileRoutes(basePath: string, routeConfigs: readonly RouteConfig[]): CompiledRoutes;
11
+ export interface AssetRouteMatch {
12
+ filePath: string;
13
+ fileRoot: string;
14
+ urlPathname: string;
15
+ urlRoot: string;
16
+ }
17
+ export declare function compileRoutes(basePath: string, mountConfigs: readonly MountConfig[]): CompiledRoutes;
18
+ export declare function resolveMountFileRoot(rootDir: string, fileRoot: string): string;
10
19
  export {};
11
20
  //# sourceMappingURL=routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/lib/routes.ts"],"names":[],"mappings":"AAqBA,UAAU,WAAW;IACnB,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACzC,OAAO,EAAE,MAAM,CAAA;CAChB;AAUD,MAAM,WAAW,cAAc;IAC7B,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IACnD,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;CAC/C;AAYD,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,SAAS,WAAW,EAAE,GACnC,cAAc,CAgDhB"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/lib/routes.ts"],"names":[],"mappings":"AASA,UAAU,WAAW;IACnB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACxC,OAAO,EAAE,MAAM,CAAA;CAChB;AASD,MAAM,WAAW,cAAc;IAC7B,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IACnD,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAAA;IAC1D,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAC9C,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAAA;CACxD;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,SAAS,WAAW,EAAE,GACnC,cAAc,CA8DhB;AAuCD,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAU9E"}