Skip to content

Commit 71a4c5f

Browse files
committed
chore: wip
1 parent 5581a78 commit 71a4c5f

34 files changed

+3191
-187
lines changed

bun.lock

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,16 @@
44
"": {
55
"name": "headwind",
66
"devDependencies": {
7-
"@stacksjs/bumpx": "^0.1.17",
7+
"@stacksjs/bumpx": "^0.1.84",
88
"@stacksjs/docs": "^0.70.23",
99
"@stacksjs/eslint-config": "^4.14.0-beta.3",
1010
"@stacksjs/gitlint": "^0.1.5",
11-
"@stacksjs/logsmith": "^0.1.8",
12-
"@types/bun": "^1.2.18",
13-
"buddy-bot": "^0.8.9",
11+
"@stacksjs/logsmith": "^0.1.18",
12+
"@types/bun": "^1.2.23",
13+
"buddy-bot": "^0.9.7",
1414
"bun-git-hooks": "^0.2.19",
1515
"bun-plugin-dtsx": "^0.21.12",
16-
"typescript": "^5.8.3",
16+
"typescript": "^5.9.3",
1717
},
1818
},
1919
},
@@ -793,7 +793,7 @@
793793

794794
"browserslist": ["[email protected]", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="],
795795

796-
"buddy-bot": ["buddy-bot@0.8.10", "", { "dependencies": { "@types/prompts": "^2.4.9", "bunfig": "^0.10.1", "cac": "6.7.14", "prompts": "^2.4.2", "ts-pkgx": "0.4.38" }, "bin": { "buddy-bot": "dist/bin/cli.js" } }, "sha512-qpS5+KOSC6Qq9/ujKnnUvXNHxXfFmRG6L5D7xb1KvC7tt1rbMRd0z7d453KDbS13TCvYT8VCLVZ4z1zcoZvziw=="],
796+
"buddy-bot": ["buddy-bot@0.9.7", "", { "dependencies": { "@types/prompts": "^2.4.9", "bunfig": "^0.14.1", "cac": "6.7.14", "prompts": "^2.4.2", "ts-pkgx": "0.4.52" }, "bin": { "buddy-bot": "dist/bin/cli.js" } }, "sha512-na9Z2btom8rewzlXfHrnskDwWV5hwb0bcZZH4e43R/MIQaWnZFXBDdrVKmC21Tx00O7ukFYwkxexmk7+b/bkYw=="],
797797

798798
"buffer": ["[email protected]", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
799799

@@ -1807,7 +1807,7 @@
18071807

18081808
"ts-declaration-location": ["[email protected]", "", { "dependencies": { "picomatch": "^4.0.2" }, "peerDependencies": { "typescript": ">=4.0.0" } }, "sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA=="],
18091809

1810-
"ts-pkgx": ["[email protected].38", "", { "dependencies": { "chromium-bidi": "^7.2.0", "electron": "^37.2.4", "js-yaml": "^4.1.0", "playwright": "^1.54.1" }, "bin": { "ts-pkgx": "dist/bin/cli.js" } }, "sha512-UJA8LikwMv+w66ZfMvLn0f6NuG1OZ49jifMaTWn2VjUe68RV8H1nzkO0ZB/QhlhRh5tKs1Htd+YVQNnOWpc7Bw=="],
1810+
"ts-pkgx": ["[email protected].52", "", { "dependencies": { "chromium-bidi": "^7.2.0", "electron": "^37.4.0", "js-yaml": "^4.1.0", "playwright": "^1.55.0" }, "bin": { "ts-pkgx": "dist/bin/cli.js" } }, "sha512-YDuXweSXHXk3Me1Q/wI0mTpl4e0GQpiZfqAckPZ9d/PwLbFCQSjinSmTj6n8pnSj0XXs5/kw9z8aLZfSZtBzmw=="],
18111811

18121812
"tslib": ["[email protected]", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
18131813

@@ -2089,8 +2089,6 @@
20892089

20902090
"babel-plugin-polyfill-corejs2/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
20912091

2092-
"buddy-bot/bunfig": ["[email protected]", "", { "bin": { "bunfig": "bin/cli.js" } }, "sha512-4IB0Te+W0Jk8LcaCK9PhZqH9KHbYBJuTr70kVPRpnCDEq2WMixRPWSzOYNOihnSJBUBse8WIi9V5Ym2cyK+MDA=="],
2093-
20942092
"clean-regexp/escape-string-regexp": ["[email protected]", "", {}, "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="],
20952093

20962094
"clone-response/mimic-response": ["[email protected]", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="],

headwind.config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { HeadwindOptions } from './packages/headwind/src/types'
2+
3+
const config: HeadwindOptions = {
4+
verbose: true,
5+
}
6+
7+
export default config

packages/headwind/bin/cli.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#!/usr/bin/env bun
2+
import process from 'node:process'
3+
import { watch } from 'node:fs'
4+
import { CAC } from 'cac'
5+
import { buildAndWrite } from '../src/build'
6+
import { config } from '../src/config'
7+
import { version } from '../package.json'
8+
9+
const cli = new CAC('headwind')
10+
11+
interface BuildOptions {
12+
output?: string
13+
minify?: boolean
14+
watch?: boolean
15+
}
16+
17+
cli
18+
.command('build', 'Build CSS from content files')
19+
.option('--output <path>', 'Output CSS file path')
20+
.option('--minify', 'Minify CSS output')
21+
.option('--watch', 'Watch for file changes')
22+
.example('headwind build')
23+
.example('headwind build --output ./dist/styles.css')
24+
.example('headwind build --minify')
25+
.example('headwind build --watch')
26+
.action(async (options?: BuildOptions) => {
27+
const buildConfig = {
28+
...config,
29+
output: options?.output || config.output,
30+
minify: options?.minify ?? config.minify,
31+
}
32+
33+
async function runBuild(): Promise<void> {
34+
try {
35+
console.log('🚀 Building CSS...')
36+
const result = await buildAndWrite(buildConfig)
37+
console.log(`✅ Built ${result.classes.size} classes in ${result.duration.toFixed(2)}ms`)
38+
console.log(`📝 Output: ${buildConfig.output}`)
39+
}
40+
catch (error) {
41+
console.error('❌ Build failed:', error)
42+
process.exit(1)
43+
}
44+
}
45+
46+
if (options?.watch) {
47+
console.log('👀 Watching for changes...')
48+
await runBuild()
49+
50+
// Watch content directories
51+
const watchDirs = new Set<string>()
52+
for (const pattern of buildConfig.content) {
53+
const dir = pattern.split('**')[0] || '.'
54+
watchDirs.add(dir)
55+
}
56+
57+
for (const dir of watchDirs) {
58+
watch(dir, { recursive: true }, async (eventType, filename) => {
59+
if (filename && /\.(html|js|ts|jsx|tsx)$/.test(filename)) {
60+
console.log(`\n📝 ${filename} changed, rebuilding...`)
61+
await runBuild()
62+
}
63+
})
64+
}
65+
66+
console.log(`\n👀 Watching: ${Array.from(watchDirs).join(', ')}`)
67+
}
68+
else {
69+
await runBuild()
70+
}
71+
})
72+
73+
cli
74+
.command('watch', 'Build and watch for changes')
75+
.option('--output <path>', 'Output CSS file path')
76+
.option('--minify', 'Minify CSS output')
77+
.example('headwind watch')
78+
.example('headwind watch --output ./dist/styles.css')
79+
.action(async (options?: BuildOptions) => {
80+
const buildConfig = {
81+
...config,
82+
output: options?.output || config.output,
83+
minify: options?.minify ?? config.minify,
84+
}
85+
86+
async function runBuild(): Promise<void> {
87+
try {
88+
console.log('🚀 Building CSS...')
89+
const result = await buildAndWrite(buildConfig)
90+
console.log(`✅ Built ${result.classes.size} classes in ${result.duration.toFixed(2)}ms`)
91+
console.log(`📝 Output: ${buildConfig.output}`)
92+
}
93+
catch (error) {
94+
console.error('❌ Build failed:', error)
95+
process.exit(1)
96+
}
97+
}
98+
99+
console.log('👀 Watching for changes...')
100+
await runBuild()
101+
102+
// Watch content directories
103+
const watchDirs = new Set<string>()
104+
for (const pattern of buildConfig.content) {
105+
const dir = pattern.split('**')[0] || '.'
106+
watchDirs.add(dir)
107+
}
108+
109+
for (const dir of watchDirs) {
110+
watch(dir, { recursive: true }, async (eventType, filename) => {
111+
if (filename && /\.(html|js|ts|jsx|tsx)$/.test(filename)) {
112+
console.log(`\n📝 ${filename} changed, rebuilding...`)
113+
await runBuild()
114+
}
115+
})
116+
}
117+
118+
console.log(`\n👀 Watching: ${Array.from(watchDirs).join(', ')}`)
119+
})
120+
121+
cli.command('version', 'Show the version of Headwind').action(() => {
122+
console.log(version)
123+
})
124+
125+
cli.version(version)
126+
cli.help()
127+
cli.parse()

packages/launchpad/build.ts renamed to packages/headwind/build.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@ import { dts } from 'bun-plugin-dtsx'
33
await Bun.build({
44
entrypoints: ['src/index.ts'],
55
outdir: './dist',
6+
target: 'bun',
67
plugins: [dts()],
78
})
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6+
<title>Headwind Example</title>
7+
</head>
8+
<body>
9+
<div class="flex flex-col items-center justify-center h-screen bg-gray-100">
10+
<h1 class="text-4xl font-bold text-gray-900 mb-4">
11+
Welcome to Headwind
12+
</h1>
13+
<p class="text-lg text-gray-600 mb-8">
14+
A Bun-powered CSS utility framework
15+
</p>
16+
<button class="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600 focus:outline-none">
17+
Get Started
18+
</button>
19+
</div>
20+
</body>
21+
</html>
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
.flex {
2+
display: flex;
3+
}
4+
5+
.flex-col {
6+
flex-direction: column;
7+
}
8+
9+
.items-center {
10+
align-items: center;
11+
}
12+
13+
.justify-center {
14+
justify-content: center;
15+
}
16+
17+
.h-screen {
18+
height: 100vh;
19+
}
20+
21+
.bg-gray-100 {
22+
background-color: #f3f4f6;
23+
}
24+
25+
.text-4xl {
26+
font-size: 2.25rem;
27+
line-height: 2.5rem;
28+
}
29+
30+
.font-bold {
31+
font-weight: 700;
32+
}
33+
34+
.text-gray-900 {
35+
color: #111827;
36+
}
37+
38+
.mb-4 {
39+
margin-bottom: 1rem;
40+
}
41+
42+
.text-lg {
43+
font-size: 1.125rem;
44+
line-height: 1.75rem;
45+
}
46+
47+
.text-gray-600 {
48+
color: #4b5563;
49+
}
50+
51+
.mb-8 {
52+
margin-bottom: 2rem;
53+
}
54+
55+
.px-6 {
56+
padding-left: 1.5rem;
57+
padding-right: 1.5rem;
58+
}
59+
60+
.py-3 {
61+
padding-top: 0.75rem;
62+
padding-bottom: 0.75rem;
63+
}
64+
65+
.bg-blue-500 {
66+
background-color: blue-500;
67+
}
68+
69+
.text-white {
70+
color: #fff;
71+
}
72+
73+
.rounded-lg {
74+
border-radius: 0.5rem;
75+
}
76+
77+
.hover\:bg-blue-600:hover {
78+
background-color: blue-600;
79+
}
File renamed without changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import type { HeadwindConfig } from './src/types'
2+
3+
export default {
4+
content: ['./example/**/*.html'],
5+
output: './example/output.css',
6+
minify: false,
7+
} satisfies Partial<HeadwindConfig>
File renamed without changes.

packages/headwind/src/build.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import type { HeadwindConfig } from './types'
2+
import { CSSGenerator } from './generator'
3+
import { Scanner } from './scanner'
4+
5+
export interface BuildResult {
6+
css: string
7+
classes: Set<string>
8+
duration: number
9+
}
10+
11+
/**
12+
* Build CSS from content patterns
13+
*/
14+
export async function build(config: HeadwindConfig): Promise<BuildResult> {
15+
const startTime = performance.now()
16+
17+
// Scan files for utility classes
18+
const scanner = new Scanner(config.content)
19+
const classes = await scanner.scan()
20+
21+
// Add safelist classes
22+
for (const cls of config.safelist) {
23+
classes.add(cls)
24+
}
25+
26+
// Generate CSS
27+
const generator = new CSSGenerator(config)
28+
29+
for (const className of classes) {
30+
generator.generate(className)
31+
}
32+
33+
// Add preflights (reset CSS)
34+
let preflightCSS = ''
35+
for (const preflight of config.preflights) {
36+
preflightCSS += preflight.getCSS() + '\n\n'
37+
}
38+
39+
const css = preflightCSS + generator.toCSS(config.minify)
40+
const duration = performance.now() - startTime
41+
42+
return {
43+
css,
44+
classes,
45+
duration,
46+
}
47+
}
48+
49+
/**
50+
* Write CSS to output file
51+
*/
52+
export async function writeCSS(css: string, outputPath: string): Promise<void> {
53+
await Bun.write(outputPath, css)
54+
}
55+
56+
/**
57+
* Build and write CSS to output file
58+
*/
59+
export async function buildAndWrite(config: HeadwindConfig): Promise<BuildResult> {
60+
const result = await build(config)
61+
await writeCSS(result.css, config.output)
62+
return result
63+
}

0 commit comments

Comments
 (0)