diff --git a/packages/gatsby-cli/.gitignore b/packages/gatsby-cli/.gitignore index 53c36b2056e94..2132b55e6405e 100644 --- a/packages/gatsby-cli/.gitignore +++ b/packages/gatsby-cli/.gitignore @@ -1,2 +1,2 @@ -/lib +/*.js yarn.lock diff --git a/packages/gatsby-cli/package.json b/packages/gatsby-cli/package.json index b20eb9906de49..df496053e33bc 100644 --- a/packages/gatsby-cli/package.json +++ b/packages/gatsby-cli/package.json @@ -4,22 +4,17 @@ "version": "1.1.2", "author": "Kyle Mathews ", "bin": { - "gatsby": "lib/index.js" + "gatsby": "./index.js" }, "dependencies": { "babel-runtime": "^6.25.0", - "bluebird": "^3.5.0", - "common-tags": "^1.4.0", - "convert-hrtime": "^2.0.0", + "commander": "^2.11.0", "core-js": "^2.5.0", - "execa": "^0.8.0", "fs-extra": "^4.0.1", "hosted-git-info": "^2.5.0", "lodash": "^4.17.4", - "pretty-error": "^2.1.1", "resolve-cwd": "^2.0.0", - "yargs": "^8.0.2", - "yurnalist": "^0.2.1" + "tracer": "^0.8.9" }, "devDependencies": { "babel-cli": "^6.24.1" @@ -28,9 +23,9 @@ "gatsby" ], "license": "MIT", - "main": "lib/index.js", + "main": "index.js", "scripts": { - "build": "babel src --out-dir lib --ignore __tests__", - "watch": "babel -w src --out-dir lib --ignore __tests__" + "build": "babel src --out-dir . --ignore __tests__", + "watch": "babel -w src --out-dir . --ignore __tests__" } } diff --git a/packages/gatsby-cli/src/.gitkeep b/packages/gatsby-cli/src/.gitkeep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/packages/gatsby-cli/src/create-cli.js b/packages/gatsby-cli/src/create-cli.js deleted file mode 100644 index f3f32eb8236cc..0000000000000 --- a/packages/gatsby-cli/src/create-cli.js +++ /dev/null @@ -1,175 +0,0 @@ -const path = require(`path`) -const resolveCwd = require(`resolve-cwd`) -const yargs = require(`yargs`) -const report = require(`./reporter`) - -const DEFAULT_BROWSERS = [`> 1%`, `last 2 versions`, `IE >= 9`] - -function buildLocalCommands(cli, isLocalSite) { - const defaultHost = `localhost` - const directory = path.resolve(`.`) - - function getSiteInfo() { - if (!isLocalSite) return {} - - const sitePackageJson = require(path.join(directory, `package.json`)) - const browserslist = sitePackageJson.browserslist || DEFAULT_BROWSERS - return { sitePackageJson, browserslist } - } - - function resolveLocalCommand(command) { - if (!isLocalSite) { - cli.showHelp() - report.verbose(`current directory: ${directory}`) - return report.panic( - `gatsby <${command}> can only be run for a gatsby site. \n` + - `Either the current working directory does not contain a package.json or ` + - `'gatsby' is not specified as a dependency` - ) - } - try { - return require(resolveCwd(`gatsby/dist/commands/${command}`)) - } catch (err) { - cli.showHelp() - return report.panic( - `There was a problem loading the local ${command} command. Gatsby may not be installed.`, - err - ) - } - } - - cli.command({ - command: `develop`, - desc: - `Start development server. Watches files, rebuilds, and hot reloads ` + - `if something changes`, - builder: _ => - _.option(`H`, { - alias: `host`, - type: `string`, - default: defaultHost, - describe: `Set host. Defaults to ${defaultHost}`, - }) - .option(`p`, { - alias: `port`, - type: `string`, - default: `8000`, - describe: `Set port. Defaults to 8000`, - }) - .option(`o`, { - alias: `open`, - type: `boolean`, - default: false, - describe: `Open the site in your browser for you.`, - }), - handler: argv => { - const { sitePackageJson, browserslist } = getSiteInfo() - - resolveLocalCommand(`develop`)({ - ...argv, - directory, - sitePackageJson, - browserslist, - }) - }, - }) - - cli.command({ - command: `build`, - desc: `Build a Gatsby project.`, - builder: _ => - _.option(`prefix-paths`, { - type: `string`, - describe: `Build site with link paths prefixed (set prefix in your config).`, - }), - handler: argv => { - process.env.NODE_ENV = `production` - const { sitePackageJson, browserslist } = getSiteInfo() - - resolveLocalCommand(`build`)({ - ...argv, - directory, - sitePackageJson, - browserslist, - }) - }, - }) - - cli.command({ - command: `serve`, - desc: `Serve previously built Gatsby site.`, - builder: _ => - _.option(`H`, { - alias: `host`, - type: `string`, - default: defaultHost, - describe: `Set host. Defaults to ${defaultHost}`, - }) - .option(`p`, { - alias: `port`, - type: `string`, - default: `8000`, - describe: `Set port. Defaults to 8000`, - }) - .option(`o`, { - alias: `open`, - type: `boolean`, - default: true, - describe: `Open the site in your browser for you.`, - }), - - handler: argv => { - const { sitePackageJson, browserslist } = getSiteInfo() - - console.log(argv) - resolveLocalCommand(`serve`)({ - ...argv, - directory, - sitePackageJson, - browserslist, - }) - }, - }) -} - -function isLocalGatsbySite() { - let inGatsbySite = false - try { - let { dependencies, devDependencies } = require(path.resolve( - `./package.json` - )) - inGatsbySite = - (dependencies && dependencies.gatsby) || - (devDependencies && devDependencies.gatsby) - } catch (err) { - /* ignore */ - } - return inGatsbySite -} - -module.exports = (argv, handlers) => { - let cli = yargs() - let isLocalSite = isLocalGatsbySite() - - cli - .usage(`Usage: $0 [options]`) - .help(`h`) - .alias(`h`, `help`) - .version() - .alias(`v`, `version`) - - buildLocalCommands(cli, isLocalSite) - - return cli - .command({ - command: `new [rootPath] [starter]`, - desc: `Create new Gatsby project.`, - handler: ({ rootPath, starter = `gatsbyjs/gatsby-starter-default` }) => { - require(`./init-starter`)(starter, { rootPath }) - }, - }) - .wrap(cli.terminalWidth()) - .demandCommand(1, `Pass --help to see all available commands and options.`) - .showHelpOnFail(true, `A command is required.`) - .parse(argv.slice(2)) -} diff --git a/packages/gatsby-cli/src/index.js b/packages/gatsby-cli/src/index.js index b1630b7a3ecc7..0147df21abd82 100755 --- a/packages/gatsby-cli/src/index.js +++ b/packages/gatsby-cli/src/index.js @@ -4,39 +4,150 @@ // use require() with backtick strings so use the es6 syntax import "babel-polyfill" +const program = require(`commander`) +const packageJson = require(`./package.json`) +const path = require(`path`) +const _ = require(`lodash`) const resolveCwd = require(`resolve-cwd`) -const createCli = require(`./create-cli`) -const report = require(`./reporter`) -global.Promise = require(`bluebird`) +program.version(packageJson.version).usage(`[command] [options]`) -const version = process.version -const verDigit = Number(version.match(/\d+/)[0]) +let inGatsbySite = false +let localPackageJSON +try { + localPackageJSON = require(path.resolve(`./package.json`)) + if ( + (localPackageJSON.dependencies && localPackageJSON.dependencies.gatsby) || + (localPackageJSON.devDependencies && + localPackageJSON.devDependencies.gatsby) + ) { + inGatsbySite = true + } else if ( + localPackageJSON.devDependencies && + localPackageJSON.devDependencies.gatsby + ) { + inGatsbySite = true + } +} catch (err) { + // ignore +} -if (verDigit < 4) { - report.panic( - `Gatsby 1.0+ requires node.js v4 or higher (you have ${version}). \n` + - `Upgrade node to the latest stable release.` - ) +const defaultHost = `localhost` + +const directory = path.resolve(`.`) +const getSiteInfo = () => { + const sitePackageJson = require(path.join(directory, `package.json`)) + const browserslist = sitePackageJson.browserslist || [ + `> 1%`, + `last 2 versions`, + `IE >= 9`, + ] + return { sitePackageJson, browserslist } } -Promise.onPossiblyUnhandledRejection(error => { - report.error(error) - throw error -}) +// If there's a package.json in the current directory w/ a gatsby dependency +// include the develop/build/serve commands. Otherwise, just the new. +if (inGatsbySite) { + program + .command(`develop`) + .description( + `Start development server. Watches files and rebuilds and hot reloads ` + + `if something changes` + ) // eslint-disable-line max-len + .option( + `-H, --host `, + `Set host. Defaults to ${defaultHost}`, + defaultHost + ) + .option(`-p, --port `, `Set port. Defaults to 8000`, `8000`) + .option(`-o, --open`, `Open the site in your browser for you.`) + .action(command => { + const developPath = resolveCwd(`gatsby/dist/utils/develop`) + const develop = require(developPath) + const { sitePackageJson, browserslist } = getSiteInfo() + const p = { + ...command, + directory, + sitePackageJson, + browserslist, + } + develop(p) + }) -process.on(`unhandledRejection`, error => { - // This will exit the process in newer Node anyway so lets be consistent - // across versions and crash - report.panic(`UNHANDLED REJECTION`, error) -}) + program + .command(`build`) + .description(`Build a Gatsby project.`) + .option( + `--prefix-paths`, + `Build site with link paths prefixed (set prefix in your config).` + ) + .action(command => { + // Set NODE_ENV to 'production' + process.env.NODE_ENV = `production` -process.on(`uncaughtException`, error => { - report.panic(`UNHANDLED EXCEPTION`, error) -}) + const buildPath = resolveCwd(`gatsby/dist/utils/build`) + const build = require(buildPath) + const { sitePackageJson, browserslist } = getSiteInfo() + const p = { + ...command, + directory, + sitePackageJson, + browserslist, + } + build(p).then(() => { + console.log(`Done building in`, process.uptime(), `seconds`) + process.exit() + }) + }) + + program + .command(`serve`) + .description(`Serve built site.`) + .option( + `-H, --host `, + `Set host. Defaults to ${defaultHost}`, + defaultHost + ) + .option(`-p, --port `, `Set port. Defaults to 9000`, `9000`) + .option(`-o, --open`, `Open the site in your browser for you.`) + .action(command => { + const servePath = resolveCwd(`gatsby/dist/utils/serve`) + const serve = require(servePath) + const { sitePackageJson, browserslist } = getSiteInfo() + const p = { + ...command, + directory, + sitePackageJson, + browserslist, + } + serve(p) + }) +} + +program + .command(`new [rootPath] [starter]`) + .description(`Create new Gatsby project.`) + .action((rootPath, starter) => { + const newCommand = require(`./new`) + newCommand(rootPath, starter) + }) -createCli(process.argv, { - develop: () => require(resolveCwd(`gatsby/dist/commands/develop`)), - build: () => require(resolveCwd(`gatsby/dist/commands/build`)), - serve: () => require(resolveCwd(`gatsby/dist/commands/serve`)), +program.on(`--help`, () => { + console.log( + `To show subcommand help: + + gatsby [command] -h +` + ) }) + +// If the user types an unknown sub-command, just display the help. +const subCmd = process.argv.slice(2, 3)[0] +let cmds = _.map(program.commands, `_name`) +cmds = cmds.concat([`--version`, `-V`]) + +if (!_.includes(cmds, subCmd)) { + program.help() +} else { + program.parse(process.argv) +} diff --git a/packages/gatsby-cli/src/init-starter.js b/packages/gatsby-cli/src/init-starter.js index 28ccc26c4de1b..e14dcfeb6c645 100644 --- a/packages/gatsby-cli/src/init-starter.js +++ b/packages/gatsby-cli/src/init-starter.js @@ -1,15 +1,10 @@ -/* @flow */ -const { execSync } = require(`child_process`) -const execa = require(`execa`) -const hostedGitInfo = require(`hosted-git-info`) -const fs = require(`fs-extra`) -const sysPath = require(`path`) -const report = require(`./reporter`) - -const spawn = (cmd: string) => { - const [file, ...args] = cmd.split(/\s+/) - return execa(file, args, { stdio: `inherit` }) -} +/* @flow weak */ +import { exec, execSync } from "child_process" +import hostedGitInfo from "hosted-git-info" +import fs from "fs-extra" +import sysPath from "path" + +let logger = console // Checks the existence of yarn package // We use yarnpkg instead of yarn to avoid conflict with Hadoop yarn @@ -25,77 +20,113 @@ const shouldUseYarn = () => { } } -// Executes `npm install` or `yarn install` in rootPath. -const install = async rootPath => { +// Executes `npm install` and `bower install` in rootPath. +// +// rootPath - String. Path to directory in which command will be executed. +// callback - Function. Takes stderr and stdout of executed process. +// +// Returns nothing. +const install = (rootPath, callback) => { const prevDir = process.cwd() - - report.info(`Installing packages...`) + logger.log(`Installing packages...`) process.chdir(rootPath) - - try { - let cmd = shouldUseYarn() ? spawn(`yarnpkg`) : spawn(`npm install`) - await cmd - } finally { + const installCmd = shouldUseYarn() ? `yarnpkg` : `npm install` + exec(installCmd, (error, stdout, stderr) => { process.chdir(prevDir) - } + if (stdout) console.log(stdout.toString()) + if (error !== null) { + const msg = stderr.toString() + callback(new Error(msg)) + } + callback(null, stdout) + }) } const ignored = path => !/^\.(git|hg)$/.test(sysPath.basename(path)) // Copy starter from file system. -const copy = async (starterPath: string, rootPath: string) => { - // Chmod with 755. - // 493 = parseInt('755', 8) - await fs.mkdirp(rootPath, { mode: 493 }) - - if (!fs.existsSync(starterPath)) { - throw new Error(`starter ${starterPath} doesn't exist`) +// +// starterPath - String, file system path from which files will be taken. +// rootPath - String, directory to which starter files will be copied. +// callback - Function. +// +// Returns nothing. +const copy = (starterPath, rootPath, callback) => { + const copyDirectory = () => { + fs.copy(starterPath, rootPath, { filter: ignored }, error => { + if (error !== null) return callback(new Error(error)) + logger.log(`Created starter directory layout`) + install(rootPath, callback) + return false + }) } - report.info(`Creating new site from local starter: ${starterPath}`) - - report.log(`Copying local starter to ${rootPath} ...`) - - await fs.copy(starterPath, rootPath, { filter: ignored }) - report.success(`Created starter directory layout`) - - await install(rootPath) - - return true + // Chmod with 755. + // 493 = parseInt('755', 8) + fs.mkdirp(rootPath, { mode: 493 }, error => { + if (error !== null) callback(new Error(error)) + return fs.exists(starterPath, exists => { + if (!exists) { + const chmodError = `starter ${starterPath} doesn't exist` + return callback(new Error(chmodError)) + } + logger.log(`Copying local starter to ${rootPath} ...`) + + copyDirectory() + return true + }) + }) } // Clones starter from URI. -const clone = async (hostInfo: any, rootPath: string) => { +// +// address - String, URI. https:, github: or git: may be used. +// rootPath - String, directory to which starter files will be copied. +// callback - Function. +// +// Returns nothing. +const clone = (hostInfo, rootPath, callback) => { const url = hostInfo.git({ noCommittish: true }) const branch = hostInfo.committish ? `-b ${hostInfo.committish}` : `` - report.info(`Creating new site from git: ${url}`) - - await spawn(`git clone ${branch} ${url} ${rootPath} --single-branch`) - - report.success(`Created starter directory layout`) + logger.log(`Cloning git repo ${url} to ${rootPath}...`) + const cmd = `git clone ${branch} ${url} ${rootPath} --single-branch` + + exec(cmd, (error, stdout, stderr) => { + if (error !== null) { + return callback(new Error(`Git clone error: ${stderr.toString()}`)) + } + logger.log(`Created starter directory layout`) + return fs.remove(sysPath.join(rootPath, `.git`), removeError => { + if (error !== null) return callback(new Error(removeError)) + install(rootPath, callback) + return true + }) + }) +} - await fs.remove(sysPath.join(rootPath, `.git`)) +// Main function that clones or copies the starter. +// +// starter - String, file system path or URI of starter. +// rootPath - String, directory to which starter files will be copied. +// callback - Function. +// +// Returns nothing. +const initStarter = (starter, options = {}) => + new Promise((resolve, reject) => { + const callback = (err, value) => (err ? reject(err) : resolve(value)) - await install(rootPath) -} + const cwd = process.cwd() + const rootPath = options.rootPath || cwd + if (options.logger) logger = options.logger -type InitOptions = { - rootPath?: string, -} + if (fs.existsSync(sysPath.join(rootPath, `package.json`))) + throw new Error(`Directory ${rootPath} is already an npm project`) -/** - * Main function that clones or copies the starter. - */ -module.exports = async (starter: string, options: InitOptions = {}) => { - const rootPath = options.rootPath || process.cwd() + const hostedInfo = hostedGitInfo.fromUrl(starter) - if (fs.existsSync(sysPath.join(rootPath, `package.json`))) { - report.panic(`Directory ${rootPath} is already an npm project`) - return - } + if (hostedInfo) clone(hostedInfo, rootPath, callback) + else copy(starter, rootPath, callback) + }) - const hostedInfo = hostedGitInfo.fromUrl(starter) - if (hostedInfo) await clone(hostedInfo, rootPath) - else await copy(starter, rootPath) -} +module.exports = initStarter diff --git a/packages/gatsby-cli/src/new.js b/packages/gatsby-cli/src/new.js new file mode 100644 index 0000000000000..afa14d139110e --- /dev/null +++ b/packages/gatsby-cli/src/new.js @@ -0,0 +1,8 @@ +/* @flow weak */ +const logger = require(`tracer`).colorConsole() + +const initStarter = require(`./init-starter`) + +module.exports = (rootPath, starter = `gatsbyjs/gatsby-starter-default`) => { + initStarter(starter, { rootPath, logger }).catch(error => logger.error(error)) +} diff --git a/packages/gatsby-link/src/index.js b/packages/gatsby-link/src/index.js index 887993fddbabd..27fb854750df5 100644 --- a/packages/gatsby-link/src/index.js +++ b/packages/gatsby-link/src/index.js @@ -9,7 +9,7 @@ if (typeof __PREFIX_PATHS__ !== `undefined` && __PREFIX_PATHS__) { } function normalizePath(path) { - return path.replace(/^\/\//g, `/`) + return path.replace(/^\/\//g, '/') } const NavLinkPropTypes = { diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 1db43473ea78f..e5b23ba15cf4b 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -4,7 +4,7 @@ "version": "1.9.3", "author": "Kyle Mathews ", "bin": { - "gatsby": "./bin/gatsby.js" + "gatsby": "./dist/gatsby-cli.js" }, "bugs": { "url": "https://github.com/gatsbyjs/gatsby/issues" @@ -50,7 +50,6 @@ "friendly-errors-webpack-plugin": "^1.6.1", "front-matter": "^2.1.0", "fs-extra": "^3.0.1", - "gatsby-cli": "^1.1.2", "glob": "^7.1.1", "graphql": "^0.10.3", "graphql-relay": "^0.5.1", @@ -152,12 +151,13 @@ "url": "git+https://github.com/gatsbyjs/gatsby.git" }, "scripts": { - "build": "rimraf dist && npm run build:src && npm run build:internal-plugins && npm run build:rawfiles", + "build": "rimraf dist && npm run build:src && npm run build:cli && npm run build:internal-plugins && npm run build:rawfiles", + "build:cli": "babel src/gatsby-cli.js --out-file dist/gatsby-cli.js --presets es2015", "build:internal-plugins": "copyfiles -u 1 src/internal-plugins/**/package.json dist", "build:rawfiles": "copyfiles -u 1 src/internal-plugins/**/raw_* dist", - "build:src": "babel src --out-dir dist --source-maps raw_*,__tests__", + "build:src": "babel src --out-dir dist --source-maps --ignore gatsby-cli.js,raw_*,__tests__", "clean-test-bundles": "find test/ -type f -name bundle.js* -exec rm -rf {} +", "test-coverage": "node_modules/.bin/nyc --reporter=lcov --reporter=text npm test", - "watch": "rimraf dist && mkdir dist && npm run build:internal-plugins && npm run build:rawfiles && npm run build:src -- --watch" + "watch": "rimraf dist && mkdir dist && npm run build:cli && npm run build:internal-plugins && npm run build:rawfiles && npm run build:src -- --watch" } } diff --git a/packages/gatsby/src/bin/cli.js b/packages/gatsby/src/bin/cli.js new file mode 100644 index 0000000000000..9b5545d15ecf1 --- /dev/null +++ b/packages/gatsby/src/bin/cli.js @@ -0,0 +1,165 @@ +// babel-preset-env doesn't find this import if you +// use require() with backtick strings so use the es6 syntax +import "babel-polyfill" + +const program = require(`commander`) +const packageJson = require(`../../package.json`) +const path = require(`path`) +const _ = require(`lodash`) +const Promise = require(`bluebird`) +const resolveCwd = require(`resolve-cwd`) + +const report = require(`../reporter`) + +// Improve Promise error handling. Maybe... what's the best +// practice for this these days? +global.Promise = require(`bluebird`) + +Promise.onPossiblyUnhandledRejection(error => { + report.error(error) + throw error +}) + +process.on(`unhandledRejection`, error => { + // This will exit the process in newer Node anyway so lets be consistent + // across versions and crash + report.panic(`UNHANDLED REJECTION`, error) +}) + +process.on(`uncaughtException`, error => { + report.panic(`UNHANDLED EXCEPTION`, error) +}) + +const defaultHost = `localhost` + +let inGatsbySite = false +let localPackageJSON +try { + localPackageJSON = require(path.resolve(`./package.json`)) + if (localPackageJSON.dependencies && localPackageJSON.dependencies.gatsby) { + inGatsbySite = true + } +} catch (err) { + // ignore +} + +const directory = path.resolve(`.`) +const getSiteInfo = () => { + const sitePackageJson = require(path.join(directory, `package.json`)) + const browserslist = sitePackageJson.browserslist || [ + `> 1%`, + `last 2 versions`, + `IE >= 9`, + ] + return { sitePackageJson, browserslist } +} + +program.version(packageJson.version).usage(`[command] [options]`) + +// If there's a package.json in the current directory w/ a gatsby dependency +// include the develop/build/serve commands. Otherwise, just the new. +if (inGatsbySite) { + program + .command(`develop`) + .description( + `Start development server. Watches files and rebuilds and hot reloads ` + + `if something changes` + ) // eslint-disable-line max-len + .option( + `-H, --host `, + `Set host. Defaults to ${defaultHost}`, + defaultHost + ) + .option(`-p, --port `, `Set port. Defaults to 8000`, `8000`) + .option(`-o, --open`, `Open the site in your browser for you.`) + .action(command => { + const developPath = resolveCwd(`gatsby/dist/utils/develop`) + const develop = require(developPath) + // console.timeEnd(`time to load develop`) + const { sitePackageJson, browserslist } = getSiteInfo() + const p = { + ...command, + directory, + sitePackageJson, + browserslist, + } + develop(p) + }) + + program + .command(`build`) + .description(`Build a Gatsby project.`) + .option( + `--prefix-paths`, + `Build site with link paths prefixed (set prefix in your config).` + ) + .action(command => { + // Set NODE_ENV to 'production' + process.env.NODE_ENV = `production` + + const buildPath = resolveCwd(`gatsby/dist/utils/build`) + const build = require(buildPath) + const { sitePackageJson, browserslist } = getSiteInfo() + const p = { + ...command, + directory, + sitePackageJson, + browserslist, + } + build(p).then(() => { + report.success(`Done building in ${process.uptime()} seconds`) + process.exit() + }) + }) + + program + .command(`serve`) + .description(`Serve built site.`) + .option( + `-H, --host `, + `Set host. Defaults to ${defaultHost}`, + defaultHost + ) + .option(`-p, --port `, `Set port. Defaults to 9000`, `9000`) + .option(`-o, --open`, `Open the site in your browser for you.`) + .action(command => { + const servePath = resolveCwd(`gatsby/dist/utils/serve`) + const serve = require(servePath) + const { sitePackageJson, browserslist } = getSiteInfo() + const p = { + ...command, + directory, + sitePackageJson, + browserslist, + } + serve(p) + }) +} + +program + .command(`new [rootPath] [starter]`) + .description(`Create new Gatsby project.`) + .action((rootPath, starter) => { + const newCommand = require(`../utils/new`) + newCommand(rootPath, starter) + }) + +program.on(`--help`, () => { + console.log( + `To show subcommand help: + + gatsby [command] -h +` + ) +}) + +// If the user types an unknown sub-command, just display the help. +const subCmd = process.argv.slice(2, 3)[0] +let cmds = _.map(program.commands, `_name`) +cmds = cmds.concat([`--version`, `-V`]) + +if (!_.includes(cmds, subCmd)) { + program.help() +} else { + program.parse(process.argv) +} diff --git a/packages/gatsby/src/bin/gatsby.js b/packages/gatsby/src/bin/gatsby.js deleted file mode 100644 index 6c430b053d924..0000000000000 --- a/packages/gatsby/src/bin/gatsby.js +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env node - -require(`gatsby-cli`) diff --git a/packages/gatsby/src/bootstrap/index.js b/packages/gatsby/src/bootstrap/index.js index 6859400186b6b..2bf2c0d42a976 100644 --- a/packages/gatsby/src/bootstrap/index.js +++ b/packages/gatsby/src/bootstrap/index.js @@ -14,7 +14,7 @@ const { graphql } = require(`graphql`) const { store, emitter } = require(`../redux`) const loadPlugins = require(`./load-plugins`) const { initCache } = require(`../utils/cache`) -const report = require(`gatsby-cli/lib/reporter`) +const report = require(`../reporter`) const { extractQueries, diff --git a/packages/gatsby/src/bootstrap/load-plugins.js b/packages/gatsby/src/bootstrap/load-plugins.js index 82da423dd477d..9b9953f003e66 100644 --- a/packages/gatsby/src/bootstrap/load-plugins.js +++ b/packages/gatsby/src/bootstrap/load-plugins.js @@ -8,7 +8,7 @@ const glob = require(`glob`) const { store } = require(`../redux`) const nodeAPIs = require(`../utils/api-node-docs`) const testRequireError = require(`../utils/test-require-error`) -const report = require(`gatsby-cli/lib/reporter`) +const report = require(`../reporter`) function createFileContentHash(root, globPattern) { const hash = crypto.createHash(`md5`) diff --git a/packages/gatsby/src/gatsby-cli.js b/packages/gatsby/src/gatsby-cli.js new file mode 100755 index 0000000000000..c49c9cb08e52f --- /dev/null +++ b/packages/gatsby/src/gatsby-cli.js @@ -0,0 +1,45 @@ +#!/usr/bin/env node +const path = require(`path`) +const fs = require(`fs`) +const _ = require(`lodash`) + +const report = require(`./reporter`) + +const version = process.version +const verDigit = Number(version.match(/\d+/)[0]) + +if (verDigit < 4) { + report.panic( + `Gatsby 1.0+ requires node.js v4 or higher (you have ${version}). \n` + + `Upgrade node to the latest stable release.` + ) +} + +/* + Get the locally installed version of gatsby/lib/bin/cli.js from the place + where this program was executed. +*/ +const cliFile = `dist/bin/cli.js` +const localPath = path.resolve(`node_modules/gatsby`, cliFile) + +const useGlobalGatsby = function() { + // Never use global install *except* for new and help commands + if (!_.includes([`new`, `--help`], process.argv[2])) { + report.panic( + `A local install of Gatsby was not found. \n` + + `You should save Gatsby as a site dependency e.g. npm install --save gatsby` + ) + } + + require(`./bin/cli`) +} + +if (fs.existsSync(localPath)) { + try { + require(localPath) + } catch (error) { + report.error(`A local install of Gatsby exists but failed to load.`, error) + } +} else { + useGlobalGatsby() +} diff --git a/packages/gatsby/src/internal-plugins/query-runner/file-parser.js b/packages/gatsby/src/internal-plugins/query-runner/file-parser.js index 69ed5dfecb010..90a1f7fdf8b24 100644 --- a/packages/gatsby/src/internal-plugins/query-runner/file-parser.js +++ b/packages/gatsby/src/internal-plugins/query-runner/file-parser.js @@ -6,7 +6,7 @@ const crypto = require(`crypto`) import traverse from "babel-traverse" const babylon = require(`babylon`) -const report = require(`gatsby-cli/lib/reporter`) +const report = require(`../../reporter`) const { getGraphQLTag } = require(`../../utils/babel-plugin-extract-graphql`) import type { DocumentNode, DefinitionNode } from "graphql" diff --git a/packages/gatsby/src/internal-plugins/query-runner/graphql-errors.js b/packages/gatsby/src/internal-plugins/query-runner/graphql-errors.js index 169895db389aa..7425b82ea28f4 100644 --- a/packages/gatsby/src/internal-plugins/query-runner/graphql-errors.js +++ b/packages/gatsby/src/internal-plugins/query-runner/graphql-errors.js @@ -3,7 +3,7 @@ import { print, visit, GraphQLError, getLocation } from "graphql" import babelCodeFrame from "babel-code-frame" import _ from "lodash" -import report from "gatsby-cli/lib/reporter" +import report from "../../reporter" type RelayGraphQLError = Error & { validationErrors?: Object } diff --git a/packages/gatsby/src/internal-plugins/query-runner/query-compiler.js b/packages/gatsby/src/internal-plugins/query-runner/query-compiler.js index 07b2bfd2e5a6a..73edd8664e84c 100644 --- a/packages/gatsby/src/internal-plugins/query-runner/query-compiler.js +++ b/packages/gatsby/src/internal-plugins/query-runner/query-compiler.js @@ -19,7 +19,7 @@ import { graphqlValidationError, multipleRootQueriesError, } from "./graphql-errors" -import report from "gatsby-cli/lib/reporter" +import report from "../../reporter" import type { DocumentNode, GraphQLSchema } from "graphql" diff --git a/packages/gatsby/src/internal-plugins/query-runner/query-runner.js b/packages/gatsby/src/internal-plugins/query-runner/query-runner.js index 4ef2b0d82da81..39a2bb3d25769 100644 --- a/packages/gatsby/src/internal-plugins/query-runner/query-runner.js +++ b/packages/gatsby/src/internal-plugins/query-runner/query-runner.js @@ -1,10 +1,10 @@ import { graphql as graphqlFunction } from "graphql" const fs = require(`fs`) const Promise = require(`bluebird`) -const report = require(`gatsby-cli/lib/reporter`) const writeFileAsync = Promise.promisify(fs.writeFile) const { joinPath } = require(`../../utils/path`) +const report = require(`../../reporter`) const { store } = require(`../../redux`) @@ -41,7 +41,14 @@ module.exports = async (pageOrLayout, component) => { ${component.query} ` ) - + console.log(``) + console.log(``) + console.log(``) + console.log(`Query:`) + console.log(component.query) + console.log(``) + console.log(`GraphQL Error:`) + console.log(result.errors) // Perhaps this isn't the best way to see if we're building? if (program._name === `build`) { process.exit(1) diff --git a/packages/gatsby/src/redux/__tests__/pages.js b/packages/gatsby/src/redux/__tests__/pages.js index 60e8a9db7add8..a25fa99b2812d 100644 --- a/packages/gatsby/src/redux/__tests__/pages.js +++ b/packages/gatsby/src/redux/__tests__/pages.js @@ -2,10 +2,10 @@ const reducer = require(`../reducers/pages`) const { actions } = require(`../actions`) const start = Date.now() -Date.now = jest.fn(() => +Date.now = jest.fn(() => { // const diff = new Date().getTime() - start - 1482363367071 // + diff -) + return 1482363367071 // + diff +}) describe(`Add pages`, () => { it(`allows you to add pages`, () => { diff --git a/packages/gatsby-cli/src/reporter/errors.js b/packages/gatsby/src/reporter/errors.js similarity index 100% rename from packages/gatsby-cli/src/reporter/errors.js rename to packages/gatsby/src/reporter/errors.js diff --git a/packages/gatsby-cli/src/reporter/index.js b/packages/gatsby/src/reporter/index.js similarity index 100% rename from packages/gatsby-cli/src/reporter/index.js rename to packages/gatsby/src/reporter/index.js diff --git a/packages/gatsby/src/utils/api-runner-node.js b/packages/gatsby/src/utils/api-runner-node.js index ad51d22d67b51..ba4c23bc5bd92 100644 --- a/packages/gatsby/src/utils/api-runner-node.js +++ b/packages/gatsby/src/utils/api-runner-node.js @@ -3,7 +3,7 @@ const glob = require(`glob`) const _ = require(`lodash`) const mapSeries = require(`async/mapSeries`) -const reporter = require(`gatsby-cli/lib/reporter`) +const reporter = require(`../reporter`) const cache = require(`./cache`) const apiList = require(`./api-node-docs`) diff --git a/packages/gatsby/src/commands/build-css.js b/packages/gatsby/src/utils/build-css.js similarity index 84% rename from packages/gatsby/src/commands/build-css.js rename to packages/gatsby/src/utils/build-css.js index a2f1ff6329348..abf9fd1b11f32 100644 --- a/packages/gatsby/src/commands/build-css.js +++ b/packages/gatsby/src/utils/build-css.js @@ -1,7 +1,8 @@ /* @flow */ -const webpack = require(`webpack`) -const fs = require(`fs-extra`) -const webpackConfig = require(`../utils/webpack.config`) +import webpack from "webpack" +import fs from "fs-extra" +import Promise from "bluebird" +import webpackConfig from "./webpack.config" module.exports = async (program: any) => { const { directory } = program diff --git a/packages/gatsby/src/commands/build-html.js b/packages/gatsby/src/utils/build-html.js similarity index 84% rename from packages/gatsby/src/commands/build-html.js rename to packages/gatsby/src/utils/build-html.js index 836c453c5c711..327516c0956aa 100644 --- a/packages/gatsby/src/commands/build-html.js +++ b/packages/gatsby/src/utils/build-html.js @@ -1,9 +1,10 @@ /* @flow */ -const webpack = require(`webpack`) -const fs = require(`fs`) -const webpackConfig = require(`../utils/webpack.config`) +import webpack from "webpack" +import Promise from "bluebird" +import fs from "fs" +import webpackConfig from "./webpack.config" const { store } = require(`../redux`) -const { createErrorFromString } = require(`gatsby-cli/lib/reporter/errors`) +const { createErrorFromString } = require(`../reporter/errors`) const debug = require(`debug`)(`gatsby:html`) diff --git a/packages/gatsby/src/commands/build-javascript.js b/packages/gatsby/src/utils/build-javascript.js similarity index 72% rename from packages/gatsby/src/commands/build-javascript.js rename to packages/gatsby/src/utils/build-javascript.js index 842d52ca31a91..f51570b0ad4af 100644 --- a/packages/gatsby/src/commands/build-javascript.js +++ b/packages/gatsby/src/utils/build-javascript.js @@ -1,6 +1,7 @@ /* @flow */ -const webpack = require(`webpack`) -const webpackConfig = require(`../utils/webpack.config`) +import webpack from "webpack" +import Promise from "bluebird" +import webpackConfig from "./webpack.config" module.exports = async program => { const { directory } = program diff --git a/packages/gatsby/src/commands/build.js b/packages/gatsby/src/utils/build.js similarity index 74% rename from packages/gatsby/src/commands/build.js rename to packages/gatsby/src/utils/build.js index 539bd0c76d252..079c1aaae1a3c 100644 --- a/packages/gatsby/src/commands/build.js +++ b/packages/gatsby/src/utils/build.js @@ -4,10 +4,10 @@ const buildCSS = require(`./build-css`) const buildHTML = require(`./build-html`) const buildProductionBundle = require(`./build-javascript`) const bootstrap = require(`../bootstrap`) -const report = require(`gatsby-cli/lib/reporter`) -const { formatStaticBuildError } = require(`gatsby-cli/lib/reporter/errors`) -const apiRunnerNode = require(`../utils/api-runner-node`) -const copyStaticDirectory = require(`../utils/copy-static-directory`) +const report = require(`../reporter`) +const { formatStaticBuildError } = require(`../reporter/errors`) +const apiRunnerNode = require(`./api-runner-node`) +const copyStaticDirectory = require(`./copy-static-directory`) function reportFailure(msg, err: Error) { report.log(``) @@ -17,7 +17,7 @@ function reportFailure(msg, err: Error) { ) } -module.exports = async function build(program: any) { +async function html(program: any) { const { graphqlRunner } = await bootstrap(program) // Copy files from the static directory to // an equivalent static directory within public. @@ -33,7 +33,7 @@ module.exports = async function build(program: any) { activity = report.activityTimer(`Compiling production bundle.js`) activity.start() await buildProductionBundle(program).catch(err => { - reportFailure(`Generating site JavaScript failed`, err) + reportFailure(`Generating JS failed`, err) }) activity.end() @@ -52,7 +52,6 @@ module.exports = async function build(program: any) { activity.end() await apiRunnerNode(`onPostBuild`, { graphql: graphqlRunner }) - - report.info(`Done building in ${process.uptime()} sec`) - process.exit() } + +module.exports = html diff --git a/packages/gatsby/src/commands/develop-html.js b/packages/gatsby/src/utils/develop-html.js similarity index 86% rename from packages/gatsby/src/commands/develop-html.js rename to packages/gatsby/src/utils/develop-html.js index 806af43ede6f2..b2ac221e618c1 100644 --- a/packages/gatsby/src/commands/develop-html.js +++ b/packages/gatsby/src/utils/develop-html.js @@ -1,9 +1,10 @@ /* @flow */ -const fs = require(`fs`) const webpack = require(`webpack`) -const { createErrorFromString } = require(`gatsby-cli/lib/reporter/errors`) +const Promise = require(`bluebird`) +const fs = require(`fs`) +const webpackConfig = require(`./webpack.config`) +const { createErrorFromString } = require(`../reporter/errors`) const debug = require(`debug`)(`gatsby:html`) -const webpackConfig = require(`../utils/webpack.config`) module.exports = async (program: any) => { const { directory } = program diff --git a/packages/gatsby/src/commands/develop.js b/packages/gatsby/src/utils/develop.js similarity index 94% rename from packages/gatsby/src/commands/develop.js rename to packages/gatsby/src/utils/develop.js index bce0425dcf91f..8a97d9fdd3942 100644 --- a/packages/gatsby/src/commands/develop.js +++ b/packages/gatsby/src/utils/develop.js @@ -1,20 +1,20 @@ /* @flow */ -const chokidar = require(`chokidar`) const express = require(`express`) const graphqlHTTP = require(`express-graphql`) -const parsePath = require(`parse-filepath`) const request = require(`request`) -const rl = require(`readline`) -const webpack = require(`webpack`) -const webpackConfig = require(`../utils/webpack.config`) const bootstrap = require(`../bootstrap`) +const chokidar = require(`chokidar`) +const webpack = require(`webpack`) +const webpackConfig = require(`./webpack.config`) +const rl = require(`readline`) +const parsePath = require(`parse-filepath`) const { store } = require(`../redux`) -const copyStaticDirectory = require(`../utils/copy-static-directory`) +const copyStaticDirectory = require(`./copy-static-directory`) const developHtml = require(`./develop-html`) -const { withBasePath } = require(`../utils/path`) -const report = require(`gatsby-cli/lib/reporter`) -const { formatStaticBuildError } = require(`gatsby-cli/lib/reporter/errors`) +const { withBasePath } = require(`./path`) +const report = require(`../reporter`) +const { formatStaticBuildError } = require(`../reporter/errors`) // Watch the static directory and copy files to public as they're added or // changed. Wait 10 seconds so copying doesn't interfer with the regular diff --git a/packages/gatsby/src/utils/init-starter.js b/packages/gatsby/src/utils/init-starter.js new file mode 100644 index 0000000000000..e14dcfeb6c645 --- /dev/null +++ b/packages/gatsby/src/utils/init-starter.js @@ -0,0 +1,132 @@ +/* @flow weak */ +import { exec, execSync } from "child_process" +import hostedGitInfo from "hosted-git-info" +import fs from "fs-extra" +import sysPath from "path" + +let logger = console + +// Checks the existence of yarn package +// We use yarnpkg instead of yarn to avoid conflict with Hadoop yarn +// Refer to https://github.com/yarnpkg/yarn/issues/673 +// +// Returns true if yarn exists, false otherwise +const shouldUseYarn = () => { + try { + execSync(`yarnpkg --version`, { stdio: `ignore` }) + return true + } catch (e) { + return false + } +} + +// Executes `npm install` and `bower install` in rootPath. +// +// rootPath - String. Path to directory in which command will be executed. +// callback - Function. Takes stderr and stdout of executed process. +// +// Returns nothing. +const install = (rootPath, callback) => { + const prevDir = process.cwd() + logger.log(`Installing packages...`) + process.chdir(rootPath) + const installCmd = shouldUseYarn() ? `yarnpkg` : `npm install` + exec(installCmd, (error, stdout, stderr) => { + process.chdir(prevDir) + if (stdout) console.log(stdout.toString()) + if (error !== null) { + const msg = stderr.toString() + callback(new Error(msg)) + } + callback(null, stdout) + }) +} + +const ignored = path => !/^\.(git|hg)$/.test(sysPath.basename(path)) + +// Copy starter from file system. +// +// starterPath - String, file system path from which files will be taken. +// rootPath - String, directory to which starter files will be copied. +// callback - Function. +// +// Returns nothing. +const copy = (starterPath, rootPath, callback) => { + const copyDirectory = () => { + fs.copy(starterPath, rootPath, { filter: ignored }, error => { + if (error !== null) return callback(new Error(error)) + logger.log(`Created starter directory layout`) + install(rootPath, callback) + return false + }) + } + + // Chmod with 755. + // 493 = parseInt('755', 8) + fs.mkdirp(rootPath, { mode: 493 }, error => { + if (error !== null) callback(new Error(error)) + return fs.exists(starterPath, exists => { + if (!exists) { + const chmodError = `starter ${starterPath} doesn't exist` + return callback(new Error(chmodError)) + } + logger.log(`Copying local starter to ${rootPath} ...`) + + copyDirectory() + return true + }) + }) +} + +// Clones starter from URI. +// +// address - String, URI. https:, github: or git: may be used. +// rootPath - String, directory to which starter files will be copied. +// callback - Function. +// +// Returns nothing. +const clone = (hostInfo, rootPath, callback) => { + const url = hostInfo.git({ noCommittish: true }) + const branch = hostInfo.committish ? `-b ${hostInfo.committish}` : `` + + logger.log(`Cloning git repo ${url} to ${rootPath}...`) + const cmd = `git clone ${branch} ${url} ${rootPath} --single-branch` + + exec(cmd, (error, stdout, stderr) => { + if (error !== null) { + return callback(new Error(`Git clone error: ${stderr.toString()}`)) + } + logger.log(`Created starter directory layout`) + return fs.remove(sysPath.join(rootPath, `.git`), removeError => { + if (error !== null) return callback(new Error(removeError)) + install(rootPath, callback) + return true + }) + }) +} + +// Main function that clones or copies the starter. +// +// starter - String, file system path or URI of starter. +// rootPath - String, directory to which starter files will be copied. +// callback - Function. +// +// Returns nothing. +const initStarter = (starter, options = {}) => + new Promise((resolve, reject) => { + const callback = (err, value) => (err ? reject(err) : resolve(value)) + + const cwd = process.cwd() + const rootPath = options.rootPath || cwd + if (options.logger) logger = options.logger + + if (fs.existsSync(sysPath.join(rootPath, `package.json`))) + throw new Error(`Directory ${rootPath} is already an npm project`) + + const hostedInfo = hostedGitInfo.fromUrl(starter) + + if (hostedInfo) clone(hostedInfo, rootPath, callback) + else copy(starter, rootPath, callback) + }) + +module.exports = initStarter diff --git a/packages/gatsby/src/utils/new.js b/packages/gatsby/src/utils/new.js new file mode 100644 index 0000000000000..afa14d139110e --- /dev/null +++ b/packages/gatsby/src/utils/new.js @@ -0,0 +1,8 @@ +/* @flow weak */ +const logger = require(`tracer`).colorConsole() + +const initStarter = require(`./init-starter`) + +module.exports = (rootPath, starter = `gatsbyjs/gatsby-starter-default`) => { + initStarter(starter, { rootPath, logger }).catch(error => logger.error(error)) +} diff --git a/packages/gatsby/src/commands/serve.js b/packages/gatsby/src/utils/serve.js similarity index 100% rename from packages/gatsby/src/commands/serve.js rename to packages/gatsby/src/utils/serve.js diff --git a/www/src/pages/tutorial.js b/www/src/pages/tutorial.js index f7daf8f4467bd..63731e840a137 100644 --- a/www/src/pages/tutorial.js +++ b/www/src/pages/tutorial.js @@ -36,7 +36,7 @@ export default () =>
  • Learn how to work with Gatsby's data layer. - {` `} + {" "} Explore source & transformer plugins. Get introduced to programmatic pages and how to write GraphQL queries. In this part of the tutorial we'll build a simple markdown blog.