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