Skip to content

Commit a2eccfd

Browse files
committed
docs: introduce Remote Assets guide
Document remote assets as a feature: pointing cli.distDir at a published npm package (via a RemoteAssets declaration) so a devframe's SPA is fetched on demand and cached locally instead of bundled into the node package. Covers a basic example, the resolution order, options, offline use, custom providers, and publishing the assets package.
1 parent 01acd22 commit a2eccfd

2 files changed

Lines changed: 102 additions & 0 deletions

File tree

docs/.vitepress/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ function guideGroups(prefix: string) {
2323
items: [
2424
{ text: 'Introduction', link: `${prefix}/guide/` },
2525
{ text: 'Devframe Definition', link: `${prefix}/guide/devframe-definition` },
26+
{ text: 'Remote Assets', link: `${prefix}/guide/remote-assets` },
2627
{ text: 'Scoped Context', link: `${prefix}/guide/scoped-context` },
2728
{ text: 'Cross-Plugin Services', link: `${prefix}/guide/services` },
2829
{ text: 'RPC', link: `${prefix}/guide/rpc` },

docs/guide/remote-assets.md

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
outline: deep
3+
---
4+
5+
# Remote Assets
6+
7+
Anywhere a devframe serves a built SPA — `cli.distDir` — you can point at a **published npm package** instead of a local directory. The assets are then fetched on demand and cached locally, so the node package doesn't have to bundle its UI.
8+
9+
This keeps the installed footprint small: a plugin's prebuilt SPA is usually the bulk of its tarball, and with remote assets it's served on first use rather than shipped to every consumer up front.
10+
11+
## Basic example
12+
13+
Give `cli.distDir` a `RemoteAssets` object naming the package and exact version that holds the built UI:
14+
15+
```ts
16+
import type { RemoteAssets } from 'devframe'
17+
import { defineDevframe } from 'devframe'
18+
import pkg from '../package.json' with { type: 'json' }
19+
20+
const distDir: RemoteAssets = {
21+
package: '@acme/my-tool-assets',
22+
version: pkg.version,
23+
resolveFrom: import.meta.url,
24+
}
25+
26+
export default defineDevframe({
27+
id: 'my-tool',
28+
version: pkg.version,
29+
packageName: pkg.name,
30+
cli: { distDir },
31+
setup(ctx) {
32+
//
33+
},
34+
})
35+
```
36+
37+
That's it — the UI mounts as usual. The first request for each file is streamed from a CDN and written to a local cache; subsequent requests are served from disk.
38+
39+
## How assets resolve
40+
41+
For each request the source resolves in order:
42+
43+
1. **Locally installed package** — resolved from `resolveFrom` (`import.meta.url`). If `@acme/my-tool-assets` is installed next to your tool, it's served directly with no network. This is the offline path.
44+
2. **On-disk cache** — files already fetched, under the project's storage directory.
45+
3. **CDN back-proxy**[jsDelivr](https://www.jsdelivr.com/) by default, mirroring npm. Each file streams to the browser and is cached on the way past.
46+
47+
Exact-version URLs are immutable, so a cached file never goes stale.
48+
49+
## Options
50+
51+
| Field | Purpose |
52+
|-------|---------|
53+
| `package` | npm package holding the built assets. |
54+
| `version` | Exact version to serve — usually your tool's own `pkg.version`. |
55+
| `resolveFrom` | `import.meta.url` of the declaring module; enables the zero-network path from a locally installed copy. Omit to skip straight to cache + CDN. |
56+
| `path` | Subpath inside the package the assets live under. Defaults to `dist`. |
57+
| `provider` | `'jsdelivr'` (default), `'unpkg'`, or a custom provider for an internal mirror. |
58+
| `offline` | `true` serves only from a local install or the cache — never the network. |
59+
60+
`package` and `version` are interpolated into the CDN URL and cache path, so they're validated: an invalid npm name or non-exact version throws [`DF0065`](../errors/DF0065).
61+
62+
## Offline and air-gapped use
63+
64+
Remote assets are a convenience, not a hard network dependency. To run with no network, install the assets package explicitly — resolution step 1 then serves it locally:
65+
66+
```sh
67+
npm install @acme/my-tool-assets
68+
```
69+
70+
Set `offline: true` to guarantee the CDN is never contacted, or point `provider` at an internal npm mirror.
71+
72+
## Custom provider
73+
74+
A custom provider supplies the file URL, and optionally a file listing (used for correct 404s, SPA fallback, and static builds):
75+
76+
```ts
77+
const distDir: RemoteAssets = {
78+
package: '@acme/my-tool-assets',
79+
version: pkg.version,
80+
resolveFrom: import.meta.url,
81+
provider: {
82+
fileUrl: (name, version, file) =>
83+
`https://npm.internal.acme.com/${name}@${version}/${file}`,
84+
},
85+
}
86+
```
87+
88+
## Publishing the assets
89+
90+
The assets package is an ordinary npm package that ships the built UI under `path` (default `dist`) and exposes its `package.json` so `resolveFrom` can locate it:
91+
92+
```json
93+
{
94+
"name": "@acme/my-tool-assets",
95+
"version": "1.0.0",
96+
"exports": { "./package.json": "./package.json" },
97+
"files": ["dist"]
98+
}
99+
```
100+
101+
Keep its version in lockstep with the tool that declares it, so `version: pkg.version` always points at matching UI.

0 commit comments

Comments
 (0)