Skip to content

Commit 8e54d40

Browse files
committed
Upgrade to tailwindcss v4
1 parent f73fb8a commit 8e54d40

File tree

6 files changed

+1205
-1205
lines changed

6 files changed

+1205
-1205
lines changed

MyApp/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
{
22
"scripts": {
3+
"postinstall": "node postinstall.js",
34
"dtos": "x mjs",
45
"dev": "dotnet watch",
56
"build": "npm run ui:build",
6-
"ui:dev": "npx tailwindcss@v3 -i ./tailwind.input.css -o ./wwwroot/css/app.css --watch",
7-
"ui:build": "npx tailwindcss@v3 -i ./tailwind.input.css -o ./wwwroot/css/app.css --minify"
7+
"ui:dev": "tailwindcss -i ./tailwind.input.css -o ./wwwroot/css/app.css --watch",
8+
"ui:build": "tailwindcss -i ./tailwind.input.css -o ./wwwroot/css/app.css --minify"
89
}
910
}

MyApp/postinstall.js

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
// Usage: npm install
2+
3+
const writeTo = './wwwroot/lib'
4+
const defaultPrefix = 'https://unpkg.com'
5+
const files = {
6+
}
7+
8+
const path = require('path')
9+
const fs = require('fs')
10+
const { pipeline } = require('stream')
11+
const { promisify } = require('util')
12+
const { execSync } = require('child_process')
13+
const pipe = promisify(pipeline)
14+
15+
;(async () => {
16+
const requests = []
17+
Object.keys(files).forEach(dir => {
18+
const dirFiles = files[dir]
19+
Object.keys(dirFiles).forEach(name => {
20+
let url = dirFiles[name]
21+
if (url.startsWith('/'))
22+
url = defaultPrefix + url
23+
const toFile = path.join(writeTo, dir, name)
24+
requests.push(fetchDownload(url, toFile, 5))
25+
})
26+
})
27+
28+
await Promise.all(requests)
29+
await downloadTailwindBinary()
30+
})()
31+
32+
async function fetchDownload(url, toFile, retries) {
33+
const toDir = path.dirname(toFile)
34+
fs.mkdirSync(toDir, { recursive: true })
35+
for (let i=retries; i>=0; --i) {
36+
try {
37+
let r = await fetch(url)
38+
if (!r.ok) {
39+
throw new Error(`${r.status} ${r.statusText}`);
40+
}
41+
let txt = await r.text()
42+
console.log(`writing ${url} to ${toFile}`)
43+
await fs.writeFileSync(toFile, txt)
44+
return
45+
} catch (e) {
46+
console.log(`get ${url} failed: ${e}${i > 0 ? `, ${i} retries remaining...` : ''}`)
47+
}
48+
}
49+
}
50+
51+
async function downloadTailwindBinary() {
52+
const platform = process.platform // e.g., 'darwin', 'linux', 'win32'
53+
const arch = process.arch // e.g., 'arm64', 'x64'
54+
55+
// Check if tailwindcss is already in PATH
56+
try {
57+
const command = platform === 'win32' ? 'where tailwindcss' : 'which tailwindcss'
58+
const result = execSync(command, { stdio: 'pipe' })
59+
if (result) {
60+
// Check version of tailwindcss by looking for 'tailwindcss v4' in `taildwindcss --help`
61+
const helpResult = execSync('tailwindcss --help', { stdio: 'pipe' })
62+
const helpOutput = helpResult.toString()
63+
if (helpOutput.includes('tailwindcss v1') || helpOutput.includes('tailwindcss v2') || helpOutput.includes('tailwindcss v3')) {
64+
console.log('old version of tailwindcss detected, please uninstall and rerun this script.')
65+
} else {
66+
console.log('tailwindcss is already installed.')
67+
}
68+
return
69+
}
70+
} catch (e) {
71+
// Command failed, tailwindcss not in PATH
72+
}
73+
74+
// if file already exists, exit
75+
const tailwindcssPath = path.join(process.cwd(), 'tailwindcss')
76+
if (fs.existsSync(tailwindcssPath)) {
77+
console.log(`${tailwindcssPath} already exists, skipping download.`)
78+
return
79+
}
80+
81+
console.log()
82+
function getBinaryFileName() {
83+
// Determine the correct binary file name based on the current OS and architecture
84+
if (platform === 'darwin') { // macOS
85+
if (arch === 'arm64') {
86+
return 'tailwindcss-macos-arm64'
87+
} else if (arch === 'x64') {
88+
return 'tailwindcss-macos-x64'
89+
}
90+
} else if (platform === 'linux') { // Linux
91+
if (arch === 'arm64') {
92+
return 'tailwindcss-linux-arm64'
93+
} else if (arch === 'x64') {
94+
return 'tailwindcss-linux-x64'
95+
}
96+
} else if (platform === 'win32') { // Windows
97+
if (arch === 'arm64') {
98+
return 'arm64-windows'
99+
} else if (arch === 'x64') {
100+
return 'tailwindcss-windows-x64.exe'
101+
}
102+
}
103+
}
104+
105+
let binaryFileName = getBinaryFileName()
106+
107+
// If no matching binary is found, exit with an error
108+
if (!binaryFileName) {
109+
console.error(`Error: Unsupported platform/architecture combination: ${platform}/${arch}`)
110+
console.error(`Please ensure your system is one of the following:`)
111+
console.error(` macOS (arm64, x64)`)
112+
console.error(` Linux (arm64, x64)`)
113+
console.error(` Windows (arm64, x64)`)
114+
process.exit(1)
115+
}
116+
117+
// Base URL for Tailwind CSS latest release downloads
118+
const downloadTailwindBaseUrl = `https://github.com/tailwindlabs/tailwindcss/releases/latest/download/`
119+
const downloadUrl = `${downloadTailwindBaseUrl}${binaryFileName}`
120+
// Set the output file name. On Windows, it should have a .exe extension.
121+
const outputFileName = (platform === 'win32' || platform === 'cygwin' || platform === 'msys') ? 'tailwindcss.exe' : 'tailwindcss'
122+
const outputPath = path.join(process.cwd(), outputFileName)
123+
124+
console.log(`Attempting to download the latest Tailwind CSS binary for ${platform}/${arch}...`)
125+
console.log(`Downloading ${downloadUrl}...`)
126+
127+
try {
128+
const response = await fetch(downloadUrl)
129+
130+
// Check if the response status is not OK (e.g., 404, 500).
131+
// Fetch automatically handles redirects (3xx status codes).
132+
if (!response.ok) {
133+
console.error(`Failed to download: HTTP Status Code ${response.status} - ${response.statusText}`)
134+
return
135+
}
136+
137+
// Ensure there's a readable stream body
138+
if (!response.body) {
139+
console.error('No response body received from the download URL.')
140+
return
141+
}
142+
143+
const fileStream = fs.createWriteStream(outputPath)
144+
// Pipe the readable stream from the fetch response body directly to the file stream
145+
await pipe(response.body, fileStream)
146+
147+
// Set executable permissions for non-Windows platforms
148+
if (platform !== 'win32' && platform !== 'cygwin' && platform !== 'msys') {
149+
console.log(`Setting executable permissions (+x) on ${outputPath}...`)
150+
// '755' means: owner can read, write, execute; group and others can read and execute.
151+
fs.chmodSync(outputPath, '755')
152+
// console.log('Permissions set successfully.')
153+
154+
const tryFolders = [
155+
`${process.env.HOME}/.local/bin`,
156+
`${process.env.HOME}/.npm-global/bin`,
157+
'/usr/local/bin',
158+
'/usr/bin',
159+
'/usr/sbin'
160+
]
161+
162+
// Move the binary to a common location in PATH
163+
for (const folder of tryFolders) {
164+
if (!fs.existsSync(folder)) {
165+
// console.log(`Folder ${folder} does not exist, skipping...`);
166+
continue
167+
}
168+
const targetPath = path.join(folder, outputFileName)
169+
if (fs.accessSync(folder, fs.constants.W_OK)) {
170+
try {
171+
fs.renameSync(outputPath, targetPath)
172+
console.log(`Saved to ${targetPath}`)
173+
break
174+
}
175+
catch (err) {
176+
console.error(`Failed to move ${outputPath} to ${targetPath}: ${err.message}`)
177+
}
178+
}
179+
180+
try {
181+
// try using sudo with process exec
182+
execSync(`sudo mv ${outputPath} ${targetPath}`)
183+
console.log(`Saved to ${targetPath}`)
184+
break
185+
}
186+
catch (err) {
187+
console.log(`Manually move tailwindcss to ${targetPath} by running:`)
188+
console.log(`sudo mv ${outputPath} ${targetPath}`)
189+
break
190+
}
191+
}
192+
} else if (platform === 'win32') {
193+
let moved = false
194+
// Move the binary to a common location in PATH for .NET Devs
195+
const tryFolders = [
196+
`${process.env.APPDATA}/npm`,
197+
`${process.env.USERPROFILE}/.dotnet/tools`,
198+
]
199+
for (const folder of tryFolders) {
200+
if (!fs.existsSync(folder)) {
201+
continue
202+
}
203+
const targetPath = path.join(folder, outputFileName)
204+
try {
205+
fs.renameSync(outputPath, targetPath)
206+
console.log(`Saved to ${targetPath}`)
207+
moved = true
208+
break
209+
}
210+
catch (err) {
211+
}
212+
}
213+
if (!moved) {
214+
console.log()
215+
console.log(`Saved to ${outputPath}`)
216+
console.log(`Tip: Make ${outputFileName} globally accessible by moving it to a folder in your PATH`)
217+
}
218+
}
219+
220+
console.log()
221+
console.log(`You can now run it from your terminal using:`)
222+
console.log(outputFileName === 'tailwindcss.exe' ? `${outputFileName} --help` : `${outputFileName} --help`)
223+
224+
} catch (error) {
225+
console.error(`\nError during download or permission setting:`)
226+
console.error(error.message)
227+
process.exit(1)
228+
}
229+
}

MyApp/tailwind.config.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
module.exports = {
2-
content: ["./wwwroot/**/*.{html,js,mjs,razor,cshtml}"],
1+
export default {
32
theme: {
4-
extend: {},
5-
},
6-
plugins: [],
7-
}
3+
extend: {
4+
colors: {
5+
'accent-1': '#FAFAFA',
6+
'accent-2': '#EAEAEA',
7+
danger: 'rgb(153 27 27)',
8+
success: 'rgb(22 101 52)',
9+
},
10+
},
11+
}
12+
}

0 commit comments

Comments
 (0)