@dhis2/create-app 5.2.1 → 5.3.0-alpha.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhis2/create-app",
3
- "version": "5.2.1",
3
+ "version": "5.3.0-alpha.2",
4
4
  "description": "",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -10,9 +10,11 @@
10
10
  "test": "echo \"Error: no test specified\" && exit 1"
11
11
  },
12
12
  "dependencies": {
13
- "@dhis2/cli-app-scripts": "alpha",
14
13
  "@dhis2/cli-helpers-engine": "^3.2.2",
15
- "@inquirer/prompts": "^7.8.4"
14
+ "@inquirer/prompts": "^7.8.4",
15
+ "decompress": "^4.2.1",
16
+ "fast-glob": "^3.3.3",
17
+ "fs-extra": "^11.3.3"
16
18
  },
17
19
  "private": false,
18
20
  "keywords": [],
package/src/index.js CHANGED
@@ -1,6 +1,9 @@
1
- const initCommand = require('@dhis2/cli-app-scripts/init')
2
- const { reporter, chalk } = require('@dhis2/cli-helpers-engine')
1
+ const path = require('path')
2
+ const { reporter, exec } = require('@dhis2/cli-helpers-engine')
3
3
  const { input, select } = require('@inquirer/prompts')
4
+ const decompress = require('decompress')
5
+ const fg = require('fast-glob')
6
+ const fs = require('fs-extra')
4
7
 
5
8
  process.on('uncaughtException', (error) => {
6
9
  if (error instanceof Error && error.name === 'ExitPromptError') {
@@ -11,18 +14,60 @@ process.on('uncaughtException', (error) => {
11
14
  }
12
15
  })
13
16
 
17
+ const templates = {
18
+ templateWithList: path.join(
19
+ __dirname,
20
+ '../templates/template-ts-dataelements.zip'
21
+ ),
22
+
23
+ templateWithReactRouter: path.join(
24
+ __dirname,
25
+ '../templates/template-ts-dataelements-react-router.zip'
26
+ ),
27
+ }
28
+
14
29
  const commandHandler = {
15
30
  command: '*', // default command
16
31
  description: 'Initialize a new DHIS2 web application',
17
32
  builder: {
18
33
  yes: {
19
34
  description:
20
- 'Skips interactive setup questions, accepting default options to create the new app (TypeScript, pnpm)',
35
+ 'Skips interactive setup questions, using default options to create the new app (TypeScript, pnpm, basic template)',
21
36
  type: 'boolean',
22
37
  default: false,
38
+ alias: 'y',
39
+ },
40
+ typescript: {
41
+ description: 'Use TypeScript or JS',
42
+ type: 'boolean',
43
+ default: true,
44
+ alias: ['ts', 'typeScript'],
45
+ },
46
+ template: {
47
+ description: 'Which template to use (Basic, With React Router)',
48
+ type: 'string',
49
+ default: 'basic',
50
+ },
51
+ packageManager: {
52
+ description: 'Package Manager',
53
+ type: 'string',
54
+ default: 'pnpm',
55
+ alias: ['package', 'packagemanager'],
23
56
  },
24
57
  },
25
58
  }
59
+ const getTemplateFile = (templateName) => {
60
+ return templateName === 'react-router'
61
+ ? templates.templateWithReactRouter
62
+ : templates.templateWithList
63
+ }
64
+
65
+ const defaultOptions = {
66
+ typeScript: true,
67
+ templateName: 'basic',
68
+ template: getTemplateFile('basic'),
69
+ packageManager: 'pnpm',
70
+ }
26
71
 
27
72
  const command = {
28
73
  command: '[app]',
@@ -32,9 +77,11 @@ const command = {
32
77
  handler: async (argv) => {
33
78
  let name = argv._[0] || argv.name
34
79
 
35
- reporter.debug(
36
- `running "npm create @dhis2/app" (or npx) command which is an alias to "d2 app scripts init"`
37
- )
80
+ const selectedOptions = {
81
+ ...defaultOptions,
82
+ }
83
+
84
+ reporter.debug(`running "npm create @dhis2/app" (or npx) command"`)
38
85
  const useDefauls = argv.yes
39
86
 
40
87
  if (!name) {
@@ -47,9 +94,11 @@ const command = {
47
94
  reporter.log(`name of project: ${name}`)
48
95
  }
49
96
 
50
- let pnpm = true
51
- let npm = false
52
- let typeScript = argv.typescript || true
97
+ selectedOptions.typeScript = argv.typescript
98
+ selectedOptions.packageManager = argv.packageManager
99
+ selectedOptions.templateName = argv.template
100
+ selectedOptions.template = getTemplateFile(argv.template)
101
+
53
102
  if (!useDefauls) {
54
103
  const packageManager = await select({
55
104
  message: 'Select a package manager',
@@ -61,43 +110,231 @@ const command = {
61
110
  ],
62
111
  })
63
112
 
64
- pnpm = packageManager === 'pnpm'
65
- npm = packageManager === 'npm'
113
+ selectedOptions.packageManager = packageManager
66
114
 
67
- const template = await select({
68
- message: 'Select a template',
115
+ const language = await select({
116
+ message: 'Select a language',
69
117
  default: 'ts',
70
118
  choices: [
71
119
  { name: 'JavaScript', value: 'js' },
72
120
  { name: 'TypeScript', value: 'ts' },
121
+ ],
122
+ })
123
+
124
+ selectedOptions.typeScript = language === 'ts'
125
+
126
+ const template = await select({
127
+ message: 'Select a teamplate',
128
+ default: 'ts',
129
+ choices: [
130
+ { name: 'Basic Template', value: 'basic' },
73
131
  {
74
- name: 'Custom (coming soon)',
75
- disabled: true,
76
- value: 'custom',
132
+ name: 'Template with React Router',
133
+ value: 'react-router',
77
134
  },
78
135
  ],
79
136
  })
80
137
 
81
- typeScript = template === 'ts'
138
+ selectedOptions.templateName = template
139
+ selectedOptions.template = getTemplateFile(template)
82
140
  }
83
141
 
84
- if (useDefauls) {
85
- reporter.info(
86
- chalk.greenBright(
87
- `These default options will be used to create a new app: \n${chalk.greenBright(
88
- '- Language: TypeScript\n- Package manager: pnpm'
89
- )}`
90
- )
142
+ // let pkgManager = 'yarn'
143
+ // if (pnpm) {
144
+ // pkgManager = 'pnpm'
145
+ // } else if (npm) {
146
+ // pkgManager = 'npm'
147
+ // }
148
+
149
+ reporter.info(
150
+ `Initialising a new project using "${selectedOptions.packageManager}" as a package manager.`
151
+ )
152
+
153
+ if (selectedOptions.packageManager !== 'pnpm') {
154
+ reporter.warn(
155
+ 'We recommend using "pnpm" as a package manager for new projects. You can do so by passing the argument --pnpm (i.e. d2 app scripts init --pnpm). This will become the default in future versions of d2 CLI.'
156
+ )
157
+ }
158
+ // create the folder where the template will be generated
159
+ let cwd = process.cwd()
160
+ cwd = path.join(cwd, name)
161
+
162
+ if (fs.existsSync(cwd)) {
163
+ reporter.error(
164
+ `The folder "${name}" already exists. Please either delete it, or choose a different name.`
91
165
  )
166
+ process.exit(1)
167
+ }
168
+
169
+ reporter.info(`selected options: ${JSON.stringify(selectedOptions)}`)
170
+
171
+ await decompress(selectedOptions.template, cwd)
172
+
173
+ const paths = {
174
+ base: cwd,
175
+ package: path.join(cwd, 'package.json'),
176
+ config: path.join(cwd, 'd2.config.js'),
177
+ pnpmLock: path.join(cwd, 'pnpm-lock.yaml'),
178
+ pnpmWorkspace: path.join(cwd, 'pnpm-workspace.yaml'),
179
+ appRootFile: path.join(cwd, 'src/App.tsx'),
180
+ initYarnLock: path.join(__dirname, '../templates/yarn.lock'),
181
+ initNpmLock: path.join(__dirname, '../templates/package-lock.json'),
92
182
  }
93
183
 
94
- await initCommand.handler({
95
- ...argv,
96
- pnpm,
97
- npm,
98
- typeScript,
99
- name,
184
+ const pnpm = selectedOptions.packageManager === 'pnpm'
185
+ const npm = selectedOptions.packageManager === 'npm'
186
+ const yarn = selectedOptions.packageManager === 'yarn'
187
+ const pkgManager = selectedOptions.packageManager
188
+ const typeScript = selectedOptions.typeScript
189
+
190
+ // Default template is with PNPM with TypeScript - some modifications here for yarn/npm/JS
191
+ const templateModifications = [
192
+ [paths.package, true, (f) => f.replace('{{template-name}}', name)],
193
+ // [
194
+ // path.join(paths.base, '.husky/pre-commit'),
195
+ // !pnpm,
196
+ // (f) => f.replace(/pnpm/gm, pkgManager),
197
+ // ],
198
+ [
199
+ paths.package,
200
+ yarn,
201
+ (f) => f.replace(/"pnpm@.+"/gm, '"yarn@1.22.22"'),
202
+ ],
203
+ [
204
+ paths.package,
205
+ npm,
206
+ (f) => f.replace(/"pnpm@.+"/gm, '"npm@10.8.2"'),
207
+ ],
208
+ [paths.package, !pnpm, (f) => f.replace(/pnpm/gm, pkgManager)],
209
+ [paths.config, !typeScript, (f) => f.replace(/\.tsx/gm, '.jsx')],
210
+ [
211
+ paths.appRootFile,
212
+ !typeScript,
213
+ (f) => f.replace(/with TypeScript/gm, 'with JavaScript'),
214
+ ],
215
+ ]
216
+ templateModifications.forEach(([filePath, condition, replaceFunc]) => {
217
+ const fileExists = fs.existsSync(filePath)
218
+
219
+ if (!condition || !fileExists) {
220
+ if (!fileExists) {
221
+ reporter.debug(
222
+ `File "${filePath}" specified in the template does not exist. This is likely a problem with the CLI and should be reported to the core team.`
223
+ )
224
+ }
225
+ return
226
+ }
227
+ let fileContent = fs.readFileSync(filePath, {
228
+ encoding: 'utf8',
229
+ })
230
+
231
+ fileContent = replaceFunc(fileContent)
232
+
233
+ fs.writeFileSync(filePath, fileContent)
234
+ })
235
+
236
+ // copy correct lock file for npm/yarn
237
+ if (!pnpm) {
238
+ if (fs.existsSync(paths.pnpmLock)) {
239
+ fs.removeSync(paths.pnpmLock)
240
+ }
241
+ if (fs.existsSync(paths.pnpmWorkspace)) {
242
+ fs.removeSync(paths.pnpmWorkspace)
243
+ }
244
+
245
+ if (npm) {
246
+ fs.copyFileSync(
247
+ paths.initNpmLock,
248
+ path.join(paths.base, 'package-lock.json')
249
+ )
250
+ } else {
251
+ fs.copyFileSync(
252
+ paths.initYarnLock,
253
+ path.join(paths.base, 'yarn.lock')
254
+ )
255
+ }
256
+ }
257
+
258
+ // convert to JS
259
+ if (!typeScript) {
260
+ reporter.info('Preparing JavaScript template')
261
+ reporter.info(` running '${pkgManager} install'`)
262
+
263
+ await exec({
264
+ cmd: pkgManager,
265
+ args: ['install'],
266
+ cwd: paths.base,
267
+ })
268
+ reporter.info(' convert template to JS with tsc')
269
+ await exec({
270
+ cmd: 'npx',
271
+ args: [
272
+ 'tsc',
273
+ // 'node_modules/.bin/tsc',
274
+ '--project',
275
+ path.join(paths.base, 'tsconfig.json'),
276
+ '--noEmit',
277
+ false,
278
+ '--jsx',
279
+ 'preserve',
280
+ ],
281
+
282
+ cwd: paths.base,
283
+ pipe: argv.debug,
284
+ })
285
+
286
+ reporter.info(' Deleting TS files')
287
+ const filePathsToRemove = path.join(paths.base, '/src/**/*.ts[x]')
288
+ const filesToRemove = await fg.glob(filePathsToRemove)
289
+
290
+ filesToRemove.forEach((file) => {
291
+ fs.removeSync(file)
292
+ })
293
+
294
+ if (fs.existsSync(path.join(paths.base, 'types'))) {
295
+ fs.removeSync(path.join(paths.base, 'types'))
296
+ }
297
+
298
+ if (fs.existsSync(path.join(paths.base, 'tsconfig.json'))) {
299
+ fs.removeSync(path.join(paths.base, 'tsconfig.json'))
300
+ }
301
+
302
+ reporter.info('Finished preparing JavaScript template')
303
+ }
304
+
305
+ reporter.info(`Running '${pkgManager} install'`)
306
+
307
+ reporter.debug(`Upgrading @dhis2 dependencies to latest`)
308
+ await exec({
309
+ cmd: pkgManager,
310
+ args: [
311
+ npm ? 'install --save' : 'upgrade',
312
+ '@dhis2/app-runtime@latest',
313
+ '@dhis2/cli-app-scripts@latest',
314
+ '@dhis2/ui@latest',
315
+ ],
316
+ cwd: paths.base,
317
+ pipe: argv.debug,
318
+ })
319
+
320
+ await exec({
321
+ cmd: pkgManager,
322
+ args: ['install'],
323
+ cwd: paths.base,
324
+ pipe: argv.debug,
100
325
  })
326
+
327
+ reporter.debug(`Running '${pkgManager} format'`)
328
+ await exec({
329
+ cmd: pkgManager,
330
+ args: npm ? ['run', 'format'] : ['format'],
331
+ cwd: paths.base,
332
+ pipe: argv.debug,
333
+ })
334
+
335
+ reporter.info('Done!')
336
+
337
+ return
101
338
  },
102
339
  }
103
340