diff --git a/src/exports.js b/src/exports.js index b86f3d3..02e5e1b 100644 --- a/src/exports.js +++ b/src/exports.js @@ -9,6 +9,7 @@ import { addRenderedClassNames, getRenderedClassNames, getBufferedStyles, + setSizeLimit, } from './inject'; import {defaultSelectorHandlers} from './generate'; @@ -188,5 +189,6 @@ export default function makeExports( flushToStyleTag, injectAndGetClassName, defaultSelectorHandlers, + setSizeLimit, }; } diff --git a/src/inject.js b/src/inject.js index c5c85d9..2d9a9ed 100644 --- a/src/inject.js +++ b/src/inject.js @@ -5,6 +5,8 @@ import OrderedElements from './ordered-elements'; import {generateCSS} from './generate'; import {hashObject, hashString} from './util'; +export const UNLIMITED_SIZE = -1; + /* :: import type { SheetDefinition, SheetDefinitions } from './index.js'; import type { MaybeSheetDefinition } from './exports.js'; @@ -150,6 +152,11 @@ let alreadyInjected = {}; // This is the buffer of styles which have not yet been flushed. let injectionBuffer /* : string[] */ = []; +let currentInjectionBufferSize = 0; + +// This is the size limit to be placed on the total css styles usage +let sizeLimit = UNLIMITED_SIZE; + // A flag to tell if we are already buffering styles. This could happen either // because we scheduled a flush call already, so newly added styles will // already be flushed, or because we are statically buffering on the server. @@ -174,6 +181,15 @@ const injectGeneratedCSSOnce = (key, generatedCSS) => { asap(flushToStyleTag); } + if (sizeLimit !== UNLIMITED_SIZE) { + const generatedCSSSize = Buffer.from(generatedCSS.toString()).length; + if (currentInjectionBufferSize + generatedCSSSize >= sizeLimit) { + throw new Error("Cannot inject css since size limit has been reached"); + } else { + currentInjectionBufferSize += generatedCSSSize; + } + } + injectionBuffer.push(...generatedCSS); alreadyInjected[key] = true; } @@ -322,3 +338,10 @@ export const injectAndGetClassName = ( return className; } + +/** + * @param {number} bytesSize + */ +export const setSizeLimit = (bytesSize /* : number */,) => { + sizeLimit = bytesSize; +} diff --git a/tests/inject_test.js b/tests/inject_test.js index af6b893..de07afb 100644 --- a/tests/inject_test.js +++ b/tests/inject_test.js @@ -12,6 +12,8 @@ import { flushToStyleTag, addRenderedClassNames, getRenderedClassNames, + setSizeLimit, + UNLIMITED_SIZE, } from '../src/inject'; import { defaultSelectorHandlers } from '../src/generate'; import { getSheetText } from './testUtils'; @@ -57,6 +59,22 @@ describe('injection', () => { assert.equal(className, 'red_137u7ef-o_O-blue_1tsdo2i'); }); + describe('size limit', () => { + beforeEach(() => { + setSizeLimit(1); + }); + + afterEach(() => { + setSizeLimit(UNLIMITED_SIZE); + }); + + it('uses should throw if over size limit', () => { + assert.throws(() => { + injectAndGetClassName(false, [sheet.red], defaultSelectorHandlers); + }, 'Cannot inject css since size limit has been reached'); + }); + }); + describe('process.env.NODE_ENV === \'production\'', () => { let prodSheet; beforeEach(() => {