@udixio/tailwind 0.5.3 → 1.1.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.
- package/.eslintrc.mjs +22 -0
- package/CHANGELOG.md +65 -0
- package/{src → dist}/file.d.ts +2 -1
- package/dist/file.d.ts.map +1 -0
- package/dist/index.cjs +360 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +342 -0
- package/dist/plugins-tailwind/index.d.ts.map +1 -0
- package/{src → dist}/tailwind.plugin.d.ts +1 -2
- package/dist/tailwind.plugin.d.ts.map +1 -0
- package/package.json +11 -7
- package/src/file.ts +163 -0
- package/src/index.test.ts +5 -0
- package/src/index.ts +5 -0
- package/src/main.ts +52 -0
- package/src/plugins-tailwind/font.ts +61 -0
- package/src/plugins-tailwind/index.ts +2 -0
- package/src/plugins-tailwind/state.ts +75 -0
- package/src/tailwind.plugin.ts +133 -0
- package/tsconfig.json +16 -0
- package/tsconfig.lib.json +37 -0
- package/tsconfig.spec.json +36 -0
- package/vite.config.ts +54 -0
- package/index.cjs +0 -313
- package/index.js +0 -296
- package/src/file.d.ts.map +0 -1
- package/src/index.d.ts +0 -6
- package/src/index.d.ts.map +0 -1
- package/src/main.d.ts +0 -3
- package/src/main.d.ts.map +0 -1
- package/src/plugins-tailwind/font.d.ts +0 -4
- package/src/plugins-tailwind/font.d.ts.map +0 -1
- package/src/plugins-tailwind/index.d.ts.map +0 -1
- package/src/plugins-tailwind/state.d.ts +0 -3
- package/src/plugins-tailwind/state.d.ts.map +0 -1
- package/src/tailwind.plugin.d.ts.map +0 -1
- /package/{LICENSE → dist/LICENSE} +0 -0
- /package/{src → dist}/plugins-tailwind/index.d.ts +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import plugin, { PluginAPI } from 'tailwindcss/plugin';
|
|
2
|
+
|
|
3
|
+
export type StateOptions = {
|
|
4
|
+
colorKeys: string[];
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
type Components = Record<string, Record<string, object>>;
|
|
8
|
+
|
|
9
|
+
const defaultConfig = {
|
|
10
|
+
statePrefix: 'state',
|
|
11
|
+
disabledStyles: {
|
|
12
|
+
textOpacity: 0.38,
|
|
13
|
+
backgroundOpacity: 0.12,
|
|
14
|
+
},
|
|
15
|
+
transition: {
|
|
16
|
+
duration: 150,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const state = plugin.withOptions(({ colorKeys }: StateOptions) => {
|
|
21
|
+
const resolved = {
|
|
22
|
+
...defaultConfig,
|
|
23
|
+
disabledStyles: {
|
|
24
|
+
...defaultConfig.disabledStyles,
|
|
25
|
+
...{},
|
|
26
|
+
},
|
|
27
|
+
transition: {
|
|
28
|
+
...defaultConfig.transition,
|
|
29
|
+
...{},
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return ({ addComponents }: PluginAPI) => {
|
|
34
|
+
const newComponents: Components = {};
|
|
35
|
+
|
|
36
|
+
for (const isGroup of [false, true]) {
|
|
37
|
+
const group = isGroup ? 'group-' : '';
|
|
38
|
+
for (const colorName of colorKeys) {
|
|
39
|
+
const className = `.${group}${resolved.statePrefix}-${colorName}`;
|
|
40
|
+
newComponents[className] = {
|
|
41
|
+
[`@apply ${group}hover:bg-${colorName}/[0.08]`]: {},
|
|
42
|
+
[`@apply ${group}active:bg-${colorName}/[0.12]`]: {},
|
|
43
|
+
[`@apply ${group}focus-visible:bg-${colorName}/[0.12]`]: {},
|
|
44
|
+
[`@apply transition-colors`]: {},
|
|
45
|
+
[`@apply duration-${resolved.transition.duration}`]: {},
|
|
46
|
+
[`@apply ${group}disabled:text-on-surface/[${resolved.disabledStyles.textOpacity}]`]:
|
|
47
|
+
{},
|
|
48
|
+
[`@apply ${group}disabled:bg-on-surface/[${resolved.disabledStyles.backgroundOpacity}]`]:
|
|
49
|
+
{},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
for (const colorName of colorKeys) {
|
|
55
|
+
for (const stateName of ['hover', 'active', 'focus', 'disabled']) {
|
|
56
|
+
const className = `.${stateName}-${resolved.statePrefix}-${colorName}`;
|
|
57
|
+
if (stateName === 'disabled') {
|
|
58
|
+
newComponents[className] = {
|
|
59
|
+
[`@apply text-on-surface/[${resolved.disabledStyles.textOpacity}]`]:
|
|
60
|
+
{},
|
|
61
|
+
[`@apply bg-on-surface/[${resolved.disabledStyles.backgroundOpacity}]`]:
|
|
62
|
+
{},
|
|
63
|
+
};
|
|
64
|
+
} else {
|
|
65
|
+
const opacity = stateName === 'hover' ? 0.08 : 0.12;
|
|
66
|
+
newComponents[className] = {
|
|
67
|
+
[`@apply bg-${colorName}/[${opacity}]`]: {},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
addComponents(newComponents);
|
|
74
|
+
};
|
|
75
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createOrUpdateFile,
|
|
3
|
+
findProjectRoot,
|
|
4
|
+
findTailwindCssFile,
|
|
5
|
+
getFileContent,
|
|
6
|
+
replaceFileContent,
|
|
7
|
+
} from './file';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { FontPlugin, PluginAbstract, PluginImplAbstract } from '@udixio/theme';
|
|
10
|
+
import { ConfigCss } from './main';
|
|
11
|
+
import * as console from 'node:console';
|
|
12
|
+
|
|
13
|
+
interface TailwindPluginOptions {
|
|
14
|
+
// darkMode?: 'class' | 'media';
|
|
15
|
+
responsiveBreakPoints?: Record<string, number>;
|
|
16
|
+
styleFilePath?: string;
|
|
17
|
+
// subThemes?: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class TailwindPlugin extends PluginAbstract<
|
|
21
|
+
TailwindImplPlugin,
|
|
22
|
+
TailwindPluginOptions
|
|
23
|
+
> {
|
|
24
|
+
public dependencies = [FontPlugin];
|
|
25
|
+
public name = 'tailwind';
|
|
26
|
+
pluginClass = TailwindImplPlugin;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class TailwindImplPlugin extends PluginImplAbstract<TailwindPluginOptions> {
|
|
30
|
+
onInit() {
|
|
31
|
+
this.options.responsiveBreakPoints ??= {
|
|
32
|
+
lg: 1.125,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
onLoad() {
|
|
37
|
+
let udixioCssPath = this.options.styleFilePath;
|
|
38
|
+
|
|
39
|
+
const projectRoot = findProjectRoot(path.resolve());
|
|
40
|
+
|
|
41
|
+
if (!udixioCssPath) {
|
|
42
|
+
const searchPattern = /@import ["']tailwindcss["'];/;
|
|
43
|
+
const replacement = `@import 'tailwindcss';\n@import "./udixio.css";`;
|
|
44
|
+
|
|
45
|
+
const tailwindCssPath = findTailwindCssFile(projectRoot, searchPattern);
|
|
46
|
+
udixioCssPath = path.join(tailwindCssPath, '../udixio.css');
|
|
47
|
+
|
|
48
|
+
console.log('rrgfgt', tailwindCssPath, udixioCssPath);
|
|
49
|
+
|
|
50
|
+
if (!getFileContent(tailwindCssPath, /@import\s+"\.\/udixio\.css";/)) {
|
|
51
|
+
replaceFileContent(tailwindCssPath, searchPattern, replacement);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const colors: Record<
|
|
56
|
+
string,
|
|
57
|
+
{
|
|
58
|
+
light: string;
|
|
59
|
+
dark: string;
|
|
60
|
+
}
|
|
61
|
+
> = {};
|
|
62
|
+
|
|
63
|
+
for (const isDark of [false, true]) {
|
|
64
|
+
this.api.themes.update({ isDark: isDark });
|
|
65
|
+
for (const [key, value] of this.api.colors.getColors().entries()) {
|
|
66
|
+
const newKey = key
|
|
67
|
+
.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2')
|
|
68
|
+
.toLowerCase();
|
|
69
|
+
colors[newKey] ??= { light: '', dark: '' };
|
|
70
|
+
colors[newKey][isDark ? 'dark' : 'light'] = value.getHex();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const { fontStyles, fontFamily } = this.api.plugins
|
|
75
|
+
.getPlugin(FontPlugin)
|
|
76
|
+
.getInstance()
|
|
77
|
+
.getFonts();
|
|
78
|
+
|
|
79
|
+
const configCss: ConfigCss = {
|
|
80
|
+
colorKeys: Object.keys(colors).join(', '),
|
|
81
|
+
fontStyles: Object.entries(fontStyles)
|
|
82
|
+
.map(([fontRole, fontStyle]) =>
|
|
83
|
+
Object.entries(fontStyle)
|
|
84
|
+
.map(
|
|
85
|
+
([fontSize, fontStyle]) =>
|
|
86
|
+
`${fontRole}-${fontSize} ${Object.entries(fontStyle)
|
|
87
|
+
.map(([name, value]) => `${name}[${value}]`)
|
|
88
|
+
.join(' ')}`,
|
|
89
|
+
)
|
|
90
|
+
.join(', '),
|
|
91
|
+
)
|
|
92
|
+
.join(', '),
|
|
93
|
+
responsiveBreakPoints: Object.entries(
|
|
94
|
+
this.options.responsiveBreakPoints ?? {},
|
|
95
|
+
)
|
|
96
|
+
.map(([key, value]) => `${key} ${value}`)
|
|
97
|
+
.join(', '),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
createOrUpdateFile(
|
|
101
|
+
udixioCssPath,
|
|
102
|
+
`
|
|
103
|
+
@plugin "@udixio/tailwind" {
|
|
104
|
+
colorKeys: ${configCss.colorKeys};
|
|
105
|
+
fontStyles: ${configCss.fontStyles};
|
|
106
|
+
responsiveBreakPoints: ${configCss.responsiveBreakPoints};
|
|
107
|
+
}
|
|
108
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
109
|
+
@theme {
|
|
110
|
+
--color-*: initial;
|
|
111
|
+
${Object.entries(colors)
|
|
112
|
+
.map(([key, value]) => `--color-${key}: ${value.light};`)
|
|
113
|
+
.join('\n ')}
|
|
114
|
+
}
|
|
115
|
+
@layer theme {
|
|
116
|
+
.dark {
|
|
117
|
+
${Object.entries(colors)
|
|
118
|
+
.map(([key, value]) => `--color-${key}: ${value.dark};`)
|
|
119
|
+
.join('\n ')}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
@theme {
|
|
123
|
+
${Object.entries(fontFamily)
|
|
124
|
+
.map(
|
|
125
|
+
([key, values]) =>
|
|
126
|
+
`--font-${key}: ${values.map((value) => `"${value}"`).join(', ')};`,
|
|
127
|
+
)
|
|
128
|
+
.join('\n ')}
|
|
129
|
+
}
|
|
130
|
+
`,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"baseUrl": ".",
|
|
5
|
+
"rootDir": "src",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo",
|
|
8
|
+
"emitDeclarationOnly": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"types": [
|
|
11
|
+
"node",
|
|
12
|
+
"vite/client"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"include": [
|
|
16
|
+
"src/**/*.ts"
|
|
17
|
+
],
|
|
18
|
+
"references": [
|
|
19
|
+
{
|
|
20
|
+
"path": "../theme/tsconfig.lib.json"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"exclude": [
|
|
24
|
+
"vite.config.ts",
|
|
25
|
+
"vite.config.mts",
|
|
26
|
+
"vitest.config.ts",
|
|
27
|
+
"vitest.config.mts",
|
|
28
|
+
"src/**/*.test.ts",
|
|
29
|
+
"src/**/*.spec.ts",
|
|
30
|
+
"src/**/*.test.tsx",
|
|
31
|
+
"src/**/*.spec.tsx",
|
|
32
|
+
"src/**/*.test.js",
|
|
33
|
+
"src/**/*.spec.js",
|
|
34
|
+
"src/**/*.test.jsx",
|
|
35
|
+
"src/**/*.spec.jsx"
|
|
36
|
+
]
|
|
37
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./out-tsc/vitest",
|
|
5
|
+
"types": [
|
|
6
|
+
"vitest/globals",
|
|
7
|
+
"vitest/importMeta",
|
|
8
|
+
"vite/client",
|
|
9
|
+
"node",
|
|
10
|
+
"vitest"
|
|
11
|
+
],
|
|
12
|
+
"module": "nodenext",
|
|
13
|
+
"moduleResolution": "nodenext",
|
|
14
|
+
"forceConsistentCasingInFileNames": true
|
|
15
|
+
},
|
|
16
|
+
"include": [
|
|
17
|
+
"vite.config.ts",
|
|
18
|
+
"vite.config.mts",
|
|
19
|
+
"vitest.config.ts",
|
|
20
|
+
"vitest.config.mts",
|
|
21
|
+
"src/**/*.test.ts",
|
|
22
|
+
"src/**/*.spec.ts",
|
|
23
|
+
"src/**/*.test.tsx",
|
|
24
|
+
"src/**/*.spec.tsx",
|
|
25
|
+
"src/**/*.test.js",
|
|
26
|
+
"src/**/*.spec.js",
|
|
27
|
+
"src/**/*.test.jsx",
|
|
28
|
+
"src/**/*.spec.jsx",
|
|
29
|
+
"src/**/*.d.ts"
|
|
30
|
+
],
|
|
31
|
+
"references": [
|
|
32
|
+
{
|
|
33
|
+
"path": "./tsconfig.lib.json"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/// <reference types='vitest' />
|
|
2
|
+
import { defineConfig } from 'vite';
|
|
3
|
+
import dts from 'vite-plugin-dts';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
|
|
6
|
+
export default defineConfig(() => ({
|
|
7
|
+
root: __dirname,
|
|
8
|
+
cacheDir: '../../node_modules/.vite/packages/tailwind',
|
|
9
|
+
plugins: [
|
|
10
|
+
dts({
|
|
11
|
+
entryRoot: 'src',
|
|
12
|
+
tsconfigPath: path.join(__dirname, 'tsconfig.lib.json'),
|
|
13
|
+
}),
|
|
14
|
+
],
|
|
15
|
+
// Uncomment this if you are using workers.
|
|
16
|
+
// worker: {
|
|
17
|
+
// plugins: [ nxViteTsPaths() ],
|
|
18
|
+
// },
|
|
19
|
+
// Configuration for building your library.
|
|
20
|
+
// See: https://vitejs.dev/guide/build.html#library-mode
|
|
21
|
+
build: {
|
|
22
|
+
outDir: './dist',
|
|
23
|
+
emptyOutDir: true,
|
|
24
|
+
reportCompressedSize: true,
|
|
25
|
+
ssr: true,
|
|
26
|
+
commonjsOptions: {
|
|
27
|
+
transformMixedEsModules: true,
|
|
28
|
+
},
|
|
29
|
+
lib: {
|
|
30
|
+
// Could also be a dictionary or array of multiple entry points.
|
|
31
|
+
entry: 'src/index.ts',
|
|
32
|
+
name: '@udixio/tailwind',
|
|
33
|
+
fileName: 'index',
|
|
34
|
+
// Change this to the formats you want to support.
|
|
35
|
+
// Don't forget to update your package.json as well.
|
|
36
|
+
formats: ['es' as const, 'cjs' as const],
|
|
37
|
+
},
|
|
38
|
+
rollupOptions: {
|
|
39
|
+
// External packages that should not be bundled into your library.
|
|
40
|
+
external: ['tailwindcss', '@udixio/theme'],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
test: {
|
|
44
|
+
watch: false,
|
|
45
|
+
globals: true,
|
|
46
|
+
environment: 'node',
|
|
47
|
+
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
|
48
|
+
reporters: ['default'],
|
|
49
|
+
coverage: {
|
|
50
|
+
reportsDirectory: './test-output/vitest/coverage',
|
|
51
|
+
provider: 'v8' as const,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
}));
|
package/index.cjs
DELETED
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
3
|
-
const theme = require("@udixio/theme");
|
|
4
|
-
const plugin = require("tailwindcss/plugin");
|
|
5
|
-
const fs = require("fs");
|
|
6
|
-
const path = require("path");
|
|
7
|
-
const replaceInFile = require("replace-in-file");
|
|
8
|
-
function _interopNamespaceDefault(e) {
|
|
9
|
-
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
10
|
-
if (e) {
|
|
11
|
-
for (const k in e) {
|
|
12
|
-
if (k !== "default") {
|
|
13
|
-
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
14
|
-
Object.defineProperty(n, k, d.get ? d : {
|
|
15
|
-
enumerable: true,
|
|
16
|
-
get: () => e[k]
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
n.default = e;
|
|
22
|
-
return Object.freeze(n);
|
|
23
|
-
}
|
|
24
|
-
const fs__namespace = /* @__PURE__ */ _interopNamespaceDefault(fs);
|
|
25
|
-
const path__namespace = /* @__PURE__ */ _interopNamespaceDefault(path);
|
|
26
|
-
const createOrUpdateFile = (filePath, content) => {
|
|
27
|
-
try {
|
|
28
|
-
if (!fs__namespace.existsSync(filePath)) {
|
|
29
|
-
const dirPath = path__namespace.dirname(filePath);
|
|
30
|
-
if (!fs__namespace.existsSync(dirPath)) {
|
|
31
|
-
fs__namespace.mkdirSync(dirPath, { recursive: true });
|
|
32
|
-
}
|
|
33
|
-
fs__namespace.writeFileSync(filePath, content);
|
|
34
|
-
console.log(`✅ File successfully created: ${filePath}`);
|
|
35
|
-
} else {
|
|
36
|
-
console.log(`⚠️ File already exists: ${filePath}`);
|
|
37
|
-
replaceFileContent(filePath, /[\s\S]*/, content);
|
|
38
|
-
}
|
|
39
|
-
} catch (error) {
|
|
40
|
-
console.error("❌ Error while creating the file:", error);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
const getFileContent = (filePath, searchPattern) => {
|
|
44
|
-
try {
|
|
45
|
-
if (!fs__namespace.existsSync(filePath)) {
|
|
46
|
-
console.error(`❌ The specified file does not exist: ${filePath}`);
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
const fileContent = fs__namespace.readFileSync(filePath, "utf8");
|
|
50
|
-
if (searchPattern) {
|
|
51
|
-
if (typeof searchPattern === "string") {
|
|
52
|
-
const found = fileContent.includes(searchPattern) ? searchPattern : false;
|
|
53
|
-
console.log(
|
|
54
|
-
found ? `✅ The file contains the specified string: "${searchPattern}"` : `⚠️ The file does NOT contain the specified string: "${searchPattern}"`
|
|
55
|
-
);
|
|
56
|
-
return found;
|
|
57
|
-
} else {
|
|
58
|
-
const match = fileContent.match(searchPattern);
|
|
59
|
-
if (match) {
|
|
60
|
-
console.log(`✅ Found match: "${match[0]}"`);
|
|
61
|
-
return match[0];
|
|
62
|
-
} else {
|
|
63
|
-
console.log(
|
|
64
|
-
`⚠️ No match found for the pattern: "${searchPattern.toString()}"`
|
|
65
|
-
);
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
console.log(`✅ File content successfully retrieved.`);
|
|
71
|
-
return fileContent;
|
|
72
|
-
} catch (error) {
|
|
73
|
-
console.error("❌ An error occurred while processing the file:", error);
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
const replaceFileContent = (filePath, searchPattern, replacement) => {
|
|
78
|
-
try {
|
|
79
|
-
const results = replaceInFile.replaceInFileSync({
|
|
80
|
-
files: filePath,
|
|
81
|
-
from: searchPattern,
|
|
82
|
-
to: replacement
|
|
83
|
-
});
|
|
84
|
-
if (results.length > 0 && results[0].hasChanged) {
|
|
85
|
-
console.log(`✅ Content successfully replaced in the file: ${filePath}`);
|
|
86
|
-
} else {
|
|
87
|
-
console.log(
|
|
88
|
-
`⚠️ No replacement made. Here are some possible reasons:
|
|
89
|
-
- The pattern ${searchPattern} was not found.
|
|
90
|
-
- The file might already contain the expected content.`
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
} catch (error) {
|
|
94
|
-
console.error("❌ Error while replacing the file content:", error);
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
const findTailwindCssFile = (startDir, searchPattern) => {
|
|
98
|
-
const files = fs__namespace.readdirSync(startDir);
|
|
99
|
-
for (const file of files) {
|
|
100
|
-
const filePath = path__namespace.join(startDir, file);
|
|
101
|
-
const stats = fs__namespace.statSync(filePath);
|
|
102
|
-
if (stats.isDirectory()) {
|
|
103
|
-
const result = findTailwindCssFile(filePath, searchPattern);
|
|
104
|
-
if (result) return result;
|
|
105
|
-
} else if (file.endsWith(".css") || file.endsWith(".scss") || file.endsWith(".sass")) {
|
|
106
|
-
const content = fs__namespace.readFileSync(filePath, "utf8");
|
|
107
|
-
if (content.includes(searchPattern)) {
|
|
108
|
-
console.log("File found:\n", filePath);
|
|
109
|
-
return filePath;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return null;
|
|
114
|
-
};
|
|
115
|
-
const state = (colorKeys) => plugin((pluginArgs) => {
|
|
116
|
-
addAllNewComponents(
|
|
117
|
-
pluginArgs,
|
|
118
|
-
{
|
|
119
|
-
statePrefix: "state",
|
|
120
|
-
disabledStyles: {
|
|
121
|
-
textOpacity: 0.38,
|
|
122
|
-
backgroundOpacity: 0.12
|
|
123
|
-
},
|
|
124
|
-
transition: {
|
|
125
|
-
duration: 150
|
|
126
|
-
}
|
|
127
|
-
},
|
|
128
|
-
colorKeys
|
|
129
|
-
);
|
|
130
|
-
}, {});
|
|
131
|
-
const addAllNewComponents = ({ addComponents }, { statePrefix, disabledStyles, transition }, colorKeys) => {
|
|
132
|
-
const newComponents = {};
|
|
133
|
-
for (const isGroup of [false, true]) {
|
|
134
|
-
const group = isGroup ? "group-" : "";
|
|
135
|
-
for (const colorName of colorKeys) {
|
|
136
|
-
const className = `.${group}${statePrefix}-${colorName}`;
|
|
137
|
-
newComponents[className] = {
|
|
138
|
-
[`@apply ${group}hover:bg-${colorName}/[0.08]`]: {},
|
|
139
|
-
[`@apply ${group}active:bg-${colorName}/[0.12]`]: {},
|
|
140
|
-
[`@apply ${group}focus-visible:bg-${colorName}/[0.12]`]: {}
|
|
141
|
-
};
|
|
142
|
-
if (transition) {
|
|
143
|
-
newComponents[className][`@apply transition-colors`] = {};
|
|
144
|
-
newComponents[className][`@apply duration-${transition.duration}`] = {};
|
|
145
|
-
}
|
|
146
|
-
if (disabledStyles) {
|
|
147
|
-
newComponents[className][`@apply ${group}disabled:text-on-surface/[${disabledStyles.textOpacity}]`] = {};
|
|
148
|
-
newComponents[className][`@apply ${group}disabled:bg-on-surface/[${disabledStyles.backgroundOpacity}]`] = {};
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
for (const colorName of colorKeys) {
|
|
153
|
-
for (const stateName of ["hover", "active", "focus", "disabled"]) {
|
|
154
|
-
const className = `.${stateName}-${statePrefix}-${colorName}`;
|
|
155
|
-
if (stateName === "active" || stateName === "focus") {
|
|
156
|
-
newComponents[className] = {
|
|
157
|
-
[`@apply bg-${colorName}/[0.12]`]: {}
|
|
158
|
-
};
|
|
159
|
-
} else if (stateName === "hover") {
|
|
160
|
-
newComponents[className] = {
|
|
161
|
-
[`@apply bg-${colorName}/[0.08]`]: {}
|
|
162
|
-
};
|
|
163
|
-
} else if (stateName === "disabled") {
|
|
164
|
-
newComponents[className] = {
|
|
165
|
-
[`@apply text-on-surface/[${disabledStyles.textOpacity}]`]: {}
|
|
166
|
-
};
|
|
167
|
-
newComponents[className] = {
|
|
168
|
-
[`@apply bg-on-surface/[${disabledStyles.backgroundOpacity}]`]: {}
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
addComponents(newComponents);
|
|
174
|
-
};
|
|
175
|
-
const font = (fontStyles, responsiveBreakPoints) => {
|
|
176
|
-
const createUtilities = ({ theme: theme2 }) => {
|
|
177
|
-
const pixelUnit = "rem";
|
|
178
|
-
const newUtilities = {};
|
|
179
|
-
const baseTextStyle = (sizeValue) => ({
|
|
180
|
-
fontSize: sizeValue.fontSize + pixelUnit,
|
|
181
|
-
fontWeight: sizeValue.fontWeight,
|
|
182
|
-
lineHeight: sizeValue.lineHeight + pixelUnit,
|
|
183
|
-
letterSpacing: sizeValue.letterSpacing ? sizeValue.letterSpacing + pixelUnit : null,
|
|
184
|
-
fontFamily: theme2("fontFamily." + sizeValue.fontFamily)
|
|
185
|
-
});
|
|
186
|
-
const responsiveTextStyle = (sizeValue, breakPointName, breakPointRatio) => ({
|
|
187
|
-
[`@media (min-width: ${theme2("screens." + breakPointName, {})})`]: {
|
|
188
|
-
fontSize: sizeValue.fontSize * breakPointRatio + pixelUnit,
|
|
189
|
-
lineHeight: sizeValue.lineHeight * breakPointRatio + pixelUnit
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
for (const [roleName, roleValue] of Object.entries(fontStyles)) {
|
|
193
|
-
for (const [sizeName, sizeValue] of Object.entries(roleValue)) {
|
|
194
|
-
newUtilities[".text-" + roleName + "-" + sizeName] = {
|
|
195
|
-
...baseTextStyle(sizeValue),
|
|
196
|
-
...Object.entries(responsiveBreakPoints).reduce(
|
|
197
|
-
(acc, [breakPointName, breakPointRatio]) => {
|
|
198
|
-
acc = {
|
|
199
|
-
...acc,
|
|
200
|
-
...responsiveTextStyle(
|
|
201
|
-
sizeValue,
|
|
202
|
-
breakPointName,
|
|
203
|
-
breakPointRatio
|
|
204
|
-
)
|
|
205
|
-
};
|
|
206
|
-
return acc;
|
|
207
|
-
},
|
|
208
|
-
{}
|
|
209
|
-
)
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
return newUtilities;
|
|
214
|
-
};
|
|
215
|
-
return plugin(
|
|
216
|
-
({
|
|
217
|
-
addUtilities,
|
|
218
|
-
theme: theme2
|
|
219
|
-
}) => {
|
|
220
|
-
const newUtilities = createUtilities({ theme: theme2 });
|
|
221
|
-
addUtilities(newUtilities);
|
|
222
|
-
}
|
|
223
|
-
);
|
|
224
|
-
};
|
|
225
|
-
class TailwindPlugin extends theme.PluginAbstract {
|
|
226
|
-
constructor() {
|
|
227
|
-
super(...arguments);
|
|
228
|
-
this.dependencies = [theme.FontPlugin];
|
|
229
|
-
this.name = "tailwind";
|
|
230
|
-
this.pluginClass = TailwindImplPlugin;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
class TailwindImplPlugin extends theme.PluginImplAbstract {
|
|
234
|
-
onInit() {
|
|
235
|
-
var _a;
|
|
236
|
-
(_a = this.options).responsiveBreakPoints ?? (_a.responsiveBreakPoints = {
|
|
237
|
-
lg: 1.125
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
load() {
|
|
241
|
-
const searchKeyword = '@plugin "@udixio/tailwind"';
|
|
242
|
-
const tailwindCssPath = this.options.styleFilePath ?? findTailwindCssFile(process.cwd(), searchKeyword);
|
|
243
|
-
if (!tailwindCssPath) {
|
|
244
|
-
throw new Error(
|
|
245
|
-
'The style file containing the Udixio plugin was not found.\n Please use it first. (@plugin "@udixio/tailwind")'
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
const searchPattern = /@import ["']tailwindcss["'];/;
|
|
249
|
-
const replacement = `@import 'tailwindcss';
|
|
250
|
-
@import "./udixio.css";`;
|
|
251
|
-
if (!getFileContent(tailwindCssPath, /@import\s+"\.\/udixio\.css";/)) {
|
|
252
|
-
replaceFileContent(tailwindCssPath, searchPattern, replacement);
|
|
253
|
-
}
|
|
254
|
-
const cssFilePath = path.dirname(tailwindCssPath);
|
|
255
|
-
const colors = {};
|
|
256
|
-
for (const isDark of [false, true]) {
|
|
257
|
-
this.api.themes.update({ isDark });
|
|
258
|
-
for (const [key, value] of this.api.colors.getColors().entries()) {
|
|
259
|
-
const newKey = key.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2").toLowerCase();
|
|
260
|
-
colors[newKey] ?? (colors[newKey] = { light: "", dark: "" });
|
|
261
|
-
colors[newKey][isDark ? "dark" : "light"] = value.getHex();
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
const { fontStyles, fontFamily } = this.api.plugins.getPlugin(theme.FontPlugin).getInstance().getFonts();
|
|
265
|
-
createOrUpdateFile(
|
|
266
|
-
path.join(cssFilePath, "udixio.css"),
|
|
267
|
-
`
|
|
268
|
-
@custom-variant dark (&:where(.dark, .dark *));
|
|
269
|
-
@theme {
|
|
270
|
-
--color-*: initial;
|
|
271
|
-
${Object.entries(colors).map(([key, value]) => `--color-${key}: ${value.light};`).join("\n ")}
|
|
272
|
-
}
|
|
273
|
-
@layer theme {
|
|
274
|
-
.dark {
|
|
275
|
-
${Object.entries(colors).map(([key, value]) => `--color-${key}: ${value.dark};`).join("\n ")}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
`
|
|
279
|
-
);
|
|
280
|
-
const plugins = [
|
|
281
|
-
state(Object.keys(colors)),
|
|
282
|
-
font(fontStyles, this.options.responsiveBreakPoints)
|
|
283
|
-
];
|
|
284
|
-
return plugin.withOptions(
|
|
285
|
-
// 1) factory(options) → la fonction “handler” du plugin
|
|
286
|
-
(options = {}) => {
|
|
287
|
-
return async function(api) {
|
|
288
|
-
plugins.forEach((plugin2) => {
|
|
289
|
-
plugin2.handler(api);
|
|
290
|
-
});
|
|
291
|
-
};
|
|
292
|
-
},
|
|
293
|
-
// 2) config(options) → objet à merger dans tailwind.config
|
|
294
|
-
(options = {}) => {
|
|
295
|
-
return {
|
|
296
|
-
theme: {
|
|
297
|
-
fontFamily
|
|
298
|
-
}
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
const createTheme = async () => {
|
|
305
|
-
const app = await theme.bootstrapFromConfig();
|
|
306
|
-
const plugin2 = app.plugins.getPlugin(TailwindPlugin).getInstance();
|
|
307
|
-
return plugin2.load();
|
|
308
|
-
};
|
|
309
|
-
exports.TailwindPlugin = TailwindPlugin;
|
|
310
|
-
exports.createTheme = createTheme;
|
|
311
|
-
exports.default = createTheme;
|
|
312
|
-
exports.font = font;
|
|
313
|
-
exports.state = state;
|