@remix-run/assets 0.2.0 → 0.4.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 (71) hide show
  1. package/README.md +241 -28
  2. package/dist/assets.d.ts +1 -0
  3. package/dist/assets.d.ts.map +1 -1
  4. package/dist/assets.js +1 -0
  5. package/dist/lib/access.d.ts +1 -0
  6. package/dist/lib/access.d.ts.map +1 -1
  7. package/dist/lib/access.js +10 -1
  8. package/dist/lib/asset-server.d.ts +33 -7
  9. package/dist/lib/asset-server.d.ts.map +1 -1
  10. package/dist/lib/asset-server.js +195 -23
  11. package/dist/lib/compilation-error.d.ts +1 -1
  12. package/dist/lib/compilation-error.d.ts.map +1 -1
  13. package/dist/lib/files/compiler.d.ts +71 -0
  14. package/dist/lib/files/compiler.d.ts.map +1 -0
  15. package/dist/lib/files/compiler.js +552 -0
  16. package/dist/lib/files/config.d.ts +101 -0
  17. package/dist/lib/files/config.d.ts.map +1 -0
  18. package/dist/lib/files/config.js +219 -0
  19. package/dist/lib/files/store.d.ts +31 -0
  20. package/dist/lib/files/store.d.ts.map +1 -0
  21. package/dist/lib/files/store.js +63 -0
  22. package/dist/lib/fingerprint.d.ts +3 -3
  23. package/dist/lib/fingerprint.d.ts.map +1 -1
  24. package/dist/lib/fingerprint.js +5 -5
  25. package/dist/lib/injected-packages.d.ts +11 -0
  26. package/dist/lib/injected-packages.d.ts.map +1 -0
  27. package/dist/lib/injected-packages.js +86 -0
  28. package/dist/lib/paths.d.ts +1 -0
  29. package/dist/lib/paths.d.ts.map +1 -1
  30. package/dist/lib/paths.js +12 -0
  31. package/dist/lib/routes.d.ts +5 -7
  32. package/dist/lib/routes.d.ts.map +1 -1
  33. package/dist/lib/routes.js +41 -36
  34. package/dist/lib/scripts/compiler.d.ts +1 -0
  35. package/dist/lib/scripts/compiler.d.ts.map +1 -1
  36. package/dist/lib/scripts/compiler.js +30 -5
  37. package/dist/lib/scripts/resolve.d.ts.map +1 -1
  38. package/dist/lib/scripts/resolve.js +57 -10
  39. package/dist/lib/scripts/transform.d.ts.map +1 -1
  40. package/dist/lib/scripts/transform.js +79 -3
  41. package/dist/lib/styles/compiler.d.ts +4 -0
  42. package/dist/lib/styles/compiler.d.ts.map +1 -1
  43. package/dist/lib/styles/compiler.js +2 -0
  44. package/dist/lib/styles/emit.d.ts +3 -0
  45. package/dist/lib/styles/emit.d.ts.map +1 -1
  46. package/dist/lib/styles/emit.js +36 -1
  47. package/dist/lib/styles/resolve.d.ts +8 -1
  48. package/dist/lib/styles/resolve.d.ts.map +1 -1
  49. package/dist/lib/styles/resolve.js +85 -32
  50. package/dist/lib/watch.d.ts +2 -0
  51. package/dist/lib/watch.d.ts.map +1 -1
  52. package/dist/lib/watch.js +25 -8
  53. package/package.json +11 -5
  54. package/src/assets.ts +1 -0
  55. package/src/lib/access.ts +10 -1
  56. package/src/lib/asset-server.ts +297 -34
  57. package/src/lib/compilation-error.ts +9 -0
  58. package/src/lib/files/compiler.ts +885 -0
  59. package/src/lib/files/config.ts +479 -0
  60. package/src/lib/files/store.ts +109 -0
  61. package/src/lib/fingerprint.ts +8 -7
  62. package/src/lib/injected-packages.ts +117 -0
  63. package/src/lib/paths.ts +28 -0
  64. package/src/lib/routes.ts +67 -55
  65. package/src/lib/scripts/compiler.ts +43 -5
  66. package/src/lib/scripts/resolve.ts +89 -11
  67. package/src/lib/scripts/transform.ts +105 -3
  68. package/src/lib/styles/compiler.ts +9 -0
  69. package/src/lib/styles/emit.ts +75 -1
  70. package/src/lib/styles/resolve.ts +132 -35
  71. package/src/lib/watch.ts +34 -12
package/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # assets
2
2
 
3
- Fetch-based server for compiling browser JS/TS and CSS assets on demand.
3
+ Fetch-based server for compiling browser assets on demand.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - **On-Demand Compilation** - Compile browser scripts and styles on demand
8
+ - **File Serving** - Serve configured file assets like images and fonts with optional transforms
8
9
  - **Custom File Mapping** - Define patterns for mapping public URLs to file paths on disk
9
10
  - **Access Control** - Control exactly which files can be served with allow and deny rules
10
11
  - **Preloads** - Generate preload URLs for scripts and styles based on imports
@@ -20,18 +21,22 @@ npm i remix
20
21
 
21
22
  ## Usage
22
23
 
23
- Use `createAssetServer` to serve browser JS/TS and CSS assets from a URL namespace in your app.
24
+ Use `createAssetServer` to serve browser assets from a URL namespace in your app.
24
25
 
25
26
  ```ts
26
- import { createRouter } from 'remix/fetch-router'
27
+ import { createRouter } from 'remix/router'
27
28
  import { createAssetServer } from 'remix/assets'
28
29
 
29
30
  let assetServer = createAssetServer({
31
+ basePath: '/assets',
30
32
  fileMap: {
31
- '/assets/app/*path': 'app/*path',
32
- '/assets/npm/*path': 'node_modules/*path',
33
+ '/app/*path': 'app/*path',
34
+ '/npm/*path': 'node_modules/*path',
33
35
  },
34
36
  allow: ['app/assets/**', 'node_modules/**'],
37
+ files: {
38
+ extensions: ['.svg', '.png', '.jpg', '.jpeg', '.woff2'],
39
+ },
35
40
  })
36
41
 
37
42
  let router = createRouter()
@@ -53,9 +58,10 @@ import { createAssetServer } from 'remix/assets'
53
58
 
54
59
  let assetServer = createAssetServer({
55
60
  rootDir: path.resolve(import.meta.dirname, '..'),
61
+ basePath: '/assets',
56
62
  fileMap: {
57
- '/assets/app/*path': 'app/*path',
58
- '/assets/npm/*path': 'node_modules/*path',
63
+ '/app/*path': 'app/*path',
64
+ '/npm/*path': 'node_modules/*path',
59
65
  },
60
66
  allow: ['app/assets/**', 'node_modules/**'],
61
67
  })
@@ -69,7 +75,8 @@ You must provide an `allow` list to specify which files are allowed to be served
69
75
  import { createAssetServer } from 'remix/assets'
70
76
 
71
77
  let assetServer = createAssetServer({
72
- fileMap: { '/assets/app/*path': 'app/*path' },
78
+ basePath: '/assets',
79
+ fileMap: { '/app/*path': 'app/*path' },
73
80
  allow: ['app/assets/**'],
74
81
  deny: ['app/**/*.server.*'],
75
82
  })
@@ -79,21 +86,22 @@ Rules for `allow` and `deny` are file paths or globs. Relative values are resolv
79
86
 
80
87
  ## File Map
81
88
 
82
- Use `fileMap` to map public URLs to file paths on disk. The keys are public URL patterns, and the values are root-relative file path patterns.
89
+ Use `fileMap` to map public URLs to file paths on disk. `basePath` defines the shared public mount point, and the `fileMap` keys are URL patterns relative to that base path. The values are root-relative file path patterns.
83
90
 
84
91
  ```ts
85
92
  import { createAssetServer } from 'remix/assets'
86
93
 
87
94
  let assetServer = createAssetServer({
95
+ basePath: '/assets',
88
96
  fileMap: {
89
- '/assets/app/*path': 'app/*path',
90
- '/assets/packages/*path': '../packages/*path',
97
+ '/app/*path': 'app/*path',
98
+ '/packages/*path': '../packages/*path',
91
99
  },
92
100
  allow: ['app/assets/**', '../packages/**'],
93
101
  })
94
102
  ```
95
103
 
96
- `fileMap` entries use [`route-pattern`](https://github.com/remix-run/remix/tree/main/packages/route-pattern) syntax for both URL and file patterns. Wildcards must be named, and the same params must appear in both patterns so imports can be rewritten back to public URLs.
104
+ `fileMap` entries use [`route-pattern`](https://github.com/remix-run/remix/tree/main/packages/route-pattern) syntax for both URL and file patterns. Wildcards must be named, and the same params must appear in both patterns so imports can be rewritten back to public URLs. For example, with `basePath: '/assets'`, a `fileMap` key of `'/app/*path'` is served at `/assets/app/*path`.
97
105
 
98
106
  ### File watching
99
107
 
@@ -103,7 +111,8 @@ The file system is watched by default so source changes are picked up without re
103
111
  import { createAssetServer } from 'remix/assets'
104
112
 
105
113
  let assetServer = createAssetServer({
106
- fileMap: { '/assets/app/*path': 'app/*path' },
114
+ basePath: '/assets',
115
+ fileMap: { '/app/*path': 'app/*path' },
107
116
  allow: ['app/assets/**', 'app/node_modules/**'],
108
117
  })
109
118
  ```
@@ -120,7 +129,8 @@ You can disable file watching if the files on disk won't change, or if watching
120
129
  import { createAssetServer } from 'remix/assets'
121
130
 
122
131
  let assetServer = createAssetServer({
123
- fileMap: { '/assets/app/*path': 'app/*path' },
132
+ basePath: '/assets',
133
+ fileMap: { '/app/*path': 'app/*path' },
124
134
  allow: ['app/assets/**', 'app/node_modules/**'],
125
135
  watch: false,
126
136
  })
@@ -132,7 +142,8 @@ You can optionally provide an array of glob patterns to the `watch.ignore` optio
132
142
  import { createAssetServer } from 'remix/assets'
133
143
 
134
144
  let assetServer = createAssetServer({
135
- fileMap: { '/assets/app/*path': 'app/*path' },
145
+ basePath: '/assets',
146
+ fileMap: { '/app/*path': 'app/*path' },
136
147
  allow: ['app/assets/**', 'app/node_modules/**'],
137
148
  watch: {
138
149
  ignore: ['**/node_modules/**'],
@@ -149,6 +160,15 @@ let src = await assetServer.getHref('app/assets/entry.tsx')
149
160
  // '/assets/app/assets/entry.tsx'
150
161
  ```
151
162
 
163
+ For configured `files` assets, you can also pass a `transform` pipeline to build a request URL with custom file transforms. Basic transforms are written as strings, while dynamic transforms use `[name, param]` tuples.
164
+
165
+ ```ts
166
+ let src = await assetServer.getHref('app/assets/image.png', {
167
+ transform: [['resize', '100x100'], 'webp'],
168
+ })
169
+ // '/assets/app/assets/image.png?transform=resize%3A100x100&transform=webp'
170
+ ```
171
+
152
172
  ## Preloads
153
173
 
154
174
  Use `assetServer.getPreloads()` when rendering HTML so you can turn the returned URLs into `<link rel="modulepreload">`, stylesheet preload tags, or `Link` headers for one or more assets and their dependencies. You can provide root-relative or absolute file paths, or `file://` URLs.
@@ -159,14 +179,14 @@ let preloads = await assetServer.getPreloads(['app/assets/entry.tsx', 'app/asset
159
179
  // '/assets/app/assets/entry.tsx',
160
180
  // '/assets/app/assets/search.tsx',
161
181
  // '/assets/app/assets/utils.ts',
162
- // '/assets/npm/@remix-run/ui/index.js',
182
+ // '/assets/npm/remix/ui/index.js',
163
183
  // ...etc
164
184
  // ]
165
185
  ```
166
186
 
167
187
  ## Fingerprinting
168
188
 
169
- By default, assets are served at stable URLs with ETags and `Cache-Control: no-cache`. Responses are cached for the lifetime of the asset server instance.
189
+ By default, assets are served at stable URLs with ETags and `Cache-Control: no-cache`.
170
190
 
171
191
  If you want clients to cache assets aggressively without revalidation, you can opt into source-based fingerprinting.
172
192
 
@@ -174,7 +194,8 @@ If you want clients to cache assets aggressively without revalidation, you can o
174
194
  import { createAssetServer } from 'remix/assets'
175
195
 
176
196
  let assetServer = createAssetServer({
177
- fileMap: { '/assets/app/*path': 'app/*path' },
197
+ basePath: '/assets',
198
+ fileMap: { '/app/*path': 'app/*path' },
178
199
  allow: ['app/assets/**'],
179
200
  watch: false,
180
201
  fingerprint: {
@@ -195,7 +216,8 @@ Use `target` to lower emitted syntax to a specific browser support policy and/or
195
216
  import { createAssetServer } from 'remix/assets'
196
217
 
197
218
  let assetServer = createAssetServer({
198
- fileMap: { '/assets/app/*path': 'app/*path' },
219
+ basePath: '/assets',
220
+ fileMap: { '/app/*path': 'app/*path' },
199
221
  allow: ['app/assets/**'],
200
222
  target: {
201
223
  chrome: '109',
@@ -215,7 +237,8 @@ Enable sourcemaps with either `'external'` or `'inline'` using `sourceMaps`:
215
237
  import { createAssetServer } from 'remix/assets'
216
238
 
217
239
  let assetServer = createAssetServer({
218
- fileMap: { '/assets/app/*path': 'app/*path' },
240
+ basePath: '/assets',
241
+ fileMap: { '/app/*path': 'app/*path' },
219
242
  allow: ['app/assets/**'],
220
243
  sourceMaps: 'external',
221
244
  })
@@ -227,7 +250,8 @@ By default, sourcemap `sources` use URLs so they're presented alongside the comp
227
250
  import { createAssetServer } from 'remix/assets'
228
251
 
229
252
  let assetServer = createAssetServer({
230
- fileMap: { '/assets/app/*path': 'app/*path' },
253
+ basePath: '/assets',
254
+ fileMap: { '/app/*path': 'app/*path' },
231
255
  allow: ['app/assets/**'],
232
256
  sourceMaps: 'inline',
233
257
  sourceMapSourcePaths: 'absolute',
@@ -242,7 +266,8 @@ Enable minification with `minify`:
242
266
  import { createAssetServer } from 'remix/assets'
243
267
 
244
268
  let assetServer = createAssetServer({
245
- fileMap: { '/assets/app/*path': 'app/*path' },
269
+ basePath: '/assets',
270
+ fileMap: { '/app/*path': 'app/*path' },
246
271
  allow: ['app/assets/**'],
247
272
  minify: true,
248
273
  })
@@ -258,7 +283,8 @@ Use `scripts.define` to replace global identifiers with constant expressions.
258
283
  import { createAssetServer } from 'remix/assets'
259
284
 
260
285
  let assetServer = createAssetServer({
261
- fileMap: { '/assets/app/*path': 'app/*path' },
286
+ basePath: '/assets',
287
+ fileMap: { '/app/*path': 'app/*path' },
262
288
  allow: ['app/assets/**', 'app/node_modules/**'],
263
289
  scripts: {
264
290
  define: {
@@ -278,7 +304,8 @@ Use `scripts.external` to leave specific import specifiers unchanged by providin
278
304
  import { createAssetServer } from 'remix/assets'
279
305
 
280
306
  let assetServer = createAssetServer({
281
- fileMap: { '/assets/app/*path': 'app/*path' },
307
+ basePath: '/assets',
308
+ fileMap: { '/app/*path': 'app/*path' },
282
309
  allow: ['app/assets/**'],
283
310
  scripts: {
284
311
  external: ['my-external-import'],
@@ -286,15 +313,200 @@ let assetServer = createAssetServer({
286
313
  })
287
314
  ```
288
315
 
316
+ ## File Options
317
+
318
+ Use `files` to serve additional leaf assets like images and fonts. File extensions must include the leading dot and are only served when explicitly configured.
319
+
320
+ ```ts
321
+ import { createAssetServer } from 'remix/assets'
322
+
323
+ let assetServer = createAssetServer({
324
+ basePath: '/assets',
325
+ fileMap: { '/app/*path': 'app/*path' },
326
+ allow: ['app/assets/**'],
327
+ files: {
328
+ extensions: ['.svg', '.png', '.jpg', '.jpeg', '.woff2'],
329
+ },
330
+ })
331
+ ```
332
+
333
+ JavaScript/TypeScript and CSS extensions not supported in `files.extensions` as they are not leaf assets and have their own module systems.
334
+
335
+ ### File transforms
336
+
337
+ Files can optionally be transformed before serving.
338
+
339
+ Use `files.transforms` for named transforms that callers can opt into per request, provided via the `transform` option when calling `assetServer.getHref()`.
340
+
341
+ ```ts
342
+ import { createAssetServer, defineFileTransform } from 'remix/assets'
343
+ import sharp from 'sharp'
344
+
345
+ let assetServer = createAssetServer({
346
+ basePath: '/assets',
347
+ fileMap: { '/app/*path': 'app/*path' },
348
+ allow: ['app/assets/**'],
349
+ files: {
350
+ extensions: ['.svg', '.png', '.jpg', '.jpeg', '.woff2'],
351
+ transforms: {
352
+ webp: defineFileTransform({
353
+ extensions: ['.png', '.jpg', '.jpeg'],
354
+ async transform(bytes) {
355
+ return {
356
+ content: await sharp(bytes).webp({ quality: 80 }).toBuffer(),
357
+ extension: '.webp',
358
+ }
359
+ },
360
+ }),
361
+ },
362
+ },
363
+ })
364
+
365
+ let imageUrl = await assetServer.getHref('app/assets/photo.jpg', {
366
+ transform: ['webp'],
367
+ })
368
+ ```
369
+
370
+ Transforms can also accept a single string param value, provided as a `[name, param]` tuple in the `transform` array when calling `assetServer.getHref()`.
371
+
372
+ ```ts
373
+ import { createAssetServer, defineFileTransform } from 'remix/assets'
374
+
375
+ let assetServer = createAssetServer({
376
+ basePath: '/assets',
377
+ fileMap: { '/app/*path': 'app/*path' },
378
+ allow: ['app/assets/**'],
379
+ files: {
380
+ extensions: ['.svg', '.png', '.jpg', '.jpeg', '.woff2'],
381
+ transforms: {
382
+ recolor: defineFileTransform({
383
+ extensions: ['.svg'],
384
+ param: true,
385
+ async transform(bytes, { param }) {
386
+ if (!/^#?(?:[\da-f]{3,4}|[\da-f]{6}(?:[\da-f]{2})?)$/i.test(param)) {
387
+ throw new TypeError('Expected a hex color, with or without a leading #')
388
+ }
389
+
390
+ let svg = new TextDecoder().decode(bytes)
391
+ return svg.replaceAll('currentColor', `${!param.startsWith('#') ? '#' : ''}${param}`)
392
+ },
393
+ }),
394
+ },
395
+ },
396
+ })
397
+
398
+ let imageUrl = await assetServer.getHref('app/assets/logo.svg', {
399
+ transform: [['recolor', '0000ff']],
400
+ })
401
+ ```
402
+
403
+ Hand-authored URLs use repeated `transform` search params with `name` or `name:param` values:
404
+
405
+ ```css
406
+ .selector {
407
+ background-image: url('/assets/app/assets/image.png?transform=resize:100x100&transform=webp');
408
+ }
409
+ ```
410
+
411
+ #### Global file transforms
412
+
413
+ Use `files.globalTransforms` to define transforms that should always happen before a file is served. These transforms are run after any request-level transforms for all configured file extensions, and can return `null` to skip themselves for a given input.
414
+
415
+ ```ts
416
+ import { createAssetServer } from 'remix/assets'
417
+ import { optimize as optimizeSvg } from 'svgo'
418
+
419
+ let assetServer = createAssetServer({
420
+ basePath: '/assets',
421
+ fileMap: { '/app/*path': 'app/*path' },
422
+ allow: ['app/assets/**'],
423
+ files: {
424
+ extensions: ['.svg', '.png', '.jpg', '.jpeg', '.woff2'],
425
+ globalTransforms: [
426
+ {
427
+ extensions: ['.svg'],
428
+ async transform(bytes) {
429
+ let svg = new TextDecoder().decode(bytes)
430
+ return optimizeSvg(svg, { multipass: true }).data
431
+ },
432
+ },
433
+ ],
434
+ },
435
+ })
436
+ ```
437
+
438
+ #### File transform caching
439
+
440
+ Use `files.cache` to store transformed file outputs via a [`file-storage`](https://github.com/remix-run/remix/tree/main/packages/file-storage) backend.
441
+
442
+ Without `files.cache`, transformed file outputs are recomputed per request.
443
+
444
+ If `fingerprint.buildId` is set, the file cache can be reused across server restarts for the same build.
445
+
446
+ ```ts
447
+ import * as path from 'node:path'
448
+ import { createAssetServer } from 'remix/assets'
449
+ import { createFsFileStorage } from 'remix/file-storage/fs'
450
+
451
+ let assetServer = createAssetServer({
452
+ basePath: '/assets',
453
+ fileMap: { '/app/*path': 'app/*path' },
454
+ allow: ['app/assets/**'],
455
+ files: {
456
+ cache: createFsFileStorage(path.resolve('.tmp/assets-cache')),
457
+ extensions: ['.svg', '.png', '.jpg', '.jpeg', '.woff2'],
458
+ transforms: {
459
+ /*...*/
460
+ },
461
+ },
462
+ })
463
+ ```
464
+
465
+ #### Request transform limits
466
+
467
+ Use `files.maxRequestTransforms` to cap request transform pipelines. It defaults to `5`.
468
+
469
+ ```ts
470
+ import { createAssetServer } from 'remix/assets'
471
+
472
+ let assetServer = createAssetServer({
473
+ basePath: '/assets',
474
+ fileMap: { '/app/*path': 'app/*path' },
475
+ allow: ['app/assets/**'],
476
+ files: {
477
+ maxRequestTransforms: 5,
478
+ extensions: ['.svg', '.png', '.jpg', '.jpeg', '.woff2'],
479
+ transforms: {
480
+ /*...*/
481
+ },
482
+ },
483
+ })
484
+ ```
485
+
289
486
  ## CSS Imports
290
487
 
291
- Relative CSS `@import` rules are rewritten to asset server URLs. External `@import` URLs are left unchanged automatically. `url()` references are preserved as authored.
488
+ Relative CSS `@import` rules and `url()` references are rewritten to asset server URLs.
292
489
 
293
490
  ```css
294
- /* Rewritten to asset server URL: */
491
+ /* Rewritten to asset server URLs: */
295
492
  @import './reset.css';
296
- /* External URL: */
493
+ .selector {
494
+ background-image: url('./image.png');
495
+ }
496
+
497
+ /* External URLs: */
297
498
  @import 'https://fonts.googleapis.com/css2?family=Inter';
499
+ .selector {
500
+ background-image: url('https://example.com/logo.svg');
501
+ }
502
+ ```
503
+
504
+ File transforms can also be applied to relative CSS `url()` references:
505
+
506
+ ```css
507
+ .selector {
508
+ background-image: url('./image.png?transform=resize:100x100&transform=webp');
509
+ }
298
510
  ```
299
511
 
300
512
  ## Error Handling
@@ -305,7 +517,8 @@ Use `onError` to report unexpected compilation failures and/or return a custom r
305
517
  import { createAssetServer } from 'remix/assets'
306
518
 
307
519
  let assetServer = createAssetServer({
308
- fileMap: { '/assets/app/*path': 'app/*path' },
520
+ basePath: '/assets',
521
+ fileMap: { '/app/*path': 'app/*path' },
309
522
  allow: ['app/assets/**'],
310
523
  onError(error) {
311
524
  console.error('Failed to build client assets', error)
package/dist/assets.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { createAssetServer } from './lib/asset-server.ts';
2
+ export { defineFileTransform } from './lib/files/config.ts';
2
3
  export type { AssetServer, AssetServerOptions } from './lib/asset-server.ts';
3
4
  //# sourceMappingURL=assets.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA"}
1
+ {"version":3,"file":"assets.d.ts","sourceRoot":"","sources":["../src/assets.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA"}
package/dist/assets.js CHANGED
@@ -1 +1,2 @@
1
1
  export { createAssetServer } from "./lib/asset-server.js";
2
+ export { defineFileTransform } from "./lib/files/config.js";
@@ -1,4 +1,5 @@
1
1
  type AccessPolicy = {
2
+ isDenied(filePath: string): boolean;
2
3
  isAllowed(filePath: string): boolean;
3
4
  };
4
5
  export declare function createAccessPolicy(options: {
@@ -1 +1 @@
1
- {"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/lib/access.ts"],"names":[],"mappings":"AAEA,KAAK,YAAY,GAAG;IAClB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;CACrC,CAAA;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,OAAO,EAAE,MAAM,CAAA;CAChB,GAAG,YAAY,CAaf"}
1
+ {"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/lib/access.ts"],"names":[],"mappings":"AAGA,KAAK,YAAY,GAAG;IAClB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IACnC,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;CACrC,CAAA;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,OAAO,EAAE,MAAM,CAAA;CAChB,GAAG,YAAY,CAoBf"}
@@ -1,12 +1,21 @@
1
1
  import { createFileMatcher } from "./file-matcher.js";
2
+ import { isInjectedPackageFilePath } from "./injected-packages.js";
2
3
  export function createAccessPolicy(options) {
3
4
  let allowMatchers = options.allow.map((pattern) => createFileMatcher(pattern, options.rootDir));
4
5
  let denyMatchers = (options.deny ?? []).map((pattern) => createFileMatcher(pattern, options.rootDir));
6
+ function isDenied(filePath) {
7
+ if (isInjectedPackageFilePath(filePath))
8
+ return false;
9
+ return denyMatchers.length > 0 && denyMatchers.some((matcher) => matcher(filePath));
10
+ }
5
11
  return {
12
+ isDenied,
6
13
  isAllowed(filePath) {
14
+ if (isInjectedPackageFilePath(filePath))
15
+ return true;
7
16
  if (!allowMatchers.some((matcher) => matcher(filePath)))
8
17
  return false;
9
- if (denyMatchers.length > 0 && denyMatchers.some((matcher) => matcher(filePath)))
18
+ if (isDenied(filePath))
10
19
  return false;
11
20
  return true;
12
21
  },
@@ -1,3 +1,4 @@
1
+ import type { AssetRequestTransformMap, AssetServerFilesOptions, AssetTransformInvocation } from './files/config.ts';
1
2
  import type { AssetTarget } from './target.ts';
2
3
  import type { ChokidarWatcher } from './watch.ts';
3
4
  interface AssetServerWatchOptions {
@@ -33,7 +34,13 @@ interface AssetServerScriptOptions {
33
34
  /** Import specifiers to leave unrewritten (CDN URLs, import map entries, etc.) */
34
35
  external?: string[];
35
36
  }
36
- export interface AssetServerOptions {
37
+ /**
38
+ * Options used to construct an {@link AssetServer} via {@link createAssetServer}.
39
+ */
40
+ export interface AssetServerOptions<transforms extends AssetRequestTransformMap = {}> {
41
+ /** Public mount path for this asset server, e.g. `'/assets'`. */
42
+ basePath: string;
43
+ /** File patterns keyed by public URL patterns relative to `basePath`. */
37
44
  /** File patterns keyed by public URL patterns. */
38
45
  fileMap: Readonly<Record<string, string>>;
39
46
  /**
@@ -80,6 +87,12 @@ export interface AssetServerOptions {
80
87
  * Script-only configuration.
81
88
  */
82
89
  scripts?: AssetServerScriptOptions;
90
+ /**
91
+ * Leaf file asset configuration. Files configured here are served directly and can be
92
+ * referenced from CSS `url(...)` rules. Compiled asset extensions like `.css` and script
93
+ * module extensions are not allowed here.
94
+ */
95
+ files?: AssetServerFilesOptions<transforms>;
83
96
  /**
84
97
  * Enable filesystem-backed cache invalidation for long-lived server instances.
85
98
  * Enabled by default. Pass `true` to use the default watcher options, an options
@@ -92,7 +105,19 @@ export interface AssetServerOptions {
92
105
  */
93
106
  onError?: (error: unknown) => void | Response | Promise<void | Response>;
94
107
  }
95
- export interface AssetServer {
108
+ type AssetServerCreateOptions<transforms extends AssetRequestTransformMap> = Omit<AssetServerOptions<transforms>, 'files'> & {
109
+ files?: Omit<AssetServerFilesOptions<transforms>, 'transforms'> & {
110
+ transforms?: transforms;
111
+ };
112
+ };
113
+ export type AssetServerGetHrefOptions<transforms extends AssetRequestTransformMap> = undefined | {
114
+ transform: readonly AssetTransformInvocation<transforms>[];
115
+ };
116
+ /**
117
+ * Serves compiled scripts and styles for asset requests routed to it.
118
+ * Construct with {@link createAssetServer}.
119
+ */
120
+ export interface AssetServer<transforms extends AssetRequestTransformMap = {}> {
96
121
  /**
97
122
  * Serves a script or style request. Returns `Response | null` — null means the request
98
123
  * was not handled by this server, letting the router fall through to a 404.
@@ -101,7 +126,7 @@ export interface AssetServer {
101
126
  /**
102
127
  * Returns the request href for a served asset file.
103
128
  */
104
- getHref(filePath: string): Promise<string>;
129
+ getHref(filePath: string, options?: AssetServerGetHrefOptions<transforms>): Promise<string>;
105
130
  /**
106
131
  * Returns preload URLs for one or more served asset files, ordered shallowest-first.
107
132
  */
@@ -111,8 +136,8 @@ export interface AssetServer {
111
136
  */
112
137
  close(): Promise<void>;
113
138
  }
114
- export declare function getInternalChokidarWatcher(assetServer: AssetServer): ChokidarWatcher | undefined;
115
- export declare function getInternalWatchTargets(assetServer: AssetServer): readonly string[];
139
+ export declare function getInternalChokidarWatcher<transforms extends AssetRequestTransformMap>(assetServer: AssetServer<transforms>): ChokidarWatcher | undefined;
140
+ export declare function getInternalWatchTargets<transforms extends AssetRequestTransformMap>(assetServer: AssetServer<transforms>): readonly string[];
116
141
  /**
117
142
  * Create an asset server instance
118
143
  *
@@ -125,8 +150,9 @@ export declare function getInternalWatchTargets(assetServer: AssetServer): reado
125
150
  * @example
126
151
  * ```ts
127
152
  * let assetServer = createAssetServer({
153
+ * basePath: '/assets',
128
154
  * fileMap: {
129
- * '/assets/app/*path': 'app/*path',
155
+ * '/app/*path': 'app/*path',
130
156
  * },
131
157
  * allow: ['app/**'],
132
158
  * })
@@ -134,6 +160,6 @@ export declare function getInternalWatchTargets(assetServer: AssetServer): reado
134
160
  * route('/assets/*path', ({ request }) => assetServer.fetch(request))
135
161
  * ```
136
162
  */
137
- export declare function createAssetServer(options: AssetServerOptions): AssetServer;
163
+ export declare function createAssetServer<const transforms extends AssetRequestTransformMap = {}>(options: AssetServerCreateOptions<transforms>): AssetServer<transforms>;
138
164
  export {};
139
165
  //# sourceMappingURL=asset-server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"asset-server.d.ts","sourceRoot":"","sources":["../../src/lib/asset-server.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,WAAW,EAA6C,MAAM,aAAa,CAAA;AAEzF,OAAO,KAAK,EAAsB,eAAe,EAAE,MAAM,YAAY,CAAA;AAErE,UAAU,uBAAuB;IAC/B;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1B;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;IACd;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,UAAU,kBAAkB;IAC1B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,KAAK,eAAe,GAAG,QAAQ,GAAG,UAAU,CAAA;AAC5C,KAAK,yBAAyB,GAAG,KAAK,GAAG,UAAU,CAAA;AAEnD,UAAU,wBAAwB;IAChC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AAID,MAAM,WAAW,kBAAkB;IACjC,kDAAkD;IAClD,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACzC;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,kBAAkB,CAAA;IAChC;;;OAGG;IACH,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,yBAAyB,CAAA;IAChD;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB;;OAEG;IACH,OAAO,CAAC,EAAE,wBAAwB,CAAA;IAClC;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,uBAAuB,CAAA;IACzC;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAAG,QAAQ,GAAG,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAA;CACzE;AAED,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IACjD;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC1C;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;IACpE;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAuBD,wBAAgB,0BAA0B,CAAC,WAAW,EAAE,WAAW,GAAG,eAAe,GAAG,SAAS,CAEhG;AAED,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,WAAW,GAAG,SAAS,MAAM,EAAE,CAEnF;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAqN1E"}
1
+ {"version":3,"file":"asset-server.d.ts","sourceRoot":"","sources":["../../src/lib/asset-server.ts"],"names":[],"mappings":"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"}