Add code gen for environement variables an theme name
This commit is contained in:
parent
e1341dfdba
commit
82d7e1371e
@ -8,6 +8,7 @@ import * as recast from "recast";
|
|||||||
import * as babelParser from "@babel/parser";
|
import * as babelParser from "@babel/parser";
|
||||||
import babelGenerate from "@babel/generator";
|
import babelGenerate from "@babel/generator";
|
||||||
import * as babelTypes from "@babel/types";
|
import * as babelTypes from "@babel/types";
|
||||||
|
import { escapeStringForPropertiesFile } from "../../tools/escapeStringForPropertiesFile";
|
||||||
|
|
||||||
export function generateMessageProperties(params: {
|
export function generateMessageProperties(params: {
|
||||||
themeSrcDirPath: string;
|
themeSrcDirPath: string;
|
||||||
@ -146,7 +147,7 @@ export function generateMessageProperties(params: {
|
|||||||
|
|
||||||
for (const [languageTag, keyValueMap] of Object.entries(keyValueMapByLanguageTag)) {
|
for (const [languageTag, keyValueMap] of Object.entries(keyValueMapByLanguageTag)) {
|
||||||
const propertiesFileSource = Object.entries(keyValueMap)
|
const propertiesFileSource = Object.entries(keyValueMap)
|
||||||
.map(([key, value]) => `${key}=${escapeString(value)}`)
|
.map(([key, value]) => `${key}=${escapeStringForPropertiesFile(value)}`)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
out.push({
|
out.push({
|
||||||
@ -164,68 +165,3 @@ export function generateMessageProperties(params: {
|
|||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert a JavaScript string to UTF-16 encoding
|
|
||||||
function toUTF16(codePoint: number): string {
|
|
||||||
if (codePoint <= 0xffff) {
|
|
||||||
// BMP character
|
|
||||||
return "\\u" + codePoint.toString(16).padStart(4, "0");
|
|
||||||
} else {
|
|
||||||
// Non-BMP character
|
|
||||||
codePoint -= 0x10000;
|
|
||||||
let highSurrogate = (codePoint >> 10) + 0xd800;
|
|
||||||
let lowSurrogate = (codePoint % 0x400) + 0xdc00;
|
|
||||||
return (
|
|
||||||
"\\u" +
|
|
||||||
highSurrogate.toString(16).padStart(4, "0") +
|
|
||||||
"\\u" +
|
|
||||||
lowSurrogate.toString(16).padStart(4, "0")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Escapes special characters for use in a .properties file
|
|
||||||
function escapeString(str: string): string {
|
|
||||||
let escapedStr = "";
|
|
||||||
for (const char of [...str]) {
|
|
||||||
const codePoint = char.codePointAt(0);
|
|
||||||
if (!codePoint) continue;
|
|
||||||
|
|
||||||
switch (char) {
|
|
||||||
case "\n":
|
|
||||||
escapedStr += "\\n";
|
|
||||||
break;
|
|
||||||
case "\r":
|
|
||||||
escapedStr += "\\r";
|
|
||||||
break;
|
|
||||||
case "\t":
|
|
||||||
escapedStr += "\\t";
|
|
||||||
break;
|
|
||||||
case "\\":
|
|
||||||
escapedStr += "\\\\";
|
|
||||||
break;
|
|
||||||
case ":":
|
|
||||||
escapedStr += "\\:";
|
|
||||||
break;
|
|
||||||
case "=":
|
|
||||||
escapedStr += "\\=";
|
|
||||||
break;
|
|
||||||
case "#":
|
|
||||||
escapedStr += "\\#";
|
|
||||||
break;
|
|
||||||
case "!":
|
|
||||||
escapedStr += "\\!";
|
|
||||||
break;
|
|
||||||
case "'":
|
|
||||||
escapedStr += "''";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (codePoint > 0x7f) {
|
|
||||||
escapedStr += toUTF16(codePoint); // Non-ASCII characters
|
|
||||||
} else {
|
|
||||||
escapedStr += char; // ASCII character needs no escape
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return escapedStr;
|
|
||||||
}
|
|
||||||
|
@ -38,6 +38,7 @@ import {
|
|||||||
type MetaInfKeycloakTheme
|
type MetaInfKeycloakTheme
|
||||||
} from "../../shared/metaInfKeycloakThemes";
|
} from "../../shared/metaInfKeycloakThemes";
|
||||||
import { objectEntries } from "tsafe/objectEntries";
|
import { objectEntries } from "tsafe/objectEntries";
|
||||||
|
import { escapeStringForPropertiesFile } from "../../tools/escapeStringForPropertiesFile";
|
||||||
|
|
||||||
export type BuildOptionsLike = BuildOptionsLike_kcContextExclusionsFtlCode &
|
export type BuildOptionsLike = BuildOptionsLike_kcContextExclusionsFtlCode &
|
||||||
BuildOptionsLike_downloadKeycloakStaticResources &
|
BuildOptionsLike_downloadKeycloakStaticResources &
|
||||||
@ -50,6 +51,7 @@ export type BuildOptionsLike = BuildOptionsLike_kcContextExclusionsFtlCode &
|
|||||||
urlPathname: string | undefined;
|
urlPathname: string | undefined;
|
||||||
reactAppRootDirPath: string;
|
reactAppRootDirPath: string;
|
||||||
keycloakifyBuildDirPath: string;
|
keycloakifyBuildDirPath: string;
|
||||||
|
environmentVariables: { name: string; default: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||||
@ -261,7 +263,11 @@ export async function generateSrcMainResourcesForMainTheme(params: {
|
|||||||
}
|
}
|
||||||
assert<Equals<typeof themeType, never>>(false);
|
assert<Equals<typeof themeType, never>>(false);
|
||||||
})()}`,
|
})()}`,
|
||||||
...(buildOptions.extraThemeProperties ?? [])
|
...(buildOptions.extraThemeProperties ?? []),
|
||||||
|
buildOptions.environmentVariables.map(
|
||||||
|
({ name, default: defaultValue }) =>
|
||||||
|
`${name}=\${env.${name}:${escapeStringForPropertiesFile(defaultValue)}}`
|
||||||
|
)
|
||||||
].join("\n\n"),
|
].join("\n\n"),
|
||||||
"utf8"
|
"utf8"
|
||||||
)
|
)
|
||||||
|
@ -205,6 +205,21 @@ program
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
program
|
||||||
|
.command({
|
||||||
|
name: "update-kc-gen",
|
||||||
|
description:
|
||||||
|
"(Webpack/Create-React-App only) Create/update the kc.gen.ts file in your project."
|
||||||
|
})
|
||||||
|
.task({
|
||||||
|
skip,
|
||||||
|
handler: async cliCommandOptions => {
|
||||||
|
const { command } = await import("./update-kc-gen");
|
||||||
|
|
||||||
|
await command({ cliCommandOptions });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Fallback to build command if no command is provided
|
// Fallback to build command if no command is provided
|
||||||
{
|
{
|
||||||
const [, , ...rest] = process.argv;
|
const [, , ...rest] = process.argv;
|
||||||
|
@ -31,15 +31,17 @@ export type BuildOptions = {
|
|||||||
assetsDirPath: string;
|
assetsDirPath: string;
|
||||||
npmWorkspaceRootDirPath: string;
|
npmWorkspaceRootDirPath: string;
|
||||||
kcContextExclusionsFtlCode: string | undefined;
|
kcContextExclusionsFtlCode: string | undefined;
|
||||||
|
environmentVariables: { name: string; default: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UserProvidedBuildOptions = {
|
export type UserProvidedBuildOptions = {
|
||||||
|
themeName?: string | string[];
|
||||||
|
environmentVariables?: { name: string; default: string }[];
|
||||||
extraThemeProperties?: string[];
|
extraThemeProperties?: string[];
|
||||||
artifactId?: string;
|
artifactId?: string;
|
||||||
groupId?: string;
|
groupId?: string;
|
||||||
loginThemeResourcesFromKeycloakVersion?: string;
|
loginThemeResourcesFromKeycloakVersion?: string;
|
||||||
keycloakifyBuildDirPath?: string;
|
keycloakifyBuildDirPath?: string;
|
||||||
themeName?: string | string[];
|
|
||||||
kcContextExclusionsFtlCode?: string;
|
kcContextExclusionsFtlCode?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -305,6 +307,7 @@ export function readBuildOptions(params: {
|
|||||||
return pathJoin(reactAppBuildDirPath, resolvedViteConfig.assetsDir);
|
return pathJoin(reactAppBuildDirPath, resolvedViteConfig.assetsDir);
|
||||||
})(),
|
})(),
|
||||||
npmWorkspaceRootDirPath,
|
npmWorkspaceRootDirPath,
|
||||||
kcContextExclusionsFtlCode: userProvidedBuildOptions.kcContextExclusionsFtlCode
|
kcContextExclusionsFtlCode: userProvidedBuildOptions.kcContextExclusionsFtlCode,
|
||||||
|
environmentVariables: userProvidedBuildOptions.environmentVariables ?? []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
61
src/bin/shared/generateKcGenTs.ts
Normal file
61
src/bin/shared/generateKcGenTs.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { assert } from "tsafe/assert";
|
||||||
|
import type { BuildOptions } from "./buildOptions";
|
||||||
|
import { getThemeSrcDirPath } from "./getThemeSrcDirPath";
|
||||||
|
import * as fs from "fs/promises";
|
||||||
|
import { join as pathJoin } from "path";
|
||||||
|
|
||||||
|
export type BuildOptionsLike = {
|
||||||
|
reactAppRootDirPath: string;
|
||||||
|
themeNames: string[];
|
||||||
|
environmentVariables: { name: string; default: string }[];
|
||||||
|
};
|
||||||
|
|
||||||
|
assert<BuildOptions extends BuildOptionsLike ? true : false>();
|
||||||
|
|
||||||
|
export async function generateKcGenTs(params: {
|
||||||
|
buildOptions: BuildOptionsLike;
|
||||||
|
}): Promise<void> {
|
||||||
|
const { buildOptions } = params;
|
||||||
|
|
||||||
|
const { themeSrcDirPath } = getThemeSrcDirPath({
|
||||||
|
reactAppRootDirPath: buildOptions.reactAppRootDirPath
|
||||||
|
});
|
||||||
|
|
||||||
|
await fs.writeFile(
|
||||||
|
pathJoin(themeSrcDirPath, "kc.gen.ts"),
|
||||||
|
Buffer.from(
|
||||||
|
[
|
||||||
|
`/* prettier-ignore-start */`,
|
||||||
|
``,
|
||||||
|
`/* eslint-disable */`,
|
||||||
|
``,
|
||||||
|
`// @ts-nocheck`,
|
||||||
|
``,
|
||||||
|
`// noinspection JSUnusedGlobalSymbols`,
|
||||||
|
``,
|
||||||
|
`// This file is auto-generated by Keycloakify`,
|
||||||
|
``,
|
||||||
|
`export type ThemeName = ${buildOptions.themeNames.map(themeName => `"${themeName}"`).join(" | ")};`,
|
||||||
|
``,
|
||||||
|
`export const themeNames: ThemeName[] = [${buildOptions.themeNames.map(themeName => `"${themeName}"`).join(", ")}];`,
|
||||||
|
``,
|
||||||
|
`export type KcEnvName = ${buildOptions.environmentVariables.length === 0 ? "never" : buildOptions.environmentVariables.map(({ name }) => `"${name}"`).join(" | ")};`,
|
||||||
|
``,
|
||||||
|
`export const KcEnvNames: KcEnvName[] = [${buildOptions.environmentVariables.map(({ name }) => `"${name}"`).join(", ")}];`,
|
||||||
|
``,
|
||||||
|
`export const kcEnvDefaults: Record<KcEnvName, string> = ${JSON.stringify(
|
||||||
|
Object.fromEntries(
|
||||||
|
buildOptions.environmentVariables.map(
|
||||||
|
({ name, default: defaultValue }) => [name, defaultValue]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}`,
|
||||||
|
``,
|
||||||
|
`/* prettier-ignore-end */`
|
||||||
|
].join("\n"),
|
||||||
|
"utf8"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
64
src/bin/tools/escapeStringForPropertiesFile.ts
Normal file
64
src/bin/tools/escapeStringForPropertiesFile.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// Convert a JavaScript string to UTF-16 encoding
|
||||||
|
function toUTF16(codePoint: number): string {
|
||||||
|
if (codePoint <= 0xffff) {
|
||||||
|
// BMP character
|
||||||
|
return "\\u" + codePoint.toString(16).padStart(4, "0");
|
||||||
|
} else {
|
||||||
|
// Non-BMP character
|
||||||
|
codePoint -= 0x10000;
|
||||||
|
let highSurrogate = (codePoint >> 10) + 0xd800;
|
||||||
|
let lowSurrogate = (codePoint % 0x400) + 0xdc00;
|
||||||
|
return (
|
||||||
|
"\\u" +
|
||||||
|
highSurrogate.toString(16).padStart(4, "0") +
|
||||||
|
"\\u" +
|
||||||
|
lowSurrogate.toString(16).padStart(4, "0")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Escapes special characters for use in a .properties file
|
||||||
|
export function escapeStringForPropertiesFile(str: string): string {
|
||||||
|
let escapedStr = "";
|
||||||
|
for (const char of [...str]) {
|
||||||
|
const codePoint = char.codePointAt(0);
|
||||||
|
if (!codePoint) continue;
|
||||||
|
|
||||||
|
switch (char) {
|
||||||
|
case "\n":
|
||||||
|
escapedStr += "\\n";
|
||||||
|
break;
|
||||||
|
case "\r":
|
||||||
|
escapedStr += "\\r";
|
||||||
|
break;
|
||||||
|
case "\t":
|
||||||
|
escapedStr += "\\t";
|
||||||
|
break;
|
||||||
|
case "\\":
|
||||||
|
escapedStr += "\\\\";
|
||||||
|
break;
|
||||||
|
case ":":
|
||||||
|
escapedStr += "\\:";
|
||||||
|
break;
|
||||||
|
case "=":
|
||||||
|
escapedStr += "\\=";
|
||||||
|
break;
|
||||||
|
case "#":
|
||||||
|
escapedStr += "\\#";
|
||||||
|
break;
|
||||||
|
case "!":
|
||||||
|
escapedStr += "\\!";
|
||||||
|
break;
|
||||||
|
case "'":
|
||||||
|
escapedStr += "''";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (codePoint > 0x7f) {
|
||||||
|
escapedStr += toUTF16(codePoint); // Non-ASCII characters
|
||||||
|
} else {
|
||||||
|
escapedStr += char; // ASCII character needs no escape
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return escapedStr;
|
||||||
|
}
|
13
src/bin/update-kc-gen.ts
Normal file
13
src/bin/update-kc-gen.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import type { CliCommandOptions } from "./main";
|
||||||
|
import { readBuildOptions } from "./shared/buildOptions";
|
||||||
|
import { generateKcGenTs } from "./shared/generateKcGenTs";
|
||||||
|
|
||||||
|
export async function command(params: { cliCommandOptions: CliCommandOptions }) {
|
||||||
|
const { cliCommandOptions } = params;
|
||||||
|
|
||||||
|
const buildOptions = readBuildOptions({
|
||||||
|
cliCommandOptions
|
||||||
|
});
|
||||||
|
|
||||||
|
await generateKcGenTs({ buildOptions });
|
||||||
|
}
|
@ -17,6 +17,7 @@ import {
|
|||||||
type ResolvedViteConfig
|
type ResolvedViteConfig
|
||||||
} from "../bin/shared/buildOptions";
|
} from "../bin/shared/buildOptions";
|
||||||
import MagicString from "magic-string";
|
import MagicString from "magic-string";
|
||||||
|
import { generateKcGenTs } from "../bin/shared/generateKcGenTs";
|
||||||
|
|
||||||
export type Params = UserProvidedBuildOptions & {
|
export type Params = UserProvidedBuildOptions & {
|
||||||
postBuild?: (buildOptions: Omit<BuildOptions, "bundler">) => Promise<void>;
|
postBuild?: (buildOptions: Omit<BuildOptions, "bundler">) => Promise<void>;
|
||||||
@ -115,13 +116,20 @@ export function keycloakify(params?: Params) {
|
|||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
await copyKeycloakResourcesToPublic({
|
const buildOptions = readBuildOptions({
|
||||||
buildOptions: readBuildOptions({
|
cliCommandOptions: {
|
||||||
cliCommandOptions: {
|
reactAppRootDirPath
|
||||||
reactAppRootDirPath
|
}
|
||||||
}
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
copyKeycloakResourcesToPublic({
|
||||||
|
buildOptions
|
||||||
|
}),
|
||||||
|
generateKcGenTs({
|
||||||
|
buildOptions
|
||||||
|
})
|
||||||
|
]);
|
||||||
},
|
},
|
||||||
transform: (code, id) => {
|
transform: (code, id) => {
|
||||||
assert(command !== undefined);
|
assert(command !== undefined);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user