Skip to content

Commit 6033737

Browse files
authored
Merge pull request #1666 from form8ion/alpha
2 parents d42f64a + 0297618 commit 6033737

35 files changed

+446
-393
lines changed

.eslintrc.yml

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
extends:
22
- '@form8ion'
33
- '@form8ion/cucumber'
4+
5+
parserOptions:
6+
ecmaVersion: 2022

.github/workflows/release.yml

+6-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ permissions:
77
contents: read
88
jobs:
99
release:
10-
uses: >-
11-
form8ion/.github/.github/workflows/release-package-semantic-release-19.yml@d7062208039222450ac7926b68f3a30d32285f26
10+
permissions:
11+
contents: write
12+
id-token: write
13+
issues: write
14+
pull-requests: write
15+
uses: form8ion/.github/.github/workflows/release-package.yml@master
1216
secrets:
1317
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}

.husky/commit-msg

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
#!/bin/sh
2-
. "$(dirname "$0")/_/husky.sh"
3-
4-
npx --no-install commitlint --edit $1
1+
npx --no-install commitlint --edit $1

.husky/pre-commit

+1-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
#!/bin/sh
2-
. "$(dirname "$0")/_/husky.sh"
3-
4-
npm test
1+
npm test

.remarkrc.cjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
exports.settings = {
2-
listItemIndent: 1,
2+
listItemIndent: 'one',
33
emphasis: '_',
44
strong: '_',
55
bullet: '*',

README.md

+30-19
Original file line numberDiff line numberDiff line change
@@ -77,28 +77,39 @@ import {lift, questionNames, scaffold} from '@form8ion/project';
7777
#### Execute
7878

7979
```javascript
80-
await scaffold({
81-
decisions: {
82-
[questionNames.PROJECT_NAME]: 'my-project',
83-
[questionNames.LICENSE]: 'MIT',
84-
[questionNames.VISIBILITY]: 'Public',
85-
[questionNames.DESCRIPTION]: 'My project',
86-
[questionNames.GIT_REPO]: false,
87-
[questionNames.COPYRIGHT_HOLDER]: 'John Smith',
88-
[questionNames.COPYRIGHT_YEAR]: '2022',
89-
[questionNames.PROJECT_LANGUAGE]: 'foo'
80+
await scaffold({
81+
decisions: {
82+
[questionNames.PROJECT_NAME]: 'my-project',
83+
[questionNames.LICENSE]: 'MIT',
84+
[questionNames.VISIBILITY]: 'Public',
85+
[questionNames.DESCRIPTION]: 'My project',
86+
[questionNames.GIT_REPO]: false,
87+
[questionNames.COPYRIGHT_HOLDER]: 'John Smith',
88+
[questionNames.COPYRIGHT_YEAR]: '2022',
89+
[questionNames.PROJECT_LANGUAGE]: 'foo'
90+
},
91+
plugins: {
92+
dependencyUpdaters: {
93+
bar: {scaffold: options => options}
9094
},
9195
languages: {
92-
foo: options => options
96+
foo: {scaffold: options => options}
97+
},
98+
vcsHosts: {
99+
baz: {
100+
scaffold: options => options,
101+
prompt: () => ({repoOwner: 'form8ion'})
102+
}
93103
}
94-
});
95-
96-
await lift({
97-
projectRoot: process.cwd(),
98-
results: {},
99-
enhancers: {foo: {test: () => true, lift: () => ({})}},
100-
vcs: {}
101-
});
104+
}
105+
});
106+
107+
await lift({
108+
projectRoot: process.cwd(),
109+
results: {},
110+
enhancers: {foo: {test: () => true, lift: () => ({})}},
111+
vcs: {}
112+
});
102113
```
103114

104115
### API

example.js

+34-23
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,42 @@ import {lift, questionNames, scaffold} from './lib/index.js';
77

88
// #### Execute
99

10-
// remark-usage-ignore-next 2
11-
(async () => {
12-
stubbedFs({templates: {'editorconfig.ini': await fs.readFile(resolve('templates', 'editorconfig.ini'))}});
10+
// remark-usage-ignore-next 4
11+
stubbedFs({
12+
templates: {'editorconfig.ini': await fs.readFile(resolve('templates', 'editorconfig.ini'))},
13+
node_modules: stubbedFs.load('node_modules')
14+
});
1315

14-
await scaffold({
15-
decisions: {
16-
[questionNames.PROJECT_NAME]: 'my-project',
17-
[questionNames.LICENSE]: 'MIT',
18-
[questionNames.VISIBILITY]: 'Public',
19-
[questionNames.DESCRIPTION]: 'My project',
20-
[questionNames.GIT_REPO]: false,
21-
[questionNames.COPYRIGHT_HOLDER]: 'John Smith',
22-
[questionNames.COPYRIGHT_YEAR]: '2022',
23-
[questionNames.PROJECT_LANGUAGE]: 'foo'
16+
await scaffold({
17+
decisions: {
18+
[questionNames.PROJECT_NAME]: 'my-project',
19+
[questionNames.LICENSE]: 'MIT',
20+
[questionNames.VISIBILITY]: 'Public',
21+
[questionNames.DESCRIPTION]: 'My project',
22+
[questionNames.GIT_REPO]: false,
23+
[questionNames.COPYRIGHT_HOLDER]: 'John Smith',
24+
[questionNames.COPYRIGHT_YEAR]: '2022',
25+
[questionNames.PROJECT_LANGUAGE]: 'foo'
26+
},
27+
plugins: {
28+
dependencyUpdaters: {
29+
bar: {scaffold: options => options}
2430
},
2531
languages: {
26-
foo: options => options
32+
foo: {scaffold: options => options}
33+
},
34+
vcsHosts: {
35+
baz: {
36+
scaffold: options => options,
37+
prompt: () => ({repoOwner: 'form8ion'})
38+
}
2739
}
28-
});
40+
}
41+
});
2942

30-
await lift({
31-
projectRoot: process.cwd(),
32-
results: {},
33-
enhancers: {foo: {test: () => true, lift: () => ({})}},
34-
vcs: {}
35-
});
36-
// remark-usage-ignore-next
37-
})();
43+
await lift({
44+
projectRoot: process.cwd(),
45+
results: {},
46+
enhancers: {foo: {test: () => true, lift: () => ({})}},
47+
vcs: {}
48+
});

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
}
5959
},
6060
"dependencies": {
61-
"@form8ion/core": "^4.3.0",
61+
"@form8ion/core": "^4.6.0",
6262
"@form8ion/execa-wrapper": "^1.0.0",
6363
"@form8ion/git": "^1.2.0",
6464
"@form8ion/overridable-prompts": "^1.1.0",

src/dependency-updater/scaffolder.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import {questionNames} from '../prompts/question-names.js';
22
import {promptForDependencyUpdaterChoice} from './prompt.js';
33

4-
export default async function (scaffolders, decisions, options) {
5-
if (!Object.keys(scaffolders).length) return undefined;
4+
export default async function (plugins, decisions, options) {
5+
if (!Object.keys(plugins).length) return undefined;
66

7-
const scaffolderDetails = scaffolders[
8-
(await promptForDependencyUpdaterChoice(scaffolders, decisions))[questionNames.DEPENDENCY_UPDATER]
7+
const plugin = plugins[
8+
(await promptForDependencyUpdaterChoice(plugins, decisions))[questionNames.DEPENDENCY_UPDATER]
99
];
1010

11-
if (scaffolderDetails) return scaffolderDetails.scaffolder(options);
11+
if (plugin) return plugin.scaffold(options);
1212

1313
return undefined;
1414
}

src/dependency-updater/scaffolder.test.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ describe('dependency-updater scaffolder', () => {
1919
const options = any.simpleObject();
2020
const chosenUpdater = any.word();
2121
const chosenUpdaterScaffolder = vi.fn();
22-
const scaffolders = {...any.simpleObject(), [chosenUpdater]: {scaffolder: chosenUpdaterScaffolder}};
22+
const plugins = {...any.simpleObject(), [chosenUpdater]: {scaffold: chosenUpdaterScaffolder}};
2323
const scaffolderResult = any.simpleObject();
2424
when(prompt.promptForDependencyUpdaterChoice)
25-
.calledWith(scaffolders, decisions)
25+
.calledWith(plugins, decisions)
2626
.mockResolvedValue({[questionNames.DEPENDENCY_UPDATER]: chosenUpdater});
2727
when(chosenUpdaterScaffolder).calledWith(options).mockResolvedValue(scaffolderResult);
2828

29-
expect(await scaffoldUpdater(scaffolders, decisions, options)).toEqual(scaffolderResult);
29+
expect(await scaffoldUpdater(plugins, decisions, options)).toEqual(scaffolderResult);
3030
});
3131

3232
it('should not present a prompt if no updaters are registered', async () => {

src/dependency-updater/schema.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import joi from 'joi';
2+
import {optionsSchemas} from '@form8ion/core';
23

3-
export default joi.object().pattern(/^/, joi.object({
4-
scaffolder: joi.func().arity(1).required()
5-
})).default({});
4+
export default joi.object().pattern(/^/, optionsSchemas.form8ionPlugin).default({});

src/dependency-updater/schema.test.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe('dependency-updater plugins schema', () => {
1111
it('should return the validated options', () => {
1212
const options = any.objectWithKeys(
1313
any.listOf(any.string),
14-
{factory: () => ({scaffolder: foo => foo})}
14+
{factory: () => ({scaffold: foo => foo})}
1515
);
1616

1717
expect(validateOptions(dependencyUpdaterPluginsSchema, options)).toEqual(options);
@@ -22,19 +22,19 @@ describe('dependency-updater plugins schema', () => {
2222
.toThrowError(`"${key}" must be of type object`);
2323
});
2424

25-
it('should require a `scaffolder` to be included', () => {
25+
it('should require a `scaffold` property to be included', () => {
2626
expect(() => validateOptions(dependencyUpdaterPluginsSchema, {[key]: {}}))
27-
.toThrowError(`"${key}.scaffolder" is required`);
27+
.toThrowError(`"${key}.scaffold" is required`);
2828
});
2929

30-
it('should require `scaffolder` to be a function', () => {
31-
expect(() => validateOptions(dependencyUpdaterPluginsSchema, {[key]: {scaffolder: any.word()}}))
32-
.toThrowError(`"${key}.scaffolder" must be of type function`);
30+
it('should require `scaffold` to be a function', () => {
31+
expect(() => validateOptions(dependencyUpdaterPluginsSchema, {[key]: {scaffold: any.word()}}))
32+
.toThrowError(`"${key}.scaffold" must be of type function`);
3333
});
3434

3535
it('should require the scaffolder to accept a single argument', () => {
36-
expect(() => validateOptions(dependencyUpdaterPluginsSchema, {[key]: {scaffolder: () => undefined}}))
37-
.toThrowError(`"${key}.scaffolder" must have an arity of 1`);
36+
expect(() => validateOptions(dependencyUpdaterPluginsSchema, {[key]: {scaffold: () => undefined}}))
37+
.toThrowError(`"${key}.scaffold" must have an arity of 1`);
3838
});
3939

4040
it('should default to an empty map when no updaters are provided', () => {

src/language/scaffolder.js

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
export default function (scaffolders, chosenLanguage, options) {
2-
const scaffolder = scaffolders[chosenLanguage];
1+
import {questionNames} from '../prompts/question-names.js';
2+
import promptForLanguageDetails from './prompt.js';
33

4-
if (scaffolder) return scaffolder(options);
4+
export default async function (languagePlugins, decisions, options) {
5+
const {[questionNames.PROJECT_LANGUAGE]: chosenLanguage} = await promptForLanguageDetails(languagePlugins, decisions);
6+
7+
const plugin = languagePlugins[chosenLanguage];
8+
9+
if (plugin) return plugin.scaffold(options);
510

611
return undefined;
712
}

src/language/scaffolder.test.js

+13-3
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,31 @@ import {describe, expect, it, vi} from 'vitest';
22
import any from '@travi/any';
33
import {when} from 'jest-when';
44

5+
import * as languagePrompt from './prompt.js';
6+
import {questionNames} from '../prompts/question-names.js';
57
import scaffold from './scaffolder.js';
68

9+
vi.mock('./prompt.js');
10+
711
describe('language scaffolder', () => {
812
it('should scaffold the chosen language', async () => {
913
const options = any.simpleObject();
1014
const chosenLanguage = any.word();
1115
const scaffolderResult = any.simpleObject();
16+
const decisions = any.simpleObject();
1217
const chosenLanguageScaffolder = vi.fn();
13-
const scaffolders = {...any.simpleObject(), [chosenLanguage]: chosenLanguageScaffolder};
18+
const plugins = {...any.simpleObject(), [chosenLanguage]: {scaffold: chosenLanguageScaffolder}};
19+
when(languagePrompt.default)
20+
.calledWith(plugins, decisions)
21+
.mockResolvedValue({[questionNames.PROJECT_LANGUAGE]: chosenLanguage});
1422
when(chosenLanguageScaffolder).calledWith(options).mockResolvedValue(scaffolderResult);
1523

16-
expect(await scaffold(scaffolders, chosenLanguage, options)).toEqual(scaffolderResult);
24+
expect(await scaffold(plugins, decisions, options)).toEqual(scaffolderResult);
1725
});
1826

1927
it('should not result in an error when choosing a language without a defined scaffolder', async () => {
20-
await scaffold(any.simpleObject(), any.word(), any.simpleObject());
28+
when(languagePrompt.default).mockResolvedValue({[questionNames.PROJECT_LANGUAGE]: any.word()});
29+
30+
await scaffold(any.simpleObject(), any.simpleObject(), any.simpleObject());
2131
});
2232
});

src/language/schema.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import joi from 'joi';
2+
import {optionsSchemas} from '@form8ion/core';
23

3-
export default joi.object().pattern(/^/, joi.func().arity(1));
4+
export default joi.object().pattern(/^/, optionsSchemas.form8ionPlugin).default({});

src/language/schema.test.js

+23-6
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,35 @@ describe('language plugins schema', () => {
99
const key = any.word();
1010

1111
it('should return the validated options', () => {
12-
const options = any.objectWithKeys(any.listOf(any.string), {factory: () => foo => foo});
12+
const options = any.objectWithKeys(
13+
any.listOf(any.string),
14+
{factory: () => ({scaffold: foo => foo})}
15+
);
1316

1417
expect(validateOptions(languageSchema, options)).toEqual(options);
1518
});
1619

17-
it('should require a scaffold function to be included', () => {
18-
expect(() => validateOptions(languageSchema, {[key]: any.word()}))
19-
.toThrowError(`"${key}" must be of type function`);
20+
it('should require options to be provided as an object', () => {
21+
expect(() => validateOptions(languageSchema, {[key]: []}))
22+
.toThrowError(`"${key}" must be of type object`);
23+
});
24+
25+
it('should require a `scaffold` property to be included', () => {
26+
expect(() => validateOptions(languageSchema, {[key]: {}}))
27+
.toThrowError(`"${key}.scaffold" is required`);
28+
});
29+
30+
it('should require `scaffold` to be a function', () => {
31+
expect(() => validateOptions(languageSchema, {[key]: {scaffold: any.word()}}))
32+
.toThrowError(`"${key}.scaffold" must be of type function`);
2033
});
2134

2235
it('should require the scaffolder to accept a single argument', () => {
23-
expect(() => validateOptions(languageSchema, {[key]: () => undefined}))
24-
.toThrowError(`"${key}" must have an arity of 1`);
36+
expect(() => validateOptions(languageSchema, {[key]: {scaffold: () => undefined}}))
37+
.toThrowError(`"${key}.scaffold" must have an arity of 1`);
38+
});
39+
40+
it('should default to an empty map when no updaters are provided', () => {
41+
expect(validateOptions(languageSchema)).toEqual({});
2542
});
2643
});

src/options-validator.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import {decisionsSchema} from './options-schemas.js';
88

99
export function validate(options) {
1010
return validateOptions(joi.object({
11-
languages: languagePluginsSchema,
12-
vcsHosts: vcsHostPluginsSchema,
1311
decisions: decisionsSchema,
14-
dependencyUpdaters: dependencyUpdaterPluginsSchema
12+
plugins: joi.object({
13+
dependencyUpdaters: dependencyUpdaterPluginsSchema,
14+
languages: languagePluginsSchema,
15+
vcsHosts: vcsHostPluginsSchema
16+
})
1517
}), options) || {};
1618
}

0 commit comments

Comments
 (0)