/
index.js
executable file
·325 lines (274 loc) · 12.5 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
const path = require('path');
const process = require('process');
const fs = require('fs-extra');
const chalk = require('chalk');
const Insight = require('insight');
const semver = require('semver');
const replace = require('replace-in-file');
const Generator = require('@ngx-rocket/core');
const asciiLogo = require('@ngx-rocket/ascii-logo');
const pkg = require('../../package.json');
const prompts = require('./prompts.js');
const options = require('./options.js');
const getLanguages = require('./languages.js');
const {deployers} = require('./deployers.js');
const packageJsonFile = 'package.json';
const appPath = 'src/app';
class NgxGenerator extends Generator {
initializing() {
this.version = pkg.version;
this.props = {};
// Disable automatic env install based on package.json
this.features.customInstallTask = true;
// Try to initialize analytics. Insight is broken for some users, so if it fails, proceed as if the --no-analytics flag was present.
try {
this.insight =
!this.options.analytics || process.env.DISABLE_NGX_ANALYTICS
? {track: () => {}}
: new Insight({trackingCode: 'UA-93069862-1', pkg});
} catch {
this.insight = {track: () => {}};
this.log(
chalk.yellow(
'There was a problem collecting analytics data. Proceeding without anonymous usage tracking. To suppress this warning in the future, use the --no-analytics flag.'
)
);
}
const minNodeVersion = pkg.engines.node.slice(2);
if (semver.lt(process.version, minNodeVersion)) {
this.log(chalk.yellow(`Angular CLI v13 needs NodeJS ${minNodeVersion} or greater.`));
this.log(chalk.yellow(`You are using ${process.version} which is unsupported, please upgrade.\n`));
// eslint-disable-next-line unicorn/no-process-exit
process.exit(-1);
}
this.argument('appName', {
description: 'Name of the app to generate',
type: String,
required: false
});
this.insight.optOut = !this.options.analytics || process.env.DISABLE_NGX_ANALYTICS;
if (this.options.raw) {
this.props.ui = 'raw';
}
if (this.options['location-strategy']) {
this.props.location = this.options['location-strategy'];
}
this.props.strict = this.options.strict;
this.props.skipInstall = this.options['skip-install'];
this.props.skipQuickstart = this.options['skip-quickstart'];
this.props.initGit = this.options.git;
this.props.usePrefix = this.options.prefix;
if (this.options.deploy) {
this.props.deploy = this.options.deploy;
}
// Updating
let fromVersion = null;
if (this.options.update) {
this.props = this.config.get('props') || this.props;
fromVersion = this.config.get('version');
}
if (fromVersion) {
if (semver.gte(fromVersion, this.version)) {
this.log(chalk.green('\nNothing to update, it’s all good!\n'));
// eslint-disable-next-line unicorn/no-process-exit
process.exit(0);
}
this.updating = true;
this.log(
`\nUpdating ${chalk.green(this.props.appName)} project (${chalk.yellow(fromVersion)} -> ${chalk.yellow(
this.version
)})\n`
);
this.log(`${chalk.yellow('Make sure you don’t have uncommitted changes before overwriting files!')}`);
this.insight.track('update', fromVersion, 'to', this.version);
} else if (!this.options['skip-welcome']) {
this.log(asciiLogo(pkg.version));
}
// Composition
const addonsOption = this.options.addons;
this.addons = addonsOption ? addonsOption.split(' ') : [];
this.addons.forEach((addon) => {
try {
if (/[:/]/.test(addon)) {
// Fetch addon name from URL/GitHub/Local package format
let splitIndex = addon.lastIndexOf('/');
splitIndex = splitIndex === -1 ? addon.lastIndexOf(':') : splitIndex;
addon = addon.slice(splitIndex + 1);
}
if (addon.startsWith('generator-')) {
// This prefix must be removed for Yeoman to work properly
addon = addon.slice(10);
}
if (addon.endsWith('.git')) {
// When working with git repos, this suffix must be removed
addon = addon.slice(0, -4);
}
this.composeWith(addon, this.options);
} catch {
this.log(chalk.red(`Error: add-on "${addon}" not found.`));
// eslint-disable-next-line unicorn/no-process-exit
process.exit(-1);
}
});
this.insight.track('version', this.version);
this.insight.track('node', process.version);
this.insight.track('platform', process.platform);
this.insight.track('addons', addonsOption);
}
async prompting() {
// Allow to pre-set any props in an add-on generator
Object.assign(this.props, this.sharedProps);
await super.prompting();
this.props.mobile = this.props.mobile || [];
this.props.desktop = this.props.desktop || [];
this.props.utility = this.props.utility || [];
this.props.tools = this.props.tools || [];
this.props.languages = this.props.languages || ['en-US', 'fr-FR'];
this.props.usePrefix = typeof this.props.usePrefix === 'boolean' ? this.props.usePrefix : true;
this.props.deploy = this.props.deploy || 'none';
this.props.features = this.props.features || [];
this.props.pwa = this.props.features.includes('pwa');
this.props.auth = this.props.features.includes('auth');
this.props.lazy = this.props.features.includes('lazy');
this.props.e2e = this.props.tools.includes('protractor'); // Legacy e2e
this.props.cypress = this.props.features.includes('cypress') && !this.props.e2e;
this.props.angulartics = this.props.features.includes('angulartics');
this.shareProps(this.props);
}
configuring() {
// Add prefix rules for languages
getLanguages().forEach((language) => {
this._prefixRules[language] = (props) => props.languages.includes(language);
});
this.insight.track(
'generator',
this.props.target,
this.props.target.includes('web') && this.props.pwa ? 'pwa' : '',
this.props.target.includes('cordova') ? this.props.mobile : '',
this.props.ui,
this.props.auth ? 'auth' : 'no-auth'
);
this.insight.track('package-manager', this.packageManager);
if (this.props.target.includes('cordova') && this.packageManager === 'yarn') {
this.log(chalk.yellow('\nWarning: Using Yarn with Cordova is NOT recommended!'));
this.log(chalk.yellow('Cordova still uses NPM to fetch packages, causing issues with Yarn.\n'));
}
}
install() {
if (!this.props.usePrefix) {
this.log(`\nConfiguring prefix, please wait…`);
const clientPath = this.isFullstack ? process.env.NGX_CLIENT_PATH : '';
const basePath = this.destinationPath(path.join(clientPath, appPath));
try {
// Rename folders
fs.removeSync(path.join(basePath, 'core'));
fs.removeSync(path.join(basePath, 'shared'));
fs.renameSync(path.join(basePath, '@shared'), path.join(basePath, 'shared'));
// Replace imports in files
const options = {files: 'src/**/*.ts'};
replace.sync({...options, from: /@shared/g, to: '@app/shared'});
} catch (error) {
this.log(`${chalk.red('An error occured during prefix config:')}\n${error && error.message}`);
}
}
if (this.props.initGit) {
this.spawnCommandSync('git', ['init', '--quiet']);
}
if (!this.props.skipInstall) {
this.log(`\nRunning ${chalk.yellow(`${this.packageManager} install`)}, please wait…`);
const install = this.packageManager === 'yarn' ? this.yarnInstall.bind(this) : this.npmInstall.bind(this);
// When using NPM, force install as peer dependencies with ionic-native packages
// cause install errors with NPM >= 7.
const options = this.packageManager === 'yarn' ? null : {force: true};
if (fs.existsSync(this.destinationPath(packageJsonFile))) {
install(null, options);
}
if (this.isFullstack) {
if (fs.existsSync(this.destinationPath(path.join(process.env.NGX_CLIENT_PATH, packageJsonFile)))) {
install(null, options, {cwd: this.destinationPath(process.env.NGX_CLIENT_PATH)});
}
if (fs.existsSync(this.destinationPath(path.join(process.env.NGX_SERVER_PATH, packageJsonFile)))) {
install(null, options, {cwd: this.destinationPath(process.env.NGX_SERVER_PATH)});
}
}
}
}
end() {
const deployer = deployers.find((d) => d.value === this.props.deploy);
if (this.props.deploy !== 'none') {
this.log(`\nConfiguring deployment with ${chalk.cyan(deployer.name)}, please wait…\n`);
const result = this.spawnCommandSync('ng', ['add', deployer.package]);
if (result.error) {
this.log(`${chalk.red('Something went wrong during deployment configuration :(')}`);
this.log(`You can retry manually using ${chalk.yellow(`npx ng add ${deployer.package}`)}`);
}
}
if (this.updating) {
this.log(`\nUpdated ${chalk.green(this.props.appName)} to ${chalk.yellow(this.version)} successfully!`);
return;
}
if (this.props.skipQuickstart) {
return;
}
this.log('\nAll done! Get started with these tasks:');
this.log(
`- $ ${chalk.green(`${this.packageManager} start`)}: start dev server with live reload on http://localhost:4200`
);
if (this.props.target.includes('web')) {
this.log(`- $ ${chalk.green(`${this.packageManager} run build`)}: build web app for production`);
if (this.props.deploy !== 'none') {
this.log(`- $ ${chalk.green(`${this.packageManager} run deploy`)}: deploy app to ${deployer.name}`);
}
}
if (this.props.target.includes('cordova')) {
this.log(`- $ ${chalk.green(`${this.packageManager} run cordova:prepare`)}: prepare for building mobile app`);
this.log(`- $ ${chalk.green(`${this.packageManager} run cordova:run`)}: run app on device or simulator`);
this.log(`- $ ${chalk.green(`${this.packageManager} run cordova:build`)}: build mobile app for production`);
}
if (this.props.target.includes('electron')) {
this.log(`- $ ${chalk.green(`${this.packageManager} run electron:build`)}: build app for electron`);
this.log(`- $ ${chalk.green(`${this.packageManager} run electron:run`)}: run app in electron`);
this.log(
`- $ ${chalk.green(
`${this.packageManager} run electron:package`
)}: package executables for all selected platforms`
);
}
this.log(`- $ ${chalk.green(`${this.packageManager} test`)}: run unit tests in watch mode for TDD`);
this.log(`- $ ${chalk.green(`${this.packageManager} run test:ci`)}: lint code and run units tests with coverage`);
if (this.props.e2e || this.props.cypress) {
this.log(`- $ ${chalk.green(`${this.packageManager} run e2e`)}: launch e2e tests`);
}
if (this.props.tools.includes('hads')) {
this.log(`- $ ${chalk.green(`${this.packageManager} run docs`)}: show docs and coding guides`);
}
if (this.props.tools.includes('compodoc')) {
this.log(`- $ ${chalk.green(`${this.packageManager} run compodoc`)}: generates docs from code`);
}
if (this.props.tools.includes('prettier')) {
this.log(`- $ ${chalk.green(`${this.packageManager} run prettier`)}: format your code automatically`);
}
}
}
module.exports = Generator.make({
baseDir: __dirname,
generator: NgxGenerator,
options,
prompts,
prefixRules: Object.assign(Generator.defaultPrefixRules, {
'ionic-tabs': (props) => props.ui === 'ionic' && props.layout === 'tabs',
'ionic-side-menu': (props) => props.ui === 'ionic' && props.layout === 'side-menu',
'material-simple': (props) => props.ui === 'material' && props.layout === 'simple',
'material-side-menu': (props) => props.ui === 'material' && props.layout === 'side-menu',
raw: (props) => props.ui === 'raw',
'electron-windows': (props) => props.desktop && props.desktop.includes('windows'),
'electron-mac': (props) => props.desktop && props.desktop.includes('mac'),
'electron-linux': (props) => props.desktop && props.desktop.includes('linux'),
'tools-hads': (props) => props.tools && props.tools.includes('hads'),
'tools-jest': (props) => props.tools && props.tools.includes('jest'),
'tools-karma': (props) => props.tools && !props.tools.includes('jest'),
e2e: (props) => props.e2e,
cypress: (props) => !props.e2e && props.features && props.features.includes('cypress'),
husky: (props) => props.initGit && props.tools.includes('prettier')
})
});