@endo/compartment-mapper
Advanced tools
Comparing version 0.8.0 to 0.8.1
{ | ||
"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] | ||
*/ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
40
4601
188476
Updated@endo/zip@^0.2.29
Updatedses@^0.18.1