Skip to content

Commit 109d568

Browse files
committed
WIP: buildProduction: Add support for --browsers switch.
Used to control autoprefixer and the splitCssIfIeLimitIsReached transform. Removes support for --autoprefix (autoprefixer is now used if and only if --browsers is specified) Currently requires autoprefixer to be available. Keeping an eye on segmentio/myth#94
1 parent 28b22cb commit 109d568

File tree

4 files changed

+104
-17
lines changed

4 files changed

+104
-17
lines changed

bin/buildProduction

+4-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@ var optimist = require('optimist'),
3535
type: 'boolean',
3636
default: false
3737
})
38-
.options('autoprefix', {
39-
describe: 'Automatically prefix all css based on the rules supplied. See https://github.com/ai/autoprefixer#browsers',
38+
.options('browsers', {
39+
alias: 'b',
40+
describe: 'Specify which browsers to support. Configures autoprefixer and controls which IE hacks to apply. Syntax: https://github.com/ai/autoprefixer#browsers',
4041
type: 'string',
4142
demand: false
4243
})
@@ -291,6 +292,7 @@ new AssetGraph({root: rootUrl})
291292
.loadAssets(inputUrls)
292293
.buildProduction({
293294
version: commandLineOptions.version,
295+
browsers: commandLineOptions.browsers,
294296
less: !commandLineOptions.noless,
295297
optimizeImages: commandLineOptions.optimizeimages,
296298
inlineByRelationType: inlineByRelationType,

lib/transforms/autoprefixer.js

+13-10
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,26 @@
55
*/
66

77
module.exports = function (options) {
8-
// See https://github.com/ai/autoprefixer#browsers
9-
var browsers = typeof options === 'string' ? options.split(/,\s*/) : options;
10-
118
return function autoprefixer(assetGraph) {
12-
var autoprefix;
139
var cssAssets = assetGraph.findAssets({type: 'Css'});
1410

1511
if (cssAssets.length > 0) {
16-
try {
17-
autoprefix = require('autoprefixer');
18-
} catch (e) {
19-
assetGraph.emit('warn', new Error('autoprefixerTransform: Found ' + cssAssets.length + ' css asset(s), but no autoprefixer module is available. Please use npm to install autoprefixer in your project so the autoprefixer transform can require it.'));
20-
return;
12+
// See https://github.com/ai/autoprefixer#browsers
13+
var autoprefixer;
14+
if (typeof options === 'function') {
15+
autoprefixer = options;
16+
} else {
17+
var browsers = typeof options === 'string' ? options.split(/,\s*/) : options;
18+
try {
19+
autoprefixer = require('autoprefixer')(browsers);
20+
} catch (e) {
21+
assetGraph.emit('warn', new Error('autoprefixer transform: Found ' + cssAssets.length + ' css asset(s), but no autoprefixer module is available. Please use npm to install autoprefixer in your project so the autoprefixer transform can require it.'));
22+
return;
23+
}
2124
}
2225

2326
cssAssets.forEach(function (cssAsset) {
24-
cssAsset.text = autoprefix(browsers).process(cssAsset.text).css;
27+
cssAsset.text = autoprefixer.process(cssAsset.text).css;
2528
});
2629
}
2730
};

lib/transforms/buildProduction.js

+74-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,64 @@ var urlTools = require('urltools');
33
module.exports = function (options) {
44
options = options || {};
55

6+
var autoprefixer,
7+
// Default to try to support all browsers
8+
browsers = {
9+
has: function () { return true; },
10+
minimum: function () { return 1; }
11+
};
12+
13+
if (options.browsers) {
14+
try {
15+
autoprefixer = require('autoprefixer')(Array.isArray(options.browsers) ? options.browsers : String(options.browsers).split(/,\s*/));
16+
} catch (e) {
17+
throw new Error('The \'browsers\' option requires the autoprefixer module to be available. Please install it in the containing project.');
18+
}
19+
// Waiting for https://github.com/segmentio/myth/issues/94
20+
browsers = autoprefixer.prefixes.browsers;
21+
browsers.requirements.olderThan = {
22+
regexp: /^(\w+) (<=?)\s*([\d\.]+)/,
23+
select: function (browser, sign, version) {
24+
version = parseFloat(version);
25+
var data = this.byName(browser);
26+
return data.versions.filter(function (v) {
27+
return sign === '<' ? v < version : v <= version;
28+
}).map(function(v) {
29+
return "" + data.name + " " + v;
30+
});
31+
}
32+
};
33+
browsers.has = function (browserOrBrowsers) {
34+
try {
35+
return browsers.parse(browserOrBrowsers).some(function (browser) {
36+
return browsers.selected.some(function (selectedBrowser) {
37+
return selectedBrowser === browser || selectedBrowser.indexOf(browser + ' ') === 0;
38+
});
39+
});
40+
} catch (e) {
41+
// Parse error, try to match it as a browser name (any version) so that
42+
// browsers.has('ie') does the expected.
43+
return browsers.selected.some(function (selectedBrowser) {
44+
return selectedBrowser.indexOf(browserOrBrowsers + ' ') === 0;
45+
});
46+
}
47+
};
48+
// Find the minimum version of a given browser that is to be supported.
49+
// For example: browsers.minimum('ie'); // 10
50+
browsers.minimum = function (browser) {
51+
var minimumVersion = null;
52+
browsers.selected.forEach(function (selectedBrowser) {
53+
if (selectedBrowser.indexOf(browser + ' ') === 0) {
54+
var version = parseFloat(selectedBrowser.substr(browser.length + 1));
55+
if (minimumVersion === null || version < minimumVersion) {
56+
minimumVersion = version;
57+
}
58+
}
59+
});
60+
return minimumVersion;
61+
};
62+
}
63+
664
var bundleStrategyName = options.sharedBundles ? 'sharedBundles' : 'oneBundlePerIncludingAsset',
765
inlineByRelationType = options.inlineByRelationType || {HtmlScript: 4096, HtmlStyle: 4096};
866

@@ -72,11 +130,16 @@ module.exports = function (options) {
72130
.convertCssImportsToHtmlStyles()
73131
.removeDuplicateHtmlStyles({type: 'Html', isInitial: true})
74132
.mergeIdenticalAssets({isLoaded: true, isInline: false, type: ['JavaScript', 'Css']})
75-
.if(options.autoprefix)
76-
.autoprefixer(options.autoprefix)
133+
.if(autoprefixer)
134+
.autoprefixer(autoprefixer)
77135
.endif()
78136
.bundleRelations({type: 'HtmlStyle', to: {type: 'Css', isLoaded: true}, node: function (node) {return !node.hasAttribute('nobundle');}}, {strategyName: bundleStrategyName})
79-
.splitCssIfIeLimitIsReached()
137+
.if(browsers.has('ie'))
138+
.splitCssIfIeLimitIsReached({type: 'Css'}, {
139+
// http://blogs.msdn.com/b/ieinternals/archive/2011/05/14/internet-explorer-stylesheet-rule-selector-import-sheet-limit-maximum.aspx
140+
rulesPerStylesheetLimit: browsers.has('ie < 10') ? 4095 : 65534
141+
})
142+
.endif()
80143
.replaceRequireJsWithAlmond()
81144
.bundleRelations({type: 'HtmlScript', to: {type: 'JavaScript', isLoaded: true}, node: function (node) {return !node.hasAttribute('nobundle');}}, {strategyName: bundleStrategyName})
82145
.mergeIdenticalAssets({isLoaded: true, isInline: false, type: ['JavaScript', 'Css']}) // The bundling might produce several identical files, especially the 'oneBundlePerIncludingAsset' strategy.
@@ -106,7 +169,14 @@ module.exports = function (options) {
106169
})
107170
.removeNobundleAttribute({type: ['HtmlScript', 'HtmlStyle']})
108171
.if(inlineByRelationType.CssImage && !options.noInlineCssImagesWithLegacyFallback)
109-
.inlineCssImagesWithLegacyFallback({type: 'Html', isInline: false, isFragment: false}, inlineByRelationType.CssImage)
172+
.inlineCssImagesWithLegacyFallback({
173+
type: 'Html',
174+
isInline: false,
175+
isFragment: false
176+
}, {
177+
sizeThreshold: inlineByRelationType.CssImage,
178+
minimumIeVersion: browsers.minimum('ie')
179+
})
110180
.endif()
111181
.minifyAssets({isLoaded: true})
112182
.if(options.addInitialHtmlExtension)

test/buildProduction.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@ describe('buildProduction', function () {
820820
new AssetGraph({root: __dirname + '/../testdata/buildProduction/issue107/'})
821821
.registerRequireJsConfig({preventPopulationOfJavaScriptAssetsUntilConfigHasBeenFound: true})
822822
.loadAssets('falcon.html')
823-
.buildProduction({version: false})
823+
.buildProduction({version: false, browsers: 'ie >= 8'})
824824
.queue(function (assetGraph) {
825825
expect(assetGraph, 'to contain relations', {type: 'HtmlStyle'}, 3);
826826
expect(assetGraph, 'to contain assets', {type: 'Css'}, 3);
@@ -843,6 +843,18 @@ describe('buildProduction', function () {
843843
.run(done);
844844
});
845845

846+
it('should leave the big stylesheet alone if IE < 10 does not need to be supported', function (done) {
847+
new AssetGraph({root: __dirname + '/../testdata/buildProduction/issue107/'})
848+
.registerRequireJsConfig({preventPopulationOfJavaScriptAssetsUntilConfigHasBeenFound: true})
849+
.loadAssets('falcon.html')
850+
.buildProduction({version: false, browsers: 'ie >= 10'})
851+
.queue(function (assetGraph) {
852+
expect(assetGraph, 'to contain relations', {type: 'HtmlStyle'}, 1);
853+
expect(assetGraph, 'to contain assets', {type: 'Css'}, 1);
854+
})
855+
.run(done);
856+
});
857+
846858
it('should handle a test case where an initial asset has no <html> element and no incoming relations (#109)', function (done) {
847859
new AssetGraph({root: __dirname + '/../testdata/buildProduction/initialAssetWithoutHtmlElement/'})
848860
.registerRequireJsConfig({preventPopulationOfJavaScriptAssetsUntilConfigHasBeenFound: true})

0 commit comments

Comments
 (0)