-
Notifications
You must be signed in to change notification settings - Fork 79
feat(compartment-mapper): support mapping of undiscoverable packages #2856
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
This adds the `additionalPackageLocations` and `additionalPackageDetails` options to `mapNodeModules`. The former is a mapping of module location to one or more additional module locations. The latter is a data structure `mapNodeModules` will populate, which can then be provided to `captureFromMap` (which is in turn provided to `makeImportHookMaker()`. This is intended to be used when dynamic requires prevent `mapNodeModules` & `captureFromMap` from otherwise creating a complete `CompartmentMapDescriptor` due to its ignorance of what would be dynamically required. Archival is not a use-case. Also: - Fixed signature of `chooseModuleDescriptor` - Tweaked ESLint config to ignore my new fixture - Add options type for `translateGraph` - Fixed deprecation notice for `compartmentMapForNodeModules`; it was previously on the options object, but it should only be on the export. - `graphPackages` now accepts an optional `Graph` object to be able to re-use the same graph between runs
// XXX: this "works" but that's probably a conincidence! | ||
record.record?.imports?.push( | ||
`${compartmentDescriptors[additionalPackageLocation].name}${additionalPackageModuleSpecifier.substring(1)}`, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⬆️ 🚨 ⬆️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm expecting an explosion of edge cases here, starting with there being a bunch of record types and only one (or two?) have the structure of record.record
so it's a little specific to what you've been testing with.
Unless no other record shape needs to be affected by this.
I don't like the concept of stuffing imports on the records. It's obviously working if added early enough, but there might be a more gentle way.
I'd rather include the information necessary for this to work in the compartmentMap itself. What's preventing that?
const compartmentMap = await mapNodeModules(readPowers, entryModuleLocation, {
additionalModuleLocations,
// ~~additionalPackageDetails~~
});
If we managed to do that, the step to manually add imports here could end up being unnecessary.
A single map of referrer-target should be enough to feed graphPackages with information to add missing links to the list it gets from packageDescriptor
Calling load on each of the targets in the same place where we call load on attenuators would suffice to keep them retained.
I'm not 100% sure the links are even necessary. If we had multiple entries, made graphPackages accept multiple (or call it many times if that works) and then proceed to load each of them, it should be enough to reach them through importNow flow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a more precise wording of what I mean in a language that works better than English:
#2864
`${compartmentDescriptors[additionalPackageLocation].name}${additionalPackageModuleSpecifier.substring(1)}`, | ||
); | ||
} else { | ||
throw new Error( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the only way for this to happen would be to provide a broken CompartmentMapDescriptor
in the first place. IDK if it's worth worrying about.
packageLocation, | ||
packageDescriptor: { name }, | ||
} of moduleDetails) { | ||
node.dependencyLocations[name] ??= packageLocation; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems to be sufficient for associating a Node
with another Node
, but maybe there's a safer way to do so?
moduleDescriptor.retained = true; | ||
compartmentMap.compartments[packageLocation].retained = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure these are actually needed.
- @boneskull: check assumptions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
manually setting retained
should not be necessary and if it is, it suggests a flaw in the design.
Causing them to be retained should make other things easier too.
@@ -1075,12 +1142,54 @@ export const mapNodeModules = async ( | |||
parseLocatedJson(packageDescriptorText, packageDescriptorLocation) | |||
); | |||
|
|||
return compartmentMapForNodeModules( | |||
await Promise.all( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
afaict there is no memoization happening here that would make it so I shouldn't just spray promises everywhere
const entryModuleLocation = new URL( | ||
'fixtures-additional-modules/node_modules/goofy/index.js', | ||
import.meta.url, | ||
).href; | ||
|
||
const additionalModuleLocations = { | ||
[entryModuleLocation]: [ | ||
new URL('fixtures-additional-modules/config.js', import.meta.url).href, | ||
], | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the fixture here mimics something like webpack. we have a tool (goofy
) and that tool dynamically loads a config file (config.js
) from our application root.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review in progress, but I think there might be a less (opinion) invasive way to serve the same need.
That being said, I want to encourage others to review because this exists and my hypothetical other way doesn't (at least until I try)
// XXX: this "works" but that's probably a conincidence! | ||
record.record?.imports?.push( | ||
`${compartmentDescriptors[additionalPackageLocation].name}${additionalPackageModuleSpecifier.substring(1)}`, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm expecting an explosion of edge cases here, starting with there being a bunch of record types and only one (or two?) have the structure of record.record
so it's a little specific to what you've been testing with.
Unless no other record shape needs to be affected by this.
I don't like the concept of stuffing imports on the records. It's obviously working if added early enough, but there might be a more gentle way.
I'd rather include the information necessary for this to work in the compartmentMap itself. What's preventing that?
const compartmentMap = await mapNodeModules(readPowers, entryModuleLocation, {
additionalModuleLocations,
// ~~additionalPackageDetails~~
});
If we managed to do that, the step to manually add imports here could end up being unnecessary.
A single map of referrer-target should be enough to feed graphPackages with information to add missing links to the list it gets from packageDescriptor
Calling load on each of the targets in the same place where we call load on attenuators would suffice to keep them retained.
I'm not 100% sure the links are even necessary. If we had multiple entries, made graphPackages accept multiple (or call it many times if that works) and then proceed to load each of them, it should be enough to reach them through importNow flow.
moduleDescriptor.retained = true; | ||
compartmentMap.compartments[packageLocation].retained = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
manually setting retained
should not be necessary and if it is, it suggests a flaw in the design.
Causing them to be retained should make other things easier too.
Description
This adds the
additionalPackageLocations
andadditionalPackageDetails
options tomapNodeModules()
. The former is a mapping of module location to one or more additional module locations. The latter is a data structuremapNodeModules
will populate, which can then be provided tocaptureFromMap
(which is in turn provided tomakeImportHookMaker()
.This is intended to be used when dynamic requires prevent
mapNodeModules
&captureFromMap
from otherwise creating a completeCompartmentMapDescriptor
due to its ignorance of what would be dynamically required. Archival is not a use-case.Also:
chooseModuleDescriptor
translateGraph
compartmentMapForNodeModules
; it was previously on the options object, but it should only be on the export.graphPackages
now accepts an optionalGraph
object to be able to re-use the same graph between runsQuestions for Reviewers
AdditionalPackageDetails
type looks like the existingPackageDetails
type (with a new field) thus influencing the naming—but they are otherwise unrelated and because of this I did not extendPackageDetails
.imports
in aRecordModuleDescriptor
object in order to direct SES' module loading. Is there a better way? The things I stuff inimports
are also extremely dubious.Security Considerations
n/a
These changes do not meddle with systems loading untrusted code, afaik.
Scaling Considerations
Use of the new option is not free.
Documentation Considerations
n/a
Testing Considerations
I did add some tests, but could probably use more.
Compatibility Considerations
No.
Upgrade Considerations
This warrants an entry in
NEWS.md
since it is part of a public API.