diff --git a/src/generate.js b/src/generate.js index fe51260..d4c287d 100644 --- a/src/generate.js +++ b/src/generate.js @@ -14,12 +14,12 @@ const prefixAll = createPrefixer(staticData); /* :: import type { SheetDefinition } from './index.js'; type StringHandlers = { [id:string]: Function }; -type SelectorCallback = (selector: string) => any; +type SelectorCallback = (selector: string) => string[]; export type SelectorHandler = ( selector: string, baseSelector: string, callback: SelectorCallback -) => string | null; +) => string[] | string | null; */ /** @@ -49,9 +49,10 @@ export type SelectorHandler = ( * callback('.foo:nth-child(2n):hover') * * to generate its subtree `{ color: 'red' }` styles with a - * '.foo:nth-child(2n):hover' selector. The callback would return CSS like + * '.foo:nth-child(2n):hover' selector. The callback would return an array of CSS + * rules like * - * '.foo:nth-child(2n):hover{color:red !important;}' + * ['.foo:nth-child(2n):hover{color:red !important;}'] * * and the handler would then return that resulting CSS. * @@ -67,16 +68,12 @@ export type SelectorHandler = ( * @param {function} generateSubtreeStyles: A function which can be called to * generate CSS for the subtree of styles corresponding to the selector. * Accepts a new baseSelector to use for generating those styles. - * @returns {?string} The generated CSS for this selector, or null if we don't - * handle this selector. + * @returns {string[] | string | null} The generated CSS for this selector, or + * null if we don't handle this selector. */ -export const defaultSelectorHandlers = [ +export const defaultSelectorHandlers /* : SelectorHandler[] */ = [ // Handle pseudo-selectors, like :hover and :nth-child(3n) - function pseudoSelectors( - selector /* : string */, - baseSelector /* : string */, - generateSubtreeStyles /* : Function */ - ) /* */ { + function pseudoSelectors(selector, baseSelector, generateSubtreeStyles) { if (selector[0] !== ":") { return null; } @@ -84,17 +81,13 @@ export const defaultSelectorHandlers = [ }, // Handle media queries (or font-faces) - function mediaQueries( - selector /* : string */, - baseSelector /* : string */, - generateSubtreeStyles /* : Function */ - ) /* */ { + function mediaQueries(selector, baseSelector, generateSubtreeStyles) { if (selector[0] !== "@") { return null; } // Generate the styles normally, and then wrap them in the media query. const generated = generateSubtreeStyles(baseSelector); - return `${selector}{${generated}}`; + return [`${selector}{${generated.join('')}}`]; }, ]; @@ -147,7 +140,7 @@ export const generateCSS = ( selectorHandlers /* : SelectorHandler[] */, stringHandlers /* : StringHandlers */, useImportant /* : boolean */ -) /* : string */ => { +) /* : string[] */ => { const merged = new OrderedElements(); for (let i = 0; i < styleTypes.length; i++) { @@ -155,7 +148,7 @@ export const generateCSS = ( } const plainDeclarations = new OrderedElements(); - let generatedStyles = ""; + const generatedStyles = []; // TODO(emily): benchmark this to see if a plain for loop would be faster. merged.forEach((val, key) => { @@ -170,7 +163,17 @@ export const generateCSS = ( if (result != null) { // If the handler returned something, add it to the generated // CSS and stop looking for another handler. - generatedStyles += result; + if (Array.isArray(result)) { + generatedStyles.push(...result); + } else { + // eslint-disable-next-line + console.warn( + 'WARNING: Selector handlers should return an array of rules.' + + 'Returning a string containing multiple rules is deprecated.', + handler, + ); + generatedStyles.push(`@media all {${result}}`); + } return true; } }); @@ -180,13 +183,20 @@ export const generateCSS = ( plainDeclarations.set(key, val, true); } }); - - return ( - generateCSSRuleset( - selector, plainDeclarations, stringHandlers, useImportant, - selectorHandlers) + - generatedStyles + const generatedRuleset = generateCSSRuleset( + selector, + plainDeclarations, + stringHandlers, + useImportant, + selectorHandlers, ); + + + if (generatedRuleset) { + generatedStyles.unshift(generatedRuleset); + } + + return generatedStyles; }; /** diff --git a/src/inject.js b/src/inject.js index 2141616..a34e8d3 100644 --- a/src/inject.js +++ b/src/inject.js @@ -19,17 +19,17 @@ type ProcessedStyleDefinitions = { // inserted anything yet. We could find this each time using // `document.querySelector("style[data-aphrodite"])`, but holding onto it is // faster. -let styleTag = null; +let styleTag /* : ?HTMLStyleElement */ = null; -// Inject a string of styles into a