sovr-patch 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.

Potentially problematic release.


This version of sovr-patch might be problematic. Click here for more details.

Files changed (102) hide show
  1. package/LICENSE +66 -0
  2. package/README.md +112 -0
  3. package/dist/audit/engine.js +140 -0
  4. package/dist/audit/engine.js.map +1 -0
  5. package/dist/audit/packs.js +21 -0
  6. package/dist/audit/packs.js.map +1 -0
  7. package/dist/audit/report.js +79 -0
  8. package/dist/audit/report.js.map +1 -0
  9. package/dist/audit/types.js +5 -0
  10. package/dist/audit/types.js.map +1 -0
  11. package/dist/cli/help.js +108 -0
  12. package/dist/cli/help.js.map +1 -0
  13. package/dist/cli/parse-argv.js +73 -0
  14. package/dist/cli/parse-argv.js.map +1 -0
  15. package/dist/commands/activate.js +179 -0
  16. package/dist/commands/activate.js.map +1 -0
  17. package/dist/commands/audit.js +89 -0
  18. package/dist/commands/audit.js.map +1 -0
  19. package/dist/commands/ci-gate.js +239 -0
  20. package/dist/commands/ci-gate.js.map +1 -0
  21. package/dist/commands/list-rules.js +11 -0
  22. package/dist/commands/list-rules.js.map +1 -0
  23. package/dist/commands/run.js +107 -0
  24. package/dist/commands/run.js.map +1 -0
  25. package/dist/commands/status.js +174 -0
  26. package/dist/commands/status.js.map +1 -0
  27. package/dist/core/engine.js +68 -0
  28. package/dist/core/engine.js.map +1 -0
  29. package/dist/core/license.js +243 -0
  30. package/dist/core/license.js.map +1 -0
  31. package/dist/core/load-project.js +41 -0
  32. package/dist/core/load-project.js.map +1 -0
  33. package/dist/core/report.js +68 -0
  34. package/dist/core/report.js.map +1 -0
  35. package/dist/core/rollback.js +160 -0
  36. package/dist/core/rollback.js.map +1 -0
  37. package/dist/core/safe-apply.js +75 -0
  38. package/dist/core/safe-apply.js.map +1 -0
  39. package/dist/core/types.js +3 -0
  40. package/dist/core/types.js.map +1 -0
  41. package/dist/core/update-check.js +162 -0
  42. package/dist/core/update-check.js.map +1 -0
  43. package/dist/core/verify.js +32 -0
  44. package/dist/core/verify.js.map +1 -0
  45. package/dist/index.js +124 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/patch/apply.js +14 -0
  48. package/dist/patch/apply.js.map +1 -0
  49. package/dist/patch/overlap.js +12 -0
  50. package/dist/patch/overlap.js.map +1 -0
  51. package/dist/patch/preview.js +48 -0
  52. package/dist/patch/preview.js.map +1 -0
  53. package/dist/rules/design-token/classify.js +16 -0
  54. package/dist/rules/design-token/classify.js.map +1 -0
  55. package/dist/rules/design-token/locate.js +113 -0
  56. package/dist/rules/design-token/locate.js.map +1 -0
  57. package/dist/rules/design-token/presets.js +53 -0
  58. package/dist/rules/design-token/presets.js.map +1 -0
  59. package/dist/rules/design-token/rule.js +13 -0
  60. package/dist/rules/design-token/rule.js.map +1 -0
  61. package/dist/rules/design-token/schema.js +84 -0
  62. package/dist/rules/design-token/schema.js.map +1 -0
  63. package/dist/rules/design-token/transform.js +27 -0
  64. package/dist/rules/design-token/transform.js.map +1 -0
  65. package/dist/rules/field-rename/classify.js +77 -0
  66. package/dist/rules/field-rename/classify.js.map +1 -0
  67. package/dist/rules/field-rename/locate.js +294 -0
  68. package/dist/rules/field-rename/locate.js.map +1 -0
  69. package/dist/rules/field-rename/rule.js +13 -0
  70. package/dist/rules/field-rename/rule.js.map +1 -0
  71. package/dist/rules/field-rename/schema.js +35 -0
  72. package/dist/rules/field-rename/schema.js.map +1 -0
  73. package/dist/rules/field-rename/transform.js +160 -0
  74. package/dist/rules/field-rename/transform.js.map +1 -0
  75. package/dist/rules/inline-style/classify.js +17 -0
  76. package/dist/rules/inline-style/classify.js.map +1 -0
  77. package/dist/rules/inline-style/locate.js +163 -0
  78. package/dist/rules/inline-style/locate.js.map +1 -0
  79. package/dist/rules/inline-style/presets.js +71 -0
  80. package/dist/rules/inline-style/presets.js.map +1 -0
  81. package/dist/rules/inline-style/rule.js +13 -0
  82. package/dist/rules/inline-style/rule.js.map +1 -0
  83. package/dist/rules/inline-style/schema.js +97 -0
  84. package/dist/rules/inline-style/schema.js.map +1 -0
  85. package/dist/rules/inline-style/transform.js +52 -0
  86. package/dist/rules/inline-style/transform.js.map +1 -0
  87. package/dist/rules/nullish-fallback/classify.js +22 -0
  88. package/dist/rules/nullish-fallback/classify.js.map +1 -0
  89. package/dist/rules/nullish-fallback/locate.js +175 -0
  90. package/dist/rules/nullish-fallback/locate.js.map +1 -0
  91. package/dist/rules/nullish-fallback/rule.js +13 -0
  92. package/dist/rules/nullish-fallback/rule.js.map +1 -0
  93. package/dist/rules/nullish-fallback/schema.js +10 -0
  94. package/dist/rules/nullish-fallback/schema.js.map +1 -0
  95. package/dist/rules/nullish-fallback/transform.js +49 -0
  96. package/dist/rules/nullish-fallback/transform.js.map +1 -0
  97. package/dist/rules/registry.js +21 -0
  98. package/dist/rules/registry.js.map +1 -0
  99. package/dist/rules/types.js +2 -0
  100. package/dist/rules/types.js.map +1 -0
  101. package/license.json +13 -0
  102. package/package.json +64 -0
@@ -0,0 +1,163 @@
1
+ import path from "node:path";
2
+ import { Node, SyntaxKind } from "ts-morph";
3
+ /**
4
+ * Locate JSX `style={{ ... }}` attributes that contain properties
5
+ * matching the configured mappings.
6
+ *
7
+ * For each matching property-value pair in a style object, we create a hit.
8
+ * The hit covers the entire JsxAttribute node (style={{ ... }}) so that
9
+ * transform can decide whether to replace the whole attribute or just parts.
10
+ */
11
+ export function locateInlineStyle(context) {
12
+ const results = [];
13
+ for (const sourceFile of context.loaded.sourceFiles) {
14
+ const filePath = path.relative(context.loaded.repoPath, sourceFile.getFilePath());
15
+ // Only scan TSX/JSX files
16
+ if (!filePath.endsWith(".tsx") && !filePath.endsWith(".jsx"))
17
+ continue;
18
+ // Find all JsxAttribute nodes named "style"
19
+ const jsxAttributes = sourceFile.getDescendantsOfKind(SyntaxKind.JsxAttribute);
20
+ for (const attr of jsxAttributes) {
21
+ const name = attr.getNameNode().getText();
22
+ if (name !== "style")
23
+ continue;
24
+ const initializer = attr.getInitializer();
25
+ if (!initializer)
26
+ continue;
27
+ // style={...} → the initializer is a JsxExpression
28
+ if (!Node.isJsxExpression(initializer))
29
+ continue;
30
+ const expression = initializer.getExpression();
31
+ if (!expression)
32
+ continue;
33
+ // style={{ ... }} → the expression is an ObjectLiteralExpression
34
+ if (!Node.isObjectLiteralExpression(expression))
35
+ continue;
36
+ const properties = expression.getProperties();
37
+ const matchAllStyles = context.config.matchAllStyles;
38
+ // In matchAllStyles mode (audit discovery), report ALL style attrs
39
+ // In map mode, only report style attrs with matching properties
40
+ const matchedMappings = [];
41
+ // Collect all literal properties for discovery
42
+ const allLiteralProps = [];
43
+ for (const prop of properties) {
44
+ if (!Node.isPropertyAssignment(prop))
45
+ continue;
46
+ const propName = prop.getNameNode().getText();
47
+ const propInit = prop.getInitializer();
48
+ if (!propInit)
49
+ continue;
50
+ let propValue;
51
+ if (Node.isStringLiteral(propInit)) {
52
+ propValue = propInit.getLiteralText();
53
+ }
54
+ else if (Node.isNumericLiteral(propInit)) {
55
+ propValue = propInit.getText();
56
+ }
57
+ else {
58
+ // Skip non-literal values (expressions, variables, etc.)
59
+ continue;
60
+ }
61
+ allLiteralProps.push({ propertyNode: prop, propertyName: propName, propertyValue: propValue });
62
+ // Check against mappings
63
+ let matched = false;
64
+ for (const mapping of context.config.mappings) {
65
+ if (mapping.property === propName &&
66
+ mapping.value.toLowerCase() === propValue.toLowerCase()) {
67
+ matchedMappings.push({
68
+ mapping,
69
+ propertyNode: prop,
70
+ propertyName: propName,
71
+ propertyValue: propValue,
72
+ });
73
+ matched = true;
74
+ break;
75
+ }
76
+ }
77
+ // In matchAllStyles mode, add unmapped properties too
78
+ if (!matched && matchAllStyles) {
79
+ matchedMappings.push({
80
+ mapping: null,
81
+ propertyNode: prop,
82
+ propertyName: propName,
83
+ propertyValue: propValue,
84
+ });
85
+ }
86
+ }
87
+ // In map mode, skip if nothing matched
88
+ if (!matchAllStyles && matchedMappings.length === 0)
89
+ continue;
90
+ // In discovery mode, skip if no literal properties at all
91
+ if (matchAllStyles && allLiteralProps.length === 0)
92
+ continue;
93
+ const totalProperties = properties.length;
94
+ const mappedCount = matchedMappings.filter((m) => m.mapping !== null).length;
95
+ const allMatched = mappedCount === totalProperties;
96
+ // Determine severity
97
+ // - matchAllStyles with no mappings → ambiguous (discovery)
98
+ // - all properties mapped → safe
99
+ // - some properties mapped → ambiguous
100
+ let severity;
101
+ let reason;
102
+ if (matchAllStyles && mappedCount === 0) {
103
+ severity = "ambiguous";
104
+ reason = "inline-style-discovered";
105
+ }
106
+ else if (allMatched) {
107
+ severity = "safe";
108
+ reason = "inline-style-all-mapped";
109
+ }
110
+ else {
111
+ severity = "ambiguous";
112
+ reason = "inline-style-partial-mapped";
113
+ }
114
+ // Create a single hit for the entire style attribute
115
+ const attrStart = attr.getStart();
116
+ const attrEnd = attr.getEnd();
117
+ const lineStarts = sourceFile.compilerNode.getLineStarts();
118
+ const lineIndex = attr.getStartLineNumber() - 1;
119
+ const lineStart = lineStarts[lineIndex] ?? 0;
120
+ const column = attrStart - lineStart + 1;
121
+ results.push({
122
+ node: attr,
123
+ hit: {
124
+ filePath,
125
+ line: attr.getStartLineNumber(),
126
+ column,
127
+ sourceText: attr.getText(),
128
+ severity,
129
+ reason,
130
+ start: attrStart,
131
+ end: attrEnd,
132
+ metadata: {
133
+ kind: "inline-style",
134
+ allMatched,
135
+ totalProperties,
136
+ matchedCount: mappedCount,
137
+ classNames: matchedMappings
138
+ .filter((m) => m.mapping !== null)
139
+ .map((m) => m.mapping.className),
140
+ matchedProperties: matchedMappings
141
+ .filter((m) => m.mapping !== null)
142
+ .map((m) => ({
143
+ property: m.propertyName,
144
+ value: m.propertyValue,
145
+ className: m.mapping.className,
146
+ })),
147
+ discoveredProperties: matchAllStyles
148
+ ? allLiteralProps.map((p) => ({
149
+ property: p.propertyName,
150
+ value: p.propertyValue,
151
+ }))
152
+ : undefined,
153
+ unmatchedProperties: properties
154
+ .filter((p) => !matchedMappings.some((m) => m.propertyNode === p))
155
+ .map((p) => p.getText()),
156
+ },
157
+ },
158
+ });
159
+ }
160
+ }
161
+ return results;
162
+ }
163
+ //# sourceMappingURL=locate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locate.js","sourceRoot":"","sources":["../../../src/rules/inline-style/locate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Inline Style Presets
3
+ *
4
+ * Presets provide predefined style → className mappings for common frameworks.
5
+ * Used in audit mode to give meaningful results without user-provided maps.
6
+ */
7
+ /**
8
+ * tailwind-common preset — most frequently used Tailwind CSS utility classes
9
+ */
10
+ const TAILWIND_COMMON = {
11
+ name: "tailwind-common",
12
+ description: "Common Tailwind CSS utility class mappings",
13
+ mappings: [
14
+ // Colors
15
+ { property: "color", value: "red", className: "text-red-500" },
16
+ { property: "color", value: "blue", className: "text-blue-500" },
17
+ { property: "color", value: "green", className: "text-green-500" },
18
+ { property: "color", value: "white", className: "text-white" },
19
+ { property: "color", value: "black", className: "text-black" },
20
+ { property: "color", value: "gray", className: "text-gray-500" },
21
+ { property: "color", value: "yellow", className: "text-yellow-500" },
22
+ // Background colors
23
+ { property: "backgroundColor", value: "red", className: "bg-red-500" },
24
+ { property: "backgroundColor", value: "blue", className: "bg-blue-500" },
25
+ { property: "backgroundColor", value: "green", className: "bg-green-500" },
26
+ { property: "backgroundColor", value: "white", className: "bg-white" },
27
+ { property: "backgroundColor", value: "black", className: "bg-black" },
28
+ { property: "backgroundColor", value: "gray", className: "bg-gray-500" },
29
+ // Display
30
+ { property: "display", value: "flex", className: "flex" },
31
+ { property: "display", value: "grid", className: "grid" },
32
+ { property: "display", value: "block", className: "block" },
33
+ { property: "display", value: "inline", className: "inline" },
34
+ { property: "display", value: "none", className: "hidden" },
35
+ { property: "display", value: "inline-block", className: "inline-block" },
36
+ // Font
37
+ { property: "fontWeight", value: "bold", className: "font-bold" },
38
+ { property: "fontWeight", value: "normal", className: "font-normal" },
39
+ { property: "fontStyle", value: "italic", className: "italic" },
40
+ // Text
41
+ { property: "textAlign", value: "center", className: "text-center" },
42
+ { property: "textAlign", value: "left", className: "text-left" },
43
+ { property: "textAlign", value: "right", className: "text-right" },
44
+ { property: "textDecoration", value: "underline", className: "underline" },
45
+ { property: "textDecoration", value: "none", className: "no-underline" },
46
+ // Position
47
+ { property: "position", value: "relative", className: "relative" },
48
+ { property: "position", value: "absolute", className: "absolute" },
49
+ { property: "position", value: "fixed", className: "fixed" },
50
+ { property: "position", value: "sticky", className: "sticky" },
51
+ // Overflow
52
+ { property: "overflow", value: "hidden", className: "overflow-hidden" },
53
+ { property: "overflow", value: "auto", className: "overflow-auto" },
54
+ { property: "overflow", value: "scroll", className: "overflow-scroll" },
55
+ // Cursor
56
+ { property: "cursor", value: "pointer", className: "cursor-pointer" },
57
+ { property: "cursor", value: "default", className: "cursor-default" },
58
+ ],
59
+ };
60
+ export const INLINE_STYLE_PRESETS = {
61
+ "tailwind-common": TAILWIND_COMMON,
62
+ };
63
+ export function getInlineStylePresetOrThrow(name) {
64
+ const preset = INLINE_STYLE_PRESETS[name];
65
+ if (!preset) {
66
+ const available = Object.keys(INLINE_STYLE_PRESETS).join(", ");
67
+ throw new Error(`inline-style: unknown preset "${name}". Available: ${available}`);
68
+ }
69
+ return preset;
70
+ }
71
+ //# sourceMappingURL=presets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.js","sourceRoot":"","sources":["../../../src/rules/inline-style/presets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { parseInlineStyleConfig } from "./schema.js";
2
+ import { locateInlineStyle } from "./locate.js";
3
+ import { classifyInlineStyle } from "./classify.js";
4
+ import { transformInlineStyle } from "./transform.js";
5
+ export const inlineStyleRule = {
6
+ name: "inline-style",
7
+ description: "Replace React inline style={{ ... }} attributes with className equivalents (e.g., Tailwind classes).",
8
+ parseConfig: parseInlineStyleConfig,
9
+ locate: locateInlineStyle,
10
+ classify: classifyInlineStyle,
11
+ transform: transformInlineStyle,
12
+ };
13
+ //# sourceMappingURL=rule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule.js","sourceRoot":"","sources":["../../../src/rules/inline-style/rule.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,CAAC,MAAM,eAAe,GAAsC;IAChE,IAAI,EAAE,cAAc;IACpB,WAAW,EACT,sGAAsG;IACxG,WAAW,EAAE,sBAAsB;IACnC,MAAM,EAAE,iBAAiB;IACzB,QAAQ,EAAE,mBAAmB;IAC7B,SAAS,EAAE,oBAAoB;CAChC,CAAC"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Inline Style to ClassName Rule — Dual Mode Schema
3
+ *
4
+ * RUN mode: Requires --map (strict, produces patches)
5
+ * AUDIT mode: Zero-config (scans all style={{}} attributes), optional --preset
6
+ *
7
+ * Example (run):
8
+ * sovr-patch run --rule inline-style --map "color:red=text-red" --repo ./web
9
+ *
10
+ * Example (audit):
11
+ * sovr-patch audit --rule inline-style --repo ./web
12
+ * sovr-patch audit --rule inline-style --preset tailwind-common --repo ./web
13
+ */
14
+ import { getInlineStylePresetOrThrow } from "./presets.js";
15
+ /**
16
+ * Parse CLI --map format: "property:value=className,..."
17
+ */
18
+ function parseMapString(mapStr) {
19
+ const mappings = [];
20
+ for (const entry of mapStr.split(",")) {
21
+ const trimmed = entry.trim();
22
+ if (!trimmed)
23
+ continue;
24
+ const eqIdx = trimmed.indexOf("=");
25
+ if (eqIdx === -1) {
26
+ throw new Error(`inline-style: invalid map entry "${trimmed}". Expected "property:value=className".`);
27
+ }
28
+ const left = trimmed.slice(0, eqIdx);
29
+ const className = trimmed.slice(eqIdx + 1).trim();
30
+ const colonIdx = left.indexOf(":");
31
+ if (colonIdx === -1) {
32
+ throw new Error(`inline-style: invalid map entry "${trimmed}". Expected "property:value=className".`);
33
+ }
34
+ const property = left.slice(0, colonIdx).trim();
35
+ const value = left.slice(colonIdx + 1).trim();
36
+ if (!property || !value || !className) {
37
+ throw new Error(`inline-style: invalid map entry "${trimmed}". All parts must be non-empty.`);
38
+ }
39
+ mappings.push({ property, value, className });
40
+ }
41
+ return mappings;
42
+ }
43
+ /**
44
+ * Parse inline-style config from CLI input.
45
+ *
46
+ * Behavior depends on mode:
47
+ * - mode="run" (default for run command): map is REQUIRED
48
+ * - mode="audit" (default for audit command): map is optional, defaults to scanning all style attrs
49
+ */
50
+ export function parseInlineStyleConfig(input) {
51
+ const mode = input.mode === "audit" ? "audit" : "run";
52
+ // Handle preset
53
+ const presetName = input.preset;
54
+ let presetMappings = [];
55
+ if (presetName) {
56
+ const preset = getInlineStylePresetOrThrow(presetName);
57
+ presetMappings = [...preset.mappings];
58
+ }
59
+ // Handle explicit mappings
60
+ let explicitMappings = [];
61
+ if (input.mappings && Array.isArray(input.mappings)) {
62
+ explicitMappings = input.mappings;
63
+ }
64
+ else if (input.map) {
65
+ const mapStr = String(input.map).trim();
66
+ if (mapStr) {
67
+ explicitMappings = parseMapString(mapStr);
68
+ }
69
+ }
70
+ // Merge preset + explicit mappings (explicit appended after preset)
71
+ const mergedMappings = [...presetMappings, ...explicitMappings];
72
+ if (mode === "run") {
73
+ // Run mode: mappings are required
74
+ if (mergedMappings.length === 0) {
75
+ throw new Error(`inline-style: "map" or "mappings" is required in run mode.\n` +
76
+ `CLI format: --map "color:red=text-red,backgroundColor:blue=bg-blue"\n` +
77
+ `Or use --preset tailwind-common`);
78
+ }
79
+ return {
80
+ mode: "run",
81
+ mappings: mergedMappings,
82
+ matchAllStyles: false,
83
+ removeEmptyStyle: input["remove-empty-style"] !== false,
84
+ preset: presetName,
85
+ };
86
+ }
87
+ // Audit mode: zero-config is OK
88
+ const matchAllStyles = mergedMappings.length === 0;
89
+ return {
90
+ mode: "audit",
91
+ mappings: mergedMappings,
92
+ matchAllStyles,
93
+ removeEmptyStyle: input["remove-empty-style"] !== false,
94
+ preset: presetName,
95
+ };
96
+ }
97
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/rules/inline-style/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Transform an inline-style hit into a className replacement.
3
+ *
4
+ * When all style properties are matched:
5
+ * style={{ color: "red", backgroundColor: "blue" }}
6
+ * → className="text-red bg-blue"
7
+ *
8
+ * When only some properties are matched (ambiguous, only if forced):
9
+ * style={{ color: "red", padding: 10 }}
10
+ * → className="text-red" style={{ padding: 10 }}
11
+ * (This case is classified as ambiguous and normally skipped)
12
+ */
13
+ export function transformInlineStyle(hit, context) {
14
+ const classNames = hit.metadata?.classNames;
15
+ const allMatched = Boolean(hit.metadata?.allMatched);
16
+ if (!classNames || classNames.length === 0) {
17
+ return null;
18
+ }
19
+ if (allMatched) {
20
+ // Full replacement: style={{ ... }} → className="..."
21
+ const classNameStr = classNames.join(" ");
22
+ const replacement = `className="${classNameStr}"`;
23
+ return {
24
+ filePath: hit.filePath,
25
+ start: hit.start,
26
+ end: hit.end,
27
+ before: hit.sourceText,
28
+ after: replacement,
29
+ ruleName: "inline-style",
30
+ reason: "full-style-to-class",
31
+ };
32
+ }
33
+ // Partial replacement: keep unmatched properties in style, add className
34
+ // This is classified as ambiguous, so normally won't reach here
35
+ const unmatchedProperties = hit.metadata?.unmatchedProperties;
36
+ if (!unmatchedProperties || unmatchedProperties.length === 0) {
37
+ return null;
38
+ }
39
+ const classNameStr = classNames.join(" ");
40
+ const remainingStyle = unmatchedProperties.join(", ");
41
+ const replacement = `className="${classNameStr}" style={{ ${remainingStyle} }}`;
42
+ return {
43
+ filePath: hit.filePath,
44
+ start: hit.start,
45
+ end: hit.end,
46
+ before: hit.sourceText,
47
+ after: replacement,
48
+ ruleName: "inline-style",
49
+ reason: "partial-style-to-class",
50
+ };
51
+ }
52
+ //# sourceMappingURL=transform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transform.js","sourceRoot":"","sources":["../../../src/rules/inline-style/transform.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAClC,GAAgB,EAChB,OAAuC;IAEvC,MAAM,UAAU,GAAG,GAAG,CAAC,QAAQ,EAAE,UAAkC,CAAC;IACpE,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAErD,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,UAAU,EAAE,CAAC;QACf,sDAAsD;QACtD,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1C,MAAM,WAAW,GAAG,cAAc,YAAY,GAAG,CAAC;QAElD,OAAO;YACL,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,MAAM,EAAE,GAAG,CAAC,UAAU;YACtB,KAAK,EAAE,WAAW;YAClB,QAAQ,EAAE,cAAc;YACxB,MAAM,EAAE,qBAAqB;SAC9B,CAAC;IACJ,CAAC;IAED,yEAAyE;IACzE,gEAAgE;IAChE,MAAM,mBAAmB,GAAG,GAAG,CAAC,QAAQ,EAAE,mBAA2C,CAAC;IACtF,IAAI,CAAC,mBAAmB,IAAI,mBAAmB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC7D,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,YAAY,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,cAAc,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtD,MAAM,WAAW,GAAG,cAAc,YAAY,cAAc,cAAc,KAAK,CAAC;IAEhF,OAAO;QACL,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,MAAM,EAAE,GAAG,CAAC,UAAU;QACtB,KAAK,EAAE,WAAW;QAClB,QAAQ,EAAE,cAAc;QACxB,MAAM,EAAE,wBAAwB;KACjC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,22 @@
1
+ import { Node } from "ts-morph";
2
+ export function classifyNullishFallback(located, _context) {
3
+ const node = located.node;
4
+ const hit = { ...located.hit };
5
+ // If already wrapped in ?? expression, mark as ambiguous
6
+ const parent = node.getParent();
7
+ if (parent && Node.isBinaryExpression(parent)) {
8
+ const op = parent.getOperatorToken().getText();
9
+ if (op === "??") {
10
+ return { ...hit, severity: "ambiguous", reason: "already-nullish-coalesced" };
11
+ }
12
+ }
13
+ // If on the left side of assignment, mark as unsafe
14
+ if (parent && Node.isBinaryExpression(parent)) {
15
+ const op = parent.getOperatorToken().getText();
16
+ if (op === "=" && parent.getLeft() === node) {
17
+ return { ...hit, severity: "unsafe", reason: "assignment-left-hand-side" };
18
+ }
19
+ }
20
+ return hit;
21
+ }
22
+ //# sourceMappingURL=classify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify.js","sourceRoot":"","sources":["../../../src/rules/nullish-fallback/classify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAKhC,MAAM,UAAU,uBAAuB,CACrC,OAAyB,EACzB,QAA4C;IAE5C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAY,CAAC;IAClC,MAAM,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAE/B,yDAAyD;IACzD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IAChC,IAAI,MAAM,IAAI,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9C,MAAM,EAAE,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC,OAAO,EAAE,CAAC;QAC/C,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,OAAO,EAAE,GAAG,GAAG,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC;QAChF,CAAC;IACH,CAAC;IAED,oDAAoD;IACpD,IAAI,MAAM,IAAI,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9C,MAAM,EAAE,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC,OAAO,EAAE,CAAC;QAC/C,IAAI,EAAE,KAAK,GAAG,IAAI,MAAM,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC;YAC5C,OAAO,EAAE,GAAG,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC;QAC7E,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,175 @@
1
+ import path from "node:path";
2
+ import { Node, SyntaxKind } from "ts-morph";
3
+ function getColumnFromNode(node) {
4
+ const sourceFile = node.getSourceFile();
5
+ const lineStarts = sourceFile.compilerNode.getLineStarts();
6
+ const lineIndex = node.getStartLineNumber() - 1;
7
+ const lineStart = lineStarts[lineIndex] ?? 0;
8
+ return node.getStart() - lineStart + 1;
9
+ }
10
+ /**
11
+ * Extract the full access chain from a PropertyAccessExpression.
12
+ */
13
+ function extractAccessChain(node) {
14
+ const parts = [];
15
+ let current = node;
16
+ while (Node.isPropertyAccessExpression(current)) {
17
+ parts.unshift(current.getName());
18
+ current = current.getExpression();
19
+ }
20
+ if (Node.isIdentifier(current)) {
21
+ parts.unshift(current.getText());
22
+ return parts;
23
+ }
24
+ if (current.getKind() === SyntaxKind.ThisKeyword) {
25
+ parts.unshift("this");
26
+ return parts;
27
+ }
28
+ return null;
29
+ }
30
+ function parseTarget(target) {
31
+ return target.replace(/\?\./g, ".").split(".").map((x) => x.trim()).filter(Boolean);
32
+ }
33
+ function chainsMatch(chain, targetChain) {
34
+ if (chain.length !== targetChain.length)
35
+ return false;
36
+ return chain.every((item, index) => item === targetChain[index]);
37
+ }
38
+ /**
39
+ * Walk up from the matched node to find the outermost PropertyAccessExpression
40
+ * chain that contains this node as a sub-expression.
41
+ *
42
+ * For example, if we matched `data.result` inside `data.result?.data`,
43
+ * this returns the `data.result?.data` node.
44
+ *
45
+ * Returns null if the matched node is already the outermost access.
46
+ */
47
+ function findOuterChain(matchedNode) {
48
+ let current = matchedNode.getParent();
49
+ let outermost = null;
50
+ while (current && Node.isPropertyAccessExpression(current)) {
51
+ outermost = current;
52
+ current = current.getParent();
53
+ }
54
+ return outermost;
55
+ }
56
+ /**
57
+ * Extract the "tail" part of the outer chain relative to the matched node.
58
+ *
59
+ * For `data.result?.data.items` where matched = `data.result`:
60
+ * tail = "?.data.items" (the raw text after the matched node)
61
+ */
62
+ function extractTailText(matchedNode, outerNode) {
63
+ const outerText = outerNode.getText();
64
+ const matchedEnd = matchedNode.getEnd();
65
+ const outerStart = outerNode.getStart();
66
+ const tailOffset = matchedEnd - outerStart;
67
+ return outerText.slice(tailOffset);
68
+ }
69
+ /**
70
+ * Detect if the effective node (the node that will be replaced) is inside
71
+ * a binary expression with an operator that has higher precedence than ??.
72
+ *
73
+ * ?? has very low precedence (5). Most binary operators have higher precedence:
74
+ * +, -, *, /, %, **, <, >, <=, >=, ==, ===, !=, !==, &, |, ^, <<, >>, >>>
75
+ *
76
+ * If so, we need to wrap the ?? expression in parentheses.
77
+ */
78
+ function needsParentheses(effectiveNode) {
79
+ const parent = effectiveNode.getParent();
80
+ if (!parent)
81
+ return false;
82
+ // If parent is a BinaryExpression and the operator is NOT ?? or ??=
83
+ if (Node.isBinaryExpression(parent)) {
84
+ const op = parent.getOperatorToken().getText();
85
+ // ?? and ??= don't need extra parens
86
+ if (op === "??" || op === "??=")
87
+ return false;
88
+ // Assignment operators don't need parens (=, +=, -=, etc.)
89
+ if (op.endsWith("=") && op !== "==" && op !== "!=" && op !== "===" && op !== "!==" && op !== "<=" && op !== ">=") {
90
+ return false;
91
+ }
92
+ // All other binary operators have higher precedence than ??
93
+ return true;
94
+ }
95
+ // If parent is a ConditionalExpression (ternary), need parens
96
+ if (Node.isConditionalExpression(parent)) {
97
+ return true;
98
+ }
99
+ // Template literal expressions don't need parens
100
+ if (parent.getKind() === SyntaxKind.TemplateSpan) {
101
+ return false;
102
+ }
103
+ return false;
104
+ }
105
+ export function locateNullishFallback(context) {
106
+ const results = [];
107
+ const targetChain = parseTarget(context.config.target);
108
+ for (const sourceFile of context.loaded.sourceFiles) {
109
+ const nodes = sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAccessExpression);
110
+ for (const node of nodes) {
111
+ const chain = extractAccessChain(node);
112
+ if (!chain)
113
+ continue;
114
+ if (!chainsMatch(chain, targetChain))
115
+ continue;
116
+ // Check if this matched node is part of a larger property access chain
117
+ const outerNode = findOuterChain(node);
118
+ if (outerNode) {
119
+ // The matched target is a sub-expression of a larger chain.
120
+ // We need to replace the ENTIRE outer chain with (target ?? fallback).tail
121
+ const tailText = extractTailText(node, outerNode);
122
+ // Convert optional chain tail (?.foo) to regular access (.foo)
123
+ // because after ?? fallback, the value is guaranteed non-nullish
124
+ const normalizedTail = tailText.replace(/\?\./g, ".");
125
+ // Check if the outer node needs parentheses due to operator precedence
126
+ const wrapParens = needsParentheses(outerNode);
127
+ results.push({
128
+ node: outerNode,
129
+ hit: {
130
+ filePath: path.relative(context.loaded.repoPath, sourceFile.getFilePath()),
131
+ line: node.getStartLineNumber(),
132
+ column: getColumnFromNode(node),
133
+ sourceText: outerNode.getText(),
134
+ severity: "safe",
135
+ reason: "nullish-target-in-chain",
136
+ start: outerNode.getStart(),
137
+ end: outerNode.getEnd(),
138
+ metadata: {
139
+ kind: "nullish-target-chain",
140
+ accessChain: chain,
141
+ matchedText: node.getText(),
142
+ tailText: normalizedTail,
143
+ needsParentheses: wrapParens,
144
+ },
145
+ },
146
+ });
147
+ }
148
+ else {
149
+ // The matched target is a standalone expression (no further chain)
150
+ // Check if it needs parentheses due to operator precedence
151
+ const wrapParens = needsParentheses(node);
152
+ results.push({
153
+ node,
154
+ hit: {
155
+ filePath: path.relative(context.loaded.repoPath, sourceFile.getFilePath()),
156
+ line: node.getStartLineNumber(),
157
+ column: getColumnFromNode(node),
158
+ sourceText: node.getText(),
159
+ severity: "safe",
160
+ reason: "nullish-target-match",
161
+ start: node.getStart(),
162
+ end: node.getEnd(),
163
+ metadata: {
164
+ kind: "nullish-target",
165
+ accessChain: chain,
166
+ needsParentheses: wrapParens,
167
+ },
168
+ },
169
+ });
170
+ }
171
+ }
172
+ }
173
+ return results;
174
+ }
175
+ //# sourceMappingURL=locate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locate.js","sourceRoot":"","sources":["../../../src/rules/nullish-fallback/locate.ts"],"names":[],"mappings":"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"}