Skip to content

Commit

Permalink
Merge pull request #1014 from form8ion/alpha
Browse files Browse the repository at this point in the history
  • Loading branch information
travi authored Jan 25, 2025
2 parents 8f9855c + 364b44d commit 5bf0012
Show file tree
Hide file tree
Showing 40 changed files with 255 additions and 265 deletions.
129 changes: 63 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,88 +46,85 @@ $ npm install @form8ion/javascript --save
#### Import

```javascript
const {dialects, projectTypes} = require('@form8ion/javascript-core');
const {dialects, projectTypes} = await import('@form8ion/javascript-core');
const {
scaffold: scaffoldJavaScript,
lift: liftJavascript,
test: thisIsAJavaScriptProject,
scaffoldUnitTesting,
questionNames
} = require('@form8ion/javascript');
} = await import('./lib/index.js');
```

#### Execute

```javascript
(async () => {
const accountName = 'form8ion';
const projectRoot = process.cwd();
const accountName = 'form8ion';
const projectRoot = process.cwd();

await scaffoldJavaScript({
projectRoot,
projectName: 'project-name',
visibility: 'Public',
license: 'MIT',
configs: {
eslint: {scope: `@${accountName}`},
remark: `@${accountName}/remark-lint-preset`,
babelPreset: {name: `@${accountName}`, packageName: `@${accountName}/babel-preset`},
commitlint: {name: `@${accountName}`, packageName: `@${accountName}/commitlint-config`}
},
plugins: {
unitTestFrameworks: {},
applicationTypes: {},
packageTypes: {},
packageBundlers: {},
ciServices: {}
},
decisions: {
[questionNames.DIALECT]: dialects.BABEL,
[questionNames.NODE_VERSION_CATEGORY]: 'LTS',
[questionNames.PACKAGE_MANAGER]: 'npm',
[questionNames.PROJECT_TYPE]: projectTypes.PACKAGE,
[questionNames.SHOULD_BE_SCOPED]: true,
[questionNames.SCOPE]: accountName,
[questionNames.AUTHOR_NAME]: 'Your Name',
[questionNames.AUTHOR_EMAIL]: '[email protected]',
[questionNames.AUTHOR_URL]: 'https://your.website.tld',
[questionNames.UNIT_TESTS]: true,
[questionNames.INTEGRATION_TESTS]: true,
[questionNames.PROVIDE_EXAMPLE]: true
}
});

await scaffoldJavaScript({
if (await thisIsAJavaScriptProject({projectRoot})) {
await liftJavascript({
projectRoot,
projectName: 'project-name',
visibility: 'Public',
license: 'MIT',
configs: {
eslint: {scope: `@${accountName}`},
remark: `@${accountName}/remark-lint-preset`,
babelPreset: {name: `@${accountName}`, packageName: `@${accountName}/babel-preset`},
commitlint: {name: `@${accountName}`, packageName: `@${accountName}/commitlint-config`}
},
plugins: {
unitTestFrameworks: {},
applicationTypes: {},
packageTypes: {},
packageBundlers: {},
ciServices: {}
configs: {eslint: {scope: '@foo'}},
results: {
dependencies: {javascript: {production: [], development: []}},
scripts: {},
eslint: {configs: [], ignore: {directories: []}},
packageManager: 'npm'
},
decisions: {
[questionNames.DIALECT]: dialects.BABEL,
[questionNames.NODE_VERSION_CATEGORY]: 'LTS',
[questionNames.PACKAGE_MANAGER]: 'npm',
[questionNames.PROJECT_TYPE]: projectTypes.PACKAGE,
[questionNames.SHOULD_BE_SCOPED]: true,
[questionNames.SCOPE]: accountName,
[questionNames.AUTHOR_NAME]: 'Your Name',
[questionNames.AUTHOR_EMAIL]: '[email protected]',
[questionNames.AUTHOR_URL]: 'https://your.website.tld',
[questionNames.UNIT_TESTS]: true,
[questionNames.INTEGRATION_TESTS]: true,
[questionNames.PROVIDE_EXAMPLE]: true
}
});

if (await thisIsAJavaScriptProject({projectRoot})) {
await liftJavascript({
projectRoot,
configs: {eslint: {scope: '@foo'}},
results: {
dependencies: [],
devDependencies: [],
scripts: {},
eslint: {configs: [], ignore: {directories: []}},
packageManager: 'npm'
},
enhancers: {
PluginName: {
test: () => true,
lift: () => ({})
}
enhancers: {
PluginName: {
test: () => true,
lift: () => ({})
}
});
}

await scaffoldUnitTesting({
projectRoot: process.cwd(),
frameworks: {
Mocha: {scaffold: options => options},
Jest: {scaffold: options => options}
},
visibility: 'Public',
vcs: {host: 'GitHub', owner: 'foo', name: 'bar'},
decisions: {[questionNames.UNIT_TEST_FRAMEWORK]: 'Mocha'}
}
});
})();
}

await scaffoldUnitTesting({
projectRoot: process.cwd(),
frameworks: {
Mocha: {scaffold: options => options},
Jest: {scaffold: options => options}
},
visibility: 'Public',
vcs: {host: 'GitHub', owner: 'foo', name: 'bar'},
decisions: {[questionNames.UNIT_TEST_FRAMEWORK]: 'Mocha'}
});
```

### Documentation
Expand Down
2 changes: 1 addition & 1 deletion docs/api/scaffold.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Language scaffolder for JavaScript projects
* [`projectRoot` __string__ (_required_)](#projectroot-string-required)
* [`projectName` __string__ (_required_)](#projectname-string-required)
* [`description` __string__ (_optional_)](#description-string-optional)
* [`pathWithinParent` __string__ (_required_)](#pathwithinparent-string-required)
* [`pathWithinParent` __string__ (_optional_)](#pathwithinparent-string-optional)
* [`license` __string__ (_required_)](#license-string-required)
* [`decisions` __object__ (_optional_)](#decisions-object-optional)
* [`visibility` __string__ (_required_)](#visibility-string-required)
Expand Down
8 changes: 4 additions & 4 deletions example.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ import stubbedFs from 'mock-fs';
import * as td from 'testdouble';
import 'validate-npm-package-name';

// remark-usage-ignore-next 10
// remark-usage-ignore-next 11
stubbedFs({
node_modules: stubbedFs.load(resolve('node_modules')),
'.nvmrc': 'v1.2.3',
lib: stubbedFs.load(resolve('lib')),
templates: stubbedFs.load(resolve('templates'))
});
const execa = td.replace('execa');
const {execa} = await td.replaceEsm('execa');
td.when(execa('. ~/.nvm/nvm.sh && nvm ls-remote --lts', {shell: true}))
.thenResolve({stdout: ['v16.5.4', ''].join('\n')});
td.when(execa('. ~/.nvm/nvm.sh && nvm install', {shell: true})).thenReturn({stdout: {pipe: () => undefined}});
td.when(execa('npm', ['--version'])).thenResolve({stdout: '10.6.18'});

const {dialects, projectTypes} = await import('@form8ion/javascript-core');
const {
Expand Down Expand Up @@ -69,8 +70,7 @@ if (await thisIsAJavaScriptProject({projectRoot})) {
projectRoot,
configs: {eslint: {scope: '@foo'}},
results: {
dependencies: [],
devDependencies: [],
dependencies: {javascript: {production: [], development: []}},
scripts: {},
eslint: {configs: [], ignore: {directories: []}},
packageManager: 'npm'
Expand Down
24 changes: 12 additions & 12 deletions package-lock.json

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

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@
"packageManager": "[email protected]+sha512.11dff29565d2297c74e7c594a9762581bde969f0aa5cbe6f5b3644bf008a16c065ece61094d9ffbb81125be38df8e1ba43eb8244b3d30c61eb797e9a2440e3ec",
"dependencies": {
"@form8ion/codecov": "^6.0.0",
"@form8ion/commit-convention": "^7.0.0",
"@form8ion/commit-convention": "^8.0.0-beta.1",
"@form8ion/config-file": "^1.1.1",
"@form8ion/core": "^4.0.0",
"@form8ion/eslint": "^6.1.0",
"@form8ion/husky": "^6.0.0-beta.2",
"@form8ion/eslint": "^7.0.0-beta.1",
"@form8ion/husky": "^7.0.0-beta.1",
"@form8ion/javascript-core": "^12.0.0-beta.1",
"@form8ion/overridable-prompts": "^1.2.0",
"@form8ion/prettier": "^2.0.0",
Expand Down
4 changes: 2 additions & 2 deletions src/code-style/remark/scaffolder.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ export default async function ({config, projectRoot, projectType, vcs}) {

return deepmerge(
{
devDependencies: [config, 'remark-cli', 'remark-toc'],
dependencies: {javascript: {development: [config, 'remark-cli', 'remark-toc']}},
scripts: {
'lint:md': 'remark . --frail',
'generate:md': 'remark . --output'
}
},
{...projectTypes.PACKAGE === projectType && {devDependencies: ['remark-usage']}}
{...projectTypes.PACKAGE === projectType && {dependencies: {javascript: {development: ['remark-usage']}}}}
);
}
4 changes: 2 additions & 2 deletions src/code-style/remark/scaffolder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('remark scaffolder', () => {
it('should write the config and define dependencies', async () => {
expect(await scaffoldRemark({config, projectRoot, vcs: any.simpleObject()}))
.toEqual({
devDependencies: [config, 'remark-cli', 'remark-toc'],
dependencies: {javascript: {development: [config, 'remark-cli', 'remark-toc']}},
scripts: {
'lint:md': 'remark . --frail',
'generate:md': 'remark . --output'
Expand Down Expand Up @@ -49,7 +49,7 @@ describe('remark scaffolder', () => {
it('should configure the remark-usage plugin for package projects', async () => {
expect(await scaffoldRemark({config, projectRoot, projectType: projectTypes.PACKAGE, vcs: any.simpleObject()}))
.toEqual({
devDependencies: [config, 'remark-cli', 'remark-toc', 'remark-usage'],
dependencies: {javascript: {development: [config, 'remark-cli', 'remark-toc', 'remark-usage']}},
scripts: {
'lint:md': 'remark . --frail',
'generate:md': 'remark . --output'
Expand Down
2 changes: 1 addition & 1 deletion src/coverage/c8/scaffolder.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default async function ({projectRoot}) {
});

return {
devDependencies: ['cross-env', 'c8'],
dependencies: {javascript: {development: ['cross-env', 'c8']}},
vcsIgnore: {files: [], directories: ['/coverage/']},
eslint: {ignore: {directories: ['/coverage/']}}
};
Expand Down
2 changes: 1 addition & 1 deletion src/coverage/c8/scaffolder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('c8 scaffolder', () => {

expect(await scaffoldC8({projectRoot, vcs: {owner: vcsOwner, name: vcsName, host: 'github'}, visibility: 'Public'}))
.toEqual({
devDependencies: ['cross-env', 'c8'],
dependencies: {javascript: {development: ['cross-env', 'c8']}},
vcsIgnore: {files: [], directories: ['/coverage/']},
eslint: {ignore: {directories: ['/coverage/']}}
});
Expand Down
18 changes: 15 additions & 3 deletions src/dependencies/processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,24 @@ import {DEV_DEPENDENCY_TYPE, PROD_DEPENDENCY_TYPE} from '@form8ion/javascript-co

import install from './installer.js';

export default async function ({dependencies, devDependencies, projectRoot, packageManager}) {
export default async function ({dependencies = {}, devDependencies, projectRoot, packageManager}) {
info('Processing dependencies');

if (Array.isArray(devDependencies)) {
throw new Error(
`devDependencies provided as: ${devDependencies}. Instead, provide under dependencies.javascript.development`
);
}

if (Array.isArray(dependencies)) {
throw new Error(`Expected dependencies to be an object. Instead received: ${dependencies}`);
}

const {javascript: {production = [], development = []} = {}} = dependencies;

try {
await install(dependencies || [], PROD_DEPENDENCY_TYPE, projectRoot, packageManager);
await install(devDependencies || [], DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
await install(production, PROD_DEPENDENCY_TYPE, projectRoot, packageManager);
await install(development, DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
} catch (e) {
error('Failed to update dependencies');
error(e, {level: 'secondary'});
Expand Down
41 changes: 35 additions & 6 deletions src/dependencies/processor.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ describe('dependencies processor', () => {
const packageManager = any.word();

it('should process the provided dependency lists', async () => {
const dependencies = any.listOf(any.word);
const devDependencies = any.listOf(any.word);
const production = any.listOf(any.word);
const development = any.listOf(any.word);

await processDependencies({dependencies, devDependencies, projectRoot, packageManager});
await processDependencies({dependencies: {javascript: {production, development}}, projectRoot, packageManager});

expect(installDependencies).toHaveBeenCalledWith(dependencies, PROD_DEPENDENCY_TYPE, projectRoot, packageManager);
expect(installDependencies).toHaveBeenCalledWith(devDependencies, DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
expect(installDependencies).toHaveBeenCalledWith(production, PROD_DEPENDENCY_TYPE, projectRoot, packageManager);
expect(installDependencies).toHaveBeenCalledWith(development, DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
});

it('should process as empty lists when dependencies are not provided', async () => {
Expand All @@ -29,9 +29,38 @@ describe('dependencies processor', () => {
expect(installDependencies).toHaveBeenCalledWith([], DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
});

it('should process as empty lists when javascript dependencies are not provided', async () => {
await processDependencies({projectRoot, packageManager, dependencies: {}});

expect(installDependencies).toHaveBeenCalledWith([], PROD_DEPENDENCY_TYPE, projectRoot, packageManager);
expect(installDependencies).toHaveBeenCalledWith([], DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
});

it('should process as empty lists when dependency types are not provided', async () => {
await processDependencies({projectRoot, packageManager, dependencies: {javascript: {}}});

expect(installDependencies).toHaveBeenCalledWith([], PROD_DEPENDENCY_TYPE, projectRoot, packageManager);
expect(installDependencies).toHaveBeenCalledWith([], DEV_DEPENDENCY_TYPE, projectRoot, packageManager);
});

it('should prevent an installation error from bubbling', async () => {
installDependencies.mockRejectedValue(new Error());

await expect(() => processDependencies({})).not.toThrowError();
await expect(() => processDependencies({dependencies: {javascript: {}}})).not.toThrowError();
});

it('should throw an error if dependencies is defined as an array rather than an object', async () => {
const dependencies = any.listOf(any.word);

await expect(() => processDependencies({dependencies}))
.rejects.toThrowError(`Expected dependencies to be an object. Instead received: ${dependencies}`);
});

it('should throw an error if devDependencies is defined as an array', async () => {
const devDependencies = any.listOf(any.word);

await expect(() => processDependencies({devDependencies})).rejects.toThrowError(
`devDependencies provided as: ${devDependencies}. Instead, provide under dependencies.javascript.development`
);
});
});
Loading

0 comments on commit 5bf0012

Please sign in to comment.