endo
endo copied to clipboard
support dynamic requires
Description
-
This PR adds support for dynamic requires via
loadFromMap()andlink()(inimport-lite.jsandlink.js, respectively).importLocation()'s signature has also been modified for support.To use this feature in either function, the following must be true:
- The
moduleTransformsoption (in the appropriateoptionsparameter) must not be present. These are asynchronous module transforms, which cannot be used by dynamic require. - The
ReadPowersparam must be a properReadPowersobject (not just aReadFn) and must contain both the newmaybeReadSync, newisAbsolute, andfileURLToPathfunctions. The new type representing this isSyncReadPowers.
If all of the above are true, then a compartment will be allowed to dynamically require something. If that thing still cannot be found in the compartment map, the sync "fallback" exit hook (see next item) will be executed.
- The
-
The new
importHookNowproperty can be provided via options, which is a synchronous exit module import hook. -
About
SyncReadPowers:SyncReadPowers.maybeReadSync()is necessary to read files synchronously which is necessary to load them synchronously. This fails gracefully if the file is not found; the implementation is platform-dependent.SyncReadPowers.isAbsolute()is necessary to determine if the module specifier of a dynamic require is absolute. If it is, it could be just about anything, and must be loaded via the user-providedimportNowHook(the sync exit module import hook).SyncReadPowers.fileURLToPath()is needed for__filenameand__dirname, which is often used to create absolute paths for requiring dynamically.
-
As an alternative to
moduleTransforms, synchronous module transforms may be provided via the newsyncModuleTransformsobject. In a non-dynamic-require use-case, if present,syncModuleTransformsare combined with themoduleTransformsoption; all sync module transforms are module transforms, but not all module transforms are sync module transforms. -
All builtin parsers are now synchronous. User-defined parsers can be async, but this will disable dynamic require support.
-
@endo/evasive-transformnow exportsevadeCensorSync()in addition toevadeCensor(). This is possible because I've swapped the async-only source-map with source-map-js, which is a fork of the former before it went async-only.source-map-jsclaims comparable performance.
Security Considerations
Dynamically requiring an exit module (e.g., a Node.js builtin) requires a user-defined hook, which has the same security considerations as a user-defined exit module hook.
Swapping out a dependency (source-map-js for source-map) incurs risk.
Scaling Considerations
n/a
Documentation Considerations
Should be announced as a user-facing feature
Testing Considerations
I've added some fixtures and tested around the conditionals I've added, but am open to any suggestions for additional coverage.
Compatibility Considerations
This increases ecosystem compatibility considerably; use of dynamic require in the ecosystem is not rare.
For example, most packages which ship a native module will be using dynamic require, because the filepath of the build artifact is dependent upon the platform and architecture.
Upgrade Considerations
Everything else should be backwards-compatible, as long as source-map-js does as it says on the tin.
Users of @endo/evasive-transform may note that native modules are neither downloaded/compiled (due to the switch from source-map to source-map-js).
UPDATE: Aug 18 2024
This PR now targets the boneskull/supertramp branch, which is PR #2263