@linaria/server
Advanced tools
Comparing version 4.0.0 to 4.1.0
@@ -0,24 +1,36 @@ | ||
import postcss from 'postcss'; | ||
/** | ||
* This utility extracts critical CSS from given HTML and CSS file to be used in SSR environments | ||
* Used to escape `RegExp` | ||
* [syntax characters](https://262.ecma-international.org/7.0/#sec-regular-expressions-patterns). | ||
*/ | ||
import postcss from 'postcss'; | ||
const extractClassesFromHtml = html => { | ||
function escapeRegex(string) { | ||
return string.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&'); | ||
} | ||
const extractClassesFromHtml = (html, ignoredClasses) => { | ||
const htmlClasses = []; | ||
const regex = /\s+class="([^"]+)"/gm; | ||
let match = regex.exec(html); | ||
const ignoredClassesDeduped = new Set(ignoredClasses); | ||
while (match !== null) { | ||
match[1].split(' ').forEach(className => { | ||
// eslint-disable-next-line no-param-reassign | ||
className = className.replace(/\\|\^|\$|\{|\}|\[|\]|\(|\)|\.|\*|\+|\?|\|/g, '\\$&'); | ||
htmlClasses.push(className); | ||
className = escapeRegex(className); | ||
if (className !== '' && !ignoredClassesDeduped.has(className)) { | ||
htmlClasses.push(className); | ||
} | ||
}); | ||
match = regex.exec(html); | ||
} | ||
return new RegExp(htmlClasses.join('|'), 'gm'); | ||
}; | ||
export default function collect(html, css) { | ||
/** | ||
* This utility extracts critical CSS from given HTML and CSS file to be used in SSR environments | ||
* @param {string} html the HTML from which classes will be parsed | ||
* @param {string} css the CSS file from which selectors will be parsed and determined as critical or other | ||
* @param {string[]} ignoredClasses classes that, when present in the HTML, will not be included in the regular expression used to match selectors | ||
* @param {string[]} blockedClasses classes that, when contained in a selector, will cause the selector to be marked as not critical | ||
* @returns {CollectResult} object containing the critical and other CSS styles | ||
*/ | ||
export default function collect(html, css, classnameModifiers) { | ||
const animations = new Set(); | ||
@@ -28,33 +40,39 @@ const other = postcss.root(); | ||
const stylesheet = postcss.parse(css); | ||
const htmlClassesRegExp = extractClassesFromHtml(html); | ||
const ignoredClasses = classnameModifiers?.ignoredClasses ?? []; | ||
const blockedClasses = classnameModifiers?.blockedClasses ?? []; | ||
const htmlClassesRegExp = extractClassesFromHtml(html, ignoredClasses); | ||
const blockedClassesSanitized = blockedClasses.map(escapeRegex); | ||
const blockedClassesRegExp = new RegExp(blockedClassesSanitized.join('|'), 'gm'); | ||
const isCritical = rule => { | ||
// Only check class names selectors | ||
if ('selector' in rule && rule.selector.startsWith('.')) { | ||
const isExcluded = blockedClasses.length > 0 && blockedClassesRegExp.test(rule.selector); | ||
if (isExcluded) return false; | ||
return Boolean(rule.selector.match(htmlClassesRegExp)); | ||
} | ||
return true; | ||
}; | ||
const handleAtRule = rule => { | ||
let addedToCritical = false; | ||
rule.each(childRule => { | ||
if (isCritical(childRule) && !addedToCritical) { | ||
critical.append(rule.clone()); | ||
addedToCritical = true; | ||
} | ||
}); | ||
if (rule.name === 'keyframes') { | ||
return; | ||
} | ||
if (addedToCritical) { | ||
rule.remove(); | ||
} else { | ||
other.append(rule); | ||
const criticalRule = rule.clone(); | ||
const otherRule = rule.clone(); | ||
let removedNodesFromOther = 0; | ||
criticalRule.each((childRule, index) => { | ||
if (isCritical(childRule)) { | ||
otherRule.nodes[index - removedNodesFromOther]?.remove(); | ||
removedNodesFromOther += 1; | ||
} else { | ||
childRule.remove(); | ||
} | ||
}); | ||
rule.remove(); | ||
if (criticalRule.nodes.length > 0) { | ||
critical.append(criticalRule); | ||
} | ||
if (otherRule.nodes.length > 0) { | ||
other.append(otherRule); | ||
} | ||
}; | ||
stylesheet.walkAtRules('font-face', rule => { | ||
@@ -74,3 +92,2 @@ /** | ||
} | ||
if (rule.parent?.type === 'atrule') { | ||
@@ -81,6 +98,4 @@ if (!walkedAtRules.has(rule.parent)) { | ||
} | ||
return; | ||
} | ||
if (isCritical(rule)) { | ||
@@ -87,0 +102,0 @@ critical.append(rule); |
@@ -7,70 +7,83 @@ "use strict"; | ||
exports.default = collect; | ||
var _postcss = _interopRequireDefault(require("postcss")); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
/** | ||
* This utility extracts critical CSS from given HTML and CSS file to be used in SSR environments | ||
* Used to escape `RegExp` | ||
* [syntax characters](https://262.ecma-international.org/7.0/#sec-regular-expressions-patterns). | ||
*/ | ||
const extractClassesFromHtml = html => { | ||
function escapeRegex(string) { | ||
return string.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&'); | ||
} | ||
const extractClassesFromHtml = (html, ignoredClasses) => { | ||
const htmlClasses = []; | ||
const regex = /\s+class="([^"]+)"/gm; | ||
let match = regex.exec(html); | ||
const ignoredClassesDeduped = new Set(ignoredClasses); | ||
while (match !== null) { | ||
match[1].split(' ').forEach(className => { | ||
// eslint-disable-next-line no-param-reassign | ||
className = className.replace(/\\|\^|\$|\{|\}|\[|\]|\(|\)|\.|\*|\+|\?|\|/g, '\\$&'); | ||
htmlClasses.push(className); | ||
className = escapeRegex(className); | ||
if (className !== '' && !ignoredClassesDeduped.has(className)) { | ||
htmlClasses.push(className); | ||
} | ||
}); | ||
match = regex.exec(html); | ||
} | ||
return new RegExp(htmlClasses.join('|'), 'gm'); | ||
}; | ||
function collect(html, css) { | ||
/** | ||
* This utility extracts critical CSS from given HTML and CSS file to be used in SSR environments | ||
* @param {string} html the HTML from which classes will be parsed | ||
* @param {string} css the CSS file from which selectors will be parsed and determined as critical or other | ||
* @param {string[]} ignoredClasses classes that, when present in the HTML, will not be included in the regular expression used to match selectors | ||
* @param {string[]} blockedClasses classes that, when contained in a selector, will cause the selector to be marked as not critical | ||
* @returns {CollectResult} object containing the critical and other CSS styles | ||
*/ | ||
function collect(html, css, classnameModifiers) { | ||
var _classnameModifiers$i, _classnameModifiers$b; | ||
const animations = new Set(); | ||
const other = _postcss.default.root(); | ||
const critical = _postcss.default.root(); | ||
const stylesheet = _postcss.default.parse(css); | ||
const htmlClassesRegExp = extractClassesFromHtml(html); | ||
const ignoredClasses = (_classnameModifiers$i = classnameModifiers === null || classnameModifiers === void 0 ? void 0 : classnameModifiers.ignoredClasses) !== null && _classnameModifiers$i !== void 0 ? _classnameModifiers$i : []; | ||
const blockedClasses = (_classnameModifiers$b = classnameModifiers === null || classnameModifiers === void 0 ? void 0 : classnameModifiers.blockedClasses) !== null && _classnameModifiers$b !== void 0 ? _classnameModifiers$b : []; | ||
const htmlClassesRegExp = extractClassesFromHtml(html, ignoredClasses); | ||
const blockedClassesSanitized = blockedClasses.map(escapeRegex); | ||
const blockedClassesRegExp = new RegExp(blockedClassesSanitized.join('|'), 'gm'); | ||
const isCritical = rule => { | ||
// Only check class names selectors | ||
if ('selector' in rule && rule.selector.startsWith('.')) { | ||
const isExcluded = blockedClasses.length > 0 && blockedClassesRegExp.test(rule.selector); | ||
if (isExcluded) return false; | ||
return Boolean(rule.selector.match(htmlClassesRegExp)); | ||
} | ||
return true; | ||
}; | ||
const handleAtRule = rule => { | ||
let addedToCritical = false; | ||
rule.each(childRule => { | ||
if (isCritical(childRule) && !addedToCritical) { | ||
critical.append(rule.clone()); | ||
addedToCritical = true; | ||
} | ||
}); | ||
if (rule.name === 'keyframes') { | ||
return; | ||
} | ||
if (addedToCritical) { | ||
rule.remove(); | ||
} else { | ||
other.append(rule); | ||
const criticalRule = rule.clone(); | ||
const otherRule = rule.clone(); | ||
let removedNodesFromOther = 0; | ||
criticalRule.each((childRule, index) => { | ||
if (isCritical(childRule)) { | ||
var _otherRule$nodes; | ||
(_otherRule$nodes = otherRule.nodes[index - removedNodesFromOther]) === null || _otherRule$nodes === void 0 ? void 0 : _otherRule$nodes.remove(); | ||
removedNodesFromOther += 1; | ||
} else { | ||
childRule.remove(); | ||
} | ||
}); | ||
rule.remove(); | ||
if (criticalRule.nodes.length > 0) { | ||
critical.append(criticalRule); | ||
} | ||
if (otherRule.nodes.length > 0) { | ||
other.append(otherRule); | ||
} | ||
}; | ||
stylesheet.walkAtRules('font-face', rule => { | ||
var _rule$parent; | ||
/** | ||
@@ -87,7 +100,5 @@ * @font-face rules may be defined also in CSS conditional groups (eg. @media) | ||
var _rule$parent2; | ||
if (rule.parent && 'name' in rule.parent && rule.parent.name === 'keyframes') { | ||
return; | ||
} | ||
if (((_rule$parent2 = rule.parent) === null || _rule$parent2 === void 0 ? void 0 : _rule$parent2.type) === 'atrule') { | ||
@@ -98,6 +109,4 @@ if (!walkedAtRules.has(rule.parent)) { | ||
} | ||
return; | ||
} | ||
if (isCritical(rule)) { | ||
@@ -104,0 +113,0 @@ critical.append(rule); |
@@ -12,6 +12,4 @@ "use strict"; | ||
}); | ||
var _collect = _interopRequireDefault(require("./collect")); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@linaria/server", | ||
"version": "4.1.0", | ||
"description": "Blazing fast zero-runtime CSS in JS library", | ||
"version": "4.0.0", | ||
"keywords": [ | ||
"css", | ||
"css-in-js", | ||
"linaria", | ||
"react", | ||
"styled-components" | ||
], | ||
"homepage": "https://github.com/callstack/linaria#readme", | ||
"bugs": "https://github.com/callstack/linaria/issues", | ||
"repository": "git@github.com:callstack/linaria.git", | ||
"license": "MIT", | ||
"main": "lib/index.js", | ||
"module": "esm/index.js", | ||
"types": "types", | ||
"files": [ | ||
"types/", | ||
"lib/", | ||
"esm/" | ||
], | ||
"dependencies": { | ||
@@ -17,25 +35,7 @@ "postcss": "^8.3.11" | ||
}, | ||
"files": [ | ||
"types/", | ||
"lib/", | ||
"esm/" | ||
], | ||
"homepage": "https://github.com/callstack/linaria#readme", | ||
"keywords": [ | ||
"css", | ||
"css-in-js", | ||
"linaria", | ||
"react", | ||
"styled-components" | ||
], | ||
"license": "MIT", | ||
"main": "lib/index.js", | ||
"module": "esm/index.js", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"repository": "git@github.com:callstack/linaria.git", | ||
"types": "types", | ||
"scripts": { | ||
"build": "npm run build:lib && npm run build:esm && npm run build:declarations", | ||
"build": "pnpm build:lib && pnpm build:esm && pnpm build:declarations", | ||
"build:declarations": "tsc --emitDeclarationOnly --outDir types", | ||
@@ -45,4 +45,4 @@ "build:esm": "babel src --out-dir esm --extensions '.js,.jsx,.ts,.tsx' --source-maps --delete-dir-on-start", | ||
"typecheck": "tsc --noEmit --composite false", | ||
"watch": "npm run build --watch" | ||
"watch": "pnpm build:lib --watch & pnpm build:esm --watch & pnpm build:declarations --watch" | ||
} | ||
} |
@@ -1,4 +0,1 @@ | ||
/** | ||
* This utility extracts critical CSS from given HTML and CSS file to be used in SSR environments | ||
*/ | ||
declare type CollectResult = { | ||
@@ -8,3 +5,15 @@ critical: string; | ||
}; | ||
export default function collect(html: string, css: string): CollectResult; | ||
interface ClassnameModifiers { | ||
ignoredClasses?: string[]; | ||
blockedClasses?: string[]; | ||
} | ||
/** | ||
* This utility extracts critical CSS from given HTML and CSS file to be used in SSR environments | ||
* @param {string} html the HTML from which classes will be parsed | ||
* @param {string} css the CSS file from which selectors will be parsed and determined as critical or other | ||
* @param {string[]} ignoredClasses classes that, when present in the HTML, will not be included in the regular expression used to match selectors | ||
* @param {string[]} blockedClasses classes that, when contained in a selector, will cause the selector to be marked as not critical | ||
* @returns {CollectResult} object containing the critical and other CSS styles | ||
*/ | ||
export default function collect(html: string, css: string, classnameModifiers?: ClassnameModifiers): CollectResult; | ||
export {}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
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
33266
272