@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,400 @@
1
+ import * as os from 'node:os'
2
+ import * as path from 'node:path'
3
+ import { fileURLToPath } from 'node:url'
4
+ import { IfNoneMatch } from '@remix-run/headers'
5
+ import { createFileMatcher } from '../file-matcher.ts'
6
+ import { formatFingerprintedPathname } from '../fingerprint.ts'
7
+ import { createModuleStore } from '../module-store.ts'
8
+ import type { ModuleRecord, ModuleStore } from '../module-store.ts'
9
+ import { normalizeFilePath, resolveFilePath } from '../paths.ts'
10
+ import type { CompiledRoutes } from '../routes.ts'
11
+ import type { ResolvedStyleTarget } from '../target.ts'
12
+ import { emitResolvedStyle } from './emit.ts'
13
+ import type { EmittedAsset, EmittedStyle } from './emit.ts'
14
+ import { resolveServedStyleOrThrow, resolveStyle } from './resolve.ts'
15
+ import type { ResolveArgs, ResolvedStyle } from './resolve.ts'
16
+ import { transformStyle } from './transform.ts'
17
+ import type { TransformArgs, TransformedStyle } from './transform.ts'
18
+
19
+ type StyleRecord = ModuleRecord<TransformedStyle, ResolvedStyle, EmittedStyle>
20
+ type StyleStore = ModuleStore<TransformedStyle, ResolvedStyle, EmittedStyle>
21
+
22
+ type StyleCompileResult = {
23
+ code: EmittedAsset
24
+ fingerprint: string | null
25
+ sourceMap: EmittedAsset | null
26
+ }
27
+
28
+ type StyleGetResult =
29
+ | {
30
+ style: StyleCompileResult
31
+ type: 'style'
32
+ }
33
+ | {
34
+ etag: string
35
+ type: 'not-modified'
36
+ }
37
+
38
+ type StyleGetOptions = {
39
+ ifNoneMatch: string | null
40
+ isSourceMapRequest: boolean
41
+ requestedFingerprint: string | null
42
+ }
43
+
44
+ type StyleCompilerOptions = {
45
+ buildId?: string
46
+ fingerprintAssets: boolean
47
+ isAllowed(absolutePath: string): boolean
48
+ minify: boolean
49
+ onWatchDirectoriesChange?: (delta: { add: string[]; remove: string[] }) => void
50
+ rootDir: string
51
+ routes: CompiledRoutes
52
+ sourceMapSourcePaths: 'absolute' | 'url'
53
+ sourceMaps?: 'external' | 'inline'
54
+ targets?: ResolvedStyleTarget
55
+ watchIgnore?: readonly string[]
56
+ }
57
+
58
+ type StyleCompiler = {
59
+ getHref(filePath: string): Promise<string>
60
+ getPreloadLayers(filePath: string | readonly string[]): Promise<string[][]>
61
+ getStyle(filePath: string, options: StyleGetOptions): Promise<StyleGetResult>
62
+ handleFileEvent(filePath: string, event: 'add' | 'change' | 'unlink'): Promise<void>
63
+ }
64
+
65
+ const preloadConcurrency = Math.max(1, Math.min(8, os.availableParallelism() - 1))
66
+ const styleExtension = '.css'
67
+
68
+ export function createStyleCompiler(options: StyleCompilerOptions): StyleCompiler {
69
+ let resolvedOptions = {
70
+ ...options,
71
+ watchIgnoreMatchers: (options.watchIgnore ?? []).map((pattern) =>
72
+ createFileMatcher(pattern, options.rootDir),
73
+ ),
74
+ }
75
+ let styleStore: StyleStore = createModuleStore<TransformedStyle, ResolvedStyle, EmittedStyle>({
76
+ onWatchDirectoriesChange: options.onWatchDirectoriesChange,
77
+ })
78
+ let resolveInFlightByCacheKey = new Map<string, Promise<ResolvedStyle>>()
79
+ let emitInFlightByCacheKey = new Map<string, Promise<EmittedStyle>>()
80
+ let resolveArgs: ResolveArgs = {
81
+ isAllowed: resolvedOptions.isAllowed,
82
+ isWatchIgnored,
83
+ routes: resolvedOptions.routes,
84
+ }
85
+ let transformArgs: TransformArgs = {
86
+ buildId: resolvedOptions.buildId ?? null,
87
+ isWatchIgnored,
88
+ minify: resolvedOptions.minify,
89
+ routes: resolvedOptions.routes,
90
+ sourceMapSourcePaths: resolvedOptions.sourceMapSourcePaths,
91
+ sourceMaps: resolvedOptions.sourceMaps ?? null,
92
+ targets: resolvedOptions.targets ?? null,
93
+ }
94
+
95
+ return {
96
+ async getHref(filePath) {
97
+ let resolvedStyle = resolveServedStyleOrThrow(resolveInputFilePath(filePath), resolveArgs)
98
+ return getServedUrl(resolvedStyle.identityPath)
99
+ },
100
+
101
+ async getPreloadLayers(filePath) {
102
+ let resolvedEntries: string[] = []
103
+ let seen = new Set<string>()
104
+
105
+ for (let resolvedStyle of (Array.isArray(filePath) ? filePath : [filePath]).map((nextPath) =>
106
+ resolveServedStyleOrThrow(resolveInputFilePath(nextPath), resolveArgs),
107
+ )) {
108
+ if (seen.has(resolvedStyle.identityPath)) continue
109
+ seen.add(resolvedStyle.identityPath)
110
+ resolvedEntries.push(resolvedStyle.identityPath)
111
+ }
112
+
113
+ let visited = new Set(resolvedEntries)
114
+ let queue = [...resolvedEntries]
115
+ let layers: string[][] = []
116
+
117
+ while (queue.length > 0) {
118
+ let frontier = queue
119
+ queue = []
120
+ let resolvedStyles = await getOrCreateResolvedStyles(
121
+ frontier.map((identityPath) => styleStore.get(identityPath)),
122
+ )
123
+ let layer: string[] = []
124
+
125
+ for (let resolvedStyle of resolvedStyles) {
126
+ layer.push(getServedUrlForResolvedStyle(resolvedStyle))
127
+
128
+ for (let dep of resolvedStyle.deps) {
129
+ if (visited.has(dep)) continue
130
+ visited.add(dep)
131
+ queue.push(dep)
132
+ }
133
+ }
134
+
135
+ layers.push(layer)
136
+ }
137
+
138
+ return layers
139
+ },
140
+
141
+ async getStyle(filePath, getOptions) {
142
+ let resolvedStyle = resolveServedStyleOrThrow(resolveInputFilePath(filePath), resolveArgs)
143
+ let record = styleStore.get(resolvedStyle.identityPath)
144
+ let notModified = getNotModifiedStyle(record.emitted, getOptions)
145
+ if (notModified) return notModified
146
+
147
+ let emitted = await getOrCreateEmittedStyle(record)
148
+ return {
149
+ style: toStyleCompileResult(emitted),
150
+ type: 'style',
151
+ }
152
+ },
153
+
154
+ async handleFileEvent(filePath, event) {
155
+ let normalizedFilePath = normalizeFilePath(filePath)
156
+ if (isWatchIgnored(normalizedFilePath)) return
157
+ styleStore.invalidateForFileEvent(normalizedFilePath, event)
158
+ },
159
+ }
160
+
161
+ function resolveInputFilePath(filePath: string): string {
162
+ if (filePath.startsWith('file://')) {
163
+ return normalizeFilePath(fileURLToPath(new URL(filePath)))
164
+ }
165
+
166
+ if (filePath.includes('://')) {
167
+ throw new TypeError(`Expected a file path or file:// URL, received "${filePath}"`)
168
+ }
169
+
170
+ return resolveFilePath(resolvedOptions.rootDir, filePath)
171
+ }
172
+
173
+ async function getOrCreateResolvedStyles(records: StyleRecord[]): Promise<ResolvedStyle[]> {
174
+ return mapWithConcurrency(records, preloadConcurrency, (record) =>
175
+ getOrCreateResolvedStyle(record),
176
+ )
177
+ }
178
+
179
+ async function getOrCreateResolvedStyle(record: StyleRecord): Promise<ResolvedStyle> {
180
+ if (record.resolved) return record.resolved
181
+
182
+ let cacheKey = getRecordCacheKey(record)
183
+ let existing = resolveInFlightByCacheKey.get(cacheKey)
184
+ if (existing) return existing
185
+
186
+ let promise = (async () => {
187
+ let startedVersion = record.invalidationVersion
188
+ let transformedStyle = await getOrCreateTransformedStyle(record)
189
+ let resolvedStyleResult = await resolveStyle(record, transformedStyle, resolveArgs)
190
+
191
+ if (!resolvedStyleResult.ok) {
192
+ if (isFresh(record, startedVersion)) {
193
+ styleStore.clearResolved(record.identityPath, [resolvedStyleResult.tracking])
194
+ }
195
+ throw resolvedStyleResult.error
196
+ }
197
+
198
+ if (isFresh(record, startedVersion)) {
199
+ styleStore.setResolved(record.identityPath, resolvedStyleResult.value, [
200
+ resolvedStyleResult.tracking,
201
+ ])
202
+ }
203
+
204
+ return resolvedStyleResult.value
205
+ })()
206
+
207
+ resolveInFlightByCacheKey.set(cacheKey, promise)
208
+
209
+ try {
210
+ return await promise
211
+ } finally {
212
+ if (resolveInFlightByCacheKey.get(cacheKey) === promise) {
213
+ resolveInFlightByCacheKey.delete(cacheKey)
214
+ }
215
+ }
216
+ }
217
+
218
+ async function getOrCreateTransformedStyle(record: StyleRecord): Promise<TransformedStyle> {
219
+ if (record.transformed) return record.transformed
220
+
221
+ let startedVersion = record.invalidationVersion
222
+ let transformStyleResult = await transformStyle(record, transformArgs)
223
+
224
+ if (!transformStyleResult.ok) {
225
+ if (isFresh(record, startedVersion)) {
226
+ styleStore.clearTransformed(record.identityPath, [transformStyleResult.tracking])
227
+ }
228
+ throw transformStyleResult.error
229
+ }
230
+
231
+ if (isFresh(record, startedVersion)) {
232
+ styleStore.setTransformed(record.identityPath, transformStyleResult.value, [
233
+ transformStyleResult.tracking,
234
+ ])
235
+ }
236
+
237
+ return transformStyleResult.value
238
+ }
239
+
240
+ async function getOrCreateEmittedStyle(record: StyleRecord): Promise<EmittedStyle> {
241
+ if (record.emitted) return record.emitted
242
+
243
+ let cacheKey = getRecordCacheKey(record)
244
+ let existing = emitInFlightByCacheKey.get(cacheKey)
245
+ if (existing) return existing
246
+
247
+ let promise = (async () => {
248
+ let startedVersion = record.invalidationVersion
249
+ let resolvedStyle = await getOrCreateResolvedStyle(record)
250
+ let emitResolvedStyleResult = await emitResolvedStyle(resolvedStyle, {
251
+ getServedUrl,
252
+ sourceMaps: resolvedOptions.sourceMaps,
253
+ })
254
+
255
+ if (!emitResolvedStyleResult.ok) {
256
+ throw emitResolvedStyleResult.error
257
+ }
258
+
259
+ if (isFresh(record, startedVersion)) {
260
+ styleStore.setEmitted(record.identityPath, emitResolvedStyleResult.value, null)
261
+ }
262
+
263
+ return emitResolvedStyleResult.value
264
+ })()
265
+
266
+ emitInFlightByCacheKey.set(cacheKey, promise)
267
+
268
+ try {
269
+ return await promise
270
+ } finally {
271
+ if (emitInFlightByCacheKey.get(cacheKey) === promise) {
272
+ emitInFlightByCacheKey.delete(cacheKey)
273
+ }
274
+ }
275
+ }
276
+
277
+ async function getServedUrl(identityPath: string): Promise<string> {
278
+ return getServedUrlForResolvedStyle(
279
+ await getOrCreateResolvedStyle(styleStore.get(identityPath)),
280
+ )
281
+ }
282
+
283
+ function getServedUrlForResolvedStyle(resolvedStyle: ResolvedStyle): string {
284
+ return formatFingerprintedPathname(
285
+ resolvedStyle.stableUrlPathname,
286
+ resolvedOptions.fingerprintAssets ? resolvedStyle.fingerprint : null,
287
+ )
288
+ }
289
+
290
+ function isWatchIgnored(filePath: string): boolean {
291
+ return resolvedOptions.watchIgnoreMatchers.some((matcher) => matcher(filePath))
292
+ }
293
+ }
294
+
295
+ function getRecordCacheKey(record: StyleRecord): string {
296
+ return `${record.identityPath}\0${record.invalidationVersion}`
297
+ }
298
+
299
+ function isFresh(record: StyleRecord, version: number): boolean {
300
+ return record.invalidationVersion === version
301
+ }
302
+
303
+ function getNotModifiedStyle(
304
+ emittedStyle: EmittedStyle | undefined,
305
+ options: StyleGetOptions,
306
+ ): StyleGetResult | null {
307
+ if (!emittedStyle || options.ifNoneMatch === null) return null
308
+
309
+ if (
310
+ options.requestedFingerprint !== null &&
311
+ emittedStyle.fingerprint !== options.requestedFingerprint
312
+ ) {
313
+ return null
314
+ }
315
+
316
+ let asset = getEmittedAssetForRequest(emittedStyle, options.isSourceMapRequest)
317
+ if (!asset) return null
318
+
319
+ if (!IfNoneMatch.from(options.ifNoneMatch).matches(asset.etag)) return null
320
+ return { etag: asset.etag, type: 'not-modified' }
321
+ }
322
+
323
+ function getEmittedAssetForRequest(
324
+ emittedStyle: EmittedStyle,
325
+ isSourceMapRequest: boolean,
326
+ ): EmittedAsset | null {
327
+ return isSourceMapRequest ? emittedStyle.sourceMap : emittedStyle.code
328
+ }
329
+
330
+ async function mapWithConcurrency<item, result>(
331
+ items: item[],
332
+ concurrency: number,
333
+ mapper: (item: item, index: number) => Promise<result>,
334
+ ): Promise<result[]> {
335
+ if (items.length === 0) return []
336
+
337
+ let results = new Array<result>(items.length)
338
+ let nextIndex = 0
339
+
340
+ async function worker() {
341
+ while (nextIndex < items.length) {
342
+ let index = nextIndex++
343
+ results[index] = await mapper(items[index], index)
344
+ }
345
+ }
346
+
347
+ await Promise.all(Array.from({ length: Math.min(concurrency, items.length) }, () => worker()))
348
+ return results
349
+ }
350
+
351
+ function toStyleCompileResult(emittedStyle: EmittedStyle): StyleCompileResult {
352
+ return {
353
+ code: emittedStyle.code,
354
+ fingerprint: emittedStyle.fingerprint,
355
+ sourceMap: emittedStyle.sourceMap,
356
+ }
357
+ }
358
+
359
+ export function isStyleFilePath(filePath: string): boolean {
360
+ return path.extname(filePath).toLowerCase() === styleExtension
361
+ }
362
+
363
+ export function createResponseForStyle(
364
+ result: StyleCompileResult,
365
+ options: {
366
+ cacheControl: string
367
+ ifNoneMatch: string | null
368
+ isSourceMapRequest: boolean
369
+ method: string
370
+ },
371
+ ): Response {
372
+ let body: string | null
373
+ let etag: string
374
+ let contentType: string
375
+
376
+ if (options.isSourceMapRequest) {
377
+ if (!result.sourceMap) {
378
+ return new Response('Not found', { status: 404 })
379
+ }
380
+ body = options.method === 'HEAD' ? null : result.sourceMap.content
381
+ etag = result.sourceMap.etag
382
+ contentType = 'application/json; charset=utf-8'
383
+ } else {
384
+ body = options.method === 'HEAD' ? null : result.code.content
385
+ etag = result.code.etag
386
+ contentType = 'text/css; charset=utf-8'
387
+ }
388
+
389
+ if (IfNoneMatch.from(options.ifNoneMatch).matches(etag)) {
390
+ return new Response(null, { status: 304, headers: { ETag: etag } })
391
+ }
392
+
393
+ return new Response(body, {
394
+ headers: {
395
+ 'Cache-Control': options.cacheControl,
396
+ 'Content-Type': contentType,
397
+ ETag: etag,
398
+ },
399
+ })
400
+ }
@@ -0,0 +1,137 @@
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 { composeSourceMaps } from '../source-maps.ts'
9
+ import type { AssetServerCompilationError } from '../compilation-error.ts'
10
+ import type { ResolvedStyle } from './resolve.ts'
11
+
12
+ export type EmittedAsset = {
13
+ content: string
14
+ etag: string
15
+ }
16
+
17
+ export type EmittedStyle = {
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: EmittedStyle
28
+ }
29
+ | {
30
+ error: AssetServerCompilationError
31
+ ok: false
32
+ }
33
+
34
+ export async function emitResolvedStyle(
35
+ resolvedStyle: ResolvedStyle,
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
+ resolvedStyle.deps.map((depPath) => options.getServedUrl(depPath)),
44
+ )
45
+ let rewriteResult = await rewriteDependencies(resolvedStyle, 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(resolvedStyle.identityPath)}.map */`
54
+ }
55
+ }
56
+
57
+ return {
58
+ ok: true,
59
+ value: {
60
+ code: await createEmittedAsset(finalCode),
61
+ fingerprint: resolvedStyle.fingerprint,
62
+ importUrls,
63
+ sourceMap: rewriteResult.sourceMap
64
+ ? await createEmittedAsset(rewriteResult.sourceMap)
65
+ : null,
66
+ },
67
+ }
68
+ } catch (error) {
69
+ return {
70
+ error: toEmitError(error, resolvedStyle.identityPath),
71
+ ok: false,
72
+ }
73
+ }
74
+ }
75
+
76
+ async function rewriteDependencies(
77
+ resolvedStyle: ResolvedStyle,
78
+ options: {
79
+ getServedUrl(identityPath: string): Promise<string>
80
+ },
81
+ ): Promise<{ code: string; sourceMap: string | null }> {
82
+ if (resolvedStyle.dependencies.length === 0) {
83
+ return {
84
+ code: resolvedStyle.rawCode,
85
+ sourceMap: resolvedStyle.sourceMap,
86
+ }
87
+ }
88
+
89
+ let rewrittenSource = new MagicString(resolvedStyle.rawCode)
90
+
91
+ for (let dependency of resolvedStyle.dependencies) {
92
+ let replacement =
93
+ dependency.kind === 'external'
94
+ ? dependency.replacement
95
+ : `${await options.getServedUrl(dependency.depPath)}${dependency.suffix}`
96
+ let start = resolvedStyle.rawCode.indexOf(dependency.placeholder)
97
+ if (start < 0) {
98
+ throw createAssetServerCompilationError(
99
+ `Missing dependency placeholder "${dependency.placeholder}" while emitting style ${resolvedStyle.identityPath}.`,
100
+ {
101
+ code: 'EMIT_FAILED',
102
+ },
103
+ )
104
+ }
105
+
106
+ rewrittenSource.overwrite(start, start + dependency.placeholder.length, replacement)
107
+ }
108
+
109
+ return {
110
+ code: rewrittenSource.toString(),
111
+ sourceMap: resolvedStyle.sourceMap
112
+ ? composeSourceMaps(
113
+ rewrittenSource.generateMap({ hires: true }).toString(),
114
+ resolvedStyle.sourceMap,
115
+ )
116
+ : null,
117
+ }
118
+ }
119
+
120
+ async function createEmittedAsset(content: string): Promise<EmittedAsset> {
121
+ return {
122
+ content,
123
+ etag: `W/"${await hashContent(content)}"`,
124
+ }
125
+ }
126
+
127
+ function toEmitError(error: unknown, identityPath: string): AssetServerCompilationError {
128
+ if (isAssetServerCompilationError(error)) return error
129
+
130
+ return createAssetServerCompilationError(
131
+ `Failed to emit style ${identityPath}. ${error instanceof Error ? error.message : String(error)}`,
132
+ {
133
+ cause: error,
134
+ code: 'EMIT_FAILED',
135
+ },
136
+ )
137
+ }