feat(endo): Search for .js or /.index.js
MODIFY
packages/endo/README.md
packages/endo/README.md
@@ -226,15 +226,9 @@ should be vended out to applications.
The file set implicitly includes all `**.js`, `**.mjs`, and `**.cjs` files.
The file set implicitly excludes anything under `node_modules`.
-> TODO
->
-> In Node.js, a module specifier that corresponds to a directory implicitly
-> redirects to the underlying `index.js` file.
-> Capturing a full list of `files` in a browser compartment map would allow
-> a compartment to follow these redirects without chancing a wasted round trip
-> to get a File not Found error.
-> This could alternately be solved by inferring the redirect when an internal
-> module specifier has no extension.
+With Endo just as in Node.js, a module specifier that has no extension may
+refer either to the file with the `js` extension, or if that file does not
+exist, to the `index.js` file in the directory with the same name.
> TODO
>

MODIFY
packages/endo/src/archive.js
packages/endo/src/archive.js
import { writeZip } from "./zip.js";
import { resolve, join } from "./node-module-specifier.js";
+import { parseExtension } from "./extension.js";
import { compartmentMapForNodeModules } from "./compartmap.js";
import { search } from "./search.js";
import { assemble } from "./assemble.js";
@@ -23,19 +24,21 @@ const makeRecordingImportHookMaker = (read, baseLocation, manifest, errors) => {
packageLocation = resolveLocation(packageLocation, baseLocation);
const importHook = async moduleSpecifier => {
// per-module:
- const moduleLocation = new URL(
- moduleSpecifier,
- packageLocation
- ).toString();
- const moduleBytes = await read(moduleLocation).catch(_error => undefined);
+ const candidates = [moduleSpecifier];
+ if (parseExtension(moduleSpecifier) === "") {
+ candidates.push(`${moduleSpecifier}.js`, `${moduleSpecifier}/index.js`);
+ }
+ for (const candidate of candidates) {
+ const moduleLocation = new URL(candidate, packageLocation).toString();
+ // eslint-disable-next-line no-await-in-loop
+ const moduleBytes = await read(moduleLocation).catch(
+ _error => undefined
+ );
if (moduleBytes === undefined) {
errors.push(
- `missing ${q(moduleSpecifier)} needed for package ${q(
- packageLocation
- )}`
+ `missing ${q(candidate)} needed for package ${q(packageLocation)}`
);
- return new StaticModuleRecord("// Module not found", moduleLocation);
- }
+ } else {
const moduleSource = decoder.decode(moduleBytes);
const packageManifest = manifest[packageLocation] || {};
@@ -43,6 +46,9 @@ const makeRecordingImportHookMaker = (read, baseLocation, manifest, errors) => {
packageManifest[moduleSpecifier] = moduleBytes;
return parse(moduleSource, moduleLocation);
+ }
+ }
+ return new StaticModuleRecord("// Module not found", moduleSpecifier);
};
return importHook;
};

ADD
packages/endo/src/lockdown.js
+/* global lockdown */
+import "ses";
+
+lockdown({
+ errorTaming: "unsafe"
+});

feat(endo): Search for .js or /.index.js (#401)