2024-05-26 17:58:41 +02:00
|
|
|
import * as child_process from "child_process";
|
|
|
|
import { Deferred } from "evt/tools/Deferred";
|
|
|
|
import { assert } from "tsafe/assert";
|
2024-06-09 09:15:16 +02:00
|
|
|
import type { BuildContext } from "../shared/buildContext";
|
2024-06-23 21:23:06 +02:00
|
|
|
import chalk from "chalk";
|
|
|
|
import { sep as pathSep, join as pathJoin } from "path";
|
2024-05-26 17:58:41 +02:00
|
|
|
|
2024-06-09 09:15:16 +02:00
|
|
|
export type BuildContextLike = {
|
2024-06-09 09:03:43 +02:00
|
|
|
projectDirPath: string;
|
2024-05-26 17:58:41 +02:00
|
|
|
keycloakifyBuildDirPath: string;
|
2024-06-23 21:23:06 +02:00
|
|
|
bundler: BuildContext["bundler"];
|
2024-06-09 09:03:43 +02:00
|
|
|
projectBuildDirPath: string;
|
2024-05-26 17:58:41 +02:00
|
|
|
};
|
|
|
|
|
2024-06-09 09:15:16 +02:00
|
|
|
assert<BuildContext extends BuildContextLike ? true : false>();
|
2024-05-26 17:58:41 +02:00
|
|
|
|
|
|
|
export async function appBuild(params: {
|
2024-06-09 09:15:16 +02:00
|
|
|
buildContext: BuildContextLike;
|
2024-05-26 17:58:41 +02:00
|
|
|
}): Promise<{ isAppBuildSuccess: boolean }> {
|
2024-06-09 09:15:16 +02:00
|
|
|
const { buildContext } = params;
|
2024-05-26 17:58:41 +02:00
|
|
|
|
2024-06-23 21:23:06 +02:00
|
|
|
switch (buildContext.bundler.type) {
|
|
|
|
case "vite":
|
|
|
|
return appBuild_vite({ buildContext });
|
|
|
|
case "webpack":
|
|
|
|
return appBuild_webpack({ buildContext });
|
|
|
|
}
|
|
|
|
}
|
2024-05-26 17:58:41 +02:00
|
|
|
|
2024-06-23 21:23:06 +02:00
|
|
|
async function appBuild_vite(params: {
|
|
|
|
buildContext: BuildContextLike;
|
|
|
|
}): Promise<{ isAppBuildSuccess: boolean }> {
|
|
|
|
const { buildContext } = params;
|
|
|
|
|
|
|
|
assert(buildContext.bundler.type === "vite");
|
2024-05-26 17:58:41 +02:00
|
|
|
|
|
|
|
const dResult = new Deferred<{ isSuccess: boolean }>();
|
|
|
|
|
2024-06-23 21:23:06 +02:00
|
|
|
const child = child_process.spawn("npx", ["vite", "build"], {
|
|
|
|
cwd: buildContext.projectDirPath,
|
|
|
|
shell: true
|
|
|
|
});
|
2024-05-26 17:58:41 +02:00
|
|
|
|
|
|
|
child.stdout.on("data", data => {
|
|
|
|
if (data.toString("utf8").includes("gzip:")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
process.stdout.write(data);
|
|
|
|
});
|
|
|
|
|
|
|
|
child.stderr.on("data", data => process.stderr.write(data));
|
|
|
|
|
|
|
|
child.on("exit", code => dResult.resolve({ isSuccess: code === 0 }));
|
|
|
|
|
|
|
|
const { isSuccess } = await dResult.pr;
|
|
|
|
|
|
|
|
return { isAppBuildSuccess: isSuccess };
|
|
|
|
}
|
2024-06-23 21:23:06 +02:00
|
|
|
|
|
|
|
async function appBuild_webpack(params: {
|
|
|
|
buildContext: BuildContextLike;
|
|
|
|
}): Promise<{ isAppBuildSuccess: boolean }> {
|
|
|
|
const { buildContext } = params;
|
|
|
|
|
|
|
|
assert(buildContext.bundler.type === "webpack");
|
|
|
|
|
|
|
|
const entries = Object.entries(buildContext.bundler.packageJsonScripts).filter(
|
|
|
|
([, scriptCommand]) => scriptCommand.includes("keycloakify build")
|
|
|
|
);
|
|
|
|
|
|
|
|
if (entries.length === 0) {
|
|
|
|
console.log(
|
|
|
|
chalk.red(
|
|
|
|
[
|
|
|
|
`You should have a script in your package.json at ${buildContext.bundler.packageJsonDirPath}`,
|
|
|
|
`that includes the 'keycloakify build' command`
|
|
|
|
].join(" ")
|
|
|
|
)
|
|
|
|
);
|
|
|
|
process.exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
const entry =
|
|
|
|
entries.length === 1
|
|
|
|
? entries[0]
|
|
|
|
: entries.find(([scriptName]) => scriptName === "build-keycloak-theme");
|
|
|
|
|
|
|
|
if (entry === undefined) {
|
|
|
|
console.log(
|
|
|
|
chalk.red(
|
|
|
|
"There's multiple candidate script for building your app, name one 'build-keycloak-theme'"
|
|
|
|
)
|
|
|
|
);
|
|
|
|
process.exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
const [scriptName, scriptCommand] = entry;
|
|
|
|
|
|
|
|
const { appBuildSubCommands } = (() => {
|
|
|
|
const appBuildSubCommands: string[] = [];
|
|
|
|
|
|
|
|
for (const subCmd of scriptCommand.split("&&").map(s => s.trim())) {
|
|
|
|
if (subCmd.includes("keycloakify build")) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
appBuildSubCommands.push(subCmd);
|
|
|
|
}
|
|
|
|
|
|
|
|
return { appBuildSubCommands };
|
|
|
|
})();
|
|
|
|
|
|
|
|
if (appBuildSubCommands.length === 0) {
|
|
|
|
console.log(
|
|
|
|
chalk.red(
|
|
|
|
`Your ${scriptName} script should look like "... && keycloakify build ..."`
|
|
|
|
)
|
|
|
|
);
|
|
|
|
process.exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const subCommand of appBuildSubCommands) {
|
|
|
|
const dIsSuccess = new Deferred<boolean>();
|
|
|
|
|
|
|
|
child_process.exec(
|
|
|
|
subCommand,
|
|
|
|
{
|
|
|
|
cwd: buildContext.bundler.packageJsonDirPath,
|
|
|
|
env: {
|
|
|
|
...process.env,
|
|
|
|
PATH: (() => {
|
|
|
|
const separator = pathSep === "/" ? ":" : ";";
|
|
|
|
|
|
|
|
return [
|
|
|
|
pathJoin(
|
|
|
|
buildContext.bundler.packageJsonDirPath,
|
|
|
|
"node_modules",
|
|
|
|
".bin"
|
|
|
|
),
|
|
|
|
...(process.env.PATH ?? "").split(separator)
|
|
|
|
].join(separator);
|
|
|
|
})()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(error, stdout, stderr) => {
|
|
|
|
if (error) {
|
|
|
|
dIsSuccess.resolve(false);
|
|
|
|
|
|
|
|
console.log(chalk.red(`Error running: '${subCommand}'`));
|
|
|
|
console.log(stdout);
|
|
|
|
console.log(stderr);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dIsSuccess.resolve(true);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
const isSuccess = await dIsSuccess.pr;
|
|
|
|
|
|
|
|
if (!isSuccess) {
|
|
|
|
return { isAppBuildSuccess: false };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return { isAppBuildSuccess: true };
|
|
|
|
}
|