@notionhq/custom-blocks-dev-shell 0.1.33 → 0.1.35

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-cli/main.js DELETED
@@ -1,271 +0,0 @@
1
- /**
2
- * Entry point for the published dev shell CLI (`npx`-run from a worker
3
- * project). Mirrors the repo's `scripts/dev.ts`: build the
4
- * worker, extract its manifest, serve one Vite dev server per custom block
5
- * (using the worker's own Vite install), and serve the prebuilt dev shell UI
6
- * with the block registry injected at runtime.
7
- */
8
- import { spawn } from "node:child_process";
9
- import { existsSync, readFileSync } from "node:fs";
10
- import { createRequire } from "node:module";
11
- import { basename, dirname, join, resolve } from "node:path";
12
- import { createInterface } from "node:readline";
13
- import { fileURLToPath } from "node:url";
14
- import { BLOCK_BASE_PORT, buildBlockRegistry, makeChangeLogger, SHELL_PORT, writeBlockViteConfig, } from "./block-server.js";
15
- import { runConvert } from "./convert.js";
16
- import { readDataSources } from "./data-sources.js";
17
- import { materializeWorkerSchemaDataSources } from "./materialize.js";
18
- import { copyPrebuiltDataSources } from "./prebuilt.js";
19
- import { serveUi } from "./serve-ui.js";
20
- import { blockCapabilities, findWorkerDir, generateWorkerManifest, } from "./worker-manifest.js";
21
- const __dirname = dirname(fileURLToPath(import.meta.url));
22
- const dim = "\x1b[2m";
23
- const bold = "\x1b[1m";
24
- const cyan = "\x1b[36m";
25
- const reset = "\x1b[0m";
26
- const label = (name) => `${cyan}[${name}]${reset}`;
27
- function parseCliArgs(argv) {
28
- const args = {
29
- worker: undefined,
30
- shellPort: SHELL_PORT,
31
- blockBasePort: BLOCK_BASE_PORT,
32
- };
33
- const takeValue = (name, index) => {
34
- const value = argv[index];
35
- if (value === undefined || value.startsWith("--")) {
36
- throw new Error(`${name} requires a value.`);
37
- }
38
- return value;
39
- };
40
- const takePort = (name, raw) => {
41
- const port = Number(raw);
42
- if (!Number.isInteger(port) || port <= 0 || port > 65535) {
43
- throw new Error(`${name} requires a port number, got "${raw}".`);
44
- }
45
- return port;
46
- };
47
- for (let index = 0; index < argv.length; index++) {
48
- const arg = argv[index];
49
- if (arg === "--worker") {
50
- args.worker = takeValue("--worker", ++index);
51
- }
52
- else if (arg.startsWith("--worker=")) {
53
- args.worker = arg.slice("--worker=".length);
54
- if (args.worker.length === 0) {
55
- throw new Error("--worker requires a path to a worker directory.");
56
- }
57
- }
58
- else if (arg === "--port") {
59
- args.shellPort = takePort("--port", takeValue("--port", ++index));
60
- }
61
- else if (arg.startsWith("--port=")) {
62
- args.shellPort = takePort("--port", arg.slice("--port=".length));
63
- }
64
- else if (arg === "--block-base-port") {
65
- args.blockBasePort = takePort("--block-base-port", takeValue("--block-base-port", ++index));
66
- }
67
- else if (arg.startsWith("--block-base-port=")) {
68
- args.blockBasePort = takePort("--block-base-port", arg.slice("--block-base-port=".length));
69
- }
70
- }
71
- return args;
72
- }
73
- function resolveWorkerDir(workerArg) {
74
- if (workerArg !== undefined) {
75
- return resolve(process.cwd(), workerArg);
76
- }
77
- const detected = findWorkerDir(process.cwd());
78
- if (detected !== undefined) {
79
- console.log(`Detected a worker at ${detected}.`);
80
- return detected;
81
- }
82
- throw new Error("No worker found: run from inside a worker directory, or pass --worker <dir>.");
83
- }
84
- /**
85
- * The worker's own Vite binary. Blocks are served with the worker's Vite (and
86
- * plugins) rather than anything bundled here, matching how the block builds in
87
- * production.
88
- */
89
- function resolveViteBin(workerDir) {
90
- let vitePkgPath;
91
- try {
92
- const workerRequire = createRequire(join(workerDir, "package.json"));
93
- vitePkgPath = workerRequire.resolve("vite/package.json");
94
- }
95
- catch {
96
- throw new Error(`Could not resolve "vite" from ${workerDir}. Add vite to the worker's ` +
97
- `devDependencies and reinstall.`);
98
- }
99
- const vitePkg = JSON.parse(readFileSync(vitePkgPath, "utf-8"));
100
- const bin = typeof vitePkg.bin === "string" ? vitePkg.bin : vitePkg.bin?.vite;
101
- if (bin === undefined) {
102
- throw new Error(`The vite package at ${vitePkgPath} exposes no bin.`);
103
- }
104
- return resolve(dirname(vitePkgPath), bin);
105
- }
106
- const procs = [];
107
- async function main() {
108
- const argv = process.argv.slice(2);
109
- // One-shot subcommands return before any dev-server machinery starts;
110
- // with no subcommand the CLI is the dev shell, as it always was.
111
- if (argv[0] === "convert") {
112
- await runConvert(argv.slice(1));
113
- return;
114
- }
115
- const cliArgs = parseCliArgs(argv);
116
- const workerDir = resolveWorkerDir(cliArgs.worker);
117
- if (!existsSync(resolve(workerDir, "node_modules"))) {
118
- throw new Error(`No node_modules in ${workerDir}. Install the worker's dependencies first ` +
119
- `(e.g. \`npm install\`), then rerun.`);
120
- }
121
- console.log(`${label(basename(workerDir))} Extracting worker manifest...`);
122
- const { manifest, manifestPath } = await generateWorkerManifest(workerDir);
123
- console.log(`${label(basename(workerDir))} Wrote ${manifestPath}`);
124
- const blocks = blockCapabilities(manifest);
125
- if (blocks.length === 0) {
126
- // Not an error — start the shell anyway; it shows "None" under Blocks.
127
- console.log(`${label(basename(workerDir))} Worker declares no custom blocks.`);
128
- }
129
- // Materialize schema-only files for the worker's declared sources, then
130
- // load the directory. Files are validated before injection; a malformed
131
- // file fails spin-up with the problem named.
132
- const dataDir = resolve(workerDir, "src/data");
133
- materializeWorkerSchemaDataSources(manifest, dataDir);
134
- const dataSources = readDataSources(dataDir);
135
- console.log(dataSources.length > 0
136
- ? `${label(basename(workerDir))} Data sources from src/data: ${dataSources
137
- .map(source => source.name)
138
- .join(", ")}`
139
- : `${label(basename(workerDir))} No data sources — create src/data/<key>.json files ` +
140
- `(format: node_modules/@notionhq/custom-blocks-dev-shell/docs/data-sources.md).`);
141
- if (blocks.length > 0 &&
142
- cliArgs.shellPort >= cliArgs.blockBasePort &&
143
- cliArgs.shellPort < cliArgs.blockBasePort + blocks.length) {
144
- throw new Error(`--port ${cliArgs.shellPort} collides with the block server ports ` +
145
- `(${cliArgs.blockBasePort}–${cliArgs.blockBasePort + blocks.length - 1}); ` +
146
- `pick a port outside that range or move --block-base-port.`);
147
- }
148
- const viteBin = blocks.length > 0 ? resolveViteBin(workerDir) : undefined;
149
- const registry = buildBlockRegistry(blocks, cliArgs.blockBasePort);
150
- for (const [index, capability] of blocks.entries()) {
151
- const blockDir = resolve(workerDir, capability.config.source.path);
152
- const configFile = writeBlockViteConfig(workerDir, blockDir, capability);
153
- const port = cliArgs.blockBasePort + index;
154
- const proc = spawn(process.execPath, [
155
- viteBin,
156
- "--config",
157
- configFile,
158
- "--port",
159
- String(port),
160
- "--strictPort",
161
- ], {
162
- cwd: workerDir,
163
- stdio: ["ignore", "pipe", "inherit"],
164
- // Process groups (and negative-PID kills) are POSIX-only; on
165
- // Windows children are killed individually in shutdown().
166
- detached: process.platform !== "win32",
167
- });
168
- if (proc.stdout !== null) {
169
- createInterface({ input: proc.stdout }).on("line", makeChangeLogger(message => console.log(`${label(capability.key)} ${message}`)));
170
- }
171
- proc.on("exit", code => {
172
- if (shuttingDown || code === 0 || code === null) {
173
- return;
174
- }
175
- console.error(`${label(capability.key)} dev server exited with code ${code}`);
176
- process.exitCode = code;
177
- shutdown("SIGTERM");
178
- });
179
- procs.push(proc);
180
- }
181
- // The published layout is dist/ next to cli/; index.html must be prebuilt.
182
- const distDir = resolve(__dirname, "..", "dist");
183
- if (!existsSync(join(distDir, "index.html"))) {
184
- throw new Error(`No prebuilt UI found at ${distDir}. This package was not assembled ` +
185
- `correctly; reinstall it.`);
186
- }
187
- const prebuiltDir = resolve(__dirname, "..", "data");
188
- try {
189
- await serveUi(distDir, cliArgs.shellPort, {
190
- blocks: registry,
191
- dataSources,
192
- prebuiltFilenames: readDataSources(prebuiltDir).map(source => source.filename),
193
- }, () => {
194
- const written = copyPrebuiltDataSources(prebuiltDir, dataDir);
195
- if (written.length > 0) {
196
- console.log(`${label(basename(workerDir))} Added pre-built data sources: ` +
197
- written.join(", "));
198
- }
199
- return readDataSources(dataDir);
200
- });
201
- }
202
- catch (error) {
203
- const code = error.code;
204
- if (code === "EADDRINUSE") {
205
- throw new Error(`Port ${cliArgs.shellPort} is already in use. Stop whatever holds it ` +
206
- `or rerun with --port <port> (and --block-base-port <port> for the ` +
207
- `block servers).`);
208
- }
209
- throw error;
210
- }
211
- console.log("");
212
- console.log(`${bold}Dev shell${reset}`);
213
- console.log(` ${label("dev-shell")} ${dim}http://localhost:${cliArgs.shellPort}${reset}`);
214
- if (blocks.length > 0) {
215
- console.log("");
216
- console.log(`${bold}Blocks${reset}`);
217
- for (const [index, entry] of registry.entries()) {
218
- console.log(` ${label(entry.key)} ${dim}http://localhost:${cliArgs.blockBasePort + index}${reset}`);
219
- }
220
- }
221
- console.log("");
222
- }
223
- function killProc(p, signal) {
224
- if (p.pid === undefined || p.killed) {
225
- return;
226
- }
227
- try {
228
- if (process.platform === "win32") {
229
- p.kill(signal);
230
- }
231
- else {
232
- process.kill(-p.pid, signal);
233
- }
234
- }
235
- catch { }
236
- }
237
- let shuttingDown = false;
238
- function shutdown(signal) {
239
- if (shuttingDown) {
240
- return;
241
- }
242
- shuttingDown = true;
243
- if (signal !== "exit" && procs.length > 0) {
244
- console.log(`\n${dim}Shutting down...${reset}`);
245
- }
246
- for (const p of procs) {
247
- killProc(p, "SIGTERM");
248
- }
249
- setTimeout(() => {
250
- for (const p of procs) {
251
- killProc(p, "SIGKILL");
252
- }
253
- // Preserve a failure exit code set before shutdown (startup errors,
254
- // crashed block servers); plain signal shutdowns still exit 0.
255
- process.exit(process.exitCode ?? 0);
256
- }, 1500).unref();
257
- }
258
- for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
259
- process.on(signal, () => shutdown(signal));
260
- }
261
- process.on("exit", () => shutdown("exit"));
262
- process.on("uncaughtException", err => {
263
- console.error(err);
264
- process.exitCode = 1;
265
- shutdown("SIGTERM");
266
- });
267
- main().catch(err => {
268
- console.error(err instanceof Error ? err.message : err);
269
- process.exitCode = 1;
270
- shutdown("SIGTERM");
271
- });