@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.
- package/LICENSE +21 -0
- package/README.md +325 -2
- package/dist/assets.d.ts +3 -0
- package/dist/assets.d.ts.map +1 -0
- package/dist/assets.js +1 -0
- package/dist/lib/access.d.ts +10 -0
- package/dist/lib/access.d.ts.map +1 -0
- package/dist/lib/access.js +14 -0
- package/dist/lib/asset-server.d.ts +139 -0
- package/dist/lib/asset-server.d.ts.map +1 -0
- package/dist/lib/asset-server.js +338 -0
- package/dist/lib/compilation-error.d.ts +33 -0
- package/dist/lib/compilation-error.d.ts.map +1 -0
- package/dist/lib/compilation-error.js +32 -0
- package/dist/lib/file-matcher.d.ts +6 -0
- package/dist/lib/file-matcher.d.ts.map +1 -0
- package/dist/lib/file-matcher.js +44 -0
- package/dist/lib/fingerprint.d.ts +12 -0
- package/dist/lib/fingerprint.d.ts.map +1 -0
- package/dist/lib/fingerprint.js +49 -0
- package/dist/lib/module-store.d.ts +41 -0
- package/dist/lib/module-store.d.ts.map +1 -0
- package/dist/lib/module-store.js +230 -0
- package/dist/lib/paths.d.ts +8 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +50 -0
- package/dist/lib/routes.d.ts +13 -0
- package/dist/lib/routes.d.ts.map +1 -0
- package/dist/lib/routes.js +94 -0
- package/dist/lib/scripts/cjs-check.d.ts +3 -0
- package/dist/lib/scripts/cjs-check.d.ts.map +1 -0
- package/dist/lib/scripts/cjs-check.js +398 -0
- package/dist/lib/scripts/compiler.d.ts +62 -0
- package/dist/lib/scripts/compiler.d.ts.map +1 -0
- package/dist/lib/scripts/compiler.js +439 -0
- package/dist/lib/scripts/emit.d.ts +25 -0
- package/dist/lib/scripts/emit.d.ts.map +1 -0
- package/dist/lib/scripts/emit.js +63 -0
- package/dist/lib/scripts/resolve.d.ts +50 -0
- package/dist/lib/scripts/resolve.d.ts.map +1 -0
- package/dist/lib/scripts/resolve.js +236 -0
- package/dist/lib/scripts/transform.d.ts +64 -0
- package/dist/lib/scripts/transform.d.ts.map +1 -0
- package/dist/lib/scripts/transform.js +373 -0
- package/dist/lib/source-maps.d.ts +4 -0
- package/dist/lib/source-maps.d.ts.map +1 -0
- package/dist/lib/source-maps.js +62 -0
- 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/dist/lib/watch.d.ts +22 -0
- package/dist/lib/watch.d.ts.map +1 -0
- package/dist/lib/watch.js +96 -0
- package/package.json +55 -12
- package/src/assets.ts +2 -0
- package/src/lib/access.ts +24 -0
- package/src/lib/asset-server.ts +537 -0
- package/src/lib/compilation-error.ts +61 -0
- package/src/lib/file-matcher.ts +63 -0
- package/src/lib/fingerprint.ts +65 -0
- package/src/lib/module-store.ts +340 -0
- package/src/lib/paths.ts +66 -0
- package/src/lib/routes.ts +164 -0
- package/src/lib/scripts/cjs-check.ts +476 -0
- package/src/lib/scripts/compiler.ts +640 -0
- package/src/lib/scripts/emit.ts +122 -0
- package/src/lib/scripts/resolve.ts +433 -0
- package/src/lib/scripts/transform.ts +609 -0
- package/src/lib/source-maps.ts +75 -0
- 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/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
|
+
}
|