diff --git a/index.js b/index.js index 8ddc89b..c3ae087 100644 --- a/index.js +++ b/index.js @@ -15,8 +15,9 @@ var PLACE_HOLDER_REGEXP = new RegExp('(\\\\)?"@__(F|R|D|M|S|A|U|I|B|L)-' + UID + // call could start past a `[native code]` match and wrongly report a native // function as safe to serialize. var IS_NATIVE_CODE_REGEXP = /\{\s*\[native code\]\s*\}/; -var IS_PURE_FUNCTION = /function.*?\(/; -var IS_ARROW_FUNCTION = /.*?=>.*?/; +// Classify the outer function, not a declaration or arrow inside its body. +var IS_PURE_FUNCTION = /^(?:async(?:\s|\/\*[\s\S]*?\*\/|\/\/[^\n]*\n)+)?function\b/; +var IS_ARROW_FUNCTION = /^(?:async(?:\s|\/\*[\s\S]*?\*\/|\/\/[^\n]*\n)+)?(?:\([\s\S]*?\)|[^\s()]+)\s*=>/; var UNSAFE_CHARS_REGEXP = /[<>\/\u2028\u2029]/g; // Matches a script end tag (case-insensitive) for XSS protection: either a // literal ``, or a bare ` value; + return nested({ value: 42 }); + } + }; + const output = eval('(' + serialize(input) + ')'); + strictEqual(output.method(), 42); + }); + + it('round trips a method containing a function declaration', function () { + const input = { + method() { + function nested() { return 42; } + return nested(); + } + }; + const output = eval('(' + serialize(input) + ')'); + strictEqual(output.method(), 42); + }); + + it('preserves async methods containing arrows', async function () { + const input = { + async method() { + const nested = () => 42; + return nested(); + } + }; + const output = eval('(' + serialize(input) + ')'); + strictEqual(await output.method(), 42); + }); + + it('preserves generator methods containing arrows', function () { + const input = { + *method() { + const nested = () => 42; + yield nested(); + } + }; + const output = eval('(' + serialize(input) + ')'); + strictEqual(output.method().next().value, 42); + }); +}); + + +describe('outer function signatures', function () { + it('preserves async signatures with comments instead of spaces', async function () { + for (const fn of [async/**/function () { return 42; }, async/**/() => 42]) { + const output = eval('(' + serialize(fn) + ')'); + strictEqual(await output(), 42); + strictEqual(output.constructor.name, 'AsyncFunction'); + } + }); + + it('preserves arrows whose default arguments contain functions', function () { + const fn = (callback = function () { return 42; }) => callback(); + const output = eval('(' + serialize(fn) + ')'); + strictEqual(output(), 42); + }); +});