@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
|
@@ -3,6 +3,7 @@ import * as os from 'node:os'
|
|
|
3
3
|
import * as path from 'node:path'
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
import { IfNoneMatch } from '@remix-run/headers/if-none-match'
|
|
6
|
+
import { getTsconfig } from 'get-tsconfig'
|
|
6
7
|
|
|
7
8
|
import { createAssetServerCompilationError } from '../compilation-error.ts'
|
|
8
9
|
import { createFileMatcher } from '../file-matcher.ts'
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
supportedScriptExtensions,
|
|
21
22
|
} from './resolve.ts'
|
|
22
23
|
import type { ResolveArgs, ResolvedModule } from './resolve.ts'
|
|
24
|
+
import { isBareImportSpecifier } from './specifiers.ts'
|
|
23
25
|
import type { CompiledRoutes } from '../routes.ts'
|
|
24
26
|
import type { ResolvedScriptTarget } from '../target.ts'
|
|
25
27
|
import { createModuleStore } from '../module-store.ts'
|
|
@@ -34,6 +36,7 @@ import type {
|
|
|
34
36
|
import { createTsconfigTransformOptionsResolver, transformModule } from './transform.ts'
|
|
35
37
|
import type { ResolveModuleResult, TransformArgs, TransformedModule } from './transform.ts'
|
|
36
38
|
import { ResolverFactory } from 'oxc-resolver'
|
|
39
|
+
import type { NapiResolveOptions } from 'oxc-resolver'
|
|
37
40
|
import type { EmittedAsset, EmittedModule } from './emit.ts'
|
|
38
41
|
|
|
39
42
|
type ScriptRecord = ModuleRecord<TransformedModule, ResolvedModule, EmittedModule>
|
|
@@ -45,6 +48,11 @@ type ScriptCompileResult = {
|
|
|
45
48
|
sourceMap: EmittedAsset | null
|
|
46
49
|
}
|
|
47
50
|
|
|
51
|
+
export type ScriptImportMap = {
|
|
52
|
+
imports: Record<string, string>
|
|
53
|
+
scopes?: Record<string, Record<string, string>>
|
|
54
|
+
}
|
|
55
|
+
|
|
48
56
|
type ScriptGetResult =
|
|
49
57
|
| {
|
|
50
58
|
script: ScriptCompileResult
|
|
@@ -62,7 +70,6 @@ type ScriptGetOptions = {
|
|
|
62
70
|
}
|
|
63
71
|
|
|
64
72
|
type ScriptCompilerOptions = {
|
|
65
|
-
buildId?: string
|
|
66
73
|
define?: Record<string, string>
|
|
67
74
|
external: string[]
|
|
68
75
|
fingerprintAssets: boolean
|
|
@@ -87,7 +94,9 @@ type ScriptCompilerOptions = {
|
|
|
87
94
|
type ScriptCompiler = {
|
|
88
95
|
getScript(filePath: string, options: ScriptGetOptions): Promise<ScriptGetResult>
|
|
89
96
|
getPreloadLayers(filePath: string | readonly string[]): Promise<string[][]>
|
|
97
|
+
getImportMap(filePath: string | readonly string[]): Promise<ScriptImportMap>
|
|
90
98
|
getHref(filePath: string): Promise<string>
|
|
99
|
+
resolveSpecifierFromRoot(specifier: string): Promise<string>
|
|
91
100
|
classifyHmrFileEvent(filePath: string, event: ModuleWatchEvent): Promise<ScriptHmrUpdate[]>
|
|
92
101
|
invalidateFileEvent(filePath: string, event: ModuleWatchEvent): void
|
|
93
102
|
parseRequestPathname(pathname: string): ParsedRequestPathname | null
|
|
@@ -109,7 +118,8 @@ export type ScriptHmrUpdate =
|
|
|
109
118
|
}
|
|
110
119
|
| {
|
|
111
120
|
accepted: true
|
|
112
|
-
|
|
121
|
+
acceptedFilePath: string
|
|
122
|
+
acceptedUrlPathname: string
|
|
113
123
|
filePath: string
|
|
114
124
|
path: string
|
|
115
125
|
timestamp: number
|
|
@@ -120,8 +130,13 @@ type ScriptHmrBoundary = {
|
|
|
120
130
|
boundaryModule: ResolvedModule
|
|
121
131
|
}
|
|
122
132
|
|
|
133
|
+
type DirectoryTsconfig = {
|
|
134
|
+
fileIndependent: boolean
|
|
135
|
+
path: string | null
|
|
136
|
+
}
|
|
137
|
+
|
|
123
138
|
const supportedScriptExtensionSet = new Set<string>(supportedScriptExtensions)
|
|
124
|
-
const
|
|
139
|
+
const scriptConcurrency = Math.max(1, Math.min(8, os.availableParallelism() - 1))
|
|
125
140
|
|
|
126
141
|
export function createScriptCompiler(options: ScriptCompilerOptions): ScriptCompiler {
|
|
127
142
|
let resolvedOptions = {
|
|
@@ -146,19 +161,22 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
146
161
|
onWatchFilesChange: options.onWatchFilesChange,
|
|
147
162
|
})
|
|
148
163
|
let tsconfigTransformOptionsResolver = createTsconfigTransformOptionsResolver()
|
|
149
|
-
let
|
|
164
|
+
let resolverOptions = {
|
|
150
165
|
aliasFields: [['browser']],
|
|
151
166
|
conditionNames: ['browser', 'import', 'module', 'default'],
|
|
152
167
|
extensionAlias: resolverExtensionAlias,
|
|
153
168
|
extensions: resolverExtensions,
|
|
154
169
|
mainFields: ['browser', 'module', 'main'],
|
|
155
|
-
|
|
156
|
-
})
|
|
170
|
+
} satisfies NapiResolveOptions
|
|
171
|
+
let resolverFactory = new ResolverFactory({ ...resolverOptions, tsconfig: 'auto' })
|
|
172
|
+
let directoryResolverByTsconfig = new Map<string | null, ResolverFactory>()
|
|
173
|
+
let tsconfigByDirectory = new Map<string, DirectoryTsconfig>()
|
|
174
|
+
let directoryResolutionIdentityByCacheKey = new Map<string, Promise<string | null>>()
|
|
157
175
|
let resolveInFlightByCacheKey = new Map<string, Promise<ResolvedModule>>()
|
|
158
176
|
let emitInFlightByCacheKey = new Map<string, Promise<EmittedModule>>()
|
|
177
|
+
let hasResolvedScripts = false
|
|
159
178
|
|
|
160
179
|
let transformArgs: TransformArgs = {
|
|
161
|
-
buildId: resolvedOptions.buildId ?? null,
|
|
162
180
|
define: resolvedOptions.define ?? null,
|
|
163
181
|
externalSet: resolvedOptions.externalSet,
|
|
164
182
|
isWatchIgnored,
|
|
@@ -172,10 +190,13 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
172
190
|
tsconfigTransformOptionsResolver,
|
|
173
191
|
}
|
|
174
192
|
let resolveArgs: ResolveArgs = {
|
|
193
|
+
concurrency: scriptConcurrency,
|
|
194
|
+
isDirectoryResolutionFileIndependent,
|
|
175
195
|
isAllowed: resolvedOptions.isAllowed,
|
|
176
196
|
isWatchIgnored,
|
|
177
197
|
resolveModulePath,
|
|
178
198
|
resolverFactory,
|
|
199
|
+
resolveDirectorySpecifierIdentity,
|
|
179
200
|
routes: resolvedOptions.routes,
|
|
180
201
|
}
|
|
181
202
|
|
|
@@ -194,17 +215,7 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
194
215
|
},
|
|
195
216
|
|
|
196
217
|
async getPreloadLayers(filePath) {
|
|
197
|
-
let resolvedEntries
|
|
198
|
-
let seen = new Set<string>()
|
|
199
|
-
|
|
200
|
-
for (let resolvedModule of (Array.isArray(filePath) ? filePath : [filePath]).map((nextPath) =>
|
|
201
|
-
resolveServedScriptOrThrow(resolveInputFilePath(nextPath)),
|
|
202
|
-
)) {
|
|
203
|
-
if (seen.has(resolvedModule.identityPath)) continue
|
|
204
|
-
seen.add(resolvedModule.identityPath)
|
|
205
|
-
resolvedEntries.push(resolvedModule.identityPath)
|
|
206
|
-
}
|
|
207
|
-
|
|
218
|
+
let resolvedEntries = resolveInputScriptRoots(filePath)
|
|
208
219
|
let visited = new Set(resolvedEntries)
|
|
209
220
|
let queue = [...resolvedEntries]
|
|
210
221
|
let layers: string[][] = []
|
|
@@ -218,9 +229,9 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
218
229
|
let layer: string[] = []
|
|
219
230
|
|
|
220
231
|
for (let resolvedModule of resolvedModules) {
|
|
221
|
-
layer.push(
|
|
232
|
+
layer.push(await getServedUrl(resolvedModule.identityPath))
|
|
222
233
|
|
|
223
|
-
for (let dep of resolvedModule.
|
|
234
|
+
for (let dep of resolvedModule.staticDeps) {
|
|
224
235
|
if (visited.has(dep)) continue
|
|
225
236
|
visited.add(dep)
|
|
226
237
|
queue.push(dep)
|
|
@@ -233,11 +244,88 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
233
244
|
return layers
|
|
234
245
|
},
|
|
235
246
|
|
|
247
|
+
async getImportMap(filePath) {
|
|
248
|
+
let resolvedEntries = resolveInputScriptRoots(filePath)
|
|
249
|
+
let resolvedEntrySet = new Set(resolvedEntries)
|
|
250
|
+
let visited = new Set<string>()
|
|
251
|
+
let queue = [...resolvedEntries]
|
|
252
|
+
let imports: Record<string, string> = {}
|
|
253
|
+
let scopes: Record<string, Record<string, string>> = {}
|
|
254
|
+
|
|
255
|
+
while (queue.length > 0) {
|
|
256
|
+
let frontier = queue
|
|
257
|
+
queue = []
|
|
258
|
+
let resolvedModules = await getOrCreateResolvedScripts(
|
|
259
|
+
frontier.map((identityPath) => scriptStore.get(identityPath)),
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
for (let resolvedModule of resolvedModules) {
|
|
263
|
+
if (visited.has(resolvedModule.identityPath)) continue
|
|
264
|
+
visited.add(resolvedModule.identityPath)
|
|
265
|
+
if (!resolvedEntrySet.has(resolvedModule.identityPath)) {
|
|
266
|
+
addImportMapEntry(
|
|
267
|
+
imports,
|
|
268
|
+
resolvedModule.stableUrlPathname,
|
|
269
|
+
await getServedUrl(resolvedModule.identityPath),
|
|
270
|
+
)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
for (let imported of resolvedModule.imports) {
|
|
274
|
+
let depUrl = await getServedUrl(imported.depPath)
|
|
275
|
+
if (isBareImportSpecifier(imported.specifier)) {
|
|
276
|
+
let scopePathname = imported.scopePathname
|
|
277
|
+
if (!scopePathname) {
|
|
278
|
+
throw new Error(
|
|
279
|
+
`Expected import map scope for bare import "${imported.specifier}" in ${resolvedModule.identityPath}`,
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
let scopeImports = (scopes[scopePathname] ??= {})
|
|
283
|
+
addImportMapEntry(scopeImports, imported.specifier, depUrl)
|
|
284
|
+
} else {
|
|
285
|
+
let browserResolvedSpecifier = resolveImportMapUrlSpecifier(
|
|
286
|
+
imported.specifier,
|
|
287
|
+
resolvedModule.stableUrlPathname,
|
|
288
|
+
)
|
|
289
|
+
addImportMapEntry(imports, browserResolvedSpecifier, depUrl)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
for (let dep of resolvedModule.deps) {
|
|
294
|
+
if (!visited.has(dep)) {
|
|
295
|
+
queue.push(dep)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
for (let [scopePathname, scopeImports] of Object.entries(scopes)) {
|
|
302
|
+
if (Object.keys(scopeImports).length === 0) {
|
|
303
|
+
delete scopes[scopePathname]
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return Object.keys(scopes).length > 0 ? { imports, scopes } : { imports }
|
|
308
|
+
},
|
|
309
|
+
|
|
236
310
|
async getHref(filePath) {
|
|
237
311
|
let resolvedModule = resolveServedScriptOrThrow(resolveInputFilePath(filePath))
|
|
238
312
|
return getServedUrl(resolvedModule.identityPath)
|
|
239
313
|
},
|
|
240
314
|
|
|
315
|
+
async resolveSpecifierFromRoot(specifier) {
|
|
316
|
+
let importerPath = path.join(resolvedOptions.rootDir, '__remix_virtual_entry__.js')
|
|
317
|
+
let resolutionResult = await resolverFactory.resolveFileAsync(importerPath, specifier)
|
|
318
|
+
if (resolutionResult.error || !resolutionResult.path) {
|
|
319
|
+
throw createAssetServerCompilationError(
|
|
320
|
+
`Failed to resolve browser module "${specifier}" from asset server root.`,
|
|
321
|
+
{ code: 'IMPORT_RESOLUTION_FAILED' },
|
|
322
|
+
)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
let resolvedModule = resolveServedScriptOrThrow(resolutionResult.path)
|
|
326
|
+
return getServedUrl(resolvedModule.identityPath)
|
|
327
|
+
},
|
|
328
|
+
|
|
241
329
|
async classifyHmrFileEvent(filePath, event) {
|
|
242
330
|
let normalizedFilePath = normalizeFilePath(filePath)
|
|
243
331
|
if (isWatchIgnored(normalizedFilePath)) return []
|
|
@@ -245,9 +333,24 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
245
333
|
let timestamp = Date.now()
|
|
246
334
|
let previousResolvedModule = scriptStore.getLastResolved(normalizedFilePath)
|
|
247
335
|
let updatePathname = previousResolvedModule?.stableUrlPathname
|
|
336
|
+
let resolutionMetadataChanged =
|
|
337
|
+
isPackageJsonPath(normalizedFilePath) || isTsconfigPath(normalizedFilePath)
|
|
248
338
|
|
|
249
339
|
invalidateScriptFileEvent(normalizedFilePath, event)
|
|
250
340
|
|
|
341
|
+
if (resolutionMetadataChanged && hasResolvedScripts) {
|
|
342
|
+
let hmrUpdate: ScriptHmrUpdate[] = [
|
|
343
|
+
{
|
|
344
|
+
accepted: false,
|
|
345
|
+
filePath: normalizedFilePath,
|
|
346
|
+
path: normalizedFilePath,
|
|
347
|
+
timestamp,
|
|
348
|
+
},
|
|
349
|
+
]
|
|
350
|
+
resolvedOptions.hmr?.send(hmrUpdate)
|
|
351
|
+
return hmrUpdate
|
|
352
|
+
}
|
|
353
|
+
|
|
251
354
|
let resolvedModule =
|
|
252
355
|
event === 'change' && updatePathname
|
|
253
356
|
? await tryGetOrCreateResolvedScript(scriptStore.get(normalizedFilePath))
|
|
@@ -305,6 +408,11 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
305
408
|
|
|
306
409
|
if (shouldClearResolverCacheForFileEvent(normalizedFilePath, event)) {
|
|
307
410
|
resolverFactory.clearCache()
|
|
411
|
+
for (let directoryResolver of directoryResolverByTsconfig.values()) {
|
|
412
|
+
directoryResolver.clearCache()
|
|
413
|
+
}
|
|
414
|
+
tsconfigByDirectory.clear()
|
|
415
|
+
directoryResolutionIdentityByCacheKey.clear()
|
|
308
416
|
}
|
|
309
417
|
|
|
310
418
|
if (isTsconfigPath(normalizedFilePath)) {
|
|
@@ -321,6 +429,50 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
321
429
|
scriptStore.invalidateForFileEvent(normalizedFilePath, event)
|
|
322
430
|
}
|
|
323
431
|
|
|
432
|
+
function isDirectoryResolutionFileIndependent(directory: string): boolean {
|
|
433
|
+
return getDirectoryTsconfig(directory).fileIndependent
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function getDirectoryTsconfig(directory: string): DirectoryTsconfig {
|
|
437
|
+
let existing = tsconfigByDirectory.get(directory)
|
|
438
|
+
if (existing) return existing
|
|
439
|
+
|
|
440
|
+
let tsconfig = getTsconfig(directory)
|
|
441
|
+
let result = {
|
|
442
|
+
fileIndependent: !tsconfig?.config.references?.length,
|
|
443
|
+
path: tsconfig?.path ?? null,
|
|
444
|
+
}
|
|
445
|
+
tsconfigByDirectory.set(directory, result)
|
|
446
|
+
return result
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
async function resolveDirectorySpecifierIdentity(
|
|
450
|
+
directory: string,
|
|
451
|
+
specifier: string,
|
|
452
|
+
): Promise<string | null> {
|
|
453
|
+
let cacheKey = `${directory}\0${specifier}`
|
|
454
|
+
let existing = directoryResolutionIdentityByCacheKey.get(cacheKey)
|
|
455
|
+
if (existing) return existing
|
|
456
|
+
|
|
457
|
+
let promise = (async () => {
|
|
458
|
+
let tsconfigPath = getDirectoryTsconfig(directory).path
|
|
459
|
+
let directoryResolver = directoryResolverByTsconfig.get(tsconfigPath)
|
|
460
|
+
if (!directoryResolver) {
|
|
461
|
+
directoryResolver = new ResolverFactory({
|
|
462
|
+
...resolverOptions,
|
|
463
|
+
tsconfig: tsconfigPath ? { configFile: tsconfigPath } : undefined,
|
|
464
|
+
})
|
|
465
|
+
directoryResolverByTsconfig.set(tsconfigPath, directoryResolver)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
let result = await directoryResolver.async(directory, specifier)
|
|
469
|
+
if (!result.path || !path.isAbsolute(result.path)) return null
|
|
470
|
+
return resolveModulePath(normalizeFilePath(result.path))?.identityPath ?? null
|
|
471
|
+
})()
|
|
472
|
+
directoryResolutionIdentityByCacheKey.set(cacheKey, promise)
|
|
473
|
+
return promise
|
|
474
|
+
}
|
|
475
|
+
|
|
324
476
|
function resolveServedScriptOrThrow(absolutePath: string): ResolveModuleResult {
|
|
325
477
|
let resolvedModule = resolveModulePath(absolutePath)
|
|
326
478
|
if (!resolvedModule) {
|
|
@@ -342,6 +494,20 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
342
494
|
return resolvedModule
|
|
343
495
|
}
|
|
344
496
|
|
|
497
|
+
function resolveInputScriptRoots(filePath: string | readonly string[]): string[] {
|
|
498
|
+
let resolvedEntries: string[] = []
|
|
499
|
+
let seen = new Set<string>()
|
|
500
|
+
|
|
501
|
+
for (let nextPath of Array.isArray(filePath) ? filePath : [filePath]) {
|
|
502
|
+
let resolvedModule = resolveServedScriptOrThrow(resolveInputFilePath(nextPath))
|
|
503
|
+
if (seen.has(resolvedModule.identityPath)) continue
|
|
504
|
+
seen.add(resolvedModule.identityPath)
|
|
505
|
+
resolvedEntries.push(resolvedModule.identityPath)
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
return resolvedEntries
|
|
509
|
+
}
|
|
510
|
+
|
|
345
511
|
function getNotModifiedScript(
|
|
346
512
|
record: ScriptRecord,
|
|
347
513
|
options: ScriptGetOptions,
|
|
@@ -363,7 +529,7 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
363
529
|
}
|
|
364
530
|
|
|
365
531
|
async function getOrCreateResolvedScripts(records: ScriptRecord[]): Promise<ResolvedModule[]> {
|
|
366
|
-
return mapWithConcurrency(records,
|
|
532
|
+
return mapWithConcurrency(records, scriptConcurrency, (record) =>
|
|
367
533
|
getOrCreateResolvedScript(record),
|
|
368
534
|
)
|
|
369
535
|
}
|
|
@@ -400,6 +566,7 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
400
566
|
resolveModuleResult.tracking,
|
|
401
567
|
])
|
|
402
568
|
}
|
|
569
|
+
hasResolvedScripts = true
|
|
403
570
|
|
|
404
571
|
return resolveModuleResult.value
|
|
405
572
|
})()
|
|
@@ -463,10 +630,12 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
463
630
|
let promise = (async () => {
|
|
464
631
|
let startedVersion = record.invalidationVersion
|
|
465
632
|
let resolvedModule = await getOrCreateResolvedScript(record)
|
|
633
|
+
await resolveScriptGraph(resolvedModule)
|
|
466
634
|
let emitResolvedModuleResult = await emitResolvedModule(resolvedModule, {
|
|
635
|
+
fingerprintAssets: resolvedOptions.fingerprintAssets,
|
|
636
|
+
getHmrImportTimestamp,
|
|
467
637
|
getServedUrl,
|
|
468
638
|
getStableUrl,
|
|
469
|
-
getHmrImportTimestamp,
|
|
470
639
|
hmrClientPathname: resolvedOptions.hmr?.clientPathname,
|
|
471
640
|
sourceMaps: resolvedOptions.sourceMaps,
|
|
472
641
|
})
|
|
@@ -497,26 +666,47 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
497
666
|
}
|
|
498
667
|
}
|
|
499
668
|
|
|
500
|
-
async function
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
669
|
+
async function resolveScriptGraph(root: ResolvedModule): Promise<void> {
|
|
670
|
+
let visited = new Set([root.identityPath])
|
|
671
|
+
let queue = [...root.deps]
|
|
672
|
+
|
|
673
|
+
while (queue.length > 0) {
|
|
674
|
+
let frontier = queue
|
|
675
|
+
queue = []
|
|
676
|
+
let resolvedModules = await getOrCreateResolvedScripts(
|
|
677
|
+
frontier
|
|
678
|
+
.filter((identityPath) => !visited.has(identityPath))
|
|
679
|
+
.map((identityPath) => scriptStore.get(identityPath)),
|
|
680
|
+
)
|
|
681
|
+
|
|
682
|
+
for (let resolvedModule of resolvedModules) {
|
|
683
|
+
if (visited.has(resolvedModule.identityPath)) continue
|
|
684
|
+
visited.add(resolvedModule.identityPath)
|
|
685
|
+
|
|
686
|
+
for (let dep of resolvedModule.deps) {
|
|
687
|
+
if (!visited.has(dep)) queue.push(dep)
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
504
691
|
}
|
|
505
692
|
|
|
506
|
-
function
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
693
|
+
async function getServedUrl(identityPath: string): Promise<string> {
|
|
694
|
+
let resolvedModule = await getOrCreateResolvedScript(scriptStore.get(identityPath))
|
|
695
|
+
if (!resolvedOptions.fingerprintAssets) {
|
|
696
|
+
return resolvedModule.stableUrlPathname
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
let emittedModule = await getOrCreateEmittedScript(scriptStore.get(identityPath))
|
|
700
|
+
return formatFingerprintedPathname(resolvedModule.stableUrlPathname, emittedModule.fingerprint)
|
|
511
701
|
}
|
|
512
702
|
|
|
513
703
|
function getStableUrl(identityPath: string): string {
|
|
514
704
|
let stableUrlPathname = resolvedOptions.routes.toUrlPathname(identityPath)
|
|
515
705
|
if (!stableUrlPathname) {
|
|
516
706
|
throw createAssetServerCompilationError(
|
|
517
|
-
`File ${identityPath} is outside all configured
|
|
707
|
+
`File ${identityPath} is outside all configured mounts.`,
|
|
518
708
|
{
|
|
519
|
-
code: '
|
|
709
|
+
code: 'FILE_OUTSIDE_MOUNTS',
|
|
520
710
|
},
|
|
521
711
|
)
|
|
522
712
|
}
|
|
@@ -545,7 +735,8 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
545
735
|
return [
|
|
546
736
|
{
|
|
547
737
|
accepted: true,
|
|
548
|
-
|
|
738
|
+
acceptedFilePath: resolvedModule.identityPath,
|
|
739
|
+
acceptedUrlPathname: updatePathname,
|
|
549
740
|
filePath: resolvedModule.identityPath,
|
|
550
741
|
path: updatePathname,
|
|
551
742
|
timestamp,
|
|
@@ -558,7 +749,8 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
558
749
|
if (sourceFilePath !== undefined && boundaries) {
|
|
559
750
|
return dedupeHmrBoundaries(boundaries).map(({ acceptedModule, boundaryModule }) => ({
|
|
560
751
|
accepted: true,
|
|
561
|
-
|
|
752
|
+
acceptedFilePath: acceptedModule.identityPath,
|
|
753
|
+
acceptedUrlPathname: acceptedModule.stableUrlPathname,
|
|
562
754
|
filePath: sourceFilePath,
|
|
563
755
|
path: boundaryModule.stableUrlPathname,
|
|
564
756
|
timestamp,
|
|
@@ -630,6 +822,15 @@ export function createScriptCompiler(options: ScriptCompilerOptions): ScriptComp
|
|
|
630
822
|
}
|
|
631
823
|
}
|
|
632
824
|
|
|
825
|
+
function resolveImportMapUrlSpecifier(specifier: string, importerUrlPathname: string): string {
|
|
826
|
+
return new URL(specifier, `http://localhost${importerUrlPathname}`).pathname
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
function addImportMapEntry(imports: Record<string, string>, specifier: string, url: string): void {
|
|
830
|
+
if (specifier === url) return
|
|
831
|
+
imports[specifier] = url
|
|
832
|
+
}
|
|
833
|
+
|
|
633
834
|
function dedupeHmrBoundaries(boundaries: ScriptHmrBoundary[]): ScriptHmrBoundary[] {
|
|
634
835
|
let seen = new Set<string>()
|
|
635
836
|
let result: ScriptHmrBoundary[] = []
|
|
@@ -658,16 +859,13 @@ function getNotModifiedResult(
|
|
|
658
859
|
): ScriptGetResult | null {
|
|
659
860
|
if (!emittedModule || options.ifNoneMatch === null) return null
|
|
660
861
|
|
|
661
|
-
if (
|
|
662
|
-
options.requestedFingerprint !== null &&
|
|
663
|
-
emittedModule.fingerprint !== options.requestedFingerprint
|
|
664
|
-
) {
|
|
665
|
-
return null
|
|
666
|
-
}
|
|
667
|
-
|
|
668
862
|
let asset = getEmittedAssetForRequest(emittedModule, options.isSourceMapRequest)
|
|
669
863
|
if (!asset) return null
|
|
670
864
|
|
|
865
|
+
if (options.requestedFingerprint !== null && asset.fingerprint !== options.requestedFingerprint) {
|
|
866
|
+
return null
|
|
867
|
+
}
|
|
868
|
+
|
|
671
869
|
if (!IfNoneMatch.from(options.ifNoneMatch).matches(asset.etag)) return null
|
|
672
870
|
return { type: 'not-modified', etag: asset.etag }
|
|
673
871
|
}
|
|
@@ -801,18 +999,6 @@ function resolveActualPath(identityPath: string): string | null {
|
|
|
801
999
|
}
|
|
802
1000
|
}
|
|
803
1001
|
|
|
804
|
-
function isBareImportSpecifier(specifier: string): boolean {
|
|
805
|
-
return (
|
|
806
|
-
!specifier.startsWith('./') &&
|
|
807
|
-
!specifier.startsWith('../') &&
|
|
808
|
-
!specifier.startsWith('/') &&
|
|
809
|
-
!specifier.startsWith('file:') &&
|
|
810
|
-
!specifier.startsWith('data:') &&
|
|
811
|
-
!specifier.startsWith('http://') &&
|
|
812
|
-
!specifier.startsWith('https://')
|
|
813
|
-
)
|
|
814
|
-
}
|
|
815
|
-
|
|
816
1002
|
function isNoEntityError(
|
|
817
1003
|
error: unknown,
|
|
818
1004
|
): error is NodeJS.ErrnoException & { code: 'ENOENT' | 'ENOTDIR' } {
|
package/src/lib/scripts/emit.ts
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
createAssetServerCompilationError,
|
|
5
5
|
isAssetServerCompilationError,
|
|
6
6
|
} from '../compilation-error.ts'
|
|
7
|
-
import { hashContent } from '../fingerprint.ts'
|
|
7
|
+
import { formatFingerprintedPathname, hashContent } from '../fingerprint.ts'
|
|
8
8
|
import type { ResolvedModule } from './resolve.ts'
|
|
9
9
|
import { composeSourceMaps } from '../source-maps.ts'
|
|
10
10
|
import type { AssetServerCompilationError } from '../compilation-error.ts'
|
|
@@ -12,12 +12,12 @@ import type { AssetServerCompilationError } from '../compilation-error.ts'
|
|
|
12
12
|
export type EmittedAsset = {
|
|
13
13
|
content: string
|
|
14
14
|
etag: string
|
|
15
|
+
fingerprint: string
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export type EmittedModule = {
|
|
18
19
|
code: EmittedAsset
|
|
19
20
|
fingerprint: string | null
|
|
20
|
-
importUrls: string[]
|
|
21
21
|
sourceMap: EmittedAsset | null
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -40,6 +40,7 @@ type RewriteImportsOptions = {
|
|
|
40
40
|
export async function emitResolvedModule(
|
|
41
41
|
resolvedModule: ResolvedModule,
|
|
42
42
|
options: {
|
|
43
|
+
fingerprintAssets: boolean
|
|
43
44
|
getHmrImportTimestamp(identityPath: string): number | null
|
|
44
45
|
getServedUrl(identityPath: string): Promise<string>
|
|
45
46
|
getStableUrl(identityPath: string): string
|
|
@@ -48,30 +49,32 @@ export async function emitResolvedModule(
|
|
|
48
49
|
},
|
|
49
50
|
): Promise<EmitResult> {
|
|
50
51
|
try {
|
|
51
|
-
let importUrls = await Promise.all(
|
|
52
|
-
resolvedModule.deps.map((depPath) => options.getServedUrl(depPath)),
|
|
53
|
-
)
|
|
54
52
|
let rewriteResult = await rewriteImports(resolvedModule, options)
|
|
55
53
|
let finalCode = prependHmrContext(resolvedModule, rewriteResult.code, options)
|
|
54
|
+
let sourceMap = rewriteResult.sourceMap
|
|
55
|
+
? await createEmittedAsset(rewriteResult.sourceMap)
|
|
56
|
+
: null
|
|
56
57
|
|
|
57
58
|
if (rewriteResult.sourceMap) {
|
|
58
59
|
if (options.sourceMaps === 'inline') {
|
|
59
60
|
let encoded = Buffer.from(rewriteResult.sourceMap).toString('base64')
|
|
60
61
|
finalCode += `\n//# sourceMappingURL=data:application/json;base64,${encoded}`
|
|
61
62
|
} else if (options.sourceMaps === 'external') {
|
|
62
|
-
finalCode += `\n//# sourceMappingURL=${
|
|
63
|
+
finalCode += `\n//# sourceMappingURL=${formatFingerprintedPathname(
|
|
64
|
+
resolvedModule.stableUrlPathname,
|
|
65
|
+
options.fingerprintAssets && sourceMap ? sourceMap.fingerprint : null,
|
|
66
|
+
)}.map`
|
|
63
67
|
}
|
|
64
68
|
}
|
|
65
69
|
|
|
70
|
+
let code = await createEmittedAsset(finalCode)
|
|
71
|
+
|
|
66
72
|
return {
|
|
67
73
|
ok: true,
|
|
68
74
|
value: {
|
|
69
|
-
code
|
|
70
|
-
fingerprint:
|
|
71
|
-
|
|
72
|
-
sourceMap: rewriteResult.sourceMap
|
|
73
|
-
? await createEmittedAsset(rewriteResult.sourceMap)
|
|
74
|
-
: null,
|
|
75
|
+
code,
|
|
76
|
+
fingerprint: options.fingerprintAssets ? code.fingerprint : null,
|
|
77
|
+
sourceMap,
|
|
75
78
|
},
|
|
76
79
|
}
|
|
77
80
|
} catch (error) {
|
|
@@ -87,18 +90,28 @@ async function rewriteImports(
|
|
|
87
90
|
options: RewriteImportsOptions,
|
|
88
91
|
): Promise<{ code: string; sourceMap: string | null }> {
|
|
89
92
|
let rewrittenSource = new MagicString(resolvedModule.rawCode)
|
|
93
|
+
let changed = false
|
|
90
94
|
|
|
91
95
|
for (let imported of resolvedModule.imports) {
|
|
92
|
-
let url = await options.getServedUrl(imported.depPath)
|
|
93
96
|
let hmrImportTimestamp = options.getHmrImportTimestamp(imported.depPath)
|
|
97
|
+
let replacementSpecifier = imported.specifier
|
|
94
98
|
if (hmrImportTimestamp !== null) {
|
|
95
|
-
|
|
99
|
+
replacementSpecifier = addTimestampQuery(
|
|
100
|
+
await options.getServedUrl(imported.depPath),
|
|
101
|
+
hmrImportTimestamp,
|
|
102
|
+
)
|
|
103
|
+
} else if (imported.compiledSpecifier === imported.specifier) {
|
|
104
|
+
continue
|
|
96
105
|
}
|
|
106
|
+
|
|
97
107
|
rewrittenSource.overwrite(
|
|
98
108
|
imported.start,
|
|
99
109
|
imported.end,
|
|
100
|
-
imported.quote
|
|
110
|
+
imported.quote
|
|
111
|
+
? `${imported.quote}${replacementSpecifier}${imported.quote}`
|
|
112
|
+
: replacementSpecifier,
|
|
101
113
|
)
|
|
114
|
+
changed = true
|
|
102
115
|
}
|
|
103
116
|
|
|
104
117
|
for (let acceptedDep of resolvedModule.hmr.acceptedDeps) {
|
|
@@ -108,12 +121,12 @@ async function rewriteImports(
|
|
|
108
121
|
acceptedDep.end,
|
|
109
122
|
acceptedDep.quote ? `${acceptedDep.quote}${url}${acceptedDep.quote}` : url,
|
|
110
123
|
)
|
|
124
|
+
changed = true
|
|
111
125
|
}
|
|
112
126
|
|
|
113
|
-
let code = rewrittenSource.toString()
|
|
127
|
+
let code = changed ? rewrittenSource.toString() : resolvedModule.rawCode
|
|
114
128
|
let sourceMap =
|
|
115
|
-
resolvedModule.sourceMap &&
|
|
116
|
-
(resolvedModule.imports.length > 0 || resolvedModule.hmr.acceptedDeps.length > 0)
|
|
129
|
+
resolvedModule.sourceMap && changed
|
|
117
130
|
? composeSourceMaps(
|
|
118
131
|
rewrittenSource.generateMap({ hires: true }).toString(),
|
|
119
132
|
resolvedModule.sourceMap,
|
|
@@ -148,9 +161,11 @@ function prependHmrContext(
|
|
|
148
161
|
}
|
|
149
162
|
|
|
150
163
|
async function createEmittedAsset(content: string): Promise<EmittedAsset> {
|
|
164
|
+
let fingerprint = await hashContent(content)
|
|
151
165
|
return {
|
|
152
166
|
content,
|
|
153
|
-
etag: `W/"${
|
|
167
|
+
etag: `W/"${fingerprint}"`,
|
|
168
|
+
fingerprint,
|
|
154
169
|
}
|
|
155
170
|
}
|
|
156
171
|
|