@magnolia/cli-jumpstart-plugin 1.0.0-preview.3 → 1.0.0-preview.4

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/LICENSE.txt ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2024 Magnolia International Ltd.
2
+ (http://www.magnolia-cms.com). All rights reserved.
3
+
4
+
5
+ The software is dual-licensed under both the Magnolia
6
+ Network Agreement and the GNU General Public License.
7
+ You may elect to use one or the other of these licenses.
8
+
9
+ The software is distributed in the hope that it will be
10
+ useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
11
+ implied warranty of MERCHANTABILITY or FITNESS FOR A
12
+ PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
13
+ Redistribution, except as permitted by whichever of the GPL
14
+ or MNA you select, is prohibited.
15
+
16
+ 1. For the GPL license (GPL), you can redistribute and/or
17
+ modify this file under the terms of the GNU General
18
+ Public License, Version 3, as published by the Free Software
19
+ Foundation. You should have received a copy of the GNU
20
+ General Public License, Version 3 along with this program;
21
+ if not, write to the Free Software Foundation, Inc., 51
22
+ Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
23
+
24
+ 2. For the Magnolia Network Agreement (MNA), this file
25
+ and the accompanying materials are made available under the
26
+ terms of the MNA which accompanies this distribution, and
27
+ is available at http://www.magnolia-cms.com/mna.html
package/README.md CHANGED
@@ -31,6 +31,7 @@ You can customize the **jumpstart** plugin's actions using these command-line op
31
31
  - `-p, --projectTemplatesPath <path>`: Specify from where the projectTemplates should be loaded.
32
32
 
33
33
  **Template option:**
34
+
34
35
  With this option, you can specify which template to use:
35
36
 
36
37
  ```bash
@@ -47,7 +48,94 @@ You can specify your own project templates list with the *--project-templates-pa
47
48
  magnolia-cli jumpstart -p "<project-templates-path>"
48
49
  ```
49
50
 
50
- **Light-modules folder:**
51
+
52
+ ## Extensions
53
+
54
+ The extensions functionality allows for greater flexibility and customizability during the setup process of a new Magnolia project.
55
+
56
+ ### Extension YAML Configuration
57
+
58
+ The plugin searches for extension YAML files in the following path within your project directory:
59
+ `<rootdir>/extensions/extension.yaml`
60
+
61
+ This YAML file defines additional prompts and post-command functions, which can be helpful for tailoring the plugin's functionality to specific project requirements.
62
+
63
+ ### Structure of the YAML File
64
+
65
+ An extension YAML file typically contains the following key sections:
66
+
67
+ - `extend`: Specifies a JavaScript file that the extension will tie into for additional logic or configuration.
68
+ - `prompts`: A series of input prompts to gather necessary information from the user. Each prompt can specify:
69
+ - `type`: The type of prompt, such as `input` for user text input.
70
+ - `name`: The internal name used to reference the collected data.
71
+ - `message`: The message displayed to the user during the prompt.
72
+ - `default`: The default value for the prompt, used if the user provides no input.
73
+ - `commands`: Defines actions to be taken after the prompts have been completed, such as:
74
+ - `post`: Commands to be executed after the setup process. This could involve downloading dependencies, setting up environment variables, etc.
75
+
76
+ ### Example
77
+
78
+ ```yaml
79
+ extend: ./example.js
80
+ prompts:
81
+ - type: input
82
+ name: clientId
83
+ message: Client ID
84
+ default: clientId_001
85
+
86
+ - type: input
87
+ name: organizationId
88
+ message: Organization Id
89
+ default: organizationId_001
90
+
91
+ - type: input
92
+ name: siteId
93
+ message: Site Id
94
+ default: siteId_001
95
+
96
+ commands:
97
+ post:
98
+ - function: example
99
+ retry: 3
100
+ delay: 1000
101
+ ```
102
+
103
+ ### Execution Flow
104
+
105
+ Upon project initialization, if an extension file is detected, the **Jumpstart** Plugin performs the following steps:
106
+
107
+ 1. **Input Gathering**: It prompts the user for input based on the `prompts` defined within the extension's YAML file.
108
+
109
+ 2. **Configuration Integration**: The plugin retrieves the main **package.json** file. The responses gathered from the prompts are then added to this configuration object under an `answers` property. This augmented **package.json** object is subsequently used to automatically populate the corresponding placeholders within your project files.
110
+
111
+ 3. **Post-Command Execution**: Each `post-command` function specified in the YAML file is executed. These functions have access to the `prompts` object, which contains the user's responses, allowing the functions to perform tasks that are tailored to the user's input.
112
+
113
+ ### Customization Example
114
+
115
+ After gathering inputs, a file containing placeholders like below:
116
+ ```js
117
+ // config.js
118
+
119
+ export default {
120
+ clientId: <%=answers.siteId%>,
121
+ organizationId: <%=answers.organizationId%>,
122
+ siteId: <%=answers.siteId%>
123
+ };
124
+ ```
125
+ will be transformed into:
126
+ ```js
127
+ // config.js
128
+
129
+ export default {
130
+ clientId: clientId_001,
131
+ organizationId: organizationId_001,
132
+ siteId: siteId_001
133
+ };
134
+ ```
135
+
136
+ By using the **Jumpstart** Plugin's extensions functionality, developers can create a more interactive and automated project setup process, making it easier to get started with a new Magnolia project.
137
+
138
+ ## Light-modules folder:
51
139
 
52
140
  The Jumpstart Plugin initiates a recursive search throughout the project's directory structure for an existing `light-modules` directory. Upon detection, the plugin avoids creating a new one, instead utilizing the existing directory to maintain the pre-configured modules. On the other hand, if a `light-modules` directory is not found, the system will generate one.
53
141
 
@@ -1,23 +1,26 @@
1
1
  import { PluginTemplate } from '@magnolia/cli-plugin-template';
2
2
  import { Option } from 'commander';
3
- import { PluginOptions, Template, Bundle, TemplateWithoutChildren } from "./types/types.js";
3
+ import { PluginOptions, Template, Bundle, TemplateWithoutChildren, Credentials } from "./types/types.js";
4
4
  import { Logger } from "winston";
5
5
  export declare let logger: Logger;
6
+ export declare let i18nInstance: {
7
+ t(key: string, options?: any): string;
8
+ };
6
9
  export default class JumpstartPlugin extends PluginTemplate {
7
10
  name: string;
8
11
  version: any;
9
12
  description: any;
10
13
  projectTemplates?: Array<Template>;
11
14
  options: Option[];
15
+ credentials?: Credentials;
12
16
  constructor();
13
17
  executePostCommands(bundle: Bundle, file: string): Promise<void>;
14
- handleTemplate(template: TemplateWithoutChildren): Promise<void>;
18
+ handleTemplate(template: TemplateWithoutChildren, options: PluginOptions): Promise<void>;
15
19
  setProjectTemplates(options: PluginOptions): Promise<void>;
16
- promptTemplateSelection(templates: Array<Template>): Promise<TemplateWithoutChildren | undefined>;
20
+ promptTemplateSelection(templates: Array<Template>, names?: string[]): Promise<TemplateWithoutChildren | undefined>;
17
21
  findTemplateByIdentifier(identifier: string): TemplateWithoutChildren | undefined;
18
22
  chooseTemplate(options: PluginOptions): Promise<void>;
19
23
  init(winstonLogger: Logger): Promise<void>;
20
24
  start(options: PluginOptions): Promise<void>;
21
25
  stop(): Promise<void>;
22
- help(): Promise<void>;
23
26
  }
@@ -19,17 +19,23 @@ import { downloadBundle } from "./lib/download.js";
19
19
  import { PostCommands } from "./types/types.js";
20
20
  import { installDependencies } from "./lib/install.js";
21
21
  import path from "path";
22
- import { handleLightModulesFolder, initializeNodeProject, installAdditionalPlugins } from "./lib/helper.js";
22
+ import { askForCredentials, handleLightModulesFolder, initI18n, initializeNodeProject, installAdditionalPlugins } from "./lib/helper.js";
23
+ import { compileCustomPrompts, evaluateCustomPrompts } from "./lib/extensions.js";
24
+ const extensionsPath = path.resolve("./extensions/extension.yaml");
23
25
  export let logger;
26
+ export let i18nInstance = {
27
+ t(key, options) { return key; }
28
+ };
24
29
  export default class JumpstartPlugin extends PluginTemplate {
25
30
  constructor() {
26
31
  super();
27
32
  this.name = "jumpstart";
28
33
  this.version = pkg.version;
29
34
  this.description = pkg.description;
35
+ i18nInstance = initI18n(this.name);
30
36
  this.options = [
31
- new Option('-t, --template <name>', 'Choose template'),
32
- new Option('-p, --project-templates-path <path>', 'Specify from where the projectTemplates should be loaded')
37
+ new Option('-t, --template <name>', i18nInstance.t("cmd-option-template")),
38
+ new Option('-p, --project-templates-path <path>', i18nInstance.t("cmd-option-project-templates-path"))
33
39
  ];
34
40
  }
35
41
  executePostCommands(bundle, file) {
@@ -50,51 +56,26 @@ export default class JumpstartPlugin extends PluginTemplate {
50
56
  }
51
57
  });
52
58
  }
53
- handleTemplate(template) {
59
+ handleTemplate(template, options) {
54
60
  return __awaiter(this, void 0, void 0, function* () {
55
- let templateCredentials;
56
61
  if (template.auth) {
57
- console.log("Please enter credentials for ", template.name);
58
- templateCredentials = yield inquirer
59
- .prompt([
60
- {
61
- type: 'input',
62
- name: 'username',
63
- message: 'Username'
64
- },
65
- {
66
- type: 'password',
67
- name: 'password',
68
- message: 'Password'
69
- }
70
- ]);
62
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("prompt-enter-credentials", {
63
+ bundle: template.name
64
+ }));
65
+ this.credentials = yield askForCredentials();
71
66
  }
72
67
  for (const bundle of template.bundles || []) {
73
- let credentials = null;
74
68
  let file;
75
69
  try {
76
- if (bundle.auth === "inherit") {
77
- file = yield downloadBundle(bundle, templateCredentials, undefined);
78
- }
79
- else if (bundle.auth) {
80
- console.log("Please enter credentials for ", bundle.name ? bundle.name : bundle.url);
81
- credentials = yield inquirer
82
- .prompt([
83
- {
84
- type: 'input',
85
- name: 'username',
86
- message: 'Username'
87
- },
88
- {
89
- type: 'password',
90
- name: 'password',
91
- message: 'Password'
92
- }
93
- ]);
94
- file = yield downloadBundle(bundle, credentials, undefined);
70
+ if (bundle.auth === true) {
71
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t('prompt-enter-credentials', {
72
+ bundle: bundle.name ? bundle.name : bundle.url
73
+ }));
74
+ const bundleCredentials = yield askForCredentials();
75
+ file = yield downloadBundle(bundle, bundleCredentials, undefined);
95
76
  }
96
77
  else {
97
- file = yield downloadBundle(bundle, null, undefined);
78
+ file = yield downloadBundle(bundle, this.credentials, undefined);
98
79
  }
99
80
  if (file) {
100
81
  yield this.executePostCommands(bundle, file);
@@ -107,23 +88,38 @@ export default class JumpstartPlugin extends PluginTemplate {
107
88
  yield handleLightModulesFolder();
108
89
  yield initializeNodeProject();
109
90
  if (template.plugins) {
110
- yield installAdditionalPlugins(template.plugins);
91
+ yield installAdditionalPlugins(template.plugins, this.credentials);
92
+ }
93
+ const extensions = yield evaluateCustomPrompts(extensionsPath);
94
+ if (extensions === null || extensions === void 0 ? void 0 : extensions.answers) {
95
+ yield compileCustomPrompts(extensions === null || extensions === void 0 ? void 0 : extensions.answers);
96
+ }
97
+ if (extensions === null || extensions === void 0 ? void 0 : extensions.executePostCommands) {
98
+ yield extensions.executePostCommands(extensions === null || extensions === void 0 ? void 0 : extensions.answers);
111
99
  }
112
100
  });
113
101
  }
114
102
  setProjectTemplates(options) {
115
- var _a;
116
103
  return __awaiter(this, void 0, void 0, function* () {
104
+ var _a;
117
105
  try {
118
106
  if ((_a = options.projectTemplatesPath) === null || _a === void 0 ? void 0 : _a.startsWith("http", 0)) {
119
- const res = yield axios.get(options.projectTemplatesPath);
107
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t('prompt-project-templates-credentials'));
108
+ this.credentials = yield askForCredentials();
109
+ const res = yield axios.get(options.projectTemplatesPath, {
110
+ auth: this.credentials
111
+ });
120
112
  this.projectTemplates = res.data.projectTemplates;
121
113
  }
122
114
  else if (options.projectTemplatesPath) {
123
- this.projectTemplates = requireFn(path.join(process.cwd(), options.projectTemplatesPath)).projectTemplates;
115
+ this.projectTemplates = requireFn(path.resolve(options.projectTemplatesPath)).projectTemplates;
124
116
  }
125
117
  else {
126
- const res = yield axios.get("https://git.magnolia-cms.com/rest/api/latest/projects/SERVICES/repos/cli-project-templates/raw/projectTemplates.json");
118
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t('prompt-project-templates-credentials'));
119
+ this.credentials = yield askForCredentials();
120
+ const res = yield axios.get("https://git.magnolia-cms.com/rest/api/latest/projects/SERVICES/repos/cli-project-templates/raw/projectTemplates.json", {
121
+ auth: this.credentials
122
+ });
127
123
  this.projectTemplates = res.data.projectTemplates;
128
124
  }
129
125
  }
@@ -132,8 +128,8 @@ export default class JumpstartPlugin extends PluginTemplate {
132
128
  }
133
129
  });
134
130
  }
135
- promptTemplateSelection(templates) {
136
- return __awaiter(this, void 0, void 0, function* () {
131
+ promptTemplateSelection(templates_1) {
132
+ return __awaiter(this, arguments, void 0, function* (templates, names = []) {
137
133
  if (!templates || !templates.length) {
138
134
  return;
139
135
  }
@@ -151,11 +147,14 @@ export default class JumpstartPlugin extends PluginTemplate {
151
147
  choices: choices
152
148
  }
153
149
  ]);
150
+ if (template.name) {
151
+ names.push(template.name);
152
+ }
154
153
  if (template.children && template.children.length) {
155
- return this.promptTemplateSelection(template.children);
154
+ return this.promptTemplateSelection(template.children, names);
156
155
  }
157
156
  if (template.bundles || template.plugins) {
158
- return template;
157
+ return Object.assign(Object.assign({}, template), { name: names.join("/") });
159
158
  }
160
159
  });
161
160
  }
@@ -179,16 +178,18 @@ export default class JumpstartPlugin extends PluginTemplate {
179
178
  if (typeof options.template === 'string') {
180
179
  const template = this.findTemplateByIdentifier(options.template);
181
180
  if (template) {
182
- yield this.handleTemplate(template);
181
+ yield this.handleTemplate(template, options);
183
182
  return;
184
183
  }
185
184
  else {
186
- logger.info(`The '${options.template}' template was not found.`);
185
+ logger.info(i18nInstance.t("info-template_not_found", {
186
+ template: options.template
187
+ }));
187
188
  }
188
189
  }
189
190
  const template = yield this.promptTemplateSelection(this.projectTemplates);
190
191
  if (template) {
191
- yield this.handleTemplate(template);
192
+ yield this.handleTemplate(template, options);
192
193
  }
193
194
  });
194
195
  }
@@ -199,19 +200,14 @@ export default class JumpstartPlugin extends PluginTemplate {
199
200
  }
200
201
  start(options) {
201
202
  return __awaiter(this, void 0, void 0, function* () {
202
- logger === null || logger === void 0 ? void 0 : logger.info(`'JumpstartPlugin' plugin started succesfully.`);
203
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-jumpstart_started"));
203
204
  yield this.setProjectTemplates(options);
204
205
  yield this.chooseTemplate(options);
205
206
  });
206
207
  }
207
208
  stop() {
208
209
  return __awaiter(this, void 0, void 0, function* () {
209
- console.log("Plugin stopped");
210
- });
211
- }
212
- help() {
213
- return __awaiter(this, void 0, void 0, function* () {
214
- console.log("Plugin help");
210
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-jumpstart_stopped"));
215
211
  });
216
212
  }
217
213
  }
@@ -1,3 +1,3 @@
1
- import { PluginRequirement } from "../types/types.js";
1
+ import { Credentials, PluginRequirement } from "../types/types.js";
2
2
  export declare const handleMGNLConfigFile: () => Promise<void>;
3
- export declare const installAndConfigPlugins: (plugins: Array<PluginRequirement>) => Promise<void>;
3
+ export declare const installAndConfigPlugins: (plugins: Array<PluginRequirement>, credentials?: Credentials) => Promise<void>;
@@ -14,7 +14,7 @@ import beautifyModule from "js-beautify";
14
14
  import path from "path";
15
15
  import { determinePackageManager, extractRepoName, installPackage, isValidURL } from "./helper.js";
16
16
  import ora from "ora";
17
- import { logger } from "../jumpstart-plugin.js";
17
+ import { i18nInstance, logger } from "../jumpstart-plugin.js";
18
18
  const { js_beautify } = beautifyModule;
19
19
  const BEAUTIFY_OPTIONS = {
20
20
  indent_size: 2,
@@ -29,7 +29,7 @@ export const handleMGNLConfigFile = () => __awaiter(void 0, void 0, void 0, func
29
29
  let isMgnlCliInstalled = true;
30
30
  if (!fs.existsSync(getPackageConfigPath())) {
31
31
  isMgnlCliInstalled = yield installPackage(determinePackageManager(), "@magnolia/cli@preview");
32
- logger === null || logger === void 0 ? void 0 : logger.warn("Couldn't install @magnolia/cli");
32
+ logger === null || logger === void 0 ? void 0 : logger.warn(i18nInstance.t('warn-config-h-could-not-install-cli'));
33
33
  }
34
34
  if (isMgnlCliInstalled && fs.existsSync(getLocalConfigPath())) {
35
35
  handleExistingConfigFile();
@@ -44,15 +44,15 @@ export const handleMGNLConfigFile = () => __awaiter(void 0, void 0, void 0, func
44
44
  // the mgnl.config.js exist, do not do anything
45
45
  }
46
46
  });
47
- export const installAndConfigPlugins = (plugins) => __awaiter(void 0, void 0, void 0, function* () {
47
+ export const installAndConfigPlugins = (plugins, credentials) => __awaiter(void 0, void 0, void 0, function* () {
48
48
  const localConfig = parseConfigFile(getLocalConfigPath());
49
49
  if (!localConfig)
50
50
  return;
51
51
  const missingPlugins = findMissingPlugins(plugins, localConfig);
52
52
  if (missingPlugins.length === 0)
53
53
  return;
54
- logger === null || logger === void 0 ? void 0 : logger.info(`Will attempt to install missing plugins`);
55
- const installedPlugins = yield installPlugins(missingPlugins);
54
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-config-h-will-install-missing-plugins"));
55
+ const installedPlugins = yield installPlugins(missingPlugins, credentials);
56
56
  const pluginsConfig = createCustomConfigForPlugins(installedPlugins);
57
57
  updateLocalConfig(pluginsConfig, localConfig);
58
58
  });
@@ -106,16 +106,25 @@ const findMissingPlugins = (plugins, config) => {
106
106
  return !config.imports.some(customImport => customImport.importedNames === packageName);
107
107
  });
108
108
  };
109
- const installPlugins = (plugins) => __awaiter(void 0, void 0, void 0, function* () {
109
+ const installPlugins = (plugins, credentials) => __awaiter(void 0, void 0, void 0, function* () {
110
110
  const packageManager = determinePackageManager();
111
111
  let installedPlugins = [];
112
112
  for (const plugin of plugins) {
113
- const installed = yield installPackage(packageManager, plugin.installReference, plugin.name);
113
+ const installed = yield installPackage(packageManager, plugin.installReference, plugin.name, credentials);
114
114
  if (installed)
115
115
  installedPlugins.push(plugin);
116
116
  }
117
117
  return installedPlugins;
118
118
  });
119
+ const extractPackageName = (installReference) => {
120
+ // Use a regular expression to match the package name.
121
+ const regex = /^(?:@([^/]+)\/)?([^@]+)/;
122
+ const match = installReference.match(regex);
123
+ if (!match)
124
+ return null;
125
+ // Construct package name. If it's a scoped package, prepend the scope.
126
+ return match[1] ? `@${match[1]}/${match[2]}` : match[2];
127
+ };
119
128
  const createCustomConfigForPlugins = (plugins) => {
120
129
  const defaultConfig = { configContent: "", imports: [], commands: [] };
121
130
  plugins.forEach(plugin => {
@@ -126,8 +135,8 @@ const createCustomConfigForPlugins = (plugins) => {
126
135
  if (!importSource) {
127
136
  importSource = plugin.installReference;
128
137
  }
129
- defaultConfig.imports.push({ importedNames: plugin.name, importSource: importSource });
130
- defaultConfig.commands.push({ commandName: plugin.name });
138
+ defaultConfig.imports.push({ importedNames: plugin.name, importSource: extractPackageName(importSource) || importSource });
139
+ defaultConfig.commands.push({ commandName: plugin.name, options: plugin.options });
131
140
  });
132
141
  return defaultConfig;
133
142
  };
@@ -137,7 +146,9 @@ const parseConfigFile = (configPath) => {
137
146
  configContent = fs.readFileSync(configPath, 'utf-8');
138
147
  }
139
148
  catch (e) {
140
- logger === null || logger === void 0 ? void 0 : logger.error(`Couldn't read ${configPath}`);
149
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-config-h-could-not-read-mgnl-config", {
150
+ configPath: configPath
151
+ }));
141
152
  console.error(e);
142
153
  return null;
143
154
  }
@@ -146,7 +157,9 @@ const parseConfigFile = (configPath) => {
146
157
  parsedConfig = parser.parse(configContent, { sourceType: 'module' });
147
158
  }
148
159
  catch (e) {
149
- logger === null || logger === void 0 ? void 0 : logger.error(`Couldn't parse ${configPath}, please check the file for issues`);
160
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-config-h-could-not-parse-mgnl-config", {
161
+ configPath: configPath
162
+ }));
150
163
  console.error(e);
151
164
  return null;
152
165
  }
@@ -193,8 +206,14 @@ const updateLocalConfig = (defaultConfig, localConfig) => {
193
206
  const updatedConfigContent = mergeCommandsWithConfigContent(localConfig.configContent, missingCommands);
194
207
  writeToLocalConfigFile(mergedImports, updatedConfigContent);
195
208
  };
209
+ const formatOptions = (options) => {
210
+ if (!options)
211
+ return '()';
212
+ const formattedOptions = JSON.stringify(options, null);
213
+ return `(${formattedOptions})`;
214
+ };
196
215
  const mergeCommandsWithConfigContent = (configContent, missingCommands) => {
197
- const missingCommandsStr = missingCommands.map(c => `new ${c.commandName}()`).join(',\n');
216
+ const missingCommandsStr = missingCommands.map(c => `new ${c.commandName}${formatOptions(c.options)}`).join(',\n');
198
217
  const commandsPattern = /(commands\s*:\s*\[)([^\]]*?)(,\s*)?(\])/;
199
218
  return configContent.replace(commandsPattern, (match, p1, p2, p3, p4) => {
200
219
  const separator = (p2.trim() !== "" && missingCommandsStr.trim() !== "") ? "," : "";
@@ -207,15 +226,15 @@ const writeToLocalConfigFile = (mergedImports, updatedConfigContent) => {
207
226
  writeConfigContent(configContentWithImports);
208
227
  };
209
228
  const writeConfigContent = (content) => {
210
- const spinner = ora().start(`Updating mgnl.config.js`);
229
+ const spinner = ora().start(i18nInstance.t("ora-updating-mgnl-config"));
211
230
  try {
212
231
  fs.writeFileSync(getLocalConfigPath(), js_beautify(content.trim(), BEAUTIFY_OPTIONS), 'utf-8');
213
232
  }
214
233
  catch (e) {
215
234
  spinner.stop();
216
- logger === null || logger === void 0 ? void 0 : logger.error('Error occured while updating mgnl.config.js');
235
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-config-h-while-updating-mgnl-config"));
217
236
  console.error(e);
218
237
  }
219
238
  spinner.stop();
220
- logger === null || logger === void 0 ? void 0 : logger.info('mgnl.config.js updated');
239
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-config-h-mgnl-config-updated"));
221
240
  };
@@ -12,12 +12,15 @@ import fs from "fs-extra";
12
12
  import axios from "axios";
13
13
  import ProgressBar from "progress";
14
14
  import inquirer from "inquirer";
15
- import { logger } from "../jumpstart-plugin.js";
15
+ import { i18nInstance, logger } from "../jumpstart-plugin.js";
16
16
  export const downloadBundle = (bundle, credentials, dest) => __awaiter(void 0, void 0, void 0, function* () {
17
17
  try {
18
18
  let url = bundle.url;
19
19
  const downloadDest = dest ? dest : "./download-" + new Date().getTime();
20
20
  let downloadUrl;
21
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-download-preparing", {
22
+ url
23
+ }));
21
24
  if (path.parse(url).name === "tags") {
22
25
  downloadUrl = yield selectTag(url, credentials);
23
26
  }
@@ -31,7 +34,7 @@ export const downloadBundle = (bundle, credentials, dest) => __awaiter(void 0, v
31
34
  method: 'get',
32
35
  responseType: 'stream',
33
36
  };
34
- if (credentials) {
37
+ if (credentials && credentials.username && credentials.password) {
35
38
  opts.auth = credentials;
36
39
  }
37
40
  if (downloadDest) {
@@ -39,8 +42,16 @@ export const downloadBundle = (bundle, credentials, dest) => __awaiter(void 0, v
39
42
  }
40
43
  const tempDownload = path.join(downloadDest, bundle.name ? bundle.name : 'temp-' + new Date().getTime());
41
44
  const target = fs.createWriteStream(tempDownload);
42
- logger === null || logger === void 0 ? void 0 : logger.info("Starting download from: " + downloadUrl);
45
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-download-starting", {
46
+ downloadUrl: downloadUrl
47
+ }));
43
48
  const res = yield axios.get(downloadUrl, opts);
49
+ if (res.status !== 200) {
50
+ throw new Error(i18nInstance.t("info-download-status-error", {
51
+ downloadUrl: downloadUrl,
52
+ status: res.status
53
+ }));
54
+ }
44
55
  if (res.headers['content-length']) {
45
56
  const len = parseInt(res.headers['content-length'], 10);
46
57
  const bar = new ProgressBar('Downloading [:bar] :percent :etas', {
@@ -54,14 +65,19 @@ export const downloadBundle = (bundle, credentials, dest) => __awaiter(void 0, v
54
65
  });
55
66
  }
56
67
  res.data.pipe(target);
57
- res.data.on('error', (error) => {
58
- throw new Error(error);
59
- });
60
68
  return new Promise((resolve) => {
61
- res.data.on('end', () => {
62
- logger === null || logger === void 0 ? void 0 : logger.info("Download finished");
69
+ target.on('finish', () => {
70
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-download-finished"));
63
71
  resolve(path.join('.', tempDownload));
64
72
  });
73
+ target.on('error', (error) => {
74
+ target.close();
75
+ throw new Error(error);
76
+ });
77
+ res.data.on('error', (error) => {
78
+ target.close();
79
+ throw new Error(error);
80
+ });
65
81
  });
66
82
  }
67
83
  catch (error) {
@@ -88,6 +104,12 @@ export const getDownloadUrl = (bundle, credentials) => __awaiter(void 0, void 0,
88
104
  return downloadUrl;
89
105
  }
90
106
  catch (error) {
107
+ if (error.code === 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY') {
108
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-download-problem-accessing-url", {
109
+ url: bundle.url
110
+ }));
111
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-download-unable-to-get-local-issuer-certificate"));
112
+ }
91
113
  throw new Error(error);
92
114
  }
93
115
  });
@@ -104,7 +126,7 @@ export const selectTag = (url, credentials) => __awaiter(void 0, void 0, void 0,
104
126
  }
105
127
  const res = yield axios.get(url, opts);
106
128
  if (!((_a = res.data) === null || _a === void 0 ? void 0 : _a.values) || res.data.values.length <= 0) {
107
- throw new Error("There are no available tags");
129
+ throw new Error(i18nInstance.t("error-download-no-tags"));
108
130
  }
109
131
  const { tag } = yield inquirer
110
132
  .prompt([
@@ -0,0 +1,5 @@
1
+ export declare const evaluateCustomPrompts: (extensionsPath: string) => Promise<{
2
+ answers: import("inquirer").Answers;
3
+ executePostCommands: (context: any) => Promise<void>;
4
+ } | undefined>;
5
+ export declare const compileCustomPrompts: (answers: any, pattern?: string) => Promise<void>;
@@ -0,0 +1,125 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import fs from "fs-extra";
11
+ import path from "path";
12
+ import inquirer from "inquirer";
13
+ import { execa } from "execa";
14
+ import { parse } from 'yaml';
15
+ import { i18nInstance, logger } from "../jumpstart-plugin.js";
16
+ import ora from "ora";
17
+ import { determinePackageManager } from "./helper.js";
18
+ import { pathToFileURL } from "url";
19
+ import { globSync } from "glob";
20
+ import _ from "underscore";
21
+ function pause(milliSeconds) {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ return new Promise((resolve) => {
24
+ setTimeout(() => {
25
+ resolve();
26
+ }, milliSeconds);
27
+ });
28
+ });
29
+ }
30
+ function callExtensionFunction(func_1, context_1, name_1) {
31
+ return __awaiter(this, arguments, void 0, function* (func, context, name, delay = 200, retry = 0, maxRetry = 0) {
32
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-extensions-calling-fn", {
33
+ name: name
34
+ }));
35
+ try {
36
+ yield func(context);
37
+ }
38
+ catch (error) {
39
+ if (retry === maxRetry) {
40
+ throw error;
41
+ }
42
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-extensions-calling-fn-retry", {
43
+ name: name,
44
+ retry: retry,
45
+ maxRetry: maxRetry,
46
+ delay: delay
47
+ }));
48
+ yield pause(delay);
49
+ yield callExtensionFunction(func, context, name, delay, retry + 1, maxRetry);
50
+ }
51
+ });
52
+ }
53
+ function executePostCommands() {
54
+ return __awaiter(this, arguments, void 0, function* (cmds = [], extensions, context) {
55
+ for (let cmd of cmds) {
56
+ const func = extensions[cmd.function];
57
+ if (func) {
58
+ try {
59
+ yield callExtensionFunction(func, context, cmd.function, cmd.delay, 0, cmd.retry);
60
+ }
61
+ catch (e) {
62
+ throw e;
63
+ }
64
+ }
65
+ else {
66
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-extensions-fn-not-found", {
67
+ function: cmd.function
68
+ }));
69
+ }
70
+ }
71
+ });
72
+ }
73
+ // evaluate the given yaml file and create cli prompts out of it, return the answers
74
+ export const evaluateCustomPrompts = (extensionsPath) => __awaiter(void 0, void 0, void 0, function* () {
75
+ if (!fs.existsSync(extensionsPath)) {
76
+ return;
77
+ }
78
+ const doc = parse(fs.readFileSync(extensionsPath, 'utf8'));
79
+ const baseDir = path.parse(extensionsPath).dir;
80
+ const packageJson = path.join(baseDir, 'package.json');
81
+ if (fs.existsSync(packageJson)) {
82
+ const npmISpinner = ora().start(i18nInstance.t("ora-installing-ext-dep"));
83
+ const packageManager = determinePackageManager(baseDir);
84
+ try {
85
+ yield execa(packageManager, ["install"], {
86
+ buffer: true,
87
+ cwd: baseDir
88
+ });
89
+ }
90
+ catch (e) {
91
+ npmISpinner.stop();
92
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-extensions-install-fail", {
93
+ packageManager: packageManager
94
+ }));
95
+ return;
96
+ }
97
+ npmISpinner.stop();
98
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-extensions-install-done"));
99
+ }
100
+ try {
101
+ const extensions = doc.extend ? yield import(pathToFileURL(path.join(baseDir, doc.extend)).href) : null;
102
+ return {
103
+ answers: doc.prompts ? yield inquirer.prompt(doc.prompts) : {},
104
+ executePostCommands: (context) => __awaiter(void 0, void 0, void 0, function* () { var _a; return yield executePostCommands((_a = doc === null || doc === void 0 ? void 0 : doc.commands) === null || _a === void 0 ? void 0 : _a.post, extensions, context); })
105
+ };
106
+ }
107
+ catch (error) {
108
+ throw error;
109
+ }
110
+ });
111
+ export const compileCustomPrompts = (answers_1, ...args_1) => __awaiter(void 0, [answers_1, ...args_1], void 0, function* (answers, pattern = '**/**/**/*.{yaml,tsx,ts,js,jsx,vue,md,json}') {
112
+ const init = globSync(pattern, { ignore: 'node_modules/**' }).map(itm => path.join(process.cwd(), itm));
113
+ const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), './package.json'), 'utf8'));
114
+ pkg.answers = answers;
115
+ for (const item of init) {
116
+ try {
117
+ const file = fs.readFileSync(item, 'utf8');
118
+ const doc = _.template(file)(pkg);
119
+ fs.outputFileSync(item, doc, 'utf8');
120
+ }
121
+ catch (e) {
122
+ throw e;
123
+ }
124
+ }
125
+ });
@@ -13,10 +13,10 @@ import walk from "walk";
13
13
  import fs from "fs-extra";
14
14
  import { PostCommands } from "../types/types.js";
15
15
  import url from "url";
16
- import { logger } from "../jumpstart-plugin.js";
16
+ import { i18nInstance, logger } from "../jumpstart-plugin.js";
17
17
  export const extract = (bundle, file, command) => __awaiter(void 0, void 0, void 0, function* () {
18
18
  var _a;
19
- logger === null || logger === void 0 ? void 0 : logger.info('Extracting...');
19
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-extract-extracting"));
20
20
  const { dest = "./", type } = bundle;
21
21
  const unzipFolder = "temp-" + new Date().getTime();
22
22
  yield decompress(file, unzipFolder);
@@ -48,7 +48,7 @@ export const findExtractedApacheTomcatDir = (p) => {
48
48
  next();
49
49
  });
50
50
  walker.on('end', function () {
51
- return reject(new Error("apache-tomcat was not found."));
51
+ return reject(new Error(i18nInstance.t("error-extract-apache-tomcat-not-found")));
52
52
  });
53
53
  });
54
54
  };
@@ -1,10 +1,13 @@
1
- import { PluginRequirement } from "../types/types.js";
1
+ import { Credentials, PluginRequirement } from "../types/types.js";
2
+ import { i18n } from "i18next";
3
+ export declare function initI18n(pluginName: string, namespace?: string): i18n;
2
4
  export declare const handleLightModulesFolder: () => Promise<void>;
3
5
  export declare const findLightModulesFolder: (p: string) => Promise<unknown>;
4
6
  export declare const findWebAppsList: (apacheTomcatPath: string) => string[];
5
- export declare const installAdditionalPlugins: (plugins: Array<PluginRequirement>) => Promise<void>;
7
+ export declare const installAdditionalPlugins: (plugins: Array<PluginRequirement>, credentials?: Credentials) => Promise<void>;
6
8
  export declare const initializeNodeProject: () => Promise<void>;
7
- export declare const installPackage: (packageManager: string, packageReference: string, packageName?: string) => Promise<boolean>;
9
+ export declare const installPackage: (packageManager: string, packageReference: string, packageName?: string, credentials?: Credentials) => Promise<boolean>;
8
10
  export declare const isValidURL: (str: string) => boolean;
9
11
  export declare const extractRepoName: (gitUrl: string) => string | null;
10
12
  export declare const determinePackageManager: (baseDirectory?: string) => "yarn" | "npm";
13
+ export declare const askForCredentials: () => Promise<Credentials>;
@@ -9,32 +9,84 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  import { handleMGNLConfigFile, installAndConfigPlugins } from "./config-helper.js";
11
11
  import { handlePackageJSON } from "./pj-helper.js";
12
+ import i18next from "i18next";
13
+ import Backend from 'i18next-fs-backend';
12
14
  import ora from "ora";
13
15
  import { execa } from "execa";
14
16
  import path from "path";
15
17
  import fs from "fs-extra";
16
18
  import { findExtractedApacheTomcatDir } from "./extract.js";
17
19
  import walk from "walk";
18
- import { logger } from "../jumpstart-plugin.js";
20
+ import { i18nInstance, logger } from "../jumpstart-plugin.js";
21
+ import { fileURLToPath } from "url";
22
+ import inquirer from "inquirer";
23
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
24
+ export function initI18n(pluginName, namespace = "translation") {
25
+ const lng = 'en';
26
+ const localesPath = path.join(__dirname, `locales/${lng}`);
27
+ const namespaces = fs.readdirSync(localesPath)
28
+ .filter(file => file.endsWith('.json'))
29
+ .map(file => file.replace('.json', ''));
30
+ if (!namespaces.includes(namespace)) {
31
+ console.warn(`${pluginName}: Requested translation file "${namespace}" not found at "${localesPath}".\n`);
32
+ if (namespaces.length > 0) {
33
+ console.warn(`Using "${namespaces[0]}" instead.\n`);
34
+ }
35
+ namespace = namespaces[0];
36
+ }
37
+ const newI18nInstance = i18next.createInstance();
38
+ newI18nInstance
39
+ .use(Backend)
40
+ .init({
41
+ joinArrays: '\n',
42
+ lng: lng,
43
+ fallbackLng: 'en',
44
+ ns: namespaces,
45
+ defaultNS: namespace,
46
+ initImmediate: false,
47
+ interpolation: {
48
+ escapeValue: false,
49
+ },
50
+ backend: {
51
+ loadPath: path.join(__dirname, 'locales/{{lng}}/{{ns}}.json')
52
+ }
53
+ });
54
+ return newI18nInstance;
55
+ }
19
56
  export const handleLightModulesFolder = () => __awaiter(void 0, void 0, void 0, function* () {
20
57
  let lightModulesPath;
21
58
  try {
22
59
  lightModulesPath = (yield findLightModulesFolder(process.cwd()));
23
- logger === null || logger === void 0 ? void 0 : logger.info(`Found existing light-modules path at '${lightModulesPath}'`);
60
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-helper-found-lm", {
61
+ lightModulesPath: lightModulesPath
62
+ }));
24
63
  }
25
64
  catch (e) {
26
- logger === null || logger === void 0 ? void 0 : logger.info(`No light-modules folder found. Attempting to create one.`);
65
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-helper-no-lm-found"));
27
66
  lightModulesPath = path.join(process.cwd(), 'light-modules');
28
67
  try {
29
68
  fs.mkdirSync(lightModulesPath, { recursive: true });
30
- logger === null || logger === void 0 ? void 0 : logger.info(`'light-modules' folder created at '${lightModulesPath}'.`);
69
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-helper-lm-created", {
70
+ lightModulesPath: lightModulesPath
71
+ }));
31
72
  }
32
73
  catch (error) {
33
- logger === null || logger === void 0 ? void 0 : logger.error(`Failed to create 'light-modules' folder: ${error instanceof Error ? error.message : ""}`);
74
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-helper-lm-create-fail", {
75
+ errorMsg: error instanceof Error ? error.message : ""
76
+ }));
34
77
  return;
35
78
  }
36
79
  }
37
- const apacheTomcatPath = yield findExtractedApacheTomcatDir(process.cwd());
80
+ let apacheTomcatPath;
81
+ try {
82
+ apacheTomcatPath = yield findExtractedApacheTomcatDir(process.cwd());
83
+ }
84
+ catch (error) {
85
+ logger === null || logger === void 0 ? void 0 : logger.warn(i18nInstance.t("warn-helper-cannot-modify-webapp", {
86
+ errorMsg: error instanceof Error ? error.message : ""
87
+ }));
88
+ return;
89
+ }
38
90
  const webApps = findWebAppsList(apacheTomcatPath);
39
91
  for (const webApp of webApps) {
40
92
  const lightModulesPathToMagnoliaHomeRelPath = process.platform === 'win32' ? path.relative(webApp, lightModulesPath).replace(/\\/g, '/') : path.relative(webApp, lightModulesPath);
@@ -51,7 +103,9 @@ export const handleLightModulesFolder = () => __awaiter(void 0, void 0, void 0,
51
103
  });
52
104
  const editProperties = (filePath, props) => {
53
105
  if (!fs.existsSync(filePath)) {
54
- logger === null || logger === void 0 ? void 0 : logger.warn(`File ${filePath} doesn't exists, skipping configuration.`);
106
+ logger === null || logger === void 0 ? void 0 : logger.warn(i18nInstance.t("warn-helper-webapp-props-not-exist", {
107
+ filePath: filePath
108
+ }));
55
109
  return;
56
110
  }
57
111
  const propertiesFileContent = fs.readFileSync(filePath, 'utf-8');
@@ -59,11 +113,17 @@ const editProperties = (filePath, props) => {
59
113
  try {
60
114
  fs.writeFileSync(filePath, replacedContent);
61
115
  Object.keys(props).forEach((key) => {
62
- logger === null || logger === void 0 ? void 0 : logger.info(`Setting ${key} to: ${props[key]} in ${filePath}`);
116
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-helper-webapp-prop-setting", {
117
+ key: key,
118
+ propsKey: props[key],
119
+ filePath: filePath
120
+ }));
63
121
  });
64
122
  }
65
123
  catch (error) {
66
- logger === null || logger === void 0 ? void 0 : logger.error(`An error occurred while updating 'magnolia.properties': ${error instanceof Error ? error.message : ""}`);
124
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-helper-webapp-props-update-error", {
125
+ errorMsg: error instanceof Error ? error.message : ""
126
+ }));
67
127
  }
68
128
  };
69
129
  const replaceInConfig = (config, props) => {
@@ -84,7 +144,7 @@ export const findLightModulesFolder = (p) => {
84
144
  next();
85
145
  });
86
146
  walker.on('end', function () {
87
- return reject(new Error("light-modules was not found."));
147
+ return reject(new Error(i18nInstance.t("error-helper-lm-not-found")));
88
148
  });
89
149
  });
90
150
  };
@@ -100,30 +160,42 @@ export const findWebAppsList = (apacheTomcatPath) => {
100
160
  }
101
161
  return webApps;
102
162
  };
103
- export const installAdditionalPlugins = (plugins) => __awaiter(void 0, void 0, void 0, function* () {
104
- yield installAndConfigPlugins(plugins);
163
+ export const installAdditionalPlugins = (plugins, credentials) => __awaiter(void 0, void 0, void 0, function* () {
164
+ yield installAndConfigPlugins(plugins, credentials);
105
165
  });
106
166
  export const initializeNodeProject = () => __awaiter(void 0, void 0, void 0, function* () {
107
167
  yield handlePackageJSON();
108
168
  yield handleMGNLConfigFile();
109
169
  });
110
- export const installPackage = (packageManager, packageReference, packageName) => __awaiter(void 0, void 0, void 0, function* () {
170
+ export const installPackage = (packageManager, packageReference, packageName, credentials) => __awaiter(void 0, void 0, void 0, function* () {
111
171
  if (!packageName) {
112
172
  packageName = packageReference;
113
173
  }
114
- const spinner = ora().start(`Installing ${packageName}`);
174
+ if (packageReference.startsWith("git+https://git.magnolia-cms.com")) {
175
+ if (credentials === undefined) {
176
+ credentials = yield askForCredentials();
177
+ }
178
+ packageReference = packageReference.replace("git+https://git.magnolia-cms.com", `git+https://${credentials.username}:${credentials.password}@git.magnolia-cms.com`);
179
+ }
180
+ const spinner = ora().start(i18nInstance.t("ora-installing-package", {
181
+ packageName: packageName
182
+ }));
115
183
  try {
116
184
  yield execa(packageManager, [packageManager === 'npm' ? 'install' : 'add', packageReference], {
117
185
  buffer: true,
118
186
  cwd: process.cwd()
119
187
  });
120
188
  spinner.stop();
121
- logger === null || logger === void 0 ? void 0 : logger.info(`${packageName} installed`);
189
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-helper-package-installed", {
190
+ packageName: packageName
191
+ }));
122
192
  return true;
123
193
  }
124
194
  catch (error) {
125
195
  spinner.stop();
126
- logger === null || logger === void 0 ? void 0 : logger.error(`Failed to install ${packageName}`);
196
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-helper-fail-to-install-package", {
197
+ packageName: packageName
198
+ }));
127
199
  return false;
128
200
  }
129
201
  });
@@ -152,3 +224,17 @@ export const determinePackageManager = (baseDirectory = process.cwd()) => {
152
224
  // Default to npm
153
225
  return 'npm';
154
226
  };
227
+ export const askForCredentials = () => __awaiter(void 0, void 0, void 0, function* () {
228
+ return inquirer.prompt([
229
+ {
230
+ type: 'input',
231
+ name: 'username',
232
+ message: 'Username'
233
+ },
234
+ {
235
+ type: 'password',
236
+ name: 'password',
237
+ message: 'Password'
238
+ }
239
+ ]);
240
+ });
@@ -11,19 +11,19 @@ import { PostCommands } from "../types/types.js";
11
11
  import path from "path";
12
12
  import { execa } from "execa";
13
13
  import ora from 'ora';
14
- import { logger } from "../jumpstart-plugin.js";
14
+ import { i18nInstance, logger } from "../jumpstart-plugin.js";
15
15
  export const installDependencies = (bundle, command) => __awaiter(void 0, void 0, void 0, function* () {
16
- const spinner = ora('Installing dependencies').start();
16
+ const spinner = ora(i18nInstance.t("ora-installing-dep")).start();
17
17
  try {
18
18
  yield execa(command === PostCommands.YarnInstall ? 'yarn' : 'npm', ['install'], {
19
19
  buffer: true,
20
20
  cwd: path.join(process.cwd(), bundle.dest ? bundle.dest : "./")
21
21
  });
22
22
  spinner.stop();
23
- logger === null || logger === void 0 ? void 0 : logger.info('Dependencies installed');
23
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-install-dep"));
24
24
  }
25
25
  catch (e) {
26
26
  spinner.stop();
27
- logger === null || logger === void 0 ? void 0 : logger.error('Failed to install dependencies');
27
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-install-dep-fail"));
28
28
  }
29
29
  });
@@ -0,0 +1,61 @@
1
+ {
2
+ "cmd-option-template": "Choose template",
3
+ "cmd-option-project-templates-path": "Specify from where the projectTemplates should be loaded",
4
+
5
+ "prompt-enter-credentials": "Please enter credentials for {{bundle}}",
6
+ "prompt-project-templates-credentials": "Please enter credentials",
7
+
8
+ "info-template_not_found": "The '{{template}}' template was not found.",
9
+ "info-jumpstart_started": "'JumpstartPlugin' plugin started successfully.",
10
+ "info-jumpstart_stopped": "'JumpstartPlugin' stopped",
11
+ "info-config-h-will-install-missing-plugins": "Will attempt to install missing plugins",
12
+ "info-config-h-mgnl-config-updated": "mgnl.config.js updated",
13
+ "info-download-preparing": "Preparing to download {{url}}",
14
+ "info-download-starting": "Starting download from: {{downloadUrl}}",
15
+ "info-download-finished": "Download finished",
16
+ "info-download-status-error": "Failed to download from: {{downloadUrl}}. Expected 200, but received: {{status}}.",
17
+ "info-extensions-calling-fn": "Calling Function {{name}}.",
18
+ "info-extensions-install-done": "Installing Extension Dependencies. Done.",
19
+ "info-extract-extracting": "Extracting...",
20
+ "info-install-dep": "Dependencies installed",
21
+ "info-pj-helper-npm-done": "Running 'npm install' done",
22
+ "info-pj-helper-pj-created": "package.json created",
23
+ "info-pj-helper-add-mgnl-to-pj": "Adding \"mgnl\" script to package.json",
24
+ "info-helper-found-lm": "Found existing light-modules path at '{{lightModulesPath}}'",
25
+ "info-helper-no-lm-found": "No light-modules folder found. Attempting to create one.",
26
+ "info-helper-lm-created": "light-modules folder created at '{{lightModulesPath}}'.",
27
+ "info-helper-webapp-prop-setting": "Setting {{key}} to: {{propsKey}} in {{filePath}}",
28
+ "info-helper-package-installed": "{{packageName}} installed",
29
+
30
+ "warn-config-h-could-not-install-cli": "Couldn't install @magnolia/cli",
31
+ "warn-pj-helper-changing-type": "Changing 'type' to \"module\"",
32
+ "warn-helper-webapp-props-not-exist": "File {{filePath}} doesn't exists, skipping configuration.",
33
+ "warn-helper-cannot-modify-webapp": "Cannot modify webapps: {{errorMsg}}",
34
+
35
+ "error-config-h-could-not-read-mgnl-config": "Couldn't read {{configPath}}",
36
+ "error-config-h-could-not-parse-mgnl-config": "Couldn't parse {{configPath}}, please check the file for issues",
37
+ "error-config-h-while-updating-mgnl-config": "Error occurred while updating mgnl.config.js",
38
+ "error-download-no-tags": "There are no available tags",
39
+ "error-download-problem-accessing-url": "A problem was detected while accessing: {{url}}.",
40
+ "error-download-unable-to-get-local-issuer-certificate": "The \"unable to get local issuer certificate\" error indicates that something in your infrastructure is preventing the CLI from downloading the Magnolia artifacts. This is not a bug in the CLI. For example, you may be behind a proxy that requires certain certificates. You might be able to ask your infrastructure team to allow requests to the Magnolia artifact repository: https://nexus.magnolia-cms.com/ Please search the web on \"unable to get local issuer certificate\" for more information.",
41
+ "error-extensions-calling-fn-retry": "Error Calling Function {{name}}. {{retry}}/{{maxRetry}}. Waiting {{delay}}ms before retry.",
42
+ "error-extensions-fn-not-found": "{{function}} Function not found",
43
+ "error-extensions-path-not-found": "{{extensionsPath}} not found. Could not evaluate custom prompts.",
44
+ "error-extensions-install-fail": "'{{packageManager}} install' has failed",
45
+ "error-extract-apache-tomcat-not-found": "apache-tomcat was not found.",
46
+ "error-install-dep-fail": "Failed to install dependencies",
47
+ "error-pj-helper-npm-fail": "'npm install' has failed",
48
+ "error-pj-helper-pj-mod-fail": "Failed to modify package.json",
49
+ "error-pj-helper-pj-create-fail": "Failed to create package.json",
50
+ "error-helper-lm-create-fail": "Failed to create 'light-modules' folder: {{errorMsg}}",
51
+ "error-helper-webapp-props-update-error": "An error occurred while updating 'magnolia.properties': {{errorMsg}}",
52
+ "error-helper-lm-not-found": "light-modules was not found",
53
+ "error-helper-fail-to-install-package": "Failed to install {{packageName}}",
54
+
55
+ "ora-updating-mgnl-config": "Updating mgnl.config.js",
56
+ "ora-installing-ext-dep": "Installing Extension Dependencies",
57
+ "ora-installing-dep": "Installing dependencies",
58
+ "ora-running-npm-install": "Didn't find 'node_modules' folder. Running 'npm install'",
59
+ "ora-creating-pj": "Creating package.json",
60
+ "ora-installing-package": "Installing {{packageName}}"
61
+ }
@@ -12,7 +12,7 @@ import ora from "ora";
12
12
  import fs from "fs-extra";
13
13
  import { execa } from "execa";
14
14
  import { determinePackageManager, installPackage } from "./helper.js";
15
- import { logger } from "../jumpstart-plugin.js";
15
+ import { i18nInstance, logger } from "../jumpstart-plugin.js";
16
16
  export const handlePackageJSON = () => __awaiter(void 0, void 0, void 0, function* () {
17
17
  const pjPath = path.join(process.cwd(), 'package.json');
18
18
  if (!fs.existsSync(pjPath)) {
@@ -31,7 +31,7 @@ export const modifyPackageJSON = (pjPath) => __awaiter(void 0, void 0, void 0, f
31
31
  }
32
32
  const nodeModulesPath = path.join(process.cwd(), "node_modules");
33
33
  if (!fs.existsSync(nodeModulesPath)) {
34
- const npmISpinner = ora().start("Didn't find 'node_modules' folder. Running 'npm install'");
34
+ const npmISpinner = ora().start(i18nInstance.t("ora-running-npm-install"));
35
35
  try {
36
36
  yield execa(determinePackageManager(), ["install"], {
37
37
  buffer: true,
@@ -40,11 +40,11 @@ export const modifyPackageJSON = (pjPath) => __awaiter(void 0, void 0, void 0, f
40
40
  }
41
41
  catch (e) {
42
42
  npmISpinner.stop();
43
- logger === null || logger === void 0 ? void 0 : logger.error("'npm install' has failed");
43
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-pj-helper-npm-fail"));
44
44
  return;
45
45
  }
46
46
  npmISpinner.stop();
47
- logger === null || logger === void 0 ? void 0 : logger.info("Running 'npm install' done");
47
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-pj-helper-npm-done"));
48
48
  }
49
49
  if (isMgnlCliInstalled) {
50
50
  const mgnlScript = { "mgnl": "node node_modules/@magnolia/cli" };
@@ -53,7 +53,7 @@ export const modifyPackageJSON = (pjPath) => __awaiter(void 0, void 0, void 0, f
53
53
  pj.scripts = {};
54
54
  }
55
55
  if (!pj.scripts[scriptKey] || pj.scripts[scriptKey] !== mgnlScript[scriptKey]) {
56
- logger === null || logger === void 0 ? void 0 : logger.info('Add "mgnl" script to package.json');
56
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-pj-helper-add-mgnl-to-pj"));
57
57
  pj.scripts[scriptKey] = mgnlScript[scriptKey];
58
58
  }
59
59
  }
@@ -61,19 +61,19 @@ export const modifyPackageJSON = (pjPath) => __awaiter(void 0, void 0, void 0, f
61
61
  pj.type = "module";
62
62
  }
63
63
  else if (pj.type !== "module") {
64
- logger === null || logger === void 0 ? void 0 : logger.warn(`Changing 'type' to "module"`);
64
+ logger === null || logger === void 0 ? void 0 : logger.warn(i18nInstance.t("warn-pj-helper-changing-type"));
65
65
  pj.type = "module";
66
66
  }
67
67
  try {
68
68
  fs.writeFileSync(pjPath, JSON.stringify(pj, null, '\t'), 'utf8');
69
69
  }
70
70
  catch (e) {
71
- logger === null || logger === void 0 ? void 0 : logger.error('Failed to modify package.json');
71
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-pj-helper-pj-mod-fail"));
72
72
  return;
73
73
  }
74
74
  });
75
75
  export const initPackageJSON = () => __awaiter(void 0, void 0, void 0, function* () {
76
- const pjSpinner = ora().start('Creating package.json');
76
+ const pjSpinner = ora().start(i18nInstance.t("ora-creating-pj"));
77
77
  try {
78
78
  yield execa(determinePackageManager(), ['init', '-y'], {
79
79
  buffer: true,
@@ -82,10 +82,10 @@ export const initPackageJSON = () => __awaiter(void 0, void 0, void 0, function*
82
82
  }
83
83
  catch (e) {
84
84
  pjSpinner.stop();
85
- logger === null || logger === void 0 ? void 0 : logger.info('Failed to create package.json');
85
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-pj-helper-pj-create-fail"));
86
86
  console.error(e);
87
87
  return;
88
88
  }
89
89
  pjSpinner.stop();
90
- logger === null || logger === void 0 ? void 0 : logger.info('package.json created');
90
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-pj-helper-pj-created"));
91
91
  });
package/dist/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@magnolia/cli-jumpstart-plugin",
3
- "version": "1.0.0-preview.3",
3
+ "version": "1.0.0-preview.4",
4
4
  "description": "Plugin to set up new projects using predefined templates and bundles. Easily select and configure project templates, and let the plugin handle bundle downloads, extraction, and post-command execution.",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
7
- "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
8
- "build": "standard && tsc && cpy package.json dist",
7
+ "test": "npm run build && node --experimental-vm-modules node_modules/jest/bin/jest.js",
8
+ "build": "standard --fix && tsc && cpy package.json dist && cpy lib/locales dist",
9
9
  "standard": "npx standard"
10
10
  },
11
11
  "keywords": [],
12
- "author": "",
13
- "license": "ISC",
12
+ "author": "Magnolia International Ltd.",
13
+ "license": "SEE LICENSE IN LICENSE.txt",
14
14
  "devDependencies": {
15
15
  "@jest/globals": "^29.6.3",
16
16
  "@types/decompress": "^4.2.4",
@@ -19,6 +19,7 @@
19
19
  "@types/js-beautify": "^1.14.0",
20
20
  "@types/node": "^20.3.3",
21
21
  "@types/progress": "^2.0.5",
22
+ "@types/underscore": "^1.11.14",
22
23
  "@types/walk": "^2.3.1",
23
24
  "@types/yauzl": "^2.10.1",
24
25
  "cpy-cli": "^5.0.0",
@@ -37,12 +38,17 @@
37
38
  "dotenv": "^16.3.1",
38
39
  "execa": "^7.2.0",
39
40
  "fs-extra": "^11.1.1",
41
+ "glob": "^10.3.10",
42
+ "i18next": "^23.8.2",
43
+ "i18next-fs-backend": "^2.3.1",
40
44
  "inquirer": "^9.2.8",
41
45
  "js-beautify": "^1.14.9",
42
46
  "ora": "^7.0.1",
43
47
  "progress": "^2.0.3",
48
+ "underscore": "^1.13.6",
44
49
  "walk": "^2.3.15",
45
- "winston": "^3.10.0"
50
+ "winston": "^3.10.0",
51
+ "yaml": "^2.3.4"
46
52
  },
47
53
  "type": "module",
48
54
  "standard": {
@@ -40,11 +40,12 @@ export type Template = TemplateWithChildren | TemplateWithoutChildren;
40
40
  export interface PluginRequirement {
41
41
  name: string;
42
42
  installReference: string;
43
+ options?: any;
43
44
  }
44
45
  export type Credentials = {
45
46
  username: string;
46
47
  password: string;
47
- } | null;
48
+ } | undefined;
48
49
  export declare enum PostCommands {
49
50
  Extract = "extract",
50
51
  ExtractAndOverride = "extract and override",
@@ -61,9 +62,15 @@ export type ImportItem = {
61
62
  };
62
63
  export interface CommandItem {
63
64
  commandName: string;
65
+ options?: any;
64
66
  }
65
67
  export type ParsedConfigData = {
66
68
  configContent: string;
67
69
  imports: ImportItem[];
68
70
  commands: CommandItem[];
69
71
  };
72
+ export interface PostCommandFunction {
73
+ function: string;
74
+ delay: number;
75
+ retry: number;
76
+ }
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@magnolia/cli-jumpstart-plugin",
3
- "version": "1.0.0-preview.3",
3
+ "version": "1.0.0-preview.4",
4
4
  "description": "Plugin to set up new projects using predefined templates and bundles. Easily select and configure project templates, and let the plugin handle bundle downloads, extraction, and post-command execution.",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
7
- "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
8
- "build": "standard && tsc && cpy package.json dist",
7
+ "test": "npm run build && node --experimental-vm-modules node_modules/jest/bin/jest.js",
8
+ "build": "standard --fix && tsc && cpy package.json dist && cpy lib/locales dist",
9
9
  "standard": "npx standard"
10
10
  },
11
11
  "keywords": [],
12
- "author": "",
13
- "license": "ISC",
12
+ "author": "Magnolia International Ltd.",
13
+ "license": "SEE LICENSE IN LICENSE.txt",
14
14
  "devDependencies": {
15
15
  "@jest/globals": "^29.6.3",
16
16
  "@types/decompress": "^4.2.4",
@@ -19,6 +19,7 @@
19
19
  "@types/js-beautify": "^1.14.0",
20
20
  "@types/node": "^20.3.3",
21
21
  "@types/progress": "^2.0.5",
22
+ "@types/underscore": "^1.11.14",
22
23
  "@types/walk": "^2.3.1",
23
24
  "@types/yauzl": "^2.10.1",
24
25
  "cpy-cli": "^5.0.0",
@@ -37,12 +38,17 @@
37
38
  "dotenv": "^16.3.1",
38
39
  "execa": "^7.2.0",
39
40
  "fs-extra": "^11.1.1",
41
+ "glob": "^10.3.10",
42
+ "i18next": "^23.8.2",
43
+ "i18next-fs-backend": "^2.3.1",
40
44
  "inquirer": "^9.2.8",
41
45
  "js-beautify": "^1.14.9",
42
46
  "ora": "^7.0.1",
43
47
  "progress": "^2.0.3",
48
+ "underscore": "^1.13.6",
44
49
  "walk": "^2.3.15",
45
- "winston": "^3.10.0"
50
+ "winston": "^3.10.0",
51
+ "yaml": "^2.3.4"
46
52
  },
47
53
  "type": "module",
48
54
  "standard": {