feat(compartment-mapper): support commonDependencies for injecting dependencies
MODIFY
packages/compartment-mapper/src/archive.js
packages/compartment-mapper/src/archive.js
@@ -255,6 +255,7 @@ const digestLocation = async (powers, moduleLocation, options) => {
tags = new Set(),
captureSourceLocation = undefined,
searchSuffixes = undefined,
+ commonDependencies = undefined,
} = options || {};
const { read, computeSha512 } = unpackReadPowers(powers);
const {
@@ -278,7 +279,7 @@ const digestLocation = async (powers, moduleLocation, options) => {
tags,
packageDescriptor,
moduleSpecifier,
- { dev },
+ { dev, commonDependencies },
);
const {

MODIFY
packages/compartment-mapper/src/bundle.js
packages/compartment-mapper/src/bundle.js
@@ -147,6 +147,7 @@ const sortedModules = (
* @param {boolean} [options.dev]
* @param {Set<string>} [options.tags]
* @param {Array<string>} [options.searchSuffixes]
+ * @param {Object} [options.commonDependencies]
* @returns {Promise<string>}
*/
export const makeBundle = async (read, moduleLocation, options) => {
@@ -155,6 +156,7 @@ export const makeBundle = async (read, moduleLocation, options) => {
dev,
tags: tagsOption,
searchSuffixes,
+ commonDependencies,
} = options || {};
const tags = new Set(tagsOption);
@@ -175,7 +177,7 @@ export const makeBundle = async (read, moduleLocation, options) => {
tags,
packageDescriptor,
moduleSpecifier,
- { dev },
+ { dev, commonDependencies },
);
const {

MODIFY
packages/compartment-mapper/src/import.js
packages/compartment-mapper/src/import.js
@@ -42,6 +42,7 @@ export const loadLocation = async (readPowers, moduleLocation, options) => {
dev = false,
tags = new Set(),
searchSuffixes = undefined,
+ commonDependencies = undefined,
} = options || {};
const { read } = unpackReadPowers(readPowers);
@@ -63,7 +64,7 @@ export const loadLocation = async (readPowers, moduleLocation, options) => {
tags,
packageDescriptor,
moduleSpecifier,
- { dev },
+ { dev, commonDependencies },
);
/** @type {ExecuteFn} */

MODIFY
packages/compartment-mapper/src/node-modules.js
packages/compartment-mapper/src/node-modules.js
* modules.
*/
+/**
+ * @typedef {Record<string, {spec: string, alias: string}>} CommonDependencyDescriptors
+ */
+
import { inferExportsAndAliases } from './infer-exports.js';
import { searchDescriptor } from './search.js';
import { parseLocatedJson } from './json.js';
@@ -228,6 +232,7 @@ const inferParsers = (descriptor, location) => {
* @param {Object} packageDetails.packageDescriptor
* @param {Set<string>} tags
* @param {boolean} dev
+ * @param {CommonDependencyDescriptors} commonDependencyDescriptors
* @returns {Promise<undefined>}
*/
const graphPackage = async (
@@ -238,6 +243,7 @@ const graphPackage = async (
{ packageLocation, packageDescriptor },
tags,
dev,
+ commonDependencyDescriptors,
) => {
if (graph[packageLocation] !== undefined) {
// Returning the promise here would create a causal cycle and stall recursion.
@@ -270,6 +276,10 @@ const graphPackage = async (
devDependencies = {},
} = packageDescriptor;
const allDependencies = {};
+ assign(allDependencies, commonDependencyDescriptors);
+ for (const [name, { spec }] of Object.entries(commonDependencyDescriptors)) {
+ allDependencies[name] = spec;
+ }
assign(allDependencies, dependencies);
assign(allDependencies, peerDependencies);
for (const [name, { optional }] of Object.entries(peerDependenciesMeta)) {
@@ -301,6 +311,7 @@ const graphPackage = async (
name,
tags,
optional,
+ commonDependencyDescriptors,
),
);
}
@@ -352,6 +363,17 @@ const graphPackage = async (
await Promise.all(children);
+ // handle commonDependencyDescriptors package aliases
+ for (const [name, { alias }] of Object.entries(commonDependencyDescriptors)) {
+ // update the dependencyLocations to point to the common dependency
+ const targetLocation = dependencyLocations[name];
+ if (targetLocation === undefined) {
+ throw new Error(
+ `Cannot find common dependency ${name} for ${packageLocation}`,
+ );
+ }
+ dependencyLocations[alias] = targetLocation;
+ }
// handle internalAliases package aliases
for (const specifier of keys(internalAliases).sort()) {
const target = internalAliases[specifier];
@@ -383,6 +405,7 @@ const graphPackage = async (
* @param {string} name - name of the package of interest.
* @param {Set<string>} tags
* @param {boolean} optional - whether the dependency is optional
+ * @param {Object} [commonDependencyDescriptors] - dependencies to be added to all packages
*/
const gatherDependency = async (
readDescriptor,
@@ -393,6 +416,7 @@ const gatherDependency = async (
name,
tags,
optional = false,
+ commonDependencyDescriptors,
) => {
const dependency = await findPackage(
readDescriptor,
@@ -416,6 +440,7 @@ const gatherDependency = async (
dependency,
tags,
false,
+ commonDependencyDescriptors,
);
};
@@ -436,6 +461,7 @@ const gatherDependency = async (
* package.json, which was already read when searching for the package.json.
* @param {boolean} dev - whether to use devDependencies from this package (and
* only this package).
+ * @param {Record<string,string>} [commonDependencies] - dependencies to be added to all packages
*/
const graphPackages = async (
read,
@@ -444,6 +470,7 @@ const graphPackages = async (
tags,
mainPackageDescriptor,
dev,
+ commonDependencies = {},
) => {
const memo = create(null);
/**
@@ -469,6 +496,24 @@ const graphPackages = async (
`Cannot find package.json for application at ${packageLocation}`,
);
}
+
+ // Resolve common dependencies.
+ /** @type {CommonDependencyDescriptors} */
+ const commonDependencyDescriptors = {};
+ const packageDescriptorDependencies = packageDescriptor.dependencies || {};
+ for (const [alias, dependencyName] of Object.entries(commonDependencies)) {
+ const spec = packageDescriptorDependencies[dependencyName];
+ if (spec === undefined) {
+ throw new Error(
+ `Cannot find dependency ${dependencyName} for ${packageLocation} from common dependencies`,
+ );
+ }
+ commonDependencyDescriptors[dependencyName] = {
+ spec,
+ alias,
+ };
+ }
+
const graph = create(null);
await graphPackage(
packageDescriptor.name,
@@ -481,6 +526,7 @@ const graphPackages = async (
},
tags,
dev,
+ commonDependencyDescriptors,
);
return graph;
};
@@ -630,6 +676,7 @@ const translateGraph = (
* @param {string} moduleSpecifier
* @param {Object} [options]
* @param {boolean} [options.dev]
+ * @param {Object} [options.commonDependencies]
* @returns {Promise<CompartmentMapDescriptor>}
*/
export const compartmentMapForNodeModules = async (
@@ -640,7 +687,7 @@ export const compartmentMapForNodeModules = async (
moduleSpecifier,
options = {},
) => {
- const { dev = false } = options;
+ const { dev = false, commonDependencies } = options;
const { read, canonical } = unpackReadPowers(readPowers);
const graph = await graphPackages(
read,
@@ -649,6 +696,7 @@ export const compartmentMapForNodeModules = async (
tags,
packageDescriptor,
dev,
+ commonDependencies,
);
trace(graph, packageLocation, []);

MODIFY
packages/compartment-mapper/src/types.js
packages/compartment-mapper/src/types.js
@@ -312,4 +312,5 @@ export {};
* @property {Set<string>} [tags]
* @property {CaptureSourceLocationHook} [captureSourceLocation]
* @property {Array<string>} [searchSuffixes]
+ * @property {Record<string, string>} [commonDependencies]
*/

ADD
packages/compartment-mapper/test/fixtures-common-deps/node_modules/app/index.js
+export { status } from 'common-dep-required';

ADD
packages/compartment-mapper/test/fixtures-common-deps/node_modules/app/package.json
+{
+ "name": "app",
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "preinstall": "echo DO NOT INSTALL TEST FIXTURES; exit -1"
+ },
+ "dependencies": {
+ "common-dep-required": "0.0.0",
+ "common-dep-target": "0.0.0"
+ }
+}

ADD
packages/compartment-mapper/test/fixtures-common-deps/node_modules/common-dep-required/index.js
+export { value as status } from 'unlisted-common-dep';

ADD
packages/compartment-mapper/test/fixtures-common-deps/node_modules/common-dep-required/package.json
+{
+ "name": "common-dep-required",
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "preinstall": "echo DO NOT INSTALL TEST FIXTURES; exit -1"
+ }
+}

ADD
packages/compartment-mapper/test/fixtures-common-deps/node_modules/common-dep-target/index.js
+export const value = 101;

ADD
packages/compartment-mapper/test/fixtures-common-deps/node_modules/common-dep-target/package.json
+{
+ "name": "common-dep-target",
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "preinstall": "echo DO NOT INSTALL TEST FIXTURES; exit -1"
+ }
+}

MODIFY
packages/compartment-mapper/test/scaffold.js
packages/compartment-mapper/test/scaffold.js
@@ -59,6 +59,7 @@ export function scaffold(
knownFailure = false,
tags = undefined,
searchSuffixes = undefined,
+ commonDependencies = undefined,
} = {},
) {
// wrapping each time allows for convenient use of test.only
@@ -93,6 +94,7 @@ export function scaffold(
dev: true,
tags,
searchSuffixes,
+ commonDependencies,
});
const { namespace } = await application.import({
globals,
@@ -113,6 +115,7 @@ export function scaffold(
dev: true,
tags,
searchSuffixes,
+ commonDependencies,
});
return namespace;
});
@@ -126,6 +129,7 @@ export function scaffold(
dev: true,
tags,
searchSuffixes,
+ commonDependencies,
});
const application = await parseArchive(archive, '<unknown>', {
modules: Object.fromEntries(
@@ -158,6 +162,7 @@ export function scaffold(
dev: true,
tags,
searchSuffixes,
+ commonDependencies,
});
const prefixArchive = new Uint8Array(archive.length + 10);
prefixArchive.set(archive, 10);
@@ -195,6 +200,7 @@ export function scaffold(
dev: true,
tags,
searchSuffixes,
+ commonDependencies,
});
const application = await loadArchive(fakeRead, 'app.agar', {
modules,
@@ -228,6 +234,7 @@ export function scaffold(
dev: true,
tags,
searchSuffixes,
+ commonDependencies,
});
const { namespace } = await importArchive(fakeRead, 'app.agar', {
globals,
@@ -248,6 +255,7 @@ export function scaffold(
dev: true,
tags,
searchSuffixes,
+ commonDependencies,
});
const archiveBytes = await makeArchive(readPowers, fixture, {
@@ -255,6 +263,7 @@ export function scaffold(
dev: true,
tags,
searchSuffixes,
+ commonDependencies,
});
const { computeSha512 } = readPowers;
@@ -282,6 +291,7 @@ export function scaffold(
dev: true,
tags,
searchSuffixes,
+ commonDependencies,
});
const archive = await makeArchive(readPowers, fixture, {
@@ -289,6 +299,7 @@ export function scaffold(
dev: true,
tags,
searchSuffixes,
+ commonDependencies,
});
const reader = new ZipReader(archive);

MODIFY
packages/compartment-mapper/test/test-main.js
packages/compartment-mapper/test/test-main.js
@@ -255,3 +255,21 @@ scaffold(
searchSuffixes: ['.abc.js'],
},
);
+
+scaffold(
+ 'provide commonDependencies',
+ test,
+ new URL(
+ 'fixtures-common-deps/node_modules/app/index.js',
+ import.meta.url,
+ ).toString(),
+ (t, { namespace: { status } }) => {
+ t.is(status, 101);
+ },
+ 1,
+ {
+ commonDependencies: {
+ 'unlisted-common-dep': 'common-dep-target',
+ },
+ },
+);

feat(compartment-mapper): support commonDependencies for injecting dependencies