Skip to content

Commit 50a80ec

Browse files
committed
Add tailwind config for Razor Web App
1 parent a5fe4d5 commit 50a80ec

4 files changed

Lines changed: 397 additions & 99 deletions

File tree

TechStacks/package.json

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
{
2-
"scripts": {
3-
"dev": "dotnet watch",
4-
"migrate": "dotnet run --AppTasks=migrate",
5-
"revert:last": "dotnet run --AppTasks=migrate.revert:last",
6-
"revert:all": "dotnet run --AppTasks=migrate.revert:all",
7-
"rerun:last": "npm run revert:last && npm run migrate"
8-
}
2+
"scripts": {
3+
"postinstall": "node postinstall.js",
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+
}
914
}

TechStacks/postinstall.js

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

TechStacks/tailwind.config.js

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)