diff --git a/scripts/generate-i18n-messages.ts b/scripts/generate-i18n-messages.ts index 4cebc755..4e83143c 100644 --- a/scripts/generate-i18n-messages.ts +++ b/scripts/generate-i18n-messages.ts @@ -1,6 +1,6 @@ import "minimal-polyfills/Object.fromEntries"; import * as fs from "fs"; -import { join as pathJoin, relative as pathRelative, dirname as pathDirname } from "path"; +import { join as pathJoin, relative as pathRelative, dirname as pathDirname, sep as pathSep } from "path"; import { crawl } from "../src/bin/tools/crawl"; import { downloadBuiltinKeycloakTheme } from "../src/bin/download-builtin-keycloak-theme"; import { getProjectRoot } from "../src/bin/tools/getProjectRoot"; @@ -35,11 +35,10 @@ async function main() { { const baseThemeDirPath = pathJoin(tmpDirPath, "base"); + const re = new RegExp(`^([^\\${pathSep}]+)\\${pathSep}messages\\${pathSep}messages_([^.]+).properties$`); crawl(baseThemeDirPath).forEach(filePath => { - const match = - filePath.match(/^([^/]+)\/messages\/messages_([^.]+)\.properties$/) || - filePath.match(/^([^\\]+)\\messages\\messages_([^.]+)\.properties$/); + const match = filePath.match(re); if (match === null) { return; diff --git a/src/bin/tools/trimIndent.ts b/src/bin/tools/trimIndent.ts index e68a80cb..7ebafb7a 100644 --- a/src/bin/tools/trimIndent.ts +++ b/src/bin/tools/trimIndent.ts @@ -2,8 +2,8 @@ * Concatenate the string fragments and interpolated values * to get a single string. */ -function populateTemplate(strings: TemplateStringsArray, ...args: any[]) { - const chunks = []; +function populateTemplate(strings: TemplateStringsArray, ...args: unknown[]) { + const chunks: string[] = []; for (let i = 0; i < strings.length; i++) { let lastStringLineLength = 0; if (strings[i]) { @@ -14,38 +14,33 @@ function populateTemplate(strings: TemplateStringsArray, ...args: any[]) { if (args[i]) { // if the interpolation value has newlines, indent the interpolation values // using the last known string indent - chunks.push(args[i].replace(/([\r?\n])/g, "$1" + " ".repeat(lastStringLineLength))); + const chunk = String(args[i]).replace(/([\r?\n])/g, "$1" + " ".repeat(lastStringLineLength)); + chunks.push(chunk); } } return chunks.join(""); } -function trimIndentPrivate(removeEmptyLeadingAndTrailingLines: boolean, strings: TemplateStringsArray, ...args: any[]) { - // Remove initial and final newlines - let string = populateTemplate(strings, ...args); - if (removeEmptyLeadingAndTrailingLines) string = string.replace(/^[\r\n]/, "").replace(/[^\S\r\n]*[\r\n]$/, ""); - const dents = string.match(/^([ \t])+/gm)?.map(s => s.length) ?? []; - // No dents? no change required - if (!dents || dents.length == 0) return string; - const minDent = Math.min(...dents); - // The min indentation is 0, no change needed - if (!minDent) return string; - const dedented = string.replace(new RegExp(`^${" ".repeat(minDent)}`, "gm"), ""); - return dedented; -} - /** * Shift all lines left by the *smallest* indentation level, * and remove initial newline and all trailing spaces. */ export default function trimIndent(strings: TemplateStringsArray, ...args: any[]) { - return trimIndentPrivate(true, strings, ...args); + // Remove initial and final newlines + let string = populateTemplate(strings, ...args) + .replace(/^[\r\n]/, "") + .replace(/\r?\n *$/, ""); + const dents = + string + .match(/^([ \t])+/gm) + ?.filter(s => /^\s+$/.test(s)) + ?.map(s => s.length) ?? []; + // No dents? no change required + if (!dents || dents.length == 0) return string; + const minDent = Math.min(...dents); + // The min indentation is 0, no change needed + if (!minDent) return string; + const re = new RegExp(`^${" ".repeat(minDent)}`, "gm"); + const dedented = string.replace(re, ""); + return dedented; } - -/** - * Shift all lines left by the *smallest* indentation level, - * and _keep_ initial newline and all trailing spaces. - */ -trimIndent.keepLeadingAndTrailingNewlines = function (strings: TemplateStringsArray, ...args: any[]) { - return trimIndentPrivate(false, strings, ...args); -}; diff --git a/test/bin/tools/trimIndet.spec.ts b/test/bin/tools/trimIndet.spec.ts new file mode 100644 index 00000000..c6a2f37f --- /dev/null +++ b/test/bin/tools/trimIndet.spec.ts @@ -0,0 +1,66 @@ +import trimIndent from "keycloakify/bin/tools/trimIndent"; +import { it, describe, assert } from "vitest"; + +describe("trimIndent", () => { + it("does not change a left-aligned string as expected", () => { + const txt = trimIndent`lorem +ipsum`; + assert.equal(txt, ["lorem", "ipsum"].join("\n")); + }); + + it("removes leading and trailing empty lines from a left-aligned string", () => { + const txt = trimIndent` +lorem +ipsum +`; + assert.equal(txt, ["lorem", "ipsum"].join("\n")); + }); + + it("removes indent from an aligned string", () => { + const txt = trimIndent` + lorem + ipsum + `; + assert.equal(txt, ["lorem", "ipsum"].join("\n")); + }); + + it("removes indent from unaligned string", () => { + const txt = trimIndent` + lorem + ipsum + `; + assert.equal(txt, ["lorem", " ipsum"].join("\n")); + }); + + it("removes only first and last empty line", () => { + const txt = trimIndent` + + lorem + ipsum + + `; + + assert.equal(txt, ["", "lorem", "ipsum", ""].join("\n")); + }); + + it("interpolates non-strings", () => { + const d = new Date(); + const txt = trimIndent` + lorem + ${d} + ipsum`; + + assert.equal(txt, ["lorem", String(d), "ipsum"].join("\n")); + }); + + it("inderpolates preserving new-lines in the interpolated bits", () => { + const a = ["ipsum", "dolor", "sit"].join('\n') + const txt = trimIndent` + lorem + ${a} + amet + ` + assert.equal(txt, ['lorem', 'ipsum', 'dolor', 'sit', 'amet'].join('\n')) + }) + +}); \ No newline at end of file