diff --git a/.yarn/versions/93ce6177.yml b/.yarn/versions/93ce6177.yml new file mode 100644 index 000000000000..14ccf038783a --- /dev/null +++ b/.yarn/versions/93ce6177.yml @@ -0,0 +1,23 @@ +releases: + "@yarnpkg/cli": patch + "@yarnpkg/plugin-dlx": patch + +declined: + - "@yarnpkg/plugin-compat" + - "@yarnpkg/plugin-constraints" + - "@yarnpkg/plugin-essentials" + - "@yarnpkg/plugin-init" + - "@yarnpkg/plugin-interactive-tools" + - "@yarnpkg/plugin-nm" + - "@yarnpkg/plugin-npm-cli" + - "@yarnpkg/plugin-pack" + - "@yarnpkg/plugin-patch" + - "@yarnpkg/plugin-pnp" + - "@yarnpkg/plugin-pnpm" + - "@yarnpkg/plugin-stage" + - "@yarnpkg/plugin-typescript" + - "@yarnpkg/plugin-version" + - "@yarnpkg/plugin-workspace-tools" + - "@yarnpkg/builder" + - "@yarnpkg/core" + - "@yarnpkg/doctor" diff --git a/packages/acceptance-tests/pkg-tests-specs/sources/commands/create.test.js b/packages/acceptance-tests/pkg-tests-specs/sources/commands/create.test.js deleted file mode 100644 index 2fd76197238a..000000000000 --- a/packages/acceptance-tests/pkg-tests-specs/sources/commands/create.test.js +++ /dev/null @@ -1,22 +0,0 @@ -const {join} = require(`path`); -const {xfs} = require(`@yarnpkg/fslib`); - -describe(`Commands`, () => { - describe(`create`, () => { - test( - `it should generate \`hello.txt\` correctly using a starter-kit-package`, - makeTemporaryEnv({}, async ({path, run, source}) => { - await run(`create`, `-q`, `test-app`); - expect(xfs.readFileSync(join(path, `hello.txt`), `utf8`)).toEqual(`Hello World`); - }), - ); - - test( - `it should generate \`hello.txt\` correctly using a scoped starter-kit-package`, - makeTemporaryEnv({}, async ({path, run, source}) => { - await run(`create`, `-q`, `@scoped/test-app`); - expect(xfs.readFileSync(join(path, `hello.txt`), `utf8`)).toEqual(`Hello World`); - }), - ); - }); -}); diff --git a/packages/acceptance-tests/pkg-tests-specs/sources/commands/create.test.ts b/packages/acceptance-tests/pkg-tests-specs/sources/commands/create.test.ts new file mode 100644 index 000000000000..d93ef01a9af6 --- /dev/null +++ b/packages/acceptance-tests/pkg-tests-specs/sources/commands/create.test.ts @@ -0,0 +1,51 @@ +import {xfs, ppath, Filename} from '@yarnpkg/fslib'; + +describe(`Commands`, () => { + describe(`create`, () => { + test( + `it should generate \`hello.txt\` correctly using a starter-kit-package`, + makeTemporaryEnv({}, async ({path, run, source}) => { + await run(`create`, `-q`, `test-app`); + expect(xfs.readFileSync(ppath.join(path, `hello.txt` as Filename), `utf8`)).toEqual(`Hello World`); + }), + ); + + test( + `it should generate \`hello.txt\` correctly using a scoped starter-kit-package`, + makeTemporaryEnv({}, async ({path, run, source}) => { + await run(`create`, `-q`, `@scoped/test-app`); + expect(xfs.readFileSync(ppath.join(path, `hello.txt` as Filename), `utf8`)).toEqual(`Hello World`); + }), + ); + + test( + `it should treat '@scope' as '@scope/create'`, + makeTemporaryEnv({}, async ({path, run, source}) => { + await expect(run(`create`, `@404-scope`)).rejects.toMatchObject({ + code: 1, + stdout: expect.stringContaining(`@404-scope/create@unknown`), + }); + }), + ); + + test( + `it should treat '@scope@next' as '@scope/create@next'`, + makeTemporaryEnv({}, async ({path, run, source}) => { + await expect(run(`create`, `@404-scope@next`)).rejects.toMatchObject({ + code: 1, + stdout: expect.stringContaining(`@404-scope/create@npm:next`), + }); + }), + ); + + test( + `it should treat '@scope/app' as '@scope/create-app'`, + makeTemporaryEnv({}, async ({path, run, source}) => { + await expect(run(`create`, `@404-scope/app`)).rejects.toMatchObject({ + code: 1, + stdout: expect.stringContaining(`@404-scope/create-app@unknown`), + }); + }), + ); + }); +}); diff --git a/packages/plugin-dlx/sources/commands/create.ts b/packages/plugin-dlx/sources/commands/create.ts index 157bbc83068f..ae65992ca7f4 100644 --- a/packages/plugin-dlx/sources/commands/create.ts +++ b/packages/plugin-dlx/sources/commands/create.ts @@ -1,6 +1,6 @@ -import {BaseCommand} from '@yarnpkg/cli'; -import {structUtils} from '@yarnpkg/core'; -import {Option} from 'clipanion'; +import {BaseCommand} from '@yarnpkg/cli'; +import {Ident, structUtils} from '@yarnpkg/core'; +import {Option} from 'clipanion'; // eslint-disable-next-line arca/no-default-export export default class CreateCommand extends BaseCommand { @@ -26,9 +26,24 @@ export default class CreateCommand extends BaseCommand { if (this.quiet) flags.push(`--quiet`); - const ident = structUtils.parseIdent(this.command); - const modified = structUtils.makeIdent(ident.scope, `create-${ident.name}`); + const descriptor = structUtils.parseDescriptor(this.command); - return this.cli.run([`dlx`, ...flags, structUtils.stringifyIdent(modified), ...this.args]); + let modifiedIdent: Ident; + + if (descriptor.scope) + // @foo/app -> @foo/create-app + modifiedIdent = structUtils.makeIdent(descriptor.scope, `create-${descriptor.name}`); + else if (descriptor.name.startsWith(`@`)) + // @foo -> @foo/create + modifiedIdent = structUtils.makeIdent(descriptor.name.substring(1), `create`); + else + // foo -> create-foo + modifiedIdent = structUtils.makeIdent(null, `create-${descriptor.name}`); + + let finalDescriptorString = structUtils.stringifyIdent(modifiedIdent); + if (descriptor.range !== `unknown`) + finalDescriptorString += `@${descriptor.range}`; + + return this.cli.run([`dlx`, ...flags, finalDescriptorString, ...this.args]); } }