fix(compartment-mapper)!: Temporarily disable CommonJS
MODIFY
packages/compartment-mapper/README.md
packages/compartment-mapper/README.md
# Compartment mapper
+> :warning: CommonJS support is temporarily disabled, pending a solution for
+> heuristic static analysis that does not entrain any Node.js built-ins.
+
The compartment mapper builds _compartment maps_ for Node.js style
applications, finding their dependencies and describing how to create
[Compartments][] for each package in the application.

MODIFY
packages/compartment-mapper/package.json
packages/compartment-mapper/package.json
"test": "yarn build && yarn qt"
},
"dependencies": {
- "@babel/parser": "^7.8.4",
- "@babel/traverse": "^7.8.4",
"ses": "^0.10.4"
},
"devDependencies": {

MODIFY
packages/compartment-mapper/src/node-modules.js
packages/compartment-mapper/src/node-modules.js
@@ -76,9 +76,9 @@ const findPackage = async (readDescriptor, directory, name) => {
}
};
-const languages = ["cjs", "mjs", "json"];
-const uncontroversialParsers = { cjs: "cjs", mjs: "mjs", json: "json" };
-const commonParsers = { js: "cjs", ...uncontroversialParsers };
+const languages = ["mjs", "json"];
+const uncontroversialParsers = { mjs: "mjs", json: "json" };
+const commonParsers = uncontroversialParsers;
const moduleParsers = { js: "mjs", ...uncontroversialParsers };
const inferParsers = (descriptor, location) => {
@@ -88,7 +88,7 @@ const inferParsers = (descriptor, location) => {
throw new Error(
`Cannot interpret parser map ${JSON.stringify(
parsers
- )} of package at ${location}, must be an object mapping file extensions to corresponding languages (mjs for ECMAScript modules, cjs for CommonJS modules, or json for JSON modules`
+ )} of package at ${location}, must be an object mapping file extensions to corresponding languages (mjs for ECMAScript modules or json for JSON modules`
);
}
const invalidLanguages = values(parsers).filter(
@@ -98,7 +98,7 @@ const inferParsers = (descriptor, location) => {
throw new Error(
`Cannot interpret parser map language values ${JSON.stringify(
invalidLanguages
- )} of package at ${location}, must be an object mapping file extensions to corresponding languages (mjs for ECMAScript modules, cjs for CommonJS modules, or json for JSON modules`
+ )} of package at ${location}, must be an object mapping file extensions to corresponding languages (mjs for ECMAScript modules or json for JSON modules`
);
}
return { ...uncontroversialParsers, ...parsers };

DELETE
packages/compartment-mapper/src/parse-requires.js
-import parser from "@babel/parser";
-import traverse from "@babel/traverse";
-
-/* parseRequires
- * Analyzes a CommonJS module's obvious static shallow dependencies, returning
- * an array of every module specifier that the module requires with a string
- * literal.
- *
- * Does not differentiate conditional dependencies, which will likely cause
- * errors in the loading phase. For example, `if (isNode) { require('fs') }`
- * will reveal an unconditional dependency on a possibly unloadable `fs`
- * module, even though the module has a contingency when that module is not
- * available.
- *
- * Does not discover dynamic dependencies, which will likely cause errors in
- * the module execution phase.
- * For example, `require(path)` will not be discovered by this parser, the
- * module will successfully load, but will likely be unable to synchronously
- * require the module with the given path.
- *
- * @typedef ImportSpecifier string
- * @param {string} source
- * @param {string} location
- * @return {Array<ImportSpecifier>}
- */
-export const parseRequires = (source, location, packageLocation) => {
- try {
- const ast = parser.parse(source);
- const required = new Set();
- traverse.default(ast, {
- CallExpression(path) {
- const { node, scope } = path;
- const { callee, arguments: args } = node;
- if (callee.name !== "require") {
- return;
- }
- // We do not recognize `require()` or `require("id", true)`.
- if (args.length !== 1) {
- return;
- }
- // We only consider the form `require("id")`, not `require(expression)`.
- const [specifier] = args;
- if (specifier.type !== "StringLiteral") {
- return;
- }
- // The existence of a require variable in any parent scope indicates that
- // this is not a free-variable use of the term `require`, so it does not
- // likely refer to the module's given `require`.
- if (scope.hasBinding("require")) {
- return;
- }
- required.add(specifier.value);
- }
- });
- return Array.from(required).sort();
- } catch (error) {
- if (/import/.exec(error.message) !== null) {
- throw new Error(
- `Cannot parse CommonJS module at ${location}, consider adding "type": "module" to package.json in ${packageLocation}: ${error}`
- );
- }
- throw new Error(`Cannot parse CommonJS module at ${location}: ${error}`);
- }
-};

MODIFY
packages/compartment-mapper/src/parse.js
packages/compartment-mapper/src/parse.js
-import { parseRequires } from "./parse-requires.js";
import { parseExtension } from "./extension.js";
import * as json from "./json.js";
@@ -17,58 +16,6 @@ export const parseMjs = (source, _specifier, location, _packageLocation) => {
};
};
-export const parseCjs = (source, _specifier, location, packageLocation) => {
- if (typeof source !== "string") {
- throw new TypeError(
- `Cannot create CommonJS static module record, module source must be a string, got ${source}`
- );
- }
- if (typeof location !== "string") {
- throw new TypeError(
- `Cannot create CommonJS static module record, module location must be a string, got ${location}`
- );
- }
-
- const imports = parseRequires(source, location, packageLocation);
- const execute = (exports, compartment, resolvedImports) => {
- const functor = compartment.evaluate(
- `(function (require, exports, module, __filename, __dirname) { ${source} //*/\n})\n//# sourceURL=${location}`
- );
-
- let moduleExports = exports;
-
- const module = freeze({
- get exports() {
- return moduleExports;
- },
- set exports(namespace) {
- moduleExports = namespace;
- exports.default = namespace;
- }
- });
-
- const require = freeze(importSpecifier => {
- const namespace = compartment.importNow(resolvedImports[importSpecifier]);
- if (namespace.default !== undefined) {
- return namespace.default;
- }
- return namespace;
- });
-
- functor(
- require,
- exports,
- module,
- location, // __filename
- new URL("./", location).toString() // __dirname
- );
- };
- return {
- parser: "cjs",
- record: freeze({ imports, execute })
- };
-};
-
export const parseJson = (source, _specifier, location, _packageLocation) => {
const imports = freeze([]);
const execute = exports => {
@@ -100,7 +47,6 @@ export const makeExtensionParser = (extensions, types) => {
export const parserForLanguage = {
mjs: parseMjs,
- cjs: parseCjs,
json: parseJson
};

MODIFY
packages/compartment-mapper/test/main.test.js
packages/compartment-mapper/test/main.test.js
-// import "./ses-lockdown.js";
import "ses";
import fs from "fs";
import tape from "tape";
@@ -33,20 +32,14 @@ const assertFixture = (t, namespace) => {
avery,
brooke,
clarke,
- danny,
builtin,
receivedGlobalProperty,
- receivedGlobalLexical,
- typecommon,
- typemodule,
- typehybrid,
- typeparsers
+ receivedGlobalLexical
} = namespace;
t.equal(avery, "Avery", "exports avery");
t.equal(brooke, "Brooke", "exports brooke");
t.equal(clarke, "Clarke", "exports clarke");
- t.equal(danny, "Danny", "exports danny");
t.equal(builtin, "builtin", "exports builtin");
@@ -56,25 +49,9 @@ const assertFixture = (t, namespace) => {
globalLexicals.globalLexical,
"exports global lexical"
);
- t.deepEqual(
- typecommon,
- [42, 42, 42, 42],
- "type=common package carries exports"
- );
- t.deepEqual(
- typemodule,
- [42, 42, 42, 42],
- "type=module package carries exports"
- );
- t.deepEqual(
- typeparsers,
- [42, 42, 42, 42],
- "parsers-specifying package carries exports"
- );
- t.equal(typehybrid, 42, "type=module and module= package carries exports");
};
-const fixtureAssertionCount = 11;
+const fixtureAssertionCount = 6;
// The "create builtin" test prepares a builtin module namespace object that
// gets threaded into all subsequent tests to satisfy the "builtin" module

MODIFY
packages/compartment-mapper/test/node_modules/app/main.js
packages/compartment-mapper/test/node_modules/app/main.js
/* global globalProperty, globalLexical */
-import typemodule from 'typemodule';
-import typecommon from 'typecommon';
-import typehybrid from 'typehybrid';
-import typeparsers from 'typeparsers';
-
-export { typemodule, typecommon, typehybrid, typeparsers };
-
export { avery } from 'avery';
export { brooke } from 'brooke';
export { clarke } from 'clarke';
-export { danny } from 'danny';
export { builtin } from 'builtin';
export const receivedGlobalProperty = globalProperty;

MODIFY
packages/compartment-mapper/test/node_modules/app/package.json
packages/compartment-mapper/test/node_modules/app/package.json
"dependencies": {
"avery": "^1.0.0",
"brooke": "^1.0.0",
- "clarke": "^1.0.0",
- "danny": "^1.0.0",
- "typecommon": "^1.0.0",
- "typemodule": "^1.0.0",
- "typehybrid": "^1.0.0",
- "typeparsers": "^1.0.0"
+ "clarke": "^1.0.0"
}
}

DELETE
packages/compartment-mapper/test/parse-requires.test.js
-import tape from "tape";
-import { parseRequires } from "../src/parse-requires.js";
-
-const { test } = tape;
-
-test("parse unique require calls", t => {
- t.plan(1);
- const code = `
- require("b"); // sorted later
-
- // require("bogus"); // not discovered
-
- function square(require) {
- require("shadowed"); // over-shadowed by argument
- }
-
- require("a");
-
- function c() {
- require("c"); // found despite inner scope
- }
-
- require("a"); // de-duplicated
-
- require( "d" ); // such space
-
- require("bogus", "bogus", "bogus");
-
- require("id\\""); // found, despite inner quote
- `;
- const requires = parseRequires(code);
- t.deepEqual(requires, ["a", "b", "c", "d", 'id"']);
-});

fix(compartment-mapper)!: Temporarily disable CommonJS