@azerion/bunosaurus-remark-plugin-npm2yarn 0.0.0-stage → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,93 @@
1
- # Temporary Holding Version
1
+ # `@docusaurus/remark-plugin-npm2yarn`
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ ## Motivation:
4
+
5
+ Transforms npm bash command code blocks to Docusaurus tabs:
6
+
7
+ The following (remove the `//`):
8
+
9
+ ````bash
10
+ // ```bash npm2yarn
11
+ // npm run build
12
+ // ```
13
+ ````
14
+
15
+ Becomes:
16
+
17
+ ![npm2yarn tabs example](./example.png)
18
+
19
+ **Note**: it only works when used with Docusaurus themes that have the `Tabs` and `TabItems` components.
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ npm install @docusaurus/remark-plugin-npm2yarn
25
+ ```
26
+
27
+ It is a Remark plugin, **not a Docusaurus plugin**, so you have to install it as a Remark plugin in the config of your Docusaurus plugins.
28
+
29
+ ```js
30
+ module.exports = {
31
+ presets: [
32
+ [
33
+ '@docusaurus/preset-classic',
34
+ {
35
+ docs: {
36
+ // ...
37
+ remarkPlugins: [
38
+ [require('@docusaurus/remark-plugin-npm2yarn'), {sync: true}],
39
+ ],
40
+ },
41
+ blog: {
42
+ // ...
43
+ remarkPlugins: [
44
+ [require('@docusaurus/remark-plugin-npm2yarn'), {sync: true}],
45
+ ],
46
+ },
47
+ pages: {
48
+ // ...
49
+ remarkPlugins: [
50
+ [require('@docusaurus/remark-plugin-npm2yarn'), {sync: true}],
51
+ ],
52
+ },
53
+ // ...
54
+ },
55
+ ],
56
+ ],
57
+ // ...
58
+ };
59
+ ```
60
+
61
+ ## Options
62
+
63
+ | Property | Type | Default | Description |
64
+ | --- | --- | --- | --- |
65
+ | `sync` | `boolean` | `false` | Syncing tab choices (Yarn and npm). See https://docusaurus.io/docs/markdown-features/#syncing-tab-choices for details. |
66
+ | `converters` | `array` | `['yarn', 'pnpm', 'bun']` | The list of converters to use. The order of the converters is important, as the first converter will be used as the default choice. |
67
+
68
+ ## Custom converters
69
+
70
+ In case you want to convert npm commands to something else than `yarn`, `pnpm` or `bun`, you can use custom converters:
71
+
72
+ ```ts
73
+ type CustomConverter = [name: string, cb: (npmCode: string) => string];
74
+ ```
75
+
76
+ ```ts
77
+ {
78
+ remarkPlugins: [
79
+ [
80
+ require('@docusaurus/remark-plugin-npm2yarn'),
81
+ {
82
+ sync: true,
83
+ converters: [
84
+ 'yarn',
85
+ 'pnpm',
86
+ 'bun',
87
+ ['Turbo', (code) => code.replace(/npm/g, 'turbo')],
88
+ ],
89
+ },
90
+ ],
91
+ ];
92
+ }
93
+ ```
package/example.png ADDED
Binary file
package/lib/index.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import type { Plugin } from 'unified';
8
+ type KnownConverter = 'yarn' | 'pnpm' | 'bun';
9
+ type CustomConverter = [name: string, cb: (npmCode: string) => string];
10
+ type Converter = CustomConverter | KnownConverter;
11
+ type PluginOptions = {
12
+ sync?: boolean;
13
+ converters?: Converter[];
14
+ };
15
+ declare const plugin: Plugin<[PluginOptions?]>;
16
+ export default plugin;
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EAAc,MAAM,EAAC,MAAM,SAAS,CAAC;AAEjD,KAAK,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE9C,KAAK,eAAe,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC;AAEvE,KAAK,SAAS,GAAG,eAAe,GAAG,cAAc,CAAC;AAElD,KAAK,aAAa,GAAG;IACnB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;CAC1B,CAAC;AAuJF,QAAA,MAAM,MAAM,EAAE,MAAM,CAAC,CAAC,aAAa,CAAC,CAAC,CA+BpC,CAAC;eAEa,MAAM"}
package/lib/index.js ADDED
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ const tslib_1 = require("tslib");
10
+ const unist_util_visit_1 = require("unist-util-visit");
11
+ const npm_to_yarn_1 = tslib_1.__importDefault(require("npm-to-yarn"));
12
+ function createAttribute(attributeName, attributeValue) {
13
+ return {
14
+ type: 'mdxJsxAttribute',
15
+ name: attributeName,
16
+ value: attributeValue,
17
+ };
18
+ }
19
+ function createTabItem({ code, node, value, label, }) {
20
+ return {
21
+ type: 'mdxJsxFlowElement',
22
+ name: 'TabItem',
23
+ attributes: [
24
+ createAttribute('value', value),
25
+ label && createAttribute('label', label),
26
+ ].filter((attr) => Boolean(attr)),
27
+ children: [
28
+ {
29
+ type: node.type,
30
+ lang: node.lang,
31
+ value: code,
32
+ },
33
+ ],
34
+ };
35
+ }
36
+ const transformNode = (node, isSync, converters) => {
37
+ const groupIdProp = isSync
38
+ ? {
39
+ type: 'mdxJsxAttribute',
40
+ name: 'groupId',
41
+ value: 'npm2yarn',
42
+ }
43
+ : undefined;
44
+ const npmCode = node.value;
45
+ function createConvertedTabItem(converter) {
46
+ if (typeof converter === 'string') {
47
+ return createTabItem({
48
+ code: (0, npm_to_yarn_1.default)(npmCode, converter),
49
+ node,
50
+ value: converter,
51
+ label: getLabelForConverter(converter),
52
+ });
53
+ }
54
+ const [converterName, converterFn] = converter;
55
+ return createTabItem({
56
+ code: converterFn(npmCode),
57
+ node,
58
+ value: converterName,
59
+ });
60
+ }
61
+ function getLabelForConverter(converter) {
62
+ switch (converter) {
63
+ case 'yarn':
64
+ return 'Yarn';
65
+ case 'bun':
66
+ return 'Bun';
67
+ default:
68
+ return converter;
69
+ }
70
+ }
71
+ return [
72
+ {
73
+ type: 'mdxJsxFlowElement',
74
+ name: 'Tabs',
75
+ attributes: [groupIdProp].filter(Boolean),
76
+ children: [
77
+ createTabItem({ code: npmCode, node, value: 'npm' }),
78
+ ...converters.flatMap(createConvertedTabItem),
79
+ ],
80
+ },
81
+ ];
82
+ };
83
+ const isMdxEsmLiteral = (node) => node.type === 'mdxjsEsm';
84
+ // TODO legacy approximation, good-enough for now but not 100% accurate
85
+ const isTabsImport = (node) => isMdxEsmLiteral(node) && node.value.includes('@theme/Tabs');
86
+ const isParent = (node) => Array.isArray(node.children);
87
+ const isNpm2Yarn = (node) => node.type === 'code' && node.meta === 'npm2yarn';
88
+ function createImportNode() {
89
+ return {
90
+ type: 'mdxjsEsm',
91
+ value: "import Tabs from '@theme/Tabs'\nimport TabItem from '@theme/TabItem'",
92
+ data: {
93
+ estree: {
94
+ type: 'Program',
95
+ body: [
96
+ {
97
+ type: 'ImportDeclaration',
98
+ specifiers: [
99
+ {
100
+ type: 'ImportDefaultSpecifier',
101
+ local: { type: 'Identifier', name: 'Tabs' },
102
+ },
103
+ ],
104
+ source: {
105
+ type: 'Literal',
106
+ value: '@theme/Tabs',
107
+ raw: "'@theme/Tabs'",
108
+ },
109
+ },
110
+ {
111
+ type: 'ImportDeclaration',
112
+ specifiers: [
113
+ {
114
+ type: 'ImportDefaultSpecifier',
115
+ local: { type: 'Identifier', name: 'TabItem' },
116
+ },
117
+ ],
118
+ source: {
119
+ type: 'Literal',
120
+ value: '@theme/TabItem',
121
+ raw: "'@theme/TabItem'",
122
+ },
123
+ },
124
+ ],
125
+ sourceType: 'module',
126
+ },
127
+ },
128
+ };
129
+ }
130
+ const plugin = (options = {}) => {
131
+ const { sync = false, converters = ['yarn', 'pnpm', 'bun'] } = options;
132
+ return (root) => {
133
+ let transformed = false;
134
+ let alreadyImported = false;
135
+ (0, unist_util_visit_1.visit)(root, (node) => {
136
+ if (isTabsImport(node)) {
137
+ alreadyImported = true;
138
+ }
139
+ if (isParent(node)) {
140
+ let index = 0;
141
+ while (index < node.children.length) {
142
+ const child = node.children[index];
143
+ if (isNpm2Yarn(child)) {
144
+ const result = transformNode(child, sync, converters);
145
+ node.children.splice(index, 1, ...result);
146
+ index += result.length;
147
+ transformed = true;
148
+ }
149
+ else {
150
+ index += 1;
151
+ }
152
+ }
153
+ }
154
+ });
155
+ if (transformed && !alreadyImported) {
156
+ root.children.unshift(createImportNode());
157
+ }
158
+ };
159
+ };
160
+ exports.default = plugin;
161
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,37 @@
1
1
  {
2
2
  "name": "@azerion/bunosaurus-remark-plugin-npm2yarn",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "4.0.0",
4
+ "description": "Remark plugin for converting npm commands to Yarn commands as tabs.",
5
+ "main": "lib/index.js",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "watch": "tsc --watch"
12
+ },
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/azerion/bunosaurus.git",
16
+ "directory": "packages/docusaurus-remark-plugin-npm2yarn"
17
+ },
18
+ "license": "MIT",
19
+ "dependencies": {
20
+ "mdast-util-mdx": "^3.0.0",
21
+ "npm-to-yarn": "^3.2.0",
22
+ "tslib": "^2.8.1",
23
+ "unified": "^11.0.5",
24
+ "unist-util-visit": "^5.1.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/mdast": "^4.0.4",
28
+ "@types/unist": "^3.0.3",
29
+ "dedent": "^1.7.2",
30
+ "remark": "^15.0.1",
31
+ "remark-mdx": "^3.1.1",
32
+ "to-vfile": "^8.0.0"
33
+ },
34
+ "engines": {
35
+ "bun": ">=1.4.1"
36
+ }
37
+ }
package/src/index.ts ADDED
@@ -0,0 +1,208 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import {visit} from 'unist-util-visit';
9
+ import npmToYarn from 'npm-to-yarn';
10
+ import type {Code, Literal} from 'mdast';
11
+ import type {MdxJsxFlowElement, MdxJsxAttribute} from 'mdast-util-mdx';
12
+ import type {Node, Parent} from 'unist';
13
+ import type {Transformer, Plugin} from 'unified';
14
+
15
+ type KnownConverter = 'yarn' | 'pnpm' | 'bun';
16
+
17
+ type CustomConverter = [name: string, cb: (npmCode: string) => string];
18
+
19
+ type Converter = CustomConverter | KnownConverter;
20
+
21
+ type PluginOptions = {
22
+ sync?: boolean;
23
+ converters?: Converter[];
24
+ };
25
+
26
+ function createAttribute(
27
+ attributeName: string,
28
+ attributeValue: MdxJsxAttribute['value'],
29
+ ): MdxJsxAttribute {
30
+ return {
31
+ type: 'mdxJsxAttribute',
32
+ name: attributeName,
33
+ value: attributeValue,
34
+ };
35
+ }
36
+
37
+ function createTabItem({
38
+ code,
39
+ node,
40
+ value,
41
+ label,
42
+ }: {
43
+ code: string;
44
+ node: Code;
45
+ value: string;
46
+ label?: string;
47
+ }): MdxJsxFlowElement {
48
+ return {
49
+ type: 'mdxJsxFlowElement',
50
+ name: 'TabItem',
51
+ attributes: [
52
+ createAttribute('value', value),
53
+ label && createAttribute('label', label),
54
+ ].filter((attr): attr is MdxJsxAttribute => Boolean(attr)),
55
+ children: [
56
+ {
57
+ type: node.type,
58
+ lang: node.lang,
59
+ value: code,
60
+ },
61
+ ],
62
+ };
63
+ }
64
+
65
+ const transformNode = (
66
+ node: Code,
67
+ isSync: boolean,
68
+ converters: Converter[],
69
+ ) => {
70
+ const groupIdProp = isSync
71
+ ? {
72
+ type: 'mdxJsxAttribute',
73
+ name: 'groupId',
74
+ value: 'npm2yarn',
75
+ }
76
+ : undefined;
77
+ const npmCode = node.value;
78
+
79
+ function createConvertedTabItem(converter: Converter) {
80
+ if (typeof converter === 'string') {
81
+ return createTabItem({
82
+ code: npmToYarn(npmCode, converter),
83
+ node,
84
+ value: converter,
85
+ label: getLabelForConverter(converter),
86
+ });
87
+ }
88
+ const [converterName, converterFn] = converter;
89
+ return createTabItem({
90
+ code: converterFn(npmCode),
91
+ node,
92
+ value: converterName,
93
+ });
94
+ }
95
+
96
+ function getLabelForConverter(converter: KnownConverter) {
97
+ switch (converter) {
98
+ case 'yarn':
99
+ return 'Yarn';
100
+ case 'bun':
101
+ return 'Bun';
102
+ default:
103
+ return converter;
104
+ }
105
+ }
106
+
107
+ return [
108
+ {
109
+ type: 'mdxJsxFlowElement',
110
+ name: 'Tabs',
111
+ attributes: [groupIdProp].filter(Boolean),
112
+ children: [
113
+ createTabItem({code: npmCode, node, value: 'npm'}),
114
+ ...converters.flatMap(createConvertedTabItem),
115
+ ],
116
+ },
117
+ ] as any[];
118
+ };
119
+
120
+ const isMdxEsmLiteral = (node: Node): node is Literal =>
121
+ node.type === 'mdxjsEsm';
122
+ // TODO legacy approximation, good-enough for now but not 100% accurate
123
+ const isTabsImport = (node: Node): boolean =>
124
+ isMdxEsmLiteral(node) && node.value.includes('@theme/Tabs');
125
+
126
+ const isParent = (node: Node): node is Parent =>
127
+ Array.isArray((node as Parent).children);
128
+ const isNpm2Yarn = (node: Node): node is Code =>
129
+ node.type === 'code' && (node as Code).meta === 'npm2yarn';
130
+
131
+ function createImportNode() {
132
+ return {
133
+ type: 'mdxjsEsm',
134
+ value:
135
+ "import Tabs from '@theme/Tabs'\nimport TabItem from '@theme/TabItem'",
136
+ data: {
137
+ estree: {
138
+ type: 'Program',
139
+ body: [
140
+ {
141
+ type: 'ImportDeclaration',
142
+ specifiers: [
143
+ {
144
+ type: 'ImportDefaultSpecifier',
145
+ local: {type: 'Identifier', name: 'Tabs'},
146
+ },
147
+ ],
148
+ source: {
149
+ type: 'Literal',
150
+ value: '@theme/Tabs',
151
+ raw: "'@theme/Tabs'",
152
+ },
153
+ },
154
+ {
155
+ type: 'ImportDeclaration',
156
+ specifiers: [
157
+ {
158
+ type: 'ImportDefaultSpecifier',
159
+ local: {type: 'Identifier', name: 'TabItem'},
160
+ },
161
+ ],
162
+ source: {
163
+ type: 'Literal',
164
+ value: '@theme/TabItem',
165
+ raw: "'@theme/TabItem'",
166
+ },
167
+ },
168
+ ],
169
+ sourceType: 'module',
170
+ },
171
+ },
172
+ };
173
+ }
174
+
175
+ const plugin: Plugin<[PluginOptions?]> = (options = {}): Transformer => {
176
+ const {sync = false, converters = ['yarn', 'pnpm', 'bun']} = options;
177
+ return (root) => {
178
+ let transformed = false;
179
+ let alreadyImported = false;
180
+
181
+ visit(root, (node: Node) => {
182
+ if (isTabsImport(node)) {
183
+ alreadyImported = true;
184
+ }
185
+
186
+ if (isParent(node)) {
187
+ let index = 0;
188
+ while (index < node.children.length) {
189
+ const child = node.children[index]!;
190
+ if (isNpm2Yarn(child)) {
191
+ const result = transformNode(child, sync, converters);
192
+ node.children.splice(index, 1, ...result);
193
+ index += result.length;
194
+ transformed = true;
195
+ } else {
196
+ index += 1;
197
+ }
198
+ }
199
+ }
200
+ });
201
+
202
+ if (transformed && !alreadyImported) {
203
+ (root as Parent).children.unshift(createImportNode());
204
+ }
205
+ };
206
+ };
207
+
208
+ export default plugin;