feat(@angular/cli): hash loaded media by default (#4878)

Currently dev builds use `--output-hashing=none` by default.

This can cause image resources to be overwritten in dev if they have the same same.

This wasn't much of a problem before when only images in global CSS would be treated as resources, but now component css also does that.

Production builds are unaffected since they already use `--output-hashing=all`.

BREAKING CHANGE: dev builds will hash relative resources from CSS (images, etc).
This commit is contained in:
Filipe Silva 2017-02-22 15:52:11 +00:00 committed by GitHub
parent e4fc2947db
commit 1655e51e7e
5 changed files with 31 additions and 23 deletions

View File

@ -78,7 +78,7 @@ export class NgCliWebpackConfig {
const targetDefaults: any = { const targetDefaults: any = {
development: { development: {
environment: 'dev', environment: 'dev',
outputHashing: 'none', outputHashing: 'media',
sourcemap: true, sourcemap: true,
extractCss: false extractCss: false
}, },

View File

@ -1,7 +1,9 @@
import * as fs from 'fs';
import { ng } from '../../utils/process'; import { ng } from '../../utils/process';
import { import {
expectFileToMatch, expectFileToMatch,
expectFileToExist, expectFileToExist,
expectFileMatchToExist,
writeMultipleFiles writeMultipleFiles
} from '../../utils/fs'; } from '../../utils/fs';
import { expectToFail } from '../../utils/utils'; import { expectToFail } from '../../utils/utils';
@ -33,24 +35,27 @@ export default function () {
.then(() => ng('build', '--extract-css', '--aot')) .then(() => ng('build', '--extract-css', '--aot'))
// Check paths are correctly generated. // Check paths are correctly generated.
.then(() => expectFileToMatch('dist/styles.bundle.css', .then(() => expectFileToMatch('dist/styles.bundle.css',
`url\('\/assets\/global-img-absolute\.svg'\)`)) /url\('\/assets\/global-img-absolute\.svg'\)/))
.then(() => expectFileToMatch('dist/styles.bundle.css', 'url\(global-img-relative.svg\)')) .then(() => expectFileToMatch('dist/styles.bundle.css',
/url\(global-img-relative\.[0-9a-f]{20}\.svg\)/))
.then(() => expectFileToMatch('dist/main.bundle.js', .then(() => expectFileToMatch('dist/main.bundle.js',
`url\(\\'\/assets\/component-img-absolute\.svg\\'\)`)) /url\(\\'\/assets\/component-img-absolute\.svg\\'\)/))
.then(() => expectFileToMatch('dist/main.bundle.js', 'url\(component-img-relative\.svg\)')) .then(() => expectFileToMatch('dist/main.bundle.js',
/url\(component-img-relative\.[0-9a-f]{20}\.svg\)/))
// Check files are correctly created. // Check files are correctly created.
.then(() => expectToFail(() => expectFileToExist('dist/global-img-absolute.svg'))) .then(() => expectToFail(() => expectFileToExist('dist/global-img-absolute.svg')))
.then(() => expectFileToExist('dist/global-img-relative.svg'))
.then(() => expectToFail(() => expectFileToExist('dist/component-img-absolute.svg'))) .then(() => expectToFail(() => expectFileToExist('dist/component-img-absolute.svg')))
.then(() => expectFileToExist('dist/component-img-relative.svg')) .then(() => expectFileMatchToExist('./dist', /global-img-relative\.[0-9a-f]{20}\.svg/))
.then(() => expectFileMatchToExist('./dist', /component-img-relative\.[0-9a-f]{20}\.svg/))
// Also check with base-href and deploy-url flags. // Also check with base-href and deploy-url flags.
.then(() => ng('build', '--base-href=/base/', '--deploy-url=deploy/', .then(() => ng('build', '--base-href=/base/', '--deploy-url=deploy/',
'--extract-css', '--aot')) '--extract-css', '--aot'))
.then(() => expectFileToMatch('dist/styles.bundle.css', .then(() => expectFileToMatch('dist/styles.bundle.css',
`url\('\/base\/deploy\/assets\/global-img-absolute\.svg'\)`)) /url\('\/base\/deploy\/assets\/global-img-absolute\.svg'\)/))
.then(() => expectFileToMatch('dist/styles.bundle.css', 'url\(global-img-relative.svg\)')) .then(() => expectFileToMatch('dist/styles.bundle.css',
/url\(global-img-relative\.[0-9a-f]{20}\.svg\)/))
.then(() => expectFileToMatch('dist/main.bundle.js', .then(() => expectFileToMatch('dist/main.bundle.js',
`url\(\\'\/base\/deploy\/assets\/component-img-absolute\.svg\\'\)`)) /url\(\\'\/base\/deploy\/assets\/component-img-absolute\.svg\\'\)/))
.then(() => expectFileToMatch('dist/main.bundle.js', .then(() => expectFileToMatch('dist/main.bundle.js',
'url\(deploy/component-img-relative\.svg\)')); /url\(deploy\/component-img-relative\.[0-9a-f]{20}\.svg\)/));
} }

View File

@ -16,7 +16,7 @@ export default function () {
.then(() => ng('build', '--deploy-url=deployUrl/', '--extract-css')) .then(() => ng('build', '--deploy-url=deployUrl/', '--extract-css'))
.then(() => expectFileToMatch('dist/index.html', 'deployUrl/main.bundle.js')) .then(() => expectFileToMatch('dist/index.html', 'deployUrl/main.bundle.js'))
// verify --deploy-url isn't applied to extracted css urls // verify --deploy-url isn't applied to extracted css urls
.then(() => expectFileToMatch('dist/styles.bundle.css', 'url\(more.svg\)')) .then(() => expectFileToMatch('dist/styles.bundle.css', /url\(more\.[0-9a-f]{20}\.svg\)/))
// verify option also works in config // verify option also works in config
.then(() => updateJsonFile('.angular-cli.json', configJson => { .then(() => updateJsonFile('.angular-cli.json', configJson => {
const app = configJson['apps'][0]; const app = configJson['apps'][0];
@ -27,5 +27,5 @@ export default function () {
// verify --deploy-url is applied to non-extracted css urls // verify --deploy-url is applied to non-extracted css urls
.then(() => ng('build', '--deploy-url=deployUrl/', '--extract-css=false')) .then(() => ng('build', '--deploy-url=deployUrl/', '--extract-css=false'))
.then(() => expectFileToMatch('dist/styles.bundle.js', .then(() => expectFileToMatch('dist/styles.bundle.js',
'__webpack_require__.p \+ \"more.svg\"')); /__webpack_require__.p \+ \"more\.[0-9a-f]{20}\.svg\"/));
} }

View File

@ -1,16 +1,9 @@
import {stripIndents} from 'common-tags'; import {stripIndents} from 'common-tags';
import * as fs from 'fs';
import {ng} from '../../utils/process'; import {ng} from '../../utils/process';
import { writeMultipleFiles, expectFileToMatch } from '../../utils/fs'; import { writeMultipleFiles, expectFileToMatch, expectFileMatchToExist } from '../../utils/fs';
function verifyMedia(css: RegExp, content: RegExp) { function verifyMedia(css: RegExp, content: RegExp) {
return new Promise((resolve, reject) => { return expectFileMatchToExist('./dist', css)
const [fileName] = fs.readdirSync('./dist').filter(name => name.match(css));
if (!fileName) {
reject(new Error(`File ${fileName} was expected to exist but not found...`));
}
resolve(fileName);
})
.then(fileName => expectFileToMatch(`dist/${fileName}`, content)); .then(fileName => expectFileToMatch(`dist/${fileName}`, content));
} }

View File

@ -120,6 +120,16 @@ export function prependToFile(filePath: string, text: string, options?: any) {
} }
export function expectFileMatchToExist(dir: string, regex: RegExp) {
return new Promise((resolve, reject) => {
const [fileName] = fs.readdirSync(dir).filter(name => name.match(regex));
if (!fileName) {
reject(new Error(`File ${regex} was expected to exist but not found...`));
}
resolve(fileName);
});
}
export function expectFileToExist(fileName: string) { export function expectFileToExist(fileName: string) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fs.exists(fileName, (exist) => { fs.exists(fileName, (exist) => {