diff --git a/README.md b/README.md index 95c33e2..f6ed1a5 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ AMD and RequireJS are great, but ES6 modules provide a nicer syntax for writing ### Installing ```sh -npm install amd-to-es6 -g +npm install amd-to-es6-other -g ``` ### Usage diff --git a/index.js b/index.js index e55e549..e14ccc0 100644 --- a/index.js +++ b/index.js @@ -3,18 +3,20 @@ var falafel = require('falafel'); var jsx = require('acorn-jsx'); var acorn = require('acorn'); var beautify = require('js-beautify').js_beautify; +var classFields = require('acorn-class-fields'); +var staticClassFeatures = require('acorn-static-class-features'); module.exports = convert; -var JSXParser = acorn.Parser.extend(jsx()); +var JSXParser = acorn.Parser.extend(jsx(), classFields, staticClassFeatures); /** * Converts some code from AMD to ES6 * @param {string} source * @param {object} [options] * @returns {string} */ -function convert (source, options) { +function convert(source, options) { options = options || {}; @@ -24,45 +26,49 @@ function convert (source, options) { var mainCallExpression = null; var result = falafel(source, { - parser: { - parse: JSXParser.parse.bind(JSXParser) + parser: { + parse: JSXParser.parse.bind(JSXParser) + }, + ecmaVersion: 10, + plugins: { + jsx: true + }, }, - ecmaVersion: 9 - }, function (node) { - if (isNamedDefine(node)) { - throw new Error('Found a named define - this is not supported.'); - } + function (node) { + if (isNamedDefine(node)) { + throw new Error('Found a named define - this is not supported.'); + } - if (isDefineUsingIdentifier(node)) { - throw new Error('Found a define using a variable as the callback - this is not supported.'); - } + if (isDefineUsingIdentifier(node)) { + throw new Error('Found a define using a variable as the callback - this is not supported.'); + } - if (isModuleDefinition(node)) { + if (isModuleDefinition(node)) { - if (mainCallExpression) { - throw new Error('Found multiple module definitions in one file.'); - } + if (mainCallExpression) { + throw new Error('Found multiple module definitions in one file.'); + } - mainCallExpression = node; + mainCallExpression = node; } else if (isSyncRequire(node)) { - syncRequires.push(node); + syncRequires.push(node); } else if (isRequireWithNoCallback(node)) { - requiresWithSideEffects.push(node); + requiresWithSideEffects.push(node); } else if (isRequireWithDynamicModuleName(node)) { - throw new Error('Dynamic module names are not supported.'); - } + throw new Error('Dynamic module names are not supported.'); + } - if (isUseStrict(node)) { - node.parent.update(''); - } + if (isUseStrict(node)) { + node.parent.update(''); + } - }); + }); // no module definition found - return source untouched if (!mainCallExpression) { @@ -94,17 +100,17 @@ function convert (source, options) { }, {})); } - syncRequires.forEach(function (node) { - var moduleName = node.arguments[0].raw; - - // if no import name assigned then create one - if (!dependenciesMap[moduleName]) { - dependenciesMap[moduleName] = makeImportName(node.arguments[0].value); - } - - // replace with the import name - node.update(dependenciesMap[moduleName]); - }); + // syncRequires.forEach(function (node) { + // var moduleName = node.arguments[0].raw; + // + // // if no import name assigned then create one + // if (!dependenciesMap[moduleName]) { + // dependenciesMap[moduleName] = makeImportName(node.arguments[0].value); + // } + // + // // replace with the import name + // node.update(dependenciesMap[moduleName]); + // }); requiresWithSideEffects.forEach(function (node) { @@ -132,7 +138,7 @@ function convert (source, options) { // fix indentation if (options.beautify) { - moduleCode = beautify(moduleCode, { indent_size: options.indent }); + moduleCode = beautify(moduleCode, {indent_size: options.indent}); // jsbeautify doesn't understand es6 module syntax yet moduleCode = moduleCode.replace(/export[\s\S]default[\s\S]/, 'export default '); @@ -150,16 +156,16 @@ function convert (source, options) { * @param {object} dependencies * @returns {string} */ -function getImportStatements (dependencies) { +function getImportStatements(dependencies) { var statements = []; for (var key in dependencies) { if (!dependencies[key]) { statements.push('import ' + key + ';'); - } - else { + } else { statements.push('import ' + dependencies[key] + ' from ' + key + ';'); +// statements.push('import * as ' + "'" + dependencies[key] + "'" + ' from ' + key + ';'); } } @@ -170,12 +176,72 @@ function getImportStatements (dependencies) { * Updates the return statement of a FunctionExpression to be an 'export default'. * @param {object} functionExpression */ -function updateReturnStatement (functionExpression) { - functionExpression.body.body.forEach(function (node) { - if (node.type === 'ReturnStatement') { - node.update(node.source().replace(/\breturn\b/, 'export default')); +function updateReturnStatement(functionExpression) { + try { + functionExpression.body.body.forEach(function (node) { + if (node.type === 'ReturnStatement') { + node.update(node.source().replace(/\breturn\b/, 'export default')); + } + }); + + } catch (e) { + if (e.message == "Cannot read property 'forEach' of undefined") { + if (functionExpression.type === "ArrowFunctionExpression") { + functionExpression.body.update(` export default ${functionExpression.body.source()}; `) + } } - }); + } + +} + + +function updateImportStatement(functionExpression) { + try { + functionExpression.body.body.forEach(function (node) { + if (node.type === 'VariableDeclaration') { + // for friendly read pls use https://regex101.com/ to get result + // this to handle const|let|var XXX|{ XXX } = require("some"); + const normalImportRegex = /\s*(const|var|let)\b\s*(\{.+\}|\w+|\$)\s*(=)\s*(require\(\s*){1}.*(\))\s*;\s*$/g; + + // this to handle const some = require("some").name; + const regex = /\s*(const|var|let)\b\s*(\{.+\}|\w+|\$)\s*(=)\s*(require\(\s*){1}.*(\))(\.)(\w+)\s*;\s*$/g + + const group = normalImportRegex.exec(node.source()) + if (group != null) { + node.update(node.source() + .replace(group[1], " import ") + .replace(group[2], group[2] == "$" ? " * as $ ": group[2] == "_" ? " * as _ " : group[2]) + .replace(group[3], "") + .replace(group[4], ' from ') + .replace(group[5], "")) + } else { + var group2 = regex.exec(node.source()) + if(group2 !=null) { + let tempResult = node.source() + .replace(group2[1], " import ") + .replace(group2[2], "{ " + group2[7] + " }") + .replace(group2[3], "") + .replace(group2[4], " from ") + .replace(group2[5], "") + + node.update(tempResult.replace(/(?<=(from\s*['"]\w*['"]))\s*\.\w*(?=\s*;)/g, "")) + } + } + } else if (node.type === "ExpressionStatement") { + const regex = /\s*require\b\(.*\)\s*;$/g; + if (regex.test(node.source())) { + node.update(node.source() + .replace("require", 'import') + .replace("(", " ") + .replace(")", " ")) + } + } + }); + } catch (e) { + if (e.message != "Cannot read property 'forEach' of undefined") { + throw e; + } + } } /** @@ -183,8 +249,9 @@ function updateReturnStatement (functionExpression) { * @param {object} moduleFuncNode * @returns {string} */ -function getModuleCode (moduleFuncNode) { +function getModuleCode(moduleFuncNode) { + updateImportStatement(moduleFuncNode); updateReturnStatement(moduleFuncNode); var moduleCode = moduleFuncNode.body.source(); @@ -201,7 +268,7 @@ function getModuleCode (moduleFuncNode) { * @param {object} callExpression * @returns {array} */ -function getArgumentsTypes (callExpression) { +function getArgumentsTypes(callExpression) { return callExpression.arguments.map(function (arg) { return arg.type; }); @@ -212,7 +279,7 @@ function getArgumentsTypes (callExpression) { * @param {object} node * @returns {boolean} */ -function isRequireOrDefine (node) { +function isRequireOrDefine(node) { return isRequire(node) || isDefine(node); } @@ -221,7 +288,7 @@ function isRequireOrDefine (node) { * @param {object} node * @returns {boolean} */ -function isRequire (node) { +function isRequire(node) { return node.type === 'CallExpression' && node.callee.name === 'require'; } @@ -230,7 +297,7 @@ function isRequire (node) { * @param {object} node * @returns {boolean} */ -function isDefine (node) { +function isDefine(node) { return node.type === 'CallExpression' && node.callee.name === 'define'; } @@ -240,7 +307,7 @@ function isDefine (node) { * @param {array} arr2 * @returns {boolean} */ -function arrayEquals (arr1, arr2) { +function arrayEquals(arr1, arr2) { if (arr1.length !== arr2.length) { return false; @@ -255,14 +322,28 @@ function arrayEquals (arr1, arr2) { return true; } +function isConstImport(node) { + if (node.type === 'VariableDeclaration' && node.kind === "const") { + const regex = /const\s.+=\srequire\('.+'\)/g; + if (regex.test(node.source())) { + node.update(node.source() + .replace("const", " import ") + .replace("require", 'from') + .replace("(", " ") + .replace(")", " ")) + } + } + +} + /** * Returns true if node is a require() call where the module name is a literal. * @param {object} node * @returns {boolean} */ -function isSyncRequire (node) { +function isSyncRequire(node) { return isRequire(node) && - arrayEquals(getArgumentsTypes(node), ['Literal']); + arrayEquals(getArgumentsTypes(node), ['Literal']); } /** @@ -283,7 +364,7 @@ function isRequireWithDynamicModuleName(node) { * @param {object} target * @param {object} source */ -function extend (target, source) { +function extend(target, source) { for (var key in source) { target[key] = source[key]; } @@ -294,7 +375,7 @@ function extend (target, source) { * @param {object} node * @returns {boolean} */ -function isModuleDefinition (node) { +function isModuleDefinition(node) { if (!isRequireOrDefine(node)) { return false; @@ -333,7 +414,7 @@ function isModuleDefinition (node) { * @param {object} node * @returns {boolean} */ -function isRequireWithNoCallback (node) { +function isRequireWithNoCallback(node) { return isRequire(node) && arrayEquals(getArgumentsTypes(node), ['ArrayExpression']); } @@ -342,7 +423,7 @@ function isRequireWithNoCallback (node) { * @param {object} node * @returns {boolean} */ -function isNamedDefine (node) { +function isNamedDefine(node) { return isDefine(node) && getArgumentsTypes(node)[0] === 'Literal'; } @@ -364,8 +445,9 @@ function isDefineUsingIdentifier(node) { * @param {string} moduleName * @returns {string} */ -function makeImportName (moduleName) { - return '$__' + moduleName.replace(/[^a-zA-Z]/g, '_'); +function makeImportName(moduleName) { + return moduleName; + // return '$__' + moduleName.replace(/[^a-zA-Z]/g, '_'); } /** @@ -373,6 +455,6 @@ function makeImportName (moduleName) { * @param {object} node * @returns {boolean} */ -function isUseStrict (node) { +function isUseStrict(node) { return node.type === 'Literal' && node.value === 'use strict'; } diff --git a/package.json b/package.json index 659e91b..2f58197 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "amd-to-es6", - "version": "0.2.0", + "name": "amd-to-es6-other", + "version": "0.2.5", "description": "Convert AMD modules into ES6 modules", "author": "Jon Bretman", "main": "index.js", @@ -14,20 +14,21 @@ ], "repository": { "type": "git", - "url": "https://github.com/jonbretman/amd-to-as6.git" + "url": "https://github.com/1483523635/amd-to-as6-other" }, "directories": { "test": "test" }, "dependencies": { "acorn": "^6.1.1", + "acorn-class-fields": "^0.3.1", "acorn-jsx": "^5.0.1", + "acorn-static-class-features": "^0.2.0", "commander": "^2.4.0", "falafel": "^2.1.0", "glob": "^7.1.3", "js-beautify": "~1.5.1", - "mkdirp": "^0.5.0" - }, + "mkdirp": "^0.5.0" }, "devDependencies": { "nodeunit": "^0.11.3" }, diff --git a/test/examples/class-require-destructuring-assignment-arrow.js b/test/examples/class-require-destructuring-assignment-arrow.js new file mode 100644 index 0000000..f5cc55c --- /dev/null +++ b/test/examples/class-require-destructuring-assignment-arrow.js @@ -0,0 +1,11 @@ +define(function (require) { + const { test } = require('some-package'); + const home = require('some').home; + const _ = require('loadsh'); + const $ = require('jquery'); + + class A { + } + + return A; +}); diff --git a/test/examples/class-require-destructuring-assignment-expected.js b/test/examples/class-require-destructuring-assignment-expected.js new file mode 100644 index 0000000..cffc4f5 --- /dev/null +++ b/test/examples/class-require-destructuring-assignment-expected.js @@ -0,0 +1,14 @@ +import { + test +} +from 'some-package'; +import { + home +} +from 'some'; +import * as _ from 'loadsh'; +import * as $ from 'jquery'; + +class A {} + +export default A; diff --git a/test/examples/class-require-destructuring-assignment.js b/test/examples/class-require-destructuring-assignment.js new file mode 100644 index 0000000..f5cc55c --- /dev/null +++ b/test/examples/class-require-destructuring-assignment.js @@ -0,0 +1,11 @@ +define(function (require) { + const { test } = require('some-package'); + const home = require('some').home; + const _ = require('loadsh'); + const $ = require('jquery'); + + class A { + } + + return A; +}); diff --git a/test/examples/class-with-array-method-arrow.js b/test/examples/class-with-array-method-arrow.js new file mode 100644 index 0000000..40c5225 --- /dev/null +++ b/test/examples/class-with-array-method-arrow.js @@ -0,0 +1,15 @@ +define((require) => { + const re = require('some-package'); + + class A { + test = () => { + return true; + } + + method() { + return false; + } + } + + return A; +}); diff --git a/test/examples/class-with-array-method-expected.js b/test/examples/class-with-array-method-expected.js new file mode 100644 index 0000000..b80e363 --- /dev/null +++ b/test/examples/class-with-array-method-expected.js @@ -0,0 +1,13 @@ +import re from 'some-package'; + +class A { + test = () => { + return true; + } + + method() { + return false; + } +} + +export default A; diff --git a/test/examples/class-with-array-method.js b/test/examples/class-with-array-method.js new file mode 100644 index 0000000..a0605e0 --- /dev/null +++ b/test/examples/class-with-array-method.js @@ -0,0 +1,15 @@ +define(function (require) { + const re = require('some-package'); + + class A { + test = () => { + return true; + } + + method() { + return false; + } + } + + return A; +}); diff --git a/test/examples/class-with-field-arrow.js b/test/examples/class-with-field-arrow.js new file mode 100644 index 0000000..961a050 --- /dev/null +++ b/test/examples/class-with-field-arrow.js @@ -0,0 +1,9 @@ +define((require) => { + const re = require('some-package'); + + class A { + x = 0; + } + + return A; +}); diff --git a/test/examples/class-with-field-expected.js b/test/examples/class-with-field-expected.js new file mode 100644 index 0000000..3609dcd --- /dev/null +++ b/test/examples/class-with-field-expected.js @@ -0,0 +1,7 @@ +import re from 'some-package'; + +class A { + x = 0; +} + +export default A; diff --git a/test/examples/class-with-field.js b/test/examples/class-with-field.js new file mode 100644 index 0000000..7235aaa --- /dev/null +++ b/test/examples/class-with-field.js @@ -0,0 +1,9 @@ +define(function (require) { + const re = require('some-package'); + + class A { + x = 0; + } + + return A; +}); diff --git a/test/examples/class-with-require-arrow.js b/test/examples/class-with-require-arrow.js new file mode 100644 index 0000000..e734f57 --- /dev/null +++ b/test/examples/class-with-require-arrow.js @@ -0,0 +1,9 @@ +define(require => { + const re = require('some-package'); + + class A { + + } + + return A; +}); diff --git a/test/examples/class-with-require-expected.js b/test/examples/class-with-require-expected.js new file mode 100644 index 0000000..1b4c1ae --- /dev/null +++ b/test/examples/class-with-require-expected.js @@ -0,0 +1,7 @@ +import re from 'some-package'; + +class A { + +} + +export default A; diff --git a/test/examples/class-with-require.js b/test/examples/class-with-require.js new file mode 100644 index 0000000..56b74e2 --- /dev/null +++ b/test/examples/class-with-require.js @@ -0,0 +1,9 @@ +define(function (require) { + const re = require('some-package'); + + class A { + + } + + return A; +}); diff --git a/test/examples/class-with-static-field-arrow.js b/test/examples/class-with-static-field-arrow.js new file mode 100644 index 0000000..d86d243 --- /dev/null +++ b/test/examples/class-with-static-field-arrow.js @@ -0,0 +1,9 @@ +define((require) => { + const re = require('some-package'); + + class A { + static x = 0; + } + + return A; +}); diff --git a/test/examples/class-with-static-field-expected.js b/test/examples/class-with-static-field-expected.js new file mode 100644 index 0000000..058de73 --- /dev/null +++ b/test/examples/class-with-static-field-expected.js @@ -0,0 +1,7 @@ +import re from 'some-package'; + +class A { + static x = 0; +} + +export default A; diff --git a/test/examples/class-with-static-field.js b/test/examples/class-with-static-field.js new file mode 100644 index 0000000..a829082 --- /dev/null +++ b/test/examples/class-with-static-field.js @@ -0,0 +1,9 @@ +define(function (require) { + const re = require('some-package'); + + class A { + static x = 0; + } + + return A; +}); diff --git a/test/examples/class-with-static-props-arrow.js b/test/examples/class-with-static-props-arrow.js new file mode 100644 index 0000000..b52c84f --- /dev/null +++ b/test/examples/class-with-static-props-arrow.js @@ -0,0 +1,11 @@ +define((require) => { + const re = require('some-package'); + + class A { + static PropTypes = { + name: 'name', + }; + } + + return A; +}); diff --git a/test/examples/class-with-static-props-expected.js b/test/examples/class-with-static-props-expected.js new file mode 100644 index 0000000..689a0bd --- /dev/null +++ b/test/examples/class-with-static-props-expected.js @@ -0,0 +1,9 @@ +import re from 'some-package'; + +class A { + static PropTypes = { + name: 'name', + }; +} + +export default A; diff --git a/test/examples/class-with-static-props.js b/test/examples/class-with-static-props.js new file mode 100644 index 0000000..29d58cb --- /dev/null +++ b/test/examples/class-with-static-props.js @@ -0,0 +1,11 @@ +define(function (require) { + const re = require('some-package'); + + class A { + static PropTypes = { + name: 'name', + }; + } + + return A; +}); diff --git a/test/examples/define-no-deps-no-brace-arrow.js b/test/examples/define-no-deps-no-brace-arrow.js new file mode 100644 index 0000000..4976bc4 --- /dev/null +++ b/test/examples/define-no-deps-no-brace-arrow.js @@ -0,0 +1 @@ +define(() => 'something'); diff --git a/test/examples/define-no-deps-no-brace-expected.js b/test/examples/define-no-deps-no-brace-expected.js new file mode 100644 index 0000000..c06681d --- /dev/null +++ b/test/examples/define-no-deps-no-brace-expected.js @@ -0,0 +1 @@ +export default 'something'; diff --git a/test/examples/define-no-deps-no-brace.js b/test/examples/define-no-deps-no-brace.js new file mode 100644 index 0000000..67e22a5 --- /dev/null +++ b/test/examples/define-no-deps-no-brace.js @@ -0,0 +1,5 @@ +define(function () { + + return 'something'; + +}); diff --git a/test/examples/inline-sync-requires-expected.js b/test/examples/inline-sync-requires-expected.js index a760b0f..904c7a7 100644 --- a/test/examples/inline-sync-requires-expected.js +++ b/test/examples/inline-sync-requires-expected.js @@ -1,9 +1,9 @@ import a from 'a'; -import $__b from 'b'; -import $__css_css___TextBox_css from 'css/css!./TextBox.css'; +import 'b'; +import 'css/css!./TextBox.css'; -var b = $__b; -var c = $__css_css___TextBox_css; +import b from 'b'; +import c from 'css/css!./TextBox.css'; export default function() { a(b(c)); diff --git a/test/examples/only-require-arrow.js b/test/examples/only-require-arrow.js new file mode 100644 index 0000000..7299890 --- /dev/null +++ b/test/examples/only-require-arrow.js @@ -0,0 +1,8 @@ +define(require => { + require('some-package'); + + class A { + } + + return A; +}); diff --git a/test/examples/only-require-expected.js b/test/examples/only-require-expected.js new file mode 100644 index 0000000..1456928 --- /dev/null +++ b/test/examples/only-require-expected.js @@ -0,0 +1,5 @@ +import 'some-package'; + +class A {} + +export default A; diff --git a/test/examples/only-require.js b/test/examples/only-require.js new file mode 100644 index 0000000..cbc3dbc --- /dev/null +++ b/test/examples/only-require.js @@ -0,0 +1,8 @@ +define(function (require) { + require('some-package'); + + class A { + } + + return A; +}); diff --git a/test/examples/require-with-custom-loader-expected.js b/test/examples/require-with-custom-loader-expected.js new file mode 100644 index 0000000..574ccaf --- /dev/null +++ b/test/examples/require-with-custom-loader-expected.js @@ -0,0 +1,9 @@ +import someCSS from 'css/css!./TextBox.css'; +import 'some/path/to/b'; +import 'some/path/to/c'; + +// do something with dep A +varNameForA(); + +// do something with dep B +varNameForB(); diff --git a/test/spec.js b/test/spec.js index 1f62d7a..3199533 100644 --- a/test/spec.js +++ b/test/spec.js @@ -14,22 +14,31 @@ var makeTest = function (name) { return function (test) { test.equal(amdToEs6(readFile(inputFilename), {beautify: true}), expectedOutputFileName); test.done(); - }; + }; }; exports[baseTestName] = makeTestCase(name); exports[baseTestName + ' (using arrow function)'] = makeTestCase(name + '-arrow'); }; -makeTest('define-with-deps'); +makeTest('async-await'); +makeTest('class-require-destructuring-assignment'); +makeTest('class-with-array-method'); +makeTest('class-with-field'); +makeTest('class-with-require'); +makeTest('class-with-static-field'); +makeTest('class-with-static-props'); makeTest('define-no-deps'); -makeTest('require-with-deps'); -makeTest('require-no-deps'); +makeTest('define-no-deps-no-brace'); +makeTest('define-with-deps'); makeTest('inline-sync-requires'); +makeTest('only-require'); makeTest('preserve-quotes'); -makeTest('use-strict'); -makeTest('async-await'); +makeTest('require-no-deps'); +makeTest('require-with-custom-loader'); +makeTest('require-with-deps'); makeTest('rest-object'); +makeTest('use-strict'); var makeErrorCaseTest = function (name, message) {