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.
- package/.claude/settings.local.json +4 -1
- package/CustomWrapperPlugin.js +61 -0
- package/bin/constants/config.js +51 -14
- package/bin/index.js +87 -89
- package/lib/at-build-generator.cjs +2 -3
- package/package.json +5 -5
- package/plopfile.js +2 -2
- package/src/constants/config.ts +62 -22
- package/src/index.ts +98 -97
- package/webpack.config.js +25 -48
- /package/{babel.config.cjs → babel.config.js} +0 -0
|
@@ -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;
|
package/bin/constants/config.js
CHANGED
|
@@ -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 =
|
|
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(
|
|
57
|
+
${formatText(`🎯 at-builder`, 'cyan', true)} ${formatText(`v${version}`, 'gray')}
|
|
52
58
|
|
|
53
|
-
${formatText("
|
|
59
|
+
${formatText("Adobe Target Activity Development CLI", 'white', true)}
|
|
54
60
|
|
|
55
|
-
|
|
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
|
-
|
|
64
|
+
${formatText("USAGE", 'yellow', true)}
|
|
65
|
+
${formatText("atb", 'cyan')} ${formatText("<command> [options]", 'gray')}
|
|
58
66
|
|
|
59
|
-
${formatText("
|
|
60
|
-
${formatText("
|
|
61
|
-
${formatText("
|
|
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
|
-
|
|
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
|
-
|
|
66
|
-
atb
|
|
67
|
-
|
|
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
|
-
|
|
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
|
-
.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.
|
|
49
|
-
.
|
|
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
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
43
|
-
|
|
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.
|
|
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
|
|
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": "[](https://www.npmjs.com/package/at-builder) [](LICENSE)",
|
|
59
58
|
"directories": {
|
|
60
|
-
"lib": "lib"
|
|
59
|
+
"lib": "lib",
|
|
60
|
+
"test": "test"
|
|
61
61
|
},
|
|
62
62
|
"devDependencies": {
|
|
63
|
-
"@babel/runtime": "^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
package/src/constants/config.ts
CHANGED
|
@@ -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 =
|
|
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(
|
|
47
|
-
|
|
48
|
-
${formatText("
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
${formatText("
|
|
55
|
-
|
|
56
|
-
${formatText("
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
.
|
|
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
|
|
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
|
|
80
|
-
logger.info("
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2
|
-
|
|
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
|
-
|
|
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
|