anubis-ui 1.0.14 → 1.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Improba
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -20,6 +20,7 @@ AnubisUI (Autonomous Nominative Utility Based Intuitive Styler) is a Vite plugin
20
20
  5. [Prefix/Declaration relations](#prefixdeclaration-relations)
21
21
  6. [Architecture](#architecture)
22
22
  7. [Credits](#credits)
23
+ 8. [Licence](#licence)
23
24
 
24
25
  ## Features
25
26
  - 🎨 Dynamic CSS generation based on utility classes
@@ -128,7 +129,55 @@ $background-opacity: (
128
129
  ## Configuration
129
130
  AnubisUI uses several configuration files located in the `src/config` directory:
130
131
  <br />
131
- Customisation from root file `anubis.config.json` will appear in future updates
132
+ To override default configuration, add a `anubis.config.json` in project root folder.
133
+ >Overriding completly replaces default configuration. You need to copy/paste it and add yours if you want to keep the default too.
134
+ <br />
135
+
136
+ For every config you want to change, add the corresponding section in your config file:
137
+
138
+ ```js
139
+ {
140
+ // files.config.json
141
+ "files": {
142
+ "targets": ["/.vue"],
143
+ "ignore": []
144
+ },
145
+
146
+ // colors.config.json
147
+ "colors": ["primary", "secondary"],
148
+
149
+ // states.config.json
150
+ "states": ["hover"],
151
+
152
+ // qol.config.json
153
+ "qol": [
154
+ {
155
+ "prefix": "bg",
156
+ "declaration": "background: ${color}"
157
+ }
158
+ ],
159
+
160
+ // presets.config.json
161
+ "presets": [
162
+ {
163
+ "prefix": "border",
164
+ "declaration": "border-width: ${value} !important; border-color: ${color} !important; border-style: solid;",
165
+ "variations": {
166
+ "default": "4px",
167
+ "thin": "2px"
168
+ }
169
+ }
170
+ ]
171
+ }
172
+ ```
173
+ <sup>anubis.config.json (example)</sup>
174
+
175
+ Only the sections you want to override need to be included - other sections will use default values. Not every
176
+ > Presets is still unstable, use at your own risks
177
+ <br />
178
+ You __MUST__ use the exact same [presets](#presets-presetsconfigjson) names syntax to keep it working, but variations key/values can change.
179
+ <br />
180
+ Copy-paste is recommanded
132
181
 
133
182
  ---
134
183
  ### Colors (`colors.config.json`)
@@ -146,7 +195,6 @@ Define your color palette
146
195
  "warning",
147
196
  "danger"
148
197
  ]
149
- // no need to includes (lowest, lower, low, high, higher, highest) variants as long as the color name is present in the class
150
198
  ```
151
199
  </details>
152
200
 
@@ -168,69 +216,131 @@ Specify which files to scan for classes
168
216
  ---
169
217
  ### Presets (`presets.config.json`)
170
218
  Configure common style presets
219
+ > If overrided in config, default key/value are __REQUIRED__
220
+
171
221
  <details>
172
222
  <summary>Default config</summary>
173
223
 
174
224
  ```json
175
- {
176
- "border": [
177
- { "default": "4px" },
178
- { "thinest": "1px" },
179
- { "thiner": "2px" },
180
- { "thin": "3px" },
181
- { "thick": "6px" },
182
- { "thicker": "8px" },
183
- { "thickest": "10px" },
184
- ],
185
-
186
- "innerBorder": [
187
- { "default": "4px" },
188
- { "thinest": "1px" },
189
- { "thiner": "2px" },
190
- { "thin": "3px" },
191
- { "thick": "6px" },
192
- { "thicker": "8px" },
193
- { "thickest": "10px" },
194
- ],
195
-
196
- "shadow": [
197
- { "default": "0px 0px 7px 1px" },
198
- { "densest": "0px 0px 3px 1px" },
199
- { "denser": "0px 0px 5px 1px" },
200
- { "dense": "0px 0px 5px 1px" },
201
- { "wide": "0px 0px 10px 1px" },
202
- { "wider": "0px 0px 15px 1px" },
203
- { "widest": "0px 0px 20px 1px" }
204
- ]
205
- }
225
+ [
226
+ {
227
+ "prefix": "bg",
228
+ "declaration": "background: ${color}"
229
+ },
230
+ {
231
+ "prefix": "border",
232
+ "declaration": "border-width: ${value} !important; border-color: ${color} !important; border-style: solid;",
233
+ "variations": {
234
+ "default": "4px",
235
+ "thinest": "1px",
236
+ "thiner": "2px",
237
+ "thin": "3px",
238
+ "thick": "6px",
239
+ "thicker": "8px",
240
+ "thickest": "10px",
241
+ "node": "0.2rem"
242
+ }
243
+ },
244
+ {
245
+ "prefix": "inner-border",
246
+ "declaration": "box-shadow: inset 0px 0px 0px ${value} ${color}",
247
+ "variations": {
248
+ "default": "4px",
249
+ "thinest": "1px",
250
+ "thiner": "2px",
251
+ "thin": "3px",
252
+ "thick": "6px",
253
+ "thicker": "8px",
254
+ "thickest": "10px",
255
+ "node": "0.2rem"
256
+ }
257
+ },
258
+ {
259
+ "prefix": "shadow",
260
+ "declaration": "box-shadow: ${value} ${color}",
261
+ "variations": {
262
+ "default": "0px 0px 7px 1px",
263
+ "densest": "0px 0px 3px 1px",
264
+ "denser": "0px 0px 5px 1px",
265
+ "dense": "0px 0px 5px 1px",
266
+ "wide": "0px 0px 10px 1px",
267
+ "wider": "0px 0px 15px 1px",
268
+ "widest": "0px 0px 20px 1px"
269
+ }
270
+ }
271
+ ]
206
272
  ```
207
273
  </details>
208
274
 
209
-
210
275
  ---
211
- ### Selectors (`selectors.config.json`)
212
- Define available states and style prefixes
213
-
276
+ ### Quality of Life (`qol.config.json`)
277
+ Define simple style rules that can have variations but don't require color values. These are CSS declarations that work independently.
214
278
  <details>
215
279
  <summary>Default config</summary>
216
280
 
217
281
  ```json
218
- {
219
- "states": [
220
- "hover",
221
- "not-hover"
222
- ],
223
- "prefixes": [
224
- "bg",
225
- "text",
226
- "border",
227
- "inner-border",
228
- "shadow"
229
- ]
230
- }
282
+ [
283
+ {
284
+ "prefix": "smooth",
285
+ "standalone": true,
286
+ "declaration": "transition-duration: ${value}",
287
+ "variations": {
288
+ "default": "0.1s",
289
+ "slowest": "0.5s",
290
+ "slower": "0.3s",
291
+ "slow": "0.2s",
292
+ "quick": "0.07s",
293
+ "quicker": "0.05s",
294
+ "quickest": "0.03s"
295
+ }
296
+ },
297
+ {
298
+ "prefix": "rounded",
299
+ "standalone": true,
300
+ "declaration": "border-radius: ${value}",
301
+ "variations": {
302
+ "default": "8px",
303
+ "square": "0px",
304
+ "xs": "2px",
305
+ "sm": "4px",
306
+ "md": "8px",
307
+ "lg": "12px",
308
+ "xl": "16px",
309
+ "very": "9999px",
310
+ "full": "50%",
311
+ "half": "100%"
312
+ }
313
+ },
314
+ {
315
+ "prefix": "border",
316
+ "declaration": "border-style: ${value}",
317
+ "variations": {
318
+ "solid": "solid",
319
+ "dashed": "dashed",
320
+ "dotted": "dotted"
321
+ }
322
+ }
323
+ ]
231
324
  ```
232
325
  </details>
233
326
 
327
+ QoL rules can have:
328
+ 1. A prefix
329
+ 2. A declaration that uses ${value} for variations
330
+ 3. Optional variations with default values
331
+ 4. Optional `standalone` flag to allow usage without variations
332
+
333
+ The `standalone` flag allows a QoL rule to be used without any variation. When set to `true`, the rule can be used:
334
+ - With a variation: `rounded-lg`, `smooth-slow`
335
+ - Without a variation: `rounded`, `smooth` (will use the default value)
336
+
337
+ When `standalone` is `false` or not specified, the rule must always include a variation (e.g., `border-dashed`).
338
+
339
+ Example usage:
340
+ ```html
341
+ <div class="rounded-lg smooth-slow border-dashed" />
342
+ <div class="rounded smooth" /> <!-- Works because these are standalone -->
343
+ ```
234
344
 
235
345
  ## Available Utility Classes
236
346
  #### Colors
@@ -240,31 +350,31 @@ Define available states and style prefixes
240
350
  - `inner-border-{color}` - Inner border color (inset box shadow, not compatible with `shadow-`)
241
351
  - `shadow-{color}` - Box shadow color (not compatible with `inner-border-`)
242
352
 
353
+ #### Presets variations
354
+ - `bg-{color}-{(10-90)}` - Background opacity
355
+ - `border-{color}-{presetKey}` - Border width
356
+ - `shadow-{color}-{presetKey}` - Box shadow spread
357
+ - `inner-border-{color}-{presetKey}` - Box shadow inset width
358
+
243
359
  #### States
244
360
  - `hover:{utility}` - Apply style on hover
245
361
  - `not-hover:{utility}` - Apply style when not hovering
246
362
 
247
- #### Presets
248
- - `bg-{color}-{(10-90)}` - Background opacity
249
- - `border-{color}-{preset}` - Border width
250
- - `shadow-{color}-{preset}` - Box shadow spread
251
- - `inner-border-{color}-{preset}` - Box shadow inset width
252
-
253
363
  ## Prefix/Declaration relations
254
364
  | prefix | declaration |
255
365
  |--------------|-----------------------------------------------------------------------------|
256
366
  | bg | `background: {color} important;` |
257
367
  | text | `color: {color} important;` |
258
- | border | `border-width: {variation \|\| default} !important; border-color: {color} !important; border-style: solid;` |
259
- | inner-border | `box-shadow: inset {variation \|\| default} {color} !important;` |
260
- | shadow | `box-shadow: {variation \|\| default} {color} !important;` |
368
+ | border | `border-width: {presetValue \|\| default} !important; border-color: {color} !important; border-style: solid;` |
369
+ | inner-border | `box-shadow: inset {presetValue \|\| default} {color} !important;` |
370
+ | shadow | `box-shadow: {presetValue \|\| default} {color} !important;` |
261
371
 
262
372
  ## Architecture
263
373
  ### Core Components
264
374
  - **Vite Plugin**: Handles file watching and build process
265
375
  - **Class Extractor**: Scans files for utility classes
266
376
  - **Rule Generator**: Converts utility classes to CSS rules
267
- - **Configuration System**: WIP - Manages user preferences and presets
377
+ - **Configuration System**: Manages user preferences and presets
268
378
 
269
379
  ### File Structure
270
380
  ```
@@ -272,11 +382,10 @@ Define available states and style prefixes
272
382
  ├── src/
273
383
  │ ├── config/ # Configuration files
274
384
  │ ├── interfaces/ # TypeScript interfaces
275
- │ ├── manual/ # Manually trigger anubis actions
276
385
  │ └── tools/
277
- ├── extract/ # Class extraction logic
278
- ├── declaration/ # WIP - Style declaration handlers
279
- └── mapping/ # Class to CSS rule mapping
386
+ ├── extraction/ # Class extraction logic
387
+ ├── fileStuff/ # File handling utilities
388
+ └── mapping/ # Class to CSS rule mapping
280
389
  └── index.js # Plugin entry point
281
390
  ```
282
391
 
@@ -285,4 +394,8 @@ Define available states and style prefixes
285
394
 
286
395
  This project was made possible thanks to the support and resources provided by [__Improba__](https://improba.fr/).
287
396
  <br />
288
- Their trust and commitment played a key role in bringing this idea to life, and i'm grateful for their involvement in making it happen.
397
+ Their trust and commitment played a key role in bringing this idea to life, and i'm grateful for their involvement in making it happen.
398
+
399
+ ## Licence
400
+
401
+ This project is licensed under the [MIT License](./LICENSE).
package/index.js CHANGED
@@ -1,13 +1,15 @@
1
1
  "use strict";
2
2
 
3
3
  const { init: initConfig } = require('./dist/config/config.tool');
4
- const { log, logPrefix } = require('./dist/tools/logger');
5
- const { init: initClassExtraction } = require('./dist/tools/extract/classes');
4
+ const { log, logPrefix, logo } = require('./dist/tools/logger');
5
+ const { init: initClassExtraction } = require('./dist/tools/extraction/extractClasses');
6
6
 
7
7
  /** List every imported colors across the projet */
8
8
  const colors = [];
9
9
 
10
10
  const init = async () => {
11
+ logo();
12
+
11
13
  console.time(`${logPrefix} Config initialized in`);
12
14
  initConfig();
13
15
  console.timeEnd(`${logPrefix} Config initialized in`);
@@ -19,19 +21,29 @@ const init = async () => {
19
21
  log('---');
20
22
  };
21
23
 
24
+ const refresh = async (file) => {
25
+ // console.log({ file });
26
+
27
+ // _ Prevent self change loop
28
+ // todo - add targets / ignore detection
29
+ if (file.endsWith('_anubis.scss')) { return }
30
+ if (file.endsWith('anubis.config.json')) {
31
+ log('Config file changed, restarting...')
32
+ init();
33
+ return
34
+ }
35
+
36
+ console.time(`${logPrefix} Refreshed in`);
37
+ await initClassExtraction();
38
+ console.timeEnd(`${logPrefix} Refreshed in`);
39
+ }
40
+
22
41
  function AnubisUI() {
23
42
  return {
24
43
  name: 'anubis-ui',
25
44
  configureServer(server) {
26
45
  server.watcher.on('change', async (file) => {
27
- console.log({ file });
28
-
29
- // _ Prevent self change loop
30
- if (file.endsWith('_anubis.scss')) { return }
31
-
32
- console.time(`${logPrefix} Refreshed in`);
33
- await initClassExtraction();
34
- console.timeEnd(`${logPrefix} Refreshed in`);
46
+ await refresh(file)
35
47
  });
36
48
  },
37
49
  async buildStart() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anubis-ui",
3
- "version": "1.0.14",
3
+ "version": "1.2.0",
4
4
  "description": "Class-based css generator",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -15,6 +15,7 @@
15
15
  "vue"
16
16
  ],
17
17
  "scripts": {
18
+ "dev": "npm run preinstall; node src/manual/build.js",
18
19
  "build": "tsc",
19
20
  "preinstall": "npm run build"
20
21
  },
@@ -24,7 +25,7 @@
24
25
  "url": "git+https://github.com/HugoLMTY/anubis-ui.git"
25
26
  },
26
27
  "author": "Improba",
27
- "license": "ISC",
28
+ "license": "MIT",
28
29
  "bugs": {
29
30
  "url": "https://github.com/HugoLMTY/anubis-ui/issues"
30
31
  },
@@ -1,62 +1,66 @@
1
- // import fs from 'fs'
2
- // import path from 'path'
1
+ import { IEnvConfig } from "../interfaces/config.interface";
2
+ import { readUserConfigFile, userConfig } from "../tools/fileStuff/configFile"
3
+ import { log } from "../tools/logger"
4
+
3
5
  const fs = require('fs')
4
6
  const path = require('path')
5
7
 
6
- import { log } from '../tools/logger'
7
-
8
- interface IPreset {
9
- [key: string]: string
10
- }
11
- interface IEnvConfig {
12
- files: {
13
- targets: string|string[],
14
- ignore: string[]
15
- },
16
- colors: string[],
17
- selectors: {
18
- states: string[],
19
- prefixes: string[]
20
- },
21
- presets: {
22
- 'border': IPreset[],
23
- 'inner-border': IPreset[],
24
- 'shadow': IPreset[],
25
- },
26
- [key: string]: any
27
- }
28
-
29
- const configFolder = path.join(__dirname, '..', '..', 'src', 'config');
30
- const configFiles = [
8
+ const anubisConfigFolder = path.join(__dirname, '..', '..', 'src', 'config');
9
+ const anubisConfigFiles = [
10
+ 'qol',
31
11
  'files',
32
12
  'colors',
13
+ 'states',
33
14
  'presets',
34
- 'selectors'
35
15
  ]
36
16
 
37
17
  const config = {
18
+ qol: [],
19
+ presets: [],
20
+
38
21
  files: { targets: [], ignore: [] },
39
22
  colors: [],
40
- selectors: { states: [], prefixes: [] },
41
- presets: {
42
- border: [],
43
- "inner-border": [],
44
- shadow: [],
45
- },
23
+ states: [],
46
24
  } as IEnvConfig
47
25
 
48
26
  const init = () => {
49
- for (const file of configFiles) {
50
- const filePath = path.join(configFolder, `${file}.config.json`)
27
+ readUserConfigFile()
28
+
29
+ checkUserConfig()
30
+
31
+ for (const file of anubisConfigFiles) {
32
+ let configToUse = null
51
33
 
52
- const configContent = fs.readFileSync(filePath, { encoding: 'utf-8' })
34
+ if (userConfig && userConfig[file]) {
35
+ log(`${file} config found, overriding default.`)
36
+ configToUse = userConfig[file]
37
+ } else {
38
+ const filePath = path.join(anubisConfigFolder, `${file}.config.json`)
39
+ const configContent = fs.readFileSync(filePath, { encoding: 'utf-8' })
53
40
 
54
- config[file as keyof typeof config] = JSON.parse(configContent)
41
+ configToUse = JSON.parse(configContent)
42
+ }
43
+
44
+ config[file as keyof typeof config] = configToUse
55
45
  }
56
46
 
57
47
  return config
58
48
  }
59
49
 
50
+ const checkUserConfig = () => {
51
+
52
+ // todo - also check values
53
+ const userConfigKeys = Object.keys(userConfig)
54
+
55
+ const unknownKeys = userConfigKeys?.filter(key => !anubisConfigFiles.includes(key))
56
+ if (!unknownKeys?.length) { return }
57
+
58
+ log(`${unknownKeys?.length} unknown config keys found in user config file`)
59
+ for (const key of unknownKeys) {
60
+ log(`- ${key}`)
61
+ }
62
+ }
63
+
60
64
  export {
61
65
  init,
62
66
  config
@@ -1,33 +1,47 @@
1
- {
2
- "border": [
3
- { "default": "4px" },
4
- { "thinest": "1px" },
5
- { "thiner": "2px" },
6
- { "thin": "3px" },
7
- { "thick": "6px" },
8
- { "thicker": "8px" },
9
- { "thickest": "10px" },
10
- { "node": "0.2rem" }
11
- ],
12
-
13
- "innerBorder": [
14
- { "default": "4px" },
15
- { "thinest": "1px" },
16
- { "thiner": "2px" },
17
- { "thin": "3px" },
18
- { "thick": "6px" },
19
- { "thicker": "8px" },
20
- { "thickest": "10px" },
21
- { "node": "0.2rem" }
22
- ],
23
-
24
- "shadow": [
25
- { "default": "0px 0px 7px 1px" },
26
- { "densest": "0px 0px 3px 1px" },
27
- { "denser": "0px 0px 5px 1px" },
28
- { "dense": "0px 0px 5px 1px" },
29
- { "wide": "0px 0px 10px 1px" },
30
- { "wider": "0px 0px 15px 1px" },
31
- { "widest": "0px 0px 20px 1px" }
32
- ]
33
- }
1
+ [
2
+ {
3
+ "prefix": "bg",
4
+ "declaration": "background: ${color}"
5
+ },
6
+ {
7
+ "prefix": "border",
8
+ "declaration": "border-width: ${value} !important; border-color: ${color} !important; border-style: solid;",
9
+ "variations": {
10
+ "default": "4px",
11
+ "thinest": "1px",
12
+ "thiner": "2px",
13
+ "thin": "3px",
14
+ "thick": "6px",
15
+ "thicker": "8px",
16
+ "thickest": "10px",
17
+ "node": "0.2rem"
18
+ }
19
+ },
20
+ {
21
+ "prefix": "inner-border",
22
+ "declaration": "box-shadow: inset 0px 0px 0px ${value} ${color}",
23
+ "variations": {
24
+ "default": "4px" ,
25
+ "thinest": "1px" ,
26
+ "thiner": "2px" ,
27
+ "thin": "3px" ,
28
+ "thick": "6px" ,
29
+ "thicker": "8px" ,
30
+ "thickest": "10px" ,
31
+ "node": "0.2rem"
32
+ }
33
+ },
34
+ {
35
+ "prefix": "shadow",
36
+ "declaration": "box-shadow: ${value} ${color}",
37
+ "variations": {
38
+ "default": "0px 0px 7px 1px" ,
39
+ "densest": "0px 0px 3px 1px" ,
40
+ "denser": "0px 0px 5px 1px" ,
41
+ "dense": "0px 0px 5px 1px" ,
42
+ "wide": "0px 0px 10px 1px" ,
43
+ "wider": "0px 0px 15px 1px" ,
44
+ "widest": "0px 0px 20px 1px"
45
+ }
46
+ }
47
+ ]
@@ -0,0 +1,42 @@
1
+ [
2
+ {
3
+ "prefix": "smooth",
4
+ "standalone": true,
5
+ "declaration": "transition-duration: ${value}",
6
+ "variations": {
7
+ "default": "0.1s",
8
+ "slowest": "0.5s",
9
+ "slower": "0.3s",
10
+ "slow": "0.2s",
11
+ "quick": "0.07s",
12
+ "quicker": "0.05s",
13
+ "quickest": "0.03s"
14
+ }
15
+ },
16
+ {
17
+ "prefix": "rounded",
18
+ "standalone": true,
19
+ "declaration": "border-radius: ${value}",
20
+ "variations": {
21
+ "default": "8px",
22
+ "square": "0px",
23
+ "xs": "2px",
24
+ "sm": "4px",
25
+ "md": "8px",
26
+ "lg": "12px",
27
+ "xl": "16s",
28
+ "very": "9999px",
29
+ "full": "50%",
30
+ "half": "100%"
31
+ }
32
+ },
33
+ {
34
+ "prefix": "border",
35
+ "declaration": "border-style: ${value}",
36
+ "variations": {
37
+ "solid": "solid",
38
+ "dashed": "dashed",
39
+ "dotted": "dotted"
40
+ }
41
+ }
42
+ ]
@@ -0,0 +1,4 @@
1
+ [
2
+ "hover",
3
+ "not-hover"
4
+ ]
@@ -0,0 +1,16 @@
1
+ import { IPreset } from "./preset.interface"
2
+
3
+ export interface IFilePatterns {
4
+ targets: string|string[],
5
+ ignore: string[]
6
+ }
7
+
8
+ export interface IEnvConfig {
9
+ qol: IPreset[]
10
+ presets: IPreset[],
11
+ files: IFilePatterns,
12
+
13
+ colors: string[],
14
+ states: string[],
15
+ [key: string]: any
16
+ }
@@ -0,0 +1,18 @@
1
+ export interface IVariation {
2
+ [key: string]: string
3
+ }
4
+
5
+ export interface IPreset {
6
+ // [key: string]: string
7
+ prefix: string,
8
+ declaration: string,
9
+
10
+ /** When true, class can be called without variation, creating a rule with default variation */
11
+ standalone?: boolean,
12
+
13
+ /**In a quasar project (quasar.variabls.scss), will set as !default the precised key with the default variations */
14
+ globalVariableOverride?: string,
15
+
16
+ /** List of every possible variations */
17
+ variations: IVariation
18
+ }
@@ -0,0 +1,3 @@
1
+ const AnubisUI = require('../../index')
2
+
3
+ AnubisUI()?.buildStart()
@@ -1,22 +1,19 @@
1
- import { config } from "../../../config/config.tool"
2
- import { buildCssRuleFile } from "../../cssFile"
3
- import { mapClassesIntoRules } from "../../mapping/mapClassIntoRule"
4
- import { getFiles } from "../extract.tools"
1
+ import { getFiles } from "../fileStuff/file.tools"
2
+ import { mapClassesIntoRules } from "../mapping/mapClassIntoRule"
3
+ import { buildCssRuleFile } from "../fileStuff/cssFile"
4
+ import { config } from "../../config/config.tool"
5
5
 
6
- // import fs from 'fs'
7
6
  const fs = require('fs')
8
7
 
9
8
  /** Fetch vue file based on config target patterns */
10
9
  const init = async () => {
11
10
  const files = await getFiles(config.files)
12
- // console.log({ files })
13
11
 
14
12
  const uniqueClasses = await getUniqueClasses(files)
15
13
  const mappedRules = mapClassesIntoRules(uniqueClasses)
16
- // console.log({ uniqueClasses, mappedRules })
17
14
 
18
15
  const file = buildCssRuleFile(mappedRules)
19
- // console.log({ file })
16
+ return file
20
17
  }
21
18
 
22
19
  /** Extract detected class and map into a flat set */
@@ -28,8 +25,6 @@ const getUniqueClasses = async (files: string[]): Promise<string[]> => {
28
25
  ?.sort()
29
26
 
30
27
  const uniqueClasses = Array.from(new Set(extractedClasses))
31
- // log(`${uniqueClasses?.length} classes found`)
32
-
33
28
  return uniqueClasses
34
29
  }
35
30
 
@@ -38,16 +33,20 @@ const extractClasses = async (filePath: string): Promise<string[]> => {
38
33
  const file = await fs.promises.readFile(filePath, 'utf-8')
39
34
  if (!file) { return [] }
40
35
 
41
- const { states, prefixes } = config.selectors
36
+ const { states, qol, presets } = config
42
37
 
43
- const partialStates = `(${states?.map(s => `${s}:`)?.join('|')})`
44
- const partialPrefixes = `(${prefixes?.map(p => `${p}-`)?.join('|')})`
38
+ const partialPrefixes = presets?.map(p => `${p.prefix}-`)
39
+ const partialQol = qol?.map(q => `${q.prefix}`)
45
40
 
46
- const classDetectionRegex = new RegExp(`${partialStates}?${partialPrefixes}(\\w+(-+)?)+`, 'gi')
41
+ const mappedPrefixes = [
42
+ ...partialPrefixes,
43
+ ...partialQol
44
+ ]?.join('|')
45
+ const mappedStates = `(${states?.map(s => `${s}:`)?.join('|')})`
47
46
 
48
- const matches = file.match(classDetectionRegex)
49
- if (!matches?.length) { return [] }
47
+ const classDetectionRegex = new RegExp(`${mappedStates}?(${mappedPrefixes})(-?(\\w+(-+)?)+)?`, 'gi')
50
48
 
49
+ const matches = file.match(classDetectionRegex) || []
51
50
  return matches
52
51
  }
53
52
 
@@ -0,0 +1,26 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+
4
+ import { log } from '../logger'
5
+
6
+ const userConfigPath = path.join(process.cwd(), 'anubis.config.json')
7
+ let userConfig = null
8
+
9
+ const readUserConfigFile = () => {
10
+ const userConfigExists = fs.existsSync(userConfigPath)
11
+
12
+ if (!userConfigExists) {
13
+ log('No user config file found, using default configuration.')
14
+ return
15
+ }
16
+
17
+ const config = fs.readFileSync(userConfigPath, { encoding: 'utf-8'})
18
+ userConfig = JSON.parse(config)
19
+
20
+ return userConfig
21
+ }
22
+
23
+ export {
24
+ userConfig,
25
+ readUserConfigFile
26
+ }
@@ -1,9 +1,7 @@
1
- // import fs from 'fs'
2
- // import path from 'path'
3
1
  const fs = require('fs')
4
2
  const path = require('path')
5
3
 
6
- import { cssHeader, log } from './logger'
4
+ import { cssHeader, log } from '../logger'
7
5
 
8
6
  const srcDir = path.join(process.cwd(), 'src', 'css')
9
7
  const outputPath = path.join(srcDir, '_anubis.scss')
@@ -1,4 +1,3 @@
1
- // import fg from 'fast-glob'
2
1
  const fg = require('fast-glob')
3
2
 
4
3
  import { IFileConfig } from '../../interfaces/files.interface'
@@ -1,6 +1,8 @@
1
1
  const logPrefix = '☯︎ [ANUBIS]'
2
2
  const log = (str: string) => console.log(`${logPrefix} ${str}`)
3
3
 
4
+ const { version } = require('./../../package.json')
5
+
4
6
  const logo = () => {
5
7
  log(' ___ _ ____ ______ _________')
6
8
  log(' / | / | / / / / / __ )/ _/ ___/')
@@ -14,7 +16,7 @@ const logo = () => {
14
16
  }
15
17
 
16
18
  const cssHeader = `/*!
17
- * * Anubis v.1.0.13
19
+ * * Anubis v.${version}
18
20
  * * Improba
19
21
  * * Released under the MIT License.
20
22
  * */`
@@ -1,4 +1,5 @@
1
1
  import { config } from "../../config/config.tool"
2
+ import { IPreset } from "../../interfaces/preset.interface"
2
3
  import { log } from "../logger"
3
4
 
4
5
  const mapClassesIntoRules = (classes: string[]) => {
@@ -13,31 +14,52 @@ const mapClassesIntoRules = (classes: string[]) => {
13
14
  }
14
15
 
15
16
  const mapClassIntoRule = (stringClass: string) => {
16
- const colorExists = config.colors?.some(color => stringClass.includes(color))
17
- if (!colorExists) { return }
18
-
19
17
  const params = getClassInfos(stringClass)
20
- // console.log({ params })
21
18
 
22
- const rule = mapIntoRule(params)
23
- // console.log({ rule })
19
+ /**
20
+ * _ If no variations are found, maybe it's just a color like bg-primary
21
+ * _ So we need to check if the color exists to avoid useless computing
22
+ * */
23
+ if (!params.preset) {
24
+ const { colorExists } = checkOpacity(params.color)
25
+
26
+ if (!colorExists) {
27
+ return
28
+ }
29
+ }
30
+
31
+ /**
32
+ * _ If the current QoL isn't standalone (can be called without variation)
33
+ * _ no
34
+ */
35
+ if (!params.color && !params.preset?.standalone) {
36
+ return
37
+ }
24
38
 
39
+ const rule = mapIntoRule(params)
25
40
  return rule
26
41
  }
27
42
 
28
- const getClassInfos = (stringClass: string): { state?: string, prefix?: string, color: string, variation?: { key: string, value: string } } => {
43
+ const getClassInfos = (stringClass: string) => {
29
44
  const { cleanedClass, state } = getStateInfos(stringClass)
30
45
  const { cleanedColor, prefix } = getPrefixInfos(cleanedClass)
31
- const { color, variation } = getVariantInfos({ cleanedColor, prefix })
46
+ const { preset, variation } = getPresetInfos({ cleanedColor, prefix })
47
+
48
+ return {
49
+ state,
32
50
 
33
- // console.log({ state, color, prefix, variation })
34
- return { state, color, prefix, variation }
51
+ color: cleanedColor,
52
+ prefix,
53
+
54
+ preset,
55
+ variation
56
+ }
35
57
  }
36
58
 
37
59
  const getStateInfos = (stringClass: string) => {
38
60
  let state = undefined
39
61
 
40
- for (const configState of config.selectors?.states) {
62
+ for (const configState of config.states) {
41
63
  if (!stringClass.startsWith(configState)) { continue }
42
64
 
43
65
  state = configState
@@ -51,62 +73,67 @@ const getStateInfos = (stringClass: string) => {
51
73
  }
52
74
 
53
75
  const getPrefixInfos = (stringClass: string): { cleanedColor: string, prefix: string } => {
54
- let prefix = ''
76
+ const prefixes = [
77
+ ...config.presets?.map(q => q.prefix),
78
+ ...config.qol?.map(q => q.prefix)
79
+ ]
55
80
 
56
- for (const configPrefix of config.selectors.prefixes) {
57
- if (!stringClass.startsWith(configPrefix)) { continue }
81
+ for (const prefix of prefixes) {
82
+ if (!stringClass.startsWith(prefix)) { continue }
58
83
 
59
- prefix = configPrefix
84
+ return {
85
+ cleanedColor: stringClass?.slice(prefix.length + 1),
86
+ prefix
87
+ }
60
88
  }
61
89
 
62
- return {
63
- cleanedColor: stringClass?.slice(prefix.length + 1),
64
- prefix,
65
- }
90
+ return null
66
91
  }
67
92
 
68
- const getVariantInfos = ({ cleanedColor, prefix }: { cleanedColor: string, prefix?: string }): { color: string, variation?: { key: string, value: string } } => {
69
- // _ Handle opacity | bg-primary-10
70
- const opacityDetectionRegex = new RegExp(/(?:(\w-?)+)-\d{2}$/, 'gm') // Strings that end with two digits
71
- const isOpacity = opacityDetectionRegex.test(cleanedColor)
72
- if (isOpacity) {
93
+ const getPresetInfos = ({ cleanedColor, prefix }: { cleanedColor: string, prefix?: string }) => {
94
+ /**
95
+ * _ Find preset variants matching the prefix from the config
96
+ * _ Since a prefix can be in multiple presets and qol, filter every matching prefixes then flatten everything
97
+ * TODO fix first default occurence getting picked when duplicate
98
+ * */
99
+ const possiblePresets = [...config.presets, ...config.qol]
100
+ ?.filter(p => p.prefix === prefix)
101
+ ?.flat()
102
+ if (!possiblePresets?.length) { return { matchingPreset: null, variation: null } }
103
+
104
+ const { colorExists } = checkOpacity(cleanedColor)
105
+
106
+ /**
107
+ * Find the preset where the variations exists
108
+ * If the color exists, it is a preset, so use the preset
109
+ * */
110
+ const matchingPreset = colorExists || !cleanedColor
111
+ ? possiblePresets[0]
112
+ : possiblePresets?.find(({ variations }) => !variations || Object.keys(variations)?.find(v => cleanedColor.endsWith(v)))
113
+
114
+ if (!matchingPreset) {
115
+ log(`No preset found for ${cleanedColor || prefix}`)
116
+
73
117
  return {
74
- color: cleanedColor // Opacity is included in the color name, whoops on me for this one (and every other one tho)
75
- // color: cleanedColor?.slice(0, -3),
76
- // variation: {
77
- // key: 'opacity',
78
- // value: cleanedColor?.slice(-2),
79
- // }
118
+ matchingPreset,
119
+ variation: null
80
120
  }
81
121
  }
82
122
 
83
- // _ Find preset variants matching the prefix from the config
84
- const variants = config.presets[prefix as keyof typeof config.presets]
85
- if (!variants) { return { color: cleanedColor } }
86
-
87
- // _ Map found variants into a key/value object
88
- const matchingVariants = variants
89
- ?.map(v => Object.entries(v))
90
- ?.flat()
91
- ?.map(([key, value]) => ({ key, value }))
92
- // ?.filter(({ key }) => key !== 'default')
123
+ const possibleVariations = (matchingPreset.variations || { default: '' })
93
124
 
94
- const variation = matchingVariants?.find(({ key }) => cleanedColor.endsWith(key)) || matchingVariants?.find(({ key }) => key === 'default')
95
- // console.log({ variation })
125
+ const matchingVariation = Object.keys(possibleVariations)
126
+ ?.find(v => cleanedColor.endsWith(v)) || 'default'
96
127
 
97
- const color = variation && variation.key !== 'default'
98
- ? cleanedColor?.slice(0, variation.key.length + 1)
99
- : cleanedColor
128
+ const variation = possibleVariations[matchingVariation]
100
129
 
101
130
  return {
102
- color,
103
- variation
131
+ preset: matchingPreset,
132
+ variation,
104
133
  }
105
134
  }
106
135
 
107
- function mapIntoRule({ state, prefix, color, variation }: { state?: string, prefix?: string, color: string, variation?: { key: string, value: string } }) {
108
- const colorVar = `var(--${color})`
109
-
136
+ const mapIntoRule = ({ state, prefix, color, preset, variation }) => {
110
137
  // _ Set state selector
111
138
  let stateSelector = ''
112
139
  switch (state) {
@@ -119,50 +146,47 @@ function mapIntoRule({ state, prefix, color, variation }: { state?: string, pref
119
146
  break
120
147
  }
121
148
 
122
- let declaration = ''
123
-
124
- // _ Set prefix declaration
125
- // ! Don't forget to add the prefix in the selector.config root file
126
- switch (prefix) {
127
- case 'bg':
128
- declaration = `background: ${colorVar} !important`
129
- break
130
-
131
- case 'text':
132
- declaration = `color: ${colorVar} !important`
133
- break
134
-
135
- case 'border':
136
- declaration = `border-width: ${variation?.value} !important; border-color: ${colorVar} !important; border-style: solid`
137
- break
138
-
139
- case 'inner-border':
140
- declaration = `box-shadow: inset ${variation?.value} ${colorVar} !important`
141
- break
142
-
143
- case 'shadow':
144
- declaration = `box-shadow: ${variation?.value} ${colorVar} !important`
145
- break
149
+ let selector = `${prefix}${color ? `-${color}` : ''}`
150
+ if (state) {
151
+ selector = `${state}\\:${selector}${stateSelector}`
152
+ }
146
153
 
147
- default:
148
- // _ custom rule from config file
149
- const customDeclaration = config.customRules?.find((r: any) => r.prefix === prefix)?.declaration
150
- if (!customDeclaration) { return }
154
+ const colorVar = `var(--${color})`
155
+ let declaration = preset.declaration
156
+ ?.replace('${value}', variation)
157
+ ?.replace('${color}', colorVar)
151
158
 
152
- declaration = customDeclaration?.replace('${value}', color) + ' !important'
153
- break
159
+ if (!declaration.endsWith(';')) {
160
+ declaration += ';'
154
161
  }
155
162
 
156
- let selector = `${prefix}-${color}`
157
- if (state) {
158
- selector = `${state}\\:${selector}${stateSelector}`
163
+ if (!declaration.includes('!important')) {
164
+ declaration = declaration
165
+ ?.replace(';', ' !important;')
159
166
  }
160
167
 
161
168
  const rule = `.${selector} { ${declaration} }`
162
-
163
169
  return rule
164
170
  }
165
171
 
172
+ /**
173
+ * _ Check if a color includes opacity (ends with 2 digits)
174
+ * * Opacity is included in the color name during mixin declaration
175
+ * */
176
+ const checkOpacity = (color: string) => {
177
+ const opacityDetectionRegex = new RegExp(/(?:(\w-?)+)-\d{2}$/, 'gm') // Strings that end with two digits
178
+ const isOpacity = opacityDetectionRegex.test(color)
179
+
180
+ const baseColor = isOpacity ? color?.slice(0, -3) : color
181
+ const colorExists = config.colors?.some(configColor => configColor === baseColor)
182
+
183
+ return {
184
+ colorExists,
185
+ isOpacity,
186
+ baseColor
187
+ }
188
+ }
189
+
166
190
  export {
167
191
  mapClassesIntoRules,
168
192
  mapClassIntoRule
@@ -1,13 +0,0 @@
1
- {
2
- "states": [
3
- "hover",
4
- "not-hover"
5
- ],
6
- "prefixes": [
7
- "bg",
8
- "text",
9
- "border",
10
- "inner-border",
11
- "shadow"
12
- ]
13
- }
package/src/index.ts DELETED
@@ -1,43 +0,0 @@
1
- import { Plugin } from 'vite'
2
-
3
- import { init as initConfig } from './config/config.tool'
4
- import { log, logo, logPrefix } from './tools/logger'
5
- // import { init as initPresets } from './config/'
6
- import { init as initClassExtraction } from './tools/extract/classes'
7
-
8
- /** List every imported colors across the projet */
9
- const colors: string[] = []
10
-
11
- const init = async () => {
12
- console.time(`${logPrefix} Config initialized in`)
13
- initConfig()
14
- console.timeEnd(`${logPrefix} Config initialized in`)
15
- log('---')
16
-
17
- console.time(`${logPrefix} Rules generated in`)
18
- await initClassExtraction()
19
- console.timeEnd(`${logPrefix} Rules generated in`)
20
- log('---')
21
- }
22
-
23
- export default function AnubisUI (): Plugin {
24
- return {
25
- name: 'anubis-ui',
26
- configureServer(server: any) {
27
- server.watcher.on('change', (file: any) => {
28
- console.log({ file })
29
- })
30
- },
31
- async buildStart() {
32
- logo()
33
-
34
- console.time(`${logPrefix} Anubis initialized in`)
35
- await init()
36
- console.timeEnd(`${logPrefix} Anubis initialized in`)
37
- }
38
- }
39
- }
40
-
41
- export {
42
- colors
43
- }
@@ -1,19 +0,0 @@
1
- interface IConfigDeclaration {
2
- [key: string]: string,
3
- }
4
-
5
- export interface IConfigPresets {
6
- defaultInnerBorderWidth?: string
7
- innerBorderWidths?: IConfigDeclaration[]
8
-
9
- defaultBorderWidth?: string
10
- borderWidths?: IConfigDeclaration[]
11
-
12
- defaultShadow?: string,
13
- shadowWidths?: IConfigDeclaration[]
14
-
15
- defaultBorderRadius?: string
16
- borderRadiuses?: IConfigDeclaration[]
17
-
18
- fontWeights?: IConfigDeclaration[]
19
- }
@@ -1,4 +0,0 @@
1
- import AnubisUI from "..";
2
-
3
- //@ts-ignore
4
- AnubisUI()?.buildStart()
@@ -1,3 +0,0 @@
1
- import { IDeclaration } from "../../interfaces/declaration.interface";
2
-
3
- const declareColor = (options: { name: string, color: string, theme?: string }) => {}
@@ -1,5 +0,0 @@
1
- const colors = [
2
- { name: 'primary', color: '#0f84cb' },
3
- { name: 'secondary', color: '#3b5161' },
4
- { name: 'accent', color: '#0f84cb' },
5
- ]