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';
|
} from '@nrwl/tao/src/shared/nx';
|
||||||
export { logger } from '@nrwl/tao/src/shared/logger';
|
export { logger } from '@nrwl/tao/src/shared/logger';
|
||||||
export { getPackageManagerCommand } from '@nrwl/tao/src/shared/package-manager';
|
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 { formatFiles } from './src/generators/format-files';
|
||||||
export { generateFiles } from './src/generators/generate-files';
|
export { generateFiles } from './src/generators/generate-files';
|
||||||
@ -30,6 +30,9 @@ export {
|
|||||||
export { toJS } from './src/generators/to-js';
|
export { toJS } from './src/generators/to-js';
|
||||||
export { visitNotIgnoredFiles } from './src/generators/visit-not-ignored-files';
|
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 { readJson, writeJson, updateJson } from './src/utils/json';
|
||||||
export { addDependenciesToPackageJson } from './src/utils/package-json';
|
export { addDependenciesToPackageJson } from './src/utils/package-json';
|
||||||
export { installPackagesTask } from './src/tasks/install-packages-task';
|
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 { logger } from '../shared/logger';
|
||||||
import { eachValueFrom } from 'rxjs-for-await';
|
import { eachValueFrom } from 'rxjs-for-await';
|
||||||
|
|
||||||
export interface RunOptions {
|
export interface Target {
|
||||||
project: string;
|
project: string;
|
||||||
target: string;
|
target: string;
|
||||||
configuration: string;
|
configuration?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RunOptions extends Target {
|
||||||
help: boolean;
|
help: boolean;
|
||||||
runOptions: Options;
|
runOptions: Options;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
"description": "Package a library"
|
"description": "Package a library"
|
||||||
},
|
},
|
||||||
"dev-server": {
|
"dev-server": {
|
||||||
"implementation": "./src/builders/dev-server/dev-server.impl",
|
"implementation": "./src/builders/dev-server/compat",
|
||||||
"schema": "./src/builders/dev-server/schema.json",
|
"schema": "./src/builders/dev-server/schema.json",
|
||||||
"description": "Serve a web application"
|
"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 * as normalizeUtils from '../../utils/normalize';
|
||||||
import { WebBuildBuilderOptions } from '../build/build.impl';
|
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', () => ({
|
jest.mock('@nrwl/devkit');
|
||||||
runWebpackDevServer: () => EMPTY,
|
import { readTargetOptions, ExecutorContext } from '@nrwl/devkit';
|
||||||
}));
|
|
||||||
|
|
||||||
jest.mock('../../utils/devserver.config', () => ({
|
jest.mock('../../utils/devserver.config', () => ({
|
||||||
getDevServerConfig: jest.fn().mockReturnValue({}),
|
getDevServerConfig: jest.fn().mockReturnValue({}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('Web Server Builder', () => {
|
describe('Web Server Builder', () => {
|
||||||
let context: MockBuilderContext;
|
let context: ExecutorContext;
|
||||||
let options: WebDevServerOptions;
|
let options: WebDevServerOptions;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
jest.clearAllMocks();
|
jest.clearAllMocks();
|
||||||
|
|
||||||
context = await getMockContext();
|
context = {
|
||||||
context.getProjectMetadata = jest
|
root: '/root',
|
||||||
.fn()
|
cwd: '/root',
|
||||||
.mockReturnValue({ sourceRoot: '/root/app/src' });
|
projectName: 'proj',
|
||||||
|
targetName: 'serve',
|
||||||
context.getTargetOptions = jest.fn().mockReturnValue({});
|
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 = {
|
options = {
|
||||||
buildTarget: 'app:build',
|
buildTarget: 'proj:build',
|
||||||
port: 4200,
|
port: 4200,
|
||||||
} as WebDevServerOptions;
|
} as WebDevServerOptions;
|
||||||
|
|
||||||
|
(readTargetOptions as any).mockImplementation(() => {});
|
||||||
|
|
||||||
jest
|
jest
|
||||||
.spyOn(normalizeUtils, 'normalizeWebBuildOptions')
|
.spyOn(normalizeUtils, 'normalizeWebBuildOptions')
|
||||||
.mockReturnValue({} as WebBuildBuilderOptions);
|
.mockReturnValue({} as WebBuildBuilderOptions);
|
||||||
@ -39,14 +59,14 @@ describe('Web Server Builder', () => {
|
|||||||
|
|
||||||
it('should pass `baseHref` to build', async () => {
|
it('should pass `baseHref` to build', async () => {
|
||||||
const baseHref = '/my-domain';
|
const baseHref = '/my-domain';
|
||||||
await run({ ...options, baseHref }, context).toPromise();
|
await webDevServerImpl({ ...options, baseHref }, context);
|
||||||
|
|
||||||
expect(normalizeUtils.normalizeWebBuildOptions).toHaveBeenCalledWith(
|
expect(normalizeUtils.normalizeWebBuildOptions).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
baseHref,
|
baseHref,
|
||||||
}),
|
}),
|
||||||
'/root',
|
'/root',
|
||||||
'/root/app/src'
|
'proj/src'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,27 +1,19 @@
|
|||||||
import {
|
import {
|
||||||
BuilderContext,
|
ExecutorContext,
|
||||||
createBuilder,
|
parseTargetString,
|
||||||
targetFromTargetString,
|
readTargetOptions,
|
||||||
} from '@angular-devkit/architect';
|
} from '@nrwl/devkit';
|
||||||
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';
|
|
||||||
import { Configuration } from 'webpack';
|
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;
|
host: string;
|
||||||
port: number;
|
port: number;
|
||||||
publicHost?: string;
|
publicHost?: string;
|
||||||
@ -39,28 +31,21 @@ export interface WebDevServerOptions extends JsonObject {
|
|||||||
baseHref?: string;
|
baseHref?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default createBuilder<WebDevServerOptions>(run);
|
export default function devServerExecutor(
|
||||||
|
|
||||||
export function run(
|
|
||||||
serveOptions: WebDevServerOptions,
|
serveOptions: WebDevServerOptions,
|
||||||
context: BuilderContext
|
context: ExecutorContext
|
||||||
): Observable<DevServerBuildOutput> {
|
) {
|
||||||
return forkJoin(
|
const sourceRoot = context.workspace.projects[context.projectName].sourceRoot;
|
||||||
|
const buildOptions = normalizeWebBuildOptions(
|
||||||
getBuildOptions(serveOptions, context),
|
getBuildOptions(serveOptions, context),
|
||||||
from(getSourceRoot(context))
|
context.root,
|
||||||
).pipe(
|
|
||||||
map(([buildOptions, sourceRoot]) => {
|
|
||||||
buildOptions = normalizeWebBuildOptions(
|
|
||||||
buildOptions,
|
|
||||||
context.workspaceRoot,
|
|
||||||
sourceRoot
|
sourceRoot
|
||||||
);
|
);
|
||||||
let webpackConfig: Configuration = getDevServerConfig(
|
let webpackConfig: Configuration = getDevServerConfig(
|
||||||
context.workspaceRoot,
|
context.root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
buildOptions,
|
buildOptions,
|
||||||
serveOptions,
|
serveOptions
|
||||||
context.logger
|
|
||||||
);
|
);
|
||||||
if (buildOptions.webpackConfig) {
|
if (buildOptions.webpackConfig) {
|
||||||
webpackConfig = require(buildOptions.webpackConfig)(webpackConfig, {
|
webpackConfig = require(buildOptions.webpackConfig)(webpackConfig, {
|
||||||
@ -68,82 +53,45 @@ export function run(
|
|||||||
configuration: serveOptions.buildTarget.split(':')[2],
|
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.logger.info(stripIndents`
|
return eachValueFrom(
|
||||||
**
|
runWebpackDevServer(webpackConfig).pipe(
|
||||||
Web Development Server is listening at ${serverUrl}
|
tap(({ stats }) => {
|
||||||
**
|
console.info(stats.toString(webpackConfig.stats));
|
||||||
`);
|
|
||||||
if (serveOptions.open) {
|
|
||||||
opn(serverUrl, {
|
|
||||||
wait: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return [_, options, serverUrl] as [
|
|
||||||
Configuration,
|
|
||||||
WebBuildBuilderOptions,
|
|
||||||
string
|
|
||||||
];
|
|
||||||
}),
|
}),
|
||||||
switchMap(([config, options, serverUrl]) => {
|
map(({ baseUrl, stats }) => {
|
||||||
return runWebpackDevServer(config, context, {
|
return {
|
||||||
logging: (stats) => {
|
stats,
|
||||||
context.logger.info(stats.toString(config.stats));
|
baseUrl,
|
||||||
},
|
success: !stats.hasErrors(),
|
||||||
webpackFactory: require('webpack'),
|
};
|
||||||
webpackDevServerFactory: require('webpack-dev-server'),
|
|
||||||
}).pipe(
|
|
||||||
map((output) => {
|
|
||||||
output.baseUrl = serverUrl;
|
|
||||||
return output;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBuildOptions(
|
function getBuildOptions(
|
||||||
options: WebDevServerOptions,
|
options: WebDevServerOptions,
|
||||||
context: BuilderContext
|
context: ExecutorContext
|
||||||
): Observable<WebBuildBuilderOptions> {
|
): WebBuildBuilderOptions {
|
||||||
const target = targetFromTargetString(options.buildTarget);
|
const target = parseTargetString(options.buildTarget);
|
||||||
const overrides: Partial<WebBuildBuilderOptions> = {};
|
const overrides: Partial<WebBuildBuilderOptions> = {
|
||||||
|
watch: false,
|
||||||
|
};
|
||||||
if (options.maxWorkers) {
|
if (options.maxWorkers) {
|
||||||
overrides.maxWorkers = options.maxWorkers;
|
overrides.maxWorkers = options.maxWorkers;
|
||||||
}
|
}
|
||||||
if (options.memoryLimit) {
|
if (options.memoryLimit) {
|
||||||
overrides.memoryLimit = options.memoryLimit;
|
overrides.memoryLimit = options.memoryLimit;
|
||||||
}
|
}
|
||||||
return from(
|
|
||||||
Promise.all([
|
|
||||||
context.getTargetOptions(target),
|
|
||||||
context.getBuilderNameForTarget(target),
|
|
||||||
])
|
|
||||||
.then(([targetOptions, builderName]) => {
|
|
||||||
if (options.baseHref) {
|
if (options.baseHref) {
|
||||||
targetOptions.baseHref = options.baseHref;
|
overrides.baseHref = options.baseHref;
|
||||||
}
|
}
|
||||||
return context.validateOptions<WebBuildBuilderOptions & JsonObject>(
|
|
||||||
targetOptions,
|
const buildOptions = readTargetOptions(target, context);
|
||||||
builderName
|
|
||||||
);
|
return {
|
||||||
})
|
...buildOptions,
|
||||||
.then((options) => ({
|
|
||||||
...options,
|
|
||||||
...overrides,
|
...overrides,
|
||||||
}))
|
};
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"title": "Web Dev Server",
|
"title": "Web Dev Server",
|
||||||
"description": "Web Dev Server",
|
"description": "Web Dev Server",
|
||||||
|
"cli": "nx",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"buildTarget": {
|
"buildTarget": {
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { getDevServerConfig } from './devserver.config';
|
import { getDevServerConfig } from './devserver.config';
|
||||||
import { Logger } from '@angular-devkit/core/src/logger';
|
|
||||||
import TsConfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
|
import TsConfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
@ -9,12 +8,14 @@ import { join } from 'path';
|
|||||||
|
|
||||||
jest.mock('tsconfig-paths-webpack-plugin');
|
jest.mock('tsconfig-paths-webpack-plugin');
|
||||||
import ForkTsCheckerWebpackPlugin = require('fork-ts-checker-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', () => {
|
describe('getDevServerConfig', () => {
|
||||||
let buildInput: WebBuildBuilderOptions;
|
let buildInput: WebBuildBuilderOptions;
|
||||||
let serveInput: WebDevServerOptions;
|
let serveInput: WebDevServerOptions;
|
||||||
let mockCompilerOptions: any;
|
let mockCompilerOptions: any;
|
||||||
let logger: Logger;
|
|
||||||
let root: string;
|
let root: string;
|
||||||
let sourceRoot: string;
|
let sourceRoot: string;
|
||||||
|
|
||||||
@ -78,8 +79,7 @@ describe('getDevServerConfig', () => {
|
|||||||
root,
|
root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
buildInput,
|
buildInput,
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.headers['Access-Control-Allow-Origin']).toEqual('*');
|
expect(result.headers['Access-Control-Allow-Origin']).toEqual('*');
|
||||||
@ -90,8 +90,7 @@ describe('getDevServerConfig', () => {
|
|||||||
root,
|
root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
buildInput,
|
buildInput,
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.overlay.warnings).toEqual(false);
|
expect(result.overlay.warnings).toEqual(false);
|
||||||
@ -102,8 +101,7 @@ describe('getDevServerConfig', () => {
|
|||||||
root,
|
root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
buildInput,
|
buildInput,
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.stats).toEqual(false);
|
expect(result.stats).toEqual(false);
|
||||||
@ -114,12 +112,76 @@ describe('getDevServerConfig', () => {
|
|||||||
root,
|
root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
buildInput,
|
buildInput,
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.contentBase).toEqual(false);
|
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', () => {
|
describe('host option', () => {
|
||||||
@ -128,8 +190,7 @@ describe('getDevServerConfig', () => {
|
|||||||
root,
|
root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
buildInput,
|
buildInput,
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.host).toEqual('localhost');
|
expect(result.host).toEqual('localhost');
|
||||||
@ -142,8 +203,7 @@ describe('getDevServerConfig', () => {
|
|||||||
root,
|
root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
buildInput,
|
buildInput,
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.port).toEqual(4200);
|
expect(result.port).toEqual(4200);
|
||||||
@ -156,8 +216,7 @@ describe('getDevServerConfig', () => {
|
|||||||
root,
|
root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
buildInput,
|
buildInput,
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.historyApiFallback).toEqual({
|
expect(result.historyApiFallback).toEqual({
|
||||||
@ -173,8 +232,7 @@ describe('getDevServerConfig', () => {
|
|||||||
root,
|
root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
buildInput,
|
buildInput,
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.compress).toEqual(false);
|
expect(result.compress).toEqual(false);
|
||||||
@ -191,8 +249,7 @@ describe('getDevServerConfig', () => {
|
|||||||
styles: false,
|
styles: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.compress).toEqual(true);
|
expect(result.compress).toEqual(true);
|
||||||
@ -209,8 +266,7 @@ describe('getDevServerConfig', () => {
|
|||||||
styles: true,
|
styles: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.compress).toEqual(true);
|
expect(result.compress).toEqual(true);
|
||||||
@ -227,8 +283,7 @@ describe('getDevServerConfig', () => {
|
|||||||
styles: true,
|
styles: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.compress).toEqual(true);
|
expect(result.compress).toEqual(true);
|
||||||
@ -245,8 +300,7 @@ describe('getDevServerConfig', () => {
|
|||||||
styles: false,
|
styles: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.overlay.errors).toEqual(true);
|
expect(result.overlay.errors).toEqual(true);
|
||||||
@ -263,8 +317,7 @@ describe('getDevServerConfig', () => {
|
|||||||
styles: true,
|
styles: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.overlay.errors).toEqual(false);
|
expect(result.overlay.errors).toEqual(false);
|
||||||
@ -277,8 +330,7 @@ describe('getDevServerConfig', () => {
|
|||||||
root,
|
root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
buildInput,
|
buildInput,
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.liveReload).toEqual(true);
|
expect(result.liveReload).toEqual(true);
|
||||||
@ -289,8 +341,7 @@ describe('getDevServerConfig', () => {
|
|||||||
root,
|
root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
buildInput,
|
buildInput,
|
||||||
{ ...serveInput, liveReload: false },
|
{ ...serveInput, liveReload: false }
|
||||||
logger
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.liveReload).toEqual(false);
|
expect(result.liveReload).toEqual(false);
|
||||||
@ -309,8 +360,7 @@ describe('getDevServerConfig', () => {
|
|||||||
styles: true,
|
styles: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.https).toEqual(false);
|
expect(result.https).toEqual(false);
|
||||||
@ -334,8 +384,7 @@ describe('getDevServerConfig', () => {
|
|||||||
ssl: true,
|
ssl: true,
|
||||||
sslKey: 'ssl.key',
|
sslKey: 'ssl.key',
|
||||||
sslCert: 'ssl.cert',
|
sslCert: 'ssl.cert',
|
||||||
},
|
}
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.https).toEqual({
|
expect(result.https).toEqual({
|
||||||
@ -364,8 +413,7 @@ describe('getDevServerConfig', () => {
|
|||||||
{
|
{
|
||||||
...serveInput,
|
...serveInput,
|
||||||
proxyConfig: 'proxy.conf',
|
proxyConfig: 'proxy.conf',
|
||||||
},
|
}
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.proxy).toEqual({
|
expect(result.proxy).toEqual({
|
||||||
@ -383,8 +431,7 @@ describe('getDevServerConfig', () => {
|
|||||||
{
|
{
|
||||||
...serveInput,
|
...serveInput,
|
||||||
allowedHosts: 'host.com,subdomain.host.com',
|
allowedHosts: 'host.com,subdomain.host.com',
|
||||||
},
|
}
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.allowedHosts).toEqual(['host.com', 'subdomain.host.com']);
|
expect(result.allowedHosts).toEqual(['host.com', 'subdomain.host.com']);
|
||||||
@ -398,8 +445,7 @@ describe('getDevServerConfig', () => {
|
|||||||
{
|
{
|
||||||
...serveInput,
|
...serveInput,
|
||||||
allowedHosts: 'host.com',
|
allowedHosts: 'host.com',
|
||||||
},
|
}
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.allowedHosts).toEqual(['host.com']);
|
expect(result.allowedHosts).toEqual(['host.com']);
|
||||||
@ -410,8 +456,7 @@ describe('getDevServerConfig', () => {
|
|||||||
root,
|
root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
buildInput,
|
buildInput,
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
expect(result.allowedHosts).toEqual([]);
|
expect(result.allowedHosts).toEqual([]);
|
||||||
@ -423,8 +468,7 @@ describe('getDevServerConfig', () => {
|
|||||||
root,
|
root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
{ ...buildInput, maxWorkers: 1 },
|
{ ...buildInput, maxWorkers: 1 },
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
const typeCheckerPlugin = result.plugins.find(
|
const typeCheckerPlugin = result.plugins.find(
|
||||||
@ -440,8 +484,7 @@ describe('getDevServerConfig', () => {
|
|||||||
root,
|
root,
|
||||||
sourceRoot,
|
sourceRoot,
|
||||||
{ ...buildInput, memoryLimit: 1024 },
|
{ ...buildInput, memoryLimit: 1024 },
|
||||||
serveInput,
|
serveInput
|
||||||
logger
|
|
||||||
) as any;
|
) as any;
|
||||||
|
|
||||||
const typeCheckerPlugin = result.plugins.find(
|
const typeCheckerPlugin = result.plugins.find(
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
|
import { logger } from '@nrwl/devkit';
|
||||||
import { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server';
|
import { Configuration as WebpackDevServerConfiguration } from 'webpack-dev-server';
|
||||||
|
|
||||||
|
import * as opn from 'opn';
|
||||||
|
import * as url from 'url';
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { getWebConfig } from './web.config';
|
import { getWebConfig } from './web.config';
|
||||||
import { Configuration } from 'webpack';
|
import { Configuration } from 'webpack';
|
||||||
import { LoggerApi } from '@angular-devkit/core/src/logger';
|
|
||||||
import { WebBuildBuilderOptions } from '../builders/build/build.impl';
|
import { WebBuildBuilderOptions } from '../builders/build/build.impl';
|
||||||
import { WebDevServerOptions } from '../builders/dev-server/dev-server.impl';
|
import { WebDevServerOptions } from '../builders/dev-server/dev-server.impl';
|
||||||
import { buildServePath } from './serve-path';
|
import { buildServePath } from './serve-path';
|
||||||
@ -13,8 +17,7 @@ export function getDevServerConfig(
|
|||||||
root: string,
|
root: string,
|
||||||
sourceRoot: string,
|
sourceRoot: string,
|
||||||
buildOptions: WebBuildBuilderOptions,
|
buildOptions: WebBuildBuilderOptions,
|
||||||
serveOptions: WebDevServerOptions,
|
serveOptions: WebDevServerOptions
|
||||||
logger: LoggerApi
|
|
||||||
) {
|
) {
|
||||||
const webpackConfig: Configuration = getWebConfig(
|
const webpackConfig: Configuration = getWebConfig(
|
||||||
root,
|
root,
|
||||||
@ -53,6 +56,23 @@ function getDevServerPartial(
|
|||||||
disableDotRule: true,
|
disableDotRule: true,
|
||||||
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
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,
|
stats: false,
|
||||||
compress: scriptsOptimization || stylesOptimization,
|
compress: scriptsOptimization || stylesOptimization,
|
||||||
https: options.ssl,
|
https: options.ssl,
|
||||||
|
|||||||
@ -1,7 +1,12 @@
|
|||||||
import * as webpack from 'webpack';
|
import * as webpack from 'webpack';
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { Stats, Configuration } from 'webpack';
|
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 { extname } from 'path';
|
||||||
|
import * as url from 'url';
|
||||||
|
|
||||||
export function runWebpack(config: Configuration): Observable<Stats> {
|
export function runWebpack(config: Configuration): Observable<Stats> {
|
||||||
return new Observable((subscriber) => {
|
return new Observable((subscriber) => {
|
||||||
@ -16,7 +21,11 @@ export function runWebpack(config: Configuration): Observable<Stats> {
|
|||||||
|
|
||||||
if (config.watch) {
|
if (config.watch) {
|
||||||
const watchOptions = config.watchOptions || {};
|
const watchOptions = config.watchOptions || {};
|
||||||
webpackCompiler.watch(watchOptions, callback);
|
const watching = webpackCompiler.watch(watchOptions, callback);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
watching.close(() => {});
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
webpackCompiler.run((err, stats) => {
|
webpackCompiler.run((err, stats) => {
|
||||||
callback(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 {
|
export interface EmittedFile {
|
||||||
id?: string;
|
id?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|||||||
@ -71,6 +71,7 @@ const IGNORE_MATCHES = {
|
|||||||
'karma-jasmine',
|
'karma-jasmine',
|
||||||
'karma-jasmine-html-reporter',
|
'karma-jasmine-html-reporter',
|
||||||
'webpack',
|
'webpack',
|
||||||
|
'webpack-dev-server',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user