@screenbook/cli 1.3.0 → 1.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/index.mjs +219 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +13 -5
package/dist/index.mjs
CHANGED
|
@@ -602,7 +602,7 @@ const devCommand = define({
|
|
|
602
602
|
const cwd = process.cwd();
|
|
603
603
|
logger.info("Starting Screenbook development server...");
|
|
604
604
|
await buildScreens(config, cwd);
|
|
605
|
-
const uiPackagePath = resolveUiPackage();
|
|
605
|
+
const uiPackagePath = resolveUiPackage$1();
|
|
606
606
|
if (!uiPackagePath) {
|
|
607
607
|
logger.errorWithHelp({
|
|
608
608
|
title: "Could not find @screenbook/ui package",
|
|
@@ -678,7 +678,7 @@ async function buildScreens(config, cwd) {
|
|
|
678
678
|
logger.blank();
|
|
679
679
|
logger.success(`Generated ${logger.path(outputPath)}`);
|
|
680
680
|
}
|
|
681
|
-
function resolveUiPackage() {
|
|
681
|
+
function resolveUiPackage$1() {
|
|
682
682
|
try {
|
|
683
683
|
return dirname(createRequire(import.meta.url).resolve("@screenbook/ui/package.json"));
|
|
684
684
|
} catch {
|
|
@@ -3004,6 +3004,18 @@ async function promptFrameworkSelection() {
|
|
|
3004
3004
|
};
|
|
3005
3005
|
}
|
|
3006
3006
|
|
|
3007
|
+
//#endregion
|
|
3008
|
+
//#region src/utils/isInteractive.ts
|
|
3009
|
+
/**
|
|
3010
|
+
* Check if the current environment supports interactive prompts.
|
|
3011
|
+
* Returns false in CI environments or when stdin is not a TTY.
|
|
3012
|
+
*/
|
|
3013
|
+
function isInteractive() {
|
|
3014
|
+
if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) return false;
|
|
3015
|
+
if (process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.JENKINS_URL) return false;
|
|
3016
|
+
return process.stdin.isTTY === true;
|
|
3017
|
+
}
|
|
3018
|
+
|
|
3007
3019
|
//#endregion
|
|
3008
3020
|
//#region src/commands/init.ts
|
|
3009
3021
|
function generateConfigTemplate(framework) {
|
|
@@ -3060,6 +3072,123 @@ function printNextSteps(hasRoutesPattern) {
|
|
|
3060
3072
|
logger.log(logger.dim(" page.tsx # Your route file"));
|
|
3061
3073
|
logger.log(logger.dim(" screen.meta.ts # Auto-generated, customize as needed"));
|
|
3062
3074
|
}
|
|
3075
|
+
/**
|
|
3076
|
+
* Resolve a boolean option with priority order:
|
|
3077
|
+
* 1. Explicit flag (e.g., --generate or --no-generate) takes precedence
|
|
3078
|
+
* 2. -y flag enables all optional features
|
|
3079
|
+
* 3. Non-interactive environments (CI mode or no TTY) fall back to ciDefault
|
|
3080
|
+
* 4. Otherwise, prompt the user interactively
|
|
3081
|
+
*/
|
|
3082
|
+
async function resolveOption(params) {
|
|
3083
|
+
const { explicitValue, yesAll, ciMode, ciDefault, promptMessage } = params;
|
|
3084
|
+
if (explicitValue !== void 0) return explicitValue;
|
|
3085
|
+
if (yesAll) return true;
|
|
3086
|
+
if (ciMode || !isInteractive()) return ciDefault;
|
|
3087
|
+
const response = await prompts({
|
|
3088
|
+
type: "confirm",
|
|
3089
|
+
name: "value",
|
|
3090
|
+
message: promptMessage,
|
|
3091
|
+
initial: true
|
|
3092
|
+
});
|
|
3093
|
+
if (response.value === void 0) {
|
|
3094
|
+
logger.blank();
|
|
3095
|
+
logger.info("Operation cancelled");
|
|
3096
|
+
process.exit(0);
|
|
3097
|
+
}
|
|
3098
|
+
return response.value;
|
|
3099
|
+
}
|
|
3100
|
+
async function countRouteFiles(routesPattern, cwd) {
|
|
3101
|
+
return (await glob(routesPattern, { cwd })).length;
|
|
3102
|
+
}
|
|
3103
|
+
async function runGenerate(routesPattern, cwd) {
|
|
3104
|
+
await generateFromRoutesPattern(routesPattern, cwd, {
|
|
3105
|
+
dryRun: false,
|
|
3106
|
+
force: false,
|
|
3107
|
+
interactive: false,
|
|
3108
|
+
ignore: ["**/node_modules/**"]
|
|
3109
|
+
});
|
|
3110
|
+
}
|
|
3111
|
+
async function buildScreensForDev(metaPattern, outDir, cwd) {
|
|
3112
|
+
const files = await glob(metaPattern, {
|
|
3113
|
+
cwd,
|
|
3114
|
+
ignore: ["**/node_modules/**"]
|
|
3115
|
+
});
|
|
3116
|
+
if (files.length === 0) {
|
|
3117
|
+
logger.warn(`No screen.meta.ts files found matching: ${metaPattern}`);
|
|
3118
|
+
return;
|
|
3119
|
+
}
|
|
3120
|
+
const jiti = createJiti(cwd);
|
|
3121
|
+
const screens = [];
|
|
3122
|
+
for (const file of files) {
|
|
3123
|
+
const absolutePath = resolve(cwd, file);
|
|
3124
|
+
try {
|
|
3125
|
+
const module = await jiti.import(absolutePath);
|
|
3126
|
+
if (module.screen) screens.push({
|
|
3127
|
+
...module.screen,
|
|
3128
|
+
filePath: absolutePath
|
|
3129
|
+
});
|
|
3130
|
+
} catch (error) {
|
|
3131
|
+
logger.itemWarn(`Failed to load ${file}`);
|
|
3132
|
+
if (error instanceof Error) logger.log(` ${logger.dim(error.message)}`);
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
const outputPath = join(cwd, outDir, "screens.json");
|
|
3136
|
+
const outputDir = dirname(outputPath);
|
|
3137
|
+
if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true });
|
|
3138
|
+
writeFileSync(outputPath, JSON.stringify(screens, null, 2));
|
|
3139
|
+
}
|
|
3140
|
+
function resolveUiPackage() {
|
|
3141
|
+
try {
|
|
3142
|
+
return dirname(createRequire(import.meta.url).resolve("@screenbook/ui/package.json"));
|
|
3143
|
+
} catch {
|
|
3144
|
+
const possiblePaths = [
|
|
3145
|
+
join(process.cwd(), "node_modules", "@screenbook", "ui"),
|
|
3146
|
+
join(process.cwd(), "..", "ui"),
|
|
3147
|
+
join(process.cwd(), "packages", "ui")
|
|
3148
|
+
];
|
|
3149
|
+
for (const p of possiblePaths) if (existsSync(join(p, "package.json"))) return p;
|
|
3150
|
+
return null;
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
async function startDevServer(metaPattern, outDir, cwd, port) {
|
|
3154
|
+
await buildScreensForDev(metaPattern, outDir, cwd);
|
|
3155
|
+
const uiPackagePath = resolveUiPackage();
|
|
3156
|
+
if (!uiPackagePath) {
|
|
3157
|
+
logger.warn("Could not find @screenbook/ui package");
|
|
3158
|
+
logger.log(` Run ${logger.code("npm install @screenbook/ui")} to install it`);
|
|
3159
|
+
return;
|
|
3160
|
+
}
|
|
3161
|
+
const screensJsonPath = join(cwd, outDir, "screens.json");
|
|
3162
|
+
const uiScreensDir = join(uiPackagePath, ".screenbook");
|
|
3163
|
+
if (!existsSync(uiScreensDir)) mkdirSync(uiScreensDir, { recursive: true });
|
|
3164
|
+
if (existsSync(screensJsonPath)) copyFileSync(screensJsonPath, join(uiScreensDir, "screens.json"));
|
|
3165
|
+
logger.blank();
|
|
3166
|
+
logger.info(`Starting UI server on ${logger.highlight(`http://localhost:${port}`)}`);
|
|
3167
|
+
logger.blank();
|
|
3168
|
+
const astroProcess = spawn("npx", [
|
|
3169
|
+
"astro",
|
|
3170
|
+
"dev",
|
|
3171
|
+
"--port",
|
|
3172
|
+
port
|
|
3173
|
+
], {
|
|
3174
|
+
cwd: uiPackagePath,
|
|
3175
|
+
stdio: "inherit",
|
|
3176
|
+
shell: true
|
|
3177
|
+
});
|
|
3178
|
+
astroProcess.on("error", (error) => {
|
|
3179
|
+
logger.error(`Failed to start server: ${error.message}`);
|
|
3180
|
+
process.exit(1);
|
|
3181
|
+
});
|
|
3182
|
+
astroProcess.on("close", (code) => {
|
|
3183
|
+
process.exit(code ?? 0);
|
|
3184
|
+
});
|
|
3185
|
+
process.on("SIGINT", () => {
|
|
3186
|
+
astroProcess.kill("SIGINT");
|
|
3187
|
+
});
|
|
3188
|
+
process.on("SIGTERM", () => {
|
|
3189
|
+
astroProcess.kill("SIGTERM");
|
|
3190
|
+
});
|
|
3191
|
+
}
|
|
3063
3192
|
const initCommand = define({
|
|
3064
3193
|
name: "init",
|
|
3065
3194
|
description: "Initialize Screenbook in a project",
|
|
@@ -3074,12 +3203,46 @@ const initCommand = define({
|
|
|
3074
3203
|
type: "boolean",
|
|
3075
3204
|
description: "Skip framework auto-detection",
|
|
3076
3205
|
default: false
|
|
3206
|
+
},
|
|
3207
|
+
generate: {
|
|
3208
|
+
type: "boolean",
|
|
3209
|
+
description: "Auto-generate screen.meta.ts files (--no-generate to skip)",
|
|
3210
|
+
default: void 0,
|
|
3211
|
+
negatable: true
|
|
3212
|
+
},
|
|
3213
|
+
dev: {
|
|
3214
|
+
type: "boolean",
|
|
3215
|
+
description: "Start development server after init (--no-dev to skip)",
|
|
3216
|
+
default: void 0,
|
|
3217
|
+
negatable: true
|
|
3218
|
+
},
|
|
3219
|
+
yes: {
|
|
3220
|
+
type: "boolean",
|
|
3221
|
+
short: "y",
|
|
3222
|
+
description: "Answer yes to all prompts",
|
|
3223
|
+
default: false
|
|
3224
|
+
},
|
|
3225
|
+
ci: {
|
|
3226
|
+
type: "boolean",
|
|
3227
|
+
description: "CI mode (no prompts, generate only)",
|
|
3228
|
+
default: false
|
|
3229
|
+
},
|
|
3230
|
+
port: {
|
|
3231
|
+
type: "string",
|
|
3232
|
+
short: "p",
|
|
3233
|
+
description: "Port for the dev server",
|
|
3234
|
+
default: "4321"
|
|
3077
3235
|
}
|
|
3078
3236
|
},
|
|
3079
3237
|
run: async (ctx) => {
|
|
3080
3238
|
const cwd = process.cwd();
|
|
3081
3239
|
const force = ctx.values.force ?? false;
|
|
3082
3240
|
const skipDetect = ctx.values.skipDetect ?? false;
|
|
3241
|
+
const generateFlag = ctx.values.generate;
|
|
3242
|
+
const devFlag = ctx.values.dev;
|
|
3243
|
+
const yesAll = ctx.values.yes ?? false;
|
|
3244
|
+
const ciMode = ctx.values.ci ?? false;
|
|
3245
|
+
const port = ctx.values.port ?? "4321";
|
|
3083
3246
|
logger.info("Initializing Screenbook...");
|
|
3084
3247
|
logger.blank();
|
|
3085
3248
|
let framework = null;
|
|
@@ -3088,11 +3251,13 @@ const initCommand = define({
|
|
|
3088
3251
|
if (framework) logger.itemSuccess(`Detected: ${framework.name}`);
|
|
3089
3252
|
else {
|
|
3090
3253
|
logger.log(" Could not auto-detect framework");
|
|
3091
|
-
|
|
3092
|
-
framework = await promptFrameworkSelection();
|
|
3093
|
-
if (framework) {
|
|
3254
|
+
if (!ciMode && isInteractive()) {
|
|
3094
3255
|
logger.blank();
|
|
3095
|
-
|
|
3256
|
+
framework = await promptFrameworkSelection();
|
|
3257
|
+
if (framework) {
|
|
3258
|
+
logger.blank();
|
|
3259
|
+
logger.itemSuccess(`Selected: ${framework.name}`);
|
|
3260
|
+
}
|
|
3096
3261
|
}
|
|
3097
3262
|
}
|
|
3098
3263
|
}
|
|
@@ -3116,8 +3281,54 @@ const initCommand = define({
|
|
|
3116
3281
|
}
|
|
3117
3282
|
logger.blank();
|
|
3118
3283
|
logger.done("Screenbook initialized successfully!");
|
|
3119
|
-
|
|
3120
|
-
|
|
3284
|
+
if (!framework?.routesPattern) {
|
|
3285
|
+
printValueProposition();
|
|
3286
|
+
printNextSteps(false);
|
|
3287
|
+
return;
|
|
3288
|
+
}
|
|
3289
|
+
const routeFileCount = await countRouteFiles(framework.routesPattern, cwd);
|
|
3290
|
+
if (routeFileCount === 0) {
|
|
3291
|
+
printValueProposition();
|
|
3292
|
+
printNextSteps(true);
|
|
3293
|
+
return;
|
|
3294
|
+
}
|
|
3295
|
+
logger.blank();
|
|
3296
|
+
if (!await resolveOption({
|
|
3297
|
+
explicitValue: generateFlag,
|
|
3298
|
+
yesAll,
|
|
3299
|
+
ciMode,
|
|
3300
|
+
ciDefault: true,
|
|
3301
|
+
promptMessage: `Found ${routeFileCount} route files. Generate screen.meta.ts files?`
|
|
3302
|
+
})) {
|
|
3303
|
+
printValueProposition();
|
|
3304
|
+
printNextSteps(true);
|
|
3305
|
+
return;
|
|
3306
|
+
}
|
|
3307
|
+
logger.blank();
|
|
3308
|
+
logger.info("Generating screen metadata...");
|
|
3309
|
+
logger.blank();
|
|
3310
|
+
await runGenerate(framework.routesPattern, cwd);
|
|
3311
|
+
if (ciMode) {
|
|
3312
|
+
logger.blank();
|
|
3313
|
+
logger.done("Initialization complete!");
|
|
3314
|
+
return;
|
|
3315
|
+
}
|
|
3316
|
+
logger.blank();
|
|
3317
|
+
if (!await resolveOption({
|
|
3318
|
+
explicitValue: devFlag,
|
|
3319
|
+
yesAll,
|
|
3320
|
+
ciMode,
|
|
3321
|
+
ciDefault: false,
|
|
3322
|
+
promptMessage: "Start the development server?"
|
|
3323
|
+
})) {
|
|
3324
|
+
logger.blank();
|
|
3325
|
+
logger.log(logger.bold("Next step:"));
|
|
3326
|
+
logger.log(` Run ${logger.code("screenbook dev")} to start the UI server`);
|
|
3327
|
+
return;
|
|
3328
|
+
}
|
|
3329
|
+
logger.blank();
|
|
3330
|
+
logger.info("Starting development server...");
|
|
3331
|
+
await startDevServer(framework.metaPattern, ".screenbook", cwd, port);
|
|
3121
3332
|
}
|
|
3122
3333
|
});
|
|
3123
3334
|
|