@glitz/core
Advanced tools
Comparing version 1.0.0-beta.3 to 1.0.0-beta.4
@@ -5,7 +5,4 @@ import Base, { DEFAULT_HYDRATE_CLASS_NAME } from '../core/Base'; | ||
import InjectorClient from './InjectorClient'; | ||
import Validator from './Validator'; | ||
export let Parent = Base; | ||
if (process.env.NODE_ENV !== 'production') { | ||
Parent = Validator; | ||
} | ||
import createValidator from './Validator'; | ||
export let Parent = process.env.NODE_ENV !== 'production' ? createValidator() : Base; | ||
export default class GlitzClient extends Parent { | ||
@@ -12,0 +9,0 @@ constructor(styleElements, options = {}) { |
@@ -1,5 +0,3 @@ | ||
import { Style } from '@glitz/type'; | ||
import Base from '../core/Base'; | ||
export default class Validator extends Base { | ||
injectStyle(style: Style): string; | ||
} | ||
export declare let createValidator: () => typeof Base; | ||
export default createValidator; |
import Base from '../core/Base'; | ||
import { validateMixingShorthandLonghand } from '../utils/mixing-shorthand-longhand'; | ||
export default class Validator extends Base { | ||
injectStyle(style) { | ||
const classNames = super.injectStyle(style); | ||
validateMixingShorthandLonghand(style, classNames); | ||
return classNames; | ||
} | ||
export let createValidator = () => Base; | ||
if (process.env.NODE_ENV !== 'production') { | ||
createValidator = () => class extends Base { | ||
injectStyle(style) { | ||
const classNames = super.injectStyle(style); | ||
validateMixingShorthandLonghand(style, classNames); | ||
return classNames; | ||
} | ||
}; | ||
} | ||
export default createValidator; |
import { Style } from '@glitz/type'; | ||
export declare function validateMixingShorthandLonghand(style: Style, classNames: string): void; | ||
export declare let validateMixingShorthandLonghand: (style: Style, classNames: string) => void; |
import { hyphenateProperty } from '../utils/parse'; | ||
const borderColor = ['border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color']; | ||
const borderStyle = ['border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style']; | ||
const borderWidth = ['border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width']; | ||
const shorthands = { | ||
animation: [ | ||
'animation-name', | ||
'animation-duration', | ||
'animation-timing-function', | ||
'animation-delay', | ||
'animation-iteration-count', | ||
'animation-direction', | ||
'animation-fill-mode', | ||
'animation-play-state', | ||
], | ||
background: [ | ||
'background-image', | ||
'background-position', | ||
'background-size', | ||
'background-repeat', | ||
'background-origin', | ||
'background-clip', | ||
'background-attachment', | ||
'background-color', | ||
], | ||
border: ['border-width', 'border-style', 'border-color'].concat(borderColor, borderStyle, borderWidth), | ||
'border-bottom': ['border-bottom-width', 'border-bottom-style', 'border-bottom-color'], | ||
'border-color': borderColor, | ||
'border-image': [ | ||
'border-image-source', | ||
'border-image-slice', | ||
'border-image-width', | ||
'border-image-outset', | ||
'border-image-repeat', | ||
], | ||
'border-left': ['border-left-width', 'border-left-style', 'border-left-color'], | ||
'border-radius': [ | ||
'border-top-left-radius', | ||
'border-top-right-radius', | ||
'border-bottom-right-radius', | ||
'border-bottom-left-radius', | ||
], | ||
'border-right': ['border-right-width', 'border-right-style', 'border-right-color'], | ||
'border-style': borderStyle, | ||
'border-top': ['border-top-width', 'border-top-style', 'border-top-color'], | ||
'border-width': borderWidth, | ||
'column-rule': ['column-rule-width', 'column-rule-style', 'column-rule-color'], | ||
columns: ['column-width', 'column-count'], | ||
cue: ['cue-before', 'cue-after'], | ||
flex: ['flex-grow', 'flex-shrink', 'flex-basis'], | ||
'flex-flow': ['flex-direction', 'flex-wrap'], | ||
font: ['font-style', 'font-variant', 'font-weight', 'font-stretch', 'font-size', 'line-height', 'font-family'], | ||
'font-variant': [ | ||
'font-variant-ligatures', | ||
'font-variant-alternates', | ||
'font-variant-caps', | ||
'font-variant-numeric', | ||
'font-variant-east-asian', | ||
], | ||
grid: [ | ||
'grid-template-rows', | ||
'grid-template-columns', | ||
'grid-template-areas', | ||
'grid-auto-rows', | ||
'grid-auto-columns', | ||
'grid-auto-flow', | ||
], | ||
'grid-area': ['grid-row-start', 'grid-column-start', 'grid-row-end', 'grid-column-end'], | ||
'grid-column': ['grid-column-start', 'grid-column-end'], | ||
'grid-row': ['grid-row-start', 'grid-row-end'], | ||
'grid-template': ['grid-template-rows', 'grid-template-columns', 'grid-template-areas'], | ||
'list-style': ['list-style-type', 'list-style-position', 'list-style-image'], | ||
margin: ['margin-top', 'margin-right', 'margin-bottom', 'margin-left'], | ||
mask: ['mask-image', 'mask-mode', 'mask-position', 'mask-size', 'mask-repeat', 'mask-origin', 'mask-clip'], | ||
'mask-border': [ | ||
'mask-border-source', | ||
'mask-border-slice', | ||
'mask-border-width', | ||
'mask-border-outset', | ||
'mask-border-repeat', | ||
'mask-border-mode', | ||
], | ||
outline: ['outline-width', 'outline-style', 'outline-color'], | ||
padding: ['padding-top', 'padding-right', 'padding-bottom', 'padding-left'], | ||
pause: ['pause-before', 'pause-after'], | ||
'place-content': ['align-content', 'justify-content'], | ||
'place-items': ['align-items', 'justify-items'], | ||
'place-self': ['align-self', 'justify-self'], | ||
rest: ['rest-before', 'rest-after'], | ||
'text-decoration': ['text-decoration-line', 'text-decoration-style', 'text-decoration-color'], | ||
'text-emphasis': ['text-emphasis-style', 'text-emphasis-color'], | ||
transition: ['transition-property', 'transition-duration', 'transition-timing-function', 'transition-delay'], | ||
export let validateMixingShorthandLonghand = () => { | ||
/* noop */ | ||
}; | ||
export function validateMixingShorthandLonghand(style, classNames) { | ||
const hyphenatedProperties = Object.keys(style).reduce((properties, property) => { | ||
properties[hyphenateProperty(property)] = property; | ||
return properties; | ||
}, {}); | ||
for (const property in hyphenatedProperties) { | ||
if (property in shorthands) { | ||
for (const longhand of shorthands[property]) { | ||
if (longhand in hyphenatedProperties) { | ||
console.error('Injected style resulting in class name `%s` had a longhand property `%s` mixed with its corresponding shorthand property `%s` in %O which may likely cause some unexpected behavior. Replace `padding` with longhand properties to solve the issue.', classNames, hyphenatedProperties[longhand], hyphenatedProperties[property], style); | ||
if (process.env.NODE_ENV !== 'production') { | ||
const borderWidth = ['border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width']; | ||
const borderStyle = ['border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style']; | ||
const borderColor = ['border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color']; | ||
const shorthands = { | ||
animation: [ | ||
'animation-name', | ||
'animation-duration', | ||
'animation-timing-function', | ||
'animation-delay', | ||
'animation-iteration-count', | ||
'animation-direction', | ||
'animation-fill-mode', | ||
'animation-play-state', | ||
], | ||
background: [ | ||
'background-image', | ||
'background-position', | ||
'background-size', | ||
'background-repeat', | ||
'background-origin', | ||
'background-clip', | ||
'background-attachment', | ||
'background-color', | ||
], | ||
border: ['border-width', 'border-style', 'border-color'].concat(borderWidth, borderStyle, borderColor), | ||
'border-bottom': ['border-bottom-width', 'border-bottom-style', 'border-bottom-color'], | ||
'border-color': borderColor, | ||
'border-image': [ | ||
'border-image-source', | ||
'border-image-slice', | ||
'border-image-width', | ||
'border-image-outset', | ||
'border-image-repeat', | ||
], | ||
'border-left': ['border-left-width', 'border-left-style', 'border-left-color'], | ||
'border-radius': [ | ||
'border-top-left-radius', | ||
'border-top-right-radius', | ||
'border-bottom-right-radius', | ||
'border-bottom-left-radius', | ||
], | ||
'border-right': ['border-right-width', 'border-right-style', 'border-right-color'], | ||
'border-style': borderStyle, | ||
'border-top': ['border-top-width', 'border-top-style', 'border-top-color'], | ||
'border-width': borderWidth, | ||
'column-rule': ['column-rule-width', 'column-rule-style', 'column-rule-color'], | ||
columns: ['column-width', 'column-count'], | ||
cue: ['cue-before', 'cue-after'], | ||
flex: ['flex-grow', 'flex-shrink', 'flex-basis'], | ||
'flex-flow': ['flex-direction', 'flex-wrap'], | ||
font: ['font-style', 'font-variant', 'font-weight', 'font-stretch', 'font-size', 'line-height', 'font-family'], | ||
'font-variant': [ | ||
'font-variant-ligatures', | ||
'font-variant-alternates', | ||
'font-variant-caps', | ||
'font-variant-numeric', | ||
'font-variant-east-asian', | ||
], | ||
grid: [ | ||
'grid-template-rows', | ||
'grid-template-columns', | ||
'grid-template-areas', | ||
'grid-auto-rows', | ||
'grid-auto-columns', | ||
'grid-auto-flow', | ||
], | ||
'grid-area': ['grid-row-start', 'grid-column-start', 'grid-row-end', 'grid-column-end'], | ||
'grid-column': ['grid-column-start', 'grid-column-end'], | ||
'grid-row': ['grid-row-start', 'grid-row-end'], | ||
'grid-template': ['grid-template-rows', 'grid-template-columns', 'grid-template-areas'], | ||
'list-style': ['list-style-type', 'list-style-position', 'list-style-image'], | ||
margin: ['margin-top', 'margin-right', 'margin-bottom', 'margin-left'], | ||
mask: ['mask-image', 'mask-mode', 'mask-position', 'mask-size', 'mask-repeat', 'mask-origin', 'mask-clip'], | ||
'mask-border': [ | ||
'mask-border-source', | ||
'mask-border-slice', | ||
'mask-border-width', | ||
'mask-border-outset', | ||
'mask-border-repeat', | ||
'mask-border-mode', | ||
], | ||
outline: ['outline-width', 'outline-style', 'outline-color'], | ||
padding: ['padding-top', 'padding-right', 'padding-bottom', 'padding-left'], | ||
pause: ['pause-before', 'pause-after'], | ||
'place-content': ['align-content', 'justify-content'], | ||
'place-items': ['align-items', 'justify-items'], | ||
'place-self': ['align-self', 'justify-self'], | ||
rest: ['rest-before', 'rest-after'], | ||
'text-decoration': ['text-decoration-line', 'text-decoration-style', 'text-decoration-color'], | ||
'text-emphasis': ['text-emphasis-style', 'text-emphasis-color'], | ||
transition: ['transition-property', 'transition-duration', 'transition-timing-function', 'transition-delay'], | ||
}; | ||
validateMixingShorthandLonghand = (style, classNames) => { | ||
const hyphenatedProperties = Object.keys(style).reduce((properties, property) => { | ||
properties[hyphenateProperty(property)] = property; | ||
return properties; | ||
}, {}); | ||
for (const property in hyphenatedProperties) { | ||
if (property in shorthands) { | ||
for (const longhand of shorthands[property]) { | ||
if (longhand in hyphenatedProperties) { | ||
console.error('Injected style resulting in class name `%s` had a longhand property `%s` mixed with its corresponding shorthand property `%s` in %O which may likely cause some unexpected behavior. Replace `padding` with longhand properties to solve the issue.', classNames, hyphenatedProperties[longhand], hyphenatedProperties[property], style, hyphenatedProperties[property]); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
} |
{ | ||
"name": "@glitz/core", | ||
"version": "1.0.0-beta.3", | ||
"main": "./es/index.js", | ||
"version": "1.0.0-beta.4", | ||
"main": "./cjs/glitz.client.js", | ||
"modules": "./es/index.js", | ||
"types": "./es/index.d.ts", | ||
@@ -11,14 +12,16 @@ "license": "MIT", | ||
"bugs": "https://github.com/frenic/glitz/issues", | ||
"side-effects": false, | ||
"dependencies": { | ||
"@glitz/type": "^1.0.0-beta.2" | ||
"@glitz/type": "^1.0.0-beta.3" | ||
}, | ||
"scripts": { | ||
"clean": "rimraf es server", | ||
"build": "tsc -p tsconfig.build.json && node ../../bundle.js src/server.ts server/index && echo export * from '../es/server'; >> server/index.d.ts && echo export {default} from '../es/server'; >> server/index.d.ts", | ||
"clean": "rimraf cjs es", | ||
"build": "tsc -p tsconfig.build.json && node ../../bundle.js src/index.ts cjs/glitz.client --double && node ../../bundle.js src/server.ts cjs/glitz.server", | ||
"prepublish": "npm run clean & npm run build" | ||
}, | ||
"files": [ | ||
"es", | ||
"server" | ||
"cjs/", | ||
"es/", | ||
"server/" | ||
] | ||
} |
@@ -1,2 +0,2 @@ | ||
export * from '../es/server'; | ||
export {default} from '../es/server'; | ||
export * from '../es/server'; | ||
export { default } from '../es/server'; |
@@ -1,270 +0,1 @@ | ||
const DEFAULT_HYDRATE_CLASS_NAME = '__glitz__'; | ||
class Base { | ||
constructor(injector, transformer) { | ||
const declarator = transformer | ||
? (property, value) => transformer(declaration(property, value)) | ||
: declaration; | ||
const declarationCache = {}; | ||
const inject = (this.inject = (style, media, pseudo) => { | ||
let classNames = ''; | ||
for (const property in style) { | ||
classNames += ' '; | ||
const value = style[property]; | ||
if (typeof value === 'string' || typeof value === 'number') { | ||
// Only supports caching of primitive values | ||
let cache = declarationCache; | ||
if (media && pseudo) { | ||
const pseudoCache = (declarationCache[media] = | ||
declarationCache[media] || {}); | ||
cache = pseudoCache[pseudo] = pseudoCache[pseudo] || {}; | ||
} | ||
else if (media) { | ||
cache = (declarationCache[media] = declarationCache[media] || {}); | ||
} | ||
else if (pseudo) { | ||
cache = (declarationCache[pseudo] = declarationCache[pseudo] || {}); | ||
} | ||
const cachedValues = (cache[property] = cache[property] || {}); | ||
if (cachedValues[value]) { | ||
classNames += cachedValues[value]; | ||
continue; | ||
} | ||
const className = injector(media).injectClassRule(declarator(property, value), pseudo); | ||
if (className) { | ||
cachedValues[value] = className; | ||
} | ||
classNames += className; | ||
continue; | ||
} | ||
if (typeof value === 'object') { | ||
// Pseudo | ||
if (property[0] === ':') { | ||
const combinedPseudo = (pseudo || '') + property; | ||
classNames += inject(value, media, combinedPseudo); | ||
continue; | ||
} | ||
if (property.indexOf('@media') === 0) { | ||
const combinedMedia = (media ? `${media} and ` : '') + property.slice(7); | ||
classNames += inject(value, combinedMedia, pseudo); | ||
continue; | ||
} | ||
if (property === '@keyframes') { | ||
const name = injector().injectKeyframesRule(value); | ||
if (name) { | ||
classNames += inject({ animationName: name }); | ||
continue; | ||
} | ||
} | ||
if (Array.isArray(value)) { | ||
classNames += injector(media).injectClassRule(declarator(property, value), pseudo); | ||
continue; | ||
} | ||
} | ||
if (process.env.NODE_ENV !== 'production') { | ||
console.error('The style property `%s` does not support the value `%o`', property, value); | ||
} | ||
} | ||
return classNames.slice(1); | ||
}); | ||
} | ||
injectStyle(style) { | ||
return this.inject(style); | ||
} | ||
} | ||
function declaration(property, value) { | ||
return { [property]: value }; | ||
} | ||
function createHashCounter(prefix = '') { | ||
let count = 0; | ||
let offset = 10; | ||
let msb = 35; | ||
let power = 1; | ||
const increment = () => { | ||
const virtualCount = count + offset; | ||
if (virtualCount === msb) { | ||
offset += (msb + 1) * 9; | ||
msb = Math.pow(36, ++power) - 1; | ||
} | ||
count++; | ||
// Skip "ad" due to ad-blockers | ||
if (virtualCount === 373) { | ||
return increment(); | ||
} | ||
return prefix + virtualCount.toString(36); | ||
}; | ||
return increment; | ||
} | ||
function formatRule(identifier, block) { | ||
return `${identifier}{${block}}`; | ||
} | ||
function formatClassRule(className, block, pseudo = '') { | ||
return `.${formatRule(className + pseudo, block)}`; | ||
} | ||
function formatKeyframesRule(name, blockList) { | ||
return `@keyframes ${formatRule(name, blockList)}`; | ||
} | ||
function parseDeclarationBlock(style) { | ||
let block = ''; | ||
let property; | ||
for (property in style) { | ||
const value = style[property]; | ||
if (typeof value === 'object') { | ||
for (const fallback of value) { | ||
if (block) { | ||
block += ';'; | ||
} | ||
block += parseDeclaration(property, fallback); | ||
} | ||
} | ||
else { | ||
if (block) { | ||
block += ';'; | ||
} | ||
block += parseDeclaration(property, value); | ||
} | ||
} | ||
return block; | ||
} | ||
function parseDeclaration(property, value) { | ||
if (typeof value === 'string' || typeof value === 'number') { | ||
if (process.env.NODE_ENV !== 'production') { | ||
if (value === '') { | ||
console.warn('Style property `%s` as empty string may cause some unexpected behavior', property); | ||
} | ||
if (typeof value === 'number' && Number.isNaN(value)) { | ||
console.warn('Style property `%s` as NaN may cause some unexpected behavior', property); | ||
} | ||
if (typeof value === 'number' && !Number.isFinite(value)) { | ||
console.warn('Style property `%s` as an infinite number may cause some unexpected behavior', property); | ||
} | ||
} | ||
return `${hyphenateProperty(property)}:${value}`; | ||
} | ||
if (process.env.NODE_ENV !== 'production') { | ||
console.error('Style property `%s` of type `%s` is not supported', property, typeof value); | ||
} | ||
return ''; | ||
} | ||
const uppercaseRegex = /[A-Z]/g; | ||
const prefixRegex = /^(ms|moz|webkit)/; | ||
const propertyCache = {}; | ||
function hyphenateProperty(property) { | ||
return property in propertyCache | ||
? propertyCache[property] | ||
: (propertyCache[property] = property | ||
.replace(uppercaseRegex, '-$&') | ||
.replace(prefixRegex, '-$&') | ||
.toLowerCase()); | ||
} | ||
class Injector { | ||
constructor(plainDictionary, pseudoDictionary, keyframesDictionary, incrementClassHash, incrementKeyframesHash, injectNewClassRule, injectNewKeyframesRule) { | ||
this.injectClassRule = (style, pseudo) => { | ||
const block = parseDeclarationBlock(style); | ||
if (block) { | ||
const dictionary = pseudo ? (pseudoDictionary[pseudo] = pseudoDictionary[pseudo] || {}) : plainDictionary; | ||
const existingClassName = dictionary[block]; | ||
if (existingClassName) { | ||
return existingClassName; | ||
} | ||
else { | ||
const className = incrementClassHash(); | ||
dictionary[block] = className; | ||
if (injectNewClassRule) { | ||
injectNewClassRule(className, block, pseudo); | ||
} | ||
return className; | ||
} | ||
} | ||
return ''; | ||
}; | ||
this.injectKeyframesRule = styleList => { | ||
let blockList = ''; | ||
for (const identifier in styleList) { | ||
const keyframeBlock = parseDeclarationBlock(styleList[identifier]); | ||
blockList += formatRule(identifier, keyframeBlock); | ||
} | ||
if (blockList) { | ||
const existingName = keyframesDictionary[blockList]; | ||
if (existingName) { | ||
return existingName; | ||
} | ||
else { | ||
const name = incrementKeyframesHash(); | ||
keyframesDictionary[blockList] = name; | ||
if (injectNewKeyframesRule) { | ||
injectNewKeyframesRule(name, blockList); | ||
} | ||
return name; | ||
} | ||
} | ||
return ''; | ||
}; | ||
} | ||
} | ||
class InjectorServer extends Injector { | ||
constructor(incrementClassHash = createHashCounter(), incrementKeyframesHash = createHashCounter()) { | ||
const plainDictionary = {}; | ||
const pseudoDictionary = {}; | ||
const keyframesDictionary = {}; | ||
super(plainDictionary, pseudoDictionary, keyframesDictionary, incrementClassHash, incrementKeyframesHash); | ||
this.getStyle = () => { | ||
let style = ''; | ||
for (const block in plainDictionary) { | ||
style += formatClassRule(plainDictionary[block], block); | ||
} | ||
for (const pseudo in pseudoDictionary) { | ||
const dictionary = pseudoDictionary[pseudo]; | ||
for (const block in dictionary) { | ||
style += formatClassRule(dictionary[block], block, pseudo); | ||
} | ||
} | ||
for (const blockList in keyframesDictionary) { | ||
style += formatKeyframesRule(keyframesDictionary[blockList], blockList); | ||
} | ||
return style; | ||
}; | ||
} | ||
} | ||
class GlitzServer extends Base { | ||
constructor(options = {}) { | ||
const classHasher = createHashCounter(options.prefix); | ||
const keyframesHasher = createHashCounter(options.prefix); | ||
let mainInjector; | ||
const mediaInjectors = {}; | ||
const injector = (media) => media | ||
? (mediaInjectors[media] = mediaInjectors[media] || new InjectorServer(classHasher, keyframesHasher)) | ||
: (mainInjector = mainInjector || new InjectorServer(classHasher, keyframesHasher)); | ||
super(injector, options.transformer); | ||
this.getStyleMarkup = (className = DEFAULT_HYDRATE_CLASS_NAME) => { | ||
let markup = ''; | ||
if (mainInjector) { | ||
markup += `<style class="${className}">${mainInjector.getStyle()}</style>`; | ||
} | ||
if (options.mediaOrder) { | ||
const orderedMedias = Object.keys(mediaInjectors).sort(options.mediaOrder); | ||
for (const media of orderedMedias) { | ||
markup += `<style class="${className}" media="${media}">${mediaInjectors[media].getStyle()}</style>`; | ||
} | ||
} | ||
else { | ||
for (const media in mediaInjectors) { | ||
markup += `<style class="${className}" media="${media}">${mediaInjectors[media].getStyle()}</style>`; | ||
} | ||
} | ||
return markup; | ||
}; | ||
} | ||
} | ||
function compose(...fns) { | ||
return fns.reduceRight((prevFn, nextFn) => value => nextFn(prevFn(value)), value => value); | ||
} | ||
export default GlitzServer; | ||
export { compose }; | ||
module.exports = require('../cjs/glitz.server'); |
Mixed license
License(Experimental) Package contains multiple licenses.
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
71786
40
1501
0
1
7
Updated@glitz/type@^1.0.0-beta.3