Compare commits

...

20 Commits

Author SHA1 Message Date
4d67f16e94 Release candidate 2024-02-10 20:40:59 +01:00
334ec1870a Throw if unrecognized bundler when getting BASE_URL for mocks 2024-02-10 20:40:41 +01:00
ef5e4fccd3 Replace the common asset path url in the ftl (upstream) to help pepole figure out what's going on 2024-02-10 20:04:08 +01:00
8535edcfd4 Relase candidate 2024-02-10 19:51:39 +01:00
bda76200d7 Base url that works everywhere in mocks 2024-02-10 19:51:24 +01:00
db0dc96cc7 Release candidate 2024-02-09 17:56:57 +01:00
6d62b5a150 Don't replace process.env.PUBLIC_URL 2024-02-09 17:56:28 +01:00
217439d673 Export polyfill of process.env.PUBLIC_URL 2024-02-09 17:52:26 +01:00
1f79a8f7dc Deprecate keycloakJsAdapter 2024-02-09 17:52:03 +01:00
7596786b18 Release candidate 2024-02-08 23:37:14 +01:00
2540b06c94 Tweak the resources of the default theme that are kept 2024-02-08 23:36:52 +01:00
43eeaf3002 Remove misleading comment in start-keycloak-container script 2024-02-08 14:41:07 +01:00
037cd150de Release candidate 2024-02-08 14:10:10 +01:00
ae0b059217 Fix a couple of bug in donwnload-builtin-keycloak-theme 2024-02-08 14:09:55 +01:00
8255ce1158 Release candidate 2024-02-08 00:56:55 +01:00
5bf905723c Feature -p or --project option for ease of use in monorepo setups #449 2024-02-08 00:56:33 +01:00
3e336f4937 Release candidate 2024-02-08 00:12:50 +01:00
cd1cc37916 Explicitely exclude a number of node deps from Keycloak commons for bundle size 2024-02-08 00:12:10 +01:00
4ad7183d7e Prevent crashing when github is not up to date yet 2024-02-07 21:22:58 +01:00
e1b52e7439 Implement remote caching mechanism to prevent full download of the Keycloak sources 2024-02-07 21:17:09 +01:00
22 changed files with 304 additions and 179 deletions

View File

@ -1,6 +1,6 @@
{
"name": "keycloakify",
"version": "9.4.0-rc.0",
"version": "9.4.0-rc.7",
"description": "Create Keycloak themes using React",
"repository": {
"type": "git",

View File

@ -5,7 +5,6 @@ import { crawl } from "../src/bin/tools/crawl";
import { downloadBuiltinKeycloakTheme } from "../src/bin/download-builtin-keycloak-theme";
import { getProjectRoot } from "../src/bin/tools/getProjectRoot";
import { getLogger } from "../src/bin/tools/logger";
import { rmSync } from "../src/bin/tools/fs.rmSync";
// NOTE: To run without argument when we want to generate src/i18n/generated_kcMessages files,
// update the version array for generating for newer version.

13
src/PUBLIC_URL.ts Normal file
View File

@ -0,0 +1,13 @@
import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "keycloakify/bin/constants";
/**
* This is an equivalent of process.env.PUBLIC_URL thay you can use in Webpack projects.
* This works both in your main app and in your Keycloak theme.
*/
export const PUBLIC_URL = (() => {
const kcContext = (window as any)[nameOfTheGlobal];
return kcContext === undefined || process.env.NODE_ENV === "development"
? process.env.PUBLIC_URL
: `${kcContext.url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}`;
})();

View File

@ -2,9 +2,7 @@ import type { DeepPartial } from "keycloakify/tools/DeepPartial";
import { deepAssign } from "keycloakify/tools/deepAssign";
import type { ExtendKcContext } from "./getKcContextFromWindow";
import { getKcContextFromWindow } from "./getKcContextFromWindow";
import { pathJoin } from "keycloakify/bin/tools/pathJoin";
import { symToStr } from "tsafe/symToStr";
import { resources_common } from "keycloakify/bin/constants";
import { kcContextMocks, kcContextCommonMock } from "keycloakify/account/kcContext/kcContextMocks";
export function createGetKcContext<KcContextExtension extends { pageId: string } = never>(params?: {
@ -88,8 +86,6 @@ export function createGetKcContext<KcContextExtension extends { pageId: string }
return { "kcContext": undefined as any };
}
realKcContext.url.resourcesCommonPath = pathJoin(realKcContext.url.resourcesPath, resources_common);
return { "kcContext": realKcContext as any };
}

View File

@ -1,12 +1,10 @@
import "minimal-polyfills/Object.fromEntries";
import { resources_common, keycloak_resources } from "keycloakify/bin/constants";
import { pathJoin } from "keycloakify/bin/tools/pathJoin";
import { id } from "tsafe/id";
import type { KcContext } from "./KcContext";
import { BASE_URL } from "keycloakify/lib/BASE_URL";
const PUBLIC_URL = (typeof process !== "object" ? undefined : process.env?.["PUBLIC_URL"]) || "/";
const resourcesPath = pathJoin(PUBLIC_URL, keycloak_resources, "account", "resources");
const resourcesPath = `${BASE_URL}${keycloak_resources}/account/resources`;
export const kcContextCommonMock: KcContext.Common = {
"themeVersion": "0.0.0",
@ -15,7 +13,7 @@ export const kcContextCommonMock: KcContext.Common = {
"themeName": "my-theme-name",
"url": {
resourcesPath,
"resourcesCommonPath": pathJoin(resourcesPath, resources_common),
"resourcesCommonPath": `${resourcesPath}/${resources_common}`,
"resourceUrl": "#",
"accountUrl": "#",
"applicationsUrl": "#",

View File

@ -7,10 +7,7 @@ import { themeTypes, keycloak_resources, lastKeycloakVersionWithAccountV1 } from
import * as fs from "fs";
(async () => {
const reactAppRootDirPath = process.cwd();
const buildOptions = readBuildOptions({
reactAppRootDirPath,
"processArgv": process.argv.slice(2)
});
@ -45,5 +42,5 @@ import * as fs from "fs";
fs.writeFileSync(pathJoin(buildOptions.publicDirPath, keycloak_resources, ".gitignore"), Buffer.from("*", "utf8"));
console.log(`${pathRelative(reactAppRootDirPath, reservedDirPath)} directory created.`);
console.log(`${pathRelative(buildOptions.reactAppRootDirPath, reservedDirPath)} directory created.`);
})();

View File

@ -50,43 +50,121 @@ export async function downloadBuiltinKeycloakTheme(params: { keycloakVersion: st
});
}
install_and_move_to_common_resources_generated_in_keycloak_v2: {
const accountV2DirSrcDirPath = pathJoin(destDirPath, "keycloak.v2", "account", "src");
remove_keycloak_v2: {
const keycloakV2DirPath = pathJoin(destDirPath, "keycloak.v2");
if (!fs.existsSync(accountV2DirSrcDirPath)) {
break install_and_move_to_common_resources_generated_in_keycloak_v2;
if (!fs.existsSync(keycloakV2DirPath)) {
break remove_keycloak_v2;
}
const packageManager = fs.existsSync(pathJoin(accountV2DirSrcDirPath, "pnpm-lock.yaml")) ? "pnpm" : "npm";
rmSync(keycloakV2DirPath, { "recursive": true });
}
if (packageManager === "pnpm") {
try {
child_process.execSync(`which pnpm`);
} catch {
console.log(`Installing pnpm globally`);
child_process.execSync(`npm install -g pnpm`);
// Note, this is an optimization for reducing the size of the jar
remove_unused_node_modules: {
const nodeModuleDirPath = pathJoin(destDirPath, "keycloak", "common", "resources", "node_modules");
if (!fs.existsSync(nodeModuleDirPath)) {
break remove_unused_node_modules;
}
const toDeletePerfixes = [
"angular",
"bootstrap",
"rcue",
"font-awesome",
"ng-file-upload",
pathJoin("patternfly", "dist", "sass"),
pathJoin("patternfly", "dist", "less"),
pathJoin("patternfly", "dist", "js"),
"d3",
pathJoin("jquery", "src"),
"c3",
"core-js",
"eonasdan-bootstrap-datetimepicker",
"moment",
"react",
"patternfly-bootstrap-treeview",
"popper.js",
"tippy.js",
"jquery-match-height",
"google-code-prettify",
"patternfly-bootstrap-combobox",
"focus-trap",
"tabbable",
"scheduler",
"@types",
"datatables.net",
"datatables.net-colreorder",
"tslib",
"prop-types",
"file-selector",
"datatables.net-colreorder-bs",
"object-assign",
"warning",
"js-tokens",
"loose-envify",
"prop-types-extra",
"attr-accept",
"datatables.net-select",
"drmonty-datatables-colvis",
"datatables.net-bs",
pathJoin("@patternfly", "react"),
pathJoin("@patternfly", "patternfly", "docs")
];
transformCodebase({
"srcDirPath": nodeModuleDirPath,
"destDirPath": nodeModuleDirPath,
"transformSourceCode": ({ sourceCode, fileRelativePath }) => {
if (fileRelativePath.endsWith(".map")) {
return undefined;
}
if (toDeletePerfixes.find(prefix => fileRelativePath.startsWith(prefix)) !== undefined) {
return undefined;
}
if (fileRelativePath.startsWith(pathJoin("patternfly", "dist", "fonts"))) {
if (
!fileRelativePath.endsWith(".woff2") &&
!fileRelativePath.endsWith(".woff") &&
!fileRelativePath.endsWith(".ttf")
) {
return undefined;
}
}
return { "modifiedSourceCode": sourceCode };
}
});
}
// Just like node_modules
remove_unused_lib: {
const libDirPath = pathJoin(destDirPath, "keycloak", "common", "resources", "lib");
if (!fs.existsSync(libDirPath)) {
break remove_unused_lib;
}
child_process.execSync(`${packageManager} install`, { "cwd": accountV2DirSrcDirPath, "stdio": "ignore" });
const toDeletePerfixes = ["ui-ace", "filesaver", "fileupload", "angular", "ui-ace", "pficon"];
const packageJsonFilePath = pathJoin(accountV2DirSrcDirPath, "package.json");
transformCodebase({
"srcDirPath": libDirPath,
"destDirPath": libDirPath,
"transformSourceCode": ({ sourceCode, fileRelativePath }) => {
if (fileRelativePath.endsWith(".map")) {
return undefined;
}
const packageJsonRaw = fs.readFileSync(packageJsonFilePath);
if (toDeletePerfixes.find(prefix => fileRelativePath.startsWith(prefix)) !== undefined) {
return undefined;
}
const parsedPackageJson = JSON.parse(packageJsonRaw.toString("utf8"));
parsedPackageJson.scripts.build = parsedPackageJson.scripts.build
.replace(`${packageManager} run check-types`, "true")
.replace(`${packageManager} run babel`, "true");
fs.writeFileSync(packageJsonFilePath, Buffer.from(JSON.stringify(parsedPackageJson, null, 2), "utf8"));
child_process.execSync(`${packageManager} run build`, { "cwd": accountV2DirSrcDirPath, "stdio": "ignore" });
fs.writeFileSync(packageJsonFilePath, packageJsonRaw);
rmSync(pathJoin(accountV2DirSrcDirPath, "node_modules"), { "recursive": true });
return { "modifiedSourceCode": sourceCode };
}
});
}
last_account_v1_transformations: {
@ -131,53 +209,29 @@ export async function downloadBuiltinKeycloakTheme(params: { keycloakVersion: st
);
}
// Note, this is an optimization for reducing the size of the jar
// Note, this is an optimization for reducing the size of the jar,
// For this version we know exactly which resources are used.
{
const defaultThemeCommonResourcesDirPath = pathJoin(destDirPath, "keycloak", "common", "resources");
const nodeModulesDirPath = pathJoin(destDirPath, "keycloak", "common", "resources", "node_modules");
const usedCommonResourceRelativeFilePaths = [
const toKeepPrefixes = [
...["patternfly.min.css", "patternfly-additions.min.css", "patternfly-additions.min.css"].map(fileBasename =>
pathJoin("node_modules", "patternfly", "dist", "css", fileBasename)
pathJoin("patternfly", "dist", "css", fileBasename)
),
...[
"OpenSans-Light-webfont.woff2",
"OpenSans-Regular-webfont.woff2",
"OpenSans-Bold-webfont.woff2",
"OpenSans-Semibold-webfont.woff2",
"OpenSans-Bold-webfont.woff",
"OpenSans-Light-webfont.woff",
"OpenSans-Regular-webfont.woff",
"OpenSans-Semibold-webfont.woff",
"OpenSans-Regular-webfont.ttf",
"OpenSans-Light-webfont.ttf",
"OpenSans-Semibold-webfont.ttf",
"OpenSans-Bold-webfont.ttf"
].map(fileBasename => pathJoin("node_modules", "patternfly", "dist", "fonts", fileBasename))
pathJoin("patternfly", "dist", "fonts")
];
transformCodebase({
"srcDirPath": defaultThemeCommonResourcesDirPath,
"destDirPath": defaultThemeCommonResourcesDirPath,
"srcDirPath": nodeModulesDirPath,
"destDirPath": nodeModulesDirPath,
"transformSourceCode": ({ sourceCode, fileRelativePath }) => {
if (!usedCommonResourceRelativeFilePaths.includes(fileRelativePath)) {
if (toKeepPrefixes.find(prefix => fileRelativePath.startsWith(prefix)) === undefined) {
return undefined;
}
return { "modifiedSourceCode": sourceCode };
}
});
}
// Other optimization: Remove AngularJS
{
const nodeModuleDirPath = pathJoin(destDirPath, "keycloak", "common", "resources", "node_modules");
fs.readdirSync(nodeModuleDirPath)
.filter(basename => basename.startsWith("angular"))
.map(basename => pathJoin(nodeModuleDirPath, basename))
.filter(dirPath => fs.statSync(dirPath).isDirectory())
.forEach(dirPath => rmSync(dirPath, { "recursive": true }));
}
}
}
}
@ -186,7 +240,6 @@ export async function downloadBuiltinKeycloakTheme(params: { keycloakVersion: st
async function main() {
const buildOptions = readBuildOptions({
"reactAppRootDirPath": process.cwd(),
"processArgv": process.argv.slice(2)
});

View File

@ -11,17 +11,14 @@ import { getThemeSrcDirPath } from "./getThemeSrcDirPath";
import { rmSync } from "./tools/fs.rmSync";
export async function main() {
const reactAppRootDirPath = process.cwd();
const buildOptions = readBuildOptions({
reactAppRootDirPath,
"processArgv": process.argv.slice(2)
});
const logger = getLogger({ "isSilent": buildOptions.isSilent });
const { themeSrcDirPath } = getThemeSrcDirPath({
reactAppRootDirPath
"reactAppRootDirPath": buildOptions.reactAppRootDirPath
});
const emailThemeSrcDirPath = pathJoin(themeSrcDirPath, "email");

View File

@ -30,8 +30,23 @@ export type BuildOptions = {
doBuildRetrocompatAccountTheme: boolean;
};
export function readBuildOptions(params: { reactAppRootDirPath: string; processArgv: string[] }): BuildOptions {
const { reactAppRootDirPath, processArgv } = params;
export function readBuildOptions(params: { processArgv: string[] }): BuildOptions {
const { processArgv } = params;
const argv = parseArgv(processArgv);
const reactAppRootDirPath = (() => {
const arg = argv["project"] ?? argv["p"];
if (typeof arg !== "string") {
return process.cwd();
}
return getAbsoluteAndInOsFormatPath({
"pathIsh": arg,
"cwd": process.cwd()
});
})();
const parsedPackageJson = readParsedPackageJson({ reactAppRootDirPath });
@ -85,11 +100,7 @@ export function readBuildOptions(params: { reactAppRootDirPath: string; processA
return {
"bundler": resolvedViteConfig !== undefined ? "vite" : "webpack",
"isSilent": (() => {
const argv = parseArgv(processArgv);
return typeof argv["silent"] === "boolean" ? argv["silent"] : false;
})(),
"isSilent": typeof argv["silent"] === "boolean" ? argv["silent"] : false,
"themeVersion": process.env.KEYCLOAKIFY_THEME_VERSION ?? parsedPackageJson.version ?? "0.0.0",
themeNames,
"extraThemeProperties": parsedPackageJson.keycloakify?.extraThemeProperties,

View File

@ -408,6 +408,14 @@
out["themeName"] = "KEYCLOAKIFY_THEME_NAME_cXxKd3xEer";
out["pageId"] = "${pageId}";
try {
out["url"]["resourcesCommonPath"] = out["url"]["resourcesPath"] + "/" + "RESOURCES_COMMON_cLsLsMrtDkpVv";
} catch(error) {
}
return out;
})()

View File

@ -7,7 +7,7 @@ import { join as pathJoin } from "path";
import { objectKeys } from "tsafe/objectKeys";
import type { BuildOptions } from "../buildOptions";
import { assert } from "tsafe/assert";
import { type ThemeType, nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "../../constants";
import { type ThemeType, nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir, resources_common } from "../../constants";
export type BuildOptionsLike = {
bundler: "vite" | "webpack";
@ -105,7 +105,8 @@ export function generateFtlFilesCodeFactory(params: {
.replace("KEYCLOAKIFY_VERSION_xEdKd3xEdr", keycloakifyVersion)
.replace("KEYCLOAKIFY_THEME_VERSION_sIgKd3xEdr3dx", buildOptions.themeVersion)
.replace("KEYCLOAKIFY_THEME_TYPE_dExKd3xEdr", themeType)
.replace("KEYCLOAKIFY_THEME_NAME_cXxKd3xEer", themeName),
.replace("KEYCLOAKIFY_THEME_NAME_cXxKd3xEer", themeName)
.replace("RESOURCES_COMMON_cLsLsMrtDkpVv", resources_common),
"<!-- xIdLqMeOedErIdLsPdNdI9dSlxI -->": [
"<#if scripts??>",
" <#list scripts as script>",

View File

@ -30,7 +30,6 @@ export function generateStartKeycloakTestingContainer(params: { jarFilePath: str
Buffer.from(
[
"#!/usr/bin/env bash",
`# If you want to test with Keycloak version prior to 23 use the retrocompat-${pathBasename(jarFilePath)}`,
"",
`docker rm ${containerName} || true`,
"",

View File

@ -11,10 +11,7 @@ import { getThemeSrcDirPath } from "../getThemeSrcDirPath";
import { getProjectRoot } from "../tools/getProjectRoot";
export async function main() {
const reactAppRootDirPath = process.cwd();
const buildOptions = readBuildOptions({
reactAppRootDirPath,
"processArgv": process.argv.slice(2)
});
@ -23,7 +20,7 @@ export async function main() {
const keycloakifyDirPath = getProjectRoot();
const { themeSrcDirPath } = getThemeSrcDirPath({ reactAppRootDirPath });
const { themeSrcDirPath } = getThemeSrcDirPath({ "reactAppRootDirPath": buildOptions.reactAppRootDirPath });
for (const themeName of buildOptions.themeNames) {
await generateTheme({
@ -83,12 +80,17 @@ export async function main() {
"",
...(!buildOptions.doCreateJar
? []
: [`✅ Your keycloak theme has been generated and bundled into .${pathSep}${pathRelative(reactAppRootDirPath, jarFilePath)} 🚀`]),
: [
`✅ Your keycloak theme has been generated and bundled into .${pathSep}${pathRelative(
buildOptions.reactAppRootDirPath,
jarFilePath
)} 🚀`
]),
"",
`To test your theme locally you can spin up a Keycloak ${containerKeycloakVersion} container image with the theme pre loaded by running:`,
"",
`👉 $ .${pathSep}${pathRelative(
reactAppRootDirPath,
buildOptions.reactAppRootDirPath,
pathJoin(buildOptions.keycloakifyBuildDirPath, generateStartKeycloakTestingContainer.basename)
)} 👈`,
``,

View File

@ -29,20 +29,6 @@ export function replaceImportsInJsCode_webpack(params: { jsCode: string; buildOp
);
}
// d={NODE_ENV:"production",PUBLIC_URL:"/foo-bar",WDS_SOCKET_HOST
// d={NODE_ENV:"production",PUBLIC_URL:"",WDS_SOCKET_HOST
// ->
// ... PUBLIC_URL:window.kcContext.url.resourcesPath+"/build" ...
fixedJsCode = fixedJsCode.replace(
new RegExp(
`NODE_ENV:"production",PUBLIC_URL:"${
buildOptions.urlPathname !== undefined ? replaceAll(buildOptions.urlPathname.slice(0, -1), "/", "\\/") : ""
}"`,
"g"
),
`NODE_ENV:"production",PUBLIC_URL:window.${nameOfTheGlobal}.url.resourcesPath+"/${basenameOfTheKeycloakifyResourcesDir}"`
);
// Example: "static/ or "foo/bar/"
const staticDir = (() => {
let out = pathRelative(buildOptions.reactAppBuildDirPath, buildOptions.assetsDirPath);

View File

@ -2,12 +2,13 @@ import { exec as execCallback } from "child_process";
import { createHash } from "crypto";
import { mkdir, readFile, stat, writeFile, unlink } from "fs/promises";
import fetch, { type FetchOptions } from "make-fetch-happen";
import { dirname as pathDirname, join as pathJoin, resolve as pathResolve, sep as pathSep } from "path";
import { dirname as pathDirname, join as pathJoin, resolve as pathResolve, sep as pathSep, basename as pathBasename } from "path";
import { assert } from "tsafe/assert";
import { promisify } from "util";
import { transformCodebase } from "./transformCodebase";
import { unzip, zip } from "./unzip";
import { rm } from "../tools/fs.rm";
import * as child_process from "child_process";
const exec = promisify(execCallback);
@ -188,10 +189,31 @@ export async function downloadAndUnzip(
const zipFilePath = pathJoin(cacheDirPath, `${zipFileBasename}.zip`);
const extractDirPath = pathJoin(cacheDirPath, `tmp_unzip_${zipFileBasename}`);
if (!(await exists(zipFilePath))) {
download_zip_and_transform: {
if (await exists(zipFilePath)) {
break download_zip_and_transform;
}
const opts = await getFetchOptions();
const response = await fetch(url, opts);
const { response, isFromRemoteCache } = await (async () => {
const response = await fetch(`https://github.com/keycloakify/keycloakify/releases/download/v0.0.1/${pathBasename(zipFilePath)}`, opts);
if (response.status === 200) {
return {
response,
"isFromRemoteCache": true
};
}
return {
"response": await fetch(url, opts),
"isFromRemoteCache": false
};
})();
await mkdir(pathDirname(zipFilePath), { "recursive": true });
/**
* The correct way to fix this is to upgrade node-fetch beyond 3.2.5
* (see https://github.com/node-fetch/node-fetch/issues/1295#issuecomment-1144061991.)
@ -201,26 +223,66 @@ export async function downloadAndUnzip(
*/
response.body?.setMaxListeners(Number.MAX_VALUE);
assert(typeof response.body !== "undefined" && response.body != null);
await writeFile(zipFilePath, response.body);
if (specificDirsToExtract !== undefined || preCacheTransform !== undefined) {
await unzip(zipFilePath, extractDirPath, specificDirsToExtract);
if (isFromRemoteCache) {
break download_zip_and_transform;
}
try {
await preCacheTransform?.action({
"destDirPath": extractDirPath
});
} catch (error) {
await Promise.all([rm(extractDirPath, { "recursive": true }), unlink(zipFilePath)]);
if (specificDirsToExtract === undefined && preCacheTransform === undefined) {
break download_zip_and_transform;
}
throw error;
await unzip(zipFilePath, extractDirPath, specificDirsToExtract);
try {
await preCacheTransform?.action({
"destDirPath": extractDirPath
});
} catch (error) {
await Promise.all([rm(extractDirPath, { "recursive": true }), unlink(zipFilePath)]);
throw error;
}
await unlink(zipFilePath);
await zip(extractDirPath, zipFilePath);
await rm(extractDirPath, { "recursive": true });
upload_to_remot_cache_if_admin: {
const githubToken = process.env["KEYCLOAKIFY_ADMIN_GITHUB_PERSONAL_ACCESS_TOKEN"];
if (githubToken === undefined) {
break upload_to_remot_cache_if_admin;
}
await unlink(zipFilePath);
console.log("uploading to remote cache");
await zip(extractDirPath, zipFilePath);
try {
child_process.execSync(`which putasset`);
} catch {
child_process.execSync(`npm install -g putasset`);
}
await rm(extractDirPath, { "recursive": true });
try {
child_process.execFileSync("putasset", [
"--owner",
"keycloakify",
"--repo",
"keycloakify",
"--tag",
"v0.0.1",
"--filename",
zipFilePath,
"--token",
githubToken
]);
} catch {
console.log("upload failed, asset probably already exists in remote cache");
}
}
}

View File

@ -1,6 +0,0 @@
export function pathJoin(...path: string[]): string {
return path
.map((part, i) => (i === 0 ? part : part.replace(/^\/+/, "")))
.map((part, i) => (i === path.length - 1 ? part : part.replace(/\/+$/, "")))
.join(typeof process !== "undefined" && process.platform === "win32" ? "\\" : "/");
}

View File

@ -17,13 +17,14 @@ type TransformSourceCode = (params: { sourceCode: Buffer; filePath: string; file
* */
export function transformCodebase(params: { srcDirPath: string; destDirPath: string; transformSourceCode?: TransformSourceCode }) {
const { srcDirPath, transformSourceCode } = params;
let { destDirPath } = params;
const isTargetSameAsSource = path.relative(srcDirPath, destDirPath) === "";
const isTargetSameAsSource = path.relative(srcDirPath, params.destDirPath) === "";
if (isTargetSameAsSource) {
destDirPath = path.join(srcDirPath, "..", "tmp_xOsPdkPsTdzPs34sOkHs");
}
const destDirPath = isTargetSameAsSource ? path.join(srcDirPath, "..", "tmp_xOsPdkPsTdzPs34sOkHs") : params.destDirPath;
fs.mkdirSync(destDirPath, {
"recursive": true
});
for (const fileRelativePath of crawl({ "dirPath": srcDirPath, "returnedPathsType": "relative to dirPath" })) {
const filePath = path.join(srcDirPath, fileRelativePath);

44
src/lib/BASE_URL.ts Normal file
View File

@ -0,0 +1,44 @@
import { assert } from "tsafe/assert";
/**
* WARNING: Internal use only!!
* THIS DOES NOT WORK IN KEYCLOAK! It's only for resolving mock assets.
* This is just a way to know what's the base url that works
* both in webpack and vite.
* You can see this as a polyfill that return `import.meta.env.BASE_URL` when in Vite
* and when in Webpack returns the base url in the same format as vite does meaning
* "/" if hosted at root or "/foo/" when hosted under a subpath (always start and ends with a "/").
*/
export const BASE_URL = (() => {
vite: {
let BASE_URL: string;
try {
// @ts-expect-error
BASE_URL = import.meta.env.BASE_URL;
assert(typeof BASE_URL === "string");
} catch {
break vite;
}
return BASE_URL;
}
webpack: {
let BASE_URL: string;
try {
// @ts-expect-error
BASE_URL = process.env.PUBLIC_URL;
assert(typeof BASE_URL === "string");
} catch {
break webpack;
}
return BASE_URL === "" ? "/" : `${BASE_URL}/`;
}
throw new Error("Bundler not supported");
})();

View File

@ -36,6 +36,10 @@ export declare namespace keycloak_js {
}
/**
* @deprecated: This will be removed in the next major version.
* If you use this, please copy paste the code into your project.
* Better yet migrate away from keycloak-js and use https://docs.oidc-spa.dev instead.
*
* NOTE: This is just a slightly modified version of the default adapter in keycloak-js
* The goal here is just to be able to inject search param in url before keycloak redirect.
* Our use case for it is to pass over the login screen the states of useGlobalState

View File

@ -7,9 +7,7 @@ import { exclude } from "tsafe/exclude";
import { assert } from "tsafe/assert";
import type { ExtendKcContext } from "./getKcContextFromWindow";
import { getKcContextFromWindow } from "./getKcContextFromWindow";
import { pathJoin } from "keycloakify/bin/tools/pathJoin";
import { symToStr } from "tsafe/symToStr";
import { resources_common } from "keycloakify/bin/constants";
export function createGetKcContext<KcContextExtension extends { pageId: string } = never>(params?: {
mockData?: readonly DeepPartial<ExtendKcContext<KcContextExtension>>[];
@ -147,8 +145,6 @@ export function createGetKcContext<KcContextExtension extends { pageId: string }
return { "kcContext": undefined as any };
}
realKcContext.url.resourcesCommonPath = pathJoin(realKcContext.url.resourcesPath, resources_common);
return { "kcContext": realKcContext as any };
}

View File

@ -1,10 +1,10 @@
import "minimal-polyfills/Object.fromEntries";
import type { KcContext, Attribute } from "./KcContext";
import { resources_common, keycloak_resources } from "keycloakify/bin/constants";
import { pathJoin } from "keycloakify/bin/tools/pathJoin";
import { id } from "tsafe/id";
import { assert, type Equals } from "tsafe/assert";
import type { LoginThemePageId } from "keycloakify/bin/keycloakify/generateFtl";
import { BASE_URL } from "keycloakify/lib/BASE_URL";
const attributes: Attribute[] = [
{
@ -100,9 +100,7 @@ const attributes: Attribute[] = [
const attributesByName = Object.fromEntries(attributes.map(attribute => [attribute.name, attribute])) as any;
const PUBLIC_URL = (typeof process !== "object" ? undefined : process.env?.["PUBLIC_URL"]) || "/";
const resourcesPath = pathJoin(PUBLIC_URL, keycloak_resources, "login", "resources");
const resourcesPath = `${BASE_URL}${keycloak_resources}/login/resources`;
export const kcContextCommonMock: KcContext.Common = {
"themeVersion": "0.0.0",
@ -112,7 +110,7 @@ export const kcContextCommonMock: KcContext.Common = {
"url": {
"loginAction": "#",
resourcesPath,
"resourcesCommonPath": pathJoin(resourcesPath, resources_common),
"resourcesCommonPath": `${resourcesPath}/${resources_common}`,
"loginRestartFlowUrl": "/auth/realms/myrealm/login-actions/restart?client_id=account&tab_id=HoAx28ja4xg",
"loginUrl": "/auth/realms/myrealm/login-actions/authenticate?client_id=account&tab_id=HoAx28ja4xg"
},

View File

@ -361,40 +361,6 @@ describe("js replacer - webpack", () => {
expect(isSameCode(fixedJsCode, fixedJsCodeExpected)).toBe(true);
});
it("replaceImportsInJsCode_webpack - 4", () => {
const jsCodeUntransformed = `d={NODE_ENV:"production",PUBLIC_URL:"",WDS_SOCKET_HOST`;
const { fixedJsCode } = replaceImportsInJsCode_webpack({
"jsCode": jsCodeUntransformed,
"buildOptions": {
"reactAppBuildDirPath": "/Users/someone/github/keycloakify-starter/build",
"assetsDirPath": "/Users/someone/github/keycloakify-starter/dist/build/static",
"urlPathname": undefined
}
});
const fixedJsCodeExpected = `d={NODE_ENV:"production",PUBLIC_URL:window.${nameOfTheGlobal}.url.resourcesPath+"/${basenameOfTheKeycloakifyResourcesDir}",WDS_SOCKET_HOST`;
expect(isSameCode(fixedJsCode, fixedJsCodeExpected)).toBe(true);
});
it("replaceImportsInJsCode_webpack - 5", () => {
const jsCodeUntransformed = `d={NODE_ENV:"production",PUBLIC_URL:"/foo-bar",WDS_SOCKET_HOST`;
const { fixedJsCode } = replaceImportsInJsCode_webpack({
"jsCode": jsCodeUntransformed,
"buildOptions": {
"reactAppBuildDirPath": "/Users/someone/github/keycloakify-starter/build",
"assetsDirPath": "/Users/someone/github/keycloakify-starter/dist/build/static",
"urlPathname": "/foo-bar/"
}
});
const fixedJsCodeExpected = `d={NODE_ENV:"production",PUBLIC_URL:window.${nameOfTheGlobal}.url.resourcesPath+"/${basenameOfTheKeycloakifyResourcesDir}",WDS_SOCKET_HOST`;
expect(isSameCode(fixedJsCode, fixedJsCodeExpected)).toBe(true);
});
});
describe("css replacer", () => {