Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@endo/compartment-mapper

Package Overview
Dependencies
Maintainers
5
Versions
58
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@endo/compartment-mapper - npm Package Compare versions

Comparing version 0.8.0 to 0.8.1

src/bundle-cjs.js

19

package.json
{
"name": "@endo/compartment-mapper",
"version": "0.8.0",
"version": "0.8.1",
"description": "The compartment mapper assembles Node applications in a sandbox",

@@ -44,10 +44,10 @@ "keywords": [

"dependencies": {
"@endo/cjs-module-analyzer": "^0.2.28",
"@endo/static-module-record": "^0.7.15",
"@endo/zip": "^0.2.28",
"ses": "^0.18.0"
"@endo/cjs-module-analyzer": "^0.2.29",
"@endo/static-module-record": "^0.7.16",
"@endo/zip": "^0.2.29",
"ses": "^0.18.1"
},
"devDependencies": {
"@endo/eslint-config": "^0.5.1",
"ava": "^3.12.1",
"@endo/eslint-config": "^0.5.2",
"ava": "^5.1.0",
"babel-eslint": "^10.0.3",

@@ -61,3 +61,3 @@ "c8": "^7.7.3",

"eslint-plugin-prettier": "^3.4.1",
"prettier": "^1.19.1",
"prettier": "^2.8.0",
"typescript": "~4.8.4"

@@ -81,2 +81,3 @@ },

"prettier": {
"arrowParens": "avoid",
"trailingComma": "all",

@@ -99,3 +100,3 @@ "singleQuote": true,

},
"gitHead": "da16a94856482e36296b7cae16d715aa63344928"
"gitHead": "ab8d64ae6fc9c628a2d1c02d16bf9ef249f5c8dc"
}

@@ -255,3 +255,6 @@ // @ts-check

dev = false,
tags = new Set(),
captureSourceLocation = undefined,
searchSuffixes = undefined,
commonDependencies = undefined,
} = options || {};

@@ -266,4 +269,2 @@ const { read, computeSha512 } = unpackReadPowers(powers);

/** @type {Set<string>} */
const tags = new Set();
tags.add('endo');

@@ -283,3 +284,3 @@ tags.add('import');

moduleSpecifier,
{ dev },
{ dev, commonDependencies },
);

@@ -302,2 +303,3 @@

computeSha512,
searchSuffixes,
);

@@ -304,0 +306,0 @@

@@ -27,7 +27,7 @@ // @ts-check

import mjsSupport from './bundle-mjs.js';
import cjsSupport from './bundle-cjs.js';
const textEncoder = new TextEncoder();
/** quotes strings */
const q = JSON.stringify;
/** @type {Record<string, ParserImplementation>} */

@@ -74,9 +74,12 @@ const parserForLanguage = {

const source = compartmentSources[compartmentName][moduleSpecifier];
if (source) {
const { record, parser } = source;
if (source !== undefined) {
const { record, parser, deferredError } = source;
if (deferredError) {
throw new Error(
`Cannot bundle: encountered deferredError ${deferredError}`,
);
}
if (record) {
const {
imports = [],
reexports = [],
} = /** @type {PrecompiledStaticModuleInterface} */ (record);
const { imports = [], reexports = [] } =
/** @type {PrecompiledStaticModuleInterface} */ (record);
const resolvedImports = Object.create(null);

@@ -129,2 +132,28 @@ for (const importSpecifier of [...imports, ...reexports]) {

const implementationPerParser = {
'pre-mjs-json': mjsSupport,
'pre-cjs-json': cjsSupport,
};
function getRuntime(parser) {
return implementationPerParser[parser]
? implementationPerParser[parser].runtime
: `/*unknown parser:${parser}*/`;
}
function getBundlerKitForModule(module) {
const parser = module.parser;
if (!implementationPerParser[parser]) {
const warning = `/*unknown parser:${parser}*/`;
// each item is a function to avoid creating more in-memory copies of the source text etc.
return {
getFunctor: () => `(()=>{${warning}})`,
getCells: `{${warning}}`,
getFunctorCall: warning,
};
}
const getBundlerKit = implementationPerParser[parser].getBundlerKit;
return getBundlerKit(module);
}
/**

@@ -135,7 +164,19 @@ * @param {ReadFn} read

* @param {ModuleTransforms} [options.moduleTransforms]
* @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) => {
const { moduleTransforms } = options || {};
const {
moduleTransforms,
dev,
tags: tagsOption,
searchSuffixes,
commonDependencies,
} = options || {};
const tags = new Set(tagsOption);
const {
packageLocation,

@@ -147,5 +188,2 @@ packageDescriptorText,

/** @type {Set<string>} */
const tags = new Set();
const packageDescriptor = parseLocatedJson(

@@ -161,2 +199,3 @@ packageDescriptorText,

moduleSpecifier,
{ dev, commonDependencies },
);

@@ -176,2 +215,5 @@

compartments,
undefined,
undefined,
searchSuffixes,
);

@@ -204,2 +246,3 @@

}
const parsersInUse = new Set();
for (const module of modules) {

@@ -212,36 +255,6 @@ module.indexedImports = Object.fromEntries(

);
parsersInUse.add(module.parser);
module.bundlerKit = getBundlerKitForModule(module);
}
// Only support mjs format.
const problems = modules
.filter(module => module.parser !== 'pre-mjs-json')
.map(
({ moduleSpecifier, compartmentName, parser }) =>
`module ${moduleSpecifier} in compartment ${compartmentName} in language ${parser}`,
);
if (problems.length) {
throw new Error(
`Can only bundle applications that only have ESM (.mjs-type) modules, got ${problems.join(
', ',
)}`,
);
}
const exportsCellRecord = exportMap =>
''.concat(
...Object.keys(exportMap).map(
exportName => `\
${exportName}: cell(${q(exportName)}),
`,
),
);
const importsCellSetter = (exportMap, index) =>
''.concat(
...Object.entries(exportMap).map(
([exportName, [importName]]) => `\
${importName}: cells[${index}].${exportName}.set,
`,
),
);
const bundle = `\

@@ -251,54 +264,32 @@ 'use strict';

const functors = [
${''.concat(
...modules.map(
({ record: { __syncModuleProgram__ } }, i) =>
`\
// === functors[${i}] ===
${__syncModuleProgram__},
`,
),
)}\
${''.concat(...modules.map(m => m.bundlerKit.getFunctor()))}\
]; // functors end
function cell(name, value = undefined) {
const cell = (name, value = undefined) => {
const observers = [];
function set(newValue) {
value = newValue;
for (const observe of observers) {
return Object.freeze({
get: Object.freeze(() => {
return value;
}),
set: Object.freeze((newValue) => {
value = newValue;
for (const observe of observers) {
observe(value);
}
}),
observe: Object.freeze((observe) => {
observers.push(observe);
observe(value);
}
}
function get() {
return value;
}
function observe(observe) {
observers.push(observe);
observe(value);
}
return { get, set, observe, enumerable: true };
}
}),
enumerable: true,
});
};
const cells = [
${''.concat(
...modules.map(
({ record: { __fixedExportMap__, __liveExportMap__ } }) => `\
{
${exportsCellRecord(__fixedExportMap__)}${exportsCellRecord(__liveExportMap__)}\
},
`,
),
)}\
${''.concat(...modules.map(m => m.bundlerKit.getCells()))}\
];
${''.concat(
...modules.flatMap(({ index, indexedImports, record: { reexports } }) =>
reexports.map(
importSpecifier => `\
Object.defineProperties(cells[${index}], Object.getOwnPropertyDescriptors(cells[${indexedImports[importSpecifier]}]));
`,
),
),
)}\
${''.concat(...modules.map(m => m.bundlerKit.getReexportsWiring()))}\
const namespaces = cells.map(cells => Object.create(null, cells));
const namespaces = cells.map(cells => Object.freeze(Object.create(null, cells)));

@@ -309,43 +300,5 @@ for (let index = 0; index < namespaces.length; index += 1) {

function observeImports(map, importName, importIndex) {
for (const [name, observers] of map.get(importName)) {
const cell = cells[importIndex][name];
if (cell === undefined) {
throw new ReferenceError(\`Cannot import name \${name}\`);
}
for (const observer of observers) {
cell.observe(observer);
}
}
}
${''.concat(...Array.from(parsersInUse).map(parser => getRuntime(parser)))}
${''.concat(
...modules.map(
({
index,
indexedImports,
record: { __liveExportMap__, __fixedExportMap__ },
}) => `\
functors[${index}]({
imports(entries) {
const map = new Map(entries);
${''.concat(
...Object.entries(indexedImports).map(
([importName, importIndex]) => `\
observeImports(map, ${q(importName)}, ${importIndex});
`,
),
)}\
},
liveVar: {
${importsCellSetter(__liveExportMap__, index)}\
},
onceVar: {
${importsCellSetter(__fixedExportMap__, index)}\
},
importMeta: {},
});
`,
),
)}\
${''.concat(...modules.map(m => m.bundlerKit.getFunctorCall()))}\

@@ -352,0 +305,0 @@ return cells[cells.length - 1]['*'].get();

@@ -22,3 +22,3 @@ // @ts-check

// eslint-disable-next-line no-nested-ternary
export const stringCompare = (a, b) => ((a === b ? 0 : a < b ? -1 : 1));
export const stringCompare = (a, b) => (a === b ? 0 : a < b ? -1 : 1);

@@ -199,10 +199,4 @@ /**

const {
compartment,
module,
location,
parser,
exit,
deferredError,
} = moduleDescriptor;
const { compartment, module, location, parser, exit, deferredError } =
moduleDescriptor;
if (compartment !== undefined || module !== undefined) {

@@ -379,12 +373,4 @@ assertCompartmentModule(moduleDescriptor, path, url);

const {
location,
name,
label,
parsers,
types,
scopes,
modules,
...extra
} = compartment;
const { location, name, label, parsers, types, scopes, modules, ...extra } =
compartment;

@@ -391,0 +377,0 @@ assertEmptyObject(

@@ -99,2 +99,9 @@ // @ts-check

const module = modules[moduleSpecifier];
if (module === undefined) {
throw new Error(
`Cannot find module ${q(moduleSpecifier)} in package ${q(
packageLocation,
)} in archive ${q(archiveLocation)}`,
);
}
if (module.deferredError !== undefined) {

@@ -136,6 +143,3 @@ return postponeErrorToExecute(module.deferredError);

if (packageName !== undefined) {
const base = packageName
.split('/')
.slice(-1)
.join('/');
const base = packageName.split('/').slice(-1).join('/');
sourceLocation = `.../${join(base, moduleSpecifier)}`;

@@ -142,0 +146,0 @@ }

@@ -5,2 +5,3 @@ // @ts-check

/** @typedef {import('ses').StaticModuleType} StaticModuleType */
/** @typedef {import('ses').RedirectStaticModuleInterface} RedirectStaticModuleInterface */
/** @typedef {import('./types.js').ReadFn} ReadFn */

@@ -14,3 +15,2 @@ /** @typedef {import('./types.js').ReadPowers} ReadPowers */

import { parseExtension } from './extension.js';
import { unpackReadPowers } from './powers.js';

@@ -50,2 +50,15 @@

// Node.js default resolution allows for an incomplement specifier that does not include a suffix.
// https://nodejs.org/api/modules.html#all-together
const nodejsConventionSearchSuffixes = [
// LOAD_AS_FILE(X)
'.js',
'.json',
'.node',
// LOAD_INDEX(X)
'/index.js',
'/index.json',
'/index.node',
];
/**

@@ -55,5 +68,11 @@ * @param {ReadFn|ReadPowers} readPowers

* @param {Sources} sources
* @param {Record<string, CompartmentDescriptor>} compartments
* @param {Record<string, CompartmentDescriptor>} compartmentDescriptors
* @param {Record<string, any>} exitModules
* @param {HashFn=} computeSha512
* @param {Array<string>} searchSuffixes - Suffixes to search if the unmodified specifier is not found.
* Pass [] to emulate Node.js’s strict behavior.
* The default handles Node.js’s CommonJS behavior.
* Unlike Node.js, the Compartment Mapper lifts CommonJS up, more like a bundler,
* and does not attempt to vary the behavior of resolution depending on the
* language of the importing module.
* @returns {ImportHookMaker}

@@ -65,5 +84,6 @@ */

sources = Object.create(null),
compartments = Object.create(null),
compartmentDescriptors = Object.create(null),
exitModules = Object.create(null),
computeSha512 = undefined,
searchSuffixes = nodejsConventionSearchSuffixes,
) => {

@@ -79,2 +99,3 @@ // Set of specifiers for modules whose parser is not using heuristics to determine imports

shouldDeferError,
compartments,
) => {

@@ -85,4 +106,6 @@ // per-compartment:

sources[packageLocation] = packageSources;
const compartment = compartments[packageLocation] || {};
const { modules = Object.create(null) } = compartment;
const compartmentDescriptor = compartmentDescriptors[packageLocation] || {};
const { modules: moduleDescriptors = Object.create(null) } =
compartmentDescriptor;
compartmentDescriptor.modules = moduleDescriptors;

@@ -123,3 +146,3 @@ /**

const importHook = async moduleSpecifier => {
compartment.retained = true;
compartmentDescriptor.retained = true;

@@ -150,14 +173,8 @@ // per-module:

// Collate candidate locations for the moduleSpecifier per Node.js
// conventions.
const candidates = [];
if (moduleSpecifier === '.') {
candidates.push('./index.js');
} else {
candidates.push(moduleSpecifier);
if (parseExtension(moduleSpecifier) === '') {
candidates.push(
`${moduleSpecifier}.js`,
`${moduleSpecifier}/index.js`,
);
// Collate candidate locations for the moduleSpecifier,
// to support Node.js conventions and similar.
const candidates = [moduleSpecifier];
if (moduleSpecifier !== '.') {
for (const candidateSuffix of searchSuffixes) {
candidates.push(`${moduleSpecifier}${candidateSuffix}`);
}

@@ -169,2 +186,31 @@ }

for (const candidateSpecifier of candidates) {
const candidateModuleDescriptor = moduleDescriptors[candidateSpecifier];
if (candidateModuleDescriptor !== undefined) {
const { compartment: candidateCompartmentName = packageLocation } =
candidateModuleDescriptor;
const candidateCompartment = compartments[candidateCompartmentName];
if (candidateCompartment === undefined) {
throw new Error(
`compartment missing for candidate ${candidateSpecifier} in ${candidateCompartmentName}`,
);
}
// modify compartmentMap to include this redirect
const candidateCompartmentDescriptor =
compartmentDescriptors[candidateCompartmentName];
if (candidateCompartmentDescriptor === undefined) {
throw new Error(
`compartmentDescriptor missing for candidate ${candidateSpecifier} in ${candidateCompartmentName}`,
);
}
candidateCompartmentDescriptor.modules[moduleSpecifier] =
candidateModuleDescriptor;
// return a redirect
/** @type {RedirectStaticModuleInterface} */
const record = {
specifier: candidateSpecifier,
compartment: candidateCompartment,
};
return record;
}
// Using a specifier as a location.

@@ -202,3 +248,3 @@ // This is not always valid.

if (candidateSpecifier !== moduleSpecifier) {
modules[moduleSpecifier] = {
moduleDescriptors[moduleSpecifier] = {
module: candidateSpecifier,

@@ -205,0 +251,0 @@ compartment: packageLocation,

@@ -40,3 +40,9 @@ // @ts-check

export const loadLocation = async (readPowers, moduleLocation, options) => {
const { moduleTransforms = {}, dev = false } = options || {};
const {
moduleTransforms = {},
dev = false,
tags = new Set(),
searchSuffixes = undefined,
commonDependencies = undefined,
} = options || {};

@@ -56,4 +62,2 @@ const { read } = unpackReadPowers(readPowers);

);
/** @type {Set<string>} */
const tags = new Set();
const compartmentMap = await compartmentMapForNodeModules(

@@ -65,3 +69,3 @@ readPowers,

moduleSpecifier,
{ dev },
{ dev, commonDependencies },
);

@@ -71,10 +75,13 @@

const execute = async (options = {}) => {
const {
globals,
modules,
transforms,
__shimTransforms__,
Compartment,
} = options;
const makeImportHook = makeImportHookMaker(readPowers, packageLocation);
const { globals, modules, transforms, __shimTransforms__, Compartment } =
options;
const makeImportHook = makeImportHookMaker(
readPowers,
packageLocation,
undefined,
compartmentMap.compartments,
undefined,
undefined,
searchSuffixes,
);
const { compartment } = link(compartmentMap, {

@@ -81,0 +88,0 @@ makeImportHook,

@@ -7,3 +7,3 @@ // @ts-check

const { entries, fromEntries } = Object;
const { entries, fromEntries, assign } = Object;
const { isArray } = Array;

@@ -13,17 +13,37 @@

* @param {string} name - the name of the referrer package.
* @param {Object} exports - the `exports` field from a package.json
* @param {Object} browser - the `browser` field from a package.json
* @param {string} main - the `main` field from a package.json
* @yields {[string, string]}
*/
function* interpretBrowserExports(name, exports) {
if (typeof exports === 'string') {
yield [name, relativize(exports)];
function* interpretBrowserField(name, browser, main = 'index.js') {
if (typeof browser === 'string') {
yield ['.', relativize(browser)];
return;
}
if (Object(exports) !== exports) {
if (Object(browser) !== browser) {
throw new Error(
`Cannot interpret package.json browser property for package ${name}, must be string or object, got ${exports}`,
`Cannot interpret package.json browser property for package ${name}, must be string or object, got ${browser}`,
);
}
for (const [key, value] of entries(exports)) {
yield [join(name, key), relativize(value)];
for (const [key, value] of entries(browser)) {
// https://github.com/defunctzombie/package-browser-field-spec#ignore-a-module
if (value === false) {
// eslint-disable-next-line no-continue
continue;
}
// replace main export in object form
// https://github.com/defunctzombie/package-browser-field-spec/issues/16
if (key === main) {
yield ['.', relativize(value)];
// eslint-disable-next-line no-continue
continue;
}
// https://github.com/defunctzombie/package-browser-field-spec#replace-specific-files---advanced
if (key.startsWith('./') || key === '.') {
// local module replace
yield [key, relativize(value)];
} else {
// dependency replace
yield [key, value];
}
}

@@ -60,3 +80,7 @@ }

if (key.startsWith('./') || key === '.') {
yield* interpretExports(join(name, key), value, tags);
if (name === '.') {
yield* interpretExports(key, value, tags);
} else {
yield* interpretExports(join(name, key), value, tags);
}
} else if (tags.has(key)) {

@@ -79,6 +103,4 @@ yield* interpretExports(name, value, tags);

* @param {Object} packageDescriptor - the parsed body of a package.json file.
* @param {string} packageDescriptor.name
* @param {string} packageDescriptor.main
* @param {string} [packageDescriptor.module]
* @param {string} [packageDescriptor.browser]
* @param {Object} [packageDescriptor.exports]

@@ -92,3 +114,3 @@ * @param {Set<string>} tags - build tags about the target environment

export const inferExportsEntries = function* inferExportsEntries(
{ name, main, module, browser, exports },
{ main, module, exports },
tags,

@@ -106,10 +128,8 @@ types,

types[spec] = 'mjs';
yield [name, spec];
} else if (browser !== undefined && tags.has('browser')) {
yield* interpretBrowserExports(name, browser);
yield ['.', spec];
} else if (main !== undefined) {
yield [name, relativize(main)];
yield ['.', relativize(main)];
}
if (exports !== undefined) {
yield* interpretExports(name, exports, tags);
yield* interpretExports('.', exports, tags);
}

@@ -138,1 +158,46 @@ // TODO Otherwise, glob 'files' for all '.js', '.cjs', and '.mjs' entry

fromEntries(inferExportsEntries(descriptor, tags, types));
export const inferExportsAndAliases = (
descriptor,
externalAliases,
internalAliases,
tags,
types,
) => {
const { name, type, main, module, exports, browser } = descriptor;
// collect externalAliases from exports and main/module
assign(
externalAliases,
fromEntries(inferExportsEntries(descriptor, tags, types)),
);
// expose default module as package root
// may be overwritten by browser field
// see https://github.com/endojs/endo/issues/1363
if (module === undefined && exports === undefined) {
const defaultModule = main !== undefined ? relativize(main) : './index.js';
externalAliases['.'] = defaultModule;
// in commonjs, expose package root as default module
if (type !== 'module') {
internalAliases['.'] = defaultModule;
}
}
// if present, allow "browser" field to populate moduleMap
if (tags.has('browser') && browser !== undefined) {
for (const [specifier, target] of interpretBrowserField(
name,
browser,
main,
)) {
const specifierIsRelative =
specifier.startsWith('./') || specifier === '.';
// only relative entries in browser field affect external aliases
if (specifierIsRelative) {
externalAliases[specifier] = target;
}
internalAliases[specifier] = target;
}
}
};

@@ -79,3 +79,3 @@ // @ts-check

* @param {Record<string, ParserImplementation>} parserForLanguage
* @param {ModuleTransforms} transforms
* @param {ModuleTransforms} moduleTransforms
* @returns {ParseFn}

@@ -87,3 +87,3 @@ */

parserForLanguage,
transforms,
moduleTransforms,
) => {

@@ -100,12 +100,7 @@ return async (bytes, specifier, location, packageLocation, options) => {

} else {
if (!has(languageForExtension, extension)) {
throw new Error(
`Cannot parse module ${specifier} at ${location}, no parser configured for extension ${extension}`,
);
}
language = languageForExtension[extension];
language = languageForExtension[extension] || extension;
}
if (has(transforms, language)) {
({ bytes, parser: language } = await transforms[language](
if (has(moduleTransforms, language)) {
({ bytes, parser: language } = await moduleTransforms[language](
bytes,

@@ -133,3 +128,3 @@ specifier,

* @param {Record<string, ParserImplementation>} parserForLanguage
* @param {ModuleTransforms} transforms
* @param {ModuleTransforms} moduleTransforms
* @returns {ParseFn}

@@ -141,3 +136,3 @@ */

parserForLanguage,
transforms = {},
moduleTransforms = {},
) => {

@@ -160,3 +155,3 @@ const languageForExtensionEntries = [];

parserForLanguage,
transforms,
moduleTransforms,
);

@@ -222,2 +217,4 @@ };

if (moduleDescriptor !== undefined) {
// "foreignCompartmentName" refers to the compartment which
// may differ from the current compartment
const {

@@ -390,3 +387,9 @@ compartment: foreignCompartmentName = compartmentName,

const importHook = makeImportHook(location, name, parse, shouldDeferError);
const importHook = makeImportHook(
location,
name,
parse,
shouldDeferError,
compartments,
);
const moduleMapHook = makeModuleMapHook(

@@ -393,0 +396,0 @@ compartmentDescriptor,

@@ -27,5 +27,6 @@ // @ts-check

* @property {Array<string>} path
* @property {boolean} explicit
* @property {Record<string, string>} exports
* @property {Record<string, string>} dependencies - from module name to
* @property {boolean} explicitExports
* @property {Record<string, string>} internalAliases
* @property {Record<string, string>} externalAliases
* @property {Record<string, string>} dependencyLocations - from module name to
* location in storage.

@@ -38,3 +39,7 @@ * @property {Record<string, Language>} parsers - the parser for

import { inferExports } from './infer-exports.js';
/**
* @typedef {Record<string, {spec: string, alias: string}>} CommonDependencyDescriptors
*/
import { inferExportsAndAliases } from './infer-exports.js';
import { searchDescriptor } from './search.js';

@@ -44,2 +49,3 @@ import { parseLocatedJson } from './json.js';

import { pathCompare } from './compartment-map.js';
import { join } from './node-module-specifier.js';

@@ -232,2 +238,3 @@ const { assign, create, keys, values } = Object;

* @param {boolean} dev
* @param {CommonDependencyDescriptors} commonDependencyDescriptors
* @returns {Promise<undefined>}

@@ -243,2 +250,3 @@ */

dev,
commonDependencyDescriptors,
) => {

@@ -252,3 +260,7 @@ if (graph[packageLocation] !== undefined) {

console.warn(
`Package named ${q(name)} does not match location ${packageLocation}`,
`Package named ${q(
name,
)} does not match location ${packageLocation} got (${q(
packageDescriptor.name,
)})`,
);

@@ -273,2 +285,6 @@ }

const allDependencies = {};
assign(allDependencies, commonDependencyDescriptors);
for (const [name, { spec }] of Object.entries(commonDependencyDescriptors)) {
allDependencies[name] = spec;
}
assign(allDependencies, dependencies);

@@ -305,2 +321,3 @@ assign(allDependencies, peerDependencies);

optional,
commonDependencyDescriptors,
),

@@ -310,3 +327,3 @@ );

const { version = '', exports } = packageDescriptor;
const { version = '', exports: exportsDescriptor } = packageDescriptor;
/** @type {Record<string, Language>} */

@@ -322,2 +339,15 @@ const types = {};

/** @type {Record<string, string>} */
const externalAliases = {};
/** @type {Record<string, string>} */
const internalAliases = {};
inferExportsAndAliases(
packageDescriptor,
externalAliases,
internalAliases,
tags,
types,
);
Object.assign(result, {

@@ -327,5 +357,6 @@ name,

label: `${name}${version ? `-v${version}` : ''}`,
explicit: exports !== undefined,
exports: inferExports(packageDescriptor, tags, types),
dependencies: dependencyLocations,
explicitExports: exportsDescriptor !== undefined,
externalAliases,
internalAliases,
dependencyLocations,
types,

@@ -336,3 +367,3 @@ parsers: inferParsers(packageDescriptor, packageLocation),

await Promise.all(
values(result.exports).map(async item => {
values(result.externalAliases).map(async item => {
const descriptor = await readDescriptorUpwards(item);

@@ -346,2 +377,33 @@ if (descriptor && descriptor.type === 'module') {

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];
// ignore all internalAliases where the specifier or target is relative
const specifierIsRelative = specifier.startsWith('./') || specifier === '.';
// eslint-disable-next-line no-continue
if (specifierIsRelative) continue;
const targetIsRelative = target.startsWith('./') || target === '.';
// eslint-disable-next-line no-continue
if (targetIsRelative) continue;
const targetLocation = dependencyLocations[target];
if (targetLocation === undefined) {
throw new Error(
`Cannot find dependency ${target} for ${packageLocation}`,
);
}
dependencyLocations[specifier] = targetLocation;
}
return undefined;

@@ -354,3 +416,3 @@ };

* @param {Graph} graph - the partially build graph.
* @param {Record<string, string>} dependencies
* @param {Record<string, string>} dependencyLocations
* @param {string} packageLocation - location of the package of interest.

@@ -360,2 +422,3 @@ * @param {string} name - name of the package of interest.

* @param {boolean} optional - whether the dependency is optional
* @param {Object} [commonDependencyDescriptors] - dependencies to be added to all packages
*/

@@ -366,3 +429,3 @@ const gatherDependency = async (

graph,
dependencies,
dependencyLocations,
packageLocation,

@@ -372,2 +435,3 @@ name,

optional = false,
commonDependencyDescriptors,
) => {

@@ -387,3 +451,3 @@ const dependency = await findPackage(

}
dependencies[name] = dependency.packageLocation;
dependencyLocations[name] = dependency.packageLocation;
await graphPackage(

@@ -397,2 +461,3 @@ name,

false,
commonDependencyDescriptors,
);

@@ -418,2 +483,3 @@ };

* only this package).
* @param {Record<string,string>} [commonDependencies] - dependencies to be added to all packages
*/

@@ -427,2 +493,3 @@ const graphPackages = async (

dev,
commonDependencies = {},
) => {

@@ -453,2 +520,20 @@ const memo = create(null);

}
// 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);

@@ -466,2 +551,3 @@ await graphPackage(

dev,
commonDependencyDescriptors,
);

@@ -493,4 +579,4 @@ return graph;

node.path = path;
for (const name of keys(node.dependencies)) {
trace(graph, node.dependencies[name], [...path, name]);
for (const name of keys(node.dependencyLocations)) {
trace(graph, node.dependencyLocations[name], [...path, name]);
}

@@ -528,10 +614,17 @@ };

// corresponding compartment can import.
for (const packageLocation of keys(graph).sort()) {
const { name, path, label, dependencies, parsers, types } = graph[
packageLocation
];
for (const dependeeLocation of keys(graph).sort()) {
const {
name,
path,
label,
dependencyLocations,
internalAliases,
parsers,
types,
} = graph[dependeeLocation];
/** @type {Record<string, ModuleDescriptor>} */
const modules = Object.create(null);
const moduleDescriptors = Object.create(null);
/** @type {Record<string, ScopeDescriptor>} */
const scopes = Object.create(null);
/**

@@ -541,12 +634,16 @@ * @param {string} dependencyName

*/
const digest = (dependencyName, packageLocation) => {
const { exports, explicit } = graph[packageLocation];
for (const exportName of keys(exports).sort()) {
const module = exports[exportName];
modules[exportName] = {
const digestExternalAliases = (dependencyName, packageLocation) => {
const { externalAliases, explicitExports } = graph[packageLocation];
for (const exportPath of keys(externalAliases).sort()) {
const targetPath = externalAliases[exportPath];
// dependency name may be different from package's name,
// as in the case of browser field dependency replacements
const localPath = join(dependencyName, exportPath);
moduleDescriptors[localPath] = {
compartment: packageLocation,
module,
module: targetPath,
};
}
if (!explicit) {
// if the exports field is not present, then all modules must be accessible
if (!explicitExports) {
scopes[dependencyName] = {

@@ -558,14 +655,28 @@ compartment: packageLocation,

// Support reflexive package imports.
digest(name, entryPackageLocation);
digestExternalAliases(name, dependeeLocation);
// Support external package imports.
for (const dependencyName of keys(dependencies).sort()) {
const packageLocation = dependencies[dependencyName];
digest(dependencyName, packageLocation);
for (const dependencyName of keys(dependencyLocations).sort()) {
const dependencyLocation = dependencyLocations[dependencyName];
digestExternalAliases(dependencyName, dependencyLocation);
}
compartments[packageLocation] = {
// digest own internal aliases
for (const modulePath of keys(internalAliases).sort()) {
const facetTarget = internalAliases[modulePath];
const targetIsRelative =
facetTarget.startsWith('./') || facetTarget === '.';
if (targetIsRelative) {
// add target to moduleDescriptors
moduleDescriptors[modulePath] = {
compartment: dependeeLocation,
module: facetTarget,
};
}
}
compartments[dependeeLocation] = {
label,
name,
path,
location: packageLocation,
modules,
location: dependeeLocation,
modules: moduleDescriptors,
scopes,

@@ -595,2 +706,3 @@ parsers,

* @param {boolean} [options.dev]
* @param {Object} [options.commonDependencies]
* @returns {Promise<CompartmentMapDescriptor>}

@@ -606,3 +718,3 @@ */

) => {
const { dev = false } = options;
const { dev = false, commonDependencies } = options;
const { read, canonical } = unpackReadPowers(readPowers);

@@ -616,2 +728,3 @@ const graph = await graphPackages(

dev,
commonDependencies,
);

@@ -618,0 +731,0 @@

@@ -23,6 +23,7 @@ // @ts-check

const { requires: imports, exports, reexports } = analyzeCommonJS(
source,
location,
);
const {
requires: imports,
exports,
reexports,
} = analyzeCommonJS(source, location);

@@ -33,2 +34,4 @@ if (!exports.includes('default')) {

const cjsWrappedSource = `(function (require, exports, module, __filename, __dirname) { ${source} //*/\n})\n`;
const pre = textEncoder.encode(

@@ -39,11 +42,13 @@ JSON.stringify({

reexports,
source: `(function (require, exports, module, __filename, __dirname) { ${source} //*/\n})\n`,
source: cjsWrappedSource,
}),
);
const cjsFunctor = `${cjsWrappedSource}//# sourceURL=${location}\n`;
return {
parser: 'pre-cjs-json',
bytes: pre,
record: /** @type {import('ses').ThirdPartyStaticModuleInterface} */ (freeze(
{
record: /** @type {import('ses').ThirdPartyStaticModuleInterface} */ (
freeze({
imports: freeze(imports),

@@ -53,4 +58,5 @@ exports: freeze(exports),

execute: noopExecute,
},
)),
cjsFunctor,
})
),
};

@@ -57,0 +63,0 @@ };

@@ -20,6 +20,7 @@ // @ts-check

const { requires: imports, exports, reexports } = analyzeCommonJS(
source,
location,
);
const {
requires: imports,
exports,
reexports,
} = analyzeCommonJS(source, location);

@@ -26,0 +27,0 @@ if (!exports.includes('default')) {

@@ -0,1 +1,6 @@

// @ts-check
/** @typedef {import('./types.js').ReadFn} ReadFn */
/** @typedef {import('./types.js').CanonicalFn} CanonicalFn */
/** @typedef {import('./types.js').ReadPowers} ReadPowers */
/** @type {CanonicalFn} */

@@ -2,0 +7,0 @@ const canonicalShim = async path => path;

@@ -94,10 +94,6 @@ // @ts-check

export const search = async (read, moduleLocation) => {
const {
data,
directory,
location,
packageDescriptorLocation,
} = await searchDescriptor(moduleLocation, loc =>
readDescriptorDefault(read, loc),
);
const { data, directory, location, packageDescriptorLocation } =
await searchDescriptor(moduleLocation, loc =>
readDescriptorDefault(read, loc),
);

@@ -104,0 +100,0 @@ if (!data) {

@@ -192,2 +192,3 @@ // @ts-check

* @param {ShouldDeferError} shouldDeferError
* @param {Record<string, Compartment>} compartments
* @returns {ImportHook}

@@ -312,3 +313,6 @@ */

* @property {boolean} [dev]
* @property {Set<string>} [tags]
* @property {CaptureSourceLocationHook} [captureSourceLocation]
* @property {Array<string>} [searchSuffixes]
* @property {Record<string, string>} [commonDependencies]
*/
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc