mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-06 17:15:47 -06:00
feat: manually download tailwindcss cli, only check for emsdkDir if not activated
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,3 +4,4 @@ emsdk/
|
|||||||
.idea
|
.idea
|
||||||
rust-sdl2-emscripten/
|
rust-sdl2-emscripten/
|
||||||
assets/site/build.css
|
assets/site/build.css
|
||||||
|
tailwindcss-*
|
||||||
|
|||||||
275
web.build.ts
275
web.build.ts
@@ -32,7 +32,7 @@ function log(msg: string) {
|
|||||||
* @param release - Whether to build in release mode.
|
* @param release - Whether to build in release mode.
|
||||||
* @param env - The environment variables to inject into build commands.
|
* @param env - The environment variables to inject into build commands.
|
||||||
*/
|
*/
|
||||||
async function build(release: boolean, env: Record<string, string>) {
|
async function build(release: boolean, env: Record<string, string> | null) {
|
||||||
log(
|
log(
|
||||||
`Building for 'wasm32-unknown-emscripten' for ${
|
`Building for 'wasm32-unknown-emscripten' for ${
|
||||||
release ? "release" : "debug"
|
release ? "release" : "debug"
|
||||||
@@ -40,11 +40,23 @@ async function build(release: boolean, env: Record<string, string>) {
|
|||||||
);
|
);
|
||||||
await $`cargo build --target=wasm32-unknown-emscripten ${
|
await $`cargo build --target=wasm32-unknown-emscripten ${
|
||||||
release ? "--release" : ""
|
release ? "--release" : ""
|
||||||
}`.env(env);
|
}`.env(env ?? undefined);
|
||||||
|
|
||||||
log("Invoking @tailwindcss/cli");
|
// Download the Tailwind CSS CLI for rendering the CSS
|
||||||
// unfortunately, bunx doesn't seem to work with @tailwindcss/cli, so we have to use npx directly
|
const tailwindExecutable = match(
|
||||||
await $`npx --yes @tailwindcss/cli --minify --input styles.css --output build.css --cwd assets/site`;
|
await downloadTailwind(process.cwd(), {
|
||||||
|
version: "latest",
|
||||||
|
force: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.with({ path: P.select() }, (path) => path)
|
||||||
|
.with({ err: P.select() }, (err) => {
|
||||||
|
throw new Error(err);
|
||||||
|
})
|
||||||
|
.exhaustive();
|
||||||
|
|
||||||
|
log(`Invoking ${tailwindExecutable}...`);
|
||||||
|
await $`${tailwindExecutable} --minify --input styles.css --output build.css --cwd assets/site`;
|
||||||
|
|
||||||
const buildType = release ? "release" : "debug";
|
const buildType = release ? "release" : "debug";
|
||||||
const siteFolder = resolve("assets/site");
|
const siteFolder = resolve("assets/site");
|
||||||
@@ -110,6 +122,115 @@ async function build(release: boolean, env: Record<string, string>) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download the Tailwind CSS CLI to the specified directory.
|
||||||
|
* @param dir - The directory to download the Tailwind CSS CLI to.
|
||||||
|
* @returns The path to the downloaded Tailwind CSS CLI, or an error message if the download fails.
|
||||||
|
*/
|
||||||
|
async function downloadTailwind(
|
||||||
|
dir: string,
|
||||||
|
options?: Partial<{
|
||||||
|
version: string; // The version of Tailwind CSS to download. If not specified, the latest version will be downloaded.
|
||||||
|
force: boolean; // Whether to force the download even if the file already exists.
|
||||||
|
}>
|
||||||
|
): Promise<{ path: string } | { err: string }> {
|
||||||
|
const asset = match(os)
|
||||||
|
.with({ type: "linux" }, () => "tailwindcss-linux-x64")
|
||||||
|
.with({ type: "macos" }, () => "tailwindcss-macos-arm64")
|
||||||
|
.with({ type: "windows" }, () => "tailwindcss-windows-x64.exe")
|
||||||
|
.exhaustive();
|
||||||
|
|
||||||
|
const version = options?.version ?? "latest";
|
||||||
|
const force = options?.force ?? false;
|
||||||
|
|
||||||
|
const url =
|
||||||
|
version === "latest" || version == null
|
||||||
|
? `https://github.com/tailwindlabs/tailwindcss/releases/latest/download/${asset}`
|
||||||
|
: `https://github.com/tailwindlabs/tailwindcss/releases/download/${version}/${asset}`;
|
||||||
|
|
||||||
|
const path = join(dir, asset);
|
||||||
|
if (await fs.exists(path)) {
|
||||||
|
const displayPath = match(relative(process.cwd(), path))
|
||||||
|
// If the path is not a subpath of cwd, display the absolute path
|
||||||
|
.with(P.string.startsWith(".."), (_relative) => path)
|
||||||
|
// Otherwise, display the relative path
|
||||||
|
.otherwise((relative) => relative);
|
||||||
|
|
||||||
|
if (!force) {
|
||||||
|
log(`Tailwind CSS CLI already exists at ${displayPath}`);
|
||||||
|
return { path };
|
||||||
|
} else {
|
||||||
|
log(`Overwriting Tailwind CSS CLI at ${displayPath}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log(`Downloading Tailwind CSS CLI to ${path}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// If the GITHUB_TOKEN environment variable is set, use it for Bearer authentication
|
||||||
|
const headers: Record<string, string> = {};
|
||||||
|
if (process.env.GITHUB_TOKEN) {
|
||||||
|
headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
log(`Fetching ${url}...`);
|
||||||
|
const response = await fetch(url, { headers });
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
return {
|
||||||
|
err: `Failed to download Tailwind CSS: ${response.status} ${response.statusText} for '${url}'`,
|
||||||
|
};
|
||||||
|
} else if (!response.body) {
|
||||||
|
return { err: `No response body received for '${url}'` };
|
||||||
|
}
|
||||||
|
|
||||||
|
log(`Writing to ${path}...`);
|
||||||
|
await fs.mkdir(dir, { recursive: true });
|
||||||
|
|
||||||
|
const file = Bun.file(path);
|
||||||
|
const writer = file.writer();
|
||||||
|
|
||||||
|
const reader = response.body.getReader();
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
writer.write(value);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
reader.releaseLock();
|
||||||
|
await writer.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the file executable on Unix-like systems
|
||||||
|
if (os.type !== "windows") {
|
||||||
|
await $`chmod +x ${path}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure file is not locked; sometimes the runtime is too fast and the file is executed before the lock is released
|
||||||
|
const timeout = Date.now() + 2500; // 2.5s timeout
|
||||||
|
do {
|
||||||
|
try {
|
||||||
|
if ((await fs.stat(path)).size > 0) break;
|
||||||
|
} catch {
|
||||||
|
// File might not be ready yet
|
||||||
|
log(`File ${path} is not ready yet, waiting...`);
|
||||||
|
}
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||||
|
} while (Date.now() < timeout);
|
||||||
|
|
||||||
|
// All done!
|
||||||
|
return { path };
|
||||||
|
} catch (error) {
|
||||||
|
return {
|
||||||
|
err: `Download failed: ${
|
||||||
|
error instanceof Error ? error.message : String(error)
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks to see if the Emscripten SDK is activated for a Windows or *nix machine by looking for a .exe file and the equivalent file on Linux/macOS. Returns both results for handling.
|
* Checks to see if the Emscripten SDK is activated for a Windows or *nix machine by looking for a .exe file and the equivalent file on Linux/macOS. Returns both results for handling.
|
||||||
* @param emsdkDir - The directory containing the Emscripten SDK.
|
* @param emsdkDir - The directory containing the Emscripten SDK.
|
||||||
@@ -128,14 +249,73 @@ async function checkEmsdkType(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Activate the Emscripten SDK environment variables.
|
* Activate the Emscripten SDK environment variables.
|
||||||
* Technically, this doesn't actaully activate the environment variables for the current shell,
|
* Technically, this doesn't actually activate the environment variables for the current shell,
|
||||||
* it just runs the environment sourcing script and returns the environment variables for future command invocations.
|
* it just runs the environment sourcing script and returns the environment variables for future command invocations.
|
||||||
* @param emsdkDir - The directory containing the Emscripten SDK.
|
* @param emsdkDir - The directory containing the Emscripten SDK.
|
||||||
* @returns A record of environment variables.
|
* @returns A record of environment variables.
|
||||||
*/
|
*/
|
||||||
async function activateEmsdk(
|
async function activateEmsdk(
|
||||||
emsdkDir: string
|
emsdkDir: string
|
||||||
): Promise<{ vars: Record<string, string> } | { err: string }> {
|
): Promise<{ vars: Record<string, string> | null } | { err: string }> {
|
||||||
|
// If the EMSDK environment variable is set already & the path specified exists, return nothing
|
||||||
|
if (process.env.EMSDK && (await fs.exists(resolve(process.env.EMSDK)))) {
|
||||||
|
log(
|
||||||
|
"Emscripten SDK already activated in environment, using existing configuration"
|
||||||
|
);
|
||||||
|
return { vars: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the emsdk directory exists
|
||||||
|
if (!(await fs.exists(emsdkDir))) {
|
||||||
|
return {
|
||||||
|
err: `Emscripten SDK directory not found at ${emsdkDir}. Please install or clone 'emsdk' and try again.`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the emsdk directory is activated/installed properly for the current OS
|
||||||
|
match({
|
||||||
|
os: os,
|
||||||
|
...(await checkEmsdkType(emsdkDir)),
|
||||||
|
})
|
||||||
|
// If the Emscripten SDK is not activated/installed properly, exit with an error
|
||||||
|
.with(
|
||||||
|
{
|
||||||
|
nix: false,
|
||||||
|
windows: false,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
return {
|
||||||
|
err: "Emscripten SDK does not appear to be activated/installed properly.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// If the Emscripten SDK is activated for Windows, but is currently running on a *nix OS, exit with an error
|
||||||
|
.with(
|
||||||
|
{
|
||||||
|
nix: false,
|
||||||
|
windows: true,
|
||||||
|
os: { type: P.not("windows") },
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
return {
|
||||||
|
err: "Emscripten SDK appears to be activated for Windows, but is currently running on a *nix OS.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// If the Emscripten SDK is activated for *nix, but is currently running on a Windows OS, exit with an error
|
||||||
|
.with(
|
||||||
|
{
|
||||||
|
nix: true,
|
||||||
|
windows: false,
|
||||||
|
os: { type: "windows" },
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
return {
|
||||||
|
err: "Emscripten SDK appears to be activated for *nix, but is currently running on a Windows OS.",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Determine the environment script to use based on the OS
|
// Determine the environment script to use based on the OS
|
||||||
const envScript = match(os)
|
const envScript = match(os)
|
||||||
.with({ type: "windows" }, () => join(emsdkDir, "emsdk_env.bat"))
|
.with({ type: "windows" }, () => join(emsdkDir, "emsdk_env.bat"))
|
||||||
@@ -188,81 +368,14 @@ async function main() {
|
|||||||
const release = process.env.RELEASE !== "0";
|
const release = process.env.RELEASE !== "0";
|
||||||
const emsdkDir = resolve("./emsdk");
|
const emsdkDir = resolve("./emsdk");
|
||||||
|
|
||||||
// Check if Emscripten is already activated in the environment
|
// Activate the Emscripten SDK (returns null if already activated)
|
||||||
const emscriptenAlreadyActivated =
|
const vars = match(await activateEmsdk(emsdkDir))
|
||||||
process.env.EMSCRIPTEN || process.env.EMSDK;
|
.with({ vars: P.select() }, (vars) => vars)
|
||||||
|
.with({ err: P.any }, ({ err }) => {
|
||||||
let vars: Record<string, string>;
|
log("Error activating Emscripten SDK: " + err);
|
||||||
|
|
||||||
if (emscriptenAlreadyActivated) {
|
|
||||||
log(
|
|
||||||
"Emscripten SDK already activated in environment, using existing configuration"
|
|
||||||
);
|
|
||||||
vars = process.env as Record<string, string>;
|
|
||||||
} else {
|
|
||||||
// Ensure the emsdk directory exists before attempting to activate or use it
|
|
||||||
if (!(await fs.exists(emsdkDir))) {
|
|
||||||
log(
|
|
||||||
`Emscripten SDK directory not found at ${emsdkDir}. Please install or clone 'emsdk' and try again.`
|
|
||||||
);
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
})
|
||||||
|
.exhaustive();
|
||||||
vars = match(await activateEmsdk(emsdkDir)) // result handling
|
|
||||||
.with({ vars: P.select() }, (vars) => vars)
|
|
||||||
.with({ err: P.any }, ({ err }) => {
|
|
||||||
log("Error activating Emscripten SDK: " + err);
|
|
||||||
process.exit(1);
|
|
||||||
})
|
|
||||||
.exhaustive();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the Emscripten SDK is activated/installed properly for the current OS
|
|
||||||
match({
|
|
||||||
os: os,
|
|
||||||
...(await checkEmsdkType(emsdkDir)),
|
|
||||||
})
|
|
||||||
// If the Emscripten SDK is not activated/installed properly, exit with an error
|
|
||||||
.with(
|
|
||||||
{
|
|
||||||
nix: false,
|
|
||||||
windows: false,
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
log(
|
|
||||||
"Emscripten SDK does not appear to be activated/installed properly."
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// If the Emscripten SDK is activated for Windows, but is currently running on a *nix OS, exit with an error
|
|
||||||
.with(
|
|
||||||
{
|
|
||||||
nix: false,
|
|
||||||
windows: true,
|
|
||||||
os: { type: P.not("windows") },
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
log(
|
|
||||||
"Emscripten SDK appears to be activated for Windows, but is currently running on a *nix OS."
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// If the Emscripten SDK is activated for *nix, but is currently running on a Windows OS, exit with an error
|
|
||||||
.with(
|
|
||||||
{
|
|
||||||
nix: true,
|
|
||||||
windows: false,
|
|
||||||
os: { type: "windows" },
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
log(
|
|
||||||
"Emscripten SDK appears to be activated for *nix, but is currently running on a Windows OS."
|
|
||||||
);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Build the application
|
// Build the application
|
||||||
await build(release, vars);
|
await build(release, vars);
|
||||||
|
|||||||
Reference in New Issue
Block a user