@remix-run/assets 0.4.4 → 0.5.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 +322 -56
- package/dist/assets.d.ts +2 -1
- package/dist/assets.d.ts.map +1 -1
- package/dist/assets.js +2 -2
- package/dist/lib/access.d.ts +6 -2
- package/dist/lib/access.d.ts.map +1 -1
- package/dist/lib/access.js +300 -5
- package/dist/lib/asset-server.d.ts +118 -5
- package/dist/lib/asset-server.d.ts.map +1 -1
- package/dist/lib/asset-server.js +260 -24
- package/dist/lib/compilation-error.d.ts +1 -1
- package/dist/lib/compilation-error.d.ts.map +1 -1
- package/dist/lib/file-matcher.js +1 -1
- package/dist/lib/files/compiler.d.ts.map +1 -1
- package/dist/lib/files/compiler.js +7 -6
- package/dist/lib/files/config.js +1 -1
- package/dist/lib/hmr.d.ts +37 -0
- package/dist/lib/hmr.d.ts.map +1 -0
- package/dist/lib/hmr.js +357 -0
- package/dist/lib/injected-packages.d.ts.map +1 -1
- package/dist/lib/injected-packages.js +36 -13
- package/dist/lib/loaders.d.ts +57 -0
- package/dist/lib/loaders.d.ts.map +1 -0
- package/dist/lib/loaders.js +1 -0
- package/dist/lib/module-store.d.ts +24 -0
- package/dist/lib/module-store.d.ts.map +1 -1
- package/dist/lib/module-store.js +136 -11
- package/dist/lib/routes.js +1 -1
- package/dist/lib/scripts/compiler.d.ts +24 -1
- package/dist/lib/scripts/compiler.d.ts.map +1 -1
- package/dist/lib/scripts/compiler.js +183 -29
- package/dist/lib/scripts/conditions.d.ts +2 -0
- package/dist/lib/scripts/conditions.d.ts.map +1 -0
- package/dist/lib/scripts/conditions.js +1 -0
- package/dist/lib/scripts/emit.d.ts +3 -0
- package/dist/lib/scripts/emit.d.ts.map +1 -1
- package/dist/lib/scripts/emit.js +24 -5
- package/dist/lib/scripts/resolve.d.ts +4 -0
- package/dist/lib/scripts/resolve.d.ts.map +1 -1
- package/dist/lib/scripts/resolve.js +70 -5
- package/dist/lib/scripts/transform.d.ts +9 -0
- package/dist/lib/scripts/transform.d.ts.map +1 -1
- package/dist/lib/scripts/transform.js +222 -18
- package/dist/lib/source-maps.js +1 -1
- package/dist/lib/styles/compiler.d.ts +14 -1
- package/dist/lib/styles/compiler.d.ts.map +1 -1
- package/dist/lib/styles/compiler.js +78 -14
- package/dist/lib/styles/emit.js +4 -4
- package/dist/lib/styles/resolve.d.ts.map +1 -1
- package/dist/lib/styles/resolve.js +8 -7
- package/dist/lib/styles/transform.js +3 -3
- package/dist/lib/target.d.ts +1 -1
- package/dist/lib/target.d.ts.map +1 -1
- package/dist/lib/watch.js +1 -1
- package/dist/types/hmr.d.ts +36 -0
- package/package.json +17 -11
- package/src/assets.ts +2 -1
- package/src/lib/access.ts +386 -5
- package/src/lib/asset-server.ts +429 -18
- package/src/lib/compilation-error.ts +1 -0
- package/src/lib/files/compiler.ts +7 -3
- package/src/lib/hmr.ts +397 -0
- package/src/lib/injected-packages.ts +41 -11
- package/src/lib/loaders.ts +80 -0
- package/src/lib/module-store.ts +190 -9
- package/src/lib/scripts/compiler.ts +248 -24
- package/src/lib/scripts/conditions.ts +1 -0
- package/src/lib/scripts/emit.ts +50 -5
- package/src/lib/scripts/resolve.ts +124 -2
- package/src/lib/scripts/transform.ts +290 -19
- package/src/lib/styles/compiler.ts +108 -8
- package/src/lib/styles/emit.ts +1 -1
- package/src/lib/styles/resolve.ts +11 -7
- package/src/types/hmr.d.ts +36 -0
package/README.md
CHANGED
|
@@ -7,11 +7,13 @@ Fetch-based server for compiling browser assets on demand.
|
|
|
7
7
|
- **On-Demand Compilation** - Compile browser scripts and styles on demand
|
|
8
8
|
- **File Serving** - Serve configured file assets like images and fonts with optional transforms
|
|
9
9
|
- **Custom File Mapping** - Define patterns for mapping public URLs to file paths on disk
|
|
10
|
-
- **Access Control** - Control exactly which files can be served
|
|
10
|
+
- **Access Control** - Control exactly which files and packages can be served
|
|
11
11
|
- **Preloads** - Generate preload URLs for scripts and styles based on imports
|
|
12
12
|
- **Caching** - Conservative caching by default with stable URLs, ETags, and revalidation
|
|
13
13
|
- **Optional Fingerprinting** - Source-based fingerprinted URLs for long-lived browser caching
|
|
14
14
|
- **Source Maps** - Serve inline or external sourcemaps
|
|
15
|
+
- **Hot Module Reloading** - Handle live code updates in development
|
|
16
|
+
- **Script Loaders** - Post-process compiled JavaScript with Node-compatible loaders
|
|
15
17
|
|
|
16
18
|
## Installation
|
|
17
19
|
|
|
@@ -33,7 +35,8 @@ let assetServer = createAssetServer({
|
|
|
33
35
|
'/app/*path': 'app/*path',
|
|
34
36
|
'/npm/*path': 'node_modules/*path',
|
|
35
37
|
},
|
|
36
|
-
|
|
38
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
39
|
+
allowPackages: ['remix'],
|
|
37
40
|
files: {
|
|
38
41
|
extensions: ['.svg', '.png', '.jpg', '.jpeg', '.woff2'],
|
|
39
42
|
},
|
|
@@ -46,7 +49,7 @@ router.get('/assets/*', ({ request }) => {
|
|
|
46
49
|
})
|
|
47
50
|
```
|
|
48
51
|
|
|
49
|
-
This example gives you an `/assets/*` endpoint that serves compiled browser
|
|
52
|
+
This example gives you an `/assets/*` endpoint that serves compiled browser source from `public/` directories throughout `app/` and from the `remix` package.
|
|
50
53
|
|
|
51
54
|
## Root Directory
|
|
52
55
|
|
|
@@ -63,26 +66,33 @@ let assetServer = createAssetServer({
|
|
|
63
66
|
'/app/*path': 'app/*path',
|
|
64
67
|
'/npm/*path': 'node_modules/*path',
|
|
65
68
|
},
|
|
66
|
-
|
|
69
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
70
|
+
allowPackages: ['remix'],
|
|
67
71
|
})
|
|
68
72
|
```
|
|
69
73
|
|
|
70
74
|
## Access Control
|
|
71
75
|
|
|
72
|
-
You must provide an `
|
|
76
|
+
You must provide an `allowFiles` list to specify which files are allowed to be served. You can also allow whole packages by name with `allowPackages`. `denyFiles` is optional and takes precedence over both `allowFiles` and `allowPackages`.
|
|
73
77
|
|
|
74
78
|
```ts
|
|
75
79
|
import { createAssetServer } from 'remix/assets'
|
|
76
80
|
|
|
77
81
|
let assetServer = createAssetServer({
|
|
78
82
|
basePath: '/assets',
|
|
79
|
-
fileMap: {
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
fileMap: {
|
|
84
|
+
'/app/*path': 'app/*path',
|
|
85
|
+
'/npm/*path': 'node_modules/*path',
|
|
86
|
+
},
|
|
87
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
88
|
+
allowPackages: ['remix'],
|
|
89
|
+
denyFiles: ['app/**/*.server.*'],
|
|
82
90
|
})
|
|
83
91
|
```
|
|
84
92
|
|
|
85
|
-
|
|
93
|
+
Values for `allowFiles` and `denyFiles` are file paths or globs. Relative values are resolved from `rootDir`. Absolute file paths match exactly, and absolute directory paths also match their descendants.
|
|
94
|
+
|
|
95
|
+
Values for `allowPackages` are exact package names. Dependencies and installed optional dependencies of packages in `allowPackages` are also allowed automatically. Peer dependencies must be listed explicitly if they should be browser-reachable. Allowed package files must still be reachable through `fileMap`.
|
|
86
96
|
|
|
87
97
|
## File Map
|
|
88
98
|
|
|
@@ -95,9 +105,10 @@ let assetServer = createAssetServer({
|
|
|
95
105
|
basePath: '/assets',
|
|
96
106
|
fileMap: {
|
|
97
107
|
'/app/*path': 'app/*path',
|
|
98
|
-
'/
|
|
108
|
+
'/npm/*path': 'node_modules/*path',
|
|
99
109
|
},
|
|
100
|
-
|
|
110
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
111
|
+
allowPackages: ['remix'],
|
|
101
112
|
})
|
|
102
113
|
```
|
|
103
114
|
|
|
@@ -112,8 +123,12 @@ import { createAssetServer } from 'remix/assets'
|
|
|
112
123
|
|
|
113
124
|
let assetServer = createAssetServer({
|
|
114
125
|
basePath: '/assets',
|
|
115
|
-
fileMap: {
|
|
116
|
-
|
|
126
|
+
fileMap: {
|
|
127
|
+
'/app/*path': 'app/*path',
|
|
128
|
+
'/npm/*path': 'node_modules/*path',
|
|
129
|
+
},
|
|
130
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
131
|
+
allowPackages: ['remix'],
|
|
117
132
|
})
|
|
118
133
|
```
|
|
119
134
|
|
|
@@ -130,8 +145,12 @@ import { createAssetServer } from 'remix/assets'
|
|
|
130
145
|
|
|
131
146
|
let assetServer = createAssetServer({
|
|
132
147
|
basePath: '/assets',
|
|
133
|
-
fileMap: {
|
|
134
|
-
|
|
148
|
+
fileMap: {
|
|
149
|
+
'/app/*path': 'app/*path',
|
|
150
|
+
'/npm/*path': 'node_modules/*path',
|
|
151
|
+
},
|
|
152
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
153
|
+
allowPackages: ['remix'],
|
|
135
154
|
watch: false,
|
|
136
155
|
})
|
|
137
156
|
```
|
|
@@ -143,8 +162,12 @@ import { createAssetServer } from 'remix/assets'
|
|
|
143
162
|
|
|
144
163
|
let assetServer = createAssetServer({
|
|
145
164
|
basePath: '/assets',
|
|
146
|
-
fileMap: {
|
|
147
|
-
|
|
165
|
+
fileMap: {
|
|
166
|
+
'/app/*path': 'app/*path',
|
|
167
|
+
'/npm/*path': 'node_modules/*path',
|
|
168
|
+
},
|
|
169
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
170
|
+
allowPackages: ['remix'],
|
|
148
171
|
watch: {
|
|
149
172
|
ignore: ['**/node_modules/**'],
|
|
150
173
|
},
|
|
@@ -156,17 +179,17 @@ let assetServer = createAssetServer({
|
|
|
156
179
|
Use `assetServer.getHref()` when you need the public URL for a served asset. You can provide a root-relative or absolute file path, or a `file://` URL.
|
|
157
180
|
|
|
158
181
|
```ts
|
|
159
|
-
let src = await assetServer.getHref('app/
|
|
160
|
-
// '/assets/app/
|
|
182
|
+
let src = await assetServer.getHref('app/actions/public/entry.ts')
|
|
183
|
+
// '/assets/app/actions/public/entry.ts'
|
|
161
184
|
```
|
|
162
185
|
|
|
163
186
|
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
187
|
|
|
165
188
|
```ts
|
|
166
|
-
let src = await assetServer.getHref('app/
|
|
189
|
+
let src = await assetServer.getHref('app/media/public/image.png', {
|
|
167
190
|
transform: [['resize', '100x100'], 'webp'],
|
|
168
191
|
})
|
|
169
|
-
// '/assets/app/
|
|
192
|
+
// '/assets/app/media/public/image.png?transform=resize%3A100x100&transform=webp'
|
|
170
193
|
```
|
|
171
194
|
|
|
172
195
|
## Preloads
|
|
@@ -174,11 +197,14 @@ let src = await assetServer.getHref('app/assets/image.png', {
|
|
|
174
197
|
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.
|
|
175
198
|
|
|
176
199
|
```ts
|
|
177
|
-
let preloads = await assetServer.getPreloads([
|
|
200
|
+
let preloads = await assetServer.getPreloads([
|
|
201
|
+
'app/actions/public/entry.ts',
|
|
202
|
+
'app/search/public/search.tsx',
|
|
203
|
+
])
|
|
178
204
|
// [
|
|
179
|
-
// '/assets/app/
|
|
180
|
-
// '/assets/app/
|
|
181
|
-
// '/assets/app/
|
|
205
|
+
// '/assets/app/actions/public/entry.ts',
|
|
206
|
+
// '/assets/app/search/public/search.tsx',
|
|
207
|
+
// '/assets/app/search/public/utils.ts',
|
|
182
208
|
// '/assets/npm/remix/ui/index.js',
|
|
183
209
|
// ...etc
|
|
184
210
|
// ]
|
|
@@ -195,8 +221,12 @@ import { createAssetServer } from 'remix/assets'
|
|
|
195
221
|
|
|
196
222
|
let assetServer = createAssetServer({
|
|
197
223
|
basePath: '/assets',
|
|
198
|
-
fileMap: {
|
|
199
|
-
|
|
224
|
+
fileMap: {
|
|
225
|
+
'/app/*path': 'app/*path',
|
|
226
|
+
'/npm/*path': 'node_modules/*path',
|
|
227
|
+
},
|
|
228
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
229
|
+
allowPackages: ['remix'],
|
|
200
230
|
watch: false,
|
|
201
231
|
fingerprint: {
|
|
202
232
|
buildId: process.env.GITHUB_SHA,
|
|
@@ -217,8 +247,12 @@ import { createAssetServer } from 'remix/assets'
|
|
|
217
247
|
|
|
218
248
|
let assetServer = createAssetServer({
|
|
219
249
|
basePath: '/assets',
|
|
220
|
-
fileMap: {
|
|
221
|
-
|
|
250
|
+
fileMap: {
|
|
251
|
+
'/app/*path': 'app/*path',
|
|
252
|
+
'/npm/*path': 'node_modules/*path',
|
|
253
|
+
},
|
|
254
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
255
|
+
allowPackages: ['remix'],
|
|
222
256
|
target: {
|
|
223
257
|
chrome: '109',
|
|
224
258
|
ios: '15.6',
|
|
@@ -238,8 +272,12 @@ import { createAssetServer } from 'remix/assets'
|
|
|
238
272
|
|
|
239
273
|
let assetServer = createAssetServer({
|
|
240
274
|
basePath: '/assets',
|
|
241
|
-
fileMap: {
|
|
242
|
-
|
|
275
|
+
fileMap: {
|
|
276
|
+
'/app/*path': 'app/*path',
|
|
277
|
+
'/npm/*path': 'node_modules/*path',
|
|
278
|
+
},
|
|
279
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
280
|
+
allowPackages: ['remix'],
|
|
243
281
|
sourceMaps: 'external',
|
|
244
282
|
})
|
|
245
283
|
```
|
|
@@ -251,8 +289,12 @@ import { createAssetServer } from 'remix/assets'
|
|
|
251
289
|
|
|
252
290
|
let assetServer = createAssetServer({
|
|
253
291
|
basePath: '/assets',
|
|
254
|
-
fileMap: {
|
|
255
|
-
|
|
292
|
+
fileMap: {
|
|
293
|
+
'/app/*path': 'app/*path',
|
|
294
|
+
'/npm/*path': 'node_modules/*path',
|
|
295
|
+
},
|
|
296
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
297
|
+
allowPackages: ['remix'],
|
|
256
298
|
sourceMaps: 'inline',
|
|
257
299
|
sourceMapSourcePaths: 'absolute',
|
|
258
300
|
})
|
|
@@ -267,8 +309,12 @@ import { createAssetServer } from 'remix/assets'
|
|
|
267
309
|
|
|
268
310
|
let assetServer = createAssetServer({
|
|
269
311
|
basePath: '/assets',
|
|
270
|
-
fileMap: {
|
|
271
|
-
|
|
312
|
+
fileMap: {
|
|
313
|
+
'/app/*path': 'app/*path',
|
|
314
|
+
'/npm/*path': 'node_modules/*path',
|
|
315
|
+
},
|
|
316
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
317
|
+
allowPackages: ['remix'],
|
|
272
318
|
minify: true,
|
|
273
319
|
})
|
|
274
320
|
```
|
|
@@ -284,8 +330,12 @@ import { createAssetServer } from 'remix/assets'
|
|
|
284
330
|
|
|
285
331
|
let assetServer = createAssetServer({
|
|
286
332
|
basePath: '/assets',
|
|
287
|
-
fileMap: {
|
|
288
|
-
|
|
333
|
+
fileMap: {
|
|
334
|
+
'/app/*path': 'app/*path',
|
|
335
|
+
'/npm/*path': 'node_modules/*path',
|
|
336
|
+
},
|
|
337
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
338
|
+
allowPackages: ['remix'],
|
|
289
339
|
scripts: {
|
|
290
340
|
define: {
|
|
291
341
|
'process.env.NODE_ENV': '"production"',
|
|
@@ -305,14 +355,46 @@ import { createAssetServer } from 'remix/assets'
|
|
|
305
355
|
|
|
306
356
|
let assetServer = createAssetServer({
|
|
307
357
|
basePath: '/assets',
|
|
308
|
-
fileMap: {
|
|
309
|
-
|
|
358
|
+
fileMap: {
|
|
359
|
+
'/app/*path': 'app/*path',
|
|
360
|
+
'/npm/*path': 'node_modules/*path',
|
|
361
|
+
},
|
|
362
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
363
|
+
allowPackages: ['remix'],
|
|
310
364
|
scripts: {
|
|
311
365
|
external: ['my-external-import'],
|
|
312
366
|
},
|
|
313
367
|
})
|
|
314
368
|
```
|
|
315
369
|
|
|
370
|
+
### Loaders
|
|
371
|
+
|
|
372
|
+
Use `scripts.loaders` to post-process compiled JavaScript. Loaders use the same function signature and chaining behavior as synchronous [`load` hooks in Node's module API](https://nodejs.org/api/module.html#customization-hooks).
|
|
373
|
+
|
|
374
|
+
```ts
|
|
375
|
+
import { createAssetServer } from 'remix/assets'
|
|
376
|
+
|
|
377
|
+
let assetServer = createAssetServer({
|
|
378
|
+
basePath: '/assets',
|
|
379
|
+
fileMap: { '/app/*path': 'app/*path' },
|
|
380
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
381
|
+
denyFiles: ['app/**/*.test.*'],
|
|
382
|
+
scripts: {
|
|
383
|
+
loaders: [
|
|
384
|
+
(url, context, nextLoad) => {
|
|
385
|
+
let result = nextLoad(url, context)
|
|
386
|
+
return {
|
|
387
|
+
...result,
|
|
388
|
+
source: `${result.source}\nconsole.log('loaded')`,
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
],
|
|
392
|
+
},
|
|
393
|
+
})
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Loaders receive JavaScript after the asset server transforms TypeScript and JavaScript, and run before HMR analysis and minification. They must return `format: 'module'`. Import attributes are not supported.
|
|
397
|
+
|
|
316
398
|
## File Options
|
|
317
399
|
|
|
318
400
|
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.
|
|
@@ -322,8 +404,12 @@ import { createAssetServer } from 'remix/assets'
|
|
|
322
404
|
|
|
323
405
|
let assetServer = createAssetServer({
|
|
324
406
|
basePath: '/assets',
|
|
325
|
-
fileMap: {
|
|
326
|
-
|
|
407
|
+
fileMap: {
|
|
408
|
+
'/app/*path': 'app/*path',
|
|
409
|
+
'/npm/*path': 'node_modules/*path',
|
|
410
|
+
},
|
|
411
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
412
|
+
allowPackages: ['remix'],
|
|
327
413
|
files: {
|
|
328
414
|
extensions: ['.svg', '.png', '.jpg', '.jpeg', '.woff2'],
|
|
329
415
|
},
|
|
@@ -344,8 +430,12 @@ import sharp from 'sharp'
|
|
|
344
430
|
|
|
345
431
|
let assetServer = createAssetServer({
|
|
346
432
|
basePath: '/assets',
|
|
347
|
-
fileMap: {
|
|
348
|
-
|
|
433
|
+
fileMap: {
|
|
434
|
+
'/app/*path': 'app/*path',
|
|
435
|
+
'/npm/*path': 'node_modules/*path',
|
|
436
|
+
},
|
|
437
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
438
|
+
allowPackages: ['remix'],
|
|
349
439
|
files: {
|
|
350
440
|
extensions: ['.svg', '.png', '.jpg', '.jpeg', '.woff2'],
|
|
351
441
|
transforms: {
|
|
@@ -362,7 +452,7 @@ let assetServer = createAssetServer({
|
|
|
362
452
|
},
|
|
363
453
|
})
|
|
364
454
|
|
|
365
|
-
let imageUrl = await assetServer.getHref('app/
|
|
455
|
+
let imageUrl = await assetServer.getHref('app/media/public/photo.jpg', {
|
|
366
456
|
transform: ['webp'],
|
|
367
457
|
})
|
|
368
458
|
```
|
|
@@ -374,8 +464,12 @@ import { createAssetServer, defineFileTransform } from 'remix/assets'
|
|
|
374
464
|
|
|
375
465
|
let assetServer = createAssetServer({
|
|
376
466
|
basePath: '/assets',
|
|
377
|
-
fileMap: {
|
|
378
|
-
|
|
467
|
+
fileMap: {
|
|
468
|
+
'/app/*path': 'app/*path',
|
|
469
|
+
'/npm/*path': 'node_modules/*path',
|
|
470
|
+
},
|
|
471
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
472
|
+
allowPackages: ['remix'],
|
|
379
473
|
files: {
|
|
380
474
|
extensions: ['.svg', '.png', '.jpg', '.jpeg', '.woff2'],
|
|
381
475
|
transforms: {
|
|
@@ -395,7 +489,7 @@ let assetServer = createAssetServer({
|
|
|
395
489
|
},
|
|
396
490
|
})
|
|
397
491
|
|
|
398
|
-
let imageUrl = await assetServer.getHref('app/
|
|
492
|
+
let imageUrl = await assetServer.getHref('app/media/public/logo.svg', {
|
|
399
493
|
transform: [['recolor', '0000ff']],
|
|
400
494
|
})
|
|
401
495
|
```
|
|
@@ -404,7 +498,7 @@ Hand-authored URLs use repeated `transform` search params with `name` or `name:p
|
|
|
404
498
|
|
|
405
499
|
```css
|
|
406
500
|
.selector {
|
|
407
|
-
background-image: url('/assets/app/
|
|
501
|
+
background-image: url('/assets/app/media/public/image.png?transform=resize:100x100&transform=webp');
|
|
408
502
|
}
|
|
409
503
|
```
|
|
410
504
|
|
|
@@ -418,8 +512,12 @@ import { optimize as optimizeSvg } from 'svgo'
|
|
|
418
512
|
|
|
419
513
|
let assetServer = createAssetServer({
|
|
420
514
|
basePath: '/assets',
|
|
421
|
-
fileMap: {
|
|
422
|
-
|
|
515
|
+
fileMap: {
|
|
516
|
+
'/app/*path': 'app/*path',
|
|
517
|
+
'/npm/*path': 'node_modules/*path',
|
|
518
|
+
},
|
|
519
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
520
|
+
allowPackages: ['remix'],
|
|
423
521
|
files: {
|
|
424
522
|
extensions: ['.svg', '.png', '.jpg', '.jpeg', '.woff2'],
|
|
425
523
|
globalTransforms: [
|
|
@@ -450,8 +548,12 @@ import { createFsFileStorage } from 'remix/file-storage/fs'
|
|
|
450
548
|
|
|
451
549
|
let assetServer = createAssetServer({
|
|
452
550
|
basePath: '/assets',
|
|
453
|
-
fileMap: {
|
|
454
|
-
|
|
551
|
+
fileMap: {
|
|
552
|
+
'/app/*path': 'app/*path',
|
|
553
|
+
'/npm/*path': 'node_modules/*path',
|
|
554
|
+
},
|
|
555
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
556
|
+
allowPackages: ['remix'],
|
|
455
557
|
files: {
|
|
456
558
|
cache: createFsFileStorage(path.resolve('.tmp/assets-cache')),
|
|
457
559
|
extensions: ['.svg', '.png', '.jpg', '.jpeg', '.woff2'],
|
|
@@ -471,8 +573,12 @@ import { createAssetServer } from 'remix/assets'
|
|
|
471
573
|
|
|
472
574
|
let assetServer = createAssetServer({
|
|
473
575
|
basePath: '/assets',
|
|
474
|
-
fileMap: {
|
|
475
|
-
|
|
576
|
+
fileMap: {
|
|
577
|
+
'/app/*path': 'app/*path',
|
|
578
|
+
'/npm/*path': 'node_modules/*path',
|
|
579
|
+
},
|
|
580
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
581
|
+
allowPackages: ['remix'],
|
|
476
582
|
files: {
|
|
477
583
|
maxRequestTransforms: 5,
|
|
478
584
|
extensions: ['.svg', '.png', '.jpg', '.jpeg', '.woff2'],
|
|
@@ -518,8 +624,12 @@ import { createAssetServer } from 'remix/assets'
|
|
|
518
624
|
|
|
519
625
|
let assetServer = createAssetServer({
|
|
520
626
|
basePath: '/assets',
|
|
521
|
-
fileMap: {
|
|
522
|
-
|
|
627
|
+
fileMap: {
|
|
628
|
+
'/app/*path': 'app/*path',
|
|
629
|
+
'/npm/*path': 'node_modules/*path',
|
|
630
|
+
},
|
|
631
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
632
|
+
allowPackages: ['remix'],
|
|
523
633
|
onError(error) {
|
|
524
634
|
console.error('Failed to build client assets', error)
|
|
525
635
|
return new Response('Client asset build failed', { status: 500 })
|
|
@@ -529,9 +639,165 @@ let assetServer = createAssetServer({
|
|
|
529
639
|
|
|
530
640
|
If `onError` returns nothing, the asset server responds with the default `500 Internal Server Error` response.
|
|
531
641
|
|
|
642
|
+
## Hot Module Reloading
|
|
643
|
+
|
|
644
|
+
Use `hmr` with `watch` to enable the `import.meta.hot` API for browser modules. The `hmr` option is designed for integrating assets with a server-level HMR runtime such as [`node-hmr`](https://github.com/remix-run/remix/tree/main/packages/node-hmr) so server and browser updates can be coordinated.
|
|
645
|
+
|
|
646
|
+
The `hmr` option accepts an async function that creates a `BrowserHmrChannel`, such as the `createBrowserHmrChannel` function from [`node-hmr`](https://github.com/remix-run/remix/tree/main/packages/node-hmr):
|
|
647
|
+
|
|
648
|
+
```ts
|
|
649
|
+
import { createAssetServer } from 'remix/assets'
|
|
650
|
+
|
|
651
|
+
let isDevelopment = process.env.NODE_ENV === 'development'
|
|
652
|
+
let assetServer = createAssetServer({
|
|
653
|
+
basePath: '/assets',
|
|
654
|
+
fileMap: { '/app/*path': 'app/*path' },
|
|
655
|
+
allowFiles: ['app/routes.ts', 'app/**/public/**'],
|
|
656
|
+
denyFiles: ['app/**/*.test.*'],
|
|
657
|
+
hmr: isDevelopment
|
|
658
|
+
? async () => (await import('remix/node-hmr/runtime')).createBrowserHmrChannel()
|
|
659
|
+
: undefined,
|
|
660
|
+
watch: isDevelopment,
|
|
661
|
+
})
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### `import.meta.hot`
|
|
665
|
+
|
|
666
|
+
The `import.meta.hot` API provided by `assets` is a small runtime contract for modules that can handle updates without reloading the page. It is primarily intended for browser modules compiled by `assets`, but it can also be used directly.
|
|
667
|
+
|
|
668
|
+
To type `import.meta.hot`, add the HMR types to your TypeScript config:
|
|
669
|
+
|
|
670
|
+
```json
|
|
671
|
+
{
|
|
672
|
+
"compilerOptions": {
|
|
673
|
+
"types": ["remix/assets/types/hmr"]
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
HMR accept calls are statically analyzed. Write them directly as `import.meta.hot.accept(...)`. Dependency accepts must use string literals or arrays of string literals; do not alias `import.meta.hot` or pass dynamically constructed dependency lists.
|
|
679
|
+
|
|
680
|
+
```ts
|
|
681
|
+
if (import.meta.hot) {
|
|
682
|
+
import.meta.hot.accept()
|
|
683
|
+
}
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
### Accepting updates
|
|
687
|
+
|
|
688
|
+
Calling `accept()` makes the current module an HMR boundary. When the module changes, `assets` evaluates the updated module and calls your callback with its exports.
|
|
689
|
+
|
|
690
|
+
```ts
|
|
691
|
+
export let value = 1
|
|
692
|
+
|
|
693
|
+
if (import.meta.hot) {
|
|
694
|
+
import.meta.hot.accept((module) => {
|
|
695
|
+
if (typeof module.value !== 'number') {
|
|
696
|
+
import.meta.hot?.invalidate('Updated module no longer exports value')
|
|
697
|
+
return
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
value = module.value
|
|
701
|
+
})
|
|
702
|
+
}
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
You can also accept updates from direct dependencies.
|
|
706
|
+
|
|
707
|
+
```ts
|
|
708
|
+
import { value } from './value.ts'
|
|
709
|
+
|
|
710
|
+
let currentValue = value
|
|
711
|
+
|
|
712
|
+
export function readValue() {
|
|
713
|
+
return currentValue
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
if (import.meta.hot) {
|
|
717
|
+
import.meta.hot.accept('./value.ts', (module) => {
|
|
718
|
+
if (typeof module.value !== 'number') {
|
|
719
|
+
import.meta.hot?.invalidate('Updated dependency no longer exports value')
|
|
720
|
+
return
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
currentValue = module.value
|
|
724
|
+
})
|
|
725
|
+
}
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
Multiple dependencies can be accepted at once. The callback receives an array where only the changed dependency is defined.
|
|
729
|
+
|
|
730
|
+
```ts
|
|
731
|
+
if (import.meta.hot) {
|
|
732
|
+
import.meta.hot.accept(['./one.ts', './two.ts'], ([oneModule, twoModule]) => {
|
|
733
|
+
// oneModule is defined when ./one.ts changed.
|
|
734
|
+
// twoModule is defined when ./two.ts changed.
|
|
735
|
+
})
|
|
736
|
+
}
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
### Cleaning up
|
|
740
|
+
|
|
741
|
+
Register cleanup that should run before the module is replaced or disposed.
|
|
742
|
+
|
|
743
|
+
```ts
|
|
744
|
+
let interval = setInterval(refreshCache, 30_000)
|
|
745
|
+
|
|
746
|
+
if (import.meta.hot) {
|
|
747
|
+
import.meta.hot.dispose(() => {
|
|
748
|
+
clearInterval(interval)
|
|
749
|
+
})
|
|
750
|
+
}
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
The `data` object is preserved across updates for the same module. Use it for small pieces of state.
|
|
754
|
+
|
|
755
|
+
```ts
|
|
756
|
+
let count = Number(import.meta.hot?.data.count ?? 0)
|
|
757
|
+
|
|
758
|
+
export function increment() {
|
|
759
|
+
count++
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
if (import.meta.hot) {
|
|
763
|
+
import.meta.hot.dispose((data) => {
|
|
764
|
+
data.count = count
|
|
765
|
+
})
|
|
766
|
+
}
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
### Invalidating updates
|
|
770
|
+
|
|
771
|
+
Call `invalidate()` inside an accept callback when the update cannot be applied safely. If no other boundary accepts the update, the browser reloads.
|
|
772
|
+
|
|
773
|
+
```ts
|
|
774
|
+
if (import.meta.hot) {
|
|
775
|
+
import.meta.hot.accept((module) => {
|
|
776
|
+
if (typeof module.value !== 'number') {
|
|
777
|
+
import.meta.hot?.invalidate('Updated module no longer exports value')
|
|
778
|
+
return
|
|
779
|
+
}
|
|
780
|
+
})
|
|
781
|
+
}
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
### Server update events
|
|
785
|
+
|
|
786
|
+
When the browser HMR channel comes from `remix/node-hmr/runtime`, server updates are sent to browser modules as `server:update` events.
|
|
787
|
+
|
|
788
|
+
```ts
|
|
789
|
+
if (import.meta.hot) {
|
|
790
|
+
import.meta.hot.on('server:update', () => {
|
|
791
|
+
window.location.reload()
|
|
792
|
+
})
|
|
793
|
+
}
|
|
794
|
+
```
|
|
795
|
+
|
|
532
796
|
## Related Packages
|
|
533
797
|
|
|
534
798
|
- [`fetch-router`](https://github.com/remix-run/remix/tree/main/packages/fetch-router) - A Fetch-based router that pairs naturally with `assets`
|
|
799
|
+
- [`node-hmr`](https://github.com/remix-run/remix/tree/main/packages/node-hmr) - Provides the server-side `import.meta.hot` runtime and browser HMR channel used by `hmr`
|
|
800
|
+
- [`ui-hmr`](https://github.com/remix-run/remix/tree/main/packages/ui-hmr) - Provides a Remix UI component HMR loader for `scripts.loaders`
|
|
535
801
|
- [`route-pattern`](https://github.com/remix-run/remix/tree/main/packages/route-pattern) - Route-pattern syntax for URL and route file matching
|
|
536
802
|
|
|
537
803
|
## License
|
package/dist/assets.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createAssetServer } from './lib/asset-server.ts';
|
|
2
2
|
export { defineFileTransform } from './lib/files/config.ts';
|
|
3
|
-
export type { AssetServer, AssetServerOptions } from './lib/asset-server.ts';
|
|
3
|
+
export type { AssetServer, AssetServerOptions, BrowserHmrChannel } from './lib/asset-server.ts';
|
|
4
|
+
export type { ModuleLoader } from './lib/loaders.ts';
|
|
4
5
|
//# sourceMappingURL=assets.d.ts.map
|
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,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAC3D,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,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AAC/F,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA"}
|
package/dist/assets.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { createAssetServer } from
|
|
2
|
-
export { defineFileTransform } from
|
|
1
|
+
export { createAssetServer } from './lib/asset-server.js';
|
|
2
|
+
export { defineFileTransform } from './lib/files/config.js';
|
package/dist/lib/access.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
type AccessPolicy = {
|
|
2
|
+
getPackageWatchDirectories(): readonly string[];
|
|
3
|
+
handleFileEvent(filePath: string): void;
|
|
2
4
|
isAllowed(filePath: string): boolean;
|
|
3
5
|
};
|
|
4
6
|
export declare function createAccessPolicy(options: {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
allowFiles: readonly string[];
|
|
8
|
+
allowPackages?: readonly string[];
|
|
9
|
+
denyFiles?: readonly string[];
|
|
10
|
+
packageSearchRoots?: readonly string[];
|
|
7
11
|
rootDir: string;
|
|
8
12
|
}): AccessPolicy;
|
|
9
13
|
export {};
|
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":"AAMA,KAAK,YAAY,GAAG;IAClB,0BAA0B,IAAI,SAAS,MAAM,EAAE,CAAA;IAC/C,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvC,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;CACrC,CAAA;AAcD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAC1C,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;IAC7B,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC7B,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACtC,OAAO,EAAE,MAAM,CAAA;CAChB,GAAG,YAAY,CAwDf"}
|