anubis-ui 1.2.15 → 1.2.16
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/README.md +315 -249
- package/index.d.ts +10 -10
- package/index.html +20 -0
- package/package.json +10 -6
- package/src/config/colors.config.json +242 -9
- package/src/config/force.config.json +1 -0
- package/src/config/utilities.config.json +141 -0
- package/src/interfaces/color.interface.ts +9 -0
- package/src/interfaces/config.interface.ts +11 -15
- package/src/interfaces/preset.interface.ts +28 -11
- package/src/tools/config.tool.ts +27 -35
- package/src/tools/extraction/extractClasses.ts +164 -36
- package/src/tools/fileStuff/config.file.ts +44 -0
- package/src/tools/fileStuff/css.file.ts +47 -0
- package/src/tools/fileStuff/file.tools.ts +9 -12
- package/src/tools/logger.ts +4 -11
- package/src/tools/mapping/mapClassIntoRule.ts +329 -190
- package/src/tools/mapping/mapColorIntoDeclaration.ts +14 -0
- package/src/tools/output/css.output.ts +105 -0
- package/src/tools/validation/color.validation.ts +68 -0
- package/tests/README.md +54 -0
- package/tests/validation/color.validation.test.ts +182 -0
- package/tsconfig.json +11 -2
- package/vitest.config.ts +19 -0
- package/src/config/presets.config.json +0 -51
- package/src/config/qol.config.json +0 -78
- package/src/interfaces/declaration.interface.ts +0 -10
- package/src/tools/fileStuff/configFile.ts +0 -26
- package/src/tools/fileStuff/cssFile.ts +0 -37
|
@@ -1,56 +1,184 @@
|
|
|
1
|
-
import { getFiles } from
|
|
2
|
-
import { mapClassesIntoRules } from
|
|
3
|
-
import {
|
|
4
|
-
import { config } from
|
|
1
|
+
import { getFiles } from '@tools/fileStuff/file.tools';
|
|
2
|
+
import { mapClassesIntoRules } from '@tools/mapping/mapClassIntoRule';
|
|
3
|
+
import { writeCssRuleFile } from '@tools/fileStuff/css.file';
|
|
4
|
+
import { config } from '@tools/config.tool';
|
|
5
|
+
import { mapColorsIntoMixinDeclaration } from '@tools/mapping/mapColorIntoDeclaration';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import {
|
|
9
|
+
CLASS_COMMENT,
|
|
10
|
+
COLOR_COMMENT,
|
|
11
|
+
VARIANT_COMMENT,
|
|
12
|
+
} from '../output/css.output';
|
|
13
|
+
import { log } from '../logger';
|
|
14
|
+
|
|
15
|
+
// Cache for regex compilation
|
|
16
|
+
let cachedRegex: RegExp | null = null;
|
|
17
|
+
let cachedConfigHash: string | null = null;
|
|
18
|
+
|
|
19
|
+
// Performance optimization: limit concurrent file reads to avoid overwhelming the system
|
|
20
|
+
const MAX_CONCURRENT_FILE_READS = 10;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Execute promises with concurrency limit
|
|
24
|
+
* @param items - Items to process
|
|
25
|
+
* @param fn - Async function to execute for each item
|
|
26
|
+
* @param limit - Maximum number of concurrent operations
|
|
27
|
+
*/
|
|
28
|
+
const pLimit = async <T, R>(
|
|
29
|
+
items: T[],
|
|
30
|
+
fn: (item: T) => Promise<R>,
|
|
31
|
+
limit: number
|
|
32
|
+
): Promise<R[]> => {
|
|
33
|
+
const results: R[] = [];
|
|
34
|
+
let index = 0;
|
|
35
|
+
|
|
36
|
+
const executeNext = async (): Promise<void> => {
|
|
37
|
+
if (index >= items.length) return;
|
|
38
|
+
|
|
39
|
+
const currentIndex = index++;
|
|
40
|
+
const item = items[currentIndex];
|
|
41
|
+
const result = await fn(item);
|
|
42
|
+
results[currentIndex] = result;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const workers = Array(Math.min(limit, items.length))
|
|
46
|
+
.fill(null)
|
|
47
|
+
.map(async () => {
|
|
48
|
+
while (index < items.length) {
|
|
49
|
+
await executeNext();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
await Promise.all(workers);
|
|
54
|
+
return results;
|
|
55
|
+
};
|
|
7
56
|
|
|
8
57
|
/** Fetch vue file based on config target patterns */
|
|
9
58
|
const init = async () => {
|
|
10
|
-
|
|
59
|
+
// console.log({ config })
|
|
60
|
+
const files = await getFiles(config.files);
|
|
61
|
+
|
|
62
|
+
const uniqueClasses = await getUniqueClasses(files);
|
|
63
|
+
const { rules, variationsFromRules } = mapClassesIntoRules(uniqueClasses);
|
|
64
|
+
|
|
65
|
+
// Generate all colors from config (no filtering)
|
|
66
|
+
const mappedColors = `${COLOR_COMMENT}\n${mapColorsIntoMixinDeclaration(
|
|
67
|
+
config.colors
|
|
68
|
+
)}`;
|
|
69
|
+
|
|
70
|
+
// Get all variations that should be exported (export-variations: true)
|
|
71
|
+
const allExportVariations = getAllExportVariations();
|
|
72
|
+
|
|
73
|
+
// Merge used variations with all export variations (all export variations take priority)
|
|
74
|
+
const finalVariations = { ...variationsFromRules, ...allExportVariations };
|
|
75
|
+
|
|
76
|
+
// Generate CSS variables for variations
|
|
77
|
+
const variationsCss = Object.entries(finalVariations)
|
|
78
|
+
.map(([varName, varValue]) => ` --${varName}: ${varValue};`)
|
|
79
|
+
.join('\n');
|
|
11
80
|
|
|
12
|
-
|
|
13
|
-
|
|
81
|
+
const wrappedVariations = variationsCss
|
|
82
|
+
? `${VARIANT_COMMENT}\n:root {\n${variationsCss}\n}`
|
|
83
|
+
: '';
|
|
14
84
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
85
|
+
const wrappedRules = rules ? `${CLASS_COMMENT}\n${rules}` : '';
|
|
86
|
+
|
|
87
|
+
const file = writeCssRuleFile(mappedColors, wrappedVariations, wrappedRules);
|
|
88
|
+
return file;
|
|
89
|
+
};
|
|
18
90
|
|
|
19
91
|
/** Extract detected class and map into a flat set */
|
|
20
92
|
const getUniqueClasses = async (files: string[]): Promise<string[]> => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
93
|
+
// Use concurrency limit to avoid overwhelming the system on large codebases
|
|
94
|
+
const extractedClasses = (
|
|
95
|
+
await pLimit(files, extractClasses, MAX_CONCURRENT_FILE_READS)
|
|
96
|
+
).flat();
|
|
97
|
+
|
|
98
|
+
const classes = [...extractedClasses, ...config.force].sort();
|
|
99
|
+
|
|
100
|
+
const uniqueClasses = Array.from(new Set(classes));
|
|
101
|
+
return uniqueClasses;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/** Build regex pattern from config */
|
|
105
|
+
const buildClassDetectionRegex = (): RegExp => {
|
|
106
|
+
const { states, utilities } = config;
|
|
107
|
+
|
|
108
|
+
const partialUtilities = utilities?.map(u => {
|
|
109
|
+
const { prefix, variations: variationEntries, declaration } = u
|
|
110
|
+
|
|
111
|
+
if (!prefix && !variationEntries) {
|
|
112
|
+
log(`Something doesn't look good -> ${u}`)
|
|
113
|
+
}
|
|
114
|
+
const variations = Array.isArray(variationEntries) ? variationEntries : Object.keys(variationEntries || {})
|
|
115
|
+
const hasVariations = !!variations?.length
|
|
116
|
+
const hasDefaultVariation = hasVariations && variations.includes('default')
|
|
117
|
+
const needColor = declaration.includes('${color}')
|
|
118
|
+
|
|
119
|
+
/** If variation has default key, it's considered as a standalone - can be used solo */
|
|
120
|
+
if (hasVariations && hasDefaultVariation && !needColor) {
|
|
121
|
+
return `${prefix}`
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return `${prefix}-`
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
const mappedUtilities = [...partialUtilities]?.join('|')
|
|
128
|
+
const mappedStates = `(${states?.map(s => `${s}:`)?.join('|')})`;
|
|
129
|
+
|
|
130
|
+
const regexp = new RegExp(
|
|
131
|
+
`${mappedStates}?(${mappedUtilities})(-?(\\w+(-+)?)+)?`,
|
|
132
|
+
'g'
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
return regexp
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/** Get cached regex or build a new one if config changed */
|
|
139
|
+
const getClassDetectionRegex = (): RegExp => {
|
|
140
|
+
const { states, qol, utilitys } = config;
|
|
141
|
+
const configHash = JSON.stringify({ states, qol, utilitys });
|
|
25
142
|
|
|
26
|
-
|
|
143
|
+
if (cachedRegex && cachedConfigHash === configHash) {
|
|
144
|
+
return cachedRegex;
|
|
145
|
+
}
|
|
27
146
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
147
|
+
cachedRegex = buildClassDetectionRegex();
|
|
148
|
+
cachedConfigHash = configHash;
|
|
149
|
+
return cachedRegex;
|
|
150
|
+
};
|
|
31
151
|
|
|
32
152
|
/** Find matching classes from a given file based on config states and prefixes */
|
|
33
153
|
const extractClasses = async (filePath: string): Promise<string[]> => {
|
|
34
|
-
|
|
35
|
-
|
|
154
|
+
const file = await fs.promises.readFile(filePath, 'utf-8');
|
|
155
|
+
if (!file) {
|
|
156
|
+
return [];
|
|
157
|
+
}
|
|
36
158
|
|
|
37
|
-
|
|
159
|
+
const classDetectionRegex = getClassDetectionRegex();
|
|
160
|
+
const matches = file.match(classDetectionRegex) || [];
|
|
38
161
|
|
|
39
|
-
|
|
40
|
-
|
|
162
|
+
return matches;
|
|
163
|
+
};
|
|
41
164
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const mappedStates = `(${states?.map(s => `${s}:`)?.join('|')})`
|
|
165
|
+
/** Get all variations from utilitys with export-variations: true */
|
|
166
|
+
const getAllExportVariations = (): Record<string, string> => {
|
|
167
|
+
const allExportVariations: Record<string, string> = {};
|
|
168
|
+
const allUtilities = [...config.utilities];
|
|
47
169
|
|
|
48
|
-
|
|
170
|
+
for (const utility of allUtilities) {
|
|
171
|
+
if (utility['export-variations'] === true && utility.variations) {
|
|
172
|
+
for (const [variantName, variantValue] of Object.entries(
|
|
173
|
+
utility.variations
|
|
174
|
+
)) {
|
|
175
|
+
const variableName = `${utility.prefix}-${variantName}`;
|
|
176
|
+
allExportVariations[variableName] = variantValue as string;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
49
180
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
181
|
+
return allExportVariations;
|
|
182
|
+
};
|
|
53
183
|
|
|
54
|
-
export {
|
|
55
|
-
init
|
|
56
|
-
}
|
|
184
|
+
export { init };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { log } from '@tools/logger';
|
|
4
|
+
|
|
5
|
+
const userConfigPath = path.join(process.cwd(), 'anubis.config.json');
|
|
6
|
+
let userConfig = null;
|
|
7
|
+
|
|
8
|
+
const readUserConfigFile = () => {
|
|
9
|
+
const userConfigExists = fs.existsSync(userConfigPath);
|
|
10
|
+
|
|
11
|
+
if (!userConfigExists) {
|
|
12
|
+
log('No user config file found, using default configuration.');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const config = fs.readFileSync(userConfigPath, { encoding: 'utf-8' });
|
|
17
|
+
userConfig = JSON.parse(config);
|
|
18
|
+
|
|
19
|
+
return userConfig;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Print a warning if the config file has unknow keys */
|
|
23
|
+
const checkUserConfigFile = (configFile: string[]): void => {
|
|
24
|
+
if (!userConfig) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// todo - also check values
|
|
29
|
+
const userConfigKeys = Object.keys(userConfig);
|
|
30
|
+
|
|
31
|
+
const unknownKeys = userConfigKeys?.filter(
|
|
32
|
+
key => !configFile.includes(key) && key !== 'force'
|
|
33
|
+
);
|
|
34
|
+
if (!unknownKeys?.length) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
log(`${unknownKeys?.length} unknown config keys found in user config file`);
|
|
39
|
+
for (const key of unknownKeys) {
|
|
40
|
+
log(`- ${key}`);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export { userConfig, readUserConfigFile, checkUserConfigFile };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
import { log } from '@tools/logger';
|
|
5
|
+
import { getHeader } from '@tools/output/css.output';
|
|
6
|
+
|
|
7
|
+
const srcDir = path.join(process.cwd(), 'src', 'css');
|
|
8
|
+
const outputPath = path.join(srcDir, '_anubis.scss');
|
|
9
|
+
|
|
10
|
+
const checkCssRuleFilePresence = () => {
|
|
11
|
+
try {
|
|
12
|
+
fs.mkdirSync(srcDir, { recursive: true });
|
|
13
|
+
|
|
14
|
+
if (fs.existsSync(outputPath)) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
log('Output file missing, generating..');
|
|
19
|
+
fs.writeFileSync(outputPath, '');
|
|
20
|
+
} catch (err: any) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Erreur lors de la vérification du fichier CSS: ${err.message}`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const writeCssRuleFile = (
|
|
28
|
+
colors: string = '',
|
|
29
|
+
variants: string = '',
|
|
30
|
+
classes: string = ''
|
|
31
|
+
) => {
|
|
32
|
+
try {
|
|
33
|
+
checkCssRuleFilePresence();
|
|
34
|
+
|
|
35
|
+
const content = `${getHeader()}\n${colors}\n\n${variants}\n\n${classes}`;
|
|
36
|
+
|
|
37
|
+
fs.writeFileSync(outputPath, content);
|
|
38
|
+
|
|
39
|
+
return outputPath;
|
|
40
|
+
} catch (err: any) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
`Erreur lors de l'écriture du fichier CSS: ${err.message}`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export { writeCssRuleFile };
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { IFileConfig } from '../../interfaces/files.interface'
|
|
1
|
+
import fg from 'fast-glob';
|
|
2
|
+
import { IFileConfig } from '@interfaces/files.interface';
|
|
4
3
|
|
|
5
4
|
const getFiles = async (routeConfig: IFileConfig) => {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
5
|
+
return await fg(routeConfig.targets || '*.vue', {
|
|
6
|
+
absolute: true,
|
|
7
|
+
onlyFiles: true,
|
|
8
|
+
ignore: routeConfig.ignore || [],
|
|
9
|
+
});
|
|
10
|
+
};
|
|
12
11
|
|
|
13
|
-
export {
|
|
14
|
-
getFiles
|
|
15
|
-
}
|
|
12
|
+
export { getFiles };
|
package/src/tools/logger.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import packageJson from '../../package.json';
|
|
2
|
+
|
|
1
3
|
const logPrefix = '☯︎ [ANUBIS]'
|
|
2
4
|
const log = (str: string) => console.log(`${logPrefix} ${str}`)
|
|
3
5
|
|
|
4
|
-
const { version } = require('./../../package.json')
|
|
5
|
-
|
|
6
6
|
const logo = () => {
|
|
7
7
|
log(' ___ _ ____ ______ _________')
|
|
8
8
|
log(' / | / | / / / / / __ )/ _/ ___/')
|
|
@@ -10,21 +10,14 @@ const logo = () => {
|
|
|
10
10
|
log(' / ___ |/ /| / /_/ / /_/ // / ___/ /')
|
|
11
11
|
log('/_/ |_/_/ |_/\\____/_____/___//____/')
|
|
12
12
|
log('')
|
|
13
|
-
log(
|
|
13
|
+
log(`Welcome to Anubis v${packageJson.version}`)
|
|
14
14
|
log('Autonomous Nominative Utility Based Intuitive Styler')
|
|
15
15
|
log('---')
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
const cssHeader = `/*!
|
|
19
|
-
* * Anubis v.${version}
|
|
20
|
-
* * Improba
|
|
21
|
-
* * Released under the MIT License.
|
|
22
|
-
* */`
|
|
23
|
-
|
|
24
18
|
export {
|
|
25
19
|
logo,
|
|
26
|
-
cssHeader,
|
|
27
20
|
|
|
28
21
|
log,
|
|
29
22
|
logPrefix
|
|
30
|
-
}
|
|
23
|
+
}
|