-
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.
Merge pull request #16 from maxholman/refactor-variants
feat: major refactor
- Loading branch information
Showing
138 changed files
with
4,510 additions
and
3,574 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 |
---|---|---|
|
@@ -3,6 +3,4 @@ dist | |
build | ||
coverage | ||
|
||
bin/*.js | ||
bin/*.js | ||
vite.config.js | ||
*.tsbuildinfo |
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,13 @@ | ||
## Capsize SCSS | ||
|
||
MIT: https://github.com/seek-oss/capsize/blob/9d10414aa0597bb24101408b0b24c22d2ec58969/LICENSE | ||
|
||
@AndrewLeedham via https://github.com/seek-oss/capsize | ||
|
||
https://github.com/seek-oss/capsize/issues/24#issuecomment-679998850 | ||
|
||
## open-props-scss | ||
|
||
MIT: https://github.com/mayank99/open-props-scss/blob/ddd1275c3ee55a31a7759ddd5b88703174987d56/LICENSE | ||
|
||
https://github.com/mayank99/open-props-scss |
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,73 +1,81 @@ | ||
|
||
SRCS = $(wildcard lib/**) | ||
|
||
.DEFAULT_GOAL := all | ||
|
||
.PHONY: all | ||
all: build | ||
all: build dist build/global.scss build/global.css | ||
|
||
node_modules: package.json pnpm-lock.yaml | ||
pnpm install | ||
touch $@ | ||
|
||
bin/token.js: meta-bundle tsconfig-node.json | ||
pnpm exec tsc -p tsconfig-node.json | ||
dist/bin/token.js: build bin/token.ts ${SRCS} | ||
pnpm exec tsc -b | ||
|
||
build/global.css: dist/bin/token.js | ||
node dist/bin/token.js -t css > $@ | ||
|
||
build/global.scss: dist/bin/token.js | ||
node dist/bin/token.js -t scss > $@ | ||
|
||
build/tokens.scss: bin/token.js meta-bundle | ||
tsconfig.json: tsconfig-vite.src.json | ||
pnpm exec tsc -p tsconfig-vite.src.json --showConfig 1> $@ | ||
|
||
src/rds.module.scss: bin/token.js build | ||
node $< > $@ | ||
pnpm exec tsc -b tsconfig-node.json --clean | ||
prettier --write $@ | ||
|
||
build: $(SRCS) node_modules vite.config.ts vite-env.d.ts | ||
NODE_ENV=production pnpm exec vite build | ||
touch $@ | ||
|
||
.PHONY: build-watch | ||
build-watch: $(SRCS) node_modules vite.config.ts vite-env.d.ts | ||
pnpm exec vite build --mode=development --clearScreen false | ||
touch -c build | ||
|
||
.PHONY: meta-bundle | ||
meta-bundle: $(SRCS) node_modules vite.config.ts types | ||
NODE_ENV=production pnpm vite build | ||
touch build | ||
.PHONY: dev-server | ||
dev-server: node_modules vite.config.ts | ||
pnpm exec vite dev --mode=development --clearScreen false | ||
|
||
build: meta-bundle build/tokens.scss | ||
PHONY: typecheck | ||
typecheck: node_modules tsconfig.json | ||
pnpm exec tsc -w --preserveWatchOutput | ||
|
||
.PHONY: dev | ||
dev: | ||
NODE_ENV=development $(MAKE) -j 3 build-watch dev-server typecheck | ||
|
||
.PHONY: debug | ||
debug: | ||
DEBUG_BUILD=1 $(MAKE) | ||
|
||
.PHONY: types | ||
types: node_modules tsconfig.json | ||
pnpm exec tsc --emitDeclarationOnly | ||
|
||
.PHONY: types-watch | ||
types-watch: node_modules tsconfig.json | ||
pnpm exec tsc --emitDeclarationOnly -w | ||
dist: node_modules tsconfig.json | ||
pnpm exec tsc | ||
|
||
.PHONY: clean | ||
clean: node_modules tsconfig.json | ||
pnpm exec tsc -b --clean | ||
pnpm exec tsc -b tsconfig-node.json --clean | ||
pnpm exec tsc -b tsconfig.node.json --clean | ||
rm -rf build dist | ||
|
||
.PHONY: clean | ||
distclean: clean | ||
rm -rf node_modules | ||
|
||
.PHONY: test | ||
test: node_modules tsconfig.json vite.config.ts | ||
$(MAKE) lint | ||
pnpm exec tsc --noEmit | ||
test: lint dist build | ||
DEBUG_BUILD=true pnpm vitest run | ||
$(MAKE) build | ||
pnpm bundlesize | ||
|
||
.PHONY: dev | ||
dev: | ||
$(MAKE) -j 2 types-watch dev-server | ||
|
||
.PHONY: dev-server | ||
dev-server: node_modules vite.config.ts | ||
pnpm vite dev | ||
DEBUG_BUILD=true pnpm vitest run | ||
pnpm exec bundlesize | ||
|
||
.PHONY: lint | ||
lint: node_modules | ||
pnpm eslint . | ||
pnpm prettier --check . | ||
pnpm exec eslint . | ||
pnpm exec prettier --check . | ||
|
||
.PHONY: pretty | ||
pretty: node_modules | ||
pnpm eslint --fix . | ||
pnpm prettier --write . | ||
|
||
tsconfig.json: tsconfig-vite.src.json | ||
pnpm exec tsc -p tsconfig-vite.src.json --showConfig > $@ | ||
pnpm exec eslint --fix . | ||
pnpm exec prettier --write . |
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,38 +1,182 @@ | ||
// eslint-disable-next-line import/no-extraneous-dependencies | ||
/* 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 { genericVars } from '#vars'; | ||
import { leafNodeMapper, type PathStr, type VarFn, vargEx } from './utils.js'; | ||
import { | ||
badgeVars, | ||
badgeVarsMapFnPrefix, | ||
buttonVars, | ||
buttonVarsMapFnPrefix, | ||
formControlTokens, | ||
formControlVarsMapFnPrefix, | ||
globalTokens, | ||
globalVars, | ||
globalVarsMapFnPrefix, | ||
panelTokens, | ||
panelVars, | ||
panelVarsMapFnPrefix, | ||
propsTokens, | ||
propsVars, | ||
propsVarsMapFnPrefix, | ||
} from '#vars'; | ||
|
||
function isPlainObject(obj: unknown): obj is Record<string, unknown> { | ||
return ( | ||
typeof obj === 'object' && | ||
obj !== null && | ||
Object.getPrototypeOf(obj) === Object.prototype | ||
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], | ||
); | ||
} | ||
|
||
function extractCustomPropertyNames( | ||
obj: Record<string, unknown>, | ||
prefix = '', | ||
): [string, string][] { | ||
return Object.entries(obj).flatMap(([key, value]): [string, string][] => { | ||
const k = prefix ? `${prefix}${key}` : key; | ||
/** | ||
* tokens | ||
*/ | ||
const knownTokens = new Map<VarFn, { value: string }>(); | ||
|
||
if (isPlainObject(value)) { | ||
return extractCustomPropertyNames(value, `${k}-`); | ||
} | ||
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], | ||
); | ||
} | ||
|
||
// 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); | ||
|
||
const v = String(value); | ||
return [[k, v.match(/^var\((--\w+)\)$/)?.[1] || v]]; | ||
}); | ||
if (token) { | ||
knownTokens.set(varName, token); | ||
} | ||
} | ||
} | ||
} | ||
|
||
function writeScss([v, ident]: [string, string]) { | ||
process.stdout.write(`$${camelCase(v)}: ${ident};`); | ||
process.stdout.write('\n'); | ||
const output = new PassThrough(); | ||
if (outputPath) { | ||
output.pipe(createWriteStream(outputPath)); | ||
} else { | ||
output.pipe(process.stdout); | ||
} | ||
|
||
const propNames = extractCustomPropertyNames({ | ||
...genericVars, | ||
}); | ||
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`); | ||
|
||
/** | ||
* 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; | ||
} | ||
|
||
propNames.forEach(writeScss); | ||
output.end(); |
Oops, something went wrong.