Skip to content

Commit 705e623

Browse files
esm: remove globalPreload hook (superseded by initialize)
PR-URL: #49144 Reviewed-By: Geoffrey Booth <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
1 parent ce30b4e commit 705e623

File tree

9 files changed

+31
-429
lines changed

9 files changed

+31
-429
lines changed

‎doc/api/module.md‎

Lines changed: 3 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ import('node:fs').then((esmFS) =>{
149149
<!-- YAML
150150
added: v8.8.0
151151
changes:
152+
- version: REPLACEME
153+
pr-url: https://github.com/nodejs/node/pull/49144
154+
description: Removed `globalPreload`.
152155
- version: v20.6.0
153156
pr-url: https://github.com/nodejs/node/pull/48842
154157
description: Added `initialize` hook to replace `globalPreload`.
@@ -674,79 +677,6 @@ export async function load(url, context, nextLoad){
674677
In a more advanced scenario, this can also be used to transform an unsupported
675678
source to a supported one (see [Examples](#examples) below).
676679
677-
#### `globalPreload()`
678-
679-
<!-- YAML
680-
changes:
681-
- version:
682-
- v18.6.0
683-
- v16.17.0
684-
pr-url: https://github.com/nodejs/node/pull/42623
685-
description: Add support for chaining globalPreload hooks.
686-
-->
687-
688-
> Stability: 1.0 - Early development
689-
690-
> **Warning:** This hook will be removed in a future version. Use
691-
> [`initialize`][] instead. When a hooks module has an `initialize` export,
692-
> `globalPreload` will be ignored.
693-
694-
* `context`{Object} Information to assist the preload code
695-
* `port`{MessagePort}
696-
* Returns:{string} Code to run before application startup
697-
698-
Sometimes it might be necessary to run some code inside of the same global
699-
scope that the application runs in. This hook allows the return of a string
700-
that is run as a sloppy-mode script on startup.
701-
702-
Similar to how CommonJS wrappers work, the code runs in an implicit function
703-
scope. The only argument is a `require`-like function that can be used to load
704-
builtins like "fs": `getBuiltin(request: string)`.
705-
706-
If the code needs more advanced `require` features, it has to construct
707-
its own `require` using `module.createRequire()`.
708-
709-
```mjs
710-
exportfunctionglobalPreload(context){
711-
return`\
712-
globalThis.someInjectedProperty = 42;
713-
console.log('I just set some globals!');
714-
715-
const{createRequire } = getBuiltin('module');
716-
const{cwd } = getBuiltin('process');
717-
718-
const require = createRequire(cwd() + '/<preload>');
719-
// [...]
720-
`;
721-
}
722-
```
723-
724-
Another argument is provided to the preload code: `port`. This is available as a
725-
parameter to the hook and inside of the source text returned by the hook. This
726-
functionality has been moved to the `initialize` hook.
727-
728-
Care must be taken in order to properly call [`port.ref()`][] and
729-
[`port.unref()`][] to prevent a process from being in a state where it won't
730-
close normally.
731-
732-
```mjs
733-
/**
734-
* This example has the application context send a message to the hook
735-
* and sends the message back to the application context
736-
*/
737-
exportfunctionglobalPreload({port }){
738-
port.onmessage= (evt) =>{
739-
port.postMessage(evt.data);
740-
};
741-
return`\
742-
port.postMessage('console.log("I went to the hook and back");');
743-
port.onmessage = (evt) =>{
744-
eval(evt.data);
745-
};
746-
`;
747-
}
748-
```
749-
750680
### Examples
751681
752682
The various module customization hooks can be used together to accomplish
@@ -1105,8 +1035,6 @@ returned object contains the following keys:
11051035
[`Uint8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
11061036
[`initialize`]: #initialize
11071037
[`module`]: modules.md#the-module-object
1108-
[`port.ref()`]: worker_threads.md#portref
1109-
[`port.unref()`]: worker_threads.md#portunref
11101038
[`register`]: #moduleregisterspecifier-parenturl-options
11111039
[`string`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String
11121040
[`util.TextDecoder`]: util.md#class-utiltextdecoder

‎lib/internal/modules/esm/hooks.js‎

Lines changed: 12 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33
const{
44
ArrayPrototypePush,
55
ArrayPrototypePushApply,
6-
FunctionPrototypeCall,
76
Int32Array,
87
ObjectAssign,
98
ObjectDefineProperty,
109
ObjectSetPrototypeOf,
1110
Promise,
1211
SafeSet,
1312
StringPrototypeSlice,
14-
StringPrototypeStartsWith,
1513
StringPrototypeToUpperCase,
1614
globalThis,
1715
}=primordials;
@@ -33,7 +31,6 @@ const{
3331
ERR_INVALID_RETURN_VALUE,
3432
ERR_LOADER_CHAIN_INCOMPLETE,
3533
ERR_METHOD_NOT_IMPLEMENTED,
36-
ERR_UNKNOWN_BUILTIN_MODULE,
3734
ERR_WORKER_UNSERIALIZABLE_ERROR,
3835
}=require('internal/errors').codes;
3936
const{exitCodes: { kUnfinishedTopLevelAwait }}=internalBinding('errors');
@@ -49,7 +46,6 @@ const{
4946
validateString,
5047
}=require('internal/validators');
5148
const{
52-
emitExperimentalWarning,
5349
kEmptyObject,
5450
}=require('internal/util');
5551

@@ -73,8 +69,6 @@ let importMetaInitializer;
7369

7470
/**
7571
* @typedef{object} ExportedHooks
76-
* @property{Function} initialize Customizations setup hook.
77-
* @property{Function} globalPreload Global preload hook.
7872
* @property{Function} resolve Resolve hook.
7973
* @property{Function} load Load hook.
8074
*/
@@ -89,13 +83,6 @@ let importMetaInitializer;
8983

9084
classHooks{
9185
#chains ={
92-
/**
93-
* Prior to ESM loading. These are called once before any modules are started.
94-
* @private
95-
* @property{KeyedHook[]} globalPreload Last-in-first-out list of preload hooks.
96-
*/
97-
globalPreload: [],
98-
9986
/**
10087
* Phase 1 of 2 in ESM loading.
10188
* The output of the `resolve` chain of hooks is passed into the `load` chain of hooks.
@@ -146,7 +133,6 @@ class Hooks{
146133

147134
/**
148135
* Collect custom/user-defined module loader hook(s).
149-
* After all hooks have been collected, the global preload hook(s) must be initialized.
150136
* @param{string} url Custom loader specifier
151137
* @param{Record<string, unknown>} exports
152138
* @param{any} [data] Arbitrary data to be passed from the custom loader (user-land)
@@ -155,18 +141,11 @@ class Hooks{
155141
*/
156142
addCustomLoader(url,exports,data){
157143
const{
158-
globalPreload,
159144
initialize,
160145
resolve,
161146
load,
162147
}=pluckHooks(exports);
163148

164-
if(globalPreload&&!initialize){
165-
emitExperimentalWarning(
166-
'`globalPreload` is planned for removal in favor of `initialize`. `globalPreload`',
167-
);
168-
ArrayPrototypePush(this.#chains.globalPreload,{__proto__: null,fn: globalPreload, url });
169-
}
170149
if(resolve){
171150
constnext=this.#chains.resolve[this.#chains.resolve.length-1];
172151
ArrayPrototypePush(this.#chains.resolve,{__proto__: null,fn: resolve, url, next });
@@ -178,49 +157,6 @@ class Hooks{
178157
returninitialize?.(data);
179158
}
180159

181-
/**
182-
* Initialize `globalPreload` hooks.
183-
*/
184-
initializeGlobalPreload(){
185-
constpreloadScripts=[];
186-
for(leti=this.#chains.globalPreload.length-1;i>=0;i--){
187-
const{ MessageChannel }=require('internal/worker/io');
188-
constchannel=newMessageChannel();
189-
const{
190-
port1: insidePreload,
191-
port2: insideLoader,
192-
}=channel;
193-
194-
insidePreload.unref();
195-
insideLoader.unref();
196-
197-
const{
198-
fn: preload,
199-
url: specifier,
200-
}=this.#chains.globalPreload[i];
201-
202-
constpreloaded=preload({
203-
port: insideLoader,
204-
});
205-
206-
if(preloaded==null){continue;}
207-
208-
if(typeofpreloaded!=='string'){// [2]
209-
thrownewERR_INVALID_RETURN_VALUE(
210-
'a string',
211-
`${specifier} globalPreload`,
212-
preload,
213-
);
214-
}
215-
216-
ArrayPrototypePush(preloadScripts,{
217-
code: preloaded,
218-
port: insidePreload,
219-
});
220-
}
221-
returnpreloadScripts;
222-
}
223-
224160
/**
225161
* Resolve the location of the module.
226162
*
@@ -559,8 +495,9 @@ class HooksProxy{
559495
AtomicsWait(this.#lock,WORKER_TO_MAIN_THREAD_NOTIFICATION,0);
560496
constresponse=this.#worker.receiveMessageSync();
561497
if(response==null||response.message.status==='exit'){return;}
562-
const{ preloadScripts }=this.#unwrapMessage(response);
563-
this.#executePreloadScripts(preloadScripts);
498+
499+
// ! This line catches initialization errors in the worker thread.
500+
this.#unwrapMessage(response);
564501
}
565502

566503
this.#isReady =true;
@@ -677,66 +614,12 @@ class HooksProxy{
677614
importMetaInitialize(meta,context,loader){
678615
this.#importMetaInitializer(meta,context,loader);
679616
}
680-
681-
#executePreloadScripts(preloadScripts){
682-
for(leti=0;i<preloadScripts.length;i++){
683-
const{ code, port }=preloadScripts[i];
684-
const{ compileFunction }=require('vm');
685-
constpreloadInit=compileFunction(
686-
code,
687-
['getBuiltin','port','setImportMetaCallback'],
688-
{
689-
filename: '<preload>',
690-
},
691-
);
692-
letfinished=false;
693-
letreplacedImportMetaInitializer=false;
694-
letnext=this.#importMetaInitializer;
695-
const{ BuiltinModule }=require('internal/bootstrap/realm');
696-
// Calls the compiled preload source text gotten from the hook
697-
// Since the parameters are named we use positional parameters
698-
// see compileFunction above to cross reference the names
699-
try{
700-
FunctionPrototypeCall(
701-
preloadInit,
702-
globalThis,
703-
// Param getBuiltin
704-
(builtinName)=>{
705-
if(StringPrototypeStartsWith(builtinName,'node:')){
706-
builtinName=StringPrototypeSlice(builtinName,5);
707-
}elseif(!BuiltinModule.canBeRequiredWithoutScheme(builtinName)){
708-
thrownewERR_UNKNOWN_BUILTIN_MODULE(builtinName);
709-
}
710-
if(BuiltinModule.canBeRequiredByUsers(builtinName)){
711-
returnrequire(builtinName);
712-
}
713-
thrownewERR_UNKNOWN_BUILTIN_MODULE(builtinName);
714-
},
715-
// Param port
716-
port,
717-
// setImportMetaCallback
718-
(fn)=>{
719-
if(finished||typeoffn!=='function'){
720-
thrownewERR_INVALID_ARG_TYPE('fn',fn);
721-
}
722-
replacedImportMetaInitializer=true;
723-
constparent=next;
724-
next=(meta,context)=>{
725-
returnfn(meta,context,parent);
726-
};
727-
},
728-
);
729-
}finally{
730-
finished=true;
731-
if(replacedImportMetaInitializer){
732-
this.#importMetaInitializer =next;
733-
}
734-
}
735-
}
736-
}
737617
}
738618
ObjectSetPrototypeOf(HooksProxy.prototype,null);
739619

620+
// TODO(JakobJingleheimer): Remove this when loaders go "stable".
621+
letglobalPreloadWarningWasEmitted=false;
622+
740623
/**
741624
* A utility function to pluck the hooks from a user-defined loader.
742625
* @param{import('./loader.js).ModuleExports} exports
@@ -750,9 +633,6 @@ function pluckHooks({
750633
}){
751634
constacceptedHooks={__proto__: null};
752635

753-
if(globalPreload){
754-
acceptedHooks.globalPreload=globalPreload;
755-
}
756636
if(resolve){
757637
acceptedHooks.resolve=resolve;
758638
}
@@ -762,6 +642,12 @@ function pluckHooks({
762642

763643
if(initialize){
764644
acceptedHooks.initialize=initialize;
645+
}elseif(globalPreload&&!globalPreloadWarningWasEmitted){
646+
process.emitWarning(
647+
'`globalPreload` has been removed; use `initialize` instead.',
648+
'UnsupportedWarning',
649+
);
650+
globalPreloadWarningWasEmitted=true;
765651
}
766652

767653
returnacceptedHooks;

‎lib/internal/modules/esm/utils.js‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,7 @@ async function initializeHooks(){
145145
);
146146
}
147147

148-
constpreloadScripts=hooks.initializeGlobalPreload();
149-
150-
return{__proto__: null, hooks, preloadScripts };
148+
returnhooks;
151149
}
152150

153151
module.exports={

‎lib/internal/modules/esm/worker.js‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ function wrapMessage(status, body){
7474
}
7575

7676
asyncfunctioncustomizedModuleWorker(lock,syncCommPort,errorHandler){
77-
lethooks,preloadScripts,initializationError;
77+
lethooks;
78+
letinitializationError;
7879
lethasInitializationError=false;
7980

8081
{
@@ -91,9 +92,7 @@ async function customizedModuleWorker(lock, syncCommPort, errorHandler){
9192

9293

9394
try{
94-
constinitResult=awaitinitializeHooks();
95-
hooks=initResult.hooks;
96-
preloadScripts=initResult.preloadScripts;
95+
hooks=awaitinitializeHooks();
9796
}catch(exception){
9897
// If there was an error while parsing and executing a user loader, for example if because a
9998
// loader contained a syntax error, then we need to send the error to the main thread so it can
@@ -107,7 +106,7 @@ async function customizedModuleWorker(lock, syncCommPort, errorHandler){
107106
if(hasInitializationError){
108107
syncCommPort.postMessage(wrapMessage('error',initializationError));
109108
}else{
110-
syncCommPort.postMessage(wrapMessage('success',{ preloadScripts }),preloadScripts.map(({ port })=>port));
109+
syncCommPort.postMessage(wrapMessage('success'));
111110
}
112111

113112
// We're ready, so unlock the main thread.

0 commit comments

Comments
(0)