@productivemark/snipcss 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +17 -0
- package/.claude-plugin/plugin.json +10 -0
- package/.mcp.json +8 -0
- package/dist/auth/config-manager.d.ts +13 -0
- package/dist/auth/config-manager.d.ts.map +1 -0
- package/dist/auth/config-manager.js +48 -0
- package/dist/auth/config-manager.js.map +1 -0
- package/dist/auth/usage-gate.d.ts +13 -0
- package/dist/auth/usage-gate.d.ts.map +1 -0
- package/dist/auth/usage-gate.js +69 -0
- package/dist/auth/usage-gate.js.map +1 -0
- package/dist/browser/browser-manager.d.ts +15 -0
- package/dist/browser/browser-manager.d.ts.map +1 -0
- package/dist/browser/browser-manager.js +61 -0
- package/dist/browser/browser-manager.js.map +1 -0
- package/dist/browser/viewport-manager.d.ts +8 -0
- package/dist/browser/viewport-manager.d.ts.map +1 -0
- package/dist/browser/viewport-manager.js +50 -0
- package/dist/browser/viewport-manager.js.map +1 -0
- package/dist/extraction/css-variable-resolver.d.ts +27 -0
- package/dist/extraction/css-variable-resolver.d.ts.map +1 -0
- package/dist/extraction/css-variable-resolver.js +105 -0
- package/dist/extraction/css-variable-resolver.js.map +1 -0
- package/dist/extraction/dom-labeler.d.ts +26 -0
- package/dist/extraction/dom-labeler.d.ts.map +1 -0
- package/dist/extraction/dom-labeler.js +124 -0
- package/dist/extraction/dom-labeler.js.map +1 -0
- package/dist/extraction/element-discovery.d.ts +59 -0
- package/dist/extraction/element-discovery.d.ts.map +1 -0
- package/dist/extraction/element-discovery.js +525 -0
- package/dist/extraction/element-discovery.js.map +1 -0
- package/dist/extraction/extraction-pipeline.d.ts +26 -0
- package/dist/extraction/extraction-pipeline.d.ts.map +1 -0
- package/dist/extraction/extraction-pipeline.js +200 -0
- package/dist/extraction/extraction-pipeline.js.map +1 -0
- package/dist/extraction/font-collector.d.ts +26 -0
- package/dist/extraction/font-collector.d.ts.map +1 -0
- package/dist/extraction/font-collector.js +160 -0
- package/dist/extraction/font-collector.js.map +1 -0
- package/dist/extraction/html-cleaner.d.ts +16 -0
- package/dist/extraction/html-cleaner.d.ts.map +1 -0
- package/dist/extraction/html-cleaner.js +149 -0
- package/dist/extraction/html-cleaner.js.map +1 -0
- package/dist/extraction/keyframe-collector.d.ts +16 -0
- package/dist/extraction/keyframe-collector.d.ts.map +1 -0
- package/dist/extraction/keyframe-collector.js +62 -0
- package/dist/extraction/keyframe-collector.js.map +1 -0
- package/dist/extraction/pseudo-state-handler.d.ts +36 -0
- package/dist/extraction/pseudo-state-handler.d.ts.map +1 -0
- package/dist/extraction/pseudo-state-handler.js +210 -0
- package/dist/extraction/pseudo-state-handler.js.map +1 -0
- package/dist/extraction/result-builder.d.ts +25 -0
- package/dist/extraction/result-builder.d.ts.map +1 -0
- package/dist/extraction/result-builder.js +136 -0
- package/dist/extraction/result-builder.js.map +1 -0
- package/dist/extraction/rule-deduplicator.d.ts +39 -0
- package/dist/extraction/rule-deduplicator.d.ts.map +1 -0
- package/dist/extraction/rule-deduplicator.js +107 -0
- package/dist/extraction/rule-deduplicator.js.map +1 -0
- package/dist/extraction/selector-fixer.d.ts +25 -0
- package/dist/extraction/selector-fixer.d.ts.map +1 -0
- package/dist/extraction/selector-fixer.js +111 -0
- package/dist/extraction/selector-fixer.js.map +1 -0
- package/dist/extraction/specificity.d.ts +17 -0
- package/dist/extraction/specificity.d.ts.map +1 -0
- package/dist/extraction/specificity.js +88 -0
- package/dist/extraction/specificity.js.map +1 -0
- package/dist/extraction/style-matcher.d.ts +33 -0
- package/dist/extraction/style-matcher.d.ts.map +1 -0
- package/dist/extraction/style-matcher.js +199 -0
- package/dist/extraction/style-matcher.js.map +1 -0
- package/dist/extraction/stylesheet-collector.d.ts +33 -0
- package/dist/extraction/stylesheet-collector.d.ts.map +1 -0
- package/dist/extraction/stylesheet-collector.js +71 -0
- package/dist/extraction/stylesheet-collector.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +235 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server.d.ts +3 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +349 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/tailwind/css-to-tailwind.d.ts +17 -0
- package/dist/tailwind/css-to-tailwind.d.ts.map +1 -0
- package/dist/tailwind/css-to-tailwind.js +1583 -0
- package/dist/tailwind/css-to-tailwind.js.map +1 -0
- package/dist/tailwind/shorthand-expander.d.ts +27 -0
- package/dist/tailwind/shorthand-expander.d.ts.map +1 -0
- package/dist/tailwind/shorthand-expander.js +812 -0
- package/dist/tailwind/shorthand-expander.js.map +1 -0
- package/dist/tailwind/tailwind-converter.d.ts +35 -0
- package/dist/tailwind/tailwind-converter.d.ts.map +1 -0
- package/dist/tailwind/tailwind-converter.js +1223 -0
- package/dist/tailwind/tailwind-converter.js.map +1 -0
- package/dist/tailwind/tailwind-helpers.d.ts +95 -0
- package/dist/tailwind/tailwind-helpers.d.ts.map +1 -0
- package/dist/tailwind/tailwind-helpers.js +593 -0
- package/dist/tailwind/tailwind-helpers.js.map +1 -0
- package/dist/tailwind/tailwind-reducer.d.ts +36 -0
- package/dist/tailwind/tailwind-reducer.d.ts.map +1 -0
- package/dist/tailwind/tailwind-reducer.js +189 -0
- package/dist/tailwind/tailwind-reducer.js.map +1 -0
- package/dist/types/index.d.ts +239 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +94 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/helpers.d.ts +34 -0
- package/dist/utils/helpers.d.ts.map +1 -0
- package/dist/utils/helpers.js +120 -0
- package/dist/utils/helpers.js.map +1 -0
- package/dist/utils/parsel.d.ts +41 -0
- package/dist/utils/parsel.d.ts.map +1 -0
- package/dist/utils/parsel.js +314 -0
- package/dist/utils/parsel.js.map +1 -0
- package/package.json +41 -0
- package/skills/workflow/SKILL.md +95 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collects @keyframes animation rules from matched styles.
|
|
3
|
+
* Port of snipbackground.js:1997-2035
|
|
4
|
+
*/
|
|
5
|
+
export class KeyframeCollector {
|
|
6
|
+
/**
|
|
7
|
+
* Extract keyframe animations from matched styles.
|
|
8
|
+
*/
|
|
9
|
+
collect(allMatchedStyles, ctx) {
|
|
10
|
+
const keyframesRules = allMatchedStyles.cssKeyframesRules || [];
|
|
11
|
+
for (const keyframesMatch of keyframesRules) {
|
|
12
|
+
const animationName = keyframesMatch.animationName?.text;
|
|
13
|
+
if (!animationName)
|
|
14
|
+
continue;
|
|
15
|
+
// Skip if already collected
|
|
16
|
+
if (ctx.animationKeyframesArr[animationName])
|
|
17
|
+
continue;
|
|
18
|
+
const keyframes = {};
|
|
19
|
+
for (const aKeyframe of keyframesMatch.keyframes) {
|
|
20
|
+
const keyText = aKeyframe.keyText?.text || '';
|
|
21
|
+
const keyStyle = aKeyframe.style;
|
|
22
|
+
if (!keyStyle || !keyStyle.cssProperties)
|
|
23
|
+
continue;
|
|
24
|
+
const props = [];
|
|
25
|
+
for (const prop of keyStyle.cssProperties) {
|
|
26
|
+
if (!prop.name || !prop.value)
|
|
27
|
+
continue;
|
|
28
|
+
if (prop.disabled)
|
|
29
|
+
continue;
|
|
30
|
+
if (prop.parsedOk === false)
|
|
31
|
+
continue;
|
|
32
|
+
const important = prop.important ? ' !important' : '';
|
|
33
|
+
props.push(` ${prop.name}: ${prop.value}${important};`);
|
|
34
|
+
}
|
|
35
|
+
if (props.length > 0) {
|
|
36
|
+
keyframes[keyText] = props.join('\n');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (Object.keys(keyframes).length > 0) {
|
|
40
|
+
ctx.animationKeyframesArr[animationName] = keyframes;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Generate CSS text for all collected keyframes.
|
|
46
|
+
*/
|
|
47
|
+
generateCss(ctx) {
|
|
48
|
+
const lines = [];
|
|
49
|
+
for (const [name, keyframes] of Object.entries(ctx.animationKeyframesArr)) {
|
|
50
|
+
lines.push(`@keyframes ${name} {`);
|
|
51
|
+
for (const [keyText, props] of Object.entries(keyframes)) {
|
|
52
|
+
lines.push(` ${keyText} {`);
|
|
53
|
+
lines.push(props);
|
|
54
|
+
lines.push(' }');
|
|
55
|
+
}
|
|
56
|
+
lines.push('}');
|
|
57
|
+
lines.push('');
|
|
58
|
+
}
|
|
59
|
+
return lines.join('\n');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=keyframe-collector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyframe-collector.js","sourceRoot":"","sources":["../../src/extraction/keyframe-collector.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,OAAO,iBAAiB;IAC5B;;OAEG;IACH,OAAO,CAAC,gBAAkC,EAAE,GAAsB;QAChE,MAAM,cAAc,GAAG,gBAAgB,CAAC,iBAAiB,IAAI,EAAE,CAAC;QAEhE,KAAK,MAAM,cAAc,IAAI,cAAc,EAAE,CAAC;YAC5C,MAAM,aAAa,GAAG,cAAc,CAAC,aAAa,EAAE,IAAI,CAAC;YACzD,IAAI,CAAC,aAAa;gBAAE,SAAS;YAE7B,4BAA4B;YAC5B,IAAI,GAAG,CAAC,qBAAqB,CAAC,aAAa,CAAC;gBAAE,SAAS;YAEvD,MAAM,SAAS,GAA2B,EAAE,CAAC;YAE7C,KAAK,MAAM,SAAS,IAAI,cAAc,CAAC,SAAS,EAAE,CAAC;gBACjD,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC;gBAC9C,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC;gBAEjC,IAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,CAAC,aAAa;oBAAE,SAAS;gBAEnD,MAAM,KAAK,GAAa,EAAE,CAAC;gBAC3B,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,aAAa,EAAE,CAAC;oBAC1C,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK;wBAAE,SAAS;oBACxC,IAAI,IAAI,CAAC,QAAQ;wBAAE,SAAS;oBAC5B,IAAI,IAAI,CAAC,QAAQ,KAAK,KAAK;wBAAE,SAAS;oBAEtC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;oBACtD,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,GAAG,SAAS,GAAG,CAAC,CAAC;gBAC3D,CAAC;gBAED,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACrB,SAAS,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACxC,CAAC;YACH,CAAC;YAED,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtC,GAAG,CAAC,qBAAqB,CAAC,aAAa,CAAC,GAAG,SAAS,CAAC;YACvD,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,GAAsB;QAChC,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,EAAE,CAAC;YAC1E,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,CAAC;YACnC,KAAK,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAmC,CAAC,EAAE,CAAC;gBACnF,KAAK,CAAC,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC;gBAC7B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAClB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpB,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChB,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;CACF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { CDPSession, Page } from 'playwright';
|
|
2
|
+
import { CDPRuleMatch } from '../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Handles forcing pseudo-states (:hover, :active, :checked) on elements
|
|
5
|
+
* and extracting the resulting matched styles.
|
|
6
|
+
*
|
|
7
|
+
* Port of snipbackground.js:1822-1995
|
|
8
|
+
*/
|
|
9
|
+
export declare class PseudoStateHandler {
|
|
10
|
+
/**
|
|
11
|
+
* Check if an element is a hover-capable tag.
|
|
12
|
+
*/
|
|
13
|
+
getTagName(page: Page, classname: string): Promise<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Check if an input element is a checkbox or radio.
|
|
16
|
+
*/
|
|
17
|
+
isCheckboxOrRadio(page: Page, classname: string): Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* Get parent element snipcss classnames for pseudo-state forcing.
|
|
20
|
+
* Parents also need hover forced so that parent:hover child selectors work.
|
|
21
|
+
*/
|
|
22
|
+
getParentClassnames(page: Page, classname: string): Promise<string[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Force hover/active pseudo-state on an element and its parents,
|
|
25
|
+
* then extract the hover-specific matched styles.
|
|
26
|
+
*
|
|
27
|
+
* Returns only the rules that contain :hover in their selector.
|
|
28
|
+
*/
|
|
29
|
+
extractHoverStyles(cdp: CDPSession, page: Page, nodeId: number, classname: string, docRootNodeId: number): Promise<CDPRuleMatch[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Extract :checked pseudo-state styles for checkbox/radio inputs.
|
|
32
|
+
* Port of snipbackground.js:1933-1988
|
|
33
|
+
*/
|
|
34
|
+
extractCheckedStyles(cdp: CDPSession, page: Page, nodeId: number, classname: string, docRootNodeId: number): Promise<CDPRuleMatch[]>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=pseudo-state-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pseudo-state-handler.d.ts","sourceRoot":"","sources":["../../src/extraction/pseudo-state-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAiC,MAAM,mBAAmB,CAAC;AAEhF;;;;;GAKG;AACH,qBAAa,kBAAkB;IAC7B;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOhE;;OAEG;IACG,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQxE;;;OAGG;IACG,mBAAmB,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAmB3E;;;;;OAKG;IACG,kBAAkB,CACtB,GAAG,EAAE,UAAU,EACf,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,YAAY,EAAE,CAAC;IAqG1B;;;OAGG;IACG,oBAAoB,CACxB,GAAG,EAAE,UAAU,EACf,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,YAAY,EAAE,CAAC;CA4D3B"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handles forcing pseudo-states (:hover, :active, :checked) on elements
|
|
3
|
+
* and extracting the resulting matched styles.
|
|
4
|
+
*
|
|
5
|
+
* Port of snipbackground.js:1822-1995
|
|
6
|
+
*/
|
|
7
|
+
export class PseudoStateHandler {
|
|
8
|
+
/**
|
|
9
|
+
* Check if an element is a hover-capable tag.
|
|
10
|
+
*/
|
|
11
|
+
async getTagName(page, classname) {
|
|
12
|
+
return await page.evaluate((cls) => {
|
|
13
|
+
const elem = document.querySelector('.' + cls);
|
|
14
|
+
return elem ? elem.tagName : '';
|
|
15
|
+
}, classname);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Check if an input element is a checkbox or radio.
|
|
19
|
+
*/
|
|
20
|
+
async isCheckboxOrRadio(page, classname) {
|
|
21
|
+
return await page.evaluate((cls) => {
|
|
22
|
+
const elem = document.querySelector('.' + cls);
|
|
23
|
+
if (!elem)
|
|
24
|
+
return false;
|
|
25
|
+
return elem.type === 'checkbox' || elem.type === 'radio';
|
|
26
|
+
}, classname);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Get parent element snipcss classnames for pseudo-state forcing.
|
|
30
|
+
* Parents also need hover forced so that parent:hover child selectors work.
|
|
31
|
+
*/
|
|
32
|
+
async getParentClassnames(page, classname) {
|
|
33
|
+
return await page.evaluate((cls) => {
|
|
34
|
+
const elem = document.querySelector('.' + cls);
|
|
35
|
+
if (!elem)
|
|
36
|
+
return [];
|
|
37
|
+
const parents = [];
|
|
38
|
+
let parent = elem.parentElement;
|
|
39
|
+
while (parent && parent !== document.body) {
|
|
40
|
+
const classes = [...parent.classList];
|
|
41
|
+
const snipcssClass = classes.find(c => c.startsWith('snipcss'));
|
|
42
|
+
if (snipcssClass) {
|
|
43
|
+
parents.push(snipcssClass);
|
|
44
|
+
}
|
|
45
|
+
parent = parent.parentElement;
|
|
46
|
+
}
|
|
47
|
+
return parents;
|
|
48
|
+
}, classname);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Force hover/active pseudo-state on an element and its parents,
|
|
52
|
+
* then extract the hover-specific matched styles.
|
|
53
|
+
*
|
|
54
|
+
* Returns only the rules that contain :hover in their selector.
|
|
55
|
+
*/
|
|
56
|
+
async extractHoverStyles(cdp, page, nodeId, classname, docRootNodeId) {
|
|
57
|
+
const tagName = await this.getTagName(page, classname);
|
|
58
|
+
// Only hover-capable elements
|
|
59
|
+
const hoverCapable = ['A', 'SPAN', 'BUTTON', 'DIV', 'LI', 'IMG'];
|
|
60
|
+
if (!hoverCapable.includes(tagName))
|
|
61
|
+
return [];
|
|
62
|
+
const hoverRules = [];
|
|
63
|
+
try {
|
|
64
|
+
// Force hover/active on the element
|
|
65
|
+
await cdp.send('CSS.forcePseudoState', {
|
|
66
|
+
nodeId,
|
|
67
|
+
forcedPseudoClasses: ['hover', 'active'],
|
|
68
|
+
});
|
|
69
|
+
// Force hover on parent elements too
|
|
70
|
+
const parentClassnames = await this.getParentClassnames(page, classname);
|
|
71
|
+
const parentNodes = [];
|
|
72
|
+
for (const parentCls of parentClassnames) {
|
|
73
|
+
try {
|
|
74
|
+
const pnode = await cdp.send('DOM.querySelector', {
|
|
75
|
+
nodeId: docRootNodeId,
|
|
76
|
+
selector: '.' + parentCls,
|
|
77
|
+
});
|
|
78
|
+
if (pnode.nodeId) {
|
|
79
|
+
parentNodes.push(pnode.nodeId);
|
|
80
|
+
await cdp.send('CSS.forcePseudoState', {
|
|
81
|
+
nodeId: pnode.nodeId,
|
|
82
|
+
forcedPseudoClasses: ['hover', 'active'],
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// Parent may not be found
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Re-fetch matched styles with hover state active
|
|
91
|
+
const allMatchedStyles2 = await cdp.send('CSS.getMatchedStylesForNode', {
|
|
92
|
+
nodeId,
|
|
93
|
+
});
|
|
94
|
+
// Collect normal hover rules
|
|
95
|
+
for (const matchNormal of allMatchedStyles2.matchedCSSRules || []) {
|
|
96
|
+
const selectorText = matchNormal.rule.selectorList?.text || '';
|
|
97
|
+
if (selectorText.includes(':hover')) {
|
|
98
|
+
matchNormal.rule.origin = 'pseudo';
|
|
99
|
+
hoverRules.push(matchNormal);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Collect inherited hover rules
|
|
103
|
+
for (const inheritMatch of allMatchedStyles2.inherited || []) {
|
|
104
|
+
for (const iRule of inheritMatch.matchedCSSRules || []) {
|
|
105
|
+
const selectorText = iRule.rule.selectorList?.text || '';
|
|
106
|
+
if (selectorText.includes(':hover')) {
|
|
107
|
+
iRule.inherited = true;
|
|
108
|
+
iRule.rule.origin = 'pseudo';
|
|
109
|
+
hoverRules.push(iRule);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Collect pseudo-element hover rules
|
|
114
|
+
for (const pseudoMatch of allMatchedStyles2.pseudoElements || []) {
|
|
115
|
+
for (const pMatch of pseudoMatch.matches) {
|
|
116
|
+
const selectorText = pMatch.rule.selectorList?.text || '';
|
|
117
|
+
if (selectorText.includes(':hover')) {
|
|
118
|
+
pMatch.rule.origin = 'psuedo';
|
|
119
|
+
hoverRules.push({
|
|
120
|
+
rule: pMatch.rule,
|
|
121
|
+
matchingSelectors: pMatch.matchingSelectors,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Reset pseudo states
|
|
127
|
+
await cdp.send('CSS.forcePseudoState', {
|
|
128
|
+
nodeId,
|
|
129
|
+
forcedPseudoClasses: [],
|
|
130
|
+
});
|
|
131
|
+
for (const parentNodeId of parentNodes) {
|
|
132
|
+
try {
|
|
133
|
+
await cdp.send('CSS.forcePseudoState', {
|
|
134
|
+
nodeId: parentNodeId,
|
|
135
|
+
forcedPseudoClasses: [],
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
// Ignore cleanup failures
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (e) {
|
|
144
|
+
console.error('Pseudo hover extraction error:', e);
|
|
145
|
+
}
|
|
146
|
+
return hoverRules;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Extract :checked pseudo-state styles for checkbox/radio inputs.
|
|
150
|
+
* Port of snipbackground.js:1933-1988
|
|
151
|
+
*/
|
|
152
|
+
async extractCheckedStyles(cdp, page, nodeId, classname, docRootNodeId) {
|
|
153
|
+
const tagName = await this.getTagName(page, classname);
|
|
154
|
+
if (tagName !== 'INPUT')
|
|
155
|
+
return [];
|
|
156
|
+
const isCheckbox = await this.isCheckboxOrRadio(page, classname);
|
|
157
|
+
if (!isCheckbox)
|
|
158
|
+
return [];
|
|
159
|
+
const checkedRules = [];
|
|
160
|
+
try {
|
|
161
|
+
// Force hover on parents (same pattern as hover extraction)
|
|
162
|
+
const parentClassnames = await this.getParentClassnames(page, classname);
|
|
163
|
+
const parentNodes = [];
|
|
164
|
+
for (const parentCls of parentClassnames) {
|
|
165
|
+
try {
|
|
166
|
+
const pnode = await cdp.send('DOM.querySelector', {
|
|
167
|
+
nodeId: docRootNodeId,
|
|
168
|
+
selector: '.' + parentCls,
|
|
169
|
+
});
|
|
170
|
+
if (pnode.nodeId) {
|
|
171
|
+
parentNodes.push(pnode.nodeId);
|
|
172
|
+
await cdp.send('CSS.forcePseudoState', {
|
|
173
|
+
nodeId: pnode.nodeId,
|
|
174
|
+
forcedPseudoClasses: ['hover', 'active'],
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Ignore
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const allMatchedStyles2 = await cdp.send('CSS.getMatchedStylesForNode', {
|
|
183
|
+
nodeId,
|
|
184
|
+
});
|
|
185
|
+
for (const matchNormal of allMatchedStyles2.matchedCSSRules || []) {
|
|
186
|
+
const selectorText = matchNormal.rule.selectorList?.text || '';
|
|
187
|
+
if (selectorText.includes(':checked')) {
|
|
188
|
+
checkedRules.push(matchNormal);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Reset parent pseudo states
|
|
192
|
+
for (const parentNodeId of parentNodes) {
|
|
193
|
+
try {
|
|
194
|
+
await cdp.send('CSS.forcePseudoState', {
|
|
195
|
+
nodeId: parentNodeId,
|
|
196
|
+
forcedPseudoClasses: [],
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
// Ignore
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch (e) {
|
|
205
|
+
console.error('Checkbox extraction error:', e);
|
|
206
|
+
}
|
|
207
|
+
return checkedRules;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=pseudo-state-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pseudo-state-handler.js","sourceRoot":"","sources":["../../src/extraction/pseudo-state-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ExtractionContext, ExtractionResult } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Builds the final CSS output from collected extraction data.
|
|
4
|
+
* Port of handleSnippedResult from snipbackground.js:3498-5800
|
|
5
|
+
* Simplified version focusing on CSS generation without extension-specific features.
|
|
6
|
+
*/
|
|
7
|
+
export declare class ResultBuilder {
|
|
8
|
+
private variableResolver;
|
|
9
|
+
private keyframeCollector;
|
|
10
|
+
/**
|
|
11
|
+
* Build the final extraction result from collected data.
|
|
12
|
+
*/
|
|
13
|
+
buildResult(ctx: ExtractionContext, elementHtml: string, options: {
|
|
14
|
+
resolveVariables: boolean;
|
|
15
|
+
}): ExtractionResult;
|
|
16
|
+
/**
|
|
17
|
+
* Format rule body from semicolon-delimited string to indented lines.
|
|
18
|
+
*/
|
|
19
|
+
private formatRuleBody;
|
|
20
|
+
/**
|
|
21
|
+
* Determine which font families are actually used in the extracted CSS.
|
|
22
|
+
*/
|
|
23
|
+
private getUsedFontFamilies;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=result-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-builder.d.ts","sourceRoot":"","sources":["../../src/extraction/result-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAe,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAKrF;;;;GAIG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,gBAAgB,CAA6B;IACrD,OAAO,CAAC,iBAAiB,CAA2B;IAEpD;;OAEG;IACH,WAAW,CACT,GAAG,EAAE,iBAAiB,EACtB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE;QAAE,gBAAgB,EAAE,OAAO,CAAA;KAAE,GACrC,gBAAgB;IA+FnB;;OAEG;IACH,OAAO,CAAC,cAAc;IAStB;;OAEG;IACH,OAAO,CAAC,mBAAmB;CA+B5B"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { CssVariableResolver } from './css-variable-resolver.js';
|
|
2
|
+
import { KeyframeCollector } from './keyframe-collector.js';
|
|
3
|
+
import { stripMarkerClasses } from '../utils/helpers.js';
|
|
4
|
+
/**
|
|
5
|
+
* Builds the final CSS output from collected extraction data.
|
|
6
|
+
* Port of handleSnippedResult from snipbackground.js:3498-5800
|
|
7
|
+
* Simplified version focusing on CSS generation without extension-specific features.
|
|
8
|
+
*/
|
|
9
|
+
export class ResultBuilder {
|
|
10
|
+
variableResolver = new CssVariableResolver();
|
|
11
|
+
keyframeCollector = new KeyframeCollector();
|
|
12
|
+
/**
|
|
13
|
+
* Build the final extraction result from collected data.
|
|
14
|
+
*/
|
|
15
|
+
buildResult(ctx, elementHtml, options) {
|
|
16
|
+
const cssLines = [];
|
|
17
|
+
// 1. Add Google Font imports
|
|
18
|
+
for (const importUrl of ctx.importfontsArr) {
|
|
19
|
+
cssLines.push(`@import url("${importUrl}");`);
|
|
20
|
+
}
|
|
21
|
+
// 2. Add @font-face declarations for used fonts
|
|
22
|
+
const usedFontFamilies = this.getUsedFontFamilies(ctx);
|
|
23
|
+
for (const font of ctx.customfontsArr) {
|
|
24
|
+
if (usedFontFamilies.has(font.font_family.toLowerCase())) {
|
|
25
|
+
if (font.full_rule) {
|
|
26
|
+
cssLines.push(font.full_rule);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// 3. Add CSS variable definitions if resolving
|
|
31
|
+
if (options.resolveVariables) {
|
|
32
|
+
const varsCss = this.variableResolver.generateVariablesCss(ctx);
|
|
33
|
+
if (varsCss) {
|
|
34
|
+
cssLines.push('');
|
|
35
|
+
cssLines.push(varsCss);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// 4. Group rules by media query
|
|
39
|
+
const noMedia = [];
|
|
40
|
+
const mediaGroups = new Map();
|
|
41
|
+
for (const rule of ctx.snippedArr) {
|
|
42
|
+
if (rule.media) {
|
|
43
|
+
if (!mediaGroups.has(rule.media)) {
|
|
44
|
+
mediaGroups.set(rule.media, []);
|
|
45
|
+
}
|
|
46
|
+
mediaGroups.get(rule.media).push(rule);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
noMedia.push(rule);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// 5. Add non-media rules
|
|
53
|
+
if (noMedia.length > 0) {
|
|
54
|
+
cssLines.push('');
|
|
55
|
+
for (const rule of noMedia) {
|
|
56
|
+
const body = this.formatRuleBody(rule.body);
|
|
57
|
+
cssLines.push(`${rule.selector} {`);
|
|
58
|
+
cssLines.push(body);
|
|
59
|
+
cssLines.push('}');
|
|
60
|
+
cssLines.push('');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// 6. Add media query grouped rules
|
|
64
|
+
for (const [media, rules] of mediaGroups) {
|
|
65
|
+
cssLines.push(`@media ${media} {`);
|
|
66
|
+
for (const rule of rules) {
|
|
67
|
+
const body = this.formatRuleBody(rule.body);
|
|
68
|
+
cssLines.push(` ${rule.selector} {`);
|
|
69
|
+
cssLines.push(body.split('\n').map(l => ' ' + l).join('\n'));
|
|
70
|
+
cssLines.push(' }');
|
|
71
|
+
cssLines.push('');
|
|
72
|
+
}
|
|
73
|
+
cssLines.push('}');
|
|
74
|
+
cssLines.push('');
|
|
75
|
+
}
|
|
76
|
+
// 7. Add keyframe animations
|
|
77
|
+
const keyframesCss = this.keyframeCollector.generateCss(ctx);
|
|
78
|
+
if (keyframesCss) {
|
|
79
|
+
cssLines.push(keyframesCss);
|
|
80
|
+
}
|
|
81
|
+
// 8. Clean HTML
|
|
82
|
+
const cleanHtml = stripMarkerClasses(elementHtml);
|
|
83
|
+
// Resolve variables in CSS if requested
|
|
84
|
+
let finalCss = cssLines.join('\n');
|
|
85
|
+
if (options.resolveVariables) {
|
|
86
|
+
finalCss = this.variableResolver.resolveVarReferences(finalCss, ctx);
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
html: cleanHtml,
|
|
90
|
+
css: finalCss,
|
|
91
|
+
tailwindHtml: '', // Filled in by tailwind converter later
|
|
92
|
+
tailwindBodyClasses: '',
|
|
93
|
+
fonts: ctx.customfontsArr.filter(f => usedFontFamilies.has(f.font_family.toLowerCase())),
|
|
94
|
+
cssVariables: this.variableResolver.getResolvedVariables(ctx),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Format rule body from semicolon-delimited string to indented lines.
|
|
99
|
+
*/
|
|
100
|
+
formatRuleBody(body) {
|
|
101
|
+
return body
|
|
102
|
+
.split(';')
|
|
103
|
+
.map(prop => prop.trim())
|
|
104
|
+
.filter(prop => prop.length > 0)
|
|
105
|
+
.map(prop => ` ${prop};`)
|
|
106
|
+
.join('\n');
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Determine which font families are actually used in the extracted CSS.
|
|
110
|
+
*/
|
|
111
|
+
getUsedFontFamilies(ctx) {
|
|
112
|
+
const used = new Set();
|
|
113
|
+
for (const rule of ctx.snippedArr) {
|
|
114
|
+
if (!rule.body)
|
|
115
|
+
continue;
|
|
116
|
+
const fontMatch = rule.body.match(/font-family\s*:\s*([^;]+)/i);
|
|
117
|
+
if (fontMatch) {
|
|
118
|
+
const families = fontMatch[1].split(',').map(f => f.trim().replace(/['"]/g, '').toLowerCase());
|
|
119
|
+
families.forEach(f => used.add(f));
|
|
120
|
+
}
|
|
121
|
+
// Also check shorthand font property
|
|
122
|
+
const shortFontMatch = rule.body.match(/(?:^|;)\s*font\s*:\s*([^;]+)/i);
|
|
123
|
+
if (shortFontMatch) {
|
|
124
|
+
// Extract font-family from shorthand (it's the last part after /)
|
|
125
|
+
const parts = shortFontMatch[1].split('/');
|
|
126
|
+
if (parts.length > 1) {
|
|
127
|
+
const familyPart = parts[parts.length - 1].trim();
|
|
128
|
+
const families = familyPart.split(',').map(f => f.trim().replace(/['"]/g, '').toLowerCase());
|
|
129
|
+
families.forEach(f => used.add(f));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return used;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=result-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-builder.js","sourceRoot":"","sources":["../../src/extraction/result-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { SnippedRule, ExtractionContext } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Deduplicates CSS rules to avoid redundant output.
|
|
4
|
+
* Port of alreadySnippedWhole and related dedup logic from snipbackground.js:728-834
|
|
5
|
+
*/
|
|
6
|
+
export declare class RuleDeduplicator {
|
|
7
|
+
/**
|
|
8
|
+
* Check if a rule has already been added (same selector + body + media).
|
|
9
|
+
*/
|
|
10
|
+
isDuplicate(rule: SnippedRule, ctx: ExtractionContext): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Find the index of a duplicate rule, or -1 if not found.
|
|
13
|
+
*/
|
|
14
|
+
findDuplicateIndex(rule: SnippedRule, ctx: ExtractionContext): number;
|
|
15
|
+
/**
|
|
16
|
+
* Track a rule in matchingFinalRules for a given classname.
|
|
17
|
+
* This must happen even for duplicate rules - different elements can share
|
|
18
|
+
* the same CSS rule but each needs a matchingFinalRules entry for Tailwind.
|
|
19
|
+
*/
|
|
20
|
+
private trackRule;
|
|
21
|
+
/**
|
|
22
|
+
* Add a rule if it's not a duplicate.
|
|
23
|
+
* Even if duplicate, still tracks in matchingFinalRules for the element.
|
|
24
|
+
*/
|
|
25
|
+
addRule(rule: SnippedRule, ctx: ExtractionContext): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Add multiple rules, filtering out duplicates.
|
|
28
|
+
*/
|
|
29
|
+
addRules(rules: SnippedRule[], ctx: ExtractionContext): number;
|
|
30
|
+
/**
|
|
31
|
+
* Get all collected rules.
|
|
32
|
+
*/
|
|
33
|
+
getRules(ctx: ExtractionContext): SnippedRule[];
|
|
34
|
+
/**
|
|
35
|
+
* Group rules by media query for organized output.
|
|
36
|
+
*/
|
|
37
|
+
groupByMedia(ctx: ExtractionContext): Map<string, SnippedRule[]>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=rule-deduplicator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-deduplicator.d.ts","sourceRoot":"","sources":["../../src/extraction/rule-deduplicator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEnE;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,iBAAiB,GAAG,OAAO;IAS/D;;OAEG;IACH,kBAAkB,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,iBAAiB,GAAG,MAAM;IASrE;;;;OAIG;IACH,OAAO,CAAC,SAAS;IA2BjB;;;OAGG;IACH,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,iBAAiB,GAAG,OAAO;IAmB3D;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,GAAG,EAAE,iBAAiB,GAAG,MAAM;IAQ9D;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,iBAAiB,GAAG,WAAW,EAAE;IAI/C;;OAEG;IACH,YAAY,CAAC,GAAG,EAAE,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC;CAajE"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deduplicates CSS rules to avoid redundant output.
|
|
3
|
+
* Port of alreadySnippedWhole and related dedup logic from snipbackground.js:728-834
|
|
4
|
+
*/
|
|
5
|
+
export class RuleDeduplicator {
|
|
6
|
+
/**
|
|
7
|
+
* Check if a rule has already been added (same selector + body + media).
|
|
8
|
+
*/
|
|
9
|
+
isDuplicate(rule, ctx) {
|
|
10
|
+
return ctx.snippedArr.some(existing => existing.selector === rule.selector &&
|
|
11
|
+
existing.body === rule.body &&
|
|
12
|
+
existing.media === rule.media);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Find the index of a duplicate rule, or -1 if not found.
|
|
16
|
+
*/
|
|
17
|
+
findDuplicateIndex(rule, ctx) {
|
|
18
|
+
return ctx.snippedArr.findIndex(existing => existing.selector === rule.selector &&
|
|
19
|
+
existing.body === rule.body &&
|
|
20
|
+
existing.media === rule.media);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Track a rule in matchingFinalRules for a given classname.
|
|
24
|
+
* This must happen even for duplicate rules - different elements can share
|
|
25
|
+
* the same CSS rule but each needs a matchingFinalRules entry for Tailwind.
|
|
26
|
+
*/
|
|
27
|
+
trackRule(rule, ruleIndex, ctx) {
|
|
28
|
+
if (!rule.classname)
|
|
29
|
+
return;
|
|
30
|
+
if (!ctx.matchingFinalRules[rule.classname]) {
|
|
31
|
+
ctx.matchingFinalRules[rule.classname] = {
|
|
32
|
+
indices: [],
|
|
33
|
+
selectors: [],
|
|
34
|
+
bodies: [],
|
|
35
|
+
media_queries: [],
|
|
36
|
+
matching_parts: [],
|
|
37
|
+
contain_type: [],
|
|
38
|
+
inherited_type: [],
|
|
39
|
+
inherited_classes: [],
|
|
40
|
+
invalid_pseudos: [],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const entry = ctx.matchingFinalRules[rule.classname];
|
|
44
|
+
entry.indices.push(ruleIndex);
|
|
45
|
+
entry.selectors.push(rule.selector);
|
|
46
|
+
entry.bodies.push(rule.body);
|
|
47
|
+
entry.media_queries.push(rule.media);
|
|
48
|
+
entry.matching_parts.push([rule.selector]);
|
|
49
|
+
entry.contain_type.push('default');
|
|
50
|
+
entry.inherited_type.push(rule.is_inherited ? 'inherited' : 'default');
|
|
51
|
+
entry.inherited_classes.push([]);
|
|
52
|
+
entry.invalid_pseudos.push(false);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Add a rule if it's not a duplicate.
|
|
56
|
+
* Even if duplicate, still tracks in matchingFinalRules for the element.
|
|
57
|
+
*/
|
|
58
|
+
addRule(rule, ctx) {
|
|
59
|
+
// Skip empty body rules
|
|
60
|
+
if (!rule.body || rule.body.trim() === '')
|
|
61
|
+
return false;
|
|
62
|
+
// Skip user-agent rules
|
|
63
|
+
if (rule.origin === 'user-agent')
|
|
64
|
+
return false;
|
|
65
|
+
const dupIndex = this.findDuplicateIndex(rule, ctx);
|
|
66
|
+
if (dupIndex >= 0) {
|
|
67
|
+
// Rule already in snippedArr, but still track it for this element
|
|
68
|
+
this.trackRule(rule, dupIndex, ctx);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
ctx.snippedArr.push(rule);
|
|
72
|
+
this.trackRule(rule, ctx.snippedArr.length - 1, ctx);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Add multiple rules, filtering out duplicates.
|
|
77
|
+
*/
|
|
78
|
+
addRules(rules, ctx) {
|
|
79
|
+
let added = 0;
|
|
80
|
+
for (const rule of rules) {
|
|
81
|
+
if (this.addRule(rule, ctx))
|
|
82
|
+
added++;
|
|
83
|
+
}
|
|
84
|
+
return added;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Get all collected rules.
|
|
88
|
+
*/
|
|
89
|
+
getRules(ctx) {
|
|
90
|
+
return ctx.snippedArr;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Group rules by media query for organized output.
|
|
94
|
+
*/
|
|
95
|
+
groupByMedia(ctx) {
|
|
96
|
+
const groups = new Map();
|
|
97
|
+
for (const rule of ctx.snippedArr) {
|
|
98
|
+
const media = rule.media || '';
|
|
99
|
+
if (!groups.has(media)) {
|
|
100
|
+
groups.set(media, []);
|
|
101
|
+
}
|
|
102
|
+
groups.get(media).push(rule);
|
|
103
|
+
}
|
|
104
|
+
return groups;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=rule-deduplicator.js.map
|