Skip to content

Transform ESM to Common JS for present NodeJS, without any junk wrappers or useless renaming

License

Notifications You must be signed in to change notification settings

sidvishnoi/esm-to-cjs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

esm-to-cjs

Transform ESM to Common JS for present NodeJS, without any junk wrappers or useless renaming.

Motivation

I was working on a TypeScript project for NodeJS and using ES modules. The transformations to CommonJS by TypeScript (or by Babel + plugins) causes variable renaming (prefixing) and adds some wrapper functions to the transformed code.

I was not happy with this transformation. So I created this tool to convert the ESM import/exports to the kinds that a NodeJS developer would write today.

// input
import { resolve as resolvePath } from "path";
resolvePath("./hello")

// what i wanted
const {  resolve: resolvePath } = require("path");
resolvePath("./hello")

// typescript gave me:
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
path_1.resolve("./hello");
// input
async () => {
  const path = await import("path");
}

// what i wanted
async () => {
  const path = require("path");
}

// typescript gave me:
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
async () => {
    const path = await Promise.resolve().then(() => __importStar(require("path")));
};
// input
export const foo = 5;
console.log(foo);

// what i wanted
const foo = 5;
module.exports = {
  foo
}

// typescript gave me:
Object.defineProperty(exports, "__esModule", { value: true });
exports.foo = 5;
console.log(exports.foo);
// input
export { foo as wow, bar } from "baz";
export { baz } from "lorem";

// what i wanted
const { foo: __foo__, bar: __bar__ } = require("baz");
module.exports = {
  wow: __foo__,
  bar: __bar__,
  baz: require("lorem").baz
}

// typescript gave me
Object.defineProperty(exports, "__esModule", { value: true });
var baz_1 = require("baz");
var lorem_1 = require("lorem");
exports.wow = baz_1.foo;
exports.bar = baz_1.bar;
exports.baz = lorem1.baz;

So, I created this tool using some simple string manipulations. A lot of sample input/output are available here.

Limitations

  • import * as foo from "bar"; is converted to const foo = require("bar");. Not sure if this is what everyone wants. I did it as per my project requirements.
  • Also, import foo from "bar"; is converted to const foo = require("bar").default;".
  • No support for export * syntax.
  • No mixing of default import, named imports and import * in same statement.
  • See Bug: "The simpler transform is semantically wrong"

Packages

This tool is available in form of two packages:

  • esm-to-cjs: the core module.
  • gulp-esm-to-cjs: as a gulp plugin.

esm-to-cjs

This is the core module. It includes a tokenizer and a transformer. I didn't use some specific JS parser due to overheads and created my own using string manipulation.

Install:

npm i --save-dev esm-to-cjs

Usage:

const { runTransform } = require("esm-to-cjs");

const input = `import { resolve as resolvePath } from "path";`
const options = { quote: "double" }; // see details below
const output = runTransform(input, options);
console.log(output);
// const { resolve: resolvePath } = require("path");

Options:

quote:
  type: string
  default: "double"
  available: "double" | "single"
  description: Tells parser what kind of quotes you use in module names, i.e. like `from "moduleName"`

lenDestructure:
  type: number (of characters)
  default: 60
  description: Used by parser to improve performance. Set to a higher value if your object destruturing statements are longer.

lenModuleName:
  type: number (of characters)
  default: 20
  description: Used by parser to improve performance. Set to a higher value if your module names are longer.

lenIdentifier:
  type: number (of characters)
  default: 60
  description: Used by parser to improve performance. Set to a higher value if your identifier names are longer.

indent:
  type: number
  default: 2
  description: Indentation (spaces) in output code.

gulp-esm-to-cjs

Gulp plugin for esm-to-cjs.

Install:

npm i --save-dev gulp-esm-to-cjs

Usage:

// gulpfile.js
const esmToCjs = require("gulp-esm-to-cjs");

function convert() {
  return gulp
    .src(src)
    .pipe(esmToCjs(options))
    .pipe(gulp.dest(dest));
}
module.exports.convert = convert;

// use as:
// $ gulp convert

Contributing

  • If you've issues regarding the project - documentation, supported features and transformations etc., please file them on GitHub where we can discuss.
  • Pull requests are welcome!
  • Please bear in mind that I created this project in a hurry, so the code isn't very impressive. Also, I didn't add all the transformations. See limitations above. Would be nice if we can overcome them!