@remix-run/assets 0.0.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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +325 -2
  3. package/dist/assets.d.ts +3 -0
  4. package/dist/assets.d.ts.map +1 -0
  5. package/dist/assets.js +1 -0
  6. package/dist/lib/access.d.ts +10 -0
  7. package/dist/lib/access.d.ts.map +1 -0
  8. package/dist/lib/access.js +14 -0
  9. package/dist/lib/asset-server.d.ts +139 -0
  10. package/dist/lib/asset-server.d.ts.map +1 -0
  11. package/dist/lib/asset-server.js +338 -0
  12. package/dist/lib/compilation-error.d.ts +33 -0
  13. package/dist/lib/compilation-error.d.ts.map +1 -0
  14. package/dist/lib/compilation-error.js +32 -0
  15. package/dist/lib/file-matcher.d.ts +6 -0
  16. package/dist/lib/file-matcher.d.ts.map +1 -0
  17. package/dist/lib/file-matcher.js +44 -0
  18. package/dist/lib/fingerprint.d.ts +12 -0
  19. package/dist/lib/fingerprint.d.ts.map +1 -0
  20. package/dist/lib/fingerprint.js +49 -0
  21. package/dist/lib/module-store.d.ts +41 -0
  22. package/dist/lib/module-store.d.ts.map +1 -0
  23. package/dist/lib/module-store.js +230 -0
  24. package/dist/lib/paths.d.ts +8 -0
  25. package/dist/lib/paths.d.ts.map +1 -0
  26. package/dist/lib/paths.js +50 -0
  27. package/dist/lib/routes.d.ts +13 -0
  28. package/dist/lib/routes.d.ts.map +1 -0
  29. package/dist/lib/routes.js +94 -0
  30. package/dist/lib/scripts/cjs-check.d.ts +3 -0
  31. package/dist/lib/scripts/cjs-check.d.ts.map +1 -0
  32. package/dist/lib/scripts/cjs-check.js +398 -0
  33. package/dist/lib/scripts/compiler.d.ts +62 -0
  34. package/dist/lib/scripts/compiler.d.ts.map +1 -0
  35. package/dist/lib/scripts/compiler.js +439 -0
  36. package/dist/lib/scripts/emit.d.ts +25 -0
  37. package/dist/lib/scripts/emit.d.ts.map +1 -0
  38. package/dist/lib/scripts/emit.js +63 -0
  39. package/dist/lib/scripts/resolve.d.ts +50 -0
  40. package/dist/lib/scripts/resolve.d.ts.map +1 -0
  41. package/dist/lib/scripts/resolve.js +236 -0
  42. package/dist/lib/scripts/transform.d.ts +64 -0
  43. package/dist/lib/scripts/transform.d.ts.map +1 -0
  44. package/dist/lib/scripts/transform.js +373 -0
  45. package/dist/lib/source-maps.d.ts +4 -0
  46. package/dist/lib/source-maps.d.ts.map +1 -0
  47. package/dist/lib/source-maps.js +62 -0
  48. package/dist/lib/styles/compiler.d.ts +52 -0
  49. package/dist/lib/styles/compiler.d.ts.map +1 -0
  50. package/dist/lib/styles/compiler.js +272 -0
  51. package/dist/lib/styles/emit.d.ts +25 -0
  52. package/dist/lib/styles/emit.d.ts.map +1 -0
  53. package/dist/lib/styles/emit.js +78 -0
  54. package/dist/lib/styles/resolve.d.ts +48 -0
  55. package/dist/lib/styles/resolve.d.ts.map +1 -0
  56. package/dist/lib/styles/resolve.js +188 -0
  57. package/dist/lib/styles/transform.d.ts +47 -0
  58. package/dist/lib/styles/transform.d.ts.map +1 -0
  59. package/dist/lib/styles/transform.js +131 -0
  60. package/dist/lib/target.d.ts +21 -0
  61. package/dist/lib/target.d.ts.map +1 -0
  62. package/dist/lib/target.js +127 -0
  63. package/dist/lib/watch.d.ts +22 -0
  64. package/dist/lib/watch.d.ts.map +1 -0
  65. package/dist/lib/watch.js +96 -0
  66. package/package.json +55 -12
  67. package/src/assets.ts +2 -0
  68. package/src/lib/access.ts +24 -0
  69. package/src/lib/asset-server.ts +537 -0
  70. package/src/lib/compilation-error.ts +61 -0
  71. package/src/lib/file-matcher.ts +63 -0
  72. package/src/lib/fingerprint.ts +65 -0
  73. package/src/lib/module-store.ts +340 -0
  74. package/src/lib/paths.ts +66 -0
  75. package/src/lib/routes.ts +164 -0
  76. package/src/lib/scripts/cjs-check.ts +476 -0
  77. package/src/lib/scripts/compiler.ts +640 -0
  78. package/src/lib/scripts/emit.ts +122 -0
  79. package/src/lib/scripts/resolve.ts +433 -0
  80. package/src/lib/scripts/transform.ts +609 -0
  81. package/src/lib/source-maps.ts +75 -0
  82. package/src/lib/styles/compiler.ts +400 -0
  83. package/src/lib/styles/emit.ts +137 -0
  84. package/src/lib/styles/resolve.ts +316 -0
  85. package/src/lib/styles/transform.ts +226 -0
  86. package/src/lib/target.ts +196 -0
  87. package/src/lib/watch.ts +136 -0
@@ -0,0 +1,122 @@
1
+ import MagicString from 'magic-string'
2
+
3
+ import {
4
+ createAssetServerCompilationError,
5
+ isAssetServerCompilationError,
6
+ } from '../compilation-error.ts'
7
+ import { hashContent } from '../fingerprint.ts'
8
+ import type { ResolvedModule } from './resolve.ts'
9
+ import { composeSourceMaps } from '../source-maps.ts'
10
+ import type { AssetServerCompilationError } from '../compilation-error.ts'
11
+
12
+ export type EmittedAsset = {
13
+ content: string
14
+ etag: string
15
+ }
16
+
17
+ export type EmittedModule = {
18
+ code: EmittedAsset
19
+ fingerprint: string | null
20
+ importUrls: string[]
21
+ sourceMap: EmittedAsset | null
22
+ }
23
+
24
+ type EmitResult =
25
+ | {
26
+ ok: true
27
+ value: EmittedModule
28
+ }
29
+ | {
30
+ ok: false
31
+ error: AssetServerCompilationError
32
+ }
33
+
34
+ export async function emitResolvedModule(
35
+ resolvedModule: ResolvedModule,
36
+ options: {
37
+ getServedUrl(identityPath: string): Promise<string>
38
+ sourceMaps?: 'external' | 'inline'
39
+ },
40
+ ): Promise<EmitResult> {
41
+ try {
42
+ let importUrls = await Promise.all(
43
+ resolvedModule.deps.map((depPath) => options.getServedUrl(depPath)),
44
+ )
45
+ let rewriteResult = await rewriteImports(resolvedModule, options)
46
+ let finalCode = rewriteResult.code
47
+
48
+ if (rewriteResult.sourceMap) {
49
+ if (options.sourceMaps === 'inline') {
50
+ let encoded = Buffer.from(rewriteResult.sourceMap).toString('base64')
51
+ finalCode += `\n//# sourceMappingURL=data:application/json;base64,${encoded}`
52
+ } else if (options.sourceMaps === 'external') {
53
+ finalCode += `\n//# sourceMappingURL=${await options.getServedUrl(resolvedModule.identityPath)}.map`
54
+ }
55
+ }
56
+
57
+ return {
58
+ ok: true,
59
+ value: {
60
+ code: await createEmittedAsset(finalCode),
61
+ fingerprint: resolvedModule.fingerprint,
62
+ importUrls,
63
+ sourceMap: rewriteResult.sourceMap
64
+ ? await createEmittedAsset(rewriteResult.sourceMap)
65
+ : null,
66
+ },
67
+ }
68
+ } catch (error) {
69
+ return {
70
+ ok: false,
71
+ error: toEmitError(error, resolvedModule.identityPath),
72
+ }
73
+ }
74
+ }
75
+
76
+ async function rewriteImports(
77
+ resolvedModule: ResolvedModule,
78
+ options: {
79
+ getServedUrl(identityPath: string): Promise<string>
80
+ },
81
+ ): Promise<{ code: string; sourceMap: string | null }> {
82
+ let rewrittenSource = new MagicString(resolvedModule.rawCode)
83
+
84
+ for (let imported of resolvedModule.imports) {
85
+ let url = await options.getServedUrl(imported.depPath)
86
+ rewrittenSource.overwrite(
87
+ imported.start,
88
+ imported.end,
89
+ imported.quote ? `${imported.quote}${url}${imported.quote}` : url,
90
+ )
91
+ }
92
+
93
+ let code = rewrittenSource.toString()
94
+ let sourceMap =
95
+ resolvedModule.sourceMap && resolvedModule.imports.length > 0
96
+ ? composeSourceMaps(
97
+ rewrittenSource.generateMap({ hires: true }).toString(),
98
+ resolvedModule.sourceMap,
99
+ )
100
+ : resolvedModule.sourceMap
101
+
102
+ return { code, sourceMap }
103
+ }
104
+
105
+ async function createEmittedAsset(content: string): Promise<EmittedAsset> {
106
+ return {
107
+ content,
108
+ etag: `W/"${await hashContent(content)}"`,
109
+ }
110
+ }
111
+
112
+ function toEmitError(error: unknown, identityPath: string): AssetServerCompilationError {
113
+ if (isAssetServerCompilationError(error)) return error
114
+
115
+ return createAssetServerCompilationError(
116
+ `Failed to emit script ${identityPath}. ${error instanceof Error ? error.message : String(error)}`,
117
+ {
118
+ cause: error,
119
+ code: 'EMIT_FAILED',
120
+ },
121
+ )
122
+ }
@@ -0,0 +1,433 @@
1
+ import * as fs from 'node:fs'
2
+ import * as path from 'node:path'
3
+ import type { ResolverFactory } from 'oxc-resolver'
4
+
5
+ import {
6
+ createAssetServerCompilationError,
7
+ isAssetServerCompilationError,
8
+ } from '../compilation-error.ts'
9
+ import type { AssetServerCompilationError } from '../compilation-error.ts'
10
+ import type { ModuleRecord, ModuleTracking } from '../module-store.ts'
11
+ import { normalizeFilePath } from '../paths.ts'
12
+ import type { CompiledRoutes } from '../routes.ts'
13
+ import type { ResolveModuleResult, TransformedModule } from './transform.ts'
14
+ import type { EmittedModule } from './emit.ts'
15
+
16
+ type ScriptRecord = ModuleRecord<TransformedModule, ResolvedModule, EmittedModule>
17
+
18
+ export const resolverExtensionAlias = {
19
+ '.js': ['.js', '.ts', '.tsx', '.jsx'],
20
+ '.jsx': ['.jsx', '.tsx'],
21
+ '.mjs': ['.mjs', '.mts'],
22
+ } satisfies Record<string, string[]>
23
+
24
+ export const resolverExtensions = ['.ts', '.tsx', '.js', '.jsx', '.mts', '.mjs']
25
+ export const supportedScriptExtensions = ['.ts', '.tsx', '.js', '.jsx', '.mts', '.mjs']
26
+ const supportedScriptExtensionSet = new Set<string>(supportedScriptExtensions)
27
+
28
+ type ResolvedImport = {
29
+ depPath: string
30
+ end: number
31
+ quote?: '"' | "'" | '`'
32
+ start: number
33
+ }
34
+
35
+ type RelativeImportResolution = {
36
+ candidatePaths: readonly string[]
37
+ candidatePrefixes: readonly string[]
38
+ specifier: string
39
+ }
40
+
41
+ type TrackedResolution = RelativeImportResolution & {
42
+ resolvedIdentityPath: string | null
43
+ }
44
+
45
+ export type ResolvedModule = {
46
+ deps: string[]
47
+ fingerprint: string | null
48
+ identityPath: string
49
+ imports: ResolvedImport[]
50
+ trackedFiles: string[]
51
+ rawCode: string
52
+ resolvedPath: string
53
+ sourceMap: string | null
54
+ stableUrlPathname: string
55
+ }
56
+
57
+ type ResolveResult = {
58
+ tracking: ModuleTracking
59
+ } & (
60
+ | {
61
+ ok: true
62
+ value: ResolvedModule
63
+ }
64
+ | {
65
+ ok: false
66
+ error: AssetServerCompilationError
67
+ }
68
+ )
69
+
70
+ export type ResolveArgs = {
71
+ isAllowed(absolutePath: string): boolean
72
+ isWatchIgnored(filePath: string): boolean
73
+ resolveModulePath(absolutePath: string): ResolveModuleResult | null
74
+ resolverFactory: ResolverFactory
75
+ routes: CompiledRoutes
76
+ }
77
+
78
+ type ResolvedSpec = {
79
+ absolutePath: string | null
80
+ packageJsonPath: string | null
81
+ specifier: string
82
+ }
83
+
84
+ export async function resolveModule(
85
+ record: ScriptRecord,
86
+ transformed: TransformedModule,
87
+ args: ResolveArgs,
88
+ ): Promise<ResolveResult> {
89
+ let trackedFiles = new Set(transformed.trackedFiles)
90
+ let trackedResolutions: TrackedResolution[] = []
91
+ let resolvedImports: Map<string, ResolvedSpec>
92
+
93
+ try {
94
+ resolvedImports =
95
+ transformed.unresolvedImports.length > 0
96
+ ? await batchResolveSpecifiers(
97
+ getUniqueSpecifiers(transformed.unresolvedImports),
98
+ transformed.resolvedPath,
99
+ args.resolverFactory,
100
+ )
101
+ : new Map<string, ResolvedSpec>()
102
+ } catch (error) {
103
+ return failResolve(error, trackedFiles, trackedResolutions, transformed.resolvedPath, {
104
+ isWatchIgnored: args.isWatchIgnored,
105
+ })
106
+ }
107
+
108
+ let importsWithPaths: ResolvedImport[] = []
109
+ let deps = new Set<string>()
110
+
111
+ for (let unresolved of transformed.unresolvedImports) {
112
+ let trackedResolution = getTrackedRelativeImportResolution(
113
+ transformed.importerDir,
114
+ unresolved.specifier,
115
+ args.isWatchIgnored,
116
+ )
117
+
118
+ let resolvedSpec = resolvedImports.get(unresolved.specifier)
119
+ if (!resolvedSpec?.absolutePath) {
120
+ return failResolve(
121
+ createAssetServerCompilationError(
122
+ `Failed to resolve import "${unresolved.specifier}" in ${transformed.resolvedPath}. ` +
123
+ `Ensure it resolves to a file within the configured asset server fileMap, or mark it as external.`,
124
+ {
125
+ code: 'IMPORT_RESOLUTION_FAILED',
126
+ },
127
+ ),
128
+ trackedFiles,
129
+ trackedResolutions,
130
+ transformed.resolvedPath,
131
+ { isWatchIgnored: args.isWatchIgnored, trackedResolution },
132
+ )
133
+ }
134
+
135
+ let resolvedImport = args.resolveModulePath(resolvedSpec.absolutePath)
136
+ if (!resolvedImport) {
137
+ return failResolve(
138
+ createAssetServerCompilationError(
139
+ `Import "${unresolved.specifier}" in ${transformed.resolvedPath}, resolved to "${resolvedSpec.absolutePath}", is not a supported script file. ` +
140
+ `Supported extensions are ${supportedScriptExtensions.join(', ')}.`,
141
+ {
142
+ code: 'IMPORT_NOT_SUPPORTED',
143
+ },
144
+ ),
145
+ trackedFiles,
146
+ trackedResolutions,
147
+ transformed.resolvedPath,
148
+ { isWatchIgnored: args.isWatchIgnored, trackedResolution },
149
+ )
150
+ }
151
+
152
+ if (!args.isAllowed(resolvedImport.identityPath)) {
153
+ return failResolve(
154
+ createAssetServerCompilationError(
155
+ `Import "${unresolved.specifier}" in ${transformed.resolvedPath}, resolved to "${resolvedImport.identityPath}", is not allowed by the asset server allow/deny configuration. ` +
156
+ `Add a matching allow rule for this file path, remove a conflicting deny rule for this file path, or mark this import as external.`,
157
+ {
158
+ code: 'IMPORT_NOT_ALLOWED',
159
+ },
160
+ ),
161
+ trackedFiles,
162
+ trackedResolutions,
163
+ transformed.resolvedPath,
164
+ { isWatchIgnored: args.isWatchIgnored, trackedResolution },
165
+ )
166
+ }
167
+
168
+ let stableUrlPathname = args.routes.toUrlPathname(resolvedImport.identityPath)
169
+ if (!stableUrlPathname) {
170
+ return failResolve(
171
+ createAssetServerCompilationError(
172
+ `Import "${unresolved.specifier}" in ${transformed.resolvedPath}, resolved to "${resolvedImport.identityPath}", is outside all configured fileMap entries. ` +
173
+ `Add a matching fileMap entry for this file path, or mark this import as external.`,
174
+ {
175
+ code: 'IMPORT_OUTSIDE_FILE_MAP',
176
+ },
177
+ ),
178
+ trackedFiles,
179
+ trackedResolutions,
180
+ transformed.resolvedPath,
181
+ { isWatchIgnored: args.isWatchIgnored, trackedResolution },
182
+ )
183
+ }
184
+
185
+ deps.add(resolvedImport.identityPath)
186
+
187
+ if (transformed.packageSpecifiers.includes(unresolved.specifier)) {
188
+ let packageJsonPath =
189
+ resolvedSpec.packageJsonPath ?? findNearestPackageJsonPath(resolvedImport.resolvedPath)
190
+ if (packageJsonPath && !args.isWatchIgnored(packageJsonPath)) {
191
+ trackedFiles.add(packageJsonPath)
192
+ }
193
+ }
194
+
195
+ if (trackedResolution) {
196
+ trackedResolutions.push({
197
+ ...trackedResolution,
198
+ resolvedIdentityPath: resolvedImport.identityPath,
199
+ })
200
+ }
201
+
202
+ importsWithPaths.push({
203
+ depPath: resolvedImport.identityPath,
204
+ end: unresolved.end,
205
+ quote: unresolved.quote,
206
+ start: unresolved.start,
207
+ })
208
+ }
209
+
210
+ return {
211
+ ok: true,
212
+ tracking: toResolveTracking(trackedFiles, trackedResolutions),
213
+ value: {
214
+ deps: [...deps],
215
+ fingerprint: transformed.fingerprint,
216
+ identityPath: record.identityPath,
217
+ imports: importsWithPaths,
218
+ trackedFiles: [...trackedFiles],
219
+ rawCode: transformed.rawCode,
220
+ resolvedPath: transformed.resolvedPath,
221
+ sourceMap: transformed.sourceMap,
222
+ stableUrlPathname: transformed.stableUrlPathname,
223
+ },
224
+ }
225
+ }
226
+
227
+ function findNearestPackageJsonPath(filePath: string): string | null {
228
+ let directory = path.dirname(filePath)
229
+
230
+ while (true) {
231
+ let packageJsonPath = path.join(directory, 'package.json')
232
+ if (fs.existsSync(packageJsonPath)) {
233
+ return normalizeFilePath(packageJsonPath)
234
+ }
235
+
236
+ let parentDirectory = path.dirname(directory)
237
+ if (parentDirectory === directory) return null
238
+ directory = parentDirectory
239
+ }
240
+ }
241
+
242
+ function isRelativeImportSpecifier(specifier: string): boolean {
243
+ return specifier.startsWith('./') || specifier.startsWith('../')
244
+ }
245
+
246
+ function getTrackedRelativeImportResolution(
247
+ importerDir: string,
248
+ specifier: string,
249
+ isWatchIgnored: (filePath: string) => boolean,
250
+ ): RelativeImportResolution | null {
251
+ if (!isRelativeImportSpecifier(specifier)) return null
252
+
253
+ let candidatePath = resolveCandidateBasePath(importerDir, specifier)
254
+ let candidatePrefixes = [`${candidatePath}/`].filter(
255
+ (candidatePrefix) => !isWatchIgnored(candidatePrefix.replace(/\/+$/, '') || '/'),
256
+ )
257
+ let extension = path.extname(specifier)
258
+ if (extension === '') {
259
+ let candidatePaths = [
260
+ candidatePath,
261
+ ...supportedScriptExtensions.map(
262
+ (candidateExtension) => `${candidatePath}${candidateExtension}`,
263
+ ),
264
+ ].filter((candidatePath) => !isWatchIgnored(candidatePath))
265
+
266
+ return candidatePaths.length === 0 && candidatePrefixes.length === 0
267
+ ? null
268
+ : {
269
+ candidatePaths,
270
+ candidatePrefixes,
271
+ specifier,
272
+ }
273
+ }
274
+
275
+ let candidateExtensions = resolverExtensionAlias[extension as keyof typeof resolverExtensionAlias]
276
+ if (!candidateExtensions && !supportedScriptExtensionSet.has(extension)) {
277
+ let candidatePaths = [
278
+ candidatePath,
279
+ ...supportedScriptExtensions.map(
280
+ (candidateExtension) => `${candidatePath}${candidateExtension}`,
281
+ ),
282
+ ].filter((candidatePath) => !isWatchIgnored(candidatePath))
283
+
284
+ return candidatePaths.length === 0 && candidatePrefixes.length === 0
285
+ ? null
286
+ : {
287
+ candidatePaths,
288
+ candidatePrefixes,
289
+ specifier,
290
+ }
291
+ }
292
+
293
+ if (!candidateExtensions) return null
294
+
295
+ let candidatePaths = [
296
+ candidatePath,
297
+ ...candidateExtensions.map(
298
+ (candidateExtension) =>
299
+ `${candidatePath.slice(0, candidatePath.length - extension.length)}${candidateExtension}`,
300
+ ),
301
+ ].filter((candidatePath) => !isWatchIgnored(candidatePath))
302
+
303
+ return candidatePaths.length === 0 && candidatePrefixes.length === 0
304
+ ? null
305
+ : {
306
+ candidatePaths,
307
+ candidatePrefixes,
308
+ specifier,
309
+ }
310
+ }
311
+
312
+ function resolveCandidateBasePath(importerDir: string, specifier: string): string {
313
+ return normalizeFilePath(path.resolve(importerDir, specifier))
314
+ }
315
+
316
+ async function batchResolveSpecifiers(
317
+ specifiers: string[],
318
+ importerPath: string,
319
+ resolverFactory: ResolveArgs['resolverFactory'],
320
+ ): Promise<Map<string, ResolvedSpec>> {
321
+ let resolvedBySpecifier = new Map<string, ResolvedSpec>()
322
+ if (specifiers.length === 0) return resolvedBySpecifier
323
+
324
+ try {
325
+ for (let specifier of specifiers) {
326
+ let resolutionResult = await resolverFactory.resolveFileAsync(importerPath, specifier)
327
+ if (resolutionResult.error) {
328
+ throw createAssetServerCompilationError(
329
+ `Failed to resolve import "${specifier}" in ${importerPath}. ` +
330
+ `Ensure it resolves to a file within the configured asset server fileMap, or mark it as external.`,
331
+ {
332
+ code: 'IMPORT_RESOLUTION_FAILED',
333
+ },
334
+ )
335
+ }
336
+
337
+ resolvedBySpecifier.set(specifier, {
338
+ absolutePath:
339
+ resolutionResult.path && path.isAbsolute(resolutionResult.path)
340
+ ? normalizeFilePath(resolutionResult.path)
341
+ : null,
342
+ packageJsonPath: resolutionResult.packageJsonPath
343
+ ? normalizeFilePath(resolutionResult.packageJsonPath)
344
+ : null,
345
+ specifier,
346
+ })
347
+ }
348
+ } catch (error) {
349
+ if (isAssetServerCompilationError(error) && error.code === 'IMPORT_RESOLUTION_FAILED') {
350
+ throw error
351
+ }
352
+
353
+ throw createAssetServerCompilationError(
354
+ `Failed to resolve imports in ${importerPath}. ${formatUnknownError(error)}`,
355
+ {
356
+ cause: error,
357
+ code: 'IMPORT_RESOLUTION_FAILED',
358
+ },
359
+ )
360
+ }
361
+
362
+ return resolvedBySpecifier
363
+ }
364
+
365
+ function getUniqueSpecifiers(unresolvedImports: TransformedModule['unresolvedImports']): string[] {
366
+ return [...new Set(unresolvedImports.map((unresolved) => unresolved.specifier))]
367
+ }
368
+
369
+ function formatUnknownError(error: unknown): string {
370
+ return error instanceof Error ? error.message : String(error)
371
+ }
372
+
373
+ function failResolve(
374
+ error: unknown,
375
+ trackedFiles: ReadonlySet<string>,
376
+ trackedResolutions: readonly TrackedResolution[],
377
+ importerPath: string,
378
+ options: {
379
+ isWatchIgnored?: (filePath: string) => boolean
380
+ trackedResolution?: RelativeImportResolution | null
381
+ } = {},
382
+ ): ResolveResult {
383
+ return {
384
+ ok: false,
385
+ error: toResolveError(error, importerPath),
386
+ tracking: toResolveTracking(
387
+ trackedFiles,
388
+ appendFailedTrackedResolution(trackedResolutions, options.trackedResolution),
389
+ ),
390
+ }
391
+ }
392
+
393
+ function toResolveTracking(
394
+ trackedFiles: ReadonlySet<string> | readonly string[],
395
+ trackedResolutions: readonly TrackedResolution[],
396
+ ): ModuleTracking {
397
+ return {
398
+ trackedFiles: [
399
+ ...trackedFiles,
400
+ ...trackedResolutions.flatMap((trackedResolution) => trackedResolution.candidatePaths),
401
+ ],
402
+ trackedDirectories: trackedResolutions.flatMap(
403
+ (trackedResolution) => trackedResolution.candidatePrefixes,
404
+ ),
405
+ }
406
+ }
407
+
408
+ function appendFailedTrackedResolution(
409
+ trackedResolutions: readonly TrackedResolution[],
410
+ trackedResolution: RelativeImportResolution | null | undefined,
411
+ ): TrackedResolution[] {
412
+ if (trackedResolution == null) return [...trackedResolutions]
413
+
414
+ return [
415
+ ...trackedResolutions,
416
+ {
417
+ ...trackedResolution,
418
+ resolvedIdentityPath: null,
419
+ },
420
+ ]
421
+ }
422
+
423
+ function toResolveError(error: unknown, importerPath: string): AssetServerCompilationError {
424
+ if (isAssetServerCompilationError(error)) return error
425
+
426
+ return createAssetServerCompilationError(
427
+ `Failed to resolve imports in ${importerPath}. ${formatUnknownError(error)}`,
428
+ {
429
+ cause: error,
430
+ code: 'IMPORT_RESOLUTION_FAILED',
431
+ },
432
+ )
433
+ }