@newlogic-digital/cli 1.2.3 → 1.3.0

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/index.mjs CHANGED
@@ -5,8 +5,58 @@ import cms from './src/commands/cms/index.mjs'
5
5
  import pc from 'picocolors'
6
6
  import { version, name } from './src/utils.mjs'
7
7
 
8
- const args = process.argv.slice(2)
9
- const command = args[0]
8
+ function normalizeOptionName(name) {
9
+ return name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())
10
+ }
11
+
12
+ function parseCommandArgs(args) {
13
+ const positionals = []
14
+ const options = {}
15
+
16
+ for (let i = 0; i < args.length; i++) {
17
+ const arg = args[i]
18
+
19
+ if (arg === '-y') {
20
+ options.y = true
21
+ continue
22
+ }
23
+
24
+ if (!arg.startsWith('--')) {
25
+ positionals.push(arg)
26
+ continue
27
+ }
28
+
29
+ if (arg.startsWith('--no-')) {
30
+ options[normalizeOptionName(arg.slice(5))] = false
31
+ continue
32
+ }
33
+
34
+ if (arg.includes('=')) {
35
+ const splitIndex = arg.indexOf('=')
36
+ const key = normalizeOptionName(arg.slice(2, splitIndex))
37
+ const value = arg.slice(splitIndex + 1)
38
+
39
+ options[key] = value || true
40
+ continue
41
+ }
42
+
43
+ const key = normalizeOptionName(arg.slice(2))
44
+ const nextArg = args[i + 1]
45
+
46
+ if (nextArg && !nextArg.startsWith('-')) {
47
+ options[key] = nextArg
48
+ i++
49
+ continue
50
+ }
51
+
52
+ options[key] = true
53
+ }
54
+
55
+ return { positionals, options }
56
+ }
57
+
58
+ const rawArgs = process.argv.slice(2)
59
+ const command = rawArgs[0]
10
60
 
11
61
  if (!command) {
12
62
  console.log(`
@@ -21,6 +71,9 @@ if (!command) {
21
71
  ${pc.green('newlogic init ui')} ${pc.yellow('<directory>')} - Creates a new ${pc.blue('@newlogic-digital/ui')} project in new directory with the name ${pc.yellow('<directory>')}
22
72
  ${pc.green('newlogic init cms')} - Creates a new ${pc.blue('@newlogic-digital/cms')} project in current directory
23
73
  ${pc.green('newlogic init cms')} ${pc.yellow('<directory>')} - Creates a new ${pc.blue('@newlogic-digital/cms')} project in new directory with the name ${pc.yellow('<directory>')}
74
+ ${pc.green('newlogic init ui')} ${pc.yellow('<directory>')} ${pc.cyan('--branch=dev --clone=https --git --remote=<git-url> --install')}
75
+ ${pc.green('newlogic init cms')} ${pc.yellow('<directory>')} ${pc.cyan('--branch=dev --clone=https --variant=cms-web --install --prepare --dev --migrations')}
76
+ ${pc.green('newlogic init ui')} ${pc.yellow('<directory>')} ${pc.cyan('-y')} - Runs with default options without prompts
24
77
 
25
78
  -- cms --
26
79
  ${pc.green('newlogic cms prepare')} - Copies templates and components from ${pc.blue('@newlogic-digital/ui')} project to ${pc.blue('@newlogic-digital/cms')}
@@ -34,15 +87,17 @@ if (!command) {
34
87
  }
35
88
 
36
89
  if (command === 'init') {
37
- const action = args[1]
38
- const name = args[2]
90
+ const { positionals, options } = parseCommandArgs(rawArgs.slice(1))
91
+ const action = positionals[0]
92
+ const name = positionals[1]
39
93
 
40
- await init(action, name)
94
+ await init(action, name, options)
41
95
  }
42
96
 
43
97
  if (command === 'cms') {
44
- const action = args[1]
45
- const name = args[2]
98
+ const { positionals } = parseCommandArgs(rawArgs.slice(1))
99
+ const action = positionals[0]
100
+ const name = positionals[1]
46
101
 
47
102
  await cms(action, name)
48
103
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newlogic-digital/cli",
3
- "version": "1.2.3",
3
+ "version": "1.3.0",
4
4
  "main": "index.mjs",
5
5
  "bin": {
6
6
  "newlogic-cli": "index.mjs",
@@ -10,12 +10,12 @@
10
10
  "eslint": "eslint '**/*.mjs' --fix"
11
11
  },
12
12
  "dependencies": {
13
- "lodash": "^4.17.21",
13
+ "lodash": "^4.17.23",
14
14
  "prompts": "^2.4.2",
15
- "fs-extra": "^11.2.0",
15
+ "fs-extra": "^11.3.3",
16
16
  "picocolors": "^1.1.1",
17
- "fast-glob": "^3.3.2",
18
- "dedent": "^1.5.3"
17
+ "fast-glob": "^3.3.3",
18
+ "dedent": "^1.7.1"
19
19
  },
20
20
  "devDependencies": {
21
21
  "eslint-config-standard": "^17.1.0"
@@ -5,6 +5,7 @@ import os from 'os'
5
5
  import pc from 'picocolors'
6
6
  import fse from 'fs-extra'
7
7
  import prompts from 'prompts'
8
+ import { isAutoYes, normalizeEnum, normalizeYesNo } from './options.mjs'
8
9
 
9
10
  const tempDir = join(os.tmpdir(), 'newlogic-cms-web')
10
11
 
@@ -12,48 +13,70 @@ async function move(path, options = {}) {
12
13
  await fse.move(join(tempDir, path), resolve(process.cwd(), path), options).catch(err => console.log(`${pc.red(err)} - ${path}`))
13
14
  }
14
15
 
15
- async function clone(path, { variant, branch }) {
16
+ async function clone(path, { variant, branch, clone: cloneOption, y, yes } = {}) {
16
17
  if (variant === 'cms-web') {
17
18
  variant = 'newlogic-cms-next-web'
18
19
  } else if (variant === 'cms-eshop') {
19
20
  variant = 'newlogic-cms-next-eshop'
20
21
  }
21
22
 
22
- const { clone } = await prompts([
23
- {
24
- type: 'select',
25
- name: 'clone',
26
- message: 'Clone with SSH or HTTPS?',
27
- choices: [
28
- { title: 'SSH', value: 'ssh' },
29
- { title: 'HTTPS', value: 'https' }
30
- ]
23
+ let cloneMethod = normalizeEnum(cloneOption, ['ssh', 'https'])
24
+ const autoYes = isAutoYes({ y, yes })
25
+
26
+ if (!cloneMethod) {
27
+ if (autoYes) {
28
+ cloneMethod = 'https'
29
+ } else {
30
+ const response = await prompts([
31
+ {
32
+ type: 'select',
33
+ name: 'clone',
34
+ message: 'Clone with SSH or HTTPS?',
35
+ choices: [
36
+ { title: 'SSH', value: 'ssh' },
37
+ { title: 'HTTPS', value: 'https' }
38
+ ]
39
+ }
40
+ ])
41
+
42
+ cloneMethod = response.clone
31
43
  }
32
- ])
44
+ }
33
45
 
34
46
  let url = ''
35
47
 
36
- if (clone === 'ssh') {
48
+ if (cloneMethod === 'ssh') {
37
49
  url = 'git@git.newlogic.cz:newlogic-digital'
38
- } else if (clone === 'https') {
50
+ } else if (cloneMethod === 'https') {
39
51
  url = 'https://git.newlogic.cz/newlogic-digital'
40
52
  }
41
53
 
42
54
  execSync(`git clone -b ${branch} --single-branch --depth 1 ${url}/${variant}.git ${path}`)
43
55
  }
44
56
 
45
- async function install(name, branch) {
46
- const { install } = await prompts([
47
- {
48
- type: 'select',
49
- name: 'install',
50
- message: 'Install project?',
51
- choices: [
52
- { title: 'yes', value: 'yes' },
53
- { title: 'no', value: 'no' }
54
- ]
57
+ async function install(name, branch, { install: installOption, y, yes } = {}) {
58
+ let install = normalizeYesNo(installOption)
59
+ const autoYes = isAutoYes({ y, yes })
60
+
61
+ if (!install) {
62
+ if (autoYes) {
63
+ install = 'yes'
64
+ } else {
65
+ const response = await prompts([
66
+ {
67
+ type: 'select',
68
+ name: 'install',
69
+ message: 'Install project?',
70
+ choices: [
71
+ { title: 'yes', value: 'yes' },
72
+ { title: 'no', value: 'no' }
73
+ ]
74
+ }
75
+ ])
76
+
77
+ install = response.install
55
78
  }
56
- ])
79
+ }
57
80
 
58
81
  if (install === 'yes') {
59
82
  execSync(`cd ${name || '.'} && composer install`)
@@ -65,70 +88,103 @@ async function install(name, branch) {
65
88
  }
66
89
  }
67
90
 
68
- async function dev(name) {
69
- const { install } = await prompts([
70
- {
71
- type: 'select',
72
- name: 'install',
73
- message: 'Start dev server?',
74
- choices: [
75
- { title: 'yes', value: 'yes' },
76
- { title: 'no', value: 'no' }
77
- ]
91
+ async function dev(name, { dev: devOption, y, yes } = {}) {
92
+ let shouldStartDev = normalizeYesNo(devOption)
93
+ const autoYes = isAutoYes({ y, yes })
94
+
95
+ if (!shouldStartDev) {
96
+ if (autoYes) {
97
+ shouldStartDev = 'no'
98
+ } else {
99
+ const response = await prompts([
100
+ {
101
+ type: 'select',
102
+ name: 'install',
103
+ message: 'Start dev server?',
104
+ choices: [
105
+ { title: 'yes', value: 'yes' },
106
+ { title: 'no', value: 'no' }
107
+ ]
108
+ }
109
+ ])
110
+
111
+ shouldStartDev = response.install
78
112
  }
79
- ])
113
+ }
80
114
 
81
- if (install === 'yes') {
115
+ if (shouldStartDev === 'yes') {
82
116
  execSync(`cd ${name || '.'} && composer dev-headless`)
83
117
  }
84
118
  }
85
119
 
86
- async function migrations(name) {
87
- const { install } = await prompts([
88
- {
89
- type: 'select',
90
- name: 'install',
91
- message: 'Init phinx migrations and seed?',
92
- choices: [
93
- { title: 'yes', value: 'yes' },
94
- { title: 'no', value: 'no' }
95
- ]
120
+ async function migrations(name, { migrations: migrationsOption, y, yes } = {}) {
121
+ let shouldInitMigrations = normalizeYesNo(migrationsOption)
122
+ const autoYes = isAutoYes({ y, yes })
123
+
124
+ if (!shouldInitMigrations) {
125
+ if (autoYes) {
126
+ shouldInitMigrations = 'no'
127
+ } else {
128
+ const response = await prompts([
129
+ {
130
+ type: 'select',
131
+ name: 'install',
132
+ message: 'Init phinx migrations and seed?',
133
+ choices: [
134
+ { title: 'yes', value: 'yes' },
135
+ { title: 'no', value: 'no' }
136
+ ]
137
+ }
138
+ ])
139
+
140
+ shouldInitMigrations = response.install
96
141
  }
97
- ])
142
+ }
98
143
 
99
- if (install === 'yes') {
144
+ if (shouldInitMigrations === 'yes') {
100
145
  execSync(`cd ${name || '.'} && composer phinx-init`)
101
146
  }
102
147
  }
103
148
 
104
- async function prepare() {
105
- const { install } = await prompts([
106
- {
107
- type: 'select',
108
- name: 'install',
109
- message: 'Prepare project with templates from frontend?',
110
- choices: [
111
- { title: 'yes', value: 'yes' },
112
- { title: 'no', value: 'no' }
113
- ]
149
+ async function prepare({ prepare: prepareOption, y, yes } = {}) {
150
+ let shouldPrepare = normalizeYesNo(prepareOption)
151
+ const autoYes = isAutoYes({ y, yes })
152
+
153
+ if (!shouldPrepare) {
154
+ if (autoYes) {
155
+ shouldPrepare = 'no'
156
+ } else {
157
+ const response = await prompts([
158
+ {
159
+ type: 'select',
160
+ name: 'install',
161
+ message: 'Prepare project with templates from frontend?',
162
+ choices: [
163
+ { title: 'yes', value: 'yes' },
164
+ { title: 'no', value: 'no' }
165
+ ]
166
+ }
167
+ ])
168
+
169
+ shouldPrepare = response.install
114
170
  }
115
- ])
171
+ }
116
172
 
117
- if (install === 'yes') {
173
+ if (shouldPrepare === 'yes') {
118
174
  execSync('newlogic cms prepare')
119
175
  }
120
176
  }
121
177
 
122
- export default async function cms(name, { variant, branch }) {
178
+ export default async function cms(name, { variant, branch, ...options } = {}) {
123
179
  if (name) {
124
180
  if (!fs.existsSync(resolve(process.cwd(), name))) {
125
- await clone(name, { variant, branch })
181
+ await clone(name, { variant, branch, clone: options.clone, y: options.y, yes: options.yes })
126
182
  }
127
183
 
128
- await install(name, branch)
129
- await prepare()
130
- await dev(name)
131
- await migrations(name)
184
+ await install(name, branch, { install: options.install, y: options.y, yes: options.yes })
185
+ await prepare({ prepare: options.prepare, y: options.y, yes: options.yes })
186
+ await dev(name, { dev: options.dev, y: options.y, yes: options.yes })
187
+ await migrations(name, { migrations: options.migrations, y: options.y, yes: options.yes })
132
188
  return
133
189
  }
134
190
 
@@ -137,7 +193,7 @@ export default async function cms(name, { variant, branch }) {
137
193
  fse.removeSync(tempDir)
138
194
  }
139
195
 
140
- await clone(tempDir, { variant, branch })
196
+ await clone(tempDir, { variant, branch, clone: options.clone, y: options.y, yes: options.yes })
141
197
 
142
198
  await move('.docker')
143
199
  await move('app')
@@ -166,8 +222,8 @@ export default async function cms(name, { variant, branch }) {
166
222
  console.log(pc.yellow('Project files already exist in the directory, skipping copy step (delete composer.json to force)'))
167
223
  }
168
224
 
169
- await install(name, branch)
170
- await prepare()
171
- await dev(name)
172
- await migrations(name)
225
+ await install(name, branch, { install: options.install, y: options.y, yes: options.yes })
226
+ await prepare({ prepare: options.prepare, y: options.y, yes: options.yes })
227
+ await dev(name, { dev: options.dev, y: options.y, yes: options.yes })
228
+ await migrations(name, { migrations: options.migrations, y: options.y, yes: options.yes })
173
229
  }
@@ -1,72 +1,99 @@
1
1
  import prompts from 'prompts'
2
2
  import ui from './ui.mjs'
3
3
  import cms from './cms.mjs'
4
+ import { isAutoYes, normalizeEnum } from './options.mjs'
4
5
 
5
- async function init(project, name) {
6
+ async function init(project, name, options = {}) {
6
7
  const nameAsProject = (project && !project.match(/^(ui|cms)$/))
8
+ const autoYes = isAutoYes(options)
9
+
10
+ name = nameAsProject ? project : name
7
11
 
8
12
  if (!project || nameAsProject) {
9
- const response = await prompts([
10
- {
11
- type: 'select',
12
- name: 'project',
13
- message: 'Select a project to init',
14
- choices: [
15
- { title: 'ui', value: 'ui' },
16
- { title: 'cms', value: 'cms' }
17
- ],
18
- onState: (state) => {
19
- if (state.aborted) {
20
- process.exit(1)
13
+ if (autoYes) {
14
+ project = 'ui'
15
+ } else {
16
+ const response = await prompts([
17
+ {
18
+ type: 'select',
19
+ name: 'project',
20
+ message: 'Select a project to init',
21
+ choices: [
22
+ { title: 'ui', value: 'ui' },
23
+ { title: 'cms', value: 'cms' }
24
+ ],
25
+ onState: (state) => {
26
+ if (state.aborted) {
27
+ process.exit(1)
28
+ }
21
29
  }
22
30
  }
23
- }
24
- ])
31
+ ])
25
32
 
26
- name = nameAsProject ? project : name
27
- project = response.project
33
+ project = response.project
34
+ }
28
35
  }
29
36
 
30
- const { branch } = await prompts([
31
- {
32
- type: 'select',
33
- name: 'branch',
34
- message: 'Select a git branch',
35
- choices: [
36
- { title: 'main', value: 'main' },
37
- { title: 'dev', value: 'dev' }
38
- ],
39
- onState: (state) => {
40
- if (state.aborted) {
41
- process.exit(1)
37
+ let branch = normalizeEnum(options.branch, ['main', 'dev'])
38
+
39
+ if (!branch) {
40
+ if (autoYes) {
41
+ branch = 'main'
42
+ } else {
43
+ const response = await prompts([
44
+ {
45
+ type: 'select',
46
+ name: 'branch',
47
+ message: 'Select a git branch',
48
+ choices: [
49
+ { title: 'main', value: 'main' },
50
+ { title: 'dev', value: 'dev' }
51
+ ],
52
+ onState: (state) => {
53
+ if (state.aborted) {
54
+ process.exit(1)
55
+ }
56
+ }
42
57
  }
43
- }
58
+ ])
59
+
60
+ branch = response.branch
44
61
  }
45
- ])
62
+ }
46
63
 
47
64
  if (project === 'ui') {
48
- await ui(name, { branch })
65
+ await ui(name, { ...options, branch })
49
66
  }
50
67
 
51
68
  if (project === 'cms') {
52
- const { variant } = await prompts([
53
- {
54
- type: 'select',
55
- name: 'variant',
56
- message: 'Select a variant to install',
57
- choices: [
58
- { title: 'cms-web', value: 'cms-web' },
59
- { title: 'cms-eshop', value: 'cms-eshop' }
60
- ],
61
- onState: (state) => {
62
- if (state.aborted) {
63
- process.exit(1)
69
+ let variant = normalizeEnum(options.variant, ['cms-web', 'cms-eshop'])
70
+
71
+ if (!variant) {
72
+ if (autoYes) {
73
+ variant = 'cms-web'
74
+ } else {
75
+ const response = await prompts([
76
+ {
77
+ type: 'select',
78
+ name: 'variant',
79
+ message: 'Select a variant to install',
80
+ choices: [
81
+ { title: 'cms-web', value: 'cms-web' },
82
+ { title: 'cms-eshop', value: 'cms-eshop' }
83
+ ],
84
+ onState: (state) => {
85
+ if (state.aborted) {
86
+ process.exit(1)
87
+ }
88
+ }
64
89
  }
65
- }
90
+ ])
91
+
92
+ variant = response.variant
66
93
  }
67
- ])
94
+ }
68
95
 
69
- await cms(name, { branch, variant })
96
+ await cms(name, { ...options, branch, variant })
70
97
  }
71
98
  }
72
99
 
@@ -0,0 +1,47 @@
1
+ function normalizeEnum(value, allowed = []) {
2
+ if (typeof value !== 'string') {
3
+ return undefined
4
+ }
5
+
6
+ const normalized = value.trim().toLowerCase()
7
+
8
+ if (!allowed.includes(normalized)) {
9
+ return undefined
10
+ }
11
+
12
+ return normalized
13
+ }
14
+
15
+ function normalizeYesNo(value) {
16
+ if (typeof value === 'boolean') {
17
+ return value ? 'yes' : 'no'
18
+ }
19
+
20
+ if (typeof value !== 'string') {
21
+ return undefined
22
+ }
23
+
24
+ const normalized = value.trim().toLowerCase()
25
+
26
+ if (['yes', 'y', 'true', '1'].includes(normalized)) {
27
+ return 'yes'
28
+ }
29
+
30
+ if (['no', 'n', 'false', '0'].includes(normalized)) {
31
+ return 'no'
32
+ }
33
+
34
+ return undefined
35
+ }
36
+
37
+ function isAutoYes(options = {}) {
38
+ const values = [normalizeYesNo(options.y), normalizeYesNo(options.yes)]
39
+
40
+ if (values.includes('no')) {
41
+ return false
42
+ }
43
+
44
+ return values.includes('yes')
45
+ }
46
+
47
+ export { normalizeEnum, normalizeYesNo, isAutoYes }
@@ -2,19 +2,31 @@ import { execSync } from '../../utils.mjs'
2
2
  import fse from 'fs-extra'
3
3
  import prompts from 'prompts'
4
4
  import { join, resolve } from 'path'
5
+ import { isAutoYes, normalizeEnum, normalizeYesNo } from './options.mjs'
5
6
 
6
- export default async function ui(name, { branch }) {
7
- const { clone } = await prompts([
8
- {
9
- type: 'select',
10
- name: 'clone',
11
- message: 'Clone with SSH or HTTPS?',
12
- choices: [
13
- { title: 'SSH', value: 'ssh' },
14
- { title: 'HTTPS', value: 'https' }
15
- ]
7
+ export default async function ui(name, { branch, ...options } = {}) {
8
+ const autoYes = isAutoYes(options)
9
+ let clone = normalizeEnum(options.clone, ['ssh', 'https'])
10
+
11
+ if (!clone) {
12
+ if (autoYes) {
13
+ clone = 'https'
14
+ } else {
15
+ const response = await prompts([
16
+ {
17
+ type: 'select',
18
+ name: 'clone',
19
+ message: 'Clone with SSH or HTTPS?',
20
+ choices: [
21
+ { title: 'SSH', value: 'ssh' },
22
+ { title: 'HTTPS', value: 'https' }
23
+ ]
24
+ }
25
+ ])
26
+
27
+ clone = response.clone
16
28
  }
17
- ])
29
+ }
18
30
 
19
31
  let url = ''
20
32
 
@@ -30,43 +42,71 @@ export default async function ui(name, { branch }) {
30
42
 
31
43
  fse.removeSync(gitPath)
32
44
 
33
- const { git } = await prompts([
34
- {
35
- type: 'select',
36
- name: 'git',
37
- message: 'Init as git repository?',
38
- choices: [
39
- { title: 'yes', value: 'yes' },
40
- { title: 'no', value: 'no' }
41
- ]
45
+ let git = normalizeYesNo(options.git)
46
+
47
+ if (!git) {
48
+ if (autoYes) {
49
+ git = 'no'
50
+ } else {
51
+ const response = await prompts([
52
+ {
53
+ type: 'select',
54
+ name: 'git',
55
+ message: 'Init as git repository?',
56
+ choices: [
57
+ { title: 'yes', value: 'yes' },
58
+ { title: 'no', value: 'no' }
59
+ ]
60
+ }
61
+ ])
62
+
63
+ git = response.git
42
64
  }
43
- ])
65
+ }
44
66
 
45
67
  if (git === 'yes') {
46
68
  execSync(`git init ${name || ''}`)
47
69
 
48
- const { remote } = await prompts([
49
- {
50
- type: 'text',
51
- name: 'remote',
52
- message: 'Set remote for git repository'
53
- }
54
- ])
70
+ let remote = (typeof options.remote === 'string' && options.remote.trim()) ? options.remote.trim() : undefined
71
+
72
+ if (!remote && !autoYes) {
73
+ const response = await prompts([
74
+ {
75
+ type: 'text',
76
+ name: 'remote',
77
+ message: 'Set remote for git repository'
78
+ }
79
+ ])
55
80
 
56
- execSync(`cd ${name || '.'} && git remote add origin ${remote} ${name ? '&& cd ..' : ''}`)
81
+ remote = response.remote
82
+ }
83
+
84
+ if (remote) {
85
+ execSync(`cd ${name || '.'} && git remote add origin ${remote} ${name ? '&& cd ..' : ''}`)
86
+ }
57
87
  }
58
88
 
59
- const { install } = await prompts([
60
- {
61
- type: 'select',
62
- name: 'install',
63
- message: 'Install project?',
64
- choices: [
65
- { title: 'yes', value: 'yes' },
66
- { title: 'no', value: 'no' }
67
- ]
89
+ let install = normalizeYesNo(options.install)
90
+
91
+ if (!install) {
92
+ if (autoYes) {
93
+ install = 'yes'
94
+ } else {
95
+ const response = await prompts([
96
+ {
97
+ type: 'select',
98
+ name: 'install',
99
+ message: 'Install project?',
100
+ choices: [
101
+ { title: 'yes', value: 'yes' },
102
+ { title: 'no', value: 'no' }
103
+ ]
104
+ }
105
+ ])
106
+
107
+ install = response.install
68
108
  }
69
- ])
109
+ }
70
110
 
71
111
  if (install === 'yes') {
72
112
  execSync(`cd ${name || '.'} && npm i`)