1
1
import '@ungap/with-resolvers' ;
2
2
import { $$ } from 'basic-devtools' ;
3
3
4
- import { assign , create , defineProperty , nodeInfo } from './utils.js' ;
4
+ import { assign , create , createOverload , createResolved , dedent , defineProperty , nodeInfo } from './utils.js' ;
5
5
import { getDetails } from './script-handler.js' ;
6
6
import { registry as defaultRegistry , prefixes , configs } from './interpreters.js' ;
7
7
import { getRuntimeID } from './loader.js' ;
8
- import { io } from './interpreter/_utils.js' ;
9
8
import { addAllListeners } from './listeners.js' ;
10
9
import { Hook , XWorker } from './xworker.js' ;
10
+ import { polluteJS , js as jsHooks , code as codeHooks } from './hooks.js' ;
11
11
import workerURL from './worker/url.js' ;
12
12
13
13
export const CUSTOM_SELECTORS = [ ] ;
@@ -44,15 +44,15 @@ export const handleCustomType = (node) => {
44
44
config,
45
45
version,
46
46
env,
47
- onInterpreterReady,
48
47
onerror,
48
+ hooks,
49
49
} = options ;
50
50
51
51
let error ;
52
52
try {
53
53
const worker = workerURL ( node ) ;
54
54
if ( worker ) {
55
- const xworker = XWorker . call ( new Hook ( null , options ) , worker , {
55
+ const xworker = XWorker . call ( new Hook ( null , hooks ) , worker , {
56
56
...nodeInfo ( node , type ) ,
57
57
version,
58
58
type : runtime ,
@@ -83,71 +83,66 @@ export const handleCustomType = (node) => {
83
83
engine . then ( ( interpreter ) => {
84
84
const module = create ( defaultRegistry . get ( runtime ) ) ;
85
85
86
- const {
87
- onBeforeRun,
88
- onBeforeRunAsync,
89
- onAfterRun,
90
- onAfterRunAsync,
91
- } = options ;
92
-
93
- const hooks = new Hook ( interpreter , options ) ;
86
+ const hook = new Hook ( interpreter , hooks ) ;
94
87
95
88
const XWorker = function XWorker ( ...args ) {
96
- return Worker . apply ( hooks , args ) ;
89
+ return Worker . apply ( hook , args ) ;
97
90
} ;
98
91
99
- // These two loops mimic a `new Map(arrayContent)` without needing
100
- // the new Map overhead so that [name, [before, after]] can be easily destructured
101
- // and new sync or async patches become easy to add (when the logic is the same).
102
-
103
- // patch sync
104
- for ( const [ name , [ before , after ] ] of [
105
- [ 'run' , [ onBeforeRun , onAfterRun ] ] ,
106
- ] ) {
107
- const method = module [ name ] ;
108
- module [ name ] = function ( interpreter , code , ...args ) {
109
- if ( before ) before . call ( this , resolved , node ) ;
110
- const result = method . call ( this , interpreter , code , ...args ) ;
111
- if ( after ) after . call ( this , resolved , node ) ;
112
- return result ;
113
- } ;
114
- }
115
-
116
- // patch async
117
- for ( const [ name , [ before , after ] ] of [
118
- [ 'runAsync' , [ onBeforeRunAsync , onAfterRunAsync ] ] ,
119
- ] ) {
120
- const method = module [ name ] ;
121
- module [ name ] = async function ( interpreter , code , ...args ) {
122
- if ( before ) await before . call ( this , resolved , node ) ;
123
- const result = await method . call (
124
- this ,
125
- interpreter ,
126
- code ,
127
- ...args
128
- ) ;
129
- if ( after ) await after . call ( this , resolved , node ) ;
130
- return result ;
131
- } ;
132
- }
133
-
134
- module . registerJSModule ( interpreter , 'polyscript' , { XWorker } ) ;
135
-
136
92
const resolved = {
137
- type,
138
- interpreter,
93
+ ...createResolved (
94
+ module ,
95
+ type ,
96
+ structuredClone ( configs . get ( name ) ) ,
97
+ interpreter ,
98
+ ) ,
139
99
XWorker,
140
- io : io . get ( interpreter ) ,
141
- config : structuredClone ( configs . get ( name ) ) ,
142
- run : module . run . bind ( module , interpreter ) ,
143
- runAsync : module . runAsync . bind ( module , interpreter ) ,
144
- runEvent : module . runEvent . bind ( module , interpreter ) ,
145
100
} ;
146
101
102
+ module . registerJSModule ( interpreter , 'polyscript' , { XWorker } ) ;
103
+
104
+ // patch methods accordingly to hooks (and only if needed)
105
+ for ( const suffix of [ 'Run' , 'RunAsync' ] ) {
106
+ const overload = createOverload ( module , `r${ suffix . slice ( 1 ) } ` ) ;
107
+
108
+ let before = '' ;
109
+ let after = '' ;
110
+
111
+ for ( const key of codeHooks ) {
112
+ const value = hooks ?. main ?. [ key ] ;
113
+ if ( value && key . endsWith ( suffix ) ) {
114
+ if ( key . startsWith ( 'codeBefore' ) )
115
+ before = dedent ( value ( ) ) ;
116
+ else
117
+ after = dedent ( value ( ) ) ;
118
+ }
119
+ }
120
+
121
+ // append code that should be executed *after* first
122
+ if ( after ) overload ( after , false ) ;
123
+
124
+ // prepend code that should be executed *before* (so that after is post-patched)
125
+ if ( before ) overload ( before , true ) ;
126
+
127
+ let beforeCB , afterCB ;
128
+ // ignore onReady and onWorker
129
+ for ( let i = 2 ; i < jsHooks . length ; i ++ ) {
130
+ const key = jsHooks [ i ] ;
131
+ const value = hooks ?. main ?. [ key ] ;
132
+ if ( value && key . endsWith ( suffix ) ) {
133
+ if ( key . startsWith ( 'onBefore' ) )
134
+ beforeCB = value ;
135
+ else
136
+ afterCB = value ;
137
+ }
138
+ }
139
+ polluteJS ( module , resolved , node , suffix . endsWith ( 'Async' ) , beforeCB , afterCB ) ;
140
+ }
141
+
147
142
details . queue = details . queue . then ( ( ) => {
148
143
resolve ( resolved ) ;
149
144
if ( error ) onerror ?. ( error , node ) ;
150
- return onInterpreterReady ?. ( resolved , node ) ;
145
+ return hooks ?. main ?. onReady ?. ( resolved , node ) ;
151
146
} ) ;
152
147
} ) ;
153
148
}
@@ -165,7 +160,6 @@ const registry = new Map();
165
160
* @prop {'pyodide' | 'micropython' | 'wasmoon' | 'ruby-wasm-wasi' } interpreter the interpreter to use
166
161
* @prop {string } [version] the optional interpreter version to use
167
162
* @prop {string } [config] the optional config to use within such interpreter
168
- * @prop {(environment: object, node: Element) => void } [onInterpreterReady] the callback that will be invoked once
169
163
*/
170
164
171
165
let dontBotherCount = 0 ;
@@ -198,17 +192,24 @@ export const define = (type, options) => {
198
192
199
193
if ( dontBother ) {
200
194
// add a script then cleanup everything once that's ready
201
- const { onInterpreterReady } = options ;
195
+ const { hooks } = options ;
196
+ const onReady = hooks ?. main ?. onReady ;
202
197
options = {
203
198
...options ,
204
- onInterpreterReady ( resolved , node ) {
205
- CUSTOM_SELECTORS . splice ( CUSTOM_SELECTORS . indexOf ( type ) , 1 ) ;
206
- defaultRegistry . delete ( type ) ;
207
- registry . delete ( type ) ;
208
- waitList . delete ( type ) ;
209
- node . remove ( ) ;
210
- onInterpreterReady ?. ( resolved ) ;
211
- }
199
+ hooks : {
200
+ ...hooks ,
201
+ main : {
202
+ ...hooks ?. main ,
203
+ onReady ( resolved , node ) {
204
+ CUSTOM_SELECTORS . splice ( CUSTOM_SELECTORS . indexOf ( type ) , 1 ) ;
205
+ defaultRegistry . delete ( type ) ;
206
+ registry . delete ( type ) ;
207
+ waitList . delete ( type ) ;
208
+ node . remove ( ) ;
209
+ onReady ?. ( resolved ) ;
210
+ }
211
+ }
212
+ } ,
212
213
} ;
213
214
document . head . append (
214
215
assign ( document . createElement ( 'script' ) , { type } )
0 commit comments