diff --git a/resolvers/README.md b/resolvers/README.md index b664721b83..ec00076b1f 100644 --- a/resolvers/README.md +++ b/resolvers/README.md @@ -18,7 +18,7 @@ To the extent it is feasible, trailing versions of the resolvers will continue t Currently, version 1 is assumed if no `interfaceVersion` is available. (didn't think to define it until v2, heh. 😅) -### `resolve(source, file, config) => { found: Boolean, path: String? }` +### `resolve(source, file, config, options) => { found: Boolean, path: String? }` Given: ```js @@ -52,6 +52,20 @@ the absolute path to the file making the import (`/some/path/to/module.js`) an object provided via the `import/resolver` setting. `my-cool-resolver` will get `["some", "stuff"]` as its `config`, while `node` will get `{ "paths": ["a", "b", "c"] }` provided as `config`. +##### `options` + +###### `options.context` + +**Only available after `eslint-plugin-import@2.27.0`** + +Please view [ESLint Context] for more details. + +##### `options.tsconfig` + +**Only available after `eslint-plugin-import@2.27.0`** + +Please view [TSConfig] and [ParsedCommandLine] for more details. + #### Return value The first resolver to return `{found: true}` is considered the source of truth. The returned object has: @@ -83,3 +97,6 @@ exports.resolve = function (source, file, config) { [Node resolver]: ./node/index.js [`resolve`]: https://www.npmjs.com/package/resolve +[ESLint Context]: https://eslint.org/docs/latest/developer-guide/working-with-rules#the-context-object +[TSConfig]: https://www.typescriptlang.org/tsconfig +[ParsedCommandLine]: https://github.com/microsoft/TypeScript/blob/fd3a84c3f0c80cb201c47399a055625f919a9b91/lib/typescriptServices.d.ts#L3168-L3178 diff --git a/src/ExportMap.js b/src/ExportMap.js index f61d3c170a..69cfa965e7 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -361,12 +361,14 @@ ExportMap.for = function (context) { ExportMap.parse = function (path, content, context) { const m = new ExportMap(path); - const isEsModuleInteropTrue = isEsModuleInterop(); + const tsconfig = getTsconfig(); + + const isEsModuleInteropTrue = !!(tsconfig && tsconfig.options && tsconfig.options.esModuleInterop); let ast; let visitorKeys; try { - const result = parse(path, content, context); + const result = parse(path, content, context, { tsconfig }); ast = result.ast; visitorKeys = result.visitorKeys; } catch (err) { @@ -442,7 +444,7 @@ ExportMap.parse = function (path, content, context) { const namespaces = new Map(); function remotePath(value) { - return resolve.relative(value, path, context.settings); + return resolve.relative(value, path, context.settings, context); } function resolveImport(value) { @@ -572,17 +574,17 @@ ExportMap.parse = function (path, content, context) { return null; } - function isEsModuleInterop() { + function getTsconfig() { const cacheKey = hashObject({ tsconfigRootDir: context.parserOptions && context.parserOptions.tsconfigRootDir, }).digest('hex'); - let tsConfig = tsconfigCache.get(cacheKey); - if (typeof tsConfig === 'undefined') { - tsConfig = readTsConfig(context); - tsconfigCache.set(cacheKey, tsConfig); + let tsconfig = tsconfigCache.get(cacheKey); + if (typeof tsconfig === 'undefined') { + tsconfig = readTsConfig(context); + tsconfigCache.set(cacheKey, tsconfig); } - return tsConfig && tsConfig.options ? tsConfig.options.esModuleInterop : false; + return tsconfig; } ast.body.forEach(function (n) { diff --git a/tests/files/foo-bar-resolver-no-version.js b/tests/files/foo-bar-resolver-no-version.js index 2a2d451850..988c3e3ad7 100644 --- a/tests/files/foo-bar-resolver-no-version.js +++ b/tests/files/foo-bar-resolver-no-version.js @@ -1,6 +1,7 @@ +var assert = require('assert') var path = require('path') -exports.resolveImport = function (modulePath, sourceFile, config) { +exports.resolveImport = function (modulePath, sourceFile, config, options) { var sourceFileName = path.basename(sourceFile) if (sourceFileName === 'foo.js') { return path.join(__dirname, 'bar.jsx') @@ -8,5 +9,8 @@ exports.resolveImport = function (modulePath, sourceFile, config) { if (sourceFileName === 'exception.js') { throw new Error('foo-bar-resolver-v1 resolveImport test exception') } + + assert.ok(options.context, 'the `context` must be presented') + return undefined; } diff --git a/tests/files/foo-bar-resolver-v1.js b/tests/files/foo-bar-resolver-v1.js index 7ba97cb55f..6afe0d36be 100644 --- a/tests/files/foo-bar-resolver-v1.js +++ b/tests/files/foo-bar-resolver-v1.js @@ -1,6 +1,7 @@ +var assert = require('assert') var path = require('path') -exports.resolveImport = function (modulePath, sourceFile, config) { +exports.resolveImport = function (modulePath, sourceFile, config, options) { var sourceFileName = path.basename(sourceFile) if (sourceFileName === 'foo.js') { return path.join(__dirname, 'bar.jsx'); @@ -8,6 +9,9 @@ exports.resolveImport = function (modulePath, sourceFile, config) { if (sourceFileName === 'exception.js') { throw new Error('foo-bar-resolver-v1 resolveImport test exception'); } + + assert.ok(options.context, 'the `context` must be presented') + return undefined; }; diff --git a/tests/files/foo-bar-resolver-v2.js b/tests/files/foo-bar-resolver-v2.js index 13135e3925..d938d190f3 100644 --- a/tests/files/foo-bar-resolver-v2.js +++ b/tests/files/foo-bar-resolver-v2.js @@ -1,6 +1,7 @@ +var assert = require('assert') var path = require('path') -exports.resolve = function (modulePath, sourceFile, config) { +exports.resolve = function (modulePath, sourceFile, config, options) { var sourceFileName = path.basename(sourceFile) if (sourceFileName === 'foo.js') { return { found: true, path: path.join(__dirname, 'bar.jsx') } @@ -8,6 +9,9 @@ exports.resolve = function (modulePath, sourceFile, config) { if (sourceFileName === 'exception.js') { throw new Error('foo-bar-resolver-v2 resolve test exception') } + + assert.ok(options.context, 'the `context` must be presented') + return { found: false }; }; diff --git a/utils/resolve.js b/utils/resolve.js index 0ed5bdb0c9..b3bf02b589 100644 --- a/utils/resolve.js +++ b/utils/resolve.js @@ -80,13 +80,13 @@ exports.fileExistsWithCaseSync = function fileExistsWithCaseSync(filepath, cache return result; }; -function relative(modulePath, sourceFile, settings) { - return fullResolve(modulePath, sourceFile, settings).path; +function relative(modulePath, sourceFile, settings, context, extra) { + return fullResolve(modulePath, sourceFile, settings, context, extra).path; } let prevSettings = null; let memoizedHash = ''; -function fullResolve(modulePath, sourceFile, settings) { +function fullResolve(modulePath, sourceFile, settings, context, extra) { // check if this is a bonus core module const coreSet = new Set(settings['import/core-modules']); if (coreSet.has(modulePath)) { return { found: true, path: null }; } @@ -111,11 +111,11 @@ function fullResolve(modulePath, sourceFile, settings) { function withResolver(resolver, config) { if (resolver.interfaceVersion === 2) { - return resolver.resolve(modulePath, sourceFile, config); + return resolver.resolve(modulePath, sourceFile, config, Object.assign({ context }, extra)); } try { - const resolved = resolver.resolveImport(modulePath, sourceFile, config); + const resolved = resolver.resolveImport(modulePath, sourceFile, config, Object.assign({ context }, extra)); if (resolved === undefined) { return { found: false }; } return { found: true, path: resolved }; } catch (err) { @@ -213,7 +213,7 @@ const erroredContexts = new Set(); */ function resolve(p, context) { try { - return relative(p, context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(), context.settings); + return relative(p, context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(), context.settings, context); } catch (err) { if (!erroredContexts.has(context)) { // The `err.stack` string starts with `err.name` followed by colon and `err.message`.