at-builder 1.4.4 → 1.5.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/src/index.ts DELETED
@@ -1,387 +0,0 @@
1
- #!/usr/bin/env node
2
- import { spawn } from "child_process";
3
- import path from "path";
4
- import fs from "fs";
5
- import { Command } from 'commander';
6
- import { getVersion, getHelpInfo, setupEnv } from "./constants/config";
7
- import { runDiagnostics, fixIssues } from "./services/doctor";
8
- import logger from "./services/logger";
9
- /**
10
- * Checks if the .env file exists at the current working directory
11
- *
12
- * @throws {Error} If the .env file is not found
13
- */
14
- const checkEnvFile = () => {
15
- logger.info("checkEnvFile", "Checking if .env file exists at current working directory");
16
- try {
17
- // Check if the file exists and is readable
18
- fs.accessSync(path.join(process.cwd(), ".env"), fs.constants.R_OK);
19
- logger.info("checkEnvFile", ".env file found");
20
- } catch (error) {
21
- // If the file does not exist, provide helpful guidance
22
- console.error('\x1b[31m%s\x1b[0m', `āŒ Error: .env file not found at ${process.cwd()}`);
23
- console.error('\x1b[33m%s\x1b[0m', 'šŸ’” Run "atb init" to create .env file or "atb doctor --fix" to diagnose and fix configuration issues.');
24
- logger.error("checkEnvFile", `Error: Couldn't find .env file at location ${process.cwd()}`);
25
- throw error;
26
- }
27
- };
28
-
29
- // Prepare environment for spawning processes
30
- const productionEnv = {
31
- ...process.env,
32
- executionPath: process.cwd()
33
- };
34
-
35
- // Commander setup with proper subcommands
36
- const setupCommander = async () => {
37
- logger.info("setupCommander", "Setting up commander");
38
- const version = await getVersion();
39
- const program = new Command();
40
-
41
- program
42
- .name('atb')
43
- .description('Adobe Target Activity Development CLI')
44
- .version(version, "-V, --version")
45
- .option("-v, --verbose", "Enable verbose logging");
46
-
47
- // Add subcommands with proper actions
48
- program
49
- .command('init')
50
- .description('Initialize project with .env configuration and templates')
51
- .action(async (options, command) => {
52
- const globalOpts = command.parent.opts();
53
- await handleInit(globalOpts.verbose);
54
- });
55
-
56
- program
57
- .command('new')
58
- .description('Create a new Adobe Target activity with variations')
59
- .action(async (options, command) => {
60
- const globalOpts = command.parent.opts();
61
- await handleNew(globalOpts.verbose);
62
- });
63
-
64
- program
65
- .command('build')
66
- .description('Build activity for development or production')
67
- .option('--prod', 'Build for production deployment')
68
- .action(async (options, command) => {
69
- const globalOpts = command.parent.opts();
70
- checkEnvFile();
71
- await handleBuild(options.prod, globalOpts.verbose);
72
- });
73
-
74
- program
75
- .command('dev')
76
- .description('Start development server with file watching')
77
- .option('--browser', 'Open in browser automatically')
78
- .action(async (options, command) => {
79
- const globalOpts = command.parent.opts();
80
- checkEnvFile();
81
- await handleDev(options.browser, globalOpts.verbose);
82
- });
83
-
84
- program
85
- .command('deploy')
86
- .description('Deploy activity to Adobe Target using at-deploy.js')
87
- .option('--dry-run', 'Run deployment in dry-run mode without actual deployment')
88
- .option('--force', 'Override the 60s post-deploy cooldown lock')
89
- .action(async (options, command) => {
90
- const globalOpts = command.parent.opts();
91
- checkEnvFile();
92
- await handleDeploy(options.dryRun, options.force, globalOpts.verbose);
93
- });
94
-
95
- program
96
- .command('sync')
97
- .description('Sync build.config.json with the Adobe Target activity definition')
98
- .option('--scaffold', 'Auto-create missing variation folders with boilerplate')
99
- .action(async (options, command) => {
100
- const globalOpts = command.parent.opts();
101
- checkEnvFile();
102
- await handleSync(options.scaffold, globalOpts.verbose);
103
- });
104
-
105
- program
106
- .command('doctor')
107
- .description('Diagnose and fix project configuration issues')
108
- .option('--fix', 'Automatically fix detected issues')
109
- .action(async (options, command) => {
110
- const globalOpts = command.parent.opts();
111
- await handleDoctor(options.fix, globalOpts.verbose);
112
- });
113
-
114
- program
115
- .command('install-extension')
116
- .description('Install the at-builder VSCode extension from the Marketplace')
117
- .option('--editor <bin>', 'Editor CLI to use (e.g. code, agy, cursor, codium)', 'code')
118
- .action(async (options, command) => {
119
- const globalOpts = command.parent.opts();
120
- await handleInstallExtension(options.editor, globalOpts.verbose);
121
- });
122
-
123
- await program.parseAsync(process.argv);
124
-
125
- logger.info("setupCommander", "Commander setup complete");
126
- };
127
-
128
-
129
-
130
- /**
131
- * Spawn a command using `npm` with the given command array and environment object.
132
- *
133
- * Any entries in `scriptArgs` are forwarded to the underlying script via npm's
134
- * `--` passthrough (e.g. `npm run foo -- --dry-run`).
135
- *
136
- * @param commandArr - The array of command strings to pass to `npm`.
137
- * @param env - The environment object to pass to the spawned process.
138
- * @param scriptArgs - Optional flags/args to forward to the npm script itself.
139
- */
140
- const runCommand = (commandArr: string[], env: NodeJS.ProcessEnv, scriptArgs: string[] = []): void => {
141
- const fullArgs = scriptArgs.length > 0
142
- ? [...commandArr, "--", ...scriptArgs]
143
- : commandArr;
144
-
145
- logger.info("runCommand", `Running npm command [${fullArgs.join(", ")}]`);
146
- logger.info("runCommand", `Current working directory: ${process.cwd()}`);
147
-
148
- spawn("npm", fullArgs, {
149
- cwd: path.join(__dirname, "../"),
150
- env,
151
- shell: true,
152
- stdio: "inherit",
153
- });
154
- };
155
-
156
- /**
157
- * Handles the init command
158
- */
159
- const handleInit = async (verbose: boolean): Promise<void> => {
160
- if (verbose) logger.info("verbose", "Initializing new .env file");
161
-
162
- // Run npm init in the current working directory (where user ran atb init)
163
- spawn("npm", ["init", "-y"], {
164
- cwd: process.cwd(),
165
- env: process.env,
166
- shell: true,
167
- stdio: "inherit",
168
- });
169
-
170
- // Install @babel/runtime as dev dependency in the current directory
171
- spawn("npm", ["install", "@babel/runtime", "-D"], {
172
- cwd: process.cwd(),
173
- env: process.env,
174
- shell: true,
175
- stdio: "inherit",
176
- });
177
-
178
- setupEnv(process.cwd());
179
- };
180
-
181
- /**
182
- * Handles the new command
183
- */
184
- const handleNew = async (verbose: boolean): Promise<void> => {
185
- if (verbose) logger.info("verbose", "Creating new activity");
186
- runCommand(["run", "atb:plop:new:activity"], productionEnv);
187
- };
188
-
189
- /**
190
- * Handles the build command
191
- */
192
- const handleBuild = async (prod: boolean, verbose: boolean): Promise<void> => {
193
- if (verbose) logger.info("verbose", `Building in ${prod ? 'production' : 'development'} mode`);
194
-
195
- if (prod) {
196
- process.env['NODE_ENV'] = 'production';
197
- logger.info("handleBuild", "Running build with production environment");
198
- runCommand(['run', 'atb:build:prod'], productionEnv);
199
- } else {
200
- logger.info("handleBuild", "Running build with development environment");
201
- runCommand(['run', 'atb:build:dev'], productionEnv);
202
- }
203
- };
204
-
205
- /**
206
- * Handles the dev command
207
- */
208
- const handleDev = async (browser: boolean, verbose: boolean): Promise<void> => {
209
- if (verbose) logger.info("verbose", `Starting development server with browser=${browser}`);
210
-
211
- const commandArr = ['run', browser ? 'atb:build:dev:puppeteer' : 'atb:build:dev'];
212
- logger.info("handleDev", `Running command: ${commandArr.join(', ')}`);
213
-
214
- runCommand(commandArr, productionEnv);
215
- };
216
-
217
- /**
218
- * Handles the deploy command
219
- */
220
- const handleDeploy = async (dryRun: boolean, force: boolean, verbose: boolean): Promise<void> => {
221
- if (verbose) logger.info("verbose", `Deploying to Adobe Target with dry-run=${dryRun} force=${force}`);
222
-
223
- logger.info("handleDeploy", "Running Adobe Target deployment");
224
-
225
- const scriptArgs: string[] = [];
226
- if (dryRun) scriptArgs.push('--dry-run');
227
- if (force) scriptArgs.push('--force');
228
-
229
- runCommand(['run', 'atb:build:deploy'], productionEnv, scriptArgs);
230
- };
231
-
232
- /**
233
- * Handles the sync command
234
- */
235
- const handleSync = async (scaffold: boolean, verbose: boolean): Promise<void> => {
236
- if (verbose) logger.info("verbose", `Syncing build.config.json with scaffold=${scaffold}`);
237
-
238
- logger.info("handleSync", "Running Adobe Target sync");
239
-
240
- const scriptArgs: string[] = [];
241
- if (scaffold) scriptArgs.push('--scaffold');
242
-
243
- runCommand(['run', 'atb:build:sync'], productionEnv, scriptArgs);
244
- };
245
-
246
- /**
247
- * Handles the install-extension command.
248
- *
249
- * Installs the at-builder VSCode extension from the VS Code Marketplace
250
- * using the editor's CLI. Defaults to `code`; users on Cursor/VSCodium
251
- * can pass --editor.
252
- */
253
- /**
254
- * Per-editor install hints for when the CLI binary isn't on PATH. Each
255
- * recognized editor gets a one-line, copy-pasteable suggestion. Anything
256
- * else falls back to a generic hint.
257
- */
258
- const EDITOR_INSTALL_HINTS: Record<string, string> = {
259
- code: 'Open VSCode and run Command Palette → "Shell Command: Install \'code\' command in PATH".',
260
- agy: 'Open Antigravity IDE and run Command Palette → "Shell Command: Install \'agy\' command in PATH".',
261
- cursor: 'Open Cursor and run Command Palette → "Shell Command: Install \'cursor\' command in PATH".',
262
- codium: 'Open VSCodium and run Command Palette → "Shell Command: Install \'codium\' command in PATH".',
263
- };
264
-
265
- const reportEditorNotFound = (editor: string): void => {
266
- console.error(`āŒ "${editor}" CLI not found in PATH.`);
267
- const hint = EDITOR_INSTALL_HINTS[editor]
268
- || `Make sure the "${editor}" CLI is installed and on your PATH, or pass a different --editor.`;
269
- console.error(`šŸ’” ${hint}`);
270
- };
271
-
272
- const handleInstallExtension = async (editor: string, verbose: boolean): Promise<void> => {
273
- const extensionId = "UpendraSengar.at-builder";
274
-
275
- if (verbose) logger.info("verbose", `Installing ${extensionId} via ${editor}`);
276
-
277
- console.log(`šŸ“¦ Installing ${extensionId} from the Marketplace via "${editor}"...`);
278
-
279
- const result = spawn(editor, ["--install-extension", extensionId], {
280
- stdio: "inherit",
281
- shell: true
282
- });
283
-
284
- result.on("error", (err) => {
285
- if ((err as NodeJS.ErrnoException).code === "ENOENT") {
286
- reportEditorNotFound(editor);
287
- } else {
288
- console.error(`āŒ Failed to launch "${editor}": ${err.message}`);
289
- }
290
- process.exit(1);
291
- });
292
-
293
- result.on("exit", (code) => {
294
- if (code === 0) {
295
- console.log("āœ… Extension installed. Reload your editor window to activate.");
296
- return;
297
- }
298
- // 127 = shell's "command not found". With shell:true the spawn 'error'
299
- // event never fires for missing CLIs, so we surface the same hint here.
300
- if (code === 127) {
301
- reportEditorNotFound(editor);
302
- process.exit(1);
303
- }
304
- console.error(`āŒ "${editor} --install-extension" exited with code ${code}.`);
305
- process.exit(code ?? 1);
306
- });
307
- };
308
-
309
- /**
310
- * Handles the doctor command
311
- */
312
- const handleDoctor = async (autoFix: boolean, verbose: boolean): Promise<void> => {
313
- if (verbose) logger.info("verbose", `Running diagnostics with auto-fix=${autoFix}`);
314
-
315
- logger.info("handleDoctor", "Diagnosing project configuration");
316
-
317
- try {
318
- // Run diagnostics
319
- const issues = await runDiagnostics(process.cwd(), verbose);
320
-
321
- if (issues.length === 0) {
322
- console.log("āœ… No issues found. Your project configuration is healthy!");
323
- return;
324
- }
325
-
326
- // Display issues
327
- console.log(`\nšŸ” Found ${issues.length} issue(s):\n`);
328
- issues.forEach((issue, index) => {
329
- console.log(`${index + 1}. ${issue.severity === 'error' ? 'āŒ' : 'āš ļø'} ${issue.message}`);
330
- if (issue.suggestion) {
331
- console.log(` šŸ’” ${issue.suggestion}`);
332
- }
333
- });
334
-
335
- if (autoFix) {
336
- console.log('\nšŸ”§ Attempting to fix issues...\n');
337
- const fixed = await fixIssues(issues, process.cwd(), verbose);
338
- console.log(`\nāœ… Fixed ${fixed} issue(s).`);
339
-
340
- if (fixed < issues.length) {
341
- console.log(`āš ļø ${issues.length - fixed} issue(s) require manual attention.`);
342
- }
343
- } else {
344
- console.log('\nšŸ’” Run `atb doctor --fix` to automatically fix resolvable issues.');
345
- }
346
-
347
- } catch (error) {
348
- logger.error("handleDoctor", `Doctor command failed: ${error.message}`);
349
- process.exit(1);
350
- }
351
- };
352
-
353
- // Command execution logic
354
- /**
355
- * Main command execution entry point
356
- */
357
- const executeCommand = async () => {
358
- logger.info("executeCommand", "Entering executeCommand");
359
- await setupCommander();
360
- logger.info("executeCommand", "Exiting executeCommand");
361
- };
362
-
363
- // Print command help
364
- const printCommandHelp = async () => {
365
- const help = await getHelpInfo();
366
- console.log(help);
367
- process.exit(0);
368
- };
369
-
370
- /**
371
- * Main program flow
372
- */
373
- const main = async () => {
374
- try {
375
- await executeCommand();
376
- } catch (error) {
377
- logger.error("main", error.message);
378
- process.exit(1);
379
- }
380
- };
381
-
382
- // Print custom help if no arguments, otherwise let Commander.js handle everything
383
- if (process.argv.length <= 2) {
384
- printCommandHelp();
385
- } else {
386
- main();
387
- }