@remix-run/assets 0.1.0 → 0.2.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 +53 -47
- package/dist/lib/asset-server.d.ts +45 -43
- package/dist/lib/asset-server.d.ts.map +1 -1
- package/dist/lib/asset-server.js +142 -46
- package/dist/lib/compilation-error.d.ts +2 -2
- package/dist/lib/compilation-error.d.ts.map +1 -1
- package/dist/lib/compilation-error.js +1 -1
- package/dist/lib/file-matcher.d.ts.map +1 -1
- package/dist/lib/file-matcher.js +3 -2
- package/dist/lib/module-store.d.ts +41 -0
- package/dist/lib/module-store.d.ts.map +1 -0
- package/dist/lib/{scripts/store.js → module-store.js} +43 -41
- package/dist/lib/scripts/compiler.d.ts +15 -15
- package/dist/lib/scripts/compiler.d.ts.map +1 -1
- package/dist/lib/scripts/compiler.js +50 -46
- package/dist/lib/scripts/emit.js +2 -2
- package/dist/lib/scripts/resolve.d.ts +7 -17
- package/dist/lib/scripts/resolve.d.ts.map +1 -1
- package/dist/lib/scripts/resolve.js +15 -9
- package/dist/lib/scripts/transform.d.ts +11 -9
- package/dist/lib/scripts/transform.d.ts.map +1 -1
- package/dist/lib/scripts/transform.js +32 -21
- package/dist/lib/source-maps.d.ts +1 -1
- package/dist/lib/source-maps.d.ts.map +1 -1
- package/dist/lib/source-maps.js +7 -1
- package/dist/lib/styles/compiler.d.ts +52 -0
- package/dist/lib/styles/compiler.d.ts.map +1 -0
- package/dist/lib/styles/compiler.js +272 -0
- package/dist/lib/styles/emit.d.ts +25 -0
- package/dist/lib/styles/emit.d.ts.map +1 -0
- package/dist/lib/styles/emit.js +78 -0
- package/dist/lib/styles/resolve.d.ts +48 -0
- package/dist/lib/styles/resolve.d.ts.map +1 -0
- package/dist/lib/styles/resolve.js +188 -0
- package/dist/lib/styles/transform.d.ts +47 -0
- package/dist/lib/styles/transform.d.ts.map +1 -0
- package/dist/lib/styles/transform.js +131 -0
- package/dist/lib/target.d.ts +21 -0
- package/dist/lib/target.d.ts.map +1 -0
- package/dist/lib/target.js +127 -0
- package/package.json +7 -2
- package/src/lib/asset-server.ts +218 -96
- package/src/lib/compilation-error.ts +7 -7
- package/src/lib/file-matcher.ts +3 -2
- package/src/lib/{scripts/store.ts → module-store.ts} +100 -87
- package/src/lib/scripts/compiler.ts +88 -70
- package/src/lib/scripts/emit.ts +2 -2
- package/src/lib/scripts/resolve.ts +34 -23
- package/src/lib/scripts/transform.ts +49 -34
- package/src/lib/source-maps.ts +8 -1
- package/src/lib/styles/compiler.ts +400 -0
- package/src/lib/styles/emit.ts +137 -0
- package/src/lib/styles/resolve.ts +316 -0
- package/src/lib/styles/transform.ts +226 -0
- package/src/lib/target.ts +196 -0
- package/dist/lib/scripts/store.d.ts +0 -40
- package/dist/lib/scripts/store.d.ts.map +0 -1
package/src/lib/asset-server.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import * as path from 'node:path'
|
|
2
2
|
import * as fs from 'node:fs'
|
|
3
|
-
import { isAssetServerCompilationError } from './compilation-error.ts'
|
|
4
3
|
import { createAccessPolicy } from './access.ts'
|
|
5
|
-
import {
|
|
4
|
+
import { isAssetServerCompilationError } from './compilation-error.ts'
|
|
5
|
+
import { getFingerprintRequestCacheControl, parseFingerprintSuffix } from './fingerprint.ts'
|
|
6
6
|
import { normalizeFilePath } from './paths.ts'
|
|
7
7
|
import { compileRoutes } from './routes.ts'
|
|
8
8
|
import type { CompiledRoutes } from './routes.ts'
|
|
9
|
+
import { createResponseForScript, createScriptCompiler } from './scripts/compiler.ts'
|
|
10
|
+
import { supportedScriptExtensions } from './scripts/resolve.ts'
|
|
11
|
+
import { createResponseForStyle, createStyleCompiler, isStyleFilePath } from './styles/compiler.ts'
|
|
12
|
+
import { resolveScriptTarget, resolveStyleTarget } from './target.ts'
|
|
13
|
+
import type { AssetTarget, ResolvedScriptTarget, ResolvedStyleTarget } from './target.ts'
|
|
9
14
|
import { createAssetServerWatcher } from './watch.ts'
|
|
10
|
-
import type { ChokidarWatcher } from './watch.ts'
|
|
11
|
-
import type { AssetServerWatcher } from './watch.ts'
|
|
15
|
+
import type { AssetServerWatcher, ChokidarWatcher } from './watch.ts'
|
|
12
16
|
|
|
13
17
|
interface AssetServerWatchOptions {
|
|
14
18
|
/**
|
|
@@ -28,30 +32,26 @@ interface AssetServerWatchOptions {
|
|
|
28
32
|
|
|
29
33
|
interface FingerprintOptions {
|
|
30
34
|
/**
|
|
31
|
-
* Per-build invalidation token that must change whenever fingerprinted
|
|
35
|
+
* Per-build invalidation token that must change whenever fingerprinted asset URLs
|
|
32
36
|
* should be invalidated together.
|
|
33
37
|
*/
|
|
34
38
|
buildId: string
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
] as const
|
|
52
|
-
const scriptTargetSet = new Set<string>(scriptTargets)
|
|
53
|
-
|
|
54
|
-
export type ScriptsTarget = (typeof scriptTargets)[number]
|
|
41
|
+
type AssetSourceMaps = 'inline' | 'external'
|
|
42
|
+
type AssetSourceMapSourcePaths = 'url' | 'absolute'
|
|
43
|
+
|
|
44
|
+
interface AssetServerScriptOptions {
|
|
45
|
+
/**
|
|
46
|
+
* Replace global expressions with constant values during transform, e.g.
|
|
47
|
+
* `{ 'process.env.NODE_ENV': '"production"' }`
|
|
48
|
+
*/
|
|
49
|
+
define?: Record<string, string>
|
|
50
|
+
/** Import specifiers to leave unrewritten (CDN URLs, import map entries, etc.) */
|
|
51
|
+
external?: string[]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const scriptExtensionSet = new Set<string>(supportedScriptExtensions)
|
|
55
55
|
|
|
56
56
|
export interface AssetServerOptions {
|
|
57
57
|
/** File patterns keyed by public URL patterns. */
|
|
@@ -69,45 +69,37 @@ export interface AssetServerOptions {
|
|
|
69
69
|
*/
|
|
70
70
|
deny?: readonly string[]
|
|
71
71
|
/**
|
|
72
|
-
* Controls optional source-based URL fingerprinting for rewritten
|
|
72
|
+
* Controls optional source-based URL fingerprinting for rewritten asset URLs.
|
|
73
73
|
*
|
|
74
|
-
* When omitted, all served
|
|
74
|
+
* When omitted, all served assets use stable non-fingerprinted URLs with `Cache-Control: no-cache`.
|
|
75
75
|
* Cannot be used together with active watch mode. Set `watch: false` when fingerprinting.
|
|
76
76
|
*/
|
|
77
77
|
fingerprint?: FingerprintOptions
|
|
78
78
|
/**
|
|
79
|
-
*
|
|
79
|
+
* Shared compatibility target for scripts and styles. Browser targets apply to both
|
|
80
|
+
* pipelines, and `es` only affects scripts.
|
|
80
81
|
*/
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
define?: Record<string, string>
|
|
103
|
-
/**
|
|
104
|
-
* Lower emitted syntax to a specific ECMAScript target. Omit this option to preserve
|
|
105
|
-
* modern syntax unless project configuration already requests a lower target.
|
|
106
|
-
*/
|
|
107
|
-
target?: ScriptsTarget
|
|
108
|
-
/** Import specifiers to leave unrewritten (CDN URLs, import map entries, etc.) */
|
|
109
|
-
external?: string[]
|
|
110
|
-
}
|
|
82
|
+
target?: AssetTarget
|
|
83
|
+
/**
|
|
84
|
+
* Source map mode for scripts and styles.
|
|
85
|
+
* - `'external'`: serve source maps as separate `.map` files
|
|
86
|
+
* - `'inline'`: embed source maps as a base64 data URL in the compiled asset
|
|
87
|
+
*/
|
|
88
|
+
sourceMaps?: AssetSourceMaps
|
|
89
|
+
/**
|
|
90
|
+
* Source path strategy for source map `sources`.
|
|
91
|
+
* - `'url'` (default): use the stable server path (e.g. `'/assets/app/entry.ts'`)
|
|
92
|
+
* - `'absolute'`: use the original filesystem path on disk
|
|
93
|
+
*/
|
|
94
|
+
sourceMapSourcePaths?: AssetSourceMapSourcePaths
|
|
95
|
+
/**
|
|
96
|
+
* Minification setting for emitted scripts and styles.
|
|
97
|
+
*/
|
|
98
|
+
minify?: boolean
|
|
99
|
+
/**
|
|
100
|
+
* Script-only configuration.
|
|
101
|
+
*/
|
|
102
|
+
scripts?: AssetServerScriptOptions
|
|
111
103
|
/**
|
|
112
104
|
* Enable filesystem-backed cache invalidation for long-lived server instances.
|
|
113
105
|
* Enabled by default. Pass `true` to use the default watcher options, an options
|
|
@@ -123,16 +115,16 @@ export interface AssetServerOptions {
|
|
|
123
115
|
|
|
124
116
|
export interface AssetServer {
|
|
125
117
|
/**
|
|
126
|
-
* Serves a script request. Returns `Response | null` — null means the request
|
|
127
|
-
* handled by this server, letting the router fall through to a 404.
|
|
118
|
+
* Serves a script or style request. Returns `Response | null` — null means the request
|
|
119
|
+
* was not handled by this server, letting the router fall through to a 404.
|
|
128
120
|
*/
|
|
129
121
|
fetch(request: Request): Promise<Response | null>
|
|
130
122
|
/**
|
|
131
|
-
* Returns the request href for a served
|
|
123
|
+
* Returns the request href for a served asset file.
|
|
132
124
|
*/
|
|
133
125
|
getHref(filePath: string): Promise<string>
|
|
134
126
|
/**
|
|
135
|
-
* Returns preload URLs for one or more served
|
|
127
|
+
* Returns preload URLs for one or more served asset files, ordered shallowest-first.
|
|
136
128
|
*/
|
|
137
129
|
getPreloads(filePath: string | readonly string[]): Promise<string[]>
|
|
138
130
|
/**
|
|
@@ -147,14 +139,15 @@ type ResolvedAssetServerOptions = {
|
|
|
147
139
|
define?: Record<string, string>
|
|
148
140
|
deny?: readonly string[]
|
|
149
141
|
external: string[]
|
|
150
|
-
|
|
142
|
+
fingerprintAssets: boolean
|
|
151
143
|
minify: boolean
|
|
152
144
|
onError: NonNullable<AssetServerOptions['onError']>
|
|
153
145
|
rootDir: string
|
|
154
146
|
routes: CompiledRoutes
|
|
155
147
|
sourceMapSourcePaths: 'url' | 'absolute'
|
|
156
148
|
sourceMaps?: 'inline' | 'external'
|
|
157
|
-
scriptsTarget?:
|
|
149
|
+
scriptsTarget?: ResolvedScriptTarget
|
|
150
|
+
stylesTarget?: ResolvedStyleTarget
|
|
158
151
|
watchOptions: AssetServerWatchOptions | null
|
|
159
152
|
}
|
|
160
153
|
|
|
@@ -172,8 +165,8 @@ export function getInternalWatchTargets(assetServer: AssetServer): readonly stri
|
|
|
172
165
|
/**
|
|
173
166
|
* Create an asset server instance
|
|
174
167
|
*
|
|
175
|
-
* Compiles TypeScript/JavaScript
|
|
176
|
-
* fingerprinting, caching, and configurable file mapping.
|
|
168
|
+
* Compiles TypeScript/JavaScript scripts and CSS styles on demand with optional
|
|
169
|
+
* source-based URL fingerprinting, caching, and configurable file mapping.
|
|
177
170
|
*
|
|
178
171
|
* @param options Server configuration
|
|
179
172
|
* @returns A {@link AssetServer} with `fetch()`, `getHref()`, and `getPreloads()` methods
|
|
@@ -199,11 +192,11 @@ export function createAssetServer(options: AssetServerOptions): AssetServer {
|
|
|
199
192
|
})
|
|
200
193
|
let watcher: AssetServerWatcher | null = null
|
|
201
194
|
let chokidarWatcher: ChokidarWatcher | null = null
|
|
202
|
-
let
|
|
195
|
+
let scriptCompiler = createScriptCompiler({
|
|
203
196
|
buildId: resolvedOptions.buildId,
|
|
204
197
|
define: resolvedOptions.define,
|
|
205
198
|
external: resolvedOptions.external,
|
|
206
|
-
|
|
199
|
+
fingerprintAssets: resolvedOptions.fingerprintAssets,
|
|
207
200
|
isAllowed: accessPolicy.isAllowed,
|
|
208
201
|
minify: resolvedOptions.minify,
|
|
209
202
|
onWatchDirectoriesChange: (delta) => {
|
|
@@ -218,6 +211,22 @@ export function createAssetServer(options: AssetServerOptions): AssetServer {
|
|
|
218
211
|
watchIgnore: resolvedOptions.watchOptions?.ignore,
|
|
219
212
|
watchMode: resolvedOptions.watchOptions !== null,
|
|
220
213
|
})
|
|
214
|
+
let styleCompiler = createStyleCompiler({
|
|
215
|
+
buildId: resolvedOptions.buildId,
|
|
216
|
+
fingerprintAssets: resolvedOptions.fingerprintAssets,
|
|
217
|
+
isAllowed: accessPolicy.isAllowed,
|
|
218
|
+
minify: resolvedOptions.minify,
|
|
219
|
+
onWatchDirectoriesChange: (delta) => {
|
|
220
|
+
if (!watcher) return
|
|
221
|
+
watcher.updateWatchedDirectories(delta)
|
|
222
|
+
},
|
|
223
|
+
rootDir: resolvedOptions.rootDir,
|
|
224
|
+
routes: resolvedOptions.routes,
|
|
225
|
+
sourceMapSourcePaths: resolvedOptions.sourceMapSourcePaths,
|
|
226
|
+
sourceMaps: resolvedOptions.sourceMaps,
|
|
227
|
+
targets: resolvedOptions.stylesTarget,
|
|
228
|
+
watchIgnore: resolvedOptions.watchOptions?.ignore,
|
|
229
|
+
})
|
|
221
230
|
if (resolvedOptions.watchOptions) {
|
|
222
231
|
watcher = createAssetServerWatcher({
|
|
223
232
|
...resolvedOptions.watchOptions,
|
|
@@ -241,7 +250,8 @@ export function createAssetServer(options: AssetServerOptions): AssetServer {
|
|
|
241
250
|
async function handleWatchEvent(filePath: string, event: 'add' | 'change' | 'unlink') {
|
|
242
251
|
try {
|
|
243
252
|
let normalizedFilePath = normalizeFilePath(filePath)
|
|
244
|
-
await
|
|
253
|
+
await scriptCompiler.handleFileEvent(normalizedFilePath, event)
|
|
254
|
+
await styleCompiler.handleFileEvent(normalizedFilePath, event)
|
|
245
255
|
} catch (error) {
|
|
246
256
|
console.error(`There was an error invalidating the asset server cache: ${error}`)
|
|
247
257
|
}
|
|
@@ -251,30 +261,66 @@ export function createAssetServer(options: AssetServerOptions): AssetServer {
|
|
|
251
261
|
async fetch(request) {
|
|
252
262
|
if (request.method !== 'GET' && request.method !== 'HEAD') return null
|
|
253
263
|
|
|
254
|
-
let parsedRequestPathname =
|
|
264
|
+
let parsedRequestPathname = parseAssetRequestPathname(new URL(request.url).pathname, {
|
|
265
|
+
fingerprintAssets: resolvedOptions.fingerprintAssets,
|
|
266
|
+
routes: resolvedOptions.routes,
|
|
267
|
+
})
|
|
255
268
|
if (!parsedRequestPathname) return null
|
|
256
269
|
|
|
257
270
|
try {
|
|
258
271
|
let ifNoneMatch = request.headers.get('If-None-Match')
|
|
259
|
-
|
|
272
|
+
|
|
273
|
+
if (isStyleFilePath(parsedRequestPathname.filePath)) {
|
|
274
|
+
let styleResult = await styleCompiler.getStyle(parsedRequestPathname.filePath, {
|
|
275
|
+
ifNoneMatch,
|
|
276
|
+
isSourceMapRequest: parsedRequestPathname.isSourceMapRequest,
|
|
277
|
+
requestedFingerprint: parsedRequestPathname.requestedFingerprint,
|
|
278
|
+
})
|
|
279
|
+
if (styleResult.type === 'not-modified') {
|
|
280
|
+
return new Response(null, {
|
|
281
|
+
status: 304,
|
|
282
|
+
headers: { ETag: styleResult.etag },
|
|
283
|
+
})
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
let compiledStyle = styleResult.style
|
|
287
|
+
|
|
288
|
+
if (parsedRequestPathname.requestedFingerprint !== null) {
|
|
289
|
+
if (compiledStyle.fingerprint !== parsedRequestPathname.requestedFingerprint)
|
|
290
|
+
return null
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return createResponseForStyle(compiledStyle, {
|
|
294
|
+
cacheControl: parsedRequestPathname.cacheControl,
|
|
295
|
+
ifNoneMatch,
|
|
296
|
+
isSourceMapRequest: parsedRequestPathname.isSourceMapRequest,
|
|
297
|
+
method: request.method,
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (!isScriptFilePath(parsedRequestPathname.filePath)) {
|
|
302
|
+
return null
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
let scriptResult = await scriptCompiler.getScript(parsedRequestPathname.filePath, {
|
|
260
306
|
ifNoneMatch,
|
|
261
307
|
isSourceMapRequest: parsedRequestPathname.isSourceMapRequest,
|
|
262
308
|
requestedFingerprint: parsedRequestPathname.requestedFingerprint,
|
|
263
309
|
})
|
|
264
|
-
if (
|
|
310
|
+
if (scriptResult.type === 'not-modified') {
|
|
265
311
|
return new Response(null, {
|
|
266
312
|
status: 304,
|
|
267
|
-
headers: { ETag:
|
|
313
|
+
headers: { ETag: scriptResult.etag },
|
|
268
314
|
})
|
|
269
315
|
}
|
|
270
316
|
|
|
271
|
-
let
|
|
317
|
+
let compiledScript = scriptResult.script
|
|
272
318
|
|
|
273
319
|
if (parsedRequestPathname.requestedFingerprint !== null) {
|
|
274
|
-
if (
|
|
320
|
+
if (compiledScript.fingerprint !== parsedRequestPathname.requestedFingerprint) return null
|
|
275
321
|
}
|
|
276
322
|
|
|
277
|
-
return
|
|
323
|
+
return createResponseForScript(compiledScript, {
|
|
278
324
|
cacheControl: parsedRequestPathname.cacheControl,
|
|
279
325
|
ifNoneMatch,
|
|
280
326
|
isSourceMapRequest: parsedRequestPathname.isSourceMapRequest,
|
|
@@ -286,7 +332,7 @@ export function createAssetServer(options: AssetServerOptions): AssetServer {
|
|
|
286
332
|
// handled here" so the outer router can continue to its own 404 behavior.
|
|
287
333
|
if (
|
|
288
334
|
isAssetServerCompilationError(error) &&
|
|
289
|
-
(error.code === '
|
|
335
|
+
(error.code === 'FILE_NOT_FOUND' || error.code === 'FILE_NOT_ALLOWED')
|
|
290
336
|
) {
|
|
291
337
|
return null
|
|
292
338
|
}
|
|
@@ -296,10 +342,46 @@ export function createAssetServer(options: AssetServerOptions): AssetServer {
|
|
|
296
342
|
},
|
|
297
343
|
|
|
298
344
|
async getHref(filePath) {
|
|
299
|
-
|
|
345
|
+
if (isStyleFilePath(filePath)) {
|
|
346
|
+
return styleCompiler.getHref(filePath)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return scriptCompiler.getHref(filePath)
|
|
300
350
|
},
|
|
301
351
|
async getPreloads(filePath) {
|
|
302
|
-
|
|
352
|
+
let filePaths = Array.isArray(filePath) ? filePath : [filePath]
|
|
353
|
+
let styleFiles: string[] = []
|
|
354
|
+
let scriptFiles: string[] = []
|
|
355
|
+
|
|
356
|
+
for (let nextFilePath of filePaths) {
|
|
357
|
+
if (isStyleFilePath(nextFilePath)) {
|
|
358
|
+
styleFiles.push(nextFilePath)
|
|
359
|
+
continue
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
scriptFiles.push(nextFilePath)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (styleFiles.length === 0 && scriptFiles.length === 0) {
|
|
366
|
+
return []
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (styleFiles.length === 0) {
|
|
370
|
+
return flattenPreloadLayers(await scriptCompiler.getPreloadLayers(filePath))
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (scriptFiles.length === 0) {
|
|
374
|
+
return flattenPreloadLayers(await styleCompiler.getPreloadLayers(filePath))
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Mixed asset type preloads need to be merged, so we merge in order of first asset type seen
|
|
378
|
+
let scriptPreloadLayersPromise = scriptCompiler.getPreloadLayers(scriptFiles)
|
|
379
|
+
let stylePreloadLayersPromise = styleCompiler.getPreloadLayers(styleFiles)
|
|
380
|
+
let preloadLayerGroups = isStyleFilePath(filePaths[0])
|
|
381
|
+
? [stylePreloadLayersPromise, scriptPreloadLayersPromise]
|
|
382
|
+
: [scriptPreloadLayersPromise, stylePreloadLayersPromise]
|
|
383
|
+
|
|
384
|
+
return mergePreloadLayers(await Promise.all(preloadLayerGroups))
|
|
303
385
|
},
|
|
304
386
|
async close() {
|
|
305
387
|
await watcher?.close()
|
|
@@ -323,6 +405,28 @@ function internalServerError(): Response {
|
|
|
323
405
|
})
|
|
324
406
|
}
|
|
325
407
|
|
|
408
|
+
function mergePreloadLayers(preloadLayersByRoot: readonly (readonly string[][])[]): string[] {
|
|
409
|
+
let urls: string[] = []
|
|
410
|
+
let seen = new Set<string>()
|
|
411
|
+
let maxDepth = Math.max(0, ...preloadLayersByRoot.map((layers) => layers.length))
|
|
412
|
+
|
|
413
|
+
for (let depth = 0; depth < maxDepth; depth++) {
|
|
414
|
+
for (let preloadLayers of preloadLayersByRoot) {
|
|
415
|
+
for (let url of preloadLayers[depth] ?? []) {
|
|
416
|
+
if (seen.has(url)) continue
|
|
417
|
+
seen.add(url)
|
|
418
|
+
urls.push(url)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return urls
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function flattenPreloadLayers(preloadLayers: readonly (readonly string[])[]): string[] {
|
|
427
|
+
return preloadLayers.flatMap((layer) => layer)
|
|
428
|
+
}
|
|
429
|
+
|
|
326
430
|
function defaultErrorHandler(error: unknown): void {
|
|
327
431
|
console.error(error)
|
|
328
432
|
}
|
|
@@ -341,35 +445,22 @@ function resolveAssetServerOptions(options: AssetServerOptions): ResolvedAssetSe
|
|
|
341
445
|
define: scriptOptions.define,
|
|
342
446
|
deny: options.deny,
|
|
343
447
|
external: scriptOptions.external ?? [],
|
|
344
|
-
|
|
345
|
-
minify:
|
|
448
|
+
fingerprintAssets: fingerprintOptions.enabled,
|
|
449
|
+
minify: options.minify ?? false,
|
|
346
450
|
onError: options.onError ?? defaultErrorHandler,
|
|
347
451
|
rootDir,
|
|
348
452
|
routes: compileRoutes({
|
|
349
453
|
fileMap: options.fileMap,
|
|
350
454
|
rootDir,
|
|
351
455
|
}),
|
|
352
|
-
sourceMapSourcePaths:
|
|
353
|
-
sourceMaps:
|
|
354
|
-
scriptsTarget:
|
|
456
|
+
sourceMapSourcePaths: options.sourceMapSourcePaths ?? 'url',
|
|
457
|
+
sourceMaps: options.sourceMaps,
|
|
458
|
+
scriptsTarget: resolveScriptTarget(options.target),
|
|
459
|
+
stylesTarget: resolveStyleTarget(options.target),
|
|
355
460
|
watchOptions: normalizeWatchOptions(options.watch),
|
|
356
461
|
}
|
|
357
462
|
}
|
|
358
463
|
|
|
359
|
-
function normalizeTarget(
|
|
360
|
-
target: NonNullable<AssetServerOptions['scripts']>['target'],
|
|
361
|
-
): ScriptsTarget | undefined {
|
|
362
|
-
if (target == null) return undefined
|
|
363
|
-
|
|
364
|
-
if (typeof target !== 'string' || !scriptTargetSet.has(target)) {
|
|
365
|
-
throw new TypeError(
|
|
366
|
-
`Expected target to be one of ${scriptTargets.map((value) => `"${value}"`).join(', ')}. Received "${target}".`,
|
|
367
|
-
)
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
return target as ScriptsTarget
|
|
371
|
-
}
|
|
372
|
-
|
|
373
464
|
function normalizeFingerprintOptions(options: {
|
|
374
465
|
fingerprint: AssetServerOptions['fingerprint']
|
|
375
466
|
watch: AssetServerOptions['watch']
|
|
@@ -413,3 +504,34 @@ function normalizeWatchOptions(
|
|
|
413
504
|
if (options == null || options === true) return {}
|
|
414
505
|
return options
|
|
415
506
|
}
|
|
507
|
+
|
|
508
|
+
function parseAssetRequestPathname(
|
|
509
|
+
pathname: string,
|
|
510
|
+
options: {
|
|
511
|
+
fingerprintAssets: boolean
|
|
512
|
+
routes: CompiledRoutes
|
|
513
|
+
},
|
|
514
|
+
): {
|
|
515
|
+
cacheControl: string
|
|
516
|
+
filePath: string
|
|
517
|
+
isSourceMapRequest: boolean
|
|
518
|
+
requestedFingerprint: string | null
|
|
519
|
+
} | null {
|
|
520
|
+
let isSourceMapRequest = pathname.endsWith('.map')
|
|
521
|
+
let pathWithoutMap = isSourceMapRequest ? pathname.slice(0, -4) : pathname
|
|
522
|
+
let fingerprint = parseFingerprintSuffix(pathWithoutMap)
|
|
523
|
+
let filePath = options.routes.resolveUrlPathname(fingerprint.pathname)
|
|
524
|
+
if (!filePath) return null
|
|
525
|
+
if (options.fingerprintAssets && fingerprint.requestedFingerprint === null) return null
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
cacheControl: getFingerprintRequestCacheControl(fingerprint.requestedFingerprint),
|
|
529
|
+
filePath,
|
|
530
|
+
isSourceMapRequest,
|
|
531
|
+
requestedFingerprint: fingerprint.requestedFingerprint,
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function isScriptFilePath(filePath: string): boolean {
|
|
536
|
+
return scriptExtensionSet.has(path.extname(filePath).toLowerCase())
|
|
537
|
+
}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
type AssetServerCompilationErrorCode =
|
|
2
|
-
| '
|
|
3
|
-
| '
|
|
4
|
-
| '
|
|
5
|
-
| '
|
|
6
|
-
| '
|
|
7
|
-
| '
|
|
2
|
+
| 'FILE_NOT_FOUND'
|
|
3
|
+
| 'FILE_NOT_ALLOWED'
|
|
4
|
+
| 'FILE_OUTSIDE_FILE_MAP'
|
|
5
|
+
| 'COMMONJS_NOT_SUPPORTED'
|
|
6
|
+
| 'TRANSFORM_FAILED'
|
|
7
|
+
| 'EMIT_FAILED'
|
|
8
8
|
| 'IMPORT_RESOLUTION_FAILED'
|
|
9
9
|
| 'IMPORT_NOT_SUPPORTED'
|
|
10
10
|
| 'IMPORT_NOT_ALLOWED'
|
|
11
11
|
| 'IMPORT_OUTSIDE_FILE_MAP'
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Internal error used by the request-time
|
|
14
|
+
* Internal error used by the request-time asset compilation pipeline.
|
|
15
15
|
*/
|
|
16
16
|
export class AssetServerCompilationError extends Error {
|
|
17
17
|
code: AssetServerCompilationErrorCode
|
package/src/lib/file-matcher.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as fs from 'node:fs'
|
|
2
|
-
import
|
|
2
|
+
import picomatch from 'picomatch'
|
|
3
3
|
|
|
4
4
|
import { normalizeFilePath, resolveFilePath } from './paths.ts'
|
|
5
5
|
|
|
@@ -37,7 +37,8 @@ export function createFileMatcher(
|
|
|
37
37
|
return (filePath) => filePath === resolvedPatternPath
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
let globMatcher = picomatch(resolvedPatternPath, { dot: true })
|
|
41
|
+
return (filePath) => globMatcher(filePath)
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
function isSameOrDescendantPath(filePath: string, directoryPath: string): boolean {
|