@sanity/workbench-cli 1.1.0-beta.0 → 1.1.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/LICENSE +21 -0
- package/dist/_exports/dev.d.ts +54 -98
- package/dist/_exports/dev.js +1 -8
- package/dist/_exports/dev.js.map +1 -1
- package/dist/actions/dev/appServerSupervisor.js +66 -0
- package/dist/actions/dev/appServerSupervisor.js.map +1 -0
- package/dist/actions/dev/deriveInterfaces.js +34 -0
- package/dist/actions/dev/deriveInterfaces.js.map +1 -0
- package/dist/actions/dev/interfaceSetId.js +51 -0
- package/dist/actions/dev/interfaceSetId.js.map +1 -0
- package/dist/actions/dev/startDevManifestWatcher.js +104 -0
- package/dist/actions/dev/startDevManifestWatcher.js.map +1 -0
- package/dist/actions/dev/startDevServerRegistration.js +78 -0
- package/dist/actions/dev/startDevServerRegistration.js.map +1 -0
- package/dist/actions/dev/startWorkbenchDev.js +152 -0
- package/dist/actions/dev/startWorkbenchDev.js.map +1 -0
- package/dist/actions/dev/startWorkbenchDevServer.js +289 -0
- package/dist/actions/dev/startWorkbenchDevServer.js.map +1 -0
- package/dist/actions/dev/writeWorkbenchRuntime.js +67 -0
- package/dist/actions/dev/writeWorkbenchRuntime.js.map +1 -0
- package/package.json +27 -26
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
|
+
import { startAppServerSupervisor } from './appServerSupervisor.js';
|
|
3
|
+
import { startDevServerRegistration } from './startDevServerRegistration.js';
|
|
4
|
+
import { startWorkbenchDevServer, startWorkbenchRemoteCoordinator } from './startWorkbenchDevServer.js';
|
|
5
|
+
/** How long teardown runs before re-raising the signal to force-exit — enough for
|
|
6
|
+
* Vite/watchers to close, short enough not to strand a backgrounded process. */ const SHUTDOWN_GRACE_MS = 5000;
|
|
7
|
+
// Bind-only addresses ('0.0.0.0', '::') aren't routable in every browser (notably
|
|
8
|
+
// Windows); the displayed URL falls back to localhost. The bind address is untouched.
|
|
9
|
+
function toDisplayHost(host) {
|
|
10
|
+
if (!host || host === '0.0.0.0' || host === '::' || host === '[::]') {
|
|
11
|
+
return 'localhost';
|
|
12
|
+
}
|
|
13
|
+
return host;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Orchestrate the dev servers a workbench project needs: a singleton workbench
|
|
17
|
+
* Vite server plus the app/studio dev server it renders, wired to the dev-server
|
|
18
|
+
* registry so view/service edits re-sync live.
|
|
19
|
+
*
|
|
20
|
+
* A running workbench claims the configured port, so the app server binds the
|
|
21
|
+
* next one. If the workbench can't start (package or port unavailable), the app
|
|
22
|
+
* server falls back to the configured port and announces its own URL, exactly as
|
|
23
|
+
* a plain `sanity dev` would.
|
|
24
|
+
*/ export async function startWorkbenchDev(options) {
|
|
25
|
+
const { appId, cacheDir, checkForDeprecatedAppId, cliConfig, extractManifest, httpHost, httpPort, isApp, output, reactStrictMode, startAppServer, workDir } = options;
|
|
26
|
+
// The remote can't render itself, so it runs as a plain app server (not the
|
|
27
|
+
// shell) that still claims the lock and bridges the registry, so app
|
|
28
|
+
// `sanity dev`s register into it.
|
|
29
|
+
if (process.env.SANITY_INTERNAL_IS_WORKBENCH_REMOTE === 'true') {
|
|
30
|
+
const remote = await startAppServer({
|
|
31
|
+
announceUrl: true,
|
|
32
|
+
cliConfig,
|
|
33
|
+
httpPort
|
|
34
|
+
});
|
|
35
|
+
if (!remote.started) return {
|
|
36
|
+
close: async ()=>{}
|
|
37
|
+
};
|
|
38
|
+
const addr = remote.server.httpServer?.address();
|
|
39
|
+
const port = (typeof addr === 'object' && addr ? addr.port : remote.server.config.server.port) ?? httpPort;
|
|
40
|
+
const coordinator = startWorkbenchRemoteCoordinator({
|
|
41
|
+
httpHost,
|
|
42
|
+
port,
|
|
43
|
+
server: remote.server
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
close: async ()=>{
|
|
47
|
+
await coordinator.close();
|
|
48
|
+
await remote.close();
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// Unwound in reverse on any failure or on close(): the watcher stops before
|
|
53
|
+
// the app server, and the supervisor waits out an in-flight rebuild.
|
|
54
|
+
const closers = [];
|
|
55
|
+
const disposeAll = async ()=>{
|
|
56
|
+
for (const close of closers.splice(0).toReversed()){
|
|
57
|
+
await close().catch(()=>{});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const workbench = await startWorkbenchDevServer({
|
|
61
|
+
cacheDir,
|
|
62
|
+
cliConfig,
|
|
63
|
+
httpHost,
|
|
64
|
+
httpPort,
|
|
65
|
+
output,
|
|
66
|
+
reactStrictMode,
|
|
67
|
+
workDir
|
|
68
|
+
});
|
|
69
|
+
closers.push(workbench.close);
|
|
70
|
+
// A running workbench owns the configured port; the app server takes the next.
|
|
71
|
+
// Without one it claims the configured port and announces its own URL.
|
|
72
|
+
const appPort = workbench.workbenchAvailable ? workbench.workbenchPort + 1 : httpPort;
|
|
73
|
+
const announceUrl = !workbench.workbenchAvailable;
|
|
74
|
+
let closing;
|
|
75
|
+
const close = ()=>{
|
|
76
|
+
closing ??= (async ()=>{
|
|
77
|
+
process.off('SIGINT', onSignal);
|
|
78
|
+
process.off('SIGTERM', onSignal);
|
|
79
|
+
await disposeAll();
|
|
80
|
+
})();
|
|
81
|
+
return closing;
|
|
82
|
+
};
|
|
83
|
+
const supervised = await startAppServerSupervisor({
|
|
84
|
+
cliConfig,
|
|
85
|
+
start: (config)=>startAppServer({
|
|
86
|
+
announceUrl,
|
|
87
|
+
cliConfig: config,
|
|
88
|
+
httpPort: appPort
|
|
89
|
+
}),
|
|
90
|
+
workDir
|
|
91
|
+
}).catch(async (err)=>{
|
|
92
|
+
await disposeAll();
|
|
93
|
+
throw err;
|
|
94
|
+
});
|
|
95
|
+
if (!supervised.started) {
|
|
96
|
+
// The app server already reported why (e.g. missing organization id). Hand
|
|
97
|
+
// back a close that releases the workbench lock; nothing else came up.
|
|
98
|
+
return {
|
|
99
|
+
close
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const { supervisor } = supervised;
|
|
103
|
+
closers.push(supervisor.close);
|
|
104
|
+
try {
|
|
105
|
+
// The deprecated-id check and manifest extractor are CLI-domain, injected here.
|
|
106
|
+
checkForDeprecatedAppId();
|
|
107
|
+
const registration = await startDevServerRegistration({
|
|
108
|
+
appId,
|
|
109
|
+
cliConfig,
|
|
110
|
+
extractManifest,
|
|
111
|
+
isApp,
|
|
112
|
+
onInterfaceSetChange: ()=>supervisor.rebuild(),
|
|
113
|
+
output,
|
|
114
|
+
server: supervisor.server,
|
|
115
|
+
workDir
|
|
116
|
+
});
|
|
117
|
+
closers.push(registration.close);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
// Registration runs after both servers are up; a failure here would leak the
|
|
120
|
+
// workbench lock and dev servers without this teardown.
|
|
121
|
+
await disposeAll();
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
124
|
+
if (workbench.workbenchAvailable) {
|
|
125
|
+
const workbenchUrl = `http://${toDisplayHost(workbench.httpHost)}:${workbench.workbenchPort}`;
|
|
126
|
+
const addr = supervisor.server.httpServer?.address();
|
|
127
|
+
const port = typeof addr === 'object' && addr ? addr.port : supervisor.server.config.server.port;
|
|
128
|
+
output.log(`Workbench dev server started at ${styleText([
|
|
129
|
+
'blue',
|
|
130
|
+
'underline'
|
|
131
|
+
], workbenchUrl)} (app on port ${port})`);
|
|
132
|
+
}
|
|
133
|
+
// Trapping the signal disables Node's default exit, and a finished teardown
|
|
134
|
+
// doesn't guarantee an empty event loop (keep-alive sockets, an extraction
|
|
135
|
+
// worker mid-run) — so re-raise after teardown to restore conventional signal
|
|
136
|
+
// exit semantics. A backstop timer force-exits if teardown wedges.
|
|
137
|
+
function onSignal(signal) {
|
|
138
|
+
const graceTimer = setTimeout(()=>process.kill(process.pid, signal), SHUTDOWN_GRACE_MS);
|
|
139
|
+
graceTimer.unref();
|
|
140
|
+
void close().finally(()=>{
|
|
141
|
+
clearTimeout(graceTimer);
|
|
142
|
+
process.kill(process.pid, signal);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
process.once('SIGINT', onSignal);
|
|
146
|
+
process.once('SIGTERM', onSignal);
|
|
147
|
+
return {
|
|
148
|
+
close
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
//# sourceMappingURL=startWorkbenchDev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/dev/startWorkbenchDev.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type CliConfig, type Output} from '@sanity/cli-core'\n\nimport {type AppServerResult, startAppServerSupervisor} from './appServerSupervisor.js'\nimport {type DevServerManifest} from './registry.js'\nimport {startDevServerRegistration} from './startDevServerRegistration.js'\nimport {\n startWorkbenchDevServer,\n startWorkbenchRemoteCoordinator,\n} from './startWorkbenchDevServer.js'\n\n/** How long teardown runs before re-raising the signal to force-exit — enough for\n * Vite/watchers to close, short enough not to strand a backgrounded process. */\nconst SHUTDOWN_GRACE_MS = 5000\n\n// Bind-only addresses ('0.0.0.0', '::') aren't routable in every browser (notably\n// Windows); the displayed URL falls back to localhost. The bind address is untouched.\nfunction toDisplayHost(host: string | undefined): string {\n if (!host || host === '0.0.0.0' || host === '::' || host === '[::]') {\n return 'localhost'\n }\n return host\n}\n\nexport interface StartWorkbenchDevOptions {\n /** Resolved app id for the registry entry (the CLI owns id resolution). */\n appId: string | undefined\n /** Directory for the workbench Vite server's dependency cache. */\n cacheDir: string\n /** CLI-domain `app.id`/`deployment.appId` deprecation check, run before registering. */\n checkForDeprecatedAppId: () => void\n cliConfig: CliConfig\n /** Extract the project manifest to inline into the registry (studio-vs-app handled by the CLI). */\n extractManifest: (params: {\n configPath: string\n workDir: string\n }) => Promise<DevServerManifest['manifest']>\n httpHost: string | undefined\n httpPort: number\n isApp: boolean\n output: Output\n reactStrictMode: boolean\n /** Start the app/studio dev server — the CLI owns the server, this orchestrates it. */\n startAppServer: (params: {\n announceUrl: boolean\n cliConfig: CliConfig\n httpPort: number\n }) => Promise<AppServerResult>\n workDir: string\n}\n\n/**\n * Orchestrate the dev servers a workbench project needs: a singleton workbench\n * Vite server plus the app/studio dev server it renders, wired to the dev-server\n * registry so view/service edits re-sync live.\n *\n * A running workbench claims the configured port, so the app server binds the\n * next one. If the workbench can't start (package or port unavailable), the app\n * server falls back to the configured port and announces its own URL, exactly as\n * a plain `sanity dev` would.\n */\nexport async function startWorkbenchDev(\n options: StartWorkbenchDevOptions,\n): Promise<{close: () => Promise<void>}> {\n const {\n appId,\n cacheDir,\n checkForDeprecatedAppId,\n cliConfig,\n extractManifest,\n httpHost,\n httpPort,\n isApp,\n output,\n reactStrictMode,\n startAppServer,\n workDir,\n } = options\n\n // The remote can't render itself, so it runs as a plain app server (not the\n // shell) that still claims the lock and bridges the registry, so app\n // `sanity dev`s register into it.\n if (process.env.SANITY_INTERNAL_IS_WORKBENCH_REMOTE === 'true') {\n const remote = await startAppServer({announceUrl: true, cliConfig, httpPort})\n if (!remote.started) return {close: async () => {}}\n\n const addr = remote.server.httpServer?.address()\n const port =\n (typeof addr === 'object' && addr ? addr.port : remote.server.config.server.port) ?? httpPort\n const coordinator = startWorkbenchRemoteCoordinator({httpHost, port, server: remote.server})\n\n return {\n close: async () => {\n await coordinator.close()\n await remote.close()\n },\n }\n }\n\n // Unwound in reverse on any failure or on close(): the watcher stops before\n // the app server, and the supervisor waits out an in-flight rebuild.\n const closers: Array<() => Promise<void>> = []\n const disposeAll = async () => {\n for (const close of closers.splice(0).toReversed()) {\n await close().catch(() => {})\n }\n }\n\n const workbench = await startWorkbenchDevServer({\n cacheDir,\n cliConfig,\n httpHost,\n httpPort,\n output,\n reactStrictMode,\n workDir,\n })\n closers.push(workbench.close)\n\n // A running workbench owns the configured port; the app server takes the next.\n // Without one it claims the configured port and announces its own URL.\n const appPort = workbench.workbenchAvailable ? workbench.workbenchPort + 1 : httpPort\n const announceUrl = !workbench.workbenchAvailable\n\n let closing: Promise<void> | undefined\n const close = () => {\n closing ??= (async () => {\n process.off('SIGINT', onSignal)\n process.off('SIGTERM', onSignal)\n await disposeAll()\n })()\n return closing\n }\n\n const supervised = await startAppServerSupervisor({\n cliConfig,\n start: (config) => startAppServer({announceUrl, cliConfig: config, httpPort: appPort}),\n workDir,\n }).catch(async (err) => {\n await disposeAll()\n throw err\n })\n\n if (!supervised.started) {\n // The app server already reported why (e.g. missing organization id). Hand\n // back a close that releases the workbench lock; nothing else came up.\n return {close}\n }\n const {supervisor} = supervised\n closers.push(supervisor.close)\n\n try {\n // The deprecated-id check and manifest extractor are CLI-domain, injected here.\n checkForDeprecatedAppId()\n const registration = await startDevServerRegistration({\n appId,\n cliConfig,\n extractManifest,\n isApp,\n onInterfaceSetChange: () => supervisor.rebuild(),\n output,\n server: supervisor.server,\n workDir,\n })\n closers.push(registration.close)\n } catch (err) {\n // Registration runs after both servers are up; a failure here would leak the\n // workbench lock and dev servers without this teardown.\n await disposeAll()\n throw err\n }\n\n if (workbench.workbenchAvailable) {\n const workbenchUrl = `http://${toDisplayHost(workbench.httpHost)}:${workbench.workbenchPort}`\n const addr = supervisor.server.httpServer?.address()\n const port = typeof addr === 'object' && addr ? addr.port : supervisor.server.config.server.port\n output.log(\n `Workbench dev server started at ${styleText(['blue', 'underline'], workbenchUrl)} (app on port ${port})`,\n )\n }\n\n // Trapping the signal disables Node's default exit, and a finished teardown\n // doesn't guarantee an empty event loop (keep-alive sockets, an extraction\n // worker mid-run) — so re-raise after teardown to restore conventional signal\n // exit semantics. A backstop timer force-exits if teardown wedges.\n function onSignal(signal: NodeJS.Signals) {\n const graceTimer = setTimeout(() => process.kill(process.pid, signal), SHUTDOWN_GRACE_MS)\n graceTimer.unref()\n void close().finally(() => {\n clearTimeout(graceTimer)\n process.kill(process.pid, signal)\n })\n }\n process.once('SIGINT', onSignal)\n process.once('SIGTERM', onSignal)\n\n return {close}\n}\n"],"names":["styleText","startAppServerSupervisor","startDevServerRegistration","startWorkbenchDevServer","startWorkbenchRemoteCoordinator","SHUTDOWN_GRACE_MS","toDisplayHost","host","startWorkbenchDev","options","appId","cacheDir","checkForDeprecatedAppId","cliConfig","extractManifest","httpHost","httpPort","isApp","output","reactStrictMode","startAppServer","workDir","process","env","SANITY_INTERNAL_IS_WORKBENCH_REMOTE","remote","announceUrl","started","close","addr","server","httpServer","address","port","config","coordinator","closers","disposeAll","splice","toReversed","catch","workbench","push","appPort","workbenchAvailable","workbenchPort","closing","off","onSignal","supervised","start","err","supervisor","registration","onInterfaceSetChange","rebuild","workbenchUrl","log","signal","graceTimer","setTimeout","kill","pid","unref","finally","clearTimeout","once"],"mappings":"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"}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { isWorkbenchApp, resolveLocalPackage, subdebug } from '@sanity/cli-core';
|
|
2
|
+
import viteReact from '@vitejs/plugin-react';
|
|
3
|
+
import { createServer } from 'vite';
|
|
4
|
+
import { z } from 'zod/mini';
|
|
5
|
+
import { createInterfacesTracker } from './interfaceSetId.js';
|
|
6
|
+
import { acquireWorkbenchLock, getRegisteredServers, readWorkbenchLock, watchRegistry } from './registry.js';
|
|
7
|
+
import { writeWorkbenchRuntime } from './writeWorkbenchRuntime.js';
|
|
8
|
+
const devDebug = subdebug('dev');
|
|
9
|
+
const noop = async ()=>{};
|
|
10
|
+
const toApplicationsPayload = (servers)=>({
|
|
11
|
+
applications: servers.map(({ host, id, interfaces, manifest, port, projectId, type })=>({
|
|
12
|
+
host,
|
|
13
|
+
id,
|
|
14
|
+
interfaces,
|
|
15
|
+
manifest,
|
|
16
|
+
port,
|
|
17
|
+
projectId,
|
|
18
|
+
type
|
|
19
|
+
}))
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* Bridge the dev-server registry into a workbench Vite server's HMR channel so
|
|
23
|
+
* the page tracks apps as they come and go. A changed interface set means a
|
|
24
|
+
* rebuilt remote — full-reload to drop the stale remote-entry; otherwise
|
|
25
|
+
* rebroadcast for a soft reconcile. Returns a detach fn.
|
|
26
|
+
*/ function attachViteDevServerBridge(server) {
|
|
27
|
+
server.ws.on('sanity:workbench:get-local-applications', (_, client)=>{
|
|
28
|
+
client.send('sanity:workbench:local-applications', toApplicationsPayload(getRegisteredServers()));
|
|
29
|
+
});
|
|
30
|
+
const setTracker = createInterfacesTracker();
|
|
31
|
+
const registryWatcher = watchRegistry((servers)=>{
|
|
32
|
+
if (setTracker.hasChanged(servers)) {
|
|
33
|
+
server.ws.send({
|
|
34
|
+
type: 'full-reload'
|
|
35
|
+
});
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
server.ws.send('sanity:workbench:local-applications', toApplicationsPayload(servers));
|
|
39
|
+
});
|
|
40
|
+
return ()=>registryWatcher.close();
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Make the workbench remote act as the machine's workbench: claim the singleton
|
|
44
|
+
* lock so app `sanity dev`s register into it instead of each starting their own,
|
|
45
|
+
* and bridge the registry so the remote shows the local apps. No-op lock if one
|
|
46
|
+
* is already held.
|
|
47
|
+
*/ export function startWorkbenchRemoteCoordinator(options) {
|
|
48
|
+
const { httpHost, port, server } = options;
|
|
49
|
+
const lock = acquireWorkbenchLock({
|
|
50
|
+
host: httpHost || 'localhost',
|
|
51
|
+
port
|
|
52
|
+
});
|
|
53
|
+
if (!lock) {
|
|
54
|
+
const existing = readWorkbenchLock();
|
|
55
|
+
devDebug('Workbench lock already held by pid %d on port %d; bridging the registry without claiming it', existing?.pid, existing?.port);
|
|
56
|
+
}
|
|
57
|
+
const detachBridge = attachViteDevServerBridge(server);
|
|
58
|
+
return {
|
|
59
|
+
close: async ()=>{
|
|
60
|
+
detachBridge();
|
|
61
|
+
lock?.release();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Start the workbench dev server when federation is enabled and the workbench
|
|
67
|
+
* package is available. If the desired port is already taken — by another
|
|
68
|
+
* workbench instance or an unrelated process — fall back to running without a
|
|
69
|
+
* workbench and let the app/studio dev server claim the configured port.
|
|
70
|
+
*/ export async function startWorkbenchDevServer(options) {
|
|
71
|
+
const { cacheDir, cliConfig, httpHost, httpPort: workbenchPort, output, reactStrictMode, workDir } = options;
|
|
72
|
+
// Workbench is opted into solely by calling `unstable_defineApp`.
|
|
73
|
+
if (!isWorkbenchApp(cliConfig?.app)) {
|
|
74
|
+
devDebug('Not a workbench app, skipping workbench dev server');
|
|
75
|
+
return {
|
|
76
|
+
close: noop,
|
|
77
|
+
httpHost,
|
|
78
|
+
workbenchAvailable: false,
|
|
79
|
+
workbenchPort
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
let workbenchAvailable = false;
|
|
83
|
+
try {
|
|
84
|
+
await resolveLocalPackage('sanity/workbench', workDir);
|
|
85
|
+
workbenchAvailable = true;
|
|
86
|
+
} catch {
|
|
87
|
+
devDebug('Workbench not available, skipping workbench dev server');
|
|
88
|
+
}
|
|
89
|
+
if (!workbenchAvailable) {
|
|
90
|
+
return {
|
|
91
|
+
close: noop,
|
|
92
|
+
httpHost,
|
|
93
|
+
workbenchAvailable,
|
|
94
|
+
workbenchPort
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// Acquire an exclusive lock — only one workbench per machine.
|
|
98
|
+
// Uses O_EXCL which is atomic at the OS level, preventing races when
|
|
99
|
+
// multiple `sanity dev` processes start simultaneously (e.g. via turbo).
|
|
100
|
+
const workbenchLock = acquireWorkbenchLock({
|
|
101
|
+
host: httpHost || 'localhost',
|
|
102
|
+
port: workbenchPort
|
|
103
|
+
});
|
|
104
|
+
if (!workbenchLock) {
|
|
105
|
+
const existing = readWorkbenchLock();
|
|
106
|
+
devDebug('Workbench already running at pid %d on port %d, skipping', existing?.pid, existing?.port);
|
|
107
|
+
return {
|
|
108
|
+
close: noop,
|
|
109
|
+
httpHost: existing?.host ?? httpHost,
|
|
110
|
+
workbenchAvailable: true,
|
|
111
|
+
workbenchPort: existing?.port ?? workbenchPort
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// The lock is already held; an exception here (runtime-file write failure,
|
|
115
|
+
// invalid remote URL) would otherwise leak it until the next acquire prunes
|
|
116
|
+
// the stale PID.
|
|
117
|
+
let result;
|
|
118
|
+
try {
|
|
119
|
+
result = await createWorkbenchViteServer({
|
|
120
|
+
cacheDir,
|
|
121
|
+
cliConfig,
|
|
122
|
+
httpHost,
|
|
123
|
+
output,
|
|
124
|
+
reactStrictMode,
|
|
125
|
+
workbenchPort,
|
|
126
|
+
workDir
|
|
127
|
+
});
|
|
128
|
+
} catch (err) {
|
|
129
|
+
workbenchLock.release();
|
|
130
|
+
throw err;
|
|
131
|
+
}
|
|
132
|
+
if (!result) {
|
|
133
|
+
workbenchLock.release();
|
|
134
|
+
return {
|
|
135
|
+
close: noop,
|
|
136
|
+
httpHost,
|
|
137
|
+
workbenchAvailable: false,
|
|
138
|
+
workbenchPort
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const { actualPort, close } = result;
|
|
142
|
+
workbenchLock.updatePort(actualPort);
|
|
143
|
+
return {
|
|
144
|
+
close: async ()=>{
|
|
145
|
+
workbenchLock.release();
|
|
146
|
+
await close();
|
|
147
|
+
},
|
|
148
|
+
httpHost,
|
|
149
|
+
workbenchAvailable,
|
|
150
|
+
workbenchPort: actualPort
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
async function createWorkbenchViteServer(options) {
|
|
154
|
+
const { cacheDir, cliConfig, httpHost, output, reactStrictMode, workbenchPort, workDir } = options;
|
|
155
|
+
const remoteUrl = parseRemoteUrl(process.env.SANITY_INTERNAL_WORKBENCH_REMOTE_URL);
|
|
156
|
+
const organizationId = resolveOrganizationId(cliConfig);
|
|
157
|
+
devDebug('Writing workbench runtime files');
|
|
158
|
+
const root = await writeWorkbenchRuntime({
|
|
159
|
+
cwd: workDir,
|
|
160
|
+
organizationId,
|
|
161
|
+
reactStrictMode,
|
|
162
|
+
remoteUrl
|
|
163
|
+
});
|
|
164
|
+
const viteConfig = {
|
|
165
|
+
// Custom cache directory so sanity's vite cache doesn't conflict with local vite projects
|
|
166
|
+
cacheDir,
|
|
167
|
+
configFile: false,
|
|
168
|
+
define: {
|
|
169
|
+
__SANITY_STAGING__: process.env.SANITY_INTERNAL_ENV === 'staging',
|
|
170
|
+
'import.meta.env.SANITY_INTERNAL_WORKBENCH_REMOTE_URL': JSON.stringify(remoteUrl)
|
|
171
|
+
},
|
|
172
|
+
logLevel: 'warn',
|
|
173
|
+
mode: 'development',
|
|
174
|
+
optimizeDeps: {
|
|
175
|
+
// Exclude sanity/workbench (and its transitive dep @sanity/workbench)
|
|
176
|
+
// from dep pre-bundling so that `import.meta.hot` is available at
|
|
177
|
+
// runtime — pre-bundled modules do not receive Vite's HMR client
|
|
178
|
+
// injection, which causes the custom HMR events for local application
|
|
179
|
+
// discovery to silently not fire.
|
|
180
|
+
exclude: [
|
|
181
|
+
'sanity',
|
|
182
|
+
'@sanity/workbench'
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
// viteReact looks inert here — it transforms none of the host's own modules —
|
|
186
|
+
// but it's load-bearing for the remotes. It serves the Fast Refresh runtime at
|
|
187
|
+
// /@react-refresh and injects the preamble that defines window.$RefreshReg$. The
|
|
188
|
+
// federated remotes loaded into this page are react-refresh transformed, so
|
|
189
|
+
// without the preamble they throw "can't detect preamble", and without the
|
|
190
|
+
// runtime their /@react-refresh import (wired by @module-federation/vite's
|
|
191
|
+
// remoteHmr) fails. Dropping it as dead code broke every panel; see #1262.
|
|
192
|
+
plugins: [
|
|
193
|
+
viteReact(),
|
|
194
|
+
...remoteUrl ? [
|
|
195
|
+
remoteManifestPreloadHeaderPlugin(remoteUrl)
|
|
196
|
+
] : []
|
|
197
|
+
],
|
|
198
|
+
resolve: {
|
|
199
|
+
dedupe: [
|
|
200
|
+
'react',
|
|
201
|
+
'react-dom'
|
|
202
|
+
]
|
|
203
|
+
},
|
|
204
|
+
root,
|
|
205
|
+
server: {
|
|
206
|
+
host: httpHost,
|
|
207
|
+
port: workbenchPort,
|
|
208
|
+
strictPort: false,
|
|
209
|
+
warmup: {
|
|
210
|
+
clientFiles: [
|
|
211
|
+
'./workbench.js'
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
devDebug('Creating workbench vite server');
|
|
217
|
+
const server = await createServer(viteConfig);
|
|
218
|
+
try {
|
|
219
|
+
await server.listen();
|
|
220
|
+
} catch (err) {
|
|
221
|
+
await server.close();
|
|
222
|
+
output.warn(`Workbench dev server failed to start: ${err instanceof Error ? err.message : String(err)}`);
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
// Vite may have picked a different port if the desired one was occupied
|
|
226
|
+
const addr = server.httpServer?.address();
|
|
227
|
+
const actualPort = typeof addr === 'object' && addr ? addr.port : workbenchPort;
|
|
228
|
+
// Fire-and-forget: warm the workbench remote's Vite transform pipeline so
|
|
229
|
+
// the first browser request hits a pre-populated module graph.
|
|
230
|
+
if (remoteUrl) {
|
|
231
|
+
fetch(remoteUrl).then((r)=>r.body?.cancel()).catch(()=>{});
|
|
232
|
+
devDebug('Warming workbench remote at %s', remoteUrl);
|
|
233
|
+
}
|
|
234
|
+
const detachBridge = attachViteDevServerBridge(server);
|
|
235
|
+
return {
|
|
236
|
+
actualPort,
|
|
237
|
+
close: async ()=>{
|
|
238
|
+
detachBridge();
|
|
239
|
+
await server.close();
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
// Workbench is opted into via `unstable_defineApp`, which carries the
|
|
244
|
+
// organization ID. Deliberately no fallback (e.g. resolving it from the
|
|
245
|
+
// configured project): the lookup would need an authenticated user and an
|
|
246
|
+
// API round-trip on every startup for something the opt-in already declares.
|
|
247
|
+
const resolveOrganizationId = (cliConfig)=>{
|
|
248
|
+
if (cliConfig.app?.organizationId) {
|
|
249
|
+
return cliConfig.app.organizationId;
|
|
250
|
+
}
|
|
251
|
+
throw new Error('Workbench requires an organization ID. Pass "organizationId" to unstable_defineApp() in sanity.cli.ts.');
|
|
252
|
+
};
|
|
253
|
+
// Restricts protocol to http(s) so the URL is safe to interpolate into HTML
|
|
254
|
+
// attributes and Link headers downstream.
|
|
255
|
+
const remoteUrlSchema = z.url({
|
|
256
|
+
normalize: true,
|
|
257
|
+
protocol: /^https?$/
|
|
258
|
+
});
|
|
259
|
+
function parseRemoteUrl(value) {
|
|
260
|
+
if (!value) return undefined;
|
|
261
|
+
const result = remoteUrlSchema.safeParse(value);
|
|
262
|
+
if (!result.success) {
|
|
263
|
+
throw new Error(`Invalid SANITY_INTERNAL_WORKBENCH_REMOTE_URL: ${value} (must be an http(s) URL)`);
|
|
264
|
+
}
|
|
265
|
+
return result.data;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Sets a `Link: <remoteUrl>; rel=preload; as=fetch; crossorigin` response header
|
|
269
|
+
* on the index document so the browser can start fetching the Module Federation
|
|
270
|
+
* manifest as soon as response headers arrive — before HTML parsing reaches the
|
|
271
|
+
* in-head preconnect hint. `as=fetch` matches how the federation runtime later
|
|
272
|
+
* retrieves the JSON manifest, allowing the preload entry to satisfy that fetch.
|
|
273
|
+
*/ function remoteManifestPreloadHeaderPlugin(remoteUrl) {
|
|
274
|
+
return {
|
|
275
|
+
apply: 'serve',
|
|
276
|
+
configureServer (server) {
|
|
277
|
+
server.middlewares.use((req, res, next)=>{
|
|
278
|
+
const pathname = (req.url || '/').split('?')[0];
|
|
279
|
+
if (pathname === '/' || pathname === '/index.html') {
|
|
280
|
+
res.setHeader('Link', `<${remoteUrl}>; rel=preload; as=fetch; crossorigin`);
|
|
281
|
+
}
|
|
282
|
+
next();
|
|
283
|
+
});
|
|
284
|
+
},
|
|
285
|
+
name: 'sanity:workbench-remote-preload-header'
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
//# sourceMappingURL=startWorkbenchDevServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/dev/startWorkbenchDevServer.ts"],"sourcesContent":["import {\n type CliConfig,\n isWorkbenchApp,\n type Output,\n resolveLocalPackage,\n subdebug,\n} from '@sanity/cli-core'\nimport viteReact from '@vitejs/plugin-react'\nimport {createServer, type InlineConfig, type Plugin, type ViteDevServer} from 'vite'\nimport {z} from 'zod/mini'\n\nimport {createInterfacesTracker} from './interfaceSetId.js'\nimport {\n acquireWorkbenchLock,\n type DevServerManifest,\n getRegisteredServers,\n readWorkbenchLock,\n watchRegistry,\n} from './registry.js'\nimport {writeWorkbenchRuntime} from './writeWorkbenchRuntime.js'\n\nconst devDebug = subdebug('dev')\n\nconst noop = async () => {}\n\nconst toApplicationsPayload = (servers: DevServerManifest[]) => ({\n applications: servers.map(({host, id, interfaces, manifest, port, projectId, type}) => ({\n host,\n id,\n interfaces,\n manifest,\n port,\n projectId,\n type,\n })),\n})\n\n/**\n * Bridge the dev-server registry into a workbench Vite server's HMR channel so\n * the page tracks apps as they come and go. A changed interface set means a\n * rebuilt remote — full-reload to drop the stale remote-entry; otherwise\n * rebroadcast for a soft reconcile. Returns a detach fn.\n */\nfunction attachViteDevServerBridge(server: ViteDevServer): () => void {\n server.ws.on('sanity:workbench:get-local-applications', (_, client) => {\n client.send(\n 'sanity:workbench:local-applications',\n toApplicationsPayload(getRegisteredServers()),\n )\n })\n\n const setTracker = createInterfacesTracker()\n const registryWatcher = watchRegistry((servers) => {\n if (setTracker.hasChanged(servers)) {\n server.ws.send({type: 'full-reload'})\n return\n }\n server.ws.send('sanity:workbench:local-applications', toApplicationsPayload(servers))\n })\n\n return () => registryWatcher.close()\n}\n\n/**\n * Make the workbench remote act as the machine's workbench: claim the singleton\n * lock so app `sanity dev`s register into it instead of each starting their own,\n * and bridge the registry so the remote shows the local apps. No-op lock if one\n * is already held.\n */\nexport function startWorkbenchRemoteCoordinator(options: {\n httpHost: string | undefined\n port: number\n server: ViteDevServer\n}): {close: () => Promise<void>} {\n const {httpHost, port, server} = options\n\n const lock = acquireWorkbenchLock({host: httpHost || 'localhost', port})\n if (!lock) {\n const existing = readWorkbenchLock()\n devDebug(\n 'Workbench lock already held by pid %d on port %d; bridging the registry without claiming it',\n existing?.pid,\n existing?.port,\n )\n }\n\n const detachBridge = attachViteDevServerBridge(server)\n\n return {\n close: async () => {\n detachBridge()\n lock?.release()\n },\n }\n}\n\ninterface WorkbenchDevServerResult {\n close: () => Promise<void>\n httpHost: string | undefined\n workbenchAvailable: boolean\n workbenchPort: number\n}\n\nexport interface StartWorkbenchOptions {\n /** Dependency-cache dir for the workbench Vite server, kept apart from the user's own. */\n cacheDir: string\n cliConfig: CliConfig\n httpHost: string | undefined\n httpPort: number\n output: Output\n /** Wrap the workbench in React StrictMode; the CLI resolves it (unset collapses to `false`). */\n reactStrictMode: boolean\n workDir: string\n}\n\n/**\n * Start the workbench dev server when federation is enabled and the workbench\n * package is available. If the desired port is already taken — by another\n * workbench instance or an unrelated process — fall back to running without a\n * workbench and let the app/studio dev server claim the configured port.\n */\nexport async function startWorkbenchDevServer(\n options: StartWorkbenchOptions,\n): Promise<WorkbenchDevServerResult> {\n const {\n cacheDir,\n cliConfig,\n httpHost,\n httpPort: workbenchPort,\n output,\n reactStrictMode,\n workDir,\n } = options\n\n // Workbench is opted into solely by calling `unstable_defineApp`.\n if (!isWorkbenchApp(cliConfig?.app)) {\n devDebug('Not a workbench app, skipping workbench dev server')\n return {close: noop, httpHost, workbenchAvailable: false, workbenchPort}\n }\n\n let workbenchAvailable = false\n\n try {\n await resolveLocalPackage('sanity/workbench', workDir)\n workbenchAvailable = true\n } catch {\n devDebug('Workbench not available, skipping workbench dev server')\n }\n\n if (!workbenchAvailable) {\n return {close: noop, httpHost, workbenchAvailable, workbenchPort}\n }\n\n // Acquire an exclusive lock — only one workbench per machine.\n // Uses O_EXCL which is atomic at the OS level, preventing races when\n // multiple `sanity dev` processes start simultaneously (e.g. via turbo).\n const workbenchLock = acquireWorkbenchLock({host: httpHost || 'localhost', port: workbenchPort})\n if (!workbenchLock) {\n const existing = readWorkbenchLock()\n devDebug(\n 'Workbench already running at pid %d on port %d, skipping',\n existing?.pid,\n existing?.port,\n )\n return {\n close: noop,\n httpHost: existing?.host ?? httpHost,\n workbenchAvailable: true,\n workbenchPort: existing?.port ?? workbenchPort,\n }\n }\n\n // The lock is already held; an exception here (runtime-file write failure,\n // invalid remote URL) would otherwise leak it until the next acquire prunes\n // the stale PID.\n let result: Awaited<ReturnType<typeof createWorkbenchViteServer>>\n try {\n result = await createWorkbenchViteServer({\n cacheDir,\n cliConfig,\n httpHost,\n output,\n reactStrictMode,\n workbenchPort,\n workDir,\n })\n } catch (err) {\n workbenchLock.release()\n throw err\n }\n\n if (!result) {\n workbenchLock.release()\n return {close: noop, httpHost, workbenchAvailable: false, workbenchPort}\n }\n\n const {actualPort, close} = result\n workbenchLock.updatePort(actualPort)\n\n return {\n close: async () => {\n workbenchLock.release()\n await close()\n },\n httpHost,\n workbenchAvailable,\n workbenchPort: actualPort,\n }\n}\n\ninterface CreateWorkbenchViteServerOptions {\n cacheDir: string\n cliConfig: CliConfig\n httpHost: string | undefined\n output: Output\n reactStrictMode: boolean\n workbenchPort: number\n workDir: string\n}\n\ninterface CreateWorkbenchViteServerResult {\n actualPort: number\n close: () => Promise<void>\n}\n\nasync function createWorkbenchViteServer(\n options: CreateWorkbenchViteServerOptions,\n): Promise<CreateWorkbenchViteServerResult | undefined> {\n const {cacheDir, cliConfig, httpHost, output, reactStrictMode, workbenchPort, workDir} = options\n\n const remoteUrl = parseRemoteUrl(process.env.SANITY_INTERNAL_WORKBENCH_REMOTE_URL)\n\n const organizationId = resolveOrganizationId(cliConfig)\n\n devDebug('Writing workbench runtime files')\n const root = await writeWorkbenchRuntime({\n cwd: workDir,\n organizationId,\n reactStrictMode,\n remoteUrl,\n })\n\n const viteConfig: InlineConfig = {\n // Custom cache directory so sanity's vite cache doesn't conflict with local vite projects\n cacheDir,\n configFile: false,\n define: {\n __SANITY_STAGING__: process.env.SANITY_INTERNAL_ENV === 'staging',\n 'import.meta.env.SANITY_INTERNAL_WORKBENCH_REMOTE_URL': JSON.stringify(remoteUrl),\n },\n logLevel: 'warn',\n mode: 'development',\n optimizeDeps: {\n // Exclude sanity/workbench (and its transitive dep @sanity/workbench)\n // from dep pre-bundling so that `import.meta.hot` is available at\n // runtime — pre-bundled modules do not receive Vite's HMR client\n // injection, which causes the custom HMR events for local application\n // discovery to silently not fire.\n exclude: ['sanity', '@sanity/workbench'],\n },\n // viteReact looks inert here — it transforms none of the host's own modules —\n // but it's load-bearing for the remotes. It serves the Fast Refresh runtime at\n // /@react-refresh and injects the preamble that defines window.$RefreshReg$. The\n // federated remotes loaded into this page are react-refresh transformed, so\n // without the preamble they throw \"can't detect preamble\", and without the\n // runtime their /@react-refresh import (wired by @module-federation/vite's\n // remoteHmr) fails. Dropping it as dead code broke every panel; see #1262.\n plugins: [viteReact(), ...(remoteUrl ? [remoteManifestPreloadHeaderPlugin(remoteUrl)] : [])],\n resolve: {dedupe: ['react', 'react-dom']},\n root,\n server: {\n host: httpHost,\n port: workbenchPort,\n strictPort: false,\n warmup: {\n clientFiles: ['./workbench.js'],\n },\n },\n }\n\n devDebug('Creating workbench vite server')\n const server = await createServer(viteConfig)\n try {\n await server.listen()\n } catch (err) {\n await server.close()\n output.warn(\n `Workbench dev server failed to start: ${err instanceof Error ? err.message : String(err)}`,\n )\n return undefined\n }\n\n // Vite may have picked a different port if the desired one was occupied\n const addr = server.httpServer?.address()\n const actualPort = typeof addr === 'object' && addr ? addr.port : workbenchPort\n\n // Fire-and-forget: warm the workbench remote's Vite transform pipeline so\n // the first browser request hits a pre-populated module graph.\n if (remoteUrl) {\n fetch(remoteUrl)\n .then((r) => r.body?.cancel())\n .catch(() => {})\n devDebug('Warming workbench remote at %s', remoteUrl)\n }\n\n const detachBridge = attachViteDevServerBridge(server)\n\n return {\n actualPort,\n close: async () => {\n detachBridge()\n await server.close()\n },\n }\n}\n\n// Workbench is opted into via `unstable_defineApp`, which carries the\n// organization ID. Deliberately no fallback (e.g. resolving it from the\n// configured project): the lookup would need an authenticated user and an\n// API round-trip on every startup for something the opt-in already declares.\nconst resolveOrganizationId = (cliConfig: CliConfig): string => {\n if (cliConfig.app?.organizationId) {\n return cliConfig.app.organizationId\n }\n\n throw new Error(\n 'Workbench requires an organization ID. Pass \"organizationId\" to unstable_defineApp() in sanity.cli.ts.',\n )\n}\n\n// Restricts protocol to http(s) so the URL is safe to interpolate into HTML\n// attributes and Link headers downstream.\nconst remoteUrlSchema = z.url({normalize: true, protocol: /^https?$/})\n\nfunction parseRemoteUrl(value: string | undefined): string | undefined {\n if (!value) return undefined\n\n const result = remoteUrlSchema.safeParse(value)\n\n if (!result.success) {\n throw new Error(\n `Invalid SANITY_INTERNAL_WORKBENCH_REMOTE_URL: ${value} (must be an http(s) URL)`,\n )\n }\n\n return result.data\n}\n\n/**\n * Sets a `Link: <remoteUrl>; rel=preload; as=fetch; crossorigin` response header\n * on the index document so the browser can start fetching the Module Federation\n * manifest as soon as response headers arrive — before HTML parsing reaches the\n * in-head preconnect hint. `as=fetch` matches how the federation runtime later\n * retrieves the JSON manifest, allowing the preload entry to satisfy that fetch.\n */\nfunction remoteManifestPreloadHeaderPlugin(remoteUrl: string): Plugin {\n return {\n apply: 'serve',\n configureServer(server) {\n server.middlewares.use((req, res, next) => {\n const pathname = (req.url || '/').split('?')[0]\n if (pathname === '/' || pathname === '/index.html') {\n res.setHeader('Link', `<${remoteUrl}>; rel=preload; as=fetch; crossorigin`)\n }\n next()\n })\n },\n name: 'sanity:workbench-remote-preload-header',\n }\n}\n"],"names":["isWorkbenchApp","resolveLocalPackage","subdebug","viteReact","createServer","z","createInterfacesTracker","acquireWorkbenchLock","getRegisteredServers","readWorkbenchLock","watchRegistry","writeWorkbenchRuntime","devDebug","noop","toApplicationsPayload","servers","applications","map","host","id","interfaces","manifest","port","projectId","type","attachViteDevServerBridge","server","ws","on","_","client","send","setTracker","registryWatcher","hasChanged","close","startWorkbenchRemoteCoordinator","options","httpHost","lock","existing","pid","detachBridge","release","startWorkbenchDevServer","cacheDir","cliConfig","httpPort","workbenchPort","output","reactStrictMode","workDir","app","workbenchAvailable","workbenchLock","result","createWorkbenchViteServer","err","actualPort","updatePort","remoteUrl","parseRemoteUrl","process","env","SANITY_INTERNAL_WORKBENCH_REMOTE_URL","organizationId","resolveOrganizationId","root","cwd","viteConfig","configFile","define","__SANITY_STAGING__","SANITY_INTERNAL_ENV","JSON","stringify","logLevel","mode","optimizeDeps","exclude","plugins","remoteManifestPreloadHeaderPlugin","resolve","dedupe","strictPort","warmup","clientFiles","listen","warn","Error","message","String","undefined","addr","httpServer","address","fetch","then","r","body","cancel","catch","remoteUrlSchema","url","normalize","protocol","value","safeParse","success","data","apply","configureServer","middlewares","use","req","res","next","pathname","split","setHeader","name"],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { subdebug } from '@sanity/cli-core';
|
|
4
|
+
const devDebug = subdebug('dev');
|
|
5
|
+
const workbenchJsTemplate = `\
|
|
6
|
+
// This file is auto-generated on 'sanity dev'
|
|
7
|
+
// Modifications to this file are automatically discarded
|
|
8
|
+
import {renderWorkbench} from "sanity/workbench"
|
|
9
|
+
|
|
10
|
+
renderWorkbench(
|
|
11
|
+
document.getElementById("workbench"),
|
|
12
|
+
{organizationId: %SANITY_WORKBENCH_ORGANIZATION_ID%},
|
|
13
|
+
{reactStrictMode: %SANITY_WORKBENCH_REACT_STRICT_MODE%}
|
|
14
|
+
)
|
|
15
|
+
`;
|
|
16
|
+
const indexHtmlTemplate = `\
|
|
17
|
+
<!DOCTYPE html>
|
|
18
|
+
<!-- This file is auto-generated on 'sanity dev' -->
|
|
19
|
+
<!-- Modifications to this file are automatically discarded -->
|
|
20
|
+
<html>
|
|
21
|
+
<head>
|
|
22
|
+
<meta charset="UTF-8" />
|
|
23
|
+
%SANITY_WORKBENCH_PREFETCH_HINTS%
|
|
24
|
+
</head>
|
|
25
|
+
<body>
|
|
26
|
+
<div id="workbench"></div>
|
|
27
|
+
<script type="module" src="./workbench.js"></script>
|
|
28
|
+
</body>
|
|
29
|
+
</html>
|
|
30
|
+
`;
|
|
31
|
+
/**
|
|
32
|
+
* Generates the `.sanity/workbench` directory with static entry files for
|
|
33
|
+
* the workbench Vite dev server.
|
|
34
|
+
*
|
|
35
|
+
* @param cwd - Current working directory (Sanity root dir)
|
|
36
|
+
* @returns The absolute path to the written workbench runtime directory
|
|
37
|
+
* @internal
|
|
38
|
+
*/ export async function writeWorkbenchRuntime(options) {
|
|
39
|
+
const { cwd, organizationId, reactStrictMode, remoteUrl } = options;
|
|
40
|
+
const workbenchDir = path.join(cwd, '.sanity', 'workbench');
|
|
41
|
+
const workbenchJs = workbenchJsTemplate.replace(/%SANITY_WORKBENCH_ORGANIZATION_ID%/, organizationId === undefined ? 'undefined' : JSON.stringify(organizationId)).replace(/%SANITY_WORKBENCH_REACT_STRICT_MODE%/, JSON.stringify(reactStrictMode));
|
|
42
|
+
const prefetchHints = buildPrefetchHints(remoteUrl);
|
|
43
|
+
const indexHtml = indexHtmlTemplate.replace(/%SANITY_WORKBENCH_PREFETCH_HINTS%/, prefetchHints);
|
|
44
|
+
devDebug('Making workbench runtime directory');
|
|
45
|
+
await fs.mkdir(workbenchDir, {
|
|
46
|
+
recursive: true
|
|
47
|
+
});
|
|
48
|
+
devDebug('Writing workbench.js to workbench runtime directory');
|
|
49
|
+
await fs.writeFile(path.join(workbenchDir, 'workbench.js'), workbenchJs);
|
|
50
|
+
devDebug('Writing index.html to workbench runtime directory');
|
|
51
|
+
await fs.writeFile(path.join(workbenchDir, 'index.html'), indexHtml);
|
|
52
|
+
return workbenchDir;
|
|
53
|
+
}
|
|
54
|
+
function buildPrefetchHints(remoteUrl) {
|
|
55
|
+
if (!remoteUrl) return '';
|
|
56
|
+
try {
|
|
57
|
+
const url = new URL(remoteUrl);
|
|
58
|
+
return [
|
|
59
|
+
` <link rel="preconnect" href="${url.origin}" />`,
|
|
60
|
+
` <link rel="preload" as="fetch" href="${url.toString()}" crossorigin />`
|
|
61
|
+
].join('\n');
|
|
62
|
+
} catch {
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=writeWorkbenchRuntime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/dev/writeWorkbenchRuntime.ts"],"sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport {subdebug} from '@sanity/cli-core'\n\nconst devDebug = subdebug('dev')\n\nconst workbenchJsTemplate = `\\\n// This file is auto-generated on 'sanity dev'\n// Modifications to this file are automatically discarded\nimport {renderWorkbench} from \"sanity/workbench\"\n\nrenderWorkbench(\n document.getElementById(\"workbench\"),\n {organizationId: %SANITY_WORKBENCH_ORGANIZATION_ID%},\n {reactStrictMode: %SANITY_WORKBENCH_REACT_STRICT_MODE%}\n)\n`\n\nconst indexHtmlTemplate = `\\\n<!DOCTYPE html>\n<!-- This file is auto-generated on 'sanity dev' -->\n<!-- Modifications to this file are automatically discarded -->\n<html>\n <head>\n <meta charset=\"UTF-8\" />\n%SANITY_WORKBENCH_PREFETCH_HINTS%\n </head>\n <body>\n <div id=\"workbench\"></div>\n <script type=\"module\" src=\"./workbench.js\"></script>\n </body>\n</html>\n`\n\n/**\n * Generates the `.sanity/workbench` directory with static entry files for\n * the workbench Vite dev server.\n *\n * @param cwd - Current working directory (Sanity root dir)\n * @returns The absolute path to the written workbench runtime directory\n * @internal\n */\nexport async function writeWorkbenchRuntime(options: {\n cwd: string\n organizationId?: string\n reactStrictMode: boolean\n remoteUrl?: string\n}): Promise<string> {\n const {cwd, organizationId, reactStrictMode, remoteUrl} = options\n const workbenchDir = path.join(cwd, '.sanity', 'workbench')\n\n const workbenchJs = workbenchJsTemplate\n .replace(\n /%SANITY_WORKBENCH_ORGANIZATION_ID%/,\n organizationId === undefined ? 'undefined' : JSON.stringify(organizationId),\n )\n .replace(/%SANITY_WORKBENCH_REACT_STRICT_MODE%/, JSON.stringify(reactStrictMode))\n\n const prefetchHints = buildPrefetchHints(remoteUrl)\n\n const indexHtml = indexHtmlTemplate.replace(/%SANITY_WORKBENCH_PREFETCH_HINTS%/, prefetchHints)\n\n devDebug('Making workbench runtime directory')\n await fs.mkdir(workbenchDir, {recursive: true})\n\n devDebug('Writing workbench.js to workbench runtime directory')\n await fs.writeFile(path.join(workbenchDir, 'workbench.js'), workbenchJs)\n\n devDebug('Writing index.html to workbench runtime directory')\n await fs.writeFile(path.join(workbenchDir, 'index.html'), indexHtml)\n\n return workbenchDir\n}\n\nfunction buildPrefetchHints(remoteUrl: string | undefined): string {\n if (!remoteUrl) return ''\n\n try {\n const url = new URL(remoteUrl)\n return [\n ` <link rel=\"preconnect\" href=\"${url.origin}\" />`,\n ` <link rel=\"preload\" as=\"fetch\" href=\"${url.toString()}\" crossorigin />`,\n ].join('\\n')\n } catch {\n return ''\n }\n}\n"],"names":["fs","path","subdebug","devDebug","workbenchJsTemplate","indexHtmlTemplate","writeWorkbenchRuntime","options","cwd","organizationId","reactStrictMode","remoteUrl","workbenchDir","join","workbenchJs","replace","undefined","JSON","stringify","prefetchHints","buildPrefetchHints","indexHtml","mkdir","recursive","writeFile","url","URL","origin","toString"],"mappings":"AAAA,OAAOA,QAAQ,mBAAkB;AACjC,OAAOC,UAAU,YAAW;AAE5B,SAAQC,QAAQ,QAAO,mBAAkB;AAEzC,MAAMC,WAAWD,SAAS;AAE1B,MAAME,sBAAsB,CAAC;;;;;;;;;;AAU7B,CAAC;AAED,MAAMC,oBAAoB,CAAC;;;;;;;;;;;;;;AAc3B,CAAC;AAED;;;;;;;CAOC,GACD,OAAO,eAAeC,sBAAsBC,OAK3C;IACC,MAAM,EAACC,GAAG,EAAEC,cAAc,EAAEC,eAAe,EAAEC,SAAS,EAAC,GAAGJ;IAC1D,MAAMK,eAAeX,KAAKY,IAAI,CAACL,KAAK,WAAW;IAE/C,MAAMM,cAAcV,oBACjBW,OAAO,CACN,sCACAN,mBAAmBO,YAAY,cAAcC,KAAKC,SAAS,CAACT,iBAE7DM,OAAO,CAAC,wCAAwCE,KAAKC,SAAS,CAACR;IAElE,MAAMS,gBAAgBC,mBAAmBT;IAEzC,MAAMU,YAAYhB,kBAAkBU,OAAO,CAAC,qCAAqCI;IAEjFhB,SAAS;IACT,MAAMH,GAAGsB,KAAK,CAACV,cAAc;QAACW,WAAW;IAAI;IAE7CpB,SAAS;IACT,MAAMH,GAAGwB,SAAS,CAACvB,KAAKY,IAAI,CAACD,cAAc,iBAAiBE;IAE5DX,SAAS;IACT,MAAMH,GAAGwB,SAAS,CAACvB,KAAKY,IAAI,CAACD,cAAc,eAAeS;IAE1D,OAAOT;AACT;AAEA,SAASQ,mBAAmBT,SAA6B;IACvD,IAAI,CAACA,WAAW,OAAO;IAEvB,IAAI;QACF,MAAMc,MAAM,IAAIC,IAAIf;QACpB,OAAO;YACL,CAAC,iCAAiC,EAAEc,IAAIE,MAAM,CAAC,IAAI,CAAC;YACpD,CAAC,yCAAyC,EAAEF,IAAIG,QAAQ,GAAG,gBAAgB,CAAC;SAC7E,CAACf,IAAI,CAAC;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/workbench-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Internal implementation detail of the Sanity CLI's unstable workbench support. Not intended for direct use.",
|
|
5
5
|
"homepage": "https://github.com/sanity-io/cli",
|
|
6
6
|
"bugs": "https://github.com/sanity-io/cli/issues",
|
|
@@ -44,6 +44,31 @@
|
|
|
44
44
|
"publishConfig": {
|
|
45
45
|
"access": "public"
|
|
46
46
|
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@module-federation/vite": "1.16.11",
|
|
49
|
+
"@vitejs/plugin-react": "^6.0.3",
|
|
50
|
+
"vite": "^8.1.0",
|
|
51
|
+
"zod": "^4.4.3",
|
|
52
|
+
"@sanity/cli-core": "^2.1.1"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@eslint/compat": "^2.1.0",
|
|
56
|
+
"@sanity/pkg-utils": "^10.5.8",
|
|
57
|
+
"@swc/cli": "^0.8.1",
|
|
58
|
+
"@swc/core": "^1.15.41",
|
|
59
|
+
"@types/node": "^22.20.0",
|
|
60
|
+
"@vitest/coverage-istanbul": "^4.1.9",
|
|
61
|
+
"eslint": "^10.4.1",
|
|
62
|
+
"publint": "^0.3.21",
|
|
63
|
+
"typescript": "^5.9.3",
|
|
64
|
+
"vitest": "^4.1.9",
|
|
65
|
+
"@repo/package.config": "0.0.1",
|
|
66
|
+
"@repo/tsconfig": "3.70.0",
|
|
67
|
+
"@sanity/eslint-config-cli": "^1.1.2"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=22.12"
|
|
71
|
+
},
|
|
47
72
|
"scripts": {
|
|
48
73
|
"build": "swc --delete-dir-on-start --strip-leading-paths --out-dir dist/ src --ignore '**/*.test.ts' --ignore '**/__tests__/**'",
|
|
49
74
|
"build:types": "pkg-utils build --emitDeclarationOnly",
|
|
@@ -55,29 +80,5 @@
|
|
|
55
80
|
"test:coverage": "vitest run --coverage",
|
|
56
81
|
"test:watch": "vitest",
|
|
57
82
|
"watch": "swc --delete-dir-on-start --strip-leading-paths --out-dir dist/ --watch src"
|
|
58
|
-
},
|
|
59
|
-
"dependencies": {
|
|
60
|
-
"@module-federation/vite": "1.16.0",
|
|
61
|
-
"@sanity/cli-core": "workspace:^",
|
|
62
|
-
"vite": "catalog:",
|
|
63
|
-
"zod": "catalog:"
|
|
64
|
-
},
|
|
65
|
-
"devDependencies": {
|
|
66
|
-
"@eslint/compat": "catalog:",
|
|
67
|
-
"@repo/package.config": "workspace:*",
|
|
68
|
-
"@repo/tsconfig": "workspace:*",
|
|
69
|
-
"@sanity/eslint-config-cli": "workspace:^",
|
|
70
|
-
"@sanity/pkg-utils": "catalog:",
|
|
71
|
-
"@swc/cli": "catalog:",
|
|
72
|
-
"@swc/core": "catalog:",
|
|
73
|
-
"@types/node": "catalog:",
|
|
74
|
-
"@vitest/coverage-istanbul": "catalog:",
|
|
75
|
-
"eslint": "catalog:",
|
|
76
|
-
"publint": "catalog:",
|
|
77
|
-
"typescript": "catalog:",
|
|
78
|
-
"vitest": "catalog:"
|
|
79
|
-
},
|
|
80
|
-
"engines": {
|
|
81
|
-
"node": ">=22.12"
|
|
82
83
|
}
|
|
83
|
-
}
|
|
84
|
+
}
|