tailwind-hyperclay 0.1.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/index.js ADDED
@@ -0,0 +1,86 @@
1
+ import { compile } from 'tailwindcss';
2
+ import { readFile, writeFile } from 'fs/promises';
3
+ import { createRequire } from 'module';
4
+ import { pathToFileURL } from 'url';
5
+ import path from 'path';
6
+
7
+ const require = createRequire(import.meta.url);
8
+
9
+ export async function generateCSS({ input, output }) {
10
+ const html = await readFile(input, 'utf-8');
11
+
12
+ const css = await compileTailwind(html);
13
+
14
+ if (output) {
15
+ await writeFile(output, css);
16
+ }
17
+
18
+ return css;
19
+ }
20
+
21
+ export async function compileTailwind(html) {
22
+ const inputCSS = `
23
+ @import "tailwindcss";
24
+ @plugin "@tailwindcss/typography";
25
+ @plugin "@tailwindcss/forms" { strategy: "class"; }
26
+ `;
27
+
28
+ const compiler = await compile(inputCSS, {
29
+ loadStylesheet: async (id, base) => {
30
+ let resolved;
31
+ const searchPaths = [base || process.cwd()];
32
+
33
+ // Try resolving as-is first (for .css files)
34
+ try {
35
+ resolved = require.resolve(id, { paths: searchPaths });
36
+ } catch {
37
+ // If that fails, try with /index.css suffix (for package imports like "tailwindcss")
38
+ resolved = require.resolve(`${id}/index.css`, { paths: searchPaths });
39
+ }
40
+
41
+ // If resolved to a JS file, try the CSS variant
42
+ if (resolved.endsWith('.js') || resolved.endsWith('.mjs')) {
43
+ resolved = require.resolve(`${id}/index.css`, { paths: searchPaths });
44
+ }
45
+
46
+ const content = await readFile(resolved, 'utf-8');
47
+ return { content, base: path.dirname(resolved) };
48
+ },
49
+ loadModule: async (id, base) => {
50
+ const resolved = require.resolve(id, { paths: [base || process.cwd()] });
51
+ const mod = await import(pathToFileURL(resolved).href);
52
+ return { module: mod.default, base: path.dirname(resolved) };
53
+ },
54
+ loadPlugin: async (plugin) => {
55
+ const resolved = require.resolve(plugin);
56
+ const mod = await import(pathToFileURL(resolved).href);
57
+ return mod.default;
58
+ }
59
+ });
60
+
61
+ const candidates = extractCandidates(html);
62
+ const css = compiler.build(candidates);
63
+
64
+ return css;
65
+ }
66
+
67
+ export function extractCandidates(html) {
68
+ const candidates = new Set();
69
+
70
+ // Extract class attribute values
71
+ const classRegex = /class\s*=\s*["']([^"']+)["']/gi;
72
+ let match;
73
+ while ((match = classRegex.exec(html)) !== null) {
74
+ const classes = match[1].split(/\s+/);
75
+ for (const cls of classes) {
76
+ if (cls) candidates.add(cls);
77
+ }
78
+ }
79
+
80
+ return Array.from(candidates);
81
+ }
82
+
83
+ export function hasTailwindLink(html, appName) {
84
+ const pattern = new RegExp(`https://hyperclay\\.com/tailwindcss/${appName}\\.css`);
85
+ return pattern.test(html);
86
+ }
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "tailwind-hyperclay",
3
+ "version": "0.1.0",
4
+ "description": "On-save Tailwind CSS generator for Hyperclay",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "exports": {
8
+ ".": "./index.js"
9
+ },
10
+ "scripts": {
11
+ "test": "node test/test.js",
12
+ "release": "./scripts/release.sh"
13
+ },
14
+ "keywords": ["tailwind", "hyperclay", "css"],
15
+ "author": "",
16
+ "license": "MIT",
17
+ "dependencies": {
18
+ "tailwindcss": "^4.0.0-beta.8",
19
+ "@tailwindcss/typography": "^0.5.15",
20
+ "@tailwindcss/forms": "^0.5.9"
21
+ }
22
+ }
@@ -0,0 +1,248 @@
1
+ #!/bin/bash
2
+ set -e # Exit on error
3
+
4
+ # tailwind-hyperclay Automated Release Script
5
+
6
+ echo "╔════════════════════════════════════════╗"
7
+ echo "║ tailwind-hyperclay Release ║"
8
+ echo "╚════════════════════════════════════════╝"
9
+ echo ""
10
+
11
+ # Color codes
12
+ RED='\033[0;31m'
13
+ GREEN='\033[0;32m'
14
+ YELLOW='\033[1;33m'
15
+ BLUE='\033[0;34m'
16
+ NC='\033[0m' # No Color
17
+
18
+ # Helper functions
19
+ info() { echo -e "${BLUE}ℹ${NC} $1"; }
20
+ success() { echo -e "${GREEN}✓${NC} $1"; }
21
+ warn() { echo -e "${YELLOW}⚠${NC} $1"; }
22
+ error() { echo -e "${RED}✗${NC} $1"; }
23
+
24
+ # Check if we're in the right directory
25
+ if [ ! -f "package.json" ] || ! grep -q '"name": "tailwind-hyperclay"' package.json; then
26
+ error "Must run from tailwind-hyperclay root directory"
27
+ exit 1
28
+ fi
29
+
30
+ # ============================================
31
+ # STEP 1: Collect Release Information
32
+ # ============================================
33
+
34
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
35
+ echo "Step 1: Release Information"
36
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
37
+ echo ""
38
+
39
+ # Get version bump type
40
+ echo "Select version bump type:"
41
+ echo " 1) patch (bug fixes, 1.0.0 → 1.0.1)"
42
+ echo " 2) minor (new features, 1.0.0 → 1.1.0)"
43
+ echo " 3) major (breaking changes, 1.0.0 → 2.0.0)"
44
+ echo " 4) custom (enter version manually)"
45
+ echo ""
46
+ read -p "Enter choice [1-4]: " version_choice
47
+
48
+ case $version_choice in
49
+ 1) VERSION_TYPE="patch" ;;
50
+ 2) VERSION_TYPE="minor" ;;
51
+ 3) VERSION_TYPE="major" ;;
52
+ 4)
53
+ read -p "Enter custom version (e.g., 2.0.0-beta.1): " CUSTOM_VERSION
54
+ VERSION_TYPE="custom"
55
+ ;;
56
+ *)
57
+ error "Invalid choice"
58
+ exit 1
59
+ ;;
60
+ esac
61
+
62
+ # Publish tag (latest, beta, alpha)
63
+ echo ""
64
+ read -p "NPM publish tag [latest/beta/alpha] (default: latest): " NPM_TAG
65
+ NPM_TAG=${NPM_TAG:-latest}
66
+
67
+ # ============================================
68
+ # STEP 2: Pre-Release Checks
69
+ # ============================================
70
+
71
+ echo ""
72
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
73
+ echo "Step 2: Pre-Release Checks"
74
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
75
+ echo ""
76
+
77
+ # Check git status
78
+ info "Checking git status..."
79
+ if [ -n "$(git status --porcelain)" ]; then
80
+ warn "You have uncommitted changes:"
81
+ git status --short
82
+ read -p "Continue anyway? [y/N]: " continue_dirty
83
+ if [[ ! $continue_dirty =~ ^[Yy]$ ]]; then
84
+ exit 1
85
+ fi
86
+ else
87
+ success "Working directory clean"
88
+ fi
89
+
90
+ # ============================================
91
+ # STEP 3: Test
92
+ # ============================================
93
+
94
+ echo ""
95
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
96
+ echo "Step 3: Test"
97
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
98
+ echo ""
99
+
100
+ # Run tests
101
+ info "Running tests..."
102
+ if npm test; then
103
+ success "All tests passed"
104
+ else
105
+ error "Tests failed"
106
+ read -p "Continue anyway? [y/N]: " continue_tests
107
+ if [[ ! $continue_tests =~ ^[Yy]$ ]]; then
108
+ exit 1
109
+ fi
110
+ fi
111
+
112
+ # ============================================
113
+ # STEP 4: Version Bump
114
+ # ============================================
115
+
116
+ echo ""
117
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
118
+ echo "Step 4: Version Bump"
119
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
120
+ echo ""
121
+
122
+ # Get current version
123
+ CURRENT_VERSION=$(node -p "require('./package.json').version")
124
+ info "Current version: $CURRENT_VERSION"
125
+
126
+ # Bump version
127
+ if [ "$VERSION_TYPE" = "custom" ]; then
128
+ NEW_VERSION="$CUSTOM_VERSION"
129
+ npm version "$NEW_VERSION" --no-git-tag-version
130
+ else
131
+ NEW_VERSION=$(npm version "$VERSION_TYPE" --no-git-tag-version | sed 's/^v//')
132
+ fi
133
+
134
+ success "Version bumped to: $NEW_VERSION"
135
+
136
+ # ============================================
137
+ # STEP 5: Commit and Tag
138
+ # ============================================
139
+
140
+ echo ""
141
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
142
+ echo "Step 5: Commit and Tag"
143
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
144
+ echo ""
145
+
146
+ # Stage changes
147
+ git add package.json package-lock.json
148
+
149
+ # Commit
150
+ COMMIT_MSG="chore: release v$NEW_VERSION"
151
+ git commit -m "$COMMIT_MSG"
152
+ success "Changes committed"
153
+
154
+ # Create tag
155
+ git tag -a "v$NEW_VERSION" -m "Release v$NEW_VERSION"
156
+ success "Tag created: v$NEW_VERSION"
157
+
158
+ # ============================================
159
+ # STEP 6: Dry Run
160
+ # ============================================
161
+
162
+ echo ""
163
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
164
+ echo "Step 6: Pre-Publish Verification"
165
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
166
+ echo ""
167
+
168
+ info "Running npm publish --dry-run..."
169
+ npm publish --dry-run --tag "$NPM_TAG"
170
+
171
+ echo ""
172
+ warn "Review the output above carefully!"
173
+ echo ""
174
+
175
+ # ============================================
176
+ # STEP 7: Publish
177
+ # ============================================
178
+
179
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
180
+ echo "Step 7: Publish to npm"
181
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
182
+ echo ""
183
+
184
+ echo "Summary:"
185
+ echo " Version: $CURRENT_VERSION → $NEW_VERSION"
186
+ echo " Tag: $NPM_TAG"
187
+ echo " Git tag: v$NEW_VERSION"
188
+ echo ""
189
+
190
+ read -p "Publish to npm? [y/N]: " confirm_publish
191
+ if [[ ! $confirm_publish =~ ^[Yy]$ ]]; then
192
+ warn "Publish cancelled"
193
+ echo ""
194
+ echo "To manually publish later:"
195
+ echo " git push origin main"
196
+ echo " git push origin --tags"
197
+ echo " npm publish --tag $NPM_TAG"
198
+ exit 0
199
+ fi
200
+
201
+ # Publish to npm
202
+ info "Publishing to npm..."
203
+ npm publish --tag "$NPM_TAG"
204
+ success "Published to npm!"
205
+
206
+ # Push to git
207
+ info "Pushing to GitHub..."
208
+ git push origin main
209
+ git push origin --tags
210
+ success "Pushed to GitHub"
211
+
212
+ # ============================================
213
+ # STEP 8: Verify
214
+ # ============================================
215
+
216
+ echo ""
217
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
218
+ echo "Step 8: Verification"
219
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
220
+ echo ""
221
+
222
+ sleep 5 # Give npm a moment to update
223
+
224
+ info "Verifying npm publication..."
225
+ NPM_VERSION=$(npm view tailwind-hyperclay version 2>/dev/null || echo "unknown")
226
+ if [ "$NPM_VERSION" = "$NEW_VERSION" ]; then
227
+ success "npm shows version: $NPM_VERSION"
228
+ else
229
+ warn "npm shows version: $NPM_VERSION (expected: $NEW_VERSION)"
230
+ warn "It may take a few moments for npm to update"
231
+ fi
232
+
233
+ # ============================================
234
+ # Done!
235
+ # ============================================
236
+
237
+ echo ""
238
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
239
+ success "Release Complete!"
240
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
241
+ echo ""
242
+ echo "Released: tailwind-hyperclay@$NEW_VERSION"
243
+ echo "npm tag: $NPM_TAG"
244
+ echo "Git tag: v$NEW_VERSION"
245
+ echo ""
246
+ echo "Install with:"
247
+ echo " npm install tailwind-hyperclay@$NEW_VERSION"
248
+ echo ""
package/test/test.js ADDED
@@ -0,0 +1,34 @@
1
+ import { compileTailwind, hasTailwindLink } from '../index.js';
2
+
3
+ const testHTML = `
4
+ <!DOCTYPE html>
5
+ <html>
6
+ <head>
7
+ <link href="https://hyperclay.com/tailwindcss/myApp.css" rel="stylesheet">
8
+ </head>
9
+ <body>
10
+ <div class="prose mx-auto p-4">
11
+ <h1 class="text-2xl font-bold text-blue-600">Hello</h1>
12
+ <input type="text" class="form-input mt-2">
13
+ </div>
14
+ </body>
15
+ </html>
16
+ `;
17
+
18
+ async function runTests() {
19
+ console.log('Testing hasTailwindLink...');
20
+ console.log(' myApp:', hasTailwindLink(testHTML, 'myApp')); // true
21
+ console.log(' other:', hasTailwindLink(testHTML, 'other')); // false
22
+
23
+ console.log('\nTesting compileTailwind...');
24
+ const css = await compileTailwind(testHTML);
25
+ console.log(' Generated CSS length:', css.length);
26
+ console.log(' Contains .prose:', css.includes('.prose'));
27
+ console.log(' Contains .form-input:', css.includes('.form-input'));
28
+ console.log(' Contains .text-2xl:', css.includes('.text-2xl'));
29
+
30
+ console.log('\nSample output (first 500 chars):');
31
+ console.log(css.slice(0, 500));
32
+ }
33
+
34
+ runTests().catch(console.error);