From ab5287a3d412d7b3a2ce37bd4f172c12a5643202 Mon Sep 17 00:00:00 2001 From: Waldemar Reusch Date: Thu, 30 Mar 2023 21:56:50 +0200 Subject: [PATCH 1/6] refactor: type-safe trimIndent --- src/bin/tools/trimIndent.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/bin/tools/trimIndent.ts b/src/bin/tools/trimIndent.ts index e68a80cb..f7daa92b 100644 --- a/src/bin/tools/trimIndent.ts +++ b/src/bin/tools/trimIndent.ts @@ -2,7 +2,7 @@ * Concatenate the string fragments and interpolated values * to get a single string. */ -function populateTemplate(strings: TemplateStringsArray, ...args: any[]) { +function populateTemplate(strings: TemplateStringsArray, ...args: unknown[]) { const chunks = []; for (let i = 0; i < strings.length; i++) { let lastStringLineLength = 0; @@ -14,7 +14,8 @@ 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(""); @@ -23,14 +24,17 @@ function populateTemplate(strings: TemplateStringsArray, ...args: any[]) { 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]$/, ""); + 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"), ""); + const re = new RegExp(`^${" ".repeat(minDent)}`, "gm"); + const dedented = string.replace(re, ""); return dedented; } @@ -38,7 +42,7 @@ function trimIndentPrivate(removeEmptyLeadingAndTrailingLines: boolean, strings: * 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[]) { +export default function trimIndent(strings: TemplateStringsArray, ...args: unknown[]) { return trimIndentPrivate(true, strings, ...args); } @@ -46,6 +50,6 @@ export default function trimIndent(strings: TemplateStringsArray, ...args: any[] * Shift all lines left by the *smallest* indentation level, * and _keep_ initial newline and all trailing spaces. */ -trimIndent.keepLeadingAndTrailingNewlines = function (strings: TemplateStringsArray, ...args: any[]) { +trimIndent.keepLeadingAndTrailingNewlines = function (strings: TemplateStringsArray, ...args: unknown[]) { return trimIndentPrivate(false, strings, ...args); }; From 218c1a5a50bf40c080c47909185f9902aaee26f7 Mon Sep 17 00:00:00 2001 From: Waldemar Reusch Date: Thu, 30 Mar 2023 22:13:32 +0200 Subject: [PATCH 2/6] refactor: use path.sep to be cross-platform --- scripts/generate-i18n-messages.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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; From 2811eb60244a2e2b7aaa914629ac718cae9b330e Mon Sep 17 00:00:00 2001 From: Waldemar Reusch Date: Thu, 30 Mar 2023 22:17:06 +0200 Subject: [PATCH 3/6] fix: fix typing --- src/bin/tools/trimIndent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/tools/trimIndent.ts b/src/bin/tools/trimIndent.ts index f7daa92b..33c195ba 100644 --- a/src/bin/tools/trimIndent.ts +++ b/src/bin/tools/trimIndent.ts @@ -3,7 +3,7 @@ * to get a single string. */ function populateTemplate(strings: TemplateStringsArray, ...args: unknown[]) { - const chunks = []; + const chunks: string[] = []; for (let i = 0; i < strings.length; i++) { let lastStringLineLength = 0; if (strings[i]) { From 46264c85f4078f5221bd0385c2eee44f23f19226 Mon Sep 17 00:00:00 2001 From: Waldemar Reusch Date: Sat, 1 Apr 2023 22:36:54 +0200 Subject: [PATCH 4/6] Add unit test and fix some more use cases --- src/bin/tools/trimIndent.ts | 33 ++++++------------ test/bin/tools/trimIndet.spec.ts | 57 ++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 22 deletions(-) create mode 100644 test/bin/tools/trimIndet.spec.ts diff --git a/src/bin/tools/trimIndent.ts b/src/bin/tools/trimIndent.ts index 33c195ba..1c826cd7 100644 --- a/src/bin/tools/trimIndent.ts +++ b/src/bin/tools/trimIndent.ts @@ -21,13 +21,18 @@ function populateTemplate(strings: TemplateStringsArray, ...args: unknown[]) { return chunks.join(""); } -function trimIndentPrivate(removeEmptyLeadingAndTrailingLines: boolean, strings: TemplateStringsArray, ...args: any[]) { +/** + * 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[]) { // 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) ?? []; + 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); @@ -37,19 +42,3 @@ function trimIndentPrivate(removeEmptyLeadingAndTrailingLines: boolean, strings: const dedented = string.replace(re, ""); 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: unknown[]) { - return trimIndentPrivate(true, strings, ...args); -} - -/** - * Shift all lines left by the *smallest* indentation level, - * and _keep_ initial newline and all trailing spaces. - */ -trimIndent.keepLeadingAndTrailingNewlines = function (strings: TemplateStringsArray, ...args: unknown[]) { - 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..b25c33e9 --- /dev/null +++ b/test/bin/tools/trimIndet.spec.ts @@ -0,0 +1,57 @@ +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')) + }) +}) \ No newline at end of file From baae22657e5ea4504ca817fbf02b489e495e0b5a Mon Sep 17 00:00:00 2001 From: Waldemar Reusch Date: Sat, 1 Apr 2023 22:44:13 +0200 Subject: [PATCH 5/6] style: fix formatting --- src/bin/tools/trimIndent.ts | 8 +++-- test/bin/tools/trimIndet.spec.ts | 50 ++++++++++++++++++-------------- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/bin/tools/trimIndent.ts b/src/bin/tools/trimIndent.ts index 1c826cd7..7ebafb7a 100644 --- a/src/bin/tools/trimIndent.ts +++ b/src/bin/tools/trimIndent.ts @@ -30,9 +30,11 @@ export default function trimIndent(strings: TemplateStringsArray, ...args: any[] 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) ?? []; + 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); diff --git a/test/bin/tools/trimIndet.spec.ts b/test/bin/tools/trimIndet.spec.ts index b25c33e9..07dd8af5 100644 --- a/test/bin/tools/trimIndet.spec.ts +++ b/test/bin/tools/trimIndet.spec.ts @@ -2,36 +2,35 @@ 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')) - }) +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')) - }) +`; + 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')) - }) + `; + 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')) - }) + `; + assert.equal(txt, ["lorem", " ipsum"].join("\n")); + }); it("removes only first and last empty line", () => { const txt = trimIndent` @@ -39,19 +38,28 @@ ipsum lorem ipsum - ` - - assert.equal(txt, ['', 'lorem', 'ipsum', ''].join('\n')) - }) + `; + assert.equal(txt, ["", "lorem", "ipsum", ""].join("\n")); + }); it("interpolates non-strings", () => { - const d = new Date() + const d = new Date(); const txt = trimIndent` lorem ${d} - ipsum` + ipsum`; - assert.equal(txt, ['lorem', String(d), 'ipsum'].join('\n')) + 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 From bd2f6d8feed011be22218203740ed57d5155ac49 Mon Sep 17 00:00:00 2001 From: Waldemar Reusch Date: Sat, 1 Apr 2023 22:52:09 +0200 Subject: [PATCH 6/6] style: move loose test into test suite --- test/bin/tools/trimIndet.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/bin/tools/trimIndet.spec.ts b/test/bin/tools/trimIndet.spec.ts index 07dd8af5..c6a2f37f 100644 --- a/test/bin/tools/trimIndet.spec.ts +++ b/test/bin/tools/trimIndet.spec.ts @@ -52,7 +52,6 @@ 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') @@ -63,3 +62,5 @@ ipsum ` assert.equal(txt, ['lorem', 'ipsum', 'dolor', 'sit', 'amet'].join('\n')) }) + +}); \ No newline at end of file