Skip to content
This repository has been archived by the owner on Sep 10, 2023. It is now read-only.

Commit

Permalink
feat: support es6 module
Browse files Browse the repository at this point in the history
  • Loading branch information
axetroy committed Mar 7, 2018
1 parent 55aa7d3 commit 955af02
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 5 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ It base on [https://github.com/bramblex/jsjs](https://github.com/bramblex/jsjs)
* [x] ECMA5
* [x] es2015
* [x] es2015-let-const
* [ ] es2015-modules-commonjs
* [x] es2015-modules-commonjs
* [x] check-es2015-constants
* [x] es2015-block-scoped-functions
* [x] es2015-arrow-functions
Expand Down
2 changes: 1 addition & 1 deletion src/error.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export class ErrNotDefined extends ReferenceError {
constructor(varName: string) {
super(`${varName} is not defined`);
super(`Uncaught ReferenceError: ${varName} is not defined`);
}
}

Expand Down
71 changes: 71 additions & 0 deletions src/evaluate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,77 @@ const evaluate_map = {
},
ObjectProperty(node: types.ObjectProperty, scope: Scope, arg) {
// do nothing
},
ImportDeclaration(node: types.ImportDeclaration, scope: Scope, arg) {
let defaultImport: string = ""; // default import object
const otherImport: string[] = []; // import property
const moduleNane: string = evaluate(node.source, scope, arg);
node.specifiers.forEach(n => {
if (types.isImportDefaultSpecifier(n)) {
defaultImport = evaluate_map.ImportDefaultSpecifier(n, scope, arg);
} else if (types.isImportSpecifier(n)) {
otherImport.push(evaluate_map.ImportSpecifier(n, scope, arg));
} else {
throw n;
}
});

const _require = scope.$find("require");

if (_require) {
const requireFunc = _require.$get();

const targetModle: any = requireFunc(moduleNane) || {};

if (defaultImport) {
scope.$const(
defaultImport,
targetModle.default ? targetModle.default : targetModle
);
}

otherImport.forEach((varName: string) => {
scope.$const(varName, targetModle[varName]);
});
}
},
ImportDefaultSpecifier(
node: types.ImportDefaultSpecifier,
scope: Scope,
arg
) {
return node.local.name;
},
ImportSpecifier(node: types.ImportSpecifier, scope: Scope, arg) {
return node.local.name;
},
ExportDefaultDeclaration(
node: types.ExportDefaultDeclaration,
scope: Scope,
arg
) {
const moduleVar = scope.$find("module");
if (moduleVar) {
const moduleObject = moduleVar.$get();
moduleObject.exports = {
...moduleObject.exports,
...evaluate(node.declaration, scope, arg)
};
}
},
ExportNamedDeclaration(
node: types.ExportNamedDeclaration,
scope: Scope,
arg
) {
node.specifiers.forEach(n => evaluate(n, scope, arg));
},
ExportSpecifier(node: types.ExportSpecifier, scope: Scope, arg) {
const moduleVar = scope.$find("module");
if (moduleVar) {
const moduleObject = moduleVar.$get();
moduleObject.exports[node.local.name] = evaluate(node.local, scope, arg);
}
}
};

Expand Down
30 changes: 27 additions & 3 deletions src/vm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,50 @@ import Context, {Sandbox$} from "./context";
import {Scope} from "./scope";
import evaluate from "./evaluate";

interface Options {
filename?: string;
}

class Vm {
createContext(sandbox: Sandbox$ = {}): Context {
return new Context(sandbox);
}
isContext(sandbox: any): boolean {
return sandbox instanceof Context;
}
runInContext(code: string, context: Context): any | null {
runInContext(
code: string,
context: Context,
ops: Options = {
filename: "index.js"
}
): any | null {
const scope = new Scope("block");
scope.isTopLevel = true;
scope.$const("this", this);
scope.$setContext(context);

// 定义 module
// define module
const $exports = {};
const $module = {exports: $exports};
scope.$const("module", $module);
scope.$const("exports", $exports);
scope.$var("exports", $exports);

// require can be cover
if (!scope.$find("require")) {
const requireFunc =
typeof require === "function"
? require
: typeof context["require"] === "function"
? context["require"]
: function _require(id: string) {
return {};
};
scope.$var("require", requireFunc);
}

const ast = parse(code, {
sourceType: "module",
plugins: [
// estree,
// "jsx",
Expand Down
80 changes: 80 additions & 0 deletions test/ExportDeclaration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import test from "ava";
import {isImportDefaultSpecifier} from "babel-types";
import vm from "../src/vm";

test("ExportDeclaration-1", t => {
const sandbox: any = vm.createContext({});

const obj: any = vm.runInContext(
`
const obj = {
a: 1,
b: 2
};
export default obj;
export {obj}
`,
sandbox
);

t.deepEqual(obj.a, 1);
t.deepEqual(obj.b, 2);
t.deepEqual(obj.obj, {
a: 1,
b: 2
});
});

test("ExportDeclaration-2", t => {
const sandbox: any = vm.createContext({});

const obj: any = vm.runInContext(
`
const obj = {
a: 1,
b: 2
};
export {obj}
export default obj;
`,
sandbox
);

t.deepEqual(obj.a, 1);
t.deepEqual(obj.b, 2);
t.deepEqual(obj.obj, {
a: 1,
b: 2
});
});

test("ExportDeclaration-3", t => {
const sandbox: any = vm.createContext({});

const obj: any = vm.runInContext(
`
const obj = {
a: 1,
b: 2
};
const c = obj.a;
const d = obj.b;
export {obj, c, d}
export default obj;
`,
sandbox
);

t.deepEqual(obj.a, 1);
t.deepEqual(obj.b, 2);
t.deepEqual(obj.c, 1);
t.deepEqual(obj.d, 2);
t.deepEqual(obj.obj, {
a: 1,
b: 2
});
});
36 changes: 36 additions & 0 deletions test/ImportDeclaration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import test from "ava";
import {isImportDefaultSpecifier} from "babel-types";
import {parse} from "babylon";
import vm from "../src/vm";

test("ImportDeclaration-1", t => {
const sandbox: any = vm.createContext({});

const obj: any = vm.runInContext(
`
import {b, c, d, isImportDefaultSpecifier} from 'babel-types';
module.exports = {b, c, d, isImportDefaultSpecifier};
`,
sandbox
);

t.deepEqual(obj.b, undefined);
t.deepEqual(obj.c, undefined);
t.deepEqual(obj.d, undefined);
t.deepEqual(obj.isImportDefaultSpecifier, isImportDefaultSpecifier);
});

test("ImportDeclaration-2", t => {
const sandbox: any = vm.createContext({});

const outputParser: any = vm.runInContext(
`
import {parse} from "babylon";
module.exports = parse;
`,
sandbox
);
t.true(outputParser === parse);
});

0 comments on commit 955af02

Please sign in to comment.