@form8ion/javascript 15.6.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 (257) hide show
  1. package/package.json +4 -3
  2. package/src/code-style/index.js +3 -0
  3. package/src/code-style/lifter.js +6 -0
  4. package/src/code-style/lifter.test.js +25 -0
  5. package/src/code-style/remark/index.js +3 -0
  6. package/src/code-style/remark/lifter.js +10 -0
  7. package/src/code-style/remark/lifter.test.js +28 -0
  8. package/src/code-style/remark/scaffolder.js +38 -0
  9. package/src/code-style/remark/scaffolder.test.js +102 -0
  10. package/src/code-style/remark/tester.js +11 -0
  11. package/src/code-style/remark/tester.test.js +46 -0
  12. package/src/code-style/scaffolder.js +26 -0
  13. package/src/code-style/scaffolder.test.js +87 -0
  14. package/src/code-style/tester.js +5 -0
  15. package/src/code-style/tester.test.js +23 -0
  16. package/src/corepack/index.js +1 -0
  17. package/src/corepack/lifter.js +5 -0
  18. package/src/corepack/lifter.test.js +22 -0
  19. package/src/coverage/index.js +3 -0
  20. package/src/coverage/lifter.js +31 -0
  21. package/src/coverage/lifter.test.js +58 -0
  22. package/src/coverage/nyc/index.js +2 -0
  23. package/src/coverage/nyc/remover.js +16 -0
  24. package/src/coverage/nyc/remover.test.js +24 -0
  25. package/src/coverage/nyc/tester.js +5 -0
  26. package/src/coverage/nyc/tester.test.js +29 -0
  27. package/src/coverage/scaffolder.js +7 -0
  28. package/src/coverage/scaffolder.test.js +32 -0
  29. package/src/coverage/tester.js +10 -0
  30. package/src/coverage/tester.test.js +50 -0
  31. package/src/dependencies/index.js +3 -0
  32. package/src/dependencies/installer.js +25 -0
  33. package/src/dependencies/installer.test.js +77 -0
  34. package/src/dependencies/package-managers.js +32 -0
  35. package/src/dependencies/package-managers.test.js +46 -0
  36. package/src/dependencies/processor.js +30 -0
  37. package/src/dependencies/processor.test.js +75 -0
  38. package/src/dependencies/remover.js +10 -0
  39. package/src/dependencies/remover.test.js +28 -0
  40. package/src/dialects/babel/config/ignore-adder.js +10 -0
  41. package/src/dialects/babel/config/ignore-adder.test.js +33 -0
  42. package/src/dialects/babel/config/index.js +3 -0
  43. package/src/dialects/babel/config/loader.js +5 -0
  44. package/src/dialects/babel/config/loader.test.js +21 -0
  45. package/src/dialects/babel/config/writer.js +6 -0
  46. package/src/dialects/babel/config/writer.test.js +20 -0
  47. package/src/dialects/babel/index.js +3 -0
  48. package/src/dialects/babel/lifter.js +7 -0
  49. package/src/dialects/babel/lifter.test.js +17 -0
  50. package/src/dialects/babel/predicate.js +5 -0
  51. package/src/dialects/babel/predicate.test.js +25 -0
  52. package/src/dialects/babel/scaffolder.js +14 -0
  53. package/src/dialects/babel/scaffolder.test.js +28 -0
  54. package/src/dialects/index.js +2 -0
  55. package/src/dialects/prompt-choices.js +10 -0
  56. package/src/dialects/prompt-choices.test.js +28 -0
  57. package/src/dialects/scaffolder.js +15 -0
  58. package/src/dialects/scaffolder.test.js +49 -0
  59. package/src/dialects/typescript/index.js +1 -0
  60. package/src/dialects/typescript/scaffolder.js +31 -0
  61. package/src/dialects/typescript/scaffolder.test.js +95 -0
  62. package/src/documentation/generation-command.js +11 -0
  63. package/src/documentation/generation-command.test.js +25 -0
  64. package/src/documentation/index.js +1 -0
  65. package/src/documentation/scaffolder.js +20 -0
  66. package/src/documentation/scaffolder.test.js +49 -0
  67. package/src/engines/index.js +2 -0
  68. package/src/engines/lifter.js +7 -0
  69. package/src/engines/lifter.test.js +18 -0
  70. package/src/engines/tester.js +7 -0
  71. package/src/engines/tester.test.js +37 -0
  72. package/src/index.js +9 -0
  73. package/src/lifter.js +55 -0
  74. package/src/lifter.test.js +96 -0
  75. package/src/linting/index.js +1 -0
  76. package/src/linting/scaffolder.js +5 -0
  77. package/src/linting/scaffolder.test.js +31 -0
  78. package/src/lockfile-lint/allowed-hosts-builder.js +6 -0
  79. package/src/lockfile-lint/allowed-hosts-builder.test.js +35 -0
  80. package/src/lockfile-lint/config.js +12 -0
  81. package/src/lockfile-lint/config.test.js +37 -0
  82. package/src/lockfile-lint/index.js +3 -0
  83. package/src/lockfile-lint/scaffolder.js +38 -0
  84. package/src/lockfile-lint/scaffolder.test.js +85 -0
  85. package/src/lockfile-lint/tester.js +5 -0
  86. package/src/lockfile-lint/tester.test.js +25 -0
  87. package/src/node-version/index.js +2 -0
  88. package/src/node-version/scaffolder.js +19 -0
  89. package/src/node-version/scaffolder.test.js +33 -0
  90. package/src/node-version/tasks.js +25 -0
  91. package/src/node-version/tasks.test.js +43 -0
  92. package/src/node-version/tester.js +5 -0
  93. package/src/node-version/tester.test.js +29 -0
  94. package/src/npm-config/index.js +5 -0
  95. package/src/npm-config/lifter.js +14 -0
  96. package/src/npm-config/lifter.test.js +23 -0
  97. package/src/npm-config/reader.js +11 -0
  98. package/src/npm-config/reader.test.js +33 -0
  99. package/src/npm-config/scaffolder.js +16 -0
  100. package/src/npm-config/scaffolder.test.js +54 -0
  101. package/src/npm-config/tester.js +5 -0
  102. package/src/npm-config/tester.test.js +29 -0
  103. package/src/npm-config/writer.js +6 -0
  104. package/src/npm-config/writer.test.js +24 -0
  105. package/src/options/schemas.js +14 -0
  106. package/src/options/schemas.test.js +147 -0
  107. package/src/options/validator.js +45 -0
  108. package/src/options/validator.test.js +79 -0
  109. package/src/package/details.js +18 -0
  110. package/src/package/details.test.js +51 -0
  111. package/src/package/index.js +2 -0
  112. package/src/package/lifter.js +47 -0
  113. package/src/package/lifter.test.js +100 -0
  114. package/src/package/package-name.js +13 -0
  115. package/src/package/package-name.test.js +52 -0
  116. package/src/package/property-sorter.js +38 -0
  117. package/src/package/property-sorter.test.js +56 -0
  118. package/src/package/scaffolder.js +32 -0
  119. package/src/package/scaffolder.test.js +46 -0
  120. package/src/package/scripts/index.js +1 -0
  121. package/src/package/scripts/lifter.js +14 -0
  122. package/src/package/scripts/lifter.test.js +31 -0
  123. package/src/package/scripts/script-comparator.js +46 -0
  124. package/src/package/scripts/script-comparator.test.js +119 -0
  125. package/src/package/scripts/scripts-sorter.js +7 -0
  126. package/src/package/scripts/scripts-sorter.test.js +20 -0
  127. package/src/package/scripts/test-script-updater.js +15 -0
  128. package/src/package/scripts/test-script-updater.test.js +32 -0
  129. package/src/package/vcs-host-details.js +12 -0
  130. package/src/package/vcs-host-details.test.js +16 -0
  131. package/src/package-managers/current-manager-resolver.js +21 -0
  132. package/src/package-managers/current-manager-resolver.test.js +51 -0
  133. package/src/package-managers/index.js +5 -0
  134. package/src/package-managers/lifter.js +7 -0
  135. package/src/package-managers/lifter.test.js +18 -0
  136. package/src/package-managers/lockfile-path-resolver.js +10 -0
  137. package/src/package-managers/lockfile-path-resolver.test.js +15 -0
  138. package/src/package-managers/npm/index.js +2 -0
  139. package/src/package-managers/npm/scaffolder.js +19 -0
  140. package/src/package-managers/npm/scaffolder.test.js +33 -0
  141. package/src/package-managers/npm/tester.js +11 -0
  142. package/src/package-managers/npm/tester.test.js +33 -0
  143. package/src/package-managers/scaffolder.js +11 -0
  144. package/src/package-managers/scaffolder.test.js +27 -0
  145. package/src/package-managers/tester.js +11 -0
  146. package/src/package-managers/tester.test.js +33 -0
  147. package/src/package-managers/yarn/index.js +2 -0
  148. package/src/package-managers/yarn/scaffolder.js +19 -0
  149. package/src/package-managers/yarn/scaffolder.test.js +33 -0
  150. package/src/package-managers/yarn/tester.js +11 -0
  151. package/src/package-managers/yarn/tester.test.js +33 -0
  152. package/src/plugins-schemas.js +4 -0
  153. package/src/plugins-schemas.test.js +28 -0
  154. package/src/project-type/application/index.js +2 -0
  155. package/src/project-type/application/predicate.js +3 -0
  156. package/src/project-type/application/predicate.test.js +14 -0
  157. package/src/project-type/application/scaffolder.js +24 -0
  158. package/src/project-type/application/scaffolder.test.js +35 -0
  159. package/src/project-type/cli/index.js +3 -0
  160. package/src/project-type/cli/lifter.js +5 -0
  161. package/src/project-type/cli/lifter.test.js +20 -0
  162. package/src/project-type/cli/scaffolder.js +52 -0
  163. package/src/project-type/cli/scaffolder.test.js +103 -0
  164. package/src/project-type/cli/tester.js +3 -0
  165. package/src/project-type/cli/tester.test.js +14 -0
  166. package/src/project-type/index.js +3 -0
  167. package/src/project-type/lifter.js +23 -0
  168. package/src/project-type/lifter.test.js +69 -0
  169. package/src/project-type/monorepo/index.js +1 -0
  170. package/src/project-type/monorepo/scaffolder.js +16 -0
  171. package/src/project-type/monorepo/scaffolder.test.js +27 -0
  172. package/src/project-type/package/build-details.js +56 -0
  173. package/src/project-type/package/build-details.test.js +111 -0
  174. package/src/project-type/package/documentation.js +34 -0
  175. package/src/project-type/package/documentation.test.js +106 -0
  176. package/src/project-type/package/index.js +3 -0
  177. package/src/project-type/package/lifter.js +5 -0
  178. package/src/project-type/package/lifter.test.js +20 -0
  179. package/src/project-type/package/scaffolder.js +84 -0
  180. package/src/project-type/package/scaffolder.test.js +267 -0
  181. package/src/project-type/package/tester.js +5 -0
  182. package/src/project-type/package/tester.test.js +28 -0
  183. package/src/project-type/publishable/access-level.js +3 -0
  184. package/src/project-type/publishable/access-level.test.js +13 -0
  185. package/src/project-type/publishable/badges.js +20 -0
  186. package/src/project-type/publishable/badges.test.js +29 -0
  187. package/src/project-type/publishable/bundler/index.js +1 -0
  188. package/src/project-type/publishable/bundler/prompt.js +16 -0
  189. package/src/project-type/publishable/bundler/prompt.test.js +35 -0
  190. package/src/project-type/publishable/bundler/scaffolder.js +8 -0
  191. package/src/project-type/publishable/bundler/scaffolder.test.js +33 -0
  192. package/src/project-type/publishable/index.js +2 -0
  193. package/src/project-type/publishable/lifter.js +24 -0
  194. package/src/project-type/publishable/lifter.test.js +49 -0
  195. package/src/project-type/publishable/provenance/index.js +1 -0
  196. package/src/project-type/publishable/provenance/lifter.js +15 -0
  197. package/src/project-type/publishable/provenance/lifter.test.js +56 -0
  198. package/src/project-type/publishable/provenance/slsa.js +17 -0
  199. package/src/project-type/publishable/provenance/slsa.test.js +21 -0
  200. package/src/project-type/publishable/registry-resolver.js +15 -0
  201. package/src/project-type/publishable/registry-resolver.test.js +60 -0
  202. package/src/project-type/publishable/scaffolder.js +7 -0
  203. package/src/project-type/publishable/scaffolder.test.js +23 -0
  204. package/src/project-type/scaffolder.js +56 -0
  205. package/src/project-type/scaffolder.test.js +115 -0
  206. package/src/project-type/tester.js +9 -0
  207. package/src/project-type/tester.test.js +51 -0
  208. package/src/project-type-plugin/index.js +1 -0
  209. package/src/project-type-plugin/prompt.js +16 -0
  210. package/src/project-type-plugin/prompt.test.js +39 -0
  211. package/src/project-type-plugin/scaffolder.js +28 -0
  212. package/src/project-type-plugin/scaffolder.test.js +70 -0
  213. package/src/prompts/conditionals.js +39 -0
  214. package/src/prompts/conditionals.test.js +95 -0
  215. package/src/prompts/question-names.js +17 -0
  216. package/src/prompts/questions.js +158 -0
  217. package/src/prompts/questions.test.js +247 -0
  218. package/src/prompts/validators.js +9 -0
  219. package/src/prompts/validators.test.js +19 -0
  220. package/src/registries/index.js +2 -0
  221. package/src/registries/lifter.js +43 -0
  222. package/src/registries/lifter.test.js +63 -0
  223. package/src/registries/npm-config/list-builder.js +9 -0
  224. package/src/registries/npm-config/list-builder.test.js +43 -0
  225. package/src/registries/tester.js +3 -0
  226. package/src/registries/tester.test.js +9 -0
  227. package/src/runkit/badge/index.js +1 -0
  228. package/src/runkit/badge/scaffolder.js +13 -0
  229. package/src/runkit/badge/scaffolder.test.js +22 -0
  230. package/src/runkit/index.js +4 -0
  231. package/src/runkit/lifter.js +5 -0
  232. package/src/runkit/lifter.test.js +17 -0
  233. package/src/runkit/remover.js +3 -0
  234. package/src/runkit/remover.test.js +9 -0
  235. package/src/runkit/scaffolder.js +11 -0
  236. package/src/runkit/scaffolder.test.js +35 -0
  237. package/src/runkit/tester.js +3 -0
  238. package/src/runkit/tester.test.js +16 -0
  239. package/src/scaffolder.js +155 -0
  240. package/src/scaffolder.test.js +239 -0
  241. package/src/tester.js +17 -0
  242. package/src/tester.test.js +37 -0
  243. package/src/testing/index.js +1 -0
  244. package/src/testing/scaffolder.js +31 -0
  245. package/src/testing/scaffolder.test.js +63 -0
  246. package/src/testing/unit/index.js +1 -0
  247. package/src/testing/unit/prompt.js +15 -0
  248. package/src/testing/unit/prompt.test.js +33 -0
  249. package/src/testing/unit/scaffolder.js +30 -0
  250. package/src/testing/unit/scaffolder.test.js +54 -0
  251. package/src/vcs/ignore-lists-builder.js +6 -0
  252. package/src/vcs/ignore-lists-builder.test.js +25 -0
  253. package/src/vcs/schema.js +7 -0
  254. package/src/vcs/schema.test.js +40 -0
  255. package/src/verification/index.js +1 -0
  256. package/src/verification/scaffolder.js +35 -0
  257. package/src/verification/scaffolder.test.js +56 -0
@@ -0,0 +1,7 @@
1
+ import deepmerge from 'deepmerge';
2
+ import {scaffold as scaffoldCodecov} from '@form8ion/codecov';
3
+ import {scaffold as scaffoldC8} from '@form8ion/c8';
4
+
5
+ export default async function scaffoldCoverage({projectRoot, vcs, visibility, pathWithinParent}) {
6
+ return deepmerge(await scaffoldC8({projectRoot}), await scaffoldCodecov({vcs, visibility, pathWithinParent}));
7
+ }
@@ -0,0 +1,32 @@
1
+ import {scaffold as scaffoldCodecov} from '@form8ion/codecov';
2
+ import {scaffold as scaffoldC8} from '@form8ion/c8';
3
+
4
+ import {describe, vi, it, expect, afterEach} from 'vitest';
5
+ import any from '@travi/any';
6
+ import {when} from 'vitest-when';
7
+
8
+ import scaffold from './scaffolder.js';
9
+
10
+ vi.mock('@form8ion/codecov');
11
+ vi.mock('@form8ion/c8');
12
+
13
+ describe('coverage scaffolder', () => {
14
+ afterEach(() => {
15
+ vi.clearAllMocks();
16
+ });
17
+
18
+ it('should scaffold coverage measurement and reporting', async () => {
19
+ const vcs = any.simpleObject();
20
+ const visibility = any.word();
21
+ const projectRoot = any.string();
22
+ const c8Results = any.simpleObject();
23
+ const codecovResults = any.simpleObject();
24
+ const pathWithinParent = any.string();
25
+ when(scaffoldC8).calledWith({projectRoot}).thenResolve(c8Results);
26
+ when(scaffoldCodecov).calledWith({vcs, visibility, pathWithinParent}).thenResolve(codecovResults);
27
+
28
+ const results = await scaffold({vcs, visibility, projectRoot, pathWithinParent});
29
+
30
+ expect(results).toEqual({...c8Results, ...codecovResults});
31
+ });
32
+ });
@@ -0,0 +1,10 @@
1
+ import {directoryExists} from '@form8ion/core';
2
+ import {test as c8IsConfigured} from '@form8ion/c8';
3
+
4
+ import nycIsConfigured from './nyc/tester.js';
5
+
6
+ export default async function testCoverageBeingCollected({projectRoot}) {
7
+ const [c8Exists, nycExists] = await Promise.all([c8IsConfigured({projectRoot}), nycIsConfigured({projectRoot})]);
8
+
9
+ return c8Exists || nycExists || directoryExists(`${projectRoot}/coverage`);
10
+ }
@@ -0,0 +1,50 @@
1
+ import {directoryExists} from '@form8ion/core';
2
+ import {test as c8IsPresent} from '@form8ion/c8';
3
+
4
+ import any from '@travi/any';
5
+ import {afterEach, vi, describe, expect, it} from 'vitest';
6
+ import {when} from 'vitest-when';
7
+
8
+ import nycIsPresent from './nyc/tester.js';
9
+ import coverageIsConfigured from './tester.js';
10
+
11
+ vi.mock('@form8ion/core');
12
+ vi.mock('@form8ion/c8');
13
+ vi.mock('./nyc/tester.js');
14
+
15
+ describe('coverage predicate', () => {
16
+ const projectRoot = any.string();
17
+
18
+ afterEach(() => {
19
+ vi.clearAllMocks();
20
+ });
21
+
22
+ it('should return `true` when nyc is detected', async () => {
23
+ when(nycIsPresent).calledWith({projectRoot}).thenResolve(true);
24
+
25
+ expect(await coverageIsConfigured({projectRoot})).toBe(true);
26
+ });
27
+
28
+ it('should return `true` when c8 is detected', async () => {
29
+ when(nycIsPresent).calledWith({projectRoot}).thenResolve(false);
30
+ when(c8IsPresent).calledWith({projectRoot}).thenResolve(true);
31
+
32
+ expect(await coverageIsConfigured({projectRoot})).toBe(true);
33
+ });
34
+
35
+ it('should return `true` if the `coverage/` directory exists', async () => {
36
+ when(nycIsPresent).calledWith({projectRoot}).thenResolve(false);
37
+ when(c8IsPresent).calledWith({projectRoot}).thenResolve(false);
38
+ when(directoryExists).calledWith(`${projectRoot}/coverage`).thenResolve(true);
39
+
40
+ expect(await coverageIsConfigured({projectRoot})).toBe(true);
41
+ });
42
+
43
+ it('should return `false` when neither c8 nor nyc are detected', async () => {
44
+ when(nycIsPresent).calledWith({projectRoot}).thenResolve(false);
45
+ when(c8IsPresent).calledWith({projectRoot}).thenResolve(false);
46
+ when(directoryExists).calledWith(`${projectRoot}/coverage`).thenResolve(false);
47
+
48
+ expect(await coverageIsConfigured({projectRoot})).toBe(false);
49
+ });
50
+ });
@@ -0,0 +1,3 @@
1
+ export {default as install} from './installer.js';
2
+ export {default as remove} from './remover.js';
3
+ export {default as process} from './processor.js';
@@ -0,0 +1,25 @@
1
+ import {info, warn} from '@travi/cli-messages';
2
+ import {DEV_DEPENDENCY_TYPE, packageManagers} from '@form8ion/javascript-core';
3
+
4
+ import {execa} from 'execa';
5
+ import {getDependencyTypeFlag, getExactFlag, getInstallationCommandFor} from './package-managers.js';
6
+
7
+ export default async function installDependencies(
8
+ dependencies,
9
+ dependenciesType,
10
+ projectRoot,
11
+ packageManager = packageManagers.NPM
12
+ ) {
13
+ if (dependencies.length) {
14
+ info(`Installing ${dependenciesType} dependencies`, {level: 'secondary'});
15
+
16
+ await execa(
17
+ `. ~/.nvm/nvm.sh && nvm use && ${packageManager} ${
18
+ getInstallationCommandFor(packageManager)
19
+ } ${[...new Set(dependencies)].join(' ')} --${getDependencyTypeFlag(packageManager, dependenciesType)}${
20
+ DEV_DEPENDENCY_TYPE === dependenciesType ? ` --${getExactFlag(packageManager)}` : ''
21
+ }`,
22
+ {shell: true, cwd: projectRoot}
23
+ );
24
+ } else warn(`No ${dependenciesType} dependencies to install`);
25
+ }
@@ -0,0 +1,77 @@
1
+ import {execa} from 'execa';
2
+ import {DEV_DEPENDENCY_TYPE, packageManagers} from '@form8ion/javascript-core';
3
+
4
+ import {vi, it, describe, expect, beforeEach} from 'vitest';
5
+ import any from '@travi/any';
6
+ import {when} from 'vitest-when';
7
+
8
+ import {getDependencyTypeFlag, getInstallationCommandFor, getExactFlag} from './package-managers.js';
9
+ import install from './installer.js';
10
+
11
+ vi.mock('execa');
12
+ vi.mock('./package-managers.js');
13
+
14
+ describe('dependencies installer', () => {
15
+ const projectRoot = any.string();
16
+ const packageManager = any.word();
17
+ const installationCommand = any.string();
18
+ const typeFlag = any.word();
19
+ const duplicateDependency = any.word();
20
+ const uniqueDependencies = any.listOf(any.word);
21
+ const dependencies = [duplicateDependency, ...uniqueDependencies, duplicateDependency];
22
+ const exactFlag = any.word();
23
+
24
+ beforeEach(() => {
25
+ when(getInstallationCommandFor).calledWith(packageManager).thenReturn(installationCommand);
26
+ });
27
+
28
+ it('should avoid execution when there are no dependencies to install', async () => {
29
+ await install([]);
30
+
31
+ expect(execa).not.toHaveBeenCalled();
32
+ });
33
+
34
+ it('should install dependencies', async () => {
35
+ const dependenciesType = any.word();
36
+ when(getDependencyTypeFlag).calledWith(packageManager, dependenciesType).thenReturn(typeFlag);
37
+
38
+ await install(dependencies, dependenciesType, projectRoot, packageManager);
39
+
40
+ expect(execa).toHaveBeenCalledWith(
41
+ `. ~/.nvm/nvm.sh && nvm use && ${packageManager} ${installationCommand} ${
42
+ [duplicateDependency, ...uniqueDependencies].join(' ')
43
+ } --${typeFlag}`,
44
+ {shell: true, cwd: projectRoot}
45
+ );
46
+ });
47
+
48
+ it('should pin versions when installing dev-dependencies', async () => {
49
+ await install(dependencies, DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
50
+ when(getDependencyTypeFlag).calledWith(packageManager, DEV_DEPENDENCY_TYPE).thenReturn(typeFlag);
51
+ when(getExactFlag).calledWith(packageManager).thenReturn(exactFlag);
52
+
53
+ await install(dependencies, DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
54
+
55
+ expect(execa).toHaveBeenCalledWith(
56
+ `. ~/.nvm/nvm.sh && nvm use && ${packageManager} ${installationCommand} ${
57
+ [duplicateDependency, ...uniqueDependencies].join(' ')
58
+ } --${typeFlag} --${exactFlag}`,
59
+ {shell: true, cwd: projectRoot}
60
+ );
61
+ });
62
+
63
+ it('should default to `npm` when the package-manager is not specified', async () => {
64
+ when(getDependencyTypeFlag).calledWith(packageManagers.NPM, DEV_DEPENDENCY_TYPE).thenReturn(typeFlag);
65
+ when(getExactFlag).calledWith(packageManagers.NPM).thenReturn(exactFlag);
66
+ when(getInstallationCommandFor).calledWith(packageManagers.NPM).thenReturn(installationCommand);
67
+
68
+ await install(dependencies, DEV_DEPENDENCY_TYPE, projectRoot);
69
+
70
+ expect(execa).toHaveBeenCalledWith(
71
+ `. ~/.nvm/nvm.sh && nvm use && ${packageManagers.NPM} ${installationCommand} ${
72
+ [duplicateDependency, ...uniqueDependencies].join(' ')
73
+ } --${typeFlag} --${exactFlag}`,
74
+ {shell: true, cwd: projectRoot}
75
+ );
76
+ });
77
+ });
@@ -0,0 +1,32 @@
1
+ import {DEV_DEPENDENCY_TYPE, PROD_DEPENDENCY_TYPE, packageManagers} from '@form8ion/javascript-core';
2
+
3
+ export const details = {
4
+ [packageManagers.NPM]: {
5
+ installationCommand: 'install',
6
+ installationFlags: {
7
+ [DEV_DEPENDENCY_TYPE]: `save-${DEV_DEPENDENCY_TYPE}`,
8
+ [PROD_DEPENDENCY_TYPE]: `save-${PROD_DEPENDENCY_TYPE}`,
9
+ exact: 'save-exact'
10
+ }
11
+ },
12
+ [packageManagers.YARN]: {
13
+ installationCommand: 'add',
14
+ installationFlags: {
15
+ [DEV_DEPENDENCY_TYPE]: DEV_DEPENDENCY_TYPE,
16
+ [PROD_DEPENDENCY_TYPE]: PROD_DEPENDENCY_TYPE,
17
+ exact: 'exact'
18
+ }
19
+ }
20
+ };
21
+
22
+ export function getInstallationCommandFor(manager) {
23
+ return details[manager].installationCommand;
24
+ }
25
+
26
+ export function getDependencyTypeFlag(manager, type) {
27
+ return details[manager].installationFlags[type];
28
+ }
29
+
30
+ export function getExactFlag(manager) {
31
+ return details[manager].installationFlags.exact;
32
+ }
@@ -0,0 +1,46 @@
1
+ import {DEV_DEPENDENCY_TYPE, packageManagers, PROD_DEPENDENCY_TYPE} from '@form8ion/javascript-core';
2
+
3
+ import {describe, expect, it} from 'vitest';
4
+ import {details, getDependencyTypeFlag, getExactFlag, getInstallationCommandFor} from './package-managers.js';
5
+
6
+ describe('package managers', () => {
7
+ describe('details', () => {
8
+ it('should map the names to the cli names', () => {
9
+ expect(packageManagers.NPM).toEqual('npm');
10
+ expect(packageManagers.YARN).toEqual('yarn');
11
+ });
12
+
13
+ it('should define installation details for npm correctly', () => {
14
+ const {installationCommand, installationFlags} = details[packageManagers.NPM];
15
+
16
+ expect(installationCommand).toEqual('install');
17
+ expect(installationFlags[DEV_DEPENDENCY_TYPE]).toEqual('save-dev');
18
+ expect(installationFlags[PROD_DEPENDENCY_TYPE]).toEqual('save-prod');
19
+ expect(installationFlags.exact).toEqual('save-exact');
20
+ });
21
+
22
+ it('should define installation details correctly for yarn', () => {
23
+ const {installationCommand, installationFlags} = details[packageManagers.YARN];
24
+
25
+ expect(installationCommand).toEqual('add');
26
+ expect(installationFlags[DEV_DEPENDENCY_TYPE]).toEqual('dev');
27
+ expect(installationFlags[PROD_DEPENDENCY_TYPE]).toEqual('prod');
28
+ expect(installationFlags.exact).toEqual('exact');
29
+ });
30
+ });
31
+
32
+ describe('resolvers', () => {
33
+ function assertPackageManagerDetails(manager) {
34
+ const {installationCommand, installationFlags} = details[manager];
35
+
36
+ expect(getInstallationCommandFor(manager)).toEqual(installationCommand);
37
+ expect(getDependencyTypeFlag(manager, DEV_DEPENDENCY_TYPE)).toEqual(installationFlags[DEV_DEPENDENCY_TYPE]);
38
+ expect(getDependencyTypeFlag(manager, PROD_DEPENDENCY_TYPE)).toEqual(installationFlags[PROD_DEPENDENCY_TYPE]);
39
+ expect(getExactFlag(manager)).toEqual(installationFlags.exact);
40
+ }
41
+
42
+ it('should resolve the proper details for npm', () => assertPackageManagerDetails(packageManagers.NPM));
43
+
44
+ it('should resolve the proper details for yarn', () => assertPackageManagerDetails(packageManagers.YARN));
45
+ });
46
+ });
@@ -0,0 +1,30 @@
1
+ import {error, info} from '@travi/cli-messages';
2
+ import {DEV_DEPENDENCY_TYPE, PROD_DEPENDENCY_TYPE} from '@form8ion/javascript-core';
3
+
4
+ import install from './installer.js';
5
+ import remove from './remover.js';
6
+
7
+ export default async function processDependencies({dependencies = {}, devDependencies, projectRoot, packageManager}) {
8
+ info('Processing dependencies');
9
+
10
+ if (Array.isArray(devDependencies)) {
11
+ throw new Error(
12
+ `devDependencies provided as: ${devDependencies}. Instead, provide under dependencies.javascript.development`
13
+ );
14
+ }
15
+
16
+ if (Array.isArray(dependencies)) {
17
+ throw new Error(`Expected dependencies to be an object. Instead received: ${dependencies}`);
18
+ }
19
+
20
+ const {javascript: {production = [], development = [], remove: dependenciesToRemove = []} = {}} = dependencies;
21
+
22
+ try {
23
+ await install(production, PROD_DEPENDENCY_TYPE, projectRoot, packageManager);
24
+ await install(development, DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
25
+ await remove({packageManager, dependencies: dependenciesToRemove});
26
+ } catch (e) {
27
+ error('Failed to update dependencies');
28
+ error(e, {level: 'secondary'});
29
+ }
30
+ }
@@ -0,0 +1,75 @@
1
+ import {DEV_DEPENDENCY_TYPE, PROD_DEPENDENCY_TYPE} from '@form8ion/javascript-core';
2
+
3
+ import {describe, it, expect, vi} from 'vitest';
4
+ import any from '@travi/any';
5
+
6
+ import installDependencies from './installer.js';
7
+ import removeDependencies from './remover.js';
8
+ import processDependencies from './processor.js';
9
+
10
+ vi.mock('./installer.js');
11
+ vi.mock('./remover.js');
12
+
13
+ describe('dependencies processor', () => {
14
+ const projectRoot = any.string();
15
+ const packageManager = any.word();
16
+
17
+ it('should process the provided dependency lists', async () => {
18
+ const production = any.listOf(any.word);
19
+ const development = any.listOf(any.word);
20
+ const remove = any.listOf(any.word);
21
+
22
+ await processDependencies({
23
+ dependencies: {javascript: {production, development, remove}},
24
+ projectRoot,
25
+ packageManager
26
+ });
27
+
28
+ expect(installDependencies).toHaveBeenCalledWith(production, PROD_DEPENDENCY_TYPE, projectRoot, packageManager);
29
+ expect(installDependencies).toHaveBeenCalledWith(development, DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
30
+ expect(removeDependencies).toHaveBeenCalledWith({packageManager, dependencies: remove});
31
+ });
32
+
33
+ it('should process as empty lists when dependencies are not provided', async () => {
34
+ await processDependencies({projectRoot, packageManager});
35
+
36
+ expect(installDependencies).toHaveBeenCalledWith([], PROD_DEPENDENCY_TYPE, projectRoot, packageManager);
37
+ expect(installDependencies).toHaveBeenCalledWith([], DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
38
+ });
39
+
40
+ it('should process as empty lists when javascript dependencies are not provided', async () => {
41
+ await processDependencies({projectRoot, packageManager, dependencies: {}});
42
+
43
+ expect(installDependencies).toHaveBeenCalledWith([], PROD_DEPENDENCY_TYPE, projectRoot, packageManager);
44
+ expect(installDependencies).toHaveBeenCalledWith([], DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
45
+ });
46
+
47
+ it('should process as empty lists when dependency types are not provided', async () => {
48
+ await processDependencies({projectRoot, packageManager, dependencies: {javascript: {}}});
49
+
50
+ expect(installDependencies).toHaveBeenCalledWith([], PROD_DEPENDENCY_TYPE, projectRoot, packageManager);
51
+ expect(installDependencies).toHaveBeenCalledWith([], DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
52
+ expect(removeDependencies).toHaveBeenCalledWith({packageManager, dependencies: []});
53
+ });
54
+
55
+ it('should prevent an installation error from bubbling', async () => {
56
+ installDependencies.mockRejectedValue(new Error());
57
+
58
+ await expect(() => processDependencies({dependencies: {javascript: {}}})).not.toThrowError();
59
+ });
60
+
61
+ it('should throw an error if dependencies is defined as an array rather than an object', async () => {
62
+ const dependencies = any.listOf(any.word);
63
+
64
+ await expect(() => processDependencies({dependencies}))
65
+ .rejects.toThrowError(`Expected dependencies to be an object. Instead received: ${dependencies}`);
66
+ });
67
+
68
+ it('should throw an error if devDependencies is defined as an array', async () => {
69
+ const devDependencies = any.listOf(any.word);
70
+
71
+ await expect(() => processDependencies({devDependencies})).rejects.toThrowError(
72
+ `devDependencies provided as: ${devDependencies}. Instead, provide under dependencies.javascript.development`
73
+ );
74
+ });
75
+ });
@@ -0,0 +1,10 @@
1
+ import {execa} from 'execa';
2
+ import {info} from '@travi/cli-messages';
3
+
4
+ export default async function removeDependencies({packageManager, dependencies}) {
5
+ if (dependencies.length) {
6
+ info('Removing dependencies dependencies', {level: 'secondary'});
7
+
8
+ await execa(packageManager, ['remove', ...dependencies]);
9
+ }
10
+ }
@@ -0,0 +1,28 @@
1
+ import {execa} from 'execa';
2
+
3
+ import any from '@travi/any';
4
+ import {describe, it, expect, vi} from 'vitest';
5
+
6
+ import removeDependencies from './remover.js';
7
+
8
+ vi.mock('execa');
9
+
10
+ describe('dependency remover', () => {
11
+ it('should remove the dependencies using the named package manager', async () => {
12
+ const packageManager = any.word();
13
+ const dependencies = any.listOf(any.word);
14
+
15
+ await removeDependencies({packageManager, dependencies});
16
+
17
+ expect(execa).toHaveBeenCalledWith(packageManager, ['remove', ...dependencies]);
18
+ });
19
+
20
+ it('should not attempt to remove when no dependencies are provided', async () => {
21
+ const packageManager = any.word();
22
+ const dependencies = [];
23
+
24
+ await removeDependencies({packageManager, dependencies});
25
+
26
+ expect(execa).not.toHaveBeenCalled();
27
+ });
28
+ });
@@ -0,0 +1,10 @@
1
+ import writeConfig from './writer.js';
2
+ import loadConfig from './loader.js';
3
+
4
+ export default async function addIgnores({projectRoot, ignore}) {
5
+ if (ignore) {
6
+ const existingConfig = await loadConfig({projectRoot});
7
+
8
+ await writeConfig({projectRoot, config: {...existingConfig, ignore: [`./${ignore}/`]}});
9
+ }
10
+ }
@@ -0,0 +1,33 @@
1
+ import any from '@travi/any';
2
+ import {describe, expect, it, vi} from 'vitest';
3
+ import {when} from 'vitest-when';
4
+
5
+ import writeConfig from './writer.js';
6
+ import loadConfig from './loader.js';
7
+ import addIgnore from './ignore-adder.js';
8
+
9
+ vi.mock('node:fs');
10
+ vi.mock('./writer.js');
11
+ vi.mock('./loader.js');
12
+
13
+ describe('babel ignore adder', () => {
14
+ const projectRoot = any.string();
15
+
16
+ it('should add the provided ignore to the existing config', async () => {
17
+ const pathToIgnore = any.string();
18
+ const existingConfig = any.simpleObject();
19
+ when(loadConfig).calledWith({projectRoot}).thenResolve(existingConfig);
20
+
21
+ await addIgnore({projectRoot, ignore: pathToIgnore});
22
+
23
+ expect(writeConfig)
24
+ .toHaveBeenCalledWith({projectRoot, config: {...existingConfig, ignore: [`./${pathToIgnore}/`]}});
25
+ });
26
+
27
+ it('should not update the config if no `buildDirectory` is provided', async () => {
28
+ await addIgnore({projectRoot});
29
+
30
+ expect(loadConfig).not.toHaveBeenCalled();
31
+ expect(writeConfig).not.toHaveBeenCalled();
32
+ });
33
+ });
@@ -0,0 +1,3 @@
1
+ export {default as write} from './writer.js';
2
+ export {default as load} from './loader.js';
3
+ export {default as addIgnore} from './ignore-adder.js';
@@ -0,0 +1,5 @@
1
+ import {fileTypes, loadConfigFile} from '@form8ion/core';
2
+
3
+ export default function loadBabelConfig({projectRoot}) {
4
+ return loadConfigFile({path: projectRoot, name: '.babelrc', format: fileTypes.JSON});
5
+ }
@@ -0,0 +1,21 @@
1
+ import {fileTypes, loadConfigFile} from '@form8ion/core';
2
+
3
+ import {describe, it, expect, vi} from 'vitest';
4
+ import any from '@travi/any';
5
+ import {when} from 'vitest-when';
6
+
7
+ import loadConfig from './loader.js';
8
+
9
+ vi.mock('@form8ion/core');
10
+
11
+ describe('babel config loader', () => {
12
+ it('should load config from an existing config file', async () => {
13
+ const projectRoot = any.string();
14
+ const parsedConfig = any.simpleObject();
15
+ when(loadConfigFile)
16
+ .calledWith({name: '.babelrc', format: fileTypes.JSON, path: projectRoot})
17
+ .thenResolve(parsedConfig);
18
+
19
+ expect(await loadConfig({projectRoot})).toEqual(parsedConfig);
20
+ });
21
+ });
@@ -0,0 +1,6 @@
1
+ import {fileTypes} from '@form8ion/core';
2
+ import {write} from '@form8ion/config-file';
3
+
4
+ export default function writeBabelConfig({projectRoot, config}) {
5
+ return write({path: projectRoot, name: 'babel', format: fileTypes.JSON, config});
6
+ }
@@ -0,0 +1,20 @@
1
+ import * as configFile from '@form8ion/config-file';
2
+ import {fileTypes} from '@form8ion/core';
3
+
4
+ import any from '@travi/any';
5
+ import {describe, expect, it, vi} from 'vitest';
6
+
7
+ import write from './writer.js';
8
+
9
+ vi.mock('@form8ion/config-file');
10
+
11
+ describe('babel config writer', () => {
12
+ it('should write the provided config to the rc file', async () => {
13
+ const projectRoot = any.string();
14
+ const config = any.simpleObject();
15
+
16
+ await write({projectRoot, config});
17
+
18
+ expect(configFile.write).toHaveBeenCalledWith({path: projectRoot, name: 'babel', format: fileTypes.JSON, config});
19
+ });
20
+ });
@@ -0,0 +1,3 @@
1
+ export {default as scaffold} from './scaffolder.js';
2
+ export {default as lift} from './lifter.js';
3
+ export {default as test} from './predicate.js';
@@ -0,0 +1,7 @@
1
+ import {addIgnore} from './config/index.js';
2
+
3
+ export default async function liftBabel({results, projectRoot}) {
4
+ await addIgnore({ignore: results.buildDirectory, projectRoot});
5
+
6
+ return {};
7
+ }
@@ -0,0 +1,17 @@
1
+ import any from '@travi/any';
2
+ import {describe, expect, it, vi} from 'vitest';
3
+
4
+ import addIgnore from './config/ignore-adder.js';
5
+ import lift from './lifter.js';
6
+
7
+ vi.mock('./config/ignore-adder.js');
8
+
9
+ describe('babel-lifter', () => {
10
+ it('should ignore the build directory', async () => {
11
+ const projectRoot = any.string();
12
+ const buildDirectory = any.string();
13
+
14
+ expect(await lift({results: {buildDirectory}, projectRoot})).toEqual({});
15
+ expect(addIgnore).toHaveBeenCalledWith({ignore: buildDirectory, projectRoot});
16
+ });
17
+ });
@@ -0,0 +1,5 @@
1
+ import {fileExists} from '@form8ion/core';
2
+
3
+ export default function babelIsInUse({projectRoot}) {
4
+ return fileExists(`${projectRoot}/.babelrc.json`);
5
+ }
@@ -0,0 +1,25 @@
1
+ import {fileExists} from '@form8ion/core';
2
+
3
+ import {expect, describe, vi, it} from 'vitest';
4
+ import any from '@travi/any';
5
+ import {when} from 'vitest-when';
6
+
7
+ import babelExists from './predicate.js';
8
+
9
+ vi.mock('@form8ion/core');
10
+
11
+ describe('babel predicate', () => {
12
+ const projectRoot = any.string();
13
+
14
+ it('should return `true` when a babel config exists', async () => {
15
+ when(fileExists).calledWith(`${projectRoot}/.babelrc.json`).thenResolve(true);
16
+
17
+ expect(await babelExists({projectRoot})).toBe(true);
18
+ });
19
+
20
+ it('should return `false` when a babel config exists', async () => {
21
+ when(fileExists).calledWith(`${projectRoot}/.babelrc.json`).thenResolve(false);
22
+
23
+ expect(await babelExists({projectRoot})).toBe(false);
24
+ });
25
+ });
@@ -0,0 +1,14 @@
1
+ import {write} from './config/index.js';
2
+
3
+ export default async function scaffoldBabel({projectRoot, preset}) {
4
+ if (!preset) {
5
+ throw new Error('No babel preset provided. Cannot configure babel transpilation');
6
+ }
7
+
8
+ await write({projectRoot, config: {presets: [preset.name]}});
9
+
10
+ return {
11
+ dependencies: {javascript: {development: ['@babel/register', preset.packageName]}},
12
+ eslint: {}
13
+ };
14
+ }
@@ -0,0 +1,28 @@
1
+ import any from '@travi/any';
2
+ import {describe, it, expect, vi} from 'vitest';
3
+
4
+ import {write} from './config/index.js';
5
+ import scaffoldBabel from './scaffolder.js';
6
+
7
+ vi.mock('./config/index.js');
8
+
9
+ describe('babel scaffolder', () => {
10
+ const projectRoot = any.string();
11
+
12
+ it('should write the babelrc if a preset is provided', async () => {
13
+ const babelPresetName = any.string();
14
+ const babelPresetPackageName = any.word();
15
+ const babelPreset = {name: babelPresetName, packageName: babelPresetPackageName};
16
+
17
+ expect(await scaffoldBabel({preset: babelPreset, projectRoot, tests: {unit: true}}))
18
+ .toEqual({dependencies: {javascript: {development: ['@babel/register', babelPresetPackageName]}}, eslint: {}});
19
+ expect(write).toHaveBeenCalledWith({projectRoot, config: {presets: [babelPresetName]}});
20
+ });
21
+
22
+ it('should throw an error if a preset is not defined', async () => {
23
+ await expect(() => scaffoldBabel({preset: undefined, projectRoot}))
24
+ .rejects.toThrowError('No babel preset provided. Cannot configure babel transpilation');
25
+
26
+ expect(write).not.toHaveBeenCalled();
27
+ });
28
+ });
@@ -0,0 +1,2 @@
1
+ export {default as scaffold} from './scaffolder.js';
2
+ export {test, lift} from './babel/index.js';