Skip to content

Commit 55a55f8

Browse files
committed
update to tailwindcss v4
1 parent c1f1e82 commit 55a55f8

File tree

7 files changed

+6582
-2123
lines changed

7 files changed

+6582
-2123
lines changed

MyApp/Components/Pages/gallery/DataGrid/Custom.razor

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@
1515
{
1616
_httpClient = HttpClientFactory.CreateClient("Blazor");
1717
}
18-
List<WeatherForecast> forecasts = new();
18+
List<WeatherForecast> forecasts = [
19+
new() { Date = new DateTime(2018, 5, 6), TemperatureC = 1, Summary = "Freezing" },
20+
new() { Date = new DateTime(2018, 5, 7), TemperatureC = 14, Summary = "Bracing" },
21+
new() { Date = new DateTime(2018, 5, 8), TemperatureC = -13, Summary = "Freezing" },
22+
new() { Date = new DateTime(2018, 5, 9), TemperatureC = -16, Summary = "Balmy" },
23+
new() { Date = new DateTime(2018, 5, 10), TemperatureC = -2, Summary = "Chilly" },
24+
];
1925

2026
protected override async Task OnInitializedAsync()
2127
{
22-
forecasts = await _httpClient.GetFromJsonAsync<List<WeatherForecast>>("/data/weather.json") ?? new();
28+
//forecasts = await _httpClient.GetFromJsonAsync<List<WeatherForecast>>("/data/weather.json") ?? new();
2329
}
2430
}

MyApp/package.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
{
2-
"scripts": {
3-
"postinstall": "dotnet run --AppTasks=migrate",
4-
"dtos": "x mjs",
5-
"dev": "dotnet watch",
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",
8-
"build": "npm run ui:build",
9-
"migrate": "dotnet run --AppTasks=migrate",
10-
"revert:last": "dotnet run --AppTasks=migrate.revert:last",
11-
"revert:all": "dotnet run --AppTasks=migrate.revert:all",
12-
"rerun:last": "npm run revert:last && npm run migrate"
13-
}
2+
"scripts": {
3+
"postinstall": "dotnet run --AppTasks=migrate",
4+
"dtos": "x mjs",
5+
"dev": "dotnet watch",
6+
"ui:dev": "tailwindcss -i ./tailwind.input.css -o ./wwwroot/css/app.css --watch",
7+
"ui:build": "tailwindcss -i ./tailwind.input.css -o ./wwwroot/css/app.css --minify",
8+
"build": "npm run ui:build",
9+
"migrate": "dotnet run --AppTasks=migrate",
10+
"revert:last": "dotnet run --AppTasks=migrate.revert:last",
11+
"revert:all": "dotnet run --AppTasks=migrate.revert:all",
12+
"rerun:last": "npm run revert:last && npm run migrate"
13+
}
1414
}

MyApp/postinstall.js

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

MyApp/tailwind.config.js

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
module.exports = {
2-
content: ["./**/*.{html,js,mjs,md,cshtml,razor,cs}", "./Components/**/*.{cshtml,razor}", "./Css.cs"],
3-
darkMode: 'class',
4-
theme: {
5-
extend: {
6-
colors: {
7-
'accent-1': '#FAFAFA',
8-
'accent-2': '#EAEAEA',
9-
danger: 'rgb(153 27 27)',
10-
success: 'rgb(22 101 52)',
11-
},
12-
},
13-
},
14-
plugins: [],
15-
}
1+
export default {
2+
theme: {
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)