arekui 1.0.0 → 2.0.0

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.
Files changed (99) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/LICENSE +21 -21
  3. package/README.md +26 -198
  4. package/arekui.css +5930 -3279
  5. package/arekui.d.ts +328 -328
  6. package/arekui.js +817 -450
  7. package/assets/img/grunge-wall-texture-freepick-kues1.webp +0 -0
  8. package/assets/img/logo.png +0 -0
  9. package/dist/css/arekui.base.min.css +1 -0
  10. package/dist/css/arekui.broadsheet.min.css +1 -0
  11. package/dist/css/arekui.brutalist.min.css +1 -0
  12. package/dist/css/arekui.default.min.css +1 -0
  13. package/dist/css/arekui.full.min.css +1 -0
  14. package/dist/css/arekui.min.css +1 -0
  15. package/dist/css/arekui.neo.min.css +1 -0
  16. package/dist/css/arekui.pixel.min.css +1 -0
  17. package/dist/js/arekui.min.js +2 -0
  18. package/package.json +64 -57
  19. package/scripts/build-css.js +95 -0
  20. package/scripts/build-custom.js +287 -318
  21. package/scripts/build-js.js +68 -0
  22. package/scripts/clean.js +13 -0
  23. package/scripts/dev.js +97 -0
  24. package/scripts/imports.js +18 -0
  25. package/scripts/split.js +256 -0
  26. package/src/arekui.base.css +63 -0
  27. package/src/arekui.broadsheet.css +6 -0
  28. package/src/arekui.brutalist.css +8 -0
  29. package/src/arekui.core.css +64 -0
  30. package/src/arekui.css +22 -5
  31. package/src/arekui.default.css +6 -0
  32. package/src/arekui.neo.css +8 -0
  33. package/src/arekui.pixel.css +8 -0
  34. package/src/base/brutalist-overrides.css +254 -0
  35. package/src/base/neo-overrides.css +330 -315
  36. package/src/base/pixel-overrides.css +336 -0
  37. package/src/base/reset.css +179 -65
  38. package/src/base/theme-brutalist.css +111 -0
  39. package/src/base/theme-default.css +184 -0
  40. package/src/base/theme-neo.css +108 -0
  41. package/src/base/theme-pixel.css +111 -0
  42. package/src/base/tokens.css +59 -302
  43. package/src/components/accordion.css +32 -33
  44. package/src/components/alert.css +24 -25
  45. package/src/components/badge.css +31 -32
  46. package/src/components/breadcrumb.css +31 -32
  47. package/src/components/broadsheet.css +264 -0
  48. package/src/components/button.css +102 -101
  49. package/src/components/card.css +22 -23
  50. package/src/components/carousel.css +49 -13
  51. package/src/components/chat.css +77 -76
  52. package/src/components/description-list.css +34 -0
  53. package/src/components/docs.css +252 -0
  54. package/src/components/empty.css +36 -0
  55. package/src/components/file-upload.css +49 -50
  56. package/src/components/footer.css +40 -25
  57. package/src/components/forms.css +67 -68
  58. package/src/components/heading.css +27 -0
  59. package/src/components/hero.css +20 -21
  60. package/src/components/join.css +12 -13
  61. package/src/components/kbd.css +15 -0
  62. package/src/components/link.css +22 -23
  63. package/src/components/loading.css +109 -0
  64. package/src/components/menu.css +104 -54
  65. package/src/components/misc.css +94 -55
  66. package/src/components/modal.css +30 -31
  67. package/src/components/navbar.css +166 -36
  68. package/src/components/pagination.css +50 -45
  69. package/src/components/popover.css +68 -69
  70. package/src/components/progress.css +17 -18
  71. package/src/components/prose.css +78 -78
  72. package/src/components/rating.css +35 -36
  73. package/src/components/scroll-top.css +34 -34
  74. package/src/components/segmented.css +44 -0
  75. package/src/components/sidebar.css +377 -0
  76. package/src/components/skeleton.css +52 -52
  77. package/src/components/spinner.css +36 -0
  78. package/src/components/swap.css +43 -43
  79. package/src/components/table.css +20 -21
  80. package/src/components/tabs.css +22 -23
  81. package/src/components/tag.css +99 -99
  82. package/src/components/textured.css +52 -32
  83. package/src/components/timeline.css +32 -33
  84. package/src/components/toast.css +132 -132
  85. package/src/components/tooltip.css +22 -23
  86. package/src/utilities/borders.css +150 -150
  87. package/src/utilities/brutalist-utilities.css +49 -0
  88. package/src/utilities/colors.css +79 -71
  89. package/src/utilities/hover.css +31 -31
  90. package/src/utilities/layout.css +98 -98
  91. package/src/utilities/neo-utilities.css +86 -93
  92. package/src/utilities/pixel-utilities.css +88 -0
  93. package/src/utilities/responsive.css +131 -126
  94. package/src/utilities/sizing.css +98 -89
  95. package/src/utilities/spacing.css +132 -129
  96. package/src/utilities/typography.css +95 -92
  97. package/dist/arekui.min.css +0 -1
  98. package/dist/arekui.min.js +0 -2
  99. package/src/components/drawer.css +0 -53
@@ -1,318 +1,287 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Arekui Build — scans your source and outputs only what you use.
5
- *
6
- * Usage:
7
- * npx arekui-build # reads arekui.config.js
8
- * npx arekui-build -i ./src -o ./assets # overrides with flags
9
- * npx arekui-build --sourcemap
10
- * npx arekui-build --full # include everything
11
- *
12
- * Config file (arekui.config.js or arekui.config.json):
13
- * module.exports = {
14
- * input: "./src", // directory to scan
15
- * output: "./assets", // output directory
16
- * name: "arekui", // output filename (without .min.css)
17
- * sourcemap: false,
18
- * full: false, // true = ignore scan, include all
19
- * };
20
- *
21
- * CLI flags override config file values.
22
- */
23
-
24
- const fs = require("fs");
25
- const path = require("path");
26
- const { bundle } = require("lightningcss");
27
-
28
- // ── Load config file ───────────────────────────────────────────────────────
29
- function loadConfig() {
30
- const candidates = ["arekui.config.js", "arekui.config.json"];
31
- for (const file of candidates) {
32
- const full = path.resolve(file);
33
- if (fs.existsSync(full)) {
34
- try {
35
- console.log(`\x1b[90m config: ${file}\x1b[0m`);
36
- return file.endsWith(".json")
37
- ? JSON.parse(fs.readFileSync(full, "utf-8"))
38
- : require(full);
39
- } catch (e) {
40
- console.error(`\x1b[31m✗\x1b[0m Failed to load ${file}: ${e.message}`);
41
- process.exit(1);
42
- }
43
- }
44
- }
45
- return {};
46
- }
47
-
48
- // ── CLI args (override config) ─────────────────────────────────────────────
49
- function getArg(flags) {
50
- const idx = process.argv.findIndex((a) => flags.includes(a));
51
- return idx === -1 ? null : process.argv[idx + 1];
52
- }
53
-
54
- const config = loadConfig();
55
-
56
- const input = getArg(["-i", "--input"]) || config.input || ".";
57
- const output = getArg(["-o", "--output"]) || config.output || "assets";
58
- const name = getArg(["-n", "--name"]) || config.name || "arekui";
59
- const sourcemap =
60
- process.argv.includes("--sourcemap") || config.sourcemap === true;
61
- const full = process.argv.includes("--full") || config.full === true;
62
-
63
- const srcDir = path.resolve(input);
64
- const outDir = path.resolve(output);
65
-
66
- // ── Component → CSS class mapping ──────────────────────────────────────────
67
- const COMPONENT_CLASSES = {
68
- accordion: ["ar-accordion", "ar-collapse"],
69
- alert: ["ar-alert"],
70
- badge: ["ar-badge"],
71
- breadcrumb: ["ar-breadcrumb"],
72
- button: ["ar-btn"],
73
- card: ["ar-card", "ar-card-body", "ar-card-title", "ar-card-figure"],
74
- carousel: ["ar-carousel", "ar-carousel-item"],
75
- chat: ["ar-chat", "ar-chat-bubble"],
76
- drawer: ["ar-drawer"],
77
- "file-upload": ["ar-file-upload"],
78
- footer: ["ar-footer"],
79
- forms: [
80
- "ar-input",
81
- "ar-textarea",
82
- "ar-select",
83
- "ar-checkbox",
84
- "ar-radio",
85
- "ar-toggle",
86
- "ar-range",
87
- "ar-label",
88
- "ar-fieldset",
89
- ],
90
- hero: ["ar-hero"],
91
- join: ["ar-join"],
92
- link: ["ar-link"],
93
- menu: ["ar-menu", "ar-dropdown"],
94
- misc: ["ar-divider", "ar-stepper", "ar-step", "ar-avatar"],
95
- modal: ["ar-modal"],
96
- navbar: ["ar-navbar"],
97
- pagination: ["ar-pagination"],
98
- popover: ["ar-popover"],
99
- progress: ["ar-progress"],
100
- prose: ["ar-prose"],
101
- rating: ["ar-rating"],
102
- "scroll-top": ["ar-scroll-top"],
103
- skeleton: ["ar-skeleton"],
104
- swap: ["ar-swap"],
105
- table: ["ar-table"],
106
- tabs: ["ar-tabs", "ar-tab"],
107
- tag: ["ar-tag"],
108
- textured: ["ar-bg-img"],
109
- timeline: ["ar-timeline"],
110
- toast: ["ar-toast"],
111
- tooltip: ["ar-tooltip"],
112
- };
113
-
114
- const UTILITY_PATTERNS = {
115
- spacing: [
116
- /^ar-p/,
117
- /^ar-px/,
118
- /^ar-py/,
119
- /^ar-m/,
120
- /^ar-mt/,
121
- /^ar-mb/,
122
- /^ar-my/,
123
- /^ar-mx/,
124
- /^ar-gap/,
125
- ],
126
- layout: [
127
- /^ar-flex/,
128
- /^ar-inline-flex/,
129
- /^ar-flex-col/,
130
- /^ar-flex-wrap/,
131
- /^ar-items-/,
132
- /^ar-justify-/,
133
- /^ar-grid/,
134
- /^ar-block/,
135
- /^ar-hidden/,
136
- ],
137
- typography: [/^ar-text-/, /^ar-font-/, /^ar-leading-/, /^ar-tracking-/],
138
- sizing: [
139
- /^ar-w-/,
140
- /^ar-h-/,
141
- /^ar-min-h-/,
142
- /^ar-max-w-/,
143
- /^ar-aspect-video/,
144
- /^ar-object-cover/,
145
- ],
146
- colors: [
147
- /^ar-bg-base/,
148
- /^ar-bg-neutral/,
149
- /^ar-bg-primary/,
150
- /^ar-bg-secondary/,
151
- /^ar-bg-accent/,
152
- /^ar-text-base/,
153
- /^ar-text-gray/,
154
- ],
155
- borders: [/^ar-border/, /^ar-rounded/, /^ar-shadow-/],
156
- hover: [/^ar-hover-/],
157
- responsive: [/^ar-md:/, /^ar-lg:/],
158
- "neo-utilities": [/^ar-neo-/],
159
- };
160
-
161
- // ── Scan source files ──────────────────────────────────────────────────────
162
- function findSourceFiles(dir) {
163
- const exts = [".html", ".jsx", ".vue", ".svelte", ".tsx", ".ts", ".js"];
164
- const files = [];
165
-
166
- function walk(d) {
167
- for (const entry of fs.readdirSync(d, { withFileTypes: true })) {
168
- const full = path.join(d, entry.name);
169
- if (
170
- entry.isDirectory() &&
171
- !entry.name.startsWith(".") &&
172
- entry.name !== "node_modules"
173
- ) {
174
- walk(full);
175
- } else if (entry.isFile() && exts.includes(path.extname(entry.name))) {
176
- files.push(full);
177
- }
178
- }
179
- }
180
- walk(dir);
181
- return files;
182
- }
183
-
184
- function scan(dir) {
185
- const files = findSourceFiles(dir);
186
- const content = files.map((f) => fs.readFileSync(f, "utf-8")).join("\n");
187
- const tokens = new Set();
188
- let m;
189
- const re = /ar-[\w-]+/g;
190
- while ((m = re.exec(content)) !== null) tokens.add(m[0]);
191
- return tokens;
192
- }
193
-
194
- function detectComponents(classes) {
195
- const found = new Set();
196
- for (const [comp, patterns] of Object.entries(COMPONENT_CLASSES)) {
197
- for (const cls of classes) {
198
- if (patterns.some((p) => cls === p || cls.startsWith(p + "-"))) {
199
- found.add(comp);
200
- break;
201
- }
202
- }
203
- }
204
- return [...found];
205
- }
206
-
207
- function detectUtilities(classes) {
208
- const found = new Set();
209
- for (const [util, regexes] of Object.entries(UTILITY_PATTERNS)) {
210
- for (const cls of classes) {
211
- if (regexes.some((r) => r.test(cls))) {
212
- found.add(util);
213
- break;
214
- }
215
- }
216
- }
217
- return [...found];
218
- }
219
-
220
- // ── Build CSS ──────────────────────────────────────────────────────────────
221
- const COMPONENT_FILES = {
222
- accordion: "accordion",
223
- alert: "alert",
224
- badge: "badge",
225
- breadcrumb: "breadcrumb",
226
- button: "button",
227
- card: "card",
228
- carousel: "carousel",
229
- chat: "chat",
230
- drawer: "drawer",
231
- "file-upload": "file-upload",
232
- footer: "footer",
233
- forms: "forms",
234
- hero: "hero",
235
- join: "join",
236
- link: "link",
237
- menu: "menu",
238
- misc: "misc",
239
- modal: "modal",
240
- navbar: "navbar",
241
- pagination: "pagination",
242
- popover: "popover",
243
- progress: "progress",
244
- prose: "prose",
245
- rating: "rating",
246
- "scroll-top": "scroll-top",
247
- skeleton: "skeleton",
248
- swap: "swap",
249
- table: "table",
250
- tabs: "tabs",
251
- tag: "tag",
252
- textured: "textured",
253
- timeline: "timeline",
254
- toast: "toast",
255
- tooltip: "tooltip",
256
- };
257
-
258
- function buildCSS(components, utilities) {
259
- const imports = [
260
- `@import "./base/tokens.css" layer(base);`,
261
- `@import "./base/reset.css" layer(base);`,
262
- `@import "./base/neo-overrides.css" layer(base);`,
263
- ];
264
-
265
- for (const c of components) {
266
- imports.push(
267
- `@import "./components/${COMPONENT_FILES[c] || c}.css" layer(components);`,
268
- );
269
- }
270
- for (const u of utilities) {
271
- imports.push(`@import "./utilities/${u}.css" layer(utilities);`);
272
- }
273
-
274
- return `@layer base, components, utilities;\n\n${imports.join("\n")}\n`;
275
- }
276
-
277
- // ── Main ───────────────────────────────────────────────────────────────────
278
- const orchestrator = path.join(__dirname, "..", "src", "arekui.css");
279
- const tmpFile = path.join(path.dirname(orchestrator), "__build.css");
280
-
281
- try {
282
- let components, utilities;
283
-
284
- if (full) {
285
- components = Object.keys(COMPONENT_FILES);
286
- utilities = Object.keys(UTILITY_PATTERNS);
287
- console.log(`\x1b[36m📦\x1b[0m Including all components`);
288
- } else {
289
- console.log(`\x1b[36m⏳\x1b[0m Scanning ${srcDir}...`);
290
- const classes = scan(srcDir);
291
- components = detectComponents(classes);
292
- utilities = detectUtilities(classes);
293
- console.log(` Found ${classes.size} ar-* tokens`);
294
- console.log(` Components: ${components.join(", ") || "(none)"}`);
295
- console.log(` Utilities: ${utilities.join(", ") || "(none)"}`);
296
- }
297
-
298
- fs.writeFileSync(tmpFile, buildCSS(components, utilities), "utf-8");
299
- fs.mkdirSync(outDir, { recursive: true });
300
-
301
- const result = bundle({
302
- filename: tmpFile,
303
- minify: true,
304
- sourceMap: sourcemap,
305
- });
306
-
307
- const outFile = path.join(outDir, `${name}.min.css`);
308
- fs.writeFileSync(outFile, result.code);
309
- if (sourcemap && result.map) fs.writeFileSync(outFile + ".map", result.map);
310
-
311
- console.log(`\n\x1b[32m✓\x1b[0m Built → ${outFile}`);
312
- console.log(
313
- ` ${components.length} components, ${utilities.length} utilities`,
314
- );
315
- console.log(` ${(result.code.length / 1024).toFixed(1)}KB`);
316
- } finally {
317
- if (fs.existsSync(tmpFile)) fs.unlinkSync(tmpFile);
318
- }
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { bundle } = require("lightningcss");
6
+ const { BASE_IMPORTS } = require("./imports");
7
+
8
+ function loadConfig() {
9
+ const candidates = ["arekui.config.js", "arekui.config.json"];
10
+ for (const file of candidates) {
11
+ const full = path.resolve(file);
12
+ if (fs.existsSync(full)) {
13
+ try {
14
+ console.log(`\x1b[90m config: ${file}\x1b[0m`);
15
+ return file.endsWith(".json")
16
+ ? JSON.parse(fs.readFileSync(full, "utf-8"))
17
+ : require(full);
18
+ } catch (e) {
19
+ console.error(`\x1b[31m✗\x1b[0m Failed to load ${file}: ${e.message}`);
20
+ process.exit(1);
21
+ }
22
+ }
23
+ }
24
+ return {};
25
+ }
26
+
27
+ function getArg(flags) {
28
+ const idx = process.argv.findIndex((a) => flags.includes(a));
29
+ return idx === -1 ? null : process.argv[idx + 1];
30
+ }
31
+
32
+ const config = loadConfig();
33
+
34
+ const input = getArg(["-i", "--input"]) || config.input || ".";
35
+ const output = getArg(["-o", "--output"]) || config.output || "assets";
36
+ const name = getArg(["-n", "--name"]) || config.name || "arekui";
37
+ const sourcemap =
38
+ process.argv.includes("--sourcemap") || config.sourcemap === true;
39
+ const full = process.argv.includes("--full") || config.full === true;
40
+
41
+ const srcDir = path.resolve(input);
42
+ const outDir = path.resolve(output);
43
+
44
+ const COMPONENT_CLASSES = {
45
+ accordion: ["ar-accordion", "ar-collapse"],
46
+ alert: ["ar-alert"],
47
+ badge: ["ar-badge"],
48
+ breadcrumb: ["ar-breadcrumb"],
49
+ broadsheet: ["ar-rule-double", "ar-masthead", "ar-folio", "ar-nameplate", "ar-banner", "ar-sheet-cols", "ar-sheet-col", "ar-plate"],
50
+ button: ["ar-btn"],
51
+ card: ["ar-card", "ar-card-body", "ar-card-title", "ar-card-figure"],
52
+ carousel: ["ar-carousel", "ar-carousel-item", "ar-carousel-frame", "ar-carousel-btn", "ar-carousel-btn-prev", "ar-carousel-btn-next"],
53
+ chat: ["ar-chat", "ar-chat-bubble"],
54
+ "description-list": ["ar-dl", "ar-dt", "ar-dd"],
55
+ sidebar: ["ar-sidebar"],
56
+ "file-upload": ["ar-file-upload"],
57
+ footer: ["ar-footer"],
58
+ forms: [
59
+ "ar-input",
60
+ "ar-textarea",
61
+ "ar-select",
62
+ "ar-checkbox",
63
+ "ar-radio",
64
+ "ar-toggle",
65
+ "ar-range",
66
+ "ar-label",
67
+ "ar-fieldset",
68
+ ],
69
+ heading: [
70
+ "ar-heading",
71
+ "ar-heading-eyebrow",
72
+ "ar-heading-title",
73
+ "ar-heading-desc",
74
+ "ar-heading-center",
75
+ ],
76
+ hero: ["ar-hero"],
77
+ join: ["ar-join"],
78
+ docs: ["ar-docs"],
79
+ empty: ["ar-empty", "ar-empty-icon", "ar-empty-title", "ar-empty-desc", "ar-empty-action"],
80
+ kbd: ["ar-kbd"],
81
+ link: ["ar-link"],
82
+ loading: ["ar-loading"],
83
+ menu: ["ar-menu", "ar-dropdown", "ar-theme-menu"],
84
+ misc: ["ar-divider", "ar-stepper", "ar-step", "ar-avatar", "ar-avatar-group", "ar-avatar-more"],
85
+ modal: ["ar-modal"],
86
+ navbar: ["ar-navbar", "ar-search"],
87
+ pagination: ["ar-pagination"],
88
+ popover: ["ar-popover"],
89
+ progress: ["ar-progress"],
90
+ prose: ["ar-prose"],
91
+ rating: ["ar-rating"],
92
+ "scroll-top": ["ar-scroll-top"],
93
+ segmented: ["ar-segmented", "ar-segmented-input", "ar-segmented-item"],
94
+ skeleton: ["ar-skeleton"],
95
+ spinner: ["ar-spinner"],
96
+ swap: ["ar-swap"],
97
+ table: ["ar-table"],
98
+ tabs: ["ar-tabs", "ar-tab"],
99
+ tag: ["ar-tag"],
100
+ textured: ["ar-bg-img"],
101
+ timeline: ["ar-timeline"],
102
+ toast: ["ar-toast"],
103
+ tooltip: ["ar-tooltip"],
104
+ };
105
+
106
+ const UTILITY_PATTERNS = {
107
+ spacing: [
108
+ /^ar-p/,
109
+ /^ar-px/,
110
+ /^ar-py/,
111
+ /^ar-m/,
112
+ /^ar-mt/,
113
+ /^ar-mb/,
114
+ /^ar-my/,
115
+ /^ar-mx/,
116
+ /^ar-gap/,
117
+ ],
118
+ layout: [
119
+ /^ar-flex/,
120
+ /^ar-inline-flex/,
121
+ /^ar-flex-col/,
122
+ /^ar-flex-wrap/,
123
+ /^ar-items-/,
124
+ /^ar-justify-/,
125
+ /^ar-grid/,
126
+ /^ar-block/,
127
+ /^ar-hidden/,
128
+ ],
129
+ typography: [/^ar-text-/, /^ar-font-/, /^ar-leading-/, /^ar-tracking-/],
130
+ sizing: [
131
+ /^ar-w-/,
132
+ /^ar-h-/,
133
+ /^ar-min-h-/,
134
+ /^ar-max-w-/,
135
+ /^ar-aspect-video/,
136
+ /^ar-object-cover/,
137
+ ],
138
+ colors: [
139
+ /^ar-bg-base/,
140
+ /^ar-bg-neutral/,
141
+ /^ar-bg-primary/,
142
+ /^ar-bg-secondary/,
143
+ /^ar-bg-accent/,
144
+ /^ar-text-base/,
145
+ /^ar-text-gray/,
146
+ ],
147
+ borders: [/^ar-border/, /^ar-rounded/, /^ar-shadow-/],
148
+ hover: [/^ar-hover-/],
149
+ responsive: [/^ar-md:/, /^ar-lg:/, /^ar-hide/],
150
+ "neo-utilities": [/^ar-neo-/],
151
+ "pixel-utilities": [/^ar-pixel-/],
152
+ "brutalist-utilities": [/^ar-brutalist-/],
153
+ };
154
+
155
+ function findSourceFiles(dir) {
156
+ const exts = [".html", ".jsx", ".vue", ".svelte", ".tsx", ".ts", ".js"];
157
+
158
+ // A consumer may point -i at a single file rather than a directory.
159
+ let stat;
160
+ try {
161
+ stat = fs.statSync(dir);
162
+ } catch {
163
+ console.error(`\x1b[31m✗\x1b[0m scan input not found: ${dir}`);
164
+ process.exit(1);
165
+ }
166
+ if (stat.isFile()) {
167
+ const ext = path.extname(dir).toLowerCase();
168
+ if (!exts.includes(ext)) {
169
+ console.error(
170
+ `\x1b[31m✗\x1b[0m ${dir} is not a scannable source - expected one of (${exts.join(" ")}) or a directory`,
171
+ );
172
+ process.exit(1);
173
+ }
174
+ return [dir];
175
+ }
176
+
177
+ const files = [];
178
+
179
+ function walk(d) {
180
+ for (const entry of fs.readdirSync(d, { withFileTypes: true })) {
181
+ const full = path.join(d, entry.name);
182
+ if (
183
+ entry.isDirectory() &&
184
+ !entry.name.startsWith(".") &&
185
+ entry.name !== "node_modules"
186
+ ) {
187
+ walk(full);
188
+ } else if (entry.isFile() && exts.includes(path.extname(entry.name))) {
189
+ files.push(full);
190
+ }
191
+ }
192
+ }
193
+ walk(dir);
194
+ return files;
195
+ }
196
+
197
+ function scan(dir) {
198
+ const files = findSourceFiles(dir);
199
+ const content = files.map((f) => fs.readFileSync(f, "utf-8")).join("\n");
200
+ const tokens = new Set();
201
+ let m;
202
+ const re = /ar-[\w-]+/g;
203
+ while ((m = re.exec(content)) !== null) tokens.add(m[0]);
204
+ return tokens;
205
+ }
206
+
207
+ // Both detectors answer the same question — which named groups have at least
208
+ // one matcher satisfied by a scanned token — so the double loop lives once.
209
+ function detect(groups, classes, isMatch) {
210
+ const found = [];
211
+ for (const [name, matchers] of Object.entries(groups)) {
212
+ for (const cls of classes) {
213
+ if (matchers.some((m) => isMatch(m, cls))) {
214
+ found.push(name);
215
+ break;
216
+ }
217
+ }
218
+ }
219
+ return found;
220
+ }
221
+
222
+ function detectComponents(classes) {
223
+ return detect(
224
+ COMPONENT_CLASSES,
225
+ classes,
226
+ (pattern, cls) => cls === pattern || cls.startsWith(pattern + "-"),
227
+ );
228
+ }
229
+
230
+ function detectUtilities(classes) {
231
+ return detect(UTILITY_PATTERNS, classes, (pattern, cls) => pattern.test(cls));
232
+ }
233
+
234
+ function buildCSS(components, utilities) {
235
+ const imports = [...BASE_IMPORTS];
236
+
237
+ for (const c of components) {
238
+ imports.push(`@import "./components/${c}.css" layer(components);`);
239
+ }
240
+ for (const u of utilities) {
241
+ imports.push(`@import "./utilities/${u}.css" layer(utilities);`);
242
+ }
243
+
244
+ return `@layer base, components, themes, utilities;\n\n${imports.join("\n")}\n`;
245
+ }
246
+
247
+ const orchestrator = path.join(__dirname, "..", "src", "arekui.css");
248
+ const tmpFile = path.join(path.dirname(orchestrator), "__build.css");
249
+
250
+ try {
251
+ let components, utilities;
252
+
253
+ if (full) {
254
+ components = Object.keys(COMPONENT_CLASSES);
255
+ utilities = Object.keys(UTILITY_PATTERNS);
256
+ console.log(`\x1b[36m📦\x1b[0m Including all components`);
257
+ } else {
258
+ console.log(`\x1b[36m⏳\x1b[0m Scanning ${srcDir}...`);
259
+ const classes = scan(srcDir);
260
+ components = detectComponents(classes);
261
+ utilities = detectUtilities(classes);
262
+ console.log(` Found ${classes.size} ar-* tokens`);
263
+ console.log(` Components: ${components.join(", ") || "(none)"}`);
264
+ console.log(` Utilities: ${utilities.join(", ") || "(none)"}`);
265
+ }
266
+
267
+ fs.writeFileSync(tmpFile, buildCSS(components, utilities), "utf-8");
268
+ fs.mkdirSync(outDir, { recursive: true });
269
+
270
+ const result = bundle({
271
+ filename: tmpFile,
272
+ minify: true,
273
+ sourceMap: sourcemap,
274
+ });
275
+
276
+ const outFile = path.join(outDir, `${name}.min.css`);
277
+ fs.writeFileSync(outFile, result.code);
278
+ if (sourcemap && result.map) fs.writeFileSync(outFile + ".map", result.map);
279
+
280
+ console.log(`\n\x1b[32m✓\x1b[0m Built → ${outFile}`);
281
+ console.log(
282
+ ` ${components.length} components, ${utilities.length} utilities`,
283
+ );
284
+ console.log(` ${(result.code.length / 1024).toFixed(1)}KB`);
285
+ } finally {
286
+ if (fs.existsSync(tmpFile)) fs.unlinkSync(tmpFile);
287
+ }