@brighterly/lib-fe-codestyle 0.5.1 → 0.5.2
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.
|
@@ -80,6 +80,26 @@ const uiKitReuseFirst = {
|
|
|
80
80
|
return out;
|
|
81
81
|
},
|
|
82
82
|
};
|
|
83
|
+
/**
|
|
84
|
+
* Blank everything outside `<style>` bodies, line count preserved. Colour tokens are a
|
|
85
|
+
* stylesheet concern: an SVG `fill="#E2E8F0"` in an inline-icon template has no place to
|
|
86
|
+
* put a `$token`, so flagging it prescribes an impossible fix (74 of 81 .vue findings
|
|
87
|
+
* fleet-wide were exactly that, 2026-10-02).
|
|
88
|
+
*/
|
|
89
|
+
const onlyStyleBlocks = (source) => {
|
|
90
|
+
let out = '';
|
|
91
|
+
let last = 0;
|
|
92
|
+
const re = /<style\b[^>]*>([\s\S]*?)<\/style>/g;
|
|
93
|
+
let m;
|
|
94
|
+
while ((m = re.exec(source)) !== null) {
|
|
95
|
+
const body = m[1] ?? '';
|
|
96
|
+
const bodyStart = m.index + m[0].length - body.length - '</style>'.length;
|
|
97
|
+
out += source.slice(last, bodyStart).replace(/[^\n]/g, ' ');
|
|
98
|
+
out += body;
|
|
99
|
+
last = bodyStart + body.length;
|
|
100
|
+
}
|
|
101
|
+
return out + source.slice(last).replace(/[^\n]/g, ' ');
|
|
102
|
+
};
|
|
83
103
|
const scssColourTokens = {
|
|
84
104
|
id: 'core/C3',
|
|
85
105
|
title: 'literal colour in SCSS instead of a design token',
|
|
@@ -96,7 +116,8 @@ const scssColourTokens = {
|
|
|
96
116
|
const palette = setting({ settings }, 'scssTokensFile', 'app/assets/scss/_colors.scss');
|
|
97
117
|
if (file === palette)
|
|
98
118
|
return [];
|
|
99
|
-
|
|
119
|
+
const scanned = file.endsWith('.vue') ? onlyStyleBlocks(code) : code;
|
|
120
|
+
return eachLine(scanned, (line, n) => {
|
|
100
121
|
// `rgba($token, .5)` is correct usage — only a NUMERIC first argument is a literal.
|
|
101
122
|
const hex = /#[0-9a-fA-F]{3,8}\b/.exec(line);
|
|
102
123
|
const numericRgb = /\brgba?\(\s*\d[^)]*\)/.exec(line);
|
|
@@ -122,12 +143,19 @@ const dateFormatEnum = {
|
|
|
122
143
|
return [];
|
|
123
144
|
// A date pattern: only Y/M/D/H/m/s and separators, and carrying a real token.
|
|
124
145
|
const FORMAT = /(['"`])((?=[^'"`]*(?:YYYY|DD|MM|HH|mm|ss))[YMDHhmsAaZ\s:/.,-]{5,})\1/;
|
|
146
|
+
// Every letter RUN must be a real dayjs token: 'ADHD / ADD' passes the char class
|
|
147
|
+
// (A, D and H are all tokens) and the DD lookahead, but neither 'ADHD' nor 'ADD'
|
|
148
|
+
// is a token — it is an answer label, not a format (found live in cs-app, 2026-10-02).
|
|
149
|
+
const TOKENS = new Set(['YYYY', 'YY', 'Y', 'MMMM', 'MMM', 'MM', 'M', 'DD', 'D', 'Do', 'dddd', 'ddd', 'dd', 'd', 'HH', 'H', 'hh', 'h', 'mm', 'm', 'ss', 's', 'SSS', 'A', 'a', 'Z', 'ZZ']);
|
|
150
|
+
const isFormat = (s) => (s.match(/[A-Za-z]+/g) ?? []).every(run => TOKENS.has(run));
|
|
125
151
|
return eachLine(code, (line, n) => {
|
|
126
|
-
|
|
127
|
-
|
|
152
|
+
// Only the day-extraction misuse shifts the calendar day. A bare toISOString()
|
|
153
|
+
// (an API timestamp) and the epoch duration trick (.slice(14, 19)) are correct uses.
|
|
154
|
+
if (/\.toISOString\s*\(\s*\)\s*(?:\.split\s*\(\s*['"`]T['"`]\s*\)|\.(?:slice|substring)\s*\(\s*0\s*,\s*10\s*\))/.test(line)) {
|
|
155
|
+
return { file, line: n, message: `toISOString() is always UTC — extracting its date part shifts the calendar day for any visitor west of Greenwich; format through ${dateModule}` };
|
|
128
156
|
}
|
|
129
157
|
const m = FORMAT.exec(line);
|
|
130
|
-
if (!m)
|
|
158
|
+
if (!m || !isFormat(m[2] ?? ''))
|
|
131
159
|
return null;
|
|
132
160
|
return { file, line: n, message: `hand-rolled date format ${m[1]}${m[2]}${m[1]} — use a member of ${dateModule}` };
|
|
133
161
|
});
|
|
@@ -33,16 +33,40 @@ export const commonFixtures = {
|
|
|
33
33
|
bad: 'window.addEventListener("resize", onResize)',
|
|
34
34
|
good: 'useEventListener(window, "resize", onResize)',
|
|
35
35
|
},
|
|
36
|
-
'core/C3':
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
file:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
36
|
+
'core/C3': [
|
|
37
|
+
{
|
|
38
|
+
file: 'app/assets/scss/components/Card.scss',
|
|
39
|
+
bad: '.card { color: #1f2937; }',
|
|
40
|
+
good: '.card { color: $slate-800; }',
|
|
41
|
+
},
|
|
42
|
+
// In a .vue file only the <style> block is a stylesheet: an SVG fill in the
|
|
43
|
+
// template has no place to put a $token, so it must stay quiet.
|
|
44
|
+
{
|
|
45
|
+
file: 'app/components/UI/Icons/IconLeaf.vue',
|
|
46
|
+
bad: '<template><svg><path fill="currentColor" /></svg></template>\n<style scoped lang="scss">\n.icon { color: #e2e8f0; }\n</style>',
|
|
47
|
+
good: '<template><svg><path fill="#E2E8F0" /></svg></template>\n<style scoped lang="scss">\n.icon { color: $slate-200; }\n</style>',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
'core/C4': [
|
|
51
|
+
{
|
|
52
|
+
file: 'app/a.ts',
|
|
53
|
+
bad: "const d = dayjs().format('YYYY-MM-DD')",
|
|
54
|
+
good: 'const d = dayjs().format(DateFormat.DefaultDate)',
|
|
55
|
+
},
|
|
56
|
+
// The day-extraction misuse fires; a bare toISOString() (an API timestamp) and the
|
|
57
|
+
// epoch duration trick do not.
|
|
58
|
+
{
|
|
59
|
+
file: 'app/b.ts',
|
|
60
|
+
bad: "const day = new Date().toISOString().split('T')[0]",
|
|
61
|
+
good: 'const generatedAt = dayjs().toISOString()\nconst duration = new Date(s * 1000).toISOString().slice(14, 19)',
|
|
62
|
+
},
|
|
63
|
+
// Every letter run must be a real dayjs token — an answer label that happens to fit
|
|
64
|
+
// the char class ('ADHD / ADD' carries a DD) is not a format string.
|
|
65
|
+
{
|
|
66
|
+
file: 'app/c.ts',
|
|
67
|
+
good: "const option = { code: 'ADHD_ADD', label: 'ADHD / ADD' }",
|
|
68
|
+
},
|
|
69
|
+
],
|
|
46
70
|
'core/C6': {
|
|
47
71
|
file: 'app/assets/scss/components/Card.scss',
|
|
48
72
|
bad: '.card { font-size: 14px; }',
|
|
@@ -42,7 +42,7 @@ export declare const sharedRuleIds: {
|
|
|
42
42
|
readonly 'core/C2': "core/C2";
|
|
43
43
|
/** `warn` — a literal colour (`#hex`, numeric `rgba()`) in SCSS/Vue instead of a palette token. Hardcoded colours survive theme and palette changes silently. Fix: a token from `settings.scssTokensFile`. */
|
|
44
44
|
readonly 'core/C3': "core/C3";
|
|
45
|
-
/** `warn` — a hand-rolled date format string (`'YYYY-MM-DD'`) or `toISOString()`. The date's shape becomes an accident instead of a contract. Fix: the shared enum (`settings.dateFormatModule`). */
|
|
45
|
+
/** `warn` — a hand-rolled date format string (`'YYYY-MM-DD'`) or a `toISOString()` whose date part is extracted (`.split('T')[0]`, `.slice(0, 10)`). The date's shape becomes an accident instead of a contract. Fix: the shared enum (`settings.dateFormatModule`). */
|
|
46
46
|
readonly 'core/C4': "core/C4";
|
|
47
47
|
/** `warn` — a type file outside `types/<entity>/{enums,interfaces,types}.ts` (root under `settings.typesRoot`). Root-level or mixed-kind type files accrete unrelated entities. `.d.ts` ambient files are exempt. */
|
|
48
48
|
readonly 'core/C5': "core/C5";
|
|
@@ -42,7 +42,7 @@ export const sharedRuleIds = {
|
|
|
42
42
|
'core/C2': 'core/C2',
|
|
43
43
|
/** `warn` — a literal colour (`#hex`, numeric `rgba()`) in SCSS/Vue instead of a palette token. Hardcoded colours survive theme and palette changes silently. Fix: a token from `settings.scssTokensFile`. */
|
|
44
44
|
'core/C3': 'core/C3',
|
|
45
|
-
/** `warn` — a hand-rolled date format string (`'YYYY-MM-DD'`) or `toISOString()`. The date's shape becomes an accident instead of a contract. Fix: the shared enum (`settings.dateFormatModule`). */
|
|
45
|
+
/** `warn` — a hand-rolled date format string (`'YYYY-MM-DD'`) or a `toISOString()` whose date part is extracted (`.split('T')[0]`, `.slice(0, 10)`). The date's shape becomes an accident instead of a contract. Fix: the shared enum (`settings.dateFormatModule`). */
|
|
46
46
|
'core/C4': 'core/C4',
|
|
47
47
|
/** `warn` — a type file outside `types/<entity>/{enums,interfaces,types}.ts` (root under `settings.typesRoot`). Root-level or mixed-kind type files accrete unrelated entities. `.d.ts` ambient files are exempt. */
|
|
48
48
|
'core/C5': 'core/C5',
|
package/package.json
CHANGED