@@ -5,10 +5,6 @@ const t = require('@babel/types')
55const ivm = require ( 'isolated-vm' )
66
77const isolate = new ivm . Isolate ( )
8- const globalContext = isolate . createContextSync ( )
9- function virtualGlobalEval ( jsStr ) {
10- return globalContext . evalSync ( String ( jsStr ) )
11- }
128
139const collect_id = {
1410 arrowFunc : null ,
@@ -148,6 +144,120 @@ const deMinifyArrow = {
148144 } ,
149145}
150146
147+ function checkArrayName ( path ) {
148+ if ( path . key !== 'argument' ) {
149+ return null
150+ }
151+ const ret_path = path . parentPath
152+ if ( ! ret_path . isReturnStatement ( ) || ret_path . key !== 0 ) {
153+ return null
154+ }
155+ const array_fn_path = ret_path . getFunctionParent ( )
156+ const array_fn_name = array_fn_path . node . id ?. name
157+ if ( ! array_fn_name ) {
158+ return null
159+ }
160+ const binding = array_fn_path . parentPath . scope . bindings [ array_fn_name ]
161+ if ( binding . references !== 1 ) {
162+ return null
163+ }
164+ let ref = binding . referencePaths [ 0 ]
165+ while ( ref && ! ref . isAssignmentExpression ( ) ) {
166+ ref = ref . parentPath
167+ }
168+ if ( ! ref ) {
169+ return null
170+ }
171+ const array_name = ref . node . left ?. name
172+ if ( ! array_name ) {
173+ return null
174+ }
175+ return {
176+ func_name : array_fn_name ,
177+ func_path : array_fn_path ,
178+ array_name : array_name ,
179+ array_path : ref ,
180+ }
181+ }
182+
183+ function parseArrayWarp ( vm , path ) {
184+ let func = path . getFunctionParent ( path )
185+ let name = null
186+ let binding = null
187+ if ( func . isArrowFunctionExpression ( ) ) {
188+ func = func . parentPath
189+ name = func . node . id . name
190+ binding = func . scope . getBinding ( name )
191+ } else {
192+ name = func . node . id . name
193+ binding = func . parentPath . scope . getBinding ( name )
194+ }
195+ console . log ( `Process array warp function: ${ name } ` )
196+ vm . evalSync ( generator ( func . node ) . code )
197+ for ( const ref of binding . referencePaths ) {
198+ const call = ref . parentPath
199+ if ( ref . key !== 'callee' ) {
200+ console . warn ( `Unexpected ref of array warp function: ${ call } ` )
201+ continue
202+ }
203+ const ret = vm . evalSync ( generator ( call . node ) . code )
204+ if ( typeof ret === 'string' ) {
205+ call . replaceWith ( t . stringLiteral ( ret ) )
206+ } else {
207+ call . replaceWithSourceString ( ret )
208+ }
209+ }
210+ binding . scope . crawl ( )
211+ binding = binding . scope . getBinding ( name )
212+ if ( ! binding . references ) {
213+ func . remove ( )
214+ }
215+ }
216+
217+ /**
218+ * Template:
219+ * ```javascript
220+ * var arrayName = getArrayFn()
221+ * function getArrayFn (){
222+ * return [...arrayExpression]
223+ * }
224+ * ```
225+ */
226+ const deDuplicateLiteral = {
227+ ArrayExpression ( path ) {
228+ let obj = checkArrayName ( path )
229+ if ( ! obj ) {
230+ return
231+ }
232+ console . log ( `Find arrayName: ${ obj . array_name } ` )
233+ let decl_node = t . variableDeclarator (
234+ obj . array_path . node . left ,
235+ obj . array_path . node . right
236+ )
237+ decl_node = t . variableDeclaration ( 'var' , [ decl_node ] )
238+ const code = [ generator ( obj . func_path . node ) . code , generator ( decl_node ) . code ]
239+ let binding = obj . array_path . scope . getBinding ( obj . array_name )
240+ for ( const ref of binding . referencePaths ) {
241+ const vm = isolate . createContextSync ( )
242+ vm . evalSync ( code [ 0 ] )
243+ vm . evalSync ( code [ 1 ] )
244+ parseArrayWarp ( vm , ref )
245+ }
246+ binding . scope . crawl ( )
247+ binding = binding . scope . bindings [ obj . array_name ]
248+ if ( ! binding . references ) {
249+ obj . array_path . remove ( )
250+ binding . path . remove ( )
251+ }
252+ binding = obj . func_path . parentPath . scope . getBinding ( obj . func_name )
253+ binding . scope . crawl ( )
254+ binding = binding . scope . getBinding ( obj . func_name )
255+ if ( ! binding . references ) {
256+ obj . func_path . remove ( )
257+ }
258+ } ,
259+ }
260+
151261function checkFuncLen ( path ) {
152262 if ( path . node ?. name !== 'configurable' || path . key !== 'key' ) {
153263 return null
@@ -222,6 +332,8 @@ module.exports = function (code) {
222332 traverse ( ast , deAntiTooling )
223333 // Minify
224334 traverse ( ast , deMinifyArrow )
335+ // DuplicateLiteralsRemoval
336+ traverse ( ast , deDuplicateLiteral )
225337 // Stack
226338 traverse ( ast , deStackFuncLen )
227339 code = generator ( ast , {
0 commit comments