Skip to content

Commit

Permalink
Merge pull request #1666 from form8ion/alpha
Browse files Browse the repository at this point in the history
  • Loading branch information
travi authored Jul 30, 2024
2 parents d42f64a + 0297618 commit 6033737
Show file tree
Hide file tree
Showing 35 changed files with 446 additions and 393 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
extends:
- '@form8ion'
- '@form8ion/cucumber'

parserOptions:
ecmaVersion: 2022
8 changes: 6 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ permissions:
contents: read
jobs:
release:
uses: >-
form8ion/.github/.github/workflows/release-package-semantic-release-19.yml@d7062208039222450ac7926b68f3a30d32285f26
permissions:
contents: write
id-token: write
issues: write
pull-requests: write
uses: form8ion/.github/.github/workflows/release-package.yml@master
secrets:
NPM_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
5 changes: 1 addition & 4 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx --no-install commitlint --edit $1
npx --no-install commitlint --edit $1
5 changes: 1 addition & 4 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm test
npm test
2 changes: 1 addition & 1 deletion .remarkrc.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
exports.settings = {
listItemIndent: 1,
listItemIndent: 'one',
emphasis: '_',
strong: '_',
bullet: '*',
Expand Down
49 changes: 30 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,28 +77,39 @@ import {lift, questionNames, scaffold} from '@form8ion/project';
#### Execute

```javascript
await scaffold({
decisions: {
[questionNames.PROJECT_NAME]: 'my-project',
[questionNames.LICENSE]: 'MIT',
[questionNames.VISIBILITY]: 'Public',
[questionNames.DESCRIPTION]: 'My project',
[questionNames.GIT_REPO]: false,
[questionNames.COPYRIGHT_HOLDER]: 'John Smith',
[questionNames.COPYRIGHT_YEAR]: '2022',
[questionNames.PROJECT_LANGUAGE]: 'foo'
await scaffold({
decisions: {
[questionNames.PROJECT_NAME]: 'my-project',
[questionNames.LICENSE]: 'MIT',
[questionNames.VISIBILITY]: 'Public',
[questionNames.DESCRIPTION]: 'My project',
[questionNames.GIT_REPO]: false,
[questionNames.COPYRIGHT_HOLDER]: 'John Smith',
[questionNames.COPYRIGHT_YEAR]: '2022',
[questionNames.PROJECT_LANGUAGE]: 'foo'
},
plugins: {
dependencyUpdaters: {
bar: {scaffold: options => options}
},
languages: {
foo: options => options
foo: {scaffold: options => options}
},
vcsHosts: {
baz: {
scaffold: options => options,
prompt: () => ({repoOwner: 'form8ion'})
}
}
});

await lift({
projectRoot: process.cwd(),
results: {},
enhancers: {foo: {test: () => true, lift: () => ({})}},
vcs: {}
});
}
});

await lift({
projectRoot: process.cwd(),
results: {},
enhancers: {foo: {test: () => true, lift: () => ({})}},
vcs: {}
});
```

### API
Expand Down
57 changes: 34 additions & 23 deletions example.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,42 @@ import {lift, questionNames, scaffold} from './lib/index.js';

// #### Execute

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

await scaffold({
decisions: {
[questionNames.PROJECT_NAME]: 'my-project',
[questionNames.LICENSE]: 'MIT',
[questionNames.VISIBILITY]: 'Public',
[questionNames.DESCRIPTION]: 'My project',
[questionNames.GIT_REPO]: false,
[questionNames.COPYRIGHT_HOLDER]: 'John Smith',
[questionNames.COPYRIGHT_YEAR]: '2022',
[questionNames.PROJECT_LANGUAGE]: 'foo'
await scaffold({
decisions: {
[questionNames.PROJECT_NAME]: 'my-project',
[questionNames.LICENSE]: 'MIT',
[questionNames.VISIBILITY]: 'Public',
[questionNames.DESCRIPTION]: 'My project',
[questionNames.GIT_REPO]: false,
[questionNames.COPYRIGHT_HOLDER]: 'John Smith',
[questionNames.COPYRIGHT_YEAR]: '2022',
[questionNames.PROJECT_LANGUAGE]: 'foo'
},
plugins: {
dependencyUpdaters: {
bar: {scaffold: options => options}
},
languages: {
foo: options => options
foo: {scaffold: options => options}
},
vcsHosts: {
baz: {
scaffold: options => options,
prompt: () => ({repoOwner: 'form8ion'})
}
}
});
}
});

await lift({
projectRoot: process.cwd(),
results: {},
enhancers: {foo: {test: () => true, lift: () => ({})}},
vcs: {}
});
// remark-usage-ignore-next
})();
await lift({
projectRoot: process.cwd(),
results: {},
enhancers: {foo: {test: () => true, lift: () => ({})}},
vcs: {}
});
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
}
},
"dependencies": {
"@form8ion/core": "^4.3.0",
"@form8ion/core": "^4.6.0",
"@form8ion/execa-wrapper": "^1.0.0",
"@form8ion/git": "^1.2.0",
"@form8ion/overridable-prompts": "^1.1.0",
Expand Down
10 changes: 5 additions & 5 deletions src/dependency-updater/scaffolder.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import {questionNames} from '../prompts/question-names.js';
import {promptForDependencyUpdaterChoice} from './prompt.js';

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

const scaffolderDetails = scaffolders[
(await promptForDependencyUpdaterChoice(scaffolders, decisions))[questionNames.DEPENDENCY_UPDATER]
const plugin = plugins[
(await promptForDependencyUpdaterChoice(plugins, decisions))[questionNames.DEPENDENCY_UPDATER]
];

if (scaffolderDetails) return scaffolderDetails.scaffolder(options);
if (plugin) return plugin.scaffold(options);

return undefined;
}
6 changes: 3 additions & 3 deletions src/dependency-updater/scaffolder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ describe('dependency-updater scaffolder', () => {
const options = any.simpleObject();
const chosenUpdater = any.word();
const chosenUpdaterScaffolder = vi.fn();
const scaffolders = {...any.simpleObject(), [chosenUpdater]: {scaffolder: chosenUpdaterScaffolder}};
const plugins = {...any.simpleObject(), [chosenUpdater]: {scaffold: chosenUpdaterScaffolder}};
const scaffolderResult = any.simpleObject();
when(prompt.promptForDependencyUpdaterChoice)
.calledWith(scaffolders, decisions)
.calledWith(plugins, decisions)
.mockResolvedValue({[questionNames.DEPENDENCY_UPDATER]: chosenUpdater});
when(chosenUpdaterScaffolder).calledWith(options).mockResolvedValue(scaffolderResult);

expect(await scaffoldUpdater(scaffolders, decisions, options)).toEqual(scaffolderResult);
expect(await scaffoldUpdater(plugins, decisions, options)).toEqual(scaffolderResult);
});

it('should not present a prompt if no updaters are registered', async () => {
Expand Down
5 changes: 2 additions & 3 deletions src/dependency-updater/schema.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import joi from 'joi';
import {optionsSchemas} from '@form8ion/core';

export default joi.object().pattern(/^/, joi.object({
scaffolder: joi.func().arity(1).required()
})).default({});
export default joi.object().pattern(/^/, optionsSchemas.form8ionPlugin).default({});
16 changes: 8 additions & 8 deletions src/dependency-updater/schema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ describe('dependency-updater plugins schema', () => {
it('should return the validated options', () => {
const options = any.objectWithKeys(
any.listOf(any.string),
{factory: () => ({scaffolder: foo => foo})}
{factory: () => ({scaffold: foo => foo})}
);

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

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

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

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

it('should default to an empty map when no updaters are provided', () => {
Expand Down
11 changes: 8 additions & 3 deletions src/language/scaffolder.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
export default function (scaffolders, chosenLanguage, options) {
const scaffolder = scaffolders[chosenLanguage];
import {questionNames} from '../prompts/question-names.js';
import promptForLanguageDetails from './prompt.js';

if (scaffolder) return scaffolder(options);
export default async function (languagePlugins, decisions, options) {
const {[questionNames.PROJECT_LANGUAGE]: chosenLanguage} = await promptForLanguageDetails(languagePlugins, decisions);

const plugin = languagePlugins[chosenLanguage];

if (plugin) return plugin.scaffold(options);

return undefined;
}
16 changes: 13 additions & 3 deletions src/language/scaffolder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,31 @@ import {describe, expect, it, vi} from 'vitest';
import any from '@travi/any';
import {when} from 'jest-when';

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

vi.mock('./prompt.js');

describe('language scaffolder', () => {
it('should scaffold the chosen language', async () => {
const options = any.simpleObject();
const chosenLanguage = any.word();
const scaffolderResult = any.simpleObject();
const decisions = any.simpleObject();
const chosenLanguageScaffolder = vi.fn();
const scaffolders = {...any.simpleObject(), [chosenLanguage]: chosenLanguageScaffolder};
const plugins = {...any.simpleObject(), [chosenLanguage]: {scaffold: chosenLanguageScaffolder}};
when(languagePrompt.default)
.calledWith(plugins, decisions)
.mockResolvedValue({[questionNames.PROJECT_LANGUAGE]: chosenLanguage});
when(chosenLanguageScaffolder).calledWith(options).mockResolvedValue(scaffolderResult);

expect(await scaffold(scaffolders, chosenLanguage, options)).toEqual(scaffolderResult);
expect(await scaffold(plugins, decisions, options)).toEqual(scaffolderResult);
});

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

await scaffold(any.simpleObject(), any.simpleObject(), any.simpleObject());
});
});
3 changes: 2 additions & 1 deletion src/language/schema.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import joi from 'joi';
import {optionsSchemas} from '@form8ion/core';

export default joi.object().pattern(/^/, joi.func().arity(1));
export default joi.object().pattern(/^/, optionsSchemas.form8ionPlugin).default({});
29 changes: 23 additions & 6 deletions src/language/schema.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,35 @@ describe('language plugins schema', () => {
const key = any.word();

it('should return the validated options', () => {
const options = any.objectWithKeys(any.listOf(any.string), {factory: () => foo => foo});
const options = any.objectWithKeys(
any.listOf(any.string),
{factory: () => ({scaffold: foo => foo})}
);

expect(validateOptions(languageSchema, options)).toEqual(options);
});

it('should require a scaffold function to be included', () => {
expect(() => validateOptions(languageSchema, {[key]: any.word()}))
.toThrowError(`"${key}" must be of type function`);
it('should require options to be provided as an object', () => {
expect(() => validateOptions(languageSchema, {[key]: []}))
.toThrowError(`"${key}" must be of type object`);
});

it('should require a `scaffold` property to be included', () => {
expect(() => validateOptions(languageSchema, {[key]: {}}))
.toThrowError(`"${key}.scaffold" is required`);
});

it('should require `scaffold` to be a function', () => {
expect(() => validateOptions(languageSchema, {[key]: {scaffold: any.word()}}))
.toThrowError(`"${key}.scaffold" must be of type function`);
});

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

it('should default to an empty map when no updaters are provided', () => {
expect(validateOptions(languageSchema)).toEqual({});
});
});
8 changes: 5 additions & 3 deletions src/options-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import {decisionsSchema} from './options-schemas.js';

export function validate(options) {
return validateOptions(joi.object({
languages: languagePluginsSchema,
vcsHosts: vcsHostPluginsSchema,
decisions: decisionsSchema,
dependencyUpdaters: dependencyUpdaterPluginsSchema
plugins: joi.object({
dependencyUpdaters: dependencyUpdaterPluginsSchema,
languages: languagePluginsSchema,
vcsHosts: vcsHostPluginsSchema
})
}), options) || {};
}
Loading

0 comments on commit 6033737

Please sign in to comment.