Skip to content

Commit

Permalink
feat(languages): moved the languages plugins under the plugins prop…
Browse files Browse the repository at this point in the history
…erty

BREAKING CHANGE: languages now must be provided under the `plugins` property and as full plugins
rather than a direct scaffolder function
  • Loading branch information
travi committed Jul 24, 2024
1 parent 7330434 commit 70c7b75
Show file tree
Hide file tree
Showing 11 changed files with 60 additions and 40 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,12 @@ import {lift, questionNames, scaffold} from '@form8ion/project';
[questionNames.COPYRIGHT_YEAR]: '2022',
[questionNames.PROJECT_LANGUAGE]: 'foo'
},
languages: {
foo: options => options
},
plugins: {
dependencyUpdaters: {
bar: {scaffold: options => options}
},
languages: {
foo: {scaffold: options => options}
}
}
});
Expand Down
6 changes: 3 additions & 3 deletions example.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ import {lift, questionNames, scaffold} from './lib/index.js';
[questionNames.COPYRIGHT_YEAR]: '2022',
[questionNames.PROJECT_LANGUAGE]: 'foo'
},
languages: {
foo: options => options
},
plugins: {
dependencyUpdaters: {
bar: {scaffold: options => options}
},
languages: {
foo: {scaffold: options => options}
}
}
});
Expand Down
6 changes: 3 additions & 3 deletions src/language/scaffolder.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export default function (scaffolders, chosenLanguage, options) {
const scaffolder = scaffolders[chosenLanguage];
export default function (languagePlugins, chosenLanguage, options) {
const plugin = languagePlugins[chosenLanguage];

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

return undefined;
}
4 changes: 2 additions & 2 deletions src/language/scaffolder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ describe('language scaffolder', () => {
const chosenLanguage = any.word();
const scaffolderResult = any.simpleObject();
const chosenLanguageScaffolder = vi.fn();
const scaffolders = {...any.simpleObject(), [chosenLanguage]: chosenLanguageScaffolder};
const plugins = {...any.simpleObject(), [chosenLanguage]: {scaffold: chosenLanguageScaffolder}};
when(chosenLanguageScaffolder).calledWith(options).mockResolvedValue(scaffolderResult);

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

it('should not result in an error when choosing a language without a defined scaffolder', async () => {
Expand Down
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({});
});
});
3 changes: 1 addition & 2 deletions src/options-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ import {decisionsSchema} from './options-schemas.js';

export function validate(options) {
return validateOptions(joi.object({
languages: languagePluginsSchema,
vcsHosts: vcsHostPluginsSchema,
decisions: decisionsSchema,
plugins: joi.object({dependencyUpdaters: dependencyUpdaterPluginsSchema})
plugins: joi.object({dependencyUpdaters: dependencyUpdaterPluginsSchema, languages: languagePluginsSchema})
}), options) || {};
}
5 changes: 3 additions & 2 deletions src/options-validator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ describe('options validator', () => {
const pluginsSchema = any.simpleObject();
const fullSchema = any.simpleObject();
const validatedOptions = any.simpleObject();
when(joi.object).calledWith({dependencyUpdaters: dependencyUpdaterPluginsSchema}).mockReturnValue(pluginsSchema);
when(joi.object)
.calledWith({dependencyUpdaters: dependencyUpdaterPluginsSchema, languages: languagePluginsSchema})
.mockReturnValue(pluginsSchema);
when(joi.object).calledWith({
languages: languagePluginsSchema,
vcsHosts: vcsHostPluginsSchema,
decisions: decisionsSchema,
plugins: pluginsSchema
Expand Down
2 changes: 1 addition & 1 deletion src/scaffolder.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import lift from './lift.js';

export async function scaffold(options) {
const projectRoot = process.cwd();
const {languages = {}, vcsHosts = {}, decisions, plugins: {dependencyUpdaters}} = validate(options);
const {vcsHosts = {}, decisions, plugins: {dependencyUpdaters, languages = {}}} = validate(options);

const {
[coreQuestionNames.PROJECT_NAME]: projectName,
Expand Down
16 changes: 8 additions & 8 deletions src/scaffolder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ describe('project scaffolder', () => {
const license = any.string();
const projectLanguage = any.word();
const licenseBadge = any.url();
const languageScaffolders = any.simpleObject();
const languages = any.simpleObject();
const vcsHosts = any.simpleObject();
const documentation = any.simpleObject();
const vcs = any.simpleObject();
Expand Down Expand Up @@ -94,7 +94,7 @@ describe('project scaffolder', () => {
const contributingResults = any.simpleObject();
when(optionsValidator.validate)
.calledWith(options)
.mockReturnValue({languages: languageScaffolders, vcsHosts, decisions, plugins: {dependencyUpdaters}});
.mockReturnValue({vcsHosts, decisions, plugins: {dependencyUpdaters, languages}});
when(prompts.promptForBaseDetails)
.calledWith(projectPath, decisions)
.mockResolvedValue({
Expand All @@ -107,7 +107,7 @@ describe('project scaffolder', () => {
[coreQuestionNames.VISIBILITY]: visibility
});
when(languagePrompt.default)
.calledWith(languageScaffolders, decisions)
.calledWith(languages, decisions)
.mockResolvedValue({[questionNames.PROJECT_LANGUAGE]: projectLanguage});
when(scaffoldGit)
.calledWith(gitRepoShouldBeInitialized, projectPath, projectName, vcsHosts, visibility, decisions)
Expand Down Expand Up @@ -259,7 +259,7 @@ describe('project scaffolder', () => {
};
when(optionsValidator.validate)
.calledWith(options)
.mockReturnValue({languages: languageScaffolders, vcsHosts, decisions, plugins: {}});
.mockReturnValue({vcsHosts, decisions, plugins: {languages}});
scaffoldGit.mockResolvedValue(vcs);
liftGit.mockResolvedValue({nextSteps: gitNextSteps});
prompts.promptForBaseDetails.mockResolvedValue({
Expand All @@ -270,9 +270,9 @@ describe('project scaffolder', () => {
[coreQuestionNames.DESCRIPTION]: description
});
when(languagePrompt.default)
.calledWith(languageScaffolders, decisions)
.calledWith(languages, decisions)
.mockResolvedValue({[questionNames.PROJECT_LANGUAGE]: projectLanguage});
when(languageScaffolder.default).calledWith(languageScaffolders, projectLanguage, {
when(languageScaffolder.default).calledWith(languages, projectLanguage, {
projectName,
projectRoot: projectPath,
visibility,
Expand Down Expand Up @@ -308,7 +308,7 @@ describe('project scaffolder', () => {
it('should consider the language details to be optional', async () => {
when(optionsValidator.validate)
.calledWith(options)
.mockReturnValue({languages: languageScaffolders, vcsHosts, decisions, plugins: {}});
.mockReturnValue({vcsHosts, decisions, plugins: {languages}});
scaffoldGit.mockResolvedValue(vcs);
prompts.promptForBaseDetails.mockResolvedValue({
[coreQuestionNames.PROJECT_NAME]: projectName,
Expand All @@ -318,7 +318,7 @@ describe('project scaffolder', () => {
[coreQuestionNames.DESCRIPTION]: description
});
when(languagePrompt.default)
.calledWith(languageScaffolders, decisions)
.calledWith(languages, decisions)
.mockResolvedValue({[questionNames.PROJECT_LANGUAGE]: projectLanguage});
vcsHostScaffolder.default.mockResolvedValue(vcsOriginDetails);
languageScaffolder.default.mockResolvedValue({});
Expand Down
20 changes: 11 additions & 9 deletions test/integration/features/step_definitions/common-steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,22 @@ When(/^the project is scaffolded$/, async function () {
this.projectDescription = any.sentence();

await scaffold({
languages: {
...'Other' !== chosenLanguage && {
[chosenLanguage]: ({projectName}) => {
info(`Scaffolding ${chosenLanguage} language details for ${projectName}`);

return this.languageScaffolderResults;
}
}
},
plugins: {
...this.updaterScaffolderDetails && {
dependencyUpdaters: {
[chosenUpdater]: {...this.updaterScaffolderDetails, scaffold: this.updaterScaffolderDetails.scaffolder}
}
},
languages: {
...'Other' !== chosenLanguage && {
[chosenLanguage]: {
scaffold: ({projectName}) => {
info(`Scaffolding ${chosenLanguage} language details for ${projectName}`);

return this.languageScaffolderResults;
}
}
}
}
},
...vcsHost && {
Expand Down

0 comments on commit 70c7b75

Please sign in to comment.