@remix-run/assets 0.0.0 → 0.1.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 (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +319 -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 +137 -0
  10. package/dist/lib/asset-server.d.ts.map +1 -0
  11. package/dist/lib/asset-server.js +242 -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 +43 -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/paths.d.ts +8 -0
  22. package/dist/lib/paths.d.ts.map +1 -0
  23. package/dist/lib/paths.js +50 -0
  24. package/dist/lib/routes.d.ts +13 -0
  25. package/dist/lib/routes.d.ts.map +1 -0
  26. package/dist/lib/routes.js +94 -0
  27. package/dist/lib/scripts/cjs-check.d.ts +3 -0
  28. package/dist/lib/scripts/cjs-check.d.ts.map +1 -0
  29. package/dist/lib/scripts/cjs-check.js +398 -0
  30. package/dist/lib/scripts/compiler.d.ts +62 -0
  31. package/dist/lib/scripts/compiler.d.ts.map +1 -0
  32. package/dist/lib/scripts/compiler.js +435 -0
  33. package/dist/lib/scripts/emit.d.ts +25 -0
  34. package/dist/lib/scripts/emit.d.ts.map +1 -0
  35. package/dist/lib/scripts/emit.js +63 -0
  36. package/dist/lib/scripts/resolve.d.ts +60 -0
  37. package/dist/lib/scripts/resolve.d.ts.map +1 -0
  38. package/dist/lib/scripts/resolve.js +230 -0
  39. package/dist/lib/scripts/store.d.ts +40 -0
  40. package/dist/lib/scripts/store.d.ts.map +1 -0
  41. package/dist/lib/scripts/store.js +228 -0
  42. package/dist/lib/scripts/transform.d.ts +62 -0
  43. package/dist/lib/scripts/transform.d.ts.map +1 -0
  44. package/dist/lib/scripts/transform.js +362 -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 +56 -0
  48. package/dist/lib/watch.d.ts +22 -0
  49. package/dist/lib/watch.d.ts.map +1 -0
  50. package/dist/lib/watch.js +96 -0
  51. package/package.json +50 -12
  52. package/src/assets.ts +2 -0
  53. package/src/lib/access.ts +24 -0
  54. package/src/lib/asset-server.ts +415 -0
  55. package/src/lib/compilation-error.ts +61 -0
  56. package/src/lib/file-matcher.ts +62 -0
  57. package/src/lib/fingerprint.ts +65 -0
  58. package/src/lib/paths.ts +66 -0
  59. package/src/lib/routes.ts +164 -0
  60. package/src/lib/scripts/cjs-check.ts +476 -0
  61. package/src/lib/scripts/compiler.ts +622 -0
  62. package/src/lib/scripts/emit.ts +122 -0
  63. package/src/lib/scripts/resolve.ts +422 -0
  64. package/src/lib/scripts/store.ts +327 -0
  65. package/src/lib/scripts/transform.ts +594 -0
  66. package/src/lib/source-maps.ts +68 -0
  67. package/src/lib/watch.ts +136 -0
@@ -0,0 +1,594 @@
1
+ import * as fs from 'node:fs'
2
+ import * as fsp from 'node:fs/promises'
3
+ import * as path from 'node:path'
4
+ import { getTsconfig } from 'get-tsconfig'
5
+ import { minify } from 'oxc-minify'
6
+ import { transform as oxcTransform } from 'oxc-transform'
7
+ import { init as esModuleLexerInit, parse as esModuleLexer } from 'es-module-lexer'
8
+ import type { Cache, TsConfigJsonResolved } from 'get-tsconfig'
9
+ import type { TransformOptions as OxcTransformOptions } from 'oxc-transform'
10
+
11
+ import { isCommonJS, mayContainCommonJSModuleGlobals } from './cjs-check.ts'
12
+ import {
13
+ createAssetServerCompilationError,
14
+ isAssetServerCompilationError,
15
+ } from '../compilation-error.ts'
16
+ import type { AssetServerCompilationError } from '../compilation-error.ts'
17
+ import { generateFingerprint } from '../fingerprint.ts'
18
+ import { normalizeFilePath } from '../paths.ts'
19
+ import type { CompiledRoutes } from '../routes.ts'
20
+ import type { ScriptsTarget } from '../asset-server.ts'
21
+ import type { ModuleRecord } from './store.ts'
22
+ import { composeSourceMaps, rewriteSourceMapSources, stringifySourceMap } from '../source-maps.ts'
23
+
24
+ type SourceLanguage = 'js' | 'jsx' | 'ts' | 'tsx'
25
+
26
+ const scriptModuleTypes = [
27
+ { extension: '.js', lang: 'js' },
28
+ { extension: '.jsx', lang: 'jsx' },
29
+ { extension: '.mjs', lang: 'js' },
30
+ { extension: '.mts', lang: 'ts' },
31
+ { extension: '.ts', lang: 'ts' },
32
+ { extension: '.tsx', lang: 'tsx' },
33
+ ] as const satisfies ReadonlyArray<{ extension: string; lang: SourceLanguage }>
34
+
35
+ const sourceLanguageByExtension = new Map<string, SourceLanguage>(
36
+ scriptModuleTypes.map(({ extension, lang }) => [extension, lang] as const),
37
+ )
38
+
39
+ const supportedTsconfigTransformCompilerOptions = {
40
+ allowNamespaces: 'allowNamespaces',
41
+ emitDecoratorMetadata: 'emitDecoratorMetadata',
42
+ experimentalDecorators: 'experimentalDecorators',
43
+ jsx: 'jsx',
44
+ jsxFactory: 'jsxFactory',
45
+ jsxFragmentFactory: 'jsxFragmentFactory',
46
+ jsxImportSource: 'jsxImportSource',
47
+ useDefineForClassFields: 'useDefineForClassFields',
48
+ } as const
49
+
50
+ export type ResolveModuleResult = {
51
+ identityPath: string
52
+ resolvedPath: string
53
+ }
54
+
55
+ type UnresolvedImport = {
56
+ end: number
57
+ quote?: '"' | "'" | '`'
58
+ specifier: string
59
+ start: number
60
+ }
61
+
62
+ export type TransformedModule = {
63
+ fingerprint: string | null
64
+ identityPath: string
65
+ importerDir: string
66
+ packageSpecifiers: string[]
67
+ rawCode: string
68
+ resolvedPath: string
69
+ sourceMap: string | null
70
+ stableUrlPathname: string
71
+ trackedFiles: string[]
72
+ unresolvedImports: UnresolvedImport[]
73
+ }
74
+
75
+ export type TransformFailureState = {
76
+ trackedFiles: readonly string[]
77
+ }
78
+
79
+ type TransformResult =
80
+ | {
81
+ ok: true
82
+ value: TransformedModule
83
+ }
84
+ | ({
85
+ ok: false
86
+ error: AssetServerCompilationError
87
+ } & TransformFailureState)
88
+
89
+ type TsconfigTransformOptions = {
90
+ trackedFiles: string[]
91
+ tsconfigRaw?: TsConfigJsonResolved
92
+ }
93
+
94
+ type TsconfigTransformOptionsResolver = ReturnType<typeof createTsconfigTransformOptionsResolver>
95
+
96
+ export type TransformArgs = {
97
+ buildId: string | null
98
+ define: Record<string, string> | null
99
+ externalSet: ReadonlySet<string>
100
+ isWatchIgnored(filePath: string): boolean
101
+ minify: boolean
102
+ resolveActualPath(identityPath: string): string | null
103
+ routes: CompiledRoutes
104
+ sourceMapSourcePaths: 'absolute' | 'url'
105
+ sourceMaps: 'external' | 'inline' | null
106
+ target: ScriptsTarget | null
107
+ tsconfigTransformOptionsResolver: TsconfigTransformOptionsResolver
108
+ }
109
+
110
+ export function createTsconfigTransformOptionsResolver() {
111
+ let fileSystemCache: Cache = new Map()
112
+ let transformOptionsByDirectory = new Map<string, TsconfigTransformOptions>()
113
+
114
+ return {
115
+ clear() {
116
+ fileSystemCache = new Map()
117
+ transformOptionsByDirectory.clear()
118
+ },
119
+ getTransformOptions(
120
+ filePath: string,
121
+ isWatchIgnored: (filePath: string) => boolean,
122
+ ): TsconfigTransformOptions {
123
+ let directory = path.dirname(filePath)
124
+ let cached = transformOptionsByDirectory.get(directory)
125
+ if (cached) return cached
126
+
127
+ let tsconfig = getTsconfig(directory, 'tsconfig.json', fileSystemCache)
128
+ if (!tsconfig) {
129
+ let transformOptions = { trackedFiles: [] }
130
+ transformOptionsByDirectory.set(directory, transformOptions)
131
+ return transformOptions
132
+ }
133
+
134
+ let tsconfigPath = findNearestTsconfigPath(directory)
135
+ let transformOptions: TsconfigTransformOptions = {
136
+ trackedFiles: tsconfigPath && !isWatchIgnored(tsconfigPath) ? [tsconfigPath] : [],
137
+ tsconfigRaw: tsconfig.config,
138
+ }
139
+
140
+ transformOptionsByDirectory.set(directory, transformOptions)
141
+ return transformOptions
142
+ },
143
+ }
144
+ }
145
+
146
+ export async function transformModule(
147
+ record: ModuleRecord,
148
+ args: TransformArgs,
149
+ ): Promise<TransformResult> {
150
+ let resolvedPath = args.resolveActualPath(record.identityPath)
151
+ if (!resolvedPath) {
152
+ return {
153
+ ok: false,
154
+ error: createAssetServerCompilationError(`Module not found: ${record.identityPath}`, {
155
+ code: 'MODULE_NOT_FOUND',
156
+ }),
157
+ trackedFiles: args.isWatchIgnored(record.identityPath) ? [] : [record.identityPath],
158
+ }
159
+ }
160
+
161
+ let transformOptions = args.tsconfigTransformOptionsResolver.getTransformOptions(
162
+ resolvedPath,
163
+ args.isWatchIgnored,
164
+ )
165
+ let trackedFiles = [
166
+ ...(args.isWatchIgnored(resolvedPath) ? [] : [resolvedPath]),
167
+ ...transformOptions.trackedFiles,
168
+ ]
169
+ let sourceText: string
170
+ try {
171
+ sourceText = await fsp.readFile(resolvedPath, 'utf-8')
172
+ } catch (error) {
173
+ if (isNoEntityError(error)) {
174
+ return {
175
+ ok: false,
176
+ error: createAssetServerCompilationError(`Module not found: ${resolvedPath}`, {
177
+ cause: error,
178
+ code: 'MODULE_NOT_FOUND',
179
+ }),
180
+ trackedFiles,
181
+ }
182
+ }
183
+ return {
184
+ ok: false,
185
+ error: toTransformFailedError(error, resolvedPath),
186
+ trackedFiles,
187
+ }
188
+ }
189
+
190
+ try {
191
+ let analysis = await analyzeModuleSource(sourceText, resolvedPath, transformOptions, {
192
+ define: args.define ?? undefined,
193
+ minify: args.minify,
194
+ sourceMaps: args.sourceMaps ?? undefined,
195
+ target: args.target ?? undefined,
196
+ })
197
+
198
+ analysis.unresolvedImports = analysis.unresolvedImports.filter(
199
+ (unresolved) => !args.externalSet.has(unresolved.specifier),
200
+ )
201
+
202
+ if (mayContainCommonJSModuleGlobals(sourceText) && isCommonJS(analysis.rawCode)) {
203
+ throw createAssetServerCompilationError(
204
+ `CommonJS module detected: ${resolvedPath}. ` +
205
+ `This module uses CommonJS (require/module.exports) which is not supported. ` +
206
+ `Please use an ESM-compatible module.`,
207
+ {
208
+ code: 'MODULE_COMMONJS_NOT_SUPPORTED',
209
+ },
210
+ )
211
+ }
212
+
213
+ let stableUrlPathname = args.routes.toUrlPathname(record.identityPath)
214
+ if (!stableUrlPathname) {
215
+ throw createAssetServerCompilationError(
216
+ `Module ${record.identityPath} is outside all configured fileMap entries.`,
217
+ {
218
+ code: 'MODULE_OUTSIDE_FILE_MAP',
219
+ },
220
+ )
221
+ }
222
+
223
+ let sourceMap = analysis.sourceMap
224
+ ? rewriteSourceMapSources(
225
+ analysis.sourceMap,
226
+ resolvedPath,
227
+ stableUrlPathname,
228
+ args.sourceMapSourcePaths,
229
+ )
230
+ : null
231
+
232
+ return {
233
+ ok: true,
234
+ value: {
235
+ fingerprint:
236
+ args.buildId === null
237
+ ? null
238
+ : await generateFingerprint({
239
+ buildId: args.buildId,
240
+ content: sourceText,
241
+ }),
242
+ identityPath: record.identityPath,
243
+ importerDir: path.dirname(resolvedPath),
244
+ packageSpecifiers: analysis.unresolvedImports
245
+ .filter((unresolved) => isPackageImportSpecifier(unresolved.specifier))
246
+ .map((unresolved) => unresolved.specifier),
247
+ rawCode: analysis.rawCode,
248
+ resolvedPath,
249
+ sourceMap,
250
+ stableUrlPathname,
251
+ trackedFiles,
252
+ unresolvedImports: analysis.unresolvedImports,
253
+ },
254
+ }
255
+ } catch (error) {
256
+ return {
257
+ ok: false,
258
+ error: toTransformFailedError(error, resolvedPath),
259
+ trackedFiles,
260
+ }
261
+ }
262
+ }
263
+
264
+ function findNearestTsconfigPath(directory: string): string | null {
265
+ let currentDirectory = directory
266
+
267
+ while (true) {
268
+ let tsconfigPath = path.join(currentDirectory, 'tsconfig.json')
269
+ if (fs.existsSync(tsconfigPath)) {
270
+ return normalizeFilePath(tsconfigPath)
271
+ }
272
+
273
+ let parentDirectory = path.dirname(currentDirectory)
274
+ if (parentDirectory === currentDirectory) return null
275
+ currentDirectory = parentDirectory
276
+ }
277
+ }
278
+
279
+ function isPackageImportSpecifier(specifier: string): boolean {
280
+ return !specifier.startsWith('./') && !specifier.startsWith('../') && !specifier.startsWith('/')
281
+ }
282
+
283
+ async function analyzeModuleSource(
284
+ sourceText: string,
285
+ resolvedPath: string,
286
+ transformOptions: TsconfigTransformOptions,
287
+ options: {
288
+ define?: Record<string, string>
289
+ minify: boolean
290
+ sourceMaps?: 'external' | 'inline'
291
+ target?: ScriptsTarget
292
+ },
293
+ ) {
294
+ let transformResult: { code: string; errors?: Array<{ message?: string }>; map?: unknown }
295
+ try {
296
+ transformResult = await oxcTransform(
297
+ resolvedPath,
298
+ sourceText,
299
+ getTransformOptions(resolvedPath, transformOptions, options),
300
+ )
301
+ assertNoCompilerErrors(transformResult.errors, resolvedPath, 'transform')
302
+ } catch (error) {
303
+ if (isAssetServerCompilationError(error)) throw error
304
+ throw createAssetServerCompilationError(
305
+ `Failed to transform module ${resolvedPath}. ${formatUnknownError(error)}`,
306
+ {
307
+ cause: error,
308
+ code: 'MODULE_TRANSFORM_FAILED',
309
+ },
310
+ )
311
+ }
312
+
313
+ let rawCode = transformResult.code.trimEnd()
314
+ let sourceMap = stringifySourceMap(transformResult.map)
315
+
316
+ if (options.minify) {
317
+ let minifyResult = await minifyModule(rawCode, resolvedPath, options.target, options.sourceMaps)
318
+ rawCode = minifyResult.code.trimEnd()
319
+ let minifyMap = stringifySourceMap(minifyResult.map)
320
+ sourceMap =
321
+ minifyMap == null
322
+ ? sourceMap
323
+ : sourceMap == null
324
+ ? minifyMap
325
+ : composeSourceMaps(minifyMap, sourceMap)
326
+ }
327
+
328
+ return {
329
+ rawCode,
330
+ sourceMap,
331
+ unresolvedImports: await getUnresolvedImportsFromLexer(rawCode),
332
+ }
333
+ }
334
+
335
+ async function minifyModule(
336
+ rawCode: string,
337
+ resolvedPath: string,
338
+ target: string | undefined,
339
+ sourceMaps?: 'external' | 'inline',
340
+ ) {
341
+ try {
342
+ let result = await minify(resolvedPath, rawCode, {
343
+ compress: target ? { target } : true,
344
+ mangle: true,
345
+ module: true,
346
+ sourcemap: sourceMaps != null,
347
+ })
348
+ assertNoCompilerErrors(result.errors, resolvedPath, 'minify')
349
+ return result
350
+ } catch (error) {
351
+ if (isAssetServerCompilationError(error)) throw error
352
+ throw createAssetServerCompilationError(
353
+ `Failed to minify module ${resolvedPath}. ${formatUnknownError(error)}`,
354
+ {
355
+ cause: error,
356
+ code: 'MODULE_TRANSFORM_FAILED',
357
+ },
358
+ )
359
+ }
360
+ }
361
+
362
+ function getTransformOptions(
363
+ resolvedPath: string,
364
+ transformOptions: TsconfigTransformOptions,
365
+ options: {
366
+ define?: Record<string, string>
367
+ sourceMaps?: 'external' | 'inline'
368
+ target?: string
369
+ },
370
+ ): OxcTransformOptions {
371
+ let compilerOptions = transformOptions.tsconfigRaw?.compilerOptions as
372
+ | Record<string, unknown>
373
+ | undefined
374
+ let useDefineForClassFields = getBooleanOption(
375
+ compilerOptions,
376
+ supportedTsconfigTransformCompilerOptions.useDefineForClassFields,
377
+ )
378
+ let jsxFactory = getStringOption(
379
+ compilerOptions,
380
+ supportedTsconfigTransformCompilerOptions.jsxFactory,
381
+ )
382
+ let jsxFragmentFactory = getStringOption(
383
+ compilerOptions,
384
+ supportedTsconfigTransformCompilerOptions.jsxFragmentFactory,
385
+ )
386
+
387
+ return {
388
+ assumptions:
389
+ useDefineForClassFields === false
390
+ ? {
391
+ setPublicClassFields: true,
392
+ }
393
+ : undefined,
394
+ decorator: getDecoratorOptions(compilerOptions),
395
+ define: options.define,
396
+ jsx: getJsxOptions(resolvedPath, compilerOptions),
397
+ lang: getSourceLanguageForPath(resolvedPath),
398
+ sourceType: 'module' as const,
399
+ sourcemap: options.sourceMaps != null,
400
+ target: options.target,
401
+ typescript: {
402
+ allowNamespaces: getBooleanOption(
403
+ compilerOptions,
404
+ supportedTsconfigTransformCompilerOptions.allowNamespaces,
405
+ ),
406
+ jsxPragma: jsxFactory,
407
+ jsxPragmaFrag: jsxFragmentFactory,
408
+ removeClassFieldsWithoutInitializer: useDefineForClassFields === false ? true : undefined,
409
+ },
410
+ }
411
+ }
412
+
413
+ function getJsxOptions(
414
+ resolvedPath: string,
415
+ compilerOptions?: Record<string, unknown>,
416
+ ): OxcTransformOptions['jsx'] | undefined {
417
+ let language = getSourceLanguageForPath(resolvedPath)
418
+ if (language !== 'jsx' && language !== 'tsx') return undefined
419
+
420
+ let jsx = getStringOption(compilerOptions, supportedTsconfigTransformCompilerOptions.jsx)
421
+ let importSource = getStringOption(
422
+ compilerOptions,
423
+ supportedTsconfigTransformCompilerOptions.jsxImportSource,
424
+ )
425
+ let pragma = getStringOption(
426
+ compilerOptions,
427
+ supportedTsconfigTransformCompilerOptions.jsxFactory,
428
+ )
429
+ let pragmaFrag = getStringOption(
430
+ compilerOptions,
431
+ supportedTsconfigTransformCompilerOptions.jsxFragmentFactory,
432
+ )
433
+
434
+ if (jsx === 'preserve' || jsx === 'react-native') {
435
+ throw createAssetServerCompilationError(
436
+ `Unsupported tsconfig compilerOptions.jsx = "${jsx}" for ${resolvedPath}. ` +
437
+ `Asset server must compile JSX to browser-runnable JavaScript.`,
438
+ {
439
+ code: 'MODULE_TRANSFORM_FAILED',
440
+ },
441
+ )
442
+ }
443
+
444
+ if (jsx === 'react-jsx' || jsx === 'react-jsxdev') {
445
+ return {
446
+ development: jsx === 'react-jsxdev',
447
+ importSource,
448
+ runtime: 'automatic',
449
+ }
450
+ }
451
+
452
+ return {
453
+ pragma,
454
+ pragmaFrag,
455
+ runtime: 'classic',
456
+ }
457
+ }
458
+
459
+ function getDecoratorOptions(
460
+ compilerOptions?: Record<string, unknown>,
461
+ ): OxcTransformOptions['decorator'] | undefined {
462
+ let legacy = getBooleanOption(
463
+ compilerOptions,
464
+ supportedTsconfigTransformCompilerOptions.experimentalDecorators,
465
+ )
466
+ let emitDecoratorMetadata = getBooleanOption(
467
+ compilerOptions,
468
+ supportedTsconfigTransformCompilerOptions.emitDecoratorMetadata,
469
+ )
470
+
471
+ if (legacy !== true && emitDecoratorMetadata !== true) return undefined
472
+
473
+ return {
474
+ emitDecoratorMetadata,
475
+ legacy,
476
+ }
477
+ }
478
+
479
+ function getBooleanOption(
480
+ compilerOptions: Record<string, unknown> | undefined,
481
+ key: string,
482
+ ): boolean | undefined {
483
+ let value = compilerOptions?.[key]
484
+ return typeof value === 'boolean' ? value : undefined
485
+ }
486
+
487
+ function getStringOption(
488
+ compilerOptions: Record<string, unknown> | undefined,
489
+ key: string,
490
+ ): string | undefined {
491
+ let value = compilerOptions?.[key]
492
+ return typeof value === 'string' ? value : undefined
493
+ }
494
+
495
+ function assertNoCompilerErrors(
496
+ errors: Array<{ message?: string }> | undefined,
497
+ resolvedPath: string,
498
+ operation: 'transform' | 'minify',
499
+ ) {
500
+ if (!errors || errors.length === 0) return
501
+
502
+ throw createAssetServerCompilationError(
503
+ `Failed to ${operation} module ${resolvedPath}. ${errors[0].message ?? 'Unknown error'}`,
504
+ {
505
+ code: 'MODULE_TRANSFORM_FAILED',
506
+ },
507
+ )
508
+ }
509
+
510
+ async function getUnresolvedImportsFromLexer(rawCode: string): Promise<UnresolvedImport[]> {
511
+ await esModuleLexerInit
512
+ let [imports] = esModuleLexer(rawCode)
513
+ let unresolvedImports: UnresolvedImport[] = []
514
+
515
+ for (let imported of imports) {
516
+ let specifier = getStaticImportSpecifier(rawCode, imported)
517
+ if (specifier == null || shouldSkipImportSpecifier(specifier)) continue
518
+ unresolvedImports.push({
519
+ specifier,
520
+ start: imported.s,
521
+ end: imported.e,
522
+ quote: getImportQuote(rawCode, imported.s),
523
+ })
524
+ }
525
+
526
+ return unresolvedImports
527
+ }
528
+
529
+ function getStaticImportSpecifier(
530
+ source: string,
531
+ imported: ReturnType<typeof esModuleLexer>[0][number],
532
+ ): string | null {
533
+ if (imported.n != null) {
534
+ return imported.n
535
+ }
536
+
537
+ if (imported.d < 0) {
538
+ return null
539
+ }
540
+
541
+ let rawSpecifier = source.slice(imported.s, imported.e)
542
+ if (!isStaticTemplateLiteral(rawSpecifier)) {
543
+ return null
544
+ }
545
+
546
+ return rawSpecifier.slice(1, -1)
547
+ }
548
+
549
+ function isStaticTemplateLiteral(specifier: string): boolean {
550
+ return specifier.startsWith('`') && specifier.endsWith('`') && !specifier.includes('${')
551
+ }
552
+
553
+ function shouldSkipImportSpecifier(specifier: string): boolean {
554
+ return (
555
+ specifier.startsWith('data:') ||
556
+ specifier.startsWith('http://') ||
557
+ specifier.startsWith('https://')
558
+ )
559
+ }
560
+
561
+ function getImportQuote(source: string, start: number): '"' | "'" | '`' | undefined {
562
+ let firstCharacter = source[start]
563
+ if (firstCharacter === '"' || firstCharacter === "'" || firstCharacter === '`') {
564
+ return firstCharacter
565
+ }
566
+ return undefined
567
+ }
568
+
569
+ function getSourceLanguageForPath(resolvedPath: string): SourceLanguage {
570
+ let extension = path.extname(resolvedPath).toLowerCase()
571
+ return sourceLanguageByExtension.get(extension) ?? 'js'
572
+ }
573
+
574
+ function formatUnknownError(error: unknown): string {
575
+ return error instanceof Error ? error.message : String(error)
576
+ }
577
+
578
+ function toTransformFailedError(error: unknown, resolvedPath: string): AssetServerCompilationError {
579
+ if (isAssetServerCompilationError(error)) return error
580
+
581
+ return createAssetServerCompilationError(
582
+ `Failed to transform module ${resolvedPath}. ${formatUnknownError(error)}`,
583
+ {
584
+ cause: error,
585
+ code: 'MODULE_TRANSFORM_FAILED',
586
+ },
587
+ )
588
+ }
589
+
590
+ function isNoEntityError(error: unknown): error is NodeJS.ErrnoException & { code: 'ENOENT' } {
591
+ return (
592
+ error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT'
593
+ )
594
+ }
@@ -0,0 +1,68 @@
1
+ import { SourceMapConsumer, SourceMapGenerator } from 'source-map-js'
2
+
3
+ import { normalizeFilePath } from './paths.ts'
4
+
5
+ export function composeSourceMaps(rewriteSourceMap: string, transformSourceMap: string): string {
6
+ let rewriteConsumer = new SourceMapConsumer(JSON.parse(rewriteSourceMap))
7
+ let transformConsumer = new SourceMapConsumer(JSON.parse(transformSourceMap))
8
+ let generator = new SourceMapGenerator()
9
+
10
+ rewriteConsumer.eachMapping((mapping) => {
11
+ if (
12
+ mapping.originalLine == null ||
13
+ mapping.originalColumn == null ||
14
+ mapping.generatedLine == null ||
15
+ mapping.generatedColumn == null
16
+ ) {
17
+ return
18
+ }
19
+
20
+ let original = transformConsumer.originalPositionFor({
21
+ line: mapping.originalLine,
22
+ column: mapping.originalColumn,
23
+ })
24
+ if (original.line == null || original.column == null || original.source == null) return
25
+
26
+ generator.addMapping({
27
+ generated: {
28
+ line: mapping.generatedLine,
29
+ column: mapping.generatedColumn,
30
+ },
31
+ original: {
32
+ line: original.line,
33
+ column: original.column,
34
+ },
35
+ source: original.source,
36
+ name: original.name ?? mapping.name ?? undefined,
37
+ })
38
+ })
39
+
40
+ for (let source of transformConsumer.sources) {
41
+ let sourceContent = transformConsumer.sourceContentFor(source, true)
42
+ if (sourceContent !== null) {
43
+ generator.setSourceContent(source, sourceContent)
44
+ }
45
+ }
46
+
47
+ return JSON.stringify(generator.toJSON())
48
+ }
49
+
50
+ export function rewriteSourceMapSources(
51
+ sourceMap: string,
52
+ resolvedPath: string,
53
+ stableUrlPathname: string,
54
+ sourceMapSourcePaths: 'absolute' | 'url',
55
+ ): string {
56
+ let json = JSON.parse(sourceMap) as { sources?: string[] }
57
+ json.sources = [
58
+ sourceMapSourcePaths === 'absolute' ? normalizeFilePath(resolvedPath) : stableUrlPathname,
59
+ ]
60
+ return JSON.stringify(json)
61
+ }
62
+
63
+ export function stringifySourceMap(map: unknown): string | null {
64
+ if (!map) return null
65
+ if (typeof map === 'string') return map
66
+ if (typeof map === 'object' && map !== null) return JSON.stringify(map)
67
+ return String(map)
68
+ }