Skip to content

Commit

Permalink
feat(app-builder): support bun
Browse files Browse the repository at this point in the history
  • Loading branch information
EdieLemoine committed Sep 27, 2023
1 parent 3389f2f commit 8a1dd6d
Show file tree
Hide file tree
Showing 22 changed files with 319 additions and 103 deletions.
11 changes: 11 additions & 0 deletions .idea/runConfigurations/libs_admin_core.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 0 additions & 11 deletions .idea/runConfigurations/libs_frontend_admin_core.xml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {executeCommand} from '../../utils';
import {type ParsedEntry, type UpgradeSubContext} from './types';
import {parseGitHubUrl} from './parseGitHubUrl';
import {type ParsedEntry, type UpgradeSubContext} from '../types';
import {parseGitHubUrl} from '../parseGitHubUrl';
import {executeCommand} from '../../../utils';

export const getComposerPackageVersion = async (context: UpgradeSubContext): Promise<ParsedEntry[]> => {
const {config, packageName} = context;
Expand Down
2 changes: 2 additions & 0 deletions apps/app-builder/src/commands/upgrade/composer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './getComposerPackageVersion';
export * from './upgradeComposerPackage';
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {executeCommand} from '../../utils';
import {type UpgradeSubMethod} from './types';
import {type UpgradeSubMethod} from '../types';
import {executeCommand} from '../../../utils';
import {getComposerPackageVersion} from './getComposerPackageVersion';

export const upgradeComposerPackage: UpgradeSubMethod = async (context) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import {UpgradeMode} from './types';
export const determineUpgradeMode = (packageName: string): UpgradeMode => {
const isComposerPackage = /^\w+\/\w+$/.exec(packageName);

return isComposerPackage ? UpgradeMode.Composer : UpgradeMode.Yarn;
return isComposerPackage ? UpgradeMode.Composer : UpgradeMode.Node;
};
22 changes: 19 additions & 3 deletions apps/app-builder/src/commands/upgrade/getRepositoryUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import {executeCommand} from '../../utils';
import {type NpmInfo} from '../../types';
import {VerbosityLevel} from '../../constants';
import {type ParsedEntry, UpgradeMode, type UpgradeSubContext} from './types';
import {NodePackageManager, type ParsedEntry, UpgradeMode, type UpgradeSubContext} from './types';
import {parseGitHubUrl} from './parseGitHubUrl';

export const getRepositoryUrl = async (entry: ParsedEntry, context: UpgradeSubContext): Promise<undefined | string> => {
Expand All @@ -17,8 +17,24 @@ export const getRepositoryUrl = async (entry: ParsedEntry, context: UpgradeSubCo
const {config, mode} = context;

switch (mode) {
case UpgradeMode.Yarn:
const stdout = await executeCommand(context, config.yarnCommand, ['npm', 'info', entry.name, '--json'], {});
case UpgradeMode.Node:
let stdout: string;

switch (config.nodePackageManager) {
case NodePackageManager.Yarn:
stdout = await executeCommand(
context,
config.nodePackageManagerCommand,
['npm', 'info', entry.name, '--json'],
{},
);
break;

case NodePackageManager.Bun:
// TODO: Change this when bun has a command that can do this
stdout = await executeCommand(context, 'npm', ['info', entry.name, '--json'], {});
break;
}

if (!stdout) {
return;
Expand Down
24 changes: 0 additions & 24 deletions apps/app-builder/src/commands/upgrade/getYarnPackageVersion.ts

This file was deleted.

20 changes: 20 additions & 0 deletions apps/app-builder/src/commands/upgrade/node/executeNodeUpgrade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {NodePackageManager, type UpgradeSubContextWithLockfile} from '../types';
import {executeCommand} from '../../../utils';

export const executeNodeUpgrade = async (context: UpgradeSubContextWithLockfile): Promise<void> => {
const {config, packageName} = context;

const upgradeArgs = [];

switch (config.nodePackageManager) {
case NodePackageManager.Bun:
upgradeArgs.push('update', packageName);
break;

case NodePackageManager.Yarn:
upgradeArgs.push('up', packageName);
break;
}

await executeCommand(context, config.nodePackageManagerCommand, upgradeArgs);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {NodePackageManager, type UpgradeSubContext} from '../types';

export const getDefaultNodeLockfilePath = (context: UpgradeSubContext): string => {
switch (context.config.nodePackageManager) {
case NodePackageManager.Yarn:
return 'yarn.lock';

case NodePackageManager.Bun:
return 'bun.lockb';
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* eslint-disable max-nested-callbacks */
import {describe, expect, it, vi} from 'vitest';
import {toArray} from '@myparcel/ts-utils';
import {NodePackageManager, UpgradeMode, type UpgradeSubContextWithLockfile} from '../types';
import {createTestContext} from '../../../__tests__/createTestContext';
import {getNodePackageVersion} from './getNodePackageVersion';

const mockStdout = vi.fn(() => '');

// mock spawnSync
vi.mock('child_process', () => ({
spawnSync: () => ({
status: 0,
stdout: mockStdout(),
}),
}));

type TestInput = {
packageManager: NodePackageManager;
stdout: string | string[];
};

describe('getNodePackageVersion', () => {
it.each([
{
packageManager: NodePackageManager.Yarn,
stdout: [
'"@myparcel-eslint/eslint-config-node@npm:1.3.1"',
'"@myparcel-pdk/admin@npm:1.0.0-alpha.109"',
'"@myparcel-pdk/app-builder@npm:1.0.0-alpha.36"',
'"@myparcel-pdk/checkout@npm:1.0.0-alpha.67"',
'"@myparcel/ts-utils@npm:1.0.0"',
'"eslint@npm:8.0.0"',
],
},
{
packageManager: NodePackageManager.Bun,
stdout: [
'/path/to/project node_modules',
'├── @myparcel-eslint/[email protected]',
'├── @myparcel-pdk/[email protected]',
'├── @myparcel-pdk/[email protected]',
'├── @myparcel-pdk/[email protected]',
'├── @myparcel/[email protected]',
'└── [email protected]',
],
},
] satisfies TestInput[])(
'should return the correct package version for $packageManager',
async ({packageManager, stdout}) => {
const testContext = createTestContext();

const context = {
...testContext,
config: {
...testContext.config,
nodePackageManager: packageManager,
},
lockfilePath: 'yarn.lock',
mode: UpgradeMode.Node,
packageName: '@myparcel-pdk/*',
} satisfies UpgradeSubContextWithLockfile;

mockStdout.mockImplementationOnce(() => toArray(stdout).join('\n'));

const res = await getNodePackageVersion(context);

expect(res).toEqual([
{
name: '@myparcel-pdk/admin',
version: '1.0.0-alpha.109',
},
{
name: '@myparcel-pdk/app-builder',
version: '1.0.0-alpha.36',
},
{
name: '@myparcel-pdk/checkout',
version: '1.0.0-alpha.67',
},
]);
},
);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {NodePackageManager, type ParsedEntry, type UpgradeSubContextWithLockfile} from '../types';
import {getPackageEntriesForYarn} from './getPackageEntriesForYarn';
import {getPackageEntriesForBun} from './getPackageEntriesForBun';

export const getNodePackageVersion = async (context: UpgradeSubContextWithLockfile): Promise<ParsedEntry[]> => {
const {lockfilePath, packageName, config} = context;

const matches: ParsedEntry[] = [];

const resolvedPackageName = packageName.replace(/\*/g, '.*').replace(/\//g, '\\/');

switch (config.nodePackageManager) {
case NodePackageManager.Yarn:
matches.push(...(await getPackageEntriesForYarn(context, resolvedPackageName)));

break;

case NodePackageManager.Bun:
matches.push(...(await getPackageEntriesForBun(context, resolvedPackageName)));

break;
}

if (matches.length === 0) {
throw new Error(`Package ${packageName} not found in lockfile ${lockfilePath}`);
}

return matches;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import {type ParsedEntry, type UpgradeSubContextWithLockfile} from '../types';
import {executeCommand} from '../../../utils';

export async function getPackageEntriesForBun(
context: UpgradeSubContextWithLockfile,
resolvedPackageName: string,
): Promise<ParsedEntry[]> {
const content = await executeCommand(context, context.config.nodePackageManagerCommand, ['pm', 'ls', '--all']);

return content
.split('\n')
.filter((line) => line.includes('@') && new RegExp(resolvedPackageName).test(line))
.map((line) => {
const regex = new RegExp(`\\s(${resolvedPackageName})@(.*)`);

const [, name, version] = regex.exec(line) ?? [];

return {
name,
version,
};
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {type ParsedEntry, type UpgradeSubContextWithLockfile} from '../types';
import {executeCommand} from '../../../utils';

export async function getPackageEntriesForYarn(
context: UpgradeSubContextWithLockfile,
resolvedPackageName: string,
): Promise<ParsedEntry[]> {
const {config} = context;

const content = await executeCommand(context, config.nodePackageManagerCommand, [
'info',
'--all',
'--name-only',
'--json',
]);

return content
.split('\n')
.map((line) => line.replace(/^"(.*)"$/, '$1'))
.filter((line) => new RegExp(`^${resolvedPackageName}@npm:`).exec(line))
.map((line) => {
const [name, version] = line.split('@npm:');

return {name, version};
});
}
6 changes: 6 additions & 0 deletions apps/app-builder/src/commands/upgrade/node/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from './executeNodeUpgrade';
export * from './getDefaultNodeLockfilePath';
export * from './getNodePackageVersion';
export * from './getPackageEntriesForBun';
export * from './getPackageEntriesForYarn';
export * from './upgradeNodePackage';
20 changes: 20 additions & 0 deletions apps/app-builder/src/commands/upgrade/node/upgradeNodePackage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {type UpgradeSubMethod} from '../types';
import {getNodePackageVersion} from './getNodePackageVersion';
import {executeNodeUpgrade} from './executeNodeUpgrade';

export const upgradeNodePackage: UpgradeSubMethod = async (context) => {
const {args} = context;

const oldVersions = await getNodePackageVersion(context);

if (!args.dryRun) {
await executeNodeUpgrade(context);
}

const newVersions = await getNodePackageVersion(context);

return {
oldVersions,
newVersions,
};
};
23 changes: 8 additions & 15 deletions apps/app-builder/src/commands/upgrade/types.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,6 @@
import {type LiftoffEnv} from 'liftoff';
import {type Debugger} from 'debug';
import {type MakeOptional, type PromiseOr} from '@myparcel/ts-utils';
import {type CommandArgs, type ResolvedPdkBuilderConfig} from '../../types';

export type YarnLockfileEntry = [
string,
{
version: string;
resolution: string;
checksum: string;
languageName: string;
linkType: string;
},
];
import {type CommandArgs, type PdkDebugger, type ResolvedPdkBuilderConfig} from '../../types';

export interface ParsedEntry {
name: string;
Expand All @@ -37,7 +25,7 @@ export type InputUpgradeCommandArgs = MakeOptional<UpgradeCommandArgs, 'composer
export interface UpgradeSubContext {
args: UpgradeCommandArgs;
config: ResolvedPdkBuilderConfig;
debug: Debugger;
debug: PdkDebugger;
env: LiftoffEnv;
mode: UpgradeMode;
packageName: string;
Expand All @@ -54,7 +42,12 @@ export interface UpgradeSubResult {

export type UpgradeSubMethod = (context: UpgradeSubContextWithLockfile) => PromiseOr<UpgradeSubResult>;

export enum UpgradeMode {
export enum NodePackageManager {
Bun = 'bun',
Yarn = 'yarn',
}

export enum UpgradeMode {
Node = 'node',
Composer = 'composer',
}
Loading

0 comments on commit 8a1dd6d

Please sign in to comment.