diff --git a/CHANGES.md b/CHANGES.md index 51e78a43..c533259b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,8 +1,17 @@ +**Added:** + +- Command-line arguments can now be used to configure settings for `nwb new`. + **Fixed:** - Demo apps weren't generating sourcemaps when bundling. - Use a non-zero exit code when displaying usage or otherwise exiting due to missing arguments [[#23](https://github.com/insin/nwb/issues/23)] +**Changed:** + +- The ES6 modules build for npm modules is now optional, controlled by a `jsNext` setting in `nwb.config.js`, defaulting to `true`. + - nwb 0.6 will default `jsNext` to `true` and log a warning when it's missing from a config file - this behaviour will be removed in nwb 0.7. + # 0.5.0 / 2015-12-15 **Added:** diff --git a/README.md b/README.md index 55c8e692..da1410d8 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,9 @@ Create a new React component module and start hot reloading its demo app: ``` $ nwb new react-component react-thing -? Do you want nwb to create a UMD build for this module? Yes +? Do you want to create a UMD build for npm? Yes ? Which global variable should the UMD build export? ReactThing +? Do you want to create an ES6 modules build for npm? Yes nwb: created /path/to/react-thing nwb: installing dependencies ... @@ -64,7 +65,7 @@ nwb: dev server listening at http://localhost:3000 ... ``` -Create a new web module and run tests on every change as you develop it: +Create a new web module without being asked any questions and run tests on every change as you develop it: ``` $ nwb new web-module get-form-data -f @@ -100,7 +101,10 @@ Project creation commands: new react-app create a React app new react-component create a React component with a demo app new web-module create a web module - -f force creation, don't ask any questions + -f, --force force creation, no questions + -g, --global global variable for npm UMD build + --no-jsnext disable npm ES6 modules build + --no-umd disable npm UMD module build Development commands: build clean and build diff --git a/docs/Commands.md b/docs/Commands.md index 9c90bafd..4f2b737d 100644 --- a/docs/Commands.md +++ b/docs/Commands.md @@ -10,23 +10,31 @@ Creates a skeleton for a React app with the given name. ``` nwb new react-component -? Do you want nwb to create a UMD build for this module? +? Do you want to create a UMD build for npm? ? Which global variable should the UMD build export? +? Do you want to create an ES6 modules build for npm? ``` Creates a skeleton for a React component with the given name, with an optional UMD build exporting a specified global variable. ``` nwb new web-module -? Do you want nwb to create a UMD build for this module? +? Do you want to create a UMD build for npm? ? Which global variable should the UMD build export? +? Do you want to create an ES6 modules build for npm? ``` Creates a skeleton for a web module with the given name, with an optional UMD build exporting a specified global variable. **Flags:** -* `-f` - force creation of the new project without asking any questions, using whichever default settings are necessary as a result. +* `-f, --force` - force creation of the new project without asking any questions, using whichever default settings are necessary as a result. + +**React component and web module flags:** + +* `-g, --global` - provide a global variable to be exported by the UMD build, implicitly enabling the UMD build. +* `--no-jsnext` - disable the npm ES6 modules build. +* `--no-umd` - disable the npm UMD build. ### `serve` - serve a React app diff --git a/docs/Configuration.md b/docs/Configuration.md index e8c87499..05274a48 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -52,11 +52,17 @@ module.exports = { } ``` +#### jsNext: `Boolean` + +Determines whether or not nwb will create an ES6 modules build for tree-shaking module bundlers when you run `nwb build` for a React component or web module. + +Defaults to `true`. + #### `umd`: `Boolean` Determines whether or not nwb will create a UMD build when you run `nwb build` for a React component or web module. -Defaults to `true` when you are prompted to onfigure this by `nwb new`. +Defaults to `true` when you are prompted to onfigure this by `nwb new`, or if you provide a UMD global variable as a command-line argument. #### `externals`: `Object` (only for UMD builds) diff --git a/src/cli.js b/src/cli.js index 70fac313..477ab4d7 100644 --- a/src/cli.js +++ b/src/cli.js @@ -28,7 +28,10 @@ export default function(argv, cb) { new react-app create a React app new react-component create a React component with a demo app new web-module create a web module - -f force creation, don't ask any questions + -f, --force force creation, no questions + -g, --global global variable for npm UMD build + --no-jsnext disable npm ES6 modules build + --no-umd disable npm UMD module build Development commands: build clean and build @@ -66,5 +69,8 @@ export default function(argv, cb) { } let commandModule = require(commandModulePath) + if (commandModule.default) { + commandModule = commandModule.default + } commandModule(args, cb) } diff --git a/src/commands/build-module.js b/src/commands/build-module.js index 803c04d1..33e5bff9 100644 --- a/src/commands/build-module.js +++ b/src/commands/build-module.js @@ -31,10 +31,12 @@ export default function(args) { console.log('nwb: build-module (es5)') exec('babel', babelArgs, {cwd}) - babelArgs[2] = es6 - babelArgs = [...babelArgs, '--blacklist=es6.modules'] - console.log('nwb: build-module (es6)') - exec('babel', babelArgs, {cwd}) + if (userConfig.jsNext) { + babelArgs[2] = es6 + babelArgs = [...babelArgs, '--blacklist=es6.modules'] + console.log('nwb: build-module (es6)') + exec('babel', babelArgs, {cwd}) + } temp.cleanupSync() } diff --git a/src/commands/new.js b/src/commands/new.js index 82f4f17b..62539f47 100644 --- a/src/commands/new.js +++ b/src/commands/new.js @@ -14,23 +14,47 @@ import pkg from '../../package.json' let nwbVersion = `~${pkg.version}` -function getWebModulePrefs(args, done) { - if (args.f) { - return done({umd: false, globalVariable: ''}) +export function getWebModulePrefs(args, done) { + // Determine defaults based on arguments + let umd = true + if (args.umd === false) { + umd = false } + else if (args.g || args.global) { + umd = true + } + else if (args.f || args.force) { + umd = false + } + let globalVariable = args.g || args.global || '' + let jsNext = true + if (args.jsnext === false) { + jsNext = false + } + + if (args.f || args.force) { + return done({umd, globalVariable, jsNext}) + } + inquirer.prompt([ { type: 'confirm', name: 'umd', - message: 'Do you want nwb to create a UMD build for this module?', - default: true + message: 'Do you want to create a UMD build for npm?', + default: umd }, { when: ({umd}) => umd, type: 'input', name: 'globalVariable', message: 'Which global variable should the UMD build export?', - default: '' + default: globalVariable + }, + { + type: 'confirm', + name: 'jsNext', + message: 'Do you want to create an ES6 modules build for npm?', + default: jsNext } ], done) } @@ -44,6 +68,11 @@ function installReact(targetDir) { }) } +export function npmModuleVars(vars) { + vars.jsNextMain = vars.jsNext ? '\n "jsnext:main": "es6/index.js",' : '' + return vars +} + let projectCreators = { [REACT_APP](args, name, targetDir, cb) { let templateDir = path.join(__dirname, `../../templates/${REACT_APP}`) @@ -58,9 +87,11 @@ let projectCreators = { }, [REACT_COMPONENT](args, name, targetDir, cb) { - getWebModulePrefs(args, ({umd, globalVariable}) => { + getWebModulePrefs(args, ({umd, globalVariable, jsNext}) => { let templateDir = path.join(__dirname, `../../templates/${REACT_COMPONENT}`) - let templateVars = {umd, globalVariable, name, nwbVersion, reactVersion} + let templateVars = npmModuleVars( + {umd, globalVariable, jsNext, name, nwbVersion, reactVersion} + ) copyTemplateDir(templateDir, targetDir, templateVars, err => { if (err) return cb(err) console.log(`nwb: created ${targetDir}`) @@ -72,9 +103,11 @@ let projectCreators = { }, [WEB_MODULE](args, name, targetDir, cb) { - getWebModulePrefs(args, ({umd, globalVariable}) => { + getWebModulePrefs(args, ({umd, globalVariable, jsNext}) => { let templateDir = path.join(__dirname, `../../templates/${WEB_MODULE}`) - let templateVars = {umd, globalVariable, name, nwbVersion} + let templateVars = npmModuleVars( + {umd, globalVariable, jsNext, name, nwbVersion} + ) copyTemplateDir(templateDir, targetDir, templateVars, err => { if (err) return cb(err) console.log(`nwb: created ${targetDir}`) diff --git a/src/getUserConfig.js b/src/getUserConfig.js index e68f4153..f20cd36d 100644 --- a/src/getUserConfig.js +++ b/src/getUserConfig.js @@ -2,10 +2,13 @@ import path from 'path' import glob from 'glob' -import {PROJECT_TYPES} from './constants' +import {PROJECT_TYPES, REACT_COMPONENT, WEB_MODULE} from './constants' import debug from './debug' import {UserError} from './errors' +// TODO Remove in 0.7 +let warnedJSNext = false + export default function getUserConfig(args = {}) { // Try to load default user config, or user a config file path we were given let userConfig = {} @@ -50,6 +53,19 @@ export default function getUserConfig(args = {}) { } } + // TODO Remove in 0.7 + if ((userConfig.type === REACT_COMPONENT || userConfig.type === WEB_MODULE) && + !('jsNext' in userConfig)) { + if (!warnedJSNext) { + console.warn([ + 'nwb: there was no jsNext setting in your nwb config file - this will default to true in nwb 0.6', + `nwb: set jsNext: true in ${path.basename(userConfigPath)} if you want to keep using the ES6 modules build` + ].join('\n')) + warnedJSNext = true + } + userConfig.jsNext = true + } + debug('final user config: %o', userConfig) return userConfig diff --git a/templates/react-component/_package.json b/templates/react-component/_package.json index ce909864..06366a7e 100644 --- a/templates/react-component/_package.json +++ b/templates/react-component/_package.json @@ -2,8 +2,7 @@ "name": "{{name}}", "version": "1.0.0", "description": "{{name}} React component", - "main": "lib/index.js", - "jsnext:main": "es6/index.js", + "main": "lib/index.js",{{jsNextMain}} "files": [ "css", "es6", diff --git a/templates/react-component/nwb.config.js b/templates/react-component/nwb.config.js index 03403b6e..98111c59 100644 --- a/templates/react-component/nwb.config.js +++ b/templates/react-component/nwb.config.js @@ -12,5 +12,10 @@ module.exports = { // build. externals: { 'react': 'React' - } + }, + + // Should nwb create a build with untranspiled ES6 modules for tree-shaking + // module bundlers? If you change your mind later, add or remove this line in + // package.json: "jsnext:main": "es6/index.js" + jsNext: {{jsNext}} } diff --git a/templates/web-module/_package.json b/templates/web-module/_package.json index 1232174f..18dd238d 100644 --- a/templates/web-module/_package.json +++ b/templates/web-module/_package.json @@ -2,8 +2,7 @@ "name": "{{name}}", "version": "1.0.0", "description": "Describe {{name}} here", - "main": "lib/index.js", - "jsnext:main": "es6/index.js", + "main": "lib/index.js",{{jsNextMain}} "files": [ "es6", "lib", diff --git a/templates/web-module/nwb.config.js b/templates/web-module/nwb.config.js index dcb4a20b..d7a91d5c 100644 --- a/templates/web-module/nwb.config.js +++ b/templates/web-module/nwb.config.js @@ -9,5 +9,10 @@ module.exports = { // A mapping from the npm package names of this module's peerDependencies - if // it has any - to the global variables they're expected to be available as // for use by the UMD build, e.g. {'react': 'React'} - externals: {} + externals: {}, + + // Should nwb create a build with untranspiled ES6 modules for tree-shaking + // module bundlers? If you change your mind later, add or remove this line in + // package.json: "jsnext:main": "es6/index.js" + jsNext: {{jsNext}} } diff --git a/tests/commands-new-test.js b/tests/commands-new-test.js new file mode 100644 index 00000000..3de505ab --- /dev/null +++ b/tests/commands-new-test.js @@ -0,0 +1,62 @@ +import expect from 'expect' +import parseArgs from 'minimist' + +let {getWebModulePrefs} = require('../src/commands/new') + +let moduleArgs = (args, cb) => { + args.push('-f') + getWebModulePrefs(parseArgs(args), cb) +} + +describe('nwb new', () => { + describe('command-line arguments', () => { + it('set umd=false by default', done => { + moduleArgs([], settings => { + expect(settings).toEqual({ + globalVariable: '', + jsNext: true, + umd: false + }) + done() + }) + }) + it('implicitly set umd=true with a --global variable', done => { + moduleArgs(['--global=Test'], settings => { + expect(settings).toEqual({ + globalVariable: 'Test', + jsNext: true, + umd: true + }) + done() + }) + }) + it('implicitly set umd=true with a -g variable', done => { + moduleArgs(['-g', 'Test'], settings => { + expect(settings).toEqual({ + globalVariable: 'Test', + jsNext: true, + umd: true + }) + done() + }) + }) + it('set umd=false with --no-umd', done => { + moduleArgs(['--no-umd'], settings => { + expect(settings.umd).toBe(false) + done() + }) + }) + it('set umd=false with --umd=false', done => { + moduleArgs(['--no-umd'], settings => { + expect(settings.umd).toBe(false) + done() + }) + }) + it('set jsNext=false with --no-jsnext', done => { + moduleArgs(['--no-jsnext'], settings => { + expect(settings.jsNext).toBe(false) + done() + }) + }) + }) +}) diff --git a/tests/commands/new-test.js b/tests/commands/new-test.js index ac678d08..fb4d1ded 100644 --- a/tests/commands/new-test.js +++ b/tests/commands/new-test.js @@ -6,10 +6,11 @@ import glob from 'glob' import rimraf from 'rimraf' import temp from 'temp' -describe('command: nwb new web-module', function() { +let cli = require('../../src/cli') + +describe('command: nwb new', function() { this.timeout(40000) - let cli = require('../../src/cli') let originalCwd let tmpDir @@ -64,83 +65,109 @@ describe('command: nwb new web-module', function() { }) }) - it('creates a new web module with a given name', done => { - cli(['new', 'web-module', 'test-module', '-f'], err => { - expect(err).toNotExist('No errors creating new web module') - expect(glob.sync('**', {dot: true})).toEqual([ - 'test-module', - 'test-module/.gitignore', - 'test-module/.travis.yml', - 'test-module/nwb.config.js', - 'test-module/package.json', - 'test-module/README.md', - 'test-module/src', - 'test-module/src/index.js', - 'test-module/tests', - 'test-module/tests/.eslintrc', - 'test-module/tests/index-test.js' - ]) - let pkg = require(path.resolve('test-module/package.json')) - expect(pkg.name).toBe('test-module') - done() + describe('web modules and React components', () => { + it('creates a new web module with a given name', done => { + cli(['new', 'web-module', 'test-module', '-f'], err => { + expect(err).toNotExist('No errors creating new web module') + expect(glob.sync('**', {dot: true})).toEqual([ + 'test-module', + 'test-module/.gitignore', + 'test-module/.travis.yml', + 'test-module/nwb.config.js', + 'test-module/package.json', + 'test-module/README.md', + 'test-module/src', + 'test-module/src/index.js', + 'test-module/tests', + 'test-module/tests/.eslintrc', + 'test-module/tests/index-test.js' + ]) + let pkg = require(path.resolve('test-module/package.json')) + expect(pkg.name).toBe('test-module') + expect(pkg['jsnext:main']).toBe('es6/index.js') + let config = require(path.resolve('test-module/nwb.config.js')) + expect(config).toEqual({ + type: 'web-module', + umd: false, + global: '', + externals: {}, + jsNext: true + }) + done() + }) }) - }) - it('creates a new React component with a given name', done => { - cli(['new', 'react-component', 'test-component', '-f'], err => { - expect(err).toNotExist('No errors creating new React component') - expect(glob.sync('**', {dot: true, 'ignore': 'test-component/node_modules/**'})).toEqual([ - 'test-component', - 'test-component/.gitignore', - 'test-component/.travis.yml', - 'test-component/demo', - 'test-component/demo/src', - 'test-component/demo/src/index.js', - 'test-component/nwb.config.js', - 'test-component/package.json', - 'test-component/README.md', - 'test-component/src', - 'test-component/src/index.js', - 'test-component/tests', - 'test-component/tests/.eslintrc', - 'test-component/tests/index-test.js' - ]) - expect(glob.sync('test-component/node_modules/*')).toEqual([ - 'test-component/node_modules/react', - 'test-component/node_modules/react-dom' - ]) - let pkg = require(path.resolve('test-component/package.json')) - expect(pkg.name).toBe('test-component') - done() + it('creates a new React component with a given name', done => { + cli(['new', 'react-component', 'test-component', '-f'], err => { + expect(err).toNotExist('No errors creating new React component') + expect(glob.sync('**', {dot: true, 'ignore': 'test-component/node_modules/**'})).toEqual([ + 'test-component', + 'test-component/.gitignore', + 'test-component/.travis.yml', + 'test-component/demo', + 'test-component/demo/src', + 'test-component/demo/src/index.js', + 'test-component/nwb.config.js', + 'test-component/package.json', + 'test-component/README.md', + 'test-component/src', + 'test-component/src/index.js', + 'test-component/tests', + 'test-component/tests/.eslintrc', + 'test-component/tests/index-test.js' + ]) + expect(glob.sync('test-component/node_modules/*')).toEqual([ + 'test-component/node_modules/react', + 'test-component/node_modules/react-dom' + ]) + let pkg = require(path.resolve('test-component/package.json')) + expect(pkg.name).toBe('test-component') + expect(pkg['jsnext:main']).toBe('es6/index.js') + let config = require(path.resolve('test-component/nwb.config.js')) + expect(config).toEqual({ + type: 'react-component', + umd: false, + global: '', + externals: {react: 'React'}, + jsNext: true + }) + done() + }) }) }) - it('creates a new react app with a given name', done => { - cli(['new', 'react-app', 'test-app', '-f'], err => { - expect(err).toNotExist('No errors creating new React component') - expect(glob.sync('**', {dot: true, 'ignore': 'test-app/node_modules/**'})).toEqual([ - 'test-app', - 'test-app/.gitignore', - 'test-app/.travis.yml', - 'test-app/nwb.config.js', - 'test-app/package.json', - 'test-app/public', - 'test-app/public/index.html', - 'test-app/README.md', - 'test-app/src', - 'test-app/src/App.js', - 'test-app/src/index.js', - 'test-app/tests', - 'test-app/tests/.eslintrc', - 'test-app/tests/App-test.js' - ]) - expect(glob.sync('test-app/node_modules/*')).toEqual([ - 'test-app/node_modules/react', - 'test-app/node_modules/react-dom' - ]) - let pkg = require(path.resolve('test-app/package.json')) - expect(pkg.name).toBe('test-app') - done() + describe('react apps', () => { + it('creates a new react app with a given name', done => { + cli(['new', 'react-app', 'test-app', '-f'], err => { + expect(err).toNotExist('No errors creating new React component') + expect(glob.sync('**', {dot: true, 'ignore': 'test-app/node_modules/**'})).toEqual([ + 'test-app', + 'test-app/.gitignore', + 'test-app/.travis.yml', + 'test-app/nwb.config.js', + 'test-app/package.json', + 'test-app/public', + 'test-app/public/index.html', + 'test-app/README.md', + 'test-app/src', + 'test-app/src/App.js', + 'test-app/src/index.js', + 'test-app/tests', + 'test-app/tests/.eslintrc', + 'test-app/tests/App-test.js' + ]) + expect(glob.sync('test-app/node_modules/*')).toEqual([ + 'test-app/node_modules/react', + 'test-app/node_modules/react-dom' + ]) + let pkg = require(path.resolve('test-app/package.json')) + expect(pkg.name).toBe('test-app') + let config = require(path.resolve('test-app/nwb.config.js')) + expect(config).toEqual({ + type: 'react-app' + }) + done() + }) }) }) }) diff --git a/tests/fixtures/minimal-module-config.js b/tests/fixtures/minimal-module-config.js new file mode 100644 index 00000000..929f7eb8 --- /dev/null +++ b/tests/fixtures/minimal-module-config.js @@ -0,0 +1,3 @@ +module.exports = { + type: 'web-module' +} diff --git a/tests/getUserConfig-test.js b/tests/getUserConfig-test.js index c3c6c951..77802f66 100644 --- a/tests/getUserConfig-test.js +++ b/tests/getUserConfig-test.js @@ -27,6 +27,14 @@ describe('getUserConfig()', () => { }) }) + // TODO Remove in 0.7 + describe('when jsNext config is missing', () => { + it('defaults to true (in nwb 0.6)', () => { + let config = getUserConfig({config: 'tests/fixtures/minimal-module-config.js'}) + expect(config.jsNext).toBe(true) + }) + }) + describe('when babel config is provided', () => { it('creates a babel-loader config if there was none', () => { let config = getUserConfig({config: 'tests/fixtures/babel-only-config.js'})