@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
@@ -6,7 +6,7 @@ import { createFileCompiler, createResponseForFile } from './files/compiler.js';
6
6
  import { normalizeFilesOptions, serializeAssetTransformInvocations } from './files/config.js';
7
7
  import { getFingerprintRequestCacheControl, parseFingerprintSuffix } from './fingerprint.js';
8
8
  import { createHmrClientSource } from './hmr.js';
9
- import { getInjectedPackageRouteConfigs } from './injected-packages.js';
9
+ import { getInjectedPackageMountConfigs } from './injected-packages.js';
10
10
  import { normalizeFilePath, normalizePathname } from './paths.js';
11
11
  import { compileRoutes } from './routes.js';
12
12
  import { createResponseForScript, createScriptCompiler } from './scripts/compiler.js';
@@ -14,7 +14,13 @@ import { supportedScriptExtensions } from './scripts/resolve.js';
14
14
  import { createResponseForStyle, createStyleCompiler, isStyleFilePath } from './styles/compiler.js';
15
15
  import { resolveScriptTarget, resolveStyleTarget } from './target.js';
16
16
  import { createAssetServerWatcher } from './watch.js';
17
+ import { createAssetInspector } from './inspection.js';
18
+ import { getVirtualStoreMountConfigs } from './virtual-store.js';
17
19
  const scriptExtensionSet = new Set(supportedScriptExtensions);
20
+ const defaultMounts = {
21
+ app: 'app',
22
+ npm: 'node_modules',
23
+ };
18
24
  const chokidarWatcherByAssetServer = new WeakMap();
19
25
  const watcherByAssetServer = new WeakMap();
20
26
  export function getInternalChokidarWatcher(assetServer) {
@@ -27,7 +33,7 @@ export function getInternalWatchTargets(assetServer) {
27
33
  * Create an asset server instance
28
34
  *
29
35
  * Compiles TypeScript/JavaScript scripts and CSS styles on demand with optional
30
- * source-based URL fingerprinting, caching, and configurable file mapping.
36
+ * content-based URL fingerprinting, caching, and configurable directory mounts.
31
37
  *
32
38
  * @param options Server configuration
33
39
  * @returns A {@link AssetServer} with `fetch()`, `getHref()`, and `getPreloads()` methods
@@ -36,9 +42,6 @@ export function getInternalWatchTargets(assetServer) {
36
42
  * ```ts
37
43
  * let assetServer = createAssetServer({
38
44
  * basePath: '/assets',
39
- * fileMap: {
40
- * '/app/*path': 'app/*path',
41
- * },
42
45
  * allowFiles: ['app/routes.ts', 'app/**\/public/**'],
43
46
  * allowPackages: ['remix'],
44
47
  * denyFiles: ['app/**\/*.test.*'],
@@ -54,10 +57,17 @@ export function createAssetServer(options) {
54
57
  allowPackages: resolvedOptions.allowPackages,
55
58
  denyFiles: resolvedOptions.denyFiles,
56
59
  packageSearchRoots: hasPackages(resolvedOptions.allowPackages)
57
- ? getPackageSearchRoots(options.fileMap, resolvedOptions.rootDir)
60
+ ? getPackageSearchRoots(resolvedOptions.mounts, resolvedOptions.rootDir)
58
61
  : undefined,
59
62
  rootDir: resolvedOptions.rootDir,
60
63
  });
64
+ let assetInspector = createAssetInspector({
65
+ accessPolicy,
66
+ allowFiles: resolvedOptions.allowFiles,
67
+ fileExtensions: resolvedOptions.files.extensions,
68
+ rootDir: resolvedOptions.rootDir,
69
+ routes: resolvedOptions.routes,
70
+ });
61
71
  let watcher = null;
62
72
  let chokidarWatcher = null;
63
73
  let fileCompiler = null;
@@ -73,8 +83,8 @@ export function createAssetServer(options) {
73
83
  });
74
84
  let sendHmrPayload = resolvedOptions.hmr ? createHmrPayloadSender() : null;
75
85
  let hmrPathnames = getHmrPathnames(resolvedOptions.basePath);
86
+ let hmrDataKey = `remix/assets@1:${resolvedOptions.basePath}`;
76
87
  let scriptCompiler = createScriptCompiler({
77
- buildId: resolvedOptions.buildId,
78
88
  define: resolvedOptions.define,
79
89
  external: resolvedOptions.external,
80
90
  fingerprintAssets: resolvedOptions.fingerprintAssets,
@@ -108,7 +118,6 @@ export function createAssetServer(options) {
108
118
  watchMode: resolvedOptions.watchOptions !== null,
109
119
  });
110
120
  let styleCompiler = createStyleCompiler({
111
- buildId: resolvedOptions.buildId,
112
121
  fingerprintAssets: resolvedOptions.fingerprintAssets,
113
122
  getServedFileUrl: (identityPath, options) => {
114
123
  if (!fileCompiler) {
@@ -159,8 +168,8 @@ export function createAssetServer(options) {
159
168
  });
160
169
  if (resolvedOptions.files.extensions.length > 0) {
161
170
  fileCompiler = createFileCompiler({
162
- buildId: resolvedOptions.buildId,
163
171
  cache: resolvedOptions.files.cache,
172
+ cacheKey: resolvedOptions.files.cacheKey,
164
173
  extensions: resolvedOptions.files.extensions,
165
174
  fingerprintAssets: resolvedOptions.fingerprintAssets,
166
175
  globalTransforms: resolvedOptions.files.globalTransforms,
@@ -208,9 +217,9 @@ export function createAssetServer(options) {
208
217
  for (let event of events) {
209
218
  let normalizedFilePath = normalizeFilePath(event.filePath);
210
219
  let scriptUpdates = await scriptCompiler.classifyHmrFileEvent(normalizedFilePath, event.event);
211
- let scriptPayload = createScriptHmrPayload(scriptUpdates);
220
+ let scriptPayload = await createScriptHmrPayloadWithImportMaps(scriptUpdates);
212
221
  if (scriptPayload) {
213
- browserHmrEvents.push(createBrowserHmrEvent(scriptPayload, getScriptHmrUpdateFiles(scriptUpdates)));
222
+ browserHmrEvents.push(createBrowserHmrEvent(scriptPayload, getScriptHmrUpdateFiles(scriptUpdates), hmrDataKey));
214
223
  }
215
224
  let styleUpdates = await styleCompiler.classifyHmrFileEvent(normalizedFilePath, event.event);
216
225
  for (let styleUpdate of styleUpdates) {
@@ -223,11 +232,34 @@ export function createAssetServer(options) {
223
232
  type: 'css',
224
233
  },
225
234
  ],
226
- }, [styleUpdate.filePath]));
235
+ }, [styleUpdate.filePath], hmrDataKey));
227
236
  }
228
237
  await fileCompiler?.handleFileEvent(normalizedFilePath, event.event);
229
238
  }
230
239
  return browserHmrEvents;
240
+ async function createScriptHmrPayloadWithImportMaps(updates) {
241
+ let payload = createScriptHmrPayload(updates);
242
+ if (!payload || payload.type === 'browser:reload')
243
+ return payload;
244
+ let acceptedUpdates = updates.filter((update) => update.accepted);
245
+ let importMaps = await Promise.all(acceptedUpdates.map(async (update) => {
246
+ try {
247
+ return await scriptCompiler.getImportMap(update.acceptedFilePath);
248
+ }
249
+ catch (error) {
250
+ if (!isAssetServerCompilationError(error))
251
+ throw error;
252
+ return undefined;
253
+ }
254
+ }));
255
+ return {
256
+ ...payload,
257
+ updates: payload.updates.map((update, index) => ({
258
+ ...update,
259
+ ...(importMaps[index] ? { importMap: importMaps[index] } : null),
260
+ })),
261
+ };
262
+ }
231
263
  }
232
264
  function updateBrowserHmrWatchedFiles(delta) {
233
265
  browserHmrChannelPromise
@@ -239,6 +271,12 @@ export function createAssetServer(options) {
239
271
  });
240
272
  }
241
273
  let assetServer = {
274
+ getAssetDetails(input) {
275
+ return assetInspector.getAssetDetails(input);
276
+ },
277
+ getAssets() {
278
+ return assetInspector.getAssets();
279
+ },
242
280
  async fetch(request) {
243
281
  if (request.method !== 'GET' && request.method !== 'HEAD')
244
282
  return null;
@@ -247,7 +285,7 @@ export function createAssetServer(options) {
247
285
  if (requestPathname === hmrPathnames.client) {
248
286
  let browserHmrChannel = await browserHmrChannelPromise;
249
287
  assertBrowserEventUrl(browserHmrChannel?.url);
250
- return createHmrClientResponse(browserHmrChannel.url, request.method);
288
+ return await createHmrClientResponse(browserHmrChannel.url, hmrDataKey, resolvedOptions.hmrModuleImporter, request.method, scriptCompiler);
251
289
  }
252
290
  }
253
291
  let requestUrl = new URL(request.url);
@@ -277,7 +315,10 @@ export function createAssetServer(options) {
277
315
  }
278
316
  let compiledStyle = styleResult.style;
279
317
  if (parsedRequestPathname.requestedFingerprint !== null) {
280
- if (compiledStyle.fingerprint !== parsedRequestPathname.requestedFingerprint)
318
+ let fingerprint = parsedRequestPathname.isSourceMapRequest
319
+ ? compiledStyle.sourceMap?.fingerprint
320
+ : compiledStyle.fingerprint;
321
+ if (fingerprint !== parsedRequestPathname.requestedFingerprint)
281
322
  return null;
282
323
  }
283
324
  return createResponseForStyle(compiledStyle, {
@@ -332,7 +373,10 @@ export function createAssetServer(options) {
332
373
  }
333
374
  let compiledScript = scriptResult.script;
334
375
  if (parsedRequestPathname.requestedFingerprint !== null) {
335
- if (compiledScript.fingerprint !== parsedRequestPathname.requestedFingerprint)
376
+ let fingerprint = parsedRequestPathname.isSourceMapRequest
377
+ ? compiledScript.sourceMap?.fingerprint
378
+ : compiledScript.fingerprint;
379
+ if (fingerprint !== parsedRequestPathname.requestedFingerprint)
336
380
  return null;
337
381
  }
338
382
  return createResponseForScript(compiledScript, {
@@ -383,6 +427,14 @@ export function createAssetServer(options) {
383
427
  }
384
428
  return scriptCompiler.getHref(filePath);
385
429
  },
430
+ async getScriptEntry(filePath) {
431
+ let [href, preloads, importMap] = await Promise.all([
432
+ assetServer.getHref(filePath),
433
+ assetServer.getPreloads(filePath),
434
+ assetServer.getImportMap(filePath),
435
+ ]);
436
+ return { href, preloads, importMap };
437
+ },
386
438
  async getPreloads(filePath) {
387
439
  let filePaths = Array.isArray(filePath) ? filePath : [filePath];
388
440
  let fileAssetFiles = [];
@@ -437,6 +489,16 @@ export function createAssetServer(options) {
437
489
  });
438
490
  return mergePreloadLayers(await Promise.all(preloadLayerGroupPromises));
439
491
  },
492
+ async getImportMap(filePath) {
493
+ let filePaths = Array.isArray(filePath) ? filePath : [filePath];
494
+ for (let nextFilePath of filePaths) {
495
+ let typeCheckFilePath = stripFilePathUrlSuffix(nextFilePath);
496
+ if (!isScriptFilePath(typeCheckFilePath)) {
497
+ throw new TypeError(`assetServer.getImportMap() only supports script files: ${nextFilePath}`);
498
+ }
499
+ }
500
+ return scriptCompiler.getImportMap(filePath);
501
+ },
440
502
  async close() {
441
503
  if (closed)
442
504
  return;
@@ -493,8 +555,13 @@ function getHmrPathnames(basePath) {
493
555
  events: `${hmrBase}/events`,
494
556
  };
495
557
  }
496
- function createHmrClientResponse(eventPathname, method) {
497
- return new Response(method === 'HEAD' ? null : createHmrClientSource({ eventPathname }), {
558
+ async function createHmrClientResponse(eventPathname, dataKey, moduleImporter, method, scriptCompiler) {
559
+ let moduleImporterHref = moduleImporter
560
+ ? await scriptCompiler.resolveSpecifierFromRoot(moduleImporter)
561
+ : null;
562
+ return new Response(method === 'HEAD'
563
+ ? null
564
+ : createHmrClientSource({ dataKey, eventPathname, moduleImporter: moduleImporterHref }), {
498
565
  headers: {
499
566
  'Cache-Control': 'no-cache',
500
567
  'Content-Type': 'application/javascript; charset=utf-8',
@@ -509,7 +576,7 @@ function assertBrowserEventUrl(url) {
509
576
  function createHmrPayloadSender() {
510
577
  return () => { };
511
578
  }
512
- function createBrowserHmrEvent(payload, files) {
579
+ function createBrowserHmrEvent(payload, files, dataKey) {
513
580
  if (payload.type === 'browser:reload') {
514
581
  return {
515
582
  ...(files.length === 0 ? {} : { files: [...files] }),
@@ -517,10 +584,14 @@ function createBrowserHmrEvent(payload, files) {
517
584
  };
518
585
  }
519
586
  return {
587
+ data: {
588
+ [dataKey]: {
589
+ timestamp: payload.timestamp,
590
+ updates: payload.updates,
591
+ },
592
+ },
520
593
  ...(files.length === 0 ? {} : { files: [...files] }),
521
- timestamp: payload.timestamp,
522
594
  type: 'update',
523
- updates: payload.updates,
524
595
  };
525
596
  }
526
597
  function getScriptHmrUpdateFiles(updates) {
@@ -541,7 +612,9 @@ export function createScriptHmrPayload(updates) {
541
612
  timestamp,
542
613
  type: 'browser:update',
543
614
  updates: acceptedUpdates.map((update) => ({
544
- ...(update.acceptedPath === update.path ? {} : { acceptedPath: update.acceptedPath }),
615
+ ...(update.acceptedUrlPathname === update.path
616
+ ? {}
617
+ : { acceptedPath: update.acceptedUrlPathname }),
545
618
  path: update.path,
546
619
  type: 'js',
547
620
  })),
@@ -588,36 +661,42 @@ function resolveAssetServerOptions(options) {
588
661
  let rootDir = normalizeFilePath(fs.realpathSync(path.resolve(options.rootDir ?? process.cwd())));
589
662
  let basePath = normalizeBasePath(options.basePath);
590
663
  let scriptOptions = options.scripts ?? {};
591
- let fingerprintOptions = normalizeFingerprintOptions({
664
+ let fingerprintAssets = normalizeFingerprintOptions({
592
665
  fingerprint: options.fingerprint,
593
666
  watch: options.watch,
594
667
  });
595
668
  let watchOptions = normalizeWatchOptions(options.watch);
596
- let hmrFactory = normalizeHmrFactory(options.hmr);
597
- if (hmrFactory && watchOptions === null) {
669
+ let hmr = normalizeHmrOptions(options.hmr);
670
+ let mounts = options.mounts ?? defaultMounts;
671
+ if (hmr.channel && watchOptions === null) {
598
672
  throw new TypeError('hmr requires watch mode');
599
673
  }
674
+ if (Object.keys(mounts).length === 0) {
675
+ throw new TypeError('mounts must include at least one entry');
676
+ }
600
677
  return {
601
678
  allowFiles: options.allowFiles,
602
679
  allowPackages: options.allowPackages,
603
680
  basePath,
604
- buildId: fingerprintOptions.buildId,
605
681
  define: scriptOptions.define,
606
682
  denyFiles: options.denyFiles,
607
683
  external: scriptOptions.external ?? [],
608
684
  files: normalizeFilesOptions(options.files),
609
- fingerprintAssets: fingerprintOptions.enabled,
610
- hmr: hmrFactory,
685
+ fingerprintAssets,
686
+ hmr: hmr.channel,
687
+ hmrModuleImporter: hmr.moduleImporter,
611
688
  minify: options.minify ?? false,
689
+ mounts,
612
690
  loaders: scriptOptions.loaders ?? [],
613
691
  onError: options.onError ?? defaultErrorHandler,
614
692
  rootDir,
615
693
  routes: compileRoutes(basePath, [
616
694
  {
617
- fileMap: options.fileMap,
695
+ mounts,
618
696
  rootDir,
619
697
  },
620
- ...getInjectedPackageRouteConfigs(),
698
+ ...getInjectedPackageMountConfigs(),
699
+ ...getVirtualStoreMountConfigs({ mounts, rootDir }),
621
700
  ]),
622
701
  sourceMapSourcePaths: options.sourceMapSourcePaths ?? 'url',
623
702
  sourceMaps: options.sourceMaps,
@@ -626,13 +705,19 @@ function resolveAssetServerOptions(options) {
626
705
  watchOptions,
627
706
  };
628
707
  }
629
- function normalizeHmrFactory(factory) {
630
- if (factory === undefined)
631
- return null;
632
- if (typeof factory !== 'function') {
633
- throw new TypeError('hmr must be a function');
708
+ function normalizeHmrOptions(options) {
709
+ if (options === undefined)
710
+ return { channel: null, moduleImporter: null };
711
+ if (typeof options === 'function')
712
+ return { channel: options, moduleImporter: null };
713
+ if (!options || typeof options !== 'object' || typeof options.channel !== 'function') {
714
+ throw new TypeError('hmr must be a function or an object with a channel function');
715
+ }
716
+ if (options.moduleImporter !== undefined &&
717
+ (typeof options.moduleImporter !== 'string' || options.moduleImporter.trim().length === 0)) {
718
+ throw new TypeError('hmr.moduleImporter must be a non-empty string');
634
719
  }
635
- return factory;
720
+ return { channel: options.channel, moduleImporter: options.moduleImporter ?? null };
636
721
  }
637
722
  function createBrowserHmrChannel(createChannel, handleFileEvents, isClosed, setUnsubscribe) {
638
723
  if (!createChannel)
@@ -680,24 +765,16 @@ function normalizeBasePath(basePath) {
680
765
  return normalizePathname(basePath || '/').replace(/\/+$/, '') || '/';
681
766
  }
682
767
  function normalizeFingerprintOptions(options) {
683
- if (!options.fingerprint) {
684
- return {
685
- enabled: false,
686
- };
768
+ if (options.fingerprint !== undefined && typeof options.fingerprint !== 'boolean') {
769
+ throw new TypeError('fingerprint must be a boolean');
687
770
  }
688
- if (typeof options.fingerprint.buildId !== 'string') {
689
- throw new TypeError('fingerprint.buildId must be a string');
690
- }
691
- if (options.fingerprint.buildId.length === 0) {
692
- throw new TypeError('fingerprint.buildId must be a non-empty string');
771
+ if (!options.fingerprint) {
772
+ return false;
693
773
  }
694
774
  if (options.watch !== false) {
695
775
  throw new TypeError('fingerprint cannot be used with watch mode');
696
776
  }
697
- return {
698
- enabled: true,
699
- buildId: options.fingerprint.buildId,
700
- };
777
+ return true;
701
778
  }
702
779
  function normalizeWatchOptions(options) {
703
780
  if (options === false)
@@ -709,13 +786,8 @@ function normalizeWatchOptions(options) {
709
786
  function hasPackages(packages) {
710
787
  return packages !== undefined && packages.length > 0;
711
788
  }
712
- function getPackageSearchRoots(fileMap, rootDir) {
713
- return Object.values(fileMap).map((filePattern) => path.resolve(rootDir, getStaticFilePatternPrefix(filePattern)));
714
- }
715
- function getStaticFilePatternPrefix(filePattern) {
716
- let firstDynamicIndex = filePattern.search(/[*:]/);
717
- let staticPrefix = firstDynamicIndex === -1 ? filePattern : filePattern.slice(0, firstDynamicIndex);
718
- return staticPrefix.replace(/[/\\]*$/, '');
789
+ function getPackageSearchRoots(mounts, rootDir) {
790
+ return Object.values(mounts).map((fileRoot) => path.resolve(rootDir, fileRoot));
719
791
  }
720
792
  function parseAssetRequestPathname(pathname, options) {
721
793
  let isSourceMapRequest = pathname.endsWith('.map');
@@ -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' | 'LOADER_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' | 'IMPORT_RESOLUTION_NOT_DIRECTORY_UNIFORM' | '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,eAAe,GACf,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,yCAAyC,GACzC,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"}
@@ -25,8 +25,8 @@ type FileGetHrefOptions = {
25
25
  transform: readonly string[] | null;
26
26
  };
27
27
  type FileCompilerOptions = {
28
- buildId?: string;
29
28
  cache?: FileStorage;
29
+ cacheKey?: string;
30
30
  extensions: readonly string[];
31
31
  fingerprintAssets: boolean;
32
32
  globalTransforms: readonly {
@@ -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":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAU1D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,KAAK,EAAE,wBAAwB,EAAE,gCAAgC,EAAE,MAAM,aAAa,CAAA;AAU7F,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,UAAU,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B,CAAA;AAID,KAAK,iBAAiB,GAAG,WAAW,CAAA;AAEpC,KAAK,aAAa,GACd;IACE,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,cAAc,CAAA;CACrB,GACD;IACE,IAAI,EAAE,iBAAiB,CAAA;IACvB,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAEL,KAAK,cAAc,GAAG;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAA;CACpC,CAAA;AAED,KAAK,kBAAkB,GAAG;IACxB,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAA;CACpC,CAAA;AAED,KAAK,mBAAmB,GAAG;IACzB,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IAC7B,iBAAiB,EAAE,OAAO,CAAA;IAC1B,gBAAgB,EAAE,SAAS;QACzB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;QAC9B,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,SAAS,CACP,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE;YACP,SAAS,EAAE,MAAM,CAAA;YACjB,QAAQ,EAAE,MAAM,CAAA;SACjB,GAEC,MAAM,GACN,UAAU,GACV,wBAAwB,GACxB,IAAI,GACJ,OAAO,CAAC,MAAM,GAAG,UAAU,GAAG,wBAAwB,GAAG,IAAI,CAAC,CAAA;KACnE,EAAE,CAAA;IACH,SAAS,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAA;IACxC,oBAAoB,EAAE,MAAM,CAAA;IAC5B,UAAU,EAAE,gCAAgC,CAAA;IAC5C,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,cAAc,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;IAC1E,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACvE,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpF,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3C,sBAAsB,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAA;CAChE,CAAA;AAED,KAAK,WAAW,GAAG;IACjB,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC/B,SAAS,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAA;IACxC,MAAM,EAAE,cAAc,CAAA;CACvB,CAAA;AAED,KAAK,YAAY,GAAG;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,iBAAiB,EAAE,MAAM,CAAA;CAC1B,CAAA;AAkBD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,YAAY,CAyZ7E;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,YAAY,CAsC1F;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE;IACP,YAAY,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,MAAM,EAAE,MAAM,CAAA;CACf,GACA,QAAQ,CAYV;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAE3F"}
@@ -5,7 +5,7 @@ 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
7
  import { createAssetServerCompilationError, isAssetServerCompilationError, } from '../compilation-error.js';
8
- import { formatFingerprintedPathname, generateFingerprint, hashContent } from '../fingerprint.js';
8
+ import { formatFingerprintedPathname, hashContent } from '../fingerprint.js';
9
9
  import { normalizeFilePath, resolveFilePath } from '../paths.js';
10
10
  import { parseAssetTransformInvocations } from './config.js';
11
11
  import { createSourceFileStore } from './store.js';
@@ -19,7 +19,7 @@ export function createFileCompiler(options) {
19
19
  let transformedAssetMetadataByCacheKey = new Map();
20
20
  let transformedCacheKeysByIdentityPath = new Map();
21
21
  let transformedEmitInFlightByCacheKey = new Map();
22
- let cacheEpoch = resolvedOptions.buildId ?? crypto.randomUUID();
22
+ let cacheKey = resolvedOptions.cacheKey ?? crypto.randomUUID();
23
23
  let resolveArgs = {
24
24
  extensions: resolvedOptions.extensionSet,
25
25
  isAllowed: resolvedOptions.isAllowed,
@@ -30,8 +30,8 @@ export function createFileCompiler(options) {
30
30
  let resolvedFile = resolveServedFileOrThrow(resolveInputFilePath(filePath), resolveArgs);
31
31
  let record = getFreshSourceFileRecord(resolvedFile.identityPath);
32
32
  if (shouldUseTransformPipeline(getOptions.transform)) {
33
- let cacheKey = getTransformedRecordCacheKey(cacheEpoch, record, getOptions.transform);
34
- let notModified = getNotModifiedFile(transformedAssetMetadataByCacheKey.get(cacheKey), getOptions);
33
+ let transformedCacheKey = getTransformedRecordCacheKey(cacheKey, record, getOptions.transform);
34
+ let notModified = getNotModifiedFile(transformedAssetMetadataByCacheKey.get(transformedCacheKey), getOptions);
35
35
  if (notModified)
36
36
  return notModified;
37
37
  let transformedFile = await getOrCreateTransformedFile(record, getOptions.transform);
@@ -62,9 +62,13 @@ export function createFileCompiler(options) {
62
62
  async getHref(filePath, hrefOptions) {
63
63
  let resolvedFile = resolveServedFileOrThrow(resolveInputFilePath(filePath), resolveArgs);
64
64
  let record = getFreshSourceFileRecord(resolvedFile.identityPath);
65
- let href = resolvedOptions.fingerprintAssets
66
- ? formatFingerprintedPathname(resolvedFile.stableUrlPathname, (await getOrCreateSourceFileMetadata(record)).fingerprint)
67
- : resolvedFile.stableUrlPathname;
65
+ let href = resolvedFile.stableUrlPathname;
66
+ if (resolvedOptions.fingerprintAssets) {
67
+ let emittedFile = shouldUseTransformPipeline(hrefOptions.transform)
68
+ ? await getOrCreateTransformedFile(record, hrefOptions.transform)
69
+ : await getOrCreateSourceFile(record);
70
+ href = formatFingerprintedPathname(href, emittedFile.fingerprint);
71
+ }
68
72
  if (shouldUseTransformPipeline(hrefOptions.transform)) {
69
73
  return appendTransformQuery(href, hrefOptions.transform);
70
74
  }
@@ -138,15 +142,15 @@ export function createFileCompiler(options) {
138
142
  }
139
143
  async function getOrCreateTransformedFile(record, transformQuery) {
140
144
  let parsedTransforms = parseRequestTransforms(transformQuery, resolvedOptions.transforms, resolvedOptions.maxRequestTransforms);
141
- let cacheKey = getTransformedRecordCacheKey(cacheEpoch, record, transformQuery);
142
- let existing = transformedEmitInFlightByCacheKey.get(cacheKey);
145
+ let transformedCacheKey = getTransformedRecordCacheKey(cacheKey, record, transformQuery);
146
+ let existing = transformedEmitInFlightByCacheKey.get(transformedCacheKey);
143
147
  if (existing)
144
148
  return existing;
145
149
  let promise = (async () => {
146
- let sourceFile = await getOrCreateSourceFile(record);
147
- let cachedFile = await getCachedTransformedFile(cacheKey, record.identityPath, sourceFile.fingerprint);
150
+ let cachedFile = await getCachedTransformedFile(transformedCacheKey, record.identityPath);
148
151
  if (cachedFile)
149
152
  return cachedFile;
153
+ let sourceFile = await getOrCreateSourceFile(record);
150
154
  let transformedFile = await applyTransforms(record.identityPath, sourceFile, parsedTransforms);
151
155
  if (transformedFile === null) {
152
156
  return sourceFile;
@@ -154,19 +158,18 @@ export function createFileCompiler(options) {
154
158
  let emittedFile = await createEmittedFile(transformedFile.body, {
155
159
  extension: transformedFile.extension,
156
160
  filePath: record.identityPath,
157
- fingerprint: sourceFile.fingerprint,
158
161
  });
159
- rememberTransformedAssetMetadata(cacheKey, record.identityPath, toEmittedFileMetadata(emittedFile));
160
- await setCachedTransformedFile(cacheKey, record.identityPath, emittedFile);
162
+ rememberTransformedAssetMetadata(transformedCacheKey, record.identityPath, toEmittedFileMetadata(emittedFile));
163
+ await setCachedTransformedFile(transformedCacheKey, record.identityPath, emittedFile);
161
164
  return emittedFile;
162
165
  })();
163
- transformedEmitInFlightByCacheKey.set(cacheKey, promise);
166
+ transformedEmitInFlightByCacheKey.set(transformedCacheKey, promise);
164
167
  try {
165
168
  return await promise;
166
169
  }
167
170
  finally {
168
- if (transformedEmitInFlightByCacheKey.get(cacheKey) === promise) {
169
- transformedEmitInFlightByCacheKey.delete(cacheKey);
171
+ if (transformedEmitInFlightByCacheKey.get(transformedCacheKey) === promise) {
172
+ transformedEmitInFlightByCacheKey.delete(transformedCacheKey);
170
173
  }
171
174
  }
172
175
  }
@@ -174,12 +177,6 @@ export function createFileCompiler(options) {
174
177
  return createEmittedFile(body, {
175
178
  extension: path.extname(identityPath).toLowerCase(),
176
179
  filePath: identityPath,
177
- fingerprint: resolvedOptions.fingerprintAssets && resolvedOptions.buildId
178
- ? await generateFingerprint({
179
- buildId: resolvedOptions.buildId,
180
- content: body,
181
- })
182
- : null,
183
180
  });
184
181
  }
185
182
  function createEmittedFileFromMetadata(body, metadata) {
@@ -253,7 +250,7 @@ export function createFileCompiler(options) {
253
250
  return null;
254
251
  return { body: currentBody, extension: currentExtension };
255
252
  }
256
- async function getCachedTransformedFile(cacheKey, identityPath, fingerprint) {
253
+ async function getCachedTransformedFile(cacheKey, identityPath) {
257
254
  if (!resolvedOptions.cache)
258
255
  return null;
259
256
  let file = await resolvedOptions.cache.get(cacheKey);
@@ -264,7 +261,6 @@ export function createFileCompiler(options) {
264
261
  (await createEmittedFileMetadata(body, {
265
262
  extension: path.extname(file.name).toLowerCase(),
266
263
  filePath: identityPath,
267
- fingerprint,
268
264
  }));
269
265
  rememberTransformedAssetMetadata(cacheKey, identityPath, metadata);
270
266
  return {
@@ -303,14 +299,15 @@ export function createFileCompiler(options) {
303
299
  transformedCacheKeysByIdentityPath.set(identityPath, cacheKeys);
304
300
  }
305
301
  async function createEmittedFileMetadata(body, options) {
302
+ let contentHash = await hashContent(body);
306
303
  let contentType = detectContentType(`file${options.extension}`) ??
307
304
  detectContentType(options.filePath) ??
308
305
  'application/octet-stream';
309
306
  return {
310
307
  contentType,
311
- etag: `W/"${await hashContent(body)}"`,
308
+ etag: `W/"${contentHash}"`,
312
309
  extension: options.extension,
313
- fingerprint: options.fingerprint,
310
+ fingerprint: resolvedOptions.fingerprintAssets ? contentHash : null,
314
311
  };
315
312
  }
316
313
  }
@@ -334,8 +331,8 @@ export function resolveServedFileOrThrow(filePath, args) {
334
331
  }
335
332
  let stableUrlPathname = args.routes.toUrlPathname(identityPath);
336
333
  if (!stableUrlPathname) {
337
- throw createAssetServerCompilationError(`File ${identityPath} is outside all configured fileMap entries.`, {
338
- code: 'FILE_OUTSIDE_FILE_MAP',
334
+ throw createAssetServerCompilationError(`File ${identityPath} is outside all configured mounts.`, {
335
+ code: 'FILE_OUTSIDE_MOUNTS',
339
336
  });
340
337
  }
341
338
  return {
@@ -361,9 +358,9 @@ export function isServedFilePath(filePath, extensions) {
361
358
  function getRecordCacheKey(record) {
362
359
  return `${record.identityPath}\0${record.invalidationVersion}`;
363
360
  }
364
- function getTransformedRecordCacheKey(cacheEpoch, record, transformQuery) {
361
+ function getTransformedRecordCacheKey(cacheKey, record, transformQuery) {
365
362
  return [
366
- encodeCacheKeyPart(cacheEpoch),
363
+ encodeCacheKeyPart(cacheKey),
367
364
  encodeCacheKeyPart(record.identityPath),
368
365
  String(record.invalidationVersion),
369
366
  encodeCacheKeyPart(JSON.stringify(transformQuery ?? [])),
@@ -73,9 +73,15 @@ export interface AssetServerFilesOptions<transforms extends AssetRequestTransfor
73
73
  * Optional backing store for cached transformed file outputs.
74
74
  */
75
75
  cache?: FileStorage;
76
+ /**
77
+ * Optional namespace for cached transformed file outputs. Use a stable value such as a
78
+ * commit SHA to reuse transformed files across server restarts for the same deployment.
79
+ */
80
+ cacheKey?: string;
76
81
  }
77
82
  export interface ResolvedAssetServerFilesOptions {
78
83
  cache?: FileStorage;
84
+ cacheKey?: string;
79
85
  extensions: readonly string[];
80
86
  globalTransforms: readonly ResolvedAssetGlobalTransform[];
81
87
  hasTransforms: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/lib/files/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/lib/files/config.ts"],"names":[],"mappings":"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"}
@@ -96,8 +96,17 @@ export function normalizeFilesOptions(files) {
96
96
  throw new TypeError('files.cache must implement the FileStorage interface');
97
97
  }
98
98
  }
99
+ if (files.cacheKey !== undefined) {
100
+ if (typeof files.cacheKey !== 'string') {
101
+ throw new TypeError('files.cacheKey must be a string');
102
+ }
103
+ if (files.cacheKey.length === 0) {
104
+ throw new TypeError('files.cacheKey must be a non-empty string');
105
+ }
106
+ }
99
107
  return {
100
108
  cache: files.cache,
109
+ cacheKey: files.cacheKey,
101
110
  extensions: normalizedExtensions,
102
111
  globalTransforms: normalizedGlobalTransforms,
103
112
  hasTransforms: normalizedTransforms.size > 0 || normalizedGlobalTransforms.length > 0,
@@ -1,8 +1,4 @@
1
1
  export declare function hashContent(content: string | Uint8Array<ArrayBufferLike>): Promise<string>;
2
- export declare function generateFingerprint(args: {
3
- buildId: string;
4
- content: string | Uint8Array<ArrayBufferLike>;
5
- }): Promise<string>;
6
2
  export declare function parseFingerprintSuffix(pathname: string): {
7
3
  pathname: string;
8
4
  requestedFingerprint: string | null;