2022-08-16 14:41:06 +07:00
|
|
|
import { parse as urlParse } from "url";
|
2023-09-03 23:26:34 +02:00
|
|
|
import { join as pathJoin } from "path";
|
2024-05-16 08:23:37 +02:00
|
|
|
import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath";
|
|
|
|
import { getNpmWorkspaceRootDirPath } from "../tools/getNpmWorkspaceRootDirPath";
|
|
|
|
import type { CliCommandOptions } from "../main";
|
|
|
|
import { z } from "zod";
|
|
|
|
import * as fs from "fs";
|
2024-06-13 22:58:32 +02:00
|
|
|
import { assert, type Equals } from "tsafe";
|
2024-05-16 08:23:37 +02:00
|
|
|
import * as child_process from "child_process";
|
|
|
|
import { vitePluginSubScriptEnvNames } from "./constants";
|
2022-08-16 14:41:06 +07:00
|
|
|
|
2024-06-09 09:15:16 +02:00
|
|
|
export type BuildContext = {
|
2024-01-30 05:54:36 +01:00
|
|
|
bundler: "vite" | "webpack";
|
2023-08-21 05:54:17 +02:00
|
|
|
themeVersion: string;
|
2024-06-10 07:57:12 +02:00
|
|
|
themeNames: [string, ...string[]];
|
2023-08-21 05:54:17 +02:00
|
|
|
extraThemeProperties: string[] | undefined;
|
|
|
|
groupId: string;
|
|
|
|
artifactId: string;
|
2023-09-03 21:02:51 +02:00
|
|
|
loginThemeResourcesFromKeycloakVersion: string;
|
2024-06-09 09:03:43 +02:00
|
|
|
projectDirPath: string;
|
|
|
|
projectBuildDirPath: string;
|
2023-08-21 05:54:17 +02:00
|
|
|
/** Directory that keycloakify outputs to. Defaults to {cwd}/build_keycloak */
|
|
|
|
keycloakifyBuildDirPath: string;
|
2023-09-03 23:26:34 +02:00
|
|
|
publicDirPath: string;
|
|
|
|
cacheDirPath: string;
|
2023-08-21 05:54:17 +02:00
|
|
|
/** If your app is hosted under a subpath, it's the case in CRA if you have "homepage": "https://example.com/my-app" in your package.json
|
|
|
|
* In this case the urlPathname will be "/my-app/" */
|
|
|
|
urlPathname: string | undefined;
|
2024-01-30 00:06:17 +01:00
|
|
|
assetsDirPath: string;
|
2024-02-11 18:28:58 +01:00
|
|
|
npmWorkspaceRootDirPath: string;
|
2024-06-06 01:31:00 +02:00
|
|
|
kcContextExclusionsFtlCode: string | undefined;
|
2024-06-08 14:02:07 +02:00
|
|
|
environmentVariables: { name: string; default: string }[];
|
2023-08-21 05:54:17 +02:00
|
|
|
};
|
2022-08-16 14:41:06 +07:00
|
|
|
|
2024-06-09 09:15:16 +02:00
|
|
|
export type BuildOptions = {
|
2024-06-08 14:02:07 +02:00
|
|
|
themeName?: string | string[];
|
|
|
|
environmentVariables?: { name: string; default: string }[];
|
2024-05-16 08:23:37 +02:00
|
|
|
extraThemeProperties?: string[];
|
|
|
|
artifactId?: string;
|
|
|
|
groupId?: string;
|
|
|
|
loginThemeResourcesFromKeycloakVersion?: string;
|
|
|
|
keycloakifyBuildDirPath?: string;
|
2024-06-09 09:20:55 +02:00
|
|
|
kcContextExclusionsFtl?: string;
|
2024-05-16 08:23:37 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
export type ResolvedViteConfig = {
|
|
|
|
buildDir: string;
|
|
|
|
publicDir: string;
|
|
|
|
assetsDir: string;
|
|
|
|
urlPathname: string | undefined;
|
2024-06-09 09:15:16 +02:00
|
|
|
buildOptions: BuildOptions;
|
2024-05-16 08:23:37 +02:00
|
|
|
};
|
|
|
|
|
2024-06-09 09:15:16 +02:00
|
|
|
export function getBuildContext(params: {
|
2024-05-20 15:48:51 +02:00
|
|
|
cliCommandOptions: CliCommandOptions;
|
2024-06-09 09:15:16 +02:00
|
|
|
}): BuildContext {
|
2024-05-15 05:14:01 +02:00
|
|
|
const { cliCommandOptions } = params;
|
2024-02-08 00:56:33 +01:00
|
|
|
|
2024-06-09 09:03:43 +02:00
|
|
|
const projectDirPath = (() => {
|
|
|
|
if (cliCommandOptions.projectDirPath === undefined) {
|
2024-05-16 07:47:50 +02:00
|
|
|
return process.cwd();
|
|
|
|
}
|
2024-02-08 00:56:33 +01:00
|
|
|
|
2024-05-16 07:47:50 +02:00
|
|
|
return getAbsoluteAndInOsFormatPath({
|
2024-06-09 09:03:43 +02:00
|
|
|
pathIsh: cliCommandOptions.projectDirPath,
|
2024-05-20 15:48:51 +02:00
|
|
|
cwd: process.cwd()
|
2024-05-16 07:47:50 +02:00
|
|
|
});
|
|
|
|
})();
|
2024-02-08 00:56:33 +01:00
|
|
|
|
2024-05-16 08:23:37 +02:00
|
|
|
const { resolvedViteConfig } = (() => {
|
2024-05-20 15:48:51 +02:00
|
|
|
if (
|
|
|
|
fs
|
2024-06-09 09:03:43 +02:00
|
|
|
.readdirSync(projectDirPath)
|
2024-05-20 15:48:51 +02:00
|
|
|
.find(fileBasename => fileBasename.startsWith("vite.config")) ===
|
|
|
|
undefined
|
|
|
|
) {
|
|
|
|
return { resolvedViteConfig: undefined };
|
2024-05-16 08:23:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const output = child_process
|
|
|
|
.execSync("npx vite", {
|
2024-06-09 09:03:43 +02:00
|
|
|
cwd: projectDirPath,
|
2024-05-20 15:48:51 +02:00
|
|
|
env: {
|
2024-05-16 08:23:37 +02:00
|
|
|
...process.env,
|
|
|
|
[vitePluginSubScriptEnvNames.resolveViteConfig]: "true"
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.toString("utf8");
|
|
|
|
|
2024-05-20 15:48:51 +02:00
|
|
|
assert(
|
|
|
|
output.includes(vitePluginSubScriptEnvNames.resolveViteConfig),
|
|
|
|
"Seems like the Keycloakify's Vite plugin is not installed."
|
|
|
|
);
|
2024-05-16 08:23:37 +02:00
|
|
|
|
2024-05-20 15:48:51 +02:00
|
|
|
const resolvedViteConfigStr = output
|
|
|
|
.split(vitePluginSubScriptEnvNames.resolveViteConfig)
|
|
|
|
.reverse()[0];
|
2023-04-18 03:10:29 +02:00
|
|
|
|
2024-05-16 08:23:37 +02:00
|
|
|
const resolvedViteConfig: ResolvedViteConfig = JSON.parse(resolvedViteConfigStr);
|
|
|
|
|
|
|
|
return { resolvedViteConfig };
|
|
|
|
})();
|
|
|
|
|
|
|
|
const parsedPackageJson = (() => {
|
2024-06-13 22:58:32 +02:00
|
|
|
type WebpackSpecificBuildOptions = {
|
|
|
|
projectBuildDirPath?: string;
|
|
|
|
};
|
|
|
|
|
2024-05-16 08:23:37 +02:00
|
|
|
type ParsedPackageJson = {
|
|
|
|
name: string;
|
|
|
|
version?: string;
|
|
|
|
homepage?: string;
|
2024-06-13 22:58:32 +02:00
|
|
|
keycloakify?: {
|
|
|
|
themeName?: string | string[];
|
|
|
|
environmentVariables?: { name: string; default: string }[];
|
|
|
|
extraThemeProperties?: string[];
|
|
|
|
artifactId?: string;
|
|
|
|
groupId?: string;
|
|
|
|
loginThemeResourcesFromKeycloakVersion?: string;
|
|
|
|
keycloakifyBuildDirPath?: string;
|
|
|
|
kcContextExclusionsFtl?: string;
|
2024-06-09 09:03:43 +02:00
|
|
|
projectBuildDirPath?: string;
|
2024-05-20 15:48:51 +02:00
|
|
|
};
|
2024-05-16 08:23:37 +02:00
|
|
|
};
|
|
|
|
|
2024-06-13 22:58:32 +02:00
|
|
|
{
|
|
|
|
type Got = NonNullable<ParsedPackageJson["keycloakify"]>;
|
|
|
|
type Expected = BuildOptions & WebpackSpecificBuildOptions;
|
|
|
|
assert<Equals<Got, Expected>>();
|
|
|
|
}
|
|
|
|
|
2024-05-16 08:23:37 +02:00
|
|
|
const zParsedPackageJson = z.object({
|
2024-05-20 15:48:51 +02:00
|
|
|
name: z.string(),
|
|
|
|
version: z.string().optional(),
|
|
|
|
homepage: z.string().optional(),
|
|
|
|
keycloakify: z
|
2024-05-16 08:23:37 +02:00
|
|
|
.object({
|
2024-05-20 15:48:51 +02:00
|
|
|
extraThemeProperties: z.array(z.string()).optional(),
|
|
|
|
artifactId: z.string().optional(),
|
|
|
|
groupId: z.string().optional(),
|
|
|
|
loginThemeResourcesFromKeycloakVersion: z.string().optional(),
|
2024-06-09 09:03:43 +02:00
|
|
|
projectBuildDirPath: z.string().optional(),
|
2024-05-20 15:48:51 +02:00
|
|
|
keycloakifyBuildDirPath: z.string().optional(),
|
2024-06-13 22:58:32 +02:00
|
|
|
kcContextExclusionsFtl: z.string().optional(),
|
|
|
|
environmentVariables: z
|
|
|
|
.array(
|
|
|
|
z.object({
|
|
|
|
name: z.string(),
|
|
|
|
default: z.string()
|
|
|
|
})
|
|
|
|
)
|
|
|
|
.optional(),
|
2024-05-20 15:48:51 +02:00
|
|
|
themeName: z.union([z.string(), z.array(z.string())]).optional()
|
2024-05-16 08:23:37 +02:00
|
|
|
})
|
|
|
|
.optional()
|
|
|
|
});
|
|
|
|
|
|
|
|
{
|
2024-06-13 22:58:32 +02:00
|
|
|
type Got = z.infer<typeof zParsedPackageJson>;
|
2024-05-16 08:23:37 +02:00
|
|
|
type Expected = ParsedPackageJson;
|
2024-06-13 22:58:32 +02:00
|
|
|
assert<Equals<Got, Expected>>();
|
2024-05-16 08:23:37 +02:00
|
|
|
}
|
|
|
|
|
2024-05-20 15:48:51 +02:00
|
|
|
return zParsedPackageJson.parse(
|
|
|
|
JSON.parse(
|
2024-06-09 09:03:43 +02:00
|
|
|
fs.readFileSync(pathJoin(projectDirPath, "package.json")).toString("utf8")
|
2024-05-20 15:48:51 +02:00
|
|
|
)
|
|
|
|
);
|
2024-05-16 08:23:37 +02:00
|
|
|
})();
|
2024-02-23 19:15:59 +01:00
|
|
|
|
2024-06-09 09:15:16 +02:00
|
|
|
const buildOptions: BuildOptions = {
|
2024-05-16 08:23:37 +02:00
|
|
|
...parsedPackageJson.keycloakify,
|
2024-06-09 09:15:16 +02:00
|
|
|
...resolvedViteConfig?.buildOptions
|
2024-02-23 19:15:59 +01:00
|
|
|
};
|
2022-08-16 14:41:06 +07:00
|
|
|
|
2024-06-10 07:57:12 +02:00
|
|
|
const themeNames = ((): [string, ...string[]] => {
|
2024-06-09 09:15:16 +02:00
|
|
|
if (buildOptions.themeName === undefined) {
|
2023-09-04 01:19:21 +02:00
|
|
|
return [
|
2024-01-30 05:54:36 +01:00
|
|
|
parsedPackageJson.name
|
2023-09-04 01:19:21 +02:00
|
|
|
.replace(/^@(.*)/, "$1")
|
|
|
|
.split("/")
|
|
|
|
.join("-")
|
|
|
|
];
|
|
|
|
}
|
2022-08-16 14:41:06 +07:00
|
|
|
|
2024-06-09 09:15:16 +02:00
|
|
|
if (typeof buildOptions.themeName === "string") {
|
|
|
|
return [buildOptions.themeName];
|
2023-09-04 01:19:21 +02:00
|
|
|
}
|
|
|
|
|
2024-06-10 07:57:12 +02:00
|
|
|
const [mainThemeName, ...themeVariantNames] = buildOptions.themeName;
|
|
|
|
|
|
|
|
assert(mainThemeName !== undefined);
|
|
|
|
|
|
|
|
return [mainThemeName, ...themeVariantNames];
|
2023-09-04 01:19:21 +02:00
|
|
|
})();
|
2022-08-16 14:41:06 +07:00
|
|
|
|
2024-06-09 09:03:43 +02:00
|
|
|
const projectBuildDirPath = (() => {
|
2024-01-30 06:55:26 +01:00
|
|
|
webpack: {
|
|
|
|
if (resolvedViteConfig !== undefined) {
|
|
|
|
break webpack;
|
|
|
|
}
|
|
|
|
|
2024-06-09 09:03:43 +02:00
|
|
|
if (parsedPackageJson.keycloakify?.projectBuildDirPath !== undefined) {
|
2024-01-30 06:55:26 +01:00
|
|
|
return getAbsoluteAndInOsFormatPath({
|
2024-06-09 09:03:43 +02:00
|
|
|
pathIsh: parsedPackageJson.keycloakify.projectBuildDirPath,
|
|
|
|
cwd: projectDirPath
|
2024-01-30 06:55:26 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-06-09 09:03:43 +02:00
|
|
|
return pathJoin(projectDirPath, "build");
|
2024-01-30 06:55:26 +01:00
|
|
|
}
|
|
|
|
|
2024-06-09 09:03:43 +02:00
|
|
|
return pathJoin(projectDirPath, resolvedViteConfig.buildDir);
|
2024-01-30 06:55:26 +01:00
|
|
|
})();
|
2024-01-30 05:54:36 +01:00
|
|
|
|
2024-05-19 23:17:45 +02:00
|
|
|
const { npmWorkspaceRootDirPath } = getNpmWorkspaceRootDirPath({
|
2024-06-09 09:03:43 +02:00
|
|
|
projectDirPath,
|
2024-05-20 15:48:51 +02:00
|
|
|
dependencyExpected: "keycloakify"
|
2024-05-19 23:17:45 +02:00
|
|
|
});
|
2024-02-11 18:28:58 +01:00
|
|
|
|
2024-01-30 05:54:36 +01:00
|
|
|
return {
|
2024-05-20 15:48:51 +02:00
|
|
|
bundler: resolvedViteConfig !== undefined ? "vite" : "webpack",
|
|
|
|
themeVersion:
|
|
|
|
process.env.KEYCLOAKIFY_THEME_VERSION ?? parsedPackageJson.version ?? "0.0.0",
|
2023-09-04 01:19:21 +02:00
|
|
|
themeNames,
|
2024-06-09 09:15:16 +02:00
|
|
|
extraThemeProperties: buildOptions.extraThemeProperties,
|
2024-05-20 15:48:51 +02:00
|
|
|
groupId: (() => {
|
2023-09-04 01:19:21 +02:00
|
|
|
const fallbackGroupId = `${themeNames[0]}.keycloak`;
|
2023-08-21 05:54:17 +02:00
|
|
|
|
|
|
|
return (
|
|
|
|
process.env.KEYCLOAKIFY_GROUP_ID ??
|
2024-06-09 09:15:16 +02:00
|
|
|
buildOptions.groupId ??
|
2024-01-30 05:54:36 +01:00
|
|
|
(parsedPackageJson.homepage === undefined
|
2023-08-21 05:54:17 +02:00
|
|
|
? fallbackGroupId
|
2024-01-30 05:54:36 +01:00
|
|
|
: urlParse(parsedPackageJson.homepage)
|
2023-08-21 05:54:17 +02:00
|
|
|
.host?.replace(/:[0-9]+$/, "")
|
|
|
|
?.split(".")
|
|
|
|
.reverse()
|
|
|
|
.join(".") ?? fallbackGroupId) + ".keycloak"
|
|
|
|
);
|
|
|
|
})(),
|
2024-05-20 15:48:51 +02:00
|
|
|
artifactId:
|
|
|
|
process.env.KEYCLOAKIFY_ARTIFACT_ID ??
|
2024-06-09 09:15:16 +02:00
|
|
|
buildOptions.artifactId ??
|
2024-05-20 15:48:51 +02:00
|
|
|
`${themeNames[0]}-keycloak-theme`,
|
|
|
|
loginThemeResourcesFromKeycloakVersion:
|
2024-06-09 09:15:16 +02:00
|
|
|
buildOptions.loginThemeResourcesFromKeycloakVersion ?? "24.0.4",
|
2024-06-09 09:03:43 +02:00
|
|
|
projectDirPath,
|
|
|
|
projectBuildDirPath,
|
2024-05-20 15:48:51 +02:00
|
|
|
keycloakifyBuildDirPath: (() => {
|
2024-06-09 09:15:16 +02:00
|
|
|
if (buildOptions.keycloakifyBuildDirPath !== undefined) {
|
2024-02-11 16:17:38 +01:00
|
|
|
return getAbsoluteAndInOsFormatPath({
|
2024-06-09 09:15:16 +02:00
|
|
|
pathIsh: buildOptions.keycloakifyBuildDirPath,
|
2024-06-09 09:03:43 +02:00
|
|
|
cwd: projectDirPath
|
2024-02-11 16:17:38 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-02-24 05:38:42 +01:00
|
|
|
return pathJoin(
|
2024-06-09 09:03:43 +02:00
|
|
|
projectDirPath,
|
2024-05-20 15:48:51 +02:00
|
|
|
resolvedViteConfig?.buildDir === undefined
|
|
|
|
? "build_keycloak"
|
|
|
|
: `${resolvedViteConfig.buildDir}_keycloak`
|
2024-02-24 05:38:42 +01:00
|
|
|
);
|
2024-02-11 16:17:38 +01:00
|
|
|
})(),
|
2024-05-20 15:48:51 +02:00
|
|
|
publicDirPath: (() => {
|
2024-01-30 06:55:26 +01:00
|
|
|
webpack: {
|
|
|
|
if (resolvedViteConfig !== undefined) {
|
|
|
|
break webpack;
|
|
|
|
}
|
2024-01-27 18:49:29 +01:00
|
|
|
|
2024-01-30 06:55:26 +01:00
|
|
|
if (process.env.PUBLIC_DIR_PATH !== undefined) {
|
|
|
|
return getAbsoluteAndInOsFormatPath({
|
2024-05-20 15:48:51 +02:00
|
|
|
pathIsh: process.env.PUBLIC_DIR_PATH,
|
2024-06-09 09:03:43 +02:00
|
|
|
cwd: projectDirPath
|
2024-01-30 06:55:26 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-06-09 09:03:43 +02:00
|
|
|
return pathJoin(projectDirPath, "public");
|
2023-08-21 05:54:17 +02:00
|
|
|
}
|
2022-08-16 14:41:06 +07:00
|
|
|
|
2024-06-09 09:03:43 +02:00
|
|
|
return pathJoin(projectDirPath, resolvedViteConfig.publicDir);
|
2023-08-21 05:54:17 +02:00
|
|
|
})(),
|
2024-05-20 15:48:51 +02:00
|
|
|
cacheDirPath: (() => {
|
2024-05-16 07:47:50 +02:00
|
|
|
const cacheDirPath = pathJoin(
|
|
|
|
(() => {
|
|
|
|
if (process.env.XDG_CACHE_HOME !== undefined) {
|
|
|
|
return getAbsoluteAndInOsFormatPath({
|
2024-05-20 15:48:51 +02:00
|
|
|
pathIsh: process.env.XDG_CACHE_HOME,
|
|
|
|
cwd: process.cwd()
|
2024-05-16 07:47:50 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return pathJoin(npmWorkspaceRootDirPath, "node_modules", ".cache");
|
|
|
|
})(),
|
|
|
|
"keycloakify"
|
|
|
|
);
|
|
|
|
|
|
|
|
return cacheDirPath;
|
|
|
|
})(),
|
2024-05-20 15:48:51 +02:00
|
|
|
urlPathname: (() => {
|
2024-01-30 06:55:26 +01:00
|
|
|
webpack: {
|
|
|
|
if (resolvedViteConfig !== undefined) {
|
|
|
|
break webpack;
|
|
|
|
}
|
|
|
|
|
|
|
|
const { homepage } = parsedPackageJson;
|
|
|
|
|
|
|
|
let url: URL | undefined = undefined;
|
2022-08-16 14:41:06 +07:00
|
|
|
|
2024-01-30 06:55:26 +01:00
|
|
|
if (homepage !== undefined) {
|
|
|
|
url = new URL(homepage);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (url === undefined) {
|
|
|
|
return undefined;
|
|
|
|
}
|
2022-08-16 14:41:06 +07:00
|
|
|
|
2024-01-30 06:55:26 +01:00
|
|
|
const out = url.pathname.replace(/([^/])$/, "$1/");
|
|
|
|
return out === "/" ? undefined : out;
|
2023-08-21 05:54:17 +02:00
|
|
|
}
|
2022-08-16 14:41:06 +07:00
|
|
|
|
2024-01-30 06:55:26 +01:00
|
|
|
return resolvedViteConfig.urlPathname;
|
|
|
|
})(),
|
2024-05-20 15:48:51 +02:00
|
|
|
assetsDirPath: (() => {
|
2024-01-30 06:55:26 +01:00
|
|
|
webpack: {
|
|
|
|
if (resolvedViteConfig !== undefined) {
|
|
|
|
break webpack;
|
|
|
|
}
|
|
|
|
|
2024-06-09 09:03:43 +02:00
|
|
|
return pathJoin(projectBuildDirPath, "static");
|
2023-08-21 05:54:17 +02:00
|
|
|
}
|
2022-08-16 14:41:06 +07:00
|
|
|
|
2024-06-09 09:03:43 +02:00
|
|
|
return pathJoin(projectBuildDirPath, resolvedViteConfig.assetsDir);
|
2024-02-04 10:25:48 +01:00
|
|
|
})(),
|
2024-06-06 01:31:00 +02:00
|
|
|
npmWorkspaceRootDirPath,
|
2024-06-09 09:20:55 +02:00
|
|
|
kcContextExclusionsFtlCode: (() => {
|
|
|
|
if (buildOptions.kcContextExclusionsFtl === undefined) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (buildOptions.kcContextExclusionsFtl.endsWith(".ftl")) {
|
|
|
|
const kcContextExclusionsFtlPath = getAbsoluteAndInOsFormatPath({
|
|
|
|
pathIsh: buildOptions.kcContextExclusionsFtl,
|
|
|
|
cwd: projectDirPath
|
|
|
|
});
|
|
|
|
|
|
|
|
return fs.readFileSync(kcContextExclusionsFtlPath).toString("utf8");
|
|
|
|
}
|
|
|
|
|
|
|
|
return buildOptions.kcContextExclusionsFtl;
|
|
|
|
})(),
|
2024-06-09 09:15:16 +02:00
|
|
|
environmentVariables: buildOptions.environmentVariables ?? []
|
2023-08-21 05:54:17 +02:00
|
|
|
};
|
2022-08-16 14:41:06 +07:00
|
|
|
}
|