Skip to content

feat: yarn, bun, pnpm support #1027

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

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
63 changes: 44 additions & 19 deletions packages/api/src/codegen/languages/typescript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,27 +166,50 @@ export default class TSGenerator extends CodeGenerator {
const installDir = storage.getIdentifierStorageDir();
const packageManager = await detectPackageManager();

const installCommand = ['install', '--save', opts.dryRun ? '--dry-run' : ''].filter(Boolean);
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;
}

// This will install the installed SDK as a dependency within the current working directory,
// adding `@api/<sdk identifier>` as a dependency there so you can load it with
// `require('@api/<sdk identifier>)`.
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;
handleExecFailure(err, options);
};

const getInstallCommands = (pm: string, dryRun?: boolean): string[] => {
const baseCommand = pm === 'npm' ? 'install' : 'add';
return [baseCommand, dryRun ? '--dry-run' : ''].filter(Boolean);
};

try {
if (!['npm', 'bun'].includes(packageManager)) {
// install internal dependencies first
// npm and bun will handle this automatically
await execa(packageManager, ['install'], { cwd: installDir });
}

const installCommand = getInstallCommands(packageManager, opts.dryRun);
const result = await execa(packageManager, [...installCommand, installDir]);

if (['yarn', 'bun'].includes(packageManager)) {
// If we're using Yarn or Bun, we need to additionally link the package after installation
// so that it is correctly recognized as a local package.
const packageName = storage.getPackageName();
if (packageName) {
await execa(packageManager, ['link'], { cwd: installDir });
await execa(packageManager, ['link', packageName]);
}
}

handleExecFailure(err, opts);
});
handleExecSuccess(result, opts);
} catch (err) {
handleError(err, opts);
}
}

static async uninstall(storage: Storage, opts: InstallerOptions = {}): Promise<void> {
Expand All @@ -206,12 +229,13 @@ export default class TSGenerator extends CodeGenerator {
// eslint-disable-next-line class-methods-use-this
async compile(storage: Storage, opts: InstallerOptions = {}): Promise<void> {
const installDir = storage.getIdentifierStorageDir();
const packageManager = await detectPackageManager();

await execa('npm', ['pkg', 'set', 'scripts.prepare=tsup'], { cwd: installDir })
.then(res => handleExecSuccess(res, opts))
.catch(err => handleExecFailure(err, opts));

await execa('npm', ['run', 'prepare'], {
await execa(packageManager, ['run', 'prepare'], {
cwd: installDir,
})
.then(res => handleExecSuccess(res, opts))
Expand Down Expand Up @@ -621,6 +645,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 = {
Expand Down