From 6958ecc2129294c160b836415973e1025845ca52 Mon Sep 17 00:00:00 2001 From: Cemre Mengu Date: Sat, 3 Nov 2018 15:20:28 +0300 Subject: [PATCH] Project scaffolding with package.json (#114) * initial work for project scaffolding * Added tests for cli, make tests pass, improve questions to match npm init * Use npm init directly instead of asking questions, rely on generify mkdir instead * Update README for new instructions --- README.md | 6 +-- app_template/.gitignore | 66 ++++++++++++++++++++++++++++++ generate.js | 89 ++++++++++++++++++++++++----------------- test/generate.js | 72 +++++++++++++-------------------- 4 files changed, 149 insertions(+), 84 deletions(-) create mode 100644 app_template/.gitignore diff --git a/README.md b/README.md index acedeb09..9b0e51d8 100644 --- a/README.md +++ b/README.md @@ -158,11 +158,9 @@ in case of a `'unhandledRejection'`. `fastify-cli` can also help with generating some project scaffolding to kickstart the development of your next Fastify application. To use it: -1. `mkdir yourapp` +1. `fastify generate ` 2. `cd yourapp` -3. `npm init` -4. `fastify generate` -5. `npm install` +3. `npm install` The sample code offers you four npm tasks: diff --git a/app_template/.gitignore b/app_template/.gitignore new file mode 100644 index 00000000..36ddc84e --- /dev/null +++ b/app_template/.gitignore @@ -0,0 +1,66 @@ +# Logs +logs +*.log +npm-debug.log* + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules +jspm_packages + +# Optional npm cache directory +.npm + +# Optional REPL history +.node_repl_history + +# 0x +profile-* + +# mac files +.DS_Store + +# vim swap files +*.swp + +# webstorm +.idea + +# vscode +.vscode +*code-workspace + +# clinic +profile* +*clinic* +*flamegraph* + +# lock files +yarn.lock +package-lock.json + +# generated code +examples/typescript-server.js +test/types/index.js diff --git a/generate.js b/generate.js index 024ec63e..728ecef3 100755 --- a/generate.js +++ b/generate.js @@ -2,71 +2,75 @@ const { readFile, - writeFile + writeFile, + existsSync } = require('fs') const chalk = require('chalk') const path = require('path') const generify = require('generify') const argv = require('yargs-parser') -const templatedir = path.join(__dirname, 'app_template') const cliPkg = require('./package') +const { execSync } = require('child_process') function generate (dir, log, cb) { if (!cb) { cb = log log = () => {} } - const pkgFile = path.join(dir, 'package.json') - log('info', `reading package.json in ${dir}`) - readFile(pkgFile, (err, data) => { + + generify(path.join(__dirname, 'app_template'), dir, {}, function (file) { + log('debug', `generated ${file}`) + }, function (err) { if (err) { return cb(err) } - var pkg - try { - pkg = JSON.parse(data) - } catch (err) { - return cb(err) - } + process.chdir(dir) + execSync('npm init -y') - pkg.scripts = Object.assign(pkg.scripts || {}, { - 'test': 'tap test/*.test.js test/*/*.test.js test/*/*/*.test.js', - 'start': 'fastify start -l info app.js', - 'dev': 'fastify start -l info -P app.js' - }) + log('info', `reading package.json in ${dir}`) + readFile('package.json', (err, data) => { + if (err) { + return cb(err) + } - pkg.dependencies = Object.assign(pkg.dependencies || {}, { - 'fastify': cliPkg.dependencies.fastify, - 'fastify-plugin': cliPkg.devDependencies['fastify-plugin'] || cliPkg.dependencies['fastify-plugin'], - 'fastify-autoload': cliPkg.devDependencies['fastify-autoload'], - 'fastify-cli': '^' + cliPkg.version - }) + var pkg + try { + pkg = JSON.parse(data) + } catch (err) { + return cb(err) + } - pkg.devDependencies = Object.assign(pkg.devDependencies || {}, { - 'tap': cliPkg.devDependencies['tap'] - }) + pkg.main = 'app.js' - log('debug', `edited package.json, saving`) + pkg.scripts = Object.assign(pkg.scripts || {}, { + 'test': 'tap test/*.test.js test/*/*.test.js test/*/*/*.test.js', + 'start': 'fastify start -l info app.js', + 'dev': 'fastify start -l info -P app.js' + }) - writeFile(pkgFile, JSON.stringify(pkg, null, 2), (err) => { - if (err) { - return cb(err) - } + pkg.dependencies = Object.assign(pkg.dependencies || {}, { + 'fastify': cliPkg.dependencies.fastify, + 'fastify-plugin': cliPkg.devDependencies['fastify-plugin'] || cliPkg.dependencies['fastify-plugin'], + 'fastify-autoload': cliPkg.devDependencies['fastify-autoload'], + 'fastify-cli': '^' + cliPkg.version + }) - log('debug', `saved package.json`) - log('info', `copying sample project`) + pkg.devDependencies = Object.assign(pkg.devDependencies || {}, { + 'tap': cliPkg.devDependencies['tap'] + }) - generify(templatedir, dir, {}, function (file) { - log('debug', `generated ${file}`) - }, function (err) { + log('debug', `edited package.json, saving`) + writeFile('package.json', JSON.stringify(pkg, null, 2), (err) => { if (err) { return cb(err) } + log('debug', `saved package.json`) log('info', `project ${pkg.name} generated successfully`) log('debug', `run '${chalk.bold('npm install')}' to install the dependencies`) log('debug', `run '${chalk.bold('npm start')}' to start the application`) + log('debug', `run '${chalk.bold('npm run dev')}' to start the application with pino-colada pretty logging (not suitable for production)`) log('debug', `run '${chalk.bold('npm test')}' to execute the unit tests`) cb() }) @@ -88,7 +92,20 @@ const colors = [ function cli (args) { const opts = argv(args) - generate(opts._[0] || process.cwd(), log, function (err) { + + if (opts._[0] && existsSync(opts._[0])) { + log('error', 'directory ' + opts._[0] + ' already exists') + process.exit(1) + } + + const dir = opts._[0] || process.cwd() + + if (existsSync(path.join(dir, 'package.json'))) { + log('error', 'a package.json file already exists in target directory') + process.exit(1) + } + + generate(dir, log, function (err) { if (err) { log('error', err.message) process.exit(1) diff --git a/test/generate.js b/test/generate.js index 054aaf97..41025c9e 100644 --- a/test/generate.js +++ b/test/generate.js @@ -6,7 +6,6 @@ process.env.TAP_BAIL = true const t = require('tap') const { - writeFile, readFile } = require('fs') const path = require('path') @@ -17,6 +16,7 @@ const { generate } = require('../generate') const workdir = path.join(__dirname, 'workdir') const templatedir = path.join(__dirname, '..', 'app_template') const cliPkg = require('../package') +const { exec } = require('child_process') const expected = {} ;(function (cb) { @@ -59,63 +59,45 @@ function define (t) { }) }) - test('errors if package.json is not there', (t) => { + test('errors if directory exists', (t) => { t.plan(2) - - generate(workdir, function (err) { - t.ok(err) - t.equal(err.code, 'ENOENT') + exec('node generate.js ./test/workdir', (err, stdout) => { + t.is('directory ./test/workdir already exists', stdout.toString().trim()) + t.is(1, err.code) }) }) - test('finish succesfully if package.json is there - npm', (t) => { - t.plan(12 + Object.keys(expected).length * 2) - - const pkgFile = path.join(workdir, 'package.json') - const pkgContent = JSON.stringify({ - name: 'an-npm-app', - version: '0.0.1', - description: 'whaat', - main: 'index.js', - scripts: {} - }, null, 2) - - writeFile(pkgFile, pkgContent, function (err) { - t.error(err) - generate(workdir, function (err) { - t.error(err) - verifyPkg(t, pkgFile, pkgContent) - verifyCopy(t, pkgFile) - }) + test('errors if pkgfile exists', (t) => { + t.plan(2) + exec('node generate.js', (err, stdout) => { + t.is('a package.json file already exists in target directory', stdout.toString().trim()) + t.is(1, err.code) }) }) - test('finish succesfully if package.json is there - yarn', (t) => { - t.plan(12 + Object.keys(expected).length * 2) + test('should finish succesfully', (t) => { + t.plan(15 + Object.keys(expected).length * 2) - const pkgFile = path.join(workdir, 'package.json') - const pkgContent = JSON.stringify({ - name: 'an-yarn-app', - version: '0.0.1', - description: 'whaat', - main: 'index.js' - }, null, 2) - writeFile(pkgFile, pkgContent, function (err) { + generate(workdir, function (err) { t.error(err) - - generate(workdir, function (err) { - t.error(err) - verifyPkg(t, pkgFile, pkgContent) - verifyCopy(t, pkgFile) - }) + verifyPkg(t) + verifyCopy(t) }) }) - function verifyPkg (t, pkgFile, pkgContent) { + function verifyPkg (t) { + const pkgFile = path.join(workdir, 'package.json') + readFile(pkgFile, function (err, data) { t.error(err) - t.equal(data.toString().split('"main"')[0], pkgContent.split('"main"')[0]) const pkg = JSON.parse(data) + t.equal(pkg.name, 'workdir') + t.equal(pkg.version, '1.0.0') + t.equal(pkg.description, '') + t.equal(pkg.author, '') + // by default this will be ISC but since we have a MIT licensed pkg file in upper dir, npm will set the license to MIT in this case + // so for local tests we need to accept MIT as well + t.ok(pkg.license === 'ISC' || pkg.license === 'MIT') t.equal(pkg.scripts.test, 'tap test/*.test.js test/*/*.test.js test/*/*/*.test.js') t.equal(pkg.scripts.start, 'fastify start -l info app.js') t.equal(pkg.scripts.dev, 'fastify start -l info -P app.js') @@ -127,7 +109,9 @@ function define (t) { }) } - function verifyCopy (t, pkgFile) { + function verifyCopy (t) { + const pkgFile = path.join(workdir, 'package.json') + walker(workdir) .on('file', function (file) { if (file === pkgFile) {