Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(utils): serialize inline functions (#5051) #5580

Merged
merged 14 commits into from Apr 22, 2019
30 changes: 30 additions & 0 deletions packages/utils/src/serialize.js
@@ -1,7 +1,37 @@
import serialize from 'serialize-javascript'

export function normalizeFunctions(obj) {
if (typeof obj !== 'object' || Array.isArray(obj) || obj === null) {
return obj
}
for (const key in obj) {
if (key === '__proto__' || key === 'constructor') {
continue
}
const val = obj[key]
if (val !== null && typeof val === 'object' && !Array.isArray(obj)) {
obj[key] = normalizeFunctions(val)
}
if (typeof obj[key] === 'function') {
const asString = obj[key].toString()
const match = asString.match(/^([^{(]+)=>\s*(.*)/s)
if (match) {
const fullFunctionBody = match[2].match(/^{?(\s*return\s+)?(.*?)}?$/s)
let functionBody = fullFunctionBody[2].trim()
if (fullFunctionBody[1] || !match[2].trim().match(/^\s*{/s)) {
functionBody = `return ${functionBody}`
}
// eslint-disable-next-line no-new-func
obj[key] = new Function(...match[1].split(',').map(arg => arg.trim()), functionBody)
}
}
}
return obj
}

export function serializeFunction(func) {
let open = false
func = normalizeFunctions(func)
return serialize(func)
.replace(serializeFunction.assignmentRE, (_, spaces) => {
return `${spaces}: function (`
Expand Down
52 changes: 51 additions & 1 deletion packages/utils/test/serialize.test.js
@@ -1,6 +1,29 @@
import { serializeFunction } from '../src/serialize'
import { serializeFunction, normalizeFunctions } from '../src/serialize'

describe('util: serialize', () => {
test('should normalize arrow functions', () => {
const obj = {
// eslint-disable-next-line arrow-parens
fn1: foobar => {},
fn2: foobar => 1,
// eslint-disable-next-line arrow-parens
fn3: foobar => {
return 3
},
// eslint-disable-next-line arrow-parens
fn4: arg1 =>
2 * arg1
}
expect(normalizeFunctions(obj).fn1.toString())
.toEqual('function anonymous(foobar\n) {\n\n}')
expect(normalizeFunctions(obj).fn2.toString())
.toEqual('function anonymous(foobar\n) {\nreturn 1\n}')
expect(normalizeFunctions(obj).fn3.toString())
.toEqual('function anonymous(foobar\n) {\nreturn 3;\n}')
expect(normalizeFunctions(obj).fn4.toString())
.toEqual('function anonymous(arg1\n) {\nreturn 2 * arg1\n}')
})

test('should serialize normal function', () => {
const obj = {
fn: function () {}
Expand All @@ -22,6 +45,33 @@ describe('util: serialize', () => {
expect(serializeFunction(obj.fn)).toEqual('() => {}')
})

test('should serialize arrow function with ternary in parens', () => {
const obj = {
// eslint-disable-next-line arrow-parens
fn: foobar => (foobar ? 1 : 0)
}
expect(serializeFunction(obj.fn)).toEqual('foobar => foobar ? 1 : 0')
})

test('should serialize arrow function with single parameter', () => {
const obj = {
// eslint-disable-next-line arrow-parens
fn1: foobar => {},
fn2: foobar => 1,
// eslint-disable-next-line arrow-parens
fn3: foobar => {
return 3
},
// eslint-disable-next-line arrow-parens
fn4: arg1 =>
2 * arg1
}
expect(serializeFunction(obj.fn1)).toEqual('foobar => {}')
expect(serializeFunction(obj.fn2)).toEqual('foobar => 1')
expect(serializeFunction(obj.fn3)).toEqual('foobar => {\n return 3;\n }')
expect(serializeFunction(obj.fn4)).toEqual('arg1 => 2 * arg1')
})

test('should not replace custom scripts', () => {
const obj = {
fn() {
Expand Down