@ikas/component-cli 1.4.0-beta.81 → 1.4.0-beta.82
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/utils/__tests__/detect-theme-global-name-lookup.test.d.ts +2 -0
- package/dist/utils/__tests__/detect-theme-global-name-lookup.test.d.ts.map +1 -0
- package/dist/utils/__tests__/detect-theme-global-name-lookup.test.js +83 -0
- package/dist/utils/__tests__/detect-theme-global-name-lookup.test.js.map +1 -0
- package/dist/utils/detect-theme-global-name-lookup.d.ts +38 -0
- package/dist/utils/detect-theme-global-name-lookup.d.ts.map +1 -0
- package/dist/utils/detect-theme-global-name-lookup.js +173 -0
- package/dist/utils/detect-theme-global-name-lookup.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-theme-global-name-lookup.test.d.ts","sourceRoot":"","sources":["../../../src/utils/__tests__/detect-theme-global-name-lookup.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { detectThemeGlobalNameLookup } from "../detect-theme-global-name-lookup.js";
|
|
2
|
+
const scan = (src) => detectThemeGlobalNameLookup(src, "src/test.tsx");
|
|
3
|
+
describe("detectThemeGlobalNameLookup", () => {
|
|
4
|
+
// ---- Positives: by-name lookups that must be flagged ----
|
|
5
|
+
it("flags a chained .find by name", () => {
|
|
6
|
+
const findings = scan(`
|
|
7
|
+
import { getThemeColors } from "@ikas/bp-storefront";
|
|
8
|
+
const primary = getThemeColors().find(c => c.name === "Primary");
|
|
9
|
+
`);
|
|
10
|
+
expect(findings).toHaveLength(1);
|
|
11
|
+
expect(findings[0].accessor).toBe("getThemeColors");
|
|
12
|
+
expect(findings[0].line).toBe(3);
|
|
13
|
+
});
|
|
14
|
+
it("flags a var-stored result filtered by name", () => {
|
|
15
|
+
const findings = scan(`
|
|
16
|
+
import { getThemeKeyframes } from "@ikas/bp-storefront";
|
|
17
|
+
const kfs = getThemeKeyframes();
|
|
18
|
+
const hover = kfs.find(k => k.name === "hoverLift");
|
|
19
|
+
`);
|
|
20
|
+
expect(findings).toHaveLength(1);
|
|
21
|
+
expect(findings[0].accessor).toBe("getThemeKeyframes");
|
|
22
|
+
});
|
|
23
|
+
it("flags a .filter by displayName", () => {
|
|
24
|
+
const findings = scan(`
|
|
25
|
+
import { getThemeSettings } from "@ikas/bp-storefront";
|
|
26
|
+
const matches = getThemeSettings().filter(s => s.displayName === "Brand Color");
|
|
27
|
+
`);
|
|
28
|
+
expect(findings).toHaveLength(1);
|
|
29
|
+
expect(findings[0].accessor).toBe("getThemeSettings");
|
|
30
|
+
});
|
|
31
|
+
it("flags a destructured callback param compared by name", () => {
|
|
32
|
+
const findings = scan(`
|
|
33
|
+
import { getThemeTypography } from "@ikas/bp-storefront";
|
|
34
|
+
const heading = getThemeTypography().find(({ name }) => name === "Heading");
|
|
35
|
+
`);
|
|
36
|
+
expect(findings).toHaveLength(1);
|
|
37
|
+
expect(findings[0].accessor).toBe("getThemeTypography");
|
|
38
|
+
});
|
|
39
|
+
it("flags name lookup on getThemeColorSchemes().values", () => {
|
|
40
|
+
const findings = scan(`
|
|
41
|
+
import { getThemeColorSchemes } from "@ikas/bp-storefront";
|
|
42
|
+
const dark = getThemeColorSchemes().values.find(v => v.name === "Dark");
|
|
43
|
+
`);
|
|
44
|
+
expect(findings).toHaveLength(1);
|
|
45
|
+
expect(findings[0].accessor).toBe("getThemeColorSchemes");
|
|
46
|
+
});
|
|
47
|
+
// ---- Negatives: id-based / unrelated usage that must NOT be flagged ----
|
|
48
|
+
it("does not flag id/ref-based lookup", () => {
|
|
49
|
+
const findings = scan(`
|
|
50
|
+
import { getThemeKeyframes } from "@ikas/bp-storefront";
|
|
51
|
+
const hover = getThemeKeyframes().find(k => k.ref === "_i3x5JPYbPI");
|
|
52
|
+
`);
|
|
53
|
+
expect(findings).toHaveLength(0);
|
|
54
|
+
});
|
|
55
|
+
it("does not flag mapping to a stable field", () => {
|
|
56
|
+
const findings = scan(`
|
|
57
|
+
import { getThemeColors } from "@ikas/bp-storefront";
|
|
58
|
+
const vars = getThemeColors().map(c => c.cssVar);
|
|
59
|
+
`);
|
|
60
|
+
expect(findings).toHaveLength(0);
|
|
61
|
+
});
|
|
62
|
+
it("does not flag a .name compare on a non-accessor array", () => {
|
|
63
|
+
const findings = scan(`
|
|
64
|
+
const products = getProducts();
|
|
65
|
+
const found = products.find(p => p.name === "Shirt");
|
|
66
|
+
`);
|
|
67
|
+
expect(findings).toHaveLength(0);
|
|
68
|
+
});
|
|
69
|
+
it("does not flag getThemeSetting by stable key", () => {
|
|
70
|
+
const findings = scan(`
|
|
71
|
+
import { getThemeSetting } from "@ikas/bp-storefront";
|
|
72
|
+
const v = getThemeSetting("_6Q0KV7VGGM");
|
|
73
|
+
`);
|
|
74
|
+
expect(findings).toHaveLength(0);
|
|
75
|
+
});
|
|
76
|
+
it("returns empty for source without any theme accessor", () => {
|
|
77
|
+
expect(scan(`const x = [1, 2, 3].find(n => n === 2);`)).toHaveLength(0);
|
|
78
|
+
});
|
|
79
|
+
it("is resilient to unparsable source", () => {
|
|
80
|
+
expect(scan(`getThemeColors().find(c => c.name === `)).toEqual(expect.any(Array));
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
//# sourceMappingURL=detect-theme-global-name-lookup.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-theme-global-name-lookup.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/detect-theme-global-name-lookup.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect when a code component (or a shared module it imports) resolves a theme
|
|
3
|
+
* global by its human NAME instead of its stable id — e.g.
|
|
4
|
+
*
|
|
5
|
+
* getThemeColors().find(c => c.name === "Primary")
|
|
6
|
+
* const cols = getThemeColors(); cols.find(c => c.name === "Primary")
|
|
7
|
+
*
|
|
8
|
+
* Why this matters: a customer can install TWO partner design assets into ONE
|
|
9
|
+
* theme. Each asset brings its own globals, so two colors named "Primary" (with
|
|
10
|
+
* DIFFERENT stable ids) can coexist. Every SDK accessor resolves by stable id
|
|
11
|
+
* (`getThemeSetting("_id")`, `var(--id)`, `_id` class/ref), so id-based code is
|
|
12
|
+
* collision-safe. The ONLY way to pick the wrong "Primary" is to filter an
|
|
13
|
+
* accessor RESULT by `.name` / `.displayName` — names are not unique, and
|
|
14
|
+
* `.find` returns whichever happens to come first. This detector flags exactly
|
|
15
|
+
* that anti-pattern so publish can warn the author to reference by id.
|
|
16
|
+
*
|
|
17
|
+
* AST (not a regex on minified output): the CLI already depends on `typescript`,
|
|
18
|
+
* and at build time the partner SOURCE is available, where the accessor call and
|
|
19
|
+
* the callback param are intact. A minified-bundle scan would be both noisy
|
|
20
|
+
* (every `product.name ===`) and leaky (a result stored in a var breaks the
|
|
21
|
+
* chain). This runs on source, so it sees both the chained and var-stored forms.
|
|
22
|
+
*/
|
|
23
|
+
export type ThemeGlobalNameLookupFinding = {
|
|
24
|
+
/** Source path relative to the project root. */
|
|
25
|
+
file: string;
|
|
26
|
+
/** 1-based line of the offending array-method call. */
|
|
27
|
+
line: number;
|
|
28
|
+
/** The theme accessor whose result is filtered by name (e.g. "getThemeColors"). */
|
|
29
|
+
accessor: string;
|
|
30
|
+
/** Short single-line excerpt of the offending expression. */
|
|
31
|
+
snippet: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Scan one source file's text and return findings for any by-name theme-global
|
|
35
|
+
* lookup. Resilient: a parse failure yields no findings (the lint never blocks a build).
|
|
36
|
+
*/
|
|
37
|
+
export declare function detectThemeGlobalNameLookup(sourceText: string, fileName: string): ThemeGlobalNameLookupFinding[];
|
|
38
|
+
//# sourceMappingURL=detect-theme-global-name-lookup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-theme-global-name-lookup.d.ts","sourceRoot":"","sources":["../../src/utils/detect-theme-global-name-lookup.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,MAAM,4BAA4B,GAAG;IACzC,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAwIF;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,4BAA4B,EAAE,CA2ChC"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
// SDK accessors that return a LIST of tokens, each carrying a non-unique `name`.
|
|
3
|
+
// `getThemeSetting`/`getThemeSettingValue` are intentionally excluded: they take a
|
|
4
|
+
// stable key argument, not a name, so they cannot be name-filtered. Duplicated here
|
|
5
|
+
// (not imported from editor-core) because the CLI ships standalone to npm — same
|
|
6
|
+
// convention as the duplicated CSS scoper in compile.ts.
|
|
7
|
+
const LIST_ACCESSORS = new Set([
|
|
8
|
+
"getThemeColors",
|
|
9
|
+
"getThemeKeyframes",
|
|
10
|
+
"getThemeTypography",
|
|
11
|
+
"getThemeBreakpoints",
|
|
12
|
+
"getThemeSettings",
|
|
13
|
+
"getThemeColorSchemes",
|
|
14
|
+
]);
|
|
15
|
+
// Array methods that take a per-element predicate/callback where a `.name` compare
|
|
16
|
+
// would live. `map`/`forEach`/`reduce` are included because a name compare can hide
|
|
17
|
+
// in any of them (e.g. `getThemeColors().map(c => c.name === target ? ... : ...)`).
|
|
18
|
+
const ARRAY_CALLBACK_METHODS = new Set([
|
|
19
|
+
"find",
|
|
20
|
+
"filter",
|
|
21
|
+
"some",
|
|
22
|
+
"every",
|
|
23
|
+
"findIndex",
|
|
24
|
+
"findLast",
|
|
25
|
+
"findLastIndex",
|
|
26
|
+
"forEach",
|
|
27
|
+
"map",
|
|
28
|
+
"flatMap",
|
|
29
|
+
"reduce",
|
|
30
|
+
]);
|
|
31
|
+
// Token properties whose comparison signals a by-name lookup. `name` and the
|
|
32
|
+
// settings token's `displayName` are the human labels; everything stable
|
|
33
|
+
// (`id`/`ref`/`cssVar`/`className`) is fine and intentionally NOT listed.
|
|
34
|
+
const NAME_PROPS = new Set(["name", "displayName"]);
|
|
35
|
+
/** The accessor name if `node` is (or unwraps to) a call to a LIST_ACCESSOR, else undefined. */
|
|
36
|
+
function listAccessorOf(node) {
|
|
37
|
+
// Unwrap `getThemeColorSchemes().values` / `.schemes` and parenthesized forms so the
|
|
38
|
+
// underlying accessor call is still recognized.
|
|
39
|
+
let expr = node;
|
|
40
|
+
if (ts.isParenthesizedExpression(expr))
|
|
41
|
+
expr = expr.expression;
|
|
42
|
+
if (ts.isPropertyAccessExpression(expr))
|
|
43
|
+
expr = expr.expression;
|
|
44
|
+
if (ts.isParenthesizedExpression(expr))
|
|
45
|
+
expr = expr.expression;
|
|
46
|
+
if (!ts.isCallExpression(expr))
|
|
47
|
+
return undefined;
|
|
48
|
+
const callee = expr.expression;
|
|
49
|
+
const name = ts.isIdentifier(callee)
|
|
50
|
+
? callee.text
|
|
51
|
+
: ts.isPropertyAccessExpression(callee)
|
|
52
|
+
? callee.name.text
|
|
53
|
+
: undefined;
|
|
54
|
+
return name && LIST_ACCESSORS.has(name) ? name : undefined;
|
|
55
|
+
}
|
|
56
|
+
/** Collect local `const x = getTheme*()` bindings so var-stored results are also tracked. */
|
|
57
|
+
function collectAccessorBoundVars(sourceFile) {
|
|
58
|
+
const bound = new Map();
|
|
59
|
+
const visit = (node) => {
|
|
60
|
+
if (ts.isVariableDeclaration(node) &&
|
|
61
|
+
node.initializer &&
|
|
62
|
+
ts.isIdentifier(node.name)) {
|
|
63
|
+
const accessor = listAccessorOf(node.initializer);
|
|
64
|
+
if (accessor)
|
|
65
|
+
bound.set(node.name.text, accessor);
|
|
66
|
+
}
|
|
67
|
+
ts.forEachChild(node, visit);
|
|
68
|
+
};
|
|
69
|
+
visit(sourceFile);
|
|
70
|
+
return bound;
|
|
71
|
+
}
|
|
72
|
+
/** True if the callback compares its iterated element's `.name`/`.displayName`. */
|
|
73
|
+
function callbackComparesName(callback, sourceFile) {
|
|
74
|
+
if (!ts.isArrowFunction(callback) && !ts.isFunctionExpression(callback))
|
|
75
|
+
return false;
|
|
76
|
+
const firstParam = callback.parameters[0];
|
|
77
|
+
if (!firstParam)
|
|
78
|
+
return false;
|
|
79
|
+
// Element accessed as `param.name` — the common `c => c.name === ...` form.
|
|
80
|
+
const paramName = ts.isIdentifier(firstParam.name) ? firstParam.name.text : undefined;
|
|
81
|
+
// Element destructured as `({ name }) => name === ...`.
|
|
82
|
+
const destructuredNames = new Set();
|
|
83
|
+
if (ts.isObjectBindingPattern(firstParam.name)) {
|
|
84
|
+
for (const el of firstParam.name.elements) {
|
|
85
|
+
const prop = el.propertyName ?? el.name;
|
|
86
|
+
if (ts.isIdentifier(prop) && NAME_PROPS.has(prop.text)) {
|
|
87
|
+
if (ts.isIdentifier(el.name))
|
|
88
|
+
destructuredNames.add(el.name.text);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!paramName && destructuredNames.size === 0)
|
|
93
|
+
return false;
|
|
94
|
+
let found = false;
|
|
95
|
+
const visit = (node) => {
|
|
96
|
+
if (found)
|
|
97
|
+
return;
|
|
98
|
+
// `param.name` / `param.displayName` referenced anywhere in the body.
|
|
99
|
+
if (paramName &&
|
|
100
|
+
ts.isPropertyAccessExpression(node) &&
|
|
101
|
+
ts.isIdentifier(node.expression) &&
|
|
102
|
+
node.expression.text === paramName &&
|
|
103
|
+
NAME_PROPS.has(node.name.text)) {
|
|
104
|
+
found = true;
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// A destructured `name`/`displayName` identifier used in an equality compare.
|
|
108
|
+
if (destructuredNames.size > 0 && ts.isBinaryExpression(node)) {
|
|
109
|
+
const op = node.operatorToken.kind;
|
|
110
|
+
const isEq = op === ts.SyntaxKind.EqualsEqualsEqualsToken ||
|
|
111
|
+
op === ts.SyntaxKind.EqualsEqualsToken ||
|
|
112
|
+
op === ts.SyntaxKind.ExclamationEqualsEqualsToken ||
|
|
113
|
+
op === ts.SyntaxKind.ExclamationEqualsToken;
|
|
114
|
+
if (isEq) {
|
|
115
|
+
for (const side of [node.left, node.right]) {
|
|
116
|
+
if (ts.isIdentifier(side) && destructuredNames.has(side.text)) {
|
|
117
|
+
found = true;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
ts.forEachChild(node, visit);
|
|
124
|
+
};
|
|
125
|
+
visit(callback);
|
|
126
|
+
return found;
|
|
127
|
+
}
|
|
128
|
+
function oneLineSnippet(node, sourceFile) {
|
|
129
|
+
const text = node.getText(sourceFile).replace(/\s+/g, " ").trim();
|
|
130
|
+
return text.length > 120 ? `${text.slice(0, 117)}...` : text;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Scan one source file's text and return findings for any by-name theme-global
|
|
134
|
+
* lookup. Resilient: a parse failure yields no findings (the lint never blocks a build).
|
|
135
|
+
*/
|
|
136
|
+
export function detectThemeGlobalNameLookup(sourceText, fileName) {
|
|
137
|
+
if (!sourceText.includes("getTheme"))
|
|
138
|
+
return [];
|
|
139
|
+
const findings = [];
|
|
140
|
+
try {
|
|
141
|
+
const sourceFile = ts.createSourceFile(fileName, sourceText, ts.ScriptTarget.Latest,
|
|
142
|
+
/* setParentNodes */ true, fileName.endsWith(".tsx") || fileName.endsWith(".jsx") ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
|
|
143
|
+
const boundVars = collectAccessorBoundVars(sourceFile);
|
|
144
|
+
const visit = (node) => {
|
|
145
|
+
// Looking for `<arraySource>.<method>(<callback>)`.
|
|
146
|
+
if (ts.isCallExpression(node) &&
|
|
147
|
+
ts.isPropertyAccessExpression(node.expression) &&
|
|
148
|
+
ARRAY_CALLBACK_METHODS.has(node.expression.name.text) &&
|
|
149
|
+
node.arguments.length > 0) {
|
|
150
|
+
const arraySource = node.expression.expression;
|
|
151
|
+
// (a) direct chain on an accessor call, or (b) a var bound to an accessor result.
|
|
152
|
+
const accessor = listAccessorOf(arraySource) ??
|
|
153
|
+
(ts.isIdentifier(arraySource) ? boundVars.get(arraySource.text) : undefined);
|
|
154
|
+
if (accessor && callbackComparesName(node.arguments[0], sourceFile)) {
|
|
155
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
156
|
+
findings.push({
|
|
157
|
+
file: fileName,
|
|
158
|
+
line: line + 1,
|
|
159
|
+
accessor,
|
|
160
|
+
snippet: oneLineSnippet(node, sourceFile),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
ts.forEachChild(node, visit);
|
|
165
|
+
};
|
|
166
|
+
visit(sourceFile);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
return findings;
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=detect-theme-global-name-lookup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-theme-global-name-lookup.js","sourceRoot":"","sources":["../../src/utils/detect-theme-global-name-lookup.ts"],"names":[],"mappings":"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"}
|