Big commit: detecting .babelrc file, removing old webpackDevServer config functions and more

This commit is contained in:
Ryan Weaver
2017-06-03 13:54:45 -04:00
parent f38e33c2ab
commit 7b1991bbd0
32 changed files with 264 additions and 295 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,3 @@
node_modules/
npm-debug.log*
test/project/*
/test_tmp

View File

@@ -485,11 +485,12 @@ Full Configuration Example
Configuring Babel
-----------------
Babel_ is automatically configured for all ``.js`` files via the
``babel-loader``. By default, the ``env`` preset is used without
any extra options.
Babel_ is automatically configured for all ``.js`` and ``.jsx`` files
via the ``babel-loader`` with sensible defaults (e.g. with the ``env``
preset and ``react`` if requested).
Need to configure Babel yourself? No problem - there are two options:
Need to extend the Babel configuration further? No problem! The easiest
way is via ``configureBabel()``:
.. code-block:: javascript
@@ -499,17 +500,15 @@ Need to configure Babel yourself? No problem - there are two options:
Encore
// ...
// Option 1) configure babel right inside webpack.config.js
// modify our default Babel configuration
.configureBabel(function(babelConfig) {
babelConfig.presets.push('es2017');
})
// Option 2) Create a .babelrc file, then tell Encore it exists
.useBabelRcFile()
;
If you create a ``.babelrc`` file, don't forget to call ``useBabelRcFile()``.
Otherwise, the default config will override your file's settings.
You can also create a standard ``.babelrc`` file at the root of your project.
Just make sure to configure it with all the presets you need: as soon as a
``.babelrc`` is present, Encore can no longer add *any* Babel configuration for you!
Using React
-----------

View File

@@ -0,0 +1,3 @@
h4 {
background: top left url('./../images/symfony_logo.png') no-repeat;
}

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,2 @@
// comments in no_require.js
// i am the no_require.js file

View File

@@ -92,29 +92,6 @@ module.exports = {
return this;
},
/**
* Call this to use the webpack-dev-server
*
* This will initialize the needed devServer config
* and point the URLs to the webpackDevServerUrl
* (http://localhost:8080 by default).
*
* Be sure to execute the webpack-dev-server when this
* option is set:
*
* ./node_modules/.bin/webpack-dev-server --hot --inline
*
* False can be passed as an argument to disable the dev server.
*
* @param {string|bool} webpackDevServerUrl
* @return {exports}
*/
useWebpackDevServer(webpackDevServerUrl = null) {
webpackConfig.useWebpackDevServer(webpackDevServerUrl);
return this;
},
enableVersioning(enabled = true) {
webpackConfig.enableVersioning(enabled);
@@ -240,19 +217,6 @@ module.exports = {
return this;
},
/**
* Should the babel-loader be allowed to load config from
* a .babelrc file?
*
* @param {boolean} shouldUse
* @return {exports}
*/
useBabelRcFile(shouldUse = true) {
webpackConfig.useBabelRcFile(shouldUse);
return this;
},
enableReactPreset(enabled = true) {
webpackConfig.enableReactPreset(enabled);

View File

@@ -9,6 +9,10 @@ function validateRuntimeConfig(runtimeConfig) {
if (null === runtimeConfig.context) {
throw new Error('RuntimeConfig.context must be set.');
}
if (null === runtimeConfig.babelRcFileExists) {
throw new Error('RuntimeConfig.babelRcFileExists must be set.')
}
}
class WebpackConfig {
@@ -18,7 +22,6 @@ class WebpackConfig {
this.outputPath = null;
this.publicPath = null;
this.manifestKeyPrefix = null;
this.webpackDevServerUrl = null;
this.entries = new Map();
this.styleEntries = new Map();
this.useVersioning = false;
@@ -30,7 +33,6 @@ class WebpackConfig {
this.sharedCommonsEntryName = null;
this.providedVariables = {};
this.babelConfigurationCallback = function() {};
this.allowBabelRcFile = false;
this.useReact = false;
}
@@ -38,6 +40,10 @@ class WebpackConfig {
return this.runtimeConfig.context;
}
doesBabelRcFileExist() {
return this.runtimeConfig.babelRcFileExists;
}
setOutputPath(outputPath) {
if (!path.isAbsolute(outputPath)) {
outputPath = path.resolve(this.getContext(), outputPath);
@@ -65,8 +71,8 @@ class WebpackConfig {
* is simply used as the default manifestKeyPrefix.
*/
if (publicPath.includes('://')) {
if (this.webpackDevServerUrl) {
throw new Error('You cannot pass an absolute URL to setPublicPath() and useWebpackDevServer() at the same time. Try using Encore.isProduction() to only configure each setting when needed.');
if (this.useDevServer()) {
throw new Error('You cannot pass an absolute URL to setPublicPath() and use the dev-server at the same time. Try using Encore.isProduction() to only configure your absolute publicPath for production.');
}
} else {
if (publicPath.indexOf('/') !== 0) {
@@ -94,39 +100,6 @@ class WebpackConfig {
this.manifestKeyPrefix = manifestKeyPrefix;
}
useWebpackDevServer(webpackDevServerUrl = null) {
// allow false to be passed to disable
if (false === webpackDevServerUrl) {
this.webpackDevServerUrl = null;
return;
}
// if true, then use the default URL (set below)
if (true === webpackDevServerUrl) {
webpackDevServerUrl = null;
}
if (this.publicPath && this.publicPath.includes('://')) {
throw new Error('You cannot pass an absolute URL to setPublicPath() and useWebpackDevServer() at the same time. Try using Encore.isProduction() to only configure each setting when needed.');
}
if (null === webpackDevServerUrl) {
// this is the default URL when you boot up webpack-dev-server
webpackDevServerUrl = 'http://localhost:8080';
}
if (!webpackDevServerUrl.includes('://')) {
throw new Error('Invalid argument passed to webpackDevServerUrl(): you must pass an absolute URL (e.g. http://localhost:8090).');
}
// guarantee a single trailing slash
webpackDevServerUrl = webpackDevServerUrl.replace(/\/$/,'');
webpackDevServerUrl = webpackDevServerUrl + '/';
this.webpackDevServerUrl = webpackDevServerUrl;
}
/**
* Returns the value that should be used as the publicPath,
* which can be overridden by enabling the webpackDevServer
@@ -135,9 +108,9 @@ class WebpackConfig {
*/
getRealPublicPath() {
// if we're using webpack-dev-server, use it & add the publicPath
if (this.webpackDevServerUrl) {
if (this.useDevServer()) {
// avoid 2 middle slashes
return this.webpackDevServerUrl.replace(/\/$/,'') + this.publicPath;
return this.runtimeConfig.devServerUrl.replace(/\/$/,'') + this.publicPath;
}
return this.publicPath;
@@ -180,14 +153,10 @@ class WebpackConfig {
configureBabel(callback) {
// todo - type check on callback?
// todo - don't allow this AND useBabelRcFile
// todo - don't allow this when a .babelrc file is present
this.babelConfigurationCallback = callback;
}
useBabelRcFile(shouldUse = true) {
this.allowBabelRcFile = shouldUse;
}
createSharedEntry(name, files) {
// don't allow to call this twice
if (this.sharedCommonsEntryName) {
@@ -240,6 +209,10 @@ class WebpackConfig {
});
}
useDevServer() {
return this.runtimeConfig.useDevServer;
}
isProduction() {
return this.runtimeConfig.environment === 'production';
}

View File

@@ -38,7 +38,7 @@ class ConfigGenerator {
}
}
if (this.webpackConfig.webpackDevServerUrl) {
if (this.webpackConfig.useDevServer()) {
config.devServer = this.buildDevServerConfig();
}
@@ -101,7 +101,8 @@ class ConfigGenerator {
};
// configure babel (unless the user is specifying .babelrc)
if (!this.webpackConfig.allowBabelRcFile) {
// todo - add a sanity check for their babelrc contents
if (!this.webpackConfig.doesBabelRcFileExist()) {
Object.assign(babelConfig, {
presets: [
['env', {

View File

@@ -9,6 +9,8 @@ class RuntimeConfig {
this.devServerUrl = null;
this.devServerHttps = null;
this.babelRcFileExists = null;
this.helpRequested = false;
}
}

View File

@@ -2,6 +2,7 @@ const RuntimeConfig = require('./RuntimeConfig');
const chalk = require('chalk');
const pkgUp = require('pkg-up');
const path = require('path');
const resolveRc = require('babel-loader/lib/resolve-rc');
/**
* @param argv
@@ -31,7 +32,7 @@ module.exports = function(argv, cwd) {
const host = argv.host ? argv.host : 'localhost';
const port = argv.port ? argv.port : '8080';
runtimeConfig.devServerUrl = `http${runtimeConfig.devServerHttps ? 's' : ''}://${host}:${port}`;
runtimeConfig.devServerUrl = `http${runtimeConfig.devServerHttps ? 's' : ''}://${host}:${port}/`;
}
break;
@@ -52,5 +53,7 @@ module.exports = function(argv, cwd) {
runtimeConfig.helpRequested = true;
}
runtimeConfig.babelRcFileExists = (typeof resolveRc(runtimeConfig.context)) !== 'undefined';
return runtimeConfig;
};

View File

@@ -65,8 +65,8 @@ class Validator {
}
_validateDevServer() {
if (this.webpackConfig.useVersioning && this.webpackConfig.webpackDevServerUrl) {
throw new Error('Don\'t enable versioning with useWebpackDevServer(). A good setting is Encore.enableVersioning(Encore.isProduction()).');
if (this.webpackConfig.useVersioning && this.webpackConfig.useDevServer()) {
throw new Error('Don\'t enable versioning with the dev-server. A good setting is Encore.enableVersioning(Encore.isProduction()).');
}
}
}

View File

@@ -9,6 +9,16 @@ const loadManifest = function(webpackConfig) {
);
};
const readOutputFile = function(webpackConfig, filePath) {
const fullPath = path.join(webpackConfig.outputPath, filePath);
if (!fs.existsSync(fullPath)) {
throw new Error(`Output file "${filePath}" does not exist.`);
}
return fs.readFileSync(fullPath, 'utf8');
};
class Assert {
/**
* @param {WebpackConfig} webpackConfig
@@ -52,15 +62,10 @@ class Assert {
}
assertOutputFileHasSourcemap(filePath) {
const fullPath = path.join(this.webpackConfig.outputPath, filePath);
const actualContents = readOutputFile(this.webpackConfig, filePath);
if (!fs.existsSync(fullPath)) {
throw new Error(`Output file "${filePath}" does not exist.`);
}
const actualContents = fs.readFileSync(fullPath, 'utf8');
if (!actualContents.includes('sourceMappingURL')) {
throw new Error(`No sourcemap found for ${fullPath}!`);
throw new Error(`No sourcemap found for ${filePath}!`);
}
const sourceMappingUrlContents = actualContents.split('sourceMappingURL')[1];
@@ -69,7 +74,15 @@ class Assert {
// incorrectly configure css/sass sourcemaps, you WILL have
// a sourcemap, but it will be too small / i.e. basically empty
if (sourceMappingUrlContents.length < 200) {
throw new Error(`Sourcemap for ${fullPath} appears to be empty!`);
throw new Error(`Sourcemap for ${filePath} appears to be empty!`);
}
}
assertOutputFileDoesNotHaveSourcemap(filePath) {
const actualContents = readOutputFile(this.webpackConfig, filePath);
if (actualContents.includes('sourceMappingURL')) {
throw new Error(`Sourcemap found for ${filePath}!`);
}
}

View File

@@ -1,6 +1,9 @@
'use strict';
const path = require('path');
const WebpackConfig = require('../WebpackConfig');
const RuntimeConfig = require('../config/RuntimeConfig');
const parseRuntime = require('../config/parse-runtime');
const webpack = require('webpack');
const fs = require('fs-extra');
const Browser = require('zombie');
@@ -9,41 +12,43 @@ const configGenerator = require('../config-generator');
const validator = require('../config/validator');
const assertUtil = require('./assert');
const testDir = path.join(__dirname, '../', '../', 'test');
const testProjectDir = path.join(testDir, 'project');
const testFixturesDir = path.join(testDir, 'fixtures');
const tmpDir = path.join(__dirname, '../', '../', 'test_tmp');
const testFixturesDir = path.join(__dirname, '../', '../', 'fixtures');
var temporaryFiles = [];
var servers = [];
let servers = [];
/**
* @param {string} outputDirName
* @param {string} environment
* @returns {WebpackConfig}
*/
function createWebpackConfig(outputDirName = '', environment = 'dev') {
const runtime = new RuntimeConfig();
runtime.environment = environment;
runtime.context = testFixturesDir;
const config = new WebpackConfig(runtime);
function createTestAppDir() {
const testAppDir = path.join(tmpDir, Math.random().toString(36).substring(7));
if (!fs.existsSync(testDir)) {
fs.mkdirSync(testDir);
}
// copy the fixtures into this new directory
fs.copySync(testFixturesDir, testAppDir);
createTestProjectDir();
const outputPath = path.join(testProjectDir, outputDirName);
// allows us to create a few levels deep without issues
fs.mkdirsSync(outputPath);
config.setOutputPath(path.join(testProjectDir, outputDirName));
return config;
return testAppDir;
}
function createTestProjectDir() {
fs.mkdirsSync(testProjectDir);
/**
* @param {string} testAppDir The dir from calling createTestAppDir()
* @param {string} outputDirName
* @param {string} command The encore command name (e.g. dev)
* @param {object} argv Additional argv commands
* @returns {WebpackConfig}
*/
function createWebpackConfig(testAppDir, outputDirName = '', command, argv = {}) {
argv._ = [command];
argv.context = testAppDir;
const runtimeConfig = parseRuntime(
argv,
__dirname
);
return testProjectDir;
const config = new WebpackConfig(runtimeConfig);
const outputPath = path.join(testAppDir, outputDirName);
// allows us to create a few levels deep without issues
fs.mkdirsSync(outputPath);
config.setOutputPath(outputPath);
return config;
}
function runWebpack(webpackConfig, callback) {
@@ -75,26 +80,8 @@ function runWebpack(webpackConfig, callback) {
});
}
function emptyTestDir() {
fs.emptyDirSync(testProjectDir);
}
function saveTemporaryFileToFixturesDirectory(filename, contents) {
const tmpFilePath = path.join(testFixturesDir, filename);
fs.writeFileSync(
tmpFilePath,
contents
);
temporaryFiles.push(tmpFilePath);
}
function deleteTemporaryFiles() {
for (let filePath of temporaryFiles) {
fs.unlinkSync(filePath);
}
temporaryFiles = [];
function emptyTmpDir() {
fs.emptyDirSync(tmpDir);
}
function touchFileInOutputDir(filename, webpackConfig) {
@@ -127,7 +114,7 @@ function stopAllServers() {
* makes a request to it, and executes a callback, passing that
* the Browser instance used to make the request.
*
* @param {string} webRootDir Directory name (e.g. public) where the web server should be rooted
* @param {string} webRootDir Directory path (e.g. /path/to/public) where the web server should be rooted
* @param {Array} scriptSrcs Used to create <script src=""> tags.
* @param {Function} callback Called after the page was requested.
* @return {void}
@@ -148,18 +135,16 @@ function requestTestPage(webRootDir, scriptSrcs, callback) {
</html>
`;
const webRoot = path.join(testProjectDir, webRootDir);
// write the testing.html file
fs.writeFileSync(
path.join(webRoot, 'testing.html'),
path.join(webRootDir, 'testing.html'),
testHtml
);
// start the main local server
startHttpServer('8080', webRoot);
startHttpServer('8080', webRootDir);
// start a secondary server - can be used as the "CDN"
startHttpServer('8090', webRoot);
startHttpServer('8090', webRootDir);
const browser = new Browser();
browser.on('error', function(error) {
@@ -173,11 +158,9 @@ function requestTestPage(webRootDir, scriptSrcs, callback) {
module.exports = {
createWebpackConfig,
createTestProjectDir,
createTestAppDir,
runWebpack,
emptyTestDir,
emptyTmpDir,
requestTestPage,
saveTemporaryFileToFixturesDirectory,
deleteTemporaryFiles,
touchFileInOutputDir
};

View File

@@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"test": "./node_modules/.bin/mocha --reporter spec",
"test": "./node_modules/.bin/mocha --reporter spec test --recursive"
"lint": "./node_modules/.bin/eslint lib",
"travis:lint": "npm run lint && npm run nsp",
"nsp": "./node_modules/.bin/nsp check --output summary"

View File

@@ -7,6 +7,7 @@ const fs = require('fs');
function createConfig() {
const runtimeConfig = new RuntimeConfig();
runtimeConfig.context = __dirname;
runtimeConfig.babelRcFileExists = false;
return new WebpackConfig(runtimeConfig);
}
@@ -94,11 +95,11 @@ describe('WebpackConfig object', () => {
it('Setting to a URL when using devServer throws an error', () => {
const config = createConfig();
config.useWebpackDevServer();
config.runtimeConfig.useDevServer = true;
expect(() => {
config.setPublicPath('https://examplecdn.com');
}).to.throw('You cannot pass an absolute URL to setPublicPath() and useWebpackDevServer()');
}).to.throw('You cannot pass an absolute URL to setPublicPath() and use the dev-server');
});
});
@@ -121,59 +122,6 @@ describe('WebpackConfig object', () => {
});
});
describe('useWebpackDevServer', () => {
it('Set with no arguments', () => {
const config = createConfig();
config.setPublicPath('/build');
config.useWebpackDevServer();
expect(config.getRealPublicPath()).to.equal('http://localhost:8080/build/');
});
it('Set to false', () => {
const config = createConfig();
config.setPublicPath('/build');
config.useWebpackDevServer(false);
expect(config.getRealPublicPath()).to.equal('/build/');
expect(config.webpackDevServerUrl).to.be.null;
});
it('Set to true', () => {
const config = createConfig();
config.setPublicPath('/build');
config.useWebpackDevServer(true);
expect(config.getRealPublicPath()).to.equal('http://localhost:8080/build/');
});
it('Set and control URL', () => {
const config = createConfig();
config.setPublicPath('/build');
config.useWebpackDevServer('https://localhost:9000');
expect(config.getRealPublicPath()).to.equal('https://localhost:9000/build/');
});
it('Exception if publicPath is set to an absolute URL', () => {
const config = createConfig();
config.setPublicPath('http://foo.com');
expect(() => {
config.useWebpackDevServer();
}).to.throw('You cannot pass an absolute URL to setPublicPath() and useWebpackDevServer()');
});
it('Exception if URL is not a URL!', () => {
const config = createConfig();
expect(() => {
config.useWebpackDevServer('localhost:8090');
}).to.throw('you must pass an absolute URL (e.g. http://localhost:8090)');
});
});
describe('addEntry', () => {
it('Calling with a duplicate name throws an error', () => {
const config = createConfig();

View File

@@ -7,12 +7,20 @@ const ManifestPlugin = require('./../lib/webpack-manifest-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
function createConfig(context = null, environment = 'dev') {
context = context ? context : __dirname;
function createConfig(runtimeConfig = null) {
runtimeConfig = runtimeConfig ? runtimeConfig : new RuntimeConfig();
const runtimeConfig = new RuntimeConfig();
runtimeConfig.context = context;
runtimeConfig.environment = environment;
if (null === runtimeConfig.context) {
runtimeConfig.context = __dirname;
}
if (null === runtimeConfig.environment) {
runtimeConfig.environment = 'dev';
}
if (null === runtimeConfig.babelRcFileExists) {
runtimeConfig.babelRcFileExists = false;
}
return new WebpackConfig(runtimeConfig);
}
@@ -45,7 +53,9 @@ describe('The config-generator function', () => {
describe('Test basic output properties', () => {
it('Returns an object with the correct properties', () => {
// setting context explicitly to make test more dependable
const config = createConfig('/foo/dir');
const runtimeConfig = new RuntimeConfig();
runtimeConfig.context = '/foo/dir';
const config = createConfig(runtimeConfig);
config.addEntry('main', './main');
config.publicPath = '/';
config.outputPath = '/tmp';
@@ -188,7 +198,10 @@ describe('The config-generator function', () => {
describe('Test production changes', () => {
it('not in production', () => {
const config = createConfig('/tmp/context', 'dev');
const runtimeConfig = new RuntimeConfig();
runtimeConfig.context = '/tmp/context';
runtimeConfig.environment = 'dev';
const config = createConfig(runtimeConfig);
config.outputPath = '/tmp/output/public-path';
config.publicPath = '/public-path';
config.addEntry('main', './main');
@@ -207,7 +220,9 @@ describe('The config-generator function', () => {
});
it('YES to production', () => {
const config = createConfig(null, 'production');
const runtimeConfig = new RuntimeConfig();
runtimeConfig.environment = 'production';
const config = createConfig(runtimeConfig);
config.outputPath = '/tmp/output/public-path';
config.publicPath = '/public-path';
config.addEntry('main', './main');
@@ -323,12 +338,13 @@ describe('The config-generator function', () => {
expect(JSON.stringify(jsRule.use.options.presets)).contains('env');
});
it('useBabelRcFile() passes *no* config', () => {
const config = createConfig();
it('If .babelrc is present, we pass *no* config', () => {
const runtimeConfig = new RuntimeConfig();
runtimeConfig.babelRcFileExists = true;
const config = createConfig(runtimeConfig);
config.outputPath = '/tmp/output/public-path';
config.publicPath = '/public-path';
config.addEntry('main', './main');
config.useBabelRcFile();
const actualConfig = configGenerator(config);
@@ -404,6 +420,7 @@ describe('The config-generator function', () => {
describe('test for devServer config', () => {
it('no devServer config when not enabled', () => {
const config = createConfig();
config.runtimeConfig.useDevServer = false;
config.publicPath = '/';
config.outputPath = '/tmp';
config.addEntry('main', './main');
@@ -414,10 +431,11 @@ describe('The config-generator function', () => {
it('contentBase is calculated correctly', () => {
const config = createConfig();
config.runtimeConfig.useDevServer = true;
config.runtimeConfig.devServerUrl = 'http://localhost:8080/';
config.outputPath = '/tmp/public/build';
config.setPublicPath('/build/');
config.addEntry('main', './main');
config.useWebpackDevServer();
const actualConfig = configGenerator(config);
// contentBase should point to the "document root", which
@@ -429,12 +447,13 @@ describe('The config-generator function', () => {
it('contentBase works ok with manifestKeyPrefix', () => {
const config = createConfig();
config.runtimeConfig.useDevServer = true;
config.runtimeConfig.devServerUrl = 'http://localhost:8080/';
config.outputPath = '/tmp/public/build';
config.setPublicPath('/subdirectory/build');
// this "fixes" the incompatibility between outputPath and publicPath
config.setManifestKeyPrefix('/build/');
config.addEntry('main', './main');
config.useWebpackDevServer();
const actualConfig = configGenerator(config);
// contentBase should point to the "document root", which

View File

@@ -1,36 +1,39 @@
'use strict';
const expect = require('chai').expect;
const parseArgv = require('../../lib/config/parse-runtime');
const testSetup = require('../../lib/test/setup');
const fs = require('fs-extra');
const path = require('path');
createArgv = function(argv) {
function createArgv (argv) {
return require('yargs/yargs')(argv).argv;
};
}
createTestDirectory= function() {
const projectDir = testSetup.createTestProjectDir();
function createTestDirectory () {
const projectDir = testSetup.createTestAppDir();
fs.writeFileSync(
path.join(projectDir, 'package.json'),
''
);
return projectDir;
};
}
describe('parse-runtime', () => {
beforeEach(() => {
testSetup.emptyTestDir();
testSetup.emptyTmpDir();
});
it('Basic usage', () => {
const testDir = createTestDirectory();
const config = parseArgv(createArgv(['fooCommand', '--bar', '--help']), testDir);
const config = parseArgv(createArgv(['foobar', '--bar', '--help']), testDir);
expect(config.command).to.equal('fooCommand');
expect(config.command).to.equal('foobar');
expect(config.context).to.equal(testDir);
expect(config.helpRequested).to.be.true;
expect(config.isValidCommand).to.be.false;
expect(config.babelRcFileExists).to.be.false;
});
it('dev command', () => {
@@ -55,7 +58,7 @@ describe('parse-runtime', () => {
expect(config.environment).to.equal('dev');
expect(config.useDevServer).to.be.true;
expect(config.devServerUrl).to.equal('http://foohost.l:9999')
expect(config.devServerUrl).to.equal('http://foohost.l:9999/')
});
it('dev-server command https', () => {
@@ -63,7 +66,7 @@ describe('parse-runtime', () => {
const config = parseArgv(createArgv(['dev-server', '--https', '--host', 'foohost.l', '--port', '9999']), testDir);
expect(config.useDevServer).to.be.true;
expect(config.devServerUrl).to.equal('https://foohost.l:9999')
expect(config.devServerUrl).to.equal('https://foohost.l:9999/')
});
it('--context is parsed correctly', () => {
@@ -72,4 +75,16 @@ describe('parse-runtime', () => {
expect(config.context).to.equal('/tmp/custom-context');
});
it('.babelrc detected when present', () => {
const projectDir = createTestDirectory();
fs.writeFileSync(
path.join(projectDir, '.babelrc'),
''
);
const config = parseArgv(createArgv(['dev']), projectDir);
expect(config.babelRcFileExists).to.be.true;
});
});

View File

@@ -5,17 +5,16 @@ const validator = require('../../lib/config/validator');
const webpack = require('webpack');
function createConfig() {
const config = new WebpackConfig(new RuntimeConfig());
config.outputPath = '/tmp/public/build';
config.setPublicPath('/build');
config.addEntry('main', './main');
const runtimeConfig = new RuntimeConfig();
runtimeConfig.context = __dirname;
runtimeConfig.babelRcFileExists = false;
return config;
return new WebpackConfig(runtimeConfig);
}
describe('The validator function', () => {
it('throws an error if there are no entries', () => {
const config = new WebpackConfig(new RuntimeConfig());
const config = createConfig();
config.publicPath = '/';
config.outputPath = '/tmp';
@@ -25,7 +24,7 @@ describe('The validator function', () => {
});
it('throws an error if there is no output path', () => {
const config = new WebpackConfig(new RuntimeConfig());
const config = createConfig();
config.publicPath = '/';
config.addEntry('main', './main');
@@ -35,7 +34,7 @@ describe('The validator function', () => {
});
it('throws an error if there is no public path', () => {
const config = new WebpackConfig(new RuntimeConfig());
const config = createConfig();
config.outputPath = '/tmp';
config.addEntry('main', './main');
@@ -46,6 +45,9 @@ describe('The validator function', () => {
it('with absolute publicPath, manifestKeyPrefix must be set', () => {
const config = createConfig();
config.outputPath = '/tmp/public/build';
config.setPublicPath('/build');
config.addEntry('main', './main');
config.setPublicPath('https://cdn.example.com');
expect(() => {
@@ -55,6 +57,8 @@ describe('The validator function', () => {
it('when outputPath and publicPath are incompatible, manifestKeyPrefix must be set', () => {
const config = createConfig();
config.outputPath = '/tmp/public/build';
config.addEntry('main', './main');
// pretend we're installed to a subdirectory
config.setPublicPath('/subdirectory/build');
@@ -65,11 +69,14 @@ describe('The validator function', () => {
it('cannot use versioning with webpack-dev-server', () => {
const config = createConfig();
config.outputPath = '/tmp/public/build';
config.setPublicPath('/build');
config.addEntry('main', './main');
config.runtimeConfig.useDevServer = true;
config.enableVersioning();
config.useWebpackDevServer();
expect(() => {
validator(config);
}).to.throw('Don\'t enable versioning with useWebpackDevServer()');
}).to.throw('Don\'t enable versioning with the dev-server');
});
});

View File

@@ -1,2 +0,0 @@
// comments in no_require.js
console.log('i am the no_require.js file');

View File

@@ -1,8 +1,20 @@
'use strict';
const chai = require('chai');
chai.use(require('chai-fs'));
const expect = chai.expect;
const path = require('path');
const testSetup = require('../lib/test/setup');
const fs = require('fs-extra');
function createWebpackConfig(outputDirName = '', command, argv = {}) {
return testSetup.createWebpackConfig(
testSetup.createTestAppDir(),
outputDirName,
command,
argv
);
}
describe('Functional tests using webpack', function() {
// being functional tests, these can take quite long
@@ -10,14 +22,11 @@ describe('Functional tests using webpack', function() {
describe('Basic scenarios', () => {
beforeEach(() => {
testSetup.emptyTestDir();
});
afterEach(() => {
testSetup.deleteTemporaryFiles();
testSetup.emptyTmpDir();
});
it('Builds a simple .js file + manifest.json', (done) => {
const config = testSetup.createWebpackConfig('web/build');
const config = createWebpackConfig('web/build', 'dev');
config.addEntry('main', './js/no_require');
config.setPublicPath('/build');
@@ -48,7 +57,7 @@ describe('Functional tests using webpack', function() {
});
it('setPublicPath with CDN loads assets from the CDN', (done) => {
const config = testSetup.createWebpackConfig('public/assets');
const config = createWebpackConfig('public/assets', 'dev');
config.addEntry('main', './js/code_splitting');
config.addStyleEntry('font', './css/roboto_font.css');
config.addStyleEntry('bg', './css/background_image.scss');
@@ -81,7 +90,7 @@ describe('Functional tests using webpack', function() {
);
testSetup.requestTestPage(
'public',
path.join(config.getContext(), 'public'),
['/assets/main.js'],
(browser) => {
webpackAssert.assertResourcesLoadedCorrectly(browser, [
@@ -100,13 +109,14 @@ describe('Functional tests using webpack', function() {
});
it('The devServer config loads successfully', (done) => {
const config = testSetup.createWebpackConfig('public/assets');
const config = createWebpackConfig('public/assets', 'dev-server', {
port: '8090'
});
config.addEntry('main', './js/code_splitting');
config.addStyleEntry('font', './css/roboto_font.css');
config.addStyleEntry('bg', './css/background_image.scss');
config.setPublicPath('/assets');
config.enableSassLoader();
config.useWebpackDevServer('http://localhost:8090');
testSetup.runWebpack(config, (webpackAssert) => {
// check that the publicPath is set correctly
@@ -126,7 +136,7 @@ describe('Functional tests using webpack', function() {
);
testSetup.requestTestPage(
'public',
path.join(config.getContext(), 'public'),
['/assets/main.js'],
(browser) => {
webpackAssert.assertResourcesLoadedCorrectly(browser, [
@@ -145,7 +155,7 @@ describe('Functional tests using webpack', function() {
});
it('Deploying to a subdirectory is no problem', (done) => {
const config = testSetup.createWebpackConfig('subdirectory/build');
const config = createWebpackConfig('subdirectory/build', 'dev');
config.addEntry('main', './js/code_splitting');
config.setPublicPath('/subdirectory/build');
config.setManifestKeyPrefix('build');
@@ -157,7 +167,8 @@ describe('Functional tests using webpack', function() {
);
testSetup.requestTestPage(
'', // the webroot will not include the /subdirectory/build part
// the webroot will not include the /subdirectory/build part
path.join(config.getContext(), ''),
['/subdirectory/build/main.js'],
(browser) => {
webpackAssert.assertResourcesLoadedCorrectly(browser, [
@@ -172,7 +183,7 @@ describe('Functional tests using webpack', function() {
});
it('Empty manifestKeyPrefix is allowed', (done) => {
const config = testSetup.createWebpackConfig('build');
const config = createWebpackConfig('build', 'dev');
config.addEntry('main', './js/code_splitting');
config.setPublicPath('/build');
config.setManifestKeyPrefix('');
@@ -188,7 +199,7 @@ describe('Functional tests using webpack', function() {
});
it('addStyleEntry .js files are removed', (done) => {
const config = testSetup.createWebpackConfig('web');
const config = createWebpackConfig('web', 'dev');
config.addEntry('main', './js/no_require');
config.setPublicPath('/');
config.addStyleEntry('styles', './css/h1_style.css');
@@ -211,21 +222,21 @@ describe('Functional tests using webpack', function() {
});
it('enableVersioning applies to js, css & manifest', (done) => {
const config = testSetup.createWebpackConfig('web/build');
const config = createWebpackConfig('web/build', 'dev');
config.addEntry('main', './js/code_splitting');
config.setPublicPath('/build');
config.addStyleEntry('h1', './css/h1_style.css');
config.addStyleEntry('bg', './css/background_image.scss');
config.addStyleEntry('bg', './css/another_bg_image.css');
config.enableSassLoader();
config.enableVersioning(true);
testSetup.runWebpack(config, (webpackAssert) => {
expect(config.outputPath).to.be.a.directory()
.with.files([
'0.fc540f51a2e2ae008038.js', // chunks are also versioned
'0.38a901b572534329da92.js', // chunks are also versioned
'main.292c0347ed1240663cb1.js',
'h1.c84caea6dd12bba7955dee9fedd5fd03.css',
'bg.f8a1ccfa5977d7c46fe2431ba5eef51e.css',
'bg.42ced8eae2254268bb3c65f1e65bd041.css',
'manifest.json'
]
);
@@ -237,7 +248,7 @@ describe('Functional tests using webpack', function() {
);
webpackAssert.assertOutputFileContains(
'bg.f8a1ccfa5977d7c46fe2431ba5eef51e.css',
'bg.42ced8eae2254268bb3c65f1e65bd041.css',
'/build/images/symfony_logo.ea1ca6f7f3719118f301a5cfcb1df3c0.png'
);
@@ -246,7 +257,7 @@ describe('Functional tests using webpack', function() {
});
it('font and image files are copied correctly', (done) => {
const config = testSetup.createWebpackConfig('www/build');
const config = createWebpackConfig('www/build', 'dev');
config.setPublicPath('/build');
config.addStyleEntry('bg', './css/background_image.scss');
config.addStyleEntry('font', './css/roboto_font.css');
@@ -292,7 +303,7 @@ describe('Functional tests using webpack', function() {
});
it('enableSourceMaps() adds to .js, css & scss', (done) => {
const config = testSetup.createWebpackConfig('www/build');
const config = createWebpackConfig('www/build', 'dev');
config.setPublicPath('/build');
config.addEntry('main', './js/no_require');
config.addStyleEntry('bg', './css/background_image.scss');
@@ -315,8 +326,32 @@ describe('Functional tests using webpack', function() {
});
});
it('Without enableSourceMaps(), there are no sourcemaps', (done) => {
const config = createWebpackConfig('www/build', 'dev');
config.setPublicPath('/build');
config.addEntry('main', './js/no_require');
config.addStyleEntry('bg', './css/background_image.scss');
config.addStyleEntry('font', './css/roboto_font.css');
config.enableSassLoader();
testSetup.runWebpack(config, (webpackAssert) => {
webpackAssert.assertOutputFileDoesNotHaveSourcemap(
'main.js'
);
// TODO - fix this, there IS always a sourcemap in .sass files
// webpackAssert.assertOutputFileDoesNotHaveSourcemap(
// 'bg.css'
// );
webpackAssert.assertOutputFileDoesNotHaveSourcemap(
'font.css'
);
done();
});
});
it('Code splitting a scss file works', (done) => {
const config = testSetup.createWebpackConfig('www/build');
const config = createWebpackConfig('www/build', 'dev');
config.setPublicPath('/build');
// loads sass_features.scss via require.ensure
config.addEntry('main', './js/code_split_load_scss');
@@ -339,7 +374,7 @@ describe('Functional tests using webpack', function() {
});
it('createdSharedEntry() creates commons files', (done) => {
const config = testSetup.createWebpackConfig('www/build');
const config = createWebpackConfig('www/build', 'dev');
config.setPublicPath('/build');
config.addEntry('main', ['./js/no_require', './js/code_splitting']);
config.addEntry('other', ['./js/no_require']);
@@ -362,7 +397,7 @@ describe('Functional tests using webpack', function() {
});
it('in production mode, code is uglified', (done) => {
const config = testSetup.createWebpackConfig('www/build', 'production');
const config = createWebpackConfig('www/build', 'production');
config.setPublicPath('/build');
config.addEntry('main', ['./js/no_require']);
@@ -378,22 +413,24 @@ describe('Functional tests using webpack', function() {
});
it('PostCSS works when enabled', (done) => {
const config = testSetup.createWebpackConfig('www/build');
config.setPublicPath('/build');
config.addStyleEntry('styles', ['./css/autoprefixer_test.css']);
config.enablePostCssLoader();
const appDir = testSetup.createTestAppDir();
testSetup.saveTemporaryFileToFixturesDirectory(
'postcss.config.js',
fs.writeFileSync(
path.join(appDir, 'postcss.config.js'),
`
module.exports = {
plugins: [
require('autoprefixer')()
]
}
`
`
);
const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev');
config.setPublicPath('/build');
config.addStyleEntry('styles', ['./css/autoprefixer_test.css']);
config.enablePostCssLoader();
testSetup.runWebpack(config, (webpackAssert) => {
// check that the autoprefixer did its work!
webpackAssert.assertOutputFileContains(
@@ -406,7 +443,7 @@ module.exports = {
});
it('less processes when enabled', (done) => {
const config = testSetup.createWebpackConfig('www/build');
const config = createWebpackConfig('www/build', 'dev');
config.setPublicPath('/build');
config.addStyleEntry('styles', ['./css/h2_styles.less']);
config.enableLessLoader();
@@ -424,7 +461,7 @@ module.exports = {
});
it('Babel is executed on .js files', (done) => {
const config = testSetup.createWebpackConfig('www/build');
const config = createWebpackConfig('www/build', 'dev');
config.setPublicPath('/build');
config.addEntry('main', './js/arrow_function');
@@ -440,14 +477,12 @@ module.exports = {
});
it('Babel can be configured via .babelrc', (done) => {
const config = testSetup.createWebpackConfig('www/build');
config.setPublicPath('/build');
config.addEntry('main', './js/class-syntax');
config.useBabelRcFile();
// create the .babelrc file first, so we see it
const appDir = testSetup.createTestAppDir();
testSetup.saveTemporaryFileToFixturesDirectory(
'.babelrc',
`
fs.writeFileSync(
path.join(appDir, '.babelrc'),
`
{
"presets": [
["env", {
@@ -460,6 +495,10 @@ module.exports = {
`
);
const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev');
config.setPublicPath('/build');
config.addEntry('main', './js/class-syntax');
testSetup.runWebpack(config, (webpackAssert) => {
// check that babel transformed the arrow function
webpackAssert.assertOutputFileContains(
@@ -473,7 +512,7 @@ module.exports = {
});
it('When enabled, react JSX is transformed!', (done) => {
const config = testSetup.createWebpackConfig('www/build');
const config = createWebpackConfig('www/build', 'dev');
config.setPublicPath('/build');
config.addEntry('main', './js/CoolReactComponent.jsx');
config.enableReactPreset();
@@ -490,7 +529,7 @@ module.exports = {
});
it('The output directory is cleaned between builds', (done) => {
const config = testSetup.createWebpackConfig('www/build');
const config = createWebpackConfig('www/build', 'dev');
config.setPublicPath('/build');
config.addEntry('main', './js/no_require');
config.cleanupOutputBeforeBuild();