From b4ba327eddeb1d7312d1fd1778c807fb84b912e5 Mon Sep 17 00:00:00 2001 From: I Ketut Sandiarsa Date: Wed, 28 Dec 2022 08:00:47 +0800 Subject: [PATCH] fix(reflect): Fix issue when reflect function inside object --- packages/reflect/src/parser.ts | 37 ++++++++++++------- .../__snapshots__/function.spec.ts.snap | 19 ++++++++++ tests/behavior/reflect/function.spec.ts | 9 +++++ 3 files changed, 51 insertions(+), 14 deletions(-) diff --git a/packages/reflect/src/parser.ts b/packages/reflect/src/parser.ts index 5a9df8fa..53b9a903 100644 --- a/packages/reflect/src/parser.ts +++ b/packages/reflect/src/parser.ts @@ -58,29 +58,38 @@ function getNamesFromAst(nodes: any[]) { return nodes.map(x => getName(x)).filter((x): x is RootNode => !!x) } -function getCode(fn: Class | Function) { - const syntaxReplacement = [ - { pattern: /^class(\s*)extends\s*BaseClass\s*{\s*}/gm, replacement: "class DynamicClass extends Parent {}" }, - { pattern: /^class(\s*){\s*}/gm, replacement: "class DynamicClass {}" }, - ] +function refineCode(fn: Class | Function, functionOnly = false) { + // some code may detected as invalid code, so its need to be fixed before parsed by acorn const code = fn.toString() - const exclude = syntaxReplacement.find(x => code.search(x.pattern) > -1) - if (exclude) { - return exclude.replacement - } - else - return code.replace("[native code]", "") + + // for class created dynamically using reflect.create() + if(code.search(/^class(\s*)extends\s*BaseClass\s*{\s*}/gm) > -1) + return "class DynamicClass extends Parent {}" + + // for class created using reflect.class() but without base class + if(code.search(/^class(\s*){\s*}/gm) > -1) + return "class DynamicClass {}" + + // in case function inside object, it will cause error + // example + // const obj = { fn(par1) {} } + // reflect(obj.fn) + if(functionOnly && code.search(/^([A-z0-9]+)\s*\((?:[^)(]+|\((?:[^)(]+|\([^)(]*\))*\))*\)/gm) > -1) + return `function ${code}`; + + // for the rest code, sometime its contain [native code], just remove it + return code.replace("[native code]", "") } function getMethodParameters(fn: Class, method: string) { - const body = getCode(fn) + const body = refineCode(fn) const ast = parse(body, { ecmaVersion: 2020 }) const ctor = getNode(ast, x => x.type === "MethodDefinition" && x.kind === "method" && x.key.name === method) return getNamesFromAst(ctor ? (ctor as any).value.params : []) } function getConstructorParameters(fn: Class) { - const body = getCode(fn) + const body = refineCode(fn) const ast = parse(body, { ecmaVersion: 2020 }) const ctor = getNode(ast, x => x.type === "MethodDefinition" && x.kind === "constructor") return getNamesFromAst(ctor ? (ctor as any).value.params : []) @@ -88,7 +97,7 @@ function getConstructorParameters(fn: Class) { function getFunctionParameters(fn: Function) { try { - const body = getCode(fn) + const body = refineCode(fn, true) const ast: any = parse(body, { ecmaVersion: 2020 }) const expBody = ast.body[0] if (expBody.type === "FunctionDeclaration") diff --git a/tests/behavior/reflect/__snapshots__/function.spec.ts.snap b/tests/behavior/reflect/__snapshots__/function.spec.ts.snap index 5f08945e..041ad3de 100644 --- a/tests/behavior/reflect/__snapshots__/function.spec.ts.snap +++ b/tests/behavior/reflect/__snapshots__/function.spec.ts.snap @@ -22,6 +22,25 @@ Object { } `; +exports[`Reflect function Should able to reflect function inside object 1`] = ` +Object { + "kind": "Function", + "name": "fn", + "parameters": Array [ + Object { + "decorators": Array [], + "fields": "par1", + "index": 0, + "kind": "Parameter", + "name": "par1", + "type": undefined, + "typeClassification": undefined, + }, + ], + "returnType": undefined, +} +`; + exports[`Reflect function Should able to reflect function parameter names 1`] = ` Object { "kind": "Function", diff --git a/tests/behavior/reflect/function.spec.ts b/tests/behavior/reflect/function.spec.ts index eb9618dd..82eff236 100644 --- a/tests/behavior/reflect/function.spec.ts +++ b/tests/behavior/reflect/function.spec.ts @@ -26,6 +26,14 @@ describe("Reflect function", () => { const meta = reflect(myFunction) expect(meta).toMatchSnapshot() }) + + it("Should able to reflect function inside object", () => { + const obj = { + fn(par1:string) {} + } + const meta = reflect(obj.fn) + expect(meta).toMatchSnapshot() + }) }) describe("Reflect lambda function", () => { @@ -53,4 +61,5 @@ describe("Reflect lambda function", () => { const meta = reflect(myFunction) expect(meta).toMatchSnapshot() }) + }) \ No newline at end of file