@rollup-extras/plugin-serve 2.3.0 → 2.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 +31 -6
- package/index.d.ts +11 -0
- package/index.js +17 -4
- package/package.json +5 -1
- package/watch-exports.d.ts +7 -0
- package/watch.js +93 -0
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
1
3
|
# Plugin Serve
|
|
2
4
|
|
|
3
5
|
Rollup plugin for a dev server.
|
|
@@ -64,15 +66,15 @@ Option to use `writeBundle` hook instead of `generateBundle`.
|
|
|
64
66
|
|
|
65
67
|
Optional, `boolean`, default: `false`.
|
|
66
68
|
|
|
67
|
-
When enabled, serves all emitted assets directly from memory instead of from disk. This
|
|
69
|
+
When enabled, serves all emitted assets directly from memory instead of from disk. This controls how files are served; Rollup still writes output when run with `rollup -cw`.
|
|
68
70
|
|
|
69
71
|
When `inMemory` is `true`, the plugin automatically uses the `generateBundle` hook (regardless of `useWriteBundle`) to capture all chunks and assets in memory. A custom Hono middleware serves these files before falling through to any disk-based static serving configured via `dirs`.
|
|
70
72
|
|
|
71
|
-
For
|
|
73
|
+
For disk-free Rollup output, use the package's [`/watch` helper](#in-memory-serving). Setting `watch: { skipWrite: true }` with Rollup's ordinary watcher skips output generation as well as writing, so the plugin receives no files to serve.
|
|
72
74
|
|
|
73
75
|
**Limitations:**
|
|
74
76
|
- Only covers Rollup's own output. Other plugins (e.g., plugin-copy, plugin-html) may still perform disk I/O independently.
|
|
75
|
-
- Plugins that only use the `writeBundle` hook won't participate
|
|
77
|
+
- Plugins that only use the `writeBundle` hook won't participate with the `/watch` helper. Configure plugins such as plugin-html to use `generateBundle` when their output should be served from memory.
|
|
76
78
|
|
|
77
79
|
### dirs
|
|
78
80
|
|
|
@@ -136,6 +138,8 @@ Set to `false` (or leave unset) to disable both the endpoint and the script inje
|
|
|
136
138
|
|
|
137
139
|
## In-Memory Serving
|
|
138
140
|
|
|
141
|
+
Keep the Rollup configuration in `rollup.config.js`:
|
|
142
|
+
|
|
139
143
|
```javascript
|
|
140
144
|
import serve from "@rollup-extras/plugin-serve";
|
|
141
145
|
|
|
@@ -145,13 +149,34 @@ export default {
|
|
|
145
149
|
format: "es",
|
|
146
150
|
dir: "dest",
|
|
147
151
|
},
|
|
148
|
-
watch: {
|
|
149
|
-
skipWrite: true, // recommended: avoid writing files to disk
|
|
150
|
-
},
|
|
151
152
|
plugins: [serve({ inMemory: true })],
|
|
152
153
|
};
|
|
153
154
|
```
|
|
154
155
|
|
|
156
|
+
Create `dev.js` to start a watcher that generates output without writing it to disk:
|
|
157
|
+
|
|
158
|
+
```javascript
|
|
159
|
+
import watch from "@rollup-extras/plugin-serve/watch";
|
|
160
|
+
import config from "./rollup.config.js";
|
|
161
|
+
|
|
162
|
+
const watcher = watch(config);
|
|
163
|
+
|
|
164
|
+
watcher.on("event", event => {
|
|
165
|
+
if (event.code === "ERROR") console.error(event.error);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
process.once("SIGINT", () => watcher.close());
|
|
169
|
+
process.once("SIGTERM", () => watcher.close());
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Run `node dev.js`. The helper accepts one Rollup configuration or an array, generates all configured outputs on each rebuild, and forces `watch.skipWrite` without mutating the supplied configuration. Configurations with `watch: false` are ignored.
|
|
173
|
+
|
|
174
|
+
It returns a Rollup watcher. Use its `END` event to observe completion of generation and build cleanup. The helper owns the build results, so consumers should not call `event.result.generate()` or `event.result.close()`. Output errors are reported through the usual `ERROR` event, and later file changes can trigger another build. `await watcher.close()` waits for pending generation and shuts down the server.
|
|
175
|
+
|
|
176
|
+
Register watched dependencies in build hooks, such as `buildStart` or `transform`. With `skipWrite`, Rollup collects watch files before `BUNDLE_END`; files first registered in output hooks during the helper's generation are not picked up by the watcher.
|
|
177
|
+
|
|
178
|
+
Running the same configuration with `rollup -cw` also serves from memory, while continuing to write the usual output files.
|
|
179
|
+
|
|
155
180
|
Hybrid mode — bundle from memory, static assets from disk:
|
|
156
181
|
|
|
157
182
|
```javascript
|
package/index.d.ts
CHANGED
|
@@ -41,3 +41,14 @@ declare module '@rollup-extras/plugin-serve' {
|
|
|
41
41
|
|
|
42
42
|
export {};
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
declare module '@rollup-extras/plugin-serve/watch' {
|
|
46
|
+
import type { RollupWatcher, RollupWatchOptions } from 'rollup';
|
|
47
|
+
/**
|
|
48
|
+
* Watch and generate Rollup output in memory, closing each build result automatically.
|
|
49
|
+
* Configure `serve({ inMemory: true })` in the supplied Rollup configuration.
|
|
50
|
+
*/
|
|
51
|
+
export default function watch(config: RollupWatchOptions | RollupWatchOptions[]): RollupWatcher;
|
|
52
|
+
|
|
53
|
+
export {};
|
|
54
|
+
}
|
package/index.js
CHANGED
|
@@ -41,12 +41,16 @@ export default function (options = {}) {
|
|
|
41
41
|
|
|
42
42
|
const memoryFiles = new Map();
|
|
43
43
|
|
|
44
|
+
let currentInputOptions;
|
|
45
|
+
|
|
46
|
+
let server;
|
|
47
|
+
|
|
44
48
|
let { dirs } = normalizedOptions,
|
|
45
49
|
collectDirs = false,
|
|
46
50
|
started = false,
|
|
47
51
|
watchMode = true;
|
|
48
52
|
|
|
49
|
-
const pluginInstance = { ...instance, outputOptions };
|
|
53
|
+
const pluginInstance = { ...instance, outputOptions, closeWatcher };
|
|
50
54
|
|
|
51
55
|
if (!dirs) {
|
|
52
56
|
dirs = [];
|
|
@@ -61,8 +65,9 @@ export default function (options = {}) {
|
|
|
61
65
|
function renderStart( outputOptions, inputOptions) {
|
|
62
66
|
|
|
63
67
|
( (instance).renderStart).call(this, outputOptions, inputOptions);
|
|
64
|
-
if (inMemory) {
|
|
68
|
+
if (inMemory && currentInputOptions !== inputOptions) {
|
|
65
69
|
memoryFiles.clear();
|
|
70
|
+
currentInputOptions = inputOptions;
|
|
66
71
|
}
|
|
67
72
|
if (collectDirs) {
|
|
68
73
|
if (outputOptions.dir) {
|
|
@@ -135,13 +140,13 @@ export default function (options = {}) {
|
|
|
135
140
|
app.use('*', serveStatic({ root: dir, ...staticOptions }));
|
|
136
141
|
}
|
|
137
142
|
|
|
138
|
-
|
|
143
|
+
server = createAdaptorServer({
|
|
139
144
|
fetch: app.fetch,
|
|
140
145
|
...(https ? { createServer: createHttpsServer, serverOptions: https } : {}),
|
|
141
146
|
});
|
|
142
147
|
globalServer = server;
|
|
143
148
|
|
|
144
|
-
const listenCb = () => internalOnListen(server);
|
|
149
|
+
const listenCb = () => internalOnListen( (server));
|
|
145
150
|
|
|
146
151
|
if (host) {
|
|
147
152
|
server.listen(port, host, listenCb);
|
|
@@ -158,6 +163,14 @@ export default function (options = {}) {
|
|
|
158
163
|
});
|
|
159
164
|
}
|
|
160
165
|
|
|
166
|
+
async function closeWatcher() {
|
|
167
|
+
if (!server || globalServer !== server) return;
|
|
168
|
+
globalLiveReload?.close();
|
|
169
|
+
globalLiveReload = undefined;
|
|
170
|
+
globalServer = undefined;
|
|
171
|
+
await new Promise(resolve => (server).close(resolve));
|
|
172
|
+
}
|
|
173
|
+
|
|
161
174
|
function internalOnListen(server) {
|
|
162
175
|
if (!onListen?.( (server))) {
|
|
163
176
|
logger.finish(`listening on ${linkFromAddress(server.address(), !!https)}`, LogLevel.info);
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
2
|
+
"version": "2.4.0",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"name": "@rollup-extras/plugin-serve",
|
|
5
5
|
"author": "Konstantin Shutkin",
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
"types": "./index.d.ts",
|
|
11
11
|
"default": "./index.js"
|
|
12
12
|
},
|
|
13
|
+
"./watch": {
|
|
14
|
+
"types": "./index.d.ts",
|
|
15
|
+
"default": "./watch.js"
|
|
16
|
+
},
|
|
13
17
|
"./package.json": "./package.json"
|
|
14
18
|
},
|
|
15
19
|
"main": "./index.js",
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { RollupWatcher, RollupWatchOptions } from 'rollup';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Watch and generate Rollup output in memory, closing each build result automatically.
|
|
5
|
+
* Configure `serve({ inMemory: true })` in the supplied Rollup configuration.
|
|
6
|
+
*/
|
|
7
|
+
export default function watch(config: RollupWatchOptions | RollupWatchOptions[]): RollupWatcher;
|
package/watch.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { watch as rollupWatch } from 'rollup';
|
|
2
|
+
|
|
3
|
+
export default function watch(config) {
|
|
4
|
+
|
|
5
|
+
let outputs = [];
|
|
6
|
+
let closing = false;
|
|
7
|
+
|
|
8
|
+
let finishBuild;
|
|
9
|
+
let pending = Promise.resolve();
|
|
10
|
+
|
|
11
|
+
let closed;
|
|
12
|
+
|
|
13
|
+
let ready;
|
|
14
|
+
const initialized = new Promise(resolve => {
|
|
15
|
+
ready = () => resolve(undefined);
|
|
16
|
+
});
|
|
17
|
+
const configs = Array.isArray(config) ? config : [config];
|
|
18
|
+
const watcher = rollupWatch(
|
|
19
|
+
configs.map(options => ({
|
|
20
|
+
...options,
|
|
21
|
+
watch: options.watch === false ? false : { ...options.watch, skipWrite: true },
|
|
22
|
+
plugins: [
|
|
23
|
+
options.plugins,
|
|
24
|
+
{
|
|
25
|
+
name: '@rollup-extras/plugin-serve/watch',
|
|
26
|
+
buildStart() {
|
|
27
|
+
outputs = Array.isArray(options.output) ? options.output : [options.output || {}];
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
}))
|
|
32
|
+
);
|
|
33
|
+
watcher.on('event', async event => {
|
|
34
|
+
if (event.code === 'BUNDLE_START') {
|
|
35
|
+
pending = new Promise(resolve => {
|
|
36
|
+
finishBuild = resolve;
|
|
37
|
+
});
|
|
38
|
+
ready?.();
|
|
39
|
+
} else if (event.code === 'BUNDLE_END') {
|
|
40
|
+
try {
|
|
41
|
+
await generate(event.result, outputs);
|
|
42
|
+
} finally {
|
|
43
|
+
finishBuild?.();
|
|
44
|
+
}
|
|
45
|
+
} else if (event.code === 'ERROR') {
|
|
46
|
+
try {
|
|
47
|
+
await event.result?.close();
|
|
48
|
+
} finally {
|
|
49
|
+
finishBuild?.();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
function close() {
|
|
55
|
+
closing = true;
|
|
56
|
+
closed ||= (async () => {
|
|
57
|
+
|
|
58
|
+
await initialized;
|
|
59
|
+
await pending;
|
|
60
|
+
await watcher.close();
|
|
61
|
+
})();
|
|
62
|
+
return closed;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const managed = new Proxy(watcher, {
|
|
66
|
+
get(target, key) {
|
|
67
|
+
if (key === 'close') return close;
|
|
68
|
+
const value = Reflect.get(target, key);
|
|
69
|
+
if (typeof value !== 'function') return value;
|
|
70
|
+
|
|
71
|
+
function call(...args) {
|
|
72
|
+
const result = value.apply(target, args);
|
|
73
|
+
return result === target ? managed : result;
|
|
74
|
+
}
|
|
75
|
+
return call;
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
return managed;
|
|
79
|
+
|
|
80
|
+
async function generate(result, outputOptions) {
|
|
81
|
+
try {
|
|
82
|
+
if (!closing) {
|
|
83
|
+
|
|
84
|
+
const generated = await Promise.allSettled(outputOptions.map(output => result.generate(output)));
|
|
85
|
+
for (const output of generated) {
|
|
86
|
+
if (output.status === 'rejected') throw output.reason;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} finally {
|
|
90
|
+
await result.close();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|