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

Commit

Permalink
Refactor create-project to support testing the cli and middleware int…
Browse files Browse the repository at this point in the history
…egration (#488)

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

* Make create-project output more helpful and descriptive

* Revise create-project tests to utilize new Vue preset and Neutrino extensions

* Run create-project tests serially to output to CI stdout more frequently :(

* Scaffold all middleware consistently in the root of the destination project
  • Loading branch information
eliperelman committed Dec 1, 2017
1 parent 135d7aa commit cd5fa51
Show file tree
Hide file tree
Showing 37 changed files with 841 additions and 402 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
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
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
@@ -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
@@ -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')}`);
}
};

0 comments on commit cd5fa51

Please sign in to comment.