@angular-devkit/build-angular 0.801.0-beta.3 → 0.801.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +12 -13
- package/plugins/webpack/analytics.d.ts +2 -0
- package/plugins/webpack/analytics.js +25 -3
- package/src/angular-cli-files/models/es5-polyfills.js +1 -0
- package/src/angular-cli-files/models/webpack-configs/common.js +24 -29
- package/src/angular-cli-files/models/webpack-configs/styles.js +22 -16
- package/src/angular-cli-files/models/webpack-configs/utils.d.ts +3 -3
- package/src/angular-cli-files/models/webpack-configs/utils.js +21 -7
- package/src/angular-cli-files/plugins/index-html-webpack-plugin.d.ts +3 -0
- package/src/angular-cli-files/plugins/index-html-webpack-plugin.js +9 -4
- package/src/angular-cli-files/plugins/karma.js +0 -1
- package/src/angular-cli-files/plugins/single-test-transform.d.ts +28 -0
- package/src/angular-cli-files/plugins/single-test-transform.js +40 -0
- package/src/angular-cli-files/utilities/find-tests.d.ts +1 -0
- package/src/angular-cli-files/utilities/find-tests.js +55 -0
- package/src/angular-cli-files/utilities/index-file/augment-index-html.d.ts +3 -0
- package/src/angular-cli-files/utilities/index-file/augment-index-html.js +13 -6
- package/src/angular-cli-files/utilities/index-file/write-index-html.d.ts +3 -1
- package/src/angular-cli-files/utilities/index-file/write-index-html.js +6 -5
- package/src/angular-cli-files/utilities/package-chunk-sort.js +4 -2
- package/src/browser/index.js +20 -14
- package/src/browser/schema.d.ts +16 -0
- package/src/browser/schema.js +9 -0
- package/src/browser/schema.json +17 -1
- package/src/dev-server/index.js +46 -22
- package/src/karma/index.js +30 -2
- package/src/karma/schema.d.ts +13 -0
- package/src/karma/schema.json +14 -1
- package/src/utils/webpack-browser-config.js +1 -1
- package/test/utils.d.ts +2 -1
- package/test/utils.js +7 -2
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* @license
|
|
5
|
+
* Copyright Google Inc. All Rights Reserved.
|
|
6
|
+
*
|
|
7
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
8
|
+
* found in the LICENSE file at https://angular.io/license
|
|
9
|
+
*/
|
|
10
|
+
const fs_1 = require("fs");
|
|
11
|
+
const glob = require("glob");
|
|
12
|
+
const path_1 = require("path");
|
|
13
|
+
const is_directory_1 = require("./is-directory");
|
|
14
|
+
// go through all patterns and find unique list of files
|
|
15
|
+
function findTests(patterns, cwd, workspaceRoot) {
|
|
16
|
+
return patterns.reduce((files, pattern) => {
|
|
17
|
+
const relativePathToMain = cwd.replace(workspaceRoot, '').substr(1); // remove leading slash
|
|
18
|
+
const tests = findMatchingTests(pattern, cwd, relativePathToMain);
|
|
19
|
+
tests.forEach(file => {
|
|
20
|
+
if (!files.includes(file)) {
|
|
21
|
+
files.push(file);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return files;
|
|
25
|
+
}, []);
|
|
26
|
+
}
|
|
27
|
+
exports.findTests = findTests;
|
|
28
|
+
function findMatchingTests(pattern, cwd, relativePathToMain) {
|
|
29
|
+
// normalize pattern, glob lib only accepts forward slashes
|
|
30
|
+
pattern = pattern.replace(/\\/g, '/');
|
|
31
|
+
relativePathToMain = relativePathToMain.replace(/\\/g, '/');
|
|
32
|
+
// remove relativePathToMain to support relative paths from root
|
|
33
|
+
// such paths are easy to get when running scripts via IDEs
|
|
34
|
+
if (pattern.startsWith(relativePathToMain + '/')) {
|
|
35
|
+
pattern = pattern.substr(relativePathToMain.length + 1); // +1 to include slash
|
|
36
|
+
}
|
|
37
|
+
// special logic when pattern does not look like a glob
|
|
38
|
+
if (!glob.hasMagic(pattern)) {
|
|
39
|
+
if (is_directory_1.isDirectory(path_1.join(cwd, pattern))) {
|
|
40
|
+
pattern = `${pattern}/**/*.spec.@(ts|tsx)`;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// see if matching spec file exists
|
|
44
|
+
const extension = path_1.extname(pattern);
|
|
45
|
+
const matchingSpec = `${path_1.basename(pattern, extension)}.spec${extension}`;
|
|
46
|
+
if (fs_1.existsSync(path_1.join(cwd, path_1.dirname(pattern), matchingSpec))) {
|
|
47
|
+
pattern = path_1.join(path_1.dirname(pattern), matchingSpec).replace(/\\/g, '/');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const files = glob.sync(pattern, {
|
|
52
|
+
cwd,
|
|
53
|
+
});
|
|
54
|
+
return files;
|
|
55
|
+
}
|
|
@@ -6,12 +6,15 @@
|
|
|
6
6
|
* found in the LICENSE file at https://angular.io/license
|
|
7
7
|
*/
|
|
8
8
|
export declare type LoadOutputFileFunctionType = (file: string) => Promise<string>;
|
|
9
|
+
export declare type CrossOriginValue = 'none' | 'anonymous' | 'use-credentials';
|
|
9
10
|
export interface AugmentIndexHtmlOptions {
|
|
10
11
|
input: string;
|
|
11
12
|
inputContent: string;
|
|
12
13
|
baseHref?: string;
|
|
13
14
|
deployUrl?: string;
|
|
14
15
|
sri: boolean;
|
|
16
|
+
/** crossorigin attribute setting of elements that provide CORS support */
|
|
17
|
+
crossOrigin?: CrossOriginValue;
|
|
15
18
|
files: FileInfo[];
|
|
16
19
|
/** Files that should be added using 'nomodule'. */
|
|
17
20
|
noModuleFiles?: FileInfo[];
|
|
@@ -17,7 +17,11 @@ const parse5 = require('parse5');
|
|
|
17
17
|
* bundles for differential serving.
|
|
18
18
|
*/
|
|
19
19
|
async function augmentIndexHtml(params) {
|
|
20
|
-
const { loadOutputFile, files, noModuleFiles = [], moduleFiles = [], entrypoints
|
|
20
|
+
const { loadOutputFile, files, noModuleFiles = [], moduleFiles = [], entrypoints } = params;
|
|
21
|
+
let { crossOrigin = 'none' } = params;
|
|
22
|
+
if (params.sri && crossOrigin === 'none') {
|
|
23
|
+
crossOrigin = 'anonymous';
|
|
24
|
+
}
|
|
21
25
|
const stylesheets = new Set();
|
|
22
26
|
const scripts = new Set();
|
|
23
27
|
// Sort files in the order we want to insert them by entrypoint and dedupes duplicates
|
|
@@ -83,6 +87,9 @@ async function augmentIndexHtml(params) {
|
|
|
83
87
|
const attrs = [
|
|
84
88
|
{ name: 'src', value: (params.deployUrl || '') + script },
|
|
85
89
|
];
|
|
90
|
+
if (crossOrigin !== 'none') {
|
|
91
|
+
attrs.push({ name: 'crossorigin', value: crossOrigin });
|
|
92
|
+
}
|
|
86
93
|
// We want to include nomodule or module when a file is not common amongs all
|
|
87
94
|
// such as runtime.js
|
|
88
95
|
const scriptPredictor = ({ file }) => file === script;
|
|
@@ -104,7 +111,7 @@ async function augmentIndexHtml(params) {
|
|
|
104
111
|
attrs.push(..._generateSriAttributes(content));
|
|
105
112
|
}
|
|
106
113
|
const attributes = attrs
|
|
107
|
-
.map(attr => attr.value === null ? attr.name : `${attr.name}="${attr.value}"`)
|
|
114
|
+
.map(attr => (attr.value === null ? attr.name : `${attr.name}="${attr.value}"`))
|
|
108
115
|
.join(' ');
|
|
109
116
|
scriptElements += `<script ${attributes}></script>`;
|
|
110
117
|
}
|
|
@@ -148,6 +155,9 @@ async function augmentIndexHtml(params) {
|
|
|
148
155
|
{ name: 'rel', value: 'stylesheet' },
|
|
149
156
|
{ name: 'href', value: (params.deployUrl || '') + stylesheet },
|
|
150
157
|
];
|
|
158
|
+
if (crossOrigin !== 'none') {
|
|
159
|
+
attrs.push({ name: 'crossorigin', value: crossOrigin });
|
|
160
|
+
}
|
|
151
161
|
if (params.sri) {
|
|
152
162
|
const content = await loadOutputFile(stylesheet);
|
|
153
163
|
attrs.push(..._generateSriAttributes(content));
|
|
@@ -164,8 +174,5 @@ function _generateSriAttributes(content) {
|
|
|
164
174
|
const hash = crypto_1.createHash(algo)
|
|
165
175
|
.update(content, 'utf8')
|
|
166
176
|
.digest('base64');
|
|
167
|
-
return [
|
|
168
|
-
{ name: 'integrity', value: `${algo}-${hash}` },
|
|
169
|
-
{ name: 'crossorigin', value: 'anonymous' },
|
|
170
|
-
];
|
|
177
|
+
return [{ name: 'integrity', value: `${algo}-${hash}` }];
|
|
171
178
|
}
|
|
@@ -9,6 +9,7 @@ import { EmittedFiles } from '@angular-devkit/build-webpack';
|
|
|
9
9
|
import { Path, virtualFs } from '@angular-devkit/core';
|
|
10
10
|
import { Observable } from 'rxjs';
|
|
11
11
|
import { ExtraEntryPoint } from '../../../browser/schema';
|
|
12
|
+
import { CrossOriginValue } from './augment-index-html';
|
|
12
13
|
export interface WriteIndexHtmlOptions {
|
|
13
14
|
host: virtualFs.Host;
|
|
14
15
|
outputPath: Path;
|
|
@@ -22,6 +23,7 @@ export interface WriteIndexHtmlOptions {
|
|
|
22
23
|
scripts?: ExtraEntryPoint[];
|
|
23
24
|
styles?: ExtraEntryPoint[];
|
|
24
25
|
postTransform?: IndexHtmlTransform;
|
|
26
|
+
crossOrigin?: CrossOriginValue;
|
|
25
27
|
}
|
|
26
28
|
export declare type IndexHtmlTransform = (content: string) => Promise<string>;
|
|
27
|
-
export declare function writeIndexHtml({ host, outputPath, indexPath, files, noModuleFiles, moduleFiles, baseHref, deployUrl, sri, scripts, styles, postTransform, }: WriteIndexHtmlOptions): Observable<void>;
|
|
29
|
+
export declare function writeIndexHtml({ host, outputPath, indexPath, files, noModuleFiles, moduleFiles, baseHref, deployUrl, sri, scripts, styles, postTransform, crossOrigin, }: WriteIndexHtmlOptions): Observable<void>;
|
|
@@ -13,24 +13,25 @@ const operators_1 = require("rxjs/operators");
|
|
|
13
13
|
const package_chunk_sort_1 = require("../package-chunk-sort");
|
|
14
14
|
const strip_bom_1 = require("../strip-bom");
|
|
15
15
|
const augment_index_html_1 = require("./augment-index-html");
|
|
16
|
-
function writeIndexHtml({ host, outputPath, indexPath, files = [], noModuleFiles = [], moduleFiles = [], baseHref, deployUrl, sri = false, scripts = [], styles = [], postTransform, }) {
|
|
17
|
-
return host.read(indexPath)
|
|
18
|
-
.pipe(operators_1.map(content => strip_bom_1.stripBom(core_1.virtualFs.fileBufferToString(content))), operators_1.switchMap(content => augment_index_html_1.augmentIndexHtml({
|
|
16
|
+
function writeIndexHtml({ host, outputPath, indexPath, files = [], noModuleFiles = [], moduleFiles = [], baseHref, deployUrl, sri = false, scripts = [], styles = [], postTransform, crossOrigin, }) {
|
|
17
|
+
return host.read(indexPath).pipe(operators_1.map(content => strip_bom_1.stripBom(core_1.virtualFs.fileBufferToString(content))), operators_1.switchMap(content => augment_index_html_1.augmentIndexHtml({
|
|
19
18
|
input: core_1.getSystemPath(outputPath),
|
|
20
19
|
inputContent: content,
|
|
21
20
|
baseHref,
|
|
22
21
|
deployUrl,
|
|
22
|
+
crossOrigin,
|
|
23
23
|
sri,
|
|
24
24
|
entrypoints: package_chunk_sort_1.generateEntryPoints({ scripts, styles }),
|
|
25
25
|
files: filterAndMapBuildFiles(files, ['.js', '.css']),
|
|
26
26
|
noModuleFiles: filterAndMapBuildFiles(noModuleFiles, '.js'),
|
|
27
27
|
moduleFiles: filterAndMapBuildFiles(moduleFiles, '.js'),
|
|
28
28
|
loadOutputFile: async (filePath) => {
|
|
29
|
-
return host
|
|
29
|
+
return host
|
|
30
|
+
.read(core_1.join(outputPath, filePath))
|
|
30
31
|
.pipe(operators_1.map(data => core_1.virtualFs.fileBufferToString(data)))
|
|
31
32
|
.toPromise();
|
|
32
33
|
},
|
|
33
|
-
})), operators_1.switchMap(content => postTransform ? postTransform(content) : rxjs_1.of(content)), operators_1.map(content => core_1.virtualFs.stringToFileBuffer(content)), operators_1.switchMap(content => host.write(core_1.join(outputPath, core_1.basename(indexPath)), content)));
|
|
34
|
+
})), operators_1.switchMap(content => (postTransform ? postTransform(content) : rxjs_1.of(content))), operators_1.map(content => core_1.virtualFs.stringToFileBuffer(content)), operators_1.switchMap(content => host.write(core_1.join(outputPath, core_1.basename(indexPath)), content)));
|
|
34
35
|
}
|
|
35
36
|
exports.writeIndexHtml = writeIndexHtml;
|
|
36
37
|
function filterAndMapBuildFiles(files, extensionFilter) {
|
|
@@ -5,7 +5,7 @@ function generateEntryPoints(appConfig) {
|
|
|
5
5
|
// Add all styles/scripts, except lazy-loaded ones.
|
|
6
6
|
const extraEntryPoints = (extraEntryPoints, defaultBundleName) => {
|
|
7
7
|
const entryPoints = utils_1.normalizeExtraEntryPoints(extraEntryPoints, defaultBundleName)
|
|
8
|
-
.filter(entry =>
|
|
8
|
+
.filter(entry => entry.inject)
|
|
9
9
|
.map(entry => entry.bundleName);
|
|
10
10
|
// remove duplicates
|
|
11
11
|
return [...new Set(entryPoints)];
|
|
@@ -19,7 +19,9 @@ function generateEntryPoints(appConfig) {
|
|
|
19
19
|
...extraEntryPoints(appConfig.scripts, 'scripts'),
|
|
20
20
|
'main',
|
|
21
21
|
];
|
|
22
|
-
const duplicates = [
|
|
22
|
+
const duplicates = [
|
|
23
|
+
...new Set(entryPoints.filter(x => entryPoints.indexOf(x) !== entryPoints.lastIndexOf(x))),
|
|
24
|
+
];
|
|
23
25
|
if (duplicates.length > 0) {
|
|
24
26
|
throw new Error(`Multiple bundles have been named the same: '${duplicates.join(`', '`)}'.`);
|
|
25
27
|
}
|
package/src/browser/index.js
CHANGED
|
@@ -61,13 +61,12 @@ function getAnalyticsConfig(wco, context) {
|
|
|
61
61
|
let category = 'build';
|
|
62
62
|
if (context.builder) {
|
|
63
63
|
// We already vetted that this is a "safe" package, otherwise the analytics would be noop.
|
|
64
|
-
category =
|
|
64
|
+
category =
|
|
65
|
+
context.builder.builderName.split(':')[1] || context.builder.builderName || 'build';
|
|
65
66
|
}
|
|
66
67
|
// The category is the builder name if it's an angular builder.
|
|
67
68
|
return {
|
|
68
|
-
plugins: [
|
|
69
|
-
new analytics_1.NgBuildAnalyticsPlugin(wco.projectRoot, context.analytics, category),
|
|
70
|
-
],
|
|
69
|
+
plugins: [new analytics_1.NgBuildAnalyticsPlugin(wco.projectRoot, context.analytics, category)],
|
|
71
70
|
};
|
|
72
71
|
}
|
|
73
72
|
return {};
|
|
@@ -97,11 +96,11 @@ function buildWebpackBrowser(options, context, transforms = {}) {
|
|
|
97
96
|
const root = core_1.normalize(context.workspaceRoot);
|
|
98
97
|
// Check Angular version.
|
|
99
98
|
version_1.assertCompatibleAngularVersion(context.workspaceRoot, context.logger);
|
|
100
|
-
const loggingFn = transforms.logging
|
|
101
|
-
|| createBrowserLoggingCallback(!!options.verbose, context.logger);
|
|
99
|
+
const loggingFn = transforms.logging || createBrowserLoggingCallback(!!options.verbose, context.logger);
|
|
102
100
|
return rxjs_1.from(initialize(options, context, host, transforms.webpackConfiguration)).pipe(operators_1.switchMap(({ workspace, config: configs }) => {
|
|
103
101
|
const projectName = context.target
|
|
104
|
-
? context.target.project
|
|
102
|
+
? context.target.project
|
|
103
|
+
: workspace.getDefaultProjectName();
|
|
105
104
|
if (!projectName) {
|
|
106
105
|
throw new Error('Must either have a target from the context or a default project.');
|
|
107
106
|
}
|
|
@@ -109,7 +108,8 @@ function buildWebpackBrowser(options, context, transforms = {}) {
|
|
|
109
108
|
const tsConfig = read_tsconfig_1.readTsconfig(options.tsConfig, context.workspaceRoot);
|
|
110
109
|
const target = tsConfig.options.target || typescript_1.ScriptTarget.ES5;
|
|
111
110
|
const buildBrowserFeatures = new utils_1.BuildBrowserFeatures(core_1.getSystemPath(projectRoot), target);
|
|
112
|
-
|
|
111
|
+
const isDifferentialLoadingNeeded = buildBrowserFeatures.isDifferentialLoadingNeeded();
|
|
112
|
+
if (target > typescript_1.ScriptTarget.ES2015 && isDifferentialLoadingNeeded) {
|
|
113
113
|
context.logger.warn(core_1.tags.stripIndent `
|
|
114
114
|
WARNING: Using differential loading with targets ES5 and ES2016 or higher may
|
|
115
115
|
cause problems. Browsers with support for ES2015 will load the ES2016+ scripts
|
|
@@ -133,14 +133,20 @@ function buildWebpackBrowser(options, context, transforms = {}) {
|
|
|
133
133
|
let noModuleFiles;
|
|
134
134
|
let moduleFiles;
|
|
135
135
|
let files;
|
|
136
|
-
const [
|
|
136
|
+
const [firstBuild, secondBuild] = buildEvents;
|
|
137
137
|
if (buildEvents.length === 2) {
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
moduleFiles = firstBuild.emittedFiles || [];
|
|
139
|
+
noModuleFiles = secondBuild.emittedFiles;
|
|
140
|
+
files = moduleFiles.filter(x => x.extension === '.css');
|
|
141
|
+
}
|
|
142
|
+
else if (options.watch && isDifferentialLoadingNeeded) {
|
|
143
|
+
// differential loading is not enabled in watch mode
|
|
144
|
+
// but we still want to use module type tags
|
|
145
|
+
moduleFiles = firstBuild.emittedFiles || [];
|
|
140
146
|
files = moduleFiles.filter(x => x.extension === '.css');
|
|
141
147
|
}
|
|
142
148
|
else {
|
|
143
|
-
const { emittedFiles = [] } =
|
|
149
|
+
const { emittedFiles = [] } = firstBuild;
|
|
144
150
|
files = emittedFiles.filter(x => x.name !== 'polyfills-es5');
|
|
145
151
|
noModuleFiles = emittedFiles.filter(x => x.name === 'polyfills-es5');
|
|
146
152
|
}
|
|
@@ -157,8 +163,8 @@ function buildWebpackBrowser(options, context, transforms = {}) {
|
|
|
157
163
|
scripts: options.scripts,
|
|
158
164
|
styles: options.styles,
|
|
159
165
|
postTransform: transforms.indexHtml,
|
|
160
|
-
|
|
161
|
-
|
|
166
|
+
crossOrigin: options.crossOrigin,
|
|
167
|
+
}).pipe(operators_1.map(() => ({ success: true })), operators_1.catchError(error => rxjs_1.of({ success: false, error: mapErrorToMessage(error) })));
|
|
162
168
|
}
|
|
163
169
|
else {
|
|
164
170
|
return rxjs_1.of({ success });
|
package/src/browser/schema.d.ts
CHANGED
|
@@ -27,6 +27,10 @@ export interface Schema {
|
|
|
27
27
|
* Use a separate bundle containing code used across multiple bundles.
|
|
28
28
|
*/
|
|
29
29
|
commonChunk?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Define the crossorigin attribute setting of elements that provide CORS support.
|
|
32
|
+
*/
|
|
33
|
+
crossOrigin?: CrossOrigin;
|
|
30
34
|
/**
|
|
31
35
|
* Delete the output path before building.
|
|
32
36
|
*/
|
|
@@ -277,6 +281,14 @@ export declare enum Type {
|
|
|
277
281
|
Bundle = "bundle",
|
|
278
282
|
Initial = "initial"
|
|
279
283
|
}
|
|
284
|
+
/**
|
|
285
|
+
* Define the crossorigin attribute setting of elements that provide CORS support.
|
|
286
|
+
*/
|
|
287
|
+
export declare enum CrossOrigin {
|
|
288
|
+
Anonymous = "anonymous",
|
|
289
|
+
None = "none",
|
|
290
|
+
UseCredentials = "use-credentials"
|
|
291
|
+
}
|
|
280
292
|
export interface FileReplacement {
|
|
281
293
|
replace?: string;
|
|
282
294
|
replaceWith?: string;
|
|
@@ -312,6 +324,10 @@ export interface ExtraEntryPointClass {
|
|
|
312
324
|
* The bundle name for this extra entry point.
|
|
313
325
|
*/
|
|
314
326
|
bundleName?: string;
|
|
327
|
+
/**
|
|
328
|
+
* If the bundle will be referenced in the HTML file.
|
|
329
|
+
*/
|
|
330
|
+
inject?: boolean;
|
|
315
331
|
/**
|
|
316
332
|
* The file to include.
|
|
317
333
|
*/
|
package/src/browser/schema.js
CHANGED
|
@@ -14,6 +14,15 @@ var Type;
|
|
|
14
14
|
Type["Bundle"] = "bundle";
|
|
15
15
|
Type["Initial"] = "initial";
|
|
16
16
|
})(Type = exports.Type || (exports.Type = {}));
|
|
17
|
+
/**
|
|
18
|
+
* Define the crossorigin attribute setting of elements that provide CORS support.
|
|
19
|
+
*/
|
|
20
|
+
var CrossOrigin;
|
|
21
|
+
(function (CrossOrigin) {
|
|
22
|
+
CrossOrigin["Anonymous"] = "anonymous";
|
|
23
|
+
CrossOrigin["None"] = "none";
|
|
24
|
+
CrossOrigin["UseCredentials"] = "use-credentials";
|
|
25
|
+
})(CrossOrigin = exports.CrossOrigin || (exports.CrossOrigin = {}));
|
|
17
26
|
/**
|
|
18
27
|
* Define the output filename cache-busting hashing mode.
|
|
19
28
|
*/
|
package/src/browser/schema.json
CHANGED
|
@@ -319,6 +319,16 @@
|
|
|
319
319
|
"webWorkerTsConfig": {
|
|
320
320
|
"type": "string",
|
|
321
321
|
"description": "TypeScript configuration for Web Worker modules."
|
|
322
|
+
},
|
|
323
|
+
"crossOrigin": {
|
|
324
|
+
"type": "string",
|
|
325
|
+
"description": "Define the crossorigin attribute setting of elements that provide CORS support.",
|
|
326
|
+
"default": "none",
|
|
327
|
+
"enum": [
|
|
328
|
+
"none",
|
|
329
|
+
"anonymous",
|
|
330
|
+
"use-credentials"
|
|
331
|
+
]
|
|
322
332
|
}
|
|
323
333
|
},
|
|
324
334
|
"additionalProperties": false,
|
|
@@ -418,7 +428,13 @@
|
|
|
418
428
|
"lazy": {
|
|
419
429
|
"type": "boolean",
|
|
420
430
|
"description": "If the bundle will be lazy loaded.",
|
|
421
|
-
"default": false
|
|
431
|
+
"default": false,
|
|
432
|
+
"x-deprecated": "Use 'inject' option with 'false' value instead."
|
|
433
|
+
},
|
|
434
|
+
"inject": {
|
|
435
|
+
"type": "boolean",
|
|
436
|
+
"description": "If the bundle will be referenced in the HTML file.",
|
|
437
|
+
"default": true
|
|
422
438
|
}
|
|
423
439
|
},
|
|
424
440
|
"additionalProperties": false,
|
package/src/dev-server/index.js
CHANGED
|
@@ -15,11 +15,13 @@ const fs_1 = require("fs");
|
|
|
15
15
|
const path = require("path");
|
|
16
16
|
const rxjs_1 = require("rxjs");
|
|
17
17
|
const operators_1 = require("rxjs/operators");
|
|
18
|
+
const ts = require("typescript");
|
|
18
19
|
const url = require("url");
|
|
19
20
|
const webpack = require("webpack");
|
|
20
21
|
const index_html_webpack_plugin_1 = require("../angular-cli-files/plugins/index-html-webpack-plugin");
|
|
21
22
|
const check_port_1 = require("../angular-cli-files/utilities/check-port");
|
|
22
23
|
const package_chunk_sort_1 = require("../angular-cli-files/utilities/package-chunk-sort");
|
|
24
|
+
const read_tsconfig_1 = require("../angular-cli-files/utilities/read-tsconfig");
|
|
23
25
|
const browser_1 = require("../browser");
|
|
24
26
|
const utils_1 = require("../utils");
|
|
25
27
|
const version_1 = require("../utils/version");
|
|
@@ -53,8 +55,7 @@ function serveWebpackBrowser(options, context, transforms = {}) {
|
|
|
53
55
|
const root = context.workspaceRoot;
|
|
54
56
|
let first = true;
|
|
55
57
|
const host = new node_1.NodeJsSyncHost();
|
|
56
|
-
const loggingFn = transforms.logging
|
|
57
|
-
|| browser_1.createBrowserLoggingCallback(!!options.verbose, context.logger);
|
|
58
|
+
const loggingFn = transforms.logging || browser_1.createBrowserLoggingCallback(!!options.verbose, context.logger);
|
|
58
59
|
async function setup() {
|
|
59
60
|
// Get the browser configuration from the target name.
|
|
60
61
|
const rawBrowserOptions = await context.getTargetOptions(browserTarget);
|
|
@@ -73,13 +74,19 @@ function serveWebpackBrowser(options, context, transforms = {}) {
|
|
|
73
74
|
// No differential loading for dev-server, hence there is just one config
|
|
74
75
|
let webpackConfig = webpackConfigResult.config[0];
|
|
75
76
|
const port = await check_port_1.checkPort(options.port || 0, options.host || 'localhost', 4200);
|
|
76
|
-
const webpackDevServerConfig = webpackConfig.devServer = buildServerConfig(root, options, browserOptions, context.logger);
|
|
77
|
+
const webpackDevServerConfig = (webpackConfig.devServer = buildServerConfig(root, options, browserOptions, context.logger));
|
|
77
78
|
if (transforms.webpackConfiguration) {
|
|
78
79
|
webpackConfig = await transforms.webpackConfiguration(webpackConfig);
|
|
79
80
|
}
|
|
80
|
-
return {
|
|
81
|
+
return {
|
|
82
|
+
browserOptions,
|
|
83
|
+
webpackConfig,
|
|
84
|
+
webpackDevServerConfig,
|
|
85
|
+
port,
|
|
86
|
+
workspace: webpackConfigResult.workspace,
|
|
87
|
+
};
|
|
81
88
|
}
|
|
82
|
-
return rxjs_1.from(setup()).pipe(operators_1.switchMap(({ browserOptions, webpackConfig, webpackDevServerConfig, port }) => {
|
|
89
|
+
return rxjs_1.from(setup()).pipe(operators_1.switchMap(({ browserOptions, webpackConfig, webpackDevServerConfig, port, workspace }) => {
|
|
83
90
|
options.port = port;
|
|
84
91
|
// Resolve public host and client address.
|
|
85
92
|
let clientAddress = url.parse(`${options.ssl ? 'https' : 'http'}://0.0.0.0:0`);
|
|
@@ -112,16 +119,32 @@ function serveWebpackBrowser(options, context, transforms = {}) {
|
|
|
112
119
|
});
|
|
113
120
|
}
|
|
114
121
|
if (browserOptions.index) {
|
|
115
|
-
const { scripts = [], styles = [], index, baseHref } = browserOptions;
|
|
122
|
+
const { scripts = [], styles = [], index, baseHref, tsConfig } = browserOptions;
|
|
123
|
+
const projectName = context.target
|
|
124
|
+
? context.target.project
|
|
125
|
+
: workspace.getDefaultProjectName();
|
|
126
|
+
if (!projectName) {
|
|
127
|
+
throw new Error('Must either have a target from the context or a default project.');
|
|
128
|
+
}
|
|
129
|
+
const projectRoot = core_1.resolve(workspace.root, core_1.normalize(workspace.getProject(projectName).root));
|
|
130
|
+
const { options: compilerOptions } = read_tsconfig_1.readTsconfig(tsConfig, context.workspaceRoot);
|
|
131
|
+
const target = compilerOptions.target || ts.ScriptTarget.ES5;
|
|
132
|
+
const buildBrowserFeatures = new utils_1.BuildBrowserFeatures(core_1.getSystemPath(projectRoot), target);
|
|
133
|
+
const entrypoints = package_chunk_sort_1.generateEntryPoints({ scripts, styles });
|
|
134
|
+
const moduleEntrypoints = buildBrowserFeatures.isDifferentialLoadingNeeded()
|
|
135
|
+
? package_chunk_sort_1.generateEntryPoints({ scripts: [], styles })
|
|
136
|
+
: [];
|
|
116
137
|
webpackConfig.plugins.push(new index_html_webpack_plugin_1.IndexHtmlWebpackPlugin({
|
|
117
138
|
input: path.resolve(root, index),
|
|
118
139
|
output: path.basename(index),
|
|
119
140
|
baseHref,
|
|
120
|
-
|
|
141
|
+
moduleEntrypoints,
|
|
142
|
+
entrypoints,
|
|
121
143
|
deployUrl: browserOptions.deployUrl,
|
|
122
144
|
sri: browserOptions.subresourceIntegrity,
|
|
123
145
|
noModuleEntrypoints: ['polyfills-es5'],
|
|
124
146
|
postTransform: transforms.indexHtml,
|
|
147
|
+
crossOrigin: browserOptions.crossOrigin,
|
|
125
148
|
}));
|
|
126
149
|
}
|
|
127
150
|
const normalizedOptimization = utils_1.normalizeOptimization(browserOptions.optimization);
|
|
@@ -196,17 +219,18 @@ function buildServerConfig(workspaceRoot, serverOptions, browserOptions, logger)
|
|
|
196
219
|
host: serverOptions.host,
|
|
197
220
|
port: serverOptions.port,
|
|
198
221
|
headers: { 'Access-Control-Allow-Origin': '*' },
|
|
199
|
-
historyApiFallback: !!browserOptions.index &&
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
222
|
+
historyApiFallback: !!browserOptions.index &&
|
|
223
|
+
{
|
|
224
|
+
index: `${servePath}/${path.basename(browserOptions.index)}`,
|
|
225
|
+
disableDotRule: true,
|
|
226
|
+
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'],
|
|
227
|
+
rewrites: [
|
|
228
|
+
{
|
|
229
|
+
from: new RegExp(`^(?!${servePath})/.*`),
|
|
230
|
+
to: context => url.format(context.parsedUrl),
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
},
|
|
210
234
|
stats: false,
|
|
211
235
|
compress: styles || scripts,
|
|
212
236
|
watchOptions: {
|
|
@@ -282,10 +306,12 @@ function _addLiveReload(options, browserOptions, webpackConfig, clientAddress, l
|
|
|
282
306
|
}
|
|
283
307
|
// If a custom path is provided the webpack dev server client drops the sockjs-node segment.
|
|
284
308
|
// This adds it back so that behavior is consistent when using a custom URL path
|
|
309
|
+
let sockjsPath = '';
|
|
285
310
|
if (clientAddress.pathname) {
|
|
286
311
|
clientAddress.pathname = path.posix.join(clientAddress.pathname, 'sockjs-node');
|
|
312
|
+
sockjsPath = '&sockPath=' + clientAddress.pathname;
|
|
287
313
|
}
|
|
288
|
-
const entryPoints = [`${webpackDevServerPath}?${url.format(clientAddress)}`];
|
|
314
|
+
const entryPoints = [`${webpackDevServerPath}?${url.format(clientAddress)}${sockjsPath}`];
|
|
289
315
|
if (options.hmr) {
|
|
290
316
|
const webpackHmrLink = 'https://webpack.js.org/guides/hot-module-replacement';
|
|
291
317
|
logger.warn(core_1.tags.oneLine `NOTICE: Hot Module Replacement (HMR) is enabled for the dev server.`);
|
|
@@ -375,9 +401,7 @@ function _findDefaultServePath(baseHref, deployUrl) {
|
|
|
375
401
|
// normalize baseHref
|
|
376
402
|
// for ng serve the starting base is always `/` so a relative
|
|
377
403
|
// and root relative value are identical
|
|
378
|
-
const baseHrefParts = (baseHref || '')
|
|
379
|
-
.split('/')
|
|
380
|
-
.filter(part => part !== '');
|
|
404
|
+
const baseHrefParts = (baseHref || '').split('/').filter(part => part !== '');
|
|
381
405
|
if (baseHref && !baseHref.endsWith('/')) {
|
|
382
406
|
baseHrefParts.pop();
|
|
383
407
|
}
|
package/src/karma/index.js
CHANGED
|
@@ -8,14 +8,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
8
8
|
* found in the LICENSE file at https://angular.io/license
|
|
9
9
|
*/
|
|
10
10
|
const architect_1 = require("@angular-devkit/architect");
|
|
11
|
+
const core_1 = require("@angular-devkit/core");
|
|
11
12
|
const path_1 = require("path");
|
|
12
13
|
const rxjs_1 = require("rxjs");
|
|
13
14
|
const operators_1 = require("rxjs/operators");
|
|
14
15
|
const webpack_configs_1 = require("../angular-cli-files/models/webpack-configs");
|
|
16
|
+
const single_test_transform_1 = require("../angular-cli-files/plugins/single-test-transform");
|
|
17
|
+
const find_tests_1 = require("../angular-cli-files/utilities/find-tests");
|
|
15
18
|
const version_1 = require("../utils/version");
|
|
16
19
|
const webpack_browser_config_1 = require("../utils/webpack-browser-config");
|
|
17
20
|
async function initialize(options, context, webpackConfigurationTransformer) {
|
|
18
|
-
const { config } = await webpack_browser_config_1.generateBrowserWebpackConfigFromContext(
|
|
21
|
+
const { config, workspace } = await webpack_browser_config_1.generateBrowserWebpackConfigFromContext(
|
|
19
22
|
// only two properties are missing:
|
|
20
23
|
// * `outputPath` which is fixed for tests
|
|
21
24
|
// * `budgets` which might be incorrect due to extra dev libs
|
|
@@ -29,6 +32,7 @@ async function initialize(options, context, webpackConfigurationTransformer) {
|
|
|
29
32
|
// tslint:disable-next-line:no-implicit-dependencies
|
|
30
33
|
const karma = await Promise.resolve().then(() => require('karma'));
|
|
31
34
|
return [
|
|
35
|
+
workspace,
|
|
32
36
|
karma,
|
|
33
37
|
webpackConfigurationTransformer ? await webpackConfigurationTransformer(config[0]) : config[0],
|
|
34
38
|
];
|
|
@@ -36,7 +40,7 @@ async function initialize(options, context, webpackConfigurationTransformer) {
|
|
|
36
40
|
function execute(options, context, transforms = {}) {
|
|
37
41
|
// Check Angular version.
|
|
38
42
|
version_1.assertCompatibleAngularVersion(context.workspaceRoot, context.logger);
|
|
39
|
-
return rxjs_1.from(initialize(options, context, transforms.webpackConfiguration)).pipe(operators_1.switchMap(([karma, webpackConfig]) => new rxjs_1.Observable(subscriber => {
|
|
43
|
+
return rxjs_1.from(initialize(options, context, transforms.webpackConfiguration)).pipe(operators_1.switchMap(([workspace, karma, webpackConfig]) => new rxjs_1.Observable(subscriber => {
|
|
40
44
|
const karmaOptions = {};
|
|
41
45
|
if (options.watch !== undefined) {
|
|
42
46
|
karmaOptions.singleRun = !options.watch;
|
|
@@ -54,6 +58,30 @@ function execute(options, context, transforms = {}) {
|
|
|
54
58
|
karmaOptions.reporters = reporters;
|
|
55
59
|
}
|
|
56
60
|
}
|
|
61
|
+
// prepend special webpack loader that will transform test.ts
|
|
62
|
+
if (webpackConfig &&
|
|
63
|
+
webpackConfig.module &&
|
|
64
|
+
options.include &&
|
|
65
|
+
options.include.length > 0) {
|
|
66
|
+
const mainFilePath = core_1.getSystemPath(core_1.join(workspace.root, options.main));
|
|
67
|
+
const files = find_tests_1.findTests(options.include, path_1.dirname(mainFilePath), core_1.getSystemPath(workspace.root));
|
|
68
|
+
// early exit, no reason to start karma
|
|
69
|
+
if (!files.length) {
|
|
70
|
+
subscriber.error(`Specified patterns: "${options.include.join(', ')}" did not match any spec files`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
webpackConfig.module.rules.unshift({
|
|
74
|
+
test: path => path === mainFilePath,
|
|
75
|
+
use: {
|
|
76
|
+
// cannot be a simple path as it differs between environments
|
|
77
|
+
loader: single_test_transform_1.SingleTestTransformLoader,
|
|
78
|
+
options: {
|
|
79
|
+
files,
|
|
80
|
+
logger: context.logger,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
57
85
|
// Assign additional karmaConfig options to the local ngapp config
|
|
58
86
|
karmaOptions.configFile = path_1.resolve(context.workspaceRoot, options.karmaConfig);
|
|
59
87
|
karmaOptions.buildWebpack = {
|
package/src/karma/schema.d.ts
CHANGED
|
@@ -31,6 +31,15 @@ export interface Schema {
|
|
|
31
31
|
* Replace files with other files in the build.
|
|
32
32
|
*/
|
|
33
33
|
fileReplacements?: FileReplacement[];
|
|
34
|
+
/**
|
|
35
|
+
* Globs of files to include, relative to workspace or project root.
|
|
36
|
+
* There are 2 special cases:
|
|
37
|
+
* - when a path to directory is provided, all spec files ending ".spec.@(ts|tsx)" will be
|
|
38
|
+
* included
|
|
39
|
+
* - when a path to a file is provided, and a matching spec file exists it will be included
|
|
40
|
+
* instead
|
|
41
|
+
*/
|
|
42
|
+
include?: string[];
|
|
34
43
|
/**
|
|
35
44
|
* The name of the Karma configuration file.
|
|
36
45
|
*/
|
|
@@ -124,6 +133,10 @@ export interface ExtraEntryPointClass {
|
|
|
124
133
|
* The bundle name for this extra entry point.
|
|
125
134
|
*/
|
|
126
135
|
bundleName?: string;
|
|
136
|
+
/**
|
|
137
|
+
* If the bundle will be referenced in the HTML file.
|
|
138
|
+
*/
|
|
139
|
+
inject?: boolean;
|
|
127
140
|
/**
|
|
128
141
|
* The file to include.
|
|
129
142
|
*/
|
package/src/karma/schema.json
CHANGED
|
@@ -63,6 +63,13 @@
|
|
|
63
63
|
"type": "string",
|
|
64
64
|
"description": "Defines the build environment."
|
|
65
65
|
},
|
|
66
|
+
"include": {
|
|
67
|
+
"type": "array",
|
|
68
|
+
"items": {
|
|
69
|
+
"type": "string"
|
|
70
|
+
},
|
|
71
|
+
"description": "Globs of files to include, relative to workspace or project root. \nThere are 2 special cases:\n - when a path to directory is provided, all spec files ending \".spec.@(ts|tsx)\" will be included\n - when a path to a file is provided, and a matching spec file exists it will be included instead"
|
|
72
|
+
},
|
|
66
73
|
"sourceMap": {
|
|
67
74
|
"description": "Output sourcemaps.",
|
|
68
75
|
"default": true,
|
|
@@ -251,7 +258,13 @@
|
|
|
251
258
|
"lazy": {
|
|
252
259
|
"type": "boolean",
|
|
253
260
|
"description": "If the bundle will be lazy loaded.",
|
|
254
|
-
"default": false
|
|
261
|
+
"default": false,
|
|
262
|
+
"x-deprecated": "Use 'inject' option with 'false' value instead."
|
|
263
|
+
},
|
|
264
|
+
"inject": {
|
|
265
|
+
"type": "boolean",
|
|
266
|
+
"description": "If the bundle will be referenced in the HTML file.",
|
|
267
|
+
"default": true
|
|
255
268
|
}
|
|
256
269
|
},
|
|
257
270
|
"additionalProperties": false,
|
|
@@ -27,7 +27,7 @@ async function generateWebpackConfig(context, workspaceRoot, projectRoot, source
|
|
|
27
27
|
&& buildBrowserFeatures.isDifferentialLoadingNeeded();
|
|
28
28
|
const scriptTargets = [scriptTarget];
|
|
29
29
|
if (differentialLoading) {
|
|
30
|
-
scriptTargets.
|
|
30
|
+
scriptTargets.push(ts.ScriptTarget.ES5);
|
|
31
31
|
}
|
|
32
32
|
// For differential loading, we can have several targets
|
|
33
33
|
return scriptTargets.map(scriptTarget => {
|
package/test/utils.d.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { Architect, BuilderOutput, ScheduleOptions, Target } from '@angular-devkit/architect';
|
|
9
9
|
import { TestProjectHost, TestingArchitectHost } from '@angular-devkit/architect/testing';
|
|
10
10
|
import { Path, experimental, json, virtualFs } from '@angular-devkit/core';
|
|
11
|
+
export declare const ivyEnabled: boolean;
|
|
11
12
|
export declare const workspaceRoot: Path;
|
|
12
13
|
export declare const host: TestProjectHost;
|
|
13
14
|
export declare const outputPath: Path;
|
|
@@ -43,7 +44,7 @@ export declare function createArchitect(workspaceRoot: Path): Promise<{
|
|
|
43
44
|
export declare function browserBuild(architect: Architect, host: virtualFs.Host, target: Target, overrides?: json.JsonObject, scheduleOptions?: ScheduleOptions): Promise<{
|
|
44
45
|
output: BuilderOutput;
|
|
45
46
|
files: {
|
|
46
|
-
[file: string]: string
|
|
47
|
+
[file: string]: Promise<string>;
|
|
47
48
|
};
|
|
48
49
|
}>;
|
|
49
50
|
export declare const lazyModuleFiles: {
|