Skip to content

Commit

Permalink
fix: respect output.publicPath for serve
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Dec 30, 2020
1 parent 2966826 commit 8ba8114
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 36 deletions.
Expand Up @@ -4,6 +4,7 @@ exports[`startDevServer should set default port and host if not provided 1`] = `
Object {
"host": "localhost",
"port": 8080,
"publicPath": "/",
}
`;

Expand All @@ -22,6 +23,7 @@ Object {
"hot": true,
"port": 9000,
"progress": true,
"publicPath": "/",
}
`;

Expand All @@ -40,6 +42,7 @@ Object {
"hot": true,
"port": 9000,
"progress": true,
"publicPath": "/",
}
`;

Expand All @@ -56,6 +59,7 @@ Object {
"host": "localhost",
"port": 9000,
"progress": true,
"publicPath": "/",
}
`;

Expand All @@ -64,6 +68,7 @@ Object {
"host": "localhost",
"port": 9001,
"progress": true,
"publicPath": "/",
}
`;

Expand Down
73 changes: 41 additions & 32 deletions packages/serve/src/startDevServer.ts
Expand Up @@ -11,10 +11,7 @@ import { devServerOptionsType } from './types';
* @returns {Object[]} array of resulting servers
*/
export default async function startDevServer(compiler, cliOptions, logger): Promise<object[]> {
let isDevServer4 = false,
devServerVersion,
Server,
findPort;
let devServerVersion, Server, findPort;

try {
// eslint-disable-next-line node/no-extraneous-require
Expand All @@ -28,24 +25,6 @@ export default async function startDevServer(compiler, cliOptions, logger): Prom
process.exit(2);
}

isDevServer4 = devServerVersion.startsWith('4');

const defaultOpts = {};
const devServerOptions = [];
const compilers = compiler.compilers || [compiler];

compilers.forEach((compiler) => {
if (compiler.options.devServer) {
devServerOptions.push(compiler.options.devServer);
}
});

if (devServerOptions.length === 0) {
devServerOptions.push(defaultOpts);
}

const servers = [];
const usedPorts: number[] = [];
const mergeOptions = (cliOptions: devServerOptionsType, devServerOptions: devServerOptionsType): devServerOptionsType => {
// CLI options should take precedence over devServer options,
// and CLI options should have no default values included
Expand All @@ -60,39 +39,69 @@ export default async function startDevServer(compiler, cliOptions, logger): Prom
return options;
};

for (const devServerOpts of devServerOptions) {
const options = mergeOptions(cliOptions, devServerOpts);
const isMultiCompiler = Boolean(compiler.compilers);

let compilersWithDevServerOption;

if (isMultiCompiler) {
compilersWithDevServerOption = compiler.compilers.filter((compiler) => compiler.options.devServer);

// No compilers found with the `devServer` option, let's use first compiler
if (compilersWithDevServerOption.length === 0) {
compilersWithDevServerOption = [compiler.compilers[0]];
}
} else {
compilersWithDevServerOption = [compiler];
}

const isDevServer4 = devServerVersion.startsWith('4');
const usedPorts = [];
const devServersOptions = [];

for (const compilerWithDevServerOption of compilersWithDevServerOption) {
const options = mergeOptions(cliOptions, compilerWithDevServerOption.options.devServer || {});

if (isDevServer4) {
options.port = await findPort(options.port);
options.client = options.client || {};
options.client.port = options.client.port || options.port;
} else {
if (!options.publicPath && compiler.options.output && compiler.options.output.publicPath) {
options.publicPath = compiler.options.output.publicPath === 'auto' ? '/' : compiler.options.output.publicPath;
if (!options.publicPath) {
options.publicPath =
typeof compilerWithDevServerOption.options.output.publicPath === 'undefined' ||
compilerWithDevServerOption.options.output.publicPath === 'auto'
? '/'
: compilerWithDevServerOption.options.output.publicPath;
}

options.host = options.host || 'localhost';
options.port = options.port || 8080;
}

if (options.port) {
const portNum = +options.port;
const portNumber = Number(options.port);

if (usedPorts.find((port) => portNum === port)) {
if (usedPorts.find((port) => portNumber === port)) {
throw new Error(
'Unique ports must be specified for each devServer option in your webpack configuration. Alternatively, run only 1 devServer config using the --config-name flag to specify your desired config.',
);
}

usedPorts.push(portNum);
usedPorts.push(portNumber);
}

devServersOptions.push({ compiler, options });
}

const servers = [];

for (const devServerOptions of devServersOptions) {
const { compiler, options } = devServerOptions;
const server = new Server(compiler, options);

server.listen(options.port, options.host, (err): void => {
if (err) {
throw err;
server.listen(options.port, options.host, (error): void => {
if (error) {
throw error;
}
});

Expand Down
26 changes: 26 additions & 0 deletions test/serve/basic/multi-dev-server-output-public-path.config.js
@@ -0,0 +1,26 @@
const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin');

module.exports = [
{
name: 'one',
mode: 'development',
devtool: false,
entry: './src/other.js',
output: {
filename: 'first-output/[name].js',
},
},
{
name: 'two',
mode: 'development',
devtool: false,
output: {
publicPath: '/my-public-path/',
filename: 'second-output/[name].js',
},
devServer: {
publicPath: '/dev-server-my-public-path/',
},
plugins: [new WebpackCLITestPlugin(['mode', 'output'], false, 'hooks.compilation.taps')],
},
];
32 changes: 32 additions & 0 deletions test/serve/basic/multi-dev-server.config.js
@@ -0,0 +1,32 @@
const getPort = require('get-port');

const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin');

module.exports = async () => [
{
name: 'one',
mode: 'development',
devtool: false,
output: {
filename: 'first-output/[name].js',
},
devServer: {
port: await getPort(),
publicPath: '/one-dev-server-my-public-path/',
},
plugins: [new WebpackCLITestPlugin(['mode', 'output'], false, 'hooks.compilation.taps')],
},
{
name: 'two',
mode: 'development',
devtool: false,
entry: './src/other.js',
output: {
filename: 'second-output/[name].js',
},
devServer: {
port: await getPort(),
publicPath: '/two-dev-server-my-public-path/',
},
},
];
23 changes: 23 additions & 0 deletions test/serve/basic/multi-output-public-path.config.js
@@ -0,0 +1,23 @@
const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin');

module.exports = [
{
name: 'one',
mode: 'development',
devtool: false,
output: {
publicPath: '/my-public-path/',
filename: 'first-output/[name].js',
},
plugins: [new WebpackCLITestPlugin(['mode', 'output'], false, 'hooks.compilation.taps')],
},
{
name: 'two',
mode: 'development',
devtool: false,
entry: './src/other.js',
output: {
filename: 'second-output/[name].js',
},
},
];
25 changes: 25 additions & 0 deletions test/serve/basic/multi.config.js
@@ -0,0 +1,25 @@
const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin');

module.exports = [
{
name: 'one',
mode: 'development',
devtool: false,
output: {
filename: 'first-output/[name].js',
},
devServer: {
publicPath: '/dev-server-my-public-path/',
},
plugins: [new WebpackCLITestPlugin(['mode', 'output'], false, 'hooks.compilation.taps')],
},
{
name: 'two',
mode: 'development',
devtool: false,
entry: './src/other.js',
output: {
filename: 'second-output/[name].js',
},
},
];
29 changes: 29 additions & 0 deletions test/serve/basic/multiple-dev-server.config.js
@@ -0,0 +1,29 @@
const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin');

module.exports = [
{
name: 'one',
mode: 'development',
devtool: false,
output: {
filename: 'first-output/[name].js',
},
devServer: {
publicPath: '/dev-server-my-public-path/',
},
plugins: [new WebpackCLITestPlugin(['mode', 'output'], false)],
},
{
name: 'two',
mode: 'development',
devtool: false,
entry: './src/other.js',
output: {
filename: 'first-output/[name].js',
},
devServer: {
publicPath: '/dev-server-my-public-path/',
},
plugins: [new WebpackCLITestPlugin(['mode', 'output'], false)],
},
];
64 changes: 60 additions & 4 deletions test/serve/basic/serve-basic.test.js
Expand Up @@ -2,7 +2,7 @@

const path = require('path');
const getPort = require('get-port');
const { runServe, isDevServer4 } = require('../../utils/test-utils');
const { runServe, isWebpack5, isDevServer4 } = require('../../utils/test-utils');

const testPath = path.resolve(__dirname);

Expand Down Expand Up @@ -34,6 +34,29 @@ describe('basic serve usage', () => {
expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull();
});

it('should work in multi compiler mode', async () => {
const { stderr, stdout } = await runServe(['serve', '--config', 'multi.config.js', '--port', port], __dirname);

expect(stderr).toBeFalsy();
expect(stdout).toContain('one');
expect(stdout).toContain('first-output/main.js');
expect(stdout).toContain('two');
expect(stdout).toContain('second-output/main.js');
expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull();
});

// TODO need fix in future, edge case
it.skip('should work in multi compiler mode with multiple dev servers', async () => {
const { stderr, stdout } = await runServe(['serve', '--config', 'multi-dev-server.config.js'], __dirname);

expect(stderr).toBeFalsy();
expect(stdout).toContain('one');
expect(stdout).toContain('first-output/main.js');
expect(stdout).toContain('two');
expect(stdout).toContain('second-output/main.js');
expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull();
});

it('should work with the "--mode" option', async () => {
const { stderr, stdout } = await runServe([], __dirname);

Expand Down Expand Up @@ -153,17 +176,34 @@ describe('basic serve usage', () => {
it('should work with the "--output-public-path" option', async () => {
const { stderr, stdout } = await runServe(['serve', '--output-public-path', '/my-public-path/'], __dirname);

if (isWebpack5) {
expect(stderr).toBeFalsy();
expect(stdout).toContain('main.js');
expect(stdout).toContain('/my-public-path/');
expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull();
} else {
expect(stderr).toContain("unknown option '--output-public-path'");
expect(stdout).toBeFalsy();
}
});

it('should respect the "publicPath" option from configuration', async () => {
const { stderr, stdout } = await runServe(['serve', '--config', 'output-public-path.config.js'], __dirname);

expect(stderr).toBeFalsy();
expect(stdout).toContain('main.js');
expect(stdout).toContain('/my-public-path/');
expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull();
});

it('should respect the "publicPath" option from configuration', async () => {
const { stderr, stdout } = await runServe(['serve', '--config', 'output-public-path.config.js'], __dirname);
it('should respect the "publicPath" option from configuration using multi compiler mode', async () => {
const { stderr, stdout } = await runServe(['serve', '--config', 'multi-output-public-path.config.js', '--port', port], __dirname);

expect(stderr).toBeFalsy();
expect(stdout).toContain('main.js');
expect(stdout).toContain('one');
expect(stdout).toContain('first-output/main.js');
expect(stdout).toContain('two');
expect(stdout).toContain('second-output/main.js');
expect(stdout).toContain('/my-public-path/');
expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull();
});
Expand All @@ -185,6 +225,22 @@ describe('basic serve usage', () => {
expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull();
});

it('should respect the "publicPath" option from configuration using multi compiler mode (from the "devServer" options)', async () => {
const { stderr, stdout } = await runServe(
['serve', '--config', 'multi-dev-server-output-public-path.config.js', '--port', port],
__dirname,
);

expect(stderr).toBeFalsy();
expect(stderr).toBeFalsy();
expect(stdout).toContain('one');
expect(stdout).toContain('first-output/main.js');
expect(stdout).toContain('two');
expect(stdout).toContain('second-output/main.js');
expect(stdout).toContain('/dev-server-my-public-path/');
expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull();
});

it('should log and error on unknown flag', async () => {
const { exitCode, stdout, stderr } = await runServe(['--port', port, '--unknown-flag'], testPath);

Expand Down
1 change: 1 addition & 0 deletions test/serve/basic/src/other.js
@@ -0,0 +1 @@
console.log('HERE');

0 comments on commit 8ba8114

Please sign in to comment.