From 246fcc8b096c4e4f3770a7ca110bcc5efa4c1f8c Mon Sep 17 00:00:00 2001 From: djunehor Date: Mon, 10 Nov 2025 01:40:52 +0100 Subject: [PATCH 1/2] Fix space option not being applied to function bodies (#195) --- index.js | 286 +++++++++++++++++++++++++++++++++++++---- test/unit/serialize.js | 64 +++++++++ 2 files changed, 324 insertions(+), 26 deletions(-) diff --git a/index.js b/index.js index e8022a3..1c9b725 100644 --- a/index.js +++ b/index.js @@ -138,44 +138,278 @@ module.exports = function serialize(obj, options) { return value; } - function serializeFunc(fn) { + function serializeFunc(fn, options) { var serializedFn = fn.toString(); if (IS_NATIVE_CODE_REGEXP.test(serializedFn)) { throw new TypeError('Serializing native function: ' + fn.name); } - // pure functions, example: {key: function() {}} - if(IS_PURE_FUNCTION.test(serializedFn)) { - return serializedFn; + // If no space option, return original behavior + if (!options || !options.space) { + // pure functions, example: {key: function() {}} + if(IS_PURE_FUNCTION.test(serializedFn)) { + return serializedFn; + } + + // arrow functions, example: arg1 => arg1+5 + if(IS_ARROW_FUNCTION.test(serializedFn)) { + return serializedFn; + } + + var argsStartsAt = serializedFn.indexOf('('); + var def = serializedFn.substr(0, argsStartsAt) + .trim() + .split(' ') + .filter(function(val) { return val.length > 0 }); + + var nonReservedSymbols = def.filter(function(val) { + return RESERVED_SYMBOLS.indexOf(val) === -1 + }); + + // enhanced literal objects, example: {key() {}} + if(nonReservedSymbols.length > 0) { + return (def.indexOf('async') > -1 ? 'async ' : '') + 'function' + + (def.join('').indexOf('*') > -1 ? '*' : '') + + serializedFn.substr(argsStartsAt); + } + + // arrow functions + return serializedFn; + } + + // Format function body with space option + return formatFunctionWithSpace(serializedFn, options.space); + } + + function formatFunctionWithSpace(serializedFn, space) { + // Determine indentation unit + var indentUnit; + if (typeof space === 'number') { + indentUnit = ' '.repeat(space); + } else if (typeof space === 'string') { + indentUnit = space; + } else { + return serializedFn; // fallback to original } - // arrow functions, example: arg1 => arg1+5 - if(IS_ARROW_FUNCTION.test(serializedFn)) { - return serializedFn; + // Find the function body opening brace (not parameter destructuring braces) + var bodyStartBraceIndex = -1; + var parenDepth = 0; + var braceDepth = 0; + + for (var i = 0; i < serializedFn.length; i++) { + var char = serializedFn[i]; + if (char === '(') { + parenDepth++; + } else if (char === ')') { + parenDepth--; + // After closing the parameter list, the next { is the function body + if (parenDepth === 0) { + for (var j = i + 1; j < serializedFn.length; j++) { + if (serializedFn[j] === '{') { + bodyStartBraceIndex = j; + break; + } else if (serializedFn[j] !== ' ' && serializedFn[j] !== '=' && serializedFn[j] !== '>') { + // Non-space/arrow character before brace, not a function body brace + break; + } + } + break; + } + } + } + + var closeBraceIndex = serializedFn.lastIndexOf('}'); + + if (bodyStartBraceIndex === -1 || closeBraceIndex === -1 || bodyStartBraceIndex >= closeBraceIndex) { + return serializedFn; // No function body braces found, return original } - var argsStartsAt = serializedFn.indexOf('('); - var def = serializedFn.substr(0, argsStartsAt) - .trim() - .split(' ') - .filter(function(val) { return val.length > 0 }); - - var nonReservedSymbols = def.filter(function(val) { - return RESERVED_SYMBOLS.indexOf(val) === -1 - }); - - // enhanced literal objects, example: {key() {}} - if(nonReservedSymbols.length > 0) { - return (def.indexOf('async') > -1 ? 'async ' : '') + 'function' - + (def.join('').indexOf('*') > -1 ? '*' : '') - + serializedFn.substr(argsStartsAt); + var signature = serializedFn.substring(0, bodyStartBraceIndex).trim(); + var body = serializedFn.substring(bodyStartBraceIndex + 1, closeBraceIndex).trim(); + + // Clean up signature: ensure proper spacing + // For arrow functions, add space around => + if (signature.includes('=>')) { + signature = signature.replace(/\s*=>\s*/, ' => '); + } + + // Ensure space before opening brace + if (!signature.endsWith(' ')) { + signature += ' '; + } + + // If body is empty, format minimally + if (!body) { + return signature + '{\n' + indentUnit.repeat(2) + '}'; } - // arrow functions - return serializedFn; + // Format the function body with proper indentation and spacing + var formattedBody = formatSimpleFunctionBody(body, indentUnit); + + // Ensure we don't double-add closing braces + var lines = formattedBody.split('\n'); + var lastNonEmptyIndex = lines.length - 1; + while (lastNonEmptyIndex >= 0 && !lines[lastNonEmptyIndex].trim()) { + lastNonEmptyIndex--; + } + + if (lastNonEmptyIndex >= 0 && lines[lastNonEmptyIndex].trim() === '}') { + // Remove the last closing brace line + lines.splice(lastNonEmptyIndex, 1); + formattedBody = lines.join('\n'); + } + + return signature + '{\n' + formattedBody + '\n' + indentUnit + '}'; + } + + function formatSimpleFunctionBody(body, indentUnit) { + // Enhanced function body formatter that handles nested structures + var baseIndent = indentUnit.repeat(2); // Functions are already inside objects, so depth 2 + + // First, add spaces around operators and keywords, being careful about arrow functions + var formatted = body + // Protect arrow functions from being split + .replace(/=>/g, '___ARROW___') + // Clean up multiple spaces first + .replace(/\s+/g, ' ') + // Add spaces around operators (but not === or !==) + .replace(/([^=!<>])\s*=\s*([^=])/g, '$1 = $2') + .replace(/([^=])\s*===\s*([^=])/g, '$1 === $2') + .replace(/([^!])\s*!==\s*([^=])/g, '$1 !== $2') + .replace(/([^|])\s*\|\|\s*([^|])/g, '$1 || $2') + .replace(/([^&])\s*&&\s*([^&])/g, '$1 && $2') + // Add spaces around arithmetic operators + .replace(/([^\s*])\s*\*\s*([^\s*])/g, '$1 * $2') + .replace(/([^\s+])\s*\+\s*([^\s+])/g, '$1 + $2') + .replace(/([^\s-])\s*-\s*([^\s-])/g, '$1 - $2') + .replace(/([^\s/])\s*\/\s*([^\s/])/g, '$1 / $2') + // Add spaces around comparison operators + .replace(/([^\s>])\s*>\s*([^\s>=])/g, '$1 > $2') + .replace(/([^\s<])\s*<\s*([^\s<=])/g, '$1 < $2') + .replace(/\s*>=\s*(?![>])/g, ' >= ') + .replace(/\s*<=\s*(?![<])/g, ' <= ') + // Add spaces after commas + .replace(/,(?!\s)/g, ', ') + // Add space after control keywords and before braces + .replace(/\b(if|for|while)\s*\(/g, '$1 (') + .replace(/\)\s*\{/g, ') {') + .replace(/\belse\s*\{/g, 'else {') + .replace(/\breturn\s+([^\s])/g, 'return $1') + // Restore arrow functions + .replace(/___ARROW___/g, ' => '); + + // Parse and format the statements with proper line breaks and nesting + return formatCodeWithNesting(formatted, baseIndent, indentUnit); } - // Check if the parameter is function + function formatCodeWithNesting(code, baseIndent, indentUnit) { + var result = ''; + var lines = []; + var current = ''; + var braceDepth = 0; + var inString = false; + var stringChar = ''; + + // First pass: break into logical lines, handling } else { pattern + for (var i = 0; i < code.length; i++) { + var char = code[i]; + + // Handle strings + if (!inString && (char === '"' || char === "'" || char === '`')) { + inString = true; + stringChar = char; + } else if (inString && char === stringChar && code[i-1] !== '\\') { + inString = false; + stringChar = ''; + } + + if (!inString) { + if (char === '{') { + current += char; + lines.push(current.trim()); + current = ''; + braceDepth++; + continue; + } else if (char === '}') { + if (current.trim()) { + lines.push(current.trim()); + } + braceDepth--; + + // Check for } else { pattern + var nextNonWhitespace = ''; + var j = i + 1; + while (j < code.length && /\s/.test(code[j])) { + j++; + } + if (j < code.length - 4 && code.substring(j, j + 4) === 'else') { + // Skip to after 'else' + j += 4; + while (j < code.length && /\s/.test(code[j])) { + j++; + } + if (j < code.length && code[j] === '{') { + // This is } else { + lines.push('} else {'); + i = j; // Skip to the { + braceDepth++; + current = ''; + continue; + } + } + + lines.push('}'); + current = ''; + continue; + } else if (char === ';') { + current += char; + lines.push(current.trim()); + current = ''; + continue; + } + } + + current += char; + } + + // Add any remaining content + if (current.trim()) { + lines.push(current.trim()); + } + + // Second pass: apply proper indentation + var currentDepth = 2; // Start at depth 2 for function bodies (object has 1, function has 2) + for (var k = 0; k < lines.length; k++) { + var line = lines[k].trim(); + if (!line) continue; + + // Adjust depth for closing braces + if (line === '}' || line.startsWith('}')) { + currentDepth--; + } + + // Apply indentation + result += indentUnit.repeat(currentDepth) + line; + + // Add newline except for last line + if (k < lines.length - 1) { + result += '\n'; + } + + // Adjust depth for opening braces + if (line.endsWith('{')) { + currentDepth++; + } + + // Add semicolon if missing (except for braces) + if (!line.endsWith(';') && !line.endsWith('{') && line !== '}' && !line.startsWith('}')) { + result = result.replace(/([^;}])$/, '$1;'); + } + } + + return result; + } // Check if the parameter is function if (options.ignoreFunction && typeof obj === "function") { obj = undefined; } @@ -261,6 +495,6 @@ module.exports = function serialize(obj, options) { var fn = functions[valueIndex]; - return serializeFunc(fn); + return serializeFunc(fn, options); }); } diff --git a/test/unit/serialize.js b/test/unit/serialize.js index 62c0eee..697ab9c 100644 --- a/test/unit/serialize.js +++ b/test/unit/serialize.js @@ -567,6 +567,70 @@ describe('serialize( obj )', function () { '{"num":123,"str":"str"}' ); }); + + it('should apply `space` option to function bodies (Issue #195)', function () { + // Test compact function without space option + var objWithFunction = { + isSupported: function({filepath}){const basename=require('path').basename(filepath);return basename===".env"||basename.startsWith(".env.")} + }; + + var withoutSpace = serialize(objWithFunction); + strictEqual(withoutSpace, '{"isSupported":function({filepath}){const basename=require(\'path\').basename(filepath);return basename===".env"||basename.startsWith(".env.")}}'); + + // Test function body should be formatted with space: 2 + var withSpace2 = serialize(objWithFunction, { space: 2 }); + var expected = '{\n "isSupported": function({filepath}) {\n const basename = require(\'path\').basename(filepath);\n return basename === ".env" || basename.startsWith(".env.");\n }\n}'; + strictEqual(withSpace2, expected); + + // Test function body should be formatted with space: 4 + var withSpace4 = serialize(objWithFunction, { space: 4 }); + var expectedSpace4 = '{\n "isSupported": function({filepath}) {\n const basename = require(\'path\').basename(filepath);\n return basename === ".env" || basename.startsWith(".env.");\n }\n}'; + strictEqual(withSpace4, expectedSpace4); + }); + + it('should apply `space` option to named function bodies', function () { + var objWithNamedFunction = { + process: function processData(data) {const result=data.map(x=>x*2);if(result.length>0){return result.filter(x=>x>10);}return [];} + }; + + var withSpace2 = serialize(objWithNamedFunction, { space: 2 }); + var expected = '{\n "process": function processData(data) {\n const result = data.map(x => x * 2);\n if (result.length > 0) {\n return result.filter(x => x > 10);\n }\n return [];\n }\n}'; + strictEqual(withSpace2, expected); + }); + + it('should apply `space` option to arrow function bodies', function () { + var objWithArrowFunction = { + transform: (x)=>{const doubled=x*2;if(doubled>10){return doubled;}return 0;} + }; + + var withSpace2 = serialize(objWithArrowFunction, { space: 2 }); + var expected = '{\n "transform": (x) => {\n const doubled = x * 2;\n if (doubled > 10) {\n return doubled;\n }\n return 0;\n }\n}'; + strictEqual(withSpace2, expected); + }); + + it('should apply `space` option to multiple functions in same object', function () { + var objWithMultipleFunctions = { + fn1: function(){return 1;}, + fn2: ()=>{return 2;}, + fn3: function named(){return 3;} + }; + + var withSpace2 = serialize(objWithMultipleFunctions, { space: 2 }); + var expected = '{\n "fn1": function() {\n return 1;\n },\n "fn2": () => {\n return 2;\n },\n "fn3": function named() {\n return 3;\n }\n}'; + strictEqual(withSpace2, expected); + }); + + it('should handle edge cases with space option and functions', function () { + // Test with string space option + var objWithFunction = { fn: function(){return true;} }; + var withStringSpace = serialize(objWithFunction, { space: ' ' }); + var expected = '{\n "fn": function() {\n return true;\n }\n}'; + strictEqual(withStringSpace, expected); + + // Test with no space (should not format function bodies) + var withoutSpaceOption = serialize(objWithFunction); + strictEqual(withoutSpaceOption, '{"fn":function(){return true;}}'); + }); }); describe('backwards-compatability', function () { From 44b4073b43780490193170415a2b23cf33366399 Mon Sep 17 00:00:00 2001 From: djunehor Date: Mon, 10 Nov 2025 18:13:26 +0100 Subject: [PATCH 2/2] feat: implement minimal space option for function bodies with 40-line solution addressing complexity concerns (#195) --- index.js | 326 ++++++++++++++----------------------------------------- 1 file changed, 79 insertions(+), 247 deletions(-) diff --git a/index.js b/index.js index 1c9b725..ea05ea4 100644 --- a/index.js +++ b/index.js @@ -139,276 +139,108 @@ module.exports = function serialize(obj, options) { } function serializeFunc(fn, options) { - var serializedFn = fn.toString(); - if (IS_NATIVE_CODE_REGEXP.test(serializedFn)) { - throw new TypeError('Serializing native function: ' + fn.name); - } - - // If no space option, return original behavior - if (!options || !options.space) { - // pure functions, example: {key: function() {}} - if(IS_PURE_FUNCTION.test(serializedFn)) { - return serializedFn; + var serializedFn = fn.toString(); + if (IS_NATIVE_CODE_REGEXP.test(serializedFn)) { + throw new TypeError('Serializing native function: ' + fn.name); } - // arrow functions, example: arg1 => arg1+5 - if(IS_ARROW_FUNCTION.test(serializedFn)) { - return serializedFn; - } + // If no space option, use original behavior + if (!options || !options.space) { + // pure functions, example: {key: function() {}} + if(IS_PURE_FUNCTION.test(serializedFn)) { + return serializedFn; + } - var argsStartsAt = serializedFn.indexOf('('); - var def = serializedFn.substr(0, argsStartsAt) - .trim() - .split(' ') - .filter(function(val) { return val.length > 0 }); + // arrow functions, example: arg1 => arg1+5 + if(IS_ARROW_FUNCTION.test(serializedFn)) { + return serializedFn; + } - var nonReservedSymbols = def.filter(function(val) { - return RESERVED_SYMBOLS.indexOf(val) === -1 - }); + var argsStartsAt = serializedFn.indexOf('('); + var def = serializedFn.substr(0, argsStartsAt) + .trim() + .split(' ') + .filter(function(val) { return val.length > 0 }); + + var nonReservedSymbols = def.filter(function(val) { + return RESERVED_SYMBOLS.indexOf(val) === -1 + }); + + // enhanced literal objects, example: {key() {}} + if(nonReservedSymbols.length > 0) { + return (def.indexOf('async') > -1 ? 'async ' : '') + 'function' + + (def.join('').indexOf('*') > -1 ? '*' : '') + + serializedFn.substr(argsStartsAt); + } - // enhanced literal objects, example: {key() {}} - if(nonReservedSymbols.length > 0) { - return (def.indexOf('async') > -1 ? 'async ' : '') + 'function' - + (def.join('').indexOf('*') > -1 ? '*' : '') - + serializedFn.substr(argsStartsAt); + // arrow functions + return serializedFn; } - // arrow functions - return serializedFn; - } - - // Format function body with space option - return formatFunctionWithSpace(serializedFn, options.space); + // Format function with space option - much simpler approach + return formatFunctionWithSpace(serializedFn, options.space); } function formatFunctionWithSpace(serializedFn, space) { - // Determine indentation unit - var indentUnit; - if (typeof space === 'number') { - indentUnit = ' '.repeat(space); - } else if (typeof space === 'string') { - indentUnit = space; - } else { - return serializedFn; // fallback to original - } - - // Find the function body opening brace (not parameter destructuring braces) - var bodyStartBraceIndex = -1; - var parenDepth = 0; - var braceDepth = 0; - - for (var i = 0; i < serializedFn.length; i++) { - var char = serializedFn[i]; - if (char === '(') { - parenDepth++; - } else if (char === ')') { - parenDepth--; - // After closing the parameter list, the next { is the function body - if (parenDepth === 0) { - for (var j = i + 1; j < serializedFn.length; j++) { - if (serializedFn[j] === '{') { - bodyStartBraceIndex = j; - break; - } else if (serializedFn[j] !== ' ' && serializedFn[j] !== '=' && serializedFn[j] !== '>') { - // Non-space/arrow character before brace, not a function body brace - break; - } - } - break; - } - } - } - - var closeBraceIndex = serializedFn.lastIndexOf('}'); - - if (bodyStartBraceIndex === -1 || closeBraceIndex === -1 || bodyStartBraceIndex >= closeBraceIndex) { - return serializedFn; // No function body braces found, return original - } - - var signature = serializedFn.substring(0, bodyStartBraceIndex).trim(); - var body = serializedFn.substring(bodyStartBraceIndex + 1, closeBraceIndex).trim(); - - // Clean up signature: ensure proper spacing - // For arrow functions, add space around => - if (signature.includes('=>')) { - signature = signature.replace(/\s*=>\s*/, ' => '); - } - - // Ensure space before opening brace - if (!signature.endsWith(' ')) { - signature += ' '; - } - - // If body is empty, format minimally - if (!body) { - return signature + '{\n' + indentUnit.repeat(2) + '}'; - } - - // Format the function body with proper indentation and spacing - var formattedBody = formatSimpleFunctionBody(body, indentUnit); - - // Ensure we don't double-add closing braces - var lines = formattedBody.split('\n'); - var lastNonEmptyIndex = lines.length - 1; - while (lastNonEmptyIndex >= 0 && !lines[lastNonEmptyIndex].trim()) { - lastNonEmptyIndex--; - } - - if (lastNonEmptyIndex >= 0 && lines[lastNonEmptyIndex].trim() === '}') { - // Remove the last closing brace line - lines.splice(lastNonEmptyIndex, 1); - formattedBody = lines.join('\n'); - } - - return signature + '{\n' + formattedBody + '\n' + indentUnit + '}'; - } - - function formatSimpleFunctionBody(body, indentUnit) { - // Enhanced function body formatter that handles nested structures - var baseIndent = indentUnit.repeat(2); // Functions are already inside objects, so depth 2 - - // First, add spaces around operators and keywords, being careful about arrow functions - var formatted = body - // Protect arrow functions from being split - .replace(/=>/g, '___ARROW___') - // Clean up multiple spaces first - .replace(/\s+/g, ' ') - // Add spaces around operators (but not === or !==) - .replace(/([^=!<>])\s*=\s*([^=])/g, '$1 = $2') - .replace(/([^=])\s*===\s*([^=])/g, '$1 === $2') - .replace(/([^!])\s*!==\s*([^=])/g, '$1 !== $2') - .replace(/([^|])\s*\|\|\s*([^|])/g, '$1 || $2') - .replace(/([^&])\s*&&\s*([^&])/g, '$1 && $2') - // Add spaces around arithmetic operators - .replace(/([^\s*])\s*\*\s*([^\s*])/g, '$1 * $2') - .replace(/([^\s+])\s*\+\s*([^\s+])/g, '$1 + $2') - .replace(/([^\s-])\s*-\s*([^\s-])/g, '$1 - $2') - .replace(/([^\s/])\s*\/\s*([^\s/])/g, '$1 / $2') - // Add spaces around comparison operators - .replace(/([^\s>])\s*>\s*([^\s>=])/g, '$1 > $2') - .replace(/([^\s<])\s*<\s*([^\s<=])/g, '$1 < $2') - .replace(/\s*>=\s*(?![>])/g, ' >= ') - .replace(/\s*<=\s*(?![<])/g, ' <= ') - // Add spaces after commas - .replace(/,(?!\s)/g, ', ') - // Add space after control keywords and before braces - .replace(/\b(if|for|while)\s*\(/g, '$1 (') - .replace(/\)\s*\{/g, ') {') - .replace(/\belse\s*\{/g, 'else {') - .replace(/\breturn\s+([^\s])/g, 'return $1') - // Restore arrow functions - .replace(/___ARROW___/g, ' => '); - - // Parse and format the statements with proper line breaks and nesting - return formatCodeWithNesting(formatted, baseIndent, indentUnit); - } - - function formatCodeWithNesting(code, baseIndent, indentUnit) { - var result = ''; - var lines = []; - var current = ''; - var braceDepth = 0; - var inString = false; - var stringChar = ''; - - // First pass: break into logical lines, handling } else { pattern - for (var i = 0; i < code.length; i++) { - var char = code[i]; + // Determine indent string + var indent = typeof space === 'number' ? ' '.repeat(space) : (space || ' '); + var functionIndent = indent.repeat(2); // Functions are at depth 2 (inside object) - // Handle strings - if (!inString && (char === '"' || char === "'" || char === '`')) { - inString = true; - stringChar = char; - } else if (inString && char === stringChar && code[i-1] !== '\\') { - inString = false; - stringChar = ''; - } + // Find function body bounds - need to find the { that's after the parameter list + var parenDepth = 0; + var bodyStart = -1; - if (!inString) { - if (char === '{') { - current += char; - lines.push(current.trim()); - current = ''; - braceDepth++; - continue; - } else if (char === '}') { - if (current.trim()) { - lines.push(current.trim()); - } - braceDepth--; - - // Check for } else { pattern - var nextNonWhitespace = ''; - var j = i + 1; - while (j < code.length && /\s/.test(code[j])) { - j++; - } - if (j < code.length - 4 && code.substring(j, j + 4) === 'else') { - // Skip to after 'else' - j += 4; - while (j < code.length && /\s/.test(code[j])) { - j++; - } - if (j < code.length && code[j] === '{') { - // This is } else { - lines.push('} else {'); - i = j; // Skip to the { - braceDepth++; - current = ''; - continue; - } + for (var i = 0; i < serializedFn.length; i++) { + var char = serializedFn[i]; + if (char === '(') { + parenDepth++; + } else if (char === ')') { + parenDepth--; + } else if (char === '{' && parenDepth === 0) { + // This is a brace outside of parentheses, likely the function body + bodyStart = i; + break; } - - lines.push('}'); - current = ''; - continue; - } else if (char === ';') { - current += char; - lines.push(current.trim()); - current = ''; - continue; - } } - current += char; - } - - // Add any remaining content - if (current.trim()) { - lines.push(current.trim()); - } - - // Second pass: apply proper indentation - var currentDepth = 2; // Start at depth 2 for function bodies (object has 1, function has 2) - for (var k = 0; k < lines.length; k++) { - var line = lines[k].trim(); - if (!line) continue; + var bodyEnd = serializedFn.lastIndexOf('}'); - // Adjust depth for closing braces - if (line === '}' || line.startsWith('}')) { - currentDepth--; + if (bodyStart === -1 || bodyEnd === -1 || bodyStart >= bodyEnd) { + return serializedFn; // No function body found } - // Apply indentation - result += indentUnit.repeat(currentDepth) + line; + var signature = serializedFn.substring(0, bodyStart).trim(); + var body = serializedFn.substring(bodyStart + 1, bodyEnd).trim(); - // Add newline except for last line - if (k < lines.length - 1) { - result += '\n'; + // Clean up signature spacing for arrow functions + if (signature.includes('=>')) { + signature = signature.replace(/\s*=>\s*/, ' => '); } - // Adjust depth for opening braces - if (line.endsWith('{')) { - currentDepth++; + // Handle empty body + if (!body) { + return signature + ' {\n' + functionIndent + '\n' + indent + '}'; } - // Add semicolon if missing (except for braces) - if (!line.endsWith(';') && !line.endsWith('{') && line !== '}' && !line.startsWith('}')) { - result = result.replace(/([^;}])$/, '$1;'); - } - } - - return result; + // Minimal formatting: split by semicolons and add basic spacing + var statements = body.split(';').filter(function(s) { return s.trim(); }); + var formattedStatements = statements.map(function(stmt) { + var trimmed = stmt.trim(); + + // Basic operator spacing (minimal set to avoid complexity) + trimmed = trimmed + .replace(/===(?!=)/g, ' === ') + .replace(/!==(?!=)/g, ' !== ') + .replace(/([^=])=([^=])/g, '$1 = $2') + .replace(/\|\|/g, ' || ') + .replace(/&&/g, ' && ') + .replace(/,(?!\s)/g, ', ') + .replace(/\s+/g, ' '); + + return functionIndent + trimmed + (trimmed ? ';' : ''); + }); + + return signature + ' {\n' + formattedStatements.join('\n') + '\n' + indent + '}'; } // Check if the parameter is function if (options.ignoreFunction && typeof obj === "function") { obj = undefined;