@sprucelabs/spruce-cli 14.26.18 → 14.28.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.
Files changed (118) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/build/.spruce/errors/errors.types.d.ts +19 -0
  3. package/build/.spruce/errors/options.types.d.ts +4 -1
  4. package/build/.spruce/errors/spruceCli/dependencyExists.schema.d.ts +3 -0
  5. package/build/.spruce/errors/spruceCli/dependencyExists.schema.js +28 -0
  6. package/build/.spruce/errors/spruceCli/dependencyExists.schema.js.map +1 -0
  7. package/build/__tests__/behavioral/AddingADependency.test.d.ts +6 -3
  8. package/build/__tests__/behavioral/AddingADependency.test.js +116 -44
  9. package/build/__tests__/behavioral/AddingADependency.test.js.map +1 -1
  10. package/build/__tests__/behavioral/InstallingASkillAtAnOrg.test.js +2 -2
  11. package/build/__tests__/behavioral/InstallingASkillAtAnOrg.test.js.map +1 -1
  12. package/build/__tests__/behavioral/LoggingInAsASkill.test.js +4 -4
  13. package/build/__tests__/behavioral/LoggingInAsASkill.test.js.map +1 -1
  14. package/build/__tests__/behavioral/ManagingDependencies.test.js +3 -3
  15. package/build/__tests__/behavioral/ManagingDependencies.test.js.map +1 -1
  16. package/build/__tests__/behavioral/RememberingUpgradeSelections.test.js +1 -1
  17. package/build/__tests__/behavioral/RememberingUpgradeSelections.test.js.map +1 -1
  18. package/build/__tests__/behavioral/SettingRemote.test.js +1 -1
  19. package/build/__tests__/behavioral/SettingRemote.test.js.map +1 -1
  20. package/build/__tests__/behavioral/SettingUpVscode.test.js +3 -3
  21. package/build/__tests__/behavioral/SettingUpVscode.test.js.map +1 -1
  22. package/build/__tests__/behavioral/TestingDataStores.test.js +2 -2
  23. package/build/__tests__/behavioral/TestingDataStores.test.js.map +1 -1
  24. package/build/__tests__/behavioral/UpgradingANodeModule.test.d.ts +1 -0
  25. package/build/__tests__/behavioral/UpgradingANodeModule.test.js +50 -3
  26. package/build/__tests__/behavioral/UpgradingANodeModule.test.js.map +1 -1
  27. package/build/__tests__/behavioral/events/CreatingAListener.test.js +2 -2
  28. package/build/__tests__/behavioral/events/CreatingAListener.test.js.map +1 -1
  29. package/build/__tests__/behavioral/events/CreatingAnEvent.test.d.ts +3 -2
  30. package/build/__tests__/behavioral/events/CreatingAnEvent.test.js +156 -106
  31. package/build/__tests__/behavioral/events/CreatingAnEvent.test.js.map +1 -1
  32. package/build/__tests__/behavioral/events/KeepingEventsInSync2.test.js +6 -9
  33. package/build/__tests__/behavioral/events/KeepingEventsInSync2.test.js.map +1 -1
  34. package/build/__tests__/behavioral/schemas/KeepingSchemasInSync.test.js +7 -4
  35. package/build/__tests__/behavioral/schemas/KeepingSchemasInSync.test.js.map +1 -1
  36. package/build/__tests__/behavioral/skill/UpgradingASkill.test.js +7 -7
  37. package/build/__tests__/behavioral/skill/UpgradingASkill.test.js.map +1 -1
  38. package/build/__tests__/behavioral/skill/UpgradingASkill3.test.js +2 -2
  39. package/build/__tests__/behavioral/skill/UpgradingASkill3.test.js.map +1 -1
  40. package/build/__tests__/behavioral/tests/SelectingAnAbstractTestClass.test.js +1 -1
  41. package/build/__tests__/behavioral/tests/SelectingAnAbstractTestClass.test.js.map +1 -1
  42. package/build/__tests__/behavioral/views/CreatingASkillView.test.js +3 -3
  43. package/build/__tests__/behavioral/views/CreatingASkillView.test.js.map +1 -1
  44. package/build/__tests__/behavioral/views/TestingViewControllers.test.js +2 -2
  45. package/build/__tests__/behavioral/views/TestingViewControllers.test.js.map +1 -1
  46. package/build/__tests__/implementation/ActionExecuter2.test.js +3 -3
  47. package/build/__tests__/implementation/ActionExecuter2.test.js.map +1 -1
  48. package/build/errors/SpruceError.js +4 -0
  49. package/build/errors/SpruceError.js.map +1 -1
  50. package/build/errors/dependencyExists.builder.d.ts +11 -0
  51. package/build/errors/dependencyExists.builder.js +22 -0
  52. package/build/errors/dependencyExists.builder.js.map +1 -0
  53. package/build/features/AbstractAction.js +1 -1
  54. package/build/features/AbstractAction.js.map +1 -1
  55. package/build/features/dependencies/actions/AddAction.js +22 -15
  56. package/build/features/dependencies/actions/AddAction.js.map +1 -1
  57. package/build/features/event/actions/CreateAction.js +1 -1
  58. package/build/features/event/actions/CreateAction.js.map +1 -1
  59. package/build/features/node/NodeFeature.d.ts +1 -0
  60. package/build/features/node/NodeFeature.js +83 -64
  61. package/build/features/node/NodeFeature.js.map +1 -1
  62. package/build/features/node/actions/UpgradeAction.js +20 -9
  63. package/build/features/node/actions/UpgradeAction.js.map +1 -1
  64. package/build/features/node/writers/NodeWriter.d.ts +5 -2
  65. package/build/features/node/writers/NodeWriter.js +37 -22
  66. package/build/features/node/writers/NodeWriter.js.map +1 -1
  67. package/build/features/skill/updaters/Updater.js +3 -1
  68. package/build/features/skill/updaters/Updater.js.map +1 -1
  69. package/build/interfaces/SpyInterface.d.ts +1 -1
  70. package/build/interfaces/SpyInterface.js +2 -2
  71. package/build/interfaces/SpyInterface.js.map +1 -1
  72. package/build/services/DependencyService.d.ts +1 -0
  73. package/build/services/DependencyService.js +18 -0
  74. package/build/services/DependencyService.js.map +1 -1
  75. package/build/tests/AbstractCliTest.js +1 -1
  76. package/build/tests/AbstractCliTest.js.map +1 -1
  77. package/build/tests/utilities/uiAssert.utility.d.ts +7 -0
  78. package/build/tests/utilities/uiAssert.utility.js +85 -0
  79. package/build/tests/utilities/uiAssert.utility.js.map +1 -0
  80. package/build/writers/AbstractWriter.d.ts +10 -10
  81. package/build/writers/AbstractWriter.js.map +1 -1
  82. package/package.json +34 -32
  83. package/src/.spruce/errors/errors.types.ts +29 -0
  84. package/src/.spruce/errors/options.types.ts +4 -1
  85. package/src/.spruce/errors/spruceCli/dependencyExists.schema.ts +22 -0
  86. package/src/__tests__/behavioral/AddingADependency.test.ts +45 -8
  87. package/src/__tests__/behavioral/InstallingASkillAtAnOrg.test.ts +3 -3
  88. package/src/__tests__/behavioral/LoggingInAsASkill.test.ts +4 -4
  89. package/src/__tests__/behavioral/ManagingDependencies.test.ts +3 -3
  90. package/src/__tests__/behavioral/RememberingUpgradeSelections.test.ts +1 -1
  91. package/src/__tests__/behavioral/SettingRemote.test.ts +1 -1
  92. package/src/__tests__/behavioral/SettingUpVscode.test.ts +3 -3
  93. package/src/__tests__/behavioral/TestingDataStores.test.ts +2 -2
  94. package/src/__tests__/behavioral/UpgradingANodeModule.test.ts +24 -0
  95. package/src/__tests__/behavioral/events/CreatingAListener.test.ts +2 -2
  96. package/src/__tests__/behavioral/events/CreatingAnEvent.test.ts +44 -22
  97. package/src/__tests__/behavioral/events/KeepingEventsInSync2.test.ts +1 -1
  98. package/src/__tests__/behavioral/schemas/KeepingSchemasInSync.test.ts +2 -0
  99. package/src/__tests__/behavioral/skill/UpgradingASkill.test.ts +7 -7
  100. package/src/__tests__/behavioral/skill/UpgradingASkill3.test.ts +2 -2
  101. package/src/__tests__/behavioral/tests/SelectingAnAbstractTestClass.test.ts +1 -1
  102. package/src/__tests__/behavioral/views/CreatingASkillView.test.ts +3 -3
  103. package/src/__tests__/behavioral/views/TestingViewControllers.test.ts +2 -2
  104. package/src/__tests__/implementation/ActionExecuter2.test.ts +3 -3
  105. package/src/errors/SpruceError.ts +4 -0
  106. package/src/errors/dependencyExists.builder.ts +12 -0
  107. package/src/features/AbstractAction.ts +1 -4
  108. package/src/features/dependencies/actions/AddAction.ts +12 -5
  109. package/src/features/event/actions/CreateAction.ts +4 -1
  110. package/src/features/node/NodeFeature.ts +31 -25
  111. package/src/features/node/actions/UpgradeAction.ts +8 -1
  112. package/src/features/node/writers/NodeWriter.ts +38 -23
  113. package/src/features/skill/updaters/Updater.ts +2 -1
  114. package/src/interfaces/SpyInterface.ts +1 -1
  115. package/src/services/DependencyService.ts +14 -0
  116. package/src/tests/AbstractCliTest.ts +1 -1
  117. package/src/tests/utilities/uiAssert.utility.ts +45 -0
  118. package/src/writers/AbstractWriter.ts +15 -10
@@ -117,7 +117,7 @@ export default class UpgradingASkillTest extends AbstractCliTest {
117
117
 
118
118
  await this.assertFailedHealthCheck(cli)
119
119
 
120
- const last = this.ui.lastInvocation()
120
+ const last = this.ui.getLastInvocation()
121
121
 
122
122
  assert.doesInclude(last, {
123
123
  'options.options.choices[].value': FILE_ACTION_OVERWRITE,
@@ -251,7 +251,7 @@ export default class UpgradingASkillTest extends AbstractCliTest {
251
251
 
252
252
  await this.waitForInput()
253
253
 
254
- const last = this.ui.lastInvocation()
254
+ const last = this.ui.getLastInvocation()
255
255
 
256
256
  assert.isEqual(last.command, 'prompt')
257
257
  assert.doesInclude(last.options.options.choices, { value: 'skip' })
@@ -277,14 +277,14 @@ export default class UpgradingASkillTest extends AbstractCliTest {
277
277
 
278
278
  await this.waitForInput()
279
279
 
280
- let last = this.ui.lastInvocation()
280
+ let last = this.ui.getLastInvocation()
281
281
 
282
282
  assert.isEqual(last.command, 'prompt')
283
283
  await this.ui.sendInput('skip')
284
284
 
285
285
  await this.waitForInput()
286
286
 
287
- last = this.ui.lastInvocation()
287
+ last = this.ui.getLastInvocation()
288
288
 
289
289
  assert.isEqual(last.command, 'prompt')
290
290
  await this.ui.sendInput('skip')
@@ -307,7 +307,7 @@ export default class UpgradingASkillTest extends AbstractCliTest {
307
307
 
308
308
  await this.waitForInput()
309
309
 
310
- let last = this.ui.lastInvocation()
310
+ let last = this.ui.getLastInvocation()
311
311
 
312
312
  assert.isEqual(last.command, 'prompt')
313
313
  await this.ui.sendInput('skipAll')
@@ -329,7 +329,7 @@ export default class UpgradingASkillTest extends AbstractCliTest {
329
329
 
330
330
  await this.waitForInput()
331
331
 
332
- let last = this.ui.lastInvocation()
332
+ let last = this.ui.getLastInvocation()
333
333
 
334
334
  assert.isEqual(last.command, 'prompt')
335
335
  await this.ui.sendInput('overwrite')
@@ -431,7 +431,7 @@ export default class UpgradingASkillTest extends AbstractCliTest {
431
431
 
432
432
  await this.waitForInput()
433
433
 
434
- const last = this.ui.lastInvocation()
434
+ const last = this.ui.getLastInvocation()
435
435
  return { last, promise }
436
436
  }
437
437
 
@@ -96,12 +96,12 @@ export default class UpgradingASkillTest extends AbstractCliTest {
96
96
 
97
97
  await this.waitForInput()
98
98
 
99
- let last = this.ui.lastInvocation()
99
+ let last = this.ui.getLastInvocation()
100
100
 
101
101
  assert.isEqual(last.command, 'prompt')
102
102
  await this.ui.sendInput('overwrite')
103
103
 
104
- last = this.ui.lastInvocation()
104
+ last = this.ui.getLastInvocation()
105
105
 
106
106
  assert.isEqual(last.command, 'prompt')
107
107
  await this.ui.sendInput('overwrite')
@@ -185,7 +185,7 @@ export default class SelectingAnAbstractTestClassTest extends AbstractTestTest {
185
185
 
186
186
  await this.waitForInput()
187
187
 
188
- const last = this.ui.lastInvocation()
188
+ const last = this.ui.getLastInvocation()
189
189
  const { choices } = last.options.options ?? {}
190
190
 
191
191
  return { promise, choices } as {
@@ -30,7 +30,7 @@ export default class CreatingASkillViewTest extends AbstractSkillTest {
30
30
 
31
31
  await this.waitForInput()
32
32
 
33
- const last = this.ui.lastInvocation()
33
+ const last = this.ui.getLastInvocation()
34
34
 
35
35
  assert.isEqual(last.command, 'confirm')
36
36
 
@@ -90,7 +90,7 @@ export default class CreatingASkillViewTest extends AbstractSkillTest {
90
90
 
91
91
  await this.waitForInput()
92
92
 
93
- let last = this.ui.lastInvocation()
93
+ let last = this.ui.getLastInvocation()
94
94
 
95
95
  assert.isEqual(last.command, 'prompt')
96
96
 
@@ -120,7 +120,7 @@ export default class CreatingASkillViewTest extends AbstractSkillTest {
120
120
 
121
121
  await this.waitForInput()
122
122
 
123
- const last = this.ui.lastInvocation()
123
+ const last = this.ui.getLastInvocation()
124
124
 
125
125
  assert.isEqual(last.command, 'prompt')
126
126
  assert.doesInclude(last.options.options.choices, {
@@ -15,7 +15,7 @@ export default class TestingViewControllersTest extends AbstractSkillTest {
15
15
 
16
16
  await this.waitForInput()
17
17
 
18
- const last = this.ui.lastInvocation()
18
+ const last = this.ui.getLastInvocation()
19
19
  assert.doesInclude(last.options.options.choices, {
20
20
  label: 'AbstractViewControllerTest (requires install)',
21
21
  })
@@ -29,7 +29,7 @@ export default class TestingViewControllersTest extends AbstractSkillTest {
29
29
 
30
30
  await this.waitForInput()
31
31
 
32
- const last = this.ui.lastInvocation()
32
+ const last = this.ui.getLastInvocation()
33
33
  assert.doesInclude(last.options.options.choices, {
34
34
  label: 'AbstractViewControllerTest',
35
35
  })
@@ -107,7 +107,7 @@ export default class FeatureCommandExecuterContTest extends AbstractSchemaTest {
107
107
 
108
108
  await this.waitForInput()
109
109
 
110
- assert.doesNotInclude(this.ui.lastInvocation(), {
110
+ assert.doesNotInclude(this.ui.getLastInvocation(), {
111
111
  command: 'prompt',
112
112
  options: {
113
113
  type: 'select',
@@ -158,7 +158,7 @@ export default class FeatureCommandExecuterContTest extends AbstractSchemaTest {
158
158
  await this.waitForInput()
159
159
 
160
160
  this.ui.reset()
161
- const lastQuestion = this.ui.lastInvocation()
161
+ const lastQuestion = this.ui.getLastInvocation()
162
162
 
163
163
  assert.isEqual(lastQuestion.command, 'prompt')
164
164
  assert.doesInclude(
@@ -227,7 +227,7 @@ export default class FeatureCommandExecuterContTest extends AbstractSchemaTest {
227
227
  assert.doesInclude(message, /2 required/gi)
228
228
  assert.doesInclude(message, /1 optional/gi)
229
229
 
230
- assert.doesInclude(this.ui.lastInvocation(), {
230
+ assert.doesInclude(this.ui.getLastInvocation(), {
231
231
  command: 'prompt',
232
232
  options: {
233
233
  type: 'select',
@@ -252,6 +252,10 @@ export default class SpruceError extends AbstractSpruceError<ErrorOptions> {
252
252
  message = `A transport named '${options.name}' already exists!`
253
253
  break
254
254
 
255
+ case 'DEPENDENCY_EXISTS':
256
+ message = `You already have ${options.namespace} as a dependency!`
257
+ break
258
+
255
259
  default:
256
260
  message = super.friendlyMessage()
257
261
  }
@@ -0,0 +1,12 @@
1
+ import { buildErrorSchema } from '@sprucelabs/schema'
2
+
3
+ export default buildErrorSchema({
4
+ id: 'dependencyExists',
5
+ name: 'Dependency exists',
6
+ fields: {
7
+ namespace: {
8
+ type: 'text',
9
+ isRequired: true,
10
+ },
11
+ },
12
+ })
@@ -1,6 +1,5 @@
1
1
  import { Schema, SchemaValues, SchemaPartialValues } from '@sprucelabs/schema'
2
2
  import { versionUtil } from '@sprucelabs/spruce-skill-utils'
3
- import { diskUtil } from '@sprucelabs/spruce-skill-utils'
4
3
  import { Templates } from '@sprucelabs/spruce-templates'
5
4
  import { GlobalEmitter } from '../GlobalEmitter'
6
5
  import ServiceFactory, {
@@ -132,9 +131,7 @@ export default abstract class AbstractAction<S extends Schema = Schema>
132
131
  resolvedDestination: string,
133
132
  fallbackVersion: string
134
133
  ) {
135
- const versions = diskUtil.doesDirExist(resolvedDestination)
136
- ? versionUtil.getAllVersions(resolvedDestination)
137
- : []
134
+ const versions = versionUtil.getAllVersions(resolvedDestination)
138
135
  const todaysVersion = versionUtil.generateVersion()
139
136
 
140
137
  let version = fallbackVersion
@@ -27,17 +27,24 @@ export default class DeployAction extends AbstractAction<OptionsSchema> {
27
27
  let { namespace } = this.validateAndNormalizeOptions(options)
28
28
 
29
29
  const skills = await this.Store('skill').fetchAllSkills()
30
+ const dependencyService = this.Service('dependency')
30
31
 
31
32
  if (!namespace) {
33
+ const dependencies = dependencyService.get().map((d) => d.namespace)
34
+
35
+ const choices = skills
36
+ .filter((s) => dependencies.indexOf(s.slug) === -1)
37
+ .map((s) => ({
38
+ value: s.slug,
39
+ label: s.name,
40
+ }))
41
+
32
42
  namespace = await this.ui.prompt({
33
43
  type: 'select',
34
44
  label: 'Which skill would you like to add as a dependency?',
35
45
  isRequired: true,
36
46
  options: {
37
- choices: skills.map((s) => ({
38
- value: s.slug,
39
- label: s.name,
40
- })),
47
+ choices,
41
48
  },
42
49
  })
43
50
  }
@@ -51,7 +58,7 @@ export default class DeployAction extends AbstractAction<OptionsSchema> {
51
58
  })
52
59
  }
53
60
 
54
- this.Service('dependency').add({
61
+ dependencyService.add({
55
62
  id: skill.id,
56
63
  namespace: skill.slug,
57
64
  })
@@ -68,7 +68,10 @@ export default class CreateAction extends AbstractAction<OptionsSchema> {
68
68
 
69
69
  try {
70
70
  const destinationDir = diskUtil.resolvePath(this.cwd, 'src', 'events')
71
- const resolvedVersion = await this.resolveVersion(version, destinationDir)
71
+ const resolvedVersion = await this.resolveVersion(
72
+ version,
73
+ diskUtil.resolvePath(destinationDir, '**', '*')
74
+ )
72
75
 
73
76
  const files = await this.Writer('event').writeEvent(destinationDir, {
74
77
  nameKebab,
@@ -5,7 +5,11 @@ import nodeFeatureOptionsSchema from '#spruce/schemas/spruceCli/v2020_07_22/node
5
5
  import { FileDescription, GeneratedFile } from '../../types/cli.types'
6
6
  import ScriptUpdater from '../../updaters/ScriptUpdater'
7
7
  import AbstractFeature, { FeatureDependency } from '../AbstractFeature'
8
- import { ActionOptions, FeatureCode } from '../features.types'
8
+ import {
9
+ ActionOptions,
10
+ FeatureActionResponse,
11
+ FeatureCode,
12
+ } from '../features.types'
9
13
  import universalDevDependencies from '../universalDevDependencies'
10
14
  import universalFileDescriptions from '../universalFileDescriptions'
11
15
  import universalScripts from '../universalScripts'
@@ -49,33 +53,36 @@ export default class NodeFeature<
49
53
 
50
54
  void this.emitter.on('feature.did-execute', async (payload) => {
51
55
  if (payload.featureCode === 'node' && payload.actionCode === 'upgrade') {
52
- try {
53
- this.ui.startLoading('Cleaning build...')
54
- await this.Service('command').execute('yarn clean.build')
55
-
56
- this.ui.startLoading('Applying lint rules to all files...')
57
- await this.Service('command').execute('yarn fix.lint')
58
-
59
- this.ui.startLoading('Rebuilding...')
60
- await this.Service('command').execute('yarn build.dev')
61
-
62
- return {
63
- summaryLines: [
64
- 'Build cleared.',
65
- 'Lint rules applied to source.',
66
- 'Code rebuilt successfully.',
67
- ],
68
- }
69
- } catch (err) {
70
- return {
71
- errors: [err],
72
- }
73
- }
56
+ return this.handleUpgrade()
74
57
  }
75
58
 
76
59
  return {}
77
60
  })
78
61
  }
62
+ private async handleUpgrade(): Promise<FeatureActionResponse> {
63
+ try {
64
+ this.ui.startLoading('Cleaning build...')
65
+ await this.Service('command').execute('yarn clean.build')
66
+
67
+ this.ui.startLoading('Applying lint rules to all files...')
68
+ await this.Service('command').execute('yarn fix.lint')
69
+
70
+ this.ui.startLoading('Rebuilding...')
71
+ await this.Service('command').execute('yarn build.dev')
72
+
73
+ return {
74
+ summaryLines: [
75
+ 'Build cleared.',
76
+ 'Lint rules applied to source.',
77
+ 'Code rebuilt successfully.',
78
+ ],
79
+ }
80
+ } catch (err) {
81
+ return {
82
+ errors: [err],
83
+ }
84
+ }
85
+ }
79
86
 
80
87
  public async beforePackageInstall(options: Options) {
81
88
  const destination = this.resolveDestination(options)
@@ -94,8 +101,7 @@ export default class NodeFeature<
94
101
 
95
102
  await this.Service('command', destination).execute('yarn init -y')
96
103
 
97
- const nodeWriter = this.Writer('node')
98
- const written = await nodeWriter.writeNodeModule(destination)
104
+ const written = await this.Writer('node').writeNodeModule(destination)
99
105
 
100
106
  files.push(...written)
101
107
 
@@ -24,6 +24,13 @@ export default class UpgradeAction extends AbstractAction<OptionsSchema> {
24
24
  })
25
25
 
26
26
  try {
27
+ const files = await this.Writer('node', {
28
+ upgradeMode: normalizedOptions.upgradeMode,
29
+ }).writeNodeModule(this.cwd, {
30
+ shouldConfirmBeforeWriting: true,
31
+ shouldWriteIndex: false,
32
+ })
33
+
27
34
  InFlightEntertainment.start([
28
35
  "Let's start the upgrade!",
29
36
  'While things are going, see if you can beat 1k points!',
@@ -34,10 +41,10 @@ export default class UpgradeAction extends AbstractAction<OptionsSchema> {
34
41
 
35
42
  return actionUtil.mergeActionResults(dependencyResults, {
36
43
  headline: 'Upgrade',
44
+ files,
37
45
  })
38
46
  } finally {
39
47
  InFlightEntertainment.stop()
40
-
41
48
  this.ui.renderHero('Upgrade')
42
49
  }
43
50
  }
@@ -1,35 +1,50 @@
1
1
  import { diskUtil } from '@sprucelabs/spruce-skill-utils'
2
2
  import { DirectoryTemplateCode } from '@sprucelabs/spruce-templates'
3
- import AbstractWriter, { WriteResults } from '../../../writers/AbstractWriter'
3
+ import { GeneratedFile } from '../../../types/cli.types'
4
+ import AbstractWriter, {
5
+ WriteDirectoryTemplateOptions,
6
+ WriteResults,
7
+ } from '../../../writers/AbstractWriter'
4
8
 
9
+ export const NODE_FILES_TO_UPGRADE = [
10
+ 'tsconfig.json',
11
+ '.eslintrc.js',
12
+ '.eslintignore',
13
+ '.gitignore',
14
+ '.nvmrc',
15
+ ]
5
16
  export default class NodeWriter extends AbstractWriter {
6
- public async writeNodeModule(destinationDir: string): Promise<WriteResults> {
7
- const contents = '//exports go here\n'
8
- const destination = diskUtil.resolvePath(destinationDir, 'src', 'index.ts')
9
-
10
- diskUtil.writeFile(destination, contents)
17
+ public async writeNodeModule(
18
+ destinationDir: string,
19
+ options?: Partial<WriteDirectoryTemplateOptions> & {
20
+ shouldWriteIndex?: boolean
21
+ }
22
+ ): Promise<WriteResults> {
23
+ let files: GeneratedFile[] = []
24
+ if (options?.shouldWriteIndex !== false) {
25
+ const contents = '//exports go here\n'
26
+ const destination = diskUtil.resolvePath(
27
+ destinationDir,
28
+ 'src',
29
+ 'index.ts'
30
+ )
31
+ diskUtil.writeFile(destination, contents)
32
+ files.push({
33
+ name: 'src/index.ts',
34
+ description: 'Placeholder entry file!',
35
+ action: 'generated',
36
+ path: destination,
37
+ })
38
+ }
11
39
 
12
- const files = await this.writeDirectoryTemplate({
40
+ const directoryTemplateFiles = await this.writeDirectoryTemplate({
13
41
  destinationDir,
14
42
  code: DirectoryTemplateCode.Skill,
15
- filesToWrite: [
16
- 'tsconfig.json',
17
- '.eslintrc.js',
18
- '.eslintignore',
19
- '.gitignore',
20
- '.nvmrc',
21
- ],
43
+ filesToWrite: NODE_FILES_TO_UPGRADE,
22
44
  context: { name: 'ignored', description: 'ignored' },
45
+ ...options,
23
46
  })
24
47
 
25
- return [
26
- {
27
- name: 'src/index.ts',
28
- description: 'Placeholder entry file!',
29
- action: 'generated',
30
- path: destination,
31
- },
32
- ...files,
33
- ]
48
+ return [...files, ...directoryTemplateFiles]
34
49
  }
35
50
  }
@@ -3,6 +3,7 @@ import { SpruceSchemas } from '#spruce/schemas/schemas.types'
3
3
  import SpruceError from '../../../errors/SpruceError'
4
4
  import { GlobalEmitter } from '../../../GlobalEmitter'
5
5
  import AbstractFeature from '../../AbstractFeature'
6
+ import { NODE_FILES_TO_UPGRADE } from '../../node/writers/NodeWriter'
6
7
 
7
8
  type UpgradeOptions = SpruceSchemas.SpruceCli.v2020_07_22.UpgradeSkillOptions
8
9
 
@@ -35,7 +36,7 @@ export default class Updater {
35
36
  SpruceError
36
37
  )
37
38
 
38
- const filesToSkip = ['package.json']
39
+ const filesToSkip = [...NODE_FILES_TO_UPGRADE, 'package.json']
39
40
 
40
41
  for (const payload of payloads) {
41
42
  if (payload.filesToSkip) {
@@ -80,7 +80,7 @@ export default class SpyInterface implements GraphicsInterface {
80
80
  clearTimeout(this.promptTimeout)
81
81
  }
82
82
 
83
- public lastInvocation() {
83
+ public getLastInvocation() {
84
84
  return this.invocations[this.invocations.length - 1]
85
85
  }
86
86
 
@@ -1,4 +1,5 @@
1
1
  import { SettingsService } from '@sprucelabs/spruce-skill-utils'
2
+ import SpruceError from '../errors/SpruceError'
2
3
 
3
4
  interface Dependency {
4
5
  id: string
@@ -16,10 +17,23 @@ export default class DependencyService {
16
17
  }
17
18
 
18
19
  public add(dependency: Dependency) {
20
+ this.assertDependencyDoesNotExist(dependency.namespace)
21
+
19
22
  const dependencies = this.settings.get('dependencies') ?? []
20
23
  dependencies.push(dependency)
21
24
  this.settings.set('dependencies', dependencies)
22
25
  }
26
+ private assertDependencyDoesNotExist(namespace: string) {
27
+ const dependencies = this.get()
28
+ const match = dependencies.find((d) => d.namespace === namespace)
29
+
30
+ if (match) {
31
+ throw new SpruceError({
32
+ code: 'DEPENDENCY_EXISTS',
33
+ namespace,
34
+ })
35
+ }
36
+ }
23
37
 
24
38
  public get(): Dependency[] {
25
39
  return this.settings.get('dependencies') ?? []
@@ -465,7 +465,7 @@ export default abstract class AbstractCliTest extends AbstractSpruceTest {
465
465
  }
466
466
 
467
467
  protected static selectOptionBasedOnLabel(label: string) {
468
- const last = this.ui.lastInvocation()
468
+ const last = this.ui.getLastInvocation()
469
469
  assert.doesInclude(last.options.options.choices, {
470
470
  label,
471
471
  })
@@ -0,0 +1,45 @@
1
+ import { assert } from '@sprucelabs/test'
2
+ import SpyInterface from '../../interfaces/SpyInterface'
3
+
4
+ const uiAssertUtil = {
5
+ async assertRendersSelect(ui: SpyInterface) {
6
+ await ui.waitForInput()
7
+
8
+ const last = ui.getLastInvocation()
9
+ assert.isTruthy(
10
+ last.options.options.choices,
11
+ `I expected a select, I did not find one!`
12
+ )
13
+ },
14
+
15
+ assertSelectDidNotRenderChoice(
16
+ ui: SpyInterface,
17
+ value: string,
18
+ label: string
19
+ ) {
20
+ const last = ui.getLastInvocation()
21
+
22
+ assert.doesNotInclude(last.options.options.choices, {
23
+ value,
24
+ label,
25
+ })
26
+ },
27
+
28
+ async assertRendersConfirmWriteFile(ui: SpyInterface) {
29
+ await ui.waitForInput()
30
+
31
+ const last = ui.getLastInvocation()
32
+
33
+ assert.isEqual(last.options.type, 'select')
34
+ assert.isEqualDeep(last.options.options.choices, [
35
+ {
36
+ label: 'Overwrite',
37
+ value: 'overwrite',
38
+ },
39
+ { value: 'skip', label: 'Skip' },
40
+ { value: 'alwaysSkip', label: 'Always skip' },
41
+ ])
42
+ },
43
+ }
44
+
45
+ export default uiAssertUtil
@@ -16,13 +16,22 @@ export type WriteResults = GeneratedFile[]
16
16
  export interface WriterOptions {
17
17
  templates: Templates
18
18
  term: GraphicsInterface
19
- askBeforeUpdating?: boolean
20
19
  upgradeMode?: UpgradeMode
21
20
  fileDescriptions: FileDescription[]
22
21
  linter?: LintService
23
22
  settings: SettingsService
24
23
  }
25
24
 
25
+ export interface WriteDirectoryTemplateOptions {
26
+ destinationDir: string
27
+ code: DirectoryTemplateCode
28
+ filesToWrite?: string[]
29
+ filesToSkip?: string[]
30
+ context: any
31
+ shouldConfirmBeforeWriting?: boolean
32
+ firstFileWriteMessage?: string
33
+ }
34
+
26
35
  export default abstract class AbstractWriter {
27
36
  protected templates: Templates
28
37
  protected ui: GraphicsInterface
@@ -59,15 +68,9 @@ export default abstract class AbstractWriter {
59
68
  this.isLintingEnabled = true
60
69
  }
61
70
 
62
- protected async writeDirectoryTemplate(options: {
63
- destinationDir: string
64
- code: DirectoryTemplateCode
65
- filesToWrite?: string[]
66
- filesToSkip?: string[]
67
- context: any
68
- shouldConfirmBeforeWriting?: boolean
69
- firstFileWriteMessage?: string
70
- }) {
71
+ protected async writeDirectoryTemplate(
72
+ options: WriteDirectoryTemplateOptions
73
+ ) {
71
74
  const {
72
75
  context,
73
76
  destinationDir,
@@ -93,6 +96,7 @@ export default abstract class AbstractWriter {
93
96
  !filesToWrite || filesToWrite.indexOf(generated.filename) > -1
94
97
  const shouldSkip =
95
98
  filesToSkip && filesToSkip.indexOf(generated.filename) > -1
99
+
96
100
  if (shouldWrite && !shouldSkip) {
97
101
  results = await this.writeFileIfChangedMixinResults(
98
102
  pathUtil.join(destinationDir, generated.relativePath),
@@ -128,6 +132,7 @@ export default abstract class AbstractWriter {
128
132
 
129
133
  if (!diskUtil.doesFileExist(destination)) {
130
134
  let write = true
135
+
131
136
  if (
132
137
  this.shouldConfirmBeforeWriting &&
133
138
  fileDescription?.confirmPromptOnFirstWrite