@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.
- package/README.md +185 -104
- package/dist/assets.d.ts +4 -1
- package/dist/assets.d.ts.map +1 -1
- package/dist/lib/access.d.ts +29 -2
- package/dist/lib/access.d.ts.map +1 -1
- package/dist/lib/access.js +52 -26
- package/dist/lib/asset-server.d.ts +61 -25
- package/dist/lib/asset-server.d.ts.map +1 -1
- package/dist/lib/asset-server.js +127 -55
- package/dist/lib/compilation-error.d.ts +1 -1
- package/dist/lib/compilation-error.d.ts.map +1 -1
- package/dist/lib/files/compiler.d.ts +1 -1
- package/dist/lib/files/compiler.d.ts.map +1 -1
- package/dist/lib/files/compiler.js +28 -31
- package/dist/lib/files/config.d.ts +6 -0
- package/dist/lib/files/config.d.ts.map +1 -1
- package/dist/lib/files/config.js +9 -0
- package/dist/lib/fingerprint.d.ts +0 -4
- package/dist/lib/fingerprint.d.ts.map +1 -1
- package/dist/lib/fingerprint.js +0 -4
- package/dist/lib/hmr.d.ts +7 -0
- package/dist/lib/hmr.d.ts.map +1 -1
- package/dist/lib/hmr.js +198 -25
- package/dist/lib/injected-packages.d.ts +3 -2
- package/dist/lib/injected-packages.d.ts.map +1 -1
- package/dist/lib/injected-packages.js +11 -8
- package/dist/lib/inspection.d.ts +39 -0
- package/dist/lib/inspection.d.ts.map +1 -0
- package/dist/lib/inspection.js +160 -0
- package/dist/lib/routes.d.ts +12 -3
- package/dist/lib/routes.d.ts.map +1 -1
- package/dist/lib/routes.js +124 -80
- package/dist/lib/scripts/compiler.d.ts +8 -2
- package/dist/lib/scripts/compiler.d.ts.map +1 -1
- package/dist/lib/scripts/compiler.js +188 -38
- package/dist/lib/scripts/emit.d.ts +2 -1
- package/dist/lib/scripts/emit.d.ts.map +1 -1
- package/dist/lib/scripts/emit.js +25 -16
- package/dist/lib/scripts/resolve.d.ts +7 -1
- package/dist/lib/scripts/resolve.d.ts.map +1 -1
- package/dist/lib/scripts/resolve.js +119 -12
- package/dist/lib/scripts/specifiers.d.ts +2 -0
- package/dist/lib/scripts/specifiers.d.ts.map +1 -0
- package/dist/lib/scripts/specifiers.js +9 -0
- package/dist/lib/scripts/transform.d.ts +2 -3
- package/dist/lib/scripts/transform.d.ts.map +1 -1
- package/dist/lib/scripts/transform.js +4 -18
- package/dist/lib/styles/compiler.d.ts +0 -1
- package/dist/lib/styles/compiler.d.ts.map +1 -1
- package/dist/lib/styles/compiler.js +105 -25
- package/dist/lib/styles/emit.d.ts +2 -1
- package/dist/lib/styles/emit.d.ts.map +1 -1
- package/dist/lib/styles/emit.js +12 -10
- package/dist/lib/styles/resolve.d.ts +0 -1
- package/dist/lib/styles/resolve.d.ts.map +1 -1
- package/dist/lib/styles/resolve.js +8 -9
- package/dist/lib/styles/transform.d.ts +0 -2
- package/dist/lib/styles/transform.d.ts.map +1 -1
- package/dist/lib/styles/transform.js +2 -9
- package/dist/lib/virtual-store.d.ts +8 -0
- package/dist/lib/virtual-store.d.ts.map +1 -0
- package/dist/lib/virtual-store.js +100 -0
- package/package.json +6 -6
- package/src/assets.ts +9 -1
- package/src/lib/access.ts +83 -30
- package/src/lib/asset-server.ts +224 -94
- package/src/lib/compilation-error.ts +4 -3
- package/src/lib/files/compiler.ts +32 -42
- package/src/lib/files/config.ts +17 -0
- package/src/lib/fingerprint.ts +0 -9
- package/src/lib/hmr.ts +210 -26
- package/src/lib/injected-packages.ts +16 -10
- package/src/lib/inspection.ts +229 -0
- package/src/lib/routes.ts +158 -126
- package/src/lib/scripts/compiler.ts +240 -54
- package/src/lib/scripts/emit.ts +34 -19
- package/src/lib/scripts/resolve.ts +175 -13
- package/src/lib/scripts/specifiers.ts +11 -0
- package/src/lib/scripts/transform.ts +6 -25
- package/src/lib/styles/compiler.ts +137 -39
- package/src/lib/styles/emit.ts +18 -13
- package/src/lib/styles/resolve.ts +8 -10
- package/src/lib/styles/transform.ts +2 -12
- package/src/lib/virtual-store.ts +124 -0
package/src/lib/asset-server.ts
CHANGED
|
@@ -17,19 +17,21 @@ import type {
|
|
|
17
17
|
import { getFingerprintRequestCacheControl, parseFingerprintSuffix } from './fingerprint.ts'
|
|
18
18
|
import { createHmrClientSource } from './hmr.ts'
|
|
19
19
|
import type { HmrPayload } from './hmr.ts'
|
|
20
|
-
import {
|
|
20
|
+
import { getInjectedPackageMountConfigs } from './injected-packages.ts'
|
|
21
21
|
import type { ModuleLoader } from './loaders.ts'
|
|
22
22
|
import { normalizeFilePath, normalizePathname } from './paths.ts'
|
|
23
23
|
import { compileRoutes } from './routes.ts'
|
|
24
24
|
import type { CompiledRoutes } from './routes.ts'
|
|
25
25
|
import { createResponseForScript, createScriptCompiler } from './scripts/compiler.ts'
|
|
26
|
-
import type { ScriptHmrUpdate } from './scripts/compiler.ts'
|
|
26
|
+
import type { ScriptHmrUpdate, ScriptImportMap } from './scripts/compiler.ts'
|
|
27
27
|
import { supportedScriptExtensions } from './scripts/resolve.ts'
|
|
28
28
|
import { createResponseForStyle, createStyleCompiler, isStyleFilePath } from './styles/compiler.ts'
|
|
29
29
|
import { resolveScriptTarget, resolveStyleTarget } from './target.ts'
|
|
30
30
|
import type { AssetTarget, ResolvedScriptTarget, ResolvedStyleTarget } from './target.ts'
|
|
31
31
|
import { createAssetServerWatcher } from './watch.ts'
|
|
32
|
+
import { createAssetInspector, type AssetDetails } from './inspection.ts'
|
|
32
33
|
import type { AssetServerWatcher, ChokidarWatcher } from './watch.ts'
|
|
34
|
+
import { getVirtualStoreMountConfigs } from './virtual-store.ts'
|
|
33
35
|
|
|
34
36
|
interface AssetServerWatchOptions {
|
|
35
37
|
/**
|
|
@@ -89,6 +91,18 @@ export type BrowserHmrChannelFactory = () =>
|
|
|
89
91
|
| undefined
|
|
90
92
|
| Promise<BrowserHmrChannel | undefined>
|
|
91
93
|
|
|
94
|
+
/** Browser HMR integration options. */
|
|
95
|
+
export interface BrowserHmrOptions {
|
|
96
|
+
/** Creates the channel that delivers browser HMR events. */
|
|
97
|
+
channel: BrowserHmrChannelFactory
|
|
98
|
+
/**
|
|
99
|
+
* Module specifier resolved relative to the asset server's root directory. It must point to a
|
|
100
|
+
* browser module exporting `importModule(specifier, parentUrl)` for evaluating JavaScript updates.
|
|
101
|
+
* The module and its dependencies must be allowed and reachable through configured mounts.
|
|
102
|
+
*/
|
|
103
|
+
moduleImporter?: string
|
|
104
|
+
}
|
|
105
|
+
|
|
92
106
|
/**
|
|
93
107
|
* Converts a watcher batch into ordered browser update or reload events.
|
|
94
108
|
*
|
|
@@ -124,14 +138,12 @@ export type BrowserHmrFileEvent = {
|
|
|
124
138
|
*/
|
|
125
139
|
export type BrowserHmrEvent =
|
|
126
140
|
| {
|
|
141
|
+
/** Consumer-owned data keyed by a stable, versioned namespace. */
|
|
142
|
+
data: Record<string, BrowserHmrData>
|
|
127
143
|
/** Absolute source file paths that triggered this update. */
|
|
128
144
|
files?: string[]
|
|
129
|
-
/** Update time used to bypass browser module and stylesheet caches. */
|
|
130
|
-
timestamp: number
|
|
131
145
|
/** Browser update event. */
|
|
132
146
|
type: 'update'
|
|
133
|
-
/** Accepted JavaScript and CSS module updates for the browser to apply in place. */
|
|
134
|
-
updates: Extract<HmrPayload, { type: 'browser:update' }>['updates']
|
|
135
147
|
}
|
|
136
148
|
| {
|
|
137
149
|
/** Absolute source file paths that could not be handled in place. */
|
|
@@ -140,16 +152,15 @@ export type BrowserHmrEvent =
|
|
|
140
152
|
type: 'reload'
|
|
141
153
|
}
|
|
142
154
|
|
|
143
|
-
interface FingerprintOptions {
|
|
144
|
-
/**
|
|
145
|
-
* Per-build invalidation token that must change whenever fingerprinted asset URLs
|
|
146
|
-
* should be invalidated together.
|
|
147
|
-
*/
|
|
148
|
-
buildId: string
|
|
149
|
-
}
|
|
150
|
-
|
|
151
155
|
type AssetSourceMaps = 'inline' | 'external'
|
|
152
156
|
type AssetSourceMapSourcePaths = 'url' | 'absolute'
|
|
157
|
+
type BrowserHmrData =
|
|
158
|
+
| null
|
|
159
|
+
| boolean
|
|
160
|
+
| number
|
|
161
|
+
| string
|
|
162
|
+
| BrowserHmrData[]
|
|
163
|
+
| { [key: string]: BrowserHmrData }
|
|
153
164
|
|
|
154
165
|
interface AssetServerScriptOptions {
|
|
155
166
|
/**
|
|
@@ -157,7 +168,7 @@ interface AssetServerScriptOptions {
|
|
|
157
168
|
* `{ 'process.env.NODE_ENV': '"production"' }`
|
|
158
169
|
*/
|
|
159
170
|
define?: Record<string, string>
|
|
160
|
-
/** Import specifiers to
|
|
171
|
+
/** Import specifiers to treat as external dependencies (CDN URLs, import map entries, etc.) */
|
|
161
172
|
external?: string[]
|
|
162
173
|
/**
|
|
163
174
|
* Synchronous loaders that post-process compiled JavaScript.
|
|
@@ -172,6 +183,10 @@ interface AssetServerScriptOptions {
|
|
|
172
183
|
}
|
|
173
184
|
|
|
174
185
|
const scriptExtensionSet = new Set<string>(supportedScriptExtensions)
|
|
186
|
+
const defaultMounts = {
|
|
187
|
+
app: 'app',
|
|
188
|
+
npm: 'node_modules',
|
|
189
|
+
} as const
|
|
175
190
|
|
|
176
191
|
/**
|
|
177
192
|
* Options used to construct an {@link AssetServer} via {@link createAssetServer}.
|
|
@@ -179,8 +194,14 @@ const scriptExtensionSet = new Set<string>(supportedScriptExtensions)
|
|
|
179
194
|
export interface AssetServerOptions<transforms extends AssetRequestTransformMap = {}> {
|
|
180
195
|
/** Public mount path for this asset server, e.g. `'/assets'`. */
|
|
181
196
|
basePath: string
|
|
182
|
-
/**
|
|
183
|
-
|
|
197
|
+
/**
|
|
198
|
+
* Directories to mount at public URL paths.
|
|
199
|
+
*
|
|
200
|
+
* Each key is a public URL path and its value is a directory relative to `rootDir`. Defaults to
|
|
201
|
+
* `{ app: 'app', npm: 'node_modules' }`. Public paths must not contain query strings, fragments,
|
|
202
|
+
* or encoded dot segments.
|
|
203
|
+
*/
|
|
204
|
+
mounts?: Readonly<Record<string, string>>
|
|
184
205
|
/**
|
|
185
206
|
* Root directory used to resolve relative file paths. Defaults to `process.cwd()`.
|
|
186
207
|
*/
|
|
@@ -191,7 +212,7 @@ export interface AssetServerOptions<transforms extends AssetRequestTransformMap
|
|
|
191
212
|
allowFiles: readonly string[]
|
|
192
213
|
/**
|
|
193
214
|
* Exact package names whose files are allowed to be served. Dependencies and installed optional
|
|
194
|
-
* dependencies are allowed automatically. Package files must still
|
|
215
|
+
* dependencies are allowed automatically. Package files must still be within a configured mount.
|
|
195
216
|
*/
|
|
196
217
|
allowPackages?: readonly string[]
|
|
197
218
|
/**
|
|
@@ -199,12 +220,12 @@ export interface AssetServerOptions<transforms extends AssetRequestTransformMap
|
|
|
199
220
|
*/
|
|
200
221
|
denyFiles?: readonly string[]
|
|
201
222
|
/**
|
|
202
|
-
* Controls optional
|
|
223
|
+
* Controls optional content-based URL fingerprinting for served asset URLs.
|
|
203
224
|
*
|
|
204
225
|
* When omitted, all served assets use stable non-fingerprinted URLs with `Cache-Control: no-cache`.
|
|
205
226
|
* Cannot be used together with active watch mode. Set `watch: false` when fingerprinting.
|
|
206
227
|
*/
|
|
207
|
-
fingerprint?:
|
|
228
|
+
fingerprint?: boolean
|
|
208
229
|
/**
|
|
209
230
|
* Shared compatibility target for scripts and styles. Browser targets apply to both
|
|
210
231
|
* pipelines, and `es` only affects scripts.
|
|
@@ -242,7 +263,7 @@ export interface AssetServerOptions<transforms extends AssetRequestTransformMap
|
|
|
242
263
|
* HMR requires `watch` to be enabled. The factory is called once for this asset server. Returning
|
|
243
264
|
* `undefined` leaves HMR inactive; a returned channel is closed by `assetServer.close()`.
|
|
244
265
|
*/
|
|
245
|
-
hmr?: BrowserHmrChannelFactory
|
|
266
|
+
hmr?: BrowserHmrChannelFactory | BrowserHmrOptions
|
|
246
267
|
/**
|
|
247
268
|
* Enable filesystem-backed cache invalidation for long-lived server instances.
|
|
248
269
|
* Enabled by default. Pass `true` to use the default watcher options, an options
|
|
@@ -271,6 +292,18 @@ export type AssetServerGetHrefOptions<transforms extends AssetRequestTransformMa
|
|
|
271
292
|
transform: readonly AssetTransformInvocation<transforms>[]
|
|
272
293
|
}
|
|
273
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Metadata needed to render or load a script entry module.
|
|
297
|
+
*/
|
|
298
|
+
export interface ScriptEntry {
|
|
299
|
+
/** Public URL for the script entry module. */
|
|
300
|
+
href: string
|
|
301
|
+
/** Public URLs that should be emitted as `modulepreload` hints for this script graph. */
|
|
302
|
+
preloads: string[]
|
|
303
|
+
/** Import map entries required to resolve this script graph in the browser. */
|
|
304
|
+
importMap: ScriptImportMap
|
|
305
|
+
}
|
|
306
|
+
|
|
274
307
|
/**
|
|
275
308
|
* Serves compiled scripts and styles for asset requests routed to it.
|
|
276
309
|
* Construct with {@link createAssetServer}.
|
|
@@ -285,10 +318,28 @@ export interface AssetServer<transforms extends AssetRequestTransformMap = {}> {
|
|
|
285
318
|
* Returns the request href for a served asset file.
|
|
286
319
|
*/
|
|
287
320
|
getHref(filePath: string, options?: AssetServerGetHrefOptions<transforms>): Promise<string>
|
|
321
|
+
/**
|
|
322
|
+
* Returns the href, preload URLs, and import map for a script entry module.
|
|
323
|
+
*/
|
|
324
|
+
getScriptEntry(filePath: string): Promise<ScriptEntry>
|
|
288
325
|
/**
|
|
289
326
|
* Returns preload URLs for one or more served asset files, ordered shallowest-first.
|
|
290
327
|
*/
|
|
291
328
|
getPreloads(filePath: string | readonly string[]): Promise<string[]>
|
|
329
|
+
/**
|
|
330
|
+
* Returns an import map for one or more script entry modules.
|
|
331
|
+
*/
|
|
332
|
+
getImportMap(filePath: string | readonly string[]): Promise<ScriptImportMap>
|
|
333
|
+
/**
|
|
334
|
+
* Returns diagnostic details about one public asset URL or file path, including the matched mount
|
|
335
|
+
* roots, access rules, file type, and browser-reachability status.
|
|
336
|
+
*/
|
|
337
|
+
getAssetDetails(input: string): Promise<AssetDetails>
|
|
338
|
+
/**
|
|
339
|
+
* Returns every file currently reachable through this asset server, sorted by public URL and
|
|
340
|
+
* then absolute file path.
|
|
341
|
+
*/
|
|
342
|
+
getAssets(): Promise<AssetDetails[]>
|
|
292
343
|
/**
|
|
293
344
|
* Closes this server's filesystem watcher and browser HMR channel.
|
|
294
345
|
*
|
|
@@ -301,17 +352,18 @@ type ResolvedAssetServerOptions<transforms extends AssetRequestTransformMap> = {
|
|
|
301
352
|
allowFiles: readonly string[]
|
|
302
353
|
allowPackages?: readonly string[]
|
|
303
354
|
basePath: string
|
|
304
|
-
buildId?: string
|
|
305
355
|
define?: Record<string, string>
|
|
306
356
|
denyFiles?: readonly string[]
|
|
307
357
|
external: string[]
|
|
308
358
|
files: ResolvedAssetServerFilesOptions
|
|
309
359
|
fingerprintAssets: boolean
|
|
310
360
|
hmr: BrowserHmrChannelFactory | null
|
|
361
|
+
hmrModuleImporter: string | null
|
|
311
362
|
minify: boolean
|
|
312
363
|
loaders: readonly ModuleLoader[]
|
|
313
364
|
onError: NonNullable<AssetServerOptions['onError']>
|
|
314
365
|
rootDir: string
|
|
366
|
+
mounts: Readonly<Record<string, string>>
|
|
315
367
|
routes: CompiledRoutes
|
|
316
368
|
sourceMapSourcePaths: 'url' | 'absolute'
|
|
317
369
|
sourceMaps?: 'inline' | 'external'
|
|
@@ -339,7 +391,7 @@ export function getInternalWatchTargets<transforms extends AssetRequestTransform
|
|
|
339
391
|
* Create an asset server instance
|
|
340
392
|
*
|
|
341
393
|
* Compiles TypeScript/JavaScript scripts and CSS styles on demand with optional
|
|
342
|
-
*
|
|
394
|
+
* content-based URL fingerprinting, caching, and configurable directory mounts.
|
|
343
395
|
*
|
|
344
396
|
* @param options Server configuration
|
|
345
397
|
* @returns A {@link AssetServer} with `fetch()`, `getHref()`, and `getPreloads()` methods
|
|
@@ -348,9 +400,6 @@ export function getInternalWatchTargets<transforms extends AssetRequestTransform
|
|
|
348
400
|
* ```ts
|
|
349
401
|
* let assetServer = createAssetServer({
|
|
350
402
|
* basePath: '/assets',
|
|
351
|
-
* fileMap: {
|
|
352
|
-
* '/app/*path': 'app/*path',
|
|
353
|
-
* },
|
|
354
403
|
* allowFiles: ['app/routes.ts', 'app/**\/public/**'],
|
|
355
404
|
* allowPackages: ['remix'],
|
|
356
405
|
* denyFiles: ['app/**\/*.test.*'],
|
|
@@ -368,10 +417,17 @@ export function createAssetServer<const transforms extends AssetRequestTransform
|
|
|
368
417
|
allowPackages: resolvedOptions.allowPackages,
|
|
369
418
|
denyFiles: resolvedOptions.denyFiles,
|
|
370
419
|
packageSearchRoots: hasPackages(resolvedOptions.allowPackages)
|
|
371
|
-
? getPackageSearchRoots(
|
|
420
|
+
? getPackageSearchRoots(resolvedOptions.mounts, resolvedOptions.rootDir)
|
|
372
421
|
: undefined,
|
|
373
422
|
rootDir: resolvedOptions.rootDir,
|
|
374
423
|
})
|
|
424
|
+
let assetInspector = createAssetInspector({
|
|
425
|
+
accessPolicy,
|
|
426
|
+
allowFiles: resolvedOptions.allowFiles,
|
|
427
|
+
fileExtensions: resolvedOptions.files.extensions,
|
|
428
|
+
rootDir: resolvedOptions.rootDir,
|
|
429
|
+
routes: resolvedOptions.routes,
|
|
430
|
+
})
|
|
375
431
|
let watcher: AssetServerWatcher | null = null
|
|
376
432
|
let chokidarWatcher: ChokidarWatcher | null = null
|
|
377
433
|
let fileCompiler: FileCompiler | null = null
|
|
@@ -392,8 +448,8 @@ export function createAssetServer<const transforms extends AssetRequestTransform
|
|
|
392
448
|
})
|
|
393
449
|
let sendHmrPayload = resolvedOptions.hmr ? createHmrPayloadSender() : null
|
|
394
450
|
let hmrPathnames = getHmrPathnames(resolvedOptions.basePath)
|
|
451
|
+
let hmrDataKey = `remix/assets@1:${resolvedOptions.basePath}`
|
|
395
452
|
let scriptCompiler = createScriptCompiler({
|
|
396
|
-
buildId: resolvedOptions.buildId,
|
|
397
453
|
define: resolvedOptions.define,
|
|
398
454
|
external: resolvedOptions.external,
|
|
399
455
|
fingerprintAssets: resolvedOptions.fingerprintAssets,
|
|
@@ -425,7 +481,6 @@ export function createAssetServer<const transforms extends AssetRequestTransform
|
|
|
425
481
|
watchMode: resolvedOptions.watchOptions !== null,
|
|
426
482
|
})
|
|
427
483
|
let styleCompiler = createStyleCompiler({
|
|
428
|
-
buildId: resolvedOptions.buildId,
|
|
429
484
|
fingerprintAssets: resolvedOptions.fingerprintAssets,
|
|
430
485
|
getServedFileUrl: (identityPath: string, options: { transform: readonly string[] | null }) => {
|
|
431
486
|
if (!fileCompiler) {
|
|
@@ -477,8 +532,8 @@ export function createAssetServer<const transforms extends AssetRequestTransform
|
|
|
477
532
|
})
|
|
478
533
|
if (resolvedOptions.files.extensions.length > 0) {
|
|
479
534
|
fileCompiler = createFileCompiler({
|
|
480
|
-
buildId: resolvedOptions.buildId,
|
|
481
535
|
cache: resolvedOptions.files.cache,
|
|
536
|
+
cacheKey: resolvedOptions.files.cacheKey,
|
|
482
537
|
extensions: resolvedOptions.files.extensions,
|
|
483
538
|
fingerprintAssets: resolvedOptions.fingerprintAssets,
|
|
484
539
|
globalTransforms: resolvedOptions.files.globalTransforms,
|
|
@@ -532,10 +587,10 @@ export function createAssetServer<const transforms extends AssetRequestTransform
|
|
|
532
587
|
for (let event of events) {
|
|
533
588
|
let normalizedFilePath = normalizeFilePath(event.filePath)
|
|
534
589
|
let scriptUpdates = await scriptCompiler.classifyHmrFileEvent(normalizedFilePath, event.event)
|
|
535
|
-
let scriptPayload =
|
|
590
|
+
let scriptPayload = await createScriptHmrPayloadWithImportMaps(scriptUpdates)
|
|
536
591
|
if (scriptPayload) {
|
|
537
592
|
browserHmrEvents.push(
|
|
538
|
-
createBrowserHmrEvent(scriptPayload, getScriptHmrUpdateFiles(scriptUpdates)),
|
|
593
|
+
createBrowserHmrEvent(scriptPayload, getScriptHmrUpdateFiles(scriptUpdates), hmrDataKey),
|
|
539
594
|
)
|
|
540
595
|
}
|
|
541
596
|
|
|
@@ -554,6 +609,7 @@ export function createAssetServer<const transforms extends AssetRequestTransform
|
|
|
554
609
|
],
|
|
555
610
|
},
|
|
556
611
|
[styleUpdate.filePath],
|
|
612
|
+
hmrDataKey,
|
|
557
613
|
),
|
|
558
614
|
)
|
|
559
615
|
}
|
|
@@ -562,6 +618,32 @@ export function createAssetServer<const transforms extends AssetRequestTransform
|
|
|
562
618
|
}
|
|
563
619
|
|
|
564
620
|
return browserHmrEvents
|
|
621
|
+
|
|
622
|
+
async function createScriptHmrPayloadWithImportMaps(
|
|
623
|
+
updates: ScriptHmrUpdate[],
|
|
624
|
+
): Promise<Extract<HmrPayload, { type: 'browser:reload' | 'browser:update' }> | null> {
|
|
625
|
+
let payload = createScriptHmrPayload(updates)
|
|
626
|
+
if (!payload || payload.type === 'browser:reload') return payload
|
|
627
|
+
|
|
628
|
+
let acceptedUpdates = updates.filter((update) => update.accepted)
|
|
629
|
+
let importMaps = await Promise.all(
|
|
630
|
+
acceptedUpdates.map(async (update) => {
|
|
631
|
+
try {
|
|
632
|
+
return await scriptCompiler.getImportMap(update.acceptedFilePath)
|
|
633
|
+
} catch (error) {
|
|
634
|
+
if (!isAssetServerCompilationError(error)) throw error
|
|
635
|
+
return undefined
|
|
636
|
+
}
|
|
637
|
+
}),
|
|
638
|
+
)
|
|
639
|
+
return {
|
|
640
|
+
...payload,
|
|
641
|
+
updates: payload.updates.map((update, index) => ({
|
|
642
|
+
...update,
|
|
643
|
+
...(importMaps[index] ? { importMap: importMaps[index] } : null),
|
|
644
|
+
})),
|
|
645
|
+
}
|
|
646
|
+
}
|
|
565
647
|
}
|
|
566
648
|
|
|
567
649
|
function updateBrowserHmrWatchedFiles(delta: BrowserHmrWatchedFileDelta): void {
|
|
@@ -575,6 +657,12 @@ export function createAssetServer<const transforms extends AssetRequestTransform
|
|
|
575
657
|
}
|
|
576
658
|
|
|
577
659
|
let assetServer: AssetServer<transforms> = {
|
|
660
|
+
getAssetDetails(input) {
|
|
661
|
+
return assetInspector.getAssetDetails(input)
|
|
662
|
+
},
|
|
663
|
+
getAssets() {
|
|
664
|
+
return assetInspector.getAssets()
|
|
665
|
+
},
|
|
578
666
|
async fetch(request) {
|
|
579
667
|
if (request.method !== 'GET' && request.method !== 'HEAD') return null
|
|
580
668
|
let requestPathname = new URL(request.url).pathname
|
|
@@ -583,7 +671,13 @@ export function createAssetServer<const transforms extends AssetRequestTransform
|
|
|
583
671
|
if (requestPathname === hmrPathnames.client) {
|
|
584
672
|
let browserHmrChannel = await browserHmrChannelPromise
|
|
585
673
|
assertBrowserEventUrl(browserHmrChannel?.url)
|
|
586
|
-
return createHmrClientResponse(
|
|
674
|
+
return await createHmrClientResponse(
|
|
675
|
+
browserHmrChannel.url,
|
|
676
|
+
hmrDataKey,
|
|
677
|
+
resolvedOptions.hmrModuleImporter,
|
|
678
|
+
request.method,
|
|
679
|
+
scriptCompiler,
|
|
680
|
+
)
|
|
587
681
|
}
|
|
588
682
|
}
|
|
589
683
|
|
|
@@ -619,8 +713,10 @@ export function createAssetServer<const transforms extends AssetRequestTransform
|
|
|
619
713
|
let compiledStyle = styleResult.style
|
|
620
714
|
|
|
621
715
|
if (parsedRequestPathname.requestedFingerprint !== null) {
|
|
622
|
-
|
|
623
|
-
|
|
716
|
+
let fingerprint = parsedRequestPathname.isSourceMapRequest
|
|
717
|
+
? compiledStyle.sourceMap?.fingerprint
|
|
718
|
+
: compiledStyle.fingerprint
|
|
719
|
+
if (fingerprint !== parsedRequestPathname.requestedFingerprint) return null
|
|
624
720
|
}
|
|
625
721
|
|
|
626
722
|
return createResponseForStyle(compiledStyle, {
|
|
@@ -684,7 +780,10 @@ export function createAssetServer<const transforms extends AssetRequestTransform
|
|
|
684
780
|
let compiledScript = scriptResult.script
|
|
685
781
|
|
|
686
782
|
if (parsedRequestPathname.requestedFingerprint !== null) {
|
|
687
|
-
|
|
783
|
+
let fingerprint = parsedRequestPathname.isSourceMapRequest
|
|
784
|
+
? compiledScript.sourceMap?.fingerprint
|
|
785
|
+
: compiledScript.fingerprint
|
|
786
|
+
if (fingerprint !== parsedRequestPathname.requestedFingerprint) return null
|
|
688
787
|
}
|
|
689
788
|
|
|
690
789
|
return createResponseForScript(compiledScript, {
|
|
@@ -747,6 +846,15 @@ export function createAssetServer<const transforms extends AssetRequestTransform
|
|
|
747
846
|
|
|
748
847
|
return scriptCompiler.getHref(filePath)
|
|
749
848
|
},
|
|
849
|
+
async getScriptEntry(filePath) {
|
|
850
|
+
let [href, preloads, importMap] = await Promise.all([
|
|
851
|
+
assetServer.getHref(filePath),
|
|
852
|
+
assetServer.getPreloads(filePath),
|
|
853
|
+
assetServer.getImportMap(filePath),
|
|
854
|
+
])
|
|
855
|
+
|
|
856
|
+
return { href, preloads, importMap }
|
|
857
|
+
},
|
|
750
858
|
async getPreloads(filePath) {
|
|
751
859
|
let filePaths = Array.isArray(filePath) ? filePath : [filePath]
|
|
752
860
|
let fileAssetFiles: string[] = []
|
|
@@ -811,6 +919,19 @@ export function createAssetServer<const transforms extends AssetRequestTransform
|
|
|
811
919
|
|
|
812
920
|
return mergePreloadLayers(await Promise.all(preloadLayerGroupPromises))
|
|
813
921
|
},
|
|
922
|
+
async getImportMap(filePath) {
|
|
923
|
+
let filePaths = Array.isArray(filePath) ? filePath : [filePath]
|
|
924
|
+
for (let nextFilePath of filePaths) {
|
|
925
|
+
let typeCheckFilePath = stripFilePathUrlSuffix(nextFilePath)
|
|
926
|
+
if (!isScriptFilePath(typeCheckFilePath)) {
|
|
927
|
+
throw new TypeError(
|
|
928
|
+
`assetServer.getImportMap() only supports script files: ${nextFilePath}`,
|
|
929
|
+
)
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
return scriptCompiler.getImportMap(filePath)
|
|
934
|
+
},
|
|
814
935
|
async close() {
|
|
815
936
|
if (closed) return
|
|
816
937
|
closed = true
|
|
@@ -876,13 +997,27 @@ function getHmrPathnames(basePath: string): { client: string; events: string } {
|
|
|
876
997
|
}
|
|
877
998
|
}
|
|
878
999
|
|
|
879
|
-
function createHmrClientResponse(
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
1000
|
+
async function createHmrClientResponse(
|
|
1001
|
+
eventPathname: string,
|
|
1002
|
+
dataKey: string,
|
|
1003
|
+
moduleImporter: string | null,
|
|
1004
|
+
method: string,
|
|
1005
|
+
scriptCompiler: ReturnType<typeof createScriptCompiler>,
|
|
1006
|
+
): Promise<Response> {
|
|
1007
|
+
let moduleImporterHref = moduleImporter
|
|
1008
|
+
? await scriptCompiler.resolveSpecifierFromRoot(moduleImporter)
|
|
1009
|
+
: null
|
|
1010
|
+
return new Response(
|
|
1011
|
+
method === 'HEAD'
|
|
1012
|
+
? null
|
|
1013
|
+
: createHmrClientSource({ dataKey, eventPathname, moduleImporter: moduleImporterHref }),
|
|
1014
|
+
{
|
|
1015
|
+
headers: {
|
|
1016
|
+
'Cache-Control': 'no-cache',
|
|
1017
|
+
'Content-Type': 'application/javascript; charset=utf-8',
|
|
1018
|
+
},
|
|
884
1019
|
},
|
|
885
|
-
|
|
1020
|
+
)
|
|
886
1021
|
}
|
|
887
1022
|
|
|
888
1023
|
function assertBrowserEventUrl(url: string | undefined): asserts url is string {
|
|
@@ -898,6 +1033,7 @@ function createHmrPayloadSender(): (payload: HmrPayload) => void {
|
|
|
898
1033
|
function createBrowserHmrEvent(
|
|
899
1034
|
payload: Extract<HmrPayload, { type: 'browser:reload' | 'browser:update' }>,
|
|
900
1035
|
files: readonly string[],
|
|
1036
|
+
dataKey: string,
|
|
901
1037
|
): BrowserHmrEvent {
|
|
902
1038
|
if (payload.type === 'browser:reload') {
|
|
903
1039
|
return {
|
|
@@ -907,10 +1043,14 @@ function createBrowserHmrEvent(
|
|
|
907
1043
|
}
|
|
908
1044
|
|
|
909
1045
|
return {
|
|
1046
|
+
data: {
|
|
1047
|
+
[dataKey]: {
|
|
1048
|
+
timestamp: payload.timestamp,
|
|
1049
|
+
updates: payload.updates,
|
|
1050
|
+
},
|
|
1051
|
+
},
|
|
910
1052
|
...(files.length === 0 ? {} : { files: [...files] }),
|
|
911
|
-
timestamp: payload.timestamp,
|
|
912
1053
|
type: 'update',
|
|
913
|
-
updates: payload.updates,
|
|
914
1054
|
}
|
|
915
1055
|
}
|
|
916
1056
|
|
|
@@ -936,7 +1076,9 @@ export function createScriptHmrPayload(
|
|
|
936
1076
|
timestamp,
|
|
937
1077
|
type: 'browser:update',
|
|
938
1078
|
updates: acceptedUpdates.map((update) => ({
|
|
939
|
-
...(update.
|
|
1079
|
+
...(update.acceptedUrlPathname === update.path
|
|
1080
|
+
? {}
|
|
1081
|
+
: { acceptedPath: update.acceptedUrlPathname }),
|
|
940
1082
|
path: update.path,
|
|
941
1083
|
type: 'js',
|
|
942
1084
|
})),
|
|
@@ -1000,37 +1142,43 @@ function resolveAssetServerOptions<transforms extends AssetRequestTransformMap>(
|
|
|
1000
1142
|
let rootDir = normalizeFilePath(fs.realpathSync(path.resolve(options.rootDir ?? process.cwd())))
|
|
1001
1143
|
let basePath = normalizeBasePath(options.basePath)
|
|
1002
1144
|
let scriptOptions = options.scripts ?? {}
|
|
1003
|
-
let
|
|
1145
|
+
let fingerprintAssets = normalizeFingerprintOptions({
|
|
1004
1146
|
fingerprint: options.fingerprint,
|
|
1005
1147
|
watch: options.watch,
|
|
1006
1148
|
})
|
|
1007
1149
|
let watchOptions = normalizeWatchOptions(options.watch)
|
|
1008
|
-
let
|
|
1150
|
+
let hmr = normalizeHmrOptions(options.hmr)
|
|
1151
|
+
let mounts = options.mounts ?? defaultMounts
|
|
1009
1152
|
|
|
1010
|
-
if (
|
|
1153
|
+
if (hmr.channel && watchOptions === null) {
|
|
1011
1154
|
throw new TypeError('hmr requires watch mode')
|
|
1012
1155
|
}
|
|
1156
|
+
if (Object.keys(mounts).length === 0) {
|
|
1157
|
+
throw new TypeError('mounts must include at least one entry')
|
|
1158
|
+
}
|
|
1013
1159
|
return {
|
|
1014
1160
|
allowFiles: options.allowFiles,
|
|
1015
1161
|
allowPackages: options.allowPackages,
|
|
1016
1162
|
basePath,
|
|
1017
|
-
buildId: fingerprintOptions.buildId,
|
|
1018
1163
|
define: scriptOptions.define,
|
|
1019
1164
|
denyFiles: options.denyFiles,
|
|
1020
1165
|
external: scriptOptions.external ?? [],
|
|
1021
1166
|
files: normalizeFilesOptions(options.files),
|
|
1022
|
-
fingerprintAssets
|
|
1023
|
-
hmr:
|
|
1167
|
+
fingerprintAssets,
|
|
1168
|
+
hmr: hmr.channel,
|
|
1169
|
+
hmrModuleImporter: hmr.moduleImporter,
|
|
1024
1170
|
minify: options.minify ?? false,
|
|
1171
|
+
mounts,
|
|
1025
1172
|
loaders: scriptOptions.loaders ?? [],
|
|
1026
1173
|
onError: options.onError ?? defaultErrorHandler,
|
|
1027
1174
|
rootDir,
|
|
1028
1175
|
routes: compileRoutes(basePath, [
|
|
1029
1176
|
{
|
|
1030
|
-
|
|
1177
|
+
mounts,
|
|
1031
1178
|
rootDir,
|
|
1032
1179
|
},
|
|
1033
|
-
...
|
|
1180
|
+
...getInjectedPackageMountConfigs(),
|
|
1181
|
+
...getVirtualStoreMountConfigs({ mounts, rootDir }),
|
|
1034
1182
|
]),
|
|
1035
1183
|
sourceMapSourcePaths: options.sourceMapSourcePaths ?? 'url',
|
|
1036
1184
|
sourceMaps: options.sourceMaps,
|
|
@@ -1040,14 +1188,22 @@ function resolveAssetServerOptions<transforms extends AssetRequestTransformMap>(
|
|
|
1040
1188
|
}
|
|
1041
1189
|
}
|
|
1042
1190
|
|
|
1043
|
-
function
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1191
|
+
function normalizeHmrOptions(options: AssetServerOptions['hmr']): {
|
|
1192
|
+
channel: BrowserHmrChannelFactory | null
|
|
1193
|
+
moduleImporter: string | null
|
|
1194
|
+
} {
|
|
1195
|
+
if (options === undefined) return { channel: null, moduleImporter: null }
|
|
1196
|
+
if (typeof options === 'function') return { channel: options, moduleImporter: null }
|
|
1197
|
+
if (!options || typeof options !== 'object' || typeof options.channel !== 'function') {
|
|
1198
|
+
throw new TypeError('hmr must be a function or an object with a channel function')
|
|
1048
1199
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1200
|
+
if (
|
|
1201
|
+
options.moduleImporter !== undefined &&
|
|
1202
|
+
(typeof options.moduleImporter !== 'string' || options.moduleImporter.trim().length === 0)
|
|
1203
|
+
) {
|
|
1204
|
+
throw new TypeError('hmr.moduleImporter must be a non-empty string')
|
|
1205
|
+
}
|
|
1206
|
+
return { channel: options.channel, moduleImporter: options.moduleImporter ?? null }
|
|
1051
1207
|
}
|
|
1052
1208
|
|
|
1053
1209
|
function createBrowserHmrChannel(
|
|
@@ -1108,37 +1264,20 @@ function normalizeBasePath(basePath: string): string {
|
|
|
1108
1264
|
function normalizeFingerprintOptions(options: {
|
|
1109
1265
|
fingerprint: AssetServerOptions['fingerprint']
|
|
1110
1266
|
watch: AssetServerOptions['watch']
|
|
1111
|
-
}):
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
buildId?: string
|
|
1115
|
-
}
|
|
1116
|
-
| {
|
|
1117
|
-
enabled: true
|
|
1118
|
-
buildId: string
|
|
1119
|
-
} {
|
|
1120
|
-
if (!options.fingerprint) {
|
|
1121
|
-
return {
|
|
1122
|
-
enabled: false,
|
|
1123
|
-
}
|
|
1267
|
+
}): boolean {
|
|
1268
|
+
if (options.fingerprint !== undefined && typeof options.fingerprint !== 'boolean') {
|
|
1269
|
+
throw new TypeError('fingerprint must be a boolean')
|
|
1124
1270
|
}
|
|
1125
1271
|
|
|
1126
|
-
if (
|
|
1127
|
-
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
|
-
if (options.fingerprint.buildId.length === 0) {
|
|
1131
|
-
throw new TypeError('fingerprint.buildId must be a non-empty string')
|
|
1272
|
+
if (!options.fingerprint) {
|
|
1273
|
+
return false
|
|
1132
1274
|
}
|
|
1133
1275
|
|
|
1134
1276
|
if (options.watch !== false) {
|
|
1135
1277
|
throw new TypeError('fingerprint cannot be used with watch mode')
|
|
1136
1278
|
}
|
|
1137
1279
|
|
|
1138
|
-
return
|
|
1139
|
-
enabled: true,
|
|
1140
|
-
buildId: options.fingerprint.buildId,
|
|
1141
|
-
}
|
|
1280
|
+
return true
|
|
1142
1281
|
}
|
|
1143
1282
|
|
|
1144
1283
|
function normalizeWatchOptions(
|
|
@@ -1154,19 +1293,10 @@ function hasPackages(packages: readonly string[] | undefined): boolean {
|
|
|
1154
1293
|
}
|
|
1155
1294
|
|
|
1156
1295
|
function getPackageSearchRoots(
|
|
1157
|
-
|
|
1296
|
+
mounts: Readonly<Record<string, string>>,
|
|
1158
1297
|
rootDir: string,
|
|
1159
1298
|
): readonly string[] {
|
|
1160
|
-
return Object.values(
|
|
1161
|
-
path.resolve(rootDir, getStaticFilePatternPrefix(filePattern)),
|
|
1162
|
-
)
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
function getStaticFilePatternPrefix(filePattern: string): string {
|
|
1166
|
-
let firstDynamicIndex = filePattern.search(/[*:]/)
|
|
1167
|
-
let staticPrefix =
|
|
1168
|
-
firstDynamicIndex === -1 ? filePattern : filePattern.slice(0, firstDynamicIndex)
|
|
1169
|
-
return staticPrefix.replace(/[/\\]*$/, '')
|
|
1299
|
+
return Object.values(mounts).map((fileRoot) => path.resolve(rootDir, fileRoot))
|
|
1170
1300
|
}
|
|
1171
1301
|
|
|
1172
1302
|
function parseAssetRequestPathname(
|
|
@@ -2,7 +2,7 @@ type AssetServerCompilationErrorCode =
|
|
|
2
2
|
| 'FILE_NOT_FOUND'
|
|
3
3
|
| 'FILE_NOT_ALLOWED'
|
|
4
4
|
| 'FILE_NOT_SUPPORTED'
|
|
5
|
-
| '
|
|
5
|
+
| 'FILE_OUTSIDE_MOUNTS'
|
|
6
6
|
| 'FILE_TRANSFORM_QUERY_INVALID'
|
|
7
7
|
| 'FILE_TRANSFORM_NOT_SUPPORTED'
|
|
8
8
|
| 'FILE_TRANSFORM_RESULT_INVALID'
|
|
@@ -14,11 +14,12 @@ type AssetServerCompilationErrorCode =
|
|
|
14
14
|
| 'IMPORT_RESOLUTION_FAILED'
|
|
15
15
|
| 'IMPORT_NOT_SUPPORTED'
|
|
16
16
|
| 'IMPORT_NOT_ALLOWED'
|
|
17
|
-
| '
|
|
17
|
+
| 'IMPORT_OUTSIDE_MOUNTS'
|
|
18
|
+
| 'IMPORT_RESOLUTION_NOT_DIRECTORY_UNIFORM'
|
|
18
19
|
| 'URL_RESOLUTION_FAILED'
|
|
19
20
|
| 'URL_NOT_SUPPORTED'
|
|
20
21
|
| 'URL_NOT_ALLOWED'
|
|
21
|
-
| '
|
|
22
|
+
| 'URL_OUTSIDE_MOUNTS'
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Internal error used by the request-time asset compilation pipeline.
|