@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 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
- logger.blank();
3092
- framework = await promptFrameworkSelection();
3093
- if (framework) {
3254
+ if (!ciMode && isInteractive()) {
3094
3255
  logger.blank();
3095
- logger.itemSuccess(`Selected: ${framework.name}`);
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
- printValueProposition();
3120
- printNextSteps(framework !== null);
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