Fundation
This commit is contained in:
parent
b6d2f9f691
commit
5b350274bd
@ -1,8 +1,11 @@
|
||||
export const nameOfTheGlobal = "kcContext";
|
||||
export const keycloak_resources = "keycloak-resources";
|
||||
export const resources_common = "resources-common";
|
||||
export const lastKeycloakVersionWithAccountV1 = "21.1.2";
|
||||
export const keycloakifyViteConfigJsonBasename = ".keycloakifyViteConfig.json";
|
||||
export const basenameOfTheKeycloakifyResourcesDir = "build";
|
||||
|
||||
export const themeTypes = ["login", "account"] as const;
|
||||
export const accountV1 = "account-v1";
|
||||
export const accountV1ThemeName = "account-v1";
|
||||
|
||||
export type ThemeType = (typeof themeTypes)[number];
|
||||
|
@ -24,6 +24,8 @@ export type BuildOptions = {
|
||||
/** 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;
|
||||
assetsDirPath: string;
|
||||
bundler: "vite" | "webpack";
|
||||
};
|
||||
|
||||
export function readBuildOptions(params: { reactAppRootDirPath: string; processArgv: string[] }): BuildOptions {
|
||||
|
@ -1 +0,0 @@
|
||||
export const ftlValuesGlobalName = "kcContext";
|
@ -5,10 +5,9 @@ import { replaceImportsInInlineCssCode } from "../replacers/replaceImportsInInli
|
||||
import * as fs from "fs";
|
||||
import { join as pathJoin } from "path";
|
||||
import { objectKeys } from "tsafe/objectKeys";
|
||||
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
|
||||
import type { BuildOptions } from "../BuildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { ThemeType } from "../../constants";
|
||||
import { type ThemeType, nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "../../constants";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
themeVersion: string;
|
||||
@ -20,7 +19,6 @@ assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||
export function generateFtlFilesCodeFactory(params: {
|
||||
themeName: string;
|
||||
indexHtmlCode: string;
|
||||
//NOTE: Expected to be an empty object if external assets mode is enabled.
|
||||
cssGlobalsToDefine: Record<string, string>;
|
||||
buildOptions: BuildOptionsLike;
|
||||
keycloakifyVersion: string;
|
||||
@ -70,7 +68,10 @@ export function generateFtlFilesCodeFactory(params: {
|
||||
|
||||
$(element).attr(
|
||||
attrName,
|
||||
href.replace(new RegExp(`^${(buildOptions.urlPathname ?? "/").replace(/\//g, "\\/")}`), "${url.resourcesPath}/build/")
|
||||
href.replace(
|
||||
new RegExp(`^${(buildOptions.urlPathname ?? "/").replace(/\//g, "\\/")}`),
|
||||
`\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/`
|
||||
)
|
||||
);
|
||||
})
|
||||
);
|
||||
@ -114,7 +115,7 @@ export function generateFtlFilesCodeFactory(params: {
|
||||
$("head").prepend(
|
||||
[
|
||||
"<script>",
|
||||
` window.${ftlValuesGlobalName}= ${objectKeys(replaceValueBySearchValue)[0]};`,
|
||||
` window.${nameOfTheGlobal}= ${objectKeys(replaceValueBySearchValue)[0]};`,
|
||||
"</script>",
|
||||
"",
|
||||
objectKeys(replaceValueBySearchValue)[1]
|
||||
|
@ -3,7 +3,7 @@ import { join as pathJoin, dirname as pathDirname } from "path";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
import type { BuildOptions } from "../BuildOptions";
|
||||
import { resources_common, lastKeycloakVersionWithAccountV1, accountV1 } from "../../constants";
|
||||
import { resources_common, lastKeycloakVersionWithAccountV1, accountV1ThemeName } from "../../constants";
|
||||
import { downloadBuiltinKeycloakTheme } from "../../download-builtin-keycloak-theme";
|
||||
import { transformCodebase } from "../../tools/transformCodebase";
|
||||
|
||||
@ -29,7 +29,7 @@ export async function bringInAccountV1(params: { buildOptions: BuildOptionsLike
|
||||
buildOptions
|
||||
});
|
||||
|
||||
const accountV1DirPath = pathJoin(buildOptions.keycloakifyBuildDirPath, "src", "main", "resources", "theme", accountV1, "account");
|
||||
const accountV1DirPath = pathJoin(buildOptions.keycloakifyBuildDirPath, "src", "main", "resources", "theme", accountV1ThemeName, "account");
|
||||
|
||||
transformCodebase({
|
||||
"srcDirPath": pathJoin(builtinKeycloakThemeTmpDirPath, "base", "account"),
|
||||
|
@ -3,7 +3,7 @@ import { join as pathJoin, dirname as pathDirname } from "path";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { Reflect } from "tsafe/Reflect";
|
||||
import type { BuildOptions } from "../BuildOptions";
|
||||
import { type ThemeType, accountV1 } from "../../constants";
|
||||
import { type ThemeType, accountV1ThemeName } from "../../constants";
|
||||
import { bringInAccountV1 } from "./bringInAccountV1";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
@ -102,7 +102,7 @@ export async function generateJavaStackFiles(params: {
|
||||
? []
|
||||
: [
|
||||
{
|
||||
"name": accountV1,
|
||||
"name": accountV1ThemeName,
|
||||
"types": ["account"]
|
||||
}
|
||||
]),
|
||||
|
@ -4,7 +4,14 @@ import { join as pathJoin, resolve as pathResolve } from "path";
|
||||
import { replaceImportsFromStaticInJsCode } from "../replacers/replaceImportsFromStaticInJsCode";
|
||||
import { replaceImportsInCssCode } from "../replacers/replaceImportsInCssCode";
|
||||
import { generateFtlFilesCodeFactory, loginThemePageIds, accountThemePageIds } from "../generateFtl";
|
||||
import { themeTypes, type ThemeType, lastKeycloakVersionWithAccountV1, keycloak_resources, accountV1 } from "../../constants";
|
||||
import {
|
||||
themeTypes,
|
||||
type ThemeType,
|
||||
lastKeycloakVersionWithAccountV1,
|
||||
keycloak_resources,
|
||||
accountV1ThemeName,
|
||||
basenameOfTheKeycloakifyResourcesDir
|
||||
} from "../../constants";
|
||||
import { isInside } from "../../tools/isInside";
|
||||
import type { BuildOptions } from "../BuildOptions";
|
||||
import { assert, type Equals } from "tsafe/assert";
|
||||
@ -18,7 +25,6 @@ export type BuildOptionsLike = {
|
||||
extraThemeProperties: string[] | undefined;
|
||||
themeVersion: string;
|
||||
loginThemeResourcesFromKeycloakVersion: string;
|
||||
urlPathname: string | undefined;
|
||||
keycloakifyBuildDirPath: string;
|
||||
reactAppBuildDirPath: string;
|
||||
cacheDirPath: string;
|
||||
@ -59,7 +65,7 @@ export async function generateTheme(params: {
|
||||
}
|
||||
|
||||
transformCodebase({
|
||||
"destDirPath": pathJoin(themeTypeDirPath, "resources", "build"),
|
||||
"destDirPath": pathJoin(themeTypeDirPath, "resources", basenameOfTheKeycloakifyResourcesDir),
|
||||
"srcDirPath": buildOptions.reactAppBuildDirPath,
|
||||
"transformSourceCode": ({ filePath, sourceCode }) => {
|
||||
//NOTE: Prevent cycles, excludes the folder we generated for debug in public/
|
||||
@ -182,7 +188,7 @@ export async function generateTheme(params: {
|
||||
`parent=${(() => {
|
||||
switch (themeType) {
|
||||
case "account":
|
||||
return accountV1;
|
||||
return accountV1ThemeName;
|
||||
case "login":
|
||||
return "keycloak";
|
||||
}
|
||||
|
79
src/bin/keycloakify/parsedKeycloakifyViteConfig.ts
Normal file
79
src/bin/keycloakify/parsedKeycloakifyViteConfig.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import * as fs from "fs";
|
||||
import { assert } from "tsafe";
|
||||
import type { Equals } from "tsafe";
|
||||
import { z } from "zod";
|
||||
import { pathJoin } from "../tools/pathJoin";
|
||||
import { keycloakifyViteConfigJsonBasename } from "../constants";
|
||||
import type { OptionalIfCanBeUndefined } from "../tools/OptionalIfCanBeUndefined";
|
||||
|
||||
export type ParsedKeycloakifyViteConfig = {
|
||||
reactAppRootDirPath: string;
|
||||
publicDirPath: string;
|
||||
assetsDirPath: string;
|
||||
reactAppBuildDirPath: string;
|
||||
urlPathname: string | undefined;
|
||||
};
|
||||
|
||||
export const zParsedKeycloakifyViteConfig = z.object({
|
||||
"reactAppRootDirPath": z.string(),
|
||||
"publicDirPath": z.string(),
|
||||
"assetsDirPath": z.string(),
|
||||
"reactAppBuildDirPath": z.string(),
|
||||
"urlPathname": z.string().optional()
|
||||
});
|
||||
|
||||
{
|
||||
type Got = ReturnType<(typeof zParsedKeycloakifyViteConfig)["parse"]>;
|
||||
type Expected = OptionalIfCanBeUndefined<ParsedKeycloakifyViteConfig>;
|
||||
|
||||
assert<Equals<Got, Expected>>();
|
||||
}
|
||||
|
||||
let cache: { parsedKeycloakifyViteConfig: ParsedKeycloakifyViteConfig | undefined } | undefined = undefined;
|
||||
|
||||
export function getParsedKeycloakifyViteConfig(params: { keycloakifyBuildDirPath: string }): ParsedKeycloakifyViteConfig | undefined {
|
||||
const { keycloakifyBuildDirPath } = params;
|
||||
|
||||
if (cache !== undefined) {
|
||||
return cache.parsedKeycloakifyViteConfig;
|
||||
}
|
||||
|
||||
const parsedKeycloakifyViteConfig = (() => {
|
||||
const keycloakifyViteConfigJsonFilePath = pathJoin(keycloakifyBuildDirPath, keycloakifyViteConfigJsonBasename);
|
||||
|
||||
if (!fs.existsSync(keycloakifyViteConfigJsonFilePath)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let out: ParsedKeycloakifyViteConfig;
|
||||
|
||||
try {
|
||||
out = JSON.parse(fs.readFileSync(keycloakifyViteConfigJsonFilePath).toString("utf8"));
|
||||
} catch {
|
||||
throw new Error("The output of the Keycloakify Vite plugin is not a valid JSON.");
|
||||
}
|
||||
|
||||
try {
|
||||
const zodParseReturn = zParsedKeycloakifyViteConfig.parse(out);
|
||||
|
||||
// So that objectKeys from tsafe return the expected result no matter what.
|
||||
Object.keys(zodParseReturn)
|
||||
.filter(key => !(key in out))
|
||||
.forEach(key => {
|
||||
delete (out as any)[key];
|
||||
});
|
||||
} catch {
|
||||
throw new Error("The output of the Keycloakify Vite plugin do not match the expected schema.");
|
||||
}
|
||||
|
||||
return out;
|
||||
})();
|
||||
|
||||
if (parsedKeycloakifyViteConfig === undefined && fs.existsSync(pathJoin(keycloakifyBuildDirPath, "vite.config.ts"))) {
|
||||
throw new Error("Make sure you have enabled the Keycloakiy plugin in your vite.config.ts");
|
||||
}
|
||||
|
||||
cache = { parsedKeycloakifyViteConfig };
|
||||
|
||||
return parsedKeycloakifyViteConfig;
|
||||
}
|
@ -10,7 +10,6 @@ export type ParsedPackageJson = {
|
||||
homepage?: string;
|
||||
keycloakify?: {
|
||||
extraThemeProperties?: string[];
|
||||
areAppAndKeycloakServerSharingSameDomain?: boolean;
|
||||
artifactId?: string;
|
||||
groupId?: string;
|
||||
doCreateJar?: boolean;
|
||||
@ -18,7 +17,6 @@ export type ParsedPackageJson = {
|
||||
reactAppBuildDirPath?: string;
|
||||
keycloakifyBuildDirPath?: string;
|
||||
themeName?: string | string[];
|
||||
doBuildRetrocompatAccountTheme?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
@ -29,22 +27,20 @@ export const zParsedPackageJson = z.object({
|
||||
"keycloakify": z
|
||||
.object({
|
||||
"extraThemeProperties": z.array(z.string()).optional(),
|
||||
"areAppAndKeycloakServerSharingSameDomain": z.boolean().optional(),
|
||||
"artifactId": z.string().optional(),
|
||||
"groupId": z.string().optional(),
|
||||
"doCreateJar": z.boolean().optional(),
|
||||
"loginThemeResourcesFromKeycloakVersion": z.string().optional(),
|
||||
"reactAppBuildDirPath": z.string().optional(),
|
||||
"keycloakifyBuildDirPath": z.string().optional(),
|
||||
"themeName": z.union([z.string(), z.array(z.string())]).optional(),
|
||||
"doBuildRetrocompatAccountTheme": z.boolean().optional()
|
||||
"themeName": z.union([z.string(), z.array(z.string())]).optional()
|
||||
})
|
||||
.optional()
|
||||
});
|
||||
|
||||
assert<Equals<ReturnType<(typeof zParsedPackageJson)["parse"]>, ParsedPackageJson>>();
|
||||
|
||||
let parsedPackageJson: undefined | ReturnType<(typeof zParsedPackageJson)["parse"]>;
|
||||
let parsedPackageJson: undefined | ParsedPackageJson;
|
||||
export function getParsedPackageJson(params: { reactAppRootDirPath: string }) {
|
||||
const { reactAppRootDirPath } = params;
|
||||
if (parsedPackageJson) {
|
||||
|
@ -1,65 +0,0 @@
|
||||
import { ftlValuesGlobalName } from "../ftlValuesGlobalName";
|
||||
|
||||
export function replaceImportsFromStaticInJsCode(params: { jsCode: string; bundler: "vite" | "webpack" }): { fixedJsCode: string } {
|
||||
const { jsCode } = params;
|
||||
|
||||
const { fixedJsCode } = (() => {
|
||||
switch (params.bundler) {
|
||||
case "vite":
|
||||
return replaceImportsFromStaticInJsCode_vite({ jsCode });
|
||||
case "webpack":
|
||||
return replaceImportsFromStaticInJsCode_webpack({ jsCode });
|
||||
}
|
||||
})();
|
||||
|
||||
return { fixedJsCode };
|
||||
}
|
||||
|
||||
export function replaceImportsFromStaticInJsCode_vite(params: { jsCode: string }): { fixedJsCode: string } {
|
||||
const { jsCode } = params;
|
||||
|
||||
const fixedJsCode = jsCode.replace(
|
||||
/\.viteFileDeps = \[(.*)\]/g,
|
||||
(...args) => `.viteFileDeps = [${args[1]}].map(viteFileDep => window.kcContext.url.resourcesPath.substring(1) + "/build/" + viteFileDep)`
|
||||
);
|
||||
|
||||
return { fixedJsCode };
|
||||
}
|
||||
|
||||
export function replaceImportsFromStaticInJsCode_webpack(params: { jsCode: string }): { fixedJsCode: string } {
|
||||
const { jsCode } = params;
|
||||
|
||||
const getReplaceArgs = (language: "js" | "css"): Parameters<typeof String.prototype.replace> => [
|
||||
new RegExp(`([a-zA-Z_]+)\\.([a-zA-Z]+)=(function\\(([a-z]+)\\){return|([a-z]+)=>)"static\\/${language}\\/"`, "g"),
|
||||
(...[, n, u, matchedFunction, eForFunction]) => {
|
||||
const isArrowFunction = matchedFunction.includes("=>");
|
||||
const e = isArrowFunction ? matchedFunction.replace("=>", "").trim() : eForFunction;
|
||||
|
||||
return `
|
||||
${n}[(function(){
|
||||
var pd = Object.getOwnPropertyDescriptor(${n}, "p");
|
||||
if( pd === undefined || pd.configurable ){
|
||||
Object.defineProperty(${n}, "p", {
|
||||
get: function() { return window.${ftlValuesGlobalName}.url.resourcesPath; },
|
||||
set: function() {}
|
||||
});
|
||||
}
|
||||
return "${u}";
|
||||
})()] = ${isArrowFunction ? `${e} =>` : `function(${e}) { return `} "/build/static/${language}/"`
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
];
|
||||
|
||||
const fixedJsCode = jsCode
|
||||
.replace(...getReplaceArgs("js"))
|
||||
.replace(...getReplaceArgs("css"))
|
||||
.replace(/[a-zA-Z]+\.[a-zA-Z]+\+"static\//g, `window.${ftlValuesGlobalName}.url.resourcesPath + "/build/static/`)
|
||||
//TODO: Write a test case for this
|
||||
.replace(
|
||||
/".chunk.css",([a-zA-Z])+=[a-zA-Z]+\.[a-zA-Z]+\+([a-zA-Z]+),/,
|
||||
(...[, group1, group2]) => `".chunk.css",${group1} = window.${ftlValuesGlobalName}.url.resourcesPath + "/build/" + ${group2},`
|
||||
);
|
||||
|
||||
return { fixedJsCode };
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import * as crypto from "crypto";
|
||||
import type { BuildOptions } from "../BuildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { basenameOfTheKeycloakifyResourcesDir } from "../../constants";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
urlPathname: string | undefined;
|
||||
@ -45,7 +46,7 @@ export function generateCssCodeToDefineGlobals(params: { cssGlobalsToDefine: Rec
|
||||
`--${cssVariableName}:`,
|
||||
cssGlobalsToDefine[cssVariableName].replace(
|
||||
new RegExp(`url\\(${(buildOptions.urlPathname ?? "/").replace(/\//g, "\\/")}`, "g"),
|
||||
"url(${url.resourcesPath}/build/"
|
||||
`url(\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/`
|
||||
)
|
||||
].join(" ")
|
||||
)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import type { BuildOptions } from "../BuildOptions";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { basenameOfTheKeycloakifyResourcesDir } from "../../constants";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
urlPathname: string | undefined;
|
||||
@ -16,7 +17,7 @@ export function replaceImportsInInlineCssCode(params: { cssCode: string; buildOp
|
||||
buildOptions.urlPathname === undefined
|
||||
? /url\(["']?\/([^/][^)"']+)["']?\)/g
|
||||
: new RegExp(`url\\(["']?${buildOptions.urlPathname}([^)"']+)["']?\\)`, "g"),
|
||||
(...[, group]) => `url(\${url.resourcesPath}/build/${group})`
|
||||
(...[, group]) => `url(\${url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/${group})`
|
||||
);
|
||||
|
||||
return { fixedCssCode };
|
||||
|
@ -0,0 +1 @@
|
||||
export * from "./replaceImportsInJsCode";
|
85
src/bin/keycloakify/replacers/replaceImportsInJsCode/vite.ts
Normal file
85
src/bin/keycloakify/replacers/replaceImportsInJsCode/vite.ts
Normal file
@ -0,0 +1,85 @@
|
||||
import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "../../../constants";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { BuildOptions } from "../../BuildOptions";
|
||||
import * as nodePath from "path";
|
||||
import { replaceAll } from "../../../tools/String.prototype.replaceAll";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
reactAppBuildDirPath: string;
|
||||
assetsDirPath: string;
|
||||
urlPathname: string | undefined;
|
||||
};
|
||||
|
||||
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||
|
||||
export function replaceImportsInJsCode_vite(params: {
|
||||
jsCode: string;
|
||||
buildOptions: BuildOptionsLike;
|
||||
basenameOfAssetsFiles: string[];
|
||||
systemType?: "posix" | "win32";
|
||||
}): {
|
||||
fixedJsCode: string;
|
||||
} {
|
||||
const { jsCode, buildOptions, basenameOfAssetsFiles, systemType = nodePath.sep === "/" ? "posix" : "win32" } = params;
|
||||
|
||||
const { relative: pathRelative, sep: pathSep } = nodePath[systemType];
|
||||
|
||||
let fixedJsCode = jsCode;
|
||||
|
||||
replace_base_javacript_import: {
|
||||
if (buildOptions.urlPathname === undefined) {
|
||||
break replace_base_javacript_import;
|
||||
}
|
||||
// Optimization
|
||||
if (!jsCode.includes(buildOptions.urlPathname)) {
|
||||
break replace_base_javacript_import;
|
||||
}
|
||||
|
||||
// Replace `Hv=function(e){return"/abcde12345/"+e}` by `Hv=function(e){return"/"+e}`
|
||||
fixedJsCode = fixedJsCode.replace(
|
||||
new RegExp(
|
||||
`([\\w\\$][\\w\\d\\$]*)=function\\(([\\w\\$][\\w\\d\\$]*)\\)\\{return"${replaceAll(buildOptions.urlPathname, "/", "\\/")}"\\+\\2\\}`,
|
||||
"g"
|
||||
),
|
||||
(...[, funcName, paramName]) => `${funcName}=function(${paramName}){return"/"+${paramName}}`
|
||||
);
|
||||
}
|
||||
|
||||
replace_javascript_relatives_import_paths: {
|
||||
// Example: "assets/ or "foo/bar/"
|
||||
const staticDir = (() => {
|
||||
let out = pathRelative(buildOptions.reactAppBuildDirPath, buildOptions.assetsDirPath);
|
||||
|
||||
out = replaceAll(out, pathSep, "/") + "/";
|
||||
|
||||
if (out === "/") {
|
||||
throw new Error(`The assetsDirPath must be a subdirectory of reactAppBuildDirPath`);
|
||||
}
|
||||
|
||||
return out;
|
||||
})();
|
||||
|
||||
// Optimization
|
||||
if (!jsCode.includes(staticDir)) {
|
||||
break replace_javascript_relatives_import_paths;
|
||||
}
|
||||
|
||||
basenameOfAssetsFiles
|
||||
.map(basenameOfAssetsFile => `${staticDir}${basenameOfAssetsFile}`)
|
||||
.forEach(relativePathOfAssetFile => {
|
||||
fixedJsCode = replaceAll(
|
||||
fixedJsCode,
|
||||
`"${relativePathOfAssetFile}"`,
|
||||
`(window.${nameOfTheGlobal}.url.resourcesPath.substring(1) + "/${basenameOfTheKeycloakifyResourcesDir}/${relativePathOfAssetFile}")`
|
||||
);
|
||||
|
||||
fixedJsCode = replaceAll(
|
||||
fixedJsCode,
|
||||
`"${buildOptions.urlPathname ?? "/"}${relativePathOfAssetFile}"`,
|
||||
`(window.${nameOfTheGlobal}.url.resourcesPath + "/${basenameOfTheKeycloakifyResourcesDir}/${relativePathOfAssetFile}")`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return { fixedJsCode };
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
import { nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "../../../constants";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { BuildOptions } from "../../BuildOptions";
|
||||
import { relative as pathRelative, sep as pathSep } from "path";
|
||||
import { replaceAll } from "../../../tools/String.prototype.replaceAll";
|
||||
|
||||
export type BuildOptionsLike = {
|
||||
reactAppBuildDirPath: string;
|
||||
assetsDirPath: string;
|
||||
urlPathname: string | undefined;
|
||||
};
|
||||
|
||||
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||
|
||||
export function replaceImportsInJsCode_webpack(params: { jsCode: string; buildOptions: BuildOptionsLike }): { fixedJsCode: string } {
|
||||
const { jsCode, buildOptions } = params;
|
||||
|
||||
let fixedJsCode = jsCode;
|
||||
|
||||
// "__esModule",{value:!0})},n.p="/",function(){if("undefined" -> n.p="/abcde12345/"
|
||||
|
||||
// d={NODE_ENV:"production",PUBLIC_URL:"/abcde12345",WDS_SOCKET_HOST
|
||||
// d={NODE_ENV:"production",PUBLIC_URL:"",WDS_SOCKET_HOST
|
||||
// ->
|
||||
// PUBLIC_URL:"${window.${nameOfTheGlobal}.url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}"`
|
||||
|
||||
if (buildOptions.urlPathname !== undefined) {
|
||||
fixedJsCode = fixedJsCode.replace(
|
||||
new RegExp(`,([a-zA-Z]\\.[a-zA-Z])="${replaceAll(buildOptions.urlPathname, "/", "\\/")}",`, "g"),
|
||||
(...[, assignTo]) => `,${assignTo}="/",`
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
out = replaceAll(out, pathSep, "/") + "/";
|
||||
|
||||
if (out === "/") {
|
||||
throw new Error(`The assetsDirPath must be a subdirectory of reactAppBuildDirPath`);
|
||||
}
|
||||
|
||||
return out;
|
||||
})();
|
||||
|
||||
const getReplaceArgs = (language: "js" | "css"): Parameters<typeof String.prototype.replace> => [
|
||||
new RegExp(`([a-zA-Z_]+)\\.([a-zA-Z]+)=(function\\(([a-z]+)\\){return|([a-z]+)=>)"${staticDir.replace(/\//g, "\\/")}${language}\\/"`, "g"),
|
||||
(...[, n, u, matchedFunction, eForFunction]) => {
|
||||
const isArrowFunction = matchedFunction.includes("=>");
|
||||
const e = isArrowFunction ? matchedFunction.replace("=>", "").trim() : eForFunction;
|
||||
|
||||
return `
|
||||
${n}[(function(){
|
||||
var pd = Object.getOwnPropertyDescriptor(${n}, "p");
|
||||
if( pd === undefined || pd.configurable ){
|
||||
Object.defineProperty(${n}, "p", {
|
||||
get: function() { return window.${nameOfTheGlobal}.url.resourcesPath; },
|
||||
set: function() {}
|
||||
});
|
||||
}
|
||||
return "${u}";
|
||||
})()] = ${isArrowFunction ? `${e} =>` : `function(${e}) { return `} "/${basenameOfTheKeycloakifyResourcesDir}/${staticDir}${language}/"`
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
];
|
||||
|
||||
fixedJsCode = fixedJsCode
|
||||
.replace(...getReplaceArgs("js"))
|
||||
.replace(...getReplaceArgs("css"))
|
||||
.replace(
|
||||
new RegExp(`[a-zA-Z]+\\.[a-zA-Z]+\\+"${staticDir.replace(/\//g, "\\/")}"`, "g"),
|
||||
`window.${nameOfTheGlobal}.url.resourcesPath + "/${basenameOfTheKeycloakifyResourcesDir}/${staticDir}`
|
||||
)
|
||||
//TODO: Write a test case for this
|
||||
.replace(
|
||||
/".chunk.css",([a-zA-Z])+=[a-zA-Z]+\.[a-zA-Z]+\+([a-zA-Z]+),/,
|
||||
(...[, group1, group2]) =>
|
||||
`".chunk.css",${group1} = window.${nameOfTheGlobal}.url.resourcesPath + "/${basenameOfTheKeycloakifyResourcesDir}/" + ${group2},`
|
||||
);
|
||||
|
||||
return { fixedJsCode };
|
||||
}
|
12
src/bin/tools/OptionalIfCanBeUndefined.ts
Normal file
12
src/bin/tools/OptionalIfCanBeUndefined.ts
Normal file
@ -0,0 +1,12 @@
|
||||
type PropertiesThatCanBeUndefined<T extends Record<string, unknown>> = {
|
||||
[Key in keyof T]: undefined extends T[Key] ? Key : never;
|
||||
}[keyof T];
|
||||
|
||||
/**
|
||||
* OptionalIfCanBeUndefined<{ p1: string | undefined; p2: string; }>
|
||||
* is
|
||||
* { p1?: string | undefined; p2: string }
|
||||
*/
|
||||
export type OptionalIfCanBeUndefined<T extends Record<string, unknown>> = {
|
||||
[K in PropertiesThatCanBeUndefined<T>]?: T[K];
|
||||
} & { [K in Exclude<keyof T, PropertiesThatCanBeUndefined<T>>]: T[K] };
|
30
src/bin/tools/String.prototype.replaceAll.ts
Normal file
30
src/bin/tools/String.prototype.replaceAll.ts
Normal file
@ -0,0 +1,30 @@
|
||||
export function replaceAll(string: string, searchValue: string | RegExp, replaceValue: string): string {
|
||||
if ((string as any).replaceAll !== undefined) {
|
||||
return (string as any).replaceAll(searchValue, replaceValue);
|
||||
}
|
||||
|
||||
// If the searchValue is a string
|
||||
if (typeof searchValue === "string") {
|
||||
// Escape special characters in the string to be used in a regex
|
||||
var escapedSearchValue = searchValue.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
var regex = new RegExp(escapedSearchValue, "g");
|
||||
|
||||
return string.replace(regex, replaceValue);
|
||||
}
|
||||
|
||||
// If the searchValue is a global RegExp, use it directly
|
||||
if (searchValue instanceof RegExp && searchValue.global) {
|
||||
return string.replace(searchValue, replaceValue);
|
||||
}
|
||||
|
||||
// If the searchValue is a non-global RegExp, throw an error
|
||||
if (searchValue instanceof RegExp) {
|
||||
throw new TypeError("replaceAll must be called with a global RegExp");
|
||||
}
|
||||
|
||||
// Convert searchValue to string if it's not a string or RegExp
|
||||
var searchString = String(searchValue);
|
||||
var regexFromString = new RegExp(searchString.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
|
||||
|
||||
return string.replace(regexFromString, replaceValue);
|
||||
}
|
232
src/vite-plugin/config.json
Normal file
232
src/vite-plugin/config.json
Normal file
@ -0,0 +1,232 @@
|
||||
{
|
||||
"plugins": [
|
||||
{
|
||||
"name": "vite:build-metadata"
|
||||
},
|
||||
{
|
||||
"name": "vite:watch-package-data"
|
||||
},
|
||||
{
|
||||
"name": "vite:pre-alias"
|
||||
},
|
||||
{
|
||||
"name": "alias"
|
||||
},
|
||||
{
|
||||
"name": "vite:react-babel",
|
||||
"enforce": "pre"
|
||||
},
|
||||
{
|
||||
"name": "vite:react-refresh",
|
||||
"enforce": "pre"
|
||||
},
|
||||
{
|
||||
"name": "vite:modulepreload-polyfill"
|
||||
},
|
||||
{
|
||||
"name": "vite:resolve"
|
||||
},
|
||||
{
|
||||
"name": "vite:html-inline-proxy"
|
||||
},
|
||||
{
|
||||
"name": "vite:css"
|
||||
},
|
||||
{
|
||||
"name": "vite:esbuild"
|
||||
},
|
||||
{
|
||||
"name": "vite:json"
|
||||
},
|
||||
{
|
||||
"name": "vite:wasm-helper"
|
||||
},
|
||||
{
|
||||
"name": "vite:worker"
|
||||
},
|
||||
{
|
||||
"name": "vite:asset"
|
||||
},
|
||||
{
|
||||
"name": "vite-plugin-commonjs"
|
||||
},
|
||||
{
|
||||
"name": "keycloakify"
|
||||
},
|
||||
{
|
||||
"name": "vite:wasm-fallback"
|
||||
},
|
||||
{
|
||||
"name": "vite:define"
|
||||
},
|
||||
{
|
||||
"name": "vite:css-post"
|
||||
},
|
||||
{
|
||||
"name": "vite:build-html"
|
||||
},
|
||||
{
|
||||
"name": "vite:worker-import-meta-url"
|
||||
},
|
||||
{
|
||||
"name": "vite:asset-import-meta-url"
|
||||
},
|
||||
{
|
||||
"name": "vite:force-systemjs-wrap-complete"
|
||||
},
|
||||
{
|
||||
"name": "commonjs",
|
||||
"version": "25.0.7"
|
||||
},
|
||||
{
|
||||
"name": "vite:data-uri"
|
||||
},
|
||||
{
|
||||
"name": "vite:dynamic-import-vars"
|
||||
},
|
||||
{
|
||||
"name": "vite:import-glob"
|
||||
},
|
||||
{
|
||||
"name": "vite:build-import-analysis"
|
||||
},
|
||||
{
|
||||
"name": "vite:esbuild-transpile"
|
||||
},
|
||||
{
|
||||
"name": "vite:terser"
|
||||
},
|
||||
{
|
||||
"name": "vite:reporter"
|
||||
},
|
||||
{
|
||||
"name": "vite:load-fallback"
|
||||
}
|
||||
],
|
||||
"optimizeDeps": {
|
||||
"disabled": "build",
|
||||
"esbuildOptions": {
|
||||
"preserveSymlinks": false,
|
||||
"jsx": "automatic",
|
||||
"plugins": [
|
||||
{
|
||||
"name": "vite-plugin-commonjs:pre-bundle"
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": ["react", "react/jsx-dev-runtime", "react/jsx-runtime"]
|
||||
},
|
||||
"build": {
|
||||
"target": ["es2020", "edge88", "firefox78", "chrome87", "safari14"],
|
||||
"cssTarget": ["es2020", "edge88", "firefox78", "chrome87", "safari14"],
|
||||
"outDir": "dist",
|
||||
"assetsDir": "assets",
|
||||
"assetsInlineLimit": 4096,
|
||||
"cssCodeSplit": true,
|
||||
"sourcemap": false,
|
||||
"rollupOptions": {},
|
||||
"minify": "esbuild",
|
||||
"terserOptions": {},
|
||||
"write": true,
|
||||
"emptyOutDir": null,
|
||||
"copyPublicDir": true,
|
||||
"manifest": false,
|
||||
"lib": false,
|
||||
"ssr": false,
|
||||
"ssrManifest": false,
|
||||
"ssrEmitAssets": false,
|
||||
"reportCompressedSize": true,
|
||||
"chunkSizeWarningLimit": 500,
|
||||
"watch": null,
|
||||
"commonjsOptions": {
|
||||
"include": [{}],
|
||||
"extensions": [".js", ".cjs"]
|
||||
},
|
||||
"dynamicImportVarsOptions": {
|
||||
"warnOnError": true,
|
||||
"exclude": [{}]
|
||||
},
|
||||
"modulePreload": {
|
||||
"polyfill": true
|
||||
},
|
||||
"cssMinify": true
|
||||
},
|
||||
"esbuild": {
|
||||
"jsxDev": false,
|
||||
"jsx": "automatic"
|
||||
},
|
||||
"resolve": {
|
||||
"mainFields": ["browser", "module", "jsnext:main", "jsnext"],
|
||||
"conditions": [],
|
||||
"extensions": [".mjs", ".js", ".mts", ".ts", ".jsx", ".tsx", ".json"],
|
||||
"dedupe": ["react", "react-dom"],
|
||||
"preserveSymlinks": false,
|
||||
"alias": [
|
||||
{
|
||||
"find": {},
|
||||
"replacement": "/@fs/Users/joseph/github/keycloakify-starter/node_modules/vite/dist/client/env.mjs"
|
||||
},
|
||||
{
|
||||
"find": {},
|
||||
"replacement": "/@fs/Users/joseph/github/keycloakify-starter/node_modules/vite/dist/client/client.mjs"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configFile": "/Users/joseph/github/keycloakify-starter/vite.config.ts",
|
||||
"configFileDependencies": ["/Users/joseph/github/keycloakify-starter/vite.config.ts"],
|
||||
"inlineConfig": {
|
||||
"optimizeDeps": {},
|
||||
"build": {}
|
||||
},
|
||||
"root": "/Users/joseph/github/keycloakify-starter",
|
||||
"base": "/",
|
||||
"rawBase": "/",
|
||||
"publicDir": "/Users/joseph/github/keycloakify-starter/public",
|
||||
"cacheDir": "/Users/joseph/github/keycloakify-starter/node_modules/.vite",
|
||||
"command": "build",
|
||||
"mode": "production",
|
||||
"ssr": {
|
||||
"target": "node",
|
||||
"optimizeDeps": {
|
||||
"disabled": true,
|
||||
"esbuildOptions": {
|
||||
"preserveSymlinks": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"isWorker": false,
|
||||
"mainConfig": null,
|
||||
"isProduction": true,
|
||||
"css": {},
|
||||
"server": {
|
||||
"preTransformRequests": true,
|
||||
"middlewareMode": false,
|
||||
"fs": {
|
||||
"strict": true,
|
||||
"allow": ["/Users/joseph/github/keycloakify-starter"],
|
||||
"deny": [".env", ".env.*", "*.{crt,pem}"],
|
||||
"cachedChecks": false
|
||||
}
|
||||
},
|
||||
"preview": {},
|
||||
"envDir": "/Users/joseph/github/keycloakify-starter",
|
||||
"env": {
|
||||
"BASE_URL": "/",
|
||||
"MODE": "production",
|
||||
"DEV": false,
|
||||
"PROD": true
|
||||
},
|
||||
"logger": {
|
||||
"hasWarned": false
|
||||
},
|
||||
"packageCache": {},
|
||||
"worker": {
|
||||
"format": "iife",
|
||||
"rollupOptions": {}
|
||||
},
|
||||
"appType": "spa",
|
||||
"experimental": {
|
||||
"importGlobRestoreExtension": false,
|
||||
"hmrPartialAccept": false
|
||||
}
|
||||
}
|
@ -2,11 +2,16 @@
|
||||
"extends": "../../tsproject.json",
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS",
|
||||
"target": "ES5",
|
||||
"target": "ES2019",
|
||||
"esModuleInterop": true,
|
||||
"lib": ["es2015", "ES2019.Object"],
|
||||
"lib": ["es2019", "es2020.bigint", "es2020.string", "es2020.symbol.wellknown"],
|
||||
"outDir": "../../dist/vite-plugin",
|
||||
"rootDir": ".",
|
||||
"skipLibCheck": true
|
||||
}
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "../bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -1,31 +1,127 @@
|
||||
// index.ts
|
||||
|
||||
import type { Plugin, ResolvedConfig } from "vite";
|
||||
import { join as pathJoin, sep as pathSep } from "path";
|
||||
import { getParsedPackageJson } from "../bin/keycloakify/parsedPackageJson";
|
||||
import type { Plugin } from "vite";
|
||||
import { assert } from "tsafe/assert";
|
||||
import { getAbsoluteAndInOsFormatPath } from "../bin/tools/getAbsoluteAndInOsFormatPath";
|
||||
import * as fs from "fs";
|
||||
|
||||
console.log("Hello world!");
|
||||
import { keycloakifyViteConfigJsonBasename, nameOfTheGlobal, basenameOfTheKeycloakifyResourcesDir } from "../bin/constants";
|
||||
import type { ParsedKeycloakifyViteConfig } from "../bin/keycloakify/parsedKeycloakifyViteConfig";
|
||||
import { replaceAll } from "../bin/tools/String.prototype.replaceAll";
|
||||
|
||||
export function keycloakify(): Plugin {
|
||||
let config: ResolvedConfig;
|
||||
let keycloakifyViteConfig: ParsedKeycloakifyViteConfig | undefined = undefined;
|
||||
|
||||
return {
|
||||
"name": "keycloakify",
|
||||
|
||||
"configResolved": resolvedConfig => {
|
||||
// Store the resolved config
|
||||
config = resolvedConfig;
|
||||
const reactAppRootDirPath = resolvedConfig.root;
|
||||
const reactAppBuildDirPath = pathJoin(reactAppRootDirPath, resolvedConfig.build.outDir);
|
||||
|
||||
console.log("========> configResolved", config);
|
||||
keycloakifyViteConfig = {
|
||||
reactAppRootDirPath,
|
||||
"publicDirPath": resolvedConfig.publicDir,
|
||||
"assetsDirPath": pathJoin(reactAppBuildDirPath, resolvedConfig.build.assetsDir),
|
||||
reactAppBuildDirPath,
|
||||
"urlPathname": (() => {
|
||||
let out = resolvedConfig.env.BASE_URL;
|
||||
|
||||
fs.writeFileSync("/Users/joseph/github/keycloakify-starter/log.txt", Buffer.from("Hello World", "utf8"));
|
||||
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 (!fs.existsSync(keycloakifyBuildDirPath)) {
|
||||
fs.mkdirSync(keycloakifyBuildDirPath);
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
pathJoin(keycloakifyBuildDirPath, keycloakifyViteConfigJsonBasename),
|
||||
Buffer.from(JSON.stringify(keycloakifyViteConfig, null, 2), "utf8")
|
||||
);
|
||||
},
|
||||
"transform": (code, id) => {
|
||||
assert(keycloakifyViteConfig !== undefined);
|
||||
|
||||
"buildStart": () => {
|
||||
console.log("Public Directory:", config.publicDir); // Path to the public directory
|
||||
console.log("Dist Directory:", config.build.outDir); // Path to the dist directory
|
||||
console.log("Assets Directory:", config.build.assetsDir); // Path to the assets directory within outDir
|
||||
let transformedCode: string | undefined = undefined;
|
||||
|
||||
replace_import_meta_env_base_url_in_source_code: {
|
||||
{
|
||||
const isWithinSourceDirectory = id.startsWith(pathJoin(keycloakifyViteConfig.publicDirPath, "src") + pathSep);
|
||||
|
||||
if (!isWithinSourceDirectory) {
|
||||
break replace_import_meta_env_base_url_in_source_code;
|
||||
}
|
||||
}
|
||||
|
||||
const isJavascriptFile = id.endsWith(".js") || id.endsWith(".jsx");
|
||||
|
||||
{
|
||||
const isTypeScriptFile = id.endsWith(".ts") || id.endsWith(".tsx");
|
||||
|
||||
if (!isTypeScriptFile && !isJavascriptFile) {
|
||||
break replace_import_meta_env_base_url_in_source_code;
|
||||
}
|
||||
}
|
||||
|
||||
const windowToken = isJavascriptFile ? "window" : "(window as any)";
|
||||
|
||||
if (transformedCode === undefined) {
|
||||
transformedCode = code;
|
||||
}
|
||||
|
||||
transformedCode = replaceAll(
|
||||
transformedCode,
|
||||
"import.meta.env.BASE_URL",
|
||||
[
|
||||
`(`,
|
||||
`(${windowToken}.${nameOfTheGlobal} === undefined || import.meta.env.MODE === "development") ?`,
|
||||
` "${keycloakifyViteConfig.urlPathname ?? "/"}" :`,
|
||||
` \`\${${windowToken}.${nameOfTheGlobal}.url.resourcesPath}/${basenameOfTheKeycloakifyResourcesDir}/\``,
|
||||
`)`
|
||||
].join("")
|
||||
);
|
||||
}
|
||||
|
||||
if (transformedCode !== undefined) {
|
||||
return {
|
||||
"code": transformedCode
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// ... other hooks
|
||||
};
|
||||
}
|
||||
|
@ -1,15 +1,56 @@
|
||||
import { replaceImportsFromStaticInJsCode } from "keycloakify/bin/keycloakify/replacers/replaceImportsFromStaticInJsCode";
|
||||
import { replaceImportsInJsCode_vite } from "keycloakify/bin/keycloakify/replacers/replaceImportsInJsCode/vite";
|
||||
import { generateCssCodeToDefineGlobals, replaceImportsInCssCode } from "keycloakify/bin/keycloakify/replacers/replaceImportsInCssCode";
|
||||
import { replaceImportsInInlineCssCode } from "keycloakify/bin/keycloakify/replacers/replaceImportsInInlineCssCode";
|
||||
import { same } from "evt/tools/inDepth/same";
|
||||
import { expect, it, describe } from "vitest";
|
||||
|
||||
import { isSameCode } from "../tools/isSameCode";
|
||||
import { basenameOfTheKeycloakifyResourcesDir, nameOfTheGlobal } from "keycloakify/bin/constants";
|
||||
|
||||
describe("bin/js-transforms", () => {
|
||||
// Vite
|
||||
{
|
||||
describe("bin/js-transforms - vite", () => {
|
||||
it("replaceImportsInJsCode_vite - 1", () => {
|
||||
const before = `Uv="modulepreload",`;
|
||||
const after = `,Wc={},`;
|
||||
const jsCodeUntransformed = `${before}Hv=function(e){return"/foo-bar-baz/"+e}${after}`;
|
||||
|
||||
const { fixedJsCode } = replaceImportsInJsCode_vite({
|
||||
"jsCode": jsCodeUntransformed,
|
||||
"basenameOfAssetsFiles": [],
|
||||
"buildOptions": {
|
||||
"reactAppBuildDirPath": "/Users/someone/github/keycloakify-starter/dist/",
|
||||
"assetsDirPath": "/Users/someone/github/keycloakify-starter/dist/assets/",
|
||||
"urlPathname": "/foo-bar-baz/"
|
||||
}
|
||||
});
|
||||
|
||||
const fixedJsCodeExpected = `${before}Hv=function(e){return"/"+e}${after}`;
|
||||
|
||||
expect(isSameCode(fixedJsCode, fixedJsCodeExpected)).toBe(true);
|
||||
});
|
||||
|
||||
it("replaceImportsInJsCode_vite - 2", () => {
|
||||
const before = `Uv="modulepreload",`;
|
||||
const after = `,Wc={},`;
|
||||
const jsCodeUntransformed = `${before}Hv=function(e){return"/foo/bar/baz/"+e}${after}`;
|
||||
|
||||
const { fixedJsCode } = replaceImportsInJsCode_vite({
|
||||
"jsCode": jsCodeUntransformed,
|
||||
"basenameOfAssetsFiles": [],
|
||||
"buildOptions": {
|
||||
"reactAppBuildDirPath": "/Users/someone/github/keycloakify-starter/dist/",
|
||||
"assetsDirPath": "/Users/someone/github/keycloakify-starter/dist/assets/",
|
||||
"urlPathname": "/foo/bar/baz/"
|
||||
}
|
||||
});
|
||||
|
||||
const fixedJsCodeExpected = `${before}Hv=function(e){return"/"+e}${after}`;
|
||||
|
||||
expect(isSameCode(fixedJsCode, fixedJsCodeExpected)).toBe(true);
|
||||
});
|
||||
|
||||
it("replaceImportsInJsCode_vite - 3", () => {
|
||||
const jsCodeUntransformed = `
|
||||
S="/assets/keycloakify-logo-mqjydaoZ.png",H=(()=>{
|
||||
|
||||
function __vite__mapDeps(indexes) {
|
||||
if (!__vite__mapDeps.viteFileDeps) {
|
||||
__vite__mapDeps.viteFileDeps = ["assets/Login-dJpPRzM4.js", "assets/index-XwzrZ5Gu.js"]
|
||||
@ -17,28 +58,157 @@ describe("bin/js-transforms", () => {
|
||||
return indexes.map((i) => __vite__mapDeps.viteFileDeps[i])
|
||||
}
|
||||
`;
|
||||
it("Correctly replace import path in Vite dist/static/xxx.js files", () => {
|
||||
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||
|
||||
for (const { reactAppBuildDirPath, assetsDirPath, systemType } of [
|
||||
{
|
||||
"systemType": "posix",
|
||||
"reactAppBuildDirPath": "/Users/someone/github/keycloakify-starter/dist",
|
||||
"assetsDirPath": "/Users/someone/github/keycloakify-starter/dist/assets"
|
||||
},
|
||||
{
|
||||
"systemType": "win32",
|
||||
"reactAppBuildDirPath": "C:\\\\Users\\someone\\github\\keycloakify-starter\\dist",
|
||||
"assetsDirPath": "C:\\\\Users\\someone\\github\\keycloakify-starter\\dist\\assets"
|
||||
}
|
||||
] as const) {
|
||||
const { fixedJsCode } = replaceImportsInJsCode_vite({
|
||||
"jsCode": jsCodeUntransformed,
|
||||
"bundler": "vite"
|
||||
"basenameOfAssetsFiles": ["Login-dJpPRzM4.js", "index-XwzrZ5Gu.js"],
|
||||
"buildOptions": {
|
||||
reactAppBuildDirPath,
|
||||
assetsDirPath,
|
||||
"urlPathname": undefined
|
||||
},
|
||||
systemType
|
||||
});
|
||||
|
||||
const fixedJsCodeExpected = `
|
||||
function __vite__mapDeps(indexes) {
|
||||
if (!__vite__mapDeps.viteFileDeps) {
|
||||
__vite__mapDeps.viteFileDeps = ["assets/Login-dJpPRzM4.js", "assets/index-XwzrZ5Gu.js"].map(viteFileDep => window.kcContext.url.resourcesPath.substring(1) + "/build/" + viteFileDep)
|
||||
S=(window.${nameOfTheGlobal}.url + "/${basenameOfTheKeycloakifyResourcesDir}/assets/keycloakify-logo-mqjydaoZ.png"),H=(()=>{
|
||||
|
||||
function __vite__mapDeps(indexes) {
|
||||
if (!__vite__mapDeps.viteFileDeps) {
|
||||
__vite__mapDeps.viteFileDeps = [
|
||||
(window.${nameOfTheGlobal}.url.resourcesPath.substring(1) + "/${basenameOfTheKeycloakifyResourcesDir}/assets/Login-dJpPRzM4.js)",
|
||||
(window.${nameOfTheGlobal}.url.resourcesPath.substring(1) + "/${basenameOfTheKeycloakifyResourcesDir}/assets/index-XwzrZ5Gu.js)"
|
||||
]
|
||||
}
|
||||
return indexes.map((i) => __vite__mapDeps.viteFileDeps[i])
|
||||
}
|
||||
return indexes.map((i) => __vite__mapDeps.viteFileDeps[i])
|
||||
}
|
||||
`;
|
||||
`;
|
||||
|
||||
expect(isSameCode(fixedJsCode, fixedJsCodeExpected)).toBe(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Webpack
|
||||
{
|
||||
it("replaceImportsInJsCode_vite - 4", () => {
|
||||
const jsCodeUntransformed = `
|
||||
S="/assets/keycloakify-logo-mqjydaoZ.png",H=(()=>{
|
||||
|
||||
function __vite__mapDeps(indexes) {
|
||||
if (!__vite__mapDeps.viteFileDeps) {
|
||||
__vite__mapDeps.viteFileDeps = ["assets/Login-dJpPRzM4.js", "assets/index-XwzrZ5Gu.js"]
|
||||
}
|
||||
return indexes.map((i) => __vite__mapDeps.viteFileDeps[i])
|
||||
}
|
||||
`;
|
||||
|
||||
for (const { reactAppBuildDirPath, assetsDirPath, systemType } of [
|
||||
{
|
||||
"systemType": "posix",
|
||||
"reactAppBuildDirPath": "/Users/someone/github/keycloakify-starter/dist",
|
||||
"assetsDirPath": "/Users/someone/github/keycloakify-starter/dist/foo/bar"
|
||||
},
|
||||
{
|
||||
"systemType": "win32",
|
||||
"reactAppBuildDirPath": "C:\\\\Users\\someone\\github\\keycloakify-starter\\dist",
|
||||
"assetsDirPath": "C:\\\\Users\\someone\\github\\keycloakify-starter\\dist\\foo\\bar"
|
||||
}
|
||||
] as const) {
|
||||
const { fixedJsCode } = replaceImportsInJsCode_vite({
|
||||
"jsCode": jsCodeUntransformed,
|
||||
"basenameOfAssetsFiles": ["Login-dJpPRzM4.js", "index-XwzrZ5Gu.js"],
|
||||
"buildOptions": {
|
||||
reactAppBuildDirPath,
|
||||
assetsDirPath,
|
||||
"urlPathname": undefined
|
||||
},
|
||||
systemType
|
||||
});
|
||||
|
||||
const fixedJsCodeExpected = `
|
||||
S=(window.${nameOfTheGlobal}.url + "/${basenameOfTheKeycloakifyResourcesDir}/foo/bar/keycloakify-logo-mqjydaoZ.png"),H=(()=>{
|
||||
|
||||
function __vite__mapDeps(indexes) {
|
||||
if (!__vite__mapDeps.viteFileDeps) {
|
||||
__vite__mapDeps.viteFileDeps = [
|
||||
(window.${nameOfTheGlobal}.url.resourcesPath.substring(1) + "/${basenameOfTheKeycloakifyResourcesDir}/foo/bar/Login-dJpPRzM4.js)",
|
||||
(window.${nameOfTheGlobal}.url.resourcesPath.substring(1) + "/${basenameOfTheKeycloakifyResourcesDir}/foo/bar/index-XwzrZ5Gu.js)"
|
||||
]
|
||||
}
|
||||
return indexes.map((i) => __vite__mapDeps.viteFileDeps[i])
|
||||
}
|
||||
`;
|
||||
|
||||
expect(isSameCode(fixedJsCode, fixedJsCodeExpected)).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it("replaceImportsInJsCode_vite - 5", () => {
|
||||
const jsCodeUntransformed = `
|
||||
S="/foo-bar-baz/assets/keycloakify-logo-mqjydaoZ.png",H=(()=>{
|
||||
|
||||
function __vite__mapDeps(indexes) {
|
||||
if (!__vite__mapDeps.viteFileDeps) {
|
||||
__vite__mapDeps.viteFileDeps = ["assets/Login-dJpPRzM4.js", "assets/index-XwzrZ5Gu.js"]
|
||||
}
|
||||
return indexes.map((i) => __vite__mapDeps.viteFileDeps[i])
|
||||
}
|
||||
`;
|
||||
|
||||
for (const { reactAppBuildDirPath, assetsDirPath, systemType } of [
|
||||
{
|
||||
"systemType": "posix",
|
||||
"reactAppBuildDirPath": "/Users/someone/github/keycloakify-starter/dist",
|
||||
"assetsDirPath": "/Users/someone/github/keycloakify-starter/dist/assets"
|
||||
},
|
||||
{
|
||||
"systemType": "win32",
|
||||
"reactAppBuildDirPath": "C:\\\\Users\\someone\\github\\keycloakify-starter\\dist",
|
||||
"assetsDirPath": "C:\\\\Users\\someone\\github\\keycloakify-starter\\dist\\assets"
|
||||
}
|
||||
] as const) {
|
||||
const { fixedJsCode } = replaceImportsInJsCode_vite({
|
||||
"jsCode": jsCodeUntransformed,
|
||||
"basenameOfAssetsFiles": ["Login-dJpPRzM4.js", "index-XwzrZ5Gu.js"],
|
||||
"buildOptions": {
|
||||
reactAppBuildDirPath,
|
||||
assetsDirPath,
|
||||
"urlPathname": "/foo-bar-baz/"
|
||||
},
|
||||
systemType
|
||||
});
|
||||
|
||||
const fixedJsCodeExpected = `
|
||||
S=(window.${nameOfTheGlobal}.url + "/${basenameOfTheKeycloakifyResourcesDir}/assets/keycloakify-logo-mqjydaoZ.png"),H=(()=>{
|
||||
|
||||
function __vite__mapDeps(indexes) {
|
||||
if (!__vite__mapDeps.viteFileDeps) {
|
||||
__vite__mapDeps.viteFileDeps = [
|
||||
(window.${nameOfTheGlobal}.url.resourcesPath.substring(1) + "/${basenameOfTheKeycloakifyResourcesDir}/assets/Login-dJpPRzM4.js)",
|
||||
(window.${nameOfTheGlobal}.url.resourcesPath.substring(1) + "/${basenameOfTheKeycloakifyResourcesDir}/assets/index-XwzrZ5Gu.js)"
|
||||
]
|
||||
}
|
||||
return indexes.map((i) => __vite__mapDeps.viteFileDeps[i])
|
||||
}
|
||||
`;
|
||||
|
||||
expect(isSameCode(fixedJsCode, fixedJsCodeExpected)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("bin/js-transforms - webpack", () => {
|
||||
const jsCodeUntransformed = `
|
||||
function f() {
|
||||
return a.p+"static/js/" + ({}[e] || e) + "." + {
|
||||
3: "0664cdc0"
|
||||
@ -68,13 +238,13 @@ describe("bin/js-transforms", () => {
|
||||
|
||||
t.miniCssF=e=>"static/css/"+e+"."+{164:"dcfd7749",908:"67c9ed2c"}[e]+".chunk.css"
|
||||
`;
|
||||
it("Correctly replace import path in Webpack build/static/js/xxx.js files", () => {
|
||||
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||
"jsCode": jsCodeUntransformed,
|
||||
"bundler": "webpack"
|
||||
});
|
||||
it("Correctly replace import path in Webpack build/static/js/xxx.js files", () => {
|
||||
const { fixedJsCode } = replaceImportsFromStaticInJsCode({
|
||||
"jsCode": jsCodeUntransformed,
|
||||
"bundler": "webpack"
|
||||
});
|
||||
|
||||
const fixedJsCodeExpected = `
|
||||
const fixedJsCodeExpected = `
|
||||
function f() {
|
||||
return window.kcContext.url.resourcesPath + "/build/static/js/" + ({}[e] || e) + "." + {
|
||||
3: "0664cdc0"
|
||||
@ -143,9 +313,8 @@ describe("bin/js-transforms", () => {
|
||||
})()] = e => "/build/static/css/"+e+"."+{164:"dcfd7749",908:"67c9ed2c"}[e]+".chunk.css"
|
||||
`;
|
||||
|
||||
expect(isSameCode(fixedJsCode, fixedJsCodeExpected)).toBe(true);
|
||||
});
|
||||
}
|
||||
expect(isSameCode(fixedJsCode, fixedJsCodeExpected)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("bin/css-transforms", () => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user