Skip to content

Commit a38566f

Browse files
alan-agius4hansl
authored andcommittedNov 16, 2018
feat(@angular-devkit/build-angular): add option to allow outputting css resources to a different folder
Added `resourcesOutputPath` option so that CSS assets such as images and fonts are outputted to a subfolder in `dist`
1 parent 5e7f995 commit a38566f

File tree

8 files changed

+137
-1
lines changed

8 files changed

+137
-1
lines changed
 

‎packages/angular_devkit/build_angular/src/angular-cli-files/models/build-options.ts

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface BuildOptions {
2121
optimization: boolean;
2222
environment?: string;
2323
outputPath: string;
24+
resourcesOutputPath?: string;
2425
aot?: boolean;
2526
sourceMap?: boolean;
2627
vendorSourceMap?: boolean;

‎packages/angular_devkit/build_angular/src/angular-cli-files/models/webpack-configs/styles.ts

+2
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export function getStylesConfig(wco: WebpackConfigOptions) {
4747
// Convert absolute resource URLs to account for base-href and deploy-url.
4848
const baseHref = wco.buildOptions.baseHref || '';
4949
const deployUrl = wco.buildOptions.deployUrl || '';
50+
const resourcesOutputPath = buildOptions.resourcesOutputPath || '';
5051

5152
const postcssPluginCreator = function (loader: webpack.loader.LoaderContext) {
5253
return [
@@ -70,6 +71,7 @@ export function getStylesConfig(wco: WebpackConfigOptions) {
7071
PostcssCliResources({
7172
baseHref,
7273
deployUrl,
74+
resourcesOutputPath,
7375
loader,
7476
filename: `[name]${hashFormat.file}.[ext]`,
7577
}),

‎packages/angular_devkit/build_angular/src/angular-cli-files/plugins/postcss-cli-resources.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ function wrapUrl(url: string): string {
2727
export interface PostcssCliResourcesOptions {
2828
baseHref?: string;
2929
deployUrl?: string;
30+
resourcesOutputPath?: string;
3031
filename: string;
3132
loader: webpack.loader.LoaderContext;
3233
}
@@ -47,6 +48,7 @@ export default postcss.plugin('postcss-cli-resources', (options: PostcssCliResou
4748
const {
4849
deployUrl = '',
4950
baseHref = '',
51+
resourcesOutputPath = '',
5052
filename,
5153
loader,
5254
} = options;
@@ -115,12 +117,16 @@ export default postcss.plugin('postcss-cli-resources', (options: PostcssCliResou
115117
return;
116118
}
117119

118-
const outputPath = interpolateName(
120+
let outputPath = interpolateName(
119121
{ resourcePath: result } as webpack.loader.LoaderContext,
120122
filename,
121123
{ content },
122124
);
123125

126+
if (resourcesOutputPath) {
127+
outputPath = path.posix.join(resourcesOutputPath, outputPath);
128+
}
129+
124130
loader.addDependency(result);
125131
loader.emitFile(outputPath, content, undefined);
126132

‎packages/angular_devkit/build_angular/src/browser/schema.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ export interface BrowserBuilderSchema {
5656
*/
5757
outputPath: string;
5858

59+
/**
60+
* Path where style resources will be placed (Relative to outputPath).
61+
*/
62+
resourcesOutputPath: string;
63+
5964
/**
6065
* Build using Ahead of Time compilation.
6166
*/

‎packages/angular_devkit/build_angular/src/browser/schema.json

+5
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@
7171
"type": "string",
7272
"description": "The full path for the new output directory, relative to the current workspace.\n\nBy default, writes output to a folder named dist/ in the current project."
7373
},
74+
"resourcesOutputPath": {
75+
"type": "string",
76+
"description": "The path where style resources will be placed, relative to outputPath.",
77+
"default": ""
78+
},
7479
"aot": {
7580
"type": "boolean",
7681
"description": "Build using Ahead of Time compilation.",

‎packages/angular_devkit/build_angular/src/server/schema.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ export interface BuildWebpackServerSchema {
4949
* Path where output will be placed.
5050
*/
5151
outputPath: string;
52+
/**
53+
* Path where style resources will be placed (Relative to outputPath).
54+
*/
55+
resourcesOutputPath: string;
5256
/**
5357
* Generates a 'stats.json' file which can be analyzed using tools such as:
5458
* #webpack-bundle-analyzer' or https: //webpack.github.io/analyse.

‎packages/angular_devkit/build_angular/src/server/schema.json

+5
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@
4444
"type": "string",
4545
"description": "Path where output will be placed."
4646
},
47+
"resourcesOutputPath": {
48+
"type": "string",
49+
"description": "The path where style resources will be placed, relative to outputPath.",
50+
"default": ""
51+
},
4752
"sourceMap": {
4853
"type": "boolean",
4954
"description": "Output sourcemaps.",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
// tslint:disable:no-big-function
9+
10+
import { runTargetSpec } from '@angular-devkit/architect/testing';
11+
import { normalize, virtualFs } from '@angular-devkit/core';
12+
import { tap } from 'rxjs/operators';
13+
import { browserTargetSpec, host } from '../utils';
14+
15+
16+
describe('Browser Builder styles resources output path', () => {
17+
const stylesBundle = 'dist/styles.css';
18+
const mainBundle = 'dist/main.js';
19+
20+
const imgSvg = `
21+
<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
22+
<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
23+
</svg>
24+
`;
25+
26+
function writeFiles() {
27+
// Use a large image for the relative ref so it cannot be inlined.
28+
host.copyFile('src/spectrum.png', './src/assets/global-img-relative.png');
29+
host.copyFile('src/spectrum.png', './src/assets/component-img-relative.png');
30+
host.writeMultipleFiles({
31+
'src/styles.css': `
32+
h1 { background: url('/assets/global-img-absolute.svg'); }
33+
h2 { background: url('./assets/global-img-relative.png'); }
34+
`,
35+
'src/app/app.component.css': `
36+
h3 { background: url('/assets/component-img-absolute.svg'); }
37+
h4 { background: url('../assets/component-img-relative.png'); }
38+
`,
39+
'src/assets/global-img-absolute.svg': imgSvg,
40+
'src/assets/component-img-absolute.svg': imgSvg,
41+
});
42+
}
43+
44+
beforeEach(done => host.initialize().toPromise().then(done, done.fail));
45+
afterEach(done => host.restore().toPromise().then(done, done.fail));
46+
47+
it(`supports resourcesOutputPath in resource urls`, (done) => {
48+
writeFiles();
49+
// Check base paths are correctly generated.
50+
const overrides = {
51+
aot: true,
52+
extractCss: true,
53+
resourcesOutputPath: 'out-assets',
54+
};
55+
56+
runTargetSpec(host, browserTargetSpec, overrides, undefined, undefined).pipe(
57+
tap(() => {
58+
const styles = virtualFs.fileBufferToString(
59+
host.scopedSync().read(normalize(stylesBundle)),
60+
);
61+
62+
const main = virtualFs.fileBufferToString(host.scopedSync().read(normalize(mainBundle)));
63+
expect(styles).toContain(`url('/assets/global-img-absolute.svg')`);
64+
expect(styles).toContain(`url('out-assets/global-img-relative.png')`);
65+
expect(main).toContain(`url('/assets/component-img-absolute.svg')`);
66+
expect(main).toContain(`url('out-assets/component-img-relative.png')`);
67+
68+
expect(host.scopedSync()
69+
.exists(normalize('dist/assets/global-img-absolute.svg'))).toBe(true);
70+
expect(host.scopedSync()
71+
.exists(normalize('dist/out-assets/global-img-relative.png'))).toBe(true);
72+
expect(host.scopedSync()
73+
.exists(normalize('dist/assets/component-img-absolute.svg'))).toBe(true);
74+
expect(host.scopedSync()
75+
.exists(normalize('dist/out-assets/component-img-relative.png'))).toBe(true);
76+
}),
77+
).toPromise().then(done, done.fail);
78+
});
79+
80+
it(`supports blank resourcesOutputPath`, (done) => {
81+
writeFiles();
82+
83+
// Check base paths are correctly generated.
84+
const overrides = { aot: true, extractCss: true };
85+
runTargetSpec(host, browserTargetSpec, overrides, undefined, undefined).pipe(
86+
tap(() => {
87+
const styles = virtualFs.fileBufferToString(
88+
host.scopedSync().read(normalize(stylesBundle)),
89+
);
90+
91+
const main = virtualFs.fileBufferToString(host.scopedSync().read(normalize(mainBundle)));
92+
expect(styles).toContain(`url('/assets/global-img-absolute.svg')`);
93+
expect(styles).toContain(`url('global-img-relative.png')`);
94+
expect(main).toContain(`url('/assets/component-img-absolute.svg')`);
95+
expect(main).toContain(`url('component-img-relative.png')`);
96+
expect(host.scopedSync().exists(normalize('dist/assets/global-img-absolute.svg')))
97+
.toBe(true);
98+
expect(host.scopedSync().exists(normalize('dist/global-img-relative.png')))
99+
.toBe(true);
100+
expect(host.scopedSync().exists(normalize('dist/assets/component-img-absolute.svg')))
101+
.toBe(true);
102+
expect(host.scopedSync().exists(normalize('dist/component-img-relative.png')))
103+
.toBe(true);
104+
}),
105+
).toPromise().then(done, done.fail);
106+
});
107+
108+
});

0 commit comments

Comments
 (0)
Please sign in to comment.