diff --git a/src/templates/__tests__/templateformatter.test.js b/src/templates/__tests__/templateformatter.test.js
index 5a8b655..8c4c745 100644
--- a/src/templates/__tests__/templateformatter.test.js
+++ b/src/templates/__tests__/templateformatter.test.js
@@ -59,4 +59,25 @@ describe('formatTemplateContent', () => {
expect(result.content).not.toMatch(/\(\s+<%/);
expect(result.content).not.toMatch(/%>\s+\)/);
});
+
+ it('should not add a space next to a colon before EJS output', () => {
+ const input =
+ 'INV: <%= _reference %>\n ';
+ const result = formatTemplateContent(input);
+
+ expect(result.content).toBe(
+ 'INV:<%= _reference %>'
+ );
+ });
+
+ it('should keep barcode content and the closing tag on one line', () => {
+ const input =
+ 'INV:<%= _reference %>';
+ const result = formatTemplateContent(input);
+
+ expect(result.content).toBe(
+ 'INV:<%= _reference %>'
+ );
+ expect(result.content).not.toMatch(/%>\s*\n\s*<\/Barcode>/);
+ });
});
diff --git a/src/templates/templateformatter.js b/src/templates/templateformatter.js
index 6182ff9..055492e 100644
--- a/src/templates/templateformatter.js
+++ b/src/templates/templateformatter.js
@@ -4,7 +4,9 @@ const { html: beautifyHtml, js: beautifyJs } = beautify;
const EJS_BLOCK_REGEX = /<%[\s\S]*?%>/g;
const EJS_TAG_REGEX = /^<%([=#-]?)([\s\S]*?)%>/;
-const EJS_PLACEHOLDER_REGEX = //g;
+const EJS_COMMENT_PLACEHOLDER_REGEX = //g;
+const EJS_INLINE_PLACEHOLDER_REGEX = /___EJS_PH_(\d+)___/g;
+const TEXT_ONLY_TAG_REGEX = /<([A-Za-z][\w:-]*)(\s[^>]*)?>([^<]*)<\/\1>/g;
const HTML_BEAUTIFY_OPTIONS = {
indent_size: 2,
@@ -47,28 +49,53 @@ function formatEjsTag(tag) {
}
}
+function isControlEjs(tag) {
+ const match = tag.match(EJS_TAG_REGEX);
+ if (!match) {
+ return false;
+ }
+
+ const [, modifier] = match;
+ return !modifier;
+}
+
function maskEjsBlocks(content) {
const ejsBlocks = [];
const masked = content.replace(EJS_BLOCK_REGEX, (match) => {
const index = ejsBlocks.length;
ejsBlocks.push(match);
- return `\n\n`;
+ if (isControlEjs(match)) {
+ return `\n\n`;
+ }
+ return `___EJS_PH_${index}___`;
});
return { masked, ejsBlocks };
}
function restoreEjsBlocks(content, ejsBlocks) {
- return content.replace(EJS_PLACEHOLDER_REGEX, (_, index) =>
- formatEjsTag(ejsBlocks[Number(index)])
- );
+ const restore = (_, index) => formatEjsTag(ejsBlocks[Number(index)]);
+ return content
+ .replace(EJS_COMMENT_PLACEHOLDER_REGEX, restore)
+ .replace(EJS_INLINE_PLACEHOLDER_REGEX, restore);
+}
+
+function collapseTextOnlyTags(content) {
+ return content.replace(TEXT_ONLY_TAG_REGEX, (_, tagName, attrs, inner) => {
+ const collapsedInner = inner.replace(/^\s+|\s+$/g, '');
+ return `<${tagName}${attrs || ''}>${collapsedInner}${tagName}>`;
+ });
}
function tightenParenthesesAroundEjs(content) {
return content.replace(/\(\s+(<%)/g, '($1').replace(/(%>)\s+\)/g, '$1)');
}
+function tightenColonsAroundEjs(content) {
+ return content.replace(/:\s+(<%)/g, ':$1');
+}
+
export function formatTemplateContent(content) {
if (content == null || typeof content !== 'string') {
return { error: 'Content is required and must be a string.', code: 400 };
@@ -81,8 +108,10 @@ export function formatTemplateContent(content) {
try {
const { masked, ejsBlocks } = maskEjsBlocks(content);
let formatted = beautifyHtml(masked, HTML_BEAUTIFY_OPTIONS);
+ formatted = collapseTextOnlyTags(formatted);
formatted = restoreEjsBlocks(formatted, ejsBlocks);
formatted = tightenParenthesesAroundEjs(formatted);
+ formatted = tightenColonsAroundEjs(formatted);
return { content: formatted.trimEnd() };
} catch (error) {
return { error: error.message || 'Failed to format template content.', code: 400 };