From fabf053def546dc8877aea85e092b687e2aaff32 Mon Sep 17 00:00:00 2001 From: Armano Date: Fri, 24 Feb 2023 17:59:15 +0100 Subject: [PATCH] feat(npm-to-yarn): add support for PnPm and custom converters (#8690) Co-authored-by: Ben Gubler Co-authored-by: sebastienlorber --- .../README.md | 27 ++ .../package.json | 2 +- .../__tests__/__fixtures__/conversion-test.md | 16 ++ .../__snapshots__/index.test.ts.snap | 272 +++++++++++++++++- .../src/__tests__/index.test.ts | 31 +- .../src/index.ts | 56 ++-- .../markdown-features-code-blocks.mdx | 15 +- website/docs/installation.mdx | 32 +-- .../docs/migration/migration-automated.mdx | 6 +- website/docs/migration/migration-manual.mdx | 8 +- yarn.lock | 8 +- 11 files changed, 409 insertions(+), 64 deletions(-) create mode 100644 packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__fixtures__/conversion-test.md diff --git a/packages/docusaurus-remark-plugin-npm2yarn/README.md b/packages/docusaurus-remark-plugin-npm2yarn/README.md index 5fc6a1705e15..055ca12d30cd 100644 --- a/packages/docusaurus-remark-plugin-npm2yarn/README.md +++ b/packages/docusaurus-remark-plugin-npm2yarn/README.md @@ -63,3 +63,30 @@ module.exports = { | Property | Type | Default | Description | | --- | --- | --- | --- | | `sync` | `boolean` | `false` | Syncing tab choices (Yarn and npm). See https://docusaurus.io/docs/markdown-features/#syncing-tab-choices for details. | +| `converters` | `array` | `'yarn'`, `'pnpm'` | The list of converters to use. The order of the converters is important, as the first converter will be used as the default choice. | + +## Custom converters + +In case you want to convert npm commands to something else than `yarn` or `pnpm`, you can use custom converters: + +```ts +type CustomConverter = [name: string, cb: (npmCode: string) => string]; +``` + +```ts +{ + remarkPlugins: [ + [ + require('@docusaurus/remark-plugin-npm2yarn'), + { + sync: true, + converters: [ + 'yarn', + 'pnpm', + ['Turbo', (code) => code.replace(/npm/g, 'turbo')], + ], + }, + ], + ]; +} +``` diff --git a/packages/docusaurus-remark-plugin-npm2yarn/package.json b/packages/docusaurus-remark-plugin-npm2yarn/package.json index cb7b5030a90d..188c5858f174 100644 --- a/packages/docusaurus-remark-plugin-npm2yarn/package.json +++ b/packages/docusaurus-remark-plugin-npm2yarn/package.json @@ -17,7 +17,7 @@ }, "license": "MIT", "dependencies": { - "npm-to-yarn": "^1.2.1", + "npm-to-yarn": "^2.0.0", "tslib": "^2.4.1", "unist-util-visit": "^2.0.3" }, diff --git a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__fixtures__/conversion-test.md b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__fixtures__/conversion-test.md new file mode 100644 index 000000000000..b0bf7f08a52b --- /dev/null +++ b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__fixtures__/conversion-test.md @@ -0,0 +1,16 @@ +```bash npm2yarn +npm run xxx -- --arg +``` + +```bash npm2yarn +npm install package +``` + +```bash npm2yarn +npm remove package-name +``` + +```bash npm2yarn +npm init docusaurus +npm init docusaurus@latest my-website classic +``` diff --git a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__snapshots__/index.test.ts.snap b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__snapshots__/index.test.ts.snap index b8e77588d05b..393470b31218 100644 --- a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__snapshots__/index.test.ts.snap +++ b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__snapshots__/index.test.ts.snap @@ -6,6 +6,7 @@ exports[`npm2yarn plugin does not re-import tabs components when already importe import TabItem from '@theme/TabItem'; + \`\`\`bash @@ -13,19 +14,30 @@ import TabItem from '@theme/TabItem'; \`\`\` + \`\`\`bash - $ yarn add --global docusaurus + $ yarn global add docusaurus +\`\`\` + + + + + +\`\`\`bash + $ pnpm add --global docusaurus \`\`\` + " `; exports[`npm2yarn plugin does not re-import tabs components when already imported below 1`] = ` " + \`\`\`bash @@ -33,13 +45,23 @@ exports[`npm2yarn plugin does not re-import tabs components when already importe \`\`\` + \`\`\`bash - $ yarn add --global docusaurus + $ yarn global add docusaurus \`\`\` + + + +\`\`\`bash + $ pnpm add --global docusaurus +\`\`\` + + + import Tabs from '@theme/Tabs'; @@ -63,11 +85,102 @@ npm install --save docusaurus-plugin-name " `; +exports[`npm2yarn plugin work with custom converter 1`] = ` +"import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Installing a plugin + +A plugin is usually a npm package, so you install them like other npm packages using npm. + + + + + +\`\`\`bash +npm install --save docusaurus-plugin-name +\`\`\` + + + + + +\`\`\`bash +turbo install --save docusaurus-plugin-name +\`\`\` + + + + +" +`; + +exports[`npm2yarn plugin work with pnpm converter 1`] = ` +"import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Installing a plugin + +A plugin is usually a npm package, so you install them like other npm packages using npm. + + + + + +\`\`\`bash +npm install --save docusaurus-plugin-name +\`\`\` + + + + + +\`\`\`bash +pnpm add docusaurus-plugin-name +\`\`\` + + + + +" +`; + +exports[`npm2yarn plugin work with yarn converter 1`] = ` +"import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Installing a plugin + +A plugin is usually a npm package, so you install them like other npm packages using npm. + + + + + +\`\`\`bash +npm install --save docusaurus-plugin-name +\`\`\` + + + + + +\`\`\`bash +yarn add docusaurus-plugin-name +\`\`\` + + + + +" +`; + exports[`npm2yarn plugin works on installation file 1`] = ` "import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; + \`\`\`bash @@ -75,13 +188,23 @@ import TabItem from '@theme/TabItem'; \`\`\` + \`\`\`bash - $ yarn add --global docusaurus + $ yarn global add docusaurus +\`\`\` + + + + + +\`\`\`bash + $ pnpm add --global docusaurus \`\`\` + " `; @@ -95,6 +218,7 @@ import TabItem from '@theme/TabItem'; A plugin is usually a npm package, so you install them like other npm packages using npm. + \`\`\`bash @@ -102,6 +226,7 @@ npm install --save docusaurus-plugin-name \`\`\` + \`\`\`bash @@ -109,6 +234,136 @@ yarn add docusaurus-plugin-name \`\`\` + + + +\`\`\`bash +pnpm add docusaurus-plugin-name +\`\`\` + + + + +" +`; + +exports[`npm2yarn plugin works with common commands 1`] = ` +"import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + + + + +\`\`\`bash +npm run xxx -- --arg +\`\`\` + + + + + +\`\`\`bash +yarn xxx --arg +\`\`\` + + + + + +\`\`\`bash +pnpm run xxx -- --arg +\`\`\` + + + + + + + + + +\`\`\`bash +npm install package +\`\`\` + + + + + +\`\`\`bash +yarn add package +\`\`\` + + + + + +\`\`\`bash +pnpm add package +\`\`\` + + + + + + + + + +\`\`\`bash +npm remove package-name +\`\`\` + + + + + +\`\`\`bash +yarn remove package-name +\`\`\` + + + + + +\`\`\`bash +pnpm remove package-name +\`\`\` + + + + + + + + + +\`\`\`bash +npm init docusaurus +npm init docusaurus@latest my-website classic +\`\`\` + + + + + +\`\`\`bash +yarn create docusaurus +yarn create docusaurus@latest my-website classic +\`\`\` + + + + + +\`\`\`bash +pnpm create docusaurus +pnpm create docusaurus@latest my-website classic +\`\`\` + + + " `; @@ -122,6 +377,7 @@ import TabItem from '@theme/TabItem'; A plugin is usually a npm package, so you install them like other npm packages using npm. + \`\`\`bash @@ -129,6 +385,7 @@ npm install --save docusaurus-plugin-name \`\`\` + \`\`\`bash @@ -136,6 +393,15 @@ yarn add docusaurus-plugin-name \`\`\` + + + +\`\`\`bash +pnpm add docusaurus-plugin-name +\`\`\` + + + " `; diff --git a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts index 2ed961332463..84afcc23ae29 100644 --- a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts +++ b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts @@ -11,7 +11,10 @@ import mdx from 'remark-mdx'; import remark from 'remark'; import npm2yarn from '../index'; -const processFixture = async (name: string, options?: {sync?: boolean}) => { +const processFixture = async ( + name: string, + options?: Parameters[0], +) => { const filePath = path.join(__dirname, '__fixtures__', `${name}.md`); const file = await vfile.read(filePath); const result = await remark().use(mdx).use(npm2yarn, options).process(file); @@ -32,6 +35,12 @@ describe('npm2yarn plugin', () => { expect(result).toMatchSnapshot(); }); + it('works with common commands', async () => { + const result = await processFixture('conversion-test', {sync: true}); + + expect(result).toMatchSnapshot(); + }); + it('works with sync option', async () => { const result = await processFixture('plugin', {sync: true}); @@ -55,4 +64,24 @@ describe('npm2yarn plugin', () => { expect(result).toMatchSnapshot(); }); + + it('work with yarn converter', async () => { + const result = await processFixture('plugin', {converters: ['yarn']}); + + expect(result).toMatchSnapshot(); + }); + + it('work with pnpm converter', async () => { + const result = await processFixture('plugin', {converters: ['pnpm']}); + + expect(result).toMatchSnapshot(); + }); + + it('work with custom converter', async () => { + const result = await processFixture('plugin', { + converters: [['Turbo', (code) => code.replace(/npm/g, 'turbo')]], + }); + + expect(result).toMatchSnapshot(); + }); }); diff --git a/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts b/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts index 1da28fb7f243..ee562f491d56 100644 --- a/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts +++ b/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts @@ -6,45 +6,67 @@ */ import visit from 'unist-util-visit'; -// @ts-expect-error: this package provides CJS import npmToYarn from 'npm-to-yarn'; import type {Code, Content, Literal} from 'mdast'; import type {Plugin} from 'unified'; import type {Node, Parent} from 'unist'; +type CustomConverter = [name: string, cb: (npmCode: string) => string]; + type PluginOptions = { sync?: boolean; + converters?: (CustomConverter | 'yarn' | 'pnpm')[]; }; -// E.g. global install: 'npm i' -> 'yarn' -const convertNpmToYarn = (npmCode: string) => npmToYarn(npmCode, 'yarn'); - -const transformNode = (node: Code, isSync: boolean) => { - const groupIdProp = isSync ? ' groupId="npm2yarn"' : ''; - const npmCode = node.value; - const yarnCode = convertNpmToYarn(node.value); +function createTabItem( + code: string, + node: Code, + value: string, + label?: string, +) { return [ { type: 'jsx', - value: `\n`, + value: ``, }, { type: node.type, lang: node.lang, - value: npmCode, + value: code, }, { type: 'jsx', - value: '\n', + value: '', }, + ] as Content[]; +} + +const transformNode = ( + node: Code, + isSync: boolean, + converters: (CustomConverter | 'yarn' | 'pnpm')[], +) => { + const groupIdProp = isSync ? ' groupId="npm2yarn"' : ''; + const npmCode = node.value; + return [ { - type: node.type, - lang: node.lang, - value: yarnCode, + type: 'jsx', + value: ``, }, + ...createTabItem(npmCode, node, 'npm'), + ...converters.flatMap((converter) => + typeof converter === 'string' + ? createTabItem( + npmToYarn(npmCode, converter), + node, + converter, + converter === 'yarn' ? 'Yarn' : converter, + ) + : createTabItem(converter[1](npmCode), node, converter[0]), + ), { type: 'jsx', - value: '\n', + value: '', }, ] as Content[]; }; @@ -61,7 +83,7 @@ const nodeForImport: Literal = { }; const plugin: Plugin<[PluginOptions?]> = (options = {}) => { - const {sync = false} = options; + const {sync = false, converters = ['yarn', 'pnpm']} = options; return (root) => { let transformed = false as boolean; let alreadyImported = false as boolean; @@ -74,7 +96,7 @@ const plugin: Plugin<[PluginOptions?]> = (options = {}) => { while (index < node.children.length) { const child = node.children[index]!; if (matchNode(child)) { - const result = transformNode(child, sync); + const result = transformNode(child, sync, converters); node.children.splice(index, 1, ...result); index += result.length; transformed = true; diff --git a/website/docs/guides/markdown-features/markdown-features-code-blocks.mdx b/website/docs/guides/markdown-features/markdown-features-code-blocks.mdx index a0ac137ca4bb..fd6d65f7e522 100644 --- a/website/docs/guides/markdown-features/markdown-features-code-blocks.mdx +++ b/website/docs/guides/markdown-features/markdown-features-code-blocks.mdx @@ -772,6 +772,14 @@ module.exports = { remarkPlugins: [require('@docusaurus/remark-plugin-npm2yarn')], }, blog: { + // highlight-start + remarkPlugins: [ + [ + require('@docusaurus/remark-plugin-npm2yarn'), + {converters: ['pnpm']}, + ], + ], + // highlight-end // ... }, }, @@ -788,7 +796,12 @@ npm install @docusaurus/remark-plugin-npm2yarn ``` ```` -Using the `{sync: true}` option would make all tab choices synced. Because the choice is stored under the same namespace `npm2yarn`, different `npm2yarn` plugin instances would also sync their choices. +#### Configuration {#npm2yarn-remark-plugin-configuration} + +| Option | Type | Default | Description | +| --- | --- | --- | --- | +| `sync` | `boolean` | `false` | Whether to sync the selected converter across all code blocks. | +| `converters` | `array` | `'yarn'`, `'pnpm'` | The list of converters to use. The order of the converters is important, as the first converter will be used as the default choice. | ## Usage in JSX {#usage-in-jsx} diff --git a/website/docs/installation.mdx b/website/docs/installation.mdx index 77d6baab05dc..38d53f425d5e 100644 --- a/website/docs/installation.mdx +++ b/website/docs/installation.mdx @@ -55,38 +55,10 @@ npx create-docusaurus@latest my-website facebook You can also initialize a new project using your preferred project manager: -```mdx-code-block - - -``` - -```bash +```bash npm2yarn npm init docusaurus ``` -```mdx-code-block - - -``` - -```bash -yarn create docusaurus -``` - -```mdx-code-block - - -``` - -```bash -pnpm create docusaurus -``` - -```mdx-code-block - - -``` - Run `npx create-docusaurus@latest --help`, or check out its [API docs](./api/misc/create-docusaurus.mdx) for more information about all available flags. @@ -193,7 +165,7 @@ npm install To check that the update occurred successfully, run: -```bash npm2yarn +```bash npx docusaurus --version ``` diff --git a/website/docs/migration/migration-automated.mdx b/website/docs/migration/migration-automated.mdx index 65f95cfdaf5a..93c41ae8863f 100644 --- a/website/docs/migration/migration-automated.mdx +++ b/website/docs/migration/migration-automated.mdx @@ -38,10 +38,10 @@ npx @docusaurus/migrate migrate ./v1-website ./v2-website 3. To view your new website locally, go into your v2 website's directory and start your development server. -```bash +```bash npm2yarn cd ./v2-website -yarn install -yarn start +npm install +npm start ``` :::danger diff --git a/website/docs/migration/migration-manual.mdx b/website/docs/migration/migration-manual.mdx index 95a8c47d0465..49a7cdd1fce4 100644 --- a/website/docs/migration/migration-manual.mdx +++ b/website/docs/migration/migration-manual.mdx @@ -622,13 +622,13 @@ my-project Start the development server and fix any errors: -```bash +```bash npm2yarn cd website -yarn start +npm start ``` You can also try to build the site for production: -```bash -yarn build +```bash npm2yarn +npm run build ``` diff --git a/yarn.lock b/yarn.lock index ce71ad55cf79..2be620ea79fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11450,10 +11450,10 @@ npm-run-path@^5.1.0: dependencies: path-key "^4.0.0" -npm-to-yarn@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/npm-to-yarn/-/npm-to-yarn-1.2.1.tgz#ee88854d03ac930510c28a621985d800b51e76e0" - integrity sha512-ci3GjP40SKgZL2OOVW6B1z/3LdLNBsEtJupkYC/NQ8/Y0grfZZNNsR5DQdoy7zgL+FsKIXtnxMgrDjoMoJ40zQ== +npm-to-yarn@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/npm-to-yarn/-/npm-to-yarn-2.0.0.tgz#59c9c615eca3ba8920308a0b418007b73ffc7492" + integrity sha512-/IbjiJ7vqbxfxJxAZ+QI9CCRjnIbvGxn5KQcSY9xHh0lMKc/Sgqmm7yp7KPmd6TiTZX5/KiSBKlkGHo59ucZbg== npmlog@^6.0.0, npmlog@^6.0.2: version "6.0.2"