Comparing version 1.2.0 to 1.3.0
@@ -0,3 +1,5 @@ | ||
export declare const CLASS_IDENT_REGEX: RegExp; | ||
export declare function ignoreCSS(re: RegExp | undefined): void; | ||
export declare function extractClasses(sel: string): string[]; | ||
export declare function monitorCSS(): void; | ||
export default function checkCSS(): Set<any> | undefined; | ||
export default function checkCSS(): void; |
@@ -0,1 +1,4 @@ | ||
// Regex for identifying class names in CSS selectors | ||
// REF: https://www.w3.org/TR/selectors-3/#lex | ||
export const CLASS_IDENT_REGEX = /\.-?(?:[_a-z]|[^\0-\x7f]|\\[0-9a-f]{1,6}\s?|\\[^\s0-9a-f])(?:[_a-z0-9-]|[^\0-\x7f]|\\[0-9a-f]{1,6}\s?|\\[^\s0-9a-f])*/gi; | ||
const seen = new Set(); | ||
@@ -10,5 +13,2 @@ let defined; | ||
for (const cl of node.classList) { | ||
// Ignore if matches the ignore regex | ||
if (ignoreRE?.test(cl)) | ||
continue; | ||
// Ignore defined and already-seen classes | ||
@@ -19,2 +19,5 @@ if (defined.has(cl) || seen.has(cl)) | ||
seen.add(cl); | ||
// Ignore classes that mathc the ignore regex | ||
if (ignoreRE?.test(cl)) | ||
continue; | ||
console.warn(`Undefined CSS class: ${cl}`, node); | ||
@@ -29,24 +32,34 @@ } | ||
} | ||
function isGroupingRule(rule) { | ||
return 'cssRules' in rule; | ||
} | ||
function isCSSStyleRule(rule) { | ||
return 'selectorText' in rule; | ||
} | ||
export function extractClasses(sel) { | ||
const classnames = sel.match(CLASS_IDENT_REGEX) ?? []; | ||
return classnames.map(c => { | ||
// Strip '.' | ||
c = c.substring(1); | ||
// Unescape numeric escape sequences (\###) | ||
c = c.replaceAll(/\\[0-9a-f]{1,6}\s?/gi, escape => { | ||
return String.fromCodePoint(parseInt(escape.substring(1), 16)); | ||
}); | ||
// Unescape character escape sequences (\[some char]) | ||
c = c.replaceAll(/\\[^\s0-9a-f]/g, c => c.substring(1)); | ||
return c; | ||
}); | ||
} | ||
function ingestRules(rules) { | ||
for (const rule of rules) { | ||
if (!rule) | ||
continue; | ||
try { | ||
rule.cssRules; | ||
} | ||
catch (err) { | ||
console.log(`Unable to access ${rule.href}`); | ||
continue; | ||
} | ||
if (rule?.cssRules) { | ||
// Rules can contain sub-rules (e.g. @media, @print) | ||
if (isGroupingRule(rule)) { | ||
// Some rules are groups of rules (e.g. CSSMediaRule), so we need to | ||
// recurse into them | ||
ingestRules(rule.cssRules); | ||
} | ||
else if (rule.selectorText) { | ||
// Get defined classes. (Regex here could probably use improvement) | ||
const classes = rule.selectorText?.match(/\.[\w-]+/g); | ||
if (classes) { | ||
for (const cl of classes) { | ||
defined.add(cl.substr(1)); | ||
} | ||
else if (isCSSStyleRule(rule)) { | ||
// Add each classname to the defined set | ||
for (const classname of extractClasses(rule.selectorText)) { | ||
console.log('INGESTING', classname); | ||
defined.add(classname); | ||
} | ||
@@ -82,3 +95,3 @@ } | ||
childList: true, | ||
subtree: true | ||
subtree: true, | ||
}); | ||
@@ -88,6 +101,8 @@ } | ||
if (defined) | ||
return defined; | ||
return; | ||
defined = new Set(); | ||
// Cache | ||
ingestRules(document.styleSheets); | ||
// Ingest rules from all stylesheets | ||
for (const sheet of document.styleSheets) { | ||
ingestRules(sheet.cssRules); | ||
} | ||
// Do a sweep of the existing DOM | ||
@@ -94,0 +109,0 @@ checkClassNames(document.documentElement, true); |
{ | ||
"name": "checkcss", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"type": "module", | ||
@@ -9,3 +9,3 @@ "description": "Utility method for warning when elements have a `class` attribute that refers to an undefined CSS class", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1", | ||
"test": "node test/test.js", | ||
"prepare": "rm -fr dist && yarn build", | ||
@@ -12,0 +12,0 @@ "build": "tsc", |
@@ -0,1 +1,6 @@ | ||
// Regex for identifying class names in CSS selectors | ||
// REF: https://www.w3.org/TR/selectors-3/#lex | ||
export const CLASS_IDENT_REGEX = | ||
/\.-?(?:[_a-z]|[^\0-\x7f]|\\[0-9a-f]{1,6}\s?|\\[^\s0-9a-f])(?:[_a-z0-9-]|[^\0-\x7f]|\\[0-9a-f]{1,6}\s?|\\[^\s0-9a-f])*/gi; | ||
const seen = new Set(); | ||
@@ -10,12 +15,13 @@ let defined: Set<any>; | ||
function checkClassNames(node: Element, includeChildren = false) { | ||
if (node?.classList) { | ||
if (node?.classList) { | ||
for (const cl of node.classList) { | ||
// Ignore if matches the ignore regex | ||
if (ignoreRE?.test(cl)) continue; | ||
// Ignore defined and already-seen classes | ||
if (defined.has(cl) || seen.has(cl)) continue; | ||
// Mark as seen | ||
seen.add(cl); | ||
// Ignore classes that mathc the ignore regex | ||
if (ignoreRE?.test(cl)) continue; | ||
console.warn(`Undefined CSS class: ${cl}`, node); | ||
@@ -32,19 +38,38 @@ } | ||
function ingestRules(rules: CSSRuleList | StyleSheetList) { | ||
function isGroupingRule(rule: CSSRule): rule is CSSGroupingRule { | ||
return 'cssRules' in rule; | ||
} | ||
function isCSSStyleRule(rule: CSSRule): rule is CSSStyleRule { | ||
return 'selectorText' in rule; | ||
} | ||
export function extractClasses(sel: string) { | ||
const classnames = sel.match(CLASS_IDENT_REGEX) ?? []; | ||
return classnames.map(c => { | ||
// Strip '.' | ||
c = c.substring(1); | ||
// Unescape numeric escape sequences (\###) | ||
c = c.replaceAll(/\\[0-9a-f]{1,6}\s?/gi, escape => { | ||
return String.fromCodePoint(parseInt(escape.substring(1), 16)); | ||
}); | ||
// Unescape character escape sequences (\[some char]) | ||
c = c.replaceAll(/\\[^\s0-9a-f]/g, c => c.substring(1)); | ||
return c; | ||
}); | ||
} | ||
function ingestRules(rules: CSSRuleList) { | ||
for (const rule of rules) { | ||
if (!rule) continue; | ||
try { | ||
(rule as CSSStyleSheet).cssRules; | ||
} catch (err) { | ||
console.log(`Unable to access ${(rule as CSSStyleSheet).href}`); | ||
continue; | ||
} | ||
if ((rule as CSSStyleSheet)?.cssRules) { | ||
// Rules can contain sub-rules (e.g. @media, @print) | ||
ingestRules((rule as CSSStyleSheet).cssRules); | ||
} else if ((rule as CSSStyleRule).selectorText) { | ||
// Get defined classes. (Regex here could probably use improvement) | ||
const classes = (rule as CSSStyleRule).selectorText?.match(/\.[\w-]+/g); | ||
if (classes) { | ||
for (const cl of classes) { defined.add(cl.substr(1)); } | ||
if (isGroupingRule(rule)) { | ||
// Some rules are groups of rules (e.g. CSSMediaRule), so we need to | ||
// recurse into them | ||
ingestRules(rule.cssRules); | ||
} else if (isCSSStyleRule(rule)) { | ||
// Add each classname to the defined set | ||
for (const classname of extractClasses(rule.selectorText)) { | ||
console.log('INGESTING', classname); | ||
defined.add(classname); | ||
} | ||
@@ -80,3 +105,3 @@ } | ||
childList: true, | ||
subtree: true | ||
subtree: true, | ||
}); | ||
@@ -86,7 +111,9 @@ } | ||
export default function checkCSS() { | ||
if (defined) return defined; | ||
if (defined) return; | ||
defined = new Set(); | ||
// Cache | ||
ingestRules(document.styleSheets); | ||
// Ingest rules from all stylesheets | ||
for (const sheet of document.styleSheets) { | ||
ingestRules(sheet.cssRules); | ||
} | ||
@@ -93,0 +120,0 @@ // Do a sweep of the existing DOM |
Sorry, the diff of this file is not supported yet
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
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
14474
9
257
1
0