Skip to content
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

feat: auto-detect package manager in create fuels #3503

Merged
merged 13 commits into from
Jan 2, 2025
5 changes: 5 additions & 0 deletions .changeset/lazy-ears-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-fuels": patch
---

feat: auto-detect package manager in `create fuels`
6 changes: 3 additions & 3 deletions apps/docs/src/guide/creating-a-fuel-dapp/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ The first step is to run the command:
::: code-group

```sh-vue [npm]
npm create fuels@{{fuels}} -- --npm
npm create fuels@{{fuels}}
```

```sh-vue [pnpm]
pnpm create fuels@{{fuels}} --pnpm
pnpm create fuels@{{fuels}}
```

```sh-vue [bun]
bunx --bun create-fuels@{{fuels}} --bun
bun create fuels@{{fuels}}
```

:::
Expand Down
18 changes: 3 additions & 15 deletions apps/docs/src/guide/creating-a-fuel-dapp/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ The `npm create fuels` command has several command-line options that you can use
::: code-group

```sh-vue [pnpm]
pnpm create fuels@{{fuels}} --pnpm [project-name] [options]
pnpm create fuels@{{fuels}} [project-name] [options]
```

```sh-vue [npm]
npm create fuels@{{fuels}} -- --npm [project-name] [options]
npm create fuels@{{fuels}} -- [project-name] [options]
```

```sh-vue [bun]
bunx --bun create-fuels@{{fuels}} --bun [project-name] [options]
bun create fuels@{{fuels}} [project-name] [options]
```

:::
Expand All @@ -27,18 +27,6 @@ bunx --bun create-fuels@{{fuels}} --bun [project-name] [options]

Specifies the template to use for your project. The available templates are: `vite` and `nextjs`. The default template is `vite`.

## `--pnpm`

Notifies the tool to use pnpm as the package manager to install the necessary dependencies.

## `--npm`

Notifies the tool to use npm as the package manager to install the necessary dependencies.

## `--bun`

Notifies the tool to use bun as the package manager to install the necessary dependencies.

## `--verbose`

Enables verbose logging. Useful when debugging issues with the tool.
Expand Down
2 changes: 1 addition & 1 deletion packages/create-fuels/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const runScaffoldCli = async ({
let projectPath = program.args[0] ?? (await promptForProjectPath());

const verboseEnabled = opts.verbose ?? false;
const packageManager = getPackageManager(opts);
const packageManager = getPackageManager();

if (!process.env.VITEST) {
await tryInstallFuelUp(verboseEnabled);
Expand Down
49 changes: 24 additions & 25 deletions packages/create-fuels/src/lib/getPackageManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,6 @@ import { mockLogger } from '../../test/utils/mockLogger';
import type { PackageManager } from './getPackageManager';
import { availablePackageManagers, getPackageManager, packageMangers } from './getPackageManager';

const mockAllDeps = () => {
const { warn } = mockLogger();

return {
warn,
};
};

const installScenarios: [PackageManager, string][] = [
['pnpm', 'pnpm install'],
['npm', 'npm install'],
Expand All @@ -24,17 +16,30 @@ const runScenarios: [PackageManager, string][] = [
['bun', 'bun run fuels:dev'],
];

const mockAllDeps = () => {
const { warn } = mockLogger();

return {
warn,
};
};

/**
* @group node
*/
describe('getPackageManager', () => {
beforeEach(() => {
delete process.env.npm_config_user_agent;
});

it.each(availablePackageManagers)(
`should get the correct package manager for %s`,
(packageManager: PackageManager) => {
const expectedPackageManager = packageMangers[packageManager];
const opts = { [packageManager]: true };

const result = getPackageManager(opts);
process.env.npm_config_user_agent = packageManager;

const result = getPackageManager();

expect(result).toEqual(expectedPackageManager);
}
Expand All @@ -43,7 +48,9 @@ describe('getPackageManager', () => {
it.each(installScenarios)(
'should have the correct install commands',
(packageManager, expectedInstallCommand) => {
const command = getPackageManager({ [packageManager]: true });
process.env.npm_config_user_agent = packageManager;

const command = getPackageManager();

const install = command.install;

Expand All @@ -54,32 +61,24 @@ describe('getPackageManager', () => {
it.each(runScenarios)(
'should have the correct run commands',
(packageManager, expectedRunCommand) => {
const command = getPackageManager({ [packageManager]: true });
process.env.npm_config_user_agent = packageManager;

const command = getPackageManager();

const run = command.run(runCommand);

expect(run).toEqual(expectedRunCommand);
}
);

it('should warn the user if more than one package manager selected', () => {
const { warn } = mockAllDeps();
const opts = { pnpm: true, npm: true };

getPackageManager(opts);

expect(warn).toBeCalledWith('More than one package manager was selected.');
});

it('should default to npm if no package manager is selected', () => {
it('should default to npm', () => {
const packageManager = 'npm';
const expectedPackageManager = packageMangers[packageManager];
const { warn } = mockAllDeps();
Dhaiwat10 marked this conversation as resolved.
Show resolved Hide resolved
const opts = {};

const result = getPackageManager(opts);
const result = getPackageManager();

expect(warn).not.toBeCalled();
expect(result).toEqual(expectedPackageManager);
expect(warn).toHaveBeenCalledWith(`This package manager is not supported. Using npm instead.`);
});
});
46 changes: 29 additions & 17 deletions packages/create-fuels/src/lib/getPackageManager.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { warn } from '../utils/logger';

import type { ProgramOptions } from './setupProgram';

export const availablePackageManagers = ['pnpm', 'npm', 'bun'] as const;
export type PackageManager = (typeof availablePackageManagers)[number];

Expand All @@ -10,39 +8,53 @@ const runnableApplicator =
(command: string = '') =>
`${commandPrefix} ${command}`;

export const packageMangers = {
export const packageMangers: Record<
PackageManager,
{
install: string;
run: (command: string) => string;
name: PackageManager;
}
> = {
pnpm: {
install: 'pnpm install',
run: runnableApplicator('pnpm'),
name: 'pnpm',
},
npm: {
install: 'npm install',
run: runnableApplicator('npm run'),
name: 'npm',
},
bun: {
install: 'bun install',
run: runnableApplicator('bun run'),
name: 'bun',
},
} as const;

export const getPackageManager = (opts: ProgramOptions) => {
const packageMangerOpts = {
pnpm: opts.pnpm,
npm: opts.npm,
bun: opts.bun,
};
export function getUserPkgManager(): PackageManager {
const userAgent = process.env.npm_config_user_agent || '';

const cliChosenPackageManagerSelected = Object.entries(packageMangerOpts)
petertonysmith94 marked this conversation as resolved.
Show resolved Hide resolved
.filter(([, v]) => v)
.map(([k]) => k) as PackageManager[];
if (userAgent.startsWith(packageMangers.pnpm.name)) {
return packageMangers.pnpm.name;
}

let packageManager: PackageManager | undefined = cliChosenPackageManagerSelected[0];
if (cliChosenPackageManagerSelected.length > 1) {
warn('More than one package manager was selected.');
if (userAgent.startsWith(packageMangers.bun.name)) {
return packageMangers.bun.name;
}

danielbate marked this conversation as resolved.
Show resolved Hide resolved
if (!packageManager) {
packageManager = 'npm'; // default to npm if the user has not specified a package manager (eg. --pnpm, --bun)
if (userAgent.startsWith(packageMangers.npm.name)) {
return packageMangers.npm.name;
}

warn(`This package manager is not supported. Using npm instead.`);

return packageMangers.npm.name;
}

export const getPackageManager = () => {
const packageManager = getUserPkgManager();

return packageMangers[packageManager];
};
14 changes: 1 addition & 13 deletions packages/create-fuels/src/lib/setupProgram.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,8 @@ import { setupProgram } from './setupProgram';
describe('setupProgram', () => {
test('setupProgram takes in args properly', () => {
const program = setupProgram();
program.parse([
'',
'',
'test-project-name',
'--template',
'nextjs',
'--pnpm',
'--npm',
'--bun',
]);
program.parse(['', '', 'test-project-name', '--template', 'nextjs']);
expect(program.args[0]).toBe('test-project-name');
expect(program.opts().pnpm).toBe(true);
expect(program.opts().npm).toBe(true);
expect(program.opts().bun).toBe(true);
expect(program.opts().install).toBe(true);
expect(program.opts().template).toBe('nextjs');
});
Expand Down
6 changes: 0 additions & 6 deletions packages/create-fuels/src/lib/setupProgram.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ export interface ProgramOptions {
contract?: boolean;
predicate?: boolean;
script?: boolean;
pnpm?: boolean;
npm?: boolean;
bun?: boolean;
verbose?: boolean;
install?: boolean;
template?: Template;
Expand All @@ -22,9 +19,6 @@ export const setupProgram = () => {
const program = new Command(packageJson.name)
.version(packageJson.version)
.arguments('[projectDirectory]')
.option('--pnpm', 'Use pnpm to install dependencies')
.option('--npm', 'Use npm to install dependencies')
.option('--bun', 'Use bun to install dependencies')
.option('--verbose', 'Enable verbose logging')
.option('--no-install', 'Do not install dependencies')
.option('--template <template>', 'Specify a template to use', defaultTemplate)
Expand Down
5 changes: 4 additions & 1 deletion packages/create-fuels/test/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,10 @@ describe('CLI', { timeout: 15_000 }, () => {
});

test('should rewrite for the appropriate package manager', async () => {
process.env.npm_config_user_agent = 'bun';

const args = generateArgv({
projectName: paths.projectRoot,
packageManager: 'bun',
template: paths.templateName,
});

Expand All @@ -114,6 +115,8 @@ describe('CLI', { timeout: 15_000 }, () => {
const readme = readFileSync(readmePath, 'utf-8');
expect(readme).toContain('bun run fuels:dev');
expect(readme).toContain('bun run dev');

delete process.env.npm_config_user_agent;
});

test('create-fuels reports an error if the project directory already exists', async () => {
Expand Down
1 change: 0 additions & 1 deletion packages/create-fuels/test/utils/generateArgs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ export const generateArgs = ({
args.push(`--template`);
args.push(template);
}
args.push(`--${packageManager}`);
args.push(`--no-install`);
return args;
};
Expand Down
Loading