Skip to content
This repository has been archived by the owner on Feb 18, 2024. It is now read-only.

Refactor create-project to support testing the cli and middleware integration #488

Merged
merged 5 commits into from
Dec 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ node_js:
- '6'
- '7'
- '8'
- '9'
before_install:
# Use newer yarn than that pre-installed in the Travis image.
- curl -sSfL https://yarnpkg.com/install.sh | bash
- export PATH="$HOME/.yarn/bin:$PATH"
install:
- yarn install --frozen-lockfile
- yarn link:all
script:
- yarn validate:eslintrc
- yarn lint
- yarn build
- yarn test
23 changes: 13 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,25 @@
"packages/*"
],
"scripts": {
"validate:eslintrc:root": "eslint --no-eslintrc --print-config . -c ./.eslintrc.js > /dev/null",
"validate:eslintrc:eslint": "eslint --no-eslintrc --print-config . -c ./packages/eslint/eslintrc.js > /dev/null",
"validate:eslintrc:airbnb": "eslint --no-eslintrc --print-config . -c ./packages/airbnb/eslintrc.js > /dev/null",
"validate:eslintrc:airbnb-base": "eslint --no-eslintrc --print-config . -c ./packages/airbnb-base/eslintrc.js > /dev/null",
"validate:eslintrc:standardjs": "eslint --no-eslintrc --print-config . -c ./packages/standardjs/eslintrc.js > /dev/null",
"validate:eslintrc": "yarn validate:eslintrc:eslint && yarn validate:eslintrc:airbnb-base && yarn validate:eslintrc:airbnb && yarn validate:eslintrc:standardjs && yarn validate:eslintrc:root",
"link:all": "lerna exec yarn link",
"lint": "node packages/neutrino/bin/neutrino lint",
"build": "lerna run build",
"changelog": "auto-changelog --remote upstream --commit-limit false",
"cnp": "NODE_ENV=test packages/create-project/bin/create-neutrino-project.js $(yarn random)",
"docs:build": "gitbook build && cp CNAME _book",
"docs:deploy": "yarn docs:build && gh-pages --dist _book --remote upstream",
"docs:serve": "gitbook serve",
"link:all": "lerna exec yarn link",
"lint": "node packages/neutrino/bin/neutrino lint",
"precommit": "lint-staged",
"release": "lerna publish --force-publish=*",
"release:preview": "lerna publish --force-publish=* --skip-git --skip-npm",
"random": "node -e \"process.stdout.write(require('crypto').randomBytes(8).toString('hex'))\"",
"release": "yarn build && lerna publish --force-publish=*",
"release:preview": "yarn build && lerna publish --force-publish=* --skip-git --skip-npm",
"test": "nyc --reporter lcov ava packages/*/test",
"validate:eslintrc:root": "eslint --no-eslintrc --print-config . -c ./.eslintrc.js > /dev/null",
"validate:eslintrc:eslint": "eslint --no-eslintrc --print-config . -c ./packages/eslint/eslintrc.js > /dev/null",
"validate:eslintrc:airbnb": "eslint --no-eslintrc --print-config . -c ./packages/airbnb/eslintrc.js > /dev/null",
"validate:eslintrc:airbnb-base": "eslint --no-eslintrc --print-config . -c ./packages/airbnb-base/eslintrc.js > /dev/null",
"validate:eslintrc:standardjs": "eslint --no-eslintrc --print-config . -c ./packages/standardjs/eslintrc.js > /dev/null",
"validate:eslintrc": "yarn validate:eslintrc:eslint && yarn validate:eslintrc:airbnb-base && yarn validate:eslintrc:airbnb && yarn validate:eslintrc:standardjs && yarn validate:eslintrc:root",
"version": "yarn changelog --package && git add CHANGELOG.md"
},
"devDependencies": {
Expand Down
18 changes: 12 additions & 6 deletions packages/create-project/bin/create-neutrino-project.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
#!/usr/bin/env node

const yeoman = require('yeoman-environment');
const yargs = require('yargs');
const path = require('path');
const { createEnv } = require('yeoman-environment');
const { basename, isAbsolute, join, resolve } = require('path');

const env = yeoman.createEnv();
const env = createEnv();
const done = exitCode => process.exit(exitCode || 0);
const dir = path.resolve(__dirname, '../commands/init');
const dir = resolve(__dirname, '../commands/init');

env.register(require.resolve(dir), 'create-neutrino-project:init');
env.register(require.resolve(dir), 'create-neutrino-project');

const cli = yargs.command('<project-directory>')
.demandCommand(1, 'Only <project-directory> is required')
.help()
.argv;
const directory = isAbsolute(cli._[0]) ? cli._[0] : join(process.cwd(), cli._[0]);
const name = basename(directory);

env.run('create-neutrino-project:init', { directory: cli._[0] }, done);
env.run('create-neutrino-project', {
directory,
name,
stdio: process.env.NODE_ENV === 'test' ? 'ignore' : 'inherit'
}, done);
197 changes: 121 additions & 76 deletions packages/create-project/commands/init/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
const { ensureDirSync, readJsonSync, writeJsonSync } = require('fs-extra');
const path = require('path');
const { basename, join, relative } = require('path');
const chalk = require('chalk');
const stringify = require('javascript-stringify');
const merge = require('deepmerge');
const { contains, partition } = require('ramda');
const Generator = require('yeoman-generator');
const questions = require('./questions');
const { projects, isYarn } = require('./utils');
const { projects, packages, isYarn } = require('./utils');

/* eslint-disable no-underscore-dangle */
module.exports = class Project extends Generator {
Expand All @@ -18,30 +19,32 @@ module.exports = class Project extends Generator {
`;
}

_makeRcFile(data) {
const rc = { use: [] };
_getProjectMiddleware() {
const { projectType, project } = this.data;

if (data.linter) {
rc.use.push(data.linter);
}

if (data.projectType === 'application' && data.project !== '@neutrinojs/node') {
rc.use.push([data.project, {
if (projectType === 'application' && project !== packages.NODE) {
return [project, {
html: {
title: this.options.directory
title: this.options.name
}
}]);
} else if (data.projectType === 'library') {
rc.use.push([data.project, {
name: this.options.directory
}]);
} else {
rc.use.push(data.project);
}];
} else if (projectType === 'library') {
return [project, {
name: this.options.name
}];
}

if (data.testRunner) {
rc.use.push(data.testRunner);
}
return project;
}

_getNeutrinorcContent() {
const rc = {
use: [
this.data.linter,
this._getProjectMiddleware(),
this.data.testRunner
].filter(Boolean)
};

return `module.exports = ${stringify(rc, null, 2)};\n`;
}
Expand All @@ -60,113 +63,155 @@ module.exports = class Project extends Generator {
} else if (deps.devDependencies.length) {
return { devDependencies: deps.devDependencies };
}

return {};
}

_initialPackageJson() {
const done = this.async();
const installer = isYarn ? 'yarn' : 'npm';
const scripts = { build: 'neutrino build' };
const scripts = { build: `${packages.NEUTRINO} build` };

if (this.data.projectType !== 'library') {
scripts.start = 'neutrino start';
scripts.start = `${packages.NEUTRINO} start`;
}

if (this.data.linter) {
scripts.lint = 'neutrino lint';
scripts.lint = `${packages.NEUTRINO} lint`;
}

if (this.data.testRunner) {
scripts.test = 'neutrino test';
scripts.test = `${packages.NEUTRINO} test`;
}

ensureDirSync(this.options.directory);

const command = this.spawnCommand(installer, ['init', '--yes'], {
cwd: path.resolve(this.options.directory),
stdio: 'ignore'
this.spawnCommandSync(installer, ['init', '--yes'], {
cwd: this.options.directory,
stdio: this.options.stdio
});

command.on('close', () => {
const jsonPath = path.join(this.options.directory, 'package.json');
const json = readJsonSync(`./${jsonPath}`);
const packageJson = Object.assign(json, { scripts });

writeJsonSync(jsonPath, packageJson, { spaces: 2 });
const jsonPath = join(this.options.directory, 'package.json');
const json = readJsonSync(jsonPath);
const packageJson = Object.assign(json, { scripts });

return done();
});
writeJsonSync(jsonPath, packageJson, { spaces: 2 });
this.log(` ${chalk.green('create')} ${join(basename(this.options.directory), 'package.json')}`);
}

prompting() {
const done = this.async();

console.log(chalk.cyan.bold(this._logo()));
console.log(chalk.magenta.bold('Welcome to Neutrino!'));
console.log(chalk.cyan('To help you create your new project, I am going to ask you a few questions. :)\n'));
this.log(chalk.cyan.bold(this._logo()));
this.log(chalk.white.bold('Welcome to Neutrino! 👋'));
this.log(chalk.cyan('To help you create your new project, I am going to ask you a few questions.\n'));

this
.prompt(questions())
.then(answers => this.data = answers)
.then(() => done());
.then(() => {
this.log(`\n👌 ${chalk.white.bold('Looks like I have all the info I need. Give me a moment while I create your project!')}\n`);
done();
});
}

writing() {
const destinationPath = this.destinationPath(this.options.directory);
const templateDir = this.data.project.replace('@neutrinojs/', '');
const templates = [this.data.project, this.data.testRunner, this.data.linter].filter(Boolean);

this._initialPackageJson();
this.fs.copyTpl(
this.templatePath(`${templateDir}/src/**/*`),
path.join(destinationPath, 'src'),
{ data: this.options }
this.fs.write(
join(this.options.directory, '.neutrinorc.js'),
this._getNeutrinorcContent()
);
this.fs.write(path.join(destinationPath, '.neutrinorc.js'), this._makeRcFile(this.data));

if (this.data.testRunner) {
const testDestinationDir = path.join(destinationPath, 'test');

ensureDirSync(testDestinationDir);
this.fs.copy(
this.templatePath(path.join('test', `${this.data.testRunner.replace('@neutrinojs/', '')}.js`)),
path.join(testDestinationDir, 'simple_test.js')
);
}

if (this.data.linter) {
this.fs.copy(
this.templatePath('eslintrc.js'),
path.join(destinationPath, '.eslintrc.js')
templates.forEach(template => {
const templateDir = template.replace('@neutrinojs/', '');

this.fs.copyTpl(
this.templatePath(`${templateDir}/**`),
this.options.directory,
{ data: this.options },
{},
{ globOptions: { dot: true } }
);
}
});
}

install() {
const installer = isYarn ? 'yarn' : 'npm';
const argument = isYarn ? 'add' : 'install';
const development = isYarn ? '--dev' : '--save-dev';
const packageManager = isYarn ? 'yarn' : 'npm';
const install = isYarn ? 'add' : 'install';
const devFlag = isYarn ? '--dev' : '--save-dev';
const { dependencies, devDependencies } = this._getDependencies();

this.log(`\n${chalk.green('success')} Saved package.json`);
process.chdir(this.options.directory);
this.log('');

if (dependencies) {
this.log(`\n${chalk.green('Installing dependencies:')} ${chalk.yellow(dependencies.join(', '))}`);
this.spawnCommandSync(installer, [argument, ...dependencies], { stdio: 'inherit' });
this.log(`${chalk.green('⏳ Installing dependencies:')} ${chalk.yellow(dependencies.join(', '))}`);
this.spawnCommandSync(packageManager, [install, ...dependencies], {
cwd: this.options.directory,
stdio: this.options.stdio,
env: process.env
});
}

if (devDependencies) {
this.log(`\n${chalk.green('Installing dev-dependencies:')} ${chalk.yellow(devDependencies.join(', '))}`);
this.spawnCommandSync(installer, [argument, development, ...devDependencies], { stdio: 'inherit' });
if (process.env.NODE_ENV === 'test') {
const [local, remote] = partition(contains(packages.NEUTRINO), devDependencies);

if (remote.length) {
this.log(`${chalk.green('⏳ Installing remote devDependencies:')} ${chalk.yellow(remote.join(', '))}`);
this.spawnCommandSync(packageManager, [install, devFlag, ...remote], {
stdio: this.options.stdio,
env: process.env,
cwd: this.options.directory
});
}

if (local.length) {
this.log(`${chalk.green('⏳ Linking local devDependencies:')} ${chalk.yellow(local.join(', '))}`);
this.spawnCommandSync('yarn', ['link', ...local], {
stdio: this.options.stdio,
env: process.env,
cwd: this.options.directory
});
}
} else {
this.log(`${chalk.green('⏳ Installing devDependencies:')} ${chalk.yellow(devDependencies.join(', '))}`);
this.spawnCommandSync(packageManager, [install, devFlag, ...devDependencies], {
stdio: this.options.stdio,
env: process.env,
cwd: this.options.directory
});
}
}

if (this.data.linter) {
this.spawnCommandSync('neutrino', ['lint', '--fix'], { stdio: 'ignore' });
this.log(`${chalk.green('⏳ Performing one-time lint')}`);
this.spawnCommandSync(packages.NEUTRINO, ['lint', '--fix'], {
stdio: this.options.stdio === 'inherit' ? 'ignore' : this.options.stdio,
cwd: this.options.directory
});
}
}

end() {
this.log(`\n${chalk.green('Success!')} Created ${chalk.cyan(this.options.directory)} at ${chalk.yellow(process.cwd())}`);
this.log(`To get started, change your current working directory to: ${chalk.cyan(this.options.directory)}`);
this.log(`\n${chalk.green('Hooray, I successfully created your project!')}`);
this.log(`\nI have added a few ${isYarn ? 'yarn' : 'npm'} scripts to help you get started:`);
this.log(` • To build your project run: ${chalk.cyan.bold(`${isYarn ? 'yarn' : 'npm run'} build`)}`);

if (this.data.projectType !== 'library') {
this.log(` • To start your project locally run: ${chalk.cyan.bold(`${isYarn ? 'yarn' : 'npm'} start`)}`);
}

if (this.data.testRunner) {
this.log(` • To execute tests run: ${chalk.cyan.bold(`${isYarn ? 'yarn' : 'npm'} test`)}`);
}

if (this.data.linter) {
this.log(` • To lint your project manually run: ${chalk.cyan.bold(`${isYarn ? 'yarn' : 'npm run'} lint`)}`);
this.log(` You can also fix some linting problems with: ${chalk.cyan.bold(`${isYarn ? 'yarn' : 'npm run'} lint --fix`)}`);
}

this.log('\nNow change your directory to the following to get started:');
this.log(` ${chalk.cyan(relative(process.cwd(), this.options.directory))}`);
this.log(`\n❤️ ${chalk.white.bold('Neutrino')}`);
}
};
Loading