@udixio/theme 1.1.0 → 1.3.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 (98) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/bin/main.ts +85 -0
  3. package/dist/API.d.ts +1 -0
  4. package/dist/API.d.ts.map +1 -1
  5. package/dist/bin.cjs +64 -0
  6. package/dist/bin.js +63 -0
  7. package/dist/browser.cjs +39 -0
  8. package/dist/browser.js +39 -0
  9. package/dist/color/color.manager.d.ts.map +1 -1
  10. package/dist/color/default-color.d.ts.map +1 -1
  11. package/dist/config/config.interface.d.ts.map +1 -0
  12. package/dist/config/define-config.d.ts.map +1 -0
  13. package/dist/config/index.d.ts +3 -0
  14. package/dist/config/index.d.ts.map +1 -0
  15. package/dist/config/index.node.d.ts +2 -0
  16. package/dist/config/index.node.d.ts.map +1 -0
  17. package/dist/config/resolver-config.d.ts +11 -0
  18. package/dist/config/resolver-config.d.ts.map +1 -0
  19. package/dist/define-config-B1QPKKY_.js +430 -0
  20. package/dist/define-config-BGgVazsr.cjs +429 -0
  21. package/dist/index.browser.d.ts +2 -0
  22. package/dist/index.browser.d.ts.map +1 -0
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.node.d.ts +4 -0
  26. package/dist/index.node.d.ts.map +1 -0
  27. package/dist/load-from-path-BSrT4DOj.js +39 -0
  28. package/dist/load-from-path-BuN8RpOs.cjs +78 -0
  29. package/dist/loader/index.d.ts +2 -0
  30. package/dist/loader/index.d.ts.map +1 -0
  31. package/dist/loader/index.node.d.ts +3 -0
  32. package/dist/loader/index.node.d.ts.map +1 -0
  33. package/dist/loader/load-from-path.d.ts +5 -0
  34. package/dist/loader/load-from-path.d.ts.map +1 -0
  35. package/dist/loader/loader.d.ts +3 -0
  36. package/dist/loader/loader.d.ts.map +1 -0
  37. package/dist/loader/unplugin.d.ts +16 -0
  38. package/dist/loader/unplugin.d.ts.map +1 -0
  39. package/dist/{index.js → loader-Bc0bstAD.js} +172 -661
  40. package/dist/{index.cjs → loader-YNN5hAF3.cjs} +148 -678
  41. package/dist/material-color-utilities/dynamic_color.d.ts +9 -15
  42. package/dist/material-color-utilities/dynamic_color.d.ts.map +1 -1
  43. package/dist/material-color-utilities/toneDeltaPair.d.ts +9 -2
  44. package/dist/material-color-utilities/toneDeltaPair.d.ts.map +1 -1
  45. package/dist/node.cjs +191 -0
  46. package/dist/node.js +170 -0
  47. package/dist/plugin/plugin.api.d.ts +1 -1
  48. package/dist/plugin/plugin.api.d.ts.map +1 -1
  49. package/dist/theme/scheme.d.ts +1 -1
  50. package/dist/theme/scheme.d.ts.map +1 -1
  51. package/dist/theme/variants/fidelity.variant.d.ts +3 -0
  52. package/dist/theme/variants/fidelity.variant.d.ts.map +1 -0
  53. package/dist/theme/variants/index.d.ts +1 -0
  54. package/dist/theme/variants/index.d.ts.map +1 -1
  55. package/package.json +27 -10
  56. package/src/API.ts +4 -0
  57. package/src/color/color.manager.ts +10 -8
  58. package/src/color/default-color.ts +82 -34
  59. package/src/config/index.node.ts +1 -0
  60. package/src/{adapter → config}/index.ts +0 -1
  61. package/src/config/resolver-config.ts +49 -0
  62. package/src/index.browser.ts +1 -0
  63. package/src/index.node.ts +3 -0
  64. package/src/index.ts +2 -2
  65. package/src/loader/index.node.ts +2 -0
  66. package/src/loader/index.ts +1 -0
  67. package/src/loader/load-from-path.ts +9 -0
  68. package/src/{adapter/adapter.abstract.ts → loader/loader.ts} +23 -28
  69. package/src/loader/unplugin.ts +158 -0
  70. package/src/material-color-utilities/dynamic_color.ts +18 -120
  71. package/src/material-color-utilities/toneDeltaPair.ts +91 -2
  72. package/src/plugin/plugin.api.ts +4 -4
  73. package/src/theme/scheme.ts +7 -1
  74. package/src/theme/variants/fidelity.variant.ts +38 -0
  75. package/src/theme/variants/index.ts +2 -0
  76. package/vite.config.ts +18 -4
  77. package/dist/adapter/adapter.abstract.d.ts +0 -10
  78. package/dist/adapter/adapter.abstract.d.ts.map +0 -1
  79. package/dist/adapter/config.interface.d.ts.map +0 -1
  80. package/dist/adapter/define-config.d.ts.map +0 -1
  81. package/dist/adapter/file-adapter.mixin.d.ts +0 -18
  82. package/dist/adapter/file-adapter.mixin.d.ts.map +0 -1
  83. package/dist/adapter/index.d.ts +0 -4
  84. package/dist/adapter/index.d.ts.map +0 -1
  85. package/dist/adapters/index.d.ts +0 -3
  86. package/dist/adapters/index.d.ts.map +0 -1
  87. package/dist/adapters/node.adapter.d.ts +0 -7
  88. package/dist/adapters/node.adapter.d.ts.map +0 -1
  89. package/dist/adapters/vite.adapter.d.ts +0 -3
  90. package/dist/adapters/vite.adapter.d.ts.map +0 -1
  91. package/src/adapter/file-adapter.mixin.ts +0 -72
  92. package/src/adapters/index.ts +0 -2
  93. package/src/adapters/node.adapter.ts +0 -57
  94. package/src/adapters/vite.adapter.ts +0 -79
  95. /package/dist/{adapter → config}/config.interface.d.ts +0 -0
  96. /package/dist/{adapter → config}/define-config.d.ts +0 -0
  97. /package/src/{adapter → config}/config.interface.ts +0 -0
  98. /package/src/{adapter → config}/define-config.ts +0 -0
@@ -1,4 +1,4 @@
1
- import { ToneDeltaPair } from '../material-color-utilities';
1
+ import { toneDeltaPair } from '../material-color-utilities';
2
2
  import { DynamicColor } from '../material-color-utilities/dynamic_color';
3
3
  import { highestSurface } from './color.manager';
4
4
  import { AddColorsOptions, ColorApi } from './color.api';
@@ -6,6 +6,11 @@ import { Hct } from '../material-color-utilities/htc';
6
6
  import { ColorOptions } from './configurable-color';
7
7
  import { Scheme } from '../theme';
8
8
  import { DynamicColorKey, getCurve, tMaxC, tMinC } from './color.utils';
9
+ import { Contrast } from '@material/material-color-utilities';
10
+
11
+ const inverseTone = (tone: number) => {
12
+ return 100 - tone;
13
+ };
9
14
 
10
15
  export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
11
16
  const getColor = (key: DynamicColorKey) => {
@@ -26,6 +31,9 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
26
31
  if (s.isDark) {
27
32
  return 4;
28
33
  } else {
34
+ if (s.variant == 'fidelity') {
35
+ return 100;
36
+ }
29
37
  if (Hct.isYellow(s.getPalette('neutral').hue)) {
30
38
  return 99;
31
39
  } else if (s.variant === 'vibrant') {
@@ -347,6 +355,8 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
347
355
  ? 88
348
356
  : 98,
349
357
  );
358
+ } else if (s.variant == 'fidelity') {
359
+ return s.sourceColorHct.tone;
350
360
  } else {
351
361
  return tMaxC(
352
362
  s.getPalette('primary'),
@@ -358,15 +368,34 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
358
368
  isBackground: true,
359
369
  background: (s) => highestSurface(s, colorService),
360
370
  contrastCurve: (s) => getCurve(4.5),
361
- toneDeltaPair: (s) =>
362
- new ToneDeltaPair(
363
- colorService.getColor('primaryContainer').getMaterialColor(),
364
- colorService.getColor('primary').getMaterialColor(),
365
- 5,
366
- 'relative_lighter',
367
- true,
368
- 'farther',
369
- ),
371
+ adjustTone: (s) =>
372
+ s.variant == 'fidelity'
373
+ ? () => {
374
+ const surfaceTone = colorService
375
+ .getColor('surface')
376
+ .getMaterialColor()
377
+ .tone(s);
378
+ const primaryTone = colorService
379
+ .getColor('primary')
380
+ .getMaterialColor()
381
+ .tone(s);
382
+ let selfTone = primaryTone;
383
+ if (Contrast.ratioOfTones(surfaceTone, selfTone) < 3) {
384
+ const result = inverseTone(primaryTone);
385
+ if (Contrast.ratioOfTones(surfaceTone, result) >= 3) {
386
+ selfTone = result;
387
+ }
388
+ }
389
+ return selfTone;
390
+ }
391
+ : toneDeltaPair(
392
+ colorService.getColor('primaryContainer').getMaterialColor(),
393
+ colorService.getColor('primary').getMaterialColor(),
394
+ 5,
395
+ 'relative_lighter',
396
+ true,
397
+ 'farther',
398
+ ),
370
399
  },
371
400
  primaryDim: {
372
401
  palette: (s) => s.getPalette('primary'),
@@ -382,8 +411,8 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
382
411
  isBackground: true,
383
412
  background: (s) => getColor('surfaceContainerHigh'),
384
413
  contrastCurve: (s) => getCurve(4.5),
385
- toneDeltaPair: (s) =>
386
- new ToneDeltaPair(
414
+ adjustTone: (s) =>
415
+ toneDeltaPair(
387
416
  colorService.getColor('primaryDim').getMaterialColor(),
388
417
  colorService.getColor('primary').getMaterialColor(),
389
418
  5,
@@ -414,6 +443,15 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
414
443
  78,
415
444
  Hct.isCyan(s.getPalette('primary').hue) ? 88 : 90,
416
445
  );
446
+ }
447
+ if (s.variant == 'fidelity') {
448
+ return s.isDark
449
+ ? tMaxC(s.getPalette('primary'), 30, 93)
450
+ : tMaxC(
451
+ s.getPalette('primary'),
452
+ 78,
453
+ Hct.isCyan(s.getPalette('primary').hue) ? 88 : 90,
454
+ );
417
455
  } else {
418
456
  // VIBRANT
419
457
  return s.isDark
@@ -427,7 +465,17 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
427
465
  },
428
466
  isBackground: true,
429
467
  background: (s) => highestSurface(s, colorService),
430
- toneDeltaPair: (s) => undefined,
468
+ adjustTone: (s) =>
469
+ s.variant == 'fidelity'
470
+ ? toneDeltaPair(
471
+ colorService.getColor('primary').getMaterialColor(),
472
+ colorService.getColor('primaryContainer').getMaterialColor(),
473
+ 15,
474
+ 'relative_darker',
475
+ true,
476
+ 'farther',
477
+ )
478
+ : undefined,
431
479
  contrastCurve: (s) => (s.contrastLevel > 0 ? getCurve(1.5) : undefined),
432
480
  },
433
481
  onPrimaryContainer: {
@@ -457,8 +505,8 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
457
505
  tone: (s) =>
458
506
  colorService.getColor('primaryFixed').getMaterialColor().getTone(s),
459
507
  isBackground: true,
460
- toneDeltaPair: (s) =>
461
- new ToneDeltaPair(
508
+ adjustTone: (s) =>
509
+ toneDeltaPair(
462
510
  getColor('primaryFixedDim'),
463
511
  getColor('primaryFixed'),
464
512
  5,
@@ -509,8 +557,8 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
509
557
  isBackground: true,
510
558
  background: (s) => highestSurface(s, colorService),
511
559
  contrastCurve: (s) => getCurve(4.5),
512
- toneDeltaPair: (s) =>
513
- new ToneDeltaPair(
560
+ adjustTone: (s) =>
561
+ toneDeltaPair(
514
562
  getColor('secondaryContainer'),
515
563
  getColor('secondary'),
516
564
  5,
@@ -531,8 +579,8 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
531
579
  isBackground: true,
532
580
  background: (s) => getColor('surfaceContainerHigh'),
533
581
  contrastCurve: (s) => getCurve(4.5),
534
- toneDeltaPair: (s) =>
535
- new ToneDeltaPair(
582
+ adjustTone: (s) =>
583
+ toneDeltaPair(
536
584
  getColor('secondaryDim'),
537
585
  getColor('secondary'),
538
586
  5,
@@ -561,7 +609,7 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
561
609
  },
562
610
  isBackground: true,
563
611
  background: (s) => highestSurface(s, colorService),
564
- toneDeltaPair: (s) => undefined,
612
+ adjustTone: (s) => undefined,
565
613
  contrastCurve: (s) => (s.contrastLevel > 0 ? getCurve(1.5) : undefined),
566
614
  },
567
615
  onSecondaryContainer: {
@@ -589,8 +637,8 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
589
637
  palette: (s) => s.getPalette('secondary'),
590
638
  tone: (s) => getColor('secondaryFixed').getTone(s),
591
639
  isBackground: true,
592
- toneDeltaPair: (s) =>
593
- new ToneDeltaPair(
640
+ adjustTone: (s) =>
641
+ toneDeltaPair(
594
642
  getColor('secondaryFixedDim'),
595
643
  getColor('secondaryFixed'),
596
644
  5,
@@ -634,8 +682,8 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
634
682
  isBackground: true,
635
683
  background: (s) => highestSurface(s, colorService),
636
684
  contrastCurve: (s) => getCurve(4.5),
637
- toneDeltaPair: (s) =>
638
- new ToneDeltaPair(
685
+ adjustTone: (s) =>
686
+ toneDeltaPair(
639
687
  getColor('tertiaryContainer'),
640
688
  getColor('tertiary'),
641
689
  5,
@@ -656,8 +704,8 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
656
704
  isBackground: true,
657
705
  background: (s) => getColor('surfaceContainerHigh'),
658
706
  contrastCurve: (s) => getCurve(4.5),
659
- toneDeltaPair: (s) =>
660
- new ToneDeltaPair(
707
+ adjustTone: (s) =>
708
+ toneDeltaPair(
661
709
  getColor('tertiaryDim'),
662
710
  getColor('tertiary'),
663
711
  5,
@@ -695,7 +743,7 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
695
743
  },
696
744
  isBackground: true,
697
745
  background: (s) => highestSurface(s, colorService),
698
- toneDeltaPair: (s) => undefined,
746
+ adjustTone: (s) => undefined,
699
747
  contrastCurve: (s) => (s.contrastLevel > 0 ? getCurve(1.5) : undefined),
700
748
  },
701
749
  onTertiaryContainer: {
@@ -723,8 +771,8 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
723
771
  palette: (s) => s.getPalette('tertiary'),
724
772
  tone: (s) => getColor('tertiaryFixed').getTone(s),
725
773
  isBackground: true,
726
- toneDeltaPair: (s) =>
727
- new ToneDeltaPair(
774
+ adjustTone: (s) =>
775
+ toneDeltaPair(
728
776
  getColor('tertiaryFixedDim'),
729
777
  getColor('tertiaryFixed'),
730
778
  5,
@@ -760,8 +808,8 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
760
808
  isBackground: true,
761
809
  background: (s) => highestSurface(s, colorService),
762
810
  contrastCurve: (s) => getCurve(4.5),
763
- toneDeltaPair: (s) =>
764
- new ToneDeltaPair(
811
+ adjustTone: (s) =>
812
+ toneDeltaPair(
765
813
  colorService.getColor('errorContainer').getMaterialColor(),
766
814
  colorService.getColor('error').getMaterialColor(),
767
815
  5,
@@ -776,8 +824,8 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
776
824
  isBackground: true,
777
825
  background: (s) => getColor('surfaceContainerHigh'),
778
826
  contrastCurve: (s) => getCurve(4.5),
779
- toneDeltaPair: (s) =>
780
- new ToneDeltaPair(
827
+ adjustTone: (s) =>
828
+ toneDeltaPair(
781
829
  getColor('errorDim'),
782
830
  getColor('error'),
783
831
  5,
@@ -800,7 +848,7 @@ export const defaultColors: AddColorsOptions = (colorService: ColorApi) => {
800
848
  },
801
849
  isBackground: true,
802
850
  background: (s) => highestSurface(s, colorService),
803
- toneDeltaPair: (s) => undefined,
851
+ adjustTone: (s) => undefined,
804
852
  contrastCurve: (s) => (s.contrastLevel > 0 ? getCurve(1.5) : undefined),
805
853
  },
806
854
  onErrorContainer: {
@@ -0,0 +1 @@
1
+ export * from './resolver-config';
@@ -1,3 +1,2 @@
1
1
  export * from './config.interface';
2
- export * from './adapter.abstract';
3
2
  export * from './define-config';
@@ -0,0 +1,49 @@
1
+ import { ConfigInterface } from './index';
2
+ import * as fs from 'node:fs';
3
+
4
+ export interface ResolvedConfigResult {
5
+ config: ConfigInterface;
6
+ filePath: string; // absolute path of the loaded config file
7
+ }
8
+
9
+ /**
10
+ * Resolve and load theme.config.{ts,js,mjs,cjs} using JITI, independent of any bundler.
11
+ * @param configPath base path without extension or full path with/without extension
12
+ */
13
+ export async function resolveConfig(
14
+ configPath = './theme.config',
15
+ ): Promise<ResolvedConfigResult> {
16
+ const { createJiti } = await import('jiti');
17
+ const { resolve } = await import('pathe');
18
+
19
+ const jiti = createJiti(import.meta.url, {
20
+ debug: process.env.NODE_ENV === 'development',
21
+ fsCache: true,
22
+ interopDefault: true,
23
+ });
24
+
25
+ // If user passed a path with extension, try it as-is first
26
+ const tryPaths: string[] = [];
27
+ const resolvedInput = resolve(configPath);
28
+
29
+ const extMatch = /\.(ts|js|mjs|cjs)$/.exec(resolvedInput);
30
+ if (extMatch) {
31
+ tryPaths.push(resolvedInput);
32
+ } else {
33
+ for (const ext of ['.ts', '.js', '.mjs', '.cjs']) {
34
+ tryPaths.push(resolvedInput + ext);
35
+ }
36
+ }
37
+
38
+ for (const file of tryPaths) {
39
+ if (fs.existsSync(file)) {
40
+ const cfg = (await jiti.import(file, {
41
+ default: true,
42
+ })) as ConfigInterface;
43
+
44
+ return { config: cfg, filePath: file };
45
+ }
46
+ }
47
+
48
+ throw new Error(`Configuration file not found for ${''}{.ts,.js,.mjs,.cjs}`);
49
+ }
@@ -0,0 +1 @@
1
+ export * from "./index"
@@ -0,0 +1,3 @@
1
+ export * from './index';
2
+ export * from './loader/index.node';
3
+ export * from './config/index.node';
package/src/index.ts CHANGED
@@ -2,10 +2,10 @@ export * from './app.container';
2
2
  export * from './app.module';
3
3
  export * from './API';
4
4
  export * from './color';
5
- export * from './adapter';
6
- export * from './adapters';
5
+ export * from './loader';
7
6
  export * from './bootstrap';
8
7
  export * from './material-color-utilities';
9
8
  export * from './plugin';
10
9
  export * from './plugins';
11
10
  export * from './theme';
11
+ export * from './config';
@@ -0,0 +1,2 @@
1
+ export * from './unplugin';
2
+ export * from './load-from-path';
@@ -0,0 +1 @@
1
+ export * from './loader';
@@ -0,0 +1,9 @@
1
+ import { loader } from './loader';
2
+ import { resolveConfig } from '../config/resolver-config';
3
+
4
+ export const loadFromPath = async (configPath = './theme.config') => {
5
+ const { config, filePath } = await resolveConfig(configPath);
6
+ const api = await loader(config);
7
+
8
+ return { filePath, api: api };
9
+ };
@@ -1,28 +1,22 @@
1
- import { ConfigInterface } from './config.interface';
2
1
  import { tonalSpotVariant } from '../theme';
3
2
  import { defaultColors } from '../color';
4
3
  import { bootstrap } from '../bootstrap';
5
- import { API } from '../API';
6
4
  import { registerModule } from '../app.container';
7
5
  import { asValue } from 'awilix';
6
+ import { ConfigInterface } from '../config';
8
7
 
9
- export abstract class AdapterAbstract {
10
- public api: API;
8
+ const initializeApi = () => {
9
+ const api = bootstrap();
10
+ registerModule({
11
+ adapter: asValue(this),
12
+ });
13
+ return api;
14
+ };
11
15
 
12
- protected constructor() {
13
- this.api = bootstrap();
14
- registerModule({
15
- adapter: asValue(this),
16
- });
17
- }
18
-
19
- abstract getConfig(): Promise<ConfigInterface>;
16
+ export const loader = async (config: ConfigInterface) => {
17
+ const api = initializeApi();
20
18
 
21
- public async init(): Promise<void> {
22
- const config = await this.getConfig();
23
- if (config == null) {
24
- return;
25
- }
19
+ const init = () => {
26
20
  const {
27
21
  sourceColor,
28
22
  contrastLevel = 0,
@@ -33,7 +27,7 @@ export abstract class AdapterAbstract {
33
27
  useDefaultColors = true,
34
28
  plugins,
35
29
  } = config;
36
- this.api.themes.create({
30
+ api.themes.create({
37
31
  contrastLevel: contrastLevel,
38
32
  isDark: isDark,
39
33
  sourceColorHex: sourceColor,
@@ -41,24 +35,25 @@ export abstract class AdapterAbstract {
41
35
  });
42
36
  if (palettes) {
43
37
  Object.entries(palettes).forEach(([key, value]) =>
44
- this.api.themes.addCustomPalette(key, value),
38
+ api.themes.addCustomPalette(key, value),
45
39
  );
46
40
  }
47
41
  if (useDefaultColors) {
48
- this.api.colors.addColors(defaultColors);
42
+ api.colors.addColors(defaultColors);
49
43
  }
50
44
  if (colors) {
51
- this.api.colors.addColors(colors);
45
+ api.colors.addColors(colors);
52
46
  }
53
47
  if (plugins) {
54
48
  plugins.forEach((plugin) => {
55
- this.api.plugins.addPlugin(plugin);
49
+ api.plugins.addPlugin(plugin);
56
50
  });
57
- this.api.plugins.initPlugins(this.api);
51
+ api.plugins.initPlugins(api);
58
52
  }
59
- }
53
+ };
54
+
55
+ init();
56
+ await api.load();
60
57
 
61
- public load() {
62
- this.api.plugins.loadPlugins();
63
- }
64
- }
58
+ return api;
59
+ };
@@ -0,0 +1,158 @@
1
+ import { loadFromPath } from './load-from-path';
2
+
3
+ export interface UdixioThemeOptions {
4
+ configPath?: string;
5
+ verbose?: boolean;
6
+ }
7
+
8
+ // Instance lazy-loadée
9
+ let unpluginInstance: any = null;
10
+
11
+ const createUnpluginTheme = async () => {
12
+ if (unpluginInstance) {
13
+ return unpluginInstance;
14
+ }
15
+
16
+ const { createUnplugin } = await import('unplugin');
17
+
18
+ unpluginInstance = createUnplugin<UdixioThemeOptions>((options = {}) => {
19
+ const { configPath = './theme.config', verbose = false } = options;
20
+
21
+ let resolvedConfigPath: string;
22
+
23
+ // Skip pendant la génération du graph NX
24
+ if (global.NX_GRAPH_CREATION) {
25
+ return {
26
+ name: 'udixio-theme',
27
+ };
28
+ }
29
+
30
+ const loadTheme = async (): Promise<void> => {
31
+ try {
32
+ if (verbose) {
33
+ console.log(`🎨 Loading theme from: ${configPath}`);
34
+ }
35
+ const result = await loadFromPath(configPath);
36
+ if (!resolvedConfigPath && result?.filePath) {
37
+ resolvedConfigPath = result.filePath;
38
+ }
39
+ if (verbose) {
40
+ console.log(`✅ Theme loaded successfully!`);
41
+ }
42
+ } catch (error) {
43
+ console.error(`❌ Theme loading failed:`, error);
44
+ throw error;
45
+ }
46
+ };
47
+
48
+ return {
49
+ name: 'udixio-theme',
50
+
51
+ // Hook appelé au début du build (tous les bundlers)
52
+ buildStart: async () => {
53
+ await loadTheme();
54
+ },
55
+
56
+ // Hook appelé pendant la génération (Rollup/Vite)
57
+ generateBundle: async () => {
58
+ await loadTheme();
59
+ },
60
+
61
+ // Support spécifique Vite pour HMR
62
+ vite: {
63
+ handleHotUpdate: async ({ server, file, modules }) => {
64
+ if (!resolvedConfigPath) {
65
+ const result = await loadFromPath(configPath);
66
+ resolvedConfigPath = result?.filePath || '';
67
+ }
68
+
69
+ if (resolvedConfigPath === file) {
70
+ if (verbose) {
71
+ console.log(`🔄 Theme config changed: ${file}`);
72
+ }
73
+ await loadTheme();
74
+ server.ws.send({ type: 'full-reload', path: '*' });
75
+ return modules;
76
+ }
77
+ return;
78
+ },
79
+ },
80
+
81
+ // Support spécifique Webpack pour HMR
82
+ webpack: (compiler) => {
83
+ if (compiler.options.mode === 'development') {
84
+ compiler.hooks.watchRun.tapAsync(
85
+ 'udixio-theme',
86
+ async (compilation, callback) => {
87
+ const changedFiles = compilation.modifiedFiles || new Set();
88
+
89
+ if (!resolvedConfigPath) {
90
+ const result = await loadFromPath(configPath);
91
+ resolvedConfigPath = result?.filePath || '';
92
+ }
93
+
94
+ if (changedFiles.has(resolvedConfigPath)) {
95
+ if (verbose) {
96
+ console.log(`🔄 Theme config changed: ${resolvedConfigPath}`);
97
+ }
98
+ await loadTheme();
99
+ }
100
+ callback();
101
+ },
102
+ );
103
+ }
104
+ },
105
+
106
+ // Support spécifique Rollup
107
+ rollup: {
108
+ watchChange: async (id) => {
109
+ if (!resolvedConfigPath) {
110
+ const result = await loadFromPath(configPath);
111
+ resolvedConfigPath = result?.filePath || '';
112
+ }
113
+
114
+ if (resolvedConfigPath === id) {
115
+ if (verbose) {
116
+ console.log(`🔄 Theme config changed: ${id}`);
117
+ }
118
+ await loadTheme();
119
+ }
120
+ },
121
+ },
122
+ };
123
+ });
124
+
125
+ return unpluginInstance;
126
+ };
127
+
128
+ // Exports avec lazy loading
129
+ export const vitePlugin = async (options?: UdixioThemeOptions) => {
130
+ const plugin = await createUnpluginTheme();
131
+ return plugin.vite(options);
132
+ };
133
+
134
+ export const webpackPlugin = async (options?: UdixioThemeOptions) => {
135
+ const plugin = await createUnpluginTheme();
136
+ return plugin.webpack(options);
137
+ };
138
+
139
+ export const rollupPlugin = async (options?: UdixioThemeOptions) => {
140
+ const plugin = await createUnpluginTheme();
141
+ return plugin.rollup(options);
142
+ };
143
+
144
+ export const esbuildPlugin = async (options?: UdixioThemeOptions) => {
145
+ const plugin = await createUnpluginTheme();
146
+ return plugin.esbuild(options);
147
+ };
148
+
149
+ // Export principal avec lazy loading
150
+ export const unpluginUdixioTheme = {
151
+ vite: vitePlugin,
152
+ webpack: webpackPlugin,
153
+ rollup: rollupPlugin,
154
+ esbuild: esbuildPlugin,
155
+ };
156
+
157
+ // Export par défaut
158
+ export default unpluginUdixioTheme;