@purgeon/analyzer-jsx 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.mjs +32 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -165,6 +165,24 @@ function resolveCssModuleRef(expr, importBindings) {
|
|
|
165
165
|
if (propName === null) return null;
|
|
166
166
|
return `${binding.source}::${propName}`;
|
|
167
167
|
}
|
|
168
|
+
const DATA_ATTR_NAME_PATTERN = /^data-/;
|
|
169
|
+
/** Detects `props.color`, bare `color`, or `props.color ?? "fallback"` — the common prop-forwarding
|
|
170
|
+
* pattern where a component mirrors one of its own props onto a `data-*` attribute. Lets callers
|
|
171
|
+
* narrow the attribute's possible values using literal usages of that prop name elsewhere in the app,
|
|
172
|
+
* instead of treating the attribute as fully dynamic. */
|
|
173
|
+
function extractPropRef(expr) {
|
|
174
|
+
if (expr.type === "Identifier") return { propRef: expr.name };
|
|
175
|
+
if (expr.type === "MemberExpression" && !expr.computed && expr.property?.type === "Identifier") return { propRef: expr.property.name };
|
|
176
|
+
if (expr.type === "LogicalExpression" && (expr.operator === "??" || expr.operator === "||")) {
|
|
177
|
+
const left = extractPropRef(expr.left);
|
|
178
|
+
if (!left) return null;
|
|
179
|
+
return expr.right.type === "Literal" && typeof expr.right.value === "string" ? {
|
|
180
|
+
propRef: left.propRef,
|
|
181
|
+
fallback: expr.right.value
|
|
182
|
+
} : left;
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
168
186
|
/** Extracts JSX props to serializable form. Optional resolveExpr hook for custom resolution (e.g. CSS Modules). */
|
|
169
187
|
function extractProps(attributes, code, resolveExpr) {
|
|
170
188
|
return attributes.map((attr) => {
|
|
@@ -189,11 +207,20 @@ function extractProps(attributes, code, resolveExpr) {
|
|
|
189
207
|
};
|
|
190
208
|
}
|
|
191
209
|
const staticResult = tryStaticEval(expr);
|
|
192
|
-
|
|
210
|
+
if (staticResult.ok) return {
|
|
193
211
|
name,
|
|
194
212
|
kind: "literal",
|
|
195
213
|
value: staticResult.value
|
|
196
|
-
}
|
|
214
|
+
};
|
|
215
|
+
if (DATA_ATTR_NAME_PATTERN.test(name)) {
|
|
216
|
+
const propRef = extractPropRef(expr);
|
|
217
|
+
if (propRef) return {
|
|
218
|
+
name,
|
|
219
|
+
kind: "prop-ref",
|
|
220
|
+
...propRef
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
197
224
|
name,
|
|
198
225
|
kind: "expr",
|
|
199
226
|
code: sourceOf(expr, code)
|
|
@@ -307,7 +334,9 @@ function toJsxPurgeIR(node) {
|
|
|
307
334
|
jsxUsages: node.jsxUsages.map((usage) => ({ props: usage.props.map((prop) => ({
|
|
308
335
|
name: prop.name,
|
|
309
336
|
kind: prop.kind,
|
|
310
|
-
value: prop.value
|
|
337
|
+
value: prop.value,
|
|
338
|
+
propRef: prop.propRef,
|
|
339
|
+
fallback: prop.fallback
|
|
311
340
|
})) })),
|
|
312
341
|
classVariantCalls: node.classVariantCalls.map((call) => ({
|
|
313
342
|
name: call.name,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/ast-utils.ts","../src/jsx-analyzer.ts","../src/purge-ir.ts","../src/index.ts"],"sourcesContent":["import { UNRESOLVED_REF_KEY, type UnresolvedRef } from \"@purgeon/core\";\nimport { walk } from \"oxc-walker\";\n\nfunction getPropKeyName(key: any): string | null {\n if (key.type === \"Identifier\") return key.name;\n if (key.type === \"Literal\") return String(key.value);\n return null;\n}\n\n/** Extracts a JSX element name: <Foo />, <Foo.Bar />, <ns:Foo /> */\nexport function getJSXElementName(nameNode: any): string | null {\n if (!nameNode) return null;\n switch (nameNode.type) {\n case \"JSXIdentifier\":\n return nameNode.name;\n case \"JSXMemberExpression\":\n return `${getJSXElementName(nameNode.object)}.${getJSXElementName(nameNode.property)}`;\n case \"JSXNamespacedName\":\n return `${nameNode.namespace.name}:${nameNode.name.name}`;\n default:\n return nameNode.name ?? null;\n }\n}\n\n/** Returns the source text of a node based on its position in the code. */\nexport function sourceOf(node: any, code: string): string | null {\n if (!node || typeof node.start !== \"number\" || typeof node.end !== \"number\") {\n return null;\n }\n return code.slice(node.start, node.end);\n}\n\n/** Static eval of an AST node → plain JS value. Returns { ok: false } for dynamic nodes. */\nexport function tryStaticEval(node: any): { ok: boolean; value?: any } {\n if (!node) return { ok: false };\n\n switch (node.type) {\n case \"Literal\":\n return { ok: true, value: node.value };\n\n case \"TemplateLiteral\":\n if (node.expressions?.length) return { ok: false };\n return { ok: true, value: node.quasis.map((q: any) => q.value.cooked ?? q.value.raw).join(\"\") };\n\n case \"ArrayExpression\": {\n const values: any[] = [];\n for (const el of node.elements) {\n if (el === null) {\n values.push(undefined);\n continue;\n }\n const result = tryStaticEval(el);\n if (!result.ok) return { ok: false };\n values.push(result.value);\n }\n return { ok: true, value: values };\n }\n\n case \"ObjectExpression\": {\n const obj: Record<string, any> = {};\n for (const prop of node.properties) {\n if (prop.type !== \"Property\" || prop.computed) return { ok: false };\n\n const keyName = getPropKeyName(prop.key);\n if (keyName === null) return { ok: false };\n\n const valueResult = tryStaticEval(prop.value);\n if (!valueResult.ok) return { ok: false };\n obj[keyName] = valueResult.value;\n }\n return { ok: true, value: obj };\n }\n\n case \"UnaryExpression\": {\n if (node.operator === \"-\") {\n const argResult = tryStaticEval(node.argument);\n if (!argResult.ok || typeof argResult.value !== \"number\") return { ok: false };\n return { ok: true, value: -argResult.value };\n }\n return { ok: false };\n }\n\n case \"TSAsExpression\":\n case \"TSSatisfiesExpression\":\n return tryStaticEval(node.expression);\n\n default:\n return { ok: false };\n }\n}\n\n/** Partial static eval: never bails on the whole structure for one dynamic sibling.\n * Unresolvable → undefined; bare identifiers → UnresolvedRef marker. */\nexport function tryStaticEvalPartial(node: any): any {\n if (!node) return undefined;\n\n switch (node.type) {\n case \"Literal\":\n return node.value;\n\n case \"Identifier\":\n return { [UNRESOLVED_REF_KEY]: node.name } satisfies UnresolvedRef;\n\n case \"TemplateLiteral\":\n if (node.expressions?.length) return undefined;\n return node.quasis.map((q: any) => q.value.cooked ?? q.value.raw).join(\"\");\n\n case \"ArrayExpression\":\n return node.elements.map((el: any) => (el === null ? undefined : tryStaticEvalPartial(el)));\n\n case \"ObjectExpression\": {\n const obj: Record<string, any> = {};\n for (const prop of node.properties) {\n if (prop.type !== \"Property\" || prop.computed) continue;\n\n const keyName = getPropKeyName(prop.key);\n if (keyName === null) continue;\n\n obj[keyName] = tryStaticEvalPartial(prop.value);\n }\n return obj;\n }\n\n case \"UnaryExpression\": {\n if (node.operator !== \"-\") return undefined;\n const argValue = tryStaticEvalPartial(node.argument);\n return typeof argValue === \"number\" ? -argValue : undefined;\n }\n\n case \"TSAsExpression\":\n case \"TSSatisfiesExpression\":\n return tryStaticEvalPartial(node.expression);\n\n default:\n return undefined;\n }\n}\n\n/** Extracts the callee name of a CallExpression, to check it against tracked function names. */\nexport function getCalleeName(callee: any): string | null {\n if (callee.type === \"Identifier\") return callee.name;\n if (callee.type === \"MemberExpression\" && callee.property.type === \"Identifier\") {\n return callee.property.name;\n }\n return null;\n}\n\n/** `export const X = ...` values, keyed by exported name, for cross-module ref resolution. */\nexport function collectStaticExports(ast: any): Record<string, unknown> {\n const exports: Record<string, unknown> = {};\n\n for (const statement of ast.body ?? ast.program?.body ?? []) {\n if (statement.type !== \"ExportNamedDeclaration\" || statement.declaration?.type !== \"VariableDeclaration\") continue;\n\n for (const declarator of statement.declaration.declarations) {\n if (declarator.id.type !== \"Identifier\" || !declarator.init) continue;\n exports[declarator.id.name] = tryStaticEvalPartial(declarator.init);\n }\n }\n\n return exports;\n}\n\n/** Extracts { localName -> importSource } from the module's import declarations. */\nexport function collectImportBindings(ast: any) {\n const bindings = new Map<string, { source: string; imported: string }>();\n\n walk(ast, {\n enter(node: any) {\n if (node.type !== \"ImportDeclaration\") return;\n const source = node.source.value;\n for (const spec of node.specifiers ?? []) {\n if (spec.type === \"ImportDefaultSpecifier\") {\n bindings.set(spec.local.name, { source, imported: \"default\" });\n } else if (spec.type === \"ImportSpecifier\") {\n bindings.set(spec.local.name, { source, imported: (spec.imported as any).name });\n } else if (spec.type === \"ImportNamespaceSpecifier\") {\n bindings.set(spec.local.name, { source, imported: \"*\" });\n }\n }\n },\n });\n\n return bindings;\n}\n\n/** Resolves styles.foo / styles[\"foo\"] → \"<specifier>::<className>\". Returns null if not a CSS Modules ref. */\nexport function resolveCssModuleRef(expr: any, importBindings: Map<string, { source: string; imported: string }>): string | null {\n if (expr.type !== \"MemberExpression\") return null;\n if (expr.object?.type !== \"Identifier\") return null;\n\n const binding = importBindings.get(expr.object.name);\n if (!binding) return null;\n if (!binding.source.endsWith(\".module.css\")) return null;\n if (binding.imported !== \"default\") return null;\n\n let propName: string | null = null;\n if (!expr.computed && expr.property?.type === \"Identifier\") {\n propName = expr.property.name;\n } else if (expr.computed && expr.property?.type === \"Literal\" && typeof expr.property.value === \"string\") {\n propName = expr.property.value;\n }\n if (propName === null) return null;\n\n return `${binding.source}::${propName}`;\n}\n\n/** Extracts JSX props to serializable form. Optional resolveExpr hook for custom resolution (e.g. CSS Modules). */\nexport function extractProps(attributes: any[], code: string, resolveExpr?: (expr: any) => { kind: string; value: unknown } | null) {\n return attributes.map((attr: any) => {\n if (attr.type === \"JSXSpreadAttribute\") {\n return { name: \"...spread\", kind: \"spread\", code: sourceOf(attr.argument, code) };\n }\n\n const name = attr.name.name ?? getJSXElementName(attr.name);\n\n if (attr.value == null) {\n return { name, kind: \"literal\", value: true };\n }\n\n if (attr.value.type === \"JSXExpressionContainer\") {\n const expr = attr.value.expression;\n\n if (resolveExpr) {\n const resolved = resolveExpr(expr);\n if (resolved) return { name, ...resolved };\n }\n\n const staticResult = tryStaticEval(expr);\n return staticResult.ok ? { name, kind: \"literal\", value: staticResult.value } : { name, kind: \"expr\", code: sourceOf(expr, code) };\n }\n\n return { name, kind: \"literal\", value: attr.value.value };\n });\n}\n","import { AnalyzerPlugin, pluginName, type ParseContext, type PluginName } from \"@purgeon/core\";\nimport { walk } from \"oxc-walker\";\n\nimport {\n collectImportBindings,\n collectStaticExports,\n extractProps,\n getCalleeName,\n getJSXElementName,\n resolveCssModuleRef,\n sourceOf,\n tryStaticEval,\n tryStaticEvalPartial,\n} from \"./ast-utils\";\nimport type { JsxAnalyzerOptions, JsxGraphNode } from \"./jsx-node\";\n\nconst DEFAULT_TRACKED_CALLS = [\"cva\", \"cx\", \"cn\", \"tv\", \"clsx\"];\n\nexport class JsxAnalyzer extends AnalyzerPlugin<JsxGraphNode> {\n readonly name: PluginName = pluginName(\"jsx-graph\");\n\n private readonly trackedCalls: Set<string>;\n\n constructor(options: JsxAnalyzerOptions = {}) {\n super();\n this.trackedCalls = new Set(options.trackedCalls ?? DEFAULT_TRACKED_CALLS);\n }\n\n protected isModuleSupported(id: string): boolean {\n return /\\.[jt]sx?$/.test(id);\n }\n\n protected parseModule(code: string, id: string, ctx: ParseContext): unknown {\n return ctx.parse(code, {\n lang: id.endsWith(\"x\") ? (id.endsWith(\".tsx\") ? \"tsx\" : \"jsx\") : \"ts\",\n sourceType: \"module\",\n });\n }\n\n protected createNode(id: string): JsxGraphNode {\n return {\n id,\n imports: [],\n importers: [],\n jsxUsages: [],\n classVariantCalls: [],\n styledComponentUsages: [],\n importBindings: {},\n staticExports: {},\n };\n }\n\n protected analyzeModule(_id: string, ast: unknown, code: string, node: JsxGraphNode): void {\n const importBindings = collectImportBindings(ast);\n node.importBindings = Object.fromEntries(importBindings);\n node.staticExports = collectStaticExports(ast);\n\n const resolveCssExpr = (expr: any) => {\n const ref = resolveCssModuleRef(expr, importBindings);\n return ref ? { kind: \"css-module-ref\", value: ref } : null;\n };\n\n walk(ast as Parameters<typeof walk>[0], {\n enter: (n) => {\n if (n.type === \"JSXOpeningElement\") {\n const name = getJSXElementName(n.name);\n const binding = name ? importBindings.get(name) : undefined;\n node.jsxUsages.push({\n component: name,\n resolvedFrom: binding ? binding.source : null,\n props: extractProps(n.attributes, code, resolveCssExpr),\n });\n return;\n }\n\n if (n.type === \"CallExpression\") {\n const calleeName = getCalleeName(n.callee);\n if (calleeName && this.trackedCalls.has(calleeName)) {\n const configResult = tryStaticEval(n.arguments[0]);\n node.classVariantCalls.push({\n name: calleeName,\n static: configResult.ok,\n config: configResult.ok ? configResult.value : tryStaticEvalPartial(n.arguments[0]),\n rawCode: configResult.ok ? null : sourceOf(n, code),\n });\n }\n return;\n }\n\n if (n.type === \"TaggedTemplateExpression\") {\n const tag = n.tag;\n if (tag.type === \"MemberExpression\" && tag.object.type === \"Identifier\" && tag.object.name === \"styled\") {\n const baseTag = tag.property.type === \"Identifier\" ? tag.property.name : null;\n node.styledComponentUsages.push({ baseTag, kind: \"element\" });\n } else if (tag.type === \"CallExpression\" && tag.callee.type === \"Identifier\" && tag.callee.name === \"styled\") {\n const arg = tag.arguments[0];\n const baseTag = arg && arg.type === \"Identifier\" ? arg.name : null;\n node.styledComponentUsages.push({ baseTag, kind: \"component\" });\n }\n }\n },\n });\n }\n}\n","import type { JsxPurgeIRNode, JsxPurgeIRUsage, JsxPurgeIRVariantCall, JsxPurgeIRImportBinding } from \"@purgeon/core\";\n\nimport type { JsxGraphNode, JsxUsage, ClassVariantCall } from \"./jsx-node\";\n\nexport function toJsxPurgeIR(node: JsxGraphNode): JsxPurgeIRNode {\n return {\n id: node.id,\n imports: node.imports,\n jsxUsages: node.jsxUsages.map(\n (usage: JsxUsage): JsxPurgeIRUsage => ({\n props: usage.props.map((prop) => ({\n name: (prop as { name: string }).name,\n kind: (prop as { kind: string }).kind,\n value: (prop as { value?: unknown }).value,\n })),\n }),\n ),\n classVariantCalls: node.classVariantCalls.map(\n (call: ClassVariantCall): JsxPurgeIRVariantCall => ({\n name: call.name,\n static: call.static,\n config: call.config,\n }),\n ),\n importBindings: Object.fromEntries(\n Object.entries(node.importBindings).map(([key, binding]) => [\n key,\n { source: binding.source, imported: binding.imported } satisfies JsxPurgeIRImportBinding,\n ]),\n ),\n staticExports: node.staticExports,\n } satisfies JsxPurgeIRNode;\n}\n","import { JsxAnalyzer } from \"./jsx-analyzer\";\nimport type { JsxAnalyzerOptions } from \"./jsx-node\";\n\nexport { JsxAnalyzer } from \"./jsx-analyzer\";\nexport { toJsxPurgeIR } from \"./purge-ir\";\nexport type { JsxAnalyzerOptions, JsxGraphNode, JsxUsage, ClassVariantCall, StyledComponentUsage } from \"./jsx-node\";\nexport const jsxAnalyzer = (options?: JsxAnalyzerOptions) => new JsxAnalyzer(options);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/ast-utils.ts","../src/jsx-analyzer.ts","../src/purge-ir.ts","../src/index.ts"],"sourcesContent":["import { UNRESOLVED_REF_KEY, type UnresolvedRef } from \"@purgeon/core\";\nimport { walk } from \"oxc-walker\";\n\nfunction getPropKeyName(key: any): string | null {\n if (key.type === \"Identifier\") return key.name;\n if (key.type === \"Literal\") return String(key.value);\n return null;\n}\n\n/** Extracts a JSX element name: <Foo />, <Foo.Bar />, <ns:Foo /> */\nexport function getJSXElementName(nameNode: any): string | null {\n if (!nameNode) return null;\n switch (nameNode.type) {\n case \"JSXIdentifier\":\n return nameNode.name;\n case \"JSXMemberExpression\":\n return `${getJSXElementName(nameNode.object)}.${getJSXElementName(nameNode.property)}`;\n case \"JSXNamespacedName\":\n return `${nameNode.namespace.name}:${nameNode.name.name}`;\n default:\n return nameNode.name ?? null;\n }\n}\n\n/** Returns the source text of a node based on its position in the code. */\nexport function sourceOf(node: any, code: string): string | null {\n if (!node || typeof node.start !== \"number\" || typeof node.end !== \"number\") {\n return null;\n }\n return code.slice(node.start, node.end);\n}\n\n/** Static eval of an AST node → plain JS value. Returns { ok: false } for dynamic nodes. */\nexport function tryStaticEval(node: any): { ok: boolean; value?: any } {\n if (!node) return { ok: false };\n\n switch (node.type) {\n case \"Literal\":\n return { ok: true, value: node.value };\n\n case \"TemplateLiteral\":\n if (node.expressions?.length) return { ok: false };\n return { ok: true, value: node.quasis.map((q: any) => q.value.cooked ?? q.value.raw).join(\"\") };\n\n case \"ArrayExpression\": {\n const values: any[] = [];\n for (const el of node.elements) {\n if (el === null) {\n values.push(undefined);\n continue;\n }\n const result = tryStaticEval(el);\n if (!result.ok) return { ok: false };\n values.push(result.value);\n }\n return { ok: true, value: values };\n }\n\n case \"ObjectExpression\": {\n const obj: Record<string, any> = {};\n for (const prop of node.properties) {\n if (prop.type !== \"Property\" || prop.computed) return { ok: false };\n\n const keyName = getPropKeyName(prop.key);\n if (keyName === null) return { ok: false };\n\n const valueResult = tryStaticEval(prop.value);\n if (!valueResult.ok) return { ok: false };\n obj[keyName] = valueResult.value;\n }\n return { ok: true, value: obj };\n }\n\n case \"UnaryExpression\": {\n if (node.operator === \"-\") {\n const argResult = tryStaticEval(node.argument);\n if (!argResult.ok || typeof argResult.value !== \"number\") return { ok: false };\n return { ok: true, value: -argResult.value };\n }\n return { ok: false };\n }\n\n case \"TSAsExpression\":\n case \"TSSatisfiesExpression\":\n return tryStaticEval(node.expression);\n\n default:\n return { ok: false };\n }\n}\n\n/** Partial static eval: never bails on the whole structure for one dynamic sibling.\n * Unresolvable → undefined; bare identifiers → UnresolvedRef marker. */\nexport function tryStaticEvalPartial(node: any): any {\n if (!node) return undefined;\n\n switch (node.type) {\n case \"Literal\":\n return node.value;\n\n case \"Identifier\":\n return { [UNRESOLVED_REF_KEY]: node.name } satisfies UnresolvedRef;\n\n case \"TemplateLiteral\":\n if (node.expressions?.length) return undefined;\n return node.quasis.map((q: any) => q.value.cooked ?? q.value.raw).join(\"\");\n\n case \"ArrayExpression\":\n return node.elements.map((el: any) => (el === null ? undefined : tryStaticEvalPartial(el)));\n\n case \"ObjectExpression\": {\n const obj: Record<string, any> = {};\n for (const prop of node.properties) {\n if (prop.type !== \"Property\" || prop.computed) continue;\n\n const keyName = getPropKeyName(prop.key);\n if (keyName === null) continue;\n\n obj[keyName] = tryStaticEvalPartial(prop.value);\n }\n return obj;\n }\n\n case \"UnaryExpression\": {\n if (node.operator !== \"-\") return undefined;\n const argValue = tryStaticEvalPartial(node.argument);\n return typeof argValue === \"number\" ? -argValue : undefined;\n }\n\n case \"TSAsExpression\":\n case \"TSSatisfiesExpression\":\n return tryStaticEvalPartial(node.expression);\n\n default:\n return undefined;\n }\n}\n\n/** Extracts the callee name of a CallExpression, to check it against tracked function names. */\nexport function getCalleeName(callee: any): string | null {\n if (callee.type === \"Identifier\") return callee.name;\n if (callee.type === \"MemberExpression\" && callee.property.type === \"Identifier\") {\n return callee.property.name;\n }\n return null;\n}\n\n/** `export const X = ...` values, keyed by exported name, for cross-module ref resolution. */\nexport function collectStaticExports(ast: any): Record<string, unknown> {\n const exports: Record<string, unknown> = {};\n\n for (const statement of ast.body ?? ast.program?.body ?? []) {\n if (statement.type !== \"ExportNamedDeclaration\" || statement.declaration?.type !== \"VariableDeclaration\") continue;\n\n for (const declarator of statement.declaration.declarations) {\n if (declarator.id.type !== \"Identifier\" || !declarator.init) continue;\n exports[declarator.id.name] = tryStaticEvalPartial(declarator.init);\n }\n }\n\n return exports;\n}\n\n/** Extracts { localName -> importSource } from the module's import declarations. */\nexport function collectImportBindings(ast: any) {\n const bindings = new Map<string, { source: string; imported: string }>();\n\n walk(ast, {\n enter(node: any) {\n if (node.type !== \"ImportDeclaration\") return;\n const source = node.source.value;\n for (const spec of node.specifiers ?? []) {\n if (spec.type === \"ImportDefaultSpecifier\") {\n bindings.set(spec.local.name, { source, imported: \"default\" });\n } else if (spec.type === \"ImportSpecifier\") {\n bindings.set(spec.local.name, { source, imported: (spec.imported as any).name });\n } else if (spec.type === \"ImportNamespaceSpecifier\") {\n bindings.set(spec.local.name, { source, imported: \"*\" });\n }\n }\n },\n });\n\n return bindings;\n}\n\n/** Resolves styles.foo / styles[\"foo\"] → \"<specifier>::<className>\". Returns null if not a CSS Modules ref. */\nexport function resolveCssModuleRef(expr: any, importBindings: Map<string, { source: string; imported: string }>): string | null {\n if (expr.type !== \"MemberExpression\") return null;\n if (expr.object?.type !== \"Identifier\") return null;\n\n const binding = importBindings.get(expr.object.name);\n if (!binding) return null;\n if (!binding.source.endsWith(\".module.css\")) return null;\n if (binding.imported !== \"default\") return null;\n\n let propName: string | null = null;\n if (!expr.computed && expr.property?.type === \"Identifier\") {\n propName = expr.property.name;\n } else if (expr.computed && expr.property?.type === \"Literal\" && typeof expr.property.value === \"string\") {\n propName = expr.property.value;\n }\n if (propName === null) return null;\n\n return `${binding.source}::${propName}`;\n}\n\nconst DATA_ATTR_NAME_PATTERN = /^data-/;\n\n/** Detects `props.color`, bare `color`, or `props.color ?? \"fallback\"` — the common prop-forwarding\n * pattern where a component mirrors one of its own props onto a `data-*` attribute. Lets callers\n * narrow the attribute's possible values using literal usages of that prop name elsewhere in the app,\n * instead of treating the attribute as fully dynamic. */\nfunction extractPropRef(expr: any): { propRef: string; fallback?: string } | null {\n if (expr.type === \"Identifier\") return { propRef: expr.name };\n\n if (expr.type === \"MemberExpression\" && !expr.computed && expr.property?.type === \"Identifier\") {\n return { propRef: expr.property.name };\n }\n\n if (expr.type === \"LogicalExpression\" && (expr.operator === \"??\" || expr.operator === \"||\")) {\n const left = extractPropRef(expr.left);\n if (!left) return null;\n return expr.right.type === \"Literal\" && typeof expr.right.value === \"string\"\n ? { propRef: left.propRef, fallback: expr.right.value }\n : left;\n }\n\n return null;\n}\n\n/** Extracts JSX props to serializable form. Optional resolveExpr hook for custom resolution (e.g. CSS Modules). */\nexport function extractProps(attributes: any[], code: string, resolveExpr?: (expr: any) => { kind: string; value: unknown } | null) {\n return attributes.map((attr: any) => {\n if (attr.type === \"JSXSpreadAttribute\") {\n return { name: \"...spread\", kind: \"spread\", code: sourceOf(attr.argument, code) };\n }\n\n const name = attr.name.name ?? getJSXElementName(attr.name);\n\n if (attr.value == null) {\n return { name, kind: \"literal\", value: true };\n }\n\n if (attr.value.type === \"JSXExpressionContainer\") {\n const expr = attr.value.expression;\n\n if (resolveExpr) {\n const resolved = resolveExpr(expr);\n if (resolved) return { name, ...resolved };\n }\n\n const staticResult = tryStaticEval(expr);\n if (staticResult.ok) return { name, kind: \"literal\", value: staticResult.value };\n\n if (DATA_ATTR_NAME_PATTERN.test(name)) {\n const propRef = extractPropRef(expr);\n if (propRef) return { name, kind: \"prop-ref\", ...propRef };\n }\n\n return { name, kind: \"expr\", code: sourceOf(expr, code) };\n }\n\n return { name, kind: \"literal\", value: attr.value.value };\n });\n}\n","import { AnalyzerPlugin, pluginName, type ParseContext, type PluginName } from \"@purgeon/core\";\nimport { walk } from \"oxc-walker\";\n\nimport {\n collectImportBindings,\n collectStaticExports,\n extractProps,\n getCalleeName,\n getJSXElementName,\n resolveCssModuleRef,\n sourceOf,\n tryStaticEval,\n tryStaticEvalPartial,\n} from \"./ast-utils\";\nimport type { JsxAnalyzerOptions, JsxGraphNode } from \"./jsx-node\";\n\nconst DEFAULT_TRACKED_CALLS = [\"cva\", \"cx\", \"cn\", \"tv\", \"clsx\"];\n\nexport class JsxAnalyzer extends AnalyzerPlugin<JsxGraphNode> {\n readonly name: PluginName = pluginName(\"jsx-graph\");\n\n private readonly trackedCalls: Set<string>;\n\n constructor(options: JsxAnalyzerOptions = {}) {\n super();\n this.trackedCalls = new Set(options.trackedCalls ?? DEFAULT_TRACKED_CALLS);\n }\n\n protected isModuleSupported(id: string): boolean {\n return /\\.[jt]sx?$/.test(id);\n }\n\n protected parseModule(code: string, id: string, ctx: ParseContext): unknown {\n return ctx.parse(code, {\n lang: id.endsWith(\"x\") ? (id.endsWith(\".tsx\") ? \"tsx\" : \"jsx\") : \"ts\",\n sourceType: \"module\",\n });\n }\n\n protected createNode(id: string): JsxGraphNode {\n return {\n id,\n imports: [],\n importers: [],\n jsxUsages: [],\n classVariantCalls: [],\n styledComponentUsages: [],\n importBindings: {},\n staticExports: {},\n };\n }\n\n protected analyzeModule(_id: string, ast: unknown, code: string, node: JsxGraphNode): void {\n const importBindings = collectImportBindings(ast);\n node.importBindings = Object.fromEntries(importBindings);\n node.staticExports = collectStaticExports(ast);\n\n const resolveCssExpr = (expr: any) => {\n const ref = resolveCssModuleRef(expr, importBindings);\n return ref ? { kind: \"css-module-ref\", value: ref } : null;\n };\n\n walk(ast as Parameters<typeof walk>[0], {\n enter: (n) => {\n if (n.type === \"JSXOpeningElement\") {\n const name = getJSXElementName(n.name);\n const binding = name ? importBindings.get(name) : undefined;\n node.jsxUsages.push({\n component: name,\n resolvedFrom: binding ? binding.source : null,\n props: extractProps(n.attributes, code, resolveCssExpr),\n });\n return;\n }\n\n if (n.type === \"CallExpression\") {\n const calleeName = getCalleeName(n.callee);\n if (calleeName && this.trackedCalls.has(calleeName)) {\n const configResult = tryStaticEval(n.arguments[0]);\n node.classVariantCalls.push({\n name: calleeName,\n static: configResult.ok,\n config: configResult.ok ? configResult.value : tryStaticEvalPartial(n.arguments[0]),\n rawCode: configResult.ok ? null : sourceOf(n, code),\n });\n }\n return;\n }\n\n if (n.type === \"TaggedTemplateExpression\") {\n const tag = n.tag;\n if (tag.type === \"MemberExpression\" && tag.object.type === \"Identifier\" && tag.object.name === \"styled\") {\n const baseTag = tag.property.type === \"Identifier\" ? tag.property.name : null;\n node.styledComponentUsages.push({ baseTag, kind: \"element\" });\n } else if (tag.type === \"CallExpression\" && tag.callee.type === \"Identifier\" && tag.callee.name === \"styled\") {\n const arg = tag.arguments[0];\n const baseTag = arg && arg.type === \"Identifier\" ? arg.name : null;\n node.styledComponentUsages.push({ baseTag, kind: \"component\" });\n }\n }\n },\n });\n }\n}\n","import type { JsxPurgeIRNode, JsxPurgeIRUsage, JsxPurgeIRVariantCall, JsxPurgeIRImportBinding } from \"@purgeon/core\";\n\nimport type { JsxGraphNode, JsxUsage, ClassVariantCall } from \"./jsx-node\";\n\nexport function toJsxPurgeIR(node: JsxGraphNode): JsxPurgeIRNode {\n return {\n id: node.id,\n imports: node.imports,\n jsxUsages: node.jsxUsages.map(\n (usage: JsxUsage): JsxPurgeIRUsage => ({\n props: usage.props.map((prop) => ({\n name: (prop as { name: string }).name,\n kind: (prop as { kind: string }).kind,\n value: (prop as { value?: unknown }).value,\n propRef: (prop as { propRef?: string }).propRef,\n fallback: (prop as { fallback?: string }).fallback,\n })),\n }),\n ),\n classVariantCalls: node.classVariantCalls.map(\n (call: ClassVariantCall): JsxPurgeIRVariantCall => ({\n name: call.name,\n static: call.static,\n config: call.config,\n }),\n ),\n importBindings: Object.fromEntries(\n Object.entries(node.importBindings).map(([key, binding]) => [\n key,\n { source: binding.source, imported: binding.imported } satisfies JsxPurgeIRImportBinding,\n ]),\n ),\n staticExports: node.staticExports,\n } satisfies JsxPurgeIRNode;\n}\n","import { JsxAnalyzer } from \"./jsx-analyzer\";\nimport type { JsxAnalyzerOptions } from \"./jsx-node\";\n\nexport { JsxAnalyzer } from \"./jsx-analyzer\";\nexport { toJsxPurgeIR } from \"./purge-ir\";\nexport type { JsxAnalyzerOptions, JsxGraphNode, JsxUsage, ClassVariantCall, StyledComponentUsage } from \"./jsx-node\";\nexport const jsxAnalyzer = (options?: JsxAnalyzerOptions) => new JsxAnalyzer(options);\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@purgeon/analyzer-jsx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Walks JSX AST via oxc-walker, tracks element usages and class-variant calls (cva/cn/cx/tv/clsx) for purgeon",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ast",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"oxc-walker": "^1.0.0",
|
|
38
|
-
"@purgeon/core": "0.
|
|
38
|
+
"@purgeon/core": "0.2.0"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsdown"
|