quiver-cli 1.2.0 → 1.3.1
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/cli.js +237 -10
- package/package.json +1 -1
- package/template/.agents/skills/agent-browser/SKILL.md +1 -0
- package/template/.agents/skills/apps/skybridge/SKILL.md +2 -0
- package/template/.agents/skills/data/prisma-cli/SKILL.md +25 -7
- package/template/.agents/skills/data/prisma-cli/references/agent-safety.md +27 -0
- package/template/.agents/skills/data/prisma-cli/references/complete.md +22 -0
- package/template/.agents/skills/data/prisma-cli/references/db-push.md +2 -0
- package/template/.agents/skills/data/prisma-cli/references/init.md +3 -0
- package/template/.agents/skills/data/prisma-cli/references/mcp.md +2 -1
- package/template/.agents/skills/data/prisma-cli/references/migrate-dev.md +1 -1
- package/template/.agents/skills/data/prisma-cli/references/migrate-reset.md +2 -0
- package/template/.agents/skills/data/prisma-client-api/SKILL.md +1 -1
- package/template/.agents/skills/data/prisma-client-api/references/constructor.md +13 -0
- package/template/.agents/skills/data/prisma-client-api/references/raw-queries.md +4 -0
- package/template/.agents/skills/design/impeccable/SKILL.md +5 -5
- package/template/.agents/skills/design/impeccable/reference/android.md +6 -0
- package/template/.agents/skills/design/impeccable/reference/animate.md +3 -0
- package/template/.agents/skills/design/impeccable/reference/bolder.md +3 -1
- package/template/.agents/skills/design/impeccable/reference/craft-floor.md +2 -0
- package/template/.agents/skills/design/impeccable/reference/critique.md +23 -5
- package/template/.agents/skills/design/impeccable/reference/degraded/asset-producer.md +15 -68
- package/template/.agents/skills/design/impeccable/reference/degraded/finish-reviewer.md +13 -12
- package/template/.agents/skills/design/impeccable/reference/distill.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/doctor.md +1 -0
- package/template/.agents/skills/design/impeccable/reference/document.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/extract.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/hooks.md +17 -11
- package/template/.agents/skills/design/impeccable/reference/init.md +9 -3
- package/template/.agents/skills/design/impeccable/reference/ios.md +6 -0
- package/template/.agents/skills/design/impeccable/reference/new-work.md +69 -29
- package/template/.agents/skills/design/impeccable/reference/overdrive.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/polish.md +13 -5
- package/template/.agents/skills/design/impeccable/reference/quieter.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/routing.md +1 -1
- package/template/.agents/skills/design/impeccable/reference/visualize.md +21 -22
- package/template/.agents/skills/design/impeccable/scripts/build-phase.mjs +1022 -0
- package/template/.agents/skills/design/impeccable/scripts/comp-diff.mjs +391 -0
- package/template/.agents/skills/design/impeccable/scripts/comp-spec.mjs +513 -0
- package/template/.agents/skills/design/impeccable/scripts/concept-seed.mjs +297 -41
- package/template/.agents/skills/design/impeccable/scripts/context-signals.mjs +10 -19
- package/template/.agents/skills/design/impeccable/scripts/context.mjs +124 -9
- package/template/.agents/skills/design/impeccable/scripts/critique-storage.mjs +279 -19
- package/template/.agents/skills/design/impeccable/scripts/data/font-index-failures.json +121 -0
- package/template/.agents/skills/design/impeccable/scripts/data/font-index.json +1 -0
- package/template/.agents/skills/design/impeccable/scripts/detect.mjs +9 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/browser/injected/index.mjs +192 -11
- package/template/.agents/skills/design/impeccable/scripts/detector/cli/main.mjs +10 -16
- package/template/.agents/skills/design/impeccable/scripts/detector/design-system.mjs +339 -11
- package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns-browser.js +1482 -722
- package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns.mjs +1 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/browser/detect-url.mjs +64 -2
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/regex/detect-text.mjs +580 -29
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +62 -7
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/detect-html.mjs +31 -7
- package/template/.agents/skills/design/impeccable/scripts/detector/node/file-system.mjs +23 -22
- package/template/.agents/skills/design/impeccable/scripts/detector/registry/antipatterns.mjs +18 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/rules/checks.mjs +471 -370
- package/template/.agents/skills/design/impeccable/scripts/detector/shared/color.mjs +474 -2
- package/template/.agents/skills/design/impeccable/scripts/detector/shared/constants.mjs +17 -2
- package/template/.agents/skills/design/impeccable/scripts/doctor.mjs +14 -21
- package/template/.agents/skills/design/impeccable/scripts/embed-prompt.mjs +81 -48
- package/template/.agents/skills/design/impeccable/scripts/font-match.mjs +457 -0
- package/template/.agents/skills/design/impeccable/scripts/generate-image.mjs +219 -12
- package/template/.agents/skills/design/impeccable/scripts/hook-admin.mjs +93 -15
- package/template/.agents/skills/design/impeccable/scripts/hook-before-edit.mjs +41 -19
- package/template/.agents/skills/design/impeccable/scripts/hook-lib.mjs +507 -117
- package/template/.agents/skills/design/impeccable/scripts/hook.mjs +10 -9
- package/template/.agents/skills/design/impeccable/scripts/lib/concept-catalog.mjs +40 -1
- package/template/.agents/skills/design/impeccable/scripts/lib/design-parser.mjs +120 -82
- package/template/.agents/skills/design/impeccable/scripts/lib/font-fingerprint.mjs +564 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/font-index.mjs +130 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/hero-checks.mjs +246 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/image-metrics.mjs +306 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/impeccable-config.mjs +41 -59
- package/template/.agents/skills/design/impeccable/scripts/lib/is-generated.mjs +5 -2
- package/template/.agents/skills/design/impeccable/scripts/lib/live-path-globs.mjs +37 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/open-system-browser.mjs +26 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/png.mjs +281 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/raster.mjs +194 -0
- package/template/.agents/skills/design/impeccable/scripts/lib/roll-selection.mjs +26 -19
- package/template/.agents/skills/design/impeccable/scripts/lib/staleness-deep.mjs +31 -3
- package/template/.agents/skills/design/impeccable/scripts/lib/staleness.mjs +93 -17
- package/template/.agents/skills/design/impeccable/scripts/lib/surface-briefs.mjs +9 -11
- package/template/.agents/skills/design/impeccable/scripts/live/browser-script-parts.mjs +31 -2
- package/template/.agents/skills/design/impeccable/scripts/live/project-ignores.mjs +139 -0
- package/template/.agents/skills/design/impeccable/scripts/live/svelte-ast.mjs +10 -2
- package/template/.agents/skills/design/impeccable/scripts/live/svelte-component.mjs +26 -2
- package/template/.agents/skills/design/impeccable/scripts/live/sveltekit-adapter.mjs +15 -27
- package/template/.agents/skills/design/impeccable/scripts/live/tanstack-adapter.mjs +4 -25
- package/template/.agents/skills/design/impeccable/scripts/live/ui-surfaces.mjs +75 -0
- package/template/.agents/skills/design/impeccable/scripts/live-accept.mjs +21 -37
- package/template/.agents/skills/design/impeccable/scripts/live-browser-ignores.js +242 -0
- package/template/.agents/skills/design/impeccable/scripts/live-browser.js +62 -23
- package/template/.agents/skills/design/impeccable/scripts/live-commit-manual-edits.mjs +68 -112
- package/template/.agents/skills/design/impeccable/scripts/live-copy-edit-agent.mjs +132 -15
- package/template/.agents/skills/design/impeccable/scripts/live-inject.mjs +2 -42
- package/template/.agents/skills/design/impeccable/scripts/live-poll.mjs +5 -4
- package/template/.agents/skills/design/impeccable/scripts/live-server.mjs +56 -19
- package/template/.agents/skills/design/impeccable/scripts/live.mjs +12 -37
- package/template/.agents/skills/design/impeccable/scripts/pin.mjs +8 -5
- package/template/.agents/skills/design/impeccable/scripts/serve-question.mjs +1006 -155
- package/template/.agents/skills/design/shadcn/rules/chat.md +26 -0
- package/template/.agents/skills/hono/SKILL.md +17 -3
- package/template/.agents/skills/integrations/langfuse/SKILL.md +8 -4
- package/template/.agents/skills/integrations/langfuse/references/cli.md +1 -1
- package/template/.agents/skills/integrations/langfuse/references/create-dataset.md +35 -0
- package/template/.agents/skills/integrations/langfuse/references/error-analysis.md +9 -10
- package/template/.agents/skills/integrations/langfuse/references/judge-calibration.md +8 -6
- package/template/.agents/skills/integrations/langfuse/references/sdk-upgrade.md +34 -0
- package/template/.agents/skills/integrations/langfuse/references/setting-up-evals.md +65 -0
- package/template/.agents/skills/integrations/langfuse/references/skill-feedback.md +20 -40
- package/template/.agents/skills/integrations/langfuse/references/user-feedback.md +4 -30
- package/template/.agents/skills/integrations/langfuse/references/v4-project-migration.md +3 -1
- package/template/.agents/skills/supabase/CHANGELOG.md +7 -0
- package/template/.agents/skills/supabase/SKILL.md +5 -1
- package/template/.agents/skills/writing/humanizer/LICENSE +21 -0
- package/template/.agents/skills/writing/humanizer/README.md +209 -0
- package/template/.agents/skills/writing/humanizer/SKILL.md +173 -338
- package/template/.agents/upstreams.json +21 -22
- package/template/.agents/skills/design/impeccable/scripts/live/ui-core.mjs +0 -180
package/template/.agents/skills/design/impeccable/scripts/detector/engines/regex/detect-text.mjs
CHANGED
|
@@ -41,6 +41,369 @@ function shouldRunPageAnalyzers(content, filePath) {
|
|
|
41
41
|
return !ext || PAGE_ANALYZER_EXTS.has(ext);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
const JS_SOURCE_EXTS = new Set(['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs']);
|
|
45
|
+
const STYLESHEET_EXTS = new Set(['.css', '.scss', '.sass', '.less']);
|
|
46
|
+
const REGEX_PREFIX_KEYWORDS = new Set(['await', 'case', 'default', 'delete', 'do', 'else', 'in', 'instanceof', 'new', 'of', 'return', 'throw', 'typeof', 'void', 'yield']);
|
|
47
|
+
const BLOCK_BRACE_PREFIX_KEYWORDS = new Set(['do', 'else', 'finally', 'try']);
|
|
48
|
+
|
|
49
|
+
function isInsideOpeningJsxTag(source) {
|
|
50
|
+
const tagStart = source.lastIndexOf('<');
|
|
51
|
+
if (tagStart === -1 || !/^<[A-Za-z][\w.:-]*/.test(source.slice(tagStart))) return false;
|
|
52
|
+
|
|
53
|
+
let quote = '';
|
|
54
|
+
for (let cursor = tagStart + 1; cursor < source.length; cursor++) {
|
|
55
|
+
const char = source[cursor];
|
|
56
|
+
if (quote) {
|
|
57
|
+
if (char === '\\') cursor++;
|
|
58
|
+
else if (char === quote) quote = '';
|
|
59
|
+
} else if (char === "'" || char === '"') {
|
|
60
|
+
quote = char;
|
|
61
|
+
} else if (char === '>') {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Blank JavaScript comments without moving any following source. Regex
|
|
70
|
+
* findings keep their original line numbers, while prose examples inside
|
|
71
|
+
* comments cannot masquerade as rendered markup.
|
|
72
|
+
*/
|
|
73
|
+
function stripJsComments(content, options = {}) {
|
|
74
|
+
let state = 'code';
|
|
75
|
+
let output = '';
|
|
76
|
+
let lastSignificant = '';
|
|
77
|
+
let previousSignificant = '';
|
|
78
|
+
let antePreviousSignificant = '';
|
|
79
|
+
let currentWord = '';
|
|
80
|
+
let currentWordPrefix = '';
|
|
81
|
+
let wordSeparated = false;
|
|
82
|
+
let regexCharClass = false;
|
|
83
|
+
let jsxExpressionDepth = 0;
|
|
84
|
+
let lastClosedBraceKind = '';
|
|
85
|
+
const braceKinds = [];
|
|
86
|
+
const templateExpressionDepths = [];
|
|
87
|
+
|
|
88
|
+
const braceKind = (startsJsxExpression = false) => (
|
|
89
|
+
!startsJsxExpression && (
|
|
90
|
+
!lastSignificant ||
|
|
91
|
+
lastSignificant === ')' ||
|
|
92
|
+
lastSignificant === ';' ||
|
|
93
|
+
lastSignificant === '}' ||
|
|
94
|
+
(previousSignificant === '=' && lastSignificant === '>') ||
|
|
95
|
+
BLOCK_BRACE_PREFIX_KEYWORDS.has(currentWord)
|
|
96
|
+
) ? 'block' : 'expression'
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const recordSignificant = (char) => {
|
|
100
|
+
if (/\s/.test(char)) {
|
|
101
|
+
wordSeparated = true;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const isWordChar = /[\w$]/.test(char);
|
|
105
|
+
if (isWordChar && (wordSeparated || !currentWord)) {
|
|
106
|
+
currentWord = '';
|
|
107
|
+
currentWordPrefix = lastSignificant;
|
|
108
|
+
} else if (!isWordChar) {
|
|
109
|
+
currentWordPrefix = '';
|
|
110
|
+
}
|
|
111
|
+
wordSeparated = false;
|
|
112
|
+
antePreviousSignificant = previousSignificant;
|
|
113
|
+
previousSignificant = lastSignificant;
|
|
114
|
+
lastSignificant = char;
|
|
115
|
+
currentWord = isWordChar ? currentWord + char : '';
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
for (let i = 0; i < content.length; i++) {
|
|
119
|
+
const char = content[i];
|
|
120
|
+
const next = content[i + 1];
|
|
121
|
+
|
|
122
|
+
if (state === 'line-comment') {
|
|
123
|
+
if (char === '\n') {
|
|
124
|
+
output += char;
|
|
125
|
+
state = 'code';
|
|
126
|
+
} else {
|
|
127
|
+
output += ' ';
|
|
128
|
+
}
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (state === 'block-comment') {
|
|
133
|
+
if (char === '*' && next === '/') {
|
|
134
|
+
output += ' ';
|
|
135
|
+
i++;
|
|
136
|
+
state = 'code';
|
|
137
|
+
} else {
|
|
138
|
+
output += char === '\n' ? '\n' : ' ';
|
|
139
|
+
}
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (state === 'regex') {
|
|
144
|
+
output += char;
|
|
145
|
+
if (char === '\\' && next) {
|
|
146
|
+
output += next;
|
|
147
|
+
i++;
|
|
148
|
+
} else if (char === '[') {
|
|
149
|
+
regexCharClass = true;
|
|
150
|
+
} else if (char === ']') {
|
|
151
|
+
regexCharClass = false;
|
|
152
|
+
} else if (char === '/' && !regexCharClass) {
|
|
153
|
+
state = 'code';
|
|
154
|
+
recordSignificant('/');
|
|
155
|
+
}
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (state === 'template' && char === '$' && next === '{') {
|
|
160
|
+
output += '${';
|
|
161
|
+
i++;
|
|
162
|
+
recordSignificant('$');
|
|
163
|
+
recordSignificant('{');
|
|
164
|
+
templateExpressionDepths.push(1);
|
|
165
|
+
braceKinds.push('expression');
|
|
166
|
+
if (jsxExpressionDepth) jsxExpressionDepth++;
|
|
167
|
+
state = 'code';
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (state !== 'code') {
|
|
172
|
+
output += char;
|
|
173
|
+
if (char === '\\' && next) {
|
|
174
|
+
output += next;
|
|
175
|
+
i++;
|
|
176
|
+
} else if (
|
|
177
|
+
(state === 'single-quote' && char === "'") ||
|
|
178
|
+
(state === 'double-quote' && char === '"') ||
|
|
179
|
+
(state === 'template' && char === '`')
|
|
180
|
+
) {
|
|
181
|
+
state = 'code';
|
|
182
|
+
recordSignificant(char);
|
|
183
|
+
}
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const jsxUrlSeparator = options.jsx && char === '/' && next === '/' &&
|
|
188
|
+
jsxExpressionDepth === 0 &&
|
|
189
|
+
(output.endsWith('http:') ||
|
|
190
|
+
output.endsWith('https:') ||
|
|
191
|
+
(/<[A-Za-z](?:[^>]*[^/])?>[^<]*$/.test(output.slice(output.lastIndexOf('\n') + 1)) &&
|
|
192
|
+
/^[\w.-]+\.[A-Za-z]{2,}(?=[:/?#\s<]|$)/.test(content.slice(i + 2))));
|
|
193
|
+
const afterPostfixUpdate = (lastSignificant === '+' || lastSignificant === '-') &&
|
|
194
|
+
previousSignificant === lastSignificant &&
|
|
195
|
+
antePreviousSignificant !== lastSignificant;
|
|
196
|
+
if (char === '/' && next === '/' && jsxUrlSeparator) {
|
|
197
|
+
output += '//';
|
|
198
|
+
i++;
|
|
199
|
+
recordSignificant('/');
|
|
200
|
+
recordSignificant('/');
|
|
201
|
+
} else if (char === '/' && next === '/') {
|
|
202
|
+
output += ' ';
|
|
203
|
+
i++;
|
|
204
|
+
state = 'line-comment';
|
|
205
|
+
} else if (char === '/' && next === '*') {
|
|
206
|
+
output += ' ';
|
|
207
|
+
i++;
|
|
208
|
+
state = 'block-comment';
|
|
209
|
+
} else if (templateExpressionDepths.length && char === '{') {
|
|
210
|
+
output += char;
|
|
211
|
+
templateExpressionDepths[templateExpressionDepths.length - 1]++;
|
|
212
|
+
braceKinds.push(braceKind());
|
|
213
|
+
if (jsxExpressionDepth) jsxExpressionDepth++;
|
|
214
|
+
recordSignificant(char);
|
|
215
|
+
} else if (templateExpressionDepths.length && char === '}') {
|
|
216
|
+
output += char;
|
|
217
|
+
const depthIndex = templateExpressionDepths.length - 1;
|
|
218
|
+
templateExpressionDepths[depthIndex]--;
|
|
219
|
+
lastClosedBraceKind = braceKinds.pop() || '';
|
|
220
|
+
if (jsxExpressionDepth) jsxExpressionDepth--;
|
|
221
|
+
recordSignificant(char);
|
|
222
|
+
if (templateExpressionDepths[depthIndex] === 0) {
|
|
223
|
+
templateExpressionDepths.pop();
|
|
224
|
+
state = 'template';
|
|
225
|
+
}
|
|
226
|
+
} else if (
|
|
227
|
+
char === '/' &&
|
|
228
|
+
(!lastSignificant ||
|
|
229
|
+
(/[=([{!?:;,&|+\-*%^~<>]/.test(lastSignificant) && !afterPostfixUpdate) ||
|
|
230
|
+
(lastSignificant === '}' && lastClosedBraceKind === 'block') ||
|
|
231
|
+
(previousSignificant === '=' && lastSignificant === '>') ||
|
|
232
|
+
(currentWordPrefix !== '.' && REGEX_PREFIX_KEYWORDS.has(currentWord)))
|
|
233
|
+
) {
|
|
234
|
+
output += char;
|
|
235
|
+
state = 'regex';
|
|
236
|
+
regexCharClass = false;
|
|
237
|
+
} else {
|
|
238
|
+
output += char;
|
|
239
|
+
const startsJsxExpression = options.jsx && char === '{' && jsxExpressionDepth === 0 &&
|
|
240
|
+
(/<[A-Za-z](?:[^>]*[^/])?>[^<]*$/.test(output.slice(output.lastIndexOf('\n') + 1, -1)) ||
|
|
241
|
+
isInsideOpeningJsxTag(output.slice(0, -1)));
|
|
242
|
+
if (char === '{') braceKinds.push(braceKind(startsJsxExpression));
|
|
243
|
+
else if (char === '}') lastClosedBraceKind = braceKinds.pop() || '';
|
|
244
|
+
if (char === '{' && (jsxExpressionDepth || startsJsxExpression)) jsxExpressionDepth++;
|
|
245
|
+
else if (char === '}' && jsxExpressionDepth) jsxExpressionDepth--;
|
|
246
|
+
recordSignificant(char);
|
|
247
|
+
if (char === "'") state = 'single-quote';
|
|
248
|
+
else if (char === '"') state = 'double-quote';
|
|
249
|
+
else if (char === '`') state = 'template';
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return output;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function stripCssComments(content) {
|
|
257
|
+
return content.replace(/\/\*[\s\S]*?\*\//g, comment => comment.replace(/[^\n]/g, ' '));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function blankHtmlComments(text) {
|
|
261
|
+
return text.replace(/<!--[\s\S]*?-->/g, comment => comment.replace(/[^\n]/g, ' '));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function blankCssLineCommentsInStyleBlocks(text) {
|
|
265
|
+
const re = /<style\b[^>]*>([\s\S]*?)<\/style>/gi;
|
|
266
|
+
let output = '';
|
|
267
|
+
let lastIndex = 0;
|
|
268
|
+
let match;
|
|
269
|
+
while ((match = re.exec(text)) !== null) {
|
|
270
|
+
const inner = match[1];
|
|
271
|
+
const openLength = match[0].length - inner.length - '</style>'.length;
|
|
272
|
+
output += text.slice(lastIndex, match.index);
|
|
273
|
+
output += match[0].slice(0, openLength);
|
|
274
|
+
output += blankCssLineComments(inner);
|
|
275
|
+
output += match[0].slice(openLength + inner.length);
|
|
276
|
+
lastIndex = re.lastIndex;
|
|
277
|
+
}
|
|
278
|
+
return output + text.slice(lastIndex);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function blankHtmlAndCssCommentsOutsideScripts(text) {
|
|
282
|
+
const re = /<script\b[^>]*>[\s\S]*?<\/script>/gi;
|
|
283
|
+
let output = '';
|
|
284
|
+
let lastIndex = 0;
|
|
285
|
+
let match;
|
|
286
|
+
while ((match = re.exec(text)) !== null) {
|
|
287
|
+
output += blankCssLineCommentsInStyleBlocks(stripCssComments(blankHtmlComments(text.slice(lastIndex, match.index))));
|
|
288
|
+
output += match[0];
|
|
289
|
+
lastIndex = re.lastIndex;
|
|
290
|
+
}
|
|
291
|
+
return output + blankCssLineCommentsInStyleBlocks(stripCssComments(blankHtmlComments(text.slice(lastIndex))));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function blankCssLineComments(text) {
|
|
295
|
+
let output = '';
|
|
296
|
+
let state = 'code';
|
|
297
|
+
let urlDepth = 0;
|
|
298
|
+
for (let i = 0; i < text.length; i++) {
|
|
299
|
+
const char = text[i];
|
|
300
|
+
const next = text[i + 1];
|
|
301
|
+
if (state === 'line') {
|
|
302
|
+
if (char === '\n') {
|
|
303
|
+
output += '\n';
|
|
304
|
+
state = 'code';
|
|
305
|
+
} else {
|
|
306
|
+
output += ' ';
|
|
307
|
+
}
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
if (state === 'single' || state === 'double') {
|
|
311
|
+
output += char;
|
|
312
|
+
if (char === '\\' && next) {
|
|
313
|
+
output += next;
|
|
314
|
+
i++;
|
|
315
|
+
} else if ((state === 'single' && char === "'") || (state === 'double' && char === '"')) {
|
|
316
|
+
state = 'code';
|
|
317
|
+
}
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
const prev = output.length ? output[output.length - 1] : '';
|
|
321
|
+
if (char === '/' && next === '/' && urlDepth === 0 && prev !== ':' && prev !== '(' && prev !== '\\') {
|
|
322
|
+
output += ' ';
|
|
323
|
+
i++;
|
|
324
|
+
state = 'line';
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
if (char === "'") state = 'single';
|
|
328
|
+
else if (char === '"') state = 'double';
|
|
329
|
+
if (char === '(') {
|
|
330
|
+
const behind = output.replace(/\s+$/, '');
|
|
331
|
+
if (urlDepth > 0 || /url$/i.test(behind)) urlDepth++;
|
|
332
|
+
} else if (char === ')' && urlDepth) {
|
|
333
|
+
urlDepth--;
|
|
334
|
+
}
|
|
335
|
+
output += char;
|
|
336
|
+
}
|
|
337
|
+
return output;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function findAstroFrontmatterClose(text) {
|
|
341
|
+
if (!text.startsWith('---')) return -1;
|
|
342
|
+
let cursor = text.indexOf('\n');
|
|
343
|
+
if (cursor === -1) return -1;
|
|
344
|
+
cursor += 1;
|
|
345
|
+
while (cursor < text.length) {
|
|
346
|
+
if (text[cursor - 1] === '\n' && text.startsWith('---', cursor)) {
|
|
347
|
+
let end = cursor + 3;
|
|
348
|
+
while (text[end] === ' ' || text[end] === '\t') end++;
|
|
349
|
+
if (end >= text.length || text[end] === '\n' || text[end] === '\r') return cursor - 1;
|
|
350
|
+
}
|
|
351
|
+
const char = text[cursor];
|
|
352
|
+
const next = text[cursor + 1];
|
|
353
|
+
if (char === "'" || char === '"') {
|
|
354
|
+
const close = findQuotedStringEnd(text, cursor, char);
|
|
355
|
+
if (close === -1) return -1;
|
|
356
|
+
cursor = close + 1;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (char === '`') {
|
|
360
|
+
const close = findTemplateLiteralEnd(text, cursor);
|
|
361
|
+
if (close === -1) return -1;
|
|
362
|
+
cursor = close + 1;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (char === '/' && next === '/') {
|
|
366
|
+
const lineEnd = text.indexOf('\n', cursor);
|
|
367
|
+
if (lineEnd === -1) return -1;
|
|
368
|
+
cursor = lineEnd;
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
if (char === '/' && next === '*') {
|
|
372
|
+
const commentEnd = text.indexOf('*/', cursor + 2);
|
|
373
|
+
if (commentEnd === -1) return -1;
|
|
374
|
+
cursor = commentEnd + 2;
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (char === '/' && next !== '/' && next !== '*') {
|
|
378
|
+
const close = findRegexLiteralEnd(text, cursor);
|
|
379
|
+
if (close !== -1) {
|
|
380
|
+
cursor = close + 1;
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
cursor++;
|
|
385
|
+
}
|
|
386
|
+
return -1;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function blankAstroFrontmatterComments(text) {
|
|
390
|
+
const close = findAstroFrontmatterClose(text);
|
|
391
|
+
if (close === -1) return text;
|
|
392
|
+
return stripJsComments(text.slice(0, close)) + text.slice(close);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function blankCommentsForMatchers(text, ext) {
|
|
396
|
+
if (PAGE_ANALYZER_EXTS.has(ext)) {
|
|
397
|
+
const withFrontmatter = ext === '.astro' ? blankAstroFrontmatterComments(text) : text;
|
|
398
|
+
return blankHtmlAndCssCommentsOutsideScripts(withFrontmatter);
|
|
399
|
+
}
|
|
400
|
+
if (STYLESHEET_EXTS.has(ext)) {
|
|
401
|
+
const withoutBlocks = stripCssComments(text);
|
|
402
|
+
return ext === '.css' ? withoutBlocks : blankCssLineComments(withoutBlocks);
|
|
403
|
+
}
|
|
404
|
+
return text;
|
|
405
|
+
}
|
|
406
|
+
|
|
44
407
|
function firstOverusedGoogleFont(text) {
|
|
45
408
|
return extractGoogleFontFamilies(text).find(f => OVERUSED_FONTS.has(f)) || '';
|
|
46
409
|
}
|
|
@@ -210,25 +573,28 @@ const REGEX_MATCHERS = [
|
|
|
210
573
|
},
|
|
211
574
|
fmt: (m) => `cubic-bezier(${m[1]}, ${m[2]}, ${m[3]}, ${m[4]})` },
|
|
212
575
|
// --- Layout property transition ---
|
|
213
|
-
|
|
576
|
+
// JSX inline style objects use comma-delimited quoted values, not semicolons (issue #548).
|
|
577
|
+
{ id: 'layout-transition', regex: /transition\s*:\s*(?:(['"])((?:(?!\1)[^\\]|\\.)*)\1|([^;{}]+))/gi,
|
|
214
578
|
test: (m) => {
|
|
215
|
-
const val = m[
|
|
579
|
+
const val = (m[2] ?? m[3] ?? '').toLowerCase();
|
|
216
580
|
if (/\ball\b/.test(val)) return false;
|
|
217
581
|
return /\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding\b|\bmargin\b/.test(val);
|
|
218
582
|
},
|
|
219
583
|
fmt: (m) => {
|
|
220
|
-
const
|
|
221
|
-
|
|
584
|
+
const raw = m[2] ?? m[3] ?? '';
|
|
585
|
+
const found = raw.match(/\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding(?:-(?:top|right|bottom|left))?\b|\bmargin(?:-(?:top|right|bottom|left))?\b/gi);
|
|
586
|
+
return `transition: ${found ? found.join(', ') : raw.trim()}`;
|
|
222
587
|
} },
|
|
223
|
-
{ id: 'layout-transition', regex: /transition-property\s*:\s*([^;{}]+)/gi,
|
|
588
|
+
{ id: 'layout-transition', regex: /transition-property\s*:\s*(?:(['"])((?:(?!\1)[^\\]|\\.)*)\1|([^;{}]+))/gi,
|
|
224
589
|
test: (m) => {
|
|
225
|
-
const val = m[
|
|
590
|
+
const val = (m[2] ?? m[3] ?? '').toLowerCase();
|
|
226
591
|
if (/\ball\b/.test(val)) return false;
|
|
227
592
|
return /\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding\b|\bmargin\b/.test(val);
|
|
228
593
|
},
|
|
229
594
|
fmt: (m) => {
|
|
230
|
-
const
|
|
231
|
-
|
|
595
|
+
const raw = m[2] ?? m[3] ?? '';
|
|
596
|
+
const found = raw.match(/\b(?:(?:max|min)-)?(?:width|height)\b|\bpadding(?:-(?:top|right|bottom|left))?\b|\bmargin(?:-(?:top|right|bottom|left))?\b/gi);
|
|
597
|
+
return `transition-property: ${found ? found.join(', ') : raw.trim()}`;
|
|
232
598
|
} },
|
|
233
599
|
// --- Broken image: src="" or src="#" or src=" " ---
|
|
234
600
|
{ id: 'broken-image', regex: /<img\b[^>]*?\bsrc\s*=\s*(?:""|''|"\s+"|'\s+'|"#"|'#')/gi,
|
|
@@ -528,18 +894,198 @@ function extractStyleBlocks(content, ext) {
|
|
|
528
894
|
|
|
529
895
|
const CSS_IN_JS_EXTENSIONS = new Set(['.js', '.ts', '.jsx', '.tsx']);
|
|
530
896
|
|
|
897
|
+
function findQuotedStringEnd(content, start, quote) {
|
|
898
|
+
for (let cursor = start + 1; cursor < content.length; cursor++) {
|
|
899
|
+
if (content[cursor] === '\\') cursor++;
|
|
900
|
+
else if (content[cursor] === quote) return cursor;
|
|
901
|
+
}
|
|
902
|
+
return -1;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function findRegexLiteralEnd(content, start) {
|
|
906
|
+
let inCharacterClass = false;
|
|
907
|
+
for (let cursor = start + 1; cursor < content.length; cursor++) {
|
|
908
|
+
const char = content[cursor];
|
|
909
|
+
if (char === '\\') {
|
|
910
|
+
cursor++;
|
|
911
|
+
} else if (char === '[') {
|
|
912
|
+
inCharacterClass = true;
|
|
913
|
+
} else if (char === ']') {
|
|
914
|
+
inCharacterClass = false;
|
|
915
|
+
} else if (char === '/' && !inCharacterClass) {
|
|
916
|
+
while (/[A-Za-z]/.test(content[cursor + 1] || '')) cursor++;
|
|
917
|
+
return cursor;
|
|
918
|
+
} else if (char === '\n' || char === '\r') {
|
|
919
|
+
return -1;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return -1;
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
function findTemplateExpressionEnd(content, start) {
|
|
926
|
+
let depth = 1;
|
|
927
|
+
let lastSignificant = '';
|
|
928
|
+
let previousSignificant = '';
|
|
929
|
+
let antePreviousSignificant = '';
|
|
930
|
+
let currentWord = '';
|
|
931
|
+
let currentWordPrefix = '';
|
|
932
|
+
let wordSeparated = false;
|
|
933
|
+
let lastClosedBraceKind = '';
|
|
934
|
+
const braceKinds = [];
|
|
935
|
+
|
|
936
|
+
const braceKind = () => (
|
|
937
|
+
lastSignificant === ')' ||
|
|
938
|
+
lastSignificant === ';' ||
|
|
939
|
+
lastSignificant === '}' ||
|
|
940
|
+
(previousSignificant === '=' && lastSignificant === '>') ||
|
|
941
|
+
BLOCK_BRACE_PREFIX_KEYWORDS.has(currentWord)
|
|
942
|
+
? 'block'
|
|
943
|
+
: 'expression'
|
|
944
|
+
);
|
|
945
|
+
|
|
946
|
+
const recordSignificant = (char) => {
|
|
947
|
+
if (/\s/.test(char)) {
|
|
948
|
+
wordSeparated = true;
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
const isWordChar = /[\w$]/.test(char);
|
|
952
|
+
if (isWordChar && (wordSeparated || !currentWord)) {
|
|
953
|
+
currentWord = '';
|
|
954
|
+
currentWordPrefix = lastSignificant;
|
|
955
|
+
} else if (!isWordChar) {
|
|
956
|
+
currentWordPrefix = '';
|
|
957
|
+
}
|
|
958
|
+
wordSeparated = false;
|
|
959
|
+
antePreviousSignificant = previousSignificant;
|
|
960
|
+
previousSignificant = lastSignificant;
|
|
961
|
+
lastSignificant = char;
|
|
962
|
+
currentWord = isWordChar ? currentWord + char : '';
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
for (let cursor = start; cursor < content.length; cursor++) {
|
|
966
|
+
const char = content[cursor];
|
|
967
|
+
const next = content[cursor + 1];
|
|
968
|
+
const afterPostfixUpdate = (lastSignificant === '+' || lastSignificant === '-') &&
|
|
969
|
+
previousSignificant === lastSignificant &&
|
|
970
|
+
antePreviousSignificant !== lastSignificant;
|
|
971
|
+
if (char === "'" || char === '"') {
|
|
972
|
+
cursor = findQuotedStringEnd(content, cursor, char);
|
|
973
|
+
if (cursor === -1) return -1;
|
|
974
|
+
recordSignificant(')');
|
|
975
|
+
} else if (char === '/' && next === '/') {
|
|
976
|
+
const lineEnd = content.indexOf('\n', cursor + 2);
|
|
977
|
+
if (lineEnd === -1) return -1;
|
|
978
|
+
cursor = lineEnd;
|
|
979
|
+
} else if (char === '/' && next === '*') {
|
|
980
|
+
const commentEnd = content.indexOf('*/', cursor + 2);
|
|
981
|
+
if (commentEnd === -1) return -1;
|
|
982
|
+
cursor = commentEnd + 1;
|
|
983
|
+
} else if (
|
|
984
|
+
char === '/' &&
|
|
985
|
+
(!lastSignificant ||
|
|
986
|
+
(/[=([{!?:;,&|+\-*%^~<>]/.test(lastSignificant) && !afterPostfixUpdate) ||
|
|
987
|
+
(lastSignificant === '}' && lastClosedBraceKind === 'block') ||
|
|
988
|
+
(previousSignificant === '=' && lastSignificant === '>') ||
|
|
989
|
+
(currentWordPrefix !== '.' && REGEX_PREFIX_KEYWORDS.has(currentWord)))
|
|
990
|
+
) {
|
|
991
|
+
cursor = findRegexLiteralEnd(content, cursor);
|
|
992
|
+
if (cursor === -1) return -1;
|
|
993
|
+
recordSignificant(')');
|
|
994
|
+
} else if (char === '`') {
|
|
995
|
+
cursor = findTemplateLiteralEnd(content, cursor);
|
|
996
|
+
if (cursor === -1) return -1;
|
|
997
|
+
recordSignificant(')');
|
|
998
|
+
} else if (char === '{') {
|
|
999
|
+
depth++;
|
|
1000
|
+
braceKinds.push(braceKind());
|
|
1001
|
+
recordSignificant(char);
|
|
1002
|
+
} else if (char === '}') {
|
|
1003
|
+
depth--;
|
|
1004
|
+
if (depth === 0) return cursor;
|
|
1005
|
+
lastClosedBraceKind = braceKinds.pop() || '';
|
|
1006
|
+
recordSignificant(char);
|
|
1007
|
+
} else {
|
|
1008
|
+
recordSignificant(char);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
return -1;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
function findTemplateLiteralEnd(content, start) {
|
|
1015
|
+
for (let cursor = start + 1; cursor < content.length; cursor++) {
|
|
1016
|
+
const char = content[cursor];
|
|
1017
|
+
if (char === '\\') {
|
|
1018
|
+
cursor++;
|
|
1019
|
+
} else if (char === '`') {
|
|
1020
|
+
return cursor;
|
|
1021
|
+
} else if (char === '$' && content[cursor + 1] === '{') {
|
|
1022
|
+
cursor = findTemplateExpressionEnd(content, cursor + 2);
|
|
1023
|
+
if (cursor === -1) return -1;
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
return -1;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
function findCSSinJSTemplates(content) {
|
|
1030
|
+
const templates = [];
|
|
1031
|
+
const tagRe = /\b(?:styled(?:\.\w+|\([^)]+\))|css)/g;
|
|
1032
|
+
let match;
|
|
1033
|
+
while ((match = tagRe.exec(content)) !== null) {
|
|
1034
|
+
let cursor = match.index + match[0].length;
|
|
1035
|
+
while (/\s/.test(content[cursor] || '')) cursor++;
|
|
1036
|
+
|
|
1037
|
+
if (content[cursor] === '<') {
|
|
1038
|
+
let depth = 0;
|
|
1039
|
+
while (cursor < content.length) {
|
|
1040
|
+
const char = content[cursor];
|
|
1041
|
+
if (char === '<') depth++;
|
|
1042
|
+
else if (char === '>' && content[cursor - 1] !== '=') depth--;
|
|
1043
|
+
cursor++;
|
|
1044
|
+
if (depth === 0) break;
|
|
1045
|
+
}
|
|
1046
|
+
if (depth !== 0) continue;
|
|
1047
|
+
while (/\s/.test(content[cursor] || '')) cursor++;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
if (content[cursor] !== '`') continue;
|
|
1051
|
+
const contentStart = cursor + 1;
|
|
1052
|
+
cursor = findTemplateLiteralEnd(content, cursor);
|
|
1053
|
+
if (cursor === -1) continue;
|
|
1054
|
+
|
|
1055
|
+
templates.push({
|
|
1056
|
+
tagStart: match.index,
|
|
1057
|
+
contentStart,
|
|
1058
|
+
contentEnd: cursor,
|
|
1059
|
+
});
|
|
1060
|
+
tagRe.lastIndex = cursor + 1;
|
|
1061
|
+
}
|
|
1062
|
+
return templates;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
531
1065
|
function extractCSSinJS(content, ext) {
|
|
532
1066
|
ext = ext.toLowerCase();
|
|
533
1067
|
if (!CSS_IN_JS_EXTENSIONS.has(ext)) return [];
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
let m;
|
|
537
|
-
while ((m = re.exec(content)) !== null) {
|
|
538
|
-
const before = content.substring(0, m.index);
|
|
1068
|
+
return findCSSinJSTemplates(content).map((template) => {
|
|
1069
|
+
const before = content.substring(0, template.tagStart);
|
|
539
1070
|
const startLine = before.split('\n').length;
|
|
540
|
-
|
|
1071
|
+
return {
|
|
1072
|
+
content: content.slice(template.contentStart, template.contentEnd),
|
|
1073
|
+
startLine,
|
|
1074
|
+
};
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
function stripCssInJsComments(content, ext) {
|
|
1079
|
+
if (!CSS_IN_JS_EXTENSIONS.has(ext.toLowerCase())) return content;
|
|
1080
|
+
const templates = findCSSinJSTemplates(content);
|
|
1081
|
+
let output = '';
|
|
1082
|
+
let cursor = 0;
|
|
1083
|
+
for (const template of templates) {
|
|
1084
|
+
output += content.slice(cursor, template.contentStart);
|
|
1085
|
+
output += stripCssComments(content.slice(template.contentStart, template.contentEnd));
|
|
1086
|
+
cursor = template.contentEnd;
|
|
541
1087
|
}
|
|
542
|
-
return
|
|
1088
|
+
return output + content.slice(cursor);
|
|
543
1089
|
}
|
|
544
1090
|
|
|
545
1091
|
function runRegexMatchers(lines, filePath, lineOffset = 0, blockContext = null, options = {}) {
|
|
@@ -627,13 +1173,16 @@ function runTextContentAnalyzers(content, filePath, options = {}) {
|
|
|
627
1173
|
function detectText(content, filePath, options = {}) {
|
|
628
1174
|
const profile = options?.profile;
|
|
629
1175
|
const findings = [];
|
|
630
|
-
const lines = content.split('\n');
|
|
631
1176
|
const ext = extFromFilePath(filePath);
|
|
1177
|
+
const commentStrippedSource = JS_SOURCE_EXTS.has(ext) ? stripJsComments(content, {
|
|
1178
|
+
jsx: ext === '.js' || ext === '.jsx' || ext === '.tsx',
|
|
1179
|
+
}) : blankCommentsForMatchers(content, ext);
|
|
1180
|
+
const source = stripCssInJsComments(commentStrippedSource, ext);
|
|
1181
|
+
const lines = source.split('\n');
|
|
632
1182
|
|
|
633
1183
|
// Run regex matchers on the full file content (catches Tailwind classes, inline styles)
|
|
634
1184
|
// Enable block context for CSS files where related properties span multiple lines
|
|
635
|
-
|
|
636
|
-
findings.push(...runRegexMatchers(lines, filePath, 0, cssLike.has(ext) || null, {
|
|
1185
|
+
findings.push(...runRegexMatchers(lines, filePath, 0, STYLESHEET_EXTS.has(ext) || null, {
|
|
637
1186
|
profile,
|
|
638
1187
|
phase: 'source',
|
|
639
1188
|
}));
|
|
@@ -648,7 +1197,7 @@ function detectText(content, filePath, options = {}) {
|
|
|
648
1197
|
scanCssTextForPseudoStripe(text).map(hit =>
|
|
649
1198
|
finding(hit.id, filePath, hit.snippet, lineOffset + text.slice(0, hit.index).split('\n').length));
|
|
650
1199
|
|
|
651
|
-
if (
|
|
1200
|
+
if (STYLESHEET_EXTS.has(ext)) {
|
|
652
1201
|
findings.push(...scanInsetStripeCss(content, filePath));
|
|
653
1202
|
findings.push(...pseudoStripeFindings(content, 0));
|
|
654
1203
|
}
|
|
@@ -661,8 +1210,8 @@ function detectText(content, filePath, options = {}) {
|
|
|
661
1210
|
phase: 'source',
|
|
662
1211
|
ruleId: 'codex-grid-background',
|
|
663
1212
|
target: filePath,
|
|
664
|
-
}, () => scanCssTextForGridBackground(
|
|
665
|
-
const line =
|
|
1213
|
+
}, () => scanCssTextForGridBackground(source).map(hit => {
|
|
1214
|
+
const line = source.substring(0, hit.index).split('\n').length;
|
|
666
1215
|
return finding('codex-grid-background', filePath, hit.snippet, line);
|
|
667
1216
|
})));
|
|
668
1217
|
|
|
@@ -676,7 +1225,8 @@ function detectText(content, filePath, options = {}) {
|
|
|
676
1225
|
}, () => extractStyleBlocks(content, ext))
|
|
677
1226
|
: extractStyleBlocks(content, ext);
|
|
678
1227
|
for (const block of styleBlocks) {
|
|
679
|
-
const
|
|
1228
|
+
const blockContent = blankCssLineComments(stripCssComments(block.content));
|
|
1229
|
+
const blockLines = blockContent.split('\n');
|
|
680
1230
|
findings.push(...runRegexMatchers(blockLines, filePath, block.startLine - 1, true, {
|
|
681
1231
|
profile,
|
|
682
1232
|
phase: 'style-block',
|
|
@@ -687,8 +1237,8 @@ function detectText(content, filePath, options = {}) {
|
|
|
687
1237
|
// 1-based, so the offset is startLine - 2; startLine - 1 double-counted and
|
|
688
1238
|
// reported every selector one line low. runRegexMatchers keeps startLine - 1
|
|
689
1239
|
// because it indexes its split lines from zero.
|
|
690
|
-
findings.push(...scanInsetStripeCss(
|
|
691
|
-
findings.push(...pseudoStripeFindings(
|
|
1240
|
+
findings.push(...scanInsetStripeCss(blockContent, filePath, block.startLine - 2));
|
|
1241
|
+
findings.push(...pseudoStripeFindings(blockContent, block.startLine - 2));
|
|
692
1242
|
}
|
|
693
1243
|
|
|
694
1244
|
// Extract and scan CSS-in-JS template literals
|
|
@@ -698,16 +1248,17 @@ function detectText(content, filePath, options = {}) {
|
|
|
698
1248
|
phase: 'extract',
|
|
699
1249
|
ruleId: 'css-in-js',
|
|
700
1250
|
target: filePath,
|
|
701
|
-
}, () => extractCSSinJS(
|
|
702
|
-
: extractCSSinJS(
|
|
1251
|
+
}, () => extractCSSinJS(source, ext))
|
|
1252
|
+
: extractCSSinJS(source, ext);
|
|
703
1253
|
for (const block of cssJsBlocks) {
|
|
704
|
-
const
|
|
1254
|
+
const blockContent = stripCssComments(block.content);
|
|
1255
|
+
const blockLines = blockContent.split('\n');
|
|
705
1256
|
findings.push(...runRegexMatchers(blockLines, filePath, block.startLine - 1, true, {
|
|
706
1257
|
profile,
|
|
707
1258
|
phase: 'css-in-js',
|
|
708
1259
|
}));
|
|
709
|
-
findings.push(...scanInsetStripeCss(
|
|
710
|
-
findings.push(...pseudoStripeFindings(
|
|
1260
|
+
findings.push(...scanInsetStripeCss(blockContent, filePath, block.startLine - 1));
|
|
1261
|
+
findings.push(...pseudoStripeFindings(blockContent, block.startLine - 1));
|
|
711
1262
|
}
|
|
712
1263
|
|
|
713
1264
|
if (options?.designSystem) {
|