@ncds/ui-admin-mcp 1.0.0-alpha.25 → 1.0.0-alpha.27
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/components.bundle.js +8 -8
- package/bin/definitions/external/editor.js +1 -53
- package/bin/definitions/external/step-guide.js +1 -52
- package/bin/definitions/rules.json +1 -1
- package/bin/server.js +1 -231
- package/bin/tools/external/editor.js +1 -88
- package/bin/tools/external/step-guide.js +1 -79
- package/bin/tools/getComponentProps.js +1 -26
- package/bin/tools/getDesignTokens.js +1 -20
- package/bin/tools/listComponents.js +1 -24
- package/bin/tools/listCompositionOverrides.js +1 -156
- package/bin/tools/listIcons.js +1 -23
- package/bin/tools/ping.js +1 -20
- package/bin/tools/renderToHtml.js +1 -882
- package/bin/tools/searchComponent.js +1 -33
- package/bin/tools/searchIcon.js +1 -19
- package/bin/tools/validateHtml.js +1 -153
- package/bin/types.js +1 -5
- package/bin/utils/bemValidator.js +1 -210
- package/bin/utils/compliance.js +1 -203
- package/bin/utils/dataLoader.js +1 -277
- package/bin/utils/domEnvironment.js +1 -77
- package/bin/utils/fuzzyMatch.js +1 -120
- package/bin/utils/logger.js +1 -27
- package/bin/utils/response.js +1 -39
- package/bin/utils/tokenValidator.js +1 -194
- package/bin/version.d.ts +1 -1
- package/bin/version.js +1 -9
- package/data/_tokens.json +3 -3
- package/data/select-box.json +6 -0
- package/data/select.json +6 -0
- package/package.json +5 -2
|
@@ -1,194 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.extractAllCss = exports.detectTokenIssues = exports.buildTokenValueMap = void 0;
|
|
4
|
-
// ── server.ts 사전 계산용 ───────────────────────────────────────────────
|
|
5
|
-
/** hex 값(lowercase) → 토큰 CSS 변수명 배열 역방향 매핑 빌드 — server.ts에서 1회 호출 */
|
|
6
|
-
const buildTokenValueMap = (tokenData) => {
|
|
7
|
-
const map = new Map();
|
|
8
|
-
for (const group of tokenData.groups) {
|
|
9
|
-
if (group.category !== 'color')
|
|
10
|
-
continue;
|
|
11
|
-
for (const token of group.tokens) {
|
|
12
|
-
const hex = token.value.toLowerCase();
|
|
13
|
-
const existing = map.get(hex);
|
|
14
|
-
if (existing)
|
|
15
|
-
existing.push(token.name);
|
|
16
|
-
else
|
|
17
|
-
map.set(hex, [token.name]);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return map;
|
|
21
|
-
};
|
|
22
|
-
exports.buildTokenValueMap = buildTokenValueMap;
|
|
23
|
-
// ── 상수 ─────────────────────────────────────────────────────────────────
|
|
24
|
-
const HEX_PATTERN_SOURCE = /#([0-9a-fA-F]{3,8})\b/g.source;
|
|
25
|
-
const RGB_PATTERN_SOURCE = /rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/g.source;
|
|
26
|
-
const VAR_PATTERN_SOURCE = /var\(\s*(--[a-zA-Z0-9-]+)(?:\s*,\s*[^)]+)?\s*\)/g.source;
|
|
27
|
-
// ── 순수 헬퍼 ────────────────────────────────────────────────────────────
|
|
28
|
-
const HEX_RADIX = 16;
|
|
29
|
-
const HEX_BYTE_LENGTH = 2;
|
|
30
|
-
const HEX_SHORT_LENGTH = 3;
|
|
31
|
-
const HEX_FULL_LENGTH = 6;
|
|
32
|
-
/** rgb(r,g,b) → #rrggbb hex 변환 */
|
|
33
|
-
const rgbToHex = (r, g, b) => `#${[r, g, b].map((v) => v.toString(HEX_RADIX).padStart(HEX_BYTE_LENGTH, '0')).join('')}`;
|
|
34
|
-
/** 3자리 hex를 6자리로 정규화 */
|
|
35
|
-
const normalizeHex = (hex) => {
|
|
36
|
-
const h = hex.toLowerCase().replace('#', '');
|
|
37
|
-
if (h.length === HEX_SHORT_LENGTH)
|
|
38
|
-
return `#${h[0]}${h[0]}${h[1]}${h[1]}${h[2]}${h[2]}`;
|
|
39
|
-
return `#${h.slice(0, HEX_FULL_LENGTH)}`;
|
|
40
|
-
};
|
|
41
|
-
/** var()의 prefix가 토큰 시스템에 속하는지 확인 */
|
|
42
|
-
const hasKnownTokenPrefix = (varName, prefixSet) => {
|
|
43
|
-
for (const prefix of prefixSet) {
|
|
44
|
-
if (varName.startsWith(prefix))
|
|
45
|
-
return true;
|
|
46
|
-
}
|
|
47
|
-
return false;
|
|
48
|
-
};
|
|
49
|
-
/** tokenData에 등록된 토큰명 prefix Set — 커스텀 변수 제외용 */
|
|
50
|
-
const buildTokenPrefixSet = (tokenData) => {
|
|
51
|
-
const prefixes = new Set();
|
|
52
|
-
for (const group of tokenData.groups) {
|
|
53
|
-
for (const token of group.tokens) {
|
|
54
|
-
const stripped = token.name.replace(/^--/, '');
|
|
55
|
-
const segments = stripped.split('-');
|
|
56
|
-
if (segments.length >= 2)
|
|
57
|
-
prefixes.add(`--${segments.slice(0, 2).join('-')}`);
|
|
58
|
-
if (segments.length >= 1)
|
|
59
|
-
prefixes.add(`--${segments[0]}`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return prefixes;
|
|
63
|
-
};
|
|
64
|
-
/** 모든 토큰명을 Set으로 수집 */
|
|
65
|
-
const buildTokenNameSet = (tokenData) => {
|
|
66
|
-
const names = new Set();
|
|
67
|
-
for (const group of tokenData.groups) {
|
|
68
|
-
for (const token of group.tokens)
|
|
69
|
-
names.add(token.name);
|
|
70
|
-
}
|
|
71
|
-
return names;
|
|
72
|
-
};
|
|
73
|
-
/** token prefix → 카테고리 역방향 맵 — invalid_token suggestion에 카테고리 명시용 */
|
|
74
|
-
const buildPrefixCategoryMap = (tokenData) => {
|
|
75
|
-
const map = new Map();
|
|
76
|
-
for (const group of tokenData.groups) {
|
|
77
|
-
for (const token of group.tokens) {
|
|
78
|
-
const stripped = token.name.replace(/^--/, '');
|
|
79
|
-
const segments = stripped.split('-');
|
|
80
|
-
if (segments.length >= 2)
|
|
81
|
-
map.set(`--${segments.slice(0, 2).join('-')}`, group.category);
|
|
82
|
-
if (segments.length >= 1)
|
|
83
|
-
map.set(`--${segments[0]}`, group.category);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return map;
|
|
87
|
-
};
|
|
88
|
-
/** var 이름의 prefix에서 카테고리 추론 — 더 긴 prefix 우선 */
|
|
89
|
-
const inferCategoryFromVarName = (varName, prefixCategoryMap) => {
|
|
90
|
-
const stripped = varName.replace(/^--/, '');
|
|
91
|
-
const segments = stripped.split('-');
|
|
92
|
-
if (segments.length >= 2) {
|
|
93
|
-
const cat = prefixCategoryMap.get(`--${segments.slice(0, 2).join('-')}`);
|
|
94
|
-
if (cat)
|
|
95
|
-
return cat;
|
|
96
|
-
}
|
|
97
|
-
return prefixCategoryMap.get(`--${segments[0]}`);
|
|
98
|
-
};
|
|
99
|
-
/** inline style + <style> 블록의 CSS 텍스트를 하나로 합침 */
|
|
100
|
-
const extractAllCss = (root) => {
|
|
101
|
-
const parts = [];
|
|
102
|
-
for (const el of root.querySelectorAll('[style]')) {
|
|
103
|
-
const style = el.getAttribute('style');
|
|
104
|
-
if (style)
|
|
105
|
-
parts.push(style);
|
|
106
|
-
}
|
|
107
|
-
for (const styleEl of root.querySelectorAll('style')) {
|
|
108
|
-
parts.push(styleEl.textContent ?? '');
|
|
109
|
-
}
|
|
110
|
-
return parts.join(' ');
|
|
111
|
-
};
|
|
112
|
-
exports.extractAllCss = extractAllCss;
|
|
113
|
-
/** CSS 텍스트에서 하드코딩된 색상값(hex/rgb)을 추출 */
|
|
114
|
-
const collectHardcodedColors = (css) => {
|
|
115
|
-
const colors = [];
|
|
116
|
-
const hexPattern = new RegExp(HEX_PATTERN_SOURCE, 'g');
|
|
117
|
-
for (const match of css.matchAll(hexPattern)) {
|
|
118
|
-
colors.push({ raw: match[0], normalized: normalizeHex(match[0]) });
|
|
119
|
-
}
|
|
120
|
-
const rgbPattern = new RegExp(RGB_PATTERN_SOURCE, 'g');
|
|
121
|
-
for (const match of css.matchAll(rgbPattern)) {
|
|
122
|
-
colors.push({
|
|
123
|
-
raw: match[0],
|
|
124
|
-
normalized: rgbToHex(Number.parseInt(match[1], 10), Number.parseInt(match[2], 10), Number.parseInt(match[3], 10)),
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
return colors;
|
|
128
|
-
};
|
|
129
|
-
/** token_not_used suggestion 생성 */
|
|
130
|
-
const buildTokenSuggestion = (raw, tokenNames) => tokenNames.length === 1
|
|
131
|
-
? `Use var(${tokenNames[0]}) instead of ${raw}`
|
|
132
|
-
: `Possible tokens: ${tokenNames.map((t) => `var(${t})`).join(', ')}`;
|
|
133
|
-
// ── 검증 함수 ────────────────────────────────────────────────────────────
|
|
134
|
-
/** 하드코딩 색상 → token_not_used 에러 수집 */
|
|
135
|
-
const collectTokenNotUsedErrors = (css, tokenValueMap) => {
|
|
136
|
-
const errors = [];
|
|
137
|
-
let missing = 0;
|
|
138
|
-
for (const color of collectHardcodedColors(css)) {
|
|
139
|
-
const tokenNames = tokenValueMap.get(color.normalized);
|
|
140
|
-
if (!tokenNames)
|
|
141
|
-
continue;
|
|
142
|
-
missing++;
|
|
143
|
-
errors.push({
|
|
144
|
-
type: 'token_not_used',
|
|
145
|
-
target: color.raw,
|
|
146
|
-
component: '(inline-style)',
|
|
147
|
-
message: `Hardcoded color '${color.raw}' should use a design token.`,
|
|
148
|
-
suggestion: buildTokenSuggestion(color.raw, tokenNames),
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
return { errors, missing };
|
|
152
|
-
};
|
|
153
|
-
/** var() 참조 → invalid_token 에러 수집 + correct 카운트 */
|
|
154
|
-
const collectInvalidTokenErrors = (css, tokenNameSet, tokenPrefixSet, prefixCategoryMap) => {
|
|
155
|
-
const errors = [];
|
|
156
|
-
let correct = 0;
|
|
157
|
-
let invalid = 0;
|
|
158
|
-
const varPattern = new RegExp(VAR_PATTERN_SOURCE, 'g');
|
|
159
|
-
for (const varMatch of css.matchAll(varPattern)) {
|
|
160
|
-
const varName = varMatch[1];
|
|
161
|
-
if (!hasKnownTokenPrefix(varName, tokenPrefixSet))
|
|
162
|
-
continue;
|
|
163
|
-
if (tokenNameSet.has(varName)) {
|
|
164
|
-
correct++;
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
invalid++;
|
|
168
|
-
const category = inferCategoryFromVarName(varName, prefixCategoryMap);
|
|
169
|
-
const suggestion = category
|
|
170
|
-
? `Call get_design_tokens('${category}') to see available ${category} tokens.`
|
|
171
|
-
: 'Call get_design_tokens to see available tokens.';
|
|
172
|
-
errors.push({
|
|
173
|
-
type: 'invalid_token',
|
|
174
|
-
target: `var(${varName})`,
|
|
175
|
-
component: '(inline-style)',
|
|
176
|
-
message: `Token '${varName}' does not exist in the design system.`,
|
|
177
|
-
suggestion,
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
return { errors, correct, invalid };
|
|
182
|
-
};
|
|
183
|
-
/** 토큰 미사용 + 무효 토큰 감지 */
|
|
184
|
-
const detectTokenIssues = (params) => {
|
|
185
|
-
const { root, tokenData, tokenValueMap } = params;
|
|
186
|
-
const allCss = extractAllCss(root);
|
|
187
|
-
const notUsed = collectTokenNotUsedErrors(allCss, tokenValueMap);
|
|
188
|
-
const invalidResult = collectInvalidTokenErrors(allCss, buildTokenNameSet(tokenData), buildTokenPrefixSet(tokenData), buildPrefixCategoryMap(tokenData));
|
|
189
|
-
return {
|
|
190
|
-
errors: [...notUsed.errors, ...invalidResult.errors],
|
|
191
|
-
tokenUsage: { correct: invalidResult.correct, missing: notUsed.missing, invalid: invalidResult.invalid },
|
|
192
|
-
};
|
|
193
|
-
};
|
|
194
|
-
exports.detectTokenIssues = detectTokenIssues;
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.extractAllCss=exports.detectTokenIssues=exports.buildTokenValueMap=void 0;const e=e=>{const t=new Map;for(const o of e.groups)if("color"===o.category)for(const e of o.tokens){const o=e.value.toLowerCase(),s=t.get(o);s?s.push(e.name):t.set(o,[e.name])}return t};exports.buildTokenValueMap=e;const t=/#([0-9a-fA-F]{3,8})\b/g.source,o=/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/g.source,s=/var\(\s*(--[a-zA-Z0-9-]+)(?:\s*,\s*[^)]+)?\s*\)/g.source,n=16,r=2,a=3,c=6,l=(e,t,o)=>`#${[e,t,o].map(e=>e.toString(n).padStart(2,"0")).join("")}`,i=e=>{const t=e.toLowerCase().replace("#","");return 3===t.length?`#${t[0]}${t[0]}${t[1]}${t[1]}${t[2]}${t[2]}`:`#${t.slice(0,6)}`},u=(e,t)=>{for(const o of t)if(e.startsWith(o))return!0;return!1},g=e=>{const t=new Set;for(const o of e.groups)for(const e of o.tokens){const o=e.name.replace(/^--/,"").split("-");o.length>=2&&t.add(`--${o.slice(0,2).join("-")}`),o.length>=1&&t.add(`--${o[0]}`)}return t},p=e=>{const t=new Set;for(const o of e.groups)for(const e of o.tokens)t.add(e.name);return t},f=e=>{const t=new Map;for(const o of e.groups)for(const e of o.tokens){const s=e.name.replace(/^--/,"").split("-");s.length>=2&&t.set(`--${s.slice(0,2).join("-")}`,o.category),s.length>=1&&t.set(`--${s[0]}`,o.category)}return t},d=(e,t)=>{const o=e.replace(/^--/,"").split("-");if(o.length>=2){const e=t.get(`--${o.slice(0,2).join("-")}`);if(e)return e}return t.get(`--${o[0]}`)},m=e=>{const t=[];for(const o of e.querySelectorAll("[style]")){const e=o.getAttribute("style");e&&t.push(e)}for(const o of e.querySelectorAll("style"))t.push(o.textContent??"");return t.join(" ")};exports.extractAllCss=m;const $=e=>{const s=[],n=new RegExp(t,"g");for(const t of e.matchAll(n))s.push({raw:t[0],normalized:i(t[0])});const r=new RegExp(o,"g");for(const t of e.matchAll(r))s.push({raw:t[0],normalized:l(Number.parseInt(t[1],10),Number.parseInt(t[2],10),Number.parseInt(t[3],10))});return s},h=(e,t)=>1===t.length?`Use var(${t[0]}) instead of ${e}`:`Possible tokens: ${t.map(e=>`var(${e})`).join(", ")}`,k=(e,t)=>{const o=[];let s=0;for(const n of $(e)){const e=t.get(n.normalized);e&&(s++,o.push({type:"token_not_used",target:n.raw,component:"(inline-style)",message:`Hardcoded color '${n.raw}' should use a design token.`,suggestion:h(n.raw,e)}))}return{errors:o,missing:s}},w=(e,t,o,n)=>{const r=[];let a=0,c=0;const l=new RegExp(s,"g");for(const s of e.matchAll(l)){const e=s[1];if(u(e,o))if(t.has(e))a++;else{c++;const t=d(e,n),o=t?`Call get_design_tokens('${t}') to see available ${t} tokens.`:"Call get_design_tokens to see available tokens.";r.push({type:"invalid_token",target:`var(${e})`,component:"(inline-style)",message:`Token '${e}' does not exist in the design system.`,suggestion:o})}}return{errors:r,correct:a,invalid:c}},x=e=>{const{root:t,tokenData:o,tokenValueMap:s}=e,n=m(t),r=k(n,s),a=w(n,p(o),g(o),f(o));return{errors:[...r.errors,...a.errors],tokenUsage:{correct:a.correct,missing:r.missing,invalid:a.invalid}}};exports.detectTokenIssues=x;
|
package/bin/version.d.ts
CHANGED
package/bin/version.js
CHANGED
|
@@ -1,9 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.VERSION = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* NCDS UI Admin MCP 서버가 노출하는 ui-admin 정합 기준 버전.
|
|
6
|
-
* scripts/generate-version-ts.ts 가 ui-admin/package.json 의 version 으로부터 자동 생성한다.
|
|
7
|
-
* 수동 편집 금지 — 빌드(`pnpm generate:version-ts` 또는 `pnpm build`) 시 덮어쓰여진다.
|
|
8
|
-
*/
|
|
9
|
-
exports.VERSION = '1.8.13';
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.VERSION=void 0,exports.VERSION="1.8.14";
|
package/data/_tokens.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
{
|
|
11
11
|
"name": "primary-red",
|
|
12
12
|
"category": "color",
|
|
13
|
-
"description": "브랜드 Primary. 450
|
|
13
|
+
"description": "브랜드 Primary. 강한 환기력 때문에 행동 트리거 전용. 450~500은 CTA·핵심 행동 트리거(450이 기본), 600~700은 에러·주의(Destructive)로 의미가 갈린다. 트리거와 상태 색을 섞지 않는다.",
|
|
14
14
|
"tokens": [
|
|
15
15
|
{
|
|
16
16
|
"name": "--primary-red-50",
|
|
@@ -178,7 +178,7 @@
|
|
|
178
178
|
{
|
|
179
179
|
"name": "blue",
|
|
180
180
|
"category": "color",
|
|
181
|
-
"description": "Info
|
|
181
|
+
"description": "정보·기능 영역의 기본색. 행동을 재촉하지 않는 중립·신뢰 기반 강조에 넓게 사용(Info·링크 포함). 그레이스케일을 압도하지 않는다.",
|
|
182
182
|
"tokens": [
|
|
183
183
|
{
|
|
184
184
|
"name": "--blue-50",
|
|
@@ -319,7 +319,7 @@
|
|
|
319
319
|
{
|
|
320
320
|
"name": "violet",
|
|
321
321
|
"category": "color",
|
|
322
|
-
"description": "Accent
|
|
322
|
+
"description": "선택적 Accent. 행동 유도와 무관한 시각적 강조·데이터 시각화 전용. 파랑과 같은 위계로 섞지 않으며, 어떤 경우에도 파랑의 비중을 넘지 않는다.",
|
|
323
323
|
"tokens": [
|
|
324
324
|
{
|
|
325
325
|
"name": "--violet-50",
|
package/data/select-box.json
CHANGED
|
@@ -145,6 +145,11 @@
|
|
|
145
145
|
"required": false,
|
|
146
146
|
"default": "선택하세요"
|
|
147
147
|
},
|
|
148
|
+
"readOnly": {
|
|
149
|
+
"type": "boolean",
|
|
150
|
+
"required": false,
|
|
151
|
+
"default": false
|
|
152
|
+
},
|
|
148
153
|
"register": {
|
|
149
154
|
"type": "object",
|
|
150
155
|
"required": false,
|
|
@@ -187,6 +192,7 @@
|
|
|
187
192
|
"ncua-selectbox--disabled",
|
|
188
193
|
"ncua-selectbox--multiple",
|
|
189
194
|
"ncua-selectbox--open",
|
|
195
|
+
"ncua-selectbox--readonly",
|
|
190
196
|
"ncua-selectbox--simple",
|
|
191
197
|
"ncua-selectbox__arrow",
|
|
192
198
|
"ncua-selectbox__arrow--up",
|
package/data/select.json
CHANGED
|
@@ -80,6 +80,11 @@
|
|
|
80
80
|
"type": "string",
|
|
81
81
|
"required": false
|
|
82
82
|
},
|
|
83
|
+
"readOnly": {
|
|
84
|
+
"type": "boolean",
|
|
85
|
+
"required": false,
|
|
86
|
+
"default": false
|
|
87
|
+
},
|
|
83
88
|
"register": {
|
|
84
89
|
"type": "object",
|
|
85
90
|
"required": false,
|
|
@@ -109,6 +114,7 @@
|
|
|
109
114
|
"bemClasses": [
|
|
110
115
|
"ncua-hint-text",
|
|
111
116
|
"ncua-select",
|
|
117
|
+
"ncua-select--readonly",
|
|
112
118
|
"ncua-select--simple",
|
|
113
119
|
"ncua-select__content",
|
|
114
120
|
"ncua-select__tag"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ncds/ui-admin-mcp",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.27",
|
|
4
4
|
"description": "NCDS UI Admin MCP 서버 — AI 에이전트가 NCUA 컴포넌트를 조회하고 HTML을 검증할 수 있는 MCP 서버",
|
|
5
5
|
"bin": {
|
|
6
6
|
"ncua-mcp": "./bin/server.mjs"
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
"generate:version-ts": "tsx scripts/generate-version-ts.ts",
|
|
23
23
|
"build:bundle": "tsx scripts/build-bundle.ts",
|
|
24
24
|
"build:server": "mkdir -p bin && rm -rf bin/definitions bin/overrides && cp -r src/definitions bin/definitions && cp -r src/overrides bin/overrides && find bin/definitions -name '*.ts' -delete && tsc --project tsconfig.json",
|
|
25
|
-
"build": "pnpm extract && pnpm extract:icons && pnpm extract:tokens && pnpm generate:cdn-meta && pnpm generate:version-ts && pnpm build:bundle && pnpm build:server",
|
|
25
|
+
"build": "pnpm extract && pnpm extract:icons && pnpm extract:tokens && pnpm generate:cdn-meta && pnpm generate:version-ts && pnpm build:bundle && pnpm build:server && pnpm minify:compiled",
|
|
26
|
+
"minify:compiled": "tsx scripts/minify-compiled.ts",
|
|
26
27
|
"check-meta": "tsx scripts/check-meta-coherence.ts",
|
|
27
28
|
"test": "vitest run --config vitest.config.ts",
|
|
28
29
|
"test:coverage": "vitest run --coverage --config vitest.config.ts",
|
|
@@ -62,6 +63,8 @@
|
|
|
62
63
|
"devDependencies": {
|
|
63
64
|
"@ncds/ui-admin": "workspace:*",
|
|
64
65
|
"@types/lodash": "^4.17.24",
|
|
66
|
+
"glob": "^10.3.0",
|
|
67
|
+
"terser": "^5.28.0",
|
|
65
68
|
"@types/node": "^25.5.0",
|
|
66
69
|
"@types/react": "^18.3.20",
|
|
67
70
|
"esbuild": "^0.28.0",
|