@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
@@ -1,19 +1,25 @@
1
1
  import * as path from 'node:path';
2
2
  import * as fs from 'node:fs';
3
- import { createAccessPolicy } from "./access.js";
4
- import { createAssetServerCompilationError, isAssetServerCompilationError, } from "./compilation-error.js";
5
- import { createFileCompiler, createResponseForFile } from "./files/compiler.js";
6
- import { normalizeFilesOptions, serializeAssetTransformInvocations } from "./files/config.js";
7
- import { getFingerprintRequestCacheControl, parseFingerprintSuffix } from "./fingerprint.js";
8
- import { getInjectedPackageRouteConfigs } from "./injected-packages.js";
9
- import { normalizeFilePath, normalizePathname } from "./paths.js";
10
- import { compileRoutes } from "./routes.js";
11
- import { createResponseForScript, createScriptCompiler } from "./scripts/compiler.js";
12
- import { supportedScriptExtensions } from "./scripts/resolve.js";
13
- import { createResponseForStyle, createStyleCompiler, isStyleFilePath } from "./styles/compiler.js";
14
- import { resolveScriptTarget, resolveStyleTarget } from "./target.js";
15
- import { createAssetServerWatcher } from "./watch.js";
3
+ import { createAccessPolicy } from './access.js';
4
+ import { createAssetServerCompilationError, isAssetServerCompilationError, } from './compilation-error.js';
5
+ import { createFileCompiler, createResponseForFile } from './files/compiler.js';
6
+ import { normalizeFilesOptions, serializeAssetTransformInvocations } from './files/config.js';
7
+ import { getFingerprintRequestCacheControl, parseFingerprintSuffix } from './fingerprint.js';
8
+ import { createHmrClientSource } from './hmr.js';
9
+ import { getInjectedPackageMountConfigs } from './injected-packages.js';
10
+ import { normalizeFilePath, normalizePathname } from './paths.js';
11
+ import { compileRoutes } from './routes.js';
12
+ import { createResponseForScript, createScriptCompiler } from './scripts/compiler.js';
13
+ import { supportedScriptExtensions } from './scripts/resolve.js';
14
+ import { createResponseForStyle, createStyleCompiler, isStyleFilePath } from './styles/compiler.js';
15
+ import { resolveScriptTarget, resolveStyleTarget } from './target.js';
16
+ import { createAssetServerWatcher } from './watch.js';
17
+ import { createAssetInspector } from './inspection.js';
16
18
  const scriptExtensionSet = new Set(supportedScriptExtensions);
19
+ const defaultMounts = {
20
+ app: 'app',
21
+ npm: 'node_modules',
22
+ };
17
23
  const chokidarWatcherByAssetServer = new WeakMap();
18
24
  const watcherByAssetServer = new WeakMap();
19
25
  export function getInternalChokidarWatcher(assetServer) {
@@ -26,7 +32,7 @@ export function getInternalWatchTargets(assetServer) {
26
32
  * Create an asset server instance
27
33
  *
28
34
  * Compiles TypeScript/JavaScript scripts and CSS styles on demand with optional
29
- * source-based URL fingerprinting, caching, and configurable file mapping.
35
+ * source-based URL fingerprinting, caching, and configurable directory mounts.
30
36
  *
31
37
  * @param options Server configuration
32
38
  * @returns A {@link AssetServer} with `fetch()`, `getHref()`, and `getPreloads()` methods
@@ -35,10 +41,9 @@ export function getInternalWatchTargets(assetServer) {
35
41
  * ```ts
36
42
  * let assetServer = createAssetServer({
37
43
  * basePath: '/assets',
38
- * fileMap: {
39
- * '/app/*path': 'app/*path',
40
- * },
41
- * allow: ['app/**'],
44
+ * allowFiles: ['app/routes.ts', 'app/**\/public/**'],
45
+ * allowPackages: ['remix'],
46
+ * denyFiles: ['app/**\/*.test.*'],
42
47
  * })
43
48
  *
44
49
  * route('/assets/*path', ({ request }) => assetServer.fetch(request))
@@ -47,18 +52,52 @@ export function getInternalWatchTargets(assetServer) {
47
52
  export function createAssetServer(options) {
48
53
  let resolvedOptions = resolveAssetServerOptions(options);
49
54
  let accessPolicy = createAccessPolicy({
50
- allow: resolvedOptions.allow,
51
- deny: resolvedOptions.deny,
55
+ allowFiles: resolvedOptions.allowFiles,
56
+ allowPackages: resolvedOptions.allowPackages,
57
+ denyFiles: resolvedOptions.denyFiles,
58
+ packageSearchRoots: hasPackages(resolvedOptions.allowPackages)
59
+ ? getPackageSearchRoots(resolvedOptions.mounts, resolvedOptions.rootDir)
60
+ : undefined,
52
61
  rootDir: resolvedOptions.rootDir,
53
62
  });
63
+ let assetInspector = createAssetInspector({
64
+ accessPolicy,
65
+ allowFiles: resolvedOptions.allowFiles,
66
+ fileExtensions: resolvedOptions.files.extensions,
67
+ rootDir: resolvedOptions.rootDir,
68
+ routes: resolvedOptions.routes,
69
+ });
54
70
  let watcher = null;
55
71
  let chokidarWatcher = null;
56
72
  let fileCompiler = null;
73
+ let closed = false;
74
+ let unsubscribeBrowserHmrFileEvents;
75
+ let browserHmrChannelPromise = createBrowserHmrChannel(resolvedOptions.hmr, handleBrowserHmrFileEvents, () => closed, (unsubscribe) => {
76
+ unsubscribeBrowserHmrFileEvents = unsubscribe;
77
+ });
78
+ void browserHmrChannelPromise.catch(() => {
79
+ // The HMR client request and close() both await this promise. Attach a
80
+ // rejection handler now so async factory failures do not surface first as
81
+ // unhandled rejections.
82
+ });
83
+ let sendHmrPayload = resolvedOptions.hmr ? createHmrPayloadSender() : null;
84
+ let hmrPathnames = getHmrPathnames(resolvedOptions.basePath);
57
85
  let scriptCompiler = createScriptCompiler({
58
86
  buildId: resolvedOptions.buildId,
59
87
  define: resolvedOptions.define,
60
88
  external: resolvedOptions.external,
61
89
  fingerprintAssets: resolvedOptions.fingerprintAssets,
90
+ loaders: resolvedOptions.loaders,
91
+ hmr: sendHmrPayload
92
+ ? {
93
+ clientPathname: hmrPathnames.client,
94
+ send(updates) {
95
+ let payload = createScriptHmrPayload(updates);
96
+ if (payload)
97
+ sendHmrPayload(payload);
98
+ },
99
+ }
100
+ : undefined,
62
101
  isAllowed: accessPolicy.isAllowed,
63
102
  minify: resolvedOptions.minify,
64
103
  onWatchDirectoriesChange: (delta) => {
@@ -66,6 +105,9 @@ export function createAssetServer(options) {
66
105
  return;
67
106
  watcher.updateWatchedDirectories(delta);
68
107
  },
108
+ onWatchFilesChange: (delta) => {
109
+ updateBrowserHmrWatchedFiles(delta);
110
+ },
69
111
  rootDir: resolvedOptions.rootDir,
70
112
  routes: resolvedOptions.routes,
71
113
  sourceMapSourcePaths: resolvedOptions.sourceMapSourcePaths,
@@ -88,6 +130,22 @@ export function createAssetServer(options) {
88
130
  }
89
131
  return fileCompiler.getHref(identityPath, { transform: options.transform });
90
132
  },
133
+ hmr: sendHmrPayload
134
+ ? {
135
+ send(pathname, timestamp) {
136
+ sendHmrPayload({
137
+ timestamp,
138
+ type: 'browser:update',
139
+ updates: [
140
+ {
141
+ path: pathname,
142
+ type: 'css',
143
+ },
144
+ ],
145
+ });
146
+ },
147
+ }
148
+ : undefined,
91
149
  isAllowed: accessPolicy.isAllowed,
92
150
  isServedFilePath(filePath) {
93
151
  return fileCompiler?.isServedFilePath(filePath) ?? false;
@@ -98,6 +156,9 @@ export function createAssetServer(options) {
98
156
  return;
99
157
  watcher.updateWatchedDirectories(delta, { includeAncestors: false });
100
158
  },
159
+ onWatchFilesChange: (delta) => {
160
+ updateBrowserHmrWatchedFiles(delta);
161
+ },
101
162
  rootDir: resolvedOptions.rootDir,
102
163
  routes: resolvedOptions.routes,
103
164
  sourceMapSourcePaths: resolvedOptions.sourceMapSourcePaths,
@@ -128,6 +189,7 @@ export function createAssetServer(options) {
128
189
  onFileEvent: handleWatchEvent,
129
190
  rootDir: resolvedOptions.rootDir,
130
191
  });
192
+ watcher.updateWatchedDirectories({ add: accessPolicy.getPackageWatchDirectories(), remove: [] }, { includeAncestors: false });
131
193
  }
132
194
  async function responseForError(error) {
133
195
  try {
@@ -141,21 +203,71 @@ export function createAssetServer(options) {
141
203
  async function handleWatchEvent(filePath, event) {
142
204
  try {
143
205
  let normalizedFilePath = normalizeFilePath(filePath);
144
- await scriptCompiler.handleFileEvent(normalizedFilePath, event);
145
- await styleCompiler.handleFileEvent(normalizedFilePath, event);
206
+ accessPolicy.handleFileEvent(normalizedFilePath);
207
+ scriptCompiler.invalidateFileEvent(normalizedFilePath, event);
208
+ styleCompiler.invalidateFileEvent(normalizedFilePath, event);
146
209
  await fileCompiler?.handleFileEvent(normalizedFilePath, event);
147
210
  }
148
211
  catch (error) {
149
212
  console.error(`There was an error invalidating the asset server cache: ${error}`);
150
213
  }
151
214
  }
215
+ async function handleBrowserHmrFileEvents(events) {
216
+ let browserHmrEvents = [];
217
+ for (let event of events) {
218
+ let normalizedFilePath = normalizeFilePath(event.filePath);
219
+ let scriptUpdates = await scriptCompiler.classifyHmrFileEvent(normalizedFilePath, event.event);
220
+ let scriptPayload = createScriptHmrPayload(scriptUpdates);
221
+ if (scriptPayload) {
222
+ browserHmrEvents.push(createBrowserHmrEvent(scriptPayload, getScriptHmrUpdateFiles(scriptUpdates)));
223
+ }
224
+ let styleUpdates = await styleCompiler.classifyHmrFileEvent(normalizedFilePath, event.event);
225
+ for (let styleUpdate of styleUpdates) {
226
+ browserHmrEvents.push(createBrowserHmrEvent({
227
+ timestamp: styleUpdate.timestamp,
228
+ type: 'browser:update',
229
+ updates: [
230
+ {
231
+ path: styleUpdate.path,
232
+ type: 'css',
233
+ },
234
+ ],
235
+ }, [styleUpdate.filePath]));
236
+ }
237
+ await fileCompiler?.handleFileEvent(normalizedFilePath, event.event);
238
+ }
239
+ return browserHmrEvents;
240
+ }
241
+ function updateBrowserHmrWatchedFiles(delta) {
242
+ browserHmrChannelPromise
243
+ .then((channel) => {
244
+ channel?.updateWatchedFiles(delta);
245
+ })
246
+ .catch((error) => {
247
+ console.error(`There was an error updating browser HMR watched files: ${error}`);
248
+ });
249
+ }
152
250
  let assetServer = {
251
+ getAssetDetails(input) {
252
+ return assetInspector.getAssetDetails(input);
253
+ },
254
+ getAssets() {
255
+ return assetInspector.getAssets();
256
+ },
153
257
  async fetch(request) {
154
258
  if (request.method !== 'GET' && request.method !== 'HEAD')
155
259
  return null;
260
+ let requestPathname = new URL(request.url).pathname;
261
+ if (resolvedOptions.hmr) {
262
+ if (requestPathname === hmrPathnames.client) {
263
+ let browserHmrChannel = await browserHmrChannelPromise;
264
+ assertBrowserEventUrl(browserHmrChannel?.url);
265
+ return createHmrClientResponse(browserHmrChannel.url, request.method);
266
+ }
267
+ }
156
268
  let requestUrl = new URL(request.url);
157
269
  let requestedTransform = normalizeRequestedTransformQuery(requestUrl.searchParams);
158
- let parsedRequestPathname = parseAssetRequestPathname(requestUrl.pathname, {
270
+ let parsedRequestPathname = parseAssetRequestPathname(requestPathname, {
159
271
  fingerprintAssets: resolvedOptions.fingerprintAssets,
160
272
  routes: resolvedOptions.routes,
161
273
  });
@@ -246,7 +358,7 @@ export function createAssetServer(options) {
246
358
  });
247
359
  }
248
360
  catch (error) {
249
- // A direct request can race with the filesystem or fail a deeper allow check while
361
+ // A direct request can race with the filesystem or fail a deeper allowFiles check while
250
362
  // compiling imports. In this fetch context, both cases should fall through as "not
251
363
  // handled here" so the outer router can continue to its own 404 behavior.
252
364
  if (isAssetServerCompilationError(error) &&
@@ -341,7 +453,19 @@ export function createAssetServer(options) {
341
453
  return mergePreloadLayers(await Promise.all(preloadLayerGroupPromises));
342
454
  },
343
455
  async close() {
344
- await watcher?.close();
456
+ if (closed)
457
+ return;
458
+ closed = true;
459
+ let unsubscribe = unsubscribeBrowserHmrFileEvents;
460
+ unsubscribeBrowserHmrFileEvents = undefined;
461
+ unsubscribe?.();
462
+ try {
463
+ let channel = await browserHmrChannelPromise;
464
+ channel?.close();
465
+ }
466
+ finally {
467
+ await watcher?.close();
468
+ }
345
469
  },
346
470
  };
347
471
  if (chokidarWatcher) {
@@ -377,6 +501,67 @@ function badRequest(message) {
377
501
  headers: { 'Content-Type': 'text/plain; charset=utf-8' },
378
502
  });
379
503
  }
504
+ function getHmrPathnames(basePath) {
505
+ let hmrBase = `${basePath}/__remix_hmr`;
506
+ return {
507
+ client: `${hmrBase}/client.js`,
508
+ events: `${hmrBase}/events`,
509
+ };
510
+ }
511
+ function createHmrClientResponse(eventPathname, method) {
512
+ return new Response(method === 'HEAD' ? null : createHmrClientSource({ eventPathname }), {
513
+ headers: {
514
+ 'Cache-Control': 'no-cache',
515
+ 'Content-Type': 'application/javascript; charset=utf-8',
516
+ },
517
+ });
518
+ }
519
+ function assertBrowserEventUrl(url) {
520
+ if (url === undefined) {
521
+ throw new TypeError('hmr must be provided');
522
+ }
523
+ }
524
+ function createHmrPayloadSender() {
525
+ return () => { };
526
+ }
527
+ function createBrowserHmrEvent(payload, files) {
528
+ if (payload.type === 'browser:reload') {
529
+ return {
530
+ ...(files.length === 0 ? {} : { files: [...files] }),
531
+ type: 'reload',
532
+ };
533
+ }
534
+ return {
535
+ ...(files.length === 0 ? {} : { files: [...files] }),
536
+ timestamp: payload.timestamp,
537
+ type: 'update',
538
+ updates: payload.updates,
539
+ };
540
+ }
541
+ function getScriptHmrUpdateFiles(updates) {
542
+ return [...new Set(updates.map((update) => update.filePath))];
543
+ }
544
+ export function createScriptHmrPayload(updates) {
545
+ let timestamp = updates[0]?.timestamp ?? Date.now();
546
+ let rejectedUpdate = updates.find((update) => !update.accepted);
547
+ if (rejectedUpdate) {
548
+ return {
549
+ type: 'browser:reload',
550
+ };
551
+ }
552
+ let acceptedUpdates = updates.filter((update) => update.accepted);
553
+ if (acceptedUpdates.length === 0)
554
+ return null;
555
+ return {
556
+ timestamp,
557
+ type: 'browser:update',
558
+ updates: acceptedUpdates.map((update) => ({
559
+ ...(update.acceptedPath === update.path ? {} : { acceptedPath: update.acceptedPath }),
560
+ path: update.path,
561
+ type: 'js',
562
+ })),
563
+ };
564
+ }
380
565
  function mergePreloadLayers(preloadLayersByRoot) {
381
566
  let urls = [];
382
567
  let seen = new Set();
@@ -422,32 +607,92 @@ function resolveAssetServerOptions(options) {
422
607
  fingerprint: options.fingerprint,
423
608
  watch: options.watch,
424
609
  });
610
+ let watchOptions = normalizeWatchOptions(options.watch);
611
+ let hmrFactory = normalizeHmrFactory(options.hmr);
612
+ let mounts = options.mounts ?? defaultMounts;
613
+ if (hmrFactory && watchOptions === null) {
614
+ throw new TypeError('hmr requires watch mode');
615
+ }
616
+ if (Object.keys(mounts).length === 0) {
617
+ throw new TypeError('mounts must include at least one entry');
618
+ }
425
619
  return {
426
- allow: options.allow,
620
+ allowFiles: options.allowFiles,
621
+ allowPackages: options.allowPackages,
427
622
  basePath,
428
623
  buildId: fingerprintOptions.buildId,
429
624
  define: scriptOptions.define,
430
- deny: options.deny,
625
+ denyFiles: options.denyFiles,
431
626
  external: scriptOptions.external ?? [],
432
627
  files: normalizeFilesOptions(options.files),
433
628
  fingerprintAssets: fingerprintOptions.enabled,
629
+ hmr: hmrFactory,
434
630
  minify: options.minify ?? false,
631
+ mounts,
632
+ loaders: scriptOptions.loaders ?? [],
435
633
  onError: options.onError ?? defaultErrorHandler,
436
634
  rootDir,
437
635
  routes: compileRoutes(basePath, [
438
636
  {
439
- fileMap: options.fileMap,
637
+ mounts,
440
638
  rootDir,
441
639
  },
442
- ...getInjectedPackageRouteConfigs(),
640
+ ...getInjectedPackageMountConfigs(),
443
641
  ]),
444
642
  sourceMapSourcePaths: options.sourceMapSourcePaths ?? 'url',
445
643
  sourceMaps: options.sourceMaps,
446
644
  scriptsTarget: resolveScriptTarget(options.target),
447
645
  stylesTarget: resolveStyleTarget(options.target),
448
- watchOptions: normalizeWatchOptions(options.watch),
646
+ watchOptions,
449
647
  };
450
648
  }
649
+ function normalizeHmrFactory(factory) {
650
+ if (factory === undefined)
651
+ return null;
652
+ if (typeof factory !== 'function') {
653
+ throw new TypeError('hmr must be a function');
654
+ }
655
+ return factory;
656
+ }
657
+ function createBrowserHmrChannel(createChannel, handleFileEvents, isClosed, setUnsubscribe) {
658
+ if (!createChannel)
659
+ return Promise.resolve(null);
660
+ let channelOrPromise;
661
+ try {
662
+ channelOrPromise = createChannel();
663
+ }
664
+ catch (error) {
665
+ return Promise.reject(error);
666
+ }
667
+ return Promise.resolve(channelOrPromise).then((channel) => {
668
+ if (channel === undefined)
669
+ return null;
670
+ validateBrowserHmrChannel(channel);
671
+ if (isClosed()) {
672
+ channel.close();
673
+ return null;
674
+ }
675
+ setUnsubscribe(channel.onFileEvents(handleFileEvents));
676
+ return channel;
677
+ });
678
+ }
679
+ function validateBrowserHmrChannel(channel) {
680
+ if (channel === null || typeof channel !== 'object') {
681
+ throw new TypeError('hmr must create an object');
682
+ }
683
+ if (!('url' in channel) || typeof channel.url !== 'string') {
684
+ throw new TypeError('hmr must create a channel with a string url');
685
+ }
686
+ if (!('close' in channel) || typeof channel.close !== 'function') {
687
+ throw new TypeError('hmr must create a channel with a close function');
688
+ }
689
+ if (!('onFileEvents' in channel) || typeof channel.onFileEvents !== 'function') {
690
+ throw new TypeError('hmr must create a channel with an onFileEvents function');
691
+ }
692
+ if (!('updateWatchedFiles' in channel) || typeof channel.updateWatchedFiles !== 'function') {
693
+ throw new TypeError('hmr must create a channel with an updateWatchedFiles function');
694
+ }
695
+ }
451
696
  function normalizeBasePath(basePath) {
452
697
  if (typeof basePath !== 'string') {
453
698
  throw new TypeError('basePath must be a string');
@@ -481,6 +726,12 @@ function normalizeWatchOptions(options) {
481
726
  return {};
482
727
  return options;
483
728
  }
729
+ function hasPackages(packages) {
730
+ return packages !== undefined && packages.length > 0;
731
+ }
732
+ function getPackageSearchRoots(mounts, rootDir) {
733
+ return Object.values(mounts).map((fileRoot) => path.resolve(rootDir, fileRoot));
734
+ }
484
735
  function parseAssetRequestPathname(pathname, options) {
485
736
  let isSourceMapRequest = pathname.endsWith('.map');
486
737
  let pathWithoutMap = isSourceMapRequest ? pathname.slice(0, -4) : pathname;
@@ -1,4 +1,4 @@
1
- type AssetServerCompilationErrorCode = 'FILE_NOT_FOUND' | 'FILE_NOT_ALLOWED' | 'FILE_NOT_SUPPORTED' | 'FILE_OUTSIDE_FILE_MAP' | 'FILE_TRANSFORM_QUERY_INVALID' | 'FILE_TRANSFORM_NOT_SUPPORTED' | 'FILE_TRANSFORM_RESULT_INVALID' | 'FILE_TRANSFORM_FAILED' | 'COMMONJS_NOT_SUPPORTED' | 'TRANSFORM_FAILED' | 'EMIT_FAILED' | 'IMPORT_RESOLUTION_FAILED' | 'IMPORT_NOT_SUPPORTED' | 'IMPORT_NOT_ALLOWED' | 'IMPORT_OUTSIDE_FILE_MAP' | 'URL_RESOLUTION_FAILED' | 'URL_NOT_SUPPORTED' | 'URL_NOT_ALLOWED' | 'URL_OUTSIDE_FILE_MAP';
1
+ type AssetServerCompilationErrorCode = 'FILE_NOT_FOUND' | 'FILE_NOT_ALLOWED' | 'FILE_NOT_SUPPORTED' | 'FILE_OUTSIDE_MOUNTS' | 'FILE_TRANSFORM_QUERY_INVALID' | 'FILE_TRANSFORM_NOT_SUPPORTED' | 'FILE_TRANSFORM_RESULT_INVALID' | 'FILE_TRANSFORM_FAILED' | 'COMMONJS_NOT_SUPPORTED' | 'TRANSFORM_FAILED' | 'LOADER_FAILED' | 'EMIT_FAILED' | 'IMPORT_RESOLUTION_FAILED' | 'IMPORT_NOT_SUPPORTED' | 'IMPORT_NOT_ALLOWED' | 'IMPORT_OUTSIDE_MOUNTS' | 'URL_RESOLUTION_FAILED' | 'URL_NOT_SUPPORTED' | 'URL_NOT_ALLOWED' | 'URL_OUTSIDE_MOUNTS';
2
2
  /**
3
3
  * Internal error used by the request-time asset compilation pipeline.
4
4
  */
@@ -1 +1 @@
1
- {"version":3,"file":"compilation-error.d.ts","sourceRoot":"","sources":["../../src/lib/compilation-error.ts"],"names":[],"mappings":"AAAA,KAAK,+BAA+B,GAChC,gBAAgB,GAChB,kBAAkB,GAClB,oBAAoB,GACpB,uBAAuB,GACvB,8BAA8B,GAC9B,8BAA8B,GAC9B,+BAA+B,GAC/B,uBAAuB,GACvB,wBAAwB,GACxB,kBAAkB,GAClB,aAAa,GACb,0BAA0B,GAC1B,sBAAsB,GACtB,oBAAoB,GACpB,yBAAyB,GACzB,uBAAuB,GACvB,mBAAmB,GACnB,iBAAiB,GACjB,sBAAsB,CAAA;AAE1B;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,IAAI,EAAE,+BAA+B,CAAA;IAErC,YACE,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QACP,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,IAAI,EAAE,+BAA+B,CAAA;KACtC,EAKF;CACF;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,2BAA2B,CAEtC;AAED;;;;;;;;GAQG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;IACP,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,EAAE,+BAA+B,CAAA;CACtC,GACA,2BAA2B,CAE7B"}
1
+ {"version":3,"file":"compilation-error.d.ts","sourceRoot":"","sources":["../../src/lib/compilation-error.ts"],"names":[],"mappings":"AAAA,KAAK,+BAA+B,GAChC,gBAAgB,GAChB,kBAAkB,GAClB,oBAAoB,GACpB,qBAAqB,GACrB,8BAA8B,GAC9B,8BAA8B,GAC9B,+BAA+B,GAC/B,uBAAuB,GACvB,wBAAwB,GACxB,kBAAkB,GAClB,eAAe,GACf,aAAa,GACb,0BAA0B,GAC1B,sBAAsB,GACtB,oBAAoB,GACpB,uBAAuB,GACvB,uBAAuB,GACvB,mBAAmB,GACnB,iBAAiB,GACjB,oBAAoB,CAAA;AAExB;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,KAAK;IACpD,IAAI,EAAE,+BAA+B,CAAA;IAErC,YACE,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QACP,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,IAAI,EAAE,+BAA+B,CAAA;KACtC,EAKF;CACF;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,2BAA2B,CAEtC;AAED;;;;;;;;GAQG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;IACP,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,IAAI,EAAE,+BAA+B,CAAA;CACtC,GACA,2BAA2B,CAE7B"}
@@ -1,6 +1,6 @@
1
1
  import * as fs from 'node:fs';
2
2
  import picomatch from 'picomatch';
3
- import { normalizeFilePath, resolveFilePath } from "./paths.js";
3
+ import { normalizeFilePath, resolveFilePath } from './paths.js';
4
4
  export function createFileMatcher(pattern, rootDir, options = {}) {
5
5
  let resolvedPatternPath = resolveFilePath(rootDir, pattern);
6
6
  let allowDirectories = options.allowDirectories ?? true;
@@ -1 +1 @@
1
- {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../../src/lib/files/compiler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../../src/lib/files/compiler.ts"],"names":[],"mappings":"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"}
@@ -4,11 +4,11 @@ import * as path from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { IfNoneMatch } from '@remix-run/headers/if-none-match';
6
6
  import { detectContentType } from '@remix-run/mime';
7
- import { createAssetServerCompilationError, isAssetServerCompilationError, } from "../compilation-error.js";
8
- import { formatFingerprintedPathname, generateFingerprint, hashContent } from "../fingerprint.js";
9
- import { normalizeFilePath, resolveFilePath } from "../paths.js";
10
- import { parseAssetTransformInvocations } from "./config.js";
11
- import { createSourceFileStore } from "./store.js";
7
+ import { createAssetServerCompilationError, isAssetServerCompilationError, } from '../compilation-error.js';
8
+ import { formatFingerprintedPathname, generateFingerprint, hashContent } from '../fingerprint.js';
9
+ import { normalizeFilePath, resolveFilePath } from '../paths.js';
10
+ import { parseAssetTransformInvocations } from './config.js';
11
+ import { createSourceFileStore } from './store.js';
12
12
  export function createFileCompiler(options) {
13
13
  let resolvedOptions = {
14
14
  ...options,
@@ -327,14 +327,15 @@ export function resolveServedFileOrThrow(filePath, args) {
327
327
  });
328
328
  }
329
329
  if (!args.isAllowed(identityPath)) {
330
- throw createAssetServerCompilationError(`File is not allowed: ${identityPath}`, {
330
+ throw createAssetServerCompilationError(`File "${identityPath}" is not allowed by the asset server access configuration. ` +
331
+ `Add a matching allowFiles or allowPackages rule, or remove a conflicting denyFiles rule.`, {
331
332
  code: 'FILE_NOT_ALLOWED',
332
333
  });
333
334
  }
334
335
  let stableUrlPathname = args.routes.toUrlPathname(identityPath);
335
336
  if (!stableUrlPathname) {
336
- throw createAssetServerCompilationError(`File ${identityPath} is outside all configured fileMap entries.`, {
337
- code: 'FILE_OUTSIDE_FILE_MAP',
337
+ throw createAssetServerCompilationError(`File ${identityPath} is outside all configured mounts.`, {
338
+ code: 'FILE_OUTSIDE_MOUNTS',
338
339
  });
339
340
  }
340
341
  return {
@@ -1,4 +1,4 @@
1
- import { supportedScriptExtensions } from "../scripts/resolve.js";
1
+ import { supportedScriptExtensions } from '../scripts/resolve.js';
2
2
  export function defineFileTransform(transform) {
3
3
  return transform;
4
4
  }
@@ -0,0 +1,37 @@
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
+ /** Indicates that server-rendered output may have changed. */
10
+ type: 'server:update';
11
+ } | {
12
+ /** JavaScript and CSS modules that the browser should update. */
13
+ updates: Array<{
14
+ /** Importing module whose dependency-accept handler accepts this update. */
15
+ acceptedPath?: string;
16
+ /** Public URL of the changed JavaScript module. */
17
+ path: string;
18
+ /** Identifies a JavaScript module update. */
19
+ type: 'js';
20
+ } | {
21
+ /** Public URL of the changed stylesheet. */
22
+ path: string;
23
+ /** Identifies a stylesheet update. */
24
+ type: 'css';
25
+ }>;
26
+ /** Update time used to bypass browser module and stylesheet caches. */
27
+ timestamp: number;
28
+ /** Indicates that the listed browser modules should update in place. */
29
+ type: 'browser:update';
30
+ } | {
31
+ /** Indicates that no HMR boundary accepted the change and the page must reload. */
32
+ type: 'browser:reload';
33
+ };
34
+ export declare function createHmrClientSource(options: {
35
+ eventPathname: string;
36
+ }): string;
37
+ //# sourceMappingURL=hmr.d.ts.map
@@ -0,0 +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"}