feat(react): migrate @nrwl/web:dev-server to devkit (#4682)
This commit is contained in:
parent
a941961bd4
commit
34781a11c2
@ -14,7 +14,7 @@ export {
|
||||
} from '@nrwl/tao/src/shared/nx';
|
||||
export { logger } from '@nrwl/tao/src/shared/logger';
|
||||
export { getPackageManagerCommand } from '@nrwl/tao/src/shared/package-manager';
|
||||
export { runExecutor } from '@nrwl/tao/src/commands/run';
|
||||
export { runExecutor, Target } from '@nrwl/tao/src/commands/run';
|
||||
|
||||
export { formatFiles } from './src/generators/format-files';
|
||||
export { generateFiles } from './src/generators/generate-files';
|
||||
@ -30,6 +30,9 @@ export {
|
||||
export { toJS } from './src/generators/to-js';
|
||||
export { visitNotIgnoredFiles } from './src/generators/visit-not-ignored-files';
|
||||
|
||||
export { parseTargetString } from './src/executors/parse-target-string';
|
||||
export { readTargetOptions } from './src/executors/read-target-options';
|
||||
|
||||
export { readJson, writeJson, updateJson } from './src/utils/json';
|
||||
export { addDependenciesToPackageJson } from './src/utils/package-json';
|
||||
export { installPackagesTask } from './src/tasks/install-packages-task';
|
||||
|
||||
11
packages/devkit/src/executors/parse-target-string.ts
Normal file
11
packages/devkit/src/executors/parse-target-string.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export function parseTargetString(targetString: string) {
|
||||
const [project, target, configuration] = targetString.split(':');
|
||||
if (!project || !target) {
|
||||
throw new Error(`Invalid Target String: ${targetString}`);
|
||||
}
|
||||
return {
|
||||
project,
|
||||
target,
|
||||
configuration,
|
||||
};
|
||||
}
|
||||
29
packages/devkit/src/executors/read-target-options.ts
Normal file
29
packages/devkit/src/executors/read-target-options.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Target } from '@nrwl/tao/src/commands/run';
|
||||
import { ExecutorContext, Workspaces } from '@nrwl/tao/src/shared/workspace';
|
||||
import { combineOptionsForExecutor } from '@nrwl/tao/src/shared/params';
|
||||
|
||||
export function readTargetOptions<T = any>(
|
||||
{ project, target, configuration }: Target,
|
||||
context: ExecutorContext
|
||||
): T {
|
||||
const projectConfiguration = context.workspace.projects[project];
|
||||
const targetConfiguration = projectConfiguration.targets[target];
|
||||
|
||||
const ws = new Workspaces(context.root);
|
||||
const [nodeModule, executorName] = targetConfiguration.executor.split(':');
|
||||
const { schema } = ws.readExecutor(nodeModule, executorName);
|
||||
|
||||
const defaultProject = ws.calculateDefaultProjectName(
|
||||
context.cwd,
|
||||
context.workspace
|
||||
);
|
||||
|
||||
return combineOptionsForExecutor(
|
||||
{},
|
||||
configuration,
|
||||
targetConfiguration,
|
||||
schema,
|
||||
defaultProject,
|
||||
ws.relativeCwd(context.cwd)
|
||||
) as T;
|
||||
}
|
||||
@ -19,10 +19,13 @@ import * as chalk from 'chalk';
|
||||
import { logger } from '../shared/logger';
|
||||
import { eachValueFrom } from 'rxjs-for-await';
|
||||
|
||||
export interface RunOptions {
|
||||
export interface Target {
|
||||
project: string;
|
||||
target: string;
|
||||
configuration: string;
|
||||
configuration?: string;
|
||||
}
|
||||
|
||||
export interface RunOptions extends Target {
|
||||
help: boolean;
|
||||
runOptions: Options;
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
"description": "Package a library"
|
||||
},
|
||||
"dev-server": {
|
||||
"implementation": "./src/builders/dev-server/dev-server.impl",
|
||||
"implementation": "./src/builders/dev-server/compat",
|
||||
"schema": "./src/builders/dev-server/schema.json",
|
||||
"description": "Serve a web application"
|
||||
},
|
||||
|
||||
5
packages/web/src/builders/dev-server/compat.ts
Normal file
5
packages/web/src/builders/dev-server/compat.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { convertNxExecutor } from '@nrwl/devkit';
|
||||
|
||||
import devServerExecutor from './dev-server.impl';
|
||||
|
||||
export default convertNxExecutor(devServerExecutor);
|
||||
@ -1,37 +1,57 @@
|
||||
import { MockBuilderContext } from '@nrwl/workspace/testing';
|
||||
import { getMockContext } from '../../utils/testing';
|
||||
import { EMPTY } from 'rxjs';
|
||||
import * as normalizeUtils from '../../utils/normalize';
|
||||
import { WebBuildBuilderOptions } from '../build/build.impl';
|
||||
import { run, WebDevServerOptions } from './dev-server.impl';
|
||||
import webDevServerImpl, { WebDevServerOptions } from './dev-server.impl';
|
||||
|
||||
jest.mock('@angular-devkit/build-webpack', () => ({
|
||||
runWebpackDevServer: () => EMPTY,
|
||||
}));
|
||||
jest.mock('@nrwl/devkit');
|
||||
import { readTargetOptions, ExecutorContext } from '@nrwl/devkit';
|
||||
|
||||
jest.mock('../../utils/devserver.config', () => ({
|
||||
getDevServerConfig: jest.fn().mockReturnValue({}),
|
||||
}));
|
||||
|
||||
describe('Web Server Builder', () => {
|
||||
let context: MockBuilderContext;
|
||||
let context: ExecutorContext;
|
||||
let options: WebDevServerOptions;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
context = await getMockContext();
|
||||
context.getProjectMetadata = jest
|
||||
.fn()
|
||||
.mockReturnValue({ sourceRoot: '/root/app/src' });
|
||||
|
||||
context.getTargetOptions = jest.fn().mockReturnValue({});
|
||||
context = {
|
||||
root: '/root',
|
||||
cwd: '/root',
|
||||
projectName: 'proj',
|
||||
targetName: 'serve',
|
||||
workspace: {
|
||||
version: 2,
|
||||
projects: {
|
||||
proj: {
|
||||
root: 'proj',
|
||||
sourceRoot: 'proj/src',
|
||||
targets: {
|
||||
serve: {
|
||||
executor: '@nrwl/web:dev-server',
|
||||
options: {
|
||||
buildTarget: 'proj:build',
|
||||
},
|
||||
},
|
||||
build: {
|
||||
executor: 'build',
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
isVerbose: false,
|
||||
};
|
||||
|
||||
options = {
|
||||
buildTarget: 'app:build',
|
||||
buildTarget: 'proj:build',
|
||||
port: 4200,
|
||||
} as WebDevServerOptions;
|
||||
|
||||
(readTargetOptions as any).mockImplementation(() => {});
|
||||
|
||||
jest
|
||||
.spyOn(normalizeUtils, 'normalizeWebBuildOptions')
|
||||
.mockReturnValue({} as WebBuildBuilderOptions);
|
||||
@ -39,14 +59,14 @@ describe('Web Server Builder', () => {
|
||||
|
||||
it('should pass `baseHref` to build', async () => {
|
||||
const baseHref = '/my-domain';
|
||||
await run({ ...options, baseHref }, context).toPromise();
|
||||
await webDevServerImpl({ ...options, baseHref }, context);
|
||||
|
||||
expect(normalizeUtils.normalizeWebBuildOptions).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
baseHref,
|
||||
}),
|
||||
'/root',
|
||||
'/root/app/src'
|
||||
'proj/src'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,27 +1,19 @@
|
||||
import {
|
||||
BuilderContext,
|
||||
createBuilder,
|
||||
targetFromTargetString,
|
||||
} from '@angular-devkit/architect';
|
||||
import { JsonObject } from '@angular-devkit/core';
|
||||
|
||||
import { Observable, from, forkJoin } from 'rxjs';
|
||||
import { normalizeWebBuildOptions } from '../../utils/normalize';
|
||||
import { map, switchMap } from 'rxjs/operators';
|
||||
import { WebBuildBuilderOptions } from '../build/build.impl';
|
||||
ExecutorContext,
|
||||
parseTargetString,
|
||||
readTargetOptions,
|
||||
} from '@nrwl/devkit';
|
||||
import { Configuration } from 'webpack';
|
||||
import * as opn from 'opn';
|
||||
import * as url from 'url';
|
||||
import { stripIndents } from '@angular-devkit/core/src/utils/literals';
|
||||
import { getDevServerConfig } from '../../utils/devserver.config';
|
||||
import { buildServePath } from '../../utils/serve-path';
|
||||
import { getSourceRoot } from '../../utils/source-root';
|
||||
import {
|
||||
runWebpackDevServer,
|
||||
DevServerBuildOutput,
|
||||
} from '@angular-devkit/build-webpack';
|
||||
|
||||
export interface WebDevServerOptions extends JsonObject {
|
||||
import { eachValueFrom } from 'rxjs-for-await';
|
||||
import { map, tap } from 'rxjs/operators';
|
||||
import { runWebpackDevServer } from '@nrwl/workspace/src/utilities/run-webpack';
|
||||
|
||||
import { normalizeWebBuildOptions } from '../../utils/normalize';
|
||||
import { WebBuildBuilderOptions } from '../build/build.impl';
|
||||
import { getDevServerConfig } from '../../utils/devserver.config';
|
||||
|
||||
export interface WebDevServerOptions {
|
||||
host: string;
|
||||
port: number;
|
||||
publicHost?: string;
|
||||
@ -39,111 +31,67 @@ export interface WebDevServerOptions extends JsonObject {
|
||||
baseHref?: string;
|
||||
}
|
||||
|
||||
export default createBuilder<WebDevServerOptions>(run);
|
||||
|
||||
export function run(
|
||||
export default function devServerExecutor(
|
||||
serveOptions: WebDevServerOptions,
|
||||
context: BuilderContext
|
||||
): Observable<DevServerBuildOutput> {
|
||||
return forkJoin(
|
||||
context: ExecutorContext
|
||||
) {
|
||||
const sourceRoot = context.workspace.projects[context.projectName].sourceRoot;
|
||||
const buildOptions = normalizeWebBuildOptions(
|
||||
getBuildOptions(serveOptions, context),
|
||||
from(getSourceRoot(context))
|
||||
).pipe(
|
||||
map(([buildOptions, sourceRoot]) => {
|
||||
buildOptions = normalizeWebBuildOptions(
|
||||
buildOptions,
|
||||
context.workspaceRoot,
|
||||
sourceRoot
|
||||
);
|
||||
let webpackConfig: Configuration = getDevServerConfig(
|
||||
context.workspaceRoot,
|
||||
sourceRoot,
|
||||
buildOptions,
|
||||
serveOptions,
|
||||
context.logger
|
||||
);
|
||||
if (buildOptions.webpackConfig) {
|
||||
webpackConfig = require(buildOptions.webpackConfig)(webpackConfig, {
|
||||
buildOptions,
|
||||
configuration: serveOptions.buildTarget.split(':')[2],
|
||||
});
|
||||
}
|
||||
return [webpackConfig, buildOptions] as [
|
||||
Configuration,
|
||||
WebBuildBuilderOptions
|
||||
];
|
||||
}),
|
||||
map(([_, options]) => {
|
||||
const path = buildServePath(options);
|
||||
const serverUrl = url.format({
|
||||
protocol: serveOptions.ssl ? 'https' : 'http',
|
||||
hostname: serveOptions.host,
|
||||
port: serveOptions.port.toString(),
|
||||
pathname: path,
|
||||
});
|
||||
context.root,
|
||||
sourceRoot
|
||||
);
|
||||
let webpackConfig: Configuration = getDevServerConfig(
|
||||
context.root,
|
||||
sourceRoot,
|
||||
buildOptions,
|
||||
serveOptions
|
||||
);
|
||||
if (buildOptions.webpackConfig) {
|
||||
webpackConfig = require(buildOptions.webpackConfig)(webpackConfig, {
|
||||
buildOptions,
|
||||
configuration: serveOptions.buildTarget.split(':')[2],
|
||||
});
|
||||
}
|
||||
|
||||
context.logger.info(stripIndents`
|
||||
**
|
||||
Web Development Server is listening at ${serverUrl}
|
||||
**
|
||||
`);
|
||||
if (serveOptions.open) {
|
||||
opn(serverUrl, {
|
||||
wait: false,
|
||||
});
|
||||
}
|
||||
return [_, options, serverUrl] as [
|
||||
Configuration,
|
||||
WebBuildBuilderOptions,
|
||||
string
|
||||
];
|
||||
}),
|
||||
switchMap(([config, options, serverUrl]) => {
|
||||
return runWebpackDevServer(config, context, {
|
||||
logging: (stats) => {
|
||||
context.logger.info(stats.toString(config.stats));
|
||||
},
|
||||
webpackFactory: require('webpack'),
|
||||
webpackDevServerFactory: require('webpack-dev-server'),
|
||||
}).pipe(
|
||||
map((output) => {
|
||||
output.baseUrl = serverUrl;
|
||||
return output;
|
||||
})
|
||||
);
|
||||
})
|
||||
return eachValueFrom(
|
||||
runWebpackDevServer(webpackConfig).pipe(
|
||||
tap(({ stats }) => {
|
||||
console.info(stats.toString(webpackConfig.stats));
|
||||
}),
|
||||
map(({ baseUrl, stats }) => {
|
||||
return {
|
||||
stats,
|
||||
baseUrl,
|
||||
success: !stats.hasErrors(),
|
||||
};
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function getBuildOptions(
|
||||
options: WebDevServerOptions,
|
||||
context: BuilderContext
|
||||
): Observable<WebBuildBuilderOptions> {
|
||||
const target = targetFromTargetString(options.buildTarget);
|
||||
const overrides: Partial<WebBuildBuilderOptions> = {};
|
||||
context: ExecutorContext
|
||||
): WebBuildBuilderOptions {
|
||||
const target = parseTargetString(options.buildTarget);
|
||||
const overrides: Partial<WebBuildBuilderOptions> = {
|
||||
watch: false,
|
||||
};
|
||||
if (options.maxWorkers) {
|
||||
overrides.maxWorkers = options.maxWorkers;
|
||||
}
|
||||
if (options.memoryLimit) {
|
||||
overrides.memoryLimit = options.memoryLimit;
|
||||
}
|
||||
return from(
|
||||
Promise.all([
|
||||
context.getTargetOptions(target),
|
||||
context.getBuilderNameForTarget(target),
|
||||
])
|
||||
.then(([targetOptions, builderName]) => {
|
||||
if (options.baseHref) {
|
||||
targetOptions.baseHref = options.baseHref;
|
||||
}
|
||||
return context.validateOptions<WebBuildBuilderOptions & JsonObject>(
|
||||
targetOptions,
|
||||
builderName
|
||||
);
|
||||
})
|
||||
.then((options) => ({
|
||||
...options,
|
||||
...overrides,
|
||||
}))
|
||||
);
|
||||
if (options.baseHref) {
|
||||
overrides.baseHref = options.baseHref;
|
||||
}
|
||||
|
||||
const buildOptions = readTargetOptions(target, context);
|
||||
|
||||
return {
|
||||
...buildOptions,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
{
|
||||
"title": "Web Dev Server",
|
||||
"description": "Web Dev Server",
|
||||
"cli": "nx",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"buildTarget": {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { getDevServerConfig } from './devserver.config';
|
||||
import { Logger } from '@angular-devkit/core/src/logger';
|
||||
import TsConfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
|
||||
import * as ts from 'typescript';
|
||||
import * as fs from 'fs';
|
||||
@ -9,12 +8,14 @@ import { join } from 'path';
|
||||
|
||||
jest.mock('tsconfig-paths-webpack-plugin');
|
||||
import ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
import { logger } from '@nrwl/devkit';
|
||||
jest.mock('opn');
|
||||
import * as opn from 'opn';
|
||||
|
||||
describe('getDevServerConfig', () => {
|
||||
let buildInput: WebBuildBuilderOptions;
|
||||
let serveInput: WebDevServerOptions;
|
||||
let mockCompilerOptions: any;
|
||||
let logger: Logger;
|
||||
let root: string;
|
||||
let sourceRoot: string;
|
||||
|
||||
@ -78,8 +79,7 @@ describe('getDevServerConfig', () => {
|
||||
root,
|
||||
sourceRoot,
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.headers['Access-Control-Allow-Origin']).toEqual('*');
|
||||
@ -90,8 +90,7 @@ describe('getDevServerConfig', () => {
|
||||
root,
|
||||
sourceRoot,
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.overlay.warnings).toEqual(false);
|
||||
@ -102,8 +101,7 @@ describe('getDevServerConfig', () => {
|
||||
root,
|
||||
sourceRoot,
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.stats).toEqual(false);
|
||||
@ -114,12 +112,76 @@ describe('getDevServerConfig', () => {
|
||||
root,
|
||||
sourceRoot,
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.contentBase).toEqual(false);
|
||||
});
|
||||
|
||||
describe('onListening', () => {
|
||||
let mockServer;
|
||||
|
||||
beforeEach(() => {
|
||||
mockServer = {
|
||||
options: {
|
||||
https: false,
|
||||
},
|
||||
hostname: 'example.com',
|
||||
listeningApp: {
|
||||
address: () => ({
|
||||
port: 9999,
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
spyOn(logger, 'info');
|
||||
opn.mockImplementation(() => {});
|
||||
});
|
||||
|
||||
it('should print out the URL of the server', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
root,
|
||||
sourceRoot,
|
||||
buildInput,
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
result.onListening(mockServer);
|
||||
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
jasmine.stringMatching(new RegExp('http://example.com:9999/'))
|
||||
);
|
||||
});
|
||||
|
||||
it('should not open the url by default', () => {
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
root,
|
||||
sourceRoot,
|
||||
buildInput,
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
result.onListening(mockServer);
|
||||
|
||||
expect(opn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should open the url if --open is passed', () => {
|
||||
mockServer.options.open = true;
|
||||
const { devServer: result } = getDevServerConfig(
|
||||
root,
|
||||
sourceRoot,
|
||||
buildInput,
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
result.onListening(mockServer);
|
||||
|
||||
expect(opn).toHaveBeenCalledWith('http://example.com:9999/', {
|
||||
wait: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('host option', () => {
|
||||
@ -128,8 +190,7 @@ describe('getDevServerConfig', () => {
|
||||
root,
|
||||
sourceRoot,
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.host).toEqual('localhost');
|
||||
@ -142,8 +203,7 @@ describe('getDevServerConfig', () => {
|
||||
root,
|
||||
sourceRoot,
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.port).toEqual(4200);
|
||||
@ -156,8 +216,7 @@ describe('getDevServerConfig', () => {
|
||||
root,
|
||||
sourceRoot,
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.historyApiFallback).toEqual({
|
||||
@ -173,8 +232,7 @@ describe('getDevServerConfig', () => {
|
||||
root,
|
||||
sourceRoot,
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.compress).toEqual(false);
|
||||
@ -191,8 +249,7 @@ describe('getDevServerConfig', () => {
|
||||
styles: false,
|
||||
},
|
||||
},
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.compress).toEqual(true);
|
||||
@ -209,8 +266,7 @@ describe('getDevServerConfig', () => {
|
||||
styles: true,
|
||||
},
|
||||
},
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.compress).toEqual(true);
|
||||
@ -227,8 +283,7 @@ describe('getDevServerConfig', () => {
|
||||
styles: true,
|
||||
},
|
||||
},
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.compress).toEqual(true);
|
||||
@ -245,8 +300,7 @@ describe('getDevServerConfig', () => {
|
||||
styles: false,
|
||||
},
|
||||
},
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.overlay.errors).toEqual(true);
|
||||
@ -263,8 +317,7 @@ describe('getDevServerConfig', () => {
|
||||
styles: true,
|
||||
},
|
||||
},
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.overlay.errors).toEqual(false);
|
||||
@ -277,8 +330,7 @@ describe('getDevServerConfig', () => {
|
||||
root,
|
||||
sourceRoot,
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
);
|
||||
|
||||
expect(result.liveReload).toEqual(true);
|
||||
@ -289,8 +341,7 @@ describe('getDevServerConfig', () => {
|
||||
root,
|
||||
sourceRoot,
|
||||
buildInput,
|
||||
{ ...serveInput, liveReload: false },
|
||||
logger
|
||||
{ ...serveInput, liveReload: false }
|
||||
);
|
||||
|
||||
expect(result.liveReload).toEqual(false);
|
||||
@ -309,8 +360,7 @@ describe('getDevServerConfig', () => {
|
||||
styles: true,
|
||||
},
|
||||
},
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.https).toEqual(false);
|
||||
@ -334,8 +384,7 @@ describe('getDevServerConfig', () => {
|
||||
ssl: true,
|
||||
sslKey: 'ssl.key',
|
||||
sslCert: 'ssl.cert',
|
||||
},
|
||||
logger
|
||||
}
|
||||
) as any;
|
||||
|
||||
expect(result.https).toEqual({
|
||||
@ -364,8 +413,7 @@ describe('getDevServerConfig', () => {
|
||||
{
|
||||
...serveInput,
|
||||
proxyConfig: 'proxy.conf',
|
||||
},
|
||||
logger
|
||||
}
|
||||
) as any;
|
||||
|
||||
expect(result.proxy).toEqual({
|
||||
@ -383,8 +431,7 @@ describe('getDevServerConfig', () => {
|
||||
{
|
||||
...serveInput,
|
||||
allowedHosts: 'host.com,subdomain.host.com',
|
||||
},
|
||||
logger
|
||||
}
|
||||
) as any;
|
||||
|
||||
expect(result.allowedHosts).toEqual(['host.com', 'subdomain.host.com']);
|
||||
@ -398,8 +445,7 @@ describe('getDevServerConfig', () => {
|
||||
{
|
||||
...serveInput,
|
||||
allowedHosts: 'host.com',
|
||||
},
|
||||
logger
|
||||
}
|
||||
) as any;
|
||||
|
||||
expect(result.allowedHosts).toEqual(['host.com']);
|
||||
@ -410,8 +456,7 @@ describe('getDevServerConfig', () => {
|
||||
root,
|
||||
sourceRoot,
|
||||
buildInput,
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
expect(result.allowedHosts).toEqual([]);
|
||||
@ -423,8 +468,7 @@ describe('getDevServerConfig', () => {
|
||||
root,
|
||||
sourceRoot,
|
||||
{ ...buildInput, maxWorkers: 1 },
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
const typeCheckerPlugin = result.plugins.find(
|
||||
@ -440,8 +484,7 @@ describe('getDevServerConfig', () => {
|
||||
root,
|
||||
sourceRoot,
|
||||
{ ...buildInput, memoryLimit: 1024 },
|
||||
serveInput,
|
||||
logger
|
||||
serveInput
|
||||
) as any;
|
||||
|
||||
const typeCheckerPlugin = result.plugins.find(
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
import { logger } from '@nrwl/devkit';
|
||||
import { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server';
|
||||
|
||||
import * as opn from 'opn';
|
||||
import * as url from 'url';
|
||||
import { readFileSync } from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { getWebConfig } from './web.config';
|
||||
import { Configuration } from 'webpack';
|
||||
import { LoggerApi } from '@angular-devkit/core/src/logger';
|
||||
import { WebBuildBuilderOptions } from '../builders/build/build.impl';
|
||||
import { WebDevServerOptions } from '../builders/dev-server/dev-server.impl';
|
||||
import { buildServePath } from './serve-path';
|
||||
@ -13,8 +17,7 @@ export function getDevServerConfig(
|
||||
root: string,
|
||||
sourceRoot: string,
|
||||
buildOptions: WebBuildBuilderOptions,
|
||||
serveOptions: WebDevServerOptions,
|
||||
logger: LoggerApi
|
||||
serveOptions: WebDevServerOptions
|
||||
) {
|
||||
const webpackConfig: Configuration = getWebConfig(
|
||||
root,
|
||||
@ -53,6 +56,23 @@ function getDevServerPartial(
|
||||
disableDotRule: true,
|
||||
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
||||
},
|
||||
noInfo: true,
|
||||
onListening: function (server: any) {
|
||||
// Depend on the info in the server for this function because the user might adjust the webpack config
|
||||
const serverUrl = url.format({
|
||||
protocol: server.options.https ? 'https' : 'http',
|
||||
hostname: server.hostname,
|
||||
port: server.listeningApp.address().port,
|
||||
pathname: buildServePath(buildOptions),
|
||||
});
|
||||
|
||||
logger.info(`NX Web Development Server is listening at ${serverUrl}`);
|
||||
if (server.options.open) {
|
||||
opn(serverUrl, {
|
||||
wait: false,
|
||||
});
|
||||
}
|
||||
},
|
||||
stats: false,
|
||||
compress: scriptsOptimization || stylesOptimization,
|
||||
https: options.ssl,
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
import * as webpack from 'webpack';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Stats, Configuration } from 'webpack';
|
||||
import * as WebpackDevServer from 'webpack-dev-server';
|
||||
import { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server';
|
||||
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { extname } from 'path';
|
||||
import * as url from 'url';
|
||||
|
||||
export function runWebpack(config: Configuration): Observable<Stats> {
|
||||
return new Observable((subscriber) => {
|
||||
@ -16,7 +21,11 @@ export function runWebpack(config: Configuration): Observable<Stats> {
|
||||
|
||||
if (config.watch) {
|
||||
const watchOptions = config.watchOptions || {};
|
||||
webpackCompiler.watch(watchOptions, callback);
|
||||
const watching = webpackCompiler.watch(watchOptions, callback);
|
||||
|
||||
return () => {
|
||||
watching.close(() => {});
|
||||
};
|
||||
} else {
|
||||
webpackCompiler.run((err, stats) => {
|
||||
callback(err, stats);
|
||||
@ -26,6 +35,59 @@ export function runWebpack(config: Configuration): Observable<Stats> {
|
||||
});
|
||||
}
|
||||
|
||||
export function runWebpackDevServer(
|
||||
config: Configuration
|
||||
): Observable<{ stats: Stats; baseUrl: string }> {
|
||||
return new Observable((subscriber) => {
|
||||
const webpackCompiler = webpack(config);
|
||||
|
||||
let baseUrl: string;
|
||||
|
||||
webpackCompiler.hooks.done.tap('build-webpack', (stats) => {
|
||||
subscriber.next({ stats, baseUrl });
|
||||
});
|
||||
|
||||
const devServerConfig = config.devServer || {};
|
||||
|
||||
const originalOnListen = devServerConfig.onListening;
|
||||
|
||||
devServerConfig.onListening = function (server: any) {
|
||||
originalOnListen(server);
|
||||
|
||||
const devServerOptions: WebpackDevServerConfiguration = server.options;
|
||||
baseUrl = url.format({
|
||||
protocol: devServerOptions.https ? 'https' : 'http',
|
||||
hostname: server.hostname,
|
||||
port: server.listeningApp.address().port,
|
||||
pathname: devServerOptions.publicPath,
|
||||
});
|
||||
};
|
||||
|
||||
const webpackServer = new WebpackDevServer(
|
||||
webpackCompiler,
|
||||
devServerConfig
|
||||
);
|
||||
|
||||
try {
|
||||
const server = webpackServer.listen(
|
||||
devServerConfig.port ?? 8080,
|
||||
devServerConfig.host ?? 'localhost',
|
||||
function (err) {
|
||||
if (err) {
|
||||
subscriber.error(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
server.close();
|
||||
};
|
||||
} catch (e) {
|
||||
throw new Error('Could not start start dev server');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export interface EmittedFile {
|
||||
id?: string;
|
||||
name?: string;
|
||||
|
||||
@ -71,6 +71,7 @@ const IGNORE_MATCHES = {
|
||||
'karma-jasmine',
|
||||
'karma-jasmine-html-reporter',
|
||||
'webpack',
|
||||
'webpack-dev-server',
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user