diff --git a/packages/api/src/codegen/languages/typescript/index.ts b/packages/api/src/codegen/languages/typescript/index.ts index 2b0c8d288..d0dee3c41 100644 --- a/packages/api/src/codegen/languages/typescript/index.ts +++ b/packages/api/src/codegen/languages/typescript/index.ts @@ -80,8 +80,10 @@ function handleExecFailure(err: Error, opts: InstallerOptions = {}) { throw err; } -async function detectPackageManager(installDir: string) { - const pm = await preferredPM(installDir); +async function detectPackageManager() { + const projectDir = Storage.getProjectDir(); + + const pm = await preferredPM(projectDir); if (pm) { return pm.name; } @@ -162,35 +164,50 @@ export default class TSGenerator extends CodeGenerator { // eslint-disable-next-line class-methods-use-this async install(storage: Storage, opts: InstallerOptions = {}): Promise { const installDir = storage.getIdentifierStorageDir(); - const packageManager = await detectPackageManager(installDir); + const packageManager = await detectPackageManager(); + + const handleError = (err: Error, options: InstallerOptions): void => { + // If `npm install` throws this error it always happens **after** our dependencies have been + // installed and is an annoying quirk that sometimes occurs when installing a package within + // our workspace as we're creating a circular dependency on `@readme/api-core`. + if ( + process.env.NODE_ENV === 'test' && + err.message.includes("npm ERR! Cannot set properties of null (setting 'dev')") + ) { + (options.logger ? options.logger : logger)("npm threw an error but we're ignoring it"); + return; + } - const installCommand = ['install', '--save', opts.dryRun ? '--dry-run' : ''].filter(Boolean); + handleExecFailure(err, options); + }; - // This will install the installed SDK as a dependency within the current working directory, - // adding `@api/` as a dependency there so you can load it with - // `require('@api/)`. - return execa(packageManager, [...installCommand, installDir].filter(Boolean)) - .then(res => handleExecSuccess(res, opts)) - .catch(err => { - // If `npm install` throws this error it always happens **after** our dependencies have been - // installed and is an annoying quirk that sometimes occurs when installing a package within - // our workspace as we're creating a circular dependency on `@readme/api-core`. - if ( - process.env.NODE_ENV === 'test' && - err.message.includes("npm ERR! Cannot set properties of null (setting 'dev')") - ) { - (opts.logger ? opts.logger : logger)("npm threw an error but we're ignoring it"); - return; - } + if (packageManager === 'yarn') { + const installCommand = ['add', opts.dryRun ? '--dry-run' : ''].filter(Boolean); + const installSubPackagesCommand = ['install', opts.dryRun ? '--dry-run' : ''].filter(Boolean); + + try { + const subPackagesResult = await execa(packageManager, [...installSubPackagesCommand], { cwd: installDir }); + handleExecSuccess(subPackagesResult, opts); + const packageResult = await execa(packageManager, [...installCommand, installDir]); + handleExecSuccess(packageResult, opts); + } catch (err) { + handleError(err, opts); + } + } else { + const installCommand = ['install', opts.dryRun ? '--dry-run' : ''].filter(Boolean); - handleExecFailure(err, opts); - }); + try { + const result = await execa(packageManager, [...installCommand, installDir]); + handleExecSuccess(result, opts); + } catch (err) { + handleError(err, opts); + } + } } static async uninstall(storage: Storage, opts: InstallerOptions = {}): Promise { const pkgName = storage.getPackageName() as string; - const installDir = storage.getIdentifierStorageDir(); - const packageManager = await detectPackageManager(installDir); + const packageManager = await detectPackageManager(); const args = ['uninstall', pkgName, opts.dryRun ? '--dry-run' : ''].filter(Boolean); return execa(packageManager, args) @@ -620,6 +637,7 @@ dist/ // If the version that's in `info.version` isn't compatible with semver NPM won't be able to // handle it properly so we need to fallback to something it can. pkgVersion = semver.coerce('0.0.0') as SemVer; + logger(`Warning: OpenAPI info.version is missing or invalid. Defaulting to ${pkgVersion.version}`); } const tsupOptions: Options = { diff --git a/packages/api/src/storage.ts b/packages/api/src/storage.ts index 7d091a7e0..56f6457bd 100644 --- a/packages/api/src/storage.ts +++ b/packages/api/src/storage.ts @@ -79,6 +79,21 @@ export default class Storage { fs.mkdirSync(Storage.getAPIsDir(), { recursive: true }); } + /** + * Retrieves the project's root directory path. + * + * If a storage directory has not been explicitly set, this method will default it to + * the current working directory. It then returns the directory name of the storage path. + * + */ + static getProjectDir() { + if (!Storage.dir) { + Storage.setStorageDir(); + } + + return path.dirname(Storage.dir); + } + /** * Reset the state of the entire storage system. *