import { parse } from '@babel/parser' ; import traverse from '@babel/traverse' ; import { DefaultDynamicExtractionsRules , DefaultExtractionRules , ExtractionRule } from '../rules' ; import { shouldExtract } from '../shouldExtract' ; import { ExtractionBabelOptions } from './options' ; import { DetectionResult } from '~/core/types' ; const defaultOptions = { ignoredJSXAttributes : [ 'class' , 'className' , 'key' , 'style' , 'ref' , 'onClick' ] , } ; export function detect ( input , rules = DefaultExtractionRules , dynamicRules = DefaultDynamicExtractionsRules , userOptions = { } ) { const { ignoredJSXAttributes , } = Object . assign ( { } , defaultOptions , userOptions ) ; const detections = [ ] ; const ast = parse ( input , { sourceType : 'unambiguous' , plugins : [ 'jsx' , 'typescript' , 'decorators-legacy' , ] , } ) ; const ignores = [ ] ; function handlePath ( path , type ) { const fullStart = path ?. node ?. start ; const fullEnd = path ?. node ?. end ; if ( ! fullStart || ! fullEnd ) return ; if ( isIgnored ( fullStart , fullEnd ) ) return ; const quoted = type !== 'jsx-text' ; const fullText = input . slice ( fullStart , fullEnd ) ; const text = quoted ? fullText . slice ( 1 , - 1 ) : fullText ; if ( type === 'js-template' && ! shouldExtract ( text , dynamicRules ) ) return ; else if ( ! shouldExtract ( text , rules ) ) return ; detections . push ( { fullEnd , fullStart , fullText , start : fullStart , end : fullEnd , text , source : type , } ) ; } function recordIgnore ( path ) { const fullStart = path ?. node ?. start ?? path ?. start ; const fullEnd = path ?. node ?. end ?? path ?. end ; if ( ! fullStart || ! fullEnd ) return ; ignores . push ( [ fullStart , fullEnd ] ) ; } function isIgnored ( start , end ) { return ignores . find ( ( [ s , e ] ) => ( s <= start && start <= e ) || ( s <= end && end <= e ) ) != null ; } traverse ( ast , { StringLiteral ( path ) { handlePath ( path , 'js-string' ) ; } , TemplateLiteral ( path ) { handlePath ( path , 'js-template' ) ; } , JSXText ( path ) { handlePath ( path , 'jsx-text' ) ; } , JSXElement ( path ) { path ?. node ?. openingElement ?. attributes ?. forEach ( ( i ) => { if ( ignoredJSXAttributes . includes ( i ?. name ?. name ) ) recordIgnore ( i ) ; } ) ; } , CallExpression ( path ) { const callee = path . get ( 'callee' ) ; if ( ! callee . isMemberExpression ( ) ) return ; if ( isGlobalConsoleId ( callee . get ( 'object' ) ) ) recordIgnore ( path ) ; } , } ) ; return detections ; } function isGlobalConsoleId ( id ) { const name = 'console' ; return ( id . isIdentifier ( { name } ) && ! id . scope . getBinding ( name ) && id . scope . hasGlobal ( name ) ) ; }