From 7e735152170c1c6cb2d1438fe640b9e27b568edd Mon Sep 17 00:00:00 2001 From: Jake Son Date: Sun, 7 May 2023 16:13:35 +0900 Subject: [PATCH] fix: resolve file path in require --- lib/plugin/utils/plugin-utils.ts | 70 +++++++++++++++++++ .../http-interface.visitor.spec.ts.snap | 12 ++-- lib/plugin/visitors/http-interface.visitor.ts | 35 +++++++--- 3 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 lib/plugin/utils/plugin-utils.ts diff --git a/lib/plugin/utils/plugin-utils.ts b/lib/plugin/utils/plugin-utils.ts new file mode 100644 index 0000000..fdaf94c --- /dev/null +++ b/lib/plugin/utils/plugin-utils.ts @@ -0,0 +1,70 @@ +/* c8 ignore start */ +/** + * codes are from '@nestjs/swagger' package + */ + +import { isAbsolute, posix } from 'path'; + +export function replaceImportPath( + typeReference: string, + fileName: string, +): string | undefined { + if (!typeReference.includes('import')) { + return typeReference; + } + let importPath = /\("([^)]).+(")/.exec(typeReference)?.[0]; + if (importPath == null) { + return undefined; + } + importPath = convertPath(importPath); + importPath = importPath.slice(2, importPath.length - 1); + + try { + if (isAbsolute(importPath)) { + throw new Error(); + } + require.resolve(importPath); + return typeReference.replace('import', 'require'); + } catch (_error) { + let relativePath = posix.relative(posix.dirname(fileName), importPath); + relativePath = relativePath[0] !== '.' ? './' + relativePath : relativePath; + + const nodeModulesText = 'node_modules'; + const nodeModulePos = relativePath.indexOf(nodeModulesText); + if (nodeModulePos >= 0) { + relativePath = relativePath.slice( + nodeModulePos + nodeModulesText.length + 1, // slash + ); + + const typesText = '@types'; + const typesPos = relativePath.indexOf(typesText); + if (typesPos >= 0) { + relativePath = relativePath.slice( + typesPos + typesText.length + 1, // slash + ); + } + + const indexText = '/index'; + const indexPos = relativePath.indexOf(indexText); + if (indexPos >= 0) { + relativePath = relativePath.slice(0, indexPos); + } + } + + typeReference = typeReference.replace(importPath, relativePath); + return typeReference.replace('import', 'require'); + } +} + +/** + * Converts Windows specific file paths to posix + * @param windowsPath + */ +function convertPath(windowsPath: string): string { + return windowsPath + .replace(/^\\\\\?\\/, '') + .replace(/\\/g, '/') + .replace(/\/\/+/g, '/'); +} + +/* c8 ignore end */ diff --git a/lib/plugin/visitors/__snapshots__/http-interface.visitor.spec.ts.snap b/lib/plugin/visitors/__snapshots__/http-interface.visitor.spec.ts.snap index a1250c5..e035805 100644 --- a/lib/plugin/visitors/__snapshots__/http-interface.visitor.spec.ts.snap +++ b/lib/plugin/visitors/__snapshots__/http-interface.visitor.spec.ts.snap @@ -3,7 +3,7 @@ exports[`HttpInterfaceVisitor > should handle array return type 1`] = ` "\\"use strict\\"; Object.defineProperty(exports, \\"__esModule\\", { value: true }); -const eager_module_1 = require(\\"@r2don/nest-http-interface\\"); +const r2don_http_module_1 = require(\\"@r2don/nest-http-interface\\"); const nest_http_interface_1 = require(\\"@r2don/nest-http-interface\\"); class ResponseClass { } @@ -20,15 +20,15 @@ let UserService = class UserService { }; __decorate([ (0, nest_http_interface_1.GetExchange)(), - eager_module_1.ResponseBody(ResponseClass) + r2don_http_module_1.ResponseBody(ResponseClass) ], UserService.prototype, \\"getUsers\\", null); __decorate([ (0, nest_http_interface_1.GetExchange)(), - eager_module_1.ResponseBody(ResponseClass) + r2don_http_module_1.ResponseBody(ResponseClass) ], UserService.prototype, \\"getUserList\\", null); __decorate([ (0, nest_http_interface_1.GetExchange)(), - eager_module_1.ResponseBody(ResponseClass) + r2don_http_module_1.ResponseBody(ResponseClass) ], UserService.prototype, \\"getUsersReadonly\\", null); UserService = __decorate([ (0, nest_http_interface_1.HttpInterface)() @@ -116,7 +116,7 @@ TextService = __decorate([ exports[`HttpInterfaceVisitor > should override plugin suffix option 1`] = ` "\\"use strict\\"; Object.defineProperty(exports, \\"__esModule\\", { value: true }); -const eager_module_1 = require(\\"@r2don/nest-http-interface\\"); +const r2don_http_module_1 = require(\\"@r2don/nest-http-interface\\"); const nest_http_interface_1 = require(\\"@r2don/nest-http-interface\\"); class ResponseClass { } @@ -127,7 +127,7 @@ let UserService = class UserService { }; __decorate([ (0, nest_http_interface_1.GraphQLExchange)(), - eager_module_1.ResponseBody(ResponseClass) + r2don_http_module_1.ResponseBody(ResponseClass) ], UserService.prototype, \\"getUser\\", null); UserService = __decorate([ (0, nest_http_interface_1.HttpInterface)() diff --git a/lib/plugin/visitors/http-interface.visitor.ts b/lib/plugin/visitors/http-interface.visitor.ts index 6aae1bb..87960af 100644 --- a/lib/plugin/visitors/http-interface.visitor.ts +++ b/lib/plugin/visitors/http-interface.visitor.ts @@ -11,6 +11,7 @@ import { GraphQLExchange, ResponseBody, } from '../../decorators'; +import { replaceImportPath } from '../utils/plugin-utils'; export class HttpInterfaceVisitor { HTTP_INTERFACE_DECORATOR = HttpInterface.name; @@ -26,7 +27,7 @@ export class HttpInterfaceVisitor { GraphQLExchange.name, ]; - libModuleAlias = 'eager_module_1'; + libModuleAlias = 'r2don_http_module_1'; libName = '@r2don/nest-http-interface'; importSet = new Set(); typeFormatFlag = @@ -45,7 +46,12 @@ export class HttpInterfaceVisitor { const typeChecker = program.getTypeChecker(); const visitNode = (node: ts.Node): ts.Node => { if (this.isHttpInterfaceClass(node)) { - return this.visitMethods(node, factory, typeChecker); + return this.visitMethods( + node, + factory, + typeChecker, + sourceFile.fileName, + ); } if (ts.isSourceFile(node)) { @@ -91,13 +97,19 @@ export class HttpInterfaceVisitor { node: ts.ClassDeclaration, factory: ts.NodeFactory, typeChecker: ts.TypeChecker, + hostFilename: string, ): ts.ClassDeclaration { const updatedMembers = node.members.map((member) => { if (!this.isExchangeMethod(member)) { return member; } - return this.appendResponseBodyDecorator(member, factory, typeChecker); + return this.appendResponseBodyDecorator( + member, + factory, + typeChecker, + hostFilename, + ); }); return factory.updateClassDeclaration( @@ -121,8 +133,13 @@ export class HttpInterfaceVisitor { node: ts.MethodDeclaration, factory: ts.NodeFactory, typeChecker: ts.TypeChecker, + hostFilename: string, ): ts.MethodDeclaration { - const returnType = this.getReturnTypeAsText(node, typeChecker); + const returnType = this.getReturnTypeAsText( + node, + typeChecker, + hostFilename, + ); if (returnType == null) { return node; @@ -157,6 +174,7 @@ export class HttpInterfaceVisitor { private getReturnTypeAsText( node: ts.MethodDeclaration, typeChecker: ts.TypeChecker, + hostFilename: string, ): string | undefined { if ( node.type == null || @@ -176,13 +194,14 @@ export class HttpInterfaceVisitor { return undefined; } - return typeChecker - .typeToString( + return replaceImportPath( + typeChecker.typeToString( typeChecker.getTypeAtLocation(innerType), undefined, this.typeFormatFlag, - ) - .replace('import', 'require'); + ), + hostFilename, + ); } private getInnerType(node?: ts.TypeNode): ts.TypeNode | undefined {