@purgeon/analyzer-css 0.1.0 → 0.2.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/dist/index.d.mts +1 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +5 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/class-graph.ts","../src/css-node.ts","../src/var-graph.ts","../src/css-analyzer.ts","../src/purge-ir.ts","../src/index.ts"],"mappings":";;cAAa;mBACM;UAET;EASR,SAAS;EAIT,gBAAgB,WAAW;EAM3B,UAAU,oBAAoB;EAI9B,WAAW;EAIX,YAAY;;;;UC5BG;EACf;EACA;;UAGe;EACf;EACA;EACA;EACA;EACA,iBAAiB;;UAGF,qBAAqB;EACpC,OAAO;;UAGQ;EACf;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/class-graph.ts","../src/css-node.ts","../src/var-graph.ts","../src/css-analyzer.ts","../src/purge-ir.ts","../src/index.ts"],"mappings":";;cAAa;mBACM;UAET;EASR,SAAS;EAIT,gBAAgB,WAAW;EAM3B,UAAU,oBAAoB;EAI9B,WAAW;EAIX,YAAY;;;;UC5BG;EACf;EACA;;UAGe;EACf;EACA;EACA;EACA;EACA;EACA,iBAAiB;;UAGF,qBAAqB;EACpC,OAAO;;UAGQ;EACf;;;;cCrBW;mBACM;mBACA;mBACA;mBACA;UAET;EASR,QAAQ,iBAAiB;EAIzB,UAAU,iBAAiB,kBAAkB;EAS7C,gBAAgB,kBAAkB;EAIlC,mBAAmB,kBAAkB;EAIrC,eAAe,kBAAkB;EAIjC,aAAa,kBAAkB;EAI/B;IACE,YAAY;IACZ,cAAc;IACd,WAAW;;;;;cC3BF,oBAAoB,eAAe;mBAMjB;WALpB,MAAM;mBAEE;mBACA;cAEY,UAAS;EAItC,iBAAiB;EAIjB,eAAe;YAIL,kBAAkB;YAIlB,YAAY,cAAc,aAAa,MAAM;YAI7C,WAAW,aAAa;YAIxB,cAAc,YAAY,eAAe,cAAc,MAAM;YAW7D,cAAc,QAAQ;UAWxB;;;;iBCpEM,aAAa,OAAO,YAAY;;;cCKnC,cAAe,UAAU,uBAAkB"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnalyzerPlugin, classesOf, pluginName, splitCompoundSelectors } from "@purgeon/core";
|
|
1
|
+
import { AnalyzerPlugin, classesOf, dataAttrsOf, pluginName, splitCompoundSelectors } from "@purgeon/core";
|
|
2
2
|
import { transform } from "lightningcss";
|
|
3
3
|
//#region src/class-graph.ts
|
|
4
4
|
var ClassGraph = class {
|
|
@@ -140,14 +140,17 @@ var CssAnalyzer = class extends AnalyzerPlugin {
|
|
|
140
140
|
visitor: {
|
|
141
141
|
Rule: { style: (rule) => {
|
|
142
142
|
const classes = /* @__PURE__ */ new Set();
|
|
143
|
+
const dataAttrs = /* @__PURE__ */ new Set();
|
|
143
144
|
for (const selector of rule.value.selectors) for (const group of splitCompoundSelectors(selector)) {
|
|
144
145
|
const groupClasses = classesOf(group);
|
|
145
146
|
for (const className of groupClasses) classes.add(className);
|
|
146
147
|
for (let i = 0; i < groupClasses.length; i++) for (let j = i + 1; j < groupClasses.length; j++) this.classGraph.addCoOccurrence(groupClasses[i], groupClasses[j]);
|
|
148
|
+
for (const attrKey of dataAttrsOf(group)) dataAttrs.add(attrKey);
|
|
147
149
|
}
|
|
148
150
|
for (const className of classes) this.classGraph.addClass(className);
|
|
149
151
|
currentRule = {
|
|
150
152
|
classes: [...classes],
|
|
153
|
+
dataAttrs: [...dataAttrs],
|
|
151
154
|
declarations: (rule.value.declarations?.declarations ?? []).map((declaration) => declaration.property),
|
|
152
155
|
declaredVars: [],
|
|
153
156
|
referencedVars: [],
|
|
@@ -189,6 +192,7 @@ var CssAnalyzer = class extends AnalyzerPlugin {
|
|
|
189
192
|
function toCssPurgeIR(rules) {
|
|
190
193
|
return rules.map((rule) => ({
|
|
191
194
|
classes: rule.classes,
|
|
195
|
+
dataAttrs: rule.dataAttrs,
|
|
192
196
|
declaredVars: rule.declaredVars,
|
|
193
197
|
referencedVars: rule.referencedVars,
|
|
194
198
|
varDeclarations: rule.varDeclarations.map((decl) => ({
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/class-graph.ts","../src/var-graph.ts","../src/css-analyzer.ts","../src/purge-ir.ts","../src/index.ts"],"sourcesContent":["export class ClassGraph {\n private readonly edges = new Map<string, Set<string>>();\n\n private ensure(className: string): Set<string> {\n let neighbors = this.edges.get(className);\n if (!neighbors) {\n neighbors = new Set();\n this.edges.set(className, neighbors);\n }\n return neighbors;\n }\n\n addClass(className: string): void {\n this.ensure(className);\n }\n\n addCoOccurrence(a: string, b: string): void {\n if (a === b) return;\n this.ensure(a).add(b);\n this.ensure(b).add(a);\n }\n\n neighbors(className: string): Set<string> | undefined {\n return this.edges.get(className);\n }\n\n classes(): IterableIterator<string> {\n return this.edges.keys();\n }\n\n toObject(): Record<string, string[]> {\n return Object.fromEntries([...this.edges].map(([className, neighbors]) => [className, [...neighbors]]));\n }\n}\n","export class VarGraph {\n private readonly declaredIn = new Map<string, Set<string>>();\n private readonly referencedIn = new Map<string, Set<string>>();\n private readonly dependsOn = new Map<string, Set<string>>();\n private readonly usedBy = new Map<string, Set<string>>();\n\n private ensure(map: Map<string, Set<string>>, key: string): Set<string> {\n let values = map.get(key);\n if (!values) {\n values = new Set();\n map.set(key, values);\n }\n return values;\n }\n\n declare(varName: string, moduleId: string): void {\n this.ensure(this.declaredIn, varName).add(moduleId);\n }\n\n reference(varName: string, moduleId: string, declaringVar?: string): void {\n this.ensure(this.referencedIn, varName).add(moduleId);\n\n if (declaringVar && declaringVar !== varName) {\n this.ensure(this.dependsOn, declaringVar).add(varName);\n this.ensure(this.usedBy, varName).add(declaringVar);\n }\n }\n\n declaredModules(varName: string): Set<string> | undefined {\n return this.declaredIn.get(varName);\n }\n\n referencingModules(varName: string): Set<string> | undefined {\n return this.referencedIn.get(varName);\n }\n\n dependenciesOf(varName: string): Set<string> | undefined {\n return this.dependsOn.get(varName);\n }\n\n dependentsOf(varName: string): Set<string> | undefined {\n return this.usedBy.get(varName);\n }\n\n toObject(): {\n declaredIn: Record<string, string[]>;\n referencedIn: Record<string, string[]>;\n dependsOn: Record<string, string[]>;\n } {\n return {\n declaredIn: toRecord(this.declaredIn),\n referencedIn: toRecord(this.referencedIn),\n dependsOn: toRecord(this.dependsOn),\n };\n }\n}\n\nfunction toRecord(map: Map<string, Set<string>>): Record<string, string[]> {\n return Object.fromEntries([...map].map(([key, values]) => [key, [...values]]));\n}\n","import {\n AnalyzerPlugin,\n classesOf,\n pluginName,\n splitCompoundSelectors,\n type OutputBundle,\n type ParseContext,\n type PluginName,\n} from \"@purgeon/core\";\nimport { transform } from \"lightningcss\";\n\nimport { ClassGraph } from \"./class-graph\";\nimport type { CssAnalyzerOptions, CssGraphNode, CssRule, CssVarDeclaration } from \"./css-node\";\nimport { VarGraph } from \"./var-graph\";\n\nfunction normalizeModuleId(id: string): string {\n return id.replace(/\\?.*$/, \"\").replace(/\\\\/g, \"/\");\n}\n\nexport class CssAnalyzer extends AnalyzerPlugin<CssGraphNode> {\n readonly name: PluginName = pluginName(\"css-graph\");\n\n private readonly classGraph = new ClassGraph();\n private readonly varGraph = new VarGraph();\n\n constructor(private readonly options: CssAnalyzerOptions = {}) {\n super();\n }\n\n getClassGraph(): ClassGraph {\n return this.classGraph;\n }\n\n getVarGraph(): VarGraph {\n return this.varGraph;\n }\n\n protected isModuleSupported(id: string): boolean {\n return id.endsWith(\".module.css\");\n }\n\n protected parseModule(code: string, _id: string, _ctx: ParseContext): unknown {\n return code;\n }\n\n protected createNode(id: string): CssGraphNode {\n return { id, imports: [], importers: [], rules: [] };\n }\n\n protected analyzeModule(id: string, _ast: unknown, code: string, node: CssGraphNode): void {\n if (node.rules.length > 0) return;\n\n const rawRules = this.parseCss(code, id);\n const normalizedId = normalizeModuleId(id);\n node.rules = rawRules.map((rule) => ({\n ...rule,\n classes: rule.classes.map((cls) => `${normalizedId}::${cls}`),\n }));\n }\n\n protected analyzeBundle(bundle: OutputBundle): void {\n for (const fileName in bundle) {\n const output = bundle[fileName]!;\n if (output.type !== \"asset\" || !fileName.endsWith(\".css\")) continue;\n\n const source = typeof output.source === \"string\" ? output.source : Buffer.from(output.source).toString(\"utf-8\");\n const node = this.graph.ensureNode(fileName, (id) => this.createNode(id));\n node.rules = this.parseCss(source, fileName);\n }\n }\n\n private parseCss(code: string, id: string): CssRule[] {\n const rules: CssRule[] = [];\n\n let currentRule: CssRule | null = null;\n let currentDeclaredVar: string | null = null;\n let currentVarDeclaration: CssVarDeclaration | null = null;\n\n transform({\n filename: this.options.filename ?? id,\n code: Buffer.from(code),\n visitor: {\n Rule: {\n style: (rule) => {\n const classes = new Set<string>();\n\n for (const selector of rule.value.selectors) {\n for (const group of splitCompoundSelectors(selector)) {\n const groupClasses = classesOf(group);\n for (const className of groupClasses) classes.add(className);\n for (let i = 0; i < groupClasses.length; i++) {\n for (let j = i + 1; j < groupClasses.length; j++) {\n this.classGraph.addCoOccurrence(groupClasses[i]!, groupClasses[j]!);\n }\n }\n }\n }\n\n for (const className of classes) this.classGraph.addClass(className);\n\n currentRule = {\n classes: [...classes],\n declarations: (rule.value.declarations?.declarations ?? []).map((declaration: { property: string }) => declaration.property),\n declaredVars: [],\n referencedVars: [],\n varDeclarations: [],\n };\n rules.push(currentRule);\n },\n },\n RuleExit: {\n style: () => {\n currentRule = null;\n },\n },\n Declaration: {\n custom: (declaration) => {\n const varName = declaration.name;\n currentDeclaredVar = varName;\n currentRule?.declaredVars.push(varName);\n this.varGraph.declare(varName, id);\n\n currentVarDeclaration = { name: varName, referencedVars: [] };\n currentRule?.varDeclarations.push(currentVarDeclaration);\n },\n },\n DeclarationExit: {\n custom: () => {\n currentDeclaredVar = null;\n currentVarDeclaration = null;\n },\n },\n Variable: (variable) => {\n const varName = variable.name.ident;\n currentRule?.referencedVars.push(varName);\n currentVarDeclaration?.referencedVars.push(varName);\n this.varGraph.reference(varName, id, currentDeclaredVar ?? undefined);\n },\n },\n });\n\n return rules;\n }\n}\n","import type { CssPurgeIRRule, CssPurgeIRVarDeclaration } from \"@purgeon/core\";\n\nimport type { CssRule, CssVarDeclaration } from \"./css-node\";\n\nexport function toCssPurgeIR(rules: CssRule[]): CssPurgeIRRule[] {\n return rules.map(\n (rule: CssRule): CssPurgeIRRule => ({\n classes: rule.classes,\n declaredVars: rule.declaredVars,\n referencedVars: rule.referencedVars,\n varDeclarations: rule.varDeclarations.map(\n (decl: CssVarDeclaration): CssPurgeIRVarDeclaration => ({\n name: decl.name,\n referencedVars: decl.referencedVars,\n }),\n ),\n }),\n );\n}\n","import { CssAnalyzer } from \"./css-analyzer\";\nimport type { CssAnalyzerOptions } from \"./css-node\";\n\nexport * from \"./class-graph\";\nexport * from \"./css-analyzer\";\nexport * from \"./css-node\";\nexport * from \"./purge-ir\";\nexport * from \"./var-graph\";\n\nexport const cssAnalyzer = (options?: CssAnalyzerOptions) => new CssAnalyzer(options);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/class-graph.ts","../src/var-graph.ts","../src/css-analyzer.ts","../src/purge-ir.ts","../src/index.ts"],"sourcesContent":["export class ClassGraph {\n private readonly edges = new Map<string, Set<string>>();\n\n private ensure(className: string): Set<string> {\n let neighbors = this.edges.get(className);\n if (!neighbors) {\n neighbors = new Set();\n this.edges.set(className, neighbors);\n }\n return neighbors;\n }\n\n addClass(className: string): void {\n this.ensure(className);\n }\n\n addCoOccurrence(a: string, b: string): void {\n if (a === b) return;\n this.ensure(a).add(b);\n this.ensure(b).add(a);\n }\n\n neighbors(className: string): Set<string> | undefined {\n return this.edges.get(className);\n }\n\n classes(): IterableIterator<string> {\n return this.edges.keys();\n }\n\n toObject(): Record<string, string[]> {\n return Object.fromEntries([...this.edges].map(([className, neighbors]) => [className, [...neighbors]]));\n }\n}\n","export class VarGraph {\n private readonly declaredIn = new Map<string, Set<string>>();\n private readonly referencedIn = new Map<string, Set<string>>();\n private readonly dependsOn = new Map<string, Set<string>>();\n private readonly usedBy = new Map<string, Set<string>>();\n\n private ensure(map: Map<string, Set<string>>, key: string): Set<string> {\n let values = map.get(key);\n if (!values) {\n values = new Set();\n map.set(key, values);\n }\n return values;\n }\n\n declare(varName: string, moduleId: string): void {\n this.ensure(this.declaredIn, varName).add(moduleId);\n }\n\n reference(varName: string, moduleId: string, declaringVar?: string): void {\n this.ensure(this.referencedIn, varName).add(moduleId);\n\n if (declaringVar && declaringVar !== varName) {\n this.ensure(this.dependsOn, declaringVar).add(varName);\n this.ensure(this.usedBy, varName).add(declaringVar);\n }\n }\n\n declaredModules(varName: string): Set<string> | undefined {\n return this.declaredIn.get(varName);\n }\n\n referencingModules(varName: string): Set<string> | undefined {\n return this.referencedIn.get(varName);\n }\n\n dependenciesOf(varName: string): Set<string> | undefined {\n return this.dependsOn.get(varName);\n }\n\n dependentsOf(varName: string): Set<string> | undefined {\n return this.usedBy.get(varName);\n }\n\n toObject(): {\n declaredIn: Record<string, string[]>;\n referencedIn: Record<string, string[]>;\n dependsOn: Record<string, string[]>;\n } {\n return {\n declaredIn: toRecord(this.declaredIn),\n referencedIn: toRecord(this.referencedIn),\n dependsOn: toRecord(this.dependsOn),\n };\n }\n}\n\nfunction toRecord(map: Map<string, Set<string>>): Record<string, string[]> {\n return Object.fromEntries([...map].map(([key, values]) => [key, [...values]]));\n}\n","import {\n AnalyzerPlugin,\n classesOf,\n dataAttrsOf,\n pluginName,\n splitCompoundSelectors,\n type OutputBundle,\n type ParseContext,\n type PluginName,\n} from \"@purgeon/core\";\nimport { transform } from \"lightningcss\";\n\nimport { ClassGraph } from \"./class-graph\";\nimport type { CssAnalyzerOptions, CssGraphNode, CssRule, CssVarDeclaration } from \"./css-node\";\nimport { VarGraph } from \"./var-graph\";\n\nfunction normalizeModuleId(id: string): string {\n return id.replace(/\\?.*$/, \"\").replace(/\\\\/g, \"/\");\n}\n\nexport class CssAnalyzer extends AnalyzerPlugin<CssGraphNode> {\n readonly name: PluginName = pluginName(\"css-graph\");\n\n private readonly classGraph = new ClassGraph();\n private readonly varGraph = new VarGraph();\n\n constructor(private readonly options: CssAnalyzerOptions = {}) {\n super();\n }\n\n getClassGraph(): ClassGraph {\n return this.classGraph;\n }\n\n getVarGraph(): VarGraph {\n return this.varGraph;\n }\n\n protected isModuleSupported(id: string): boolean {\n return id.endsWith(\".module.css\");\n }\n\n protected parseModule(code: string, _id: string, _ctx: ParseContext): unknown {\n return code;\n }\n\n protected createNode(id: string): CssGraphNode {\n return { id, imports: [], importers: [], rules: [] };\n }\n\n protected analyzeModule(id: string, _ast: unknown, code: string, node: CssGraphNode): void {\n if (node.rules.length > 0) return;\n\n const rawRules = this.parseCss(code, id);\n const normalizedId = normalizeModuleId(id);\n node.rules = rawRules.map((rule) => ({\n ...rule,\n classes: rule.classes.map((cls) => `${normalizedId}::${cls}`),\n }));\n }\n\n protected analyzeBundle(bundle: OutputBundle): void {\n for (const fileName in bundle) {\n const output = bundle[fileName]!;\n if (output.type !== \"asset\" || !fileName.endsWith(\".css\")) continue;\n\n const source = typeof output.source === \"string\" ? output.source : Buffer.from(output.source).toString(\"utf-8\");\n const node = this.graph.ensureNode(fileName, (id) => this.createNode(id));\n node.rules = this.parseCss(source, fileName);\n }\n }\n\n private parseCss(code: string, id: string): CssRule[] {\n const rules: CssRule[] = [];\n\n let currentRule: CssRule | null = null;\n let currentDeclaredVar: string | null = null;\n let currentVarDeclaration: CssVarDeclaration | null = null;\n\n transform({\n filename: this.options.filename ?? id,\n code: Buffer.from(code),\n visitor: {\n Rule: {\n style: (rule) => {\n const classes = new Set<string>();\n const dataAttrs = new Set<string>();\n\n for (const selector of rule.value.selectors) {\n for (const group of splitCompoundSelectors(selector)) {\n const groupClasses = classesOf(group);\n for (const className of groupClasses) classes.add(className);\n for (let i = 0; i < groupClasses.length; i++) {\n for (let j = i + 1; j < groupClasses.length; j++) {\n this.classGraph.addCoOccurrence(groupClasses[i]!, groupClasses[j]!);\n }\n }\n\n for (const attrKey of dataAttrsOf(group)) dataAttrs.add(attrKey);\n }\n }\n\n for (const className of classes) this.classGraph.addClass(className);\n\n currentRule = {\n classes: [...classes],\n dataAttrs: [...dataAttrs],\n declarations: (rule.value.declarations?.declarations ?? []).map((declaration: { property: string }) => declaration.property),\n declaredVars: [],\n referencedVars: [],\n varDeclarations: [],\n };\n rules.push(currentRule);\n },\n },\n RuleExit: {\n style: () => {\n currentRule = null;\n },\n },\n Declaration: {\n custom: (declaration) => {\n const varName = declaration.name;\n currentDeclaredVar = varName;\n currentRule?.declaredVars.push(varName);\n this.varGraph.declare(varName, id);\n\n currentVarDeclaration = { name: varName, referencedVars: [] };\n currentRule?.varDeclarations.push(currentVarDeclaration);\n },\n },\n DeclarationExit: {\n custom: () => {\n currentDeclaredVar = null;\n currentVarDeclaration = null;\n },\n },\n Variable: (variable) => {\n const varName = variable.name.ident;\n currentRule?.referencedVars.push(varName);\n currentVarDeclaration?.referencedVars.push(varName);\n this.varGraph.reference(varName, id, currentDeclaredVar ?? undefined);\n },\n },\n });\n\n return rules;\n }\n}\n","import type { CssPurgeIRRule, CssPurgeIRVarDeclaration } from \"@purgeon/core\";\n\nimport type { CssRule, CssVarDeclaration } from \"./css-node\";\n\nexport function toCssPurgeIR(rules: CssRule[]): CssPurgeIRRule[] {\n return rules.map(\n (rule: CssRule): CssPurgeIRRule => ({\n classes: rule.classes,\n dataAttrs: rule.dataAttrs,\n declaredVars: rule.declaredVars,\n referencedVars: rule.referencedVars,\n varDeclarations: rule.varDeclarations.map(\n (decl: CssVarDeclaration): CssPurgeIRVarDeclaration => ({\n name: decl.name,\n referencedVars: decl.referencedVars,\n }),\n ),\n }),\n );\n}\n","import { CssAnalyzer } from \"./css-analyzer\";\nimport type { CssAnalyzerOptions } from \"./css-node\";\n\nexport * from \"./class-graph\";\nexport * from \"./css-analyzer\";\nexport * from \"./css-node\";\nexport * from \"./purge-ir\";\nexport * from \"./var-graph\";\n\nexport const cssAnalyzer = (options?: CssAnalyzerOptions) => new CssAnalyzer(options);\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@purgeon/analyzer-css",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Walks bundled CSS via lightningcss visitor, builds class co-occurrence and custom-property graphs for purgeon",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"css",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"lightningcss": "^1.32.0",
|
|
35
|
-
"@purgeon/core": "0.
|
|
35
|
+
"@purgeon/core": "0.2.0"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "tsdown"
|