@rollup-extras/plugin-serve 2.3.1 → 2.4.1
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 +29 -6
- package/index.d.ts +11 -0
- package/index.js +19 -4
- package/package.json +6 -2
- package/watch-exports.d.ts +7 -0
- package/watch.js +93 -0
package/README.md
CHANGED
|
@@ -66,15 +66,15 @@ Option to use `writeBundle` hook instead of `generateBundle`.
|
|
|
66
66
|
|
|
67
67
|
Optional, `boolean`, default: `false`.
|
|
68
68
|
|
|
69
|
-
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`.
|
|
70
70
|
|
|
71
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`.
|
|
72
72
|
|
|
73
|
-
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.
|
|
74
74
|
|
|
75
75
|
**Limitations:**
|
|
76
76
|
- Only covers Rollup's own output. Other plugins (e.g., plugin-copy, plugin-html) may still perform disk I/O independently.
|
|
77
|
-
- 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.
|
|
78
78
|
|
|
79
79
|
### dirs
|
|
80
80
|
|
|
@@ -138,6 +138,8 @@ Set to `false` (or leave unset) to disable both the endpoint and the script inje
|
|
|
138
138
|
|
|
139
139
|
## In-Memory Serving
|
|
140
140
|
|
|
141
|
+
Keep the Rollup configuration in `rollup.config.js`:
|
|
142
|
+
|
|
141
143
|
```javascript
|
|
142
144
|
import serve from "@rollup-extras/plugin-serve";
|
|
143
145
|
|
|
@@ -147,13 +149,34 @@ export default {
|
|
|
147
149
|
format: "es",
|
|
148
150
|
dir: "dest",
|
|
149
151
|
},
|
|
150
|
-
watch: {
|
|
151
|
-
skipWrite: true, // recommended: avoid writing files to disk
|
|
152
|
-
},
|
|
153
152
|
plugins: [serve({ inMemory: true })],
|
|
154
153
|
};
|
|
155
154
|
```
|
|
156
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
|
+
|
|
157
180
|
Hybrid mode — bundle from memory, static assets from disk:
|
|
158
181
|
|
|
159
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
|
@@ -36,19 +36,21 @@ export default function (options = {}) {
|
|
|
36
36
|
normalizedOptions;
|
|
37
37
|
const useWriteBundle = inMemory ? false : normalizedOptions.useWriteBundle;
|
|
38
38
|
const instance = inMemory
|
|
39
|
-
? multiConfigPluginBase(useWriteBundle, pluginName, serve, captureOutput)
|
|
39
|
+
? multiConfigPluginBase(useWriteBundle, pluginName, serve, captureOutput, resetMemory)
|
|
40
40
|
: multiConfigPluginBase(useWriteBundle, pluginName, serve);
|
|
41
41
|
|
|
42
42
|
const memoryFiles = new Map();
|
|
43
43
|
|
|
44
44
|
let currentInputOptions;
|
|
45
45
|
|
|
46
|
+
let server;
|
|
47
|
+
|
|
46
48
|
let { dirs } = normalizedOptions,
|
|
47
49
|
collectDirs = false,
|
|
48
50
|
started = false,
|
|
49
51
|
watchMode = true;
|
|
50
52
|
|
|
51
|
-
const pluginInstance = { ...instance, outputOptions };
|
|
53
|
+
const pluginInstance = { ...instance, outputOptions, closeWatcher };
|
|
52
54
|
|
|
53
55
|
if (!dirs) {
|
|
54
56
|
dirs = [];
|
|
@@ -87,6 +89,11 @@ export default function (options = {}) {
|
|
|
87
89
|
}
|
|
88
90
|
}
|
|
89
91
|
|
|
92
|
+
function resetMemory() {
|
|
93
|
+
memoryFiles.clear();
|
|
94
|
+
currentInputOptions = undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
90
97
|
function captureOutput(
|
|
91
98
|
_options,
|
|
92
99
|
bundle,
|
|
@@ -138,13 +145,13 @@ export default function (options = {}) {
|
|
|
138
145
|
app.use('*', serveStatic({ root: dir, ...staticOptions }));
|
|
139
146
|
}
|
|
140
147
|
|
|
141
|
-
|
|
148
|
+
server = createAdaptorServer({
|
|
142
149
|
fetch: app.fetch,
|
|
143
150
|
...(https ? { createServer: createHttpsServer, serverOptions: https } : {}),
|
|
144
151
|
});
|
|
145
152
|
globalServer = server;
|
|
146
153
|
|
|
147
|
-
const listenCb = () => internalOnListen(server);
|
|
154
|
+
const listenCb = () => internalOnListen( (server));
|
|
148
155
|
|
|
149
156
|
if (host) {
|
|
150
157
|
server.listen(port, host, listenCb);
|
|
@@ -161,6 +168,14 @@ export default function (options = {}) {
|
|
|
161
168
|
});
|
|
162
169
|
}
|
|
163
170
|
|
|
171
|
+
async function closeWatcher() {
|
|
172
|
+
if (!server || globalServer !== server) return;
|
|
173
|
+
globalLiveReload?.close();
|
|
174
|
+
globalLiveReload = undefined;
|
|
175
|
+
globalServer = undefined;
|
|
176
|
+
await new Promise(resolve => (server).close(resolve));
|
|
177
|
+
}
|
|
178
|
+
|
|
164
179
|
function internalOnListen(server) {
|
|
165
180
|
if (!onListen?.( (server))) {
|
|
166
181
|
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.1",
|
|
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",
|
|
@@ -35,7 +39,7 @@
|
|
|
35
39
|
"@niceties/logger": "^2.1.0",
|
|
36
40
|
"@hono/node-server": "^2.0.0",
|
|
37
41
|
"hono": "^4.12.8",
|
|
38
|
-
"@rollup-extras/utils": "^2.0.
|
|
42
|
+
"@rollup-extras/utils": "^2.0.2"
|
|
39
43
|
},
|
|
40
44
|
"peerDependencies": {
|
|
41
45
|
"rollup": "^4.0.0"
|
|
@@ -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
|
+
}
|