2022-08-16 14:41:06 +07:00
|
|
|
import { z } from "zod";
|
|
|
|
import { assert } from "tsafe/assert";
|
|
|
|
import type { Equals } from "tsafe";
|
|
|
|
import { id } from "tsafe/id";
|
|
|
|
import { parse as urlParse } from "url";
|
2023-02-04 17:44:02 +01:00
|
|
|
import { typeGuard } from "tsafe/typeGuard";
|
2023-02-04 18:02:39 +01:00
|
|
|
import { symToStr } from "tsafe/symToStr";
|
2022-08-16 14:41:06 +07:00
|
|
|
|
2023-02-04 18:02:39 +01:00
|
|
|
const bundlers = ["mvn", "keycloakify", "none"] as const;
|
|
|
|
type Bundler = typeof bundlers[number];
|
2022-08-16 14:41:06 +07:00
|
|
|
type ParsedPackageJson = {
|
|
|
|
name: string;
|
|
|
|
version: string;
|
|
|
|
homepage?: string;
|
|
|
|
keycloakify?: {
|
|
|
|
extraPages?: string[];
|
|
|
|
extraThemeProperties?: string[];
|
2022-09-07 11:25:46 +02:00
|
|
|
areAppAndKeycloakServerSharingSameDomain?: boolean;
|
2023-02-03 14:28:06 +01:00
|
|
|
artifactId?: string;
|
|
|
|
groupId?: string;
|
|
|
|
bundler?: Bundler;
|
2022-08-16 14:41:06 +07:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const zParsedPackageJson = z.object({
|
|
|
|
"name": z.string(),
|
|
|
|
"version": z.string(),
|
|
|
|
"homepage": z.string().optional(),
|
|
|
|
"keycloakify": z
|
|
|
|
.object({
|
|
|
|
"extraPages": z.array(z.string()).optional(),
|
|
|
|
"extraThemeProperties": z.array(z.string()).optional(),
|
2023-02-03 14:28:06 +01:00
|
|
|
"areAppAndKeycloakServerSharingSameDomain": z.boolean().optional(),
|
|
|
|
"artifactId": z.string().optional(),
|
|
|
|
"groupId": z.string().optional(),
|
2023-02-04 18:02:39 +01:00
|
|
|
"bundler": z.enum(bundlers).optional()
|
2022-08-16 14:41:06 +07:00
|
|
|
})
|
2022-08-20 11:44:48 +07:00
|
|
|
.optional()
|
2022-08-16 14:41:06 +07:00
|
|
|
});
|
|
|
|
|
|
|
|
assert<Equals<ReturnType<typeof zParsedPackageJson["parse"]>, ParsedPackageJson>>();
|
|
|
|
|
|
|
|
/** Consolidated build option gathered form CLI arguments and config in package.json */
|
|
|
|
export type BuildOptions = BuildOptions.Standalone | BuildOptions.ExternalAssets;
|
|
|
|
|
|
|
|
export namespace BuildOptions {
|
|
|
|
export type Common = {
|
2022-09-08 12:06:26 +03:00
|
|
|
isSilent: boolean;
|
2022-08-16 14:41:06 +07:00
|
|
|
version: string;
|
|
|
|
themeName: string;
|
|
|
|
extraPages?: string[];
|
|
|
|
extraThemeProperties?: string[];
|
|
|
|
groupId: string;
|
2023-02-03 14:28:06 +01:00
|
|
|
artifactId?: string;
|
2023-02-04 18:02:39 +01:00
|
|
|
bundler: Bundler;
|
2022-08-16 14:41:06 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
export type Standalone = Common & {
|
|
|
|
isStandalone: true;
|
|
|
|
urlPathname: string | undefined;
|
|
|
|
};
|
|
|
|
|
|
|
|
export type ExternalAssets = ExternalAssets.SameDomain | ExternalAssets.DifferentDomains;
|
|
|
|
|
|
|
|
export namespace ExternalAssets {
|
|
|
|
export type CommonExternalAssets = Common & {
|
|
|
|
isStandalone: false;
|
|
|
|
};
|
|
|
|
|
|
|
|
export type SameDomain = CommonExternalAssets & {
|
2022-09-07 11:25:46 +02:00
|
|
|
areAppAndKeycloakServerSharingSameDomain: true;
|
2022-08-16 14:41:06 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
export type DifferentDomains = CommonExternalAssets & {
|
2022-09-07 11:25:46 +02:00
|
|
|
areAppAndKeycloakServerSharingSameDomain: false;
|
2022-08-16 14:41:06 +07:00
|
|
|
urlOrigin: string;
|
|
|
|
urlPathname: string | undefined;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function readBuildOptions(params: {
|
|
|
|
packageJson: string;
|
|
|
|
CNAME: string | undefined;
|
|
|
|
isExternalAssetsCliParamProvided: boolean;
|
2022-09-08 12:06:26 +03:00
|
|
|
isSilent: boolean;
|
2022-08-16 14:41:06 +07:00
|
|
|
}): BuildOptions {
|
2022-09-08 12:06:26 +03:00
|
|
|
const { packageJson, CNAME, isExternalAssetsCliParamProvided, isSilent } = params;
|
2022-08-16 14:41:06 +07:00
|
|
|
|
|
|
|
const parsedPackageJson = zParsedPackageJson.parse(JSON.parse(packageJson));
|
|
|
|
|
|
|
|
const url = (() => {
|
|
|
|
const { homepage } = parsedPackageJson;
|
|
|
|
|
|
|
|
let url: URL | undefined = undefined;
|
|
|
|
|
|
|
|
if (homepage !== undefined) {
|
|
|
|
url = new URL(homepage);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CNAME !== undefined) {
|
|
|
|
url = new URL(`https://${CNAME.replace(/\s+$/, "")}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (url === undefined) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
"origin": url.origin,
|
|
|
|
"pathname": (() => {
|
|
|
|
const out = url.pathname.replace(/([^/])$/, "$1/");
|
|
|
|
|
|
|
|
return out === "/" ? undefined : out;
|
2022-08-20 11:44:48 +07:00
|
|
|
})()
|
2022-08-16 14:41:06 +07:00
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
|
|
const common: BuildOptions.Common = (() => {
|
|
|
|
const { name, keycloakify = {}, version, homepage } = parsedPackageJson;
|
|
|
|
|
2023-02-03 14:28:06 +01:00
|
|
|
const { extraPages, extraThemeProperties, groupId, artifactId, bundler } = keycloakify ?? {};
|
2022-08-16 14:41:06 +07:00
|
|
|
|
|
|
|
const themeName = name
|
|
|
|
.replace(/^@(.*)/, "$1")
|
|
|
|
.split("/")
|
|
|
|
.join("-");
|
|
|
|
|
|
|
|
return {
|
|
|
|
themeName,
|
2023-02-04 17:44:02 +01:00
|
|
|
"bundler": (() => {
|
|
|
|
const { KEYCLOAKIFY_BUNDLER } = process.env;
|
|
|
|
|
|
|
|
assert(
|
|
|
|
typeGuard<Bundler | undefined>(
|
|
|
|
KEYCLOAKIFY_BUNDLER,
|
2023-02-04 18:02:39 +01:00
|
|
|
[undefined, ...id<readonly string[]>(bundlers)].includes(KEYCLOAKIFY_BUNDLER)
|
|
|
|
),
|
|
|
|
`${symToStr({ KEYCLOAKIFY_BUNDLER })} should be one of ${bundlers.join(", ")}`
|
2023-02-04 17:44:02 +01:00
|
|
|
);
|
|
|
|
|
2023-02-04 18:02:39 +01:00
|
|
|
return KEYCLOAKIFY_BUNDLER ?? bundler ?? "keycloakify";
|
2023-02-04 17:44:02 +01:00
|
|
|
})(),
|
2023-02-03 14:28:06 +01:00
|
|
|
"artifactId": process.env.KEYCLOAKIFY_ARTIFACT_ID ?? artifactId,
|
2022-08-16 14:41:06 +07:00
|
|
|
"groupId": (() => {
|
|
|
|
const fallbackGroupId = `${themeName}.keycloak`;
|
|
|
|
|
|
|
|
return (
|
2023-02-03 14:28:06 +01:00
|
|
|
process.env.KEYCLOAKIFY_GROUP_ID ??
|
|
|
|
groupId ??
|
2022-08-16 14:41:06 +07:00
|
|
|
(!homepage
|
|
|
|
? fallbackGroupId
|
|
|
|
: urlParse(homepage)
|
|
|
|
.host?.replace(/:[0-9]+$/, "")
|
|
|
|
?.split(".")
|
|
|
|
.reverse()
|
|
|
|
.join(".") ?? fallbackGroupId) + ".keycloak"
|
|
|
|
);
|
|
|
|
})(),
|
2023-02-04 17:44:02 +01:00
|
|
|
"version": process.env.KEYCLOAKIFY_VERSION ?? version,
|
2022-08-16 14:41:06 +07:00
|
|
|
extraPages,
|
2022-09-08 12:06:26 +03:00
|
|
|
extraThemeProperties,
|
|
|
|
isSilent
|
2022-08-16 14:41:06 +07:00
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
|
|
if (isExternalAssetsCliParamProvided) {
|
|
|
|
const commonExternalAssets = id<BuildOptions.ExternalAssets.CommonExternalAssets>({
|
|
|
|
...common,
|
2022-08-20 11:44:48 +07:00
|
|
|
"isStandalone": false
|
2022-08-16 14:41:06 +07:00
|
|
|
});
|
|
|
|
|
2022-09-07 11:25:46 +02:00
|
|
|
if (parsedPackageJson.keycloakify?.areAppAndKeycloakServerSharingSameDomain) {
|
2022-08-16 14:41:06 +07:00
|
|
|
return id<BuildOptions.ExternalAssets.SameDomain>({
|
|
|
|
...commonExternalAssets,
|
2022-09-07 11:25:46 +02:00
|
|
|
"areAppAndKeycloakServerSharingSameDomain": true
|
2022-08-16 14:41:06 +07:00
|
|
|
});
|
|
|
|
} else {
|
|
|
|
assert(
|
|
|
|
url !== undefined,
|
|
|
|
[
|
|
|
|
"Can't compile in external assets mode if we don't know where",
|
|
|
|
"the app will be hosted.",
|
|
|
|
"You should provide a homepage field in the package.json (or create a",
|
|
|
|
"public/CNAME file.",
|
|
|
|
"Alternatively, if your app and the Keycloak server are on the same domain, ",
|
|
|
|
"eg https://example.com is your app and https://example.com/auth is the keycloak",
|
2022-09-07 11:25:46 +02:00
|
|
|
'admin UI, you can set "keycloakify": { "areAppAndKeycloakServerSharingSameDomain": true }',
|
2022-08-20 11:44:48 +07:00
|
|
|
"in your package.json"
|
|
|
|
].join(" ")
|
2022-08-16 14:41:06 +07:00
|
|
|
);
|
|
|
|
|
|
|
|
return id<BuildOptions.ExternalAssets.DifferentDomains>({
|
|
|
|
...commonExternalAssets,
|
2022-09-07 11:25:46 +02:00
|
|
|
"areAppAndKeycloakServerSharingSameDomain": false,
|
2022-08-16 14:41:06 +07:00
|
|
|
"urlOrigin": url.origin,
|
2022-08-20 11:44:48 +07:00
|
|
|
"urlPathname": url.pathname
|
2022-08-16 14:41:06 +07:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return id<BuildOptions.Standalone>({
|
|
|
|
...common,
|
|
|
|
"isStandalone": true,
|
2022-08-20 11:44:48 +07:00
|
|
|
"urlPathname": url?.pathname
|
2022-08-16 14:41:06 +07:00
|
|
|
});
|
|
|
|
}
|