fix(css): when compiling CSS, leave absolute imports alone
Closes #4592
This commit is contained in:
parent
6b00b60488
commit
04b3dee667
@ -24,6 +24,11 @@ function extractUrls(resolver: UrlResolver, baseUrl: string, cssText: string, fo
|
|||||||
string {
|
string {
|
||||||
return StringWrapper.replaceAllMapped(cssText, _cssImportRe, (m) => {
|
return StringWrapper.replaceAllMapped(cssText, _cssImportRe, (m) => {
|
||||||
var url = isPresent(m[1]) ? m[1] : m[2];
|
var url = isPresent(m[1]) ? m[1] : m[2];
|
||||||
|
var schemeMatch = RegExpWrapper.firstMatch(_urlWithSchemaRe, url);
|
||||||
|
if (isPresent(schemeMatch) && schemeMatch[1] != 'package') {
|
||||||
|
// Do not attempt to resolve non-package absolute URLs with URI scheme
|
||||||
|
return m[0];
|
||||||
|
}
|
||||||
foundUrls.push(resolver.resolve(baseUrl, url));
|
foundUrls.push(resolver.resolve(baseUrl, url));
|
||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
@ -50,3 +55,6 @@ var _cssUrlRe = /(url\()([^)]*)(\))/g;
|
|||||||
var _cssImportRe = /@import\s+(?:url\()?\s*(?:(?:['"]([^'"]*))|([^;\)\s]*))[^;]*;?/g;
|
var _cssImportRe = /@import\s+(?:url\()?\s*(?:(?:['"]([^'"]*))|([^;\)\s]*))[^;]*;?/g;
|
||||||
var _quoteRe = /['"]/g;
|
var _quoteRe = /['"]/g;
|
||||||
var _dataUrlRe = /^['"]?data:/g;
|
var _dataUrlRe = /^['"]?data:/g;
|
||||||
|
// TODO: can't use /^[^:/?#.]+:/g due to clang-format bug:
|
||||||
|
// https://github.com/angular/angular/issues/4596
|
||||||
|
var _urlWithSchemaRe = /^['"]?([a-zA-Z\-\+\.]+):/g;
|
||||||
|
@ -28,6 +28,8 @@ List<EmulatedCssRule> emulateRules(Iterable<cssv.TreeNode> rules) {
|
|||||||
return new EmulatedCssStyleRule(node);
|
return new EmulatedCssStyleRule(node);
|
||||||
} else if (node is cssv.MediaDirective) {
|
} else if (node is cssv.MediaDirective) {
|
||||||
return new EmulatedCssMedialRule(node);
|
return new EmulatedCssMedialRule(node);
|
||||||
|
} else if (node is cssv.ImportDirective) {
|
||||||
|
return new EmulatedCssImportRule(node);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.where((r) => r != null)
|
.where((r) => r != null)
|
||||||
@ -100,3 +102,12 @@ class EmulatedMediaList {
|
|||||||
.map((q) => q.span.text).join(' and ');
|
.map((q) => q.span.text).join(' and ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Emulates [CSSImportRule](https://developer.mozilla.org/en-US/docs/Web/API/CSSImportRule)
|
||||||
|
class EmulatedCssImportRule extends EmulatedCssRule {
|
||||||
|
EmulatedCssImportRule(cssv.ImportDirective directive) {
|
||||||
|
this
|
||||||
|
..type = 3
|
||||||
|
..cssText = '@${directive.span.text};';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -154,5 +154,11 @@ export function main() {
|
|||||||
var css = s('x >>> y {}', 'a');
|
var css = s('x >>> y {}', 'a');
|
||||||
expect(css).toEqual('x[a] y[a] {}');
|
expect(css).toEqual('x[a] y[a] {}');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should pass through @import directives', () => {
|
||||||
|
var styleStr = '@import url("https://fonts.googleapis.com/css?family=Roboto");';
|
||||||
|
var css = s(styleStr, 'a');
|
||||||
|
expect(css).toEqual(styleStr);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -88,5 +88,26 @@ export function main() {
|
|||||||
.toEqual(['http://ng.io/print1.css', 'http://ng.io/print2.css']);
|
.toEqual(['http://ng.io/print1.css', 'http://ng.io/print2.css']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should leave absolute non-package @import urls intact', () => {
|
||||||
|
var css = `@import url('http://server.com/some.css');`;
|
||||||
|
var styleWithImports = resolveStyleUrls(urlResolver, 'http://ng.io', css);
|
||||||
|
expect(styleWithImports.style.trim()).toEqual(`@import url('http://server.com/some.css');`);
|
||||||
|
expect(styleWithImports.styleUrls).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve package @import urls', () => {
|
||||||
|
var css = `@import url('package:a/b/some.css');`;
|
||||||
|
var styleWithImports = resolveStyleUrls(new FakeUrlResolver(), 'http://ng.io', css);
|
||||||
|
expect(styleWithImports.style.trim()).toEqual(``);
|
||||||
|
expect(styleWithImports.styleUrls).toEqual(['fake_resolved_url']);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The real thing behaves differently between Dart and JS for package URIs.
|
||||||
|
class FakeUrlResolver extends UrlResolver {
|
||||||
|
constructor() { super(); }
|
||||||
|
|
||||||
|
resolve(baseUrl: string, url: string): string { return 'fake_resolved_url'; }
|
||||||
|
}
|
||||||
|
@ -59,9 +59,8 @@ Uri toAssetScheme(Uri absoluteUri) {
|
|||||||
return absoluteUri;
|
return absoluteUri;
|
||||||
}
|
}
|
||||||
if (absoluteUri.scheme != 'package') {
|
if (absoluteUri.scheme != 'package') {
|
||||||
throw new FormatException(
|
// Pass through URIs with non-package scheme
|
||||||
'Unsupported URI scheme "${absoluteUri.scheme}" encountered.',
|
return absoluteUri;
|
||||||
absoluteUri);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (absoluteUri.pathSegments.length < 2) {
|
if (absoluteUri.pathSegments.length < 2) {
|
||||||
|
@ -88,9 +88,9 @@ void allTests() {
|
|||||||
.toThrowWith(anInstanceOf: FormatException);
|
.toThrowWith(anInstanceOf: FormatException);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw for unsupported schemes', () {
|
it('should pass through unsupported schemes', () {
|
||||||
expect(() => toAssetScheme(Uri.parse('file:///angular2')))
|
var uri = 'http://server.com/style.css';
|
||||||
.toThrowWith(anInstanceOf: FormatException);
|
expect('${toAssetScheme(Uri.parse(uri))}').toEqual(uri);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if passed a null uri', () {
|
it('should throw if passed a null uri', () {
|
||||||
|
@ -16,6 +16,9 @@ const SIMPLE_CSS = '''
|
|||||||
}
|
}
|
||||||
''';
|
''';
|
||||||
|
|
||||||
|
const HTTP_IMPORT = 'https://fonts.googleapis.com/css?family=Roboto';
|
||||||
|
const CSS_WITH_IMPORT = '@import url(${HTTP_IMPORT});';
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
Html5LibDomAdapter.makeCurrent();
|
Html5LibDomAdapter.makeCurrent();
|
||||||
allTests();
|
allTests();
|
||||||
@ -60,6 +63,20 @@ allTests() {
|
|||||||
expect(transform.outputs[1].id.toString())
|
expect(transform.outputs[1].id.toString())
|
||||||
.toEqual('somepackage|lib/style.css.shim.dart');
|
.toEqual('somepackage|lib/style.css.shim.dart');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should compile stylesheets with imports', () async {
|
||||||
|
var cssFile = new Asset.fromString(
|
||||||
|
new AssetId('somepackage', 'lib/style.css'), CSS_WITH_IMPORT);
|
||||||
|
var transform = new FakeTransform()..primaryInput = cssFile;
|
||||||
|
await subject.apply(transform);
|
||||||
|
expect(transform.outputs.length).toBe(2);
|
||||||
|
expect(transform.outputs[0].id.toString())
|
||||||
|
.toEqual('somepackage|lib/style.css.dart');
|
||||||
|
expect(transform.outputs[1].id.toString())
|
||||||
|
.toEqual('somepackage|lib/style.css.shim.dart');
|
||||||
|
expect(await transform.outputs[0].readAsString()).toContain(HTTP_IMPORT);
|
||||||
|
expect(await transform.outputs[1].readAsString()).toContain(HTTP_IMPORT);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
|
Loading…
x
Reference in New Issue
Block a user