@@ -27,24 +27,37 @@ import { safeReplace } from './safe-replace.js';
2727
2828const resourceDetails = new Map ( ) ;
2929const resourceAliases = new Map ( ) ;
30- const scriptletFiles = new Map ( ) ;
30+ const worldTemplate = {
31+ scriptletFunctions : new Map ( ) ,
32+ allFunctions : new Map ( ) ,
33+ args : new Map ( ) ,
34+ arglists : new Map ( ) ,
35+ hostnames : new Map ( ) ,
36+ matches : new Set ( ) ,
37+ hasEntities : false ,
38+ hasAncestors : false ,
39+ } ;
40+ const worlds = {
41+ ISOLATED : structuredClone ( worldTemplate ) ,
42+ MAIN : structuredClone ( worldTemplate ) ,
43+ } ;
3144
3245/******************************************************************************/
3346
34- function createScriptletCoreCode ( scriptletToken ) {
35- const details = resourceDetails . get ( scriptletToken ) ;
36- const components = new Map ( [ [ scriptletToken , details . code ] ] ) ;
37- const dependencies = details . dependencies && details . dependencies . slice ( ) || [ ] ;
47+ function createScriptletCoreCode ( worldDetails , resourceEntry ) {
48+ const { allFunctions } = worldDetails ;
49+ allFunctions . set ( resourceEntry . name , resourceEntry . code ) ;
50+ const dependencies = resourceEntry . dependencies &&
51+ resourceEntry . dependencies . slice ( ) || [ ] ;
3852 while ( dependencies . length !== 0 ) {
3953 const token = dependencies . shift ( ) ;
40- if ( components . has ( token ) ) { continue ; }
4154 const details = resourceDetails . get ( token ) ;
4255 if ( details === undefined ) { continue ; }
43- components . set ( token , details . code ) ;
56+ if ( allFunctions . has ( details . name ) ) { continue ; }
57+ allFunctions . set ( details . name , details . code ) ;
4458 if ( Array . isArray ( details . dependencies ) === false ) { continue ; }
4559 dependencies . push ( ...details . dependencies ) ;
4660 }
47- return Array . from ( components . values ( ) ) . join ( '\n\n' ) ;
4861}
4962
5063/******************************************************************************/
@@ -70,7 +83,8 @@ export function init() {
7083/******************************************************************************/
7184
7285export function reset ( ) {
73- scriptletFiles . clear ( ) ;
86+ worlds . ISOLATED = structuredClone ( worldTemplate ) ;
87+ worlds . MAIN = structuredClone ( worldTemplate ) ;
7488}
7589
7690/******************************************************************************/
@@ -85,56 +99,58 @@ export function compile(assetDetails, details) {
8599 const scriptletToken = details . args [ 0 ] ;
86100 const resourceEntry = resourceDetails . get ( scriptletToken ) ;
87101 if ( resourceEntry === undefined ) { return ; }
88- const argsToken = JSON . stringify ( details . args . slice ( 1 ) ) ;
89102 if ( resourceEntry . requiresTrust && details . trustedSource !== true ) {
90- console . log ( `Rejecting +js(${ scriptletToken } , ${ argsToken . slice ( 1 , - 1 ) } ): ${ assetDetails . id } is not trusted` ) ;
103+ console . log ( `Rejecting +js(${ details . args . join ( ) } ): ${ assetDetails . id } is not trusted` ) ;
91104 return ;
92105 }
93- if ( scriptletFiles . has ( scriptletToken ) === false ) {
94- scriptletFiles . set ( scriptletToken , {
95- name : resourceEntry . name ,
96- code : createScriptletCoreCode ( scriptletToken ) ,
97- world : resourceEntry . world ,
98- args : new Map ( ) ,
99- hostnames : new Map ( ) ,
100- exceptions : new Map ( ) ,
101- hasEntities : false ,
102- hasAncestors : false ,
103- matches : new Set ( ) ,
104- } ) ;
106+ const worldDetails = worlds [ resourceEntry . world ] ;
107+ const { scriptletFunctions } = worldDetails ;
108+ if ( scriptletFunctions . has ( resourceEntry . name ) === false ) {
109+ scriptletFunctions . set ( resourceEntry . name , scriptletFunctions . size ) ;
110+ createScriptletCoreCode ( worldDetails , resourceEntry ) ;
105111 }
106- const scriptletDetails = scriptletFiles . get ( scriptletToken ) ;
107- if ( scriptletDetails . args . has ( argsToken ) === false ) {
108- scriptletDetails . args . set ( argsToken , scriptletDetails . args . size ) ;
112+ // Convert args to arg indices
113+ const arglist = details . args . slice ( ) ;
114+ arglist [ 0 ] = scriptletFunctions . get ( resourceEntry . name ) ;
115+ for ( let i = 1 ; i < arglist . length ; i ++ ) {
116+ const arg = arglist [ i ] ;
117+ if ( worldDetails . args . has ( arg ) === false ) {
118+ worldDetails . args . set ( arg , worldDetails . args . size ) ;
119+ }
120+ arglist [ i ] = worldDetails . args . get ( arg ) ;
121+ }
122+ const arglistKey = JSON . stringify ( arglist ) . slice ( 1 , - 1 ) ;
123+ if ( worldDetails . arglists . has ( arglistKey ) === false ) {
124+ worldDetails . arglists . set ( arglistKey , worldDetails . arglists . size ) ;
109125 }
110- const iArgs = scriptletDetails . args . get ( argsToken ) ;
126+ const arglistIndex = worldDetails . arglists . get ( arglistKey ) ;
111127 if ( details . matches ) {
112128 for ( const hn of details . matches ) {
113129 const isEntity = hn . endsWith ( '.*' ) || hn . endsWith ( '.*>>' ) ;
114- scriptletDetails . hasEntities ||= isEntity ;
130+ worldDetails . hasEntities ||= isEntity ;
115131 const isAncestor = hn . endsWith ( '>>' )
116- scriptletDetails . hasAncestors ||= isAncestor ;
132+ worldDetails . hasAncestors ||= isAncestor ;
117133 if ( isEntity || isAncestor ) {
118- scriptletDetails . matches . clear ( ) ;
119- scriptletDetails . matches . add ( '*' ) ;
134+ worldDetails . matches . clear ( ) ;
135+ worldDetails . matches . add ( '*' ) ;
120136 }
121- if ( scriptletDetails . matches . has ( '*' ) === false ) {
122- scriptletDetails . matches . add ( hn ) ;
137+ if ( worldDetails . matches . has ( '*' ) === false ) {
138+ worldDetails . matches . add ( hn ) ;
123139 }
124- if ( scriptletDetails . hostnames . has ( hn ) === false ) {
125- scriptletDetails . hostnames . set ( hn , new Set ( ) ) ;
140+ if ( worldDetails . hostnames . has ( hn ) === false ) {
141+ worldDetails . hostnames . set ( hn , new Set ( ) ) ;
126142 }
127- scriptletDetails . hostnames . get ( hn ) . add ( iArgs ) ;
143+ worldDetails . hostnames . get ( hn ) . add ( arglistIndex ) ;
128144 }
129145 } else {
130- scriptletDetails . matches . add ( '*' ) ;
146+ worldDetails . matches . add ( '*' ) ;
131147 }
132148 if ( details . excludeMatches ) {
133149 for ( const hn of details . excludeMatches ) {
134- if ( scriptletDetails . exceptions . has ( hn ) === false ) {
135- scriptletDetails . exceptions . set ( hn , [ ] ) ;
150+ if ( worldDetails . hostnames . has ( hn ) === false ) {
151+ worldDetails . hostnames . set ( hn , new Set ( ) ) ;
136152 }
137- scriptletDetails . exceptions . get ( hn ) . push ( iArgs ) ;
153+ worldDetails . hostnames . get ( hn ) . add ( ~ arglistIndex ) ;
138154 }
139155 }
140156}
@@ -146,51 +162,57 @@ export async function commit(rulesetId, path, writeFn) {
146162 './scriptlets/scriptlet.template.js' ,
147163 { encoding : 'utf8' }
148164 ) ;
149- const patchHnMap = hnmap => {
150- const out = Array . from ( hnmap ) ;
151- out . forEach ( a => {
152- const values = Array . from ( a [ 1 ] ) ;
153- a [ 1 ] = values . length === 1 ? values [ 0 ] : values ;
154- } ) ;
155- return out ;
156- } ;
157- const scriptletStats = [ ] ;
158- for ( const [ name , details ] of scriptletFiles ) {
159- let content = safeReplace ( scriptletTemplate ,
160- 'function $scriptletName$(){}' ,
161- details . code
165+ const stats = { } ;
166+ for ( const world of Object . keys ( worlds ) ) {
167+ const worldDetails = worlds [ world ] ;
168+ const { scriptletFunctions, allFunctions, args, arglists } = worldDetails ;
169+ if ( scriptletFunctions . size === 0 ) { continue ; }
170+ const hostnames = Array . from ( worldDetails . hostnames ) . toSorted ( ( a , b ) => {
171+ const d = a [ 0 ] . length - b [ 0 ] . length ;
172+ if ( d !== 0 ) { return d ; }
173+ return a [ 0 ] < b [ 0 ] ? - 1 : 1 ;
174+ } ) . map ( a => ( [ a [ 0 ] , JSON . stringify ( Array . from ( a [ 1 ] ) . map ( a => JSON . parse ( a ) ) ) . slice ( 1 , - 1 ) ] ) ) ;
175+ let content = safeReplace ( scriptletTemplate , / \$ r u l e s e t I d \$ / , rulesetId , 0 ) ;
176+ if ( worldDetails . hasEntities ) {
177+ content = safeReplace ( content ,
178+ 'const $hasEntities$ = false;' ,
179+ 'const $hasEntities$ = true;'
180+ ) ;
181+ }
182+ if ( worldDetails . hasAncestors ) {
183+ content = safeReplace ( content ,
184+ 'const $hasAncestors$ = false;' ,
185+ 'const $hasAncestors$ = true;'
186+ ) ;
187+ } ;
188+ content = safeReplace ( content ,
189+ 'const $scriptletHostnames$ = [];' ,
190+ `const $scriptletHostnames$ = /* ${ hostnames . length } */ ${ JSON . stringify ( hostnames . map ( a => a [ 0 ] ) ) } ;`
162191 ) ;
163- content = safeReplace ( content , / \$ r u l e s e t I d \$ / , rulesetId , 0 ) ;
164- content = safeReplace ( content , / \$ s c r i p t l e t N a m e \$ / , details . name , 0 ) ;
165192 content = safeReplace ( content ,
166- 'self.$argsList$ ' ,
167- JSON . stringify ( Array . from ( details . args . keys ( ) ) . map ( a => JSON . parse ( a ) ) )
193+ 'const $scriptletArglistRefs$ = []; ' ,
194+ `const $scriptletArglistRefs$ = /* ${ hostnames . length } */ ${ JSON . stringify ( hostnames . map ( a => a [ 1 ] ) . join ( ';' ) ) } ;`
168195 ) ;
169196 content = safeReplace ( content ,
170- 'self.$hostnamesMap$ ' ,
171- JSON . stringify ( patchHnMap ( details . hostnames ) )
197+ 'const $scriptletArglists$ = []; ' ,
198+ `const $scriptletArglists$ = /* ${ arglists . size } */ ${ JSON . stringify ( Array . from ( arglists . keys ( ) ) . join ( ';' ) ) } ;`
172199 ) ;
173200 content = safeReplace ( content ,
174- 'self.$hasEntities$ ' ,
175- JSON . stringify ( details . hasEntities )
201+ 'const $scriptletArgs$ = []; ' ,
202+ `const $scriptletArgs$ = /* ${ args . size } */ ${ JSON . stringify ( Array . from ( args . keys ( ) ) . join ( '\n' ) ) } ;`
176203 ) ;
177204 content = safeReplace ( content ,
178- 'self.$hasAncestors$ ' ,
179- JSON . stringify ( details . hasAncestors )
205+ 'const $scriptletFunctions$ = []; ' ,
206+ `const $scriptletFunctions$ = /* ${ scriptletFunctions . size } */\n[ ${ Array . from ( scriptletFunctions . keys ( ) ) . join ( ',' ) } ];`
180207 ) ;
181208 content = safeReplace ( content ,
182- 'self.$exceptionsMap$ ' ,
183- JSON . stringify ( Array . from ( details . exceptions ) )
209+ 'function $scriptletCode$(){} // eslint-disable-line ' ,
210+ Array . from ( allFunctions . values ( ) ) . join ( '\n\n' )
184211 ) ;
185- writeFn ( `${ path } /${ rulesetId } .${ name } ` , content ) ;
186- scriptletStats . push ( [
187- name . slice ( 0 , - 3 ) , {
188- hostnames : Array . from ( details . matches ) . sort ( ) ,
189- world : details . world ,
190- }
191- ] ) ;
212+ writeFn ( `${ path } /${ world . toLowerCase ( ) } /${ rulesetId } .js` , content ) ;
213+ stats [ world ] = Array . from ( worldDetails . matches ) . sort ( ) ;
192214 }
193- return scriptletStats ;
215+ return stats ;
194216}
195217
196218/******************************************************************************/
0 commit comments