Skip to content

Commit fd49f7d

Browse files
committed
Make webpack-dev-server optional
1 parent 9f31ac1 commit fd49f7d

File tree

6 files changed

+175
-5
lines changed

6 files changed

+175
-5
lines changed

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,23 @@ Encore.configureDevServerOptions((options) => {
7272
});
7373
```
7474

75+
* #1336 Make webpack-dev-server optional (@Kocal)
76+
77+
The `webpack-dev-server` package is now an optional peer dependency.
78+
It has been removed because some projects may not use it, and it was installing a bunch of unnecessary dependencies.
79+
80+
Removing the `webpack-dev-server` dependency from Encore reduces the number of dependencies from **626** to **295** (**-331**!),
81+
it helps to reduce the size of the `node_modules` directory and the number of possible vulnerabilities.
82+
83+
To use the `webpack-dev-server` again, you need to install it manually:
84+
```shell
85+
npm install webpack-dev-server --save-dev
86+
# or
87+
yarn add webpack-dev-server --dev
88+
# or
89+
pnpm install webpack-dev-server --save-dev
90+
```
91+
7592
## 4.7.0
7693

7794
### Features

bin/encore.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const parseRuntime = require('../lib/config/parse-runtime');
1414
const context = require('../lib/context');
1515
const pc = require('picocolors');
1616
const logger = require('../lib/logger');
17+
const featuresHelper = require("../lib/features");
1718

1819
const runtimeConfig = parseRuntime(
1920
require('yargs-parser')(process.argv.slice(2)),
@@ -56,6 +57,13 @@ if (runtimeConfig.helpRequested) {
5657
}
5758

5859
if (runtimeConfig.useDevServer) {
60+
try {
61+
featuresHelper.ensurePackagesExistAndAreCorrectVersion('webpack-dev-server', 'the webpack Development Server');
62+
} catch (e) {
63+
console.log(e);
64+
process.exit(1);
65+
}
66+
5967
console.log('Running webpack-dev-server ...');
6068
console.log();
6169

index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const configGenerator = require('./lib/config-generator');
2727
const validator = require('./lib/config/validator');
2828
const parseRuntime = require('./lib/config/parse-runtime');
2929
const context = require('./lib/context');
30+
const featuresHelper = require('./lib/features');
3031

3132
let runtimeConfig = context.runtimeConfig;
3233
let webpackConfig = runtimeConfig ? new WebpackConfig(runtimeConfig) : null;
@@ -730,6 +731,8 @@ class Encore {
730731
* @returns {Encore}
731732
*/
732733
configureDevServerOptions(callback) {
734+
featuresHelper.ensurePackagesExistAndAreCorrectVersion('webpack-dev-server');
735+
733736
webpackConfig.configureDevServerOptions(callback);
734737

735738
return this;

lib/features.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ const packageHelper = require('./package-helper');
1414
/**
1515
* An object that holds internal configuration about different
1616
* "loaders"/"plugins" that can be enabled/used.
17+
*
18+
* @type {{[key: string]: {
19+
* method: string,
20+
* packages: Array<{ name: string, version?: string } | Array<{ name: string }>>,
21+
* description: string,
22+
* }}}
1723
*/
1824
const features = {
1925
sass: {
@@ -146,7 +152,14 @@ const features = {
146152
{ name: 'svelte-loader', enforce_version: true }
147153
],
148154
description: 'process Svelte JS files'
149-
}
155+
},
156+
'webpack-dev-server': {
157+
method: 'configureDevServerOptions()',
158+
packages: [
159+
{ name: 'webpack-dev-server' }
160+
],
161+
description: 'run the Webpack development server'
162+
},
150163
};
151164

152165
function getFeatureConfig(featureName) {
@@ -158,12 +171,12 @@ function getFeatureConfig(featureName) {
158171
}
159172

160173
module.exports = {
161-
ensurePackagesExistAndAreCorrectVersion: function(featureName) {
174+
ensurePackagesExistAndAreCorrectVersion: function(featureName, method = null) {
162175
const config = getFeatureConfig(featureName);
163176

164177
packageHelper.ensurePackagesExist(
165178
packageHelper.addPackagesVersionConstraint(config.packages),
166-
config.method
179+
method || config.method
167180
);
168181
},
169182

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
"tapable": "^2.2.1",
4343
"terser-webpack-plugin": "^5.3.0",
4444
"tmp": "^0.2.1",
45-
"webpack-dev-server": "^5.0.4",
4645
"yargs-parser": "^21.0.0"
4746
},
4847
"devDependencies": {
@@ -97,6 +96,7 @@
9796
"vue-loader": "^17.0.0",
9897
"webpack": "^5.72",
9998
"webpack-cli": "^5.1.4",
99+
"webpack-dev-server": "^5.0.4",
100100
"webpack-notifier": "^1.15.0"
101101
},
102102
"peerDependencies": {
@@ -211,6 +211,9 @@
211211
"webpack-cli": {
212212
"optional": false
213213
},
214+
"webpack-dev-server": {
215+
"optional": true
216+
},
214217
"webpack-notifier": {
215218
"optional": true
216219
}

test/bin/encore.js

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ const expect = chai.expect;
1515
const path = require('path');
1616
const testSetup = require('../helpers/setup');
1717
const fs = require('fs-extra');
18-
const exec = require('child_process').exec;
18+
const { exec, execSync, spawn } = require('child_process');
19+
20+
const projectDir = path.resolve(__dirname, '../', '../');
1921

2022
describe('bin/encore.js', function() {
2123
// being functional tests, these can take quite long
@@ -213,4 +215,128 @@ module.exports = Encore.getWebpackConfig();
213215
done();
214216
});
215217
});
218+
219+
it('Run the webpack-dev-server successfully', (done) => {
220+
testSetup.emptyTmpDir();
221+
const testDir = testSetup.createTestAppDir();
222+
223+
fs.writeFileSync(
224+
path.join(testDir, 'package.json'),
225+
`{
226+
"devDependencies": {
227+
"@symfony/webpack-encore": "*"
228+
}
229+
}`
230+
);
231+
232+
fs.writeFileSync(
233+
path.join(testDir, 'webpack.config.js'),
234+
`
235+
const Encore = require('../../index.js');
236+
Encore
237+
.enableSingleRuntimeChunk()
238+
.setOutputPath('build/')
239+
.setPublicPath('/build')
240+
.addEntry('main', './js/no_require')
241+
;
242+
243+
module.exports = Encore.getWebpackConfig();
244+
`
245+
);
246+
247+
const binPath = path.resolve(__dirname, '../', '../', 'bin', 'encore.js');
248+
const abortController = new AbortController();
249+
const node = spawn('node', [binPath, 'dev-server', `--context=${testDir}`], { cwd: testDir, env: { ...process.env, NO_COLOR: 'true' }, signal: abortController.signal });
250+
251+
let stdout = '';
252+
let stderr = '';
253+
254+
node.stdout.on('data', (data) => {
255+
stdout += data.toString();
256+
});
257+
258+
node.stderr.on('data', (data) => {
259+
stderr += data.toString();
260+
});
261+
262+
node.on('error', (error) => {
263+
if (error.name !== 'AbortError') {
264+
throw new Error('Error executing encore', { cause: error });
265+
}
266+
267+
expect(stdout).to.contain('Running webpack-dev-server ...');
268+
expect(stdout).to.contain('Compiled successfully in');
269+
expect(stdout).to.contain('webpack compiled successfully');
270+
271+
expect(stderr).to.contain('[webpack-dev-server] Project is running at:');
272+
expect(stderr).to.contain('[webpack-dev-server] Loopback: http://localhost:8080/, http://127.0.0.1:8080/');
273+
expect(stderr).to.contain('[webpack-dev-server] Content not from webpack is served from');
274+
275+
done();
276+
});
277+
278+
setTimeout(() => {
279+
abortController.abort();
280+
}, 2500);
281+
});
282+
283+
describe('Without webpack-dev-server installed', () => {
284+
const webpackDevServerConstraint = require('../../package.json').devDependencies['webpack-dev-server'] || null;
285+
if (!webpackDevServerConstraint) {
286+
throw new Error('Missing "webpack-dev-server" as dev dependency in package.json.');
287+
}
288+
289+
before(() => {
290+
execSync('yarn remove webpack-dev-server --dev', { cwd: projectDir });
291+
});
292+
293+
after(() => {
294+
// Re-install webpack-dev-server and ensure the project is in a clean state
295+
execSync(`yarn add webpack-dev-server@${webpackDevServerConstraint} --dev`, { cwd: projectDir });
296+
execSync('git checkout yarn.lock', { cwd: projectDir });
297+
execSync('yarn install', { cwd: projectDir });
298+
});
299+
300+
it('Throw an error when trying to use the webpack-dev-server if not installed', done => {
301+
testSetup.emptyTmpDir();
302+
const testDir = testSetup.createTestAppDir();
303+
304+
fs.writeFileSync(
305+
path.join(testDir, 'package.json'),
306+
`{
307+
"devDependencies": {
308+
"@symfony/webpack-encore": "*"
309+
}
310+
}`
311+
);
312+
313+
fs.writeFileSync(
314+
path.join(testDir, 'webpack.config.js'),
315+
`
316+
const Encore = require('../../index.js');
317+
Encore
318+
.enableSingleRuntimeChunk()
319+
.setOutputPath('build/')
320+
.setPublicPath('/build')
321+
.addEntry('main', './js/no_require')
322+
;
323+
324+
module.exports = Encore.getWebpackConfig();
325+
`
326+
);
327+
328+
const binPath = path.resolve(projectDir, 'bin', 'encore.js');
329+
exec(`node ${binPath} dev-server --context=${testDir}`, { cwd: testDir, env: { ...process.env, NO_COLOR: 'true' }, }, (err, stdout, stderr) => {
330+
expect(stdout).to.contain('Install webpack-dev-server to use the webpack Development Server');
331+
expect(stdout).to.contain('npm install webpack-dev-server --save-dev');
332+
expect(stderr).to.equal('');
333+
334+
expect(stdout).not.to.contain('Running webpack-dev-server ...');
335+
expect(stdout).not.to.contain('Compiled successfully in');
336+
expect(stdout).not.to.contain('webpack compiled successfully');
337+
338+
done();
339+
});
340+
});
341+
});
216342
});

0 commit comments

Comments
 (0)