Skip to content

Commit 61f02a2

Browse files
michaelfaithljharb
authored andcommitted
[Fix] exportMap: export map cache is tainted by unreliable parse results
This change addresses and issue observed with the v9 upgrade, where the ExportMap Cache is being tainted with a bad export map, if the parse doesn't yield a `visitorKeys` (which can happen if an incompatible parser is used (e.g. og babel eslint)) for one run of the no-cycle rule. If a subsequent test is run with a compatible parser, then the bad export map will be found in the cache and used instead of attempting to proceed with the parse. I also updated the `getExports` test to use a valid parserPath, rather than a mocked one, so that the tests are acting on a valid parserPath. Otherwise the export map won't be cached following this change.
1 parent 6218a8a commit 61f02a2

File tree

3 files changed

+25
-7
lines changed

3 files changed

+25
-7
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
1313
- `ExportMap` / flat config: include `languageOptions` in context ([#3052], thanks [@michaelfaith])
1414
- [`no-named-as-default`]: Allow using an identifier if the export is both a named and a default export ([#3032], thanks [@akwodkiewicz])
1515
- [`export`]: False positive for exported overloaded functions in TS ([#3065], thanks [@liuxingbaoyu])
16+
- `exportMap`: export map cache is tainted by unreliable parse results ([#3062], thanks [@michaelfaith])
1617

1718
### Changed
1819
- [Docs] [`no-relative-packages`]: fix typo ([#3066], thanks [@joshuaobrien])
@@ -1146,6 +1147,7 @@ for info on changes for earlier releases.
11461147
[#3068]: https://github.com/import-js/eslint-plugin-import/pull/3068
11471148
[#3066]: https://github.com/import-js/eslint-plugin-import/pull/3066
11481149
[#3065]: https://github.com/import-js/eslint-plugin-import/pull/3065
1150+
[#3062]: https://github.com/import-js/eslint-plugin-import/pull/3062
11491151
[#3052]: https://github.com/import-js/eslint-plugin-import/pull/3052
11501152
[#3043]: https://github.com/import-js/eslint-plugin-import/pull/3043
11511153
[#3036]: https://github.com/import-js/eslint-plugin-import/pull/3036

src/exportMap/builder.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ export default class ExportMapBuilder {
9292

9393
exportMap.mtime = stats.mtime;
9494

95-
exportCache.set(cacheKey, exportMap);
95+
// If the visitor keys were not populated, then we shouldn't save anything to the cache,
96+
// since the parse results may not be reliable.
97+
if (exportMap.visitorKeys) {
98+
exportCache.set(cacheKey, exportMap);
99+
}
96100
return exportMap;
97101
}
98102

tests/src/core/getExports.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
import { expect } from 'chai';
2+
import fs from 'fs';
23
import semver from 'semver';
34
import sinon from 'sinon';
45
import eslintPkg from 'eslint/package.json';
6+
import { test as testUnambiguous } from 'eslint-module-utils/unambiguous';
57
import typescriptPkg from 'typescript/package.json';
68
import * as tsConfigLoader from 'tsconfig-paths/lib/tsconfig-loader';
7-
import ExportMapBuilder from '../../../src/exportMap/builder';
8-
9-
import * as fs from 'fs';
109

10+
import ExportMapBuilder from '../../../src/exportMap/builder';
1111
import { getFilename } from '../utils';
12-
import { test as testUnambiguous } from 'eslint-module-utils/unambiguous';
12+
13+
const babelPath = require.resolve('babel-eslint');
14+
const hypotheticalLocation = babelPath.replace('index.js', 'visitor-keys.js');
15+
const isVisitorKeysSupported = fs.existsSync(hypotheticalLocation);
1316

1417
describe('ExportMap', function () {
1518
const fakeContext = Object.assign(
@@ -21,7 +24,7 @@ describe('ExportMap', function () {
2124
},
2225
{
2326
settings: {},
24-
parserPath: 'babel-eslint',
27+
parserPath: require.resolve('babel-eslint'),
2528
},
2629
);
2730

@@ -36,11 +39,20 @@ describe('ExportMap', function () {
3639

3740
});
3841

39-
it('returns a cached copy on subsequent requests', function () {
42+
(isVisitorKeysSupported ? it : it.skip)('returns a cached copy on subsequent requests', function () {
4043
expect(ExportMapBuilder.get('./named-exports', fakeContext))
4144
.to.exist.and.equal(ExportMapBuilder.get('./named-exports', fakeContext));
4245
});
4346

47+
it('does not return a cached copy if the parse does not yield a visitor keys', function () {
48+
const mockContext = {
49+
...fakeContext,
50+
parserPath: 'not-real',
51+
};
52+
expect(ExportMapBuilder.get('./named-exports', mockContext))
53+
.to.exist.and.not.equal(ExportMapBuilder.get('./named-exports', mockContext));
54+
});
55+
4456
it('does not return a cached copy after modification', (done) => {
4557
const firstAccess = ExportMapBuilder.get('./mutator', fakeContext);
4658
expect(firstAccess).to.exist;

0 commit comments

Comments
 (0)