Skip to content

Commit

Permalink
feat(compartment-mapper): Thread url into power makers for Windows su…
Browse files Browse the repository at this point in the history
…pport
  • Loading branch information
kriskowal committed Dec 8, 2021
1 parent 3bbe180 commit fedcc8c
Show file tree
Hide file tree
Showing 13 changed files with 131 additions and 30 deletions.
11 changes: 11 additions & 0 deletions packages/compartment-mapper/NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
User-visible changes to the compartment mapper:

# Next Release

- The `node-powers.js` module now exports `makeReadPowers` and
`makeWritePowers` which replace the deprecated functions `makeNodeReadPowers`
and `makeNodeWritePowers`.
The rename is necessary to facilitate a change to the signature of these
methods so that `url` may be accepted as another dependency, to facilitate
Windows support.
Both accept a bag of Node.js modules that must include `fs` and `url`.
The read powers may optionally take the `crypto` module.

# 0.5.2 (2021-11-16)

- Adds source URL suffixes to archives, such that the archive hash remains
Expand Down
2 changes: 2 additions & 0 deletions packages/compartment-mapper/node-powers.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export { makeReadPowers, makeWritePowers } from './src/node-powers.js';
// Deprecated:
export { makeNodeReadPowers, makeNodeWritePowers } from './src/node-powers.js';
4 changes: 3 additions & 1 deletion packages/compartment-mapper/src/node-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ const q = JSON.stringify;
* @param {string} abs - a fully qualified URL
* @returns {string}
*/
const resolveLocation = (rel, abs) => new URL(rel, abs).toString();
const resolveLocation = (rel, abs) => {
return new URL(rel, abs).toString();
};

/**
* @param {string} location
Expand Down
103 changes: 90 additions & 13 deletions packages/compartment-mapper/src/node-powers.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,46 @@
/** @typedef {import('./types.js').WritePowers} WritePowers */

/**
* @param {typeof import('fs')} fs
* @param {typeof import('crypto')} [crypto]
* @returns {ReadPowers}
* @param {string} location
*/
export const makeNodeReadPowers = (fs, crypto = undefined) => {
const fakeFileURLToPath = location => {
const url = new URL(location);
if (url.protocol !== 'file:') {
throw new Error(`Cannot convert URL to file path: ${location}`);
}
return url.pathname;
};

/**
* @param {string} path
*/
const fakePathToFileURL = path => {
return new URL(path, 'file://').toString();
};

/**
* The implementation of `makeReadPowers` and the deprecated
* `makeNodeReadPowers` handles the case when the `url` power is not provided,
* but `makeReadPowers` presents a type that requires `url`.
*
* @param {Object} args
* @param {typeof import('fs')} args.fs
* @param {typeof import('url')} [args.url]
* @param {typeof import('crypto')} [args.crypto]
*/
const makeReadPowersSloppy = ({ fs, url = undefined, crypto = undefined }) => {
const fileURLToPath =
url === undefined ? fakeFileURLToPath : url.fileURLToPath;
const pathToFileURL =
url === undefined ? fakePathToFileURL : url.pathToFileURL;

/**
* @param {string} location
*/
const read = async location => {
try {
return await fs.promises.readFile(new URL(location).pathname);
const path = fileURLToPath(location);
return await fs.promises.readFile(path);
} catch (error) {
throw new Error(error.message);
}
Expand All @@ -40,12 +69,12 @@ export const makeNodeReadPowers = (fs, crypto = undefined) => {
try {
if (location.endsWith('/')) {
const realPath = await fs.promises.realpath(
new URL(location).pathname.replace(/\/$/, ''),
fileURLToPath(location).replace(/\/$/, ''),
);
return new URL(`${realPath}/`, location).toString();
return `${pathToFileURL(realPath)}/`;
} else {
const realPath = await fs.promises.realpath(new URL(location).pathname);
return new URL(realPath, location).toString();
const realPath = await fs.promises.realpath(fileURLToPath(location));
return pathToFileURL(realPath).toString();
}
} catch {
return location;
Expand All @@ -65,21 +94,69 @@ export const makeNodeReadPowers = (fs, crypto = undefined) => {
};

/**
* @param {typeof import('fs')} fs
* @returns {WritePowers}
* The implementation of `makeWritePowers` and the deprecated
* `makeNodeWritePowers` handles the case when the `url` power is not provided,
* but `makeWritePowers` presents a type that requires `url`.
*
* @param {Object} args
* @param {typeof import('fs')} args.fs
* @param {typeof import('url')} [args.url]
*/
export const makeNodeWritePowers = fs => {
const makeWritePowersSloppy = ({ fs, url = undefined }) => {
const fileURLToPath =
url === undefined ? fakeFileURLToPath : url.fileURLToPath;

/**
* @param {string} location
* @param {Uint8Array} data
*/
const write = async (location, data) => {
try {
return await fs.promises.writeFile(new URL(location).pathname, data);
return await fs.promises.writeFile(fileURLToPath(location), data);
} catch (error) {
throw new Error(error.message);
}
};

return { write };
};

/**
* @param {Object} args
* @param {typeof import('fs')} args.fs
* @param {typeof import('url')} args.url
* @param {typeof import('crypto')} [args.crypto]
*/
export const makeReadPowers = makeReadPowersSloppy;

/**
* @param {Object} args
* @param {typeof import('fs')} args.fs
* @param {typeof import('url')} args.url
*/
export const makeWritePowers = makeWritePowersSloppy;

/**
* @deprecated in favor of makeReadPowers.
* It transpires that positional arguments needed to become an arguments bag to
* reasonably expand to multiple optional dependencies.
*
* @param {typeof import('fs')} fs
* @param {typeof import('crypto')} [crypto]
* @returns {ReadPowers}
*/
export const makeNodeReadPowers = (fs, crypto = undefined) => {
return makeReadPowersSloppy({ fs, crypto });
};

/**
* @deprecated in favor of makeWritePowers.
* It transpires that positional arguments needed to become an arguments bag to
* reasonably expand to multiple optional dependencies.
*
* @param {typeof import('fs')} fs
* @returns {WritePowers}
*/
export const makeNodeWritePowers = fs => {
return makeWritePowersSloppy({ fs });
};
Binary file modified packages/compartment-mapper/test/app.agar
Binary file not shown.
7 changes: 4 additions & 3 deletions packages/compartment-mapper/test/app.agar-make.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
import 'ses';
import fs from 'fs';
import crypto from 'crypto';
import url from 'url';
import { writeArchive } from '../archive.js';
import { makeNodeReadPowers, makeNodeWritePowers } from '../src/node-powers.js';
import { makeReadPowers, makeWritePowers } from '../src/node-powers.js';

const readPowers = makeNodeReadPowers(fs, crypto);
const { write } = makeNodeWritePowers(fs);
const readPowers = makeReadPowers({ fs, crypto, url });
const { write } = makeWritePowers({ fs, url });

const fixture = new URL(
'fixtures-0/node_modules/app/main.js',
Expand Down
5 changes: 3 additions & 2 deletions packages/compartment-mapper/test/scaffold.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'ses';
import fs from 'fs';
import crypto from 'crypto';
import url from 'url';
import { ZipReader, ZipWriter } from '@endo/zip';
import {
loadLocation,
Expand All @@ -12,9 +13,9 @@ import {
importArchive,
hashLocation,
} from '../index.js';
import { makeNodeReadPowers } from '../src/node-powers.js';
import { makeReadPowers } from '../src/node-powers.js';

export const readPowers = makeNodeReadPowers(fs, crypto);
export const readPowers = makeReadPowers({ fs, crypto, url });

const globals = {
globalProperty: 42,
Expand Down
4 changes: 3 additions & 1 deletion packages/compartment-mapper/test/test-bundle.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import 'ses';
import fs from 'fs';
import url from 'url';
import test from 'ava';
import { makeBundle, makeArchive, parseArchive } from '../index.js';
import { makeReadPowers } from '../node-powers.js';

const fixture = new URL(
'fixtures-0/node_modules/bundle/main.js',
import.meta.url,
).toString();

const read = async location => fs.promises.readFile(new URL(location).pathname);
const { read } = makeReadPowers({ fs, url });

const expectedLog = [
'foo',
Expand Down
5 changes: 3 additions & 2 deletions packages/compartment-mapper/test/test-cycle-cjs.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// import "./ses-lockdown.js";
import 'ses';
import fs from 'fs';
import url from 'url';
import test from 'ava';
import { loadLocation } from '../src/import.js';
import { makeNodeReadPowers } from '../src/node-powers.js';
import { makeReadPowers } from '../src/node-powers.js';

const readPowers = makeNodeReadPowers(fs);
const readPowers = makeReadPowers({ fs, url });
const { read } = readPowers;

test('reflexive CommonJS cyclic import', async t => {
Expand Down
5 changes: 3 additions & 2 deletions packages/compartment-mapper/test/test-cycle-mjs.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// import "./ses-lockdown.js";
import 'ses';
import fs from 'fs';
import url from 'url';
import test from 'ava';
import { loadLocation } from '../src/import.js';
import { makeNodeReadPowers } from '../src/node-powers.js';
import { makeReadPowers } from '../src/node-powers.js';

const readPowers = makeNodeReadPowers(fs);
const readPowers = makeReadPowers({ fs, url });
const { read } = readPowers;

test('reflexive cycle of ESM', async t => {
Expand Down
5 changes: 3 additions & 2 deletions packages/compartment-mapper/test/test-source-url-rewrite.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@ import 'ses';
import test from 'ava';
import fs from 'fs';
import crypto from 'crypto';
import url from 'url';
import { parseArchive, makeArchive } from '../index.js';
import { makeNodeReadPowers } from '../node-powers.js';
import { makeReadPowers } from '../node-powers.js';

const fixtureLocation = new URL(
'fixtures-stack/index.js',
import.meta.url,
).toString();

const readPowers = makeNodeReadPowers(fs, crypto);
const readPowers = makeReadPowers({ fs, crypto, url });

test('rewrite source url', async t => {
const locations = new Map();
Expand Down
5 changes: 3 additions & 2 deletions packages/compartment-mapper/test/test-stack.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import 'ses';
import test from 'ava';
import fs from 'fs';
import crypto from 'crypto';
import url from 'url';
import { importLocation, parseArchive, makeArchive } from '../index.js';
import { makeNodeReadPowers } from '../node-powers.js';
import { makeReadPowers } from '../node-powers.js';

const fixtureLocation = new URL(
'fixtures-stack/index.js',
import.meta.url,
).toString();

const readPowers = makeNodeReadPowers(fs, crypto);
const readPowers = makeReadPowers({ fs, crypto, url });

// This test confirms that the stack trace generated by an error in an archive
// will be influenced by the sourceURL assigned by the importer from the stable
Expand Down
5 changes: 3 additions & 2 deletions packages/compartment-mapper/test/test-transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
import 'ses';
import fs from 'fs';
import test from 'ava';
import url from 'url';
import { loadLocation } from '../src/import.js';
import { makeArchive } from '../src/archive.js';
import { parseArchive } from '../src/import-archive.js';
import { makeNodeReadPowers } from '../src/node-powers.js';
import { makeReadPowers } from '../src/node-powers.js';

const readPowers = makeNodeReadPowers(fs);
const readPowers = makeReadPowers({ fs, url });
const { read } = readPowers;

test('transforms applied to evaluation', async t => {
Expand Down

0 comments on commit fedcc8c

Please sign in to comment.