iterate-ui-vite 0.1.2 → 0.2.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/dist/index.d.ts +20 -2
- package/dist/index.js +84 -17
- package/package.json +9 -6
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
|
|
3
3
|
interface IteratePluginOptions {
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Port for the iterate daemon. If omitted, resolved in order:
|
|
6
|
+
* 1. ITERATE_DAEMON_PORT env var
|
|
7
|
+
* 2. An already-running daemon's port from .iterate/daemon.lock
|
|
8
|
+
* 3. `daemonPort` from .iterate/config.json
|
|
9
|
+
* 4. Auto-picked starting from the default (47100)
|
|
10
|
+
*/
|
|
5
11
|
daemonPort?: number;
|
|
12
|
+
/**
|
|
13
|
+
* Name of the app this plugin instance wraps. Must match a `name` in
|
|
14
|
+
* `.iterate/config.json`'s `apps[]` array. When set, the overlay forwards
|
|
15
|
+
* this name to the daemon's `/api/command` and `/api/iterations` endpoints
|
|
16
|
+
* when the user creates iterations via the overlay toolbar — so iterations
|
|
17
|
+
* spawn the right dev server for the app the user is currently viewing.
|
|
18
|
+
*
|
|
19
|
+
* Required in multi-app repos. Optional in single-app repos.
|
|
20
|
+
*/
|
|
21
|
+
appName?: string;
|
|
6
22
|
/** Disable the babel plugin that injects component names/source locations (default: false) */
|
|
7
23
|
disableBabelPlugin?: boolean;
|
|
8
24
|
}
|
|
@@ -26,5 +42,7 @@ interface IteratePluginOptions {
|
|
|
26
42
|
* 5. Cleans up daemon when dev server stops
|
|
27
43
|
*/
|
|
28
44
|
declare function iterate(options?: IteratePluginOptions): Plugin[];
|
|
45
|
+
/** Exported for tests. Resolves the daemon port for a vite plugin invocation. */
|
|
46
|
+
declare function resolveDaemonPort(repoRoot: string, startingFrom: number, override?: number): Promise<number>;
|
|
29
47
|
|
|
30
|
-
export { type IteratePluginOptions, iterate as default, iterate };
|
|
48
|
+
export { type IteratePluginOptions, iterate as default, iterate, resolveDaemonPort };
|
package/dist/index.js
CHANGED
|
@@ -2,10 +2,29 @@
|
|
|
2
2
|
import { spawn, execSync } from "child_process";
|
|
3
3
|
import { createRequire } from "module";
|
|
4
4
|
import { readFileSync } from "fs";
|
|
5
|
+
import { dirname, join } from "path";
|
|
5
6
|
import http from "http";
|
|
7
|
+
import {
|
|
8
|
+
findFreePort,
|
|
9
|
+
isPortInUse,
|
|
10
|
+
loadConfig,
|
|
11
|
+
readLockfile,
|
|
12
|
+
isDaemonAlive
|
|
13
|
+
} from "iterate-ui-core/node";
|
|
14
|
+
function resolvePackageEntry(packageName, _require) {
|
|
15
|
+
try {
|
|
16
|
+
return _require.resolve(packageName);
|
|
17
|
+
} catch {
|
|
18
|
+
const pkgJsonPath = _require.resolve(`${packageName}/package.json`);
|
|
19
|
+
const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
|
|
20
|
+
const main = pkg.exports?.["."]?.import ?? pkg.main ?? "index.js";
|
|
21
|
+
return join(dirname(pkgJsonPath), main);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
6
24
|
function iterate(options = {}) {
|
|
7
|
-
const daemonPort = options.daemonPort ?? 4e3;
|
|
8
25
|
let daemon = null;
|
|
26
|
+
let resolvedPort = null;
|
|
27
|
+
let resolvedBase = "";
|
|
9
28
|
let overlayJS = null;
|
|
10
29
|
const plugins = [];
|
|
11
30
|
if (!options.disableBabelPlugin) {
|
|
@@ -52,7 +71,7 @@ function iterate(options = {}) {
|
|
|
52
71
|
});
|
|
53
72
|
if (!result?.code) return null;
|
|
54
73
|
return { code: result.code, map: result.map };
|
|
55
|
-
} catch
|
|
74
|
+
} catch {
|
|
56
75
|
return null;
|
|
57
76
|
}
|
|
58
77
|
}
|
|
@@ -62,15 +81,31 @@ function iterate(options = {}) {
|
|
|
62
81
|
name: "iterate",
|
|
63
82
|
apply: "serve",
|
|
64
83
|
// Only active during dev
|
|
65
|
-
|
|
84
|
+
async configResolved(config) {
|
|
85
|
+
resolvedBase = (config.base ?? "/").replace(/\/+$/, "");
|
|
86
|
+
const repoRoot = getGitRoot() ?? config.root;
|
|
87
|
+
const fileConfig = (() => {
|
|
88
|
+
try {
|
|
89
|
+
return loadConfig(repoRoot);
|
|
90
|
+
} catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
})();
|
|
94
|
+
const envPort = process.env.ITERATE_DAEMON_PORT ? parseInt(process.env.ITERATE_DAEMON_PORT, 10) : void 0;
|
|
95
|
+
const startingPort = options.daemonPort ?? envPort ?? fileConfig?.daemonPort ?? 47100;
|
|
96
|
+
resolvedPort = await resolveDaemonPort(repoRoot, startingPort, options.daemonPort ?? envPort);
|
|
97
|
+
},
|
|
98
|
+
async configureServer(server) {
|
|
66
99
|
const repoRoot = getGitRoot() ?? server.config.root;
|
|
67
|
-
|
|
68
|
-
|
|
100
|
+
const port = resolvedPort ?? 47100;
|
|
101
|
+
daemon = await startDaemonIfNeeded(port, repoRoot);
|
|
102
|
+
const overlayPath = `${resolvedBase}/__iterate__/overlay.js`;
|
|
103
|
+
server.middlewares.use(overlayPath, (_req, res) => {
|
|
69
104
|
if (!overlayJS) {
|
|
70
105
|
try {
|
|
71
106
|
const require2 = createRequire(import.meta.url);
|
|
72
|
-
const
|
|
73
|
-
overlayJS = readFileSync(
|
|
107
|
+
const path = require2.resolve("iterate-ui-overlay/standalone");
|
|
108
|
+
overlayJS = readFileSync(path, "utf-8");
|
|
74
109
|
} catch {
|
|
75
110
|
res.statusCode = 404;
|
|
76
111
|
res.end("Overlay bundle not found");
|
|
@@ -81,10 +116,15 @@ function iterate(options = {}) {
|
|
|
81
116
|
res.setHeader("Cache-Control", "no-cache");
|
|
82
117
|
res.end(overlayJS);
|
|
83
118
|
});
|
|
119
|
+
const basePrefix = resolvedBase ? resolvedBase : "";
|
|
84
120
|
server.middlewares.use((req, res, next) => {
|
|
85
121
|
const url = req.url ?? "";
|
|
86
|
-
|
|
87
|
-
|
|
122
|
+
const matches = (prefix) => url.startsWith(prefix) || !!basePrefix && url.startsWith(`${basePrefix}${prefix}`);
|
|
123
|
+
if (matches("/api/") || matches("/__iterate__/")) {
|
|
124
|
+
if (basePrefix && url.startsWith(basePrefix)) {
|
|
125
|
+
req.url = url.slice(basePrefix.length) || "/";
|
|
126
|
+
}
|
|
127
|
+
proxyRequest(req, res, port);
|
|
88
128
|
return;
|
|
89
129
|
}
|
|
90
130
|
next();
|
|
@@ -94,12 +134,12 @@ function iterate(options = {}) {
|
|
|
94
134
|
const proxy = http.request(
|
|
95
135
|
{
|
|
96
136
|
hostname: "127.0.0.1",
|
|
97
|
-
port
|
|
137
|
+
port,
|
|
98
138
|
path: "/ws",
|
|
99
139
|
method: "GET",
|
|
100
140
|
headers: {
|
|
101
141
|
...req.headers,
|
|
102
|
-
host: `127.0.0.1:${
|
|
142
|
+
host: `127.0.0.1:${port}`
|
|
103
143
|
}
|
|
104
144
|
},
|
|
105
145
|
() => {
|
|
@@ -127,25 +167,37 @@ Sec-WebSocket-Accept: ${_proxyRes.headers["sec-websocket-accept"]}\r
|
|
|
127
167
|
}
|
|
128
168
|
});
|
|
129
169
|
server.httpServer?.on("close", () => {
|
|
130
|
-
|
|
131
|
-
|
|
170
|
+
if (daemon) {
|
|
171
|
+
stopDaemon(daemon, port);
|
|
172
|
+
daemon = null;
|
|
173
|
+
}
|
|
132
174
|
});
|
|
133
175
|
},
|
|
134
176
|
// Inject the overlay script into HTML
|
|
135
177
|
transformIndexHtml(html) {
|
|
136
178
|
const iterationName = JSON.stringify(process.env.ITERATE_ITERATION_NAME ?? "__original__");
|
|
179
|
+
const port = resolvedPort ?? 47100;
|
|
180
|
+
const overlaySrc = `${resolvedBase}/__iterate__/overlay.js`;
|
|
181
|
+
const appNameKey = options.appName ? `, appName: ${JSON.stringify(options.appName)}` : "";
|
|
137
182
|
return html.replace(
|
|
138
183
|
"</body>",
|
|
139
184
|
`<script>
|
|
140
|
-
window.__iterate_shell__ = { activeTool: 'select', activeIteration: ${iterationName}, daemonPort: ${
|
|
185
|
+
window.__iterate_shell__ = { activeTool: 'select', activeIteration: ${iterationName}, daemonPort: ${port}, basePath: ${JSON.stringify(resolvedBase)}${appNameKey} };
|
|
141
186
|
</script>
|
|
142
|
-
<script src
|
|
187
|
+
<script src=${JSON.stringify(overlaySrc)} defer></script>
|
|
143
188
|
</body>`
|
|
144
189
|
);
|
|
145
190
|
}
|
|
146
191
|
});
|
|
147
192
|
return plugins;
|
|
148
193
|
}
|
|
194
|
+
async function resolveDaemonPort(repoRoot, startingFrom, override) {
|
|
195
|
+
if (override && Number.isFinite(override)) return override;
|
|
196
|
+
const lock = readLockfile(repoRoot);
|
|
197
|
+
if (lock && isDaemonAlive(lock)) return lock.port;
|
|
198
|
+
if (await isPortInUse(startingFrom)) return startingFrom;
|
|
199
|
+
return await findFreePort(startingFrom);
|
|
200
|
+
}
|
|
149
201
|
function getGitRoot() {
|
|
150
202
|
try {
|
|
151
203
|
return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
@@ -153,10 +205,24 @@ function getGitRoot() {
|
|
|
153
205
|
return null;
|
|
154
206
|
}
|
|
155
207
|
}
|
|
208
|
+
async function startDaemonIfNeeded(port, cwd) {
|
|
209
|
+
if (await isPortInUse(port)) {
|
|
210
|
+
console.log(`[iterate] daemon already running on port ${port}`);
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
return startDaemon(port, cwd);
|
|
214
|
+
}
|
|
156
215
|
function startDaemon(port, cwd) {
|
|
216
|
+
const _req = createRequire(import.meta.url);
|
|
217
|
+
const daemonEntryPath = resolvePackageEntry("iterate-ui-daemon", _req);
|
|
218
|
+
const daemonPath = `file://${daemonEntryPath}`;
|
|
157
219
|
const child = spawn(
|
|
158
220
|
process.execPath,
|
|
159
|
-
[
|
|
221
|
+
[
|
|
222
|
+
"--input-type=module",
|
|
223
|
+
"-e",
|
|
224
|
+
`import { startDaemon } from ${JSON.stringify(daemonPath)}; startDaemon({ port: ${port}, cwd: ${JSON.stringify(cwd)} });`
|
|
225
|
+
],
|
|
160
226
|
{
|
|
161
227
|
cwd,
|
|
162
228
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -218,5 +284,6 @@ function proxyRequest(req, res, port) {
|
|
|
218
284
|
var index_default = iterate;
|
|
219
285
|
export {
|
|
220
286
|
index_default as default,
|
|
221
|
-
iterate
|
|
287
|
+
iterate,
|
|
288
|
+
resolveDaemonPort
|
|
222
289
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iterate-ui-vite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "iterate Vite plugin — auto-starts daemon and injects overlay",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -30,9 +30,10 @@
|
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"iterate-ui-
|
|
34
|
-
"iterate-ui-
|
|
35
|
-
"iterate-ui-
|
|
33
|
+
"iterate-ui-core": "0.2.0",
|
|
34
|
+
"iterate-ui-daemon": "0.2.0",
|
|
35
|
+
"iterate-ui-babel-plugin": "0.1.2",
|
|
36
|
+
"iterate-ui-overlay": "0.2.0"
|
|
36
37
|
},
|
|
37
38
|
"peerDependencies": {
|
|
38
39
|
"vite": "^5.0.0 || ^6.0.0"
|
|
@@ -43,11 +44,13 @@
|
|
|
43
44
|
"@types/node": "^22.0.0",
|
|
44
45
|
"tsup": "^8.3.0",
|
|
45
46
|
"typescript": "^5.7.0",
|
|
46
|
-
"vite": "^6.0.0"
|
|
47
|
+
"vite": "^6.0.0",
|
|
48
|
+
"vitest": "^4.0.18"
|
|
47
49
|
},
|
|
48
50
|
"scripts": {
|
|
49
51
|
"build": "tsup src/index.ts --format esm --dts --external @babel/core",
|
|
50
52
|
"dev": "tsup src/index.ts --format esm --dts --watch --external @babel/core",
|
|
51
|
-
"clean": "rm -rf dist"
|
|
53
|
+
"clean": "rm -rf dist",
|
|
54
|
+
"test": "vitest run"
|
|
52
55
|
}
|
|
53
56
|
}
|