@dsai-io/tools 0.0.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.
@@ -0,0 +1,1039 @@
1
+ import { z } from 'zod';
2
+ import path2 from 'path';
3
+ import { cosmiconfig, cosmiconfigSync } from 'cosmiconfig';
4
+
5
+ /* @dsai-io/tools - DSAi Design System Build Tools */
6
+
7
+ var logLevelSchema = z.enum(["silent", "error", "warn", "info", "debug", "verbose"]);
8
+ var outputFormatSchema = z.enum(["css", "scss", "less", "json", "js", "ts", "esm", "cjs"]);
9
+ var frameworkSchema = z.enum(["react", "vue", "angular", "svelte", "vanilla"]);
10
+ var hashTypeSchema = z.enum(["content", "timestamp", "version", "none"]);
11
+ var globPatternSchema = z.string().min(1, "Glob pattern cannot be empty");
12
+ var filePathSchema = z.string().min(1, "File path cannot be empty");
13
+ var versionSchema = z.string().refine(
14
+ (val) => {
15
+ const parts = val.split("-");
16
+ const version = parts[0] ?? "";
17
+ const preRelease = parts[1];
18
+ const versionParts = version.split(".");
19
+ if (versionParts.length !== 3) {
20
+ return false;
21
+ }
22
+ for (const part of versionParts) {
23
+ const num = Number(part);
24
+ if (!Number.isInteger(num) || num < 0) {
25
+ return false;
26
+ }
27
+ }
28
+ if (preRelease !== void 0 && preRelease.length === 0) {
29
+ return false;
30
+ }
31
+ return true;
32
+ },
33
+ { message: "Invalid semantic version format" }
34
+ );
35
+ var customTransformSchema = z.object({
36
+ name: z.string().min(1, "Transform name is required"),
37
+ description: z.string().optional(),
38
+ type: z.enum(["value", "attribute", "name"]).optional().default("value"),
39
+ transform: z.function().args(z.any(), z.any()).returns(z.any()).optional(),
40
+ filter: z.function().args(z.any()).returns(z.boolean()).optional(),
41
+ matcher: z.function().args(z.any()).returns(z.boolean()).optional()
42
+ });
43
+ var customFormatSchema = z.object({
44
+ name: z.string().min(1, "Format name is required"),
45
+ description: z.string().optional(),
46
+ formatter: z.function().args(z.any()).returns(z.string()).optional(),
47
+ extension: z.string().min(1).optional()
48
+ });
49
+ var themeModeSchema = z.object({
50
+ selector: z.string().min(1, "Theme selector is required"),
51
+ mediaQuery: z.string().optional(),
52
+ dataAttribute: z.string().optional(),
53
+ cssVariables: z.boolean().optional().default(true),
54
+ generateSeparateFiles: z.boolean().optional().default(false),
55
+ prefix: z.string().optional()
56
+ });
57
+ var themesConfigSchema = z.object({
58
+ enabled: z.boolean().optional().default(true),
59
+ defaultMode: z.enum(["light", "dark", "system"]).optional().default("light"),
60
+ modes: z.record(z.string(), themeModeSchema).optional().default({
61
+ light: { selector: ":root", cssVariables: true, generateSeparateFiles: false },
62
+ dark: {
63
+ selector: '[data-theme="dark"]',
64
+ mediaQuery: "(prefers-color-scheme: dark)",
65
+ cssVariables: true,
66
+ generateSeparateFiles: false
67
+ }
68
+ }),
69
+ outputFileName: z.string().optional().default("themes"),
70
+ colorScheme: z.object({
71
+ light: z.string().optional(),
72
+ dark: z.string().optional()
73
+ }).optional()
74
+ });
75
+ var iconOptimizationSchema = z.object({
76
+ enabled: z.boolean().optional().default(true),
77
+ removeComments: z.boolean().optional().default(true),
78
+ removeDimensions: z.boolean().optional().default(false),
79
+ removeViewBox: z.boolean().optional().default(false),
80
+ removeXMLNS: z.boolean().optional().default(true),
81
+ cleanupIds: z.boolean().optional().default(true),
82
+ minify: z.boolean().optional().default(true)
83
+ });
84
+ var iconSpriteSchema = z.object({
85
+ enabled: z.boolean().optional().default(true),
86
+ fileName: z.string().optional().default("icons"),
87
+ format: z.enum(["symbol", "stack", "css"]).optional().default("symbol"),
88
+ prefix: z.string().optional().default("icon-")
89
+ });
90
+ var iconsConfigSchema = z.object({
91
+ enabled: z.boolean().optional().default(true),
92
+ sourceDir: z.string().optional().default("assets/icons"),
93
+ outputDir: z.string().optional().default("dist/icons"),
94
+ formats: z.array(z.enum(["svg", "react", "vue", "sprite", "font"])).optional().default(["svg"]),
95
+ optimization: iconOptimizationSchema.optional(),
96
+ sprite: iconSpriteSchema.optional(),
97
+ componentPrefix: z.string().optional().default("Icon"),
98
+ componentSuffix: z.string().optional().default(""),
99
+ generateIndex: z.boolean().optional().default(true),
100
+ generateTypes: z.boolean().optional().default(true)
101
+ });
102
+ var tokenBuildConfigSchema = z.object({
103
+ format: outputFormatSchema,
104
+ outputDir: z.string().optional(),
105
+ outputFileName: z.string().optional(),
106
+ fileExtension: z.string().optional(),
107
+ prefix: z.string().optional(),
108
+ useVariables: z.boolean().optional(),
109
+ selector: z.string().optional(),
110
+ transforms: z.array(z.string()).optional(),
111
+ customTransforms: z.array(customTransformSchema).optional(),
112
+ filter: z.function().args(z.any()).returns(z.union([z.boolean(), z.promise(z.boolean())])).optional(),
113
+ header: z.string().optional(),
114
+ footer: z.string().optional()
115
+ });
116
+ var tokenCacheConfigSchema = z.object({
117
+ enabled: z.boolean().optional().default(true),
118
+ directory: z.string().optional().default(".cache"),
119
+ hashType: hashTypeSchema.optional().default("content"),
120
+ maxAge: z.number().optional().default(864e5)
121
+ });
122
+ var tokenWatchConfigSchema = z.object({
123
+ enabled: z.boolean().optional().default(false),
124
+ debounce: z.number().optional().default(300),
125
+ clearScreen: z.boolean().optional().default(true),
126
+ ignorePatterns: z.array(z.string()).optional().default([])
127
+ });
128
+ var tokensHooksSchema = z.object({
129
+ onBuildStart: z.function().args(z.any()).returns(z.union([z.void(), z.promise(z.void())])).optional(),
130
+ onFormatComplete: z.function().args(z.any()).returns(z.union([z.void(), z.promise(z.void())])).optional(),
131
+ onAllFormatsComplete: z.function().args(z.any()).returns(z.union([z.void(), z.promise(z.void())])).optional(),
132
+ onBuildComplete: z.function().args(z.any()).returns(z.union([z.void(), z.promise(z.void())])).optional(),
133
+ onError: z.function().args(z.any()).returns(z.union([z.void(), z.promise(z.void())])).optional()
134
+ });
135
+ var buildPipelineStepSchema = z.enum([
136
+ "validate",
137
+ "transform",
138
+ "style-dictionary",
139
+ "sync",
140
+ "sass-theme",
141
+ "sass-theme-minified",
142
+ "postprocess",
143
+ "sass-utilities",
144
+ "sass-utilities-minified",
145
+ "bundle"
146
+ ]);
147
+ var tokensBuildPipelineSchema = z.object({
148
+ /**
149
+ * Steps to include in the build.
150
+ * Order matters - steps run in sequence.
151
+ * Default includes all steps for full @dsai-io/tokens build.
152
+ * Simpler packages can use subset like ['validate', 'transform', 'style-dictionary']
153
+ */
154
+ steps: z.array(buildPipelineStepSchema).optional().default([
155
+ "validate",
156
+ "transform",
157
+ "style-dictionary",
158
+ "sync",
159
+ "sass-theme",
160
+ "sass-theme-minified",
161
+ "postprocess",
162
+ "sass-utilities",
163
+ "sass-utilities-minified",
164
+ "bundle"
165
+ ]),
166
+ /**
167
+ * Paths configuration for build steps
168
+ */
169
+ paths: z.object({
170
+ /** Source file for sync step (Style Dictionary JS output) */
171
+ syncSource: z.string().optional().default("dist/js/tokens.js"),
172
+ /** Target file for sync step */
173
+ syncTarget: z.string().optional().default("src/tokens-flat.ts"),
174
+ /** SCSS theme input file */
175
+ sassThemeInput: z.string().optional().default("src/scss/dsai-theme-bs.scss"),
176
+ /** CSS theme output file */
177
+ sassThemeOutput: z.string().optional().default("dist/css/dsai-theme-bs.css"),
178
+ /** CSS theme minified output file */
179
+ sassThemeMinifiedOutput: z.string().optional().default("dist/css/dsai-theme-bs.min.css"),
180
+ /** SCSS utilities input file */
181
+ sassUtilitiesInput: z.string().optional().default("src/scss/dsai-utilities.scss"),
182
+ /** CSS utilities output file */
183
+ sassUtilitiesOutput: z.string().optional().default("dist/css/dsai.css"),
184
+ /** CSS utilities minified output file */
185
+ sassUtilitiesMinifiedOutput: z.string().optional().default("dist/css/dsai.min.css")
186
+ }).optional(),
187
+ /** Style Dictionary config file name */
188
+ styleDictionaryConfig: z.string().optional().default("sd.config.mjs")
189
+ });
190
+ var tokensConfigSchema = z.object({
191
+ enabled: z.boolean().optional().default(true),
192
+ sourcePatterns: z.array(z.string()).optional().default(["src/tokens/**/*.json", "src/tokens/**/*.yaml"]),
193
+ outputDirs: z.object({
194
+ css: z.string().optional().default("dist/css"),
195
+ scss: z.string().optional().default("dist/scss"),
196
+ less: z.string().optional().default("dist/less"),
197
+ js: z.string().optional().default("dist/js"),
198
+ ts: z.string().optional().default("dist/ts"),
199
+ json: z.string().optional().default("dist/json")
200
+ }).optional(),
201
+ additionalScssDirectories: z.array(z.string()).optional().default([]),
202
+ outputFileNames: z.object({
203
+ variables: z.string().optional().default("variables"),
204
+ utilities: z.string().optional().default("utilities"),
205
+ mixins: z.string().optional().default("mixins"),
206
+ tokens: z.string().optional().default("tokens")
207
+ }).optional(),
208
+ prefix: z.string().optional().default("dsai"),
209
+ mergeOrder: z.array(z.string()).optional().default(["base", "semantic", "component"]),
210
+ createBundle: z.boolean().optional().default(true),
211
+ bundleFileName: z.string().optional().default("bundle"),
212
+ formats: z.array(outputFormatSchema).optional().default(["css", "scss", "json"]),
213
+ platforms: z.record(z.string(), tokenBuildConfigSchema).optional(),
214
+ transforms: z.array(z.string()).optional().default([]),
215
+ customTransforms: z.array(customTransformSchema).optional().default([]),
216
+ customFormats: z.array(customFormatSchema).optional().default([]),
217
+ hooks: tokensHooksSchema.optional(),
218
+ cache: tokenCacheConfigSchema.optional(),
219
+ watch: tokenWatchConfigSchema.optional(),
220
+ verbose: z.boolean().optional().default(false),
221
+ /** Build pipeline configuration */
222
+ pipeline: tokensBuildPipelineSchema.optional()
223
+ });
224
+ var buildConfigSchema = z.object({
225
+ outDir: z.string().optional().default("dist"),
226
+ clean: z.boolean().optional().default(true),
227
+ sourcemap: z.boolean().optional().default(false),
228
+ minify: z.boolean().optional().default(true),
229
+ parallel: z.boolean().optional().default(true),
230
+ maxConcurrency: z.number().optional().default(4)
231
+ });
232
+ var globalConfigSchema = z.object({
233
+ logLevel: logLevelSchema.optional().default("info"),
234
+ colors: z.boolean().optional().default(true),
235
+ ci: z.boolean().optional().default(false),
236
+ dryRun: z.boolean().optional().default(false),
237
+ cwd: z.string().optional(),
238
+ configPath: z.string().optional(),
239
+ framework: frameworkSchema.optional(),
240
+ build: buildConfigSchema.optional()
241
+ });
242
+ var dsaiConfigSchema = z.object({
243
+ $schema: z.string().optional(),
244
+ extends: z.union([z.string(), z.array(z.string())]).optional(),
245
+ global: globalConfigSchema.optional(),
246
+ tokens: tokensConfigSchema.optional(),
247
+ themes: themesConfigSchema.optional(),
248
+ icons: iconsConfigSchema.optional()
249
+ });
250
+ function formatValidationErrors(zodError) {
251
+ return zodError.errors.map((err) => ({
252
+ path: err.path.join(".") || "root",
253
+ message: err.message,
254
+ code: err.code,
255
+ expected: "expected" in err ? String(err.expected) : void 0,
256
+ received: "received" in err ? String(err.received) : void 0
257
+ }));
258
+ }
259
+ function validateConfig(config) {
260
+ const result = dsaiConfigSchema.safeParse(config);
261
+ if (result.success) {
262
+ return {
263
+ success: true,
264
+ data: result.data
265
+ };
266
+ }
267
+ return {
268
+ success: false,
269
+ errors: formatValidationErrors(result.error)
270
+ };
271
+ }
272
+ function validateConfigOrThrow(config) {
273
+ const result = validateConfig(config);
274
+ if (!result.success) {
275
+ const errorMessages = result.errors?.map((e) => ` - ${e.path}: ${e.message}`).join("\n");
276
+ throw new Error(`Configuration validation failed:
277
+ ${errorMessages}`);
278
+ }
279
+ return result.data;
280
+ }
281
+ function validateConfigSection(section, config) {
282
+ const sectionSchemas = {
283
+ global: globalConfigSchema,
284
+ tokens: tokensConfigSchema,
285
+ themes: themesConfigSchema,
286
+ icons: iconsConfigSchema
287
+ };
288
+ const schema = sectionSchemas[section];
289
+ if (!schema) {
290
+ return {
291
+ success: false,
292
+ errors: [
293
+ {
294
+ path: section,
295
+ message: `Unknown configuration section: ${section}`,
296
+ code: "unknown_section"
297
+ }
298
+ ]
299
+ };
300
+ }
301
+ const result = schema.safeParse(config);
302
+ if (result.success) {
303
+ return {
304
+ success: true,
305
+ data: result.data
306
+ };
307
+ }
308
+ return {
309
+ success: false,
310
+ errors: formatValidationErrors(result.error)
311
+ };
312
+ }
313
+ function formatErrorMessage(errors) {
314
+ const lines = ["Configuration validation failed:", ""];
315
+ for (const error of errors) {
316
+ lines.push(` \u2717 ${error.path}`);
317
+ lines.push(` ${error.message}`);
318
+ if (error.expected && error.received) {
319
+ lines.push(` Expected: ${error.expected}`);
320
+ lines.push(` Received: ${error.received}`);
321
+ }
322
+ lines.push("");
323
+ }
324
+ return lines.join("\n");
325
+ }
326
+
327
+ // src/config/defaults.ts
328
+ var DEFAULT_PREFIX = "--dsai-";
329
+ var DEFAULT_LOG_LEVEL = "info";
330
+ var DEFAULT_OUTPUT_DIR = "dist";
331
+ var DEFAULT_SOURCE_DIR = "figma-exports";
332
+ var DEFAULT_COLLECTIONS_DIR = "collections";
333
+ var DEFAULT_ICONS_SOURCE_DIR = "icons";
334
+ var DEFAULT_ICONS_OUTPUT_DIR = "dist/icons";
335
+ var defaultSourcePatterns = ["theme.json", "tokens.json", "*.tokens.json"];
336
+ var defaultFormats = ["css", "scss", "js", "ts", "json"];
337
+ var defaultOutputFileNames = {
338
+ css: "tokens.css",
339
+ scss: "_tokens.scss",
340
+ js: "tokens.js",
341
+ ts: "tokens.ts",
342
+ json: "tokens.json",
343
+ android: "tokens.xml",
344
+ ios: "tokens.h"
345
+ };
346
+ var defaultSelectorPattern = {
347
+ default: ":root",
348
+ others: '[data-dsai-theme="{mode}"]'
349
+ };
350
+ var defaultThemesConfig = {
351
+ autoDetect: true,
352
+ default: "Light",
353
+ ignoreModes: [],
354
+ selectorPattern: defaultSelectorPattern
355
+ };
356
+ var defaultIconFramework = "react";
357
+ var defaultIconsConfig = {
358
+ sourceDir: DEFAULT_ICONS_SOURCE_DIR,
359
+ outputDir: DEFAULT_ICONS_OUTPUT_DIR,
360
+ framework: defaultIconFramework,
361
+ typescript: true,
362
+ optimize: true,
363
+ prefix: "Icon"
364
+ };
365
+ var defaultTokensConfig = {
366
+ source: "theme",
367
+ sourceDir: DEFAULT_SOURCE_DIR,
368
+ collectionsDir: DEFAULT_COLLECTIONS_DIR,
369
+ sourcePatterns: defaultSourcePatterns,
370
+ collectionMapping: {},
371
+ outputDir: DEFAULT_OUTPUT_DIR,
372
+ outputDirs: {},
373
+ outputFileNames: defaultOutputFileNames,
374
+ prefix: DEFAULT_PREFIX,
375
+ formats: defaultFormats,
376
+ additionalScssDirectories: [],
377
+ additionalCssDirectories: [],
378
+ mergeOrder: "after",
379
+ createBundle: false,
380
+ themes: defaultThemesConfig,
381
+ transforms: [],
382
+ customFormats: [],
383
+ preprocessors: [],
384
+ filters: [],
385
+ outputReferences: true,
386
+ baseFontSize: 16,
387
+ separateThemeFiles: false,
388
+ watch: false,
389
+ watchDirectories: []
390
+ };
391
+ var defaultGlobalConfig = {
392
+ cwd: process.cwd(),
393
+ debug: false,
394
+ logLevel: DEFAULT_LOG_LEVEL
395
+ };
396
+ var defaultConfig = {
397
+ tokens: defaultTokensConfig,
398
+ icons: defaultIconsConfig,
399
+ global: defaultGlobalConfig,
400
+ configDir: process.cwd()
401
+ };
402
+ var envMappings = {
403
+ // Global settings
404
+ DSAI_LOG_LEVEL: "global.logLevel",
405
+ DSAI_DEBUG: "global.debug",
406
+ DSAI_CWD: "global.cwd",
407
+ // Token settings
408
+ DSAI_PREFIX: "tokens.prefix",
409
+ DSAI_SOURCE_DIR: "tokens.sourceDir",
410
+ DSAI_OUTPUT_DIR: "tokens.outputDir",
411
+ DSAI_WATCH: "tokens.watch",
412
+ DSAI_CREATE_BUNDLE: "tokens.createBundle",
413
+ DSAI_BASE_FONT_SIZE: "tokens.baseFontSize",
414
+ // Theme settings
415
+ DSAI_DEFAULT_THEME: "tokens.themes.default",
416
+ DSAI_AUTO_DETECT_THEMES: "tokens.themes.autoDetect",
417
+ // Icon settings
418
+ DSAI_ICONS_SOURCE_DIR: "icons.sourceDir",
419
+ DSAI_ICONS_OUTPUT_DIR: "icons.outputDir",
420
+ DSAI_ICONS_FRAMEWORK: "icons.framework",
421
+ DSAI_ICONS_TYPESCRIPT: "icons.typescript",
422
+ DSAI_ICONS_OPTIMIZE: "icons.optimize",
423
+ DSAI_ICONS_PREFIX: "icons.prefix"
424
+ };
425
+ var envBooleanKeys = /* @__PURE__ */ new Set([
426
+ "DSAI_DEBUG",
427
+ "DSAI_WATCH",
428
+ "DSAI_CREATE_BUNDLE",
429
+ "DSAI_AUTO_DETECT_THEMES",
430
+ "DSAI_ICONS_TYPESCRIPT",
431
+ "DSAI_ICONS_OPTIMIZE"
432
+ ]);
433
+ var envNumberKeys = /* @__PURE__ */ new Set(["DSAI_BASE_FONT_SIZE"]);
434
+ var envArrayKeys = /* @__PURE__ */ new Set(["DSAI_FORMATS", "DSAI_IGNORE_MODES"]);
435
+ function getOutputFileName(format, theme) {
436
+ let baseName;
437
+ switch (format) {
438
+ case "css":
439
+ baseName = "tokens.css";
440
+ break;
441
+ case "scss":
442
+ baseName = "_tokens.scss";
443
+ break;
444
+ case "js":
445
+ baseName = "tokens.js";
446
+ break;
447
+ case "ts":
448
+ baseName = "tokens.ts";
449
+ break;
450
+ case "json":
451
+ baseName = "tokens.json";
452
+ break;
453
+ case "android":
454
+ baseName = "tokens.xml";
455
+ break;
456
+ case "ios":
457
+ baseName = "tokens.h";
458
+ break;
459
+ default:
460
+ baseName = "tokens.txt";
461
+ }
462
+ if (!theme) {
463
+ return baseName;
464
+ }
465
+ const dotIndex = baseName.lastIndexOf(".");
466
+ if (dotIndex === -1) {
467
+ return `${baseName}-${theme}`;
468
+ }
469
+ const name = baseName.slice(0, dotIndex);
470
+ const ext = baseName.slice(dotIndex);
471
+ return `${name}-${theme}${ext}`;
472
+ }
473
+ function getDefaultOutputDir(format) {
474
+ switch (format) {
475
+ case "css":
476
+ return `${DEFAULT_OUTPUT_DIR}/css`;
477
+ case "scss":
478
+ return `${DEFAULT_OUTPUT_DIR}/scss`;
479
+ case "js":
480
+ case "ts":
481
+ return `${DEFAULT_OUTPUT_DIR}/js`;
482
+ case "json":
483
+ return `${DEFAULT_OUTPUT_DIR}/json`;
484
+ case "android":
485
+ return `${DEFAULT_OUTPUT_DIR}/android`;
486
+ case "ios":
487
+ return `${DEFAULT_OUTPUT_DIR}/ios`;
488
+ default:
489
+ return DEFAULT_OUTPUT_DIR;
490
+ }
491
+ }
492
+ function getDefaultExtension(format) {
493
+ switch (format) {
494
+ case "css":
495
+ return ".css";
496
+ case "scss":
497
+ return ".scss";
498
+ case "js":
499
+ return ".js";
500
+ case "ts":
501
+ return ".ts";
502
+ case "json":
503
+ return ".json";
504
+ case "android":
505
+ return ".xml";
506
+ case "ios":
507
+ return ".h";
508
+ default:
509
+ return ".txt";
510
+ }
511
+ }
512
+ function resolveGlobalConfig(config, options) {
513
+ const base = defaultGlobalConfig;
514
+ const cwd = options.cwd ?? process.cwd();
515
+ return {
516
+ cwd: config?.cwd ? path2.resolve(cwd, config.cwd) : cwd,
517
+ debug: config?.debug ?? base.debug,
518
+ logLevel: config?.logLevel ?? base.logLevel
519
+ };
520
+ }
521
+ function resolveThemesConfig(config) {
522
+ const base = defaultThemesConfig;
523
+ return {
524
+ autoDetect: config?.autoDetect ?? base.autoDetect,
525
+ default: config?.default ?? base.default,
526
+ ignoreModes: config?.ignoreModes ?? [...base.ignoreModes],
527
+ selectorPattern: {
528
+ default: config?.selectorPattern?.default ?? base.selectorPattern.default,
529
+ others: config?.selectorPattern?.others ?? base.selectorPattern.others
530
+ }
531
+ };
532
+ }
533
+ function resolveIconsConfig(config, options) {
534
+ const base = defaultIconsConfig;
535
+ const configDir = options.configDir ?? options.cwd ?? process.cwd();
536
+ return {
537
+ sourceDir: config?.sourceDir ? path2.resolve(configDir, config.sourceDir) : base.sourceDir,
538
+ outputDir: config?.outputDir ? path2.resolve(configDir, config.outputDir) : base.outputDir,
539
+ framework: config?.framework ?? base.framework,
540
+ typescript: config?.typescript ?? base.typescript,
541
+ optimize: config?.optimize ?? base.optimize,
542
+ prefix: config?.prefix ?? base.prefix
543
+ };
544
+ }
545
+ function resolveTokensConfig(config, options) {
546
+ const base = defaultTokensConfig;
547
+ const configDir = options.configDir ?? options.cwd ?? process.cwd();
548
+ const resolveDir = (dir) => path2.resolve(configDir, dir);
549
+ const outputDirsMap = new Map(Object.entries(base.outputDirs));
550
+ if (config?.outputDirs) {
551
+ for (const [format, dir] of Object.entries(config.outputDirs)) {
552
+ if (dir !== void 0) {
553
+ outputDirsMap.set(format, resolveDir(dir));
554
+ }
555
+ }
556
+ }
557
+ const outputDirs = Object.fromEntries(outputDirsMap);
558
+ const collectionMappingMap = /* @__PURE__ */ new Map();
559
+ if (config?.collectionMapping) {
560
+ for (const [name, filePath] of Object.entries(config.collectionMapping)) {
561
+ collectionMappingMap.set(name, resolveDir(filePath));
562
+ }
563
+ }
564
+ const collectionMapping = Object.fromEntries(collectionMappingMap);
565
+ const additionalScssDirectories = (config?.additionalScssDirectories ?? []).map(resolveDir);
566
+ const additionalCssDirectories = (config?.additionalCssDirectories ?? []).map(resolveDir);
567
+ const watchDirectories = (config?.watchDirectories ?? []).map(resolveDir);
568
+ return {
569
+ source: config?.source ?? base.source,
570
+ sourceDir: config?.sourceDir ? resolveDir(config.sourceDir) : base.sourceDir,
571
+ collectionsDir: config?.collectionsDir ? resolveDir(config.collectionsDir) : base.collectionsDir,
572
+ sourcePatterns: config?.sourcePatterns ?? [...base.sourcePatterns],
573
+ collectionMapping,
574
+ outputDir: config?.outputDir ? resolveDir(config.outputDir) : base.outputDir,
575
+ outputDirs,
576
+ outputFileNames: { ...base.outputFileNames, ...config?.outputFileNames },
577
+ prefix: config?.prefix ?? base.prefix,
578
+ formats: config?.formats ?? [...base.formats],
579
+ additionalScssDirectories,
580
+ additionalCssDirectories,
581
+ mergeOrder: config?.mergeOrder ?? base.mergeOrder,
582
+ createBundle: config?.createBundle ?? base.createBundle,
583
+ scssImportHeader: config?.scssImportHeader,
584
+ themes: resolveThemesConfig(config?.themes),
585
+ transforms: config?.transforms ?? [],
586
+ customFormats: config?.customFormats ?? [],
587
+ preprocessors: config?.preprocessors ?? [],
588
+ filters: config?.filters ?? [],
589
+ onBuildStart: config?.onBuildStart,
590
+ onFormatComplete: config?.onFormatComplete,
591
+ onAllFormatsComplete: config?.onAllFormatsComplete,
592
+ onBuildComplete: config?.onBuildComplete,
593
+ outputReferences: config?.outputReferences ?? base.outputReferences,
594
+ baseFontSize: config?.baseFontSize ?? base.baseFontSize,
595
+ separateThemeFiles: config?.separateThemeFiles ?? base.separateThemeFiles,
596
+ watch: config?.watch ?? base.watch,
597
+ watchDirectories,
598
+ pipeline: config?.pipeline
599
+ };
600
+ }
601
+ function applyOverrides(config, overrides) {
602
+ return {
603
+ tokens: overrides.tokens ? { ...config.tokens, ...overrides.tokens } : config.tokens,
604
+ icons: overrides.icons ? { ...config.icons, ...overrides.icons } : config.icons,
605
+ global: overrides.global ? { ...config.global, ...overrides.global } : config.global
606
+ };
607
+ }
608
+ function resolveConfig(config = {}, options = {}) {
609
+ const mergedConfig = options.overrides ? applyOverrides(config, options.overrides) : config;
610
+ const configDir = options.configDir ?? options.cwd ?? process.cwd();
611
+ return {
612
+ global: resolveGlobalConfig(mergedConfig.global, options),
613
+ tokens: resolveTokensConfig(mergedConfig.tokens, options),
614
+ icons: resolveIconsConfig(mergedConfig.icons, options),
615
+ configDir
616
+ };
617
+ }
618
+ function mergeConfigs(...configs) {
619
+ let result = {};
620
+ for (const config of configs) {
621
+ result = applyOverrides(result, config);
622
+ }
623
+ return result;
624
+ }
625
+ function createResolvedConfig(partial = {}) {
626
+ return {
627
+ global: partial.global ?? defaultConfig.global,
628
+ tokens: partial.tokens ?? defaultConfig.tokens,
629
+ icons: partial.icons ?? defaultConfig.icons,
630
+ configDir: partial.configDir ?? process.cwd(),
631
+ configPath: partial.configPath
632
+ };
633
+ }
634
+
635
+ // src/config/loader.ts
636
+ var MODULE_NAME = "dsai";
637
+ var CONFIG_FILE_NAMES = [
638
+ "dsai.config.ts",
639
+ "dsai.config.mjs",
640
+ "dsai.config.js",
641
+ "dsai.config.cjs",
642
+ ".dsairc.ts",
643
+ ".dsairc.mjs",
644
+ ".dsairc.js",
645
+ ".dsairc.cjs",
646
+ ".dsairc.json",
647
+ ".dsairc.yaml",
648
+ ".dsairc.yml",
649
+ ".dsairc",
650
+ "package.json"
651
+ ];
652
+ function createExplorer(options) {
653
+ return cosmiconfig(MODULE_NAME, {
654
+ searchPlaces: CONFIG_FILE_NAMES,
655
+ stopDir: options?.stopDir,
656
+ packageProp: MODULE_NAME
657
+ });
658
+ }
659
+ function createExplorerSync(options) {
660
+ return cosmiconfigSync(MODULE_NAME, {
661
+ searchPlaces: CONFIG_FILE_NAMES,
662
+ stopDir: options?.stopDir,
663
+ packageProp: MODULE_NAME
664
+ });
665
+ }
666
+ async function loadConfig(options = {}) {
667
+ const { cwd = process.cwd(), configPath, overrides, skipFile = false } = options;
668
+ const warnings = [];
669
+ let fileConfig = {};
670
+ let resolvedConfigPath;
671
+ if (!skipFile) {
672
+ const explorer = createExplorer({ stopDir: path2.dirname(cwd) });
673
+ let cosmicResult;
674
+ try {
675
+ if (configPath) {
676
+ const absolutePath = path2.isAbsolute(configPath) ? configPath : path2.resolve(cwd, configPath);
677
+ cosmicResult = await explorer.load(absolutePath);
678
+ } else {
679
+ cosmicResult = await explorer.search(cwd);
680
+ }
681
+ if (cosmicResult && !cosmicResult.isEmpty) {
682
+ fileConfig = cosmicResult.config;
683
+ resolvedConfigPath = cosmicResult.filepath;
684
+ }
685
+ } catch (error) {
686
+ const message = error instanceof Error ? error.message : String(error);
687
+ warnings.push(`Failed to load configuration: ${message}`);
688
+ }
689
+ }
690
+ const configDir = resolvedConfigPath ? path2.dirname(resolvedConfigPath) : cwd;
691
+ const resolvedConfig = resolveConfig(fileConfig, {
692
+ cwd,
693
+ configDir,
694
+ overrides
695
+ });
696
+ return {
697
+ config: resolvedConfig,
698
+ configPath: resolvedConfigPath,
699
+ warnings
700
+ };
701
+ }
702
+ async function searchConfigFile(cwd = process.cwd()) {
703
+ const explorer = createExplorer();
704
+ try {
705
+ const result = await explorer.search(cwd);
706
+ return result?.filepath;
707
+ } catch {
708
+ return void 0;
709
+ }
710
+ }
711
+ function clearConfigCache() {
712
+ const explorer = createExplorer();
713
+ explorer.clearCaches();
714
+ }
715
+ function loadConfigSync(options = {}) {
716
+ const { cwd = process.cwd(), configPath, overrides, skipFile = false } = options;
717
+ const warnings = [];
718
+ let fileConfig = {};
719
+ let resolvedConfigPath;
720
+ if (!skipFile) {
721
+ const explorer = createExplorerSync({ stopDir: path2.dirname(cwd) });
722
+ try {
723
+ if (configPath) {
724
+ const absolutePath = path2.isAbsolute(configPath) ? configPath : path2.resolve(cwd, configPath);
725
+ const result = explorer.load(absolutePath);
726
+ if (result && !result.isEmpty) {
727
+ fileConfig = result.config;
728
+ resolvedConfigPath = result.filepath;
729
+ }
730
+ } else {
731
+ const result = explorer.search(cwd);
732
+ if (result && !result.isEmpty) {
733
+ fileConfig = result.config;
734
+ resolvedConfigPath = result.filepath;
735
+ }
736
+ }
737
+ } catch (error) {
738
+ const message = error instanceof Error ? error.message : String(error);
739
+ warnings.push(`Failed to load configuration: ${message}`);
740
+ }
741
+ }
742
+ const configDir = resolvedConfigPath ? path2.dirname(resolvedConfigPath) : cwd;
743
+ const resolvedConfig = resolveConfig(fileConfig, {
744
+ cwd,
745
+ configDir,
746
+ overrides
747
+ });
748
+ return {
749
+ config: resolvedConfig,
750
+ configPath: resolvedConfigPath,
751
+ warnings
752
+ };
753
+ }
754
+ function defineConfig(config) {
755
+ return config;
756
+ }
757
+ function defineConfigAsync(configFn) {
758
+ return configFn();
759
+ }
760
+
761
+ // src/config/env.ts
762
+ function parseBoolean(value) {
763
+ if (value === void 0) {
764
+ return void 0;
765
+ }
766
+ const normalized = value.toLowerCase().trim();
767
+ if (normalized === "true" || normalized === "1" || normalized === "yes") {
768
+ return true;
769
+ }
770
+ if (normalized === "false" || normalized === "0" || normalized === "no") {
771
+ return false;
772
+ }
773
+ return void 0;
774
+ }
775
+ function parseNumber(value) {
776
+ if (value === void 0) {
777
+ return void 0;
778
+ }
779
+ const num = Number(value);
780
+ return Number.isNaN(num) ? void 0 : num;
781
+ }
782
+ function parseArray(value) {
783
+ if (value === void 0 || value.trim() === "") {
784
+ return void 0;
785
+ }
786
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
787
+ }
788
+ function setNestedValue(obj, path3, value) {
789
+ const parts = path3.split(".");
790
+ if (parts.length === 1) {
791
+ const key = parts[0];
792
+ if (key !== void 0) {
793
+ Object.defineProperty(obj, key, {
794
+ value,
795
+ writable: true,
796
+ enumerable: true,
797
+ configurable: true
798
+ });
799
+ }
800
+ return;
801
+ }
802
+ if (parts.length === 2) {
803
+ const [first, second] = parts;
804
+ if (first !== void 0 && second !== void 0) {
805
+ if (!(first in obj)) {
806
+ Object.defineProperty(obj, first, {
807
+ value: {},
808
+ writable: true,
809
+ enumerable: true,
810
+ configurable: true
811
+ });
812
+ }
813
+ const nested = obj[first];
814
+ Object.defineProperty(nested, second, {
815
+ value,
816
+ writable: true,
817
+ enumerable: true,
818
+ configurable: true
819
+ });
820
+ }
821
+ return;
822
+ }
823
+ if (parts.length === 3) {
824
+ const [first, second, third] = parts;
825
+ if (first !== void 0 && second !== void 0 && third !== void 0) {
826
+ if (!(first in obj)) {
827
+ Object.defineProperty(obj, first, {
828
+ value: {},
829
+ writable: true,
830
+ enumerable: true,
831
+ configurable: true
832
+ });
833
+ }
834
+ const nested1 = obj[first];
835
+ if (!(second in nested1)) {
836
+ Object.defineProperty(nested1, second, {
837
+ value: {},
838
+ writable: true,
839
+ enumerable: true,
840
+ configurable: true
841
+ });
842
+ }
843
+ const nested2 = nested1[second];
844
+ Object.defineProperty(nested2, third, {
845
+ value,
846
+ writable: true,
847
+ enumerable: true,
848
+ configurable: true
849
+ });
850
+ }
851
+ }
852
+ }
853
+ function getConfigFromEnv(options = {}) {
854
+ const env = options.env ?? process.env;
855
+ const prefix = options.prefix ?? "DSAI_";
856
+ const config = {};
857
+ for (const [envKey, configPath] of Object.entries(envMappings)) {
858
+ if (!envKey.startsWith(prefix)) {
859
+ continue;
860
+ }
861
+ const envValue = env[envKey];
862
+ if (envValue === void 0) {
863
+ continue;
864
+ }
865
+ let parsedValue;
866
+ if (envBooleanKeys.has(envKey)) {
867
+ parsedValue = parseBoolean(envValue);
868
+ } else if (envNumberKeys.has(envKey)) {
869
+ parsedValue = parseNumber(envValue);
870
+ } else if (envArrayKeys.has(envKey)) {
871
+ parsedValue = parseArray(envValue);
872
+ } else {
873
+ parsedValue = envValue;
874
+ }
875
+ if (parsedValue !== void 0) {
876
+ setNestedValue(config, configPath, parsedValue);
877
+ }
878
+ }
879
+ return config;
880
+ }
881
+ function isCI(env = process.env) {
882
+ return env["CI"] === "true" || env["CONTINUOUS_INTEGRATION"] === "true" || env["GITHUB_ACTIONS"] === "true" || env["GITLAB_CI"] === "true" || env["CIRCLECI"] === "true" || env["TRAVIS"] === "true" || env["BUILDKITE"] === "true" || env["JENKINS_URL"] !== void 0 || env["TF_BUILD"] === "True";
883
+ }
884
+ function shouldDisableColors(env = process.env) {
885
+ return env["NO_COLOR"] !== void 0 || env["FORCE_COLOR"] === "0" || env["TERM"] === "dumb";
886
+ }
887
+ function getLogLevelFromEnv(env = process.env) {
888
+ const dsaiLogLevel = env["DSAI_LOG_LEVEL"];
889
+ const dsaiDebug = env["DSAI_DEBUG"];
890
+ const debug = env["DEBUG"];
891
+ const verbose = env["VERBOSE"];
892
+ if (dsaiLogLevel) {
893
+ return dsaiLogLevel;
894
+ }
895
+ if (dsaiDebug === "true" || debug?.includes("dsai")) {
896
+ return "debug";
897
+ }
898
+ if (verbose === "true") {
899
+ return "verbose";
900
+ }
901
+ return void 0;
902
+ }
903
+ function getEnvOverrides(env = process.env) {
904
+ const config = getConfigFromEnv({ env });
905
+ if (isCI(env) && !config.global) {
906
+ config.global = { debug: false };
907
+ }
908
+ const logLevel = getLogLevelFromEnv(env);
909
+ if (logLevel) {
910
+ config.global = {
911
+ ...config.global,
912
+ logLevel
913
+ };
914
+ }
915
+ return config;
916
+ }
917
+
918
+ // src/config/migrate.ts
919
+ function checkMigrationNeeded(config, filename) {
920
+ const warnings = [];
921
+ const suggestions = [];
922
+ if (filename?.includes("tokens.config.json")) {
923
+ warnings.push("tokens.config.json is deprecated. Please migrate to dsai.config.mjs.");
924
+ suggestions.push("Run: npx @dsai-io/tools migrate to automatically migrate your config.");
925
+ return {
926
+ needsMigration: true,
927
+ detectedFormat: "legacy-tokens",
928
+ warnings,
929
+ suggestions
930
+ };
931
+ }
932
+ if (isLegacyTokensConfig(config)) {
933
+ warnings.push("Legacy configuration format detected. Some options may be deprecated.");
934
+ suggestions.push("Consider updating to the new configuration format.");
935
+ return {
936
+ needsMigration: true,
937
+ detectedFormat: "legacy-tokens",
938
+ warnings,
939
+ suggestions
940
+ };
941
+ }
942
+ return {
943
+ needsMigration: false,
944
+ detectedFormat: "current",
945
+ warnings,
946
+ suggestions
947
+ };
948
+ }
949
+ function isLegacyTokensConfig(config) {
950
+ if (typeof config !== "object" || config === null) {
951
+ return false;
952
+ }
953
+ const obj = config;
954
+ return typeof obj["output"] === "string" || typeof obj["source"] === "string" && !("sourceDir" in obj);
955
+ }
956
+ function migrateLegacyTokensConfig(legacy) {
957
+ const tokens = {};
958
+ if (legacy.source !== void 0 && legacy.source !== null) {
959
+ tokens.sourceDir = legacy.source;
960
+ }
961
+ if (legacy.output !== void 0 && legacy.output !== null) {
962
+ tokens.outputDir = legacy.output;
963
+ }
964
+ if (legacy.prefix !== void 0 && legacy.prefix !== null) {
965
+ tokens.prefix = legacy.prefix;
966
+ }
967
+ if (Array.isArray(legacy.formats)) {
968
+ const validFormats = ["css", "scss", "js", "ts", "json", "android", "ios"];
969
+ tokens.formats = legacy.formats.filter(
970
+ (f) => validFormats.includes(f)
971
+ );
972
+ }
973
+ if (legacy.themes !== void 0 && legacy.themes !== null) {
974
+ tokens.themes = {
975
+ default: legacy.themes.default ?? "Light"
976
+ // ignoreModes can be derived from modes if needed
977
+ };
978
+ }
979
+ return { tokens };
980
+ }
981
+ function migrateConfig(config, filename) {
982
+ const check = checkMigrationNeeded(config, filename);
983
+ if (!check.needsMigration) {
984
+ return {
985
+ config,
986
+ warnings: check.warnings
987
+ };
988
+ }
989
+ switch (check.detectedFormat) {
990
+ case "legacy-tokens":
991
+ return {
992
+ config: migrateLegacyTokensConfig(config),
993
+ warnings: check.warnings
994
+ };
995
+ default:
996
+ return {
997
+ config,
998
+ warnings: [...check.warnings, "Unknown config format - using as-is."]
999
+ };
1000
+ }
1001
+ }
1002
+ function checkDeprecatedOptions(config) {
1003
+ const warnings = [];
1004
+ const configAny = config;
1005
+ const tokens = configAny["tokens"];
1006
+ if (tokens !== void 0) {
1007
+ if ("output" in tokens) {
1008
+ warnings.push(
1009
+ `Deprecated: Use 'tokens.outputDir' instead of 'tokens.output' (tokens.outputDir)`
1010
+ );
1011
+ }
1012
+ if ("source" in tokens && typeof tokens["source"] === "string") {
1013
+ warnings.push(
1014
+ `Deprecated: Use 'tokens.sourceDir' instead of 'tokens.source' for directory paths (tokens.sourceDir)`
1015
+ );
1016
+ }
1017
+ }
1018
+ return warnings;
1019
+ }
1020
+ function generateMigrationScript(_oldConfig, newConfig) {
1021
+ const configJson = JSON.stringify(newConfig, null, " ");
1022
+ return `// @ts-check
1023
+ /**
1024
+ * DSAI Configuration
1025
+ *
1026
+ * Migrated from legacy format.
1027
+ * Please review and adjust as needed.
1028
+ *
1029
+ * @type {import('@dsai-io/tools').DsaiConfig}
1030
+ */
1031
+ import { defineConfig } from '@dsai-io/tools';
1032
+
1033
+ export default defineConfig(${configJson.replace(/"([^"]+)":/g, "$1:")});
1034
+ `;
1035
+ }
1036
+
1037
+ export { CONFIG_FILE_NAMES, DEFAULT_COLLECTIONS_DIR, DEFAULT_ICONS_OUTPUT_DIR, DEFAULT_ICONS_SOURCE_DIR, DEFAULT_LOG_LEVEL, DEFAULT_OUTPUT_DIR, DEFAULT_PREFIX, DEFAULT_SOURCE_DIR, buildConfigSchema, checkDeprecatedOptions, checkMigrationNeeded, clearConfigCache, createResolvedConfig, customFormatSchema, customTransformSchema, defaultConfig, defaultFormats, defaultGlobalConfig, defaultIconFramework, defaultIconsConfig, defaultOutputFileNames, defaultSelectorPattern, defaultSourcePatterns, defaultThemesConfig, defaultTokensConfig, defineConfig, defineConfigAsync, dsaiConfigSchema, envArrayKeys, envBooleanKeys, envMappings, envNumberKeys, filePathSchema, formatErrorMessage, formatValidationErrors, frameworkSchema, generateMigrationScript, getConfigFromEnv, getDefaultExtension, getDefaultOutputDir, getEnvOverrides, getLogLevelFromEnv, getOutputFileName, globPatternSchema, globalConfigSchema, hashTypeSchema, iconOptimizationSchema, iconSpriteSchema, iconsConfigSchema, isCI, loadConfig, loadConfigSync, logLevelSchema, mergeConfigs, migrateConfig, migrateLegacyTokensConfig, outputFormatSchema, resolveConfig, searchConfigFile, shouldDisableColors, themeModeSchema, themesConfigSchema, tokenBuildConfigSchema, tokenCacheConfigSchema, tokenWatchConfigSchema, tokensConfigSchema, tokensHooksSchema, validateConfig, validateConfigOrThrow, validateConfigSection, versionSchema };
1038
+ //# sourceMappingURL=index.js.map
1039
+ //# sourceMappingURL=index.js.map