specli 0.0.17 → 0.0.19

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.
@@ -1,4 +1,11 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
1
5
  import { deriveBinaryName } from "./derive-name.js";
6
+ // Resolve the package root directory (at runtime this file is at dist/cli/compile.js)
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+ const packageRoot = path.resolve(__dirname, "../..");
2
9
  function parseKeyValue(input) {
3
10
  const idx = input.indexOf("=");
4
11
  if (idx === -1)
@@ -9,6 +16,72 @@ function parseKeyValue(input) {
9
16
  throw new Error(`Invalid --define '${input}', missing key`);
10
17
  return { key, value };
11
18
  }
19
+ /**
20
+ * Loads the OpenAPI spec from a URL or file path.
21
+ */
22
+ async function loadSpec(spec) {
23
+ if (!spec)
24
+ throw new Error("Missing spec path/URL");
25
+ if (/^https?:\/\//i.test(spec)) {
26
+ const res = await fetch(spec);
27
+ if (!res.ok) {
28
+ throw new Error(`Failed to fetch spec: ${res.status} ${res.statusText}`);
29
+ }
30
+ return await res.text();
31
+ }
32
+ return await fs.promises.readFile(spec, "utf-8");
33
+ }
34
+ /**
35
+ * Reads the package version from package.json.
36
+ */
37
+ function getPackageVersion() {
38
+ const packageJsonPath = path.join(packageRoot, "package.json");
39
+ const content = fs.readFileSync(packageJsonPath, "utf-8");
40
+ const packageJson = JSON.parse(content);
41
+ return packageJson.version;
42
+ }
43
+ /**
44
+ * Generates a temporary entrypoint file with all values hardcoded.
45
+ * This avoids the Bun macro security restriction in node_modules.
46
+ */
47
+ function generateEntrypoint(options) {
48
+ const mainImportPath = path.join(packageRoot, "dist/cli/main.js");
49
+ // Escape the spec text for embedding as a string literal
50
+ const escapedSpec = JSON.stringify(options.specText);
51
+ const escapedName = options.cliName
52
+ ? JSON.stringify(options.cliName)
53
+ : "undefined";
54
+ const escapedServer = options.server
55
+ ? JSON.stringify(options.server)
56
+ : "undefined";
57
+ const escapedServerVars = options.serverVars
58
+ ? JSON.stringify(options.serverVars)
59
+ : "undefined";
60
+ const escapedAuth = options.auth ? JSON.stringify(options.auth) : "undefined";
61
+ const escapedVersion = JSON.stringify(options.version);
62
+ return `#!/usr/bin/env bun
63
+ // Auto-generated entrypoint for specli compile
64
+ // This file embeds all configuration at build time
65
+
66
+ import { main } from ${JSON.stringify(mainImportPath)};
67
+
68
+ const embeddedSpecText = ${escapedSpec};
69
+ const cliName = ${escapedName};
70
+ const server = ${escapedServer};
71
+ const serverVars = ${escapedServerVars};
72
+ const auth = ${escapedAuth};
73
+ const embeddedVersion = ${escapedVersion};
74
+
75
+ await main(process.argv, {
76
+ embeddedSpecText,
77
+ cliName,
78
+ server,
79
+ serverVars: serverVars ? serverVars.split(",") : undefined,
80
+ auth,
81
+ version: embeddedVersion,
82
+ });
83
+ `;
84
+ }
12
85
  export async function compileCommand(spec, options) {
13
86
  // Derive name from spec if not provided
14
87
  const name = options.name ?? (await deriveBinaryName(spec));
@@ -16,64 +89,74 @@ export async function compileCommand(spec, options) {
16
89
  const target = options.target
17
90
  ? options.target
18
91
  : `bun-${process.platform}-${process.arch}`;
19
- // Parse --define pairs
20
- const define = {};
21
- if (options.define) {
22
- for (const pair of options.define) {
23
- const { key, value } = parseKeyValue(pair);
24
- define[key] = JSON.stringify(value);
92
+ // Load the spec content
93
+ process.stdout.write(`Loading spec: ${spec}\n`);
94
+ const specText = await loadSpec(spec);
95
+ // Get package version
96
+ const version = getPackageVersion();
97
+ // Generate temporary entrypoint file
98
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "specli-"));
99
+ const tempEntrypoint = path.join(tempDir, "entrypoint.ts");
100
+ const entrypointCode = generateEntrypoint({
101
+ specText,
102
+ cliName: name,
103
+ server: options.server,
104
+ serverVars: options.serverVar?.join(","),
105
+ auth: options.auth,
106
+ version,
107
+ });
108
+ fs.writeFileSync(tempEntrypoint, entrypointCode);
109
+ try {
110
+ // Parse --define pairs
111
+ const define = {};
112
+ if (options.define) {
113
+ for (const pair of options.define) {
114
+ const { key, value } = parseKeyValue(pair);
115
+ define[key] = JSON.stringify(value);
116
+ }
25
117
  }
118
+ // Build command args
119
+ const buildArgs = [
120
+ "build",
121
+ "--compile",
122
+ `--outfile=${outfile}`,
123
+ `--target=${target}`,
124
+ ];
125
+ if (options.minify)
126
+ buildArgs.push("--minify");
127
+ if (options.bytecode)
128
+ buildArgs.push("--bytecode");
129
+ for (const [k, v] of Object.entries(define)) {
130
+ buildArgs.push("--define", `${k}=${v}`);
131
+ }
132
+ if (options.dotenv === false)
133
+ buildArgs.push("--no-compile-autoload-dotenv");
134
+ if (options.bunfig === false)
135
+ buildArgs.push("--no-compile-autoload-bunfig");
136
+ buildArgs.push(tempEntrypoint);
137
+ const proc = Bun.spawn({
138
+ cmd: ["bun", ...buildArgs],
139
+ stdout: "pipe",
140
+ stderr: "pipe",
141
+ env: process.env,
142
+ });
143
+ const output = await new Response(proc.stdout).text();
144
+ const error = await new Response(proc.stderr).text();
145
+ const code = await proc.exited;
146
+ if (output)
147
+ process.stdout.write(output);
148
+ if (error)
149
+ process.stderr.write(error);
150
+ if (code !== 0) {
151
+ process.exitCode = code;
152
+ return;
153
+ }
154
+ process.stdout.write(`ok: built ${outfile}\n`);
155
+ process.stdout.write(`target: ${target}\n`);
156
+ process.stdout.write(`name: ${name}\n`);
26
157
  }
27
- // Build command args
28
- const buildArgs = [
29
- "build",
30
- "--compile",
31
- `--outfile=${outfile}`,
32
- `--target=${target}`,
33
- ];
34
- if (options.minify)
35
- buildArgs.push("--minify");
36
- if (options.bytecode)
37
- buildArgs.push("--bytecode");
38
- for (const [k, v] of Object.entries(define)) {
39
- buildArgs.push("--define", `${k}=${v}`);
40
- }
41
- if (options.dotenv === false)
42
- buildArgs.push("--no-compile-autoload-dotenv");
43
- if (options.bunfig === false)
44
- buildArgs.push("--no-compile-autoload-bunfig");
45
- buildArgs.push("./src/compiled.ts");
46
- // Only set env vars that have actual values - avoid empty strings
47
- // because the macros will embed them and they will override defaults.
48
- const buildEnv = {
49
- ...process.env,
50
- SPECLI_SPEC: spec,
51
- SPECLI_NAME: name,
52
- };
53
- if (options.server)
54
- buildEnv.SPECLI_SERVER = options.server;
55
- if (options.serverVar?.length)
56
- buildEnv.SPECLI_SERVER_VARS = options.serverVar.join(",");
57
- if (options.auth)
58
- buildEnv.SPECLI_AUTH = options.auth;
59
- const proc = Bun.spawn({
60
- cmd: ["bun", ...buildArgs],
61
- stdout: "pipe",
62
- stderr: "pipe",
63
- env: buildEnv,
64
- });
65
- const output = await new Response(proc.stdout).text();
66
- const error = await new Response(proc.stderr).text();
67
- const code = await proc.exited;
68
- if (output)
69
- process.stdout.write(output);
70
- if (error)
71
- process.stderr.write(error);
72
- if (code !== 0) {
73
- process.exitCode = code;
74
- return;
158
+ finally {
159
+ // Clean up temporary files
160
+ fs.rmSync(tempDir, { recursive: true, force: true });
75
161
  }
76
- process.stdout.write(`ok: built ${outfile}\n`);
77
- process.stdout.write(`target: ${target}\n`);
78
- process.stdout.write(`name: ${name}\n`);
79
162
  }
package/dist/cli.d.ts CHANGED
@@ -1,2 +1 @@
1
- #!/usr/bin/env node
2
1
  export {};
package/dist/cli.js CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  import { readFileSync } from "node:fs";
3
2
  import { dirname, join } from "node:path";
4
3
  import { fileURLToPath } from "node:url";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specli",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "type": "module",
5
5
  "module": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",