11import fsOperation from "fileSystem" ;
2+ import { parse } from "acorn" ;
23import toast from "components/toast" ;
34import appSettings from "lib/settings" ;
45import prettierPluginBabel from "prettier/plugins/babel" ;
@@ -319,7 +320,7 @@ function parseJsonLike(text) {
319320 const parsed = helpers . parseJSON ( trimmed ) ;
320321 if ( parsed ) return parsed ;
321322 try {
322- return new Function ( `return ( ${ trimmed } );` ) ( ) ;
323+ return parseSafeExpression ( trimmed ) ;
323324 } catch ( _ ) {
324325 return null ;
325326 }
@@ -329,26 +330,180 @@ function parseJsConfig(directory, source, absolutePath) {
329330 if ( ! source ) return null ;
330331 void directory ;
331332 void absolutePath ;
332- let transformed = source ;
333- if ( / e x p o r t \s + d e f a u l t / . test ( transformed ) ) {
334- transformed = transformed . replace ( / e x p o r t \s + d e f a u l t / , "module.exports =" ) ;
335- }
336- const module = { exports : { } } ;
337- const exports = module . exports ;
338- function requireStub ( request ) {
339- throw new Error (
340- `require(\"${ request } \") is not supported in Prettier configs inside Acode` ,
341- ) ;
342- }
343333 try {
344- const fn = new Function ( "module" , "exports" , "require" , transformed ) ;
345- fn ( module , exports , requireStub ) ;
346- return module . exports ?? exports ;
334+ return extractConfigFromProgram ( source ) ;
347335 } catch ( _ ) {
348336 return null ;
349337 }
350338}
351339
340+ function parseProgram ( source ) {
341+ try {
342+ return parse ( source , {
343+ ecmaVersion : "latest" ,
344+ sourceType : "module" ,
345+ allowHashBang : true ,
346+ } ) ;
347+ } catch ( _ ) {
348+ return parse ( source , {
349+ ecmaVersion : "latest" ,
350+ sourceType : "script" ,
351+ allowHashBang : true ,
352+ } ) ;
353+ }
354+ }
355+
356+ function extractConfigFromProgram ( source ) {
357+ const ast = parseProgram ( source ) ;
358+ const scope = new Map ( ) ;
359+
360+ for ( const statement of ast . body ) {
361+ const declared = readVariableDeclaration ( statement , scope ) ;
362+ if ( declared ) {
363+ for ( const [ name , value ] of declared ) {
364+ scope . set ( name , value ) ;
365+ }
366+ continue ;
367+ }
368+
369+ const exported = readCommonJsExport ( statement , scope ) ;
370+ if ( exported !== undefined ) return exported ;
371+
372+ const esmExported = readEsmExport ( statement , scope ) ;
373+ if ( esmExported !== undefined ) return esmExported ;
374+ }
375+
376+ return null ;
377+ }
378+
379+ function parseSafeExpression ( text ) {
380+ const wrapped = `(${ text } )` ;
381+ const ast = parse ( wrapped , {
382+ ecmaVersion : "latest" ,
383+ sourceType : "module" ,
384+ allowHashBang : true ,
385+ } ) ;
386+ const statement = ast . body [ 0 ] ;
387+ if ( statement ?. type !== "ExpressionStatement" ) return null ;
388+ return evaluateNode ( statement . expression , new Map ( ) ) ;
389+ }
390+
391+ function readVariableDeclaration ( statement , scope ) {
392+ if ( statement ?. type !== "VariableDeclaration" ) return null ;
393+ const values = new Map ( ) ;
394+ const lookupScope = new Map ( scope ) ;
395+
396+ for ( const decl of statement . declarations || [ ] ) {
397+ if ( ! decl || decl . type !== "VariableDeclarator" ) continue ;
398+ if ( decl . id ?. type !== "Identifier" ) continue ;
399+ if ( ! decl . init ) continue ;
400+ try {
401+ const value = evaluateNode ( decl . init , lookupScope ) ;
402+ values . set ( decl . id . name , value ) ;
403+ lookupScope . set ( decl . id . name , value ) ;
404+ } catch ( _ ) {
405+ // Ignore unsupported declarations
406+ }
407+ }
408+
409+ return values . size ? values : null ;
410+ }
411+
412+ function readCommonJsExport ( statement , scope ) {
413+ if ( statement ?. type !== "ExpressionStatement" ) return undefined ;
414+ const expr = statement . expression ;
415+ if ( expr ?. type !== "AssignmentExpression" || expr . operator !== "=" ) {
416+ return undefined ;
417+ }
418+
419+ if ( ! isModuleExports ( expr . left ) ) return undefined ;
420+ return evaluateNode ( expr . right , scope ) ;
421+ }
422+
423+ function readEsmExport ( statement , scope ) {
424+ if ( statement ?. type !== "ExportDefaultDeclaration" ) return undefined ;
425+ return evaluateNode ( statement . declaration , scope ) ;
426+ }
427+
428+ function isModuleExports ( node ) {
429+ return (
430+ node ?. type === "MemberExpression" &&
431+ ! node . computed &&
432+ node . object ?. type === "Identifier" &&
433+ node . object . name === "module" &&
434+ node . property ?. type === "Identifier" &&
435+ node . property . name === "exports"
436+ ) ;
437+ }
438+
439+ function evaluateNode ( node , scope ) {
440+ if ( ! node ) return null ;
441+
442+ switch ( node . type ) {
443+ case "ObjectExpression" :
444+ return evaluateObjectExpression ( node , scope ) ;
445+ case "ArrayExpression" :
446+ return node . elements . map ( ( entry ) => evaluateNode ( entry , scope ) ) ;
447+ case "Literal" :
448+ return node . value ;
449+ case "TemplateLiteral" :
450+ if ( node . expressions . length ) {
451+ throw new Error ( "Template expressions are not supported" ) ;
452+ }
453+ return node . quasis . map ( ( part ) => part . value . cooked ?? "" ) . join ( "" ) ;
454+ case "Identifier" :
455+ if ( scope . has ( node . name ) ) return scope . get ( node . name ) ;
456+ if ( node . name === "undefined" ) return undefined ;
457+ throw new Error ( `Unsupported identifier: ${ node . name } ` ) ;
458+ case "UnaryExpression" :
459+ return evaluateUnaryExpression ( node , scope ) ;
460+ default :
461+ throw new Error ( `Unsupported node type: ${ node . type } ` ) ;
462+ }
463+ }
464+
465+ function evaluateObjectExpression ( node , scope ) {
466+ const output = { } ;
467+ for ( const property of node . properties || [ ] ) {
468+ if ( ! property || property . type !== "Property" ) {
469+ throw new Error ( "Unsupported object property" ) ;
470+ }
471+ if ( property . kind !== "init" || property . method || property . shorthand ) {
472+ throw new Error ( "Unsupported object property kind" ) ;
473+ }
474+ const key = property . computed
475+ ? evaluateNode ( property . key , scope )
476+ : getPropertyKey ( property . key ) ;
477+ const normalizedKey =
478+ typeof key === "string" || typeof key === "number" ? String ( key ) : null ;
479+ if ( ! normalizedKey ) {
480+ throw new Error ( "Unsupported object key" ) ;
481+ }
482+ output [ normalizedKey ] = evaluateNode ( property . value , scope ) ;
483+ }
484+ return output ;
485+ }
486+
487+ function getPropertyKey ( node ) {
488+ if ( node ?. type === "Identifier" ) return node . name ;
489+ if ( node ?. type === "Literal" ) return node . value ;
490+ throw new Error ( "Unsupported property key" ) ;
491+ }
492+
493+ function evaluateUnaryExpression ( node , scope ) {
494+ const value = evaluateNode ( node . argument , scope ) ;
495+ switch ( node . operator ) {
496+ case "+" :
497+ return + value ;
498+ case "-" :
499+ return - value ;
500+ case "!" :
501+ return ! value ;
502+ default :
503+ throw new Error ( `Unsupported unary operator: ${ node . operator } ` ) ;
504+ }
505+ }
506+
352507function normalizePath ( path ) {
353508 let result = String ( path || "" ) . replace ( / \\ / g, "/" ) ;
354509 while ( result . length > 1 && result . endsWith ( "/" ) ) {
0 commit comments