at-builder 1.2.0 → 1.2.2

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,7 +1,10 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
- "Bash(npm run build:prod:*)"
4
+ "Bash(npm run build:prod:*)",
5
+ "Bash(node:*)",
6
+ "Bash(npx tsc:*)",
7
+ "Bash(find:*)"
5
8
  ],
6
9
  "deny": []
7
10
  }
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * A custom Webpack plugin to wrap bundled assets with a header and footer.
5
+ */
6
+ class CustomWrapperPlugin {
7
+ /**
8
+ * Creates an instance of CustomWrapperPlugin.
9
+ * @param {object} options - The plugin options.
10
+ * @param {RegExp} options.test - A regular expression to match file paths that should be wrapped.
11
+ * @param {string|Function} [options.header=''] - The content to prepend to the file. Can be a string or a function that returns a string.
12
+ * @param {string|Function} [options.footer=''] - The content to append to the file. Can be a string or a function that returns a string.
13
+ */
14
+ constructor(options) {
15
+ this.options = options || {};
16
+ }
17
+
18
+ /**
19
+ * Applies the plugin to the Webpack compiler.
20
+ * @param {import('webpack').Compiler} compiler - The Webpack compiler instance.
21
+ */
22
+ apply(compiler) {
23
+ const pluginName = this.constructor.name;
24
+ const { webpack } = compiler;
25
+ const { Compilation } = webpack;
26
+
27
+ compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
28
+ compilation.hooks.processAssets.tap(
29
+ {
30
+ name: pluginName,
31
+ stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE
32
+ },
33
+ (assets) => {
34
+ for (const pathname in assets) {
35
+ if (this.options.test && this.options.test.test(pathname)) {
36
+ const asset = compilation.getAsset(pathname);
37
+ const source = asset.source.source();
38
+ const args = { hash: compilation.hash };
39
+
40
+ const wrappedSource =
41
+ (typeof this.options.header === 'function'
42
+ ? this.options.header(source, args)
43
+ : this.options.header || '') +
44
+ source +
45
+ (typeof this.options.footer === 'function'
46
+ ? this.options.footer(source, args)
47
+ : this.options.footer || '');
48
+
49
+ compilation.updateAsset(
50
+ pathname,
51
+ new webpack.sources.RawSource(wrappedSource)
52
+ );
53
+ }
54
+ }
55
+ }
56
+ );
57
+ });
58
+ }
59
+ }
60
+
61
+ module.exports = CustomWrapperPlugin;
@@ -34,7 +34,13 @@ exports.getVersion = exports.getVersion;
34
34
  * @returns {string} The formatted text
35
35
  */
36
36
  const formatText = (text, color = "white", bold = false, underline = false) => {
37
- let formattedText = cli_color_1.default[color](text);
37
+ let formattedText = text;
38
+ // Apply color
39
+ const colorFn = cli_color_1.default[color];
40
+ if (typeof colorFn === 'function') {
41
+ formattedText = colorFn(text);
42
+ }
43
+ // Apply formatting
38
44
  if (bold)
39
45
  formattedText = cli_color_1.default.bold(formattedText);
40
46
  if (underline)
@@ -48,26 +54,57 @@ const formatText = (text, color = "white", bold = false, underline = false) => {
48
54
  const getHelpInfo = async () => {
49
55
  const version = await (0, exports.getVersion)();
50
56
  return `
51
- ${formatText(`at-builder (version: ${version})`, 'green', false, true)}
57
+ ${formatText(`🎯 at-builder`, 'cyan', true)} ${formatText(`v${version}`, 'gray')}
52
58
 
53
- ${formatText("A simple command to run Adobe Target builder.", 'white', true)}
59
+ ${formatText("Adobe Target Activity Development CLI", 'white', true)}
54
60
 
55
- Usage: \`atb\`
61
+ A streamlined command-line tool for creating, building, and deploying Adobe Target
62
+ A/B testing activities with modern web technologies.
56
63
 
57
- Available Commands:
64
+ ${formatText("USAGE", 'yellow', true)}
65
+ ${formatText("atb", 'cyan')} ${formatText("<command> [options]", 'gray')}
58
66
 
59
- ${formatText("new", 'blue', true, true)} Creates a new activity
60
- ${formatText("build", 'blue', true, true)} Create build
61
- ${formatText("build -p", 'blue', true, true)} Create production build
67
+ ${formatText("COMMANDS", 'yellow', true)}
68
+ ${formatText("init", 'cyan', true)} Initialize project with .env configuration and templates
69
+ ${formatText("new", 'cyan', true)} Create a new Adobe Target activity with variations
70
+ ${formatText("build", 'cyan', true)} Build activity for development
71
+ ${formatText("build --prod", 'cyan', true)} Build for production deployment
72
+ ${formatText("dev", 'cyan', true)} Start development server with file watching
73
+ ${formatText("dev --browser", 'cyan', true)} Start development server and open in browser
74
+
75
+ ${formatText("GLOBAL OPTIONS", 'yellow', true)}
76
+ ${formatText("-v, --verbose", 'green')} Enable detailed logging
77
+ ${formatText("-V, --version", 'green')} Display version information
78
+ ${formatText("-h, --help", 'green')} Show help information
62
79
 
63
- Example:
80
+ ${formatText("EXAMPLES", 'yellow', true)}
81
+ ${formatText("# Initialize new project", 'gray')}
82
+ ${formatText("atb init", 'white')}
83
+
84
+ ${formatText("# Create new activity", 'gray')}
85
+ ${formatText("atb new", 'white')}
86
+
87
+ ${formatText("# Development build with hot reload", 'gray')}
88
+ ${formatText("atb build", 'white')}
89
+
90
+ ${formatText("# Production build for Adobe Target deployment", 'gray')}
91
+ ${formatText("atb build --prod", 'white')}
92
+
93
+ ${formatText("# Development server with browser", 'gray')}
94
+ ${formatText("atb dev --browser", 'white')}
64
95
 
65
- atb -c new # Creates a new activity
66
- atb -c build # Creates a build
67
- atb -p -c build # Creates a build for production
96
+ ${formatText("WORKFLOW", 'yellow', true)}
97
+ ${formatText("1.", 'cyan')} Run ${formatText("atb init", 'white')} to set up project configuration
98
+ ${formatText("2.", 'cyan')} Run ${formatText("atb new", 'white')} to create activity templates
99
+ ${formatText("3.", 'cyan')} Develop variations in ${formatText("/Activities/{name}/Variation-*/", 'gray')}
100
+ ${formatText("4.", 'cyan')} Run ${formatText("atb build", 'white')} for development testing
101
+ ${formatText("5.", 'cyan')} Run ${formatText("atb build --prod", 'white')} for Adobe Target deployment
68
102
 
69
- For more information on a specific command, run \`atb <command> --help\`
70
- `;
103
+ ${formatText("SUPPORT", 'yellow', true)}
104
+ Repository: ${formatText("https://github.com/upesenga/at-builder", 'blue')}
105
+ Issues: ${formatText("https://github.com/upesenga/at-builder/issues", 'blue')}
106
+ License: ${formatText("MIT", 'green')}
107
+ `;
71
108
  };
72
109
  exports.getHelpInfo = getHelpInfo;
73
110
  const setupEnv = async (basePath) => {
package/bin/index.js CHANGED
@@ -7,7 +7,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  const child_process_1 = require("child_process");
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const fs_1 = __importDefault(require("fs"));
10
- const inquirer_1 = __importDefault(require("inquirer"));
11
10
  const commander_1 = require("commander");
12
11
  const config_1 = require("./constants/config");
13
12
  const logger_1 = __importDefault(require("./services/logger"));
@@ -34,21 +33,51 @@ const productionEnv = {
34
33
  ...process.env,
35
34
  executionPath: process.cwd()
36
35
  };
37
- // Commander setup
36
+ // Commander setup with proper subcommands
38
37
  const setupCommander = async () => {
39
38
  logger_1.default.info("setupCommander", "Setting up commander");
40
39
  const version = await (0, config_1.getVersion)();
41
- logger_1.default.info("setupCommander", `Version: ${version}`);
42
40
  const program = new commander_1.Command();
43
41
  program
42
+ .name('atb')
43
+ .description('Adobe Target Activity Development CLI')
44
44
  .version(version, "-V, --version")
45
- .usage("[OPTIONS]...")
46
- .option("-c, --command <value>")
47
- .option("-p, --prod")
48
- .option("-v, --verbose", "Enable verbose logging")
49
- .parse(process.argv);
45
+ .option("-v, --verbose", "Enable verbose logging");
46
+ // Add subcommands with proper actions
47
+ program
48
+ .command('init')
49
+ .description('Initialize project with .env configuration and templates')
50
+ .action(async (options, command) => {
51
+ const globalOpts = command.parent.opts();
52
+ await handleInit(globalOpts.verbose);
53
+ });
54
+ program
55
+ .command('new')
56
+ .description('Create a new Adobe Target activity with variations')
57
+ .action(async (options, command) => {
58
+ const globalOpts = command.parent.opts();
59
+ await handleNew(globalOpts.verbose);
60
+ });
61
+ program
62
+ .command('build')
63
+ .description('Build activity for development or production')
64
+ .option('--prod', 'Build for production deployment')
65
+ .action(async (options, command) => {
66
+ const globalOpts = command.parent.opts();
67
+ checkEnvFile();
68
+ await handleBuild(options.prod, globalOpts.verbose);
69
+ });
70
+ program
71
+ .command('dev')
72
+ .description('Start development server with file watching')
73
+ .option('--browser', 'Open in browser automatically')
74
+ .action(async (options, command) => {
75
+ const globalOpts = command.parent.opts();
76
+ checkEnvFile();
77
+ await handleDev(options.browser, globalOpts.verbose);
78
+ });
79
+ await program.parseAsync(process.argv);
50
80
  logger_1.default.info("setupCommander", "Commander setup complete");
51
- return program.opts();
52
81
  };
53
82
  /**
54
83
  * Spawn a command using `npm` with the given command array and environment object.
@@ -69,120 +98,89 @@ const runCommand = (commandArr, env) => {
69
98
  });
70
99
  };
71
100
  /**
72
- * Handles the build process based on the given options.
73
- *
74
- * @param {boolean} prod - If true, runs the build with production environment.
75
- * If false, runs the build with development environment.
101
+ * Handles the init command
102
+ */
103
+ const handleInit = async (verbose) => {
104
+ if (verbose)
105
+ logger_1.default.info("verbose", "Initializing new .env file");
106
+ // Run npm init in the current working directory (where user ran atb init)
107
+ (0, child_process_1.spawn)("npm", ["init", "-y"], {
108
+ cwd: process.cwd(),
109
+ env: process.env,
110
+ shell: true,
111
+ stdio: "inherit",
112
+ });
113
+ // Install @babel/runtime as dev dependency in the current directory
114
+ (0, child_process_1.spawn)("npm", ["install", "@babel/runtime", "-D"], {
115
+ cwd: process.cwd(),
116
+ env: process.env,
117
+ shell: true,
118
+ stdio: "inherit",
119
+ });
120
+ (0, config_1.setupEnv)(process.cwd());
121
+ };
122
+ /**
123
+ * Handles the new command
124
+ */
125
+ const handleNew = async (verbose) => {
126
+ if (verbose)
127
+ logger_1.default.info("verbose", "Creating new activity");
128
+ runCommand(["run", "new-activity"], productionEnv);
129
+ };
130
+ /**
131
+ * Handles the build command
76
132
  */
77
- const handleBuild = async (prod) => {
78
- logger_1.default.info("handleBuild", `Handling build with options: production=${prod}`);
133
+ const handleBuild = async (prod, verbose) => {
134
+ if (verbose)
135
+ logger_1.default.info("verbose", `Building in ${prod ? 'production' : 'development'} mode`);
79
136
  if (prod) {
80
- // Set production environment
81
137
  process.env['NODE_ENV'] = 'production';
82
138
  logger_1.default.info("handleBuild", "Running build with production environment");
83
- // Run the build command
84
139
  runCommand(['run', 'build-prod'], productionEnv);
85
140
  }
86
141
  else {
87
142
  logger_1.default.info("handleBuild", "Running build with development environment");
88
- // Prompt the user to ask if they want to open the build in a browser tab
89
- const { openInBrowser } = await inquirer_1.default.prompt([
90
- {
91
- type: 'confirm',
92
- name: 'openInBrowser',
93
- message: 'Do you want to open your build in a browser tab?',
94
- default: false,
95
- },
96
- ]);
97
- logger_1.default.info("handleBuild", `User opted for opening in browser: ${openInBrowser}`);
98
- const commandArr = ['run', 'dev'];
99
- if (openInBrowser) {
100
- // If the user wants to open the build in a browser, run the build with
101
- // puppeteer
102
- commandArr[1] = 'dev-puppeteer';
103
- }
104
- logger_1.default.info("handleBuild", `Running command: ${commandArr.join(', ')}`);
105
- // Run the build command
106
- runCommand(commandArr, productionEnv);
107
- // TO_DO: verify
108
- // process.exit(0);
143
+ runCommand(['run', 'dev'], productionEnv);
109
144
  }
110
145
  };
146
+ /**
147
+ * Handles the dev command
148
+ */
149
+ const handleDev = async (browser, verbose) => {
150
+ if (verbose)
151
+ logger_1.default.info("verbose", `Starting development server with browser=${browser}`);
152
+ const commandArr = ['run', browser ? 'dev-puppeteer' : 'dev'];
153
+ logger_1.default.info("handleDev", `Running command: ${commandArr.join(', ')}`);
154
+ runCommand(commandArr, productionEnv);
155
+ };
111
156
  // Command execution logic
112
157
  /**
113
- * Handles the command execution based on the given options.
114
- *
115
- * @returns {Promise<void>}
158
+ * Main command execution entry point
116
159
  */
117
160
  const executeCommand = async () => {
118
161
  logger_1.default.info("executeCommand", "Entering executeCommand");
119
- // Check if .env file is present
120
- // Get the command options
121
- const options = await setupCommander();
122
- if (options.command !== 'init')
123
- checkEnvFile();
124
- logger_1.default.info("executeCommand", `Options: ${JSON.stringify(options)}`);
125
- /**
126
- * Logs a message if verbose mode is enabled
127
- * @param {string} message - The message to log
128
- */
129
- const verboseLog = (message) => {
130
- if (options.verbose) {
131
- logger_1.default.info("verbose", message);
132
- }
133
- };
134
- switch (options.command) {
135
- case "init":
136
- // Initialize new .env file
137
- verboseLog("Initializing new .env file");
138
- runCommand(["init -y"], productionEnv);
139
- runCommand(["install @babel/runtime", "-D"], productionEnv);
140
- (0, config_1.setupEnv)(process.cwd());
141
- break;
142
- case "new":
143
- // Create new activity
144
- verboseLog("Creating new activity");
145
- runCommand(["run", "new-activity"], productionEnv);
146
- break;
147
- case "build":
148
- // Build the application in production or development mode
149
- verboseLog(`Building in ${options.prod ? 'production' : 'development'} mode`);
150
- await handleBuild(options.prod);
151
- break;
152
- default:
153
- // Print command help
154
- printCommandHelp();
155
- process.exit(0);
156
- break;
157
- }
162
+ await setupCommander();
158
163
  logger_1.default.info("executeCommand", "Exiting executeCommand");
159
164
  };
160
165
  // Print command help
161
166
  const printCommandHelp = async () => {
162
167
  const help = await (0, config_1.getHelpInfo)();
163
- logger_1.default.info("printCommandHelp", help);
168
+ console.log(help);
164
169
  process.exit(0);
165
170
  };
166
171
  /**
167
172
  * Main program flow
168
- *
169
- * This function is the main entry point of the program. It attempts to execute
170
- * the command specified by the user. If the command fails, it logs the error
171
- * message and exits with code 1.
172
173
  */
173
174
  const main = async () => {
174
175
  try {
175
- // Execute the command specified by the user
176
176
  await executeCommand();
177
177
  }
178
178
  catch (error) {
179
- // Log the error message if the command fails
180
179
  logger_1.default.error("main", error.message);
181
- // Exit with code 1 if the command fails
182
180
  process.exit(1);
183
181
  }
184
182
  };
185
- // Print help if no arguments
183
+ // Print custom help if no arguments, otherwise let Commander.js handle everything
186
184
  if (process.argv.length <= 2) {
187
185
  printCommandHelp();
188
186
  }
@@ -39,9 +39,8 @@ class AdobeTargetBuildGeneratorPlugin {
39
39
  function wrapFileContent(content, outputPath) {
40
40
  const headerContent = (typeof header === 'function') ? header(content, outputPath) : header;
41
41
  const footerContent = (typeof footer === 'function') ? footer(content, outputPath) : footer;
42
- return `${headerContent}
43
- ${content}
44
- ${footerContent}`;
42
+ return `
43
+ ${headerContent}${content}${footerContent}`;
45
44
  }
46
45
 
47
46
  function generateTargetReadyBuild({ source, targetPath }) {
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "at-builder",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "main": "bin/index.js",
5
5
  "bin": {
6
6
  "atb": "bin/index.js"
7
7
  },
8
- "type": "module",
9
8
  "scripts": {
10
9
  "build": "tsc -w",
11
- "build:prod": "cross-env NODE_ENV=production webpack",
10
+ "build-prod": "cross-env NODE_ENV=production webpack",
12
11
  "dev": "webpack -- -w",
13
12
  "dev-puppeteer": "webpack -- -w & npm run puppeteer",
14
13
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -57,10 +56,11 @@
57
56
  },
58
57
  "description": "[![npm version](https://badge.fury.io/js/at-builder.svg)](https://www.npmjs.com/package/at-builder) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)",
59
58
  "directories": {
60
- "lib": "lib"
59
+ "lib": "lib",
60
+ "test": "test"
61
61
  },
62
62
  "devDependencies": {
63
- "@babel/runtime": "^7.26.7",
63
+ "@babel/runtime": "^7.28.3",
64
64
  "@eslint/js": "^9.20.0",
65
65
  "globals": "^15.15.0",
66
66
  "kleur": "^4.1.5",
package/plopfile.js CHANGED
@@ -1,6 +1,6 @@
1
- import components from "./.plop/generators/components";
1
+ const components = require("./.plop/generators/components");
2
2
 
3
3
 
4
- export default function (plop) {
4
+ module.exports = function (plop) {
5
5
  plop.setGenerator('components', components );
6
6
  };
@@ -28,10 +28,19 @@ exports.getVersion = getVersion;
28
28
  * @param {boolean} underline Whether to underline the text
29
29
  * @returns {string} The formatted text
30
30
  */
31
- const formatText = (text, color = "white", bold = false, underline = false) => {
32
- let formattedText = clc[color](text);
31
+ const formatText = (text: string, color: string = "white", bold: boolean = false, underline: boolean = false): string => {
32
+ let formattedText = text;
33
+
34
+ // Apply color
35
+ const colorFn = clc[color as keyof typeof clc];
36
+ if (typeof colorFn === 'function') {
37
+ formattedText = colorFn(text);
38
+ }
39
+
40
+ // Apply formatting
33
41
  if (bold) formattedText = clc.bold(formattedText);
34
42
  if (underline) formattedText = clc.underline(formattedText);
43
+
35
44
  return formattedText;
36
45
  };
37
46
 
@@ -43,26 +52,57 @@ export const getHelpInfo = async () => {
43
52
  const version = await getVersion();
44
53
 
45
54
  return `
46
- ${formatText(`at-builder (version: ${version})`, 'green', false, true)}
47
-
48
- ${formatText("A simple command to run Adobe Target builder.", 'white', true)}
49
-
50
- Usage: \`atb\`
51
-
52
- Available Commands:
53
-
54
- ${formatText("new", 'blue', true, true)} Creates a new activity
55
- ${formatText("build", 'blue', true, true)} Create build
56
- ${formatText("build -p", 'blue', true, true)} Create production build
57
-
58
- Example:
59
-
60
- atb -c new # Creates a new activity
61
- atb -c build # Creates a build
62
- atb -p -c build # Creates a build for production
63
-
64
- For more information on a specific command, run \`atb <command> --help\`
65
- `;
55
+ ${formatText(`🎯 at-builder`, 'cyan', true)} ${formatText(`v${version}`, 'gray')}
56
+
57
+ ${formatText("Adobe Target Activity Development CLI", 'white', true)}
58
+
59
+ A streamlined command-line tool for creating, building, and deploying Adobe Target
60
+ A/B testing activities with modern web technologies.
61
+
62
+ ${formatText("USAGE", 'yellow', true)}
63
+ ${formatText("atb", 'cyan')} ${formatText("<command> [options]", 'gray')}
64
+
65
+ ${formatText("COMMANDS", 'yellow', true)}
66
+ ${formatText("init", 'cyan', true)} Initialize project with .env configuration and templates
67
+ ${formatText("new", 'cyan', true)} Create a new Adobe Target activity with variations
68
+ ${formatText("build", 'cyan', true)} Build activity for development
69
+ ${formatText("build --prod", 'cyan', true)} Build for production deployment
70
+ ${formatText("dev", 'cyan', true)} Start development server with file watching
71
+ ${formatText("dev --browser", 'cyan', true)} Start development server and open in browser
72
+
73
+ ${formatText("GLOBAL OPTIONS", 'yellow', true)}
74
+ ${formatText("-v, --verbose", 'green')} Enable detailed logging
75
+ ${formatText("-V, --version", 'green')} Display version information
76
+ ${formatText("-h, --help", 'green')} Show help information
77
+
78
+ ${formatText("EXAMPLES", 'yellow', true)}
79
+ ${formatText("# Initialize new project", 'gray')}
80
+ ${formatText("atb init", 'white')}
81
+
82
+ ${formatText("# Create new activity", 'gray')}
83
+ ${formatText("atb new", 'white')}
84
+
85
+ ${formatText("# Development build with hot reload", 'gray')}
86
+ ${formatText("atb build", 'white')}
87
+
88
+ ${formatText("# Production build for Adobe Target deployment", 'gray')}
89
+ ${formatText("atb build --prod", 'white')}
90
+
91
+ ${formatText("# Development server with browser", 'gray')}
92
+ ${formatText("atb dev --browser", 'white')}
93
+
94
+ ${formatText("WORKFLOW", 'yellow', true)}
95
+ ${formatText("1.", 'cyan')} Run ${formatText("atb init", 'white')} to set up project configuration
96
+ ${formatText("2.", 'cyan')} Run ${formatText("atb new", 'white')} to create activity templates
97
+ ${formatText("3.", 'cyan')} Develop variations in ${formatText("/Activities/{name}/Variation-*/", 'gray')}
98
+ ${formatText("4.", 'cyan')} Run ${formatText("atb build", 'white')} for development testing
99
+ ${formatText("5.", 'cyan')} Run ${formatText("atb build --prod", 'white')} for Adobe Target deployment
100
+
101
+ ${formatText("SUPPORT", 'yellow', true)}
102
+ Repository: ${formatText("https://github.com/upesenga/at-builder", 'blue')}
103
+ Issues: ${formatText("https://github.com/upesenga/at-builder/issues", 'blue')}
104
+ License: ${formatText("MIT", 'green')}
105
+ `;
66
106
  };
67
107
 
68
108
 
package/src/index.ts CHANGED
@@ -2,7 +2,6 @@
2
2
  import { spawn } from "child_process";
3
3
  import path from "path";
4
4
  import fs from "fs";
5
- import inquirer from 'inquirer';
6
5
  import { Command } from 'commander';
7
6
  import { getVersion, getHelpInfo, setupEnv } from "./constants/config";
8
7
  import logger from "./services/logger";
@@ -30,22 +29,58 @@ const productionEnv = {
30
29
  executionPath: process.cwd()
31
30
  };
32
31
 
33
- // Commander setup
32
+ // Commander setup with proper subcommands
34
33
  const setupCommander = async () => {
35
34
  logger.info("setupCommander", "Setting up commander");
36
35
  const version = await getVersion();
37
- logger.info("setupCommander", `Version: ${version}`);
38
36
  const program = new Command();
37
+
39
38
  program
39
+ .name('atb')
40
+ .description('Adobe Target Activity Development CLI')
40
41
  .version(version, "-V, --version")
41
- .usage("[OPTIONS]...")
42
- .option("-c, --command <value>")
43
- .option("-p, --prod")
44
- .option("-v, --verbose", "Enable verbose logging")
45
- .parse(process.argv);
42
+ .option("-v, --verbose", "Enable verbose logging");
43
+
44
+ // Add subcommands with proper actions
45
+ program
46
+ .command('init')
47
+ .description('Initialize project with .env configuration and templates')
48
+ .action(async (options, command) => {
49
+ const globalOpts = command.parent.opts();
50
+ await handleInit(globalOpts.verbose);
51
+ });
52
+
53
+ program
54
+ .command('new')
55
+ .description('Create a new Adobe Target activity with variations')
56
+ .action(async (options, command) => {
57
+ const globalOpts = command.parent.opts();
58
+ await handleNew(globalOpts.verbose);
59
+ });
46
60
 
61
+ program
62
+ .command('build')
63
+ .description('Build activity for development or production')
64
+ .option('--prod', 'Build for production deployment')
65
+ .action(async (options, command) => {
66
+ const globalOpts = command.parent.opts();
67
+ checkEnvFile();
68
+ await handleBuild(options.prod, globalOpts.verbose);
69
+ });
70
+
71
+ program
72
+ .command('dev')
73
+ .description('Start development server with file watching')
74
+ .option('--browser', 'Open in browser automatically')
75
+ .action(async (options, command) => {
76
+ const globalOpts = command.parent.opts();
77
+ checkEnvFile();
78
+ await handleDev(options.browser, globalOpts.verbose);
79
+ });
80
+
81
+ await program.parseAsync(process.argv);
82
+
47
83
  logger.info("setupCommander", "Commander setup complete");
48
- return program.opts();
49
84
  };
50
85
 
51
86
 
@@ -71,130 +106,96 @@ const runCommand = (commandArr: string[], env: NodeJS.ProcessEnv): void => {
71
106
  };
72
107
 
73
108
  /**
74
- * Handles the build process based on the given options.
75
- *
76
- * @param {boolean} prod - If true, runs the build with production environment.
77
- * If false, runs the build with development environment.
109
+ * Handles the init command
78
110
  */
79
- const handleBuild = async (prod: boolean): Promise<void> => {
80
- logger.info("handleBuild", `Handling build with options: production=${prod}`);
111
+ const handleInit = async (verbose: boolean): Promise<void> => {
112
+ if (verbose) logger.info("verbose", "Initializing new .env file");
113
+
114
+ // Run npm init in the current working directory (where user ran atb init)
115
+ spawn("npm", ["init", "-y"], {
116
+ cwd: process.cwd(),
117
+ env: process.env,
118
+ shell: true,
119
+ stdio: "inherit",
120
+ });
121
+
122
+ // Install @babel/runtime as dev dependency in the current directory
123
+ spawn("npm", ["install", "@babel/runtime", "-D"], {
124
+ cwd: process.cwd(),
125
+ env: process.env,
126
+ shell: true,
127
+ stdio: "inherit",
128
+ });
129
+
130
+ setupEnv(process.cwd());
131
+ };
132
+
133
+ /**
134
+ * Handles the new command
135
+ */
136
+ const handleNew = async (verbose: boolean): Promise<void> => {
137
+ if (verbose) logger.info("verbose", "Creating new activity");
138
+ runCommand(["run", "new-activity"], productionEnv);
139
+ };
140
+
141
+ /**
142
+ * Handles the build command
143
+ */
144
+ const handleBuild = async (prod: boolean, verbose: boolean): Promise<void> => {
145
+ if (verbose) logger.info("verbose", `Building in ${prod ? 'production' : 'development'} mode`);
146
+
81
147
  if (prod) {
82
- // Set production environment
83
148
  process.env['NODE_ENV'] = 'production';
84
149
  logger.info("handleBuild", "Running build with production environment");
85
- // Run the build command
86
150
  runCommand(['run', 'build-prod'], productionEnv);
87
151
  } else {
88
152
  logger.info("handleBuild", "Running build with development environment");
89
- // Prompt the user to ask if they want to open the build in a browser tab
90
- const { openInBrowser } = await inquirer.prompt([
91
- {
92
- type: 'confirm',
93
- name: 'openInBrowser',
94
- message: 'Do you want to open your build in a browser tab?',
95
- default: false,
96
- },
97
- ]);
98
-
99
- logger.info("handleBuild", `User opted for opening in browser: ${openInBrowser}`);
100
- const commandArr = ['run', 'dev'];
101
- if (openInBrowser) {
102
- // If the user wants to open the build in a browser, run the build with
103
- // puppeteer
104
- commandArr[1] = 'dev-puppeteer';
105
- }
106
-
107
- logger.info("handleBuild", `Running command: ${commandArr.join(', ')}`);
108
- // Run the build command
109
- runCommand(commandArr, productionEnv);
110
- // TO_DO: verify
111
- // process.exit(0);
153
+ runCommand(['run', 'dev'], productionEnv);
112
154
  }
113
155
  };
114
156
 
157
+ /**
158
+ * Handles the dev command
159
+ */
160
+ const handleDev = async (browser: boolean, verbose: boolean): Promise<void> => {
161
+ if (verbose) logger.info("verbose", `Starting development server with browser=${browser}`);
162
+
163
+ const commandArr = ['run', browser ? 'dev-puppeteer' : 'dev'];
164
+ logger.info("handleDev", `Running command: ${commandArr.join(', ')}`);
165
+
166
+ runCommand(commandArr, productionEnv);
167
+ };
168
+
115
169
  // Command execution logic
116
170
  /**
117
- * Handles the command execution based on the given options.
118
- *
119
- * @returns {Promise<void>}
171
+ * Main command execution entry point
120
172
  */
121
173
  const executeCommand = async () => {
122
174
  logger.info("executeCommand", "Entering executeCommand");
123
- // Check if .env file is present
124
- // Get the command options
125
- const options = await setupCommander();
126
-
127
- if (options.command !== 'init')
128
- checkEnvFile();
129
-
130
- logger.info("executeCommand", `Options: ${JSON.stringify(options)}`);
131
- /**
132
- * Logs a message if verbose mode is enabled
133
- * @param {string} message - The message to log
134
- */
135
- const verboseLog = (message: string) => {
136
- if (options.verbose) {
137
- logger.info("verbose", message);
138
- }
139
- };
140
-
141
- switch (options.command) {
142
- case "init":
143
- // Initialize new .env file
144
- verboseLog("Initializing new .env file");
145
- runCommand(["init -y"], productionEnv);
146
- runCommand(["install @babel/runtime","-D"], productionEnv);
147
- setupEnv(process.cwd());
148
- break;
149
-
150
- case "new":
151
- // Create new activity
152
- verboseLog("Creating new activity");
153
- runCommand(["run", "new-activity"], productionEnv);
154
- break;
155
-
156
- case "build":
157
- // Build the application in production or development mode
158
- verboseLog(`Building in ${options.prod ? 'production' : 'development'} mode`);
159
- await handleBuild(options.prod);
160
- break;
161
-
162
- default:
163
- // Print command help
164
- printCommandHelp();
165
- process.exit(0);
166
- break;
167
- }
175
+ await setupCommander();
168
176
  logger.info("executeCommand", "Exiting executeCommand");
169
177
  };
170
178
 
171
179
  // Print command help
172
180
  const printCommandHelp = async () => {
173
181
  const help = await getHelpInfo();
174
- logger.info("printCommandHelp", help);
182
+ console.log(help);
175
183
  process.exit(0);
176
184
  };
177
185
 
178
186
  /**
179
187
  * Main program flow
180
- *
181
- * This function is the main entry point of the program. It attempts to execute
182
- * the command specified by the user. If the command fails, it logs the error
183
- * message and exits with code 1.
184
188
  */
185
189
  const main = async () => {
186
190
  try {
187
- // Execute the command specified by the user
188
191
  await executeCommand();
189
192
  } catch (error) {
190
- // Log the error message if the command fails
191
193
  logger.error("main", error.message);
192
- // Exit with code 1 if the command fails
193
194
  process.exit(1);
194
195
  }
195
196
  };
196
197
 
197
- // Print help if no arguments
198
+ // Print custom help if no arguments, otherwise let Commander.js handle everything
198
199
  if (process.argv.length <= 2) {
199
200
  printCommandHelp();
200
201
  } else {
package/webpack.config.js CHANGED
@@ -1,9 +1,8 @@
1
- import path from 'path';
2
- import fs from 'fs';
3
- import { createRequire } from 'module';
4
-
5
- const require = createRequire(import.meta.url);
1
+ const path = require('path');
2
+ const fs = require('fs');
6
3
  const dotenv = require('dotenv');
4
+ const { Compilation } = require('webpack');
5
+ const WrapperPlugin = require('./lib/CustomWrapperPlugin');
7
6
  const TerserPlugin = require('terser-webpack-plugin');
8
7
  const ESLintPlugin = require('eslint-webpack-plugin');
9
8
  const postcssPresetEnv = require('postcss-preset-env');
@@ -99,48 +98,29 @@ const plugins = [
99
98
  extensions: "js",
100
99
  outputReport: true,
101
100
  fix: true
101
+ }),
102
+ new WrapperPlugin({
103
+ test: /\.js$/,
104
+ header: function (_, args) {
105
+ //Handle target issue for reloading the styles.
106
+ let _uniqueTestId = `TargetBuild_${args.hash}`;
107
+ args._uniqueTestId = _uniqueTestId;
108
+ return `
109
+ (function(){
110
+ if(!window.${_uniqueTestId}){
111
+ //# sourceURL=${_uniqueTestId}.js
112
+ `;
113
+ },
114
+ footer: function (_, args) {
115
+ return `\n
116
+ window.${args._uniqueTestId} = true;
117
+ }
118
+ })();\n`
119
+ },
120
+ stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE,
102
121
  })
103
122
  ];
104
123
 
105
- // Modern replacement for WrapperPlugin using webpack's BannerPlugin and custom plugin
106
- plugins.push({
107
- apply: (compiler) => {
108
- compiler.hooks.compilation.tap('ModernWrapperPlugin', (compilation) => {
109
- compilation.hooks.processAssets.tap(
110
- {
111
- name: 'ModernWrapperPlugin',
112
- stage: compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_INLINE
113
- },
114
- async (assets) => {
115
- const crypto = await import('crypto');
116
- const webpackSources = await import('webpack-sources');
117
-
118
- for (const assetName of Object.keys(assets)) {
119
- if (assetName.endsWith('.js')) {
120
- const asset = assets[assetName];
121
- const source = asset.source();
122
-
123
- // Generate unique test ID
124
- const preFix = process.env.TRACK || 'TargetBuild';
125
- const hash = crypto.createHash('md5').update(assetName + source).digest('hex').substring(0, 8);
126
- const uniqueTestId = `${preFix}_${hash}`;
127
-
128
- const wrappedSource = `(function(){
129
- if(!window.${uniqueTestId}){
130
- //# sourceURL=${uniqueTestId}.js
131
- ${source}
132
- window.${uniqueTestId} = true;
133
- }
134
- })();`;
135
-
136
- compilation.updateAsset(assetName, new webpackSources.RawSource(wrappedSource));
137
- }
138
- }
139
- }
140
- );
141
- });
142
- }
143
- });
144
124
 
145
125
  // Build Summary Plugin
146
126
  plugins.push({
@@ -219,7 +199,7 @@ if (isProdMode) {
219
199
  }
220
200
 
221
201
  // Webpack config object with dynamic entry points
222
- export default {
202
+ module.exports = {
223
203
  devtool: false,
224
204
  // devtool: 'source-map', // generate source-map in build directory
225
205
  //...(environment !== PRODUCTION && { devtool: 'inline-source-map' }),
@@ -246,9 +226,6 @@ export default {
246
226
  {
247
227
  test: /\.js$/,
248
228
  exclude: /node_modules/,
249
- resolve: {
250
- fullySpecified: false
251
- },
252
229
  use: {
253
230
  loader: 'babel-loader',
254
231
  options: {
File without changes