@sigx/cli 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +125 -124
- package/dist/cli.js.map +1 -1
- package/dist/commands/create.js +35 -34
- package/dist/commands/create.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/plugin.js +2 -1
- package/dist/plugin.js.map +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { DefinitionError, a, command, runMain } from "@sigx/args";
|
|
3
3
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
@@ -104,103 +104,97 @@ function getVersion(cmd) {
|
|
|
104
104
|
return null;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
-
var infoCommand =
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
console.log(`
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
107
|
+
var infoCommand = command("info").describe("Print environment and project info").run(() => {
|
|
108
|
+
const cwd = process.cwd();
|
|
109
|
+
console.log("\n \x1B[1msigx environment info\x1B[0m\n");
|
|
110
|
+
console.log(` Platform: ${process.platform} ${process.arch}`);
|
|
111
|
+
console.log(` Node: ${process.version}`);
|
|
112
|
+
const pnpmVer = getVersion("pnpm --version");
|
|
113
|
+
if (pnpmVer) console.log(` pnpm: v${pnpmVer}`);
|
|
114
|
+
else {
|
|
115
|
+
const npmVer = getVersion("npm --version");
|
|
116
|
+
if (npmVer) console.log(` npm: v${npmVer}`);
|
|
117
|
+
}
|
|
118
|
+
const pkgPath = join(cwd, "package.json");
|
|
119
|
+
if (existsSync(pkgPath)) try {
|
|
120
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
121
|
+
console.log(` Project: ${pkg.name || "(unnamed)"} v${pkg.version || "0.0.0"}`);
|
|
122
|
+
const allDeps = {
|
|
123
|
+
...pkg.dependencies,
|
|
124
|
+
...pkg.devDependencies
|
|
125
|
+
};
|
|
126
|
+
const sigxPkgs = Object.entries(allDeps).filter(([name]) => name.startsWith("@sigx/") || name === "sigx").map(([name, version]) => `${name}@${version}`);
|
|
127
|
+
if (sigxPkgs.length > 0) {
|
|
128
|
+
console.log(` Sigx packages:`);
|
|
129
|
+
for (const p of sigxPkgs) console.log(` - ${p}`);
|
|
122
130
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
label: "Lynx (rspeedy)"
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
file: "lynx.config.js",
|
|
153
|
-
label: "Lynx (rspeedy)"
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
file: "ssg.config.ts",
|
|
157
|
-
label: "SSG"
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
file: "vite.config.ts",
|
|
161
|
-
label: "Vite"
|
|
162
|
-
}
|
|
163
|
-
].filter((c) => existsSync(join(cwd, c.file)));
|
|
164
|
-
if (detected.length > 0) {
|
|
165
|
-
console.log(` Config files:`);
|
|
166
|
-
for (const c of detected) console.log(` - ${c.file} (${c.label})`);
|
|
131
|
+
} catch {}
|
|
132
|
+
else console.log(" Project: (no package.json found)");
|
|
133
|
+
const detected = [
|
|
134
|
+
{
|
|
135
|
+
file: "sigx.lynx.config.ts",
|
|
136
|
+
label: "Lynx"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
file: "sigx.lynx.config.js",
|
|
140
|
+
label: "Lynx"
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
file: "lynx.config.ts",
|
|
144
|
+
label: "Lynx (rspeedy)"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
file: "lynx.config.js",
|
|
148
|
+
label: "Lynx (rspeedy)"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
file: "ssg.config.ts",
|
|
152
|
+
label: "SSG"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
file: "vite.config.ts",
|
|
156
|
+
label: "Vite"
|
|
167
157
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (
|
|
198
|
-
const podVer = getVersion("pod --version");
|
|
199
|
-
if (podVer) console.log(` CocoaPods: v${podVer}`);
|
|
158
|
+
].filter((c) => existsSync(join(cwd, c.file)));
|
|
159
|
+
if (detected.length > 0) {
|
|
160
|
+
console.log(` Config files:`);
|
|
161
|
+
for (const c of detected) console.log(` - ${c.file} (${c.label})`);
|
|
162
|
+
}
|
|
163
|
+
if (detected.some((c) => c.label.includes("Lynx"))) {
|
|
164
|
+
console.log("");
|
|
165
|
+
console.log(" \x1B[1mLynx Environment\x1B[0m");
|
|
166
|
+
const rspeedyVer = getVersion("npx rspeedy --version 2>&1");
|
|
167
|
+
if (rspeedyVer) console.log(` rspeedy: v${rspeedyVer.trim()}`);
|
|
168
|
+
const androidHome = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;
|
|
169
|
+
if (androidHome) console.log(` Android SDK: ${androidHome}`);
|
|
170
|
+
const javaVer = getVersion("java -version 2>&1");
|
|
171
|
+
if (javaVer) {
|
|
172
|
+
const match = javaVer.match(/version "([^"]+)"/);
|
|
173
|
+
if (match) console.log(` JDK: ${match[1]}`);
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
const adbVer = getVersion("adb version");
|
|
177
|
+
if (adbVer) {
|
|
178
|
+
const verMatch = adbVer.match(/version ([\d.]+)/);
|
|
179
|
+
console.log(` ADB: ${verMatch ? `v${verMatch[1]}` : "available"}`);
|
|
180
|
+
const devices = (0, import___vite_browser_external.execSync)("adb devices -l", {
|
|
181
|
+
stdio: "pipe",
|
|
182
|
+
encoding: "utf-8"
|
|
183
|
+
}).split("\n").slice(1).filter((l) => l.trim().length > 0).map((l) => {
|
|
184
|
+
const modelMatch = l.match(/model:(\S+)/);
|
|
185
|
+
return modelMatch ? modelMatch[1].replace(/_/g, " ") : l.split(/\s+/)[0];
|
|
186
|
+
});
|
|
187
|
+
if (devices.length > 0) console.log(` Devices: ${devices.join(", ")}`);
|
|
200
188
|
}
|
|
189
|
+
} catch {}
|
|
190
|
+
if (process.platform === "darwin") {
|
|
191
|
+
const xcodeVer = getVersion("xcodebuild -version 2>/dev/null");
|
|
192
|
+
if (xcodeVer) console.log(` Xcode: ${xcodeVer.split("\n")[0]}`);
|
|
193
|
+
const podVer = getVersion("pod --version");
|
|
194
|
+
if (podVer) console.log(` CocoaPods: v${podVer}`);
|
|
201
195
|
}
|
|
202
|
-
console.log("");
|
|
203
196
|
}
|
|
197
|
+
console.log("");
|
|
204
198
|
});
|
|
205
199
|
//#endregion
|
|
206
200
|
//#region src/utils/logger.ts
|
|
@@ -230,19 +224,15 @@ function createLogger(prefix = "sigx") {
|
|
|
230
224
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
231
225
|
var pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
232
226
|
var logger = createLogger();
|
|
233
|
-
function wrapPluginCommand(cmd, plugins) {
|
|
234
|
-
return
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
plugins,
|
|
243
|
-
cliVersion: pkg.version
|
|
244
|
-
});
|
|
245
|
-
}
|
|
227
|
+
function wrapPluginCommand(name, cmd, plugins) {
|
|
228
|
+
return command(name).describe(cmd.description).args(cmd.args ?? {}).run(async ({ args }) => {
|
|
229
|
+
await cmd.run({
|
|
230
|
+
cwd: process.cwd(),
|
|
231
|
+
args,
|
|
232
|
+
logger,
|
|
233
|
+
plugins,
|
|
234
|
+
cliVersion: pkg.version
|
|
235
|
+
});
|
|
246
236
|
});
|
|
247
237
|
}
|
|
248
238
|
async function main() {
|
|
@@ -251,28 +241,39 @@ async function main() {
|
|
|
251
241
|
logger
|
|
252
242
|
});
|
|
253
243
|
const subCommands = { info: infoCommand };
|
|
254
|
-
subCommands.create =
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
244
|
+
subCommands.create = command("create").describe("Scaffold a new SignalX project").args({
|
|
245
|
+
name: a.positional().describe("Project name"),
|
|
246
|
+
type: a.enum([
|
|
247
|
+
"basic",
|
|
248
|
+
"ssr",
|
|
249
|
+
"ssg",
|
|
250
|
+
"lynx"
|
|
251
|
+
]).describe("Project type"),
|
|
252
|
+
styling: a.enum([
|
|
253
|
+
"none",
|
|
254
|
+
"tailwind",
|
|
255
|
+
"daisyui"
|
|
256
|
+
]).describe("Styling setup"),
|
|
257
|
+
yes: a.boolean().alias("y").describe("Skip prompts (headless mode)")
|
|
258
|
+
}).run(async ({ args }) => {
|
|
259
|
+
const { runCreate } = await import("./commands/create.js");
|
|
260
|
+
await runCreate(args);
|
|
263
261
|
});
|
|
264
262
|
for (const plugin of plugins) for (const [name, cmd] of Object.entries(plugin.commands)) {
|
|
263
|
+
let wrapped;
|
|
264
|
+
try {
|
|
265
|
+
wrapped = wrapPluginCommand(name, cmd, plugins);
|
|
266
|
+
} catch (err) {
|
|
267
|
+
if (err instanceof DefinitionError) {
|
|
268
|
+
logger.warn(`Plugin "${plugin.name}" command "${name}" has an invalid args schema: ${err.message}`);
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
throw err;
|
|
272
|
+
}
|
|
265
273
|
if (subCommands[name]) logger.warn(`Plugin "${plugin.name}" overrides command "${name}"`);
|
|
266
|
-
subCommands[name] =
|
|
274
|
+
subCommands[name] = wrapped;
|
|
267
275
|
}
|
|
268
|
-
await runMain(
|
|
269
|
-
meta: {
|
|
270
|
-
name: "sigx",
|
|
271
|
-
version: pkg.version,
|
|
272
|
-
description: "SignalX CLI"
|
|
273
|
-
},
|
|
274
|
-
subCommands
|
|
275
|
-
}));
|
|
276
|
+
await runMain(command("sigx").version(pkg.version).describe("SignalX CLI").subcommands(subCommands));
|
|
276
277
|
}
|
|
277
278
|
main().catch((err) => {
|
|
278
279
|
logger.error(err.message || String(err));
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/discover.ts","../__vite-browser-external","../src/commands/info.ts","../src/utils/logger.ts","../src/cli.ts"],"sourcesContent":["/**\n * Auto-discover sigx CLI plugins from the project's dependencies.\n *\n * Scans the project's package.json for dependencies that declare\n * a `\"sigx-cli\"` manifest field:\n *\n * \"sigx-cli\": {\n * \"plugin\": \"./dist/plugin.js\",\n * \"requires\": \">=0.3.0\" // optional: CLI version range\n * }\n *\n * Loads each plugin and calls `detect(cwd)` to check if it applies.\n * When `requires` is declared and the RUNNING CLI binary doesn't satisfy\n * it, the plugin still loads (best effort) but a prominent warning tells\n * the user exactly how to update — peerDependencies can't cover this,\n * because the binary that dispatches commands isn't necessarily the\n * install the peer range was resolved against.\n */\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { SigxPlugin, Logger } from './plugin.js';\n\ninterface DepPackageJson {\n 'sigx-cli'?: {\n plugin: string;\n /** Semver range of @sigx/cli this plugin needs: '^x.y.z', '>=x.y.z', or exact. */\n requires?: string;\n };\n}\n\ninterface ParsedVersion { major: number; minor: number; patch: number }\n\nfunction parseVersion(v: string): ParsedVersion | null {\n const m = v.trim().match(/^(\\d+)\\.(\\d+)\\.(\\d+)/);\n if (!m) return null;\n return { major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3]) };\n}\n\nfunction compare(a: ParsedVersion, b: ParsedVersion): number {\n return a.major - b.major || a.minor - b.minor || a.patch - b.patch;\n}\n\n/**\n * Minimal semver-range check covering the forms plugins actually use:\n * `^x.y.z` (0.x caret = same minor, like npm), `>=x.y.z`, and exact.\n * Unknown/garbled ranges return true — never block on a malformed field.\n */\nexport function satisfiesCliRange(version: string, range: string): boolean {\n const v = parseVersion(version);\n if (!v) return true;\n const r = range.trim();\n if (r.startsWith('^')) {\n const want = parseVersion(r.slice(1));\n if (!want) return true;\n if (compare(v, want) < 0) return false;\n // Caret: same major; on 0.x the minor acts as the major.\n if (v.major !== want.major) return false;\n if (want.major === 0 && v.minor !== want.minor) return false;\n return true;\n }\n if (r.startsWith('>=')) {\n const want = parseVersion(r.slice(2));\n return !want || compare(v, want) >= 0;\n }\n const want = parseVersion(r);\n return !want || compare(v, want) === 0;\n}\n\nexport interface DiscoverOptions {\n /** The running CLI's version — enables `requires` compatibility checks. */\n cliVersion?: string;\n /** Where compatibility warnings go. */\n logger?: Logger;\n}\n\nexport async function discoverPlugins(cwd: string, opts: DiscoverOptions = {}): Promise<SigxPlugin[]> {\n const pkgPath = join(cwd, 'package.json');\n if (!existsSync(pkgPath)) return [];\n\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n } catch {\n return [];\n }\n\n const allDeps: Record<string, string> = {\n ...(pkg.dependencies as Record<string, string> | undefined),\n ...(pkg.devDependencies as Record<string, string> | undefined),\n };\n\n const plugins: SigxPlugin[] = [];\n\n for (const depName of Object.keys(allDeps)) {\n try {\n const depPkgPath = join(cwd, 'node_modules', depName, 'package.json');\n if (!existsSync(depPkgPath)) continue;\n\n const depPkg: DepPackageJson = JSON.parse(readFileSync(depPkgPath, 'utf-8'));\n const pluginField = depPkg['sigx-cli'];\n if (!pluginField?.plugin) continue;\n\n const pluginPath = join(cwd, 'node_modules', depName, pluginField.plugin);\n if (!existsSync(pluginPath)) continue;\n\n if (\n pluginField.requires &&\n opts.cliVersion &&\n !satisfiesCliRange(opts.cliVersion, pluginField.requires)\n ) {\n opts.logger?.warn(\n `Plugin \"${depName}\" requires sigx CLI ${pluginField.requires} ` +\n `but this project runs ${opts.cliVersion} — some commands may misbehave. ` +\n `Update with: pnpm up @sigx/cli --latest`,\n );\n }\n\n const mod = await import(pathToFileURL(pluginPath).href);\n const plugin: SigxPlugin = mod.default || mod;\n\n if (typeof plugin.detect === 'function' && plugin.detect(cwd)) {\n plugins.push(plugin);\n }\n } catch {\n // Not a valid plugin or failed to load — skip silently\n }\n }\n\n return plugins;\n}\n","module.exports = {}","import { defineCommand } from 'citty';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { execSync } from 'node:child_process';\n\nfunction getVersion(cmd: string): string | null {\n try {\n return execSync(cmd, { stdio: 'pipe', encoding: 'utf-8' }).trim();\n } catch {\n return null;\n }\n}\n\nexport const infoCommand = defineCommand({\n meta: {\n name: 'info',\n description: 'Print environment and project info',\n },\n run() {\n const cwd = process.cwd();\n\n console.log('\\n \\x1b[1msigx environment info\\x1b[0m\\n');\n console.log(` Platform: ${process.platform} ${process.arch}`);\n console.log(` Node: ${process.version}`);\n\n // Package manager\n const pnpmVer = getVersion('pnpm --version');\n if (pnpmVer) console.log(` pnpm: v${pnpmVer}`);\n else {\n const npmVer = getVersion('npm --version');\n if (npmVer) console.log(` npm: v${npmVer}`);\n }\n\n // Project info\n const pkgPath = join(cwd, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n console.log(` Project: ${pkg.name || '(unnamed)'} v${pkg.version || '0.0.0'}`);\n\n // Detect sigx packages\n const allDeps: Record<string, string> = {\n ...pkg.dependencies,\n ...pkg.devDependencies,\n };\n const sigxPkgs = Object.entries(allDeps)\n .filter(([name]) => name.startsWith('@sigx/') || name === 'sigx')\n .map(([name, version]) => `${name}@${version}`);\n\n if (sigxPkgs.length > 0) {\n console.log(` Sigx packages:`);\n for (const p of sigxPkgs) {\n console.log(` - ${p}`);\n }\n }\n } catch {\n // ignore parse errors\n }\n } else {\n console.log(' Project: (no package.json found)');\n }\n\n // Config files\n const configs = [\n { file: 'sigx.lynx.config.ts', label: 'Lynx' },\n { file: 'sigx.lynx.config.js', label: 'Lynx' },\n { file: 'lynx.config.ts', label: 'Lynx (rspeedy)' },\n { file: 'lynx.config.js', label: 'Lynx (rspeedy)' },\n { file: 'ssg.config.ts', label: 'SSG' },\n { file: 'vite.config.ts', label: 'Vite' },\n ];\n const detected = configs.filter(c => existsSync(join(cwd, c.file)));\n if (detected.length > 0) {\n console.log(` Config files:`);\n for (const c of detected) {\n console.log(` - ${c.file} (${c.label})`);\n }\n }\n\n // Lynx-specific info\n const isLynx = detected.some(c => c.label.includes('Lynx'));\n if (isLynx) {\n console.log('');\n console.log(' \\x1b[1mLynx Environment\\x1b[0m');\n\n // rspeedy\n const rspeedyVer = getVersion('npx rspeedy --version 2>&1');\n if (rspeedyVer) console.log(` rspeedy: v${rspeedyVer.trim()}`);\n\n // Android SDK\n const androidHome = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;\n if (androidHome) {\n console.log(` Android SDK: ${androidHome}`);\n }\n\n // JDK\n const javaVer = getVersion('java -version 2>&1');\n if (javaVer) {\n const match = javaVer.match(/version \"([^\"]+)\"/);\n if (match) console.log(` JDK: ${match[1]}`);\n }\n\n // ADB + devices\n try {\n const adbVer = getVersion('adb version');\n if (adbVer) {\n const verMatch = adbVer.match(/version ([\\d.]+)/);\n console.log(` ADB: ${verMatch ? `v${verMatch[1]}` : 'available'}`);\n\n const devOutput = execSync('adb devices -l', { stdio: 'pipe', encoding: 'utf-8' });\n const devices = devOutput.split('\\n')\n .slice(1)\n .filter(l => l.trim().length > 0)\n .map(l => {\n const modelMatch = l.match(/model:(\\S+)/);\n return modelMatch ? modelMatch[1].replace(/_/g, ' ') : l.split(/\\s+/)[0];\n });\n\n if (devices.length > 0) {\n console.log(` Devices: ${devices.join(', ')}`);\n }\n }\n } catch {\n // ADB not available\n }\n\n // Xcode (macOS only)\n if (process.platform === 'darwin') {\n const xcodeVer = getVersion('xcodebuild -version 2>/dev/null');\n if (xcodeVer) console.log(` Xcode: ${xcodeVer.split('\\n')[0]}`);\n\n const podVer = getVersion('pod --version');\n if (podVer) console.log(` CocoaPods: v${podVer}`);\n }\n }\n\n console.log('');\n },\n});\n","import type { Logger } from '../plugin.js';\n\nexport function createLogger(prefix = 'sigx'): Logger {\n return {\n log: (msg: string) => console.log(`[${prefix}] ${msg}`),\n warn: (msg: string) => console.warn(`[${prefix}] WARN: ${msg}`),\n error: (msg: string) => console.error(`[${prefix}] ERROR: ${msg}`),\n };\n}\n","#!/usr/bin/env node\n\n/**\n * sigx CLI — unified command-line tool for SignalX projects.\n *\n * Core commands (always available):\n * sigx create — scaffold a new project\n * sigx info — print environment info\n *\n * Plugin commands (auto-discovered from installed packages):\n * sigx dev — start dev server\n * sigx build — production build\n * sigx preview — preview build (SSG)\n * sigx prebuild — generate native project files (Lynx)\n * sigx run:android / run:ios — build + launch on device (Lynx)\n */\n\nimport { defineCommand, runMain } from 'citty';\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { discoverPlugins } from './discover.js';\nimport { infoCommand } from './commands/info.js';\nimport { createLogger } from './utils/logger.js';\nimport type { PluginCommand, SigxPlugin } from './plugin.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));\nconst logger = createLogger();\n\nfunction wrapPluginCommand(cmd: PluginCommand, plugins: SigxPlugin[]) {\n return defineCommand({\n meta: { description: cmd.description },\n args: cmd.args as any,\n async run({ args }) {\n // `plugins` lets a shell-hosting command (e.g. lynx dev) merge\n // peer plugins' TUI contributions via runShell({ plugins }).\n await cmd.run({ cwd: process.cwd(), args, logger, plugins, cliVersion: pkg.version });\n },\n });\n}\n\nasync function main() {\n const cwd = process.cwd();\n const plugins = await discoverPlugins(cwd, { cliVersion: pkg.version, logger });\n\n // Build subcommand map: core + plugin commands\n const subCommands: Record<string, ReturnType<typeof defineCommand>> = {\n info: infoCommand,\n };\n\n // Lazy-load create command only when needed (it pulls in @sigx/terminal)\n subCommands.create = defineCommand({\n meta: { name: 'create', description: 'Scaffold a new SignalX project' },\n async run() {\n const { runCreate } = await import('./commands/create.js');\n await runCreate();\n },\n });\n\n // Register plugin commands\n for (const plugin of plugins) {\n for (const [name, cmd] of Object.entries(plugin.commands)) {\n if (subCommands[name]) {\n // Command conflict — last plugin wins, but warn\n logger.warn(`Plugin \"${plugin.name}\" overrides command \"${name}\"`);\n }\n subCommands[name] = wrapPluginCommand(cmd, plugins);\n }\n }\n\n const mainCommand = defineCommand({\n meta: {\n name: 'sigx',\n version: pkg.version,\n description: 'SignalX CLI',\n },\n subCommands,\n });\n\n await runMain(mainCommand);\n}\n\nmain().catch((err) => {\n logger.error(err.message || String(err));\n process.exit(1);\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/discover.ts","../__vite-browser-external","../src/commands/info.ts","../src/utils/logger.ts","../src/cli.ts"],"sourcesContent":["/**\n * Auto-discover sigx CLI plugins from the project's dependencies.\n *\n * Scans the project's package.json for dependencies that declare\n * a `\"sigx-cli\"` manifest field:\n *\n * \"sigx-cli\": {\n * \"plugin\": \"./dist/plugin.js\",\n * \"requires\": \">=0.3.0\" // optional: CLI version range\n * }\n *\n * Loads each plugin and calls `detect(cwd)` to check if it applies.\n * When `requires` is declared and the RUNNING CLI binary doesn't satisfy\n * it, the plugin still loads (best effort) but a prominent warning tells\n * the user exactly how to update — peerDependencies can't cover this,\n * because the binary that dispatches commands isn't necessarily the\n * install the peer range was resolved against.\n */\n\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { SigxPlugin, Logger } from './plugin.js';\n\ninterface DepPackageJson {\n 'sigx-cli'?: {\n plugin: string;\n /** Semver range of @sigx/cli this plugin needs: '^x.y.z', '>=x.y.z', or exact. */\n requires?: string;\n };\n}\n\ninterface ParsedVersion { major: number; minor: number; patch: number }\n\nfunction parseVersion(v: string): ParsedVersion | null {\n const m = v.trim().match(/^(\\d+)\\.(\\d+)\\.(\\d+)/);\n if (!m) return null;\n return { major: Number(m[1]), minor: Number(m[2]), patch: Number(m[3]) };\n}\n\nfunction compare(a: ParsedVersion, b: ParsedVersion): number {\n return a.major - b.major || a.minor - b.minor || a.patch - b.patch;\n}\n\n/**\n * Minimal semver-range check covering the forms plugins actually use:\n * `^x.y.z` (0.x caret = same minor, like npm), `>=x.y.z`, and exact.\n * Unknown/garbled ranges return true — never block on a malformed field.\n */\nexport function satisfiesCliRange(version: string, range: string): boolean {\n const v = parseVersion(version);\n if (!v) return true;\n const r = range.trim();\n if (r.startsWith('^')) {\n const want = parseVersion(r.slice(1));\n if (!want) return true;\n if (compare(v, want) < 0) return false;\n // Caret: same major; on 0.x the minor acts as the major.\n if (v.major !== want.major) return false;\n if (want.major === 0 && v.minor !== want.minor) return false;\n return true;\n }\n if (r.startsWith('>=')) {\n const want = parseVersion(r.slice(2));\n return !want || compare(v, want) >= 0;\n }\n const want = parseVersion(r);\n return !want || compare(v, want) === 0;\n}\n\nexport interface DiscoverOptions {\n /** The running CLI's version — enables `requires` compatibility checks. */\n cliVersion?: string;\n /** Where compatibility warnings go. */\n logger?: Logger;\n}\n\nexport async function discoverPlugins(cwd: string, opts: DiscoverOptions = {}): Promise<SigxPlugin[]> {\n const pkgPath = join(cwd, 'package.json');\n if (!existsSync(pkgPath)) return [];\n\n let pkg: Record<string, unknown>;\n try {\n pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n } catch {\n return [];\n }\n\n const allDeps: Record<string, string> = {\n ...(pkg.dependencies as Record<string, string> | undefined),\n ...(pkg.devDependencies as Record<string, string> | undefined),\n };\n\n const plugins: SigxPlugin[] = [];\n\n for (const depName of Object.keys(allDeps)) {\n try {\n const depPkgPath = join(cwd, 'node_modules', depName, 'package.json');\n if (!existsSync(depPkgPath)) continue;\n\n const depPkg: DepPackageJson = JSON.parse(readFileSync(depPkgPath, 'utf-8'));\n const pluginField = depPkg['sigx-cli'];\n if (!pluginField?.plugin) continue;\n\n const pluginPath = join(cwd, 'node_modules', depName, pluginField.plugin);\n if (!existsSync(pluginPath)) continue;\n\n if (\n pluginField.requires &&\n opts.cliVersion &&\n !satisfiesCliRange(opts.cliVersion, pluginField.requires)\n ) {\n opts.logger?.warn(\n `Plugin \"${depName}\" requires sigx CLI ${pluginField.requires} ` +\n `but this project runs ${opts.cliVersion} — some commands may misbehave. ` +\n `Update with: pnpm up @sigx/cli --latest`,\n );\n }\n\n const mod = await import(pathToFileURL(pluginPath).href);\n const plugin: SigxPlugin = mod.default || mod;\n\n if (typeof plugin.detect === 'function' && plugin.detect(cwd)) {\n plugins.push(plugin);\n }\n } catch {\n // Not a valid plugin or failed to load — skip silently\n }\n }\n\n return plugins;\n}\n","module.exports = {}","import { command } from '@sigx/args';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { execSync } from 'node:child_process';\n\nfunction getVersion(cmd: string): string | null {\n try {\n return execSync(cmd, { stdio: 'pipe', encoding: 'utf-8' }).trim();\n } catch {\n return null;\n }\n}\n\nexport const infoCommand = command('info')\n .describe('Print environment and project info')\n .run(() => {\n const cwd = process.cwd();\n\n console.log('\\n \\x1b[1msigx environment info\\x1b[0m\\n');\n console.log(` Platform: ${process.platform} ${process.arch}`);\n console.log(` Node: ${process.version}`);\n\n // Package manager\n const pnpmVer = getVersion('pnpm --version');\n if (pnpmVer) console.log(` pnpm: v${pnpmVer}`);\n else {\n const npmVer = getVersion('npm --version');\n if (npmVer) console.log(` npm: v${npmVer}`);\n }\n\n // Project info\n const pkgPath = join(cwd, 'package.json');\n if (existsSync(pkgPath)) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));\n console.log(` Project: ${pkg.name || '(unnamed)'} v${pkg.version || '0.0.0'}`);\n\n // Detect sigx packages\n const allDeps: Record<string, string> = {\n ...pkg.dependencies,\n ...pkg.devDependencies,\n };\n const sigxPkgs = Object.entries(allDeps)\n .filter(([name]) => name.startsWith('@sigx/') || name === 'sigx')\n .map(([name, version]) => `${name}@${version}`);\n\n if (sigxPkgs.length > 0) {\n console.log(` Sigx packages:`);\n for (const p of sigxPkgs) {\n console.log(` - ${p}`);\n }\n }\n } catch {\n // ignore parse errors\n }\n } else {\n console.log(' Project: (no package.json found)');\n }\n\n // Config files\n const configs = [\n { file: 'sigx.lynx.config.ts', label: 'Lynx' },\n { file: 'sigx.lynx.config.js', label: 'Lynx' },\n { file: 'lynx.config.ts', label: 'Lynx (rspeedy)' },\n { file: 'lynx.config.js', label: 'Lynx (rspeedy)' },\n { file: 'ssg.config.ts', label: 'SSG' },\n { file: 'vite.config.ts', label: 'Vite' },\n ];\n const detected = configs.filter(c => existsSync(join(cwd, c.file)));\n if (detected.length > 0) {\n console.log(` Config files:`);\n for (const c of detected) {\n console.log(` - ${c.file} (${c.label})`);\n }\n }\n\n // Lynx-specific info\n const isLynx = detected.some(c => c.label.includes('Lynx'));\n if (isLynx) {\n console.log('');\n console.log(' \\x1b[1mLynx Environment\\x1b[0m');\n\n // rspeedy\n const rspeedyVer = getVersion('npx rspeedy --version 2>&1');\n if (rspeedyVer) console.log(` rspeedy: v${rspeedyVer.trim()}`);\n\n // Android SDK\n const androidHome = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;\n if (androidHome) {\n console.log(` Android SDK: ${androidHome}`);\n }\n\n // JDK\n const javaVer = getVersion('java -version 2>&1');\n if (javaVer) {\n const match = javaVer.match(/version \"([^\"]+)\"/);\n if (match) console.log(` JDK: ${match[1]}`);\n }\n\n // ADB + devices\n try {\n const adbVer = getVersion('adb version');\n if (adbVer) {\n const verMatch = adbVer.match(/version ([\\d.]+)/);\n console.log(` ADB: ${verMatch ? `v${verMatch[1]}` : 'available'}`);\n\n const devOutput = execSync('adb devices -l', { stdio: 'pipe', encoding: 'utf-8' });\n const devices = devOutput.split('\\n')\n .slice(1)\n .filter(l => l.trim().length > 0)\n .map(l => {\n const modelMatch = l.match(/model:(\\S+)/);\n return modelMatch ? modelMatch[1].replace(/_/g, ' ') : l.split(/\\s+/)[0];\n });\n\n if (devices.length > 0) {\n console.log(` Devices: ${devices.join(', ')}`);\n }\n }\n } catch {\n // ADB not available\n }\n\n // Xcode (macOS only)\n if (process.platform === 'darwin') {\n const xcodeVer = getVersion('xcodebuild -version 2>/dev/null');\n if (xcodeVer) console.log(` Xcode: ${xcodeVer.split('\\n')[0]}`);\n\n const podVer = getVersion('pod --version');\n if (podVer) console.log(` CocoaPods: v${podVer}`);\n }\n }\n\n console.log('');\n });\n","import type { Logger } from '../plugin.js';\n\nexport function createLogger(prefix = 'sigx'): Logger {\n return {\n log: (msg: string) => console.log(`[${prefix}] ${msg}`),\n warn: (msg: string) => console.warn(`[${prefix}] WARN: ${msg}`),\n error: (msg: string) => console.error(`[${prefix}] ERROR: ${msg}`),\n };\n}\n","#!/usr/bin/env node\n\n/**\n * sigx CLI — unified command-line tool for SignalX projects.\n *\n * Core commands (always available):\n * sigx create — scaffold a new project\n * sigx info — print environment info\n *\n * Plugin commands (auto-discovered from installed packages):\n * sigx dev — start dev server\n * sigx build — production build\n * sigx preview — preview build (SSG)\n * sigx prebuild — generate native project files (Lynx)\n * sigx run:android / run:ios — build + launch on device (Lynx)\n */\n\nimport { a, command, runMain, DefinitionError, type AnyCommand } from '@sigx/args';\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { discoverPlugins } from './discover.js';\nimport { infoCommand } from './commands/info.js';\nimport { createLogger } from './utils/logger.js';\nimport type { PluginCommand, SigxPlugin } from './plugin.js';\n\nconst __dirname = dirname(fileURLToPath(import.meta.url));\nconst pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));\nconst logger = createLogger();\n\nfunction wrapPluginCommand(name: string, cmd: PluginCommand, plugins: SigxPlugin[]): AnyCommand {\n return command(name)\n .describe(cmd.description)\n .args(cmd.args ?? {})\n .run(async ({ args }) => {\n // `plugins` lets a shell-hosting command (e.g. lynx dev) merge\n // peer plugins' TUI contributions via runShell({ plugins }).\n await cmd.run({ cwd: process.cwd(), args, logger, plugins, cliVersion: pkg.version });\n });\n}\n\nasync function main() {\n const cwd = process.cwd();\n const plugins = await discoverPlugins(cwd, { cliVersion: pkg.version, logger });\n\n // Build subcommand map: core + plugin commands\n const subCommands: Record<string, AnyCommand> = {\n info: infoCommand,\n };\n\n // Lazy-load create command only when needed (it pulls in @sigx/terminal)\n subCommands.create = command('create')\n .describe('Scaffold a new SignalX project')\n .args({\n name: a.positional().describe('Project name'),\n type: a.enum(['basic', 'ssr', 'ssg', 'lynx']).describe('Project type'),\n styling: a.enum(['none', 'tailwind', 'daisyui']).describe('Styling setup'),\n yes: a.boolean().alias('y').describe('Skip prompts (headless mode)'),\n })\n .run(async ({ args }) => {\n const { runCreate } = await import('./commands/create.js');\n await runCreate(args);\n });\n\n // Register plugin commands\n for (const plugin of plugins) {\n for (const [name, cmd] of Object.entries(plugin.commands)) {\n let wrapped: AnyCommand;\n try {\n wrapped = wrapPluginCommand(name, cmd, plugins);\n } catch (err) {\n // A bad schema in one plugin must not take down the whole CLI.\n if (err instanceof DefinitionError) {\n logger.warn(`Plugin \"${plugin.name}\" command \"${name}\" has an invalid args schema: ${err.message}`);\n continue;\n }\n throw err;\n }\n if (subCommands[name]) {\n // Command conflict — last plugin wins, but warn\n logger.warn(`Plugin \"${plugin.name}\" overrides command \"${name}\"`);\n }\n subCommands[name] = wrapped;\n }\n }\n\n const mainCommand = command('sigx').version(pkg.version).describe('SignalX CLI').subcommands(subCommands);\n\n await runMain(mainCommand);\n}\n\nmain().catch((err) => {\n logger.error(err.message || String(err));\n process.exit(1);\n});\n"],"mappings":"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"}
|
package/dist/commands/create.js
CHANGED
|
@@ -5,37 +5,37 @@ import { cancel, intro, isCancel, note, outro, select, spinner, text } from "@si
|
|
|
5
5
|
* `sigx create` — interactive scaffolder on the @sigx/terminal prompt kit,
|
|
6
6
|
* with a flag-driven headless mode for CI (`--type`, `--styling`, `--yes`,
|
|
7
7
|
* or any non-TTY stdio).
|
|
8
|
+
*
|
|
9
|
+
* Two entry paths share this module: the sigx CLI passes args it already
|
|
10
|
+
* parsed with @sigx/args (`runCreate(ctx.args)`), while the bare
|
|
11
|
+
* `runCreate()` call from the `@sigx/create` shim (`pnpm create @sigx …`)
|
|
12
|
+
* falls back to parsing `process.argv` itself.
|
|
8
13
|
*/
|
|
9
|
-
|
|
10
|
-
function
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
/** Fallback argv parsing for the `@sigx/create` shim, which has no parser of its own. */
|
|
15
|
+
function parseArgvFallback() {
|
|
16
|
+
const rawArgs = process.argv.slice(2);
|
|
17
|
+
function getFlag(name) {
|
|
18
|
+
const eq = rawArgs.find((a) => a.startsWith(`--${name}=`));
|
|
19
|
+
if (eq) return eq.slice(name.length + 3);
|
|
20
|
+
const idx = rawArgs.indexOf(`--${name}`);
|
|
21
|
+
if (idx !== -1 && rawArgs[idx + 1] && !rawArgs[idx + 1].startsWith("-")) return rawArgs[idx + 1];
|
|
22
|
+
}
|
|
23
|
+
function hasFlag(name, short) {
|
|
24
|
+
return rawArgs.includes(`--${name}`) || (short ? rawArgs.includes(`-${short}`) : false);
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
name: rawArgs.filter((a) => !a.startsWith("-") && a !== "create")[0],
|
|
28
|
+
type: getFlag("type"),
|
|
29
|
+
styling: getFlag("styling"),
|
|
30
|
+
yes: hasFlag("yes", "y")
|
|
31
|
+
};
|
|
18
32
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const validTypes = [
|
|
26
|
-
"basic",
|
|
27
|
-
"ssr",
|
|
28
|
-
"ssg",
|
|
29
|
-
"lynx"
|
|
30
|
-
];
|
|
31
|
-
const validStyling = [
|
|
32
|
-
"none",
|
|
33
|
-
"tailwind",
|
|
34
|
-
"daisyui"
|
|
35
|
-
];
|
|
36
|
-
const projectName = argProjectName || "my-sigx-app";
|
|
37
|
-
const projectType = argType ?? "basic";
|
|
38
|
-
const styling = argStyling ?? "none";
|
|
33
|
+
function runHeadless(opts) {
|
|
34
|
+
const validTypes = projectTypeOptions.map((o) => o.value);
|
|
35
|
+
const validStyling = [...new Set([...webStylingOptions, ...lynxStylingOptions].map((o) => o.value))];
|
|
36
|
+
const projectName = opts.name || "my-sigx-app";
|
|
37
|
+
const projectType = opts.type ?? "basic";
|
|
38
|
+
const styling = opts.styling ?? "none";
|
|
39
39
|
if (!validTypes.includes(projectType)) {
|
|
40
40
|
console.error(`Error: --type must be one of ${validTypes.join(", ")}`);
|
|
41
41
|
return 2;
|
|
@@ -67,25 +67,26 @@ function bail() {
|
|
|
67
67
|
cancel("Cancelled — nothing was created.");
|
|
68
68
|
process.exit(130);
|
|
69
69
|
}
|
|
70
|
-
async function runCreate() {
|
|
71
|
-
|
|
70
|
+
async function runCreate(opts) {
|
|
71
|
+
const options = opts ?? parseArgvFallback();
|
|
72
|
+
if (!process.stdout.isTTY || !process.stdin.isTTY || Boolean(options.yes) || Boolean(options.type && options.name)) process.exit(runHeadless(options));
|
|
72
73
|
intro("⚡ Create SignalX App");
|
|
73
74
|
const projectName = await text({
|
|
74
75
|
message: "Project name",
|
|
75
76
|
placeholder: "my-sigx-app",
|
|
76
|
-
initialValue:
|
|
77
|
+
initialValue: options.name || "my-sigx-app",
|
|
77
78
|
validate: (v) => v.trim() ? void 0 : "Project name is required"
|
|
78
79
|
});
|
|
79
80
|
if (isCancel(projectName)) bail();
|
|
80
81
|
const projectType = await select({
|
|
81
82
|
message: "Project type",
|
|
82
|
-
initialValue:
|
|
83
|
+
initialValue: options.type ?? "basic",
|
|
83
84
|
options: projectTypeOptions
|
|
84
85
|
});
|
|
85
86
|
if (isCancel(projectType)) bail();
|
|
86
87
|
const styling = await select({
|
|
87
88
|
message: "Styling",
|
|
88
|
-
initialValue:
|
|
89
|
+
initialValue: options.styling ?? "none",
|
|
89
90
|
options: projectType === "lynx" ? lynxStylingOptions : webStylingOptions
|
|
90
91
|
});
|
|
91
92
|
if (isCancel(styling)) bail();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.js","names":[],"sources":["../../src/commands/create.ts"],"sourcesContent":["/**\n * `sigx create` — interactive scaffolder on the @sigx/terminal prompt kit,\n * with a flag-driven headless mode for CI (`--type`, `--styling`, `--yes`,\n * or any non-TTY stdio).\n */\nimport { intro, outro, note, cancel, isCancel, text, select, spinner } from '@sigx/terminal';\nimport {\n scaffoldProject,\n projectTypeOptions,\n webStylingOptions,\n lynxStylingOptions,\n type ProjectType,\n type Styling,\n} from './scaffold.js';\n\n
|
|
1
|
+
{"version":3,"file":"create.js","names":[],"sources":["../../src/commands/create.ts"],"sourcesContent":["/**\n * `sigx create` — interactive scaffolder on the @sigx/terminal prompt kit,\n * with a flag-driven headless mode for CI (`--type`, `--styling`, `--yes`,\n * or any non-TTY stdio).\n *\n * Two entry paths share this module: the sigx CLI passes args it already\n * parsed with @sigx/args (`runCreate(ctx.args)`), while the bare\n * `runCreate()` call from the `@sigx/create` shim (`pnpm create @sigx …`)\n * falls back to parsing `process.argv` itself.\n */\nimport { intro, outro, note, cancel, isCancel, text, select, spinner } from '@sigx/terminal';\nimport {\n scaffoldProject,\n projectTypeOptions,\n webStylingOptions,\n lynxStylingOptions,\n type ProjectType,\n type Styling,\n} from './scaffold.js';\n\nexport interface CreateOptions {\n /** Project name (positional). */\n name?: string;\n type?: ProjectType;\n styling?: Styling;\n /** Skip prompts (headless mode). */\n yes?: boolean;\n}\n\n/** Fallback argv parsing for the `@sigx/create` shim, which has no parser of its own. */\nfunction parseArgvFallback(): CreateOptions {\n const rawArgs = process.argv.slice(2);\n function getFlag(name: string): string | undefined {\n const eq = rawArgs.find(a => a.startsWith(`--${name}=`));\n if (eq) return eq.slice(name.length + 3);\n const idx = rawArgs.indexOf(`--${name}`);\n if (idx !== -1 && rawArgs[idx + 1] && !rawArgs[idx + 1].startsWith('-')) return rawArgs[idx + 1];\n return undefined;\n }\n function hasFlag(name: string, short?: string): boolean {\n return rawArgs.includes(`--${name}`) || (short ? rawArgs.includes(`-${short}`) : false);\n }\n const positionalArgs = rawArgs.filter(a => !a.startsWith('-') && a !== 'create');\n return {\n name: positionalArgs[0],\n type: getFlag('type') as ProjectType | undefined,\n styling: getFlag('styling') as Styling | undefined,\n yes: hasFlag('yes', 'y'),\n };\n}\n\nfunction runHeadless(opts: CreateOptions): number {\n const validTypes = projectTypeOptions.map((o) => o.value);\n const validStyling = [...new Set([...webStylingOptions, ...lynxStylingOptions].map((o) => o.value))];\n\n const projectName = opts.name || 'my-sigx-app';\n const projectType: ProjectType = opts.type ?? 'basic';\n const styling: Styling = opts.styling ?? 'none';\n\n if (!validTypes.includes(projectType)) {\n console.error(`Error: --type must be one of ${validTypes.join(', ')}`);\n return 2;\n }\n if (!validStyling.includes(styling)) {\n console.error(`Error: --styling must be one of ${validStyling.join(', ')}`);\n return 2;\n }\n\n console.log(`\\n ⚡ Creating SignalX app \"${projectName}\"`);\n console.log(` type: ${projectType}`);\n console.log(` styling: ${styling}\\n`);\n\n const result = scaffoldProject({ projectName, projectType, styling });\n if (!result.ok) {\n console.error(`Error: ${result.error}`);\n return 1;\n }\n\n console.log(` ✓ Project created\\n`);\n console.log(` Next steps:`);\n console.log(` cd ${projectName}`);\n console.log(` pnpm install`);\n console.log(` ${projectType === 'lynx' ? 'sigx dev' : 'pnpm dev'}\\n`);\n return 0;\n}\n\nfunction bail(): never {\n cancel('Cancelled — nothing was created.');\n process.exit(130);\n}\n\nexport async function runCreate(opts?: CreateOptions): Promise<void> {\n const options = opts ?? parseArgvFallback();\n const isNonInteractive =\n !process.stdout.isTTY || !process.stdin.isTTY || Boolean(options.yes) || Boolean(options.type && options.name);\n\n if (isNonInteractive) {\n process.exit(runHeadless(options));\n }\n\n intro('⚡ Create SignalX App');\n\n const projectName = await text({\n message: 'Project name',\n placeholder: 'my-sigx-app',\n initialValue: options.name || 'my-sigx-app',\n validate: (v: string) => (v.trim() ? undefined : 'Project name is required'),\n });\n if (isCancel(projectName)) bail();\n\n const projectType = await select<ProjectType>({\n message: 'Project type',\n initialValue: options.type ?? 'basic',\n options: projectTypeOptions,\n });\n if (isCancel(projectType)) bail();\n\n const styling = await select<Styling>({\n message: 'Styling',\n initialValue: options.styling ?? 'none',\n options: projectType === 'lynx' ? lynxStylingOptions : webStylingOptions,\n });\n if (isCancel(styling)) bail();\n\n const s = spinner();\n s.start(`Scaffolding ${projectName}`);\n const result = scaffoldProject({ projectName, projectType, styling });\n if (!result.ok) {\n s.stop(result.error, 'error');\n process.exit(1);\n }\n s.stop(`Created ${projectName} (${projectType}${styling !== 'none' ? ` + ${styling}` : ''})`);\n\n note(\n `cd ${projectName}\\npnpm install\\n${projectType === 'lynx' ? 'sigx dev' : 'pnpm dev'}`,\n 'Next steps',\n );\n outro('Happy hacking!');\n process.exit(0);\n}\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { definePlugin } from "./plugin.js";
|
|
2
|
-
export { definePlugin };
|
|
1
|
+
import { a, definePlugin } from "./plugin.js";
|
|
2
|
+
export { a, definePlugin };
|
package/dist/plugin.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { a } from "@sigx/args";
|
|
1
2
|
//#region src/plugin.ts
|
|
2
3
|
/**
|
|
3
4
|
* Define a sigx CLI plugin. Identity function for type safety.
|
|
@@ -6,6 +7,6 @@ function definePlugin(plugin) {
|
|
|
6
7
|
return plugin;
|
|
7
8
|
}
|
|
8
9
|
//#endregion
|
|
9
|
-
export { definePlugin };
|
|
10
|
+
export { a, definePlugin };
|
|
10
11
|
|
|
11
12
|
//# sourceMappingURL=plugin.js.map
|
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * Plugin interface for the sigx CLI.\n *\n * Packages that want to extend the CLI declare a `\"sigx-cli\"` field\n * in their package.json pointing to a module that default-exports\n * a SigxPlugin created with `definePlugin()`.\n *\n * This module is
|
|
1
|
+
{"version":3,"file":"plugin.js","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["/**\n * Plugin interface for the sigx CLI.\n *\n * Packages that want to extend the CLI declare a `\"sigx-cli\"` field\n * in their package.json pointing to a module that default-exports\n * a SigxPlugin created with `definePlugin()`.\n *\n * Command args are declared with @sigx/args' fluent `a` builders\n * (re-exported here so plugins keep a single import surface):\n *\n * args: { port: a.number().alias('p').default(8788), open: a.boolean() }\n *\n * This module pulls in nothing beyond @sigx/args (which is itself\n * zero-dependency): plugins import it via `@sigx/cli/plugin` without the TUI\n * stack. The TUI types below (`ShellTab`, `ShellHandle`, …) are structural\n * contracts consumed by `runShell` from `@sigx/cli/shell`.\n */\n\nimport type { ArgsShape } from '@sigx/args';\n\nexport { a } from '@sigx/args';\nexport type { AnyArg, ArgsShape, InferArgs } from '@sigx/args';\n\nexport interface CommandContext {\n cwd: string;\n /**\n * Parsed args, per the command's `args` builders. Declared positionals\n * bind by position; `args._` holds the verbatim tokens after a bare `--`.\n * Use a rest builder (`a.rest()`) for variadic positionals.\n */\n args: Record<string, unknown>;\n logger: Logger;\n /**\n * All plugins discovered for this project — lets a shell-hosting command\n * (e.g. lynx `dev`) merge peer plugins' TUI contributions via\n * `runShell({ plugins })` from `@sigx/cli/shell`.\n */\n plugins?: SigxPlugin[];\n /** The running CLI binary's version — for plugin feature detection. */\n cliVersion?: string;\n}\n\nexport interface Logger {\n log: (msg: string) => void;\n warn: (msg: string) => void;\n error: (msg: string) => void;\n}\n\nexport interface PluginCommand {\n description: string;\n /** Fluent arg builders, e.g. `{ port: a.number().default(8788) }`. */\n args?: ArgsShape;\n run: (ctx: CommandContext) => Promise<void>;\n}\n\n/**\n * Opaque renderable returned by a tab's `render()`. Author it with JSX\n * (`@jsxImportSource @sigx/terminal`); typed as `unknown` so this module\n * stays dependency-free — the shell passes it straight to the renderer.\n */\nexport type ShellNode = unknown;\n\n/** One entry in the shell's status line. `tone` is a theme token. */\nexport interface StatusItem {\n label: string;\n value: string;\n /** e.g. 'success' | 'warn' | 'danger' | 'dim' | 'accent' */\n tone?: string;\n}\n\n/** A tab in the shell's tab strip. */\nexport interface ShellTab {\n id: string;\n label: string;\n render: () => ShellNode;\n}\n\n/** A `/command` offered in the shell input's intellisense. */\nexport interface SlashCommand {\n /** Includes the leading slash, e.g. '/reload'. */\n name: string;\n description: string;\n run: (shell: ShellHandle) => void | Promise<void>;\n}\n\n/** A single-key shortcut, active only while the command input is empty. */\nexport interface Shortcut {\n key: string;\n label: string;\n run: (shell: ShellHandle) => void | Promise<void>;\n}\n\n/** Structural subset of @sigx/terminal's LogStore — keeps this module dep-free. */\nexport interface ShellLogStore {\n push: (chunk: string) => void;\n}\n\n/**\n * Handle to a running shell, passed to slash commands, shortcuts, and the\n * host's onReady. In non-TTY environments (`isInteractive: false`) the shell\n * never mounts: `say` writes plain lines, the store streams through, and the\n * navigation methods are no-ops — callers keep a single code path.\n */\nexport interface ShellHandle {\n isInteractive: boolean;\n /** Print a permanent transcript line above the live region. */\n say: (text?: string) => void;\n /** The main streaming log store (feeds the host's Logs tab). */\n store: ShellLogStore;\n setStatus: (items: StatusItem[]) => void;\n switchTab: (id: string) => void;\n pushView: (id: string) => void;\n popView: () => void;\n /**\n * Register teardown to run BEFORE the process exits — on `exit()`,\n * Ctrl+C/q, and external SIGTERM/SIGHUP/SIGINT. Subscribers run after\n * the host's onExit, most-recently-registered first. Returns an\n * unsubscribe. Use this for servers, watchers, locks.\n */\n onExit: (cb: () => void | Promise<void>) => () => void;\n exit: (code?: number) => void;\n}\n\n/**\n * TUI contributions a plugin offers to whichever plugin hosts the shell:\n * tabs, slash commands, shortcuts, and status-line items.\n */\nexport interface TuiContribution {\n tabs?: ShellTab[];\n commands?: SlashCommand[];\n shortcuts?: Shortcut[];\n status?: () => StatusItem[];\n /**\n * Lifecycle: called once when the hosting shell comes up (interactive or\n * plain). Start servers/watchers here; an optionally returned function is\n * registered as teardown (equivalent to `shell.onExit(fn)`).\n */\n setup?: (shell: ShellHandle) => void | (() => void | Promise<void>) | Promise<void | (() => void | Promise<void>)>;\n}\n\nexport interface SigxPlugin {\n /** Unique plugin name (e.g. 'ssg', 'lynx') */\n name: string;\n /** Return true if this plugin handles the current project */\n detect: (cwd: string) => boolean;\n /** Commands this plugin provides */\n commands: Record<string, PluginCommand>;\n /** Optional TUI contributions merged into any plugin-hosted shell. */\n tui?: TuiContribution;\n}\n\n/**\n * Define a sigx CLI plugin. Identity function for type safety.\n */\nexport function definePlugin(plugin: SigxPlugin): SigxPlugin {\n return plugin;\n}\n"],"mappings":";;;;;AA0JA,SAAgB,aAAa,QAAgC;CACzD,OAAO"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sigx/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Unified CLI for SignalX — create, dev, build, and run projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"templates"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@sigx/
|
|
35
|
-
"
|
|
34
|
+
"@sigx/args": "^0.6.0",
|
|
35
|
+
"@sigx/terminal": "^0.5.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@sigx/vite": "^0.4.7",
|