@form8ion/javascript 15.5.0 → 15.6.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 (259) hide show
  1. package/lib/index.js +2 -2
  2. package/lib/index.js.map +1 -1
  3. package/package.json +4 -3
  4. package/src/code-style/index.js +3 -0
  5. package/src/code-style/lifter.js +6 -0
  6. package/src/code-style/lifter.test.js +25 -0
  7. package/src/code-style/remark/index.js +3 -0
  8. package/src/code-style/remark/lifter.js +10 -0
  9. package/src/code-style/remark/lifter.test.js +28 -0
  10. package/src/code-style/remark/scaffolder.js +38 -0
  11. package/src/code-style/remark/scaffolder.test.js +102 -0
  12. package/src/code-style/remark/tester.js +11 -0
  13. package/src/code-style/remark/tester.test.js +46 -0
  14. package/src/code-style/scaffolder.js +26 -0
  15. package/src/code-style/scaffolder.test.js +87 -0
  16. package/src/code-style/tester.js +5 -0
  17. package/src/code-style/tester.test.js +23 -0
  18. package/src/corepack/index.js +1 -0
  19. package/src/corepack/lifter.js +5 -0
  20. package/src/corepack/lifter.test.js +22 -0
  21. package/src/coverage/index.js +3 -0
  22. package/src/coverage/lifter.js +31 -0
  23. package/src/coverage/lifter.test.js +58 -0
  24. package/src/coverage/nyc/index.js +2 -0
  25. package/src/coverage/nyc/remover.js +16 -0
  26. package/src/coverage/nyc/remover.test.js +24 -0
  27. package/src/coverage/nyc/tester.js +5 -0
  28. package/src/coverage/nyc/tester.test.js +29 -0
  29. package/src/coverage/scaffolder.js +7 -0
  30. package/src/coverage/scaffolder.test.js +32 -0
  31. package/src/coverage/tester.js +10 -0
  32. package/src/coverage/tester.test.js +50 -0
  33. package/src/dependencies/index.js +3 -0
  34. package/src/dependencies/installer.js +25 -0
  35. package/src/dependencies/installer.test.js +77 -0
  36. package/src/dependencies/package-managers.js +32 -0
  37. package/src/dependencies/package-managers.test.js +46 -0
  38. package/src/dependencies/processor.js +30 -0
  39. package/src/dependencies/processor.test.js +75 -0
  40. package/src/dependencies/remover.js +10 -0
  41. package/src/dependencies/remover.test.js +28 -0
  42. package/src/dialects/babel/config/ignore-adder.js +10 -0
  43. package/src/dialects/babel/config/ignore-adder.test.js +33 -0
  44. package/src/dialects/babel/config/index.js +3 -0
  45. package/src/dialects/babel/config/loader.js +5 -0
  46. package/src/dialects/babel/config/loader.test.js +21 -0
  47. package/src/dialects/babel/config/writer.js +6 -0
  48. package/src/dialects/babel/config/writer.test.js +20 -0
  49. package/src/dialects/babel/index.js +3 -0
  50. package/src/dialects/babel/lifter.js +7 -0
  51. package/src/dialects/babel/lifter.test.js +17 -0
  52. package/src/dialects/babel/predicate.js +5 -0
  53. package/src/dialects/babel/predicate.test.js +25 -0
  54. package/src/dialects/babel/scaffolder.js +14 -0
  55. package/src/dialects/babel/scaffolder.test.js +28 -0
  56. package/src/dialects/index.js +2 -0
  57. package/src/dialects/prompt-choices.js +10 -0
  58. package/src/dialects/prompt-choices.test.js +28 -0
  59. package/src/dialects/scaffolder.js +15 -0
  60. package/src/dialects/scaffolder.test.js +49 -0
  61. package/src/dialects/typescript/index.js +1 -0
  62. package/src/dialects/typescript/scaffolder.js +31 -0
  63. package/src/dialects/typescript/scaffolder.test.js +95 -0
  64. package/src/documentation/generation-command.js +11 -0
  65. package/src/documentation/generation-command.test.js +25 -0
  66. package/src/documentation/index.js +1 -0
  67. package/src/documentation/scaffolder.js +20 -0
  68. package/src/documentation/scaffolder.test.js +49 -0
  69. package/src/engines/index.js +2 -0
  70. package/src/engines/lifter.js +7 -0
  71. package/src/engines/lifter.test.js +18 -0
  72. package/src/engines/tester.js +7 -0
  73. package/src/engines/tester.test.js +37 -0
  74. package/src/index.js +9 -0
  75. package/src/lifter.js +55 -0
  76. package/src/lifter.test.js +96 -0
  77. package/src/linting/index.js +1 -0
  78. package/src/linting/scaffolder.js +5 -0
  79. package/src/linting/scaffolder.test.js +31 -0
  80. package/src/lockfile-lint/allowed-hosts-builder.js +6 -0
  81. package/src/lockfile-lint/allowed-hosts-builder.test.js +35 -0
  82. package/src/lockfile-lint/config.js +12 -0
  83. package/src/lockfile-lint/config.test.js +37 -0
  84. package/src/lockfile-lint/index.js +3 -0
  85. package/src/lockfile-lint/scaffolder.js +38 -0
  86. package/src/lockfile-lint/scaffolder.test.js +85 -0
  87. package/src/lockfile-lint/tester.js +5 -0
  88. package/src/lockfile-lint/tester.test.js +25 -0
  89. package/src/node-version/index.js +2 -0
  90. package/src/node-version/scaffolder.js +19 -0
  91. package/src/node-version/scaffolder.test.js +33 -0
  92. package/src/node-version/tasks.js +25 -0
  93. package/src/node-version/tasks.test.js +43 -0
  94. package/src/node-version/tester.js +5 -0
  95. package/src/node-version/tester.test.js +29 -0
  96. package/src/npm-config/index.js +5 -0
  97. package/src/npm-config/lifter.js +14 -0
  98. package/src/npm-config/lifter.test.js +23 -0
  99. package/src/npm-config/reader.js +11 -0
  100. package/src/npm-config/reader.test.js +33 -0
  101. package/src/npm-config/scaffolder.js +16 -0
  102. package/src/npm-config/scaffolder.test.js +54 -0
  103. package/src/npm-config/tester.js +5 -0
  104. package/src/npm-config/tester.test.js +29 -0
  105. package/src/npm-config/writer.js +6 -0
  106. package/src/npm-config/writer.test.js +24 -0
  107. package/src/options/schemas.js +14 -0
  108. package/src/options/schemas.test.js +147 -0
  109. package/src/options/validator.js +45 -0
  110. package/src/options/validator.test.js +79 -0
  111. package/src/package/details.js +18 -0
  112. package/src/package/details.test.js +51 -0
  113. package/src/package/index.js +2 -0
  114. package/src/package/lifter.js +47 -0
  115. package/src/package/lifter.test.js +100 -0
  116. package/src/package/package-name.js +13 -0
  117. package/src/package/package-name.test.js +52 -0
  118. package/src/package/property-sorter.js +38 -0
  119. package/src/package/property-sorter.test.js +56 -0
  120. package/src/package/scaffolder.js +32 -0
  121. package/src/package/scaffolder.test.js +46 -0
  122. package/src/package/scripts/index.js +1 -0
  123. package/src/package/scripts/lifter.js +14 -0
  124. package/src/package/scripts/lifter.test.js +31 -0
  125. package/src/package/scripts/script-comparator.js +46 -0
  126. package/src/package/scripts/script-comparator.test.js +119 -0
  127. package/src/package/scripts/scripts-sorter.js +7 -0
  128. package/src/package/scripts/scripts-sorter.test.js +20 -0
  129. package/src/package/scripts/test-script-updater.js +15 -0
  130. package/src/package/scripts/test-script-updater.test.js +32 -0
  131. package/src/package/vcs-host-details.js +12 -0
  132. package/src/package/vcs-host-details.test.js +16 -0
  133. package/src/package-managers/current-manager-resolver.js +21 -0
  134. package/src/package-managers/current-manager-resolver.test.js +51 -0
  135. package/src/package-managers/index.js +5 -0
  136. package/src/package-managers/lifter.js +7 -0
  137. package/src/package-managers/lifter.test.js +18 -0
  138. package/src/package-managers/lockfile-path-resolver.js +10 -0
  139. package/src/package-managers/lockfile-path-resolver.test.js +15 -0
  140. package/src/package-managers/npm/index.js +2 -0
  141. package/src/package-managers/npm/scaffolder.js +19 -0
  142. package/src/package-managers/npm/scaffolder.test.js +33 -0
  143. package/src/package-managers/npm/tester.js +11 -0
  144. package/src/package-managers/npm/tester.test.js +33 -0
  145. package/src/package-managers/scaffolder.js +11 -0
  146. package/src/package-managers/scaffolder.test.js +27 -0
  147. package/src/package-managers/tester.js +11 -0
  148. package/src/package-managers/tester.test.js +33 -0
  149. package/src/package-managers/yarn/index.js +2 -0
  150. package/src/package-managers/yarn/scaffolder.js +19 -0
  151. package/src/package-managers/yarn/scaffolder.test.js +33 -0
  152. package/src/package-managers/yarn/tester.js +11 -0
  153. package/src/package-managers/yarn/tester.test.js +33 -0
  154. package/src/plugins-schemas.js +4 -0
  155. package/src/plugins-schemas.test.js +28 -0
  156. package/src/project-type/application/index.js +2 -0
  157. package/src/project-type/application/predicate.js +3 -0
  158. package/src/project-type/application/predicate.test.js +14 -0
  159. package/src/project-type/application/scaffolder.js +24 -0
  160. package/src/project-type/application/scaffolder.test.js +35 -0
  161. package/src/project-type/cli/index.js +3 -0
  162. package/src/project-type/cli/lifter.js +5 -0
  163. package/src/project-type/cli/lifter.test.js +20 -0
  164. package/src/project-type/cli/scaffolder.js +52 -0
  165. package/src/project-type/cli/scaffolder.test.js +103 -0
  166. package/src/project-type/cli/tester.js +3 -0
  167. package/src/project-type/cli/tester.test.js +14 -0
  168. package/src/project-type/index.js +3 -0
  169. package/src/project-type/lifter.js +23 -0
  170. package/src/project-type/lifter.test.js +69 -0
  171. package/src/project-type/monorepo/index.js +1 -0
  172. package/src/project-type/monorepo/scaffolder.js +16 -0
  173. package/src/project-type/monorepo/scaffolder.test.js +27 -0
  174. package/src/project-type/package/build-details.js +56 -0
  175. package/src/project-type/package/build-details.test.js +111 -0
  176. package/src/project-type/package/documentation.js +34 -0
  177. package/src/project-type/package/documentation.test.js +106 -0
  178. package/src/project-type/package/index.js +3 -0
  179. package/src/project-type/package/lifter.js +5 -0
  180. package/src/project-type/package/lifter.test.js +20 -0
  181. package/src/project-type/package/scaffolder.js +84 -0
  182. package/src/project-type/package/scaffolder.test.js +267 -0
  183. package/src/project-type/package/tester.js +5 -0
  184. package/src/project-type/package/tester.test.js +28 -0
  185. package/src/project-type/publishable/access-level.js +3 -0
  186. package/src/project-type/publishable/access-level.test.js +13 -0
  187. package/src/project-type/publishable/badges.js +20 -0
  188. package/src/project-type/publishable/badges.test.js +29 -0
  189. package/src/project-type/publishable/bundler/index.js +1 -0
  190. package/src/project-type/publishable/bundler/prompt.js +16 -0
  191. package/src/project-type/publishable/bundler/prompt.test.js +35 -0
  192. package/src/project-type/publishable/bundler/scaffolder.js +8 -0
  193. package/src/project-type/publishable/bundler/scaffolder.test.js +33 -0
  194. package/src/project-type/publishable/index.js +2 -0
  195. package/src/project-type/publishable/lifter.js +24 -0
  196. package/src/project-type/publishable/lifter.test.js +49 -0
  197. package/src/project-type/publishable/provenance/index.js +1 -0
  198. package/src/project-type/publishable/provenance/lifter.js +15 -0
  199. package/src/project-type/publishable/provenance/lifter.test.js +56 -0
  200. package/src/project-type/publishable/provenance/slsa.js +17 -0
  201. package/src/project-type/publishable/provenance/slsa.test.js +21 -0
  202. package/src/project-type/publishable/registry-resolver.js +15 -0
  203. package/src/project-type/publishable/registry-resolver.test.js +60 -0
  204. package/src/project-type/publishable/scaffolder.js +7 -0
  205. package/src/project-type/publishable/scaffolder.test.js +23 -0
  206. package/src/project-type/scaffolder.js +56 -0
  207. package/src/project-type/scaffolder.test.js +115 -0
  208. package/src/project-type/tester.js +9 -0
  209. package/src/project-type/tester.test.js +51 -0
  210. package/src/project-type-plugin/index.js +1 -0
  211. package/src/project-type-plugin/prompt.js +16 -0
  212. package/src/project-type-plugin/prompt.test.js +39 -0
  213. package/src/project-type-plugin/scaffolder.js +28 -0
  214. package/src/project-type-plugin/scaffolder.test.js +70 -0
  215. package/src/prompts/conditionals.js +39 -0
  216. package/src/prompts/conditionals.test.js +95 -0
  217. package/src/prompts/question-names.js +17 -0
  218. package/src/prompts/questions.js +158 -0
  219. package/src/prompts/questions.test.js +247 -0
  220. package/src/prompts/validators.js +9 -0
  221. package/src/prompts/validators.test.js +19 -0
  222. package/src/registries/index.js +2 -0
  223. package/src/registries/lifter.js +43 -0
  224. package/src/registries/lifter.test.js +63 -0
  225. package/src/registries/npm-config/list-builder.js +9 -0
  226. package/src/registries/npm-config/list-builder.test.js +43 -0
  227. package/src/registries/tester.js +3 -0
  228. package/src/registries/tester.test.js +9 -0
  229. package/src/runkit/badge/index.js +1 -0
  230. package/src/runkit/badge/scaffolder.js +13 -0
  231. package/src/runkit/badge/scaffolder.test.js +22 -0
  232. package/src/runkit/index.js +4 -0
  233. package/src/runkit/lifter.js +5 -0
  234. package/src/runkit/lifter.test.js +17 -0
  235. package/src/runkit/remover.js +3 -0
  236. package/src/runkit/remover.test.js +9 -0
  237. package/src/runkit/scaffolder.js +11 -0
  238. package/src/runkit/scaffolder.test.js +35 -0
  239. package/src/runkit/tester.js +3 -0
  240. package/src/runkit/tester.test.js +16 -0
  241. package/src/scaffolder.js +155 -0
  242. package/src/scaffolder.test.js +239 -0
  243. package/src/tester.js +17 -0
  244. package/src/tester.test.js +37 -0
  245. package/src/testing/index.js +1 -0
  246. package/src/testing/scaffolder.js +31 -0
  247. package/src/testing/scaffolder.test.js +63 -0
  248. package/src/testing/unit/index.js +1 -0
  249. package/src/testing/unit/prompt.js +15 -0
  250. package/src/testing/unit/prompt.test.js +33 -0
  251. package/src/testing/unit/scaffolder.js +30 -0
  252. package/src/testing/unit/scaffolder.test.js +54 -0
  253. package/src/vcs/ignore-lists-builder.js +6 -0
  254. package/src/vcs/ignore-lists-builder.test.js +25 -0
  255. package/src/vcs/schema.js +7 -0
  256. package/src/vcs/schema.test.js +40 -0
  257. package/src/verification/index.js +1 -0
  258. package/src/verification/scaffolder.js +35 -0
  259. package/src/verification/scaffolder.test.js +56 -0
@@ -0,0 +1,247 @@
1
+ import {execa} from 'execa';
2
+ import * as commonPrompts from '@travi/language-scaffolder-prompts';
3
+ import * as prompts from '@form8ion/overridable-prompts';
4
+ import {packageManagers, projectTypes} from '@form8ion/javascript-core';
5
+
6
+ import {expect, describe, it, vi, beforeEach} from 'vitest';
7
+ import any from '@travi/any';
8
+ import {when} from 'vitest-when';
9
+
10
+ import npmConfFactory from '../../thirdparty-wrappers/npm-conf.js';
11
+ import buildDialectChoices from '../dialects/prompt-choices.js';
12
+ import {questionNames} from './question-names.js';
13
+ import * as conditionals from './conditionals.js';
14
+ import {prompt} from './questions.js';
15
+ import * as validators from './validators.js';
16
+
17
+ vi.mock('execa');
18
+ vi.mock('@travi/language-scaffolder-prompts');
19
+ vi.mock('@form8ion/overridable-prompts');
20
+ vi.mock('../../thirdparty-wrappers/npm-conf.js');
21
+ vi.mock('../dialects/prompt-choices.js');
22
+ vi.mock('./validators.js');
23
+ vi.mock('./conditionals.js');
24
+
25
+ describe('prompts', () => {
26
+ const commonQuestions = any.listOf(any.simpleObject);
27
+ const decisions = any.simpleObject();
28
+ const vcs = any.simpleObject();
29
+ const pathWithinParent = any.string();
30
+ const ciServices = any.simpleObject();
31
+ const visibility = any.word();
32
+ const integrationTested = any.boolean();
33
+ const unitTested = any.boolean();
34
+ const tests = {unit: unitTested, integration: integrationTested};
35
+ const authorName = any.string();
36
+ const authorEmail = any.string();
37
+ const authorUrl = any.url();
38
+ const author = {name: authorName, email: authorEmail, url: authorUrl};
39
+ const chosenHost = any.word();
40
+ const dialect = any.word();
41
+ const ci = any.word();
42
+ const nodeVersionCategory = any.word();
43
+ const packageManager = any.word();
44
+ const projectType = any.word();
45
+ const scope = any.word();
46
+ const provideExample = any.boolean();
47
+ const answers = {
48
+ [commonPrompts.questionNames.UNIT_TESTS]: unitTested,
49
+ [commonPrompts.questionNames.INTEGRATION_TESTS]: integrationTested,
50
+ [questionNames.PROJECT_TYPE]: projectType,
51
+ [commonPrompts.questionNames.CI_SERVICE]: ci,
52
+ [questionNames.HOST]: chosenHost,
53
+ [questionNames.SCOPE]: scope,
54
+ [questionNames.NODE_VERSION_CATEGORY]: nodeVersionCategory,
55
+ [questionNames.AUTHOR_NAME]: authorName,
56
+ [questionNames.AUTHOR_EMAIL]: authorEmail,
57
+ [questionNames.AUTHOR_URL]: authorUrl,
58
+ [questionNames.PACKAGE_MANAGER]: packageManager,
59
+ [questionNames.DIALECT]: dialect,
60
+ [questionNames.PROVIDE_EXAMPLE]: provideExample
61
+ };
62
+
63
+ beforeEach(() => {
64
+ when(commonPrompts.questions)
65
+ .calledWith({vcs, ciServices, pathWithinParent: undefined})
66
+ .thenReturn(commonQuestions);
67
+ });
68
+
69
+ it('should prompt the user for the necessary details', async () => {
70
+ const npmUser = any.word();
71
+ const get = vi.fn();
72
+ const hosts = any.simpleObject();
73
+ const dialects = any.listOf(any.simpleObject);
74
+ const configs = any.simpleObject();
75
+ const scopeValidator = () => undefined;
76
+ const scopePromptShouldBePresented = () => undefined;
77
+ when(npmConfFactory).calledWith().thenReturn({get});
78
+ when(get).calledWith('init.author.name').thenReturn(authorName);
79
+ when(get).calledWith('init.author.email').thenReturn(authorEmail);
80
+ when(get).calledWith('init.author.url').thenReturn(authorUrl);
81
+ when(execa).calledWith('npm', ['whoami']).thenResolve({stdout: npmUser});
82
+ when(validators.scope).calledWith(visibility).thenReturn(scopeValidator);
83
+ when(conditionals.scopePromptShouldBePresentedFactory)
84
+ .calledWith(visibility)
85
+ .thenReturn(scopePromptShouldBePresented);
86
+ when(buildDialectChoices).calledWith(configs).thenReturn(dialects);
87
+ when(prompts.prompt)
88
+ .calledWith([
89
+ {
90
+ name: questionNames.DIALECT,
91
+ message: 'Which JavaScript dialect should this project follow?',
92
+ type: 'list',
93
+ choices: dialects,
94
+ default: 'babel'
95
+ },
96
+ {
97
+ name: questionNames.NODE_VERSION_CATEGORY,
98
+ message: 'What node.js version should be used?',
99
+ type: 'list',
100
+ choices: ['LTS', 'Latest'],
101
+ default: 'LTS'
102
+ },
103
+ {
104
+ name: questionNames.PACKAGE_MANAGER,
105
+ message: 'Which package manager will be used with this project?',
106
+ type: 'list',
107
+ choices: Object.values(packageManagers),
108
+ default: packageManagers.NPM
109
+ },
110
+ {
111
+ name: questionNames.PROJECT_TYPE,
112
+ message: 'What type of JavaScript project is this?',
113
+ type: 'list',
114
+ choices: [...Object.values(projectTypes), 'Other'],
115
+ default: projectTypes.PACKAGE
116
+ },
117
+ {
118
+ name: questionNames.SHOULD_BE_SCOPED,
119
+ message: 'Should this package be scoped?',
120
+ type: 'confirm',
121
+ when: conditionals.shouldBeScopedPromptShouldBePresented,
122
+ default: true
123
+ },
124
+ {
125
+ name: questionNames.SCOPE,
126
+ message: 'What is the scope?',
127
+ when: scopePromptShouldBePresented,
128
+ validate: scopeValidator,
129
+ default: npmUser
130
+ },
131
+ {
132
+ name: questionNames.AUTHOR_NAME,
133
+ message: 'What is the author\'s name?',
134
+ default: authorName
135
+ },
136
+ {
137
+ name: questionNames.AUTHOR_EMAIL,
138
+ message: 'What is the author\'s email?',
139
+ default: authorEmail
140
+ },
141
+ {
142
+ name: questionNames.AUTHOR_URL,
143
+ message: 'What is the author\'s website url?',
144
+ default: authorUrl
145
+ },
146
+ ...commonQuestions,
147
+ {
148
+ name: questionNames.CONFIGURE_LINTING,
149
+ message: 'Will there be source code that should be linted?',
150
+ type: 'confirm',
151
+ when: conditionals.lintingPromptShouldBePresented
152
+ },
153
+ {
154
+ name: questionNames.PROVIDE_EXAMPLE,
155
+ message: 'Should an example be provided in the README?',
156
+ type: 'confirm',
157
+ when: conditionals.projectIsPackage
158
+ },
159
+ {
160
+ name: questionNames.HOST,
161
+ type: 'list',
162
+ message: 'Where will the application be hosted?',
163
+ when: conditionals.projectIsApplication,
164
+ choices: [...Object.keys(hosts), 'Other']
165
+ }
166
+ ], decisions)
167
+ .thenResolve({...answers, [questionNames.CONFIGURE_LINTING]: any.word()});
168
+
169
+ expect(await prompt(ciServices, hosts, visibility, vcs, decisions, configs)).toEqual({
170
+ tests,
171
+ projectType,
172
+ ci,
173
+ chosenHost,
174
+ scope,
175
+ nodeVersionCategory,
176
+ author,
177
+ packageManager,
178
+ dialect,
179
+ configureLinting: true,
180
+ provideExample
181
+ });
182
+ });
183
+
184
+ it('should not override the transpile/lint value when set to `false`', async () => {
185
+ const npmUser = any.word();
186
+ const get = vi.fn();
187
+ npmConfFactory.mockReturnValue({get});
188
+ when(execa).calledWith('npm', ['whoami']).thenResolve({stdout: npmUser});
189
+ prompts.prompt.mockResolvedValue({...answers, [questionNames.CONFIGURE_LINTING]: false});
190
+
191
+ expect(await prompt(ciServices, {}, visibility, vcs, decisions)).toEqual({
192
+ tests,
193
+ projectType,
194
+ ci,
195
+ chosenHost,
196
+ scope,
197
+ nodeVersionCategory,
198
+ author,
199
+ packageManager,
200
+ provideExample,
201
+ dialect,
202
+ configureLinting: false
203
+ });
204
+ });
205
+
206
+ it('should not ask about node version for sub-projects since the parent project already defines', async () => {
207
+ when(execa).calledWith('npm', ['whoami']).thenResolve({stdout: any.word()});
208
+ npmConfFactory.mockReturnValue({get: () => undefined});
209
+ when(commonPrompts.questions)
210
+ .calledWith({vcs, ciServices, pathWithinParent})
211
+ .thenReturn(commonQuestions);
212
+ prompts.prompt.mockResolvedValue(answers);
213
+
214
+ await prompt(ciServices, {}, 'Private', vcs, null, null, pathWithinParent);
215
+
216
+ const [questions] = prompts.prompt.mock.lastCall;
217
+ expect(questions.filter(question => questionNames.NODE_VERSION_CATEGORY === question.name).length).toEqual(0);
218
+ });
219
+
220
+ it('should not ask whether private packages should be scoped', async () => {
221
+ when(execa).calledWith('npm', ['whoami']).thenResolve({stdout: any.word()});
222
+ npmConfFactory.mockReturnValue({get: () => undefined});
223
+ when(commonPrompts.questions)
224
+ .calledWith({vcs, ciServices, pathWithinParent})
225
+ .thenReturn(commonQuestions);
226
+ prompts.prompt.mockResolvedValue(answers);
227
+
228
+ await prompt(ciServices, {}, 'Private', vcs, null, null, pathWithinParent);
229
+
230
+ const [questions] = prompts.prompt.mock.lastCall;
231
+ expect(questions.filter(question => questionNames.SHOULD_BE_SCOPED === question.name).length).toEqual(0);
232
+ });
233
+
234
+ it('should handle a non-logged-in user gracefully', async () => {
235
+ when(execa).calledWith('npm', ['whoami']).thenReject(new Error());
236
+ npmConfFactory.mockReturnValue({get: () => undefined});
237
+ when(commonPrompts.questions)
238
+ .calledWith({vcs, ciServices, pathWithinParent})
239
+ .thenReturn(commonQuestions);
240
+ prompts.prompt.mockResolvedValue(answers);
241
+
242
+ await prompt(ciServices, {}, 'Public', vcs, {}, null, pathWithinParent);
243
+
244
+ const [questions] = prompts.prompt.mock.lastCall;
245
+ expect(questions.filter(question => questionNames.SHOULD_BE_SCOPED === question.name).length).toEqual(1);
246
+ });
247
+ });
@@ -0,0 +1,9 @@
1
+ export function scope(visibility) {
2
+ return input => {
3
+ if (!input && 'Private' === visibility) {
4
+ return 'Private packages must be scoped (https://docs.npmjs.com/private-modules/intro#setting-up-your-package)';
5
+ }
6
+
7
+ return true;
8
+ };
9
+ }
@@ -0,0 +1,19 @@
1
+ import {expect, it, describe} from 'vitest';
2
+
3
+ import {scope} from './validators.js';
4
+
5
+ describe('question validators', () => {
6
+ it('should require a scope for private project', () => {
7
+ expect(scope('Private')()).toEqual(
8
+ 'Private packages must be scoped (https://docs.npmjs.com/private-modules/intro#setting-up-your-package)'
9
+ );
10
+ });
11
+
12
+ it('it should consider a provided value to be a valid answer to private projects', () => {
13
+ expect(scope('Public')()).toBe(true);
14
+ });
15
+
16
+ it('it should consider an empty value to be a valid answer to public projects', () => {
17
+ expect(scope('Public')()).toBe(true);
18
+ });
19
+ });
@@ -0,0 +1,2 @@
1
+ export {default as test} from './tester.js';
2
+ export {default as lift} from './lifter.js';
@@ -0,0 +1,43 @@
1
+ import {
2
+ scaffold as scaffoldLockfileLint,
3
+ test as lockfileLintIsAlreadyConfigured,
4
+ read as readLockfileLintConfig,
5
+ write as writeLockfileLintConfig
6
+ } from '../lockfile-lint/index.js';
7
+ import {read as readNpmConfig, write as writeNpmConfig} from '../npm-config/index.js';
8
+ import buildRegistriesConfig from './npm-config/list-builder.js';
9
+ import buildAllowedHostsList from '../lockfile-lint/allowed-hosts-builder.js';
10
+
11
+ async function updateRegistriesInNpmConfig(registries, projectRoot) {
12
+ const registriesForNpmConfig = buildRegistriesConfig(registries);
13
+
14
+ await writeNpmConfig({
15
+ projectRoot,
16
+ config: {
17
+ ...(await readNpmConfig({projectRoot})),
18
+ ...registriesForNpmConfig
19
+ }
20
+ });
21
+ }
22
+
23
+ async function updateRegistriesInLockfileLintConfig(projectRoot, packageManager, registries) {
24
+ await writeLockfileLintConfig({
25
+ projectRoot,
26
+ config: {
27
+ ...await readLockfileLintConfig({projectRoot}),
28
+ 'allowed-hosts': buildAllowedHostsList({packageManager, registries})
29
+ }
30
+ });
31
+ }
32
+
33
+ export default async function liftRegistries({projectRoot, packageManager, configs: {registries}}) {
34
+ await updateRegistriesInNpmConfig(registries, projectRoot);
35
+
36
+ if (!(await lockfileLintIsAlreadyConfigured({projectRoot}))) {
37
+ return scaffoldLockfileLint({projectRoot, packageManager, registries});
38
+ }
39
+
40
+ await updateRegistriesInLockfileLintConfig(projectRoot, packageManager, registries);
41
+
42
+ return {};
43
+ }
@@ -0,0 +1,63 @@
1
+ import any from '@travi/any';
2
+ import {beforeEach, describe, expect, it, vi} from 'vitest';
3
+ import {when} from 'vitest-when';
4
+
5
+ import buildAllowedHostsList from '../lockfile-lint/allowed-hosts-builder.js';
6
+ import {
7
+ scaffold as scaffoldLockfileLint,
8
+ test as lockfileLintIsAlreadyConfigured,
9
+ read as readLockfileLintConfig,
10
+ write as writeLockfileLintConfig
11
+ } from '../lockfile-lint/index.js';
12
+ import {read as readNpmConfig, write as writeNpmConfig} from '../npm-config/index.js';
13
+ import buildRegistriesConfig from './npm-config/list-builder.js';
14
+ import liftRegistries from './lifter.js';
15
+
16
+ vi.mock('../lockfile-lint/allowed-hosts-builder.js');
17
+ vi.mock('../lockfile-lint/index.js');
18
+ vi.mock('../registries/npm-config/list-builder.js');
19
+ vi.mock('../npm-config/index.js');
20
+
21
+ describe('registries lifter', () => {
22
+ const projectRoot = any.string();
23
+ const packageManager = any.word();
24
+ const registries = any.simpleObject();
25
+ const configs = {...any.simpleObject(), registries};
26
+ const processedRegistryDetails = any.simpleObject();
27
+ const existingNpmConfig = any.simpleObject();
28
+
29
+ beforeEach(() => {
30
+ when(readNpmConfig).calledWith({projectRoot}).thenResolve(existingNpmConfig);
31
+ when(buildRegistriesConfig).calledWith(registries).thenReturn(processedRegistryDetails);
32
+ });
33
+
34
+ it('should define the registries in the npmrc and lockfile-lint configs', async () => {
35
+ const existingLockfileLintConfig = any.simpleObject();
36
+ const allowedHosts = any.listOf(any.url);
37
+ when(lockfileLintIsAlreadyConfigured).calledWith({projectRoot}).thenResolve(true);
38
+ when(readLockfileLintConfig).calledWith({projectRoot}).thenResolve(existingLockfileLintConfig);
39
+ when(buildAllowedHostsList).calledWith({packageManager, registries}).thenReturn(allowedHosts);
40
+
41
+ expect(await liftRegistries({projectRoot, packageManager, configs})).toEqual({});
42
+
43
+ expect(writeNpmConfig).toHaveBeenCalledWith({
44
+ projectRoot,
45
+ config: {...existingNpmConfig, ...processedRegistryDetails}
46
+ });
47
+ expect(writeLockfileLintConfig).toHaveBeenCalledWith({
48
+ projectRoot,
49
+ config: {...existingLockfileLintConfig, 'allowed-hosts': allowedHosts}
50
+ });
51
+ });
52
+
53
+ it('should scaffold lockfile-lint if not already present', async () => {
54
+ const lockfileLintResults = any.simpleObject();
55
+ when(lockfileLintIsAlreadyConfigured).calledWith({projectRoot}).thenResolve(false);
56
+ when(scaffoldLockfileLint)
57
+ .calledWith({projectRoot, packageManager, registries})
58
+ .thenResolve(lockfileLintResults);
59
+
60
+ expect(await liftRegistries({projectRoot, packageManager, configs}))
61
+ .toEqual(lockfileLintResults);
62
+ });
63
+ });
@@ -0,0 +1,9 @@
1
+ export default function buildRegistriesList(registries = {}) {
2
+ return Object.entries(registries)
3
+ .filter(([scope]) => 'publish' !== scope)
4
+ .reduce((acc, [scope, url]) => {
5
+ if ('registry' === scope) return {...acc, registry: url};
6
+
7
+ return {...acc, [`@${scope}:registry`]: url};
8
+ }, {registry: 'https://registry.npmjs.org'});
9
+ }
@@ -0,0 +1,43 @@
1
+ import {describe, expect, it} from 'vitest';
2
+ import any from '@travi/any';
3
+
4
+ import buildList from './list-builder.js';
5
+
6
+ describe('registries list builder', () => {
7
+ it('should define the default registry even if no registries are provided', () => {
8
+ expect(buildList()).toEqual({registry: 'https://registry.npmjs.org'});
9
+ });
10
+
11
+ it('should enable overriding the default registry', () => {
12
+ const registry = any.url();
13
+
14
+ expect(buildList({registry})).toEqual({registry});
15
+ });
16
+
17
+ it('should enable defining scoped registries', () => {
18
+ const registries = any.objectWithKeys(any.listOf(any.word), {factory: any.word});
19
+
20
+ expect(buildList(registries)).toEqual({
21
+ registry: 'https://registry.npmjs.org',
22
+ ...Object.fromEntries(
23
+ Object.entries(registries).map(([scope, url]) => ([`@${scope}:registry`, url]))
24
+ )
25
+ });
26
+ });
27
+
28
+ it('should enable defining scoped registries and overriding the default registry at the same time', () => {
29
+ const registries = any.objectWithKeys(any.listOf(any.word), {factory: any.word});
30
+ const registry = any.url();
31
+
32
+ expect(buildList({...registries, registry})).toEqual({
33
+ registry,
34
+ ...Object.fromEntries(
35
+ Object.entries(registries).map(([scope, url]) => ([`@${scope}:registry`, url]))
36
+ )
37
+ });
38
+ });
39
+
40
+ it('should not define the publish registry in the list', () => {
41
+ expect(buildList({publish: any.url})).toEqual({registry: 'https://registry.npmjs.org'});
42
+ });
43
+ });
@@ -0,0 +1,3 @@
1
+ export default function registriesAreInUse() {
2
+ return true;
3
+ }
@@ -0,0 +1,9 @@
1
+ import {describe, it, expect} from 'vitest';
2
+
3
+ import registriesAreUsed from './tester.js';
4
+
5
+ describe('registries tester', () => {
6
+ it('should always return `true`', async () => {
7
+ expect(await registriesAreUsed()).toBe(true);
8
+ });
9
+ });
@@ -0,0 +1 @@
1
+ export {default as scaffold} from './scaffolder.js';
@@ -0,0 +1,13 @@
1
+ export default function scaffoldRunkitBadge({packageName}) {
2
+ return {
3
+ badges: {
4
+ consumer: {
5
+ runkit: {
6
+ img: `https://badge.runkitcdn.com/${packageName}.svg`,
7
+ text: `Try ${packageName} on RunKit`,
8
+ link: `https://npm.runkit.com/${packageName}`
9
+ }
10
+ }
11
+ }
12
+ };
13
+ }
@@ -0,0 +1,22 @@
1
+ import {describe, it, expect} from 'vitest';
2
+ import any from '@travi/any';
3
+
4
+ import scaffoldRunkitBadge from './scaffolder.js';
5
+
6
+ describe('runkit badge scaffolder', () => {
7
+ it('should define the badge details', async () => {
8
+ const packageName = any.word();
9
+
10
+ expect(scaffoldRunkitBadge({packageName})).toEqual({
11
+ badges: {
12
+ consumer: {
13
+ runkit: {
14
+ img: `https://badge.runkitcdn.com/${packageName}.svg`,
15
+ text: `Try ${packageName} on RunKit`,
16
+ link: `https://npm.runkit.com/${packageName}`
17
+ }
18
+ }
19
+ }
20
+ });
21
+ });
22
+ });
@@ -0,0 +1,4 @@
1
+ export {default as scaffold} from './scaffolder.js';
2
+ export {default as remove} from './remover.js';
3
+ export {default as test} from './tester.js';
4
+ export {default as lift} from './lifter.js';
@@ -0,0 +1,5 @@
1
+ import removeRunkit from './remover.js';
2
+
3
+ export default function liftRunkit() {
4
+ return removeRunkit();
5
+ }
@@ -0,0 +1,17 @@
1
+ import {describe, it, vi, expect} from 'vitest';
2
+ import {when} from 'vitest-when';
3
+ import any from '@travi/any';
4
+
5
+ import removeRunkit from './remover.js';
6
+ import liftRunkit from './lifter.js';
7
+
8
+ vi.mock('./remover.js');
9
+
10
+ describe('runkit lifter', () => {
11
+ it('should remove runkit', async () => {
12
+ const removalResults = any.simpleObject();
13
+ when(removeRunkit).calledWith().thenResolve(removalResults);
14
+
15
+ expect(await liftRunkit()).toEqual(removalResults);
16
+ });
17
+ });
@@ -0,0 +1,3 @@
1
+ export default function removeRunkit() {
2
+
3
+ }
@@ -0,0 +1,9 @@
1
+ import {describe, it} from 'vitest';
2
+
3
+ import removeRunkit from './remover.js';
4
+
5
+ describe('runkit remover', () => {
6
+ it('should remove runkit details from the project', async () => {
7
+ await removeRunkit();
8
+ });
9
+ });
@@ -0,0 +1,11 @@
1
+ import {mergeIntoExistingPackageJson} from '@form8ion/javascript-core';
2
+
3
+ import {scaffold as scaffoldBadge} from './badge/index.js';
4
+
5
+ export default async function scaffoldRunkit({projectRoot, packageName, visibility}) {
6
+ if ('Public' !== visibility) return {};
7
+
8
+ await mergeIntoExistingPackageJson({projectRoot, config: {runkitExampleFilename: './example.js'}});
9
+
10
+ return scaffoldBadge({packageName});
11
+ }
@@ -0,0 +1,35 @@
1
+ import {mergeIntoExistingPackageJson} from '@form8ion/javascript-core';
2
+
3
+ import {describe, expect, it, vi} from 'vitest';
4
+ import {when} from 'vitest-when';
5
+ import any from '@travi/any';
6
+
7
+ import {scaffold as scaffoldBadge} from './badge/index.js';
8
+ import scaffoldRunkit from './scaffolder.js';
9
+
10
+ vi.mock('@form8ion/javascript-core');
11
+ vi.mock('./badge/index.js');
12
+
13
+ describe('runkit scaffolder', () => {
14
+ it('should scaffold runkit details', async () => {
15
+ const projectRoot = any.string();
16
+ const visibility = 'Public';
17
+ const badgeResults = any.simpleObject();
18
+ const packageName = any.word();
19
+ when(scaffoldBadge).calledWith({packageName}).thenReturn(badgeResults);
20
+
21
+ expect(await scaffoldRunkit({projectRoot, packageName, visibility})).toEqual(badgeResults);
22
+
23
+ expect(mergeIntoExistingPackageJson).toHaveBeenCalledWith({
24
+ projectRoot,
25
+ config: {runkitExampleFilename: './example.js'}
26
+ });
27
+ });
28
+
29
+ it('should not scaffold runkit details if the project is not public', async () => {
30
+ await scaffoldRunkit({visibility: any.word()});
31
+
32
+ expect(mergeIntoExistingPackageJson).not.toHaveBeenCalled();
33
+ expect(scaffoldBadge).not.toHaveBeenCalled();
34
+ });
35
+ });
@@ -0,0 +1,3 @@
1
+ export default function runkitIsConfigured({packageDetails}) {
2
+ return !!packageDetails.runkitExampleFilename;
3
+ }
@@ -0,0 +1,16 @@
1
+ import {describe, expect, it} from 'vitest';
2
+ import any from '@travi/any';
3
+
4
+ import runkitIsConfigured from './tester.js';
5
+
6
+ describe('runkit predicate', () => {
7
+ it('should return `true` if the `runkitExampleFilename` property exists in the `package.json`', async () => {
8
+ expect(
9
+ await runkitIsConfigured({packageDetails: {...any.simpleObject(), runkitExampleFilename: any.word()}})
10
+ ).toBe(true);
11
+ });
12
+
13
+ it('should return `false` if the `runkitExampleFilename` property does not exist in the `package.json`', async () => {
14
+ expect(await runkitIsConfigured({packageDetails: any.simpleObject()})).toBe(false);
15
+ });
16
+ });