at-builder 1.1.9 ā 1.2.1
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 +11 -0
- package/CustomWrapperPlugin.js +61 -0
- package/bin/constants/config.js +72 -32
- package/bin/index.js +75 -89
- package/lib/CustomWrapperPlugin.js +61 -0
- package/package.json +5 -3
- package/puppeteer.js +7 -6
- package/src/constants/config.ts +65 -25
- package/src/index.ts +83 -97
- package/webpack.config.js +100 -52
- /package/lib/{at-build-generator.js ā at-build-generator.cjs} +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
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.getENV = exports.setupEnv = exports.getHelpInfo = exports.getVersion = void 0;
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const cli_color_1 = __importDefault(require("cli-color"));
|
|
7
10
|
/**
|
|
8
11
|
* Reads and returns the version from package.json
|
|
9
12
|
* @returns {Promise<string>} The version of the package
|
|
10
13
|
*/
|
|
11
14
|
const getVersion = async () => {
|
|
12
|
-
const packageJSONPath =
|
|
15
|
+
const packageJSONPath = path_1.default.resolve(__dirname, "../../package.json");
|
|
13
16
|
try {
|
|
14
|
-
const content = await
|
|
17
|
+
const content = await fs_1.default.promises.readFile(packageJSONPath, { encoding: "utf8" });
|
|
15
18
|
const config = JSON.parse(content);
|
|
16
19
|
return config.version;
|
|
17
20
|
}
|
|
18
21
|
catch (error) {
|
|
19
|
-
console.error(
|
|
22
|
+
console.error(cli_color_1.default.red("Error reading package.json or parsing JSON"));
|
|
20
23
|
process.exit(1); // Exit the process if package.json is not found or malformed
|
|
21
24
|
}
|
|
22
25
|
};
|
|
@@ -31,11 +34,17 @@ exports.getVersion = exports.getVersion;
|
|
|
31
34
|
* @returns {string} The formatted text
|
|
32
35
|
*/
|
|
33
36
|
const formatText = (text, color = "white", bold = false, underline = false) => {
|
|
34
|
-
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
|
|
35
44
|
if (bold)
|
|
36
|
-
formattedText =
|
|
45
|
+
formattedText = cli_color_1.default.bold(formattedText);
|
|
37
46
|
if (underline)
|
|
38
|
-
formattedText =
|
|
47
|
+
formattedText = cli_color_1.default.underline(formattedText);
|
|
39
48
|
return formattedText;
|
|
40
49
|
};
|
|
41
50
|
/**
|
|
@@ -45,33 +54,64 @@ const formatText = (text, color = "white", bold = false, underline = false) => {
|
|
|
45
54
|
const getHelpInfo = async () => {
|
|
46
55
|
const version = await (0, exports.getVersion)();
|
|
47
56
|
return `
|
|
48
|
-
${formatText(
|
|
57
|
+
${formatText(`šÆ at-builder`, 'cyan', true)} ${formatText(`v${version}`, 'gray')}
|
|
49
58
|
|
|
50
|
-
${formatText("
|
|
59
|
+
${formatText("Adobe Target Activity Development CLI", 'white', true)}
|
|
51
60
|
|
|
52
|
-
|
|
61
|
+
A streamlined command-line tool for creating, building, and deploying Adobe Target
|
|
62
|
+
A/B testing activities with modern web technologies.
|
|
53
63
|
|
|
54
|
-
|
|
64
|
+
${formatText("USAGE", 'yellow', true)}
|
|
65
|
+
${formatText("atb", 'cyan')} ${formatText("<command> [options]", 'gray')}
|
|
55
66
|
|
|
56
|
-
${formatText("
|
|
57
|
-
${formatText("
|
|
58
|
-
${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
|
|
59
79
|
|
|
60
|
-
|
|
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')}
|
|
61
95
|
|
|
62
|
-
|
|
63
|
-
atb
|
|
64
|
-
|
|
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
|
|
65
102
|
|
|
66
|
-
|
|
67
|
-
|
|
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
|
+
`;
|
|
68
108
|
};
|
|
69
109
|
exports.getHelpInfo = getHelpInfo;
|
|
70
110
|
const setupEnv = async (basePath) => {
|
|
71
111
|
// Path to the .env file
|
|
72
|
-
const envPath =
|
|
112
|
+
const envPath = path_1.default.join(basePath, '.env');
|
|
73
113
|
// Check if the .env file exists
|
|
74
|
-
if (!
|
|
114
|
+
if (!fs_1.default.existsSync(envPath)) {
|
|
75
115
|
// Define the content of the .env file
|
|
76
116
|
const envContent = `
|
|
77
117
|
ACTIVITY_FOLDER_NAME=""
|
|
@@ -83,7 +123,7 @@ NODE_ENV="development"
|
|
|
83
123
|
VERBOSE=false
|
|
84
124
|
`;
|
|
85
125
|
// Write the content to the .env file
|
|
86
|
-
|
|
126
|
+
fs_1.default.writeFileSync(envPath, envContent.trim(), 'utf8');
|
|
87
127
|
console.log('.env file created successfully!');
|
|
88
128
|
}
|
|
89
129
|
else {
|
|
@@ -94,14 +134,14 @@ VERBOSE=false
|
|
|
94
134
|
exports.setupEnv = setupEnv;
|
|
95
135
|
const createWatchConfig = (basePath) => {
|
|
96
136
|
// Path to the watch-config.json file
|
|
97
|
-
const watchConfigPath =
|
|
137
|
+
const watchConfigPath = path_1.default.join(basePath, 'watch-config.json');
|
|
98
138
|
// Default content for watch-config.json
|
|
99
139
|
const watchConfigContent = {
|
|
100
140
|
"VARIATION": "Variation-1"
|
|
101
141
|
};
|
|
102
142
|
// Check if the watch-config.json file already exists
|
|
103
|
-
if (!
|
|
104
|
-
|
|
143
|
+
if (!fs_1.default.existsSync(watchConfigPath)) {
|
|
144
|
+
fs_1.default.writeFileSync(watchConfigPath, JSON.stringify(watchConfigContent, null, 2), 'utf8');
|
|
105
145
|
console.log('watch-config.json file created successfully!');
|
|
106
146
|
}
|
|
107
147
|
else {
|
|
@@ -114,14 +154,14 @@ const createWatchConfig = (basePath) => {
|
|
|
114
154
|
* @returns {Promise<Object>} Object containing environment variables
|
|
115
155
|
*/
|
|
116
156
|
const getENV = async (basePath) => {
|
|
117
|
-
const envPath =
|
|
157
|
+
const envPath = path_1.default.join(basePath, '.env');
|
|
118
158
|
try {
|
|
119
159
|
// Check if .env file exists
|
|
120
|
-
if (!
|
|
160
|
+
if (!fs_1.default.existsSync(envPath)) {
|
|
121
161
|
throw new Error('.env file not found');
|
|
122
162
|
}
|
|
123
163
|
// Read the .env file
|
|
124
|
-
const envContent = await
|
|
164
|
+
const envContent = await fs_1.default.promises.readFile(envPath, { encoding: 'utf8' });
|
|
125
165
|
// Parse the .env file content
|
|
126
166
|
const envVars = {};
|
|
127
167
|
envContent.split('\n').forEach(line => {
|
|
@@ -139,7 +179,7 @@ const getENV = async (basePath) => {
|
|
|
139
179
|
return envVars;
|
|
140
180
|
}
|
|
141
181
|
catch (error) {
|
|
142
|
-
console.error(
|
|
182
|
+
console.error(cli_color_1.default.red(`Error reading .env file: ${error.message}`));
|
|
143
183
|
throw error;
|
|
144
184
|
}
|
|
145
185
|
};
|
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,77 @@ 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
|
+
runCommand(["init -y"], productionEnv);
|
|
107
|
+
runCommand(["install @babel/runtime", "-D"], productionEnv);
|
|
108
|
+
(0, config_1.setupEnv)(process.cwd());
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* Handles the new command
|
|
112
|
+
*/
|
|
113
|
+
const handleNew = async (verbose) => {
|
|
114
|
+
if (verbose)
|
|
115
|
+
logger_1.default.info("verbose", "Creating new activity");
|
|
116
|
+
runCommand(["run", "new-activity"], productionEnv);
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Handles the build command
|
|
76
120
|
*/
|
|
77
|
-
const handleBuild = async (prod) => {
|
|
78
|
-
|
|
121
|
+
const handleBuild = async (prod, verbose) => {
|
|
122
|
+
if (verbose)
|
|
123
|
+
logger_1.default.info("verbose", `Building in ${prod ? 'production' : 'development'} mode`);
|
|
79
124
|
if (prod) {
|
|
80
|
-
// Set production environment
|
|
81
125
|
process.env['NODE_ENV'] = 'production';
|
|
82
126
|
logger_1.default.info("handleBuild", "Running build with production environment");
|
|
83
|
-
// Run the build command
|
|
84
127
|
runCommand(['run', 'build-prod'], productionEnv);
|
|
85
128
|
}
|
|
86
129
|
else {
|
|
87
130
|
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);
|
|
131
|
+
runCommand(['run', 'dev'], productionEnv);
|
|
109
132
|
}
|
|
110
133
|
};
|
|
134
|
+
/**
|
|
135
|
+
* Handles the dev command
|
|
136
|
+
*/
|
|
137
|
+
const handleDev = async (browser, verbose) => {
|
|
138
|
+
if (verbose)
|
|
139
|
+
logger_1.default.info("verbose", `Starting development server with browser=${browser}`);
|
|
140
|
+
const commandArr = ['run', browser ? 'dev-puppeteer' : 'dev'];
|
|
141
|
+
logger_1.default.info("handleDev", `Running command: ${commandArr.join(', ')}`);
|
|
142
|
+
runCommand(commandArr, productionEnv);
|
|
143
|
+
};
|
|
111
144
|
// Command execution logic
|
|
112
145
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* @returns {Promise<void>}
|
|
146
|
+
* Main command execution entry point
|
|
116
147
|
*/
|
|
117
148
|
const executeCommand = async () => {
|
|
118
149
|
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
|
-
}
|
|
150
|
+
await setupCommander();
|
|
158
151
|
logger_1.default.info("executeCommand", "Exiting executeCommand");
|
|
159
152
|
};
|
|
160
153
|
// Print command help
|
|
161
154
|
const printCommandHelp = async () => {
|
|
162
155
|
const help = await (0, config_1.getHelpInfo)();
|
|
163
|
-
|
|
156
|
+
console.log(help);
|
|
164
157
|
process.exit(0);
|
|
165
158
|
};
|
|
166
159
|
/**
|
|
167
160
|
* 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
161
|
*/
|
|
173
162
|
const main = async () => {
|
|
174
163
|
try {
|
|
175
|
-
// Execute the command specified by the user
|
|
176
164
|
await executeCommand();
|
|
177
165
|
}
|
|
178
166
|
catch (error) {
|
|
179
|
-
// Log the error message if the command fails
|
|
180
167
|
logger_1.default.error("main", error.message);
|
|
181
|
-
// Exit with code 1 if the command fails
|
|
182
168
|
process.exit(1);
|
|
183
169
|
}
|
|
184
170
|
};
|
|
185
|
-
// Print help if no arguments
|
|
171
|
+
// Print custom help if no arguments, otherwise let Commander.js handle everything
|
|
186
172
|
if (process.argv.length <= 2) {
|
|
187
173
|
printCommandHelp();
|
|
188
174
|
}
|
|
@@ -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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "at-builder",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"main": "bin/index.js",
|
|
5
5
|
"bin": {
|
|
6
6
|
"atb": "bin/index.js"
|
|
@@ -56,12 +56,14 @@
|
|
|
56
56
|
},
|
|
57
57
|
"description": "[](https://www.npmjs.com/package/at-builder) [](LICENSE)",
|
|
58
58
|
"directories": {
|
|
59
|
-
"lib": "lib"
|
|
59
|
+
"lib": "lib",
|
|
60
|
+
"test": "test"
|
|
60
61
|
},
|
|
61
62
|
"devDependencies": {
|
|
62
|
-
"@babel/runtime": "^7.
|
|
63
|
+
"@babel/runtime": "^7.28.3",
|
|
63
64
|
"@eslint/js": "^9.20.0",
|
|
64
65
|
"globals": "^15.15.0",
|
|
66
|
+
"kleur": "^4.1.5",
|
|
65
67
|
"typescript-eslint": "^8.24.1"
|
|
66
68
|
},
|
|
67
69
|
"repository": {
|
package/puppeteer.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import puppeteer from 'puppeteer';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import chokidar from 'chokidar';
|
|
6
|
+
import { executionPath } from process.env;
|
|
7
|
+
dotenv.config({ path: path.join(executionPath, ".env") });
|
|
7
8
|
//const watchConfig = "./../watch-config.json";
|
|
8
9
|
|
|
9
10
|
let watchConfig = '';
|
package/src/constants/config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import clc from "cli-color";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Reads and returns the version from package.json
|
|
@@ -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
|
+
});
|
|
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
|
+
});
|
|
46
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,81 @@ const runCommand = (commandArr: string[], env: NodeJS.ProcessEnv): void => {
|
|
|
71
106
|
};
|
|
72
107
|
|
|
73
108
|
/**
|
|
74
|
-
* Handles the
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
109
|
+
* Handles the init command
|
|
110
|
+
*/
|
|
111
|
+
const handleInit = async (verbose: boolean): Promise<void> => {
|
|
112
|
+
if (verbose) logger.info("verbose", "Initializing new .env file");
|
|
113
|
+
runCommand(["init -y"], productionEnv);
|
|
114
|
+
runCommand(["install @babel/runtime","-D"], productionEnv);
|
|
115
|
+
setupEnv(process.cwd());
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Handles the new command
|
|
120
|
+
*/
|
|
121
|
+
const handleNew = async (verbose: boolean): Promise<void> => {
|
|
122
|
+
if (verbose) logger.info("verbose", "Creating new activity");
|
|
123
|
+
runCommand(["run", "new-activity"], productionEnv);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Handles the build command
|
|
78
128
|
*/
|
|
79
|
-
const handleBuild = async (prod: boolean): Promise<void> => {
|
|
80
|
-
logger.info("
|
|
129
|
+
const handleBuild = async (prod: boolean, verbose: boolean): Promise<void> => {
|
|
130
|
+
if (verbose) logger.info("verbose", `Building in ${prod ? 'production' : 'development'} mode`);
|
|
131
|
+
|
|
81
132
|
if (prod) {
|
|
82
|
-
// Set production environment
|
|
83
133
|
process.env['NODE_ENV'] = 'production';
|
|
84
134
|
logger.info("handleBuild", "Running build with production environment");
|
|
85
|
-
// Run the build command
|
|
86
135
|
runCommand(['run', 'build-prod'], productionEnv);
|
|
87
136
|
} else {
|
|
88
137
|
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);
|
|
138
|
+
runCommand(['run', 'dev'], productionEnv);
|
|
112
139
|
}
|
|
113
140
|
};
|
|
114
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Handles the dev command
|
|
144
|
+
*/
|
|
145
|
+
const handleDev = async (browser: boolean, verbose: boolean): Promise<void> => {
|
|
146
|
+
if (verbose) logger.info("verbose", `Starting development server with browser=${browser}`);
|
|
147
|
+
|
|
148
|
+
const commandArr = ['run', browser ? 'dev-puppeteer' : 'dev'];
|
|
149
|
+
logger.info("handleDev", `Running command: ${commandArr.join(', ')}`);
|
|
150
|
+
|
|
151
|
+
runCommand(commandArr, productionEnv);
|
|
152
|
+
};
|
|
153
|
+
|
|
115
154
|
// Command execution logic
|
|
116
155
|
/**
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
* @returns {Promise<void>}
|
|
156
|
+
* Main command execution entry point
|
|
120
157
|
*/
|
|
121
158
|
const executeCommand = async () => {
|
|
122
159
|
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
|
-
}
|
|
160
|
+
await setupCommander();
|
|
168
161
|
logger.info("executeCommand", "Exiting executeCommand");
|
|
169
162
|
};
|
|
170
163
|
|
|
171
164
|
// Print command help
|
|
172
165
|
const printCommandHelp = async () => {
|
|
173
166
|
const help = await getHelpInfo();
|
|
174
|
-
|
|
167
|
+
console.log(help);
|
|
175
168
|
process.exit(0);
|
|
176
169
|
};
|
|
177
170
|
|
|
178
171
|
/**
|
|
179
172
|
* 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
173
|
*/
|
|
185
174
|
const main = async () => {
|
|
186
175
|
try {
|
|
187
|
-
// Execute the command specified by the user
|
|
188
176
|
await executeCommand();
|
|
189
177
|
} catch (error) {
|
|
190
|
-
// Log the error message if the command fails
|
|
191
178
|
logger.error("main", error.message);
|
|
192
|
-
// Exit with code 1 if the command fails
|
|
193
179
|
process.exit(1);
|
|
194
180
|
}
|
|
195
181
|
};
|
|
196
182
|
|
|
197
|
-
// Print help if no arguments
|
|
183
|
+
// Print custom help if no arguments, otherwise let Commander.js handle everything
|
|
198
184
|
if (process.argv.length <= 2) {
|
|
199
185
|
printCommandHelp();
|
|
200
186
|
} else {
|
package/webpack.config.js
CHANGED
|
@@ -1,42 +1,26 @@
|
|
|
1
|
-
const { executionPath: PWD } = process.env;
|
|
2
1
|
const path = require('path');
|
|
3
|
-
require('dotenv').config({ path: path.join(PWD, ".env") });
|
|
4
2
|
const fs = require('fs');
|
|
5
|
-
const
|
|
6
|
-
const
|
|
3
|
+
const dotenv = require('dotenv');
|
|
4
|
+
const { Compilation } = require('webpack');
|
|
5
|
+
const WrapperPlugin = require('./lib/CustomWrapperPlugin');
|
|
6
|
+
const TerserPlugin = require('terser-webpack-plugin');
|
|
7
7
|
const ESLintPlugin = require('eslint-webpack-plugin');
|
|
8
8
|
const postcssPresetEnv = require('postcss-preset-env');
|
|
9
|
-
const AdobeTargetBuildGeneratorPlugin = require('./lib/at-build-generator');
|
|
10
|
-
// const commander = require("commander");
|
|
9
|
+
const AdobeTargetBuildGeneratorPlugin = require('./lib/at-build-generator.cjs');
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
const PWD = process.env.executionPath || process.cwd();
|
|
12
|
+
dotenv.config({ path: path.join(PWD, ".env") });
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Load from env files
|
|
16
16
|
*/
|
|
17
17
|
const PRODUCTION = "production";
|
|
18
18
|
|
|
19
|
-
let environment = "development";
|
|
20
|
-
|
|
21
19
|
/**
|
|
22
20
|
* Set current running environment
|
|
23
21
|
*/
|
|
24
|
-
|
|
25
|
-
console.log("\x1b[36m%s\x1b[0m", "------ Running Production Build ------")
|
|
26
|
-
environment = PRODUCTION;
|
|
27
|
-
} else {
|
|
28
|
-
console.log("\x1b[36m%s\x1b[0m", "------ Running Development Build ------")
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// /**
|
|
32
|
-
// * commander
|
|
33
|
-
// */
|
|
34
|
-
// commander
|
|
35
|
-
// .version("1.0.0", "-v, --version")
|
|
36
|
-
// .option("-p, --path <value>", "Overwriting value.", process.cwd())
|
|
37
|
-
// .parse(process.argv);
|
|
22
|
+
let environment = process.env.NODE_ENV === PRODUCTION ? PRODUCTION : "development";
|
|
38
23
|
|
|
39
|
-
// const options = commander.opts();
|
|
40
24
|
|
|
41
25
|
/**
|
|
42
26
|
* Update folder name here if wanted to skip while transpilation
|
|
@@ -74,7 +58,7 @@ var traversePath = function (dir) {
|
|
|
74
58
|
if (!fileName.startsWith("V")) {
|
|
75
59
|
return;
|
|
76
60
|
}
|
|
77
|
-
console.log(++count + ")", "\x1b[36m", fileName, "\x1b[0m");
|
|
61
|
+
// console.log(++count + ")", "\x1b[36m", fileName, "\x1b[0m", `\n Build path: ${file}/dist/build.js`);
|
|
78
62
|
var res = traversePath(file);
|
|
79
63
|
// Process only if have child nodes
|
|
80
64
|
if (res && res.length)
|
|
@@ -88,11 +72,9 @@ var traversePath = function (dir) {
|
|
|
88
72
|
return results;
|
|
89
73
|
}
|
|
90
74
|
|
|
91
|
-
console.log("\x1b[33m%s\x1b[0m", `${process.env.ACTIVITY_FOLDER_NAME}`);
|
|
75
|
+
// console.log("\x1b[33m%s\x1b[0m", `${process.env.ACTIVITY_FOLDER_NAME}`);
|
|
92
76
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
var files = traversePath(path.join(filepath, 'Activities', process.env.ACTIVITY_FOLDER_NAME).toString());
|
|
77
|
+
var files = traversePath(path.join(PWD, 'Activities', process.env.ACTIVITY_FOLDER_NAME).toString());
|
|
96
78
|
|
|
97
79
|
//Create entry object which is needed for webpack config
|
|
98
80
|
var entries = {};
|
|
@@ -100,17 +82,14 @@ files.forEach((file) => {
|
|
|
100
82
|
var foldername = file.split("/").pop();
|
|
101
83
|
let distFolderPath = file.replace("/" + foldername, "/dist");
|
|
102
84
|
|
|
103
|
-
console.log("distfolder", distFolderPath)
|
|
104
85
|
if (fs.existsSync(distFolderPath) && environment == PRODUCTION) {
|
|
105
|
-
fs.
|
|
106
|
-
// fs.rm(distFolderPath, console.log);
|
|
86
|
+
fs.rmSync(distFolderPath, { recursive: true, force: true });
|
|
107
87
|
}
|
|
108
88
|
var newPath = `${distFolderPath}/build`;
|
|
109
89
|
// Replace path with absolute path before making a key of the entries object
|
|
110
|
-
entries[newPath.replace(PWD, "")] = file;
|
|
90
|
+
entries[newPath.replace(PWD, "").replace(/^\//, '')] = file;
|
|
111
91
|
});
|
|
112
92
|
|
|
113
|
-
console.log("++++ENTRIES++++", entries);
|
|
114
93
|
|
|
115
94
|
const isProdMode = environment == PRODUCTION;
|
|
116
95
|
|
|
@@ -120,18 +99,15 @@ const plugins = [
|
|
|
120
99
|
outputReport: true,
|
|
121
100
|
fix: true
|
|
122
101
|
}),
|
|
123
|
-
|
|
124
102
|
new WrapperPlugin({
|
|
125
103
|
test: /\.js$/,
|
|
126
104
|
header: function (_, args) {
|
|
127
105
|
//Handle target issue for reloading the styles.
|
|
128
|
-
|
|
129
|
-
const preFix = process.env.TRACK ? process.env.TRACK : 'TargetBuild';
|
|
130
|
-
let _uniqueTestId = `${preFix}_${args.hash}`;
|
|
106
|
+
let _uniqueTestId = `TargetBuild_${args.hash}`;
|
|
131
107
|
args._uniqueTestId = _uniqueTestId;
|
|
132
108
|
return `(function(){
|
|
133
109
|
if(!window.${_uniqueTestId}){
|
|
134
|
-
//# sourceURL=${_uniqueTestId}.js
|
|
110
|
+
//# sourceURL=${_uniqueTestId}.js
|
|
135
111
|
`;
|
|
136
112
|
},
|
|
137
113
|
footer: function (_, args) {
|
|
@@ -140,17 +116,80 @@ const plugins = [
|
|
|
140
116
|
}
|
|
141
117
|
})();`
|
|
142
118
|
},
|
|
143
|
-
|
|
119
|
+
stage: Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE,
|
|
144
120
|
})
|
|
145
121
|
];
|
|
146
122
|
|
|
123
|
+
|
|
124
|
+
// Build Summary Plugin
|
|
125
|
+
plugins.push({
|
|
126
|
+
apply: (compiler) => {
|
|
127
|
+
compiler.hooks.done.tap('BuildSummaryPlugin', async (stats) => {
|
|
128
|
+
let kleur;
|
|
129
|
+
try {
|
|
130
|
+
const kleurModule = await import('kleur');
|
|
131
|
+
kleur = kleurModule.default;
|
|
132
|
+
} catch (e) {
|
|
133
|
+
// Fallback if kleur is not available
|
|
134
|
+
kleur = {
|
|
135
|
+
cyan: (text) => `\x1b[36m${text}\x1b[0m`,
|
|
136
|
+
gray: (text) => `\x1b[90m${text}\x1b[0m`,
|
|
137
|
+
bold: (text) => `\x1b[1m${text}\x1b[0m`,
|
|
138
|
+
yellow: (text) => `\x1b[33m${text}\x1b[0m`,
|
|
139
|
+
green: (text) => `\x1b[32m${text}\x1b[0m`,
|
|
140
|
+
blue: (text) => `\x1b[34m${text}\x1b[0m`,
|
|
141
|
+
magenta: (text) => `\x1b[35m${text}\x1b[0m`
|
|
142
|
+
};
|
|
143
|
+
kleur.gray.underline = (text) => `\x1b[90m\x1b[4m${text}\x1b[0m`;
|
|
144
|
+
}
|
|
145
|
+
const project = process.env.ACTIVITY_FOLDER_NAME || 'Project';
|
|
146
|
+
const info = stats.toJson({ all: false, assets: true, modules: true, version: true, timings: true });
|
|
147
|
+
|
|
148
|
+
const buildModules = Object.entries(entries).map(([out, src], idx) => {
|
|
149
|
+
const parts = out.split(path.sep);
|
|
150
|
+
let name = parts[parts.length - 3] || `Module-${idx + 1}`;
|
|
151
|
+
name = name.replace(/dist|build/gi, '').replace(/[-_]/g, '').trim() || `Module-${idx + 1}`;
|
|
152
|
+
return { idx: idx + 1, name, path: out };
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const duration = (stats.endTime - stats.startTime) / 1000;
|
|
156
|
+
const mode = environment === PRODUCTION ? 'Production' : 'Development';
|
|
157
|
+
|
|
158
|
+
console.log(kleur.cyan(`\nš¦ ${mode} Build Summary`));
|
|
159
|
+
console.log(kleur.gray('āāā ') + kleur.bold('Activity: ') + kleur.yellow(project));
|
|
160
|
+
console.log(kleur.gray('āāā ') + kleur.bold('Modules: '));
|
|
161
|
+
|
|
162
|
+
buildModules.forEach((m, i) => {
|
|
163
|
+
const assetPath = m.path.replace(/^\/+/, '');
|
|
164
|
+
const assetName = assetPath + '.js';
|
|
165
|
+
const asset = info.assets.find(a => a.name === assetName);
|
|
166
|
+
const sizeKB = asset ? (asset.size / 1024).toFixed(2) : '0.00';
|
|
167
|
+
const absPath = path.resolve(PWD, assetPath.replace(/\/build$/, ""), environment === PRODUCTION ? 'at-build.html' : 'build.js');
|
|
168
|
+
const atBuildLink = makeClickableLink(absPath, 1, 1);
|
|
169
|
+
|
|
170
|
+
const prefix = (i === buildModules.length - 1) ? 'āāā' : 'āāā';
|
|
171
|
+
console.log(kleur.gray(`ā ${prefix} `) + kleur.green(`${m.name} (${sizeKB} KB)`));
|
|
172
|
+
if (environment === PRODUCTION) {
|
|
173
|
+
console.log(`š ${kleur.blue(`${m.name} Production Build`)} š`);
|
|
174
|
+
console.log(`${kleur.gray().underline(atBuildLink)}`);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
console.log(kleur.gray('ā '));
|
|
178
|
+
console.log(kleur.gray('āāā ') + kleur.bold('Assets: ') + kleur.magenta(info.assets.length));
|
|
179
|
+
console.log(kleur.gray('āāā ') + kleur.bold('Modules: ') + kleur.magenta(info.modules.length));
|
|
180
|
+
console.log(kleur.gray('āāā ') + kleur.bold('Webpack: ') + kleur.magenta(info.version));
|
|
181
|
+
console.log(kleur.gray('āāā ') + kleur.bold('Duration: ') + kleur.magenta(duration.toFixed(2) + 's'));
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
147
186
|
if (isProdMode) {
|
|
148
187
|
plugins.push(new AdobeTargetBuildGeneratorPlugin({
|
|
149
188
|
test: /\.js$/,
|
|
150
189
|
fileName: "at-build",
|
|
151
190
|
fileType: "html",
|
|
152
191
|
header: function (source, outputPath) {
|
|
153
|
-
return `<script targetExp="${process.env.ACTIVITY_FOLDER_NAME}" variation="${(outputPath.split(path.sep)).slice(-3, -1)[
|
|
192
|
+
return `<script targetExp="${process.env.ACTIVITY_FOLDER_NAME}" variation="${(outputPath.split(path.sep)).slice(-3, -1)[1]}" type="application/javascript">`;
|
|
154
193
|
},
|
|
155
194
|
footer: function () {
|
|
156
195
|
return `</script>`;
|
|
@@ -160,7 +199,9 @@ if (isProdMode) {
|
|
|
160
199
|
|
|
161
200
|
// Webpack config object with dynamic entry points
|
|
162
201
|
module.exports = {
|
|
163
|
-
|
|
202
|
+
devtool: false,
|
|
203
|
+
// devtool: 'source-map', // generate source-map in build directory
|
|
204
|
+
//...(environment !== PRODUCTION && { devtool: 'inline-source-map' }),
|
|
164
205
|
mode: environment,
|
|
165
206
|
entry: {
|
|
166
207
|
...entries
|
|
@@ -222,10 +263,14 @@ module.exports = {
|
|
|
222
263
|
{
|
|
223
264
|
loader: 'style-loader',
|
|
224
265
|
options: {
|
|
225
|
-
injectType: 'singletonStyleTag',
|
|
266
|
+
injectType: 'singletonStyleTag', // styleTag | singletonStyleTag | lazyStyleTag | lazySingletonStyleTag | linkTag
|
|
226
267
|
attributes: {
|
|
227
|
-
id: "ddo-styles"
|
|
268
|
+
id: process.env.STYLE_ID || "ddo-styles"
|
|
228
269
|
}
|
|
270
|
+
// sourceMap: environment !== PRODUCTION
|
|
271
|
+
// insert: "body" | (element) => {}
|
|
272
|
+
// insert:(element) => {}
|
|
273
|
+
// esModule: false, default is true - ES modules is beneficial, like in the case of module concatenation and tree shaking.
|
|
229
274
|
}
|
|
230
275
|
},
|
|
231
276
|
{
|
|
@@ -251,6 +296,9 @@ module.exports = {
|
|
|
251
296
|
},
|
|
252
297
|
{
|
|
253
298
|
loader: "sass-loader",
|
|
299
|
+
options: {
|
|
300
|
+
api: 'modern'
|
|
301
|
+
}
|
|
254
302
|
}
|
|
255
303
|
]
|
|
256
304
|
|
|
@@ -271,7 +319,7 @@ module.exports = {
|
|
|
271
319
|
comments: /# sourceURL=/i
|
|
272
320
|
},
|
|
273
321
|
compress: {
|
|
274
|
-
drop_console:
|
|
322
|
+
drop_console: environment === PRODUCTION,
|
|
275
323
|
}
|
|
276
324
|
},
|
|
277
325
|
extractComments: false
|
|
@@ -280,10 +328,10 @@ module.exports = {
|
|
|
280
328
|
]
|
|
281
329
|
},
|
|
282
330
|
plugins: plugins,
|
|
283
|
-
stats: 'minimal'
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
}
|
|
331
|
+
stats: 'minimal'
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
function makeClickableLink(fp, line = 1, col = 1) {
|
|
335
|
+
const uri = `${fp}:${line}:${col}`;
|
|
336
|
+
return `${uri}`;
|
|
337
|
+
}
|
|
File without changes
|