@klodd/ds 5.7.0 → 5.7.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/bin/verify.js CHANGED
@@ -1,9 +1,14 @@
1
1
  #!/usr/bin/env node
2
- /* @klodd/ds verify - diff:ar committade DS-filer mot paket-källan.
3
- * Kör i CI: npx @klodd/ds verify
4
- * Fail:ar med exit code 1 om drift detekteras.
2
+ /* @klodd/ds verify - validerar att en app stammer mot paketet.
3
+ * Kör i CI: npx @klodd/ds verify. Exit 1 vid problem.
5
4
  *
6
- * exclude-fältet (v3.5.0+) i klodd-ds.json hoppas över vid diff. */
5
+ * Check 1 - fil-drift: diffar committade DS-filer mot paket-källan.
6
+ * exclude-fältet (v3.5.0+) i klodd-ds.json hoppas över.
7
+ * Check 2 - deprecade selektorer: letar i app-ägd kod (app/templates/
8
+ * + app/static/, exkl. synkade paket-filer i ds/) efter klass-
9
+ * ANVANDNING av namn listade i paketets deprecated-classes.txt.
10
+ * Kommentarer ignoreras - bara riktiga class=""/selektor/JS-strang-
11
+ * referenser flaggas. */
7
12
  'use strict';
8
13
 
9
14
  const fs = require('fs');
@@ -26,6 +31,7 @@ function hashFile(filePath) {
26
31
 
27
32
  function listFilesRec(dir) {
28
33
  const out = [];
34
+ if (!fs.existsSync(dir)) return out;
29
35
  const stack = [{ abs: dir, rel: '' }];
30
36
  while (stack.length) {
31
37
  const { abs, rel } = stack.pop();
@@ -57,17 +63,151 @@ function diffDirs(sourceDir, targetRel, kind) {
57
63
  return drifted;
58
64
  }
59
65
 
66
+ // --- Deprecated-class-check ---
67
+
68
+ function escapeRe(s) {
69
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
70
+ }
71
+
72
+ // deprecated-classes.txt: en CSS-klass per rad (utan punkt). Allt efter
73
+ // klassnamnet pa raden ar fri kommentar. Tomma rader + #-rader hoppas.
74
+ function loadDeprecatedClasses() {
75
+ const file = path.join(pkgDir, 'deprecated-classes.txt');
76
+ if (!fs.existsSync(file)) return [];
77
+ return fs.readFileSync(file, 'utf8')
78
+ .split('\n')
79
+ .map((line) => line.trim())
80
+ .filter((line) => line && !line.startsWith('#'))
81
+ .map((line) => line.split(/\s+/)[0]);
82
+ }
83
+
84
+ // Returnerar varje rads kod med kommentarer borttagna. Flerrads
85
+ // /* */ (css/js), <!-- --> + {# #} (html) och radslut // (js).
86
+ function stripComments(content, lang) {
87
+ const blocks = {
88
+ css: [['/*', '*/']],
89
+ js: [['/*', '*/']],
90
+ html: [['<!--', '-->'], ['{#', '#}']],
91
+ }[lang] || [];
92
+ const lineMarker = lang === 'js' ? '//' : null;
93
+ const out = [];
94
+ let close = null;
95
+ for (const raw of content.split('\n')) {
96
+ let code = '';
97
+ let i = 0;
98
+ while (i < raw.length) {
99
+ if (close) {
100
+ const end = raw.indexOf(close, i);
101
+ if (end === -1) { i = raw.length; }
102
+ else { i = end + close.length; close = null; }
103
+ continue;
104
+ }
105
+ let bestPos = -1;
106
+ let bestOpenLen = 0;
107
+ let bestClose = null;
108
+ for (const [open, cl] of blocks) {
109
+ const p = raw.indexOf(open, i);
110
+ if (p !== -1 && (bestPos === -1 || p < bestPos)) {
111
+ bestPos = p;
112
+ bestOpenLen = open.length;
113
+ bestClose = cl;
114
+ }
115
+ }
116
+ const lc = lineMarker ? raw.indexOf(lineMarker, i) : -1;
117
+ if (lc !== -1 && (bestPos === -1 || lc < bestPos)) {
118
+ code += raw.slice(i, lc);
119
+ i = raw.length;
120
+ } else if (bestPos !== -1) {
121
+ code += raw.slice(i, bestPos);
122
+ i = bestPos + bestOpenLen;
123
+ close = bestClose;
124
+ } else {
125
+ code += raw.slice(i);
126
+ i = raw.length;
127
+ }
128
+ }
129
+ out.push(code);
130
+ }
131
+ return out;
132
+ }
133
+
134
+ // Sann om raden (kommentarsbefriad) ANVANDER klassen: class=""-attribut
135
+ // (html), .selektor (css) eller klass-strang i JS.
136
+ function usesClass(code, cls, lang) {
137
+ const c = escapeRe(cls);
138
+ if (lang === 'css') {
139
+ return new RegExp('\\.' + c + '(?![\\w-])').test(code);
140
+ }
141
+ if (lang === 'js') {
142
+ return new RegExp("['\"`](?:[^'\"`]*\\.)?" + c + '(?![\\w-])').test(code);
143
+ }
144
+ if (lang === 'html') {
145
+ const attrs = code.match(/class\s*=\s*("[^"]*"|'[^']*')/gi) || [];
146
+ return attrs.some((a) => new RegExp('(^|[^\\w-])' + c + '(?![\\w-])').test(a));
147
+ }
148
+ return false;
149
+ }
150
+
151
+ // Synkade paket-filer (css/ds/<X>, js/ds/<X> dar <X> finns i paketet)
152
+ // hoppas - checken galler app-agd kod. App-agda domain-filer i ds/
153
+ // (ekonom.css, jubb.css, bundles/ osv.) behalls i kontrollen.
154
+ function grepDeprecated(classNames) {
155
+ const hits = [];
156
+ if (classNames.length === 0) return hits;
157
+ const LANG = { '.html': 'html', '.css': 'css', '.js': 'js' };
158
+ const pkgCss = new Set(listFilesRec(path.join(pkgDir, 'css')));
159
+ const pkgJs = new Set(listFilesRec(path.join(pkgDir, 'js')));
160
+ function isSyncedPackageFile(rel) {
161
+ if (rel.startsWith('css/ds/')) return pkgCss.has(rel.slice('css/ds/'.length));
162
+ if (rel.startsWith('js/ds/')) return pkgJs.has(rel.slice('js/ds/'.length));
163
+ return false;
164
+ }
165
+ for (const dir of ['app/templates', 'app/static']) {
166
+ const abs = path.join(process.cwd(), dir);
167
+ if (!fs.existsSync(abs)) continue;
168
+ for (const rel of listFilesRec(abs)) {
169
+ const lang = LANG[path.extname(rel)];
170
+ if (!lang) continue;
171
+ if (dir === 'app/static' && isSyncedPackageFile(rel)) continue;
172
+ const code = stripComments(fs.readFileSync(path.join(abs, rel), 'utf8'), lang);
173
+ code.forEach((line, idx) => {
174
+ for (const cls of classNames) {
175
+ if (usesClass(line, cls, lang)) {
176
+ hits.push(`${dir}/${rel}:${idx + 1} ${cls}`);
177
+ }
178
+ }
179
+ });
180
+ }
181
+ }
182
+ return hits;
183
+ }
184
+
185
+ // --- Check 1: fil-drift mot paket-kallan ---
60
186
  const cssDrift = diffDirs(path.join(pkgDir, 'css'), config.cssTarget, 'css');
61
187
  const jsDrift = diffDirs(path.join(pkgDir, 'js'), config.jsTarget, 'js');
62
188
  const allDrift = [...cssDrift, ...jsDrift];
63
189
 
190
+ // --- Check 2: deprecade selektorer i app-koden ---
191
+ const deprecatedHits = grepDeprecated(loadDeprecatedClasses());
192
+
193
+ let failed = false;
194
+
64
195
  if (allDrift.length > 0) {
196
+ failed = true;
65
197
  console.error('FAIL: @klodd/ds drift detekterad:');
66
198
  allDrift.forEach((d) => console.error(' ' + d));
67
- console.error('\nKör `npm run build:ds` för att synka.');
68
- process.exit(1);
199
+ console.error('Kör `npm run build:ds` för att synka.\n');
69
200
  }
70
201
 
202
+ if (deprecatedHits.length > 0) {
203
+ failed = true;
204
+ console.error(`FAIL: ${deprecatedHits.length} referens(er) till deprecade klasser:`);
205
+ deprecatedHits.forEach((h) => console.error(' ' + h));
206
+ console.error('Se @klodd/ds deprecated-classes.txt för ersättningar.\n');
207
+ }
208
+
209
+ if (failed) process.exit(1);
210
+
71
211
  const excludedCount = exclude.size > 0 ? ` (${exclude.size} excluderade fran kontroll)` : '';
72
- console.log(`OK: alla DS-filer matchar @klodd/ds-paketet${excludedCount}.`);
212
+ console.log(`OK: alla DS-filer matchar @klodd/ds-paketet${excludedCount}, 0 deprecade selektorer.`);
73
213
  process.exit(0);
@@ -0,0 +1,10 @@
1
+ # @klodd/ds deprecated-classes.txt
2
+ # En CSS-klass per rad (utan punkt). Text efter klassnamnet på raden
3
+ # är fri kommentar. Tomma rader och #-rader hoppas över.
4
+ # npx @klodd/ds verify greppar app/templates/ + app/static/ efter
5
+ # varje klassnamn och exit 1 vid träff.
6
+
7
+ sub-row (→ expandable-row__item matched-row, ADR 0002)
8
+ hero-amount (→ hero__amount, ADR 0019. /design-demon fixad, 0 träffar - kvar som påminnelse)
9
+ btn--negative (raderad, ingen ersättning)
10
+ tx-row (→ list-row)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@klodd/ds",
3
- "version": "5.7.0",
3
+ "version": "5.7.1",
4
4
  "description": "Klodd shared design system - tokens, components, JS",
5
5
  "main": "css/index.css",
6
6
  "bin": {
@@ -10,6 +10,7 @@
10
10
  "css/",
11
11
  "js/",
12
12
  "bin/",
13
+ "deprecated-classes.txt",
13
14
  "stylelint-plugin/",
14
15
  "SKILL.md",
15
16
  "references/"