UNPKG

@aesthetic/style

Version:

A low-level, high-performance, atomic-based CSS-in-JS style engine.

245 lines (190 loc) 7.22 kB
// Bundled with Packemon: https://packemon.dev // Platform: browser, Support: stable, Format: esm import { arrayReduce, arrayLoop, nonce, objectReduce, objectLoop } from '@aesthetic/utils'; import sortMediaQueries from 'sort-css-media-queries'; import { S as STYLE_RULE, M as MEDIA_RULE, a as SUPPORTS_RULE, k as isAtRule, F as FONT_FACE_RULE, K as KEYFRAMES_RULE, I as IMPORT_RULE, l as isImportRule, j as insertImportRule, h as insertAtRule, i as insertRule, w as formatVariableBlock, b as createStyleEngine, d as createCacheManager, f as formatVariable } from './bundle-30cd3868.js'; class TransientSheet { constructor(type = STYLE_RULE, rule = '') { this.conditionText = ''; this.cssRules = []; this.cssVariables = {}; this.textContent = ''; this.type = void 0; this.rule = void 0; this.rule = rule; this.type = type; if (type === MEDIA_RULE || type === SUPPORTS_RULE) { this.rule = ''; this.conditionText = rule.slice(0, rule.indexOf('{') - 1).replace(this.conditionAtRule, '').trim(); } } get cssText() { const css = arrayReduce(this.cssRules, rule => rule.cssText, this.rule); if (this.type === MEDIA_RULE || this.type === SUPPORTS_RULE) { return `${this.conditionAtRule} ${this.conditionText} { ${css} }`; } return css; } insertRule(rule, index) { this.cssRules.splice(index, 0, new TransientSheet(this.determineType(rule), rule)); return index; } get conditionAtRule() { return this.type === MEDIA_RULE ? '@media' : '@supports'; } determineType(rule) { if (!isAtRule(rule)) { return STYLE_RULE; } if (rule.startsWith('@media')) { return MEDIA_RULE; } if (rule.startsWith('@supports')) { return SUPPORTS_RULE; } if (rule.startsWith('@font-face')) { return FONT_FACE_RULE; } if (rule.startsWith('@keyframes')) { return KEYFRAMES_RULE; } if (rule.startsWith('@import')) { return IMPORT_RULE; } return STYLE_RULE; } } function findNestedRule(sheet, query, type) { // eslint-disable-next-line @typescript-eslint/prefer-for-of for (let i = 0; i < sheet.cssRules.length; i += 1) { const child = sheet.cssRules[i]; if (child && child.type === type && child.conditionText === query) { return child; } } return null; } function insertFeatureRule(manager, sheet, query, rule, parentSheet) { const formattedRule = `@supports ${query} { ${rule} }`; // Already exists so append a new rule if (parentSheet && parentSheet !== sheet) { return parentSheet.insertRule(formattedRule, parentSheet.cssRules.length); } // Insert the rule and capture the instance const index = insertRule(sheet, formattedRule); manager.featureQueries[query] = sheet.cssRules[index]; return index; } function insertMediaRule(manager, sheet, query, rule, parentSheet) { const formattedRule = `@media ${query} { ${rule} }`; // Already exists so append a new rule (except for root sorting) if (parentSheet && parentSheet !== sheet) { return parentSheet.insertRule(formattedRule, parentSheet.cssRules.length); } // Sort and determine the index in which to insert a new query const sortedQueries = [...Object.keys(manager.mediaQueries), query].sort(sortMediaQueries); const index = sortedQueries.indexOf(query); insertRule(sheet, formattedRule, index); manager.mediaQueries[query] = sheet.cssRules[index]; return index; } function insertConditionRule(manager, sheet, rule, conditions) { let parent = sheet; arrayLoop(conditions, ({ query, type }) => { const instance = findNestedRule(parent, query, type); // Nested found, so continue without inserting a new rule if (instance) { parent = instance; return; } const index = type === MEDIA_RULE ? insertMediaRule(manager, sheet, query, '', parent) : insertFeatureRule(manager, sheet, query, '', parent); parent = parent.cssRules[index]; }); return parent.insertRule(rule, parent.cssRules.length); } function createSheetManager(sheets) { const manager = { featureQueries: {}, insertRule(rule, options, index) { const sheet = sheets[options.type ?? (options.media || options.supports ? 'conditions' : 'standard')]; // Imports highest precedence if (isImportRule(rule)) { return insertImportRule(sheet, rule); } // Media and feature queries require special treatment if (options.media || options.supports) { const conditions = []; if (options.supports) { conditions.push({ query: options.supports, type: SUPPORTS_RULE }); } if (options.media) { conditions.push({ query: options.media, type: MEDIA_RULE }); } return insertConditionRule(manager, sheet, rule, conditions); } // Font faces and keyframes lowest precedence if (isAtRule(rule)) { return insertAtRule(sheet, rule); } return insertRule(sheet, rule, index); }, mediaQueries: {}, sheets }; return manager; } function extractCssFromSheet(sheet) { let css = ''; if (Object.keys(sheet.cssVariables).length > 0) { css += `:root { ${formatVariableBlock(sheet.cssVariables)} }`; } css += sheet.cssText; return css; } function getStyleElementAttributes(type, sheet, ruleCount) { return { 'data-aesthetic-hydrate-index': sheet.cssRules.length - 1, 'data-aesthetic-rule-count': ruleCount, 'data-aesthetic-type': type, id: `aesthetic-${type}`, media: 'screen', nonce: nonce(), type: 'text/css' }; } function createStyleElement(type, sheet, ruleCount) { const css = extractCssFromSheet(sheet); const attrs = objectReduce(getStyleElementAttributes(type, sheet, ruleCount), (value, key) => value === undefined ? '' : ` ${key}="${value}"`); return `<style ${attrs}>${css}</style>`; } function renderToStyleMarkup(engine) { return createStyleElement('global', engine.sheetManager.sheets.global, engine.ruleCount) + createStyleElement('standard', engine.sheetManager.sheets.standard, engine.ruleCount) + createStyleElement('conditions', engine.sheetManager.sheets.conditions, engine.ruleCount); } function setRootVariables(engine, vars) { const sheet = engine.sheetManager.sheets.global; objectLoop(vars, (value, key) => { sheet.cssVariables[formatVariable(key)] = String(value); }); } function extractStyles(engine, result) { global.AESTHETIC_CUSTOM_ENGINE = engine; process.env.AESTHETIC_SSR = 'true'; return result; } function createServerEngine(options = {}) { const engine = createStyleEngine({ cacheManager: createCacheManager(), sheetManager: createSheetManager({ conditions: new TransientSheet(), global: new TransientSheet(), standard: new TransientSheet() }), ...options }); engine.setRootVariables = vars => void setRootVariables(engine, vars); engine.extractStyles = result => extractStyles(engine, result); return engine; } export { createServerEngine, extractCssFromSheet, getStyleElementAttributes, renderToStyleMarkup }; //# sourceMappingURL=server.js.map