diff --git a/packages/docusaurus-remark-plugin-npm2yarn/package.json b/packages/docusaurus-remark-plugin-npm2yarn/package.json index f0b9c5c623..b5de28b4b0 100644 --- a/packages/docusaurus-remark-plugin-npm2yarn/package.json +++ b/packages/docusaurus-remark-plugin-npm2yarn/package.json @@ -2,10 +2,14 @@ "name": "@docusaurus/remark-plugin-npm2yarn", "version": "2.0.0-beta.9", "description": "Remark plugin for converting npm commands to Yarn commands as tabs.", - "main": "src/index.js", + "main": "lib/index.js", "publishConfig": { "access": "public" }, + "scripts": { + "build": "tsc", + "watch": "tsc --watch" + }, "repository": { "type": "git", "url": "https://github.com/facebook/docusaurus.git", @@ -13,9 +17,11 @@ }, "license": "MIT", "dependencies": { - "npm-to-yarn": "^1.0.1" + "npm-to-yarn": "^1.0.1", + "unist-util-visit": "^2.0.2" }, "devDependencies": { + "@types/mdast": "^3.0.10", "remark": "^12.0.0", "remark-mdx": "^1.6.21", "to-vfile": "^6.0.0" diff --git a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__snapshots__/index.test.js.snap b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__snapshots__/index.test.ts.snap similarity index 73% rename from packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__snapshots__/index.test.js.snap rename to packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__snapshots__/index.test.ts.snap index b58a985064..290c9d567c 100644 --- a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__snapshots__/index.test.js.snap +++ b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/__snapshots__/index.test.ts.snap @@ -5,11 +5,7 @@ exports[`npm2yarn plugin test: already imported tabs components above are not re import TabItem from '@theme/TabItem'; - + \`\`\`bash @@ -17,7 +13,7 @@ import TabItem from '@theme/TabItem'; \`\`\` - + \`\`\`bash $ yarn add --global docusaurus @@ -29,11 +25,7 @@ import TabItem from '@theme/TabItem'; `; exports[`npm2yarn plugin test: already imported tabs components below are not re-imported 1`] = ` -" +" \`\`\`bash @@ -41,7 +33,7 @@ exports[`npm2yarn plugin test: already imported tabs components below are not re \`\`\` - + \`\`\`bash $ yarn add --global docusaurus @@ -60,11 +52,7 @@ exports[`npm2yarn plugin test: installation file 1`] = ` "import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; - + \`\`\`bash @@ -72,7 +60,7 @@ import TabItem from '@theme/TabItem'; \`\`\` - + \`\`\`bash $ yarn add --global docusaurus @@ -106,11 +94,7 @@ import TabItem from '@theme/TabItem'; A plugin is usually a npm package, so you install them like other npm packages using npm. - + \`\`\`bash @@ -118,7 +102,7 @@ npm install --save docusaurus-plugin-name \`\`\` - + \`\`\`bash yarn add docusaurus-plugin-name diff --git a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.js b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts similarity index 85% rename from packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.js rename to packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts index 77eca4cbd7..3cab9ab431 100644 --- a/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.js +++ b/packages/docusaurus-remark-plugin-npm2yarn/src/__tests__/index.test.ts @@ -6,14 +6,19 @@ */ import remark from 'remark'; -import npm2yarn from '../index'; +// import from the transpiled lib because Babel can't transpile `export =` syntax +// TODO change to `../index` after migrating to ESM +import npm2yarn from '../../lib/index'; import vfile from 'to-vfile'; import {join, relative} from 'path'; import mdx from 'remark-mdx'; const staticDir = `./${relative(process.cwd(), join(__dirname, 'fixtures'))}`; -const processFixture = async (name, options) => { +const processFixture = async ( + name: string, + options: {sync?: boolean; staticDir: string}, +) => { const path = join(__dirname, 'fixtures', `${name}.md`); const file = await vfile.read(path); const result = await remark() diff --git a/packages/docusaurus-remark-plugin-npm2yarn/src/index.js b/packages/docusaurus-remark-plugin-npm2yarn/src/index.js deleted file mode 100644 index f0c6a26334..0000000000 --- a/packages/docusaurus-remark-plugin-npm2yarn/src/index.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -const npmToYarn = require('npm-to-yarn'); - -// E.g. global install: 'npm i' -> 'yarn' -const convertNpmToYarn = (npmCode) => npmToYarn(npmCode, 'yarn'); - -const transformNode = (node, isSync) => { - const groupIdProp = isSync ? 'groupId="npm2yarn" ' : ''; - const npmCode = node.value; - const yarnCode = convertNpmToYarn(node.value); - return [ - { - type: 'jsx', - value: - ` -`, - }, - { - type: node.type, - lang: node.lang, - value: npmCode, - }, - { - type: 'jsx', - value: '\n', - }, - { - type: node.type, - lang: node.lang, - value: yarnCode, - }, - { - type: 'jsx', - value: '\n', - }, - ]; -}; - -const matchNode = (node) => node.type === 'code' && node.meta === 'npm2yarn'; -const nodeForImport = { - type: 'import', - value: - "import Tabs from '@theme/Tabs';\nimport TabItem from '@theme/TabItem';", -}; - -module.exports = (options = {}) => { - const {sync = false} = options; - let transformed = false; - let alreadyImported = false; - const transformer = (node) => { - if (node.type === 'import' && node.value.includes('@theme/Tabs')) { - alreadyImported = true; - } - if (matchNode(node)) { - transformed = true; - return transformNode(node, sync); - } - if (Array.isArray(node.children)) { - let index = 0; - while (index < node.children.length) { - const result = transformer(node.children[index]); - if (result) { - node.children.splice(index, 1, ...result); - index += result.length; - } else { - index += 1; - } - } - } - if (node.type === 'root' && transformed && !alreadyImported) { - node.children.unshift(nodeForImport); - } - return null; - }; - return transformer; -}; diff --git a/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts b/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts new file mode 100644 index 0000000000..7f80ed3678 --- /dev/null +++ b/packages/docusaurus-remark-plugin-npm2yarn/src/index.ts @@ -0,0 +1,95 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import type {Code, Content, Literal} from 'mdast'; +import type {Plugin, Transformer} from 'unified'; +import type {Node, Parent} from 'unist'; +import visit from 'unist-util-visit'; +import npmToYarn from 'npm-to-yarn'; + +interface PluginOptions { + sync?: boolean; +} + +// 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); + return [ + { + type: 'jsx', + value: `\n`, + }, + { + type: node.type, + lang: node.lang, + value: npmCode, + }, + { + type: 'jsx', + value: '\n', + }, + { + type: node.type, + lang: node.lang, + value: yarnCode, + }, + { + type: 'jsx', + value: '\n', + }, + ] as Content[]; +}; + +const isImport = (node: Node): node is Literal => node.type === 'import'; +const isParent = (node: Node): node is Parent => + Array.isArray((node as Parent).children); +const matchNode = (node: Node): node is Code => + node.type === 'code' && (node as Code).meta === 'npm2yarn'; +const nodeForImport: Literal = { + type: 'import', + value: + "import Tabs from '@theme/Tabs';\nimport TabItem from '@theme/TabItem';", +}; + +const plugin: Plugin<[PluginOptions?]> = (options = {}) => { + const {sync = false} = options; + let transformed = false; + let alreadyImported = false; + const transformer: Transformer = (root) => { + visit(root, (node: Node) => { + if (isImport(node) && node.value.includes('@theme/Tabs')) { + alreadyImported = true; + } + if (isParent(node)) { + let index = 0; + while (index < node.children.length) { + const child = node.children[index]; + if (matchNode(child)) { + const result = transformNode(child, sync); + node.children.splice(index, 1, ...result); + index += result.length; + transformed = true; + } else { + index += 1; + } + } + } + }); + if (transformed && !alreadyImported) { + (root as Parent).children.unshift(nodeForImport); + } + }; + return transformer; +}; + +// To continue supporting `require('npm2yarn')` without the `.default` ㄟ(▔,▔)ㄏ +// TODO change to export default after migrating to ESM +export = plugin; diff --git a/packages/docusaurus-remark-plugin-npm2yarn/tsconfig.json b/packages/docusaurus-remark-plugin-npm2yarn/tsconfig.json new file mode 100644 index 0000000000..f5902ba108 --- /dev/null +++ b/packages/docusaurus-remark-plugin-npm2yarn/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "incremental": true, + "tsBuildInfoFile": "./lib/.tsbuildinfo", + "rootDir": "src", + "outDir": "lib" + } +} diff --git a/yarn.lock b/yarn.lock index ac83f91ddb..e442b79bbb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4262,7 +4262,7 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.174.tgz#b4b06b6eced9850eed6b6a8f1abdd0f5192803c1" integrity sha512-KMBLT6+g9qrGXpDt7ohjWPUD34WA/jasrtjTEHStF0NPdEwJ1N9SZ+4GaMVDeuk/y0+X5j9xFm6mNiXS7UoaLQ== -"@types/mdast@^3.0.0", "@types/mdast@^3.0.7": +"@types/mdast@^3.0.0", "@types/mdast@^3.0.10", "@types/mdast@^3.0.7": version "3.0.10" resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" integrity sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==