Skip to content

Commit e6252b8

Browse files
mblaszczyk-atlassianljharb
authored andcommitted
[Fix] import/no-cycle: fix perf regression
Fixes import-js#1943.
1 parent 462b016 commit e6252b8

File tree

3 files changed

+61
-8
lines changed

3 files changed

+61
-8
lines changed

src/ExportMap.js

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ let parseConfigFileTextToJson;
2222
const log = debug('eslint-plugin-import:ExportMap');
2323

2424
const exportCache = new Map();
25+
const tsConfigCache = new Map();
2526

2627
export default class ExportMap {
2728
constructor(path) {
@@ -438,9 +439,11 @@ ExportMap.parse = function (path, content, context) {
438439

439440
const source = makeSourceCode(content, ast);
440441

441-
function isEsModuleInterop() {
442+
function readTsConfig() {
442443
const tsConfigInfo = tsConfigLoader({
443-
cwd: context.parserOptions && context.parserOptions.tsconfigRootDir || process.cwd(),
444+
cwd:
445+
(context.parserOptions && context.parserOptions.tsconfigRootDir) ||
446+
process.cwd(),
444447
getEnv: (key) => process.env[key],
445448
});
446449
try {
@@ -450,12 +453,26 @@ ExportMap.parse = function (path, content, context) {
450453
// this is because projects not using TypeScript won't have typescript installed
451454
({ parseConfigFileTextToJson } = require('typescript'));
452455
}
453-
const tsConfig = parseConfigFileTextToJson(tsConfigInfo.tsConfigPath, jsonText).config;
454-
return tsConfig.compilerOptions.esModuleInterop;
456+
return parseConfigFileTextToJson(tsConfigInfo.tsConfigPath, jsonText).config;
455457
}
456458
} catch (e) {
457-
return false;
459+
// Catch any errors
458460
}
461+
462+
return null;
463+
}
464+
465+
function isEsModuleInterop() {
466+
const cacheKey = hashObject({
467+
tsconfigRootDir: context.parserOptions && context.parserOptions.tsconfigRootDir,
468+
}).digest('hex');
469+
let tsConfig = tsConfigCache.get(cacheKey);
470+
if (typeof tsConfig === 'undefined') {
471+
tsConfig = readTsConfig();
472+
tsConfigCache.set(cacheKey, tsConfig);
473+
}
474+
475+
return tsConfig !== null ? tsConfig.compilerOptions.esModuleInterop : false;
459476
}
460477

461478
ast.body.forEach(function (n) {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const AnalyticsNode = { Analytics: {} };
2+
3+
export = AnalyticsNode.Analytics;

tests/src/core/getExports.js

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { expect } from 'chai';
22
import semver from 'semver';
3+
import sinon from 'sinon';
34
import eslintPkg from 'eslint/package.json';
5+
import * as tsConfigLoader from 'tsconfig-paths/lib/tsconfig-loader';
46
import ExportMap from '../../../src/ExportMap';
57

68
import * as fs from 'fs';
@@ -49,9 +51,9 @@ describe('ExportMap', function () {
4951
expect(firstAccess).to.exist;
5052

5153
const differentSettings = Object.assign(
52-
{},
5354
fakeContext,
54-
{ parserPath: 'espree' });
55+
{ parserPath: 'espree' },
56+
);
5557

5658
expect(ExportMap.get('./named-exports', differentSettings))
5759
.to.exist.and
@@ -358,8 +360,12 @@ describe('ExportMap', function () {
358360
let imports;
359361
before('load imports', function () {
360362
this.timeout(20000); // takes a long time :shrug:
363+
sinon.spy(tsConfigLoader, 'tsConfigLoader');
361364
imports = ExportMap.get('./typescript.ts', context);
362365
});
366+
after('clear spies', function () {
367+
tsConfigLoader.tsConfigLoader.restore();
368+
});
363369

364370
it('returns something for a TypeScript file', function () {
365371
expect(imports).to.exist;
@@ -388,9 +394,36 @@ describe('ExportMap', function () {
388394
it('has exported abstract class', function () {
389395
expect(imports.has('Bar')).to.be.true;
390396
});
397+
398+
it('should cache tsconfig until tsconfigRootDir parser option changes', function () {
399+
const customContext = Object.assign(
400+
context,
401+
{
402+
parserOptions: {
403+
tsconfigRootDir: null,
404+
},
405+
},
406+
);
407+
expect(tsConfigLoader.tsConfigLoader.callCount).to.equal(0);
408+
ExportMap.parse('./baz.ts', 'export const baz = 5', customContext);
409+
expect(tsConfigLoader.tsConfigLoader.callCount).to.equal(1);
410+
ExportMap.parse('./baz.ts', 'export const baz = 5', customContext);
411+
expect(tsConfigLoader.tsConfigLoader.callCount).to.equal(1);
412+
413+
const differentContext = Object.assign(
414+
context,
415+
{
416+
parserOptions: {
417+
tsconfigRootDir: process.cwd(),
418+
},
419+
},
420+
);
421+
422+
ExportMap.parse('./baz.ts', 'export const baz = 5', differentContext);
423+
expect(tsConfigLoader.tsConfigLoader.callCount).to.equal(2);
424+
});
391425
});
392426
});
393-
394427
});
395428

396429
// todo: move to utils

0 commit comments

Comments
 (0)