Skip to content

Commit

Permalink
Switch up how we insantiate Bottleneck so rate limit can be configured
Browse files Browse the repository at this point in the history
  • Loading branch information
ostowe committed Jun 13, 2018
1 parent c209c0d commit 510b17a
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 22 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,4 @@ The plugin takes a single object as its only parameter. The following properties
| `preserve` | `boolean` | Whether or not to remove selectors from primary CSS document once they've been marked as critical. This should prevent duplication of selectors across critical and non-critical CSS. | `true` |
| `minify` | `boolean` | Minify output CSS? | `true` |
| `ignoreSelectors` | `array` | Array of selectors to globally exclude from critical CSS output | `[]` |
| `fsWriteRate` | `number` | Minimum amount of time between file writes, in millisenconds. This is intended to prevent overlapping file writes for codebases with numerous calls to `@critical` | `250` |
40 changes: 27 additions & 13 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,22 @@ var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
* @param {bool} dryRun Do a dry run?
* @param {string} filePath Path to write file to.
* @param {Object} result PostCSS root object.
* @param {number} fsWriteRate minimum time between file writes
* @return {Promise} Resolves with writeCriticalFile or doDryRun function call.
*/
let dryRunOrWriteFile = (() => {
var _ref = (0, _asyncToGenerator3.default)(function* (dryRun, filePath, result) {
var _ref = (0, _asyncToGenerator3.default)(function* (dryRun, filePath, result, fsWriteRate) {
const css = result.css;

if (dryRun) {
doDryRun(css);
} else {
yield limiter.schedule(writeCriticalFile, filePath, css);
// Write file at a maximum of once ever 250ms
yield createOrGetLimiter(fsWriteRate).schedule(writeCriticalFile, filePath, css);
}
});

return function dryRunOrWriteFile(_x, _x2, _x3) {
return function dryRunOrWriteFile(_x, _x2, _x3, _x4) {
return _ref.apply(this, arguments);
};
})();
Expand All @@ -49,7 +51,6 @@ let dryRunOrWriteFile = (() => {
*/
let writeCriticalFile = (() => {
var _ref2 = (0, _asyncToGenerator3.default)(function* (filePath, css) {
console.log(`writeCriticalFile: ${filePath}`);
try {
yield _fsExtra2.default.outputFile(filePath, css, { flag: append ? 'a' : 'w' });
append = true;
Expand All @@ -61,7 +62,7 @@ let writeCriticalFile = (() => {
}
});

return function writeCriticalFile(_x4, _x5) {
return function writeCriticalFile(_x5, _x6) {
return _ref2.apply(this, arguments);
};
})();
Expand Down Expand Up @@ -106,14 +107,24 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
let append = false;

/**
* Rate limiter for file writes
* Instance of Bottleneck for rate-limiting file writes
*
* @param {number} fsWriteRate minimum time between file writes
*/


const limiter = new _bottleneck2.default({
maxConcurrent: 1,
minTime: 250
});
let limiter;

function createOrGetLimiter(fsWriteRate) {
if (!limiter) {
limiter = new _bottleneck2.default({
maxConcurrent: 1,
minTime: fsWriteRate
});
}

return limiter;
}

/**
* Clean the original root node passed to the plugin, removing custom atrules,
Expand Down Expand Up @@ -187,9 +198,11 @@ function doDryRun(css) {
preserve: true,
minify: true,
dryRun: false,
ignoreSelectors: [':export', ':import']
ignoreSelectors: [],
fsWriteRate: 250
}, filteredOptions);
append = false;

return (() => {
var _ref3 = (0, _asyncToGenerator3.default)(function* (css) {
const dryRun = args.dryRun,
Expand All @@ -212,6 +225,7 @@ function doDryRun(css) {
const criticalCSS = _postcss2.default.root();
const filePath = _path2.default.join(outputPath, outputFile);
criticalOutput[outputFile].each(function (rule) {
// Check if we should remove this selector from output
const shouldIgnore = rule.selector && args.ignoreSelectors.some(function (ignore) {
return rule.selector.includes(ignore);
});
Expand All @@ -222,7 +236,7 @@ function doDryRun(css) {
return criticalCSS.append(rule.clone());
});
result = yield (0, _postcss2.default)(minify ? [_cssnano2.default] : []).process(criticalCSS, { from: undefined });
yield dryRunOrWriteFile(dryRun, filePath, result);
yield dryRunOrWriteFile(dryRun, filePath, result, args.fsWriteRate);
clean(css, preserve);
}
} catch (err) {
Expand All @@ -243,7 +257,7 @@ function doDryRun(css) {
return result;
});

return function (_x6) {
return function (_x7) {
return _ref3.apply(this, arguments);
};
})();
Expand Down
35 changes: 26 additions & 9 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,26 @@ import { getCriticalRules } from './getCriticalRules'
let append = false

/**
* Rate limiter for file writes
* Instance of Bottleneck for rate-limiting file writes
*/
const limiter = new Bottleneck({
maxConcurrent: 1,
minTime: 250
})
let limiter

/**
* Create or get a "singleton" instance of Bottleneck
*
* @param {number} fsWriteRate minimum time between file writes
* @return {Bottleneck} instnance of Bottleneck
*/
function createOrGetLimiter (fsWriteRate: number): Bottleneck {
if (!limiter) {
limiter = new Bottleneck({
maxConcurrent: 1,
minTime: fsWriteRate
})
}

return limiter
}

/**
* Clean the original root node passed to the plugin, removing custom atrules,
Expand Down Expand Up @@ -95,19 +109,21 @@ function doDryRun (css: string) {
* @param {bool} dryRun Do a dry run?
* @param {string} filePath Path to write file to.
* @param {Object} result PostCSS root object.
* @param {number} fsWriteRate minimum time between file writes
* @return {Promise} Resolves with writeCriticalFile or doDryRun function call.
*/
async function dryRunOrWriteFile (
dryRun: boolean,
filePath: string,
result: Object
result: Object,
fsWriteRate: number
): Promise<any> {
const { css } = result
if (dryRun) {
doDryRun(css)
} else {
// Write file at a maximum of once ever 250ms
await limiter.schedule(
await createOrGetLimiter(fsWriteRate).schedule(
writeCriticalFile,
filePath,
css
Expand Down Expand Up @@ -136,7 +152,6 @@ function hasNoOtherChildNodes (
* @param {string} css CSS to write to file.
*/
async function writeCriticalFile (filePath: string, css: string): Promise<any> {
console.log(`writeCriticalFile: ${filePath}`)
try {
await fs.outputFile(
filePath,
Expand Down Expand Up @@ -173,9 +188,11 @@ function buildCritical (options: Object = {}): Function {
minify: true,
dryRun: false,
ignoreSelectors: [],
fsWriteRate: 250,
...filteredOptions
}
append = false

return async (css: Object): Object => {
const { dryRun, preserve, minify, outputPath, outputDest } = args
const criticalOutput = getCriticalRules(css, outputDest)
Expand All @@ -196,7 +213,7 @@ function buildCritical (options: Object = {}): Function {
})
result = await postcss(minify ? [cssnano] : [])
.process(criticalCSS, { from: undefined })
await dryRunOrWriteFile(dryRun, filePath, result)
await dryRunOrWriteFile(dryRun, filePath, result, args.fsWriteRate)
clean(css, preserve)
}

Expand Down

0 comments on commit 510b17a

Please sign in to comment.