diff --git a/packages/next/README.md b/packages/next/README.md index 485f8e32..a6b2073a 100644 --- a/packages/next/README.md +++ b/packages/next/README.md @@ -62,6 +62,7 @@ - [Configuring the onDemandEntries](#configuring-the-ondemandentries) - [Configuring extensions looked for when resolving pages in `pages`](#configuring-extensions-looked-for-when-resolving-pages-in-pages) - [Configuring the build ID](#configuring-the-build-id) + - [Configuring Next process script](#configuring-next-process-script) - [Customizing webpack config](#customizing-webpack-config) - [Customizing babel config](#customizing-babel-config) - [Exposing configuration to the server / client side](#exposing-configuration-to-the-server--client-side) @@ -1337,6 +1338,17 @@ module.exports = { } ``` +#### Configuring next process script + +You can pass any node arguments to `next` cli command. + + +```bash +next --node-args="--throw-deprecation" +next start --node-args="--inspect" +next build --node-args="-r esm" +``` + ### Customizing webpack config
diff --git a/packages/next/bin/next b/packages/next/bin/next deleted file mode 100755 index 196f138e..00000000 --- a/packages/next/bin/next +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env node -import { join } from 'path' -import { spawn } from 'cross-spawn' -import pkg from '../../package.json' -import {CONFIG_FILE} from 'next-server/constants' - -if (pkg.peerDependencies) { - Object.keys(pkg.peerDependencies).forEach(dependency => { - try { - // When 'npm link' is used it checks the clone location. Not the project. - require.resolve(dependency) - } catch (err) { - console.warn(`The module '${dependency}' was not found. Next.js requires that you include it in 'dependencies' of your 'package.json'. To add it, run 'npm install --save ${dependency}'`) - } - }) -} - -const defaultCommand = 'dev' -const commands = new Set([ - 'init', - 'build', - 'start', - 'export', - defaultCommand -]) - -let cmd = process.argv[2] -let args = [] -let nodeArgs = [] - -if (new Set(['--version', '-v']).has(cmd)) { - console.log(`next.js v${pkg.version}`) - process.exit(0) -} - -const inspectArg = process.argv.find(arg => arg.includes('--inspect')) -if (inspectArg) { - nodeArgs.push(inspectArg) -} - -if (new Set(['--help', '-h']).has(cmd)) { - console.log(` - Usage - $ next - - Available commands - ${Array.from(commands).join(', ')} - - For more information run a command with the --help flag - $ next init --help - `) - process.exit(0) -} - -if (commands.has(cmd)) { - args = process.argv.slice(3) -} else { - cmd = defaultCommand - args = process.argv.slice(2) -} - -const defaultEnv = cmd === 'dev' ? 'development' : 'production' -process.env.NODE_ENV = process.env.NODE_ENV || defaultEnv - -const bin = join(__dirname, 'next-' + cmd) - -const startProcess = () => { - const proc = spawn('node', [...nodeArgs, ...[bin], ...args], { stdio: 'inherit', customFds: [0, 1, 2] }) - proc.on('close', (code, signal) => { - if (code !== null) { - process.exit(code) - } - if (signal) { - if (signal === 'SIGKILL') { - process.exit(137) - } - console.log(`got signal ${signal}, exiting`) - process.exit(signal === 'SIGINT' ? 0 : 1) - } - process.exit(0) - }) - proc.on('error', (err) => { - console.error(err) - process.exit(1) - }) - return proc -} - -let proc = startProcess() - -const wrapper = () => { - if (proc) { - proc.kill() - } -} -process.on('SIGINT', wrapper) -process.on('SIGTERM', wrapper) -process.on('exit', wrapper) - -if (cmd === 'dev') { - const {watchFile} = require('fs') - watchFile(`${process.cwd()}/${CONFIG_FILE}`, (cur, prev) => { - if (cur.size > 0 || prev.size > 0) { - console.log(`\n> Found a change in ${CONFIG_FILE}, restarting the server...`) - // Don't listen to 'close' now since otherwise parent gets killed by listener - proc.removeAllListeners('close') - proc.kill() - proc = startProcess() - } - }) -} diff --git a/packages/next/bin/next-build b/packages/next/bin/next-build.ts similarity index 82% rename from packages/next/bin/next-build rename to packages/next/bin/next-build.ts index 197ee07e..34bb1f78 100755 --- a/packages/next/bin/next-build +++ b/packages/next/bin/next-build.ts @@ -1,19 +1,21 @@ #!/usr/bin/env node import { resolve, join } from 'path' import { existsSync } from 'fs' -import parseArgs from 'minimist' +import arg from 'arg' import build from '../build' import { printAndExit } from '../server/lib/utils' -const argv = parseArgs(process.argv.slice(2), { - alias: { - h: 'help', - l: 'lambdas' - }, - boolean: ['h', 'l'] +const args = arg({ + // Types + '--help': Boolean, + '--lambdas': Boolean, + + // Aliases + '-h': '--help', + '-l': '--lambdas' }) -if (argv.help) { +if (args['--help']) { printAndExit(` Description Compiles the application for production deployment @@ -27,8 +29,8 @@ if (argv.help) { `, 0) } -const dir = resolve(argv._[0] || '.') -const lambdas = argv.lambdas +const dir = resolve(args._[0] || '.') +const lambdas = args['--lambdas'] // Check if pages dir exists and warn if not if (!existsSync(dir)) { diff --git a/packages/next/bin/next-dev b/packages/next/bin/next-dev.ts similarity index 79% rename from packages/next/bin/next-dev rename to packages/next/bin/next-dev.ts index 80dddb14..aac9c459 100755 --- a/packages/next/bin/next-dev +++ b/packages/next/bin/next-dev.ts @@ -1,22 +1,23 @@ #!/usr/bin/env node import { resolve, join } from 'path' -import parseArgs from 'minimist' +import arg from 'arg' import { existsSync } from 'fs' import startServer from '../server/lib/start-server' import { printAndExit } from '../server/lib/utils' -const argv = parseArgs(process.argv.slice(2), { - alias: { - h: 'help', - H: 'hostname', - p: 'port' - }, - boolean: ['h'], - string: ['H'], - default: { p: 3000 } +const args = arg({ + // Types + '--help': Boolean, + '--port': Number, + '--hostname': String, + + // Aliases + '-h': '--help', + '-p': '--port', + '-H': '--hostname' }) -if (argv.help) { +if (args['--help']) { console.log(` Description Starts the application in development mode (hot-code reloading, error @@ -37,7 +38,7 @@ if (argv.help) { process.exit(0) } -const dir = resolve(argv._[0] || '.') +const dir = resolve(args._[0] || '.') // Check if pages dir exists and warn if not if (!existsSync(dir)) { @@ -52,13 +53,14 @@ if (!existsSync(join(dir, 'pages'))) { printAndExit('> Couldn\'t find a `pages` directory. Please create one under the project root') } -startServer({dir, dev: true}, argv.port, argv.hostname) +const port = args['--port'] || 3000 +startServer({dir, dev: true}, port, args['--hostname']) .then(async () => { - console.log(`> Ready on http://${argv.hostname ? argv.hostname : 'localhost'}:${argv.port}`) + console.log(`> Ready on http://${args['--hostname'] || 'localhost'}:${port}`) }) .catch((err) => { if (err.code === 'EADDRINUSE') { - let errorMessage = `Port ${argv.port} is already in use.` + let errorMessage = `Port ${port} is already in use.` const pkgAppPath = require('find-up').sync('package.json', { cwd: dir }) diff --git a/packages/next/bin/next-export b/packages/next/bin/next-export.ts similarity index 72% rename from packages/next/bin/next-export rename to packages/next/bin/next-export.ts index ddb93020..88d78713 100755 --- a/packages/next/bin/next-export +++ b/packages/next/bin/next-export.ts @@ -1,24 +1,25 @@ #!/usr/bin/env node import { resolve, join } from 'path' import { existsSync } from 'fs' -import parseArgs from 'minimist' +import arg from 'arg' import exportApp from '../export' import { printAndExit } from '../server/lib/utils' -const argv = parseArgs(process.argv.slice(2), { - alias: { - h: 'help', - s: 'silent', - o: 'outdir' - }, - boolean: ['h'], - default: { - s: false, - o: null - } +const args = arg({ + // Types + '--help': Boolean, + '--silent': Boolean, + '--outdir': String, + '--threads': Number, + '--concurrency': Number, + + // Aliases + '-h': '--help', + '-s': '--silent', + '-o': '--outdir' }) -if (argv.help) { +if (args['--help']) { console.log(` Description Exports the application for production deployment @@ -37,7 +38,7 @@ if (argv.help) { process.exit(0) } -const dir = resolve(argv._[0] || '.') +const dir = resolve(args._[0] || '.') // Check if pages dir exists and warn if not if (!existsSync(dir)) { @@ -53,10 +54,10 @@ if (!existsSync(join(dir, 'pages'))) { } const options = { - silent: argv.silent, - threads: argv.threads, - concurrency: argv.concurrency, - outdir: argv.outdir ? resolve(argv.outdir) : resolve(dir, 'out') + silent: args['--silent'] || false, + threads: args['--threads'], + concurrency: args['--concurrency'], + outdir: args['--outdir'] ? resolve(args['--outdir']) : join(dir, 'out') } exportApp(dir, options) diff --git a/packages/next/bin/next-init b/packages/next/bin/next-init deleted file mode 100755 index b3b74944..00000000 --- a/packages/next/bin/next-init +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env node -console.log('`next init` is not supported anymore. These community projects provide the same functionality as `next init` with additional features: http://npmjs.com/next-init and http://npmjs.com/create-next-app.') - -process.exit(0) diff --git a/packages/next/bin/next-start b/packages/next/bin/next-start.ts similarity index 63% rename from packages/next/bin/next-start rename to packages/next/bin/next-start.ts index cce686e6..8be936a7 100755 --- a/packages/next/bin/next-start +++ b/packages/next/bin/next-start.ts @@ -1,26 +1,22 @@ #!/usr/bin/env node import { resolve } from 'path' -import parseArgs from 'minimist' +import arg from 'arg' import startServer from '../server/lib/start-server' -const argv = parseArgs(process.argv.slice(2), { - alias: { - h: 'help', - H: 'hostname', - p: 'port' - }, - boolean: ['h'], - string: ['H'], - default: { p: 3000 } +const args = arg({ + // Types + '--help': Boolean, + '--port': Number, + '--hostname': String, + + // Aliases + '-h': '--help', + '-p': '--port', + '-H': '--hostname' }) -if (argv.hostname === '') { - console.error(`> Provided hostname argument has no value`) - process.exit(1) -} - -if (argv.help) { +if (args['--help']) { console.log(` Description Starts the application in production mode. @@ -42,11 +38,11 @@ if (argv.help) { process.exit(0) } -const dir = resolve(argv._[0] || '.') - -startServer({dir}, argv.port, argv.hostname) +const dir = resolve(args._[0] || '.') +const port = args['--port'] || 3000 +startServer({dir}, port, args['--hostname']) .then(() => { - console.log(`> Ready on http://${argv.hostname ? argv.hostname : 'localhost'}:${argv.port}`) + console.log(`> Ready on http://${args['--hostname']}:${port}`) }) .catch((err) => { console.error(err) diff --git a/packages/next/bin/next.ts b/packages/next/bin/next.ts new file mode 100755 index 00000000..03ddfa4a --- /dev/null +++ b/packages/next/bin/next.ts @@ -0,0 +1,137 @@ +#!/usr/bin/env node +import { join } from 'path' +import spawn from 'cross-spawn' +import arg from 'arg' + +['react', 'react-dom'].forEach(dependency => { + try { + // When 'npm link' is used it checks the clone location. Not the project. + require.resolve(dependency) + } catch (err) { + console.warn(`The module '${dependency}' was not found. Next.js requires that you include it in 'dependencies' of your 'package.json'. To add it, run 'npm install --save ${dependency}'`) + } +}) + +const defaultCommand = 'dev' +const commands = [ + 'build', + 'start', + 'export', + defaultCommand +] + +const args = arg({ + // Types + '--version': Boolean, + '--help': Boolean, + '--node-args': String, + '--inspect': Boolean, + + // Aliases + '-v': '--version', + '-h': '--help' +}, { + permissive: true +}) + +// Version is inlined into the file using taskr build pipeline +if (args['--version']) { + console.log(`Next.js v${process.env.NEXT_VERSION}`) + process.exit(0) +} + +// Check if we are running `next ` or `next` +const foundCommand = args._.find(cmd => commands.includes(cmd)) + +// Makes sure the `next --help` case is covered +// This help message is only showed for `next --help` +if (!foundCommand && args['--help']) { + console.log(` + Usage + $ next + + Available commands + ${commands.join(', ')} + + Options + --version, -p Version number + --node-args Node arguments applied to the process + --help, -h Displays this message + + For more information run a command with the --help flag + $ next build --help + `) + process.exit(0) +} + +// Add support for `--node-args` to send Node.js arguments to the spawned process +const nodeArguments = args['--node-args'] && args['--node-args'] !== '' ? args['--node-args'].split(' ') : [] +if (args['--inspect']) { + console.log('The `--inspect` option is deprecated in favor of `--node-args`') + nodeArguments.push('--inspect') +} + +if (nodeArguments.length > 0) { + console.log(`Passing arguments to Node.js: "${nodeArguments.join(' ')}"`) +} + +const command = foundCommand || defaultCommand +const forwardedArgs = args._.filter(arg => arg !== command) + +// Make sure the `next --help` case is covered +if (args['--help']) { + forwardedArgs.push('--help') +} + +const defaultEnv = command === 'dev' ? 'development' : 'production' +process.env.NODE_ENV = process.env.NODE_ENV || defaultEnv + +const bin = join(__dirname, 'next-' + command) + +const startProcess = () => { + const proc = spawn('node', [...nodeArguments, bin, ...forwardedArgs], { stdio: 'inherit' }) + proc.on('close', (code, signal) => { + if (code !== null) { + process.exit(code) + } + if (signal) { + if (signal === 'SIGKILL') { + process.exit(137) + } + console.log(`got signal ${signal}, exiting`) + process.exit(signal === 'SIGINT' ? 0 : 1) + } + process.exit(0) + }) + proc.on('error', (err) => { + console.error(err) + process.exit(1) + }) + return proc +} + +let proc = startProcess() + +function wrapper () { + if (proc) { + proc.kill() + } +} + +process.on('SIGINT', wrapper) +process.on('SIGTERM', wrapper) +process.on('exit', wrapper) + +if (command === 'dev') { + const {CONFIG_FILE} = require('next-server/constants') + const {watchFile} = require('fs') + watchFile(`${process.cwd()}/${CONFIG_FILE}`, (cur: any, prev: any) => { + if (cur.size > 0 || prev.size > 0) { + console.log(`\n> Found a change in ${CONFIG_FILE}, restarting the server...`) + // Don't listen to 'close' now since otherwise parent gets killed by listener + proc.removeAllListeners('close') + proc.kill() + proc = startProcess() + } + }) +} diff --git a/packages/next/package.json b/packages/next/package.json index f35be4bc..4389d221 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -49,6 +49,7 @@ "@babel/runtime-corejs2": "7.1.2", "@babel/template": "7.1.2", "ansi-html": "0.0.7", + "arg": "3.0.0", "async-sema": "^2.1.4", "autodll-webpack-plugin": "0.4.2", "babel-core": "7.0.0-bridge.0", @@ -68,7 +69,6 @@ "http-status": "1.0.1", "launch-editor": "2.2.1", "loader-utils": "1.1.0", - "minimist": "1.2.0", "mkdirp-then": "1.2.0", "nanoid": "1.2.1", "next-server": "^7.0.2-canary.43", @@ -103,6 +103,7 @@ "@types/babel__generator": "7.0.1", "@types/babel__template": "7.0.1", "@types/babel__traverse": "7.0.3", + "@types/cross-spawn": "6.0.0", "@types/etag": "1.8.0", "@types/fresh": "0.5.0", "@types/nanoid": "1.2.0", diff --git a/packages/next/taskfile-typescript.js b/packages/next/taskfile-typescript.js index d5c02da1..185c06cb 100644 --- a/packages/next/taskfile-typescript.js +++ b/packages/next/taskfile-typescript.js @@ -6,11 +6,13 @@ try { module.exports = function (task) { task.plugin('typescript', { every: true }, function * (file, options) { + const {stripExtension} = options const opts = { fileName: file.base, compilerOptions: { ...config.compilerOptions, - ...options + ...options, + stripExtension: undefined // since it's an option of the typescript taskr plugin } } @@ -19,7 +21,7 @@ try { if (ext) { // Replace `.ts` with `.js` const extRegex = new RegExp(ext.replace('.', '\\.') + '$', 'i') - file.base = file.base.replace(extRegex, '.js') + file.base = file.base.replace(extRegex, stripExtension ? '' : '.js') } // compile output @@ -35,7 +37,7 @@ try { } // update file's data - file.data = Buffer.from(result.outputText, 'utf8') + file.data = Buffer.from(result.outputText.replace(/process\.env\.NEXT_VERSION/, `"${require('./package.json').version}"`), 'utf8') }) } } catch (err) { diff --git a/packages/next/taskfile.js b/packages/next/taskfile.js index 9b7af2d5..da6fef5a 100644 --- a/packages/next/taskfile.js +++ b/packages/next/taskfile.js @@ -5,7 +5,7 @@ export async function compile (task) { } export async function bin (task, opts) { - await task.source(opts.src || 'bin/*').typescript({module: 'commonjs'}).target('dist/bin', {mode: '0755'}) + await task.source(opts.src || 'bin/*').typescript({module: 'commonjs', stripExtension: true}).target('dist/bin', {mode: '0755'}) notify('Compiled binaries') } diff --git a/packages/next/types/index.d.ts b/packages/next/types/index.d.ts index c938e9cf..1e2bda41 100644 --- a/packages/next/types/index.d.ts +++ b/packages/next/types/index.d.ts @@ -1,3 +1,26 @@ declare module '@babel/plugin-transform-modules-commonjs'; declare module 'next-server/next-config'; declare module 'next-server/constants'; +declare module 'arg' { + function arg(spec: T, options?: {argv?: string[], permissive?: boolean}): arg.Result; + + namespace arg { + export type Handler = (value: string) => any; + + export interface Spec { + [key: string]: string | Handler | [Handler]; + } + + export type Result = { _: string[] } & { + [K in keyof T]: T[K] extends string + ? never + : T[K] extends Handler + ? ReturnType + : T[K] extends [Handler] + ? Array> + : never + }; + } + + export = arg; +} \ No newline at end of file diff --git a/test/integration/cli/pages/index.js b/test/integration/cli/pages/index.js new file mode 100644 index 00000000..f40492ac --- /dev/null +++ b/test/integration/cli/pages/index.js @@ -0,0 +1 @@ +export default () => 'test' diff --git a/test/integration/cli/test/index.test.js b/test/integration/cli/test/index.test.js new file mode 100644 index 00000000..20c8c6bb --- /dev/null +++ b/test/integration/cli/test/index.test.js @@ -0,0 +1,151 @@ +/* eslint-env jest */ +/* global jasmine */ +import { + runNextCommand, + runNextCommandDev, + findPort +} from 'next-test-utils' +import {join} from 'path' +import pkg from 'next/package' +jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 5 + +const dir = join(__dirname, '..') + +describe('CLI Usage', () => { + describe('no command', () => { + test('--help', async () => { + const help = await runNextCommand(['--help'], { + stdout: true + }) + expect(help.stdout).toMatch(/Usage/) + }) + + test('-h', async () => { + const help = await runNextCommand(['-h'], { + stdout: true + }) + expect(help.stdout).toMatch(/Usage/) + }) + + test('--version', async () => { + const output = await runNextCommand(['--version'], { + stdout: true + }) + expect(output.stdout).toMatch(new RegExp(`Next\\.js v${pkg.version.replace(/\./g, '\\.')}`)) + }) + + test('-v', async () => { + const output = await runNextCommand(['--version'], { + stdout: true + }) + expect(output.stdout).toMatch(new RegExp(`Next\\.js v${pkg.version.replace(/\./g, '\\.')}`)) + }) + + test('--inspect', async () => { + const output = await runNextCommand(['build', dir, '--inspect'], { + stdout: true + }) + expect(output.stdout).toMatch(/The `--inspect` option is deprecated/) + }) + + test('--node-args', async () => { + const output = await runNextCommand(['build', dir, '--node-args=--inspect'], { + stdout: true + }) + expect(output.stdout).toMatch(/Passing arguments to Node\.js: "--inspect"/) + }) + }) + describe('build', () => { + test('--help', async () => { + const help = await runNextCommand(['build', '--help'], { + stdout: true + }) + expect(help.stdout).toMatch(/Compiles the application for production deployment/) + }) + + test('-h', async () => { + const help = await runNextCommand(['build', '-h'], { + stdout: true + }) + expect(help.stdout).toMatch(/Compiles the application for production deployment/) + }) + }) + + describe('dev', () => { + test('--help', async () => { + const help = await runNextCommand(['dev', '--help'], { + stdout: true + }) + expect(help.stdout).toMatch(/Starts the application in development mode/) + }) + + test('-h', async () => { + const help = await runNextCommand(['dev', '-h'], { + stdout: true + }) + expect(help.stdout).toMatch(/Starts the application in development mode/) + }) + + test('custom directory', async () => { + const port = await findPort() + const output = await runNextCommandDev([dir, '--port', port], true) + expect(output).toMatch(/Ready on/) + }) + + test('--port', async () => { + const port = await findPort() + const output = await runNextCommandDev([dir, '--port', port], true) + expect(output).toMatch(new RegExp(`http://localhost:${port}`)) + }) + + test('-p', async () => { + const port = await findPort() + const output = await runNextCommandDev([dir, '-p', port], true) + expect(output).toMatch(new RegExp(`http://localhost:${port}`)) + }) + + test('--hostname', async () => { + const port = await findPort() + const output = await runNextCommandDev([dir, '--hostname', '0.0.0.0', '--port', port], true) + expect(output).toMatch(new RegExp(`http://0.0.0.0:${port}`)) + }) + + test('-H', async () => { + const port = await findPort() + const output = await runNextCommandDev([dir, '-H', '0.0.0.0', '--port', port], true) + expect(output).toMatch(new RegExp(`http://0.0.0.0:${port}`)) + }) + }) + + describe('start', () => { + test('--help', async () => { + const help = await runNextCommand(['start', '--help'], { + stdout: true + }) + expect(help.stdout).toMatch(/Starts the application in production mode/) + }) + + test('-h', async () => { + const help = await runNextCommand(['start', '-h'], { + stdout: true + }) + expect(help.stdout).toMatch(/Starts the application in production mode/) + }) + }) + + describe('export', () => { + test('--help', async () => { + const help = await runNextCommand(['export', '--help'], { + stdout: true + }) + expect(help.stdout).toMatch(/Exports the application/) + }) + + test('-h', async () => { + const help = await runNextCommand(['export', '-h'], { + stdout: true + }) + expect(help.stdout).toMatch(/Exports the application/) + }) + }) +}) diff --git a/test/lib/next-test-utils.js b/test/lib/next-test-utils.js index a30f5395..ce0fff4a 100644 --- a/test/lib/next-test-utils.js +++ b/test/lib/next-test-utils.js @@ -4,7 +4,7 @@ import http from 'http' import express from 'express' import path from 'path' import getPort from 'get-port' -import { spawn } from 'child_process' +import spawn from 'cross-spawn' import { readFileSync, writeFileSync, existsSync, unlinkSync } from 'fs' import fkill from 'fkill' @@ -12,13 +12,9 @@ import fkill from 'fkill' // This is done so that requiring from `next` works. // The reason we don't import the relative path `../../dist/` is that it would lead to inconsistent module singletons import server from 'next/dist/server/next' -import build from 'next/dist/build' -import _export from 'next/dist/export' import _pkg from 'next/package.json' export const nextServer = server -export const nextBuild = build -export const nextExport = _export export const pkg = _pkg export function initNextServerScript (scriptPath, successRegexp, env) { @@ -69,16 +65,40 @@ export function findPort () { return getPort() } -// Launch the app in dev mode. -export function launchApp (dir, port) { +export function runNextCommand (argv, options = {}) { const cwd = path.dirname(require.resolve('next/package')) return new Promise((resolve, reject) => { - const instance = spawn('node', ['dist/bin/next', dir, '-p', port], { cwd }) + console.log(`Running command "next ${argv.join(' ')}"`) + const instance = spawn('node', ['dist/bin/next', ...argv], { cwd, stdio: options.stdout ? ['ignore', 'pipe', 'ignore'] : 'inherit' }) + + let stdoutOutput = '' + if (options.stdout) { + instance.stdout.on('data', function (chunk) { + stdoutOutput += chunk + }) + } + + instance.on('close', () => { + resolve({ + stdout: stdoutOutput + }) + }) + + instance.on('error', (err) => { + reject(err) + }) + }) +} + +export function runNextCommandDev (argv, stdOut) { + const cwd = path.dirname(require.resolve('next/package')) + return new Promise((resolve, reject) => { + const instance = spawn('node', ['dist/bin/next', ...argv], { cwd }) function handleStdout (data) { const message = data.toString() if (/> Ready on/.test(message)) { - resolve(instance) + resolve(stdOut ? message : instance) } process.stdout.write(message) } @@ -101,6 +121,19 @@ export function launchApp (dir, port) { }) } +// Launch the app in dev mode. +export function launchApp (dir, port) { + return runNextCommandDev([dir, '-p', port]) +} + +export function nextBuild (dir) { + return runNextCommand(['build', dir]) +} + +export function nextExport (dir, {outdir}) { + return runNextCommand(['export', dir, '--outdir', outdir]) +} + // Kill a launched app export async function killApp (instance) { await fkill(instance.pid) diff --git a/yarn.lock b/yarn.lock index dabcc208..20c5a319 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1378,6 +1378,13 @@ dependencies: "@babel/types" "^7.0.0" +"@types/cross-spawn@6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@types/cross-spawn/-/cross-spawn-6.0.0.tgz#320aaf1d1a12979f1b84fe7a5590a7e860bf3a80" + integrity sha512-evp2ZGsFw9YKprDbg8ySgC9NA15g3YgiI8ANkGmKKvvi0P2aDGYLPxQIC5qfeKNUOe3TjABVGuah6omPRpIYhg== + dependencies: + "@types/node" "*" + "@types/etag@1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@types/etag/-/etag-1.8.0.tgz#37f0b1f3ea46da7ae319bbedb607e375b4c99f7e" @@ -1900,6 +1907,11 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" +arg@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/arg/-/arg-3.0.0.tgz#386c20035dfbeb13e280ca8a1e6868aa17942def" + integrity sha512-C5Scb477yHhNck9AFzW5RwAzS2Eqn0HR+Fv0pmcZBXBT8g/g7OOuZTr0upVSSUGWZQH+XWdAKIw2OfC86EuggQ== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -7530,7 +7542,7 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: +minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=