Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(endo): Add command line #398

Merged
merged 8 commits into from
Aug 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/endo/bin/endo
5 changes: 5 additions & 0 deletions packages/endo/bin/endo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env node
import fs from "fs";
import { main } from "../src/cli.js";

main(process, { fs: fs.promises });
3 changes: 3 additions & 0 deletions packages/endo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
"require": "./dist/endo.cjs",
"browser": "./dist/endo.umd.js"
},
"bin": {
"endo": "./bin/endo.js"
},
"scripts": {
"build": "rollup --config rollup.config.js",
"clean": "rm -rf dist",
Expand Down
110 changes: 110 additions & 0 deletions packages/endo/src/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/* eslint no-shadow: [0] */
import "./lockdown.js";
import { writeArchive } from "./main.js";
import { search } from "./search.js";
import { compartmentMapForNodeModules } from "./compartmap.js";

function usage(message) {
console.error(message);
return 1;
}

async function noEntryUsage() {
return usage(`expected path to program`);
}

async function noArchiveUsage() {
return usage(`expected path for archive`);
}

async function subcommand([arg, ...rest], handlers) {
const keys = Object.keys(handlers);
if (arg === undefined || !keys.includes(arg)) {
return usage(`expected one of ${keys.join(", ")}`);
}
return handlers[arg](rest);
}

async function parameter(args, handle, usage) {
const [arg, ...rest] = args;
if (arg === undefined) {
return usage(`expected an argument`);
}
if (arg.startsWith("-")) {
return usage(`unexpected flag: ${arg}`);
}
return handle(arg, rest);
}

async function run(args, { cwd, read, write, stdout }) {
async function compartmap(args) {
async function handleEntry(applicationPath, args) {
if (args.length) {
return usage(`unexpected arguments: ${JSON.stringify(args)}`);
}
const currentLocation = new URL(`${cwd()}/`, "file:///");
const applicationLocation = new URL(applicationPath, currentLocation);
const { packageLocation } = await search(read, applicationLocation);
const compartmentMap = await compartmentMapForNodeModules(
read,
packageLocation
);
stdout.write(`${JSON.stringify(compartmentMap, null, 2)}\n`);
return 0;
}
return parameter(args, handleEntry, noEntryUsage);
}

async function archive(args) {
async function handleArchive(archivePath, args) {
async function handleEntry(applicationPath, args) {
if (args.length) {
return usage(`unexpected arguments: ${JSON.stringify(args)}`);
}
const currentLocation = new URL(`${cwd()}/`, "file:///");
const archiveLocation = new URL(archivePath, currentLocation);
const applicationLocation = new URL(applicationPath, currentLocation);
await writeArchive(write, read, archiveLocation, applicationLocation);
return 0;
}
return parameter(args, handleEntry, noEntryUsage);
}
return parameter(args, handleArchive, noArchiveUsage);
}

return subcommand(args, { compartmap, archive });
}

export async function main(process, modules) {
const { fs } = modules;
const { cwd, stdout } = process;

// Filesystem errors often don't have stacks:

async function read(location) {
try {
return await fs.readFile(new URL(location).pathname);
} catch (error) {
throw new Error(error.message);
}
}

async function write(location, content) {
try {
return await fs.writeFile(new URL(location).pathname, content);
} catch (error) {
throw new Error(error.message);
}
}

try {
process.exitCode = await run(process.argv.slice(2), {
read,
write,
cwd,
stdout
});
} catch (error) {
process.exitCode = usage(error.stack || error.message);
}
}
2 changes: 1 addition & 1 deletion packages/endo/src/compartmap.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ const inferParsers = (type, location) => {
// that the package exports.

const graphPackage = async (
name,
name = "",
readDescriptor,
graph,
{ packageLocation, packageDescriptor },
Expand Down
19 changes: 12 additions & 7 deletions packages/endo/src/import-archive.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ const makeArchiveImportHookMaker = archive => {
return makeImportHook;
};

export const parseArchive = async archiveBytes => {
const archive = await readZip(archiveBytes);
export const parseArchive = async (archiveBytes, archiveLocation) => {
const archive = await readZip(archiveBytes, archiveLocation);

const compartmentMapBytes = await archive.read("compartmap.json");
const compartmentMapText = decoder.decode(compartmentMapBytes);
Expand All @@ -47,12 +47,17 @@ export const parseArchive = async archiveBytes => {
return { execute };
};

export const loadArchive = async (read, archivePath) => {
const archiveBytes = await read(archivePath);
return parseArchive(archiveBytes);
export const loadArchive = async (read, archiveLocation) => {
const archiveBytes = await read(archiveLocation);
return parseArchive(archiveBytes, archiveLocation);
};

export const importArchive = async (read, archivePath, endowments, modules) => {
const archive = await loadArchive(read, archivePath);
export const importArchive = async (
read,
archiveLocation,
endowments,
modules
) => {
const archive = await loadArchive(read, archiveLocation);
return archive.execute(endowments, modules);
};
12 changes: 10 additions & 2 deletions packages/endo/src/zip.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@

import JSZip from "jszip";

export const readZip = async data => {
export const readZip = async (data, location) => {
const zip = new JSZip();
await zip.loadAsync(data);
const read = async path => zip.file(path).async("uint8array");
const read = async path => {
const file = zip.file(path);
if (file === undefined) {
throw new Error(
`Cannot find file to read ${path} in archive ${location}`
);
}
return file.async("uint8array");
};
return { read };
};

Expand Down