-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
28 changed files
with
412 additions
and
227 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,106 +1,182 @@ | ||
// eslint-disable-next-line import/no-extraneous-dependencies | ||
// import { camelCase } from 'change-case'; | ||
import { createGlobalThemeMapFn } from '../lib/css-helpers.js'; | ||
/* eslint-disable import/no-extraneous-dependencies */ | ||
/* eslint-disable no-restricted-syntax */ | ||
import { createWriteStream } from 'node:fs'; | ||
import { PassThrough } from 'node:stream'; | ||
import { parseArgs } from 'node:util'; | ||
import { camelCase } from 'change-case'; | ||
import { leafNodeMapper, type PathStr, type VarFn, vargEx } from './utils.js'; | ||
import { | ||
badgeVars, | ||
badgeVarsMapFnPrefix, | ||
buttonVars, | ||
calloutVars, | ||
formControlVars, | ||
generalVars, | ||
buttonVarsMapFnPrefix, | ||
formControlTokens, | ||
formControlVarsMapFnPrefix, | ||
globalTokens, | ||
globalVars, | ||
} from '../lib/ve.css.js'; | ||
globalVarsMapFnPrefix, | ||
panelTokens, | ||
panelVars, | ||
panelVarsMapFnPrefix, | ||
propsTokens, | ||
propsVars, | ||
propsVarsMapFnPrefix, | ||
} from '#vars'; | ||
|
||
type JsonObject = { [Key in string]: string | JsonObject }; | ||
const { | ||
values: { type, outputPath }, | ||
} = parseArgs({ | ||
options: { | ||
type: { | ||
type: 'string', | ||
short: 't', | ||
default: 'css', | ||
}, | ||
outputPath: { | ||
type: 'string', | ||
short: 'o', | ||
}, | ||
}, | ||
}); | ||
/** | ||
* vars | ||
*/ | ||
const knownVars = new Map<VarFn, { path: PathStr }>(); | ||
for (const [vars, prefix] of [ | ||
[globalVars, globalVarsMapFnPrefix] as const, | ||
[propsVars, propsVarsMapFnPrefix] as const, | ||
[panelVars, panelVarsMapFnPrefix] as const, | ||
[badgeVars, badgeVarsMapFnPrefix] as const, | ||
[buttonVars, buttonVarsMapFnPrefix] as const, | ||
]) { | ||
leafNodeMapper( | ||
vars, | ||
(path, value) => { | ||
knownVars.set(value, { path }); | ||
}, | ||
[prefix], | ||
); | ||
} | ||
|
||
/** | ||
* tokens | ||
*/ | ||
const knownTokens = new Map<VarFn, { value: string }>(); | ||
|
||
function isPlainObject(obj: unknown): obj is Record<string, unknown> { | ||
return ( | ||
typeof obj === 'object' && | ||
obj !== null && | ||
Object.getPrototypeOf(obj) === Object.prototype | ||
for (const [vars, prefix] of [ | ||
[globalTokens, globalVarsMapFnPrefix] as const, | ||
[propsTokens, propsVarsMapFnPrefix] as const, | ||
[panelTokens, panelVarsMapFnPrefix] as const, | ||
[formControlTokens, formControlVarsMapFnPrefix] as const, | ||
]) { | ||
leafNodeMapper( | ||
vars, | ||
(path, value) => { | ||
knownTokens.set(path, { value }); | ||
}, | ||
[prefix], | ||
); | ||
} | ||
|
||
function flattenObject( | ||
obj: JsonObject, | ||
prefixFn: (value: string | null, path: string[]) => string, | ||
path: string[] = [], | ||
): [string, string][] { | ||
return Object.entries(obj).flatMap(([key, value]) => { | ||
const thisPrefix = prefixFn(key, path); | ||
if (isPlainObject(value)) { | ||
return flattenObject(value, prefixFn, [...path, key]); | ||
// resolved tokens, in case they have vars that reference each other | ||
for (const [varName, { value }] of knownTokens) { | ||
if (value.match(vargEx)) { | ||
const cssVar = knownVars.get(value); | ||
|
||
if (cssVar) { | ||
// get the value of the css var | ||
const token = knownTokens.get(cssVar.path); | ||
|
||
if (token) { | ||
knownTokens.set(varName, token); | ||
} | ||
} | ||
return [[thisPrefix, value]]; | ||
}); | ||
} | ||
} | ||
|
||
// function writeScssCssVars([cssVarFunctionPrefix, bareCssVar]: [ | ||
// string, | ||
// string, | ||
// ]) { | ||
// process.stdout.write(`$${camelCase(cssVarFunctionPrefix)}: ${bareCssVar};`); | ||
// process.stdout.write('\n'); | ||
// } | ||
|
||
const vars = flattenObject( | ||
{ | ||
...globalVars, | ||
...generalVars, | ||
...badgeVars, | ||
...buttonVars, | ||
...calloutVars, | ||
...formControlVars, | ||
}, | ||
createGlobalThemeMapFn('geklol'), | ||
); | ||
const output = new PassThrough(); | ||
if (outputPath) { | ||
output.pipe(createWriteStream(outputPath)); | ||
} else { | ||
output.pipe(process.stdout); | ||
} | ||
|
||
const autoGeneratedBanner = [ | ||
'This file is auto-generated.', | ||
'', | ||
'Do not edit this file directly.', | ||
'', | ||
`Date: ${new Date().toISOString()} by ${process.env.USER}`, | ||
]; | ||
|
||
output.write(`/* | ||
* ${autoGeneratedBanner.join('\n * ')} | ||
*/\n`); | ||
|
||
/** | ||
* { | ||
'color.brand': 'var(--color-brand)', | ||
'color.accent': 'var(--color-accent)', | ||
'color.fgColor': 'var(--color-fgColor)', | ||
'color.bgColor': 'var(--color-bgColor)', | ||
'color.borderColor': 'var(--color-borderColor)', | ||
'color.muted.fgColor': 'var(--color-muted-fgColor)', | ||
'color.muted.bgColor': 'var(--color-muted-bgColor)', | ||
'color.muted.borderColor': 'var(--color-muted-borderColor)', | ||
'color.emphasis.fgColor': 'var(--colo | ||
*/ | ||
console.log({ vars }); | ||
|
||
// console.log(flattenObject(globalVarTokens, 'globals')); | ||
// console.log(flattenObject(generalTokens, 'general')); | ||
// console.log(flattenObject(panelTokens, 'panel')); | ||
// console.log(flattenObject(formControlTokens, 'formControl')); | ||
|
||
// const tokens = flattenObject( | ||
// { | ||
// ...generalTokens, | ||
// ...panelTokens, | ||
// ...formControlTokens, | ||
// }, | ||
// 'yay-tokens', | ||
// ); | ||
|
||
// console.log({ tokens }); | ||
|
||
// vars.forEach(writeScssCssVars); | ||
|
||
// function writeScssDefaults([cssVarFunctionPrefix, bareCssVar]: [ | ||
// string, | ||
// string, | ||
// ]) { | ||
// process.stdout.write(`$${camelCase(cssVarFunctionPrefix)}: ${bareCssVar};`); | ||
// process.stdout.write('\n'); | ||
// } | ||
|
||
// const tokens = extractTokens({ | ||
// ...globalVarTokens, | ||
// ...panelTokens, | ||
// ...formControlTokens, | ||
// }); | ||
|
||
// tokens.forEach(writeScssDefaults); | ||
|
||
// --- | ||
// --- | ||
* Output | ||
* */ | ||
switch (type) { | ||
case 'scss': { | ||
const defaults: string[] = []; | ||
const definitions: string[] = []; | ||
|
||
for (const [varFn, { path }] of knownVars) { | ||
const tokenValue = knownTokens.get(path); | ||
|
||
if (tokenValue) { | ||
const [, varName] = varFn.match(vargEx) || []; | ||
|
||
if (!varName) { | ||
throw new Error(`Could not extract var name from ${varFn}`); | ||
} | ||
const scssVar = camelCase(varName, { | ||
mergeAmbiguousCharacters: true, | ||
}); | ||
|
||
// output.write(` $${scssVar}: ${tokenValue.value} !default;\n`); | ||
// output.write(` --${varName}: #{$${scssVar}};\n\n`); | ||
|
||
defaults.push(`$${scssVar}: ${tokenValue.value} !default;`); | ||
definitions.push(`--${varName}: #{$${scssVar}};`); | ||
} else { | ||
output.write( | ||
`\n/* WARN: skipped ${varFn} as token path "${path}" is not known */\n`, | ||
); | ||
} | ||
} | ||
|
||
output.write(defaults.join('\n')); | ||
output.write('\n'); | ||
output.write(`@mixin tokens($args...) {\n`); | ||
output.write(definitions.map((d) => ` ${d}`).join('\n')); | ||
output.write('\n'); | ||
output.write(`}\n`); | ||
break; | ||
} | ||
case 'css': | ||
default: | ||
output.write(`:root {\n`); | ||
|
||
for (const [varFn, { path }] of knownVars) { | ||
const tokenValue = knownTokens.get(path); | ||
|
||
if (tokenValue) { | ||
const [, varName] = varFn.match(vargEx) || []; | ||
|
||
if (!varName) { | ||
throw new Error(`Could not extract var name from ${varFn}`); | ||
} | ||
|
||
output.write(`--${varName}: ${tokenValue.value};\n`); | ||
} else { | ||
output.write( | ||
`\n/* WARN: skipped ${varFn} as token path "${path}" is not known */\n`, | ||
); | ||
} | ||
} | ||
output.write(`}\n`); | ||
break; | ||
} | ||
|
||
output.end(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* eslint-disable no-restricted-syntax */ | ||
export type RecursiveStringStringObj = { | ||
[Key in string]: string | RecursiveStringStringObj; | ||
}; | ||
|
||
export type VarFn = string; | ||
export type PathPart = string; | ||
export type PathParts = PathPart[]; | ||
export type PathStr = PathPart | `${PathPart}.${PathPart}`; | ||
|
||
function pathToString(parts: PathParts) { | ||
return parts | ||
.filter((part, idx) => !(part === '' && idx === 0)) | ||
.join('.') as PathStr; | ||
} | ||
|
||
// extract the var names from the leaf nodes of panelVars | ||
// and add them to the knownVars map | ||
export function leafNodeMapper( | ||
obj: RecursiveStringStringObj, | ||
callback: (path: PathStr, value: string) => void, | ||
pathParts: string[] = [], | ||
) { | ||
for (const [key, value] of Object.entries(obj)) { | ||
const thisPath = [...pathParts, key]; | ||
if (typeof value === 'string') { | ||
callback(pathToString(thisPath), value); | ||
} else { | ||
leafNodeMapper(value, callback, thisPath); | ||
} | ||
} | ||
} | ||
|
||
export const vargEx = /var\(--(.*)\)/; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.