@@ -118,6 +118,100 @@ const availableDocStyleParsers = {
118118
119119const supportedImportTypes = new Set ( [ 'ImportDefaultSpecifier' , 'ImportNamespaceSpecifier' ] ) ;
120120
121+ let parserOptionsHash = '' ;
122+ let prevParserOptions = '' ;
123+ let settingsHash = '' ;
124+ let prevSettings = '' ;
125+ /**
126+ * don't hold full context object in memory, just grab what we need.
127+ * also calculate a cacheKey, where parts of the cacheKey hash are memoized
128+ */
129+ function childContext ( path , context ) {
130+ const { settings, parserOptions, parserPath } = context ;
131+
132+ if ( JSON . stringify ( settings ) !== prevSettings ) {
133+ settingsHash = hashObject ( { settings } ) . digest ( 'hex' ) ;
134+ prevSettings = JSON . stringify ( settings ) ;
135+ }
136+
137+ if ( JSON . stringify ( parserOptions ) !== prevParserOptions ) {
138+ parserOptionsHash = hashObject ( { parserOptions } ) . digest ( 'hex' ) ;
139+ prevParserOptions = JSON . stringify ( parserOptions ) ;
140+ }
141+
142+ return {
143+ cacheKey : String ( parserPath ) + parserOptionsHash + settingsHash + String ( path ) ,
144+ settings,
145+ parserOptions,
146+ parserPath,
147+ path,
148+ } ;
149+ }
150+
151+ /**
152+ * sometimes legacy support isn't _that_ hard... right?
153+ */
154+ function makeSourceCode ( text , ast ) {
155+ if ( SourceCode . length > 1 ) {
156+ // ESLint 3
157+ return new SourceCode ( text , ast ) ;
158+ } else {
159+ // ESLint 4, 5
160+ return new SourceCode ( { text, ast } ) ;
161+ }
162+ }
163+
164+ /**
165+ * Traverse a pattern/identifier node, calling 'callback'
166+ * for each leaf identifier.
167+ * @param {node } pattern
168+ * @param {Function } callback
169+ * @return {void }
170+ */
171+ export function recursivePatternCapture ( pattern , callback ) {
172+ switch ( pattern . type ) {
173+ case 'Identifier' : // base case
174+ callback ( pattern ) ;
175+ break ;
176+
177+ case 'ObjectPattern' :
178+ pattern . properties . forEach ( ( p ) => {
179+ if ( p . type === 'ExperimentalRestProperty' || p . type === 'RestElement' ) {
180+ callback ( p . argument ) ;
181+ return ;
182+ }
183+ recursivePatternCapture ( p . value , callback ) ;
184+ } ) ;
185+ break ;
186+
187+ case 'ArrayPattern' :
188+ pattern . elements . forEach ( ( element ) => {
189+ if ( element == null ) { return ; }
190+ if ( element . type === 'ExperimentalRestProperty' || element . type === 'RestElement' ) {
191+ callback ( element . argument ) ;
192+ return ;
193+ }
194+ recursivePatternCapture ( element , callback ) ;
195+ } ) ;
196+ break ;
197+
198+ case 'AssignmentPattern' :
199+ callback ( pattern . left ) ;
200+ break ;
201+ default :
202+ }
203+ }
204+
205+ /**
206+ * The creation of this closure is isolated from other scopes
207+ * to avoid over-retention of unrelated variables, which has
208+ * caused memory leaks. See #1266.
209+ */
210+ function thunkFor ( p , context ) {
211+ // eslint-disable-next-line no-use-before-define
212+ return ( ) => ExportMapBuilder . for ( childContext ( p , context ) ) ;
213+ }
214+
121215export default class ExportMapBuilder {
122216 static get ( source , context ) {
123217 const path = resolve ( source , context ) ;
@@ -183,6 +277,43 @@ export default class ExportMapBuilder {
183277 }
184278
185279 static parse ( path , content , context ) {
280+ function readTsConfig ( context ) {
281+ const tsconfigInfo = tsConfigLoader ( {
282+ cwd : context . parserOptions && context . parserOptions . tsconfigRootDir || process . cwd ( ) ,
283+ getEnv : ( key ) => process . env [ key ] ,
284+ } ) ;
285+ try {
286+ if ( tsconfigInfo . tsConfigPath !== undefined ) {
287+ // Projects not using TypeScript won't have `typescript` installed.
288+ if ( ! ts ) { ts = require ( 'typescript' ) ; } // eslint-disable-line import/no-extraneous-dependencies
289+
290+ const configFile = ts . readConfigFile ( tsconfigInfo . tsConfigPath , ts . sys . readFile ) ;
291+ return ts . parseJsonConfigFileContent (
292+ configFile . config ,
293+ ts . sys ,
294+ dirname ( tsconfigInfo . tsConfigPath ) ,
295+ ) ;
296+ }
297+ } catch ( e ) {
298+ // Catch any errors
299+ }
300+
301+ return null ;
302+ }
303+
304+ function isEsModuleInterop ( ) {
305+ const cacheKey = hashObject ( {
306+ tsconfigRootDir : context . parserOptions && context . parserOptions . tsconfigRootDir ,
307+ } ) . digest ( 'hex' ) ;
308+ let tsConfig = tsconfigCache . get ( cacheKey ) ;
309+ if ( typeof tsConfig === 'undefined' ) {
310+ tsConfig = readTsConfig ( context ) ;
311+ tsconfigCache . set ( cacheKey , tsConfig ) ;
312+ }
313+
314+ return tsConfig && tsConfig . options ? tsConfig . options . esModuleInterop : false ;
315+ }
316+
186317 const m = new ExportMap ( path ) ;
187318 const isEsModuleInteropTrue = isEsModuleInterop ( ) ;
188319
@@ -201,6 +332,10 @@ export default class ExportMapBuilder {
201332
202333 let hasDynamicImports = false ;
203334
335+ function remotePath ( value ) {
336+ return resolve . relative ( value , path , context . settings ) ;
337+ }
338+
204339 function processDynamicImport ( source ) {
205340 hasDynamicImports = true ;
206341 if ( source . type !== 'Literal' ) {
@@ -264,10 +399,6 @@ export default class ExportMapBuilder {
264399
265400 const namespaces = new Map ( ) ;
266401
267- function remotePath ( value ) {
268- return resolve . relative ( value , path , context . settings ) ;
269- }
270-
271402 function resolveImport ( value ) {
272403 const rp = remotePath ( value ) ;
273404 if ( rp == null ) { return null ; }
@@ -324,27 +455,6 @@ export default class ExportMapBuilder {
324455 m . reexports . set ( s . exported . name , { local, getImport : ( ) => resolveImport ( nsource ) } ) ;
325456 }
326457
327- function captureDependencyWithSpecifiers ( n ) {
328- // import type { Foo } (TS and Flow); import typeof { Foo } (Flow)
329- const declarationIsType = n . importKind === 'type' || n . importKind === 'typeof' ;
330- // import './foo' or import {} from './foo' (both 0 specifiers) is a side effect and
331- // shouldn't be considered to be just importing types
332- let specifiersOnlyImportingTypes = n . specifiers . length > 0 ;
333- const importedSpecifiers = new Set ( ) ;
334- n . specifiers . forEach ( ( specifier ) => {
335- if ( specifier . type === 'ImportSpecifier' ) {
336- importedSpecifiers . add ( specifier . imported . name || specifier . imported . value ) ;
337- } else if ( supportedImportTypes . has ( specifier . type ) ) {
338- importedSpecifiers . add ( specifier . type ) ;
339- }
340-
341- // import { type Foo } (Flow); import { typeof Foo } (Flow)
342- specifiersOnlyImportingTypes = specifiersOnlyImportingTypes
343- && ( specifier . importKind === 'type' || specifier . importKind === 'typeof' ) ;
344- } ) ;
345- captureDependency ( n , declarationIsType || specifiersOnlyImportingTypes , importedSpecifiers ) ;
346- }
347-
348458 function captureDependency ( { source } , isOnlyImportingTypes , importedSpecifiers = new Set ( ) ) {
349459 if ( source == null ) { return null ; }
350460
@@ -369,44 +479,28 @@ export default class ExportMapBuilder {
369479 return getter ;
370480 }
371481
372- const source = makeSourceCode ( content , ast ) ;
373-
374- function readTsConfig ( context ) {
375- const tsconfigInfo = tsConfigLoader ( {
376- cwd : context . parserOptions && context . parserOptions . tsconfigRootDir || process . cwd ( ) ,
377- getEnv : ( key ) => process . env [ key ] ,
378- } ) ;
379- try {
380- if ( tsconfigInfo . tsConfigPath !== undefined ) {
381- // Projects not using TypeScript won't have `typescript` installed.
382- if ( ! ts ) { ts = require ( 'typescript' ) ; } // eslint-disable-line import/no-extraneous-dependencies
383-
384- const configFile = ts . readConfigFile ( tsconfigInfo . tsConfigPath , ts . sys . readFile ) ;
385- return ts . parseJsonConfigFileContent (
386- configFile . config ,
387- ts . sys ,
388- dirname ( tsconfigInfo . tsConfigPath ) ,
389- ) ;
482+ function captureDependencyWithSpecifiers ( n ) {
483+ // import type { Foo } (TS and Flow); import typeof { Foo } (Flow)
484+ const declarationIsType = n . importKind === 'type' || n . importKind === 'typeof' ;
485+ // import './foo' or import {} from './foo' (both 0 specifiers) is a side effect and
486+ // shouldn't be considered to be just importing types
487+ let specifiersOnlyImportingTypes = n . specifiers . length > 0 ;
488+ const importedSpecifiers = new Set ( ) ;
489+ n . specifiers . forEach ( ( specifier ) => {
490+ if ( specifier . type === 'ImportSpecifier' ) {
491+ importedSpecifiers . add ( specifier . imported . name || specifier . imported . value ) ;
492+ } else if ( supportedImportTypes . has ( specifier . type ) ) {
493+ importedSpecifiers . add ( specifier . type ) ;
390494 }
391- } catch ( e ) {
392- // Catch any errors
393- }
394495
395- return null ;
496+ // import { type Foo } (Flow); import { typeof Foo } (Flow)
497+ specifiersOnlyImportingTypes = specifiersOnlyImportingTypes
498+ && ( specifier . importKind === 'type' || specifier . importKind === 'typeof' ) ;
499+ } ) ;
500+ captureDependency ( n , declarationIsType || specifiersOnlyImportingTypes , importedSpecifiers ) ;
396501 }
397502
398- function isEsModuleInterop ( ) {
399- const cacheKey = hashObject ( {
400- tsconfigRootDir : context . parserOptions && context . parserOptions . tsconfigRootDir ,
401- } ) . digest ( 'hex' ) ;
402- let tsConfig = tsconfigCache . get ( cacheKey ) ;
403- if ( typeof tsConfig === 'undefined' ) {
404- tsConfig = readTsConfig ( context ) ;
405- tsconfigCache . set ( cacheKey , tsConfig ) ;
406- }
407-
408- return tsConfig && tsConfig . options ? tsConfig . options . esModuleInterop : false ;
409- }
503+ const source = makeSourceCode ( content , ast ) ;
410504
411505 ast . body . forEach ( function ( n ) {
412506 if ( n . type === 'ExportDefaultDeclaration' ) {
@@ -555,96 +649,3 @@ export default class ExportMapBuilder {
555649 return m ;
556650 }
557651}
558-
559- /**
560- * The creation of this closure is isolated from other scopes
561- * to avoid over-retention of unrelated variables, which has
562- * caused memory leaks. See #1266.
563- */
564- function thunkFor ( p , context ) {
565- return ( ) => ExportMapBuilder . for ( childContext ( p , context ) ) ;
566- }
567-
568- /**
569- * Traverse a pattern/identifier node, calling 'callback'
570- * for each leaf identifier.
571- * @param {node } pattern
572- * @param {Function } callback
573- * @return {void }
574- */
575- export function recursivePatternCapture ( pattern , callback ) {
576- switch ( pattern . type ) {
577- case 'Identifier' : // base case
578- callback ( pattern ) ;
579- break ;
580-
581- case 'ObjectPattern' :
582- pattern . properties . forEach ( ( p ) => {
583- if ( p . type === 'ExperimentalRestProperty' || p . type === 'RestElement' ) {
584- callback ( p . argument ) ;
585- return ;
586- }
587- recursivePatternCapture ( p . value , callback ) ;
588- } ) ;
589- break ;
590-
591- case 'ArrayPattern' :
592- pattern . elements . forEach ( ( element ) => {
593- if ( element == null ) { return ; }
594- if ( element . type === 'ExperimentalRestProperty' || element . type === 'RestElement' ) {
595- callback ( element . argument ) ;
596- return ;
597- }
598- recursivePatternCapture ( element , callback ) ;
599- } ) ;
600- break ;
601-
602- case 'AssignmentPattern' :
603- callback ( pattern . left ) ;
604- break ;
605- default :
606- }
607- }
608-
609- let parserOptionsHash = '' ;
610- let prevParserOptions = '' ;
611- let settingsHash = '' ;
612- let prevSettings = '' ;
613- /**
614- * don't hold full context object in memory, just grab what we need.
615- * also calculate a cacheKey, where parts of the cacheKey hash are memoized
616- */
617- function childContext ( path , context ) {
618- const { settings, parserOptions, parserPath } = context ;
619-
620- if ( JSON . stringify ( settings ) !== prevSettings ) {
621- settingsHash = hashObject ( { settings } ) . digest ( 'hex' ) ;
622- prevSettings = JSON . stringify ( settings ) ;
623- }
624-
625- if ( JSON . stringify ( parserOptions ) !== prevParserOptions ) {
626- parserOptionsHash = hashObject ( { parserOptions } ) . digest ( 'hex' ) ;
627- prevParserOptions = JSON . stringify ( parserOptions ) ;
628- }
629-
630- return {
631- cacheKey : String ( parserPath ) + parserOptionsHash + settingsHash + String ( path ) ,
632- settings,
633- parserOptions,
634- parserPath,
635- path,
636- } ;
637- }
638-
639- /**
640- * sometimes legacy support isn't _that_ hard... right?
641- */
642- function makeSourceCode ( text , ast ) {
643- if ( SourceCode . length > 1 ) {
644- // ESLint 3
645- return new SourceCode ( text , ast ) ;
646- } else {
647- // ESLint 4, 5
648- return new SourceCode ( { text, ast } ) ;
649- }
650- }
0 commit comments