@neurodevs/ndx-cli 0.1.32 → 0.1.33

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.
@@ -0,0 +1,94 @@
1
+ import os from 'os'
2
+ import path from 'path'
3
+ import { NpmAutopackage } from '@neurodevs/meta-node'
4
+ import CliCommandRunner from '../CliCommandRunner'
5
+
6
+ export default class CreatePackageCommand {
7
+ private packageName!: string
8
+ private description!: string
9
+ private keywords!: string[]
10
+
11
+ public constructor() {}
12
+
13
+ public async run() {
14
+ const { packageName, description, keywords } =
15
+ await this.promptForAutopackage()
16
+
17
+ this.packageName = packageName
18
+ this.description = description
19
+ this.keywords = keywords
20
+
21
+ if (!this.userInputExistsForCreatePackage) {
22
+ return
23
+ }
24
+
25
+ const autopackage = this.NpmAutopackage()
26
+ await autopackage.run()
27
+ }
28
+
29
+ private async promptForAutopackage() {
30
+ return await this.prompts([
31
+ {
32
+ type: 'text',
33
+ name: 'packageName',
34
+ message: this.packageNameMessage,
35
+ },
36
+ {
37
+ type: 'text',
38
+ name: 'description',
39
+ message: this.descriptionMessage,
40
+ },
41
+ {
42
+ type: 'text',
43
+ name: 'keywords',
44
+ message: this.keywordsMessage,
45
+ initial: '',
46
+ format: (value) =>
47
+ value ? this.splitOnCommaOrWhitespace(value) : [],
48
+ },
49
+ ])
50
+ }
51
+
52
+ private readonly packageNameMessage =
53
+ 'What should the package be called? Example: useful-package'
54
+
55
+ private readonly descriptionMessage =
56
+ 'What should the package description be? Example: A useful package.'
57
+
58
+ private readonly keywordsMessage =
59
+ 'Enter keywords (comma or space separated, lowercase, optional):'
60
+
61
+ private splitOnCommaOrWhitespace(value: string) {
62
+ return value
63
+ .split(/[\s,]+/)
64
+ .map((v: string) => v.trim())
65
+ .filter(Boolean)
66
+ }
67
+
68
+ private get userInputExistsForCreatePackage() {
69
+ return this.packageName && this.description
70
+ }
71
+
72
+ private expandHomeDir(inputPath: string): string {
73
+ return inputPath.startsWith('~')
74
+ ? path.join(os.homedir(), inputPath.slice(1))
75
+ : inputPath
76
+ }
77
+
78
+ private get prompts() {
79
+ return CliCommandRunner.prompts
80
+ }
81
+
82
+ private NpmAutopackage() {
83
+ return NpmAutopackage.Create({
84
+ name: this.packageName,
85
+ description: this.description,
86
+ keywords: ['nodejs', 'typescript', 'tdd', ...this.keywords],
87
+ gitNamespace: 'neurodevs',
88
+ npmNamespace: 'neurodevs',
89
+ installDir: this.expandHomeDir('~/dev'),
90
+ license: 'MIT',
91
+ author: 'Eric Yates <hello@ericthecurious.com>',
92
+ })
93
+ }
94
+ }
@@ -0,0 +1,243 @@
1
+ import { UiAutomodule } from '@neurodevs/meta-node'
2
+ import CliCommandRunner from '../CliCommandRunner'
3
+
4
+ export default class CreateUiCommand {
5
+ private componentName!: string
6
+
7
+ public constructor() {}
8
+
9
+ public async run() {
10
+ await this.installDependenciesIfNeeded()
11
+
12
+ const { componentName } = await this.promptForUimodule()
13
+
14
+ this.componentName = componentName
15
+
16
+ if (!this.componentName) {
17
+ return
18
+ }
19
+
20
+ await this.makeRequiredUiDirectories()
21
+
22
+ const instance = this.UiAutomodule()
23
+ await instance.run()
24
+ }
25
+
26
+ private async installDependenciesIfNeeded() {
27
+ const isInstalled = await this.checkIfDependenciesAreInstalled()
28
+
29
+ if (!isInstalled) {
30
+ const { shouldInstall } = await this.promptForInstallDependencies()
31
+
32
+ if (shouldInstall) {
33
+ await this.installReactDependencies()
34
+ await this.updateTsconfigForReact()
35
+ await this.createSetupTestsFile()
36
+ await this.addSetupTestsToPackageJson()
37
+ await this.recompileForTsxFiles()
38
+ }
39
+ }
40
+ }
41
+
42
+ private async checkIfDependenciesAreInstalled() {
43
+ const original = await this.loadPackageJson()
44
+ const parsed = JSON.parse(original)
45
+
46
+ const dependencies = Object.keys(parsed.dependencies ?? {})
47
+
48
+ const areDepsInstalled = this.requiredDependencies.every((dep) =>
49
+ dependencies.includes(dep)
50
+ )
51
+
52
+ const devDependencies = Object.keys(parsed.devDependencies ?? {})
53
+
54
+ const areDevDepsInstalled = this.requiredDevDependencies.every((dep) =>
55
+ devDependencies.includes(dep)
56
+ )
57
+
58
+ return areDepsInstalled && areDevDepsInstalled
59
+ }
60
+
61
+ private async loadPackageJson() {
62
+ return await this.readFile(this.packageJsonPath, 'utf-8')
63
+ }
64
+
65
+ private readonly packageJsonPath = 'package.json'
66
+
67
+ private readonly requiredDependencies = ['react', 'react-dom']
68
+
69
+ private readonly requiredDevDependencies = [
70
+ '@types/react',
71
+ '@types/react-dom',
72
+ '@types/jsdom',
73
+ '@testing-library/react',
74
+ '@testing-library/dom',
75
+ '@testing-library/jest-dom',
76
+ 'jsdom',
77
+ ]
78
+
79
+ private async promptForInstallDependencies() {
80
+ return await this.prompts([
81
+ {
82
+ type: 'confirm',
83
+ name: 'shouldInstall',
84
+ message:
85
+ 'Some required dependencies are missing! Press Enter to install, or any other key to abort.',
86
+ initial: true,
87
+ },
88
+ ])
89
+ }
90
+
91
+ private async installReactDependencies() {
92
+ await this.installDependencies()
93
+ await this.installDevDependencies()
94
+ }
95
+
96
+ private async installDependencies() {
97
+ console.log('Installing required dependencies...')
98
+ await this.exec('yarn add react react-dom')
99
+ }
100
+
101
+ private async installDevDependencies() {
102
+ console.log('Installing required dev dependencies...')
103
+ await this.exec(
104
+ 'yarn add -D @types/react @types/react-dom @types/jsdom @testing-library/react @testing-library/dom @testing-library/jest-dom jsdom'
105
+ )
106
+ }
107
+
108
+ private async updateTsconfigForReact() {
109
+ console.log('Updating tsconfig.json for React...')
110
+
111
+ const original = await this.loadTsconfig()
112
+ const parsed = JSON.parse(original)
113
+
114
+ const updated = JSON.stringify(
115
+ {
116
+ ...parsed,
117
+ compilerOptions: {
118
+ jsx: 'react-jsx',
119
+ ...parsed.compilerOptions,
120
+ },
121
+ include: ['src'],
122
+ },
123
+ null,
124
+ 4
125
+ )
126
+
127
+ await this.writeFile(this.tsconfigPath, updated)
128
+ }
129
+
130
+ private async loadTsconfig() {
131
+ return await this.readFile(this.tsconfigPath, 'utf-8')
132
+ }
133
+
134
+ private readonly tsconfigPath = 'tsconfig.json'
135
+
136
+ private async createSetupTestsFile() {
137
+ console.log('Creating src/setupTests.ts...')
138
+ await this.writeFile('src/__tests__/setupTests.ts', this.setupTestsFile)
139
+ }
140
+
141
+ private async addSetupTestsToPackageJson() {
142
+ console.log('Adding setupTests.ts to package.json...')
143
+
144
+ const original = await this.loadPackageJson()
145
+ const parsed = JSON.parse(original)
146
+
147
+ const updated = JSON.stringify(
148
+ {
149
+ ...parsed,
150
+ jest: {
151
+ ...parsed.jest,
152
+ setupFiles: ['<rootDir>/build/__tests__/setupTests.js'],
153
+ },
154
+ },
155
+ null,
156
+ 4
157
+ )
158
+
159
+ await this.writeFile(this.packageJsonPath, updated)
160
+ }
161
+
162
+ private async recompileForTsxFiles() {
163
+ console.log('Recompiling project for .tsx files...')
164
+ await this.exec('npx tsc')
165
+ }
166
+
167
+ private async promptForUimodule() {
168
+ return await this.prompts([
169
+ {
170
+ type: 'text',
171
+ name: 'componentName',
172
+ message: this.componentNameMessage,
173
+ },
174
+ ])
175
+ }
176
+
177
+ private readonly componentNameMessage =
178
+ 'What should the component be called? Example: YourComponent'
179
+
180
+ private async makeRequiredUiDirectories() {
181
+ await this.mkdir(this.uiTestSaveDir, { recursive: true })
182
+ await this.mkdir(this.uiModuleSaveDir, { recursive: true })
183
+ await this.mkdir(this.uiFakeSaveDir, { recursive: true })
184
+ }
185
+
186
+ private readonly uiTestSaveDir = 'src/__tests__/ui'
187
+ private readonly uiModuleSaveDir = 'src/ui'
188
+
189
+ private get uiFakeSaveDir() {
190
+ return `src/testDoubles/${this.componentName}`
191
+ }
192
+
193
+ private get exec() {
194
+ return CliCommandRunner.exec
195
+ }
196
+
197
+ private get mkdir() {
198
+ return CliCommandRunner.mkdir
199
+ }
200
+
201
+ private get prompts() {
202
+ return CliCommandRunner.prompts
203
+ }
204
+
205
+ private get readFile() {
206
+ return CliCommandRunner.readFile
207
+ }
208
+
209
+ private get writeFile() {
210
+ return CliCommandRunner.writeFile
211
+ }
212
+
213
+ private readonly setupTestsFile = `
214
+ import { JSDOM } from 'jsdom'
215
+
216
+ const jsdom = new JSDOM('<!doctype html><html><body></body></html>', {
217
+ url: 'http://localhost',
218
+ })
219
+
220
+ global.window = jsdom.window as unknown as Window & typeof globalThis
221
+ global.document = jsdom.window.document
222
+ global.navigator = jsdom.window.navigator
223
+ global.HTMLElement = jsdom.window.HTMLElement
224
+ global.getComputedStyle = jsdom.window.getComputedStyle
225
+
226
+ global.ResizeObserver = class {
227
+ public observe() {}
228
+ public unobserve() {}
229
+ public disconnect() {}
230
+ }
231
+
232
+ global.SVGElement = jsdom.window.SVGElement
233
+ `
234
+
235
+ private UiAutomodule() {
236
+ return UiAutomodule.Create({
237
+ testSaveDir: this.uiTestSaveDir,
238
+ moduleSaveDir: this.uiModuleSaveDir,
239
+ fakeSaveDir: this.uiFakeSaveDir,
240
+ componentName: this.componentName,
241
+ })
242
+ }
243
+ }
@@ -0,0 +1,58 @@
1
+ import os from 'os'
2
+ import path from 'path'
3
+ import { NpmAutopackage } from '@neurodevs/meta-node'
4
+ import CliCommandRunner from '../CliCommandRunner'
5
+
6
+ export default class UpgradePackageCommand {
7
+ private packageName!: string
8
+ private description!: string
9
+ private keywords!: string[]
10
+
11
+ public constructor() {}
12
+
13
+ public async run() {
14
+ await this.loadInfoFromPackageJson()
15
+
16
+ const autopackage = this.NpmAutopackage()
17
+ await autopackage.run()
18
+ }
19
+
20
+ private async loadInfoFromPackageJson() {
21
+ const raw = await this.readFile('package.json', 'utf-8')
22
+ const { name, description, keywords } = JSON.parse(raw)
23
+
24
+ this.packageName = name
25
+ this.description = description
26
+
27
+ this.keywords = this.defaultKeywords.every((keyword) =>
28
+ keywords?.includes(keyword)
29
+ )
30
+ ? keywords
31
+ : [...this.defaultKeywords, ...(keywords || [])]
32
+ }
33
+
34
+ private readonly defaultKeywords = ['nodejs', 'typescript', 'tdd']
35
+
36
+ private expandHomeDir(inputPath: string): string {
37
+ return inputPath.startsWith('~')
38
+ ? path.join(os.homedir(), inputPath.slice(1))
39
+ : inputPath
40
+ }
41
+
42
+ private get readFile() {
43
+ return CliCommandRunner.readFile
44
+ }
45
+
46
+ private NpmAutopackage() {
47
+ return NpmAutopackage.Create({
48
+ name: this.packageName,
49
+ description: this.description,
50
+ keywords: this.keywords,
51
+ gitNamespace: 'neurodevs',
52
+ npmNamespace: 'neurodevs',
53
+ installDir: this.expandHomeDir('~/dev'),
54
+ license: 'MIT',
55
+ author: 'Eric Yates <hello@ericthecurious.com>',
56
+ })
57
+ }
58
+ }