@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.
- package/README.md +241 -28
- package/dist/assets.d.ts +1 -0
- package/dist/assets.d.ts.map +1 -1
- package/dist/assets.js +1 -0
- package/dist/lib/access.d.ts +1 -0
- package/dist/lib/access.d.ts.map +1 -1
- package/dist/lib/access.js +10 -1
- package/dist/lib/asset-server.d.ts +33 -7
- package/dist/lib/asset-server.d.ts.map +1 -1
- package/dist/lib/asset-server.js +195 -23
- package/dist/lib/compilation-error.d.ts +1 -1
- package/dist/lib/compilation-error.d.ts.map +1 -1
- package/dist/lib/files/compiler.d.ts +71 -0
- package/dist/lib/files/compiler.d.ts.map +1 -0
- package/dist/lib/files/compiler.js +552 -0
- package/dist/lib/files/config.d.ts +101 -0
- package/dist/lib/files/config.d.ts.map +1 -0
- package/dist/lib/files/config.js +219 -0
- package/dist/lib/files/store.d.ts +31 -0
- package/dist/lib/files/store.d.ts.map +1 -0
- package/dist/lib/files/store.js +63 -0
- package/dist/lib/fingerprint.d.ts +3 -3
- package/dist/lib/fingerprint.d.ts.map +1 -1
- package/dist/lib/fingerprint.js +5 -5
- package/dist/lib/injected-packages.d.ts +11 -0
- package/dist/lib/injected-packages.d.ts.map +1 -0
- package/dist/lib/injected-packages.js +86 -0
- package/dist/lib/paths.d.ts +1 -0
- package/dist/lib/paths.d.ts.map +1 -1
- package/dist/lib/paths.js +12 -0
- package/dist/lib/routes.d.ts +5 -7
- package/dist/lib/routes.d.ts.map +1 -1
- package/dist/lib/routes.js +41 -36
- package/dist/lib/scripts/compiler.d.ts +1 -0
- package/dist/lib/scripts/compiler.d.ts.map +1 -1
- package/dist/lib/scripts/compiler.js +30 -5
- package/dist/lib/scripts/resolve.d.ts.map +1 -1
- package/dist/lib/scripts/resolve.js +57 -10
- package/dist/lib/scripts/transform.d.ts.map +1 -1
- package/dist/lib/scripts/transform.js +79 -3
- package/dist/lib/styles/compiler.d.ts +4 -0
- package/dist/lib/styles/compiler.d.ts.map +1 -1
- package/dist/lib/styles/compiler.js +2 -0
- package/dist/lib/styles/emit.d.ts +3 -0
- package/dist/lib/styles/emit.d.ts.map +1 -1
- package/dist/lib/styles/emit.js +36 -1
- package/dist/lib/styles/resolve.d.ts +8 -1
- package/dist/lib/styles/resolve.d.ts.map +1 -1
- package/dist/lib/styles/resolve.js +85 -32
- package/dist/lib/watch.d.ts +2 -0
- package/dist/lib/watch.d.ts.map +1 -1
- package/dist/lib/watch.js +25 -8
- package/package.json +11 -5
- package/src/assets.ts +1 -0
- package/src/lib/access.ts +10 -1
- package/src/lib/asset-server.ts +297 -34
- package/src/lib/compilation-error.ts +9 -0
- package/src/lib/files/compiler.ts +885 -0
- package/src/lib/files/config.ts +479 -0
- package/src/lib/files/store.ts +109 -0
- package/src/lib/fingerprint.ts +8 -7
- package/src/lib/injected-packages.ts +117 -0
- package/src/lib/paths.ts +28 -0
- package/src/lib/routes.ts +67 -55
- package/src/lib/scripts/compiler.ts +43 -5
- package/src/lib/scripts/resolve.ts +89 -11
- package/src/lib/scripts/transform.ts +105 -3
- package/src/lib/styles/compiler.ts +9 -0
- package/src/lib/styles/emit.ts +75 -1
- package/src/lib/styles/resolve.ts +132 -35
- 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
|
|
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
|
|
24
|
+
Use `createAssetServer` to serve browser assets from a URL namespace in your app.
|
|
24
25
|
|
|
25
26
|
```ts
|
|
26
|
-
import { createRouter } from 'remix/
|
|
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
|
-
'/
|
|
32
|
-
'/
|
|
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
|
-
'/
|
|
58
|
-
'/
|
|
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
|
-
|
|
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.
|
|
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
|
-
'/
|
|
90
|
-
'/
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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`.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
488
|
+
Relative CSS `@import` rules and `url()` references are rewritten to asset server URLs.
|
|
292
489
|
|
|
293
490
|
```css
|
|
294
|
-
/* Rewritten to asset server
|
|
491
|
+
/* Rewritten to asset server URLs: */
|
|
295
492
|
@import './reset.css';
|
|
296
|
-
|
|
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
|
-
|
|
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
package/dist/assets.d.ts.map
CHANGED
|
@@ -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
package/dist/lib/access.d.ts
CHANGED
package/dist/lib/access.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/lib/access.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/lib/access.js
CHANGED
|
@@ -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 (
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
* '/
|
|
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:
|
|
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":"
|
|
1
|
+
{"version":3,"file":"asset-server.d.ts","sourceRoot":"","sources":["../../src/lib/asset-server.ts"],"names":[],"mappings":"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"}
|