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.
@@ -0,0 +1,11 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm run build:prod:*)",
5
+ "Bash(node:*)",
6
+ "Bash(npx tsc:*)",
7
+ "Bash(find:*)"
8
+ ],
9
+ "deny": []
10
+ }
11
+ }
@@ -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;
@@ -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 path = require("path");
5
- const fs = require("fs");
6
- var clc = require("cli-color");
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 = path.resolve(__dirname, "../../package.json");
15
+ const packageJSONPath = path_1.default.resolve(__dirname, "../../package.json");
13
16
  try {
14
- const content = await fs.promises.readFile(packageJSONPath, { encoding: "utf8" });
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(clc.red("Error reading package.json or parsing JSON"));
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 = clc[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
35
44
  if (bold)
36
- formattedText = clc.bold(formattedText);
45
+ formattedText = cli_color_1.default.bold(formattedText);
37
46
  if (underline)
38
- formattedText = clc.underline(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(`at-builder (version: ${version})`, 'green', false, true)}
57
+ ${formatText(`šŸŽÆ at-builder`, 'cyan', true)} ${formatText(`v${version}`, 'gray')}
49
58
 
50
- ${formatText("A simple command to run Adobe Target builder.", 'white', true)}
59
+ ${formatText("Adobe Target Activity Development CLI", 'white', true)}
51
60
 
52
- Usage: \`atb\`
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
- Available Commands:
64
+ ${formatText("USAGE", 'yellow', true)}
65
+ ${formatText("atb", 'cyan')} ${formatText("<command> [options]", 'gray')}
55
66
 
56
- ${formatText("new", 'blue', true, true)} Creates a new activity
57
- ${formatText("build", 'blue', true, true)} Create build
58
- ${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
59
79
 
60
- 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')}
61
95
 
62
- atb -c new # Creates a new activity
63
- atb -c build # Creates a build
64
- 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
65
102
 
66
- For more information on a specific command, run \`atb <command> --help\`
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 = path.join(basePath, '.env');
112
+ const envPath = path_1.default.join(basePath, '.env');
73
113
  // Check if the .env file exists
74
- if (!fs.existsSync(envPath)) {
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
- fs.writeFileSync(envPath, envContent.trim(), 'utf8');
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 = path.join(basePath, 'watch-config.json');
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 (!fs.existsSync(watchConfigPath)) {
104
- fs.writeFileSync(watchConfigPath, JSON.stringify(watchConfigContent, null, 2), 'utf8');
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 = path.join(basePath, '.env');
157
+ const envPath = path_1.default.join(basePath, '.env');
118
158
  try {
119
159
  // Check if .env file exists
120
- if (!fs.existsSync(envPath)) {
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 fs.promises.readFile(envPath, { encoding: 'utf8' });
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(clc.red(`Error reading .env file: ${error.message}`));
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
- .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,77 @@ 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
+ 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
- logger_1.default.info("handleBuild", `Handling build with options: production=${prod}`);
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
- // 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);
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
- * Handles the command execution based on the given options.
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
- // 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
- }
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
- logger_1.default.info("printCommandHelp", help);
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.9",
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": "[![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)",
58
58
  "directories": {
59
- "lib": "lib"
59
+ "lib": "lib",
60
+ "test": "test"
60
61
  },
61
62
  "devDependencies": {
62
- "@babel/runtime": "^7.26.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
- const path = require('path');
2
- const { executionPath } = process.env;
3
- require('dotenv').config({ path: path.join(executionPath, ".env") });
4
- const puppeteer = require('puppeteer');
5
- const fs = require('fs');
6
- const chokidar = require('chokidar');
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 = '';
@@ -1,6 +1,6 @@
1
- const path = require("path");
2
- const fs = require("fs");
3
- var clc = require("cli-color");
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 = 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
+ });
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 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
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("handleBuild", `Handling build with options: production=${prod}`);
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
- // 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);
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
- * Handles the command execution based on the given options.
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
- // 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
- }
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
- logger.info("printCommandHelp", help);
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 WrapperPlugin = require('wrapper-webpack-plugin');
6
- const TerserPlugin = require("terser-webpack-plugin");
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
- console.log(process.env.ACTIVITY_FOLDER_NAME)
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
- if (process.env.NODE_ENV === PRODUCTION) {
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
- const filepath = PWD;
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.rmdirSync(distFolderPath, { recursive: true });
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
- afterOptimization: true,
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)[0]}" type="application/javascript">`;
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
- ...(environment !== PRODUCTION && { devtool: 'inline-source-map' }),
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" //TO_DO this should load from env or watch config
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: isProdMode,
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
- performance: {
285
- maxAssetSize: 1024000,
286
- maxEntrypointSize: 1024000,
287
- hints: "warning"
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
+ }