Socket
Socket
Sign inDemoInstall

cjs-es

Package Overview
Dependencies
Maintainers
1
Versions
31
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cjs-es - npm Package Compare versions

Comparing version 0.2.2 to 0.3.0

lib/dynamic.js

450

index.js
const {walk} = require("estree-walker");
const MagicString = require("magic-string");
const {createTopLevelAnalyzer, createScopeAnalyzer} = require("./lib/util");
const {
createTopLevelExportTransformer,
createTopLevelImportTransformer
} = require("./lib/top-level");
const {createDynamicImportTransformer} = require("./lib/dynamic");
const {
createHoistExportTransformer,
createHoistImportTransformer
} = require("./lib/hoist");
function getExportInfo(node) {
if (node.left.type === "MemberExpression") {
if (node.left.object.name === "module" && node.left.property.name === "exports") {
return {
type: "default",
left: node.left,
value: node.right
};
}
if (
node.left.object.type === "MemberExpression" &&
node.left.object.object.name === "module" &&
node.left.object.property.name === "exports"
) {
return {
type: "named",
name: node.left.property.name,
left: node.left,
value: node.right
};
}
if (node.left.object.name === "exports") {
return {
type: "named",
name: node.left.property.name,
left: node.left,
value: node.right
};
}
}
}
function getDeclareExport(node) {
if (node.declarations.length !== 1) {
return;
}
const dec = node.declarations[0];
if (dec.id.type !== "Identifier" || dec.init.type !== "AssignmentExpression") {
return;
}
const exported = getExportInfo(dec.init);
if (!exported) {
return;
}
if (exported.name === dec.id.name) {
return {
kind: node.kind,
exported
};
}
}
function getDeclareImport(node) {
if (node.declarations.length !== 1) {
return;
}
const dec = node.declarations[0];
if (dec.init.type !== "CallExpression") {
return;
}
const required = getRequireInfo(dec.init);
if (!required) {
return;
}
let object;
if (dec.id.type === "ObjectPattern") {
object = getObjectInfo(dec.id, true);
if (!object) {
return;
}
} else if (dec.id.type !== "Identifier") {
return;
}
return {
object,
left: dec.id,
right: dec.init,
required
};
}
function getDynamicImport(node) {
if (
node.callee.type !== "MemberExpression" ||
node.callee.object.name !== "Promise" ||
node.callee.property.name !== "resolve"
) {
return;
}
if (
node.arguments.length !== 1 ||
node.arguments[0].type !== "CallExpression"
) {
return;
}
const required = getRequireInfo(node.arguments[0]);
if (required) {
return {
start: node.start,
end: node.end,
required
};
}
}
function getRequireInfo(node) {
if (
node.callee.name === "require" &&
node.arguments.length === 1 &&
node.arguments[0].type === "Literal"
) {
return node.arguments[0];
}
}
function getObjectInfo(node, checkValueType) {
if (!node.properties.length) {
return;
}
const properties = [];
for (const prop of node.properties) {
if (prop.key.type !== "Identifier") {
return;
}
if (checkValueType && prop.value.type !== "Identifier") {
return;
}
if (prop.method) {
properties.push({
name: prop.key.name,
method: true,
generator: prop.value.generator,
key: prop.key,
value: prop.value
});
} else {
// note that if prop.shorthand == true then prop.key == prop.value
properties.push({
name: prop.key.name,
key: prop.key,
value: prop.value
});
}
}
return {
start: node.start,
end: node.end,
properties
};
}
function makeCallable(func) {

@@ -162,229 +21,88 @@ if (typeof func === "function") {

function createExportTransformer({s, exportStyle, code}) {
let isTouched = false;
function transform({
parse,
code,
sourceMap = false,
importStyle = "named",
exportStyle = "named",
hoist = false,
dynamicImport = false
} = {}) {
return {
transformExportAssign,
transformExportDeclare,
isTouched: () => isTouched
};
importStyle = makeCallable(importStyle);
exportStyle = makeCallable(exportStyle);
function transformExportAssign(node) {
const exported = getExportInfo(node);
if (!exported) {
return;
}
if (exported.type === "named") {
if (exported.value.type === "Identifier") {
// exports.foo = foo
s.overwrite(
node.start,
exported.value.start,
"export {"
);
s.appendLeft(
exported.value.end,
exported.value.name === exported.name ?
"}" : ` as ${exported.name}}`
);
} else {
// exports.foo = "not an identifier"
s.overwrite(
node.start,
exported.left.end,
`const _export_${exported.name}_`
);
s.appendLeft(node.end, `;\nexport {_export_${exported.name}_ as ${exported.name}}`);
}
} else {
const rx = /.*?\/\/.*\bdefault\b/y;
rx.lastIndex = node.start;
if (exported.value.type !== "ObjectExpression" || exportStyle() === "default" || rx.test(code)) {
// module.exports = ...
s.overwrite(
node.start,
exported.value.start,
"export default "
);
} else {
// module.exports = {...}
const objMap = getObjectInfo(exported.value);
if (objMap) {
const overwrite = (start, property, newLine, semi) => {
if (property.value.type === "Identifier") {
// foo: bar
s.overwrite(start, property.value.start, `${newLine ? "\n" : ""}export {`);
s.appendLeft(
property.value.end,
`${
property.value.name === property.name ?
"" : ` as ${property.name}`
}}${semi ? ";" : ""}`
);
} else {
// foo: "not an identifier"
s.overwrite(
start,
property.value.start,
`${newLine ? "\n" : ""}const _export_${property.name}_ = ${
property.method ?
`function${property.generator ? "*" : ""} ` : ""
}`
);
s.appendLeft(
property.value.end,
`;\nexport {_export_${property.name}_ as ${property.name}}${semi ? ";" : ""}`
);
}
};
// module.exports = { ...
let start = node.start;
for (let i = 0; i < objMap.properties.length; i++) {
overwrite(
start,
objMap.properties[i],
i > 0,
i < objMap.properties.length - 1
);
start = objMap.properties[i].value.end;
}
// , ... }
s.remove(start, node.end);
}
}
}
isTouched = true;
}
function transformExportDeclare(node) {
const declared = getDeclareExport(node);
if (!declared) {
return;
}
// const foo = exports.foo = ...
s.overwrite(
node.start,
declared.exported.left.end,
`export ${declared.kind} ${declared.exported.name}`
);
isTouched = true;
}
}
function createImportTransformer({s, importStyle, code}) {
let isTouched = false;
const s = new MagicString(code);
const ast = parse(code);
const topLevel = createTopLevelAnalyzer();
const scope = hoist || dynamicImport ? createScopeAnalyzer(ast) : null;
return {
transformImportBare,
transformImportDynamic,
transformImportDeclare,
isTouched: () => isTouched
};
const topLevelImportTransformer = createTopLevelImportTransformer({code, s, importStyle});
const topLevelExportTransformer = createTopLevelExportTransformer({code, s, exportStyle});
function transformImportDeclare(node) {
const declared = getDeclareImport(node);
if (!declared) {
return;
}
if (!declared.object) {
// const foo = require("foo")
const rx = /.*?\/\/.*\bdefault\b/y;
rx.lastIndex = declared.required.end;
if (rx.test(code) || importStyle(declared.required.value) === "default") {
// import default
s.overwrite(
node.start,
declared.left.start,
"import "
);
} else {
// import named
s.overwrite(
node.start,
declared.left.start,
"import * as "
);
const dynamicImportTransformer = dynamicImport ? createDynamicImportTransformer({s, scope}) : null;
const hoistImportTransformer = hoist ?
createHoistImportTransformer({s, topLevel, scope, code}) : null;
const hoistExportTransformer = hoist ?
createHoistExportTransformer({s, topLevel, scope}) : null;
walk(ast, {
enter(node, parent) {
if (node.shouldSkip) {
this.skip();
}
} else {
// const {foo, bar}
s.overwrite(
node.start,
declared.object.start,
"import "
);
// foo: bar
for (const prop of declared.object.properties) {
if (prop.key.end < prop.value.start) {
s.overwrite(
prop.key.end,
prop.value.start,
" as "
);
topLevel.enter(node, parent);
if (scope) {
scope.enter(node);
}
if (node.type === "VariableDeclaration" && topLevel.isTop()) {
topLevelImportTransformer.transformImportDeclare(node);
topLevelExportTransformer.transformExportDeclare(node);
} else if (node.type === "AssignmentExpression" && topLevel.isTopChild()) {
if (!hoistExportTransformer || !hoistExportTransformer.isModuleDeclared()) {
topLevelExportTransformer.transformExportAssign(node);
}
} else if (node.type === "CallExpression") {
if (dynamicImport) {
dynamicImportTransformer.transform(node);
}
if (topLevel.isTopChild()) {
topLevelImportTransformer.transformImportBare(node);
}
if (hoist) {
hoistImportTransformer.transform(node);
}
} else if (node.type === "Identifier" && hoist) {
hoistExportTransformer.transformExport(node, parent);
hoistExportTransformer.transformModule(node, parent);
}
if (!dynamicImport && !hoist && !topLevel.isTop()) {
this.skip();
if (scope) {
scope.leave(node);
}
}
},
leave(node) {
if (scope) {
scope.leave(node);
}
}
s.overwrite(
declared.left.end,
declared.required.start,
" from "
);
s.remove(declared.required.end, declared.right.end);
isTouched = true;
}
function transformImportDynamic(node) {
const imported = getDynamicImport(node);
if (!imported) {
return;
}
s.overwrite(
imported.start,
imported.required.start,
"import("
);
s.overwrite(
imported.required.end,
imported.end,
")"
);
isTouched = true;
}
});
function transformImportBare(node) {
const required = getRequireInfo(node);
if (required) {
s.overwrite(node.start, required.start, "import ");
s.remove(required.end, node.end);
isTouched = true;
}
if (hoist) {
hoistExportTransformer.writeDeclare();
hoistExportTransformer.writeExport();
}
}
function transform({parse, code, sourceMap = false, importStyle = "named", exportStyle = "named"} = {}) {
importStyle = makeCallable(importStyle);
exportStyle = makeCallable(exportStyle);
const s = new MagicString(code);
const importTransformer = createImportTransformer({code, s, importStyle});
const exportTransformer = createExportTransformer({code, s, exportStyle});
const isTouched =
topLevelImportTransformer.isTouched() ||
topLevelExportTransformer.isTouched() ||
(hoist && (
hoistImportTransformer.isTouched() ||
hoistExportTransformer.isTouched()
)) ||
dynamicImport && dynamicImportTransformer.isTouched();
const ast = parse(code);
walk(ast, {enter(node, parent) {
if (node.type === "VariableDeclaration" && parent.type === "Program") {
importTransformer.transformImportDeclare(node);
exportTransformer.transformExportDeclare(node);
} else if (node.type === "AssignmentExpression" && parent.topLevel) {
exportTransformer.transformExportAssign(node);
} else if (node.type === "ExpressionStatement" && parent.type === "Program") {
node.topLevel = true;
} else if (node.type === "CallExpression") {
importTransformer.transformImportDynamic(node);
if (parent.topLevel) {
importTransformer.transformImportBare(node);
}
}
}});
const isTouched = importTransformer.isTouched() || exportTransformer.isTouched();
return {

@@ -391,0 +109,0 @@ code: isTouched ? s.toString() : code,

{
"name": "cjs-es",
"version": "0.2.2",
"version": "0.3.0",
"description": "Transform CommonJS module into ES module.",

@@ -29,4 +29,6 @@ "keywords": [

"estree-walker": "^0.5.1",
"magic-string": "^0.24.0"
"is-reference": "^1.1.0",
"magic-string": "^0.24.0",
"rollup-pluginutils": "^2.0.1"
}
}

@@ -11,13 +11,14 @@ cjs-es

* Lightweight.
* Prefer named import/export when possible.
* Only support the syntax that is interchangeable between mjs and js.
* Convert in-place. It only converts:
* Support the syntax that is interchangeable between mjs and js.
* Convert in-place. By default, it only converts:
- top-level `require` declaration (`const foo = require("foo")`),
- top-level `module.exports`, `exports` assignment (`module.exports = ...`/`const foo = exports.foo = ...`),
- dynamic-require expression (`Promise.resolve(require("foo"))`).
There are more samples under `test/cases` folder.
* Hoist the `require`, `exports` statements that is not top-level.
* Transform dynamic imports. (`Promise.resolve(require("foo"))`)
There are more samples under `test/cases` folder.
Usage

@@ -117,2 +118,41 @@ -----

Hoist
-----
If the `require`/`module`/`exports` statement are not at the top level, they would be hoisted:
```js
if (foo) {
require("foo").foo();
}
```
Result:
```js
import * as _require_foo_ from "foo";
if (foo) {
_require_foo_.foo();
}
```
Dynamic import
--------------
ES6 lazy load `import("...")` is async and return a promise. It is interchangeable with `Promise.resolve(require("..."))` in CommonJS:
```js
module.exports = () => {
return Promise.resolve(require("foo"));
};
```
Result:
```js
export default () => {
return import("foo");
};
```
API reference

@@ -139,3 +179,7 @@ -------------

* `exportStyle?`: `string` or `function -> string`. The result must be `"named"` or `"default"`. Default: `"named"`
* `hoist?`: `boolean`. If true then turn on hoist transformer. Default: `false`.
* `dynamicImport?`: `boolean`. If true then turn on dynamic import transformer. Default: `false`.
If `hoist` and `dynamicImport` are both `false`, the transformer would only traverse top-level nodes of the AST.
The result object has following members:

@@ -150,2 +194,8 @@

* 0.3.0 (Apr 27, 2018)
- Merge cjs-hoist.
- Add: `hoist` option.
- Add: `dynamicImport` option.
* 0.2.2 (Apr 26, 2018)

@@ -152,0 +202,0 @@

@@ -7,21 +7,41 @@ /* eslint-env mocha */

describe("cases", () => {
for (const dir of fs.readdirSync(__dirname + "/cases")) {
it(dir, () => {
const readFile = filename => {
try {
return fs.readFileSync(`${__dirname}/cases/${dir}/${filename}`, "utf8").replace(/\r/g, "");
} catch (err) {
// pass
}
};
const options = JSON.parse(readFile("options.json") || "{}");
const input = readFile("input.js");
const output = readFile("output.js");
const result = transform(Object.assign({code: input, parse}, options));
assert.equal(result.code, output);
assert.equal(result.isTouched, input !== output);
});
const cases = [
{
name: "top-level only",
test: dir => !dir.startsWith("hoist") && !dir.startsWith("dynamic"),
options: {}
}, {
name: "top-level + hoist + dynamic",
test: () => true,
options: {dynamicImport: true, hoist: true}
}
});
];
for (const c of cases) {
describe(c.name, () => {
for (const dir of fs.readdirSync(__dirname + "/cases")) {
if (!c.test(dir)) {
continue;
}
it(dir, () => {
const readFile = filename => {
try {
return fs.readFileSync(`${__dirname}/cases/${dir}/${filename}`, "utf8").replace(/\r/g, "");
} catch (err) {
// pass
}
};
const options = JSON.parse(readFile("options.json") || "{}");
const input = readFile("input.js");
const output = readFile("output.js");
const result = transform(Object.assign({
code: input,
parse
}, c.options, options));
assert.equal(result.code, output);
assert.equal(result.isTouched, input !== output);
});
}
});
}

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc