feat(endo): Improve JSON file parse error messages
MODIFY
packages/endo/src/archive.js
packages/endo/src/archive.js
@@ -6,6 +6,7 @@ import { parseExtension } from "./extension.js";
import { compartmentMapForNodeModules } from "./compartmap.js";
import { search } from "./search.js";
import { assemble } from "./assemble.js";
+import * as json from "./json.js";
const { entries, freeze, fromEntries, values } = Object;
@@ -165,10 +166,14 @@ export const makeArchive = async (read, moduleLocation) => {
const {
packageLocation,
packageDescriptorText,
+ packageDescriptorLocation,
moduleSpecifier
} = await search(read, moduleLocation);
- const packageDescriptor = JSON.parse(packageDescriptorText);
+ const packageDescriptor = json.parse(
+ packageDescriptorText,
+ packageDescriptorLocation
+ );
const compartmentMap = await compartmentMapForNodeModules(
read,
packageLocation,

MODIFY
packages/endo/src/compartmap.js
packages/endo/src/compartmap.js
/* eslint no-shadow: 0 */
import { inferExports } from "./infer-exports.js";
+import * as json from "./json.js";
const { create, entries, keys, values } = Object;
@@ -9,7 +10,7 @@ const decoder = new TextDecoder();
// q, as in quote, for enquoting strings in error messages.
const q = JSON.stringify;
-const resolve = (rel, abs) => new URL(rel, abs).toString();
+const resolveLocation = (rel, abs) => new URL(rel, abs).toString();
const basename = location => {
const { pathname } = new URL(location);
@@ -21,13 +22,15 @@ const basename = location => {
};
const readDescriptor = async (read, packageLocation) => {
- const descriptorPath = resolve("package.json", packageLocation);
- const descriptorBytes = await read(descriptorPath).catch(_error => undefined);
+ const descriptorLocation = resolveLocation("package.json", packageLocation);
+ const descriptorBytes = await read(descriptorLocation).catch(
+ _error => undefined
+ );
if (descriptorBytes === undefined) {
return undefined;
}
const descriptorText = decoder.decode(descriptorBytes);
- const descriptor = JSON.parse(descriptorText);
+ const descriptor = json.parse(descriptorText, descriptorLocation);
return descriptor;
};
@@ -49,14 +52,14 @@ const readDescriptorWithMemo = async (memo, read, packageLocation) => {
// find it efficiently.
const findPackage = async (readDescriptor, directory, name) => {
for (;;) {
- const packageLocation = resolve(`node_modules/${name}/`, directory);
+ const packageLocation = resolveLocation(`node_modules/${name}/`, directory);
// eslint-disable-next-line no-await-in-loop
const packageDescriptor = await readDescriptor(packageLocation);
if (packageDescriptor !== undefined) {
return { packageLocation, packageDescriptor };
}
- const parent = resolve("../", directory);
+ const parent = resolveLocation("../", directory);
if (parent === directory) {
return undefined;
}
@@ -64,7 +67,7 @@ const findPackage = async (readDescriptor, directory, name) => {
const base = basename(directory);
if (base === "node_modules") {
- directory = resolve("../", directory);
+ directory = resolveLocation("../", directory);
if (parent === directory) {
return undefined;
}

MODIFY
packages/endo/src/import-archive.js
packages/endo/src/import-archive.js
import { readZip } from "./zip.js";
import { assemble } from "./assemble.js";
import { parserForLanguage } from "./parse.js";
+import * as json from "./json.js";
const decoder = new TextDecoder();
@@ -35,7 +36,7 @@ export const parseArchive = async (archiveBytes, archiveLocation) => {
const compartmentMapBytes = await archive.read("compartmap.json");
const compartmentMapText = decoder.decode(compartmentMapBytes);
- const compartmentMap = JSON.parse(compartmentMapText);
+ const compartmentMap = json.parse(compartmentMapText, "compartmap.json");
const execute = (endowments, modules) => {
const { compartments, entry: moduleSpecifier } = compartmentMap;

MODIFY
packages/endo/src/import.js
packages/endo/src/import.js
@@ -4,6 +4,7 @@ import { compartmentMapForNodeModules } from "./compartmap.js";
import { search } from "./search.js";
import { assemble } from "./assemble.js";
import { parseExtension } from "./extension.js";
+import * as json from "./json.js";
const decoder = new TextDecoder();
@@ -72,10 +73,14 @@ export const loadLocation = async (read, moduleLocation) => {
const {
packageLocation,
packageDescriptorText,
+ packageDescriptorLocation,
moduleSpecifier
} = await search(read, moduleLocation);
- const packageDescriptor = JSON.parse(packageDescriptorText);
+ const packageDescriptor = json.parse(
+ packageDescriptorText,
+ packageDescriptorLocation
+ );
const compartmentMap = await compartmentMapForNodeModules(
read,
packageLocation,

ADD
packages/endo/src/json.js
+export const parse = (source, location) => {
+ try {
+ return JSON.parse(source);
+ } catch (error) {
+ if (error instanceof SyntaxError) {
+ throw new SyntaxError(`Cannot parse JSON from ${location}, ${error}`);
+ }
+ throw error;
+ }
+};

MODIFY
packages/endo/src/parse.js
packages/endo/src/parse.js
import { parseRequires } from "./parse-requires.js";
import { parseExtension } from "./extension.js";
+import * as json from "./json.js";
const { entries, freeze, fromEntries } = Object;
@@ -73,13 +74,7 @@ export const parseCjs = (source, _specifier, location, packageLocation) => {
export const parseJson = (source, _specifier, location, _packageLocation) => {
const imports = freeze([]);
const execute = exports => {
- try {
- exports.default = JSON.parse(source);
- } catch (error) {
- throw new SyntaxError(
- `Cannot parse JSON module at ${location}, ${error}`
- );
- }
+ exports.default = json.parse(source, location);
};
return {
parser: "json",

MODIFY
packages/endo/src/search.js
packages/endo/src/search.js
@@ -29,6 +29,7 @@ export const search = async (read, moduleLocation) => {
return {
packageLocation: directory,
packageDescriptorText,
+ packageDescriptorLocation,
moduleSpecifier: relativize(relative(directory, moduleLocation))
};
}

feat(endo): Improve JSON file parse error messages (#430)