From 62b2f14cc50fbadd92dff54754ec77e2bcfba735 Mon Sep 17 00:00:00 2001 From: Aryaman Dhingra Date: Tue, 21 May 2024 11:38:22 -0400 Subject: [PATCH] feat(openapi-generator): make node_module resolving more generic by removing @bitgo prefix --- packages/openapi-generator/src/project.ts | 96 +++++++++---------- packages/openapi-generator/src/resolveInit.ts | 8 +- .../test/externalModule.test.ts | 14 +-- 3 files changed, 53 insertions(+), 65 deletions(-) diff --git a/packages/openapi-generator/src/project.ts b/packages/openapi-generator/src/project.ts index 96a8fe16..9aa98cda 100644 --- a/packages/openapi-generator/src/project.ts +++ b/packages/openapi-generator/src/project.ts @@ -54,12 +54,23 @@ export class Project { this.add(path, sourceFile); for (const sym of Object.values(sourceFile.symbols.imports)) { - const filePath = p.dirname(path); - const absImportPathE = this.resolve(filePath, sym.from); - if (E.isLeft(absImportPathE)) { - return absImportPathE; - } else if (!this.has(absImportPathE.right)) { - queue.push(absImportPathE.right); + if (!sym.from.startsWith('.')) { + // If we are not resolving a relative path, we need to resolve the entry point + const baseDir = p.dirname(sourceFile.path); + let entryPoint = this.resolveEntryPoint(baseDir, sym.from); + if (E.isLeft(entryPoint)) { + continue; + } else if (!this.has(entryPoint.right)) { + queue.push(entryPoint.right); + } + } else { + const filePath = p.dirname(path); + const absImportPathE = this.resolve(filePath, sym.from); + if (E.isLeft(absImportPathE)) { + return absImportPathE; + } else if (!this.has(absImportPathE.right)) { + queue.push(absImportPathE.right); + } } } for (const starExport of sourceFile.symbols.exportStarFiles) { @@ -80,66 +91,45 @@ export class Project { return await readFile(filename, 'utf8'); } - private resolvePath(path: string, basedir: string): E.Either { + resolveEntryPoint(basedir: string, library: string): E.Either { try { - const result = resolve.sync(path, { + const packageJson = resolve.sync(`${library}/package.json`, { basedir, - extensions: ['.ts', '.js', '.d.ts'], + extensions: ['.json'], }); + const packageInfo = JSON.parse(fs.readFileSync(packageJson, 'utf8')); - return E.right(result); - } catch (e: unknown) { - if (e instanceof Error && e.message) { - return E.left(e.message); + let typesEntryPoint = ''; + + if (packageInfo['types']) { + typesEntryPoint = packageInfo['types']; } - return E.left(JSON.stringify(e)); - } - } + if (packageInfo['typings']) { + typesEntryPoint = packageInfo['typings']; + } - private findSourceFileFromPackage(path: string): E.Either { - const mapName = path.replace('.js', '.js.map'); + if (!typesEntryPoint) { + return E.left(`Could not find types entry point for ${library}`); + } - if (fs.existsSync(mapName)) { - const mapJson = JSON.parse(fs.readFileSync(mapName, 'utf8')); - const dirName = p.dirname(path); - const source = mapJson.sources[0]; - const response = resolve.sync(source, { basedir: dirName }); - return E.right(response); + const entryPoint = resolve.sync(`${library}/${typesEntryPoint}`, { + basedir, + extensions: ['.ts', '.js'], + }); + return E.right(entryPoint); + } catch (err) { + return E.left(`Could not resolve entry point for ${library}: ${err}`); } - - return E.left('Map file not found for ' + path); } resolve(basedir: string, path: string): E.Either { - const BITGO_PREFIX = '@bitgo'; try { - let resolved = this.resolvePath(path, basedir); - if (E.isLeft(resolved)) { - // Could not resolve the path, try resolving in the types package - resolved = this.resolvePath('@types/' + path, basedir); - } - - // Types package wasn't found, return an error - if (E.isLeft(resolved)) { - return E.left('Could not resolve ' + path + ' from ' + basedir); - } - - const result = resolved.right; - - // If we are parsing an internal type package, we want to return the path to the source TS file - if (path.startsWith(BITGO_PREFIX)) { - return this.findSourceFileFromPackage(result); - } else { - // Else - find the declaration file and return it if it exists - const dTsName = result.replace('.js', '.d.ts'); - - if (fs.existsSync(dTsName)) { - return E.right(dTsName); - } - - return E.right(result); - } + const result = resolve.sync(path, { + basedir, + extensions: ['.ts', '.js'], + }); + return E.right(result); } catch (e: unknown) { if (e instanceof Error && e.message) { return E.left(e.message); diff --git a/packages/openapi-generator/src/resolveInit.ts b/packages/openapi-generator/src/resolveInit.ts index 243d4d8f..6c254b22 100644 --- a/packages/openapi-generator/src/resolveInit.ts +++ b/packages/openapi-generator/src/resolveInit.ts @@ -13,7 +13,13 @@ function resolveImportPath( sourceFile: SourceFile, path: string, ): E.Either { - const importPathE = project.resolve(dirname(sourceFile.path), path); + let importPathE; + if (path.startsWith('.')) { + importPathE = project.resolve(dirname(sourceFile.path), path); + } else { + importPathE = project.resolveEntryPoint(dirname(sourceFile.path), path); + } + if (E.isLeft(importPathE)) { return importPathE; } diff --git a/packages/openapi-generator/test/externalModule.test.ts b/packages/openapi-generator/test/externalModule.test.ts index 31ee1893..b7831c42 100644 --- a/packages/openapi-generator/test/externalModule.test.ts +++ b/packages/openapi-generator/test/externalModule.test.ts @@ -5,6 +5,7 @@ import * as p from 'path'; import { parsePlainInitializer, Project, type Schema } from '../src'; import { KNOWN_IMPORTS } from '../src/knownImports'; +// import { resolve } from 'node:path'; /** External library parsing test case * @@ -18,23 +19,16 @@ async function testCase( entryPoint: string, expected: Record>, expectedErrors: Record = {}, - parseErrorRegex: RegExp | undefined = undefined, ) { test(description, async () => { const project = new Project({}, KNOWN_IMPORTS); const entryPointPath = p.resolve(entryPoint); - const parsed = await project.parseEntryPoint(entryPointPath); - - if (parseErrorRegex !== undefined) { - assert(E.isLeft(parsed)); - assert(parseErrorRegex.test(parsed.left)); - return; - } + await project.parseEntryPoint(entryPointPath); for (const path of Object.keys(expected)) { const resolvedPath = p.resolve(path); const sourceFile = project.get(resolvedPath); - + if (sourceFile === undefined) { throw new Error(`Source file ${path} not found`); } @@ -193,7 +187,6 @@ testCase( 'test/sample-types/importPathError.ts', {}, {}, - /Could not resolve io-tsg from .*\/test\/sample-types\/node_modules\/@bitgo\/foobar3\/src/, ); testCase( @@ -201,5 +194,4 @@ testCase( 'test/sample-types/exportPathError.ts', {}, {}, - /Could not resolve .\/foobart from .*\/test\/sample-types\/node_modules\/@bitgo\/foobar6\/src/, );