@bebopdev/cli 0.1.0-beta.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/LICENSE +21 -0
- package/README.md +26 -0
- package/bin/bebop.mjs +322 -0
- package/lib/config-dependencies.mjs +40 -0
- package/package.json +34 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Carlos Bensant
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# @bebopdev/cli
|
|
2
|
+
|
|
3
|
+
The `bebop` command generates Jazz artifacts from a Bebop config and runs a schema-watching development server.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add -D @bebopdev/cli
|
|
9
|
+
pnpm add @bebopdev/core jazz-tools
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The CLI currently requires Node.js 22.12 or newer and targets `jazz-tools@2.0.0-alpha.57`.
|
|
13
|
+
|
|
14
|
+
## Commands
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pnpm exec bebop generate
|
|
18
|
+
pnpm exec bebop validate
|
|
19
|
+
pnpm exec bebop dev -- vite
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
`generate` writes `bebop-generated-schema.ts`, `bebop-admin-manifest.ts`, `bebop-generated-client.ts`, Jazz's `schema.ts` entry point, and `permissions.ts` beside `bebop.config.ts`. `dev` watches the config and regenerates those files before restarting the supplied server command.
|
|
23
|
+
|
|
24
|
+
Pass `--config <path>` to select another config or `--out-dir <path>` to choose a generated-file directory. Run `pnpm exec bebop --help` for usage.
|
|
25
|
+
|
|
26
|
+
See the [repository README](https://github.com/reezerdev/bebop#readme) for a complete setup example.
|
package/bin/bebop.mjs
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { access, mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
|
+
import { tsImport } from "tsx/esm/api";
|
|
9
|
+
import { collectConfigDependencies } from "../lib/config-dependencies.mjs";
|
|
10
|
+
|
|
11
|
+
const usage = `Usage:
|
|
12
|
+
bebop generate [--config <path>] [--out-dir <path>]
|
|
13
|
+
bebop validate [--config <path>] [--out-dir <path>]
|
|
14
|
+
bebop dev [--config <path>] [--out-dir <path>] [-- <command> ...args]
|
|
15
|
+
|
|
16
|
+
Commands:
|
|
17
|
+
generate Generate Bebop schema, Jazz entry point, and permissions
|
|
18
|
+
validate Validate the generated schema with jazz-tools
|
|
19
|
+
dev Generate, watch Bebop and Better Auth config, and run the local dev server`;
|
|
20
|
+
|
|
21
|
+
function parseArgs(args) {
|
|
22
|
+
const [command, ...rest] = args;
|
|
23
|
+
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
24
|
+
return { help: true };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const options = { command, config: "bebop.config.ts", outDir: undefined, commandArgs: [] };
|
|
28
|
+
let index = 0;
|
|
29
|
+
|
|
30
|
+
while (index < rest.length) {
|
|
31
|
+
const value = rest[index];
|
|
32
|
+
if (value === "--") {
|
|
33
|
+
options.commandArgs = rest.slice(index + 1);
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
if (value === "--config" || value === "--out-dir") {
|
|
37
|
+
const optionValue = rest[index + 1];
|
|
38
|
+
if (!optionValue || optionValue.startsWith("--")) {
|
|
39
|
+
throw new Error(`Expected a path after ${value}.`);
|
|
40
|
+
}
|
|
41
|
+
if (value === "--config") options.config = optionValue;
|
|
42
|
+
else options.outDir = optionValue;
|
|
43
|
+
index += 2;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
throw new Error(`Unknown option: ${value}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!["generate", "validate", "dev"].includes(command)) {
|
|
50
|
+
throw new Error(`Unknown command: ${command}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return options;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resolveProjectPaths(options) {
|
|
57
|
+
const configPath = path.resolve(process.cwd(), options.config);
|
|
58
|
+
return {
|
|
59
|
+
configPath,
|
|
60
|
+
configDirectory: path.dirname(configPath),
|
|
61
|
+
outputDirectory: path.resolve(process.cwd(), options.outDir ?? path.dirname(configPath)),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function loadConfig(configPath) {
|
|
66
|
+
const configUrl = pathToFileURL(configPath);
|
|
67
|
+
configUrl.searchParams.set("bebop-reload", randomUUID());
|
|
68
|
+
const loaded = await tsImport(configUrl.href, { parentURL: import.meta.url });
|
|
69
|
+
const config = loaded.default ?? loaded.config;
|
|
70
|
+
if (!config || typeof config !== "object") {
|
|
71
|
+
throw new Error(`Expected ${configPath} to export a Bebop config as its default export.`);
|
|
72
|
+
}
|
|
73
|
+
return config;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function loadCompiler(configPath) {
|
|
77
|
+
const { compileArtifacts } = await tsImport("@bebopdev/core", {
|
|
78
|
+
parentURL: pathToFileURL(configPath).href,
|
|
79
|
+
});
|
|
80
|
+
return compileArtifacts;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function moduleSpecifier(fromDirectory, targetFile) {
|
|
84
|
+
let relativePath = path.relative(fromDirectory, targetFile).split(path.sep).join("/");
|
|
85
|
+
relativePath = relativePath.replace(/\.tsx$/, ".jsx").replace(/\.mts$/, ".mjs").replace(/\.cts$/, ".cjs").replace(/\.ts$/, ".js");
|
|
86
|
+
return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function writeIfChanged(filePath, content) {
|
|
90
|
+
try {
|
|
91
|
+
if (await readFile(filePath, "utf8") === content) return false;
|
|
92
|
+
} catch (error) {
|
|
93
|
+
if (error.code !== "ENOENT") throw error;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const temporaryPath = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
97
|
+
try {
|
|
98
|
+
await writeFile(temporaryPath, content, { encoding: "utf8", flag: "wx" });
|
|
99
|
+
await rename(temporaryPath, filePath);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
await rm(temporaryPath, { force: true });
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function generate(paths, { quiet = false } = {}) {
|
|
108
|
+
const config = await loadConfig(paths.configPath);
|
|
109
|
+
const compileArtifacts = await loadCompiler(paths.configPath);
|
|
110
|
+
|
|
111
|
+
// Compile outputs before writing so invalid config keeps the last valid
|
|
112
|
+
// generated schema and permissions available to the dev server.
|
|
113
|
+
const configModuleSpecifier = moduleSpecifier(paths.outputDirectory, paths.configPath);
|
|
114
|
+
const { schema, permissions, adminManifest, clientFactory } = compileArtifacts(config, configModuleSpecifier);
|
|
115
|
+
await mkdir(paths.outputDirectory, { recursive: true });
|
|
116
|
+
|
|
117
|
+
if (config.auth) await generateBetterAuthSchema(paths, config.auth);
|
|
118
|
+
|
|
119
|
+
const schemaFingerprintSource = config.auth
|
|
120
|
+
? `${schema}\n${await readFile(path.join(paths.outputDirectory, "schema-better-auth", "schema.ts"), "utf8")}`
|
|
121
|
+
: schema;
|
|
122
|
+
const schemaFingerprint = createHash("sha256").update(schemaFingerprintSource).digest("hex");
|
|
123
|
+
const jazzSchemaEntry =
|
|
124
|
+
`// Generated by Bebop as Jazz's conventional schema.ts entry point.\n` +
|
|
125
|
+
`// Bebop's generated schema is in bebop-generated-schema.ts.\n` +
|
|
126
|
+
`// Schema fingerprint: ${schemaFingerprint}\n` +
|
|
127
|
+
`export { app } from "./bebop-generated-schema.js";\n`;
|
|
128
|
+
|
|
129
|
+
const changed = await Promise.all([
|
|
130
|
+
writeIfChanged(path.join(paths.outputDirectory, "bebop-generated-schema.ts"), schema),
|
|
131
|
+
writeIfChanged(path.join(paths.outputDirectory, "bebop-admin-manifest.ts"), adminManifest),
|
|
132
|
+
writeIfChanged(path.join(paths.outputDirectory, "bebop-generated-client.ts"), clientFactory),
|
|
133
|
+
writeIfChanged(path.join(paths.outputDirectory, "schema.ts"), jazzSchemaEntry),
|
|
134
|
+
writeIfChanged(path.join(paths.outputDirectory, "permissions.ts"), permissions),
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
if (!quiet) {
|
|
138
|
+
const output = path.relative(process.cwd(), paths.outputDirectory) || ".";
|
|
139
|
+
const action = changed.some(Boolean) ? "Generated" : "Up to date";
|
|
140
|
+
console.log(`${action} bebop-generated-schema.ts, bebop-admin-manifest.ts, bebop-generated-client.ts, Jazz schema.ts entry point, and permissions.ts in ${output}`);
|
|
141
|
+
}
|
|
142
|
+
return config;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function generateBetterAuthSchema(paths, authConfig) {
|
|
146
|
+
const configFile = path.resolve(
|
|
147
|
+
paths.configDirectory,
|
|
148
|
+
authConfig.generateConfig ?? "auth-generate.ts",
|
|
149
|
+
);
|
|
150
|
+
try {
|
|
151
|
+
await access(configFile);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (error.code !== "ENOENT") throw error;
|
|
154
|
+
throw new Error(
|
|
155
|
+
`Better Auth is enabled, but ${path.relative(process.cwd(), configFile)} does not exist. ` +
|
|
156
|
+
"Add a Better Auth CLI config that uses jazzAdapter, then run bebop generate again.",
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const outputFile = path.join(paths.outputDirectory, "schema-better-auth", "schema.ts");
|
|
161
|
+
await mkdir(path.dirname(outputFile), { recursive: true });
|
|
162
|
+
|
|
163
|
+
let code;
|
|
164
|
+
try {
|
|
165
|
+
code = await run(
|
|
166
|
+
"auth",
|
|
167
|
+
["generate", "--config", configFile, "--output", outputFile, "--yes"],
|
|
168
|
+
{ cwd: paths.configDirectory },
|
|
169
|
+
);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
if (error.code === "ENOENT") {
|
|
172
|
+
throw new Error(
|
|
173
|
+
'Better Auth is enabled but its CLI is unavailable. Install matching dependencies with `pnpm add better-auth` and `pnpm add -D auth`, then run bebop generate again.',
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (code !== 0) {
|
|
180
|
+
throw new Error(`Better Auth schema generation failed with exit code ${code}.`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function run(command, args, { cwd = process.cwd() } = {}) {
|
|
185
|
+
return new Promise((resolve, reject) => {
|
|
186
|
+
const child = spawn(command, args, { cwd, stdio: "inherit" });
|
|
187
|
+
child.once("error", reject);
|
|
188
|
+
child.once("exit", (code, signal) => {
|
|
189
|
+
resolve(code ?? (signal === "SIGINT" ? 130 : signal === "SIGTERM" ? 143 : 1));
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async function validate(paths) {
|
|
195
|
+
await generate(paths);
|
|
196
|
+
const code = await run("jazz-tools", ["validate"], { cwd: paths.outputDirectory });
|
|
197
|
+
if (code !== 0) process.exitCode = code;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function dev(paths, commandArgs) {
|
|
201
|
+
const config = await generate(paths);
|
|
202
|
+
const command = commandArgs.length ? commandArgs[0] : "vite";
|
|
203
|
+
const args = commandArgs.length ? commandArgs.slice(1) : ["--host", "127.0.0.1"];
|
|
204
|
+
|
|
205
|
+
const configEntries = [paths.configPath];
|
|
206
|
+
if (config.auth) {
|
|
207
|
+
configEntries.push(
|
|
208
|
+
path.resolve(paths.configDirectory, config.auth.generateConfig ?? "auth-generate.ts"),
|
|
209
|
+
);
|
|
210
|
+
configEntries.push(path.resolve(paths.configDirectory, "auth-options.ts"));
|
|
211
|
+
}
|
|
212
|
+
const fingerprint = async (file) => {
|
|
213
|
+
try {
|
|
214
|
+
const info = await stat(file);
|
|
215
|
+
return `${info.mtimeMs}:${info.size}`;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
if (error.code === "ENOENT") return "missing";
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
let watchedFiles = new Map();
|
|
222
|
+
const syncWatchers = async () => {
|
|
223
|
+
const nextFiles = await collectConfigDependencies(configEntries);
|
|
224
|
+
watchedFiles = new Map(await Promise.all([...nextFiles].map(async (file) => [file, await fingerprint(file)])));
|
|
225
|
+
console.log(`Watching ${[...nextFiles].map((file) => path.relative(process.cwd(), file)).join(", ")} for schema changes`);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
let timer;
|
|
229
|
+
let generating = false;
|
|
230
|
+
let pendingGeneration = false;
|
|
231
|
+
const scheduleGeneration = () => {
|
|
232
|
+
clearTimeout(timer);
|
|
233
|
+
timer = setTimeout(async () => {
|
|
234
|
+
if (generating) {
|
|
235
|
+
pendingGeneration = true;
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
generating = true;
|
|
239
|
+
try {
|
|
240
|
+
// A fresh process reloads imported collection modules as well as the root config.
|
|
241
|
+
const code = await run(process.execPath, [fileURLToPath(import.meta.url), "generate", "--config", paths.configPath, "--out-dir", paths.outputDirectory]);
|
|
242
|
+
if (code !== 0) console.error(`Could not regenerate the schema (exit code ${code}).`);
|
|
243
|
+
await syncWatchers();
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.error(`Could not regenerate the schema: ${error.message}`);
|
|
246
|
+
} finally {
|
|
247
|
+
generating = false;
|
|
248
|
+
if (pendingGeneration) {
|
|
249
|
+
pendingGeneration = false;
|
|
250
|
+
scheduleGeneration();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}, 120);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
await syncWatchers();
|
|
257
|
+
let polling = false;
|
|
258
|
+
const poll = setInterval(() => {
|
|
259
|
+
if (polling) return;
|
|
260
|
+
polling = true;
|
|
261
|
+
void (async () => {
|
|
262
|
+
try {
|
|
263
|
+
for (const [file, previous] of watchedFiles) {
|
|
264
|
+
const current = await fingerprint(file);
|
|
265
|
+
if (current !== previous) {
|
|
266
|
+
watchedFiles.set(file, current);
|
|
267
|
+
scheduleGeneration();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.error(`Config watcher error: ${error.message}`);
|
|
272
|
+
} finally {
|
|
273
|
+
polling = false;
|
|
274
|
+
}
|
|
275
|
+
})();
|
|
276
|
+
}, 500);
|
|
277
|
+
const closeWatchers = () => clearInterval(poll);
|
|
278
|
+
|
|
279
|
+
const child = spawn(command, args, { cwd: process.cwd(), stdio: "inherit" });
|
|
280
|
+
let stopping = false;
|
|
281
|
+
const stopChild = (signal) => {
|
|
282
|
+
if (stopping) return;
|
|
283
|
+
stopping = true;
|
|
284
|
+
closeWatchers();
|
|
285
|
+
clearTimeout(timer);
|
|
286
|
+
child.kill(signal);
|
|
287
|
+
};
|
|
288
|
+
const onInterrupt = () => stopChild("SIGINT");
|
|
289
|
+
const onTerminate = () => stopChild("SIGTERM");
|
|
290
|
+
process.once("SIGINT", onInterrupt);
|
|
291
|
+
process.once("SIGTERM", onTerminate);
|
|
292
|
+
|
|
293
|
+
const code = await new Promise((resolve, reject) => {
|
|
294
|
+
child.once("error", reject);
|
|
295
|
+
child.once("exit", (exitCode, signal) => {
|
|
296
|
+
closeWatchers();
|
|
297
|
+
clearTimeout(timer);
|
|
298
|
+
resolve(exitCode ?? (signal === "SIGINT" ? 130 : signal === "SIGTERM" ? 143 : 1));
|
|
299
|
+
});
|
|
300
|
+
}).finally(() => {
|
|
301
|
+
closeWatchers();
|
|
302
|
+
clearTimeout(timer);
|
|
303
|
+
process.removeListener("SIGINT", onInterrupt);
|
|
304
|
+
process.removeListener("SIGTERM", onTerminate);
|
|
305
|
+
});
|
|
306
|
+
if (code !== 0) process.exitCode = code;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
const options = parseArgs(process.argv.slice(2));
|
|
311
|
+
if (options.help) {
|
|
312
|
+
console.log(usage);
|
|
313
|
+
} else {
|
|
314
|
+
const paths = resolveProjectPaths(options);
|
|
315
|
+
if (options.command === "generate") await generate(paths);
|
|
316
|
+
else if (options.command === "validate") await validate(paths);
|
|
317
|
+
else await dev(paths, options.commandArgs);
|
|
318
|
+
}
|
|
319
|
+
} catch (error) {
|
|
320
|
+
console.error(error instanceof Error ? error.message : error);
|
|
321
|
+
process.exitCode = 1;
|
|
322
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { access, readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const importPattern = /\bfrom\s*["'](\.[^"']+)["']|\bimport\s*["'](\.[^"']+)["']|\bimport\s*\(\s*["'](\.[^"']+)["']/g;
|
|
5
|
+
const sourceExtensions = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
|
|
6
|
+
|
|
7
|
+
async function resolveLocalImport(fromFile, specifier) {
|
|
8
|
+
const target = path.resolve(path.dirname(fromFile), specifier);
|
|
9
|
+
const extension = path.extname(target);
|
|
10
|
+
const candidates = extension
|
|
11
|
+
? [target, ...sourceExtensions.map((sourceExtension) => target.slice(0, -extension.length) + sourceExtension)]
|
|
12
|
+
: [...sourceExtensions.map((sourceExtension) => target + sourceExtension), ...sourceExtensions.map((sourceExtension) => path.join(target, `index${sourceExtension}`))];
|
|
13
|
+
for (const candidate of candidates) {
|
|
14
|
+
try {
|
|
15
|
+
await access(candidate);
|
|
16
|
+
return candidate;
|
|
17
|
+
} catch { /* Try the next source extension. */ }
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Follow local static imports so a config split into collection modules regenerates on edit. */
|
|
23
|
+
export async function collectConfigDependencies(entryFiles) {
|
|
24
|
+
const visited = new Set();
|
|
25
|
+
const pending = [...entryFiles];
|
|
26
|
+
while (pending.length) {
|
|
27
|
+
const file = pending.pop();
|
|
28
|
+
if (!file || visited.has(file)) continue;
|
|
29
|
+
visited.add(file);
|
|
30
|
+
let source;
|
|
31
|
+
try {
|
|
32
|
+
source = await readFile(file, "utf8");
|
|
33
|
+
} catch { continue; }
|
|
34
|
+
for (const match of source.matchAll(importPattern)) {
|
|
35
|
+
const dependency = await resolveLocalImport(file, match[1] ?? match[2] ?? match[3]);
|
|
36
|
+
if (dependency && !visited.has(dependency)) pending.push(dependency);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return visited;
|
|
40
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bebopdev/cli",
|
|
3
|
+
"version": "0.1.0-beta.0",
|
|
4
|
+
"description": "Generate Jazz schemas from Bebop collections and run the Bebop development server.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/reezerdev/bebop.git",
|
|
9
|
+
"directory": "packages/cli"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/reezerdev/bebop#readme",
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"bin",
|
|
17
|
+
"lib",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"type": "module",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=22.12.0"
|
|
23
|
+
},
|
|
24
|
+
"bin": {
|
|
25
|
+
"bebop": "./bin/bebop.mjs"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@bebopdev/core": "0.1.0-beta.0",
|
|
29
|
+
"tsx": "^4.23.15"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"test": "node --test test/*.test.mjs"
|
|
33
|
+
}
|
|
34
|
+
}
|