From 7049593b6e8de04b430273cba5949cab0a44cf52 Mon Sep 17 00:00:00 2001 From: Alexandre Date: Sat, 15 Nov 2025 22:40:37 +0100 Subject: [PATCH] fix .gitignore to be able to compile --- .gitignore | 3 + frontend/src/lib/workspaceUtils.js | 107 ++++++++++++++++++++++++ frontend/src/lib/workspaceUtils.test.js | 57 +++++++++++++ 3 files changed, 167 insertions(+) create mode 100644 frontend/src/lib/workspaceUtils.js create mode 100644 frontend/src/lib/workspaceUtils.test.js diff --git a/.gitignore b/.gitignore index 41229b5d..71e50a59 100644 --- a/.gitignore +++ b/.gitignore @@ -199,3 +199,6 @@ backend/server backend/src/.gocache /config/credentials.json credentials.json +# Allow frontend source utilities +!frontend/src/lib/ +!frontend/src/lib/** diff --git a/frontend/src/lib/workspaceUtils.js b/frontend/src/lib/workspaceUtils.js new file mode 100644 index 00000000..607fb9b8 --- /dev/null +++ b/frontend/src/lib/workspaceUtils.js @@ -0,0 +1,107 @@ +const PARAM_START = ""; +const PARAM_END = ""; + +export function parseErrorLines(txt) { + if (!txt) return []; + const starts = []; + const regex = /(^|\r?\n)(Warning|Error)\b/g; + let m; + while ((m = regex.exec(txt)) !== null) { + const start = m.index + (m[1] ? m[1].length : 0); + starts.push(start); + } + if (starts.length === 0) { + return [txt.trim()].filter(Boolean); + } + const lines = []; + const leading = txt.slice(0, starts[0]).trim(); + if (leading) lines.push(leading); + for (let i = 0; i < starts.length; i++) { + const s = starts[i]; + const e = i + 1 < starts.length ? starts[i + 1] : txt.length; + const chunk = txt.slice(s, e).trim(); + if (chunk) lines.push(chunk); + } + return lines; +} + +export function stripParamBlock(text) { + const start = text.indexOf(PARAM_START); + const end = text.indexOf(PARAM_END); + let result = text; + if (start !== -1 && end !== -1 && end > start) { + let before = text.slice(0, start); + let after = text.slice(end + PARAM_END.length); + if (before.endsWith("\n")) before = before.slice(0, -1); + if (after.startsWith("\n")) after = after.slice(1); + result = before + after; + } + return result.replace( + /[ \t]*]*?(?:\/>|>[\s\S]*?<\/xsl:param>)[ \t]*(?:\r?\n)?/g, + "", + ); +} + +export function getParamBlock(text) { + const start = text.indexOf(PARAM_START); + const end = text.indexOf(PARAM_END); + if (start !== -1 && end !== -1 && end > start) { + return text.slice(start, end); + } + return text; +} + +export function injectParamBlock(text, params) { + const clean = stripParamBlock(text); + const match = clean.match(/]*>/); + if (!match) return clean; + const idx = match.index + match[0].length; + const paramLines = params + .filter((p) => p.name) + .map((p) => ``) + .join("\n"); + const block = `\n${PARAM_START}\n${paramLines}\n${PARAM_END}`; + return clean.slice(0, idx) + block + clean.slice(idx); +} + +export function extractParamNames(text) { + const clean = getParamBlock(text); + const names = new Set(); + const regex = /]*name="([^"]+)"[^>]*>/g; + let m; + while ((m = regex.exec(clean))) { + names.add(m[1]); + } + return Array.from(names); +} + +export function addParams(text, tab) { + const extractedParams = extractParamNames(text); + const existingNames = new Set(tab.params.map((p) => p.name)); + const newParams = [...tab.params]; + + extractedParams.forEach((name) => { + if (!existingNames.has(name)) { + newParams.push({ name, value: "", open: false }); + } + }); + + return newParams; +} + +export function setStylesheetVersion(text, version) { + const regex = /]*)>/; + const match = text.match(regex); + if (!match) return text; + let attrs = match[1]; + if (/version=['"][^'"]*['"]/.test(attrs)) { + attrs = attrs.replace(/version=['"][^'"]*['"]/, `version="${version}"`); + } else { + attrs += ` version="${version}"`; + } + return text.replace(regex, ``); +} + +export function getParamBlockMarkers() { + return { PARAM_START, PARAM_END }; +} diff --git a/frontend/src/lib/workspaceUtils.test.js b/frontend/src/lib/workspaceUtils.test.js new file mode 100644 index 00000000..a0552c3a --- /dev/null +++ b/frontend/src/lib/workspaceUtils.test.js @@ -0,0 +1,57 @@ +import { describe, it, expect } from "vitest"; +import { + addParams, + extractParamNames, + injectParamBlock, + parseErrorLines, + setStylesheetVersion, + stripParamBlock, +} from "./workspaceUtils"; + +const SAMPLE_STYLESHEET = ` + + + + + +`; + +describe("workspace utils", () => { + it("parses error lines with warning/error prefixes", () => { + const lines = parseErrorLines("intro\nWarning test 1\nError details\n"); + expect(lines).toEqual(["intro", "Warning test 1", "Error details"]); + }); + + it("strips injected param blocks and inline params", () => { + const injected = injectParamBlock(SAMPLE_STYLESHEET, [ + { name: "alpha" }, + { name: "beta" }, + ]); + const cleaned = stripParamBlock(injected); + expect(cleaned).not.toContain("PARAMS_START"); + expect(cleaned).not.toContain("alpha"); + expect(cleaned).not.toContain("beta"); + expect(cleaned).not.toMatch(/ { + expect(extractParamNames(SAMPLE_STYLESHEET).sort()).toEqual(["bar", "foo"]); + }); + + it("adds new params discovered in xslt", () => { + const tab = { + params: [{ name: "foo", value: "value", open: false }], + }; + const params = addParams(SAMPLE_STYLESHEET, tab); + expect(params).toHaveLength(2); + expect(params.some((p) => p.name === "bar")).toBe(true); + }); + + it("updates or injects stylesheet version attribute", () => { + const noVersion = SAMPLE_STYLESHEET.replace(`version="1.0"`, ""); + expect(setStylesheetVersion(noVersion, "2.0")).toContain(`version="2.0"`); + expect(setStylesheetVersion(SAMPLE_STYLESHEET, "3.0")).toContain( + `version="3.0"`, + ); + }); +});