feat: add the pnpm create command (#3829)

This commit is contained in:
Lev Chelyadinov
2021-10-10 01:57:28 +03:00
committed by GitHub
parent 4027a3c691
commit c83488d01f
5 changed files with 151 additions and 1 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/plugin-commands-script-runners": minor
"pnpm": minor
---
New command added: create. `pnpm create` is similar to `yarn create`.

View File

@@ -0,0 +1,76 @@
import renderHelp from 'render-help'
import { docsUrl } from '@pnpm/cli-utils'
import PnpmError from '@pnpm/error'
import * as dlx from './dlx'
export const commandNames = ['create']
export async function handler (_opts: Record<string, never>, params: string[]) {
const [packageName, ...packageArgs] = params
if (packageName === undefined) {
throw new PnpmError(
'MISSING_ARGS',
'Missing the template package name.\n' +
'The correct usage is `pnpm create <name>` ' +
'with <name> substituted for a package name.'
)
}
const createPackageName = convertToCreateName(packageName)
return dlx.handler({}, [createPackageName, ...packageArgs])
}
export function rcOptionsTypes () {
return {}
}
export function cliOptionsTypes () {
return {}
}
export function help () {
return renderHelp({
description: 'Creates a project from a `create-*` starter kit.',
url: docsUrl('create'),
usages: [
'pnpm create <name>',
'pnpm create <name-without-create>',
'pnpm create <@scope>',
],
})
}
const CREATE_PREFIX = 'create-'
/**
* Defines the npm's algorithm for resolving a package name
* for create-* packages.
*
* Example:
* - `foo` -> `create-foo`
* - `@usr/foo` -> `@usr/create-foo`
* - `@usr` -> `@usr/create`
*
* For more info, see https://docs.npmjs.com/cli/v7/commands/npm-init#description
*/
function convertToCreateName (packageName: string) {
if (packageName.startsWith('@')) {
const [scope, scopedPackage = ''] = packageName.split('/')
if (scopedPackage === '') {
return `${scope}/create`
} else {
return `${scope}/${ensureCreatePrefixed(scopedPackage)}`
}
} else {
return ensureCreatePrefixed(packageName)
}
}
function ensureCreatePrefixed (packageName: string) {
if (packageName.startsWith(CREATE_PREFIX)) {
return packageName
} else {
return `${CREATE_PREFIX}${packageName}`
}
}

View File

@@ -1,3 +1,4 @@
import * as create from './create'
import * as dlx from './dlx'
import * as exec from './exec'
import * as restart from './restart'
@@ -9,4 +10,4 @@ const test = {
..._test,
}
export { dlx, exec, restart, run, test }
export { create, dlx, exec, restart, run, test }

View File

@@ -0,0 +1,65 @@
import PnpmError from '@pnpm/error'
import { create, dlx } from '../src'
jest.mock('../src/dlx', () => ({ handler: jest.fn() }))
beforeEach((dlx.handler as jest.Mock).mockClear)
it('throws an error if called without arguments', async () => {
await expect(create.handler({}, [])).rejects.toThrow(PnpmError)
expect(dlx.handler).not.toBeCalled()
})
it(
'appends `create-` to an unscoped package that doesn\'t start with `create-`',
async () => {
await create.handler({}, ['some-app'])
expect(dlx.handler).toBeCalledWith({}, ['create-some-app'])
await create.handler({}, ['create_no_dash'])
expect(dlx.handler).toBeCalledWith({}, ['create-create_no_dash'])
}
)
it(
'does not append `create-` to an unscoped package that starts with `create-`',
async () => {
await create.handler({}, ['create-some-app'])
expect(dlx.handler).toBeCalledWith({}, ['create-some-app'])
await create.handler({}, ['create-'])
expect(dlx.handler).toBeCalledWith({}, ['create-'])
}
)
it(
'appends `create-` to a scoped package that doesn\'t start with `create-`',
async () => {
await create.handler({}, ['@scope/some-app'])
expect(dlx.handler).toBeCalledWith({}, ['@scope/create-some-app'])
await create.handler({}, ['@scope/create_no_dash'])
expect(dlx.handler).toBeCalledWith({}, ['@scope/create-create_no_dash'])
}
)
it(
'does not append `create-` to a scoped package that starts with `create-`',
async () => {
await create.handler({}, ['@scope/create-some-app'])
expect(dlx.handler).toBeCalledWith({}, ['@scope/create-some-app'])
await create.handler({}, ['@scope/create-'])
expect(dlx.handler).toBeCalledWith({}, ['@scope/create-'])
}
)
it('infers a package name from a plain scope', async () => {
await create.handler({}, ['@scope'])
expect(dlx.handler).toBeCalledWith({}, ['@scope/create'])
})
it('passes the remaining arguments to `dlx`', async () => {
await create.handler({}, ['some-app', 'directory/', '--silent'])
expect(dlx.handler).toBeCalledWith({}, ['create-some-app', 'directory/', '--silent'])
})

View File

@@ -9,6 +9,7 @@ import { outdated } from '@pnpm/plugin-commands-outdated'
import { pack, publish } from '@pnpm/plugin-commands-publishing'
import { rebuild } from '@pnpm/plugin-commands-rebuild'
import {
create,
dlx,
exec,
restart,
@@ -64,6 +65,7 @@ const commands: Array<{
add,
audit,
bin,
create,
dlx,
env,
exec,