Moving on

This commit is contained in:
Joseph Garrone 2024-01-30 05:54:36 +01:00
parent 2799a52d0c
commit 22fa1411bf
5 changed files with 166 additions and 146 deletions

View File

@ -4,9 +4,11 @@ import { join as pathJoin } from "path";
import parseArgv from "minimist"; import parseArgv from "minimist";
import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath"; import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath";
import * as fs from "fs"; import * as fs from "fs";
import { getParsedKeycloakifyViteConfig, getKeycloakifyBuildDirPath } from "./parsedKeycloakifyViteConfig";
/** Consolidated build option gathered form CLI arguments and config in package.json */ /** Consolidated build option gathered form CLI arguments and config in package.json */
export type BuildOptions = { export type BuildOptions = {
bundler: "vite" | "webpack";
isSilent: boolean; isSilent: boolean;
themeVersion: string; themeVersion: string;
themeNames: string[]; themeNames: string[];
@ -25,113 +27,102 @@ export type BuildOptions = {
* In this case the urlPathname will be "/my-app/" */ * In this case the urlPathname will be "/my-app/" */
urlPathname: string | undefined; urlPathname: string | undefined;
assetsDirPath: string; assetsDirPath: string;
bundler: "vite" | "webpack";
}; };
export function readBuildOptions(params: { reactAppRootDirPath: string; processArgv: string[] }): BuildOptions { export function readBuildOptions(params: { reactAppRootDirPath: string; processArgv: string[] }): BuildOptions {
const { reactAppRootDirPath, processArgv } = params; const { reactAppRootDirPath, processArgv } = params;
const { isSilentCliParamProvided } = (() => {
const argv = parseArgv(processArgv);
return {
"isSilentCliParamProvided": typeof argv["silent"] === "boolean" ? argv["silent"] : false
};
})();
const parsedPackageJson = getParsedPackageJson({ reactAppRootDirPath }); const parsedPackageJson = getParsedPackageJson({ reactAppRootDirPath });
const { name, keycloakify = {}, version, homepage } = parsedPackageJson; const { parsedKeycloakifyViteConfig } =
getParsedKeycloakifyViteConfig({
const { extraThemeProperties, groupId, artifactId, doCreateJar, loginThemeResourcesFromKeycloakVersion } = keycloakify ?? {}; "parsedPackageJson_keycloakify_keycloakifyBuildDirPath": parsedPackageJson.keycloakify?.keycloakifyBuildDirPath,
reactAppRootDirPath
}) ?? {};
const themeNames = (() => { const themeNames = (() => {
if (keycloakify.themeName === undefined) { if (parsedPackageJson.keycloakify?.themeName === undefined) {
return [ return [
name parsedPackageJson.name
.replace(/^@(.*)/, "$1") .replace(/^@(.*)/, "$1")
.split("/") .split("/")
.join("-") .join("-")
]; ];
} }
if (typeof keycloakify.themeName === "string") { if (typeof parsedPackageJson.keycloakify.themeName === "string") {
return [keycloakify.themeName]; return [parsedPackageJson.keycloakify.themeName];
} }
return keycloakify.themeName; return parsedPackageJson.keycloakify.themeName;
})(); })();
return { const { keycloakifyBuildDirPath } = getKeycloakifyBuildDirPath({
"parsedPackageJson_keycloakify_keycloakifyBuildDirPath": parsedPackageJson.keycloakify?.keycloakifyBuildDirPath,
reactAppRootDirPath, reactAppRootDirPath,
"bundler": parsedKeycloakifyViteConfig !== undefined ? "vite" : "webpack"
});
//const keycloakifyBuildDirPath = keycloakifyBuildDirPath_vite ?? pathJoin(reactAppRootDirPath, "build_keycloak");
return {
"bundler": parsedKeycloakifyViteConfig !== undefined ? "vite" : "webpack",
"isSilent": (() => {
const argv = parseArgv(processArgv);
return typeof argv["silent"] === "boolean" ? argv["silent"] : false;
})(),
"themeVersion": process.env.KEYCLOAKIFY_THEME_VERSION ?? parsedPackageJson.version ?? "0.0.0",
themeNames, themeNames,
"doCreateJar": doCreateJar ?? true, "extraThemeProperties": parsedPackageJson.keycloakify?.extraThemeProperties,
"artifactId": process.env.KEYCLOAKIFY_ARTIFACT_ID ?? artifactId ?? `${themeNames[0]}-keycloak-theme`,
"groupId": (() => { "groupId": (() => {
const fallbackGroupId = `${themeNames[0]}.keycloak`; const fallbackGroupId = `${themeNames[0]}.keycloak`;
return ( return (
process.env.KEYCLOAKIFY_GROUP_ID ?? process.env.KEYCLOAKIFY_GROUP_ID ??
groupId ?? parsedPackageJson.keycloakify?.groupId ??
(!homepage (parsedPackageJson.homepage === undefined
? fallbackGroupId ? fallbackGroupId
: urlParse(homepage) : urlParse(parsedPackageJson.homepage)
.host?.replace(/:[0-9]+$/, "") .host?.replace(/:[0-9]+$/, "")
?.split(".") ?.split(".")
.reverse() .reverse()
.join(".") ?? fallbackGroupId) + ".keycloak" .join(".") ?? fallbackGroupId) + ".keycloak"
); );
})(), })(),
"themeVersion": process.env.KEYCLOAKIFY_THEME_VERSION ?? process.env.KEYCLOAKIFY_VERSION ?? version ?? "0.0.0", "artifactId": process.env.KEYCLOAKIFY_ARTIFACT_ID ?? parsedPackageJson.keycloakify?.artifactId ?? `${themeNames[0]}-keycloak-theme`,
extraThemeProperties, "doCreateJar": parsedPackageJson.keycloakify?.doCreateJar ?? true,
"isSilent": isSilentCliParamProvided, "loginThemeResourcesFromKeycloakVersion": parsedPackageJson.keycloakify?.loginThemeResourcesFromKeycloakVersion ?? "11.0.3",
"loginThemeResourcesFromKeycloakVersion": loginThemeResourcesFromKeycloakVersion ?? "11.0.3", reactAppRootDirPath,
"publicDirPath": (() => { "reactAppBuildDirPath": (() => {
let { PUBLIC_DIR_PATH } = process.env; if (parsedKeycloakifyViteConfig !== undefined) {
return pathJoin(reactAppRootDirPath, parsedKeycloakifyViteConfig.buildDir);
}
if (PUBLIC_DIR_PATH !== undefined) { if (parsedPackageJson.keycloakify?.reactAppBuildDirPath !== undefined) {
return getAbsoluteAndInOsFormatPath({ return getAbsoluteAndInOsFormatPath({
"pathIsh": PUBLIC_DIR_PATH, "pathIsh": parsedPackageJson.keycloakify?.reactAppBuildDirPath,
"cwd": reactAppRootDirPath
});
}
return pathJoin(reactAppRootDirPath, "build");
})(),
"publicDirPath": (() => {
if (parsedKeycloakifyViteConfig !== undefined) {
return parsedKeycloakifyViteConfig.publicDirPath;
}
if (process.env.PUBLIC_DIR_PATH !== undefined) {
return getAbsoluteAndInOsFormatPath({
"pathIsh": process.env.PUBLIC_DIR_PATH,
"cwd": reactAppRootDirPath "cwd": reactAppRootDirPath
}); });
} }
return pathJoin(reactAppRootDirPath, "public"); return pathJoin(reactAppRootDirPath, "public");
})(), })(),
"reactAppBuildDirPath": (() => { keycloakifyBuildDirPath,
const { reactAppBuildDirPath } = parsedPackageJson.keycloakify ?? {};
if (reactAppBuildDirPath !== undefined) {
return getAbsoluteAndInOsFormatPath({
"pathIsh": reactAppBuildDirPath,
"cwd": reactAppRootDirPath
});
}
for (const name of ["build", "dist"]) {
const out = pathJoin(reactAppRootDirPath, name);
if (!fs.existsSync(out)) {
continue;
}
return out;
}
throw new Error("Please use the reactAppBuildDirPath option to specify the build directory of your react app");
})(),
"keycloakifyBuildDirPath": (() => {
const { keycloakifyBuildDirPath } = parsedPackageJson.keycloakify ?? {};
if (keycloakifyBuildDirPath !== undefined) {
return getAbsoluteAndInOsFormatPath({
"pathIsh": keycloakifyBuildDirPath,
"cwd": reactAppRootDirPath
});
}
return pathJoin(reactAppRootDirPath, "build_keycloak");
})(),
"cacheDirPath": pathJoin( "cacheDirPath": pathJoin(
(() => { (() => {
let { XDG_CACHE_HOME } = process.env; let { XDG_CACHE_HOME } = process.env;

View File

@ -5,20 +5,19 @@ import { z } from "zod";
import { pathJoin } from "../tools/pathJoin"; import { pathJoin } from "../tools/pathJoin";
import { keycloakifyViteConfigJsonBasename } from "../constants"; import { keycloakifyViteConfigJsonBasename } from "../constants";
import type { OptionalIfCanBeUndefined } from "../tools/OptionalIfCanBeUndefined"; import type { OptionalIfCanBeUndefined } from "../tools/OptionalIfCanBeUndefined";
import { getAbsoluteAndInOsFormatPath } from "../tools/getAbsoluteAndInOsFormatPath";
export type ParsedKeycloakifyViteConfig = { export type ParsedKeycloakifyViteConfig = {
reactAppRootDirPath: string; buildDir: string;
publicDirPath: string; publicDir: string;
assetsDirPath: string; assetsDir: string;
reactAppBuildDirPath: string;
urlPathname: string | undefined; urlPathname: string | undefined;
}; };
export const zParsedKeycloakifyViteConfig = z.object({ const zParsedKeycloakifyViteConfig = z.object({
"reactAppRootDirPath": z.string(), "buildDir": z.string(),
"publicDirPath": z.string(), "publicDir": z.string(),
"assetsDirPath": z.string(), "assetsDir": z.string(),
"reactAppBuildDirPath": z.string(),
"urlPathname": z.string().optional() "urlPathname": z.string().optional()
}); });
@ -29,20 +28,33 @@ export const zParsedKeycloakifyViteConfig = z.object({
assert<Equals<Got, Expected>>(); assert<Equals<Got, Expected>>();
} }
let cache: { parsedKeycloakifyViteConfig: ParsedKeycloakifyViteConfig | undefined } | undefined = undefined; export function getParsedKeycloakifyViteConfig(params: {
reactAppRootDirPath: string;
parsedPackageJson_keycloakify_keycloakifyBuildDirPath: string | undefined;
}):
| {
parsedKeycloakifyViteConfig: ParsedKeycloakifyViteConfig;
}
| undefined {
const { reactAppRootDirPath, parsedPackageJson_keycloakify_keycloakifyBuildDirPath } = params;
export function getParsedKeycloakifyViteConfig(params: { keycloakifyBuildDirPath: string }): ParsedKeycloakifyViteConfig | undefined { const viteConfigTsFilePath = pathJoin(reactAppRootDirPath, "vite.config.ts");
const { keycloakifyBuildDirPath } = params;
if (cache !== undefined) { if (!fs.existsSync(viteConfigTsFilePath)) {
return cache.parsedKeycloakifyViteConfig; return undefined;
} }
const { keycloakifyBuildDirPath } = getKeycloakifyBuildDirPath({
reactAppRootDirPath,
parsedPackageJson_keycloakify_keycloakifyBuildDirPath,
"bundler": "vite"
});
const parsedKeycloakifyViteConfig = (() => { const parsedKeycloakifyViteConfig = (() => {
const keycloakifyViteConfigJsonFilePath = pathJoin(keycloakifyBuildDirPath, keycloakifyViteConfigJsonBasename); const keycloakifyViteConfigJsonFilePath = pathJoin(keycloakifyBuildDirPath, keycloakifyViteConfigJsonBasename);
if (!fs.existsSync(keycloakifyViteConfigJsonFilePath)) { if (!fs.existsSync(keycloakifyViteConfigJsonFilePath)) {
return undefined; throw new Error("Missing Keycloakify Vite plugin output.");
} }
let out: ParsedKeycloakifyViteConfig; let out: ParsedKeycloakifyViteConfig;
@ -69,11 +81,36 @@ export function getParsedKeycloakifyViteConfig(params: { keycloakifyBuildDirPath
return out; return out;
})(); })();
if (parsedKeycloakifyViteConfig === undefined && fs.existsSync(pathJoin(keycloakifyBuildDirPath, "vite.config.ts"))) { return { parsedKeycloakifyViteConfig };
throw new Error("Make sure you have enabled the Keycloakiy plugin in your vite.config.ts"); }
}
export function getKeycloakifyBuildDirPath(params: {
cache = { parsedKeycloakifyViteConfig }; reactAppRootDirPath: string;
parsedPackageJson_keycloakify_keycloakifyBuildDirPath: string | undefined;
return parsedKeycloakifyViteConfig; bundler: "vite" | "webpack";
}) {
const { reactAppRootDirPath, parsedPackageJson_keycloakify_keycloakifyBuildDirPath, bundler } = params;
const keycloakifyBuildDirPath = (() => {
if (parsedPackageJson_keycloakify_keycloakifyBuildDirPath !== undefined) {
getAbsoluteAndInOsFormatPath({
"pathIsh": parsedPackageJson_keycloakify_keycloakifyBuildDirPath,
"cwd": reactAppRootDirPath
});
}
return pathJoin(
reactAppRootDirPath,
`${(() => {
switch (bundler) {
case "vite":
return "dist";
case "webpack":
return "build";
}
})()}_keycloak`
);
})();
return { keycloakifyBuildDirPath };
} }

View File

@ -20,7 +20,7 @@ export type ParsedPackageJson = {
}; };
}; };
export const zParsedPackageJson = z.object({ const zParsedPackageJson = z.object({
"name": z.string(), "name": z.string(),
"version": z.string().optional(), "version": z.string().optional(),
"homepage": z.string().optional(), "homepage": z.string().optional(),

View File

@ -7,6 +7,8 @@ export function getAbsoluteAndInOsFormatPath(params: { pathIsh: string; cwd: str
pathOut = pathOut.replace(/\//g, pathSep); pathOut = pathOut.replace(/\//g, pathSep);
pathOut = pathOut.endsWith(pathSep) ? pathOut.slice(0, -1) : pathOut;
if (!pathIsAbsolute(pathOut)) { if (!pathIsAbsolute(pathOut)) {
pathOut = pathJoin(cwd, pathOut); pathOut = pathJoin(cwd, pathOut);
} }

View File

@ -1,87 +1,75 @@
import { join as pathJoin, sep as pathSep } from "path"; import { join as pathJoin, relative as pathRelative, sep as pathSep } from "path";
import { getParsedPackageJson } from "../bin/keycloakify/parsedPackageJson"; import { getParsedPackageJson } from "../bin/keycloakify/parsedPackageJson";
import type { Plugin } from "vite"; import type { Plugin } from "vite";
import { assert } from "tsafe/assert"; import { assert } from "tsafe/assert";
import { getAbsoluteAndInOsFormatPath } from "../bin/tools/getAbsoluteAndInOsFormatPath";
import * as fs from "fs"; import * as fs from "fs";
import { keycloakifyViteConfigJsonBasename, nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "../bin/constants"; import { keycloakifyViteConfigJsonBasename, nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "../bin/constants";
import type { ParsedKeycloakifyViteConfig } from "../bin/keycloakify/parsedKeycloakifyViteConfig"; import { type ParsedKeycloakifyViteConfig, getKeycloakifyBuildDirPath } from "../bin/keycloakify/parsedKeycloakifyViteConfig";
import { replaceAll } from "../bin/tools/String.prototype.replaceAll"; import { replaceAll } from "../bin/tools/String.prototype.replaceAll";
import { id } from "tsafe/id";
export function keycloakify(): Plugin { export function keycloakify(): Plugin {
let keycloakifyViteConfig: ParsedKeycloakifyViteConfig | undefined = undefined; let reactAppRootDirPath: string | undefined = undefined;
let urlPathname: string | undefined = undefined;
return { return {
"name": "keycloakify", "name": "keycloakify",
"configResolved": resolvedConfig => { "configResolved": resolvedConfig => {
const reactAppRootDirPath = resolvedConfig.root; reactAppRootDirPath = resolvedConfig.root;
const reactAppBuildDirPath = pathJoin(reactAppRootDirPath, resolvedConfig.build.outDir); urlPathname = (() => {
let out = resolvedConfig.env.BASE_URL;
keycloakifyViteConfig = { if (out === undefined) {
reactAppRootDirPath, return undefined;
"publicDirPath": resolvedConfig.publicDir,
"assetsDirPath": pathJoin(reactAppBuildDirPath, resolvedConfig.build.assetsDir),
reactAppBuildDirPath,
"urlPathname": (() => {
let out = resolvedConfig.env.BASE_URL;
if (out === undefined) {
return undefined;
}
if (!out.startsWith("/")) {
out = "/" + out;
}
if (!out.endsWith("/")) {
out += "/";
}
return out;
})()
};
const parsedPackageJson = getParsedPackageJson({ reactAppRootDirPath });
if (parsedPackageJson.keycloakify?.reactAppBuildDirPath !== undefined) {
throw new Error(
[
"Please do not use the keycloakify.reactAppBuildDirPath option in your package.json.",
"In Vite setups it's inferred automatically from the vite config."
].join(" ")
);
}
const keycloakifyBuildDirPath = (() => {
const { keycloakifyBuildDirPath } = parsedPackageJson.keycloakify ?? {};
if (keycloakifyBuildDirPath !== undefined) {
return getAbsoluteAndInOsFormatPath({
"pathIsh": keycloakifyBuildDirPath,
"cwd": reactAppRootDirPath
});
} }
return pathJoin(reactAppRootDirPath, "build_keycloak"); if (!out.startsWith("/")) {
out = "/" + out;
}
if (!out.endsWith("/")) {
out += "/";
}
return out;
})(); })();
const { keycloakifyBuildDirPath } = getKeycloakifyBuildDirPath({
"parsedPackageJson_keycloakify_keycloakifyBuildDirPath": getParsedPackageJson({ reactAppRootDirPath }).keycloakify
?.keycloakifyBuildDirPath,
reactAppRootDirPath,
"bundler": "vite"
});
if (!fs.existsSync(keycloakifyBuildDirPath)) { if (!fs.existsSync(keycloakifyBuildDirPath)) {
fs.mkdirSync(keycloakifyBuildDirPath); fs.mkdirSync(keycloakifyBuildDirPath);
} }
fs.writeFileSync( fs.writeFileSync(
pathJoin(keycloakifyBuildDirPath, keycloakifyViteConfigJsonBasename), pathJoin(keycloakifyBuildDirPath, keycloakifyViteConfigJsonBasename),
Buffer.from(JSON.stringify(keycloakifyViteConfig, null, 2), "utf8") Buffer.from(
JSON.stringify(
id<ParsedKeycloakifyViteConfig>({
"publicDir": pathRelative(reactAppRootDirPath, resolvedConfig.publicDir),
"assetsDir": resolvedConfig.build.assetsDir,
"buildDir": resolvedConfig.build.outDir,
urlPathname
}),
null,
2
),
"utf8"
)
); );
}, },
"transform": (code, id) => { "transform": (code, id) => {
assert(keycloakifyViteConfig !== undefined); assert(reactAppRootDirPath !== undefined);
let transformedCode: string | undefined = undefined; let transformedCode: string | undefined = undefined;
replace_import_meta_env_base_url_in_source_code: { replace_import_meta_env_base_url_in_source_code: {
{ {
const isWithinSourceDirectory = id.startsWith(pathJoin(keycloakifyViteConfig.publicDirPath, "src") + pathSep); const isWithinSourceDirectory = id.startsWith(pathJoin(reactAppRootDirPath, "src") + pathSep);
if (!isWithinSourceDirectory) { if (!isWithinSourceDirectory) {
break replace_import_meta_env_base_url_in_source_code; break replace_import_meta_env_base_url_in_source_code;
@ -110,18 +98,20 @@ export function keycloakify(): Plugin {
[ [
`(`, `(`,
`(${windowToken}.${nameOfTheGlobal} === undefined || import.meta.env.MODE === "development") ?`, `(${windowToken}.${nameOfTheGlobal} === undefined || import.meta.env.MODE === "development") ?`,
` "${keycloakifyViteConfig.urlPathname ?? "/"}" :`, ` "${urlPathname ?? "/"}" :`,
` \`\${${windowToken}.${nameOfTheGlobal}.url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/\``, ` \`\${${windowToken}.${nameOfTheGlobal}.url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/\``,
`)` `)`
].join("") ].join("")
); );
} }
if (transformedCode !== undefined) { if (transformedCode === undefined) {
return { return;
"code": transformedCode
};
} }
return {
"code": transformedCode
};
} }
}; };
} }