├── .gitignore ├── .prettierrc ├── images └── icon.png ├── .vscodeignore ├── .vscode ├── extensions.json └── launch.json ├── jsconfig.json ├── .eslintrc.json ├── LICENSE.md ├── extension.js ├── README.md ├── package.json └── plugins ├── esm-cjs-plugin.js └── cjs-esm-plugin.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode-test/ 3 | *.vsix 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "arrowParens": "avoid" 4 | } -------------------------------------------------------------------------------- /images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangrenyang/esm-cjs-converter/HEAD/images/icon.png -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | test/** 4 | .gitignore 5 | .yarnrc 6 | vsc-extension-quickstart.md 7 | **/jsconfig.json 8 | **/*.map 9 | **/.eslintrc.json 10 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "checkJs": true, /* Typecheck .js files. */ 6 | "lib": [ 7 | "es2021" 8 | ] 9 | }, 10 | "exclude": [ 11 | "node_modules" 12 | ] 13 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true, 7 | "mocha": true 8 | }, 9 | "parserOptions": { 10 | "ecmaVersion": 2018, 11 | "ecmaFeatures": { 12 | "jsx": true 13 | }, 14 | "sourceType": "module" 15 | }, 16 | "rules": { 17 | "no-const-assign": "warn", 18 | "no-this-before-super": "warn", 19 | "no-undef": "warn", 20 | "no-unreachable": "warn", 21 | "no-unused-vars": "warn", 22 | "constructor-super": "warn", 23 | "valid-typeof": "warn" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ] 15 | }, 16 | { 17 | "name": "Extension Tests", 18 | "type": "extensionHost", 19 | "request": "launch", 20 | "args": [ 21 | "--extensionDevelopmentPath=${workspaceFolder}", 22 | "--extensionTestsPath=${workspaceFolder}/test/suite/index" 23 | ] 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /extension.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode'); 2 | const babel = require("@babel/core"); 3 | const esmCjsPlugin = require('./plugins/esm-cjs-plugin') 4 | const cjsEsmPlugin = require('./plugins/cjs-esm-plugin') 5 | function activate(context) { 6 | const esmoduleToCommonjsDisposable = vscode.commands.registerCommand('extension.esmoduleToCommonjs', function () { 7 | vscode.window.activeTextEditor.edit(editBuilder => { 8 | const text = vscode.window.activeTextEditor.document.getText(); 9 | let { code } = babel.transformSync(text, { 10 | sourceType: 'module', 11 | plugins: [esmCjsPlugin] 12 | 13 | }); 14 | const end = new vscode.Position(vscode.window.activeTextEditor.document.lineCount + 1, 0); 15 | editBuilder.replace(new vscode.Range(new vscode.Position(0, 0), end), code); 16 | }); 17 | }); 18 | context.subscriptions.push(esmoduleToCommonjsDisposable); 19 | const commonjsToEsmoduleDisposable = vscode.commands.registerCommand('extension.commonjsToEsmodule', function () { 20 | vscode.window.activeTextEditor.edit(editBuilder => { 21 | const text = vscode.window.activeTextEditor.document.getText(); 22 | let { code } = babel.transformSync(text, { 23 | sourceType: 'module', 24 | plugins: [cjsEsmPlugin] 25 | }); 26 | const end = new vscode.Position(vscode.window.activeTextEditor.document.lineCount + 1, 0); 27 | editBuilder.replace(new vscode.Range(new vscode.Position(0, 0), end), code); 28 | }); 29 | }); 30 | context.subscriptions.push(commonjsToEsmoduleDisposable); 31 | } 32 | function deactivate() { } 33 | module.exports = { 34 | activate, 35 | deactivate 36 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## English 2 | ### esm-cjs-converter 3 | - Convert ES modules to CommonJS 4 | - Convert CommonJS to ES modules 5 | 6 | ### usage 7 | ![](https://static.zhufengpeixun.com/esmcjsconverter_1680840927105.png) 8 | #### Convert ES modules to CommonJS 9 | Before 10 | ```js 11 | import { a } from './a.js'; 12 | import b from './b.js'; 13 | 14 | export var c = 1; 15 | export default 2 16 | export default class Person { } 17 | export default function(){} 18 | ``` 19 | 20 | After 21 | ```js 22 | const {a} = require('./a.js'); 23 | const b = require('./b.js'); 24 | var c = 1; 25 | exports.c = c; 26 | module.exports = 2; 27 | ``` 28 | 29 | #### Convert CommonJS to ES modules 30 | Before 31 | ```js 32 | const {a} = require('./a.js'); 33 | const b = require('./b.js'); 34 | var c = 1; 35 | exports.c = c; 36 | module.exports = 2; 37 | ``` 38 | 39 | After 40 | ```js 41 | import { a } from "./a.js"; 42 | import b from "./b.js"; 43 | var c = 1; 44 | export { c }; 45 | export default 2; 46 | ``` 47 | 48 | ## 中文 49 | ### esm-cjs-converter 50 | - 将 ES 模块转换为 CommonJS 51 | - 将 CommonJS 转换为 ES 模块 52 | 53 | ### 使用方法 54 | ![](https://static.zhufengpeixun.com/esmcjsconverter_1680840927105.png) 55 | #### 把 ES modules 转成 CommonJS 56 | 转换前 57 | ```js 58 | import { a } from './a.js'; 59 | import b from './b.js'; 60 | 61 | export var c = 1; 62 | export default 2 63 | ``` 64 | 65 | 转换后 66 | ```js 67 | const {a} = require('./a.js'); 68 | const b = require('./b.js'); 69 | var c = 1; 70 | exports.c = c; 71 | module.exports = 2; 72 | ``` 73 | 74 | #### 把 CommonJS 转成 ES modules 75 | 转换前 76 | ```js 77 | const {a} = require('./a.js'); 78 | const b = require('./b.js'); 79 | var c = 1; 80 | exports.c = c; 81 | module.exports = 2; 82 | ``` 83 | 84 | 转换后 85 | ```js 86 | import { a } from "./a.js"; 87 | import b from "./b.js"; 88 | var c = 1; 89 | export { c }; 90 | export default 2; 91 | ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esm-cjs-converter", 3 | "displayName": "esm-cjs-converter", 4 | "description": "Convert ES modules to CommonJS, or convert CommonJS to ES modules.", 5 | "keywords": [ 6 | "esmodule", 7 | "commonjs", 8 | "module", 9 | "transform", 10 | "conversion", 11 | "esm", 12 | "cjs", 13 | "javascript", 14 | "bundler", 15 | "import", 16 | "export", 17 | "interop" 18 | ], 19 | "version": "1.0.6", 20 | "publisher": "zhang-renyang", 21 | "icon": "images/icon.png", 22 | "repository": "https://github.com/zhangrenyang/esm-cjs-converter", 23 | "engines": { 24 | "vscode": "^1.49.0" 25 | }, 26 | "categories": [ 27 | "Formatters" 28 | ], 29 | "main": "./extension.js", 30 | "activationEvents": [ 31 | "onCommand:extension.esmoduleToCommonjs", 32 | "onCommand:extension.commonjsToEsmodule" 33 | ], 34 | "contributes": { 35 | "commands": [ 36 | { 37 | "command": "extension.esmoduleToCommonjs", 38 | "title": "esmoduleToCommonjs" 39 | }, 40 | { 41 | "command": "extension.commonjsToEsmodule", 42 | "title": "commonjsToEsmodule" 43 | } 44 | ], 45 | "keybindings": [ 46 | { 47 | "command": "extension.esmoduleToCommonjs", 48 | "key": "ctrl+f11", 49 | "mac": "cmd+f11", 50 | "when": "editorTextFocus" 51 | }, 52 | { 53 | "command": "extension.commonjsToEsmodule", 54 | "key": "ctrl+f12", 55 | "mac": "cmd+f12", 56 | "when": "commonjsToEsmodule" 57 | } 58 | ], 59 | "menus": { 60 | "editor/context": [ 61 | { 62 | "when": "editorFocus", 63 | "command": "extension.esmoduleToCommonjs", 64 | "group": "navigation" 65 | }, 66 | { 67 | "when": "editorFocus", 68 | "command": "extension.commonjsToEsmodule", 69 | "group": "navigation" 70 | } 71 | ] 72 | } 73 | }, 74 | "scripts": {}, 75 | "dependencies": { 76 | "@babel/core": "^7.21.4" 77 | } 78 | } -------------------------------------------------------------------------------- /plugins/esm-cjs-plugin.js: -------------------------------------------------------------------------------- 1 | module.exports = function (babel) { 2 | const { types: t } = babel; 3 | return { 4 | name: "esm-to-cjs", 5 | visitor: { 6 | ImportDeclaration(path) { 7 | const { node } = path; 8 | const specifiers = node.specifiers; 9 | const source = node.source; 10 | if (t.isImportDefaultSpecifier(specifiers[0])) { 11 | const newNode = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(specifiers[0].local.name), t.callExpression(t.identifier("require"), [source]))]); 12 | path.replaceWith(newNode); 13 | } else { 14 | const newNode = t.variableDeclaration("const", [t.variableDeclarator(t.objectPattern( 15 | specifiers.map(item => t.objectProperty(t.identifier(item.local.name), t.identifier(item.local.name), false, true))) 16 | , t.callExpression(t.identifier("require"), [source]))]); 17 | path.replaceWith(newNode); 18 | } 19 | }, 20 | ExportNamedDeclaration(path) { 21 | const { node } = path; 22 | if (node.declaration) { 23 | if (t.isVariableDeclaration(node.declaration)) { 24 | const newNodes = []; 25 | for (let i = 0; i < node.declaration.declarations.length; i++) { 26 | const newNode = t.expressionStatement( 27 | t.assignmentExpression("=", 28 | t.memberExpression(t.identifier("exports"), 29 | node.declaration.declarations[i].id), 30 | node.declaration.declarations[i].init)); 31 | newNodes.push(newNode); 32 | } 33 | path.replaceWithMultiple(newNodes); 34 | } 35 | } else { 36 | if (node.specifiers.length > 0) { 37 | const newNodes = []; 38 | for (let i = 0; i < node.specifiers.length; i++) { 39 | const newNode = t.expressionStatement( 40 | t.assignmentExpression("=", 41 | t.memberExpression(t.identifier("exports"), 42 | node.specifiers[i].exported), 43 | node.specifiers[i].local)); 44 | newNodes.push(newNode); 45 | } 46 | path.replaceWithMultiple(newNodes); 47 | } 48 | } 49 | }, 50 | ExportDefaultDeclaration(path) { 51 | const { node } = path; 52 | if (t.isIdentifier(node.declaration)) { 53 | const newNode = t.expressionStatement(t.assignmentExpression("=", t.memberExpression(t.identifier("module"), t.identifier("exports")), node.declaration)); 54 | path.replaceWith(newNode); 55 | } else { 56 | if (t.isFunctionDeclaration(node.declaration)) { 57 | const newNode = t.expressionStatement(t.assignmentExpression("=", t.memberExpression(t.identifier("module"), t.identifier("exports")), t.functionExpression(null, node.declaration.params, node.declaration.body, node.declaration.generator, node.declaration.async))); 58 | path.replaceWith(newNode); 59 | } else if (t.isClassDeclaration(node.declaration)) { 60 | const newNode = t.expressionStatement( 61 | t.assignmentExpression("=", 62 | t.memberExpression( 63 | t.identifier("module"), t.identifier("exports")), 64 | t.classExpression(node.declaration.id, 65 | node.declaration.superClass, 66 | node.declaration.body, node.declaration.decorators 67 | ))); 68 | path.replaceWith(newNode); 69 | } else { 70 | const newNode = t.expressionStatement( 71 | t.assignmentExpression("=", 72 | t.memberExpression(t.identifier("module"), t.identifier("exports")), 73 | node.declaration)); 74 | path.replaceWith(newNode); 75 | } 76 | } 77 | } 78 | } 79 | }; 80 | }; -------------------------------------------------------------------------------- /plugins/cjs-esm-plugin.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function (babel) { 3 | const { types: t } = babel; 4 | return { 5 | name: "cjs-to-esm", 6 | visitor: { 7 | CallExpression(path) { 8 | if (t.isIdentifier(path.node.callee, { name: "require" })) { 9 | if (path.parentPath.node.type === "VariableDeclarator") { 10 | let newNode; 11 | if (t.isObjectPattern(path.parentPath.node.id)) { 12 | newNode = t.importDeclaration( 13 | path.parentPath.node.id.properties.map((item) => { 14 | return t.importSpecifier( 15 | t.identifier(item.key.name), 16 | t.identifier(item.value.name) 17 | ); 18 | }), 19 | t.stringLiteral(path.node.arguments[0].value) 20 | ); 21 | path.parentPath.replaceWith(newNode); 22 | } else { 23 | newNode = t.importDeclaration( 24 | [ 25 | t.importDefaultSpecifier( 26 | t.identifier(path.parentPath.node.id.name) 27 | ), 28 | ], 29 | t.stringLiteral(path.node.arguments[0].value) 30 | ); 31 | path.parentPath.replaceWith(newNode); 32 | } 33 | } 34 | if (path.parentPath.node.type === "VariableDeclaration") { 35 | const newNode = t.importDeclaration( 36 | [ 37 | t.importDefaultSpecifier( 38 | t.identifier(path.parentPath.node.declarations[0].id.name) 39 | ), 40 | ], 41 | t.stringLiteral(path.node.arguments[0].value) 42 | ); 43 | path.parentPath.replaceWith(newNode); 44 | } 45 | if (path.parentPath.node.type === "ObjectProperty") { 46 | const newNode = t.importDeclaration( 47 | [ 48 | t.importDefaultSpecifier( 49 | t.identifier(path.parentPath.node.key.name) 50 | ), 51 | ], 52 | t.stringLiteral(path.node.arguments[0].value) 53 | ); 54 | path.parentPath.replaceWith(newNode); 55 | } 56 | if (path.parentPath.node.type === "ObjectExpression") { 57 | const newNode = t.importDeclaration( 58 | [ 59 | t.importDefaultSpecifier( 60 | t.identifier(path.parentPath.node.properties[0].key.name) 61 | ), 62 | ], 63 | t.stringLiteral(path.node.arguments[0].value) 64 | ); 65 | path.parentPath.replaceWith(newNode); 66 | } 67 | if (path.parentPath.node.type === "CallExpression") { 68 | const newNode = t.importDeclaration( 69 | [ 70 | t.importDefaultSpecifier( 71 | t.identifier(path.parentPath.node.arguments[0].name) 72 | ), 73 | ], 74 | t.stringLiteral(path.node.arguments[0].value) 75 | ); 76 | path.parentPath.replaceWith(newNode); 77 | } 78 | //删除最左则的const或var,但是要保留 import语句 79 | if (path.parentPath.parentPath.node.type === "VariableDeclaration") { 80 | path.parentPath.parentPath.replaceWith(path.parentPath.node); 81 | } 82 | } 83 | }, 84 | AssignmentExpression(path) { 85 | if ( 86 | t.isMemberExpression(path.node.left) && 87 | t.isIdentifier(path.node.left.object, { name: "module" }) && 88 | t.isIdentifier(path.node.left.property, { name: "exports" }) 89 | ) { 90 | const newNode = t.exportDefaultDeclaration(path.node.right); 91 | path.parentPath.replaceWith(newNode); 92 | } else if ( 93 | t.isMemberExpression(path.node.left) && 94 | t.isIdentifier(path.node.left.object, { name: "exports" }) 95 | ) { 96 | //如果path.node.right是一个Identifier,那么就是exports.a = a;这种情况 97 | if (t.isIdentifier(path.node.right)) { 98 | const newNode = t.exportNamedDeclaration( 99 | null, 100 | [t.exportSpecifier(t.identifier(path.node.left.property.name), t.identifier(path.node.left.property.name))], 101 | null 102 | ); 103 | path.parentPath.replaceWith(newNode); 104 | } else { 105 | //如果path.node.right是一个值,那么就是exports.a = 1;这种情况 106 | const newNode = t.variableDeclaration("let", [ 107 | t.variableDeclarator( 108 | t.identifier(path.node.left.property.name), 109 | path.node.right 110 | ), 111 | ]); 112 | path.parentPath.replaceWith(newNode); 113 | const newNode2 = t.exportNamedDeclaration( 114 | null, 115 | [t.exportSpecifier(t.identifier(path.node.left.property.name), t.identifier(path.node.left.property.name))], 116 | null 117 | ); 118 | path.insertAfter(newNode2); 119 | } 120 | } 121 | }, 122 | }, 123 | }; 124 | }; 125 | --------------------------------------------------------------------------------