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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.1",
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,219 @@ const command = {
61
110
  ],
62
111
  })
63
112
 
64
- pnpm = packageManager === 'pnpm'
65
- npm = packageManager === 'npm'
113
+ // pnpm = packageManager === 'pnpm'
114
+ // npm = packageManager === 'npm'
115
+ selectedOptions.packageManager = packageManager
66
116
 
67
- const template = await select({
68
- message: 'Select a template',
117
+ const language = await select({
118
+ message: 'Select a language',
69
119
  default: 'ts',
70
120
  choices: [
71
121
  { name: 'JavaScript', value: 'js' },
72
122
  { name: 'TypeScript', value: 'ts' },
123
+ ],
124
+ })
125
+
126
+ selectedOptions.typeScript = language === 'ts'
127
+
128
+ const template = await select({
129
+ message: 'Select a teamplate',
130
+ default: 'ts',
131
+ choices: [
132
+ { name: 'Basic Template', value: 'basic' },
73
133
  {
74
- name: 'Custom (coming soon)',
75
- disabled: true,
76
- value: 'custom',
134
+ name: 'Template with React Router',
135
+ value: 'react-router',
77
136
  },
78
137
  ],
79
138
  })
80
139
 
81
- typeScript = template === 'ts'
140
+ selectedOptions.templateName = template
141
+ selectedOptions.template = getTemplateFile(template)
82
142
  }
83
143
 
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
- )
144
+ // let pkgManager = 'yarn'
145
+ // if (pnpm) {
146
+ // pkgManager = 'pnpm'
147
+ // } else if (npm) {
148
+ // pkgManager = 'npm'
149
+ // }
150
+
151
+ reporter.info(
152
+ `Initialising a new project using "${selectedOptions.packageManager}" as a package manager.`
153
+ )
154
+
155
+ if (selectedOptions.packageManager !== 'pnpm') {
156
+ reporter.warn(
157
+ '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.'
158
+ )
159
+ }
160
+ // create the folder where the template will be generated
161
+ let cwd = process.cwd()
162
+ cwd = path.join(cwd, name)
163
+
164
+ if (fs.existsSync(cwd)) {
165
+ reporter.error(
166
+ `The folder "${name}" already exists. Please either delete it, or choose a different name.`
91
167
  )
168
+ process.exit(1)
92
169
  }
93
170
 
94
- await initCommand.handler({
95
- ...argv,
96
- pnpm,
97
- npm,
98
- typeScript,
99
- name,
171
+ reporter.info(`selected options: ${JSON.stringify(selectedOptions)}`)
172
+
173
+ await decompress(selectedOptions.template, cwd)
174
+
175
+ const paths = {
176
+ base: cwd,
177
+ package: path.join(cwd, 'package.json'),
178
+ config: path.join(cwd, 'd2.config.js'),
179
+ pnpmLock: path.join(cwd, 'pnpm-lock.yaml'),
180
+ pnpmWorkspace: path.join(cwd, 'pnpm-workspace.yaml'),
181
+ appRootFile: path.join(cwd, 'src/App.tsx'),
182
+ initYarnLock: path.join(__dirname, '../templates/yarn.lock'),
183
+ initNpmLock: path.join(__dirname, '../templates/package-lock.json'),
184
+ }
185
+
186
+ const pnpm = selectedOptions.packageManager === 'pnpm'
187
+ const npm = selectedOptions.packageManager === 'npm'
188
+ const yarn = selectedOptions.packageManager === 'yarn'
189
+ const pkgManager = selectedOptions.packageManager
190
+ const typeScript = selectedOptions.typeScript
191
+
192
+ // Default template is with PNPM with TypeScript - some modifications here for yarn/npm/JS
193
+ const templateModifications = [
194
+ [paths.package, true, (f) => f.replace('{{template-name}}', name)],
195
+ // [
196
+ // path.join(paths.base, '.husky/pre-commit'),
197
+ // !pnpm,
198
+ // (f) => f.replace(/pnpm/gm, pkgManager),
199
+ // ],
200
+ [
201
+ paths.package,
202
+ yarn,
203
+ (f) => f.replace(/"pnpm@.+"/gm, '"yarn@1.22.22"'),
204
+ ],
205
+ [
206
+ paths.package,
207
+ npm,
208
+ (f) => f.replace(/"pnpm@.+"/gm, '"npm@10.8.2"'),
209
+ ],
210
+ [paths.package, !pnpm, (f) => f.replace(/pnpm/gm, pkgManager)],
211
+ [paths.config, !typeScript, (f) => f.replace(/\.tsx/gm, '.jsx')],
212
+ [
213
+ paths.appRootFile,
214
+ !typeScript,
215
+ (f) => f.replace(/with TypeScript/gm, 'with JavaScript'),
216
+ ],
217
+ ]
218
+ templateModifications.forEach(([filePath, condition, replaceFunc]) => {
219
+ const fileExists = fs.existsSync(filePath)
220
+
221
+ if (!condition || !fileExists) {
222
+ if (!fileExists) {
223
+ reporter.debug(
224
+ `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.`
225
+ )
226
+ }
227
+ return
228
+ }
229
+ let fileContent = fs.readFileSync(filePath, {
230
+ encoding: 'utf8',
231
+ })
232
+
233
+ fileContent = replaceFunc(fileContent)
234
+
235
+ fs.writeFileSync(filePath, fileContent)
100
236
  })
237
+
238
+ // copy correct lock file for npm/yarn
239
+ if (!pnpm) {
240
+ if (fs.existsSync(paths.pnpmLock)) {
241
+ fs.removeSync(paths.pnpmLock)
242
+ }
243
+ if (fs.existsSync(paths.pnpmWorkspace)) {
244
+ fs.removeSync(paths.pnpmWorkspace)
245
+ }
246
+
247
+ if (npm) {
248
+ fs.copyFileSync(
249
+ paths.initNpmLock,
250
+ path.join(paths.base, 'package-lock.json')
251
+ )
252
+ } else {
253
+ fs.copyFileSync(
254
+ paths.initYarnLock,
255
+ path.join(paths.base, 'yarn.lock')
256
+ )
257
+ }
258
+ }
259
+
260
+ // convert to JS
261
+ if (!typeScript) {
262
+ reporter.info('Preparing JavaScript template')
263
+ reporter.info(` running '${pkgManager} install'`)
264
+
265
+ await exec({
266
+ cmd: pkgManager,
267
+ args: ['install'],
268
+ cwd: paths.base,
269
+ })
270
+ reporter.info(' convert template to JS with tsc')
271
+ await exec({
272
+ cmd: 'npx',
273
+ args: [
274
+ 'tsc',
275
+ // 'node_modules/.bin/tsc',
276
+ '--project',
277
+ path.join(paths.base, 'tsconfig.json'),
278
+ '--noEmit',
279
+ false,
280
+ '--jsx',
281
+ 'preserve',
282
+ ],
283
+
284
+ cwd: paths.base,
285
+ pipe: argv.debug,
286
+ })
287
+
288
+ reporter.info(' Deleting TS files')
289
+ const filePathsToRemove = path.join(paths.base, '/src/**/*.ts[x]')
290
+ const filesToRemove = await fg.glob(filePathsToRemove)
291
+
292
+ filesToRemove.forEach((file) => {
293
+ fs.removeSync(file)
294
+ })
295
+
296
+ if (fs.existsSync(path.join(paths.base, 'types'))) {
297
+ fs.removeSync(path.join(paths.base, 'types'))
298
+ }
299
+
300
+ if (fs.existsSync(path.join(paths.base, 'tsconfig.json'))) {
301
+ fs.removeSync(path.join(paths.base, 'tsconfig.json'))
302
+ }
303
+
304
+ reporter.info('Finished preparing JavaScript template')
305
+ }
306
+
307
+ reporter.info(`Running '${pkgManager} install'`)
308
+
309
+ await exec({
310
+ cmd: pkgManager,
311
+ args: ['install'],
312
+ cwd: paths.base,
313
+ pipe: argv.debug,
314
+ })
315
+
316
+ await exec({
317
+ cmd: pkgManager,
318
+ args: npm ? ['run', 'format'] : ['format'],
319
+ cwd: paths.base,
320
+ pipe: argv.debug,
321
+ })
322
+
323
+ reporter.info('Done!')
324
+
325
+ return
101
326
  },
102
327
  }
103
328