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

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/README.md CHANGED
@@ -27,15 +27,17 @@ When run, the plugin will prompt the user to select a template. It will then pro
27
27
  ### **command-line options**
28
28
 
29
29
  You can customize the **jumpstart** plugin's actions using these command-line options:
30
- - `-t, --template <name>`: Choose template.
31
- - `-p, --projectTemplatesPath <path>`: Specify from where the projectTemplates should be loaded.
30
+ - `-t, --template <name>`, choose a template from available project templates
31
+ - `-p, --project-templates-path <path>`, specify the path to load project templates from
32
+ - `-m, --magnolia-version <version>`, set the Magnolia version; defaults to the latest stable version
33
+ - `-s, --snapshot`, download the latest snapshot version of the specified or latest stable webapp
32
34
 
33
35
  **Template option:**
34
36
 
35
37
  With this option, you can specify which template to use:
36
38
 
37
39
  ```bash
38
- magnolia-cli jumpstart --template "headless/minimal-headless-spa-demos/dx-core/latest"
40
+ magnolia-cli jumpstart --template "headless/minimal-headless-spa-demos/dx-core"
39
41
  ```
40
42
 
41
43
 
@@ -9,10 +9,10 @@ export declare let i18nInstance: {
9
9
  export default class JumpstartPlugin extends PluginTemplate {
10
10
  name: string;
11
11
  version: any;
12
- description: any;
13
12
  projectTemplates?: Array<Template>;
14
13
  options: Option[];
15
14
  credentials?: Credentials;
15
+ description: string;
16
16
  constructor();
17
17
  executePostCommands(bundle: Bundle, file: string): Promise<void>;
18
18
  handleTemplate(template: TemplateWithoutChildren, options: PluginOptions): Promise<void>;
@@ -19,8 +19,10 @@ 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 { askForCredentials, handleLightModulesFolder, initI18n, initializeNodeProject, installAdditionalPlugins } from "./lib/helper.js";
22
+ import { askForCredentials, handleLightModulesFolder, initI18n, initializeNodeProject } from "./lib/helper.js";
23
23
  import { compileCustomPrompts, evaluateCustomPrompts } from "./lib/extensions.js";
24
+ import { addConfigProps } from "./lib/config-helper.js";
25
+ import { installAdditionalPlugins } from "./lib/config-helper.js";
24
26
  const extensionsPath = path.resolve("./extensions/extension.yaml");
25
27
  export let logger;
26
28
  export let i18nInstance = {
@@ -31,11 +33,13 @@ export default class JumpstartPlugin extends PluginTemplate {
31
33
  super();
32
34
  this.name = "jumpstart";
33
35
  this.version = pkg.version;
34
- this.description = pkg.description;
35
36
  i18nInstance = initI18n(this.name);
37
+ this.description = i18nInstance.t('cmd-option-description');
36
38
  this.options = [
37
39
  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"))
40
+ new Option('-p, --project-templates-path <path>', i18nInstance.t("cmd-option-project-templates-path")),
41
+ new Option('-m, --magnolia-version <version>', i18nInstance.t('cmd-option-magnolia-version')),
42
+ new Option('-s, --snapshot', i18nInstance.t('cmd-option-snapshot'))
39
43
  ];
40
44
  }
41
45
  executePostCommands(bundle, file) {
@@ -46,6 +50,7 @@ export default class JumpstartPlugin extends PluginTemplate {
46
50
  switch (command) {
47
51
  case PostCommands.Extract:
48
52
  case PostCommands.ExtractAndOverride:
53
+ case PostCommands.ExtractAndUnfold:
49
54
  yield extract(bundle, file, command);
50
55
  break;
51
56
  case PostCommands.NpmInstall:
@@ -72,10 +77,10 @@ export default class JumpstartPlugin extends PluginTemplate {
72
77
  bundle: bundle.name ? bundle.name : bundle.url
73
78
  }));
74
79
  const bundleCredentials = yield askForCredentials();
75
- file = yield downloadBundle(bundle, bundleCredentials, undefined);
80
+ file = yield downloadBundle(bundle, bundleCredentials, undefined, options);
76
81
  }
77
82
  else {
78
- file = yield downloadBundle(bundle, this.credentials, undefined);
83
+ file = yield downloadBundle(bundle, this.credentials, undefined, options);
79
84
  }
80
85
  if (file) {
81
86
  yield this.executePostCommands(bundle, file);
@@ -88,7 +93,10 @@ export default class JumpstartPlugin extends PluginTemplate {
88
93
  yield handleLightModulesFolder();
89
94
  yield initializeNodeProject();
90
95
  if (template.plugins) {
91
- yield installAdditionalPlugins(template.plugins, this.credentials);
96
+ yield installAdditionalPlugins(template.plugins);
97
+ }
98
+ if (template.configVars) {
99
+ yield addConfigProps(template.configVars);
92
100
  }
93
101
  const extensions = yield evaluateCustomPrompts(extensionsPath);
94
102
  if (extensions === null || extensions === void 0 ? void 0 : extensions.answers) {
@@ -104,22 +112,14 @@ export default class JumpstartPlugin extends PluginTemplate {
104
112
  var _a;
105
113
  try {
106
114
  if ((_a = options.projectTemplatesPath) === null || _a === void 0 ? void 0 : _a.startsWith("http", 0)) {
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
- });
115
+ const res = yield axios.get(options.projectTemplatesPath);
112
116
  this.projectTemplates = res.data.projectTemplates;
113
117
  }
114
118
  else if (options.projectTemplatesPath) {
115
119
  this.projectTemplates = requireFn(path.resolve(options.projectTemplatesPath)).projectTemplates;
116
120
  }
117
121
  else {
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
- });
122
+ const res = yield axios.get("https://bitbucket.org/magnolia-cms/project-templates/raw/HEAD/projectTemplates.json");
123
123
  this.projectTemplates = res.data.projectTemplates;
124
124
  }
125
125
  }
@@ -1,3 +1,4 @@
1
- import { Credentials, PluginRequirement } from "../types/types.js";
1
+ import { PluginRequirement } from "../types/types";
2
+ export declare const installAdditionalPlugins: (plugins: Array<PluginRequirement>) => Promise<void>;
2
3
  export declare const handleMGNLConfigFile: () => Promise<void>;
3
- export declare const installAndConfigPlugins: (plugins: Array<PluginRequirement>, credentials?: Credentials) => Promise<void>;
4
+ export declare const addConfigProps: (vars: any) => Promise<void>;
@@ -10,21 +10,64 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import fs from "fs-extra";
11
11
  import parser from "@babel/parser";
12
12
  import t from "@babel/types";
13
+ const generate = _generate.default;
14
+ const traverse = _traverse.default;
13
15
  import beautifyModule from "js-beautify";
14
16
  import path from "path";
15
- import { determinePackageManager, extractRepoName, installPackage, isValidURL } from "./helper.js";
17
+ import { determinePackageManager, installPackage } from "./helper.js";
16
18
  import ora from "ora";
17
19
  import { i18nInstance, logger } from "../jumpstart-plugin.js";
20
+ import _generate from "@babel/generator";
21
+ import _traverse from "@babel/traverse";
22
+ import { execa } from "execa";
23
+ import JSON5 from "json5";
18
24
  const { js_beautify } = beautifyModule;
19
25
  const BEAUTIFY_OPTIONS = {
20
26
  indent_size: 2,
21
27
  end_with_newline: true,
22
28
  max_preserve_newlines: 2,
23
29
  keep_array_indentation: false,
24
- preserve_newlines: true
30
+ preserve_newlines: true,
31
+ wrap_line_length: 1
25
32
  };
26
33
  const getLocalConfigPath = () => path.join(process.cwd(), 'mgnl.config.js');
27
34
  const getPackageConfigPath = () => path.join(process.cwd(), 'node_modules/@magnolia/cli/mgnl.config.js');
35
+ export const installAdditionalPlugins = (plugins) => __awaiter(void 0, void 0, void 0, function* () {
36
+ const pm = determinePackageManager();
37
+ const addPluginExists = yield execa(pm, 'run mgnl -s -- add-plugin -v'.split(' '), { cwd: process.cwd() });
38
+ if (addPluginExists.exitCode !== 0 || !addPluginExists.stdout.match(/(\d.)+(\d).*/)) {
39
+ logger.warn(i18nInstance.t("warn-config-h-no-add-plugin"));
40
+ let installPluginsText = "";
41
+ plugins.forEach(plugin => {
42
+ installPluginsText = `${installPluginsText} - ${plugin.plugin}\n`;
43
+ if (plugin.pluginArgs) {
44
+ installPluginsText = `${installPluginsText}${i18nInstance.t("install-plugin-with-args-text", { pluginArgs: JSON5.stringify(plugin.pluginArgs) })}`;
45
+ }
46
+ });
47
+ logger.warn(i18nInstance.t("warn-config-h-install-plugins-manually", { installPluginsText: installPluginsText }));
48
+ return;
49
+ }
50
+ for (const plugin of plugins) {
51
+ try {
52
+ const addPluginArgs = `run mgnl -s -- add-plugin ${plugin.plugin}`.split(' ');
53
+ try {
54
+ if (plugin.pluginArgs !== undefined && plugin.pluginArgs !== 'undefined' && plugin.pluginArgs !== '') {
55
+ addPluginArgs.push(JSON5.stringify(JSON5.parse(JSON5.stringify(plugin.pluginArgs))));
56
+ }
57
+ }
58
+ catch (e) {
59
+ logger.warn(i18nInstance.t('warn-config-h-error-cannot-parse-plugin-args', { pluginArgs: plugin.pluginArgs }));
60
+ }
61
+ yield execa(pm, addPluginArgs, {
62
+ stdio: 'inherit',
63
+ cwd: process.cwd()
64
+ });
65
+ }
66
+ catch (e) {
67
+ logger.error(i18nInstance.t('error-config-h-while-add-plugin', { plugin: plugin }));
68
+ }
69
+ }
70
+ });
28
71
  export const handleMGNLConfigFile = () => __awaiter(void 0, void 0, void 0, function* () {
29
72
  let isMgnlCliInstalled = true;
30
73
  if (!fs.existsSync(getPackageConfigPath())) {
@@ -44,18 +87,6 @@ export const handleMGNLConfigFile = () => __awaiter(void 0, void 0, void 0, func
44
87
  // the mgnl.config.js exist, do not do anything
45
88
  }
46
89
  });
47
- export const installAndConfigPlugins = (plugins, credentials) => __awaiter(void 0, void 0, void 0, function* () {
48
- const localConfig = parseConfigFile(getLocalConfigPath());
49
- if (!localConfig)
50
- return;
51
- const missingPlugins = findMissingPlugins(plugins, localConfig);
52
- if (missingPlugins.length === 0)
53
- return;
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
- const pluginsConfig = createCustomConfigForPlugins(installedPlugins);
57
- updateLocalConfig(pluginsConfig, localConfig);
58
- });
59
90
  const handleExistingConfigFile = () => {
60
91
  const localConfigContent = fs.readFileSync(getLocalConfigPath(), "utf8");
61
92
  const packageConfigContent = fs.readFileSync(getPackageConfigPath(), "utf8");
@@ -100,141 +131,80 @@ const extractLoggerConfig = (content) => {
100
131
  const loggerRegex = /(logger\s*:\s*{[^}]*})/s;
101
132
  return loggerRegex.exec(content);
102
133
  };
103
- const findMissingPlugins = (plugins, config) => {
104
- return plugins.filter(plugin => {
105
- let packageName = plugin.name;
106
- return !config.imports.some(customImport => customImport.importedNames === packageName);
107
- });
108
- };
109
- const installPlugins = (plugins, credentials) => __awaiter(void 0, void 0, void 0, function* () {
110
- const packageManager = determinePackageManager();
111
- let installedPlugins = [];
112
- for (const plugin of plugins) {
113
- const installed = yield installPackage(packageManager, plugin.installReference, plugin.name, credentials);
114
- if (installed)
115
- installedPlugins.push(plugin);
116
- }
117
- return installedPlugins;
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
- };
128
- const createCustomConfigForPlugins = (plugins) => {
129
- const defaultConfig = { configContent: "", imports: [], commands: [] };
130
- plugins.forEach(plugin => {
131
- let importSource;
132
- if (isValidURL(plugin.installReference)) {
133
- importSource = extractRepoName(plugin.installReference);
134
- }
135
- if (!importSource) {
136
- importSource = plugin.installReference;
137
- }
138
- defaultConfig.imports.push({ importedNames: plugin.name, importSource: extractPackageName(importSource) || importSource });
139
- defaultConfig.commands.push({ commandName: plugin.name, options: plugin.options });
140
- });
141
- return defaultConfig;
142
- };
143
- const parseConfigFile = (configPath) => {
144
- let configContent;
145
- try {
146
- configContent = fs.readFileSync(configPath, 'utf-8');
147
- }
148
- catch (e) {
149
- logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-config-h-could-not-read-mgnl-config", {
150
- configPath: configPath
151
- }));
152
- console.error(e);
153
- return null;
154
- }
155
- let parsedConfig;
134
+ const writeConfigContent = (content) => {
135
+ const spinner = ora().start(i18nInstance.t("ora-updating-mgnl-config"));
156
136
  try {
157
- parsedConfig = parser.parse(configContent, { sourceType: 'module' });
137
+ fs.writeFileSync(getLocalConfigPath(), js_beautify(content.trim(), BEAUTIFY_OPTIONS), 'utf-8');
158
138
  }
159
139
  catch (e) {
160
- logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-config-h-could-not-parse-mgnl-config", {
161
- configPath: configPath
162
- }));
140
+ spinner.stop();
141
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-config-h-while-updating-mgnl-config"));
163
142
  console.error(e);
164
- return null;
165
143
  }
166
- if (!parsedConfig)
167
- return null;
168
- const { imports, commands } = extractImportsAndCommands(parsedConfig);
169
- return { configContent, imports, commands };
144
+ spinner.stop();
145
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-config-h-mgnl-config-updated"));
170
146
  };
171
- const extractImportsAndCommands = (ast) => {
172
- const imports = [];
173
- const commands = [];
174
- ast.program.body.forEach(node => {
175
- if (t.isImportDeclaration(node)) {
176
- imports.push({
177
- importedNames: node.specifiers.map(specifier => specifier.local.name).join(', '),
178
- importSource: node.source.value
179
- });
180
- }
181
- if (t.isExportDefaultDeclaration(node) && t.isObjectExpression(node.declaration)) {
182
- node.declaration.properties.forEach(property => {
183
- if (t.isObjectProperty(property) && t.isArrayExpression(property.value)) {
184
- property.value.elements.forEach(element => {
185
- if (t.isNewExpression(element) && t.isIdentifier(element.callee)) {
186
- commands.push({ commandName: element.callee.name });
147
+ export const addConfigProps = (vars) => __awaiter(void 0, void 0, void 0, function* () {
148
+ const spinner = ora().start(i18nInstance.t("ora-adding-shared-vars"));
149
+ try {
150
+ const filePath = getLocalConfigPath();
151
+ let content = fs.readFileSync(filePath, 'utf-8');
152
+ const valueToAstNode = (value) => {
153
+ if (typeof value === 'string') {
154
+ return t.stringLiteral(value);
155
+ }
156
+ else if (typeof value === 'boolean') {
157
+ return t.booleanLiteral(value);
158
+ }
159
+ else if (typeof value === 'number') {
160
+ return t.numericLiteral(value);
161
+ }
162
+ else if (Array.isArray(value)) {
163
+ return t.arrayExpression(value.map(item => valueToAstNode(item)));
164
+ }
165
+ else if (typeof value === 'object') {
166
+ return t.objectExpression(Object.entries(value).map(([key, val]) => t.objectProperty(t.isValidIdentifier(key)
167
+ ? t.identifier(key)
168
+ : t.stringLiteral(key), valueToAstNode(val))));
169
+ }
170
+ else {
171
+ throw new TypeError('Unsupported type');
172
+ }
173
+ };
174
+ const ast = parser.parse(content, {
175
+ sourceType: 'module',
176
+ plugins: ['typescript'], // if you're using TypeScript
177
+ attachComment: true
178
+ });
179
+ let modificationsMade = false;
180
+ traverse(ast, {
181
+ ExportDefaultDeclaration({ node }) {
182
+ if (t.isObjectExpression(node.declaration)) {
183
+ // Check each required property to see if it exists in the declaration
184
+ Object.entries(vars).forEach(([key, value]) => {
185
+ let propExists = node.declaration.properties.some((property) => t.isObjectProperty(property) &&
186
+ ((t.isIdentifier(property.key) && property.key.name === key) ||
187
+ (t.isStringLiteral(property.key) && property.key.value === key)));
188
+ if (!propExists) {
189
+ // If the property doesn't exist, add it
190
+ node.declaration.properties.push(t.objectProperty(t.isValidIdentifier(key)
191
+ ? t.identifier(key) : t.stringLiteral(key), valueToAstNode(value)));
192
+ modificationsMade = true;
187
193
  }
188
194
  });
189
195
  }
190
- });
196
+ },
197
+ });
198
+ if (modificationsMade) {
199
+ const output = generate(ast, {}, content);
200
+ fs.writeFileSync(getLocalConfigPath(), js_beautify(output.code.trim(), BEAUTIFY_OPTIONS), 'utf-8');
201
+ spinner.stop();
202
+ logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-config-h-mgnl-vars-added"));
191
203
  }
192
- });
193
- return { imports, commands };
194
- };
195
- const updateLocalConfig = (defaultConfig, localConfig) => {
196
- const getMissingItems = (defaultItems, customItems, comparator) => {
197
- return defaultItems.filter(defaultItem => !customItems.some(customItem => comparator(defaultItem, customItem)));
198
- };
199
- const mergeImports = (customImports, missingImports) => {
200
- return customImports.concat(missingImports)
201
- .map(i => `import ${i.importedNames} from "${i.importSource}";`).join('\n');
202
- };
203
- const missingImports = getMissingItems(defaultConfig.imports, localConfig.imports, (a, b) => a.importSource === b.importSource);
204
- const missingCommands = getMissingItems(defaultConfig.commands, localConfig.commands, (a, b) => a.commandName === b.commandName);
205
- const mergedImports = mergeImports(localConfig.imports, missingImports);
206
- const updatedConfigContent = mergeCommandsWithConfigContent(localConfig.configContent, missingCommands);
207
- writeToLocalConfigFile(mergedImports, updatedConfigContent);
208
- };
209
- const formatOptions = (options) => {
210
- if (!options)
211
- return '()';
212
- const formattedOptions = JSON.stringify(options, null);
213
- return `(${formattedOptions})`;
214
- };
215
- const mergeCommandsWithConfigContent = (configContent, missingCommands) => {
216
- const missingCommandsStr = missingCommands.map(c => `new ${c.commandName}${formatOptions(c.options)}`).join(',\n');
217
- const commandsPattern = /(commands\s*:\s*\[)([^\]]*?)(,\s*)?(\])/;
218
- return configContent.replace(commandsPattern, (match, p1, p2, p3, p4) => {
219
- const separator = (p2.trim() !== "" && missingCommandsStr.trim() !== "") ? "," : "";
220
- return `${p1}${p2}${separator}${missingCommandsStr}${p4}`;
221
- });
222
- };
223
- const writeToLocalConfigFile = (mergedImports, updatedConfigContent) => {
224
- const importPattern = /import .* from ['"].*['"];\s*/g;
225
- const configContentWithImports = `${mergedImports}\n\n${updatedConfigContent.replace(importPattern, '')}`;
226
- writeConfigContent(configContentWithImports);
227
- };
228
- const writeConfigContent = (content) => {
229
- const spinner = ora().start(i18nInstance.t("ora-updating-mgnl-config"));
230
- try {
231
- fs.writeFileSync(getLocalConfigPath(), js_beautify(content.trim(), BEAUTIFY_OPTIONS), 'utf-8');
232
204
  }
233
205
  catch (e) {
234
206
  spinner.stop();
235
- logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-config-h-while-updating-mgnl-config"));
207
+ logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-config-h-while-adding-vars"));
236
208
  console.error(e);
237
209
  }
238
- spinner.stop();
239
- logger === null || logger === void 0 ? void 0 : logger.info(i18nInstance.t("info-config-h-mgnl-config-updated"));
240
- };
210
+ });
@@ -1,4 +1,4 @@
1
- import { Bundle, Credentials } from "../types/types.js";
2
- export declare const downloadBundle: (bundle: Bundle, credentials: Credentials, dest: string | undefined) => Promise<string>;
3
- export declare const getDownloadUrl: (bundle: Bundle, credentials: Credentials) => Promise<string>;
1
+ import { Bundle, Credentials, PluginOptions } from "../types/types.js";
2
+ export declare const downloadBundle: (bundle: Bundle, credentials: Credentials, dest: string | undefined, options: PluginOptions) => Promise<string>;
3
+ export declare const getDownloadUrl: (bundle: Bundle, credentials: Credentials, options: PluginOptions) => Promise<string>;
4
4
  export declare const selectTag: (url: string, credentials: Credentials) => Promise<string>;
@@ -13,7 +13,7 @@ import axios from "axios";
13
13
  import ProgressBar from "progress";
14
14
  import inquirer from "inquirer";
15
15
  import { i18nInstance, logger } from "../jumpstart-plugin.js";
16
- export const downloadBundle = (bundle, credentials, dest) => __awaiter(void 0, void 0, void 0, function* () {
16
+ export const downloadBundle = (bundle, credentials, dest, options) => __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();
@@ -25,7 +25,7 @@ export const downloadBundle = (bundle, credentials, dest) => __awaiter(void 0, v
25
25
  downloadUrl = yield selectTag(url, credentials);
26
26
  }
27
27
  else if (url.includes('https://nexus.magnolia-cms.com/service/rest/v1/search')) {
28
- downloadUrl = yield getDownloadUrl(bundle, credentials);
28
+ downloadUrl = yield getDownloadUrl(bundle, credentials, options);
29
29
  }
30
30
  else {
31
31
  downloadUrl = url;
@@ -84,7 +84,8 @@ export const downloadBundle = (bundle, credentials, dest) => __awaiter(void 0, v
84
84
  throw new Error(error);
85
85
  }
86
86
  });
87
- export const getDownloadUrl = (bundle, credentials) => __awaiter(void 0, void 0, void 0, function* () {
87
+ export const getDownloadUrl = (bundle, credentials, options) => __awaiter(void 0, void 0, void 0, function* () {
88
+ var _a, _b, _c;
88
89
  const opts = {
89
90
  method: 'get',
90
91
  responseType: 'json',
@@ -96,11 +97,49 @@ export const getDownloadUrl = (bundle, credentials) => __awaiter(void 0, void 0,
96
97
  opts.auth = credentials;
97
98
  }
98
99
  try {
99
- const response = yield axios.get(bundle.url + '&prerelease=false', opts);
100
+ const url = new URL(bundle.url);
101
+ url.searchParams.set('prerelease', "false");
102
+ if (bundle.version) {
103
+ if (options.snapshot || bundle.version.toLowerCase().includes("snapshot")) {
104
+ url.searchParams.set('prerelease', "true");
105
+ }
106
+ if (options.magnoliaVersion) {
107
+ bundle.version = options.magnoliaVersion;
108
+ }
109
+ if (bundle.version.toLowerCase() !== "latest" && bundle.version.toLowerCase() !== "alpha" && bundle.version.toLowerCase() !== "beta") {
110
+ url.searchParams.set('maven.baseVersion', bundle.version);
111
+ }
112
+ }
113
+ const response = yield axios.get(url.toString(), opts);
100
114
  const params = new URLSearchParams(bundle.url.split("?")[1]);
101
- let downloadUrl = response.data.items[0].downloadUrl;
115
+ let item = response.data.items[0];
116
+ if (((_a = bundle.version) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === "latest") {
117
+ item = response.data.items.find((item) => {
118
+ var _a;
119
+ const itemVersion = (_a = item === null || item === void 0 ? void 0 : item.maven2) === null || _a === void 0 ? void 0 : _a.version;
120
+ return !(itemVersion === null || itemVersion === void 0 ? void 0 : itemVersion.includes("-alpha")) && !(itemVersion === null || itemVersion === void 0 ? void 0 : itemVersion.includes("-beta"));
121
+ });
122
+ }
123
+ else if (((_b = bundle.version) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === "alpha") {
124
+ item = response.data.items.find((item) => {
125
+ var _a;
126
+ const itemVersion = (_a = item === null || item === void 0 ? void 0 : item.maven2) === null || _a === void 0 ? void 0 : _a.version;
127
+ return itemVersion === null || itemVersion === void 0 ? void 0 : itemVersion.includes("-alpha");
128
+ });
129
+ }
130
+ else if (((_c = bundle.version) === null || _c === void 0 ? void 0 : _c.toLowerCase()) === "beta") {
131
+ item = response.data.items.find((item) => {
132
+ var _a;
133
+ const itemVersion = (_a = item === null || item === void 0 ? void 0 : item.maven2) === null || _a === void 0 ? void 0 : _a.version;
134
+ return itemVersion === null || itemVersion === void 0 ? void 0 : itemVersion.includes("-beta");
135
+ });
136
+ }
137
+ if (!item) {
138
+ throw new Error(i18nInstance.t('error-download-no-artifact-available', { version: bundle.version, interpolation: { escapeValue: false } }));
139
+ }
140
+ let downloadUrl = item.downloadUrl;
102
141
  downloadUrl = downloadUrl === null || downloadUrl === void 0 ? void 0 : downloadUrl.split('/magnolia.');
103
- downloadUrl = downloadUrl && `${downloadUrl[0]}/${params.get('repository')}/${response.data.items[0].path}`;
142
+ downloadUrl = downloadUrl && `${downloadUrl[0]}/${params.get('repository')}${item.path}`;
104
143
  return downloadUrl;
105
144
  }
106
145
  catch (error) {
@@ -110,11 +149,11 @@ export const getDownloadUrl = (bundle, credentials) => __awaiter(void 0, void 0,
110
149
  }));
111
150
  logger === null || logger === void 0 ? void 0 : logger.error(i18nInstance.t("error-download-unable-to-get-local-issuer-certificate"));
112
151
  }
113
- throw new Error(error);
152
+ throw new Error(error.message);
114
153
  }
115
154
  });
116
155
  export const selectTag = (url, credentials) => __awaiter(void 0, void 0, void 0, function* () {
117
- var _a;
156
+ var _d;
118
157
  const opts = {
119
158
  method: 'get',
120
159
  headers: {
@@ -125,7 +164,7 @@ export const selectTag = (url, credentials) => __awaiter(void 0, void 0, void 0,
125
164
  opts.auth = credentials;
126
165
  }
127
166
  const res = yield axios.get(url, opts);
128
- if (!((_a = res.data) === null || _a === void 0 ? void 0 : _a.values) || res.data.values.length <= 0) {
167
+ if (!((_d = res.data) === null || _d === void 0 ? void 0 : _d.values) || res.data.values.length <= 0) {
129
168
  throw new Error(i18nInstance.t("error-download-no-tags"));
130
169
  }
131
170
  const { tag } = yield inquirer
@@ -28,7 +28,15 @@ export const extract = (bundle, file, command) => __awaiter(void 0, void 0, void
28
28
  yield extractApacheTomcat(dest, unzipFolder);
29
29
  }
30
30
  else {
31
- fs.copySync(unzipFolder, dest, { overwrite: true });
31
+ let sourceFolder = unzipFolder;
32
+ if (command === PostCommands.ExtractAndUnfold) {
33
+ const firstDir = fs.readdirSync(unzipFolder).find(item => {
34
+ const itemPath = path.join(unzipFolder, item);
35
+ return fs.statSync(itemPath).isDirectory();
36
+ });
37
+ sourceFolder = firstDir ? path.join(unzipFolder, firstDir) : unzipFolder;
38
+ }
39
+ fs.copySync(sourceFolder, dest, { overwrite: true });
32
40
  }
33
41
  fs.rmSync(unzipFolder, { recursive: true, force: true });
34
42
  fs.rmSync(path.dirname(file), { recursive: true, force: true });
@@ -1,10 +1,9 @@
1
- import { Credentials, PluginRequirement } from "../types/types.js";
1
+ import { Credentials } from "../types/types.js";
2
2
  import { i18n } from "i18next";
3
3
  export declare function initI18n(pluginName: string, namespace?: string): i18n;
4
4
  export declare const handleLightModulesFolder: () => Promise<void>;
5
5
  export declare const findLightModulesFolder: (p: string) => Promise<unknown>;
6
6
  export declare const findWebAppsList: (apacheTomcatPath: string) => string[];
7
- export declare const installAdditionalPlugins: (plugins: Array<PluginRequirement>, credentials?: Credentials) => Promise<void>;
8
7
  export declare const initializeNodeProject: () => Promise<void>;
9
8
  export declare const installPackage: (packageManager: string, packageReference: string, packageName?: string, credentials?: Credentials) => Promise<boolean>;
10
9
  export declare const isValidURL: (str: string) => boolean;
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- import { handleMGNLConfigFile, installAndConfigPlugins } from "./config-helper.js";
10
+ import { handleMGNLConfigFile } from "./config-helper.js";
11
11
  import { handlePackageJSON } from "./pj-helper.js";
12
12
  import i18next from "i18next";
13
13
  import Backend from 'i18next-fs-backend';
@@ -160,9 +160,6 @@ export const findWebAppsList = (apacheTomcatPath) => {
160
160
  }
161
161
  return webApps;
162
162
  };
163
- export const installAdditionalPlugins = (plugins, credentials) => __awaiter(void 0, void 0, void 0, function* () {
164
- yield installAndConfigPlugins(plugins, credentials);
165
- });
166
163
  export const initializeNodeProject = () => __awaiter(void 0, void 0, void 0, function* () {
167
164
  yield handlePackageJSON();
168
165
  yield handleMGNLConfigFile();
@@ -1,15 +1,18 @@
1
1
  {
2
- "cmd-option-template": "Choose template",
3
- "cmd-option-project-templates-path": "Specify from where the projectTemplates should be loaded",
2
+ "cmd-option-description": "download and set up a new headless or freemarker-based project with Magnolia webapp",
3
+ "cmd-option-template": "choose a template from available project templates",
4
+ "cmd-option-project-templates-path": "specify the path to load project templates from",
5
+ "cmd-option-magnolia-version": "set the Magnolia version; defaults to the latest stable version",
6
+ "cmd-option-snapshot": "download the latest snapshot version of the specified or latest stable webapp",
4
7
 
5
8
  "prompt-enter-credentials": "Please enter credentials for {{bundle}}",
6
- "prompt-project-templates-credentials": "Please enter credentials",
7
9
 
8
10
  "info-template_not_found": "The '{{template}}' template was not found.",
9
11
  "info-jumpstart_started": "'JumpstartPlugin' plugin started successfully.",
10
12
  "info-jumpstart_stopped": "'JumpstartPlugin' stopped",
11
13
  "info-config-h-will-install-missing-plugins": "Will attempt to install missing plugins",
12
14
  "info-config-h-mgnl-config-updated": "mgnl.config.js updated",
15
+ "info-config-h-mgnl-vars-added": "Shared variables added to mgnl.config.js",
13
16
  "info-download-preparing": "Preparing to download {{url}}",
14
17
  "info-download-starting": "Starting download from: {{downloadUrl}}",
15
18
  "info-download-finished": "Download finished",
@@ -28,16 +31,22 @@
28
31
  "info-helper-package-installed": "{{packageName}} installed",
29
32
 
30
33
  "warn-config-h-could-not-install-cli": "Couldn't install @magnolia/cli",
34
+ "warn-config-h-no-add-plugin": "Cannot find add-plugin command. Please update @magnolia/cli package.",
35
+ "warn-config-h-install-plugins-manually": "Please install following plugins:\n{{installPluginsText}}and add them to mgnl.config.js manually.",
31
36
  "warn-pj-helper-changing-type": "Changing 'type' to \"module\"",
32
37
  "warn-helper-webapp-props-not-exist": "File {{filePath}} doesn't exists, skipping configuration.",
33
38
  "warn-helper-cannot-modify-webapp": "Cannot modify webapps: {{errorMsg}}",
39
+ "warn-config-h-error-cannot-parse-plugin-args": "Provided plugin argument:\n{{pluginArgs}}\nis not a valid JSON5 object.",
34
40
 
35
41
  "error-config-h-could-not-read-mgnl-config": "Couldn't read {{configPath}}",
36
42
  "error-config-h-could-not-parse-mgnl-config": "Couldn't parse {{configPath}}, please check the file for issues",
37
43
  "error-config-h-while-updating-mgnl-config": "Error occurred while updating mgnl.config.js",
44
+ "error-config-h-while-adding-vars": "Error occurred while adding variables to mgnl.config.js",
45
+ "error-config-h-while-add-plugin": "An error occurred while installing following plugin: {{plugin}}",
38
46
  "error-download-no-tags": "There are no available tags",
39
47
  "error-download-problem-accessing-url": "A problem was detected while accessing: {{url}}.",
40
48
  "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.",
49
+ "error-download-no-artifact-available": "No Magnolia bundle with \"{{version}}\" version is available.",
41
50
  "error-extensions-calling-fn-retry": "Error Calling Function {{name}}. {{retry}}/{{maxRetry}}. Waiting {{delay}}ms before retry.",
42
51
  "error-extensions-fn-not-found": "{{function}} Function not found",
43
52
  "error-extensions-path-not-found": "{{extensionsPath}} not found. Could not evaluate custom prompts.",
@@ -53,9 +62,12 @@
53
62
  "error-helper-fail-to-install-package": "Failed to install {{packageName}}",
54
63
 
55
64
  "ora-updating-mgnl-config": "Updating mgnl.config.js",
65
+ "ora-adding-shared-vars": "Adding variables to mgnl.config.js",
56
66
  "ora-installing-ext-dep": "Installing Extension Dependencies",
57
67
  "ora-installing-dep": "Installing dependencies",
58
68
  "ora-running-npm-install": "Didn't find 'node_modules' folder. Running 'npm install'",
59
69
  "ora-creating-pj": "Creating package.json",
60
- "ora-installing-package": "Installing {{packageName}}"
70
+ "ora-installing-package": "Installing {{packageName}}",
71
+
72
+ "install-plugin-with-args-text": "\twith following args: {{pluginArgs}}\n"
61
73
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magnolia/cli-jumpstart-plugin",
3
- "version": "1.0.0-preview.4",
3
+ "version": "1.0.0-preview.5",
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": {
@@ -28,7 +28,9 @@
28
28
  "typescript": "^5.1.6"
29
29
  },
30
30
  "dependencies": {
31
+ "@babel/generator": "^7.24.4",
31
32
  "@babel/parser": "^7.23.6",
33
+ "@babel/traverse": "^7.24.1",
32
34
  "@babel/types": "^7.23.6",
33
35
  "@magnolia/cli-plugin-template": "^1.1.0",
34
36
  "axios": "^1.4.0",
@@ -43,6 +45,7 @@
43
45
  "i18next-fs-backend": "^2.3.1",
44
46
  "inquirer": "^9.2.8",
45
47
  "js-beautify": "^1.14.9",
48
+ "json5": "^2.2.3",
46
49
  "ora": "^7.0.1",
47
50
  "progress": "^2.0.3",
48
51
  "underscore": "^1.13.6",
@@ -1,6 +1,8 @@
1
1
  export interface PluginOptions {
2
2
  template: string | boolean;
3
3
  projectTemplatesPath: string;
4
+ magnoliaVersion?: string;
5
+ snapshot?: boolean;
4
6
  }
5
7
  export interface Bundle {
6
8
  url: string;
@@ -10,6 +12,7 @@ export interface Bundle {
10
12
  dest?: string;
11
13
  downloadDest?: string;
12
14
  name?: string;
15
+ version?: string;
13
16
  }
14
17
  export interface TemplateBase {
15
18
  name: string;
@@ -19,28 +22,33 @@ export interface TemplateWithChildren extends TemplateBase {
19
22
  children: Array<Template>;
20
23
  bundles?: never;
21
24
  plugins?: never;
25
+ configVars?: never;
22
26
  }
23
27
  export interface TemplateWithBundles extends TemplateBase {
24
28
  bundles: Array<Bundle>;
25
29
  children?: never;
26
30
  plugins?: never;
31
+ configVars?: never;
27
32
  }
28
33
  export interface TemplateWithPlugins extends TemplateBase {
29
34
  plugins: Array<PluginRequirement>;
30
35
  bundles?: never;
31
36
  children?: never;
37
+ configVars?: never;
32
38
  }
33
39
  export interface TemplateWithBundlesAndPlugins extends TemplateBase {
34
40
  bundles: Array<Bundle>;
35
41
  plugins: Array<PluginRequirement>;
36
42
  children?: never;
43
+ configVars?: {
44
+ [key: string]: any;
45
+ };
37
46
  }
38
47
  export type TemplateWithoutChildren = TemplateWithBundles | TemplateWithPlugins | TemplateWithBundlesAndPlugins;
39
48
  export type Template = TemplateWithChildren | TemplateWithoutChildren;
40
49
  export interface PluginRequirement {
41
- name: string;
42
- installReference: string;
43
- options?: any;
50
+ plugin: string;
51
+ pluginArgs?: any;
44
52
  }
45
53
  export type Credentials = {
46
54
  username: string;
@@ -49,26 +57,10 @@ export type Credentials = {
49
57
  export declare enum PostCommands {
50
58
  Extract = "extract",
51
59
  ExtractAndOverride = "extract and override",
60
+ ExtractAndUnfold = "extract and unfold",
52
61
  NpmInstall = "npm install",
53
62
  YarnInstall = "yarn install"
54
63
  }
55
- export interface PluginRequirement {
56
- name: string;
57
- installReference: string;
58
- }
59
- export type ImportItem = {
60
- importedNames: string;
61
- importSource: string;
62
- };
63
- export interface CommandItem {
64
- commandName: string;
65
- options?: any;
66
- }
67
- export type ParsedConfigData = {
68
- configContent: string;
69
- imports: ImportItem[];
70
- commands: CommandItem[];
71
- };
72
64
  export interface PostCommandFunction {
73
65
  function: string;
74
66
  delay: number;
@@ -2,6 +2,7 @@ export var PostCommands;
2
2
  (function (PostCommands) {
3
3
  PostCommands["Extract"] = "extract";
4
4
  PostCommands["ExtractAndOverride"] = "extract and override";
5
+ PostCommands["ExtractAndUnfold"] = "extract and unfold";
5
6
  PostCommands["NpmInstall"] = "npm install";
6
7
  PostCommands["YarnInstall"] = "yarn install";
7
8
  })(PostCommands || (PostCommands = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magnolia/cli-jumpstart-plugin",
3
- "version": "1.0.0-preview.4",
3
+ "version": "1.0.0-preview.5",
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": {
@@ -28,7 +28,9 @@
28
28
  "typescript": "^5.1.6"
29
29
  },
30
30
  "dependencies": {
31
+ "@babel/generator": "^7.24.4",
31
32
  "@babel/parser": "^7.23.6",
33
+ "@babel/traverse": "^7.24.1",
32
34
  "@babel/types": "^7.23.6",
33
35
  "@magnolia/cli-plugin-template": "^1.1.0",
34
36
  "axios": "^1.4.0",
@@ -43,6 +45,7 @@
43
45
  "i18next-fs-backend": "^2.3.1",
44
46
  "inquirer": "^9.2.8",
45
47
  "js-beautify": "^1.14.9",
48
+ "json5": "^2.2.3",
46
49
  "ora": "^7.0.1",
47
50
  "progress": "^2.0.3",
48
51
  "underscore": "^1.13.6",