diff --git a/docs/DevServer.html b/docs/DevServer.html index c2a1940..d447060 100644 --- a/docs/DevServer.html +++ b/docs/DevServer.html @@ -96,7 +96,7 @@


-

new DevServer(script, style, devDir [, port] [, react])

+

new DevServer(script [, options])

@@ -183,125 +183,13 @@

Parameters:
- style + options -string - - - - - - - - - - - - - - - - - - - - - - -

a full system path to a SASS file

- - - - - - - devDir - - - - - -string - - - - - - - - - - - - - - - - - - - - - - -

a full system path to a directory in which to put any compiled development resources

- - - - - - - port - - - - - -number - - - - - - - - - - <optional>
- - - - - - - - - - - - 3000 - - - - -

a port at which to start the dev server

- - - - - - - react - - - - - -boolean +DevServerConfig @@ -323,12 +211,12 @@
Parameters:
- true + {} -

false to disable the react hot loader plugin

+

optional configuration

@@ -373,7 +261,7 @@
Parameters:
@@ -417,11 +305,11 @@
Example
// or - var DevServer = require('webcompiler/lib/DevServer').DevServer; import {join} from 'path'; -const rootDir = join(__dirname, '..'), - devDir = join(rootDir, 'development'), - server = new DevServer(join(devDir, 'script.js'), join(devDir, 'app.scss'), devDir); +const devDir = join(__dirname, '..', 'development'), + script = join(devDir, 'script.js'), + style = join(devDir, 'app.scss'); -server.run(rootDir); +new DevServer(script, {style}).run(); // now navigate to http://localhost:3000 using your favorite browser ( preferably Chrome :) ) @@ -505,7 +393,7 @@

run()

@@ -599,7 +487,7 @@

watchJS()

@@ -693,7 +581,7 @@

watchSASS()< diff --git a/docs/DevServer.js.html b/docs/DevServer.js.html index 0d0a10a..af26f02 100644 --- a/docs/DevServer.js.html +++ b/docs/DevServer.js.html @@ -84,6 +84,7 @@

Source: DevServer.js

/* @flow */
 
+import type {DevServerConfig} from './typedef';
 import {SASSCompiler} from './SASSCompiler';
 import {watch} from './watch';
 import tinylr from 'tiny-lr';
@@ -93,7 +94,13 @@ 

Source: DevServer.js

const LIVERELOAD_PORT = 35729, WEB_PORT = 3000, - cwd = process.cwd(); + cwd = process.cwd(), + {HotModuleReplacementPlugin, NoErrorsPlugin} = webpack, + defaultOptions = { + port: WEB_PORT, + react: true, + contentBase: cwd + }; /** * A lightweight development server that rapidly recompiles the JavaScript and SASS files when they are edited and @@ -106,11 +113,8 @@

Source: DevServer.js

* Please install and enable the LiveReload browser extension for the CSS reloading to work. * * @class DevServer - * @param {string} script - a full system path to a JavaScript file - * @param {string} style - a full system path to a SASS file - * @param {string} devDir - a full system path to a directory in which to put any compiled development resources - * @param {number} [port=3000] - a port at which to start the dev server - * @param {boolean} [react=true] - false to disable the react hot loader plugin + * @param {string} script - a full system path to a JavaScript file + * @param {DevServerConfig} [options={}] - optional configuration * @see {@link http://webpack.github.io/docs/webpack-dev-server.html webpack-dev-server} * @see {@link http://gaearon.github.io/react-hot-loader/ React Hot Loader} * @example @@ -120,151 +124,167 @@

Source: DevServer.js

* // or - var DevServer = require('webcompiler/lib/DevServer').DevServer; * import {join} from 'path'; * - * const rootDir = join(__dirname, '..'), - * devDir = join(rootDir, 'development'), - * server = new DevServer(join(devDir, 'script.js'), join(devDir, 'app.scss'), devDir); + * const devDir = join(__dirname, '..', 'development'), + * script = join(devDir, 'script.js'), + * style = join(devDir, 'app.scss'); * - * server.run(rootDir); + * new DevServer(script, {style}).run(); * // now navigate to http://localhost:3000 using your favorite browser ( preferably Chrome :) ) */ export class DevServer { /** - * a port at which to start the dev server + * a full system path to a JavaScript file * - * @member {number} port + * @member {string} script * @memberof DevServer * @private * @instance */ - port: number; + script: string; /** - * a LiveReload server + * optional configuration * - * @member {tinylr.Server} lr + * @member {Object} options * @memberof DevServer * @private * @instance */ - lr: tinylr.Server; + options: Object; + + /* eslint-disable require-jsdoc */ + constructor(script: string, options: DevServerConfig = {}) { + /* eslint-enable require-jsdoc */ + this.script = script; + this.options = {...defaultOptions, ...options}; + } /** - * recompiles SASS and notifies LiveReload + * Returns the HTML page layout * - * @method compileSASS * @memberof DevServer * @private * @instance + * @method layout + * @return {string} HTML layout */ - compileSASS: () => void; + layout(): string { + return `<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Development server - WebCompiler</title> + <link rel="shortcut icon" href="/favicon.ico">${ + this.options.style + ? '\n <link rel="stylesheet" href="/style.css">' + : '' + } + </head> + <body> + <div id="app"></div> + <script src="/script.js" async defer></script> + </body> +</html>`; + } /** - * the Webpack development server + * Returns the JavaScript module loaders array necessary to run the server * - * @member {WebpackDevServer} server * @memberof DevServer * @private * @instance + * @method loaders + * @return {Array<string>} JavaScript module loaders */ - server: WebpackDevServer; + loaders(): Array<string> { + const loaders = []; - /* eslint-disable require-jsdoc */ - constructor(script: string, style: string, devDir: string, port: number = WEB_PORT, react: boolean = true) { - /* eslint-enable require-jsdoc */ - const sass = new SASSCompiler(), - loaders = []; - - if (react) { + if (this.options.react) { loaders.push('react-hot'); } loaders.push('babel'); - this.port = port; - this.lr = tinylr(); - this.compileSASS = sass.fe.bind(sass, style, join(devDir, 'style.css'), () => { - this.lr.changed({body: {files: ['style.css']}}); - }); - this.server = new WebpackDevServer(webpack({ + return loaders; + } + + /** + * Compile SASS and start watching for file changes + * + * @memberof DevServer + * @instance + * @method watchSASS + * @example + * server.watchSASS(); + */ + watchSASS() { + const {style, contentBase} = this.options; + + if (!style) { + return; + } + + const sass = new SASSCompiler(false), + lr = tinylr(), + compileSASS = sass.fe.bind(sass, style, join(contentBase, 'style.css'), () => { + lr.changed({body: {files: ['style.css']}}); + }); + + lr.listen(LIVERELOAD_PORT); + compileSASS(); + watch(cwd, 'scss', compileSASS); + } + + /** + * Starts the Webpack development server + * + * @memberof DevServer + * @instance + * @method watchJS + * @example + * server.watchJS(); + */ + watchJS() { + const {port, contentBase} = this.options; + + const server = new WebpackDevServer(webpack({ cache: {}, debug: true, devtool: 'eval-source-map', entry: [ - `webpack-dev-server/client?http://localhost:${this.port}`, + `webpack-dev-server/client?http://localhost:${port}`, 'webpack/hot/only-dev-server', - script + this.script ], output: { - path: devDir, + path: contentBase, filename: 'script.js', publicPath: '/' }, - plugins: [ - new webpack.HotModuleReplacementPlugin(), - new webpack.NoErrorsPlugin() - ], + plugins: [new HotModuleReplacementPlugin(), new NoErrorsPlugin()], module: { loaders: [{ test: /\.js$/, exclude: /node_modules/, - loaders + loaders: this.loaders() }, { test: /\.json$/, loader: 'json' }] } }), { - contentBase: devDir, + contentBase, publicPath: '/', hot: true, historyApiFallback: true }); - this.server.app.get('*', (req, res) => { - res.send(`<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="utf-8"> - <meta http-equiv="X-UA-Compatible" content="IE=edge"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <title>Development server - WebCompiler</title> - <link href="/style.css" rel="stylesheet"> - </head> - <body> - <div id="app"></div> - <script src="/script.js" async defer></script> - </body> -</html>`); + server.app.use((req, res) => { + res.send(this.layout()); }); - } - - /** - * Compile SASS and start watching for file changes - * - * @memberof DevServer - * @instance - * @method watchSASS - * @example - * server.watchSASS(); - */ - watchSASS() { - this.lr.listen(LIVERELOAD_PORT); - this.compileSASS(); - watch(cwd, 'scss', this.compileSASS); - } - - /** - * Starts the Webpack development server - * - * @memberof DevServer - * @instance - * @method watchJS - * @example - * server.watchJS(); - */ - watchJS() { - const port = this.port; - this.server.listen(port, '0.0.0.0', error => { + server.listen(port, '0.0.0.0', error => { if (error) { return console.error(error); } diff --git a/docs/global.html b/docs/global.html index 62c6466..057a08b 100644 --- a/docs/global.html +++ b/docs/global.html @@ -548,6 +548,270 @@

Type Definitions

+
+
+

DevServerConfig

+ + +
+
+ +
+

DevServer configuration object

+
+ + + +
Type:
+
    +
  • + +Object + + + +
  • +
+ + + +
+ + +
Properties:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDefaultDescription
style + + +string + + + + + + + <optional>
+ + + +
+ +

a full system path to a SASS file

port + + +number + + + + + + + <optional>
+ + + +
+ + 3000 + +

a port at which to start the dev server

react + + +boolean + + + + + + + <optional>
+ + + +
+ + true + +

false to disable the react hot loader plugin

contentBase + + +string + + + + + + + <optional>
+ + + +
+ + CWD + +

a full system path to the server web root

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ +
+ + + + + + + +
+ + + +
+ + +

DocumentationConfig

@@ -835,7 +1099,7 @@
Properties:
@@ -969,7 +1233,7 @@
Parameters:
@@ -1411,7 +1675,7 @@
Parameters:
@@ -1589,7 +1853,7 @@
Parameters:
@@ -1881,7 +2145,7 @@
Parameters:
@@ -2015,7 +2279,7 @@
Parameters:
@@ -2233,7 +2497,7 @@
Properties:
@@ -2415,7 +2679,7 @@
Properties:
diff --git a/docs/quicksearch.html b/docs/quicksearch.html index 4041651..70b4fc2 100644 --- a/docs/quicksearch.html +++ b/docs/quicksearch.html @@ -7,7 +7,7 @@ \n \n'); - }); - } /** - * Compile SASS and start watching for file changes + * optional configuration * + * @member {Object} options * @memberof DevServer + * @private * @instance - * @method watchSASS - * @example - * server.watchSASS(); */ + DevServer.prototype.layout = function layout() { + return '\n\n \n \n \n \n Development server - WebCompiler\n ' + (this.options.style ? '\n ' : '') + '\n \n \n
\n \n \n'; + }; + /** - * the Webpack development server + * Returns the JavaScript module loaders array necessary to run the server * - * @member {WebpackDevServer} server * @memberof DevServer * @private * @instance + * @method loaders + * @return {Array} JavaScript module loaders */ + DevServer.prototype.loaders = function loaders() { + var loaders = []; + + if (this.options.react) { + loaders.push('react-hot'); + } + loaders.push('babel'); + + return loaders; + }; + /** - * a LiveReload server + * Compile SASS and start watching for file changes * - * @member {tinylr.Server} lr * @memberof DevServer - * @private * @instance + * @method watchSASS + * @example + * server.watchSASS(); */ DevServer.prototype.watchSASS = function watchSASS() { - this.lr.listen(LIVERELOAD_PORT); - this.compileSASS(); - (0, _watch.watch)(cwd, 'scss', this.compileSASS); + var _options = this.options; + var style = _options.style; + var contentBase = _options.contentBase; + + + if (!style) { + return; + } + + var sass = new _SASSCompiler.SASSCompiler(false), + lr = (0, _tinyLr2.default)(), + compileSASS = sass.fe.bind(sass, style, (0, _path.join)(contentBase, 'style.css'), function () { + lr.changed({ body: { files: ['style.css'] } }); + }); + + lr.listen(LIVERELOAD_PORT); + compileSASS(); + (0, _watch.watch)(cwd, 'scss', compileSASS); }; /** @@ -190,9 +185,46 @@ var DevServer = exports.DevServer = function () { DevServer.prototype.watchJS = function watchJS() { - var port = this.port; + var _this = this; + + var _options2 = this.options; + var port = _options2.port; + var contentBase = _options2.contentBase; + + + var server = new _webpackDevServer2.default((0, _webpack2.default)({ + cache: {}, + debug: true, + devtool: 'eval-source-map', + entry: ['webpack-dev-server/client?http://localhost:' + port, 'webpack/hot/only-dev-server', this.script], + output: { + path: contentBase, + filename: 'script.js', + publicPath: '/' + }, + plugins: [new HotModuleReplacementPlugin(), new NoErrorsPlugin()], + module: { + loaders: [{ + test: /\.js$/, + exclude: /node_modules/, + loaders: this.loaders() + }, { + test: /\.json$/, + loader: 'json' + }] + } + }), { + contentBase: contentBase, + publicPath: '/', + hot: true, + historyApiFallback: true + }); + + server.app.use(function (req, res) { + res.send(_this.layout()); + }); - this.server.listen(port, '0.0.0.0', function (error) { + server.listen(port, '0.0.0.0', function (error) { if (error) { return console.error(error); } diff --git a/package.json b/package.json index 550f6d4..9119c88 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "autoprefixer": "^6.3.7", "babel-cli": "^6.10.1", "babel-core": "^6.10.4", - "babel-eslint": "^6.1.1", + "babel-eslint": "^6.1.2", "babel-loader": "^6.2.4", "babel-plugin-transform-class-properties": "^6.10.2", "babel-plugin-transform-member-expression-literals": "^6.8.0", @@ -91,7 +91,7 @@ "node-sass": "^3.8.0", "node-sass-import-once": "^1.2.0", "null-loader": "^0.1.1", - "postcss": "^5.0.21", + "postcss": "^5.1.0", "react": "^15.2.1", "react-hot-loader": "^1.3.0", "remarkable": "^1.6.2", @@ -101,7 +101,7 @@ }, "devDependencies": { "chai": "^3.5.0", - "coveralls": "^2.11.9", + "coveralls": "^2.11.11", "isparta": "^4.0.0", "mocha": "^2.5.3", "mt-changelog": "^0.6.2", diff --git a/src/DevServer.js b/src/DevServer.js index 727cc26..994ea94 100644 --- a/src/DevServer.js +++ b/src/DevServer.js @@ -1,5 +1,6 @@ /* @flow */ +import type {DevServerConfig} from './typedef'; import {SASSCompiler} from './SASSCompiler'; import {watch} from './watch'; import tinylr from 'tiny-lr'; @@ -9,7 +10,13 @@ import {join} from 'path'; const LIVERELOAD_PORT = 35729, WEB_PORT = 3000, - cwd = process.cwd(); + cwd = process.cwd(), + {HotModuleReplacementPlugin, NoErrorsPlugin} = webpack, + defaultOptions = { + port: WEB_PORT, + react: true, + contentBase: cwd + }; /** * A lightweight development server that rapidly recompiles the JavaScript and SASS files when they are edited and @@ -22,11 +29,8 @@ const LIVERELOAD_PORT = 35729, * Please install and enable the LiveReload browser extension for the CSS reloading to work. * * @class DevServer - * @param {string} script - a full system path to a JavaScript file - * @param {string} style - a full system path to a SASS file - * @param {string} devDir - a full system path to a directory in which to put any compiled development resources - * @param {number} [port=3000] - a port at which to start the dev server - * @param {boolean} [react=true] - false to disable the react hot loader plugin + * @param {string} script - a full system path to a JavaScript file + * @param {DevServerConfig} [options={}] - optional configuration * @see {@link http://webpack.github.io/docs/webpack-dev-server.html webpack-dev-server} * @see {@link http://gaearon.github.io/react-hot-loader/ React Hot Loader} * @example @@ -36,151 +40,167 @@ const LIVERELOAD_PORT = 35729, * // or - var DevServer = require('webcompiler/lib/DevServer').DevServer; * import {join} from 'path'; * - * const rootDir = join(__dirname, '..'), - * devDir = join(rootDir, 'development'), - * server = new DevServer(join(devDir, 'script.js'), join(devDir, 'app.scss'), devDir); + * const devDir = join(__dirname, '..', 'development'), + * script = join(devDir, 'script.js'), + * style = join(devDir, 'app.scss'); * - * server.run(rootDir); + * new DevServer(script, {style}).run(); * // now navigate to http://localhost:3000 using your favorite browser ( preferably Chrome :) ) */ export class DevServer { /** - * a port at which to start the dev server + * a full system path to a JavaScript file * - * @member {number} port + * @member {string} script * @memberof DevServer * @private * @instance */ - port: number; + script: string; /** - * a LiveReload server + * optional configuration * - * @member {tinylr.Server} lr + * @member {Object} options * @memberof DevServer * @private * @instance */ - lr: tinylr.Server; + options: Object; + + /* eslint-disable require-jsdoc */ + constructor(script: string, options: DevServerConfig = {}) { + /* eslint-enable require-jsdoc */ + this.script = script; + this.options = {...defaultOptions, ...options}; + } /** - * recompiles SASS and notifies LiveReload + * Returns the HTML page layout * - * @method compileSASS * @memberof DevServer * @private * @instance + * @method layout + * @return {string} HTML layout */ - compileSASS: () => void; + layout(): string { + return ` + + + + + + Development server - WebCompiler + ${ + this.options.style + ? '\n ' + : '' + } + + +
+ + +`; + } /** - * the Webpack development server + * Returns the JavaScript module loaders array necessary to run the server * - * @member {WebpackDevServer} server * @memberof DevServer * @private * @instance + * @method loaders + * @return {Array} JavaScript module loaders */ - server: WebpackDevServer; + loaders(): Array { + const loaders = []; - /* eslint-disable require-jsdoc */ - constructor(script: string, style: string, devDir: string, port: number = WEB_PORT, react: boolean = true) { - /* eslint-enable require-jsdoc */ - const sass = new SASSCompiler(), - loaders = []; - - if (react) { + if (this.options.react) { loaders.push('react-hot'); } loaders.push('babel'); - this.port = port; - this.lr = tinylr(); - this.compileSASS = sass.fe.bind(sass, style, join(devDir, 'style.css'), () => { - this.lr.changed({body: {files: ['style.css']}}); - }); - this.server = new WebpackDevServer(webpack({ + return loaders; + } + + /** + * Compile SASS and start watching for file changes + * + * @memberof DevServer + * @instance + * @method watchSASS + * @example + * server.watchSASS(); + */ + watchSASS() { + const {style, contentBase} = this.options; + + if (!style) { + return; + } + + const sass = new SASSCompiler(false), + lr = tinylr(), + compileSASS = sass.fe.bind(sass, style, join(contentBase, 'style.css'), () => { + lr.changed({body: {files: ['style.css']}}); + }); + + lr.listen(LIVERELOAD_PORT); + compileSASS(); + watch(cwd, 'scss', compileSASS); + } + + /** + * Starts the Webpack development server + * + * @memberof DevServer + * @instance + * @method watchJS + * @example + * server.watchJS(); + */ + watchJS() { + const {port, contentBase} = this.options; + + const server = new WebpackDevServer(webpack({ cache: {}, debug: true, devtool: 'eval-source-map', entry: [ - `webpack-dev-server/client?http://localhost:${this.port}`, + `webpack-dev-server/client?http://localhost:${port}`, 'webpack/hot/only-dev-server', - script + this.script ], output: { - path: devDir, + path: contentBase, filename: 'script.js', publicPath: '/' }, - plugins: [ - new webpack.HotModuleReplacementPlugin(), - new webpack.NoErrorsPlugin() - ], + plugins: [new HotModuleReplacementPlugin(), new NoErrorsPlugin()], module: { loaders: [{ test: /\.js$/, exclude: /node_modules/, - loaders + loaders: this.loaders() }, { test: /\.json$/, loader: 'json' }] } }), { - contentBase: devDir, + contentBase, publicPath: '/', hot: true, historyApiFallback: true }); - this.server.app.get('*', (req, res) => { - res.send(` - - - - - - Development server - WebCompiler - - - -
- - -`); + server.app.use((req, res) => { + res.send(this.layout()); }); - } - - /** - * Compile SASS and start watching for file changes - * - * @memberof DevServer - * @instance - * @method watchSASS - * @example - * server.watchSASS(); - */ - watchSASS() { - this.lr.listen(LIVERELOAD_PORT); - this.compileSASS(); - watch(cwd, 'scss', this.compileSASS); - } - - /** - * Starts the Webpack development server - * - * @memberof DevServer - * @instance - * @method watchJS - * @example - * server.watchJS(); - */ - watchJS() { - const port = this.port; - this.server.listen(port, '0.0.0.0', error => { + server.listen(port, '0.0.0.0', error => { if (error) { return console.error(error); } diff --git a/src/typedef.js b/src/typedef.js index cced61b..dc774be 100644 --- a/src/typedef.js +++ b/src/typedef.js @@ -30,6 +30,22 @@ export type JSLintError = { column: number; }; +/** + * DevServer configuration object + * + * @typedef {Object} DevServerConfig + * @property {string} [style] - a full system path to a SASS file + * @property {number} [port=3000] - a port at which to start the dev server + * @property {boolean} [react=true] - false to disable the react hot loader plugin + * @property {string} [contentBase=CWD] - a full system path to the server web root + */ +export type DevServerConfig = { + style?: string; + port?: number; + react?: boolean; + contentBase?: string; +}; + /** * Documentation generator configuration object * diff --git a/test/DevServer.spec.js b/test/DevServer.spec.js index 6cf0f84..e6a7c6f 100644 --- a/test/DevServer.spec.js +++ b/test/DevServer.spec.js @@ -12,8 +12,7 @@ chai.use(sinonChai); /* eslint-disable no-unused-expressions */ /* eslint-disable require-jsdoc */ -const DEFAULT_WEB_PORT = 3000, - WEB_PORT = 8000, +const WEB_PORT = 3000, LIVERELOAD_PORT = 35729, cwd = process.cwd(); @@ -35,7 +34,7 @@ describe('DevServer', () => { webpack.NoErrorsPlugin = NoErrorsPlugin; stub(SASSCompiler.prototype, 'fe').callsArg(2); spy(SASSCompiler.prototype.fe, 'bind'); - spy(WebpackDevServer.prototype.app, 'get'); + spy(WebpackDevServer.prototype.app, 'use'); DevServer = req({'./watch': {watch}, 'tiny-lr': tinylr, 'webpack-dev-server': WebpackDevServer, webpack}); stub(console, 'log'); stub(console, 'error'); @@ -45,244 +44,289 @@ describe('DevServer', () => { /* @flowignore */ SASSCompiler.prototype.fe.bind.restore(); SASSCompiler.prototype.fe.restore(); - WebpackDevServer.prototype.app.get.restore(); + WebpackDevServer.prototype.app.use.restore(); console.log.restore(); console.error.restore(); }); - describe('non-react', () => { + describe('no options', () => { beforeEach(() => { - cmp = new DevServer('/path/to/a/script/file.js', '/path/to/a/style/file.scss', - '/path/to/the/development/directory', WEB_PORT, false); + cmp = new DevServer('/path/to/a/script/file.js'); }); - it('assigns a port number', () => { - expect(cmp.port).equal(WEB_PORT); + it('configures script', () => { + expect(cmp.script).equal('/path/to/a/script/file.js'); }); - it('constructs a LiveReload server instance', () => { - expect(cmp.lr).equal(srv); + it('configures options', () => { + expect(cmp.options).eql({ + port: WEB_PORT, + react: true, + contentBase: cwd + }); }); - it('binds the sass compiler', () => { - expect(SASSCompiler.prototype.fe.bind).calledWith(match.instanceOf(SASSCompiler), '/path/to/a/style/file.scss', - '/path/to/the/development/directory/style.css', match.func); - expect(cmp.compileSASS).a('function'); + describe('layout', () => { + + beforeEach(() => { + spy(cmp, 'layout'); + cmp.layout(); + }); + + afterEach(() => { + cmp.layout.restore(); + }); + + it('returns layout without style', () => { + expect(cmp.layout).returned(` + + + + + + Development server - WebCompiler + + + +
+ + +`); + }); + + }); + + describe('watchSASS', () => { + + beforeEach(() => { + cmp.watchSASS(); + }); + + it('does not start up the watcher', () => { + expect(watch).not.called; + }); + }); - describe('invoke notifies LiveReload when SASS is recompiled', () => { + describe('run', () => { beforeEach(() => { - stub(cmp.lr, 'changed'); - cmp.compileSASS(); + stub(cmp, 'watchJS'); + stub(cmp, 'watchSASS'); + cmp.run(); }); afterEach(() => { - cmp.lr.changed.restore(); + cmp.watchJS.restore(); + cmp.watchSASS.restore(); }); - it('calls changed', () => { - expect(cmp.lr.changed).calledWith({body: {files: ['style.css']}}); + it('invokes the watchJS method', () => { + expect(cmp.watchJS).called; + }); + + it('invokes the watchSASS method', () => { + expect(cmp.watchSASS).called; }); }); - it('invokes webpack', () => { - expect(webpack).calledWith({ - cache: {}, - debug: true, - devtool: 'eval-source-map', - entry: [ - 'webpack-dev-server/client?http://localhost:8000', - 'webpack/hot/only-dev-server', - '/path/to/a/script/file.js' - ], - output: { - path: '/path/to/the/development/directory', - filename: 'script.js', - publicPath: '/' - }, - plugins: [ - match.instanceOf(HotModuleReplacementPlugin), - match.instanceOf(NoErrorsPlugin) - ], - module: { - loaders: [{ - test: /\.js$/, - exclude: /node_modules/, - loaders: ['babel'] - }, { - test: /\.json$/, - loader: 'json' - }] - } + }); + + describe('options', () => { + + beforeEach(() => { + cmp = new DevServer('/path/to/a/script/file.js', { + style: '/path/to/a/style/file.scss', + contentBase: '/path/to/the/public/directory' }); }); - it('instantiates WebpackDevServer', () => { - expect(cmp.server).instanceof(WebpackDevServer); - expect(cmp.server.webpackInstance).equal('the webpack module bundler'); - expect(cmp.server.config).eql({ - contentBase: '/path/to/the/development/directory', - publicPath: '/', - hot: true, - historyApiFallback: true + describe('layout', () => { + + beforeEach(() => { + spy(cmp, 'layout'); + cmp.layout(); + }); + + afterEach(() => { + cmp.layout.restore(); }); - }); - it('creates the index route', () => { - expect(cmp.server.app.get).calledWith('*', match.func); - cmp.server.app.handler(null, {send}); - expect(send).calledWith(` + it('returns layout without style', () => { + expect(cmp.layout).returned(` Development server - WebCompiler - + +
`); - }); - - }); - - describe('react', () => { - - beforeEach(() => { - cmp = new DevServer('/path/to/a/script/file.js', '/path/to/a/style/file.scss', - '/path/to/the/development/directory'); - }); - - it('invokes webpack', () => { - expect(webpack).calledWith({ - cache: {}, - debug: true, - devtool: 'eval-source-map', - entry: [ - 'webpack-dev-server/client?http://localhost:3000', - 'webpack/hot/only-dev-server', - '/path/to/a/script/file.js' - ], - output: { - path: '/path/to/the/development/directory', - filename: 'script.js', - publicPath: '/' - }, - plugins: [ - match.instanceOf(HotModuleReplacementPlugin), - match.instanceOf(NoErrorsPlugin) - ], - module: { - loaders: [{ - test: /\.js$/, - exclude: /node_modules/, - loaders: ['react-hot', 'babel'] - }, { - test: /\.json$/, - loader: 'json' - }] - } }); + }); describe('watchSASS', () => { beforeEach(() => { - stub(cmp.lr, 'listen'); - stub(cmp, 'compileSASS'); + stub(srv, 'listen'); cmp.watchSASS(); }); afterEach(() => { - cmp.lr.listen.restore(); - cmp.compileSASS.restore(); + srv.listen.restore(); }); - it('starts up LiveReload', () => { - expect(cmp.lr.listen).calledWith(LIVERELOAD_PORT); + it('calls tinylr', () => { + expect(tinylr).called; }); - it('compiles SASS on start up', () => { - expect(cmp.compileSASS).called; + it('binds the sass compiler', () => { + expect(SASSCompiler.prototype.fe.bind).calledWith(match.instanceOf(SASSCompiler), '/path/to/a/style/file.scss', + '/path/to/the/public/directory/style.css', match.func); + }); + + it('starts up LiveReload', () => { + expect(srv.listen).calledWith(LIVERELOAD_PORT); }); it('starts up the watcher', () => { - expect(watch).calledWith(cwd, 'scss', cmp.compileSASS); + expect(watch).calledWith(cwd, 'scss', match.func); }); }); - describe('watchJS start up error', () => { + describe('loaders', () => { beforeEach(() => { - stub(cmp.server, 'listen').callsArgWith(2, 'something bad happened'); - cmp.watchJS(); + spy(cmp, 'loaders'); }); afterEach(() => { - cmp.server.listen.restore(); + cmp.loaders.restore(); }); - it('invokes the server listen method', () => { - expect(cmp.server.listen).calledWith(DEFAULT_WEB_PORT, '0.0.0.0', match.func); - }); + describe('non-react', () => { + + beforeEach(() => { + cmp.options.react = false; + cmp.loaders(); + }); + + it('returns result', () => { + expect(cmp.loaders).returned(['babel']); + }); - it('prints the error on screen', () => { - expect(console.error).calledWith('something bad happened'); }); - it('does not print the success message', () => { - expect(console.log).not.called; + describe('react', () => { + + beforeEach(() => { + cmp.options.react = true; + cmp.loaders(); + }); + + it('returns result', () => { + expect(cmp.loaders).returned(['react-hot', 'babel']); + }); + }); }); - describe('watchJS success', () => { + describe('watchJS listen error', () => { beforeEach(() => { - stub(cmp.server, 'listen').callsArg(2); + stub(cmp, 'loaders').returns('collection of loaders'); + stub(cmp, 'layout').returns('html string'); + stub(WebpackDevServer.prototype, 'listen').callsArgWith(2, 'something bad happened'); cmp.watchJS(); }); afterEach(() => { - cmp.server.listen.restore(); + cmp.loaders.restore(); + cmp.layout.restore(); + WebpackDevServer.prototype.listen.restore(); }); - it('does not print any errors', () => { - expect(console.error).not.called; + it('invokes webpack', () => { + expect(webpack).calledWith({ + cache: {}, + debug: true, + devtool: 'eval-source-map', + entry: [ + 'webpack-dev-server/client?http://localhost:3000', + 'webpack/hot/only-dev-server', + '/path/to/a/script/file.js' + ], + output: { + path: '/path/to/the/public/directory', + filename: 'script.js', + publicPath: '/' + }, + plugins: [ + match.instanceOf(HotModuleReplacementPlugin), + match.instanceOf(NoErrorsPlugin) + ], + module: { + loaders: [{ + test: /\.js$/, + exclude: /node_modules/, + loaders: 'collection of loaders' + }, { + test: /\.json$/, + loader: 'json' + }] + } + }); }); - it('prints the successful message', () => { - expect(console.log).calledWith('\x1b[32mStarted the development server at localhost:%d\x1b[0m', cmp.port); + it('creates the index route', () => { + expect(WebpackDevServer.prototype.app.use).calledWith(match.func); + WebpackDevServer.prototype.app.handler(null, {send}); + expect(send).calledWith('html string'); + }); + + it('invokes the server listen method', () => { + expect(WebpackDevServer.prototype.listen).calledWith(cmp.options.port, '0.0.0.0', match.func); + }); + + it('prints the error on screen', () => { + expect(console.error).calledWith('something bad happened'); + }); + + it('does not print the success message', () => { + expect(console.log).not.called; }); }); - describe('run', () => { + describe('watchJS listen success', () => { beforeEach(() => { - stub(cmp, 'watchJS'); - stub(cmp, 'watchSASS'); - cmp.run(); + stub(WebpackDevServer.prototype, 'listen').callsArg(2); + cmp.watchJS(); }); afterEach(() => { - cmp.watchJS.restore(); - cmp.watchSASS.restore(); + WebpackDevServer.prototype.listen.restore(); }); - it('invokes the watchJS method', () => { - expect(cmp.watchJS).called; + it('does not print any errors', () => { + expect(console.error).not.called; }); - it('invokes the watchSASS method', () => { - expect(cmp.watchSASS).called; + it('prints the successful message', () => { + expect(console.log).calledWith('\x1b[32mStarted the development server at localhost:%d\x1b[0m', + cmp.options.port); }); }); diff --git a/test/mock.js b/test/mock.js index 4318d31..8167345 100644 --- a/test/mock.js +++ b/test/mock.js @@ -74,6 +74,7 @@ export class WebpackDevServer { webpackInstance: any; config: Object; app: Object; + listen: () => void; constructor(webpackInstance: any, config: Object) { this.webpackInstance = webpackInstance; this.config = config; @@ -81,10 +82,11 @@ export class WebpackDevServer { } WebpackDevServer.prototype.app = { - get(route: string, handler: () => void) { + use(handler: () => void) { this.handler = handler; } }; +WebpackDevServer.prototype.listen = noop; export class Server { changed: () => void;