@mattsjones/css-core
Advanced tools
Comparing version 0.0.10 to 0.0.11
@@ -8,3 +8,2 @@ 'use strict'; | ||
registerClassName: () => {}, | ||
getRegisteredClassNames: () => [], | ||
onEndFileScope: () => {} | ||
@@ -21,5 +20,2 @@ }; | ||
}; | ||
const getRegisteredClassNames = (...props) => { | ||
return adapter.getRegisteredClassNames(...props); | ||
}; | ||
const onEndFileScope = (...props) => { | ||
@@ -30,5 +26,4 @@ return adapter.onEndFileScope(...props); | ||
exports.appendCss = appendCss; | ||
exports.getRegisteredClassNames = getRegisteredClassNames; | ||
exports.onEndFileScope = onEndFileScope; | ||
exports.registerClassName = registerClassName; | ||
exports.setAdapter = setAdapter; |
let adapter = { | ||
appendCss: () => {}, | ||
registerClassName: () => {}, | ||
getRegisteredClassNames: () => [], | ||
onEndFileScope: () => {} | ||
@@ -16,5 +15,2 @@ }; | ||
}; | ||
const getRegisteredClassNames = (...props) => { | ||
return adapter.getRegisteredClassNames(...props); | ||
}; | ||
const onEndFileScope = (...props) => { | ||
@@ -24,2 +20,2 @@ return adapter.onEndFileScope(...props); | ||
export { appendCss, getRegisteredClassNames, onEndFileScope, registerClassName, setAdapter }; | ||
export { appendCss, onEndFileScope, registerClassName, setAdapter }; |
@@ -8,3 +8,2 @@ 'use strict'; | ||
registerClassName: () => {}, | ||
getRegisteredClassNames: () => [], | ||
onEndFileScope: () => {} | ||
@@ -21,5 +20,2 @@ }; | ||
}; | ||
const getRegisteredClassNames = (...props) => { | ||
return adapter.getRegisteredClassNames(...props); | ||
}; | ||
const onEndFileScope = (...props) => { | ||
@@ -30,5 +26,4 @@ return adapter.onEndFileScope(...props); | ||
exports.appendCss = appendCss; | ||
exports.getRegisteredClassNames = getRegisteredClassNames; | ||
exports.onEndFileScope = onEndFileScope; | ||
exports.registerClassName = registerClassName; | ||
exports.setAdapter = setAdapter; |
@@ -8,3 +8,2 @@ 'use strict'; | ||
registerClassName: () => {}, | ||
getRegisteredClassNames: () => [], | ||
onEndFileScope: () => {} | ||
@@ -21,5 +20,2 @@ }; | ||
}; | ||
const getRegisteredClassNames = (...props) => { | ||
return adapter.getRegisteredClassNames(...props); | ||
}; | ||
const onEndFileScope = (...props) => { | ||
@@ -30,5 +26,4 @@ return adapter.onEndFileScope(...props); | ||
exports.appendCss = appendCss; | ||
exports.getRegisteredClassNames = getRegisteredClassNames; | ||
exports.onEndFileScope = onEndFileScope; | ||
exports.registerClassName = registerClassName; | ||
exports.setAdapter = setAdapter; |
let adapter = { | ||
appendCss: () => {}, | ||
registerClassName: () => {}, | ||
getRegisteredClassNames: () => [], | ||
onEndFileScope: () => {} | ||
@@ -16,5 +15,2 @@ }; | ||
}; | ||
const getRegisteredClassNames = (...props) => { | ||
return adapter.getRegisteredClassNames(...props); | ||
}; | ||
const onEndFileScope = (...props) => { | ||
@@ -24,2 +20,2 @@ return adapter.onEndFileScope(...props); | ||
export { appendCss, getRegisteredClassNames, onEndFileScope, registerClassName, setAdapter }; | ||
export { appendCss, onEndFileScope, registerClassName, setAdapter }; |
# @mattsjones/css-core | ||
## 0.0.11 | ||
### Patch Changes | ||
- 8835fec: Export Adapter type | ||
- 3ab713c: Escape generated class names | ||
## 0.0.10 | ||
@@ -4,0 +11,0 @@ |
@@ -5,3 +5,2 @@ import type { Adapter } from './types'; | ||
export declare const registerClassName: Adapter['registerClassName']; | ||
export declare const getRegisteredClassNames: Adapter['getRegisteredClassNames']; | ||
export declare const onEndFileScope: Adapter['onEndFileScope']; |
import { CSS } from './types'; | ||
export declare function generateCss(...allCssObjs: Array<CSS>): Array<string>; | ||
interface GenerateCssParams { | ||
localClassNames: Array<string>; | ||
cssObjs: Array<CSS>; | ||
} | ||
export declare function generateCss({ localClassNames, cssObjs, }: GenerateCssParams): Array<string>; | ||
export {}; |
@@ -1,2 +0,2 @@ | ||
export type { StyleRule } from './types'; | ||
export type { StyleRule, Adapter } from './types'; | ||
export * from './api'; |
import type { CSS } from './types'; | ||
export declare const simplePseudos: readonly [":-moz-any-link", ":-moz-full-screen", ":-moz-placeholder", ":-moz-read-only", ":-moz-read-write", ":-ms-fullscreen", ":-ms-input-placeholder", ":-webkit-any-link", ":-webkit-full-screen", "::-moz-placeholder", "::-moz-progress-bar", "::-moz-range-progress", "::-moz-range-thumb", "::-moz-range-track", "::-moz-selection", "::-ms-backdrop", "::-ms-browse", "::-ms-check", "::-ms-clear", "::-ms-fill", "::-ms-fill-lower", "::-ms-fill-upper", "::-ms-reveal", "::-ms-thumb", "::-ms-ticks-after", "::-ms-ticks-before", "::-ms-tooltip", "::-ms-track", "::-ms-value", "::-webkit-backdrop", "::-webkit-input-placeholder", "::-webkit-progress-bar", "::-webkit-progress-inner-value", "::-webkit-progress-value", "::-webkit-slider-runnable-track", "::-webkit-slider-thumb", "::after", "::backdrop", "::before", "::cue", "::first-letter", "::first-line", "::grammar-error", "::placeholder", "::selection", "::spelling-error", ":active", ":after", ":any-link", ":before", ":blank", ":checked", ":default", ":defined", ":disabled", ":empty", ":enabled", ":first", ":first-child", ":first-letter", ":first-line", ":first-of-type", ":focus", ":focus-visible", ":focus-within", ":fullscreen", ":hover", ":in-range", ":indeterminate", ":invalid", ":last-child", ":last-of-type", ":left", ":link", ":only-child", ":only-of-type", ":optional", ":out-of-range", ":placeholder-shown", ":read-only", ":read-write", ":required", ":right", ":root", ":scope", ":target", ":valid", ":visited"]; | ||
export declare type SimplePseudos = typeof simplePseudos; | ||
export declare function transformCss(...allCssObjs: Array<CSS>): any; | ||
interface TransformCSSParams { | ||
localClassNames: Array<string>; | ||
cssObjs: Array<CSS>; | ||
} | ||
export declare function transformCss({ localClassNames, cssObjs }: TransformCSSParams): any; | ||
export {}; |
@@ -41,5 +41,4 @@ import type { PropertiesFallback } from 'csstype'; | ||
registerClassName: (className: string) => void; | ||
getRegisteredClassNames: () => Array<string>; | ||
onEndFileScope: (fileScope: string) => void; | ||
} | ||
export {}; |
@@ -1,1 +0,1 @@ | ||
export declare const validateSelector: (selector: string) => void[]; | ||
export declare const validateSelector: (selector: string, targetClassName: string) => void[]; |
@@ -5,6 +5,7 @@ 'use strict'; | ||
var generateCss_dist_mattsjonesCssCoreGenerateCss = require('./generateCss-0f19bc03.browser.cjs.js'); | ||
var generateCss_dist_mattsjonesCssCoreGenerateCss = require('../generateCss/dist/mattsjones-css-core-generateCss.browser.cjs.js'); | ||
var adapter_dist_mattsjonesCssCoreAdapter = require('../adapter/dist/mattsjones-css-core-adapter.browser.cjs.js'); | ||
var hash = require('@emotion/hash'); | ||
var get = require('lodash/get'); | ||
var cssesc = require('cssesc'); | ||
var fileScope_dist_mattsjonesCssCoreFileScope = require('../fileScope/dist/mattsjones-css-core-fileScope.browser.cjs.js'); | ||
@@ -24,2 +25,3 @@ require('postcss-js'); | ||
var get__default = /*#__PURE__*/_interopDefault(get); | ||
var cssesc__default = /*#__PURE__*/_interopDefault(cssesc); | ||
@@ -53,5 +55,7 @@ let styleSheet; | ||
}, | ||
getRegisteredClassNames: () => Array.from(localClassNames), | ||
onEndFileScope: () => { | ||
const css = generateCss_dist_mattsjonesCssCoreGenerateCss.generateCss(...bufferedCSSObjs); | ||
const css = generateCss_dist_mattsjonesCssCoreGenerateCss.generateCss({ | ||
localClassNames: Array.from(localClassNames), | ||
cssObjs: bufferedCSSObjs | ||
}); | ||
const stylesheet = getStylesheet(); | ||
@@ -85,3 +89,3 @@ | ||
const className = process.env.NODE_ENV !== 'production' && debugId ? `${getShortFileName()}_${debugId}__${hash__default['default'](fileScope_dist_mattsjonesCssCoreFileScope.getFileScope())}${refCount}` : `${hash__default['default'](fileScope_dist_mattsjonesCssCoreFileScope.getFileScope())}${refCount}`; | ||
return generateCss_dist_mattsjonesCssCoreGenerateCss.sanitiseIdent(className); | ||
return className.match(/^[0-9]/) ? `_${className}` : className; | ||
}; | ||
@@ -114,3 +118,5 @@ | ||
const cssVarName = generateCss_dist_mattsjonesCssCoreGenerateCss.sanitiseIdent(varName).replace(/([A-Z])/g, '-$1').toLowerCase(); | ||
const cssVarName = cssesc__default['default'](varName.match(/^[0-9]/) ? `_${varName}` : varName, { | ||
isIdentifier: true | ||
}).replace(/([A-Z])/g, '-$1').toLowerCase(); | ||
return `var(--${cssVarName})`; | ||
@@ -117,0 +123,0 @@ } |
@@ -1,5 +0,6 @@ | ||
import { g as generateCss, s as sanitiseIdent } from './generateCss-a758bf8f.browser.esm.js'; | ||
import { generateCss } from '../generateCss/dist/mattsjones-css-core-generateCss.browser.esm.js'; | ||
import { registerClassName, appendCss, setAdapter } from '../adapter/dist/mattsjones-css-core-adapter.browser.esm.js'; | ||
import hash from '@emotion/hash'; | ||
import get from 'lodash/get'; | ||
import cssesc from 'cssesc'; | ||
import { getAndIncrementRefCounter, getFileScope } from '../fileScope/dist/mattsjones-css-core-fileScope.browser.esm.js'; | ||
@@ -42,5 +43,7 @@ import 'postcss-js'; | ||
}, | ||
getRegisteredClassNames: () => Array.from(localClassNames), | ||
onEndFileScope: () => { | ||
const css = generateCss(...bufferedCSSObjs); | ||
const css = generateCss({ | ||
localClassNames: Array.from(localClassNames), | ||
cssObjs: bufferedCSSObjs | ||
}); | ||
const stylesheet = getStylesheet(); | ||
@@ -74,3 +77,3 @@ | ||
const className = process.env.NODE_ENV !== 'production' && debugId ? `${getShortFileName()}_${debugId}__${hash(getFileScope())}${refCount}` : `${hash(getFileScope())}${refCount}`; | ||
return sanitiseIdent(className); | ||
return className.match(/^[0-9]/) ? `_${className}` : className; | ||
}; | ||
@@ -103,3 +106,5 @@ | ||
const cssVarName = sanitiseIdent(varName).replace(/([A-Z])/g, '-$1').toLowerCase(); | ||
const cssVarName = cssesc(varName.match(/^[0-9]/) ? `_${varName}` : varName, { | ||
isIdentifier: true | ||
}).replace(/([A-Z])/g, '-$1').toLowerCase(); | ||
return `var(--${cssVarName})`; | ||
@@ -106,0 +111,0 @@ } |
@@ -5,6 +5,7 @@ 'use strict'; | ||
var generateCss_dist_mattsjonesCssCoreGenerateCss = require('./generateCss-35f6aedf.cjs.dev.js'); | ||
var generateCss_dist_mattsjonesCssCoreGenerateCss = require('../generateCss/dist/mattsjones-css-core-generateCss.cjs.dev.js'); | ||
var adapter_dist_mattsjonesCssCoreAdapter = require('../adapter/dist/mattsjones-css-core-adapter.cjs.dev.js'); | ||
var hash = require('@emotion/hash'); | ||
var get = require('lodash/get'); | ||
var cssesc = require('cssesc'); | ||
var fileScope_dist_mattsjonesCssCoreFileScope = require('../fileScope/dist/mattsjones-css-core-fileScope.cjs.dev.js'); | ||
@@ -24,2 +25,3 @@ require('postcss-js'); | ||
var get__default = /*#__PURE__*/_interopDefault(get); | ||
var cssesc__default = /*#__PURE__*/_interopDefault(cssesc); | ||
@@ -53,5 +55,7 @@ let styleSheet; | ||
}, | ||
getRegisteredClassNames: () => Array.from(localClassNames), | ||
onEndFileScope: () => { | ||
const css = generateCss_dist_mattsjonesCssCoreGenerateCss.generateCss(...bufferedCSSObjs); | ||
const css = generateCss_dist_mattsjonesCssCoreGenerateCss.generateCss({ | ||
localClassNames: Array.from(localClassNames), | ||
cssObjs: bufferedCSSObjs | ||
}); | ||
const stylesheet = getStylesheet(); | ||
@@ -85,3 +89,3 @@ | ||
const className = process.env.NODE_ENV !== 'production' && debugId ? `${getShortFileName()}_${debugId}__${hash__default['default'](fileScope_dist_mattsjonesCssCoreFileScope.getFileScope())}${refCount}` : `${hash__default['default'](fileScope_dist_mattsjonesCssCoreFileScope.getFileScope())}${refCount}`; | ||
return generateCss_dist_mattsjonesCssCoreGenerateCss.sanitiseIdent(className); | ||
return className.match(/^[0-9]/) ? `_${className}` : className; | ||
}; | ||
@@ -114,3 +118,5 @@ | ||
const cssVarName = generateCss_dist_mattsjonesCssCoreGenerateCss.sanitiseIdent(varName).replace(/([A-Z])/g, '-$1').toLowerCase(); | ||
const cssVarName = cssesc__default['default'](varName.match(/^[0-9]/) ? `_${varName}` : varName, { | ||
isIdentifier: true | ||
}).replace(/([A-Z])/g, '-$1').toLowerCase(); | ||
return `var(--${cssVarName})`; | ||
@@ -117,0 +123,0 @@ } |
@@ -5,6 +5,7 @@ 'use strict'; | ||
var generateCss_dist_mattsjonesCssCoreGenerateCss = require('./generateCss-0e42352c.cjs.prod.js'); | ||
var generateCss_dist_mattsjonesCssCoreGenerateCss = require('../generateCss/dist/mattsjones-css-core-generateCss.cjs.prod.js'); | ||
var adapter_dist_mattsjonesCssCoreAdapter = require('../adapter/dist/mattsjones-css-core-adapter.cjs.prod.js'); | ||
var hash = require('@emotion/hash'); | ||
var get = require('lodash/get'); | ||
var cssesc = require('cssesc'); | ||
var fileScope_dist_mattsjonesCssCoreFileScope = require('../fileScope/dist/mattsjones-css-core-fileScope.cjs.prod.js'); | ||
@@ -24,2 +25,3 @@ require('postcss-js'); | ||
var get__default = /*#__PURE__*/_interopDefault(get); | ||
var cssesc__default = /*#__PURE__*/_interopDefault(cssesc); | ||
@@ -53,5 +55,7 @@ let styleSheet; | ||
}, | ||
getRegisteredClassNames: () => Array.from(localClassNames), | ||
onEndFileScope: () => { | ||
const css = generateCss_dist_mattsjonesCssCoreGenerateCss.generateCss(...bufferedCSSObjs); | ||
const css = generateCss_dist_mattsjonesCssCoreGenerateCss.generateCss({ | ||
localClassNames: Array.from(localClassNames), | ||
cssObjs: bufferedCSSObjs | ||
}); | ||
const stylesheet = getStylesheet(); | ||
@@ -74,3 +78,3 @@ | ||
const className = `${hash__default['default'](fileScope_dist_mattsjonesCssCoreFileScope.getFileScope())}${refCount}`; | ||
return generateCss_dist_mattsjonesCssCoreGenerateCss.sanitiseIdent(className); | ||
return className.match(/^[0-9]/) ? `_${className}` : className; | ||
}; | ||
@@ -103,3 +107,5 @@ | ||
const cssVarName = generateCss_dist_mattsjonesCssCoreGenerateCss.sanitiseIdent(varName).replace(/([A-Z])/g, '-$1').toLowerCase(); | ||
const cssVarName = cssesc__default['default'](varName.match(/^[0-9]/) ? `_${varName}` : varName, { | ||
isIdentifier: true | ||
}).replace(/([A-Z])/g, '-$1').toLowerCase(); | ||
return `var(--${cssVarName})`; | ||
@@ -106,0 +112,0 @@ } |
@@ -1,5 +0,6 @@ | ||
import { g as generateCss, s as sanitiseIdent } from './generateCss-addefd6f.esm.js'; | ||
import { generateCss } from '../generateCss/dist/mattsjones-css-core-generateCss.esm.js'; | ||
import { registerClassName, appendCss, setAdapter } from '../adapter/dist/mattsjones-css-core-adapter.esm.js'; | ||
import hash from '@emotion/hash'; | ||
import get from 'lodash/get'; | ||
import cssesc from 'cssesc'; | ||
import { getAndIncrementRefCounter, getFileScope } from '../fileScope/dist/mattsjones-css-core-fileScope.esm.js'; | ||
@@ -42,5 +43,7 @@ import 'postcss-js'; | ||
}, | ||
getRegisteredClassNames: () => Array.from(localClassNames), | ||
onEndFileScope: () => { | ||
const css = generateCss(...bufferedCSSObjs); | ||
const css = generateCss({ | ||
localClassNames: Array.from(localClassNames), | ||
cssObjs: bufferedCSSObjs | ||
}); | ||
const stylesheet = getStylesheet(); | ||
@@ -74,3 +77,3 @@ | ||
const className = process.env.NODE_ENV !== 'production' && debugId ? `${getShortFileName()}_${debugId}__${hash(getFileScope())}${refCount}` : `${hash(getFileScope())}${refCount}`; | ||
return sanitiseIdent(className); | ||
return className.match(/^[0-9]/) ? `_${className}` : className; | ||
}; | ||
@@ -103,3 +106,5 @@ | ||
const cssVarName = sanitiseIdent(varName).replace(/([A-Z])/g, '-$1').toLowerCase(); | ||
const cssVarName = cssesc(varName.match(/^[0-9]/) ? `_${varName}` : varName, { | ||
isIdentifier: true | ||
}).replace(/([A-Z])/g, '-$1').toLowerCase(); | ||
return `var(--${cssVarName})`; | ||
@@ -106,0 +111,0 @@ } |
@@ -5,16 +5,304 @@ 'use strict'; | ||
require('postcss-js'); | ||
require('postcss'); | ||
var generateCss_dist_mattsjonesCssCoreGenerateCss = require('../../dist/generateCss-0f19bc03.browser.cjs.js'); | ||
require('@emotion/hash'); | ||
require('lodash/each'); | ||
require('lodash/omit'); | ||
require('lodash/isEqual'); | ||
require('lodash/mapKeys'); | ||
require('css-selector-parser'); | ||
require('dedent'); | ||
require('../../adapter/dist/mattsjones-css-core-adapter.browser.cjs.js'); | ||
var postcssJs = require('postcss-js'); | ||
var postcss = require('postcss'); | ||
var cssesc = require('cssesc'); | ||
var hash = require('@emotion/hash'); | ||
var each = require('lodash/each'); | ||
var omit = require('lodash/omit'); | ||
var isEqual = require('lodash/isEqual'); | ||
var mapKeys = require('lodash/mapKeys'); | ||
var cssSelectorParser = require('css-selector-parser'); | ||
var dedent = require('dedent'); | ||
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; } | ||
var postcssJs__default = /*#__PURE__*/_interopDefault(postcssJs); | ||
var postcss__default = /*#__PURE__*/_interopDefault(postcss); | ||
var cssesc__default = /*#__PURE__*/_interopDefault(cssesc); | ||
var hash__default = /*#__PURE__*/_interopDefault(hash); | ||
var each__default = /*#__PURE__*/_interopDefault(each); | ||
var omit__default = /*#__PURE__*/_interopDefault(omit); | ||
var isEqual__default = /*#__PURE__*/_interopDefault(isEqual); | ||
var mapKeys__default = /*#__PURE__*/_interopDefault(mapKeys); | ||
var dedent__default = /*#__PURE__*/_interopDefault(dedent); | ||
exports.generateCss = generateCss_dist_mattsjonesCssCoreGenerateCss.generateCss; | ||
function escapeRegex(string) { | ||
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); | ||
} | ||
const parser = new cssSelectorParser.CssSelectorParser(); | ||
parser.registerSelectorPseudos('has'); | ||
parser.registerNestingOperators('>', '+', '~'); | ||
parser.registerAttrEqualityMods('^', '$', '*', '~'); | ||
parser.enableSubstitutes(); | ||
const validateSelector = (selector, targetClassName) => { | ||
const replaceTarget = () => { | ||
const targetRegex = new RegExp(`.${escapeRegex(cssesc__default['default'](targetClassName, { | ||
isIdentifier: true | ||
}))}`, 'g'); | ||
return selector.replace(targetRegex, '&'); | ||
}; | ||
return selector.split(',').map(selectorPart => { | ||
if (selectorPart.indexOf(targetClassName) === -1) { | ||
throw new Error(dedent__default['default']` | ||
Invalid selector: ${replaceTarget()} | ||
Selectors must target the ampersand character ('&'), which refers to the generated class name, e.g. '&:nth-child(2n)' | ||
`); | ||
} | ||
let currentRule; | ||
try { | ||
const result = parser.parse(selectorPart); | ||
if (result.type === 'ruleSet') { | ||
currentRule = result.rule; | ||
} else { | ||
throw new Error(); | ||
} | ||
} catch (err) { | ||
throw new Error(`Invalid selector: ${replaceTarget()}`); | ||
} | ||
while (currentRule.rule) { | ||
currentRule = currentRule.rule; | ||
} | ||
const targetRule = currentRule; | ||
if (!Array.isArray(targetRule.classNames) || !targetRule.classNames.find(className => className === targetClassName)) { | ||
throw new Error(dedent__default['default']` | ||
Invalid selector: ${replaceTarget()} | ||
Style selectors must end with the '&' character (along with any modifiers), e.g. ${'`${parent} &`'} or ${'`${parent} &:hover`'}. | ||
This is to ensure that each style block only affects the styling of a single class. | ||
If your selector is targeting another class, you should move it to the style definition for that class, e.g. given we have styles for 'parent' and 'child' elements, instead of adding a selector of ${'`& ${child}`'}) to 'parent', you should add ${'`${parent} &`'} to 'child'). | ||
If your selector is targeting something global, use the 'globalStyle' function instead, e.g. if you wanted to write ${'`& h1`'}, you should instead write 'globalStyle(${'`${parent} h1`'}, { ... })' | ||
`); | ||
} | ||
}); | ||
}; | ||
const simplePseudos = [':-moz-any-link', ':-moz-full-screen', ':-moz-placeholder', ':-moz-read-only', ':-moz-read-write', ':-ms-fullscreen', ':-ms-input-placeholder', ':-webkit-any-link', ':-webkit-full-screen', '::-moz-placeholder', '::-moz-progress-bar', '::-moz-range-progress', '::-moz-range-thumb', '::-moz-range-track', '::-moz-selection', '::-ms-backdrop', '::-ms-browse', '::-ms-check', '::-ms-clear', '::-ms-fill', '::-ms-fill-lower', '::-ms-fill-upper', '::-ms-reveal', '::-ms-thumb', '::-ms-ticks-after', '::-ms-ticks-before', '::-ms-tooltip', '::-ms-track', '::-ms-value', '::-webkit-backdrop', '::-webkit-input-placeholder', '::-webkit-progress-bar', '::-webkit-progress-inner-value', '::-webkit-progress-value', '::-webkit-slider-runnable-track', '::-webkit-slider-thumb', '::after', '::backdrop', '::before', '::cue', '::first-letter', '::first-line', '::grammar-error', '::placeholder', '::selection', '::spelling-error', ':active', ':after', ':any-link', ':before', ':blank', ':checked', ':default', ':defined', ':disabled', ':empty', ':enabled', ':first', ':first-child', ':first-letter', ':first-line', ':first-of-type', ':focus', ':focus-visible', ':focus-within', ':fullscreen', ':hover', ':in-range', ':indeterminate', ':invalid', ':last-child', ':last-of-type', ':left', ':link', ':only-child', ':only-of-type', ':optional', ':out-of-range', ':placeholder-shown', ':read-only', ':read-write', ':required', ':right', ':root', ':scope', ':target', ':valid', ':visited']; | ||
const simplePseudoSet = new Set(simplePseudos); | ||
const specialKeys = [...simplePseudos, '@media', '@supports', 'selectors']; | ||
class Stylesheet { | ||
constructor(localClassNames) { | ||
this.rules = []; | ||
this.conditionalRules = []; | ||
this.localClassNameRegex = localClassNames.length > 0 ? RegExp(`(^|[^\.])(${localClassNames.join('|')})`, 'g') : null; | ||
} | ||
processCssObj(root) { | ||
// Add main styles | ||
const mainRule = omit__default['default'](root.rule, specialKeys); | ||
this.addRule({ | ||
selector: root.selector, | ||
rule: mainRule | ||
}); | ||
this.transformSimplePsuedos(root.rule, root.selector); | ||
this.transformMedia(root.rule['@media'], root.selector); | ||
this.transformSupports(root.rule['@supports'], root.selector); | ||
this.transformSelectors(root.rule, root.selector); | ||
} | ||
addRule(cssRule) { | ||
const rule = this.transformVars(this.transformRuleKeyframes(cssRule.rule)); | ||
const selector = this.transformSelector(cssRule.selector); | ||
if (cssRule.conditions) { | ||
this.conditionalRules.push({ | ||
selector, | ||
rule, | ||
conditions: cssRule.conditions.sort() | ||
}); | ||
} else { | ||
this.rules.push({ | ||
selector, | ||
rule | ||
}); | ||
} | ||
} | ||
transformVars({ | ||
vars, | ||
...rest | ||
}) { | ||
if (!vars) { | ||
return rest; | ||
} | ||
return { ...mapKeys__default['default'](vars, (_value, key) => { | ||
const matches = key.match(/^var\((.*)\)$/); | ||
if (matches) { | ||
return matches[1]; | ||
} | ||
return key; | ||
}), | ||
...rest | ||
}; | ||
} | ||
transformRuleKeyframes(rule) { | ||
let { | ||
'@keyframes': keyframes, | ||
animation, | ||
animationName, | ||
...rest | ||
} = rule; | ||
if (!keyframes && !rule.animation && !rule.animationName) { | ||
return rest; | ||
} | ||
let keyframesRef = typeof keyframes === 'string' ? keyframes : ''; | ||
if (keyframes && typeof keyframes !== 'string') { | ||
keyframesRef = cssesc__default['default'](hash__default['default'](JSON.stringify(keyframes)), { | ||
isIdentifier: true | ||
}); // Hoist keyframes to the top of the stylesheet | ||
this.rules.unshift({ | ||
selector: `@keyframes ${keyframesRef}`, | ||
rule: keyframes | ||
}); | ||
} | ||
return { ...rest, | ||
animation: animation && typeof animation === 'string' ? // @ts-expect-error Why??????????? | ||
animation.replace('@keyframes', keyframesRef) : animation, | ||
animationName: animationName ? undefined : keyframesRef | ||
}; | ||
} | ||
transformSelector(selector) { | ||
return this.localClassNameRegex ? selector.replace(this.localClassNameRegex, (_, leadingChar, className) => `${leadingChar}.${cssesc__default['default'](className, { | ||
isIdentifier: true | ||
})}`) : selector; | ||
} | ||
transformSelectors(rule, rootSelector, conditions) { | ||
each__default['default'](rule.selectors, (selectorRule, selector) => { | ||
const transformedSelector = this.transformSelector(selector.replace(RegExp('&', 'g'), rootSelector)); | ||
validateSelector(transformedSelector, rootSelector); | ||
this.addRule({ | ||
conditions, | ||
selector: transformedSelector, | ||
rule: selectorRule | ||
}); | ||
}); | ||
} | ||
transformMedia(rules, rootSelector, parentConditions = []) { | ||
each__default['default'](rules, (mediaRule, query) => { | ||
const conditions = [`@media ${query}`, ...parentConditions]; | ||
this.addRule({ | ||
conditions, | ||
selector: rootSelector, | ||
rule: omit__default['default'](mediaRule, specialKeys) | ||
}); | ||
this.transformSimplePsuedos(mediaRule, rootSelector, conditions); | ||
this.transformSelectors(mediaRule, rootSelector, conditions); | ||
this.transformSupports(mediaRule['@supports'], rootSelector, conditions); | ||
}); | ||
} | ||
transformSupports(rules, rootSelector, parentConditions = []) { | ||
each__default['default'](rules, (supportsRule, query) => { | ||
const conditions = [`@supports ${query}`, ...parentConditions]; | ||
this.addRule({ | ||
conditions, | ||
selector: rootSelector, | ||
rule: omit__default['default'](supportsRule, specialKeys) | ||
}); | ||
this.transformSimplePsuedos(supportsRule, rootSelector, conditions); | ||
this.transformSelectors(supportsRule, rootSelector, conditions); | ||
this.transformMedia(supportsRule['@media'], rootSelector, conditions); | ||
}); | ||
} | ||
transformSimplePsuedos(rule, rootSelector, conditions) { | ||
for (const key of Object.keys(rule)) { | ||
// Process simple psuedos | ||
if (simplePseudoSet.has(key)) { | ||
this.addRule({ | ||
conditions, | ||
selector: `${rootSelector}${key}`, | ||
rule: rule[key] | ||
}); | ||
} | ||
} | ||
} | ||
toPostcssJs() { | ||
const styles = {}; | ||
for (const rule of [...this.rules, ...this.conditionalRules]) { | ||
if (rule.conditions && isEqual__default['default'](styles[rule.selector], rule.rule)) { | ||
// Ignore conditional rules if they are identical to a non-conditional rule | ||
continue; | ||
} | ||
if (Object.keys(rule.rule).length === 0) { | ||
// Ignore empty rules | ||
continue; | ||
} | ||
let styleNode = styles; | ||
for (const condition of rule.conditions || []) { | ||
if (!styleNode[condition]) { | ||
styleNode[condition] = {}; | ||
} | ||
styleNode = styleNode[condition]; | ||
} | ||
styleNode[rule.selector] = { ...styleNode[rule.selector], | ||
...rule.rule | ||
}; | ||
} | ||
return styles; | ||
} | ||
} | ||
function transformCss({ | ||
localClassNames, | ||
cssObjs | ||
}) { | ||
const stylesheet = new Stylesheet(localClassNames); | ||
for (const root of cssObjs) { | ||
stylesheet.processCssObj(root); | ||
} | ||
return stylesheet.toPostcssJs(); | ||
} | ||
// @ts-expect-error | ||
function generateCss({ | ||
localClassNames, | ||
cssObjs | ||
}) { | ||
const flattenedCss = transformCss({ | ||
localClassNames, | ||
cssObjs | ||
}); | ||
const result = postcss__default['default']().process(flattenedCss, { | ||
parser: postcssJs__default['default'], | ||
from: undefined | ||
}); | ||
return result.root.nodes.map(node => node.toString()); | ||
} | ||
exports.generateCss = generateCss; |
@@ -1,11 +0,291 @@ | ||
import 'postcss-js'; | ||
import 'postcss'; | ||
export { g as generateCss } from '../../dist/generateCss-a758bf8f.browser.esm.js'; | ||
import '@emotion/hash'; | ||
import 'lodash/each'; | ||
import 'lodash/omit'; | ||
import 'lodash/isEqual'; | ||
import 'lodash/mapKeys'; | ||
import 'css-selector-parser'; | ||
import 'dedent'; | ||
import '../../adapter/dist/mattsjones-css-core-adapter.browser.esm.js'; | ||
import postcssJs from 'postcss-js'; | ||
import postcss from 'postcss'; | ||
import cssesc from 'cssesc'; | ||
import hash from '@emotion/hash'; | ||
import each from 'lodash/each'; | ||
import omit from 'lodash/omit'; | ||
import isEqual from 'lodash/isEqual'; | ||
import mapKeys from 'lodash/mapKeys'; | ||
import { CssSelectorParser } from 'css-selector-parser'; | ||
import dedent from 'dedent'; | ||
function escapeRegex(string) { | ||
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); | ||
} | ||
const parser = new CssSelectorParser(); | ||
parser.registerSelectorPseudos('has'); | ||
parser.registerNestingOperators('>', '+', '~'); | ||
parser.registerAttrEqualityMods('^', '$', '*', '~'); | ||
parser.enableSubstitutes(); | ||
const validateSelector = (selector, targetClassName) => { | ||
const replaceTarget = () => { | ||
const targetRegex = new RegExp(`.${escapeRegex(cssesc(targetClassName, { | ||
isIdentifier: true | ||
}))}`, 'g'); | ||
return selector.replace(targetRegex, '&'); | ||
}; | ||
return selector.split(',').map(selectorPart => { | ||
if (selectorPart.indexOf(targetClassName) === -1) { | ||
throw new Error(dedent` | ||
Invalid selector: ${replaceTarget()} | ||
Selectors must target the ampersand character ('&'), which refers to the generated class name, e.g. '&:nth-child(2n)' | ||
`); | ||
} | ||
let currentRule; | ||
try { | ||
const result = parser.parse(selectorPart); | ||
if (result.type === 'ruleSet') { | ||
currentRule = result.rule; | ||
} else { | ||
throw new Error(); | ||
} | ||
} catch (err) { | ||
throw new Error(`Invalid selector: ${replaceTarget()}`); | ||
} | ||
while (currentRule.rule) { | ||
currentRule = currentRule.rule; | ||
} | ||
const targetRule = currentRule; | ||
if (!Array.isArray(targetRule.classNames) || !targetRule.classNames.find(className => className === targetClassName)) { | ||
throw new Error(dedent` | ||
Invalid selector: ${replaceTarget()} | ||
Style selectors must end with the '&' character (along with any modifiers), e.g. ${'`${parent} &`'} or ${'`${parent} &:hover`'}. | ||
This is to ensure that each style block only affects the styling of a single class. | ||
If your selector is targeting another class, you should move it to the style definition for that class, e.g. given we have styles for 'parent' and 'child' elements, instead of adding a selector of ${'`& ${child}`'}) to 'parent', you should add ${'`${parent} &`'} to 'child'). | ||
If your selector is targeting something global, use the 'globalStyle' function instead, e.g. if you wanted to write ${'`& h1`'}, you should instead write 'globalStyle(${'`${parent} h1`'}, { ... })' | ||
`); | ||
} | ||
}); | ||
}; | ||
const simplePseudos = [':-moz-any-link', ':-moz-full-screen', ':-moz-placeholder', ':-moz-read-only', ':-moz-read-write', ':-ms-fullscreen', ':-ms-input-placeholder', ':-webkit-any-link', ':-webkit-full-screen', '::-moz-placeholder', '::-moz-progress-bar', '::-moz-range-progress', '::-moz-range-thumb', '::-moz-range-track', '::-moz-selection', '::-ms-backdrop', '::-ms-browse', '::-ms-check', '::-ms-clear', '::-ms-fill', '::-ms-fill-lower', '::-ms-fill-upper', '::-ms-reveal', '::-ms-thumb', '::-ms-ticks-after', '::-ms-ticks-before', '::-ms-tooltip', '::-ms-track', '::-ms-value', '::-webkit-backdrop', '::-webkit-input-placeholder', '::-webkit-progress-bar', '::-webkit-progress-inner-value', '::-webkit-progress-value', '::-webkit-slider-runnable-track', '::-webkit-slider-thumb', '::after', '::backdrop', '::before', '::cue', '::first-letter', '::first-line', '::grammar-error', '::placeholder', '::selection', '::spelling-error', ':active', ':after', ':any-link', ':before', ':blank', ':checked', ':default', ':defined', ':disabled', ':empty', ':enabled', ':first', ':first-child', ':first-letter', ':first-line', ':first-of-type', ':focus', ':focus-visible', ':focus-within', ':fullscreen', ':hover', ':in-range', ':indeterminate', ':invalid', ':last-child', ':last-of-type', ':left', ':link', ':only-child', ':only-of-type', ':optional', ':out-of-range', ':placeholder-shown', ':read-only', ':read-write', ':required', ':right', ':root', ':scope', ':target', ':valid', ':visited']; | ||
const simplePseudoSet = new Set(simplePseudos); | ||
const specialKeys = [...simplePseudos, '@media', '@supports', 'selectors']; | ||
class Stylesheet { | ||
constructor(localClassNames) { | ||
this.rules = []; | ||
this.conditionalRules = []; | ||
this.localClassNameRegex = localClassNames.length > 0 ? RegExp(`(^|[^\.])(${localClassNames.join('|')})`, 'g') : null; | ||
} | ||
processCssObj(root) { | ||
// Add main styles | ||
const mainRule = omit(root.rule, specialKeys); | ||
this.addRule({ | ||
selector: root.selector, | ||
rule: mainRule | ||
}); | ||
this.transformSimplePsuedos(root.rule, root.selector); | ||
this.transformMedia(root.rule['@media'], root.selector); | ||
this.transformSupports(root.rule['@supports'], root.selector); | ||
this.transformSelectors(root.rule, root.selector); | ||
} | ||
addRule(cssRule) { | ||
const rule = this.transformVars(this.transformRuleKeyframes(cssRule.rule)); | ||
const selector = this.transformSelector(cssRule.selector); | ||
if (cssRule.conditions) { | ||
this.conditionalRules.push({ | ||
selector, | ||
rule, | ||
conditions: cssRule.conditions.sort() | ||
}); | ||
} else { | ||
this.rules.push({ | ||
selector, | ||
rule | ||
}); | ||
} | ||
} | ||
transformVars({ | ||
vars, | ||
...rest | ||
}) { | ||
if (!vars) { | ||
return rest; | ||
} | ||
return { ...mapKeys(vars, (_value, key) => { | ||
const matches = key.match(/^var\((.*)\)$/); | ||
if (matches) { | ||
return matches[1]; | ||
} | ||
return key; | ||
}), | ||
...rest | ||
}; | ||
} | ||
transformRuleKeyframes(rule) { | ||
let { | ||
'@keyframes': keyframes, | ||
animation, | ||
animationName, | ||
...rest | ||
} = rule; | ||
if (!keyframes && !rule.animation && !rule.animationName) { | ||
return rest; | ||
} | ||
let keyframesRef = typeof keyframes === 'string' ? keyframes : ''; | ||
if (keyframes && typeof keyframes !== 'string') { | ||
keyframesRef = cssesc(hash(JSON.stringify(keyframes)), { | ||
isIdentifier: true | ||
}); // Hoist keyframes to the top of the stylesheet | ||
this.rules.unshift({ | ||
selector: `@keyframes ${keyframesRef}`, | ||
rule: keyframes | ||
}); | ||
} | ||
return { ...rest, | ||
animation: animation && typeof animation === 'string' ? // @ts-expect-error Why??????????? | ||
animation.replace('@keyframes', keyframesRef) : animation, | ||
animationName: animationName ? undefined : keyframesRef | ||
}; | ||
} | ||
transformSelector(selector) { | ||
return this.localClassNameRegex ? selector.replace(this.localClassNameRegex, (_, leadingChar, className) => `${leadingChar}.${cssesc(className, { | ||
isIdentifier: true | ||
})}`) : selector; | ||
} | ||
transformSelectors(rule, rootSelector, conditions) { | ||
each(rule.selectors, (selectorRule, selector) => { | ||
const transformedSelector = this.transformSelector(selector.replace(RegExp('&', 'g'), rootSelector)); | ||
validateSelector(transformedSelector, rootSelector); | ||
this.addRule({ | ||
conditions, | ||
selector: transformedSelector, | ||
rule: selectorRule | ||
}); | ||
}); | ||
} | ||
transformMedia(rules, rootSelector, parentConditions = []) { | ||
each(rules, (mediaRule, query) => { | ||
const conditions = [`@media ${query}`, ...parentConditions]; | ||
this.addRule({ | ||
conditions, | ||
selector: rootSelector, | ||
rule: omit(mediaRule, specialKeys) | ||
}); | ||
this.transformSimplePsuedos(mediaRule, rootSelector, conditions); | ||
this.transformSelectors(mediaRule, rootSelector, conditions); | ||
this.transformSupports(mediaRule['@supports'], rootSelector, conditions); | ||
}); | ||
} | ||
transformSupports(rules, rootSelector, parentConditions = []) { | ||
each(rules, (supportsRule, query) => { | ||
const conditions = [`@supports ${query}`, ...parentConditions]; | ||
this.addRule({ | ||
conditions, | ||
selector: rootSelector, | ||
rule: omit(supportsRule, specialKeys) | ||
}); | ||
this.transformSimplePsuedos(supportsRule, rootSelector, conditions); | ||
this.transformSelectors(supportsRule, rootSelector, conditions); | ||
this.transformMedia(supportsRule['@media'], rootSelector, conditions); | ||
}); | ||
} | ||
transformSimplePsuedos(rule, rootSelector, conditions) { | ||
for (const key of Object.keys(rule)) { | ||
// Process simple psuedos | ||
if (simplePseudoSet.has(key)) { | ||
this.addRule({ | ||
conditions, | ||
selector: `${rootSelector}${key}`, | ||
rule: rule[key] | ||
}); | ||
} | ||
} | ||
} | ||
toPostcssJs() { | ||
const styles = {}; | ||
for (const rule of [...this.rules, ...this.conditionalRules]) { | ||
if (rule.conditions && isEqual(styles[rule.selector], rule.rule)) { | ||
// Ignore conditional rules if they are identical to a non-conditional rule | ||
continue; | ||
} | ||
if (Object.keys(rule.rule).length === 0) { | ||
// Ignore empty rules | ||
continue; | ||
} | ||
let styleNode = styles; | ||
for (const condition of rule.conditions || []) { | ||
if (!styleNode[condition]) { | ||
styleNode[condition] = {}; | ||
} | ||
styleNode = styleNode[condition]; | ||
} | ||
styleNode[rule.selector] = { ...styleNode[rule.selector], | ||
...rule.rule | ||
}; | ||
} | ||
return styles; | ||
} | ||
} | ||
function transformCss({ | ||
localClassNames, | ||
cssObjs | ||
}) { | ||
const stylesheet = new Stylesheet(localClassNames); | ||
for (const root of cssObjs) { | ||
stylesheet.processCssObj(root); | ||
} | ||
return stylesheet.toPostcssJs(); | ||
} | ||
// @ts-expect-error | ||
function generateCss({ | ||
localClassNames, | ||
cssObjs | ||
}) { | ||
const flattenedCss = transformCss({ | ||
localClassNames, | ||
cssObjs | ||
}); | ||
const result = postcss().process(flattenedCss, { | ||
parser: postcssJs, | ||
from: undefined | ||
}); | ||
return result.root.nodes.map(node => node.toString()); | ||
} | ||
export { generateCss }; |
@@ -5,16 +5,304 @@ 'use strict'; | ||
require('postcss-js'); | ||
require('postcss'); | ||
var generateCss_dist_mattsjonesCssCoreGenerateCss = require('../../dist/generateCss-35f6aedf.cjs.dev.js'); | ||
require('@emotion/hash'); | ||
require('lodash/each'); | ||
require('lodash/omit'); | ||
require('lodash/isEqual'); | ||
require('lodash/mapKeys'); | ||
require('css-selector-parser'); | ||
require('dedent'); | ||
require('../../adapter/dist/mattsjones-css-core-adapter.cjs.dev.js'); | ||
var postcssJs = require('postcss-js'); | ||
var postcss = require('postcss'); | ||
var cssesc = require('cssesc'); | ||
var hash = require('@emotion/hash'); | ||
var each = require('lodash/each'); | ||
var omit = require('lodash/omit'); | ||
var isEqual = require('lodash/isEqual'); | ||
var mapKeys = require('lodash/mapKeys'); | ||
var cssSelectorParser = require('css-selector-parser'); | ||
var dedent = require('dedent'); | ||
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; } | ||
var postcssJs__default = /*#__PURE__*/_interopDefault(postcssJs); | ||
var postcss__default = /*#__PURE__*/_interopDefault(postcss); | ||
var cssesc__default = /*#__PURE__*/_interopDefault(cssesc); | ||
var hash__default = /*#__PURE__*/_interopDefault(hash); | ||
var each__default = /*#__PURE__*/_interopDefault(each); | ||
var omit__default = /*#__PURE__*/_interopDefault(omit); | ||
var isEqual__default = /*#__PURE__*/_interopDefault(isEqual); | ||
var mapKeys__default = /*#__PURE__*/_interopDefault(mapKeys); | ||
var dedent__default = /*#__PURE__*/_interopDefault(dedent); | ||
exports.generateCss = generateCss_dist_mattsjonesCssCoreGenerateCss.generateCss; | ||
function escapeRegex(string) { | ||
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); | ||
} | ||
const parser = new cssSelectorParser.CssSelectorParser(); | ||
parser.registerSelectorPseudos('has'); | ||
parser.registerNestingOperators('>', '+', '~'); | ||
parser.registerAttrEqualityMods('^', '$', '*', '~'); | ||
parser.enableSubstitutes(); | ||
const validateSelector = (selector, targetClassName) => { | ||
const replaceTarget = () => { | ||
const targetRegex = new RegExp(`.${escapeRegex(cssesc__default['default'](targetClassName, { | ||
isIdentifier: true | ||
}))}`, 'g'); | ||
return selector.replace(targetRegex, '&'); | ||
}; | ||
return selector.split(',').map(selectorPart => { | ||
if (selectorPart.indexOf(targetClassName) === -1) { | ||
throw new Error(dedent__default['default']` | ||
Invalid selector: ${replaceTarget()} | ||
Selectors must target the ampersand character ('&'), which refers to the generated class name, e.g. '&:nth-child(2n)' | ||
`); | ||
} | ||
let currentRule; | ||
try { | ||
const result = parser.parse(selectorPart); | ||
if (result.type === 'ruleSet') { | ||
currentRule = result.rule; | ||
} else { | ||
throw new Error(); | ||
} | ||
} catch (err) { | ||
throw new Error(`Invalid selector: ${replaceTarget()}`); | ||
} | ||
while (currentRule.rule) { | ||
currentRule = currentRule.rule; | ||
} | ||
const targetRule = currentRule; | ||
if (!Array.isArray(targetRule.classNames) || !targetRule.classNames.find(className => className === targetClassName)) { | ||
throw new Error(dedent__default['default']` | ||
Invalid selector: ${replaceTarget()} | ||
Style selectors must end with the '&' character (along with any modifiers), e.g. ${'`${parent} &`'} or ${'`${parent} &:hover`'}. | ||
This is to ensure that each style block only affects the styling of a single class. | ||
If your selector is targeting another class, you should move it to the style definition for that class, e.g. given we have styles for 'parent' and 'child' elements, instead of adding a selector of ${'`& ${child}`'}) to 'parent', you should add ${'`${parent} &`'} to 'child'). | ||
If your selector is targeting something global, use the 'globalStyle' function instead, e.g. if you wanted to write ${'`& h1`'}, you should instead write 'globalStyle(${'`${parent} h1`'}, { ... })' | ||
`); | ||
} | ||
}); | ||
}; | ||
const simplePseudos = [':-moz-any-link', ':-moz-full-screen', ':-moz-placeholder', ':-moz-read-only', ':-moz-read-write', ':-ms-fullscreen', ':-ms-input-placeholder', ':-webkit-any-link', ':-webkit-full-screen', '::-moz-placeholder', '::-moz-progress-bar', '::-moz-range-progress', '::-moz-range-thumb', '::-moz-range-track', '::-moz-selection', '::-ms-backdrop', '::-ms-browse', '::-ms-check', '::-ms-clear', '::-ms-fill', '::-ms-fill-lower', '::-ms-fill-upper', '::-ms-reveal', '::-ms-thumb', '::-ms-ticks-after', '::-ms-ticks-before', '::-ms-tooltip', '::-ms-track', '::-ms-value', '::-webkit-backdrop', '::-webkit-input-placeholder', '::-webkit-progress-bar', '::-webkit-progress-inner-value', '::-webkit-progress-value', '::-webkit-slider-runnable-track', '::-webkit-slider-thumb', '::after', '::backdrop', '::before', '::cue', '::first-letter', '::first-line', '::grammar-error', '::placeholder', '::selection', '::spelling-error', ':active', ':after', ':any-link', ':before', ':blank', ':checked', ':default', ':defined', ':disabled', ':empty', ':enabled', ':first', ':first-child', ':first-letter', ':first-line', ':first-of-type', ':focus', ':focus-visible', ':focus-within', ':fullscreen', ':hover', ':in-range', ':indeterminate', ':invalid', ':last-child', ':last-of-type', ':left', ':link', ':only-child', ':only-of-type', ':optional', ':out-of-range', ':placeholder-shown', ':read-only', ':read-write', ':required', ':right', ':root', ':scope', ':target', ':valid', ':visited']; | ||
const simplePseudoSet = new Set(simplePseudos); | ||
const specialKeys = [...simplePseudos, '@media', '@supports', 'selectors']; | ||
class Stylesheet { | ||
constructor(localClassNames) { | ||
this.rules = []; | ||
this.conditionalRules = []; | ||
this.localClassNameRegex = localClassNames.length > 0 ? RegExp(`(^|[^\.])(${localClassNames.join('|')})`, 'g') : null; | ||
} | ||
processCssObj(root) { | ||
// Add main styles | ||
const mainRule = omit__default['default'](root.rule, specialKeys); | ||
this.addRule({ | ||
selector: root.selector, | ||
rule: mainRule | ||
}); | ||
this.transformSimplePsuedos(root.rule, root.selector); | ||
this.transformMedia(root.rule['@media'], root.selector); | ||
this.transformSupports(root.rule['@supports'], root.selector); | ||
this.transformSelectors(root.rule, root.selector); | ||
} | ||
addRule(cssRule) { | ||
const rule = this.transformVars(this.transformRuleKeyframes(cssRule.rule)); | ||
const selector = this.transformSelector(cssRule.selector); | ||
if (cssRule.conditions) { | ||
this.conditionalRules.push({ | ||
selector, | ||
rule, | ||
conditions: cssRule.conditions.sort() | ||
}); | ||
} else { | ||
this.rules.push({ | ||
selector, | ||
rule | ||
}); | ||
} | ||
} | ||
transformVars({ | ||
vars, | ||
...rest | ||
}) { | ||
if (!vars) { | ||
return rest; | ||
} | ||
return { ...mapKeys__default['default'](vars, (_value, key) => { | ||
const matches = key.match(/^var\((.*)\)$/); | ||
if (matches) { | ||
return matches[1]; | ||
} | ||
return key; | ||
}), | ||
...rest | ||
}; | ||
} | ||
transformRuleKeyframes(rule) { | ||
let { | ||
'@keyframes': keyframes, | ||
animation, | ||
animationName, | ||
...rest | ||
} = rule; | ||
if (!keyframes && !rule.animation && !rule.animationName) { | ||
return rest; | ||
} | ||
let keyframesRef = typeof keyframes === 'string' ? keyframes : ''; | ||
if (keyframes && typeof keyframes !== 'string') { | ||
keyframesRef = cssesc__default['default'](hash__default['default'](JSON.stringify(keyframes)), { | ||
isIdentifier: true | ||
}); // Hoist keyframes to the top of the stylesheet | ||
this.rules.unshift({ | ||
selector: `@keyframes ${keyframesRef}`, | ||
rule: keyframes | ||
}); | ||
} | ||
return { ...rest, | ||
animation: animation && typeof animation === 'string' ? // @ts-expect-error Why??????????? | ||
animation.replace('@keyframes', keyframesRef) : animation, | ||
animationName: animationName ? undefined : keyframesRef | ||
}; | ||
} | ||
transformSelector(selector) { | ||
return this.localClassNameRegex ? selector.replace(this.localClassNameRegex, (_, leadingChar, className) => `${leadingChar}.${cssesc__default['default'](className, { | ||
isIdentifier: true | ||
})}`) : selector; | ||
} | ||
transformSelectors(rule, rootSelector, conditions) { | ||
each__default['default'](rule.selectors, (selectorRule, selector) => { | ||
const transformedSelector = this.transformSelector(selector.replace(RegExp('&', 'g'), rootSelector)); | ||
validateSelector(transformedSelector, rootSelector); | ||
this.addRule({ | ||
conditions, | ||
selector: transformedSelector, | ||
rule: selectorRule | ||
}); | ||
}); | ||
} | ||
transformMedia(rules, rootSelector, parentConditions = []) { | ||
each__default['default'](rules, (mediaRule, query) => { | ||
const conditions = [`@media ${query}`, ...parentConditions]; | ||
this.addRule({ | ||
conditions, | ||
selector: rootSelector, | ||
rule: omit__default['default'](mediaRule, specialKeys) | ||
}); | ||
this.transformSimplePsuedos(mediaRule, rootSelector, conditions); | ||
this.transformSelectors(mediaRule, rootSelector, conditions); | ||
this.transformSupports(mediaRule['@supports'], rootSelector, conditions); | ||
}); | ||
} | ||
transformSupports(rules, rootSelector, parentConditions = []) { | ||
each__default['default'](rules, (supportsRule, query) => { | ||
const conditions = [`@supports ${query}`, ...parentConditions]; | ||
this.addRule({ | ||
conditions, | ||
selector: rootSelector, | ||
rule: omit__default['default'](supportsRule, specialKeys) | ||
}); | ||
this.transformSimplePsuedos(supportsRule, rootSelector, conditions); | ||
this.transformSelectors(supportsRule, rootSelector, conditions); | ||
this.transformMedia(supportsRule['@media'], rootSelector, conditions); | ||
}); | ||
} | ||
transformSimplePsuedos(rule, rootSelector, conditions) { | ||
for (const key of Object.keys(rule)) { | ||
// Process simple psuedos | ||
if (simplePseudoSet.has(key)) { | ||
this.addRule({ | ||
conditions, | ||
selector: `${rootSelector}${key}`, | ||
rule: rule[key] | ||
}); | ||
} | ||
} | ||
} | ||
toPostcssJs() { | ||
const styles = {}; | ||
for (const rule of [...this.rules, ...this.conditionalRules]) { | ||
if (rule.conditions && isEqual__default['default'](styles[rule.selector], rule.rule)) { | ||
// Ignore conditional rules if they are identical to a non-conditional rule | ||
continue; | ||
} | ||
if (Object.keys(rule.rule).length === 0) { | ||
// Ignore empty rules | ||
continue; | ||
} | ||
let styleNode = styles; | ||
for (const condition of rule.conditions || []) { | ||
if (!styleNode[condition]) { | ||
styleNode[condition] = {}; | ||
} | ||
styleNode = styleNode[condition]; | ||
} | ||
styleNode[rule.selector] = { ...styleNode[rule.selector], | ||
...rule.rule | ||
}; | ||
} | ||
return styles; | ||
} | ||
} | ||
function transformCss({ | ||
localClassNames, | ||
cssObjs | ||
}) { | ||
const stylesheet = new Stylesheet(localClassNames); | ||
for (const root of cssObjs) { | ||
stylesheet.processCssObj(root); | ||
} | ||
return stylesheet.toPostcssJs(); | ||
} | ||
// @ts-expect-error | ||
function generateCss({ | ||
localClassNames, | ||
cssObjs | ||
}) { | ||
const flattenedCss = transformCss({ | ||
localClassNames, | ||
cssObjs | ||
}); | ||
const result = postcss__default['default']().process(flattenedCss, { | ||
parser: postcssJs__default['default'], | ||
from: undefined | ||
}); | ||
return result.root.nodes.map(node => node.toString()); | ||
} | ||
exports.generateCss = generateCss; |
@@ -5,16 +5,304 @@ 'use strict'; | ||
require('postcss-js'); | ||
require('postcss'); | ||
var generateCss_dist_mattsjonesCssCoreGenerateCss = require('../../dist/generateCss-0e42352c.cjs.prod.js'); | ||
require('@emotion/hash'); | ||
require('lodash/each'); | ||
require('lodash/omit'); | ||
require('lodash/isEqual'); | ||
require('lodash/mapKeys'); | ||
require('css-selector-parser'); | ||
require('dedent'); | ||
require('../../adapter/dist/mattsjones-css-core-adapter.cjs.prod.js'); | ||
var postcssJs = require('postcss-js'); | ||
var postcss = require('postcss'); | ||
var cssesc = require('cssesc'); | ||
var hash = require('@emotion/hash'); | ||
var each = require('lodash/each'); | ||
var omit = require('lodash/omit'); | ||
var isEqual = require('lodash/isEqual'); | ||
var mapKeys = require('lodash/mapKeys'); | ||
var cssSelectorParser = require('css-selector-parser'); | ||
var dedent = require('dedent'); | ||
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; } | ||
var postcssJs__default = /*#__PURE__*/_interopDefault(postcssJs); | ||
var postcss__default = /*#__PURE__*/_interopDefault(postcss); | ||
var cssesc__default = /*#__PURE__*/_interopDefault(cssesc); | ||
var hash__default = /*#__PURE__*/_interopDefault(hash); | ||
var each__default = /*#__PURE__*/_interopDefault(each); | ||
var omit__default = /*#__PURE__*/_interopDefault(omit); | ||
var isEqual__default = /*#__PURE__*/_interopDefault(isEqual); | ||
var mapKeys__default = /*#__PURE__*/_interopDefault(mapKeys); | ||
var dedent__default = /*#__PURE__*/_interopDefault(dedent); | ||
exports.generateCss = generateCss_dist_mattsjonesCssCoreGenerateCss.generateCss; | ||
function escapeRegex(string) { | ||
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); | ||
} | ||
const parser = new cssSelectorParser.CssSelectorParser(); | ||
parser.registerSelectorPseudos('has'); | ||
parser.registerNestingOperators('>', '+', '~'); | ||
parser.registerAttrEqualityMods('^', '$', '*', '~'); | ||
parser.enableSubstitutes(); | ||
const validateSelector = (selector, targetClassName) => { | ||
const replaceTarget = () => { | ||
const targetRegex = new RegExp(`.${escapeRegex(cssesc__default['default'](targetClassName, { | ||
isIdentifier: true | ||
}))}`, 'g'); | ||
return selector.replace(targetRegex, '&'); | ||
}; | ||
return selector.split(',').map(selectorPart => { | ||
if (selectorPart.indexOf(targetClassName) === -1) { | ||
throw new Error(dedent__default['default']` | ||
Invalid selector: ${replaceTarget()} | ||
Selectors must target the ampersand character ('&'), which refers to the generated class name, e.g. '&:nth-child(2n)' | ||
`); | ||
} | ||
let currentRule; | ||
try { | ||
const result = parser.parse(selectorPart); | ||
if (result.type === 'ruleSet') { | ||
currentRule = result.rule; | ||
} else { | ||
throw new Error(); | ||
} | ||
} catch (err) { | ||
throw new Error(`Invalid selector: ${replaceTarget()}`); | ||
} | ||
while (currentRule.rule) { | ||
currentRule = currentRule.rule; | ||
} | ||
const targetRule = currentRule; | ||
if (!Array.isArray(targetRule.classNames) || !targetRule.classNames.find(className => className === targetClassName)) { | ||
throw new Error(dedent__default['default']` | ||
Invalid selector: ${replaceTarget()} | ||
Style selectors must end with the '&' character (along with any modifiers), e.g. ${'`${parent} &`'} or ${'`${parent} &:hover`'}. | ||
This is to ensure that each style block only affects the styling of a single class. | ||
If your selector is targeting another class, you should move it to the style definition for that class, e.g. given we have styles for 'parent' and 'child' elements, instead of adding a selector of ${'`& ${child}`'}) to 'parent', you should add ${'`${parent} &`'} to 'child'). | ||
If your selector is targeting something global, use the 'globalStyle' function instead, e.g. if you wanted to write ${'`& h1`'}, you should instead write 'globalStyle(${'`${parent} h1`'}, { ... })' | ||
`); | ||
} | ||
}); | ||
}; | ||
const simplePseudos = [':-moz-any-link', ':-moz-full-screen', ':-moz-placeholder', ':-moz-read-only', ':-moz-read-write', ':-ms-fullscreen', ':-ms-input-placeholder', ':-webkit-any-link', ':-webkit-full-screen', '::-moz-placeholder', '::-moz-progress-bar', '::-moz-range-progress', '::-moz-range-thumb', '::-moz-range-track', '::-moz-selection', '::-ms-backdrop', '::-ms-browse', '::-ms-check', '::-ms-clear', '::-ms-fill', '::-ms-fill-lower', '::-ms-fill-upper', '::-ms-reveal', '::-ms-thumb', '::-ms-ticks-after', '::-ms-ticks-before', '::-ms-tooltip', '::-ms-track', '::-ms-value', '::-webkit-backdrop', '::-webkit-input-placeholder', '::-webkit-progress-bar', '::-webkit-progress-inner-value', '::-webkit-progress-value', '::-webkit-slider-runnable-track', '::-webkit-slider-thumb', '::after', '::backdrop', '::before', '::cue', '::first-letter', '::first-line', '::grammar-error', '::placeholder', '::selection', '::spelling-error', ':active', ':after', ':any-link', ':before', ':blank', ':checked', ':default', ':defined', ':disabled', ':empty', ':enabled', ':first', ':first-child', ':first-letter', ':first-line', ':first-of-type', ':focus', ':focus-visible', ':focus-within', ':fullscreen', ':hover', ':in-range', ':indeterminate', ':invalid', ':last-child', ':last-of-type', ':left', ':link', ':only-child', ':only-of-type', ':optional', ':out-of-range', ':placeholder-shown', ':read-only', ':read-write', ':required', ':right', ':root', ':scope', ':target', ':valid', ':visited']; | ||
const simplePseudoSet = new Set(simplePseudos); | ||
const specialKeys = [...simplePseudos, '@media', '@supports', 'selectors']; | ||
class Stylesheet { | ||
constructor(localClassNames) { | ||
this.rules = []; | ||
this.conditionalRules = []; | ||
this.localClassNameRegex = localClassNames.length > 0 ? RegExp(`(^|[^\.])(${localClassNames.join('|')})`, 'g') : null; | ||
} | ||
processCssObj(root) { | ||
// Add main styles | ||
const mainRule = omit__default['default'](root.rule, specialKeys); | ||
this.addRule({ | ||
selector: root.selector, | ||
rule: mainRule | ||
}); | ||
this.transformSimplePsuedos(root.rule, root.selector); | ||
this.transformMedia(root.rule['@media'], root.selector); | ||
this.transformSupports(root.rule['@supports'], root.selector); | ||
this.transformSelectors(root.rule, root.selector); | ||
} | ||
addRule(cssRule) { | ||
const rule = this.transformVars(this.transformRuleKeyframes(cssRule.rule)); | ||
const selector = this.transformSelector(cssRule.selector); | ||
if (cssRule.conditions) { | ||
this.conditionalRules.push({ | ||
selector, | ||
rule, | ||
conditions: cssRule.conditions.sort() | ||
}); | ||
} else { | ||
this.rules.push({ | ||
selector, | ||
rule | ||
}); | ||
} | ||
} | ||
transformVars({ | ||
vars, | ||
...rest | ||
}) { | ||
if (!vars) { | ||
return rest; | ||
} | ||
return { ...mapKeys__default['default'](vars, (_value, key) => { | ||
const matches = key.match(/^var\((.*)\)$/); | ||
if (matches) { | ||
return matches[1]; | ||
} | ||
return key; | ||
}), | ||
...rest | ||
}; | ||
} | ||
transformRuleKeyframes(rule) { | ||
let { | ||
'@keyframes': keyframes, | ||
animation, | ||
animationName, | ||
...rest | ||
} = rule; | ||
if (!keyframes && !rule.animation && !rule.animationName) { | ||
return rest; | ||
} | ||
let keyframesRef = typeof keyframes === 'string' ? keyframes : ''; | ||
if (keyframes && typeof keyframes !== 'string') { | ||
keyframesRef = cssesc__default['default'](hash__default['default'](JSON.stringify(keyframes)), { | ||
isIdentifier: true | ||
}); // Hoist keyframes to the top of the stylesheet | ||
this.rules.unshift({ | ||
selector: `@keyframes ${keyframesRef}`, | ||
rule: keyframes | ||
}); | ||
} | ||
return { ...rest, | ||
animation: animation && typeof animation === 'string' ? // @ts-expect-error Why??????????? | ||
animation.replace('@keyframes', keyframesRef) : animation, | ||
animationName: animationName ? undefined : keyframesRef | ||
}; | ||
} | ||
transformSelector(selector) { | ||
return this.localClassNameRegex ? selector.replace(this.localClassNameRegex, (_, leadingChar, className) => `${leadingChar}.${cssesc__default['default'](className, { | ||
isIdentifier: true | ||
})}`) : selector; | ||
} | ||
transformSelectors(rule, rootSelector, conditions) { | ||
each__default['default'](rule.selectors, (selectorRule, selector) => { | ||
const transformedSelector = this.transformSelector(selector.replace(RegExp('&', 'g'), rootSelector)); | ||
validateSelector(transformedSelector, rootSelector); | ||
this.addRule({ | ||
conditions, | ||
selector: transformedSelector, | ||
rule: selectorRule | ||
}); | ||
}); | ||
} | ||
transformMedia(rules, rootSelector, parentConditions = []) { | ||
each__default['default'](rules, (mediaRule, query) => { | ||
const conditions = [`@media ${query}`, ...parentConditions]; | ||
this.addRule({ | ||
conditions, | ||
selector: rootSelector, | ||
rule: omit__default['default'](mediaRule, specialKeys) | ||
}); | ||
this.transformSimplePsuedos(mediaRule, rootSelector, conditions); | ||
this.transformSelectors(mediaRule, rootSelector, conditions); | ||
this.transformSupports(mediaRule['@supports'], rootSelector, conditions); | ||
}); | ||
} | ||
transformSupports(rules, rootSelector, parentConditions = []) { | ||
each__default['default'](rules, (supportsRule, query) => { | ||
const conditions = [`@supports ${query}`, ...parentConditions]; | ||
this.addRule({ | ||
conditions, | ||
selector: rootSelector, | ||
rule: omit__default['default'](supportsRule, specialKeys) | ||
}); | ||
this.transformSimplePsuedos(supportsRule, rootSelector, conditions); | ||
this.transformSelectors(supportsRule, rootSelector, conditions); | ||
this.transformMedia(supportsRule['@media'], rootSelector, conditions); | ||
}); | ||
} | ||
transformSimplePsuedos(rule, rootSelector, conditions) { | ||
for (const key of Object.keys(rule)) { | ||
// Process simple psuedos | ||
if (simplePseudoSet.has(key)) { | ||
this.addRule({ | ||
conditions, | ||
selector: `${rootSelector}${key}`, | ||
rule: rule[key] | ||
}); | ||
} | ||
} | ||
} | ||
toPostcssJs() { | ||
const styles = {}; | ||
for (const rule of [...this.rules, ...this.conditionalRules]) { | ||
if (rule.conditions && isEqual__default['default'](styles[rule.selector], rule.rule)) { | ||
// Ignore conditional rules if they are identical to a non-conditional rule | ||
continue; | ||
} | ||
if (Object.keys(rule.rule).length === 0) { | ||
// Ignore empty rules | ||
continue; | ||
} | ||
let styleNode = styles; | ||
for (const condition of rule.conditions || []) { | ||
if (!styleNode[condition]) { | ||
styleNode[condition] = {}; | ||
} | ||
styleNode = styleNode[condition]; | ||
} | ||
styleNode[rule.selector] = { ...styleNode[rule.selector], | ||
...rule.rule | ||
}; | ||
} | ||
return styles; | ||
} | ||
} | ||
function transformCss({ | ||
localClassNames, | ||
cssObjs | ||
}) { | ||
const stylesheet = new Stylesheet(localClassNames); | ||
for (const root of cssObjs) { | ||
stylesheet.processCssObj(root); | ||
} | ||
return stylesheet.toPostcssJs(); | ||
} | ||
// @ts-expect-error | ||
function generateCss({ | ||
localClassNames, | ||
cssObjs | ||
}) { | ||
const flattenedCss = transformCss({ | ||
localClassNames, | ||
cssObjs | ||
}); | ||
const result = postcss__default['default']().process(flattenedCss, { | ||
parser: postcssJs__default['default'], | ||
from: undefined | ||
}); | ||
return result.root.nodes.map(node => node.toString()); | ||
} | ||
exports.generateCss = generateCss; |
@@ -1,11 +0,291 @@ | ||
import 'postcss-js'; | ||
import 'postcss'; | ||
export { g as generateCss } from '../../dist/generateCss-addefd6f.esm.js'; | ||
import '@emotion/hash'; | ||
import 'lodash/each'; | ||
import 'lodash/omit'; | ||
import 'lodash/isEqual'; | ||
import 'lodash/mapKeys'; | ||
import 'css-selector-parser'; | ||
import 'dedent'; | ||
import '../../adapter/dist/mattsjones-css-core-adapter.esm.js'; | ||
import postcssJs from 'postcss-js'; | ||
import postcss from 'postcss'; | ||
import cssesc from 'cssesc'; | ||
import hash from '@emotion/hash'; | ||
import each from 'lodash/each'; | ||
import omit from 'lodash/omit'; | ||
import isEqual from 'lodash/isEqual'; | ||
import mapKeys from 'lodash/mapKeys'; | ||
import { CssSelectorParser } from 'css-selector-parser'; | ||
import dedent from 'dedent'; | ||
function escapeRegex(string) { | ||
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); | ||
} | ||
const parser = new CssSelectorParser(); | ||
parser.registerSelectorPseudos('has'); | ||
parser.registerNestingOperators('>', '+', '~'); | ||
parser.registerAttrEqualityMods('^', '$', '*', '~'); | ||
parser.enableSubstitutes(); | ||
const validateSelector = (selector, targetClassName) => { | ||
const replaceTarget = () => { | ||
const targetRegex = new RegExp(`.${escapeRegex(cssesc(targetClassName, { | ||
isIdentifier: true | ||
}))}`, 'g'); | ||
return selector.replace(targetRegex, '&'); | ||
}; | ||
return selector.split(',').map(selectorPart => { | ||
if (selectorPart.indexOf(targetClassName) === -1) { | ||
throw new Error(dedent` | ||
Invalid selector: ${replaceTarget()} | ||
Selectors must target the ampersand character ('&'), which refers to the generated class name, e.g. '&:nth-child(2n)' | ||
`); | ||
} | ||
let currentRule; | ||
try { | ||
const result = parser.parse(selectorPart); | ||
if (result.type === 'ruleSet') { | ||
currentRule = result.rule; | ||
} else { | ||
throw new Error(); | ||
} | ||
} catch (err) { | ||
throw new Error(`Invalid selector: ${replaceTarget()}`); | ||
} | ||
while (currentRule.rule) { | ||
currentRule = currentRule.rule; | ||
} | ||
const targetRule = currentRule; | ||
if (!Array.isArray(targetRule.classNames) || !targetRule.classNames.find(className => className === targetClassName)) { | ||
throw new Error(dedent` | ||
Invalid selector: ${replaceTarget()} | ||
Style selectors must end with the '&' character (along with any modifiers), e.g. ${'`${parent} &`'} or ${'`${parent} &:hover`'}. | ||
This is to ensure that each style block only affects the styling of a single class. | ||
If your selector is targeting another class, you should move it to the style definition for that class, e.g. given we have styles for 'parent' and 'child' elements, instead of adding a selector of ${'`& ${child}`'}) to 'parent', you should add ${'`${parent} &`'} to 'child'). | ||
If your selector is targeting something global, use the 'globalStyle' function instead, e.g. if you wanted to write ${'`& h1`'}, you should instead write 'globalStyle(${'`${parent} h1`'}, { ... })' | ||
`); | ||
} | ||
}); | ||
}; | ||
const simplePseudos = [':-moz-any-link', ':-moz-full-screen', ':-moz-placeholder', ':-moz-read-only', ':-moz-read-write', ':-ms-fullscreen', ':-ms-input-placeholder', ':-webkit-any-link', ':-webkit-full-screen', '::-moz-placeholder', '::-moz-progress-bar', '::-moz-range-progress', '::-moz-range-thumb', '::-moz-range-track', '::-moz-selection', '::-ms-backdrop', '::-ms-browse', '::-ms-check', '::-ms-clear', '::-ms-fill', '::-ms-fill-lower', '::-ms-fill-upper', '::-ms-reveal', '::-ms-thumb', '::-ms-ticks-after', '::-ms-ticks-before', '::-ms-tooltip', '::-ms-track', '::-ms-value', '::-webkit-backdrop', '::-webkit-input-placeholder', '::-webkit-progress-bar', '::-webkit-progress-inner-value', '::-webkit-progress-value', '::-webkit-slider-runnable-track', '::-webkit-slider-thumb', '::after', '::backdrop', '::before', '::cue', '::first-letter', '::first-line', '::grammar-error', '::placeholder', '::selection', '::spelling-error', ':active', ':after', ':any-link', ':before', ':blank', ':checked', ':default', ':defined', ':disabled', ':empty', ':enabled', ':first', ':first-child', ':first-letter', ':first-line', ':first-of-type', ':focus', ':focus-visible', ':focus-within', ':fullscreen', ':hover', ':in-range', ':indeterminate', ':invalid', ':last-child', ':last-of-type', ':left', ':link', ':only-child', ':only-of-type', ':optional', ':out-of-range', ':placeholder-shown', ':read-only', ':read-write', ':required', ':right', ':root', ':scope', ':target', ':valid', ':visited']; | ||
const simplePseudoSet = new Set(simplePseudos); | ||
const specialKeys = [...simplePseudos, '@media', '@supports', 'selectors']; | ||
class Stylesheet { | ||
constructor(localClassNames) { | ||
this.rules = []; | ||
this.conditionalRules = []; | ||
this.localClassNameRegex = localClassNames.length > 0 ? RegExp(`(^|[^\.])(${localClassNames.join('|')})`, 'g') : null; | ||
} | ||
processCssObj(root) { | ||
// Add main styles | ||
const mainRule = omit(root.rule, specialKeys); | ||
this.addRule({ | ||
selector: root.selector, | ||
rule: mainRule | ||
}); | ||
this.transformSimplePsuedos(root.rule, root.selector); | ||
this.transformMedia(root.rule['@media'], root.selector); | ||
this.transformSupports(root.rule['@supports'], root.selector); | ||
this.transformSelectors(root.rule, root.selector); | ||
} | ||
addRule(cssRule) { | ||
const rule = this.transformVars(this.transformRuleKeyframes(cssRule.rule)); | ||
const selector = this.transformSelector(cssRule.selector); | ||
if (cssRule.conditions) { | ||
this.conditionalRules.push({ | ||
selector, | ||
rule, | ||
conditions: cssRule.conditions.sort() | ||
}); | ||
} else { | ||
this.rules.push({ | ||
selector, | ||
rule | ||
}); | ||
} | ||
} | ||
transformVars({ | ||
vars, | ||
...rest | ||
}) { | ||
if (!vars) { | ||
return rest; | ||
} | ||
return { ...mapKeys(vars, (_value, key) => { | ||
const matches = key.match(/^var\((.*)\)$/); | ||
if (matches) { | ||
return matches[1]; | ||
} | ||
return key; | ||
}), | ||
...rest | ||
}; | ||
} | ||
transformRuleKeyframes(rule) { | ||
let { | ||
'@keyframes': keyframes, | ||
animation, | ||
animationName, | ||
...rest | ||
} = rule; | ||
if (!keyframes && !rule.animation && !rule.animationName) { | ||
return rest; | ||
} | ||
let keyframesRef = typeof keyframes === 'string' ? keyframes : ''; | ||
if (keyframes && typeof keyframes !== 'string') { | ||
keyframesRef = cssesc(hash(JSON.stringify(keyframes)), { | ||
isIdentifier: true | ||
}); // Hoist keyframes to the top of the stylesheet | ||
this.rules.unshift({ | ||
selector: `@keyframes ${keyframesRef}`, | ||
rule: keyframes | ||
}); | ||
} | ||
return { ...rest, | ||
animation: animation && typeof animation === 'string' ? // @ts-expect-error Why??????????? | ||
animation.replace('@keyframes', keyframesRef) : animation, | ||
animationName: animationName ? undefined : keyframesRef | ||
}; | ||
} | ||
transformSelector(selector) { | ||
return this.localClassNameRegex ? selector.replace(this.localClassNameRegex, (_, leadingChar, className) => `${leadingChar}.${cssesc(className, { | ||
isIdentifier: true | ||
})}`) : selector; | ||
} | ||
transformSelectors(rule, rootSelector, conditions) { | ||
each(rule.selectors, (selectorRule, selector) => { | ||
const transformedSelector = this.transformSelector(selector.replace(RegExp('&', 'g'), rootSelector)); | ||
validateSelector(transformedSelector, rootSelector); | ||
this.addRule({ | ||
conditions, | ||
selector: transformedSelector, | ||
rule: selectorRule | ||
}); | ||
}); | ||
} | ||
transformMedia(rules, rootSelector, parentConditions = []) { | ||
each(rules, (mediaRule, query) => { | ||
const conditions = [`@media ${query}`, ...parentConditions]; | ||
this.addRule({ | ||
conditions, | ||
selector: rootSelector, | ||
rule: omit(mediaRule, specialKeys) | ||
}); | ||
this.transformSimplePsuedos(mediaRule, rootSelector, conditions); | ||
this.transformSelectors(mediaRule, rootSelector, conditions); | ||
this.transformSupports(mediaRule['@supports'], rootSelector, conditions); | ||
}); | ||
} | ||
transformSupports(rules, rootSelector, parentConditions = []) { | ||
each(rules, (supportsRule, query) => { | ||
const conditions = [`@supports ${query}`, ...parentConditions]; | ||
this.addRule({ | ||
conditions, | ||
selector: rootSelector, | ||
rule: omit(supportsRule, specialKeys) | ||
}); | ||
this.transformSimplePsuedos(supportsRule, rootSelector, conditions); | ||
this.transformSelectors(supportsRule, rootSelector, conditions); | ||
this.transformMedia(supportsRule['@media'], rootSelector, conditions); | ||
}); | ||
} | ||
transformSimplePsuedos(rule, rootSelector, conditions) { | ||
for (const key of Object.keys(rule)) { | ||
// Process simple psuedos | ||
if (simplePseudoSet.has(key)) { | ||
this.addRule({ | ||
conditions, | ||
selector: `${rootSelector}${key}`, | ||
rule: rule[key] | ||
}); | ||
} | ||
} | ||
} | ||
toPostcssJs() { | ||
const styles = {}; | ||
for (const rule of [...this.rules, ...this.conditionalRules]) { | ||
if (rule.conditions && isEqual(styles[rule.selector], rule.rule)) { | ||
// Ignore conditional rules if they are identical to a non-conditional rule | ||
continue; | ||
} | ||
if (Object.keys(rule.rule).length === 0) { | ||
// Ignore empty rules | ||
continue; | ||
} | ||
let styleNode = styles; | ||
for (const condition of rule.conditions || []) { | ||
if (!styleNode[condition]) { | ||
styleNode[condition] = {}; | ||
} | ||
styleNode = styleNode[condition]; | ||
} | ||
styleNode[rule.selector] = { ...styleNode[rule.selector], | ||
...rule.rule | ||
}; | ||
} | ||
return styles; | ||
} | ||
} | ||
function transformCss({ | ||
localClassNames, | ||
cssObjs | ||
}) { | ||
const stylesheet = new Stylesheet(localClassNames); | ||
for (const root of cssObjs) { | ||
stylesheet.processCssObj(root); | ||
} | ||
return stylesheet.toPostcssJs(); | ||
} | ||
// @ts-expect-error | ||
function generateCss({ | ||
localClassNames, | ||
cssObjs | ||
}) { | ||
const flattenedCss = transformCss({ | ||
localClassNames, | ||
cssObjs | ||
}); | ||
const result = postcss().process(flattenedCss, { | ||
parser: postcssJs, | ||
from: undefined | ||
}); | ||
return result.root.nodes.map(node => node.toString()); | ||
} | ||
export { generateCss }; |
{ | ||
"name": "@mattsjones/css-core", | ||
"version": "0.0.10", | ||
"version": "0.0.11", | ||
"main": "dist/mattsjones-css-core.cjs.js", | ||
@@ -29,2 +29,3 @@ "module": "dist/mattsjones-css-core.esm.js", | ||
"css-selector-parser": "^1.4.1", | ||
"cssesc": "^3.0.0", | ||
"csstype": "^3.0.7", | ||
@@ -37,2 +38,3 @@ "dedent": "^0.7.0", | ||
"devDependencies": { | ||
"@types/cssesc": "^3.0.0", | ||
"@types/dedent": "^0.7.0", | ||
@@ -39,0 +41,0 @@ "@types/lodash": "^4.14.168" |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2517
101122
8
3
42
1
+ Addedcssesc@^3.0.0
+ Addedcssesc@3.0.0(transitive)