elcrm 1.1.6 → 1.1.7

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.
@@ -21,8 +21,24 @@ export type MinifyCssVarsOptions = {
21
21
  css?: boolean;
22
22
  };
23
23
 
24
+ export declare function toIndex(value: unknown, fallback?: number): number;
25
+
24
26
  export declare function encodeName(index: number, length: number): string;
25
27
 
28
+ export declare function namespaceSize(length: number): number;
29
+
30
+ /**
31
+ * Короткие имена без коллизий: пропускает `reserved`, не выдаёт одно `to` дважды.
32
+ */
33
+ export declare function allocateUnique(
34
+ originals: string[],
35
+ reserved: Set<string>,
36
+ make: (index: number) => string,
37
+ start: number,
38
+ capacity: number,
39
+ kind: string,
40
+ ): Map<string, string>;
41
+
26
42
  export declare function minifyCssVars(
27
43
  options?: MinifyCssVarsOptions,
28
44
  ): import("vite").Plugin;
@@ -30,10 +30,16 @@ function collectJsVars(texts) {
30
30
  }
31
31
  return set;
32
32
  }
33
+ function toIndex(value, fallback = 0) {
34
+ const n = typeof value === "number" ? value : Number(value);
35
+ if (!Number.isFinite(n) || n < 0)
36
+ return fallback;
37
+ return Math.floor(n);
38
+ }
33
39
  function encodeName(index, length) {
34
40
  const n = LETTERS.length;
35
41
  const len = Math.max(1, Math.min(8, Math.floor(length)));
36
- let i = Math.max(0, Math.floor(index));
42
+ let i = toIndex(index, 0);
37
43
  let s = "";
38
44
  for (let k = 0;k < len; k++) {
39
45
  s = LETTERS[i % n] + s;
@@ -55,6 +61,47 @@ function shouldMinifyClass(name, minLen, prefix) {
55
61
  return false;
56
62
  return /[-_]/.test(name);
57
63
  }
64
+ function namespaceSize(length) {
65
+ const len = Math.max(1, Math.min(8, Math.floor(length)));
66
+ let n = 1;
67
+ for (let i = 0;i < len; i++)
68
+ n *= LETTERS.length;
69
+ return n;
70
+ }
71
+ function allocateUnique(originals, reserved, make, start, capacity, kind) {
72
+ const taken = new Set(reserved);
73
+ const out = new Map;
74
+ let cursor = start;
75
+ for (const from of originals) {
76
+ if (out.has(from))
77
+ continue;
78
+ let found;
79
+ for (let n = 0;n < capacity; n++) {
80
+ const candidate = make(cursor);
81
+ cursor++;
82
+ if (taken.has(candidate) || candidate === from)
83
+ continue;
84
+ found = candidate;
85
+ break;
86
+ }
87
+ if (!found) {
88
+ throw new Error(`[minify-css-vars] нет свободных имён для ${kind}: нужно ${originals.length}, ёмкость ${capacity}, занято ${taken.size}. Увеличьте var/name или сдвиньте start.`);
89
+ }
90
+ out.set(from, found);
91
+ taken.add(found);
92
+ }
93
+ const used = new Set;
94
+ for (const [from, to] of out) {
95
+ if (used.has(to)) {
96
+ throw new Error(`[minify-css-vars] коллизия ${kind}: «${from}» и ещё одно имя → «${to}»`);
97
+ }
98
+ used.add(to);
99
+ if (reserved.has(to)) {
100
+ throw new Error(`[minify-css-vars] «${from}» → «${to}» затирает существующее имя ${kind}`);
101
+ }
102
+ }
103
+ return out;
104
+ }
58
105
  function replaceVarsInCss(text, map) {
59
106
  let out = text;
60
107
  for (const [from, to] of map)
@@ -101,12 +148,12 @@ function normalizeOpts(raw) {
101
148
  const o = raw ?? {};
102
149
  const varLen = o.var ?? 3;
103
150
  const nameLen = o.name ?? 2;
104
- const start = o.start ?? 0;
151
+ const start = toIndex(o.start, 0);
105
152
  return {
106
153
  varLen,
107
154
  nameLen,
108
- varStart: o.varStart ?? start,
109
- nameStart: o.nameStart ?? start,
155
+ varStart: toIndex(o.varStart, start),
156
+ nameStart: toIndex(o.nameStart, start),
110
157
  vars: o.vars !== false,
111
158
  classes: o.classes !== false,
112
159
  minVar: o.minVar ?? Math.max(4, varLen + 2),
@@ -143,37 +190,44 @@ function minifyCssVars(options) {
143
190
  ...collect(cssTexts, VAR_RE),
144
191
  ...collectJsVars(jsTexts)
145
192
  ]);
146
- let vi = 0;
147
- for (const name of byLongest(names)) {
148
- if (alreadyShortVar(name, opt.varLen))
149
- continue;
150
- if (/^--[a-zA-Z_]$/.test(name))
151
- continue;
152
- if (name.length < opt.minVar)
193
+ const remap = [];
194
+ const reserved = new Set;
195
+ for (const name of names) {
196
+ if (alreadyShortVar(name, opt.varLen) || /^--[a-zA-Z_]$/.test(name) || name.length < opt.minVar) {
197
+ reserved.add(name);
153
198
  continue;
154
- varMap.set(name, `--${encodeName(opt.varStart + vi, opt.varLen)}`);
155
- vi++;
199
+ }
200
+ remap.push(name);
156
201
  }
202
+ const cap = namespaceSize(opt.varLen);
203
+ const allocated = allocateUnique(byLongest(remap), reserved, (i) => `--${encodeName(i, opt.varLen)}`, opt.varStart, cap, "vars");
204
+ for (const [k, v] of allocated)
205
+ varMap.set(k, v);
157
206
  }
158
207
  const classMap = new Map;
159
208
  if (opt.classes) {
160
- const classNames = new Set;
209
+ const allClasses = new Set;
161
210
  for (const file of files) {
162
211
  if (!file.isCss)
163
212
  continue;
164
213
  CLASS_SEL_RE.lastIndex = 0;
165
214
  let m;
166
- while (m = CLASS_SEL_RE.exec(file.raw)) {
167
- if (shouldMinifyClass(m[1], opt.minClass, opt.classPrefix)) {
168
- classNames.add(m[1]);
169
- }
170
- }
215
+ while (m = CLASS_SEL_RE.exec(file.raw))
216
+ allClasses.add(m[1]);
171
217
  }
172
- let ci = 0;
173
- for (const name of byLongest(classNames)) {
174
- classMap.set(name, `${opt.classPrefix}${encodeName(opt.nameStart + ci, opt.nameLen)}`);
175
- ci++;
218
+ const remap = [];
219
+ const reserved = new Set;
220
+ for (const name of allClasses) {
221
+ if (shouldMinifyClass(name, opt.minClass, opt.classPrefix)) {
222
+ remap.push(name);
223
+ } else {
224
+ reserved.add(name);
225
+ }
176
226
  }
227
+ const cap = namespaceSize(opt.nameLen);
228
+ const allocated = allocateUnique(byLongest(remap), reserved, (i) => `${opt.classPrefix}${encodeName(i, opt.nameLen)}`, opt.nameStart, cap, "classes");
229
+ for (const [k, v] of allocated)
230
+ classMap.set(k, v);
177
231
  }
178
232
  if (!varMap.size && !classMap.size) {
179
233
  if (opt.report) {
@@ -218,6 +272,9 @@ function minifyCssVars(options) {
218
272
  };
219
273
  }
220
274
  export {
275
+ toIndex,
276
+ namespaceSize,
221
277
  minifyCssVars,
222
- encodeName
278
+ encodeName,
279
+ allocateUnique
223
280
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "elcrm",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "CLI @elcrm/*: update --fix --test, css (prune imports), docs, cursor, migrate, audit",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -90,6 +90,23 @@ elcrm test # статический анализ + scripts п
90
90
  elcrm test --front|--server|--no-scripts
91
91
  ```
92
92
 
93
+ ### Vite-плагины из пакета `elcrm`
94
+
95
+ Нужен **elcrm ≥ 1.1.6** в `devDependencies` приложения (не только глобальный `bun i -g`). Иначе Vite: `Missing "./vite/minify-css-vars" specifier`.
96
+
97
+ ```js
98
+ import { minifyCssVars } from "elcrm/vite/minify-css-vars";
99
+
100
+ // только command === "build"
101
+ minifyCssVars({ var: 2, name: 3, classPrefix: "x" })
102
+ ```
103
+
104
+ `nameStart` / `varStart` — **числа** (индекс алфавита), не `"x"`. Префикс класса — `classPrefix`. В JS плагин не трогает декремент `--n` (иначе `aqt is not defined`).
105
+
106
+ Короткие имена **уникальны**: занятые `--aa` / `xaa` пропускаются, одно `to` не выдаётся дважды; если слотов нет — сборка падает, а не молча затирает токены.
107
+
108
+ Также: `elcrm/vite/plugin-css-scoped`, `discover-lib`, `concat-css`, `check-tokens`, `postbuild`.
109
+
93
110
  ### Прочее
94
111
 
95
112
  ```bash