178 lines
26 KiB
JavaScript
178 lines
26 KiB
JavaScript
|
'use strict';var _slicedToArray = function () {function sliceIterator(arr, i) {var _arr = [];var _n = true;var _d = false;var _e = undefined;try {for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {_arr.push(_s.value);if (i && _arr.length === i) break;}} catch (err) {_d = true;_e = err;} finally {try {if (!_n && _i["return"]) _i["return"]();} finally {if (_d) throw _e;}}return _arr;}return function (arr, i) {if (Array.isArray(arr)) {return arr;} else if (Symbol.iterator in Object(arr)) {return sliceIterator(arr, i);} else {throw new TypeError("Invalid attempt to destructure non-iterable instance");}};}(); /**
|
||
|
* @fileOverview Ensures that no imported module imports the linted module.
|
||
|
* @author Ben Mosher
|
||
|
*/
|
||
|
|
||
|
var _resolve = require('eslint-module-utils/resolve');var _resolve2 = _interopRequireDefault(_resolve);
|
||
|
var _builder = require('../exportMap/builder');var _builder2 = _interopRequireDefault(_builder);
|
||
|
var _scc = require('../scc');var _scc2 = _interopRequireDefault(_scc);
|
||
|
var _importType = require('../core/importType');
|
||
|
var _moduleVisitor = require('eslint-module-utils/moduleVisitor');var _moduleVisitor2 = _interopRequireDefault(_moduleVisitor);
|
||
|
var _docsUrl = require('../docsUrl');var _docsUrl2 = _interopRequireDefault(_docsUrl);function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { 'default': obj };}function _toConsumableArray(arr) {if (Array.isArray(arr)) {for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {arr2[i] = arr[i];}return arr2;} else {return Array.from(arr);}}
|
||
|
|
||
|
var traversed = new Set();
|
||
|
|
||
|
function routeString(route) {
|
||
|
return route.map(function (s) {return String(s.value) + ':' + String(s.loc.start.line);}).join('=>');
|
||
|
}
|
||
|
|
||
|
module.exports = {
|
||
|
meta: {
|
||
|
type: 'suggestion',
|
||
|
docs: {
|
||
|
category: 'Static analysis',
|
||
|
description: 'Forbid a module from importing a module with a dependency path back to itself.',
|
||
|
url: (0, _docsUrl2['default'])('no-cycle') },
|
||
|
|
||
|
schema: [(0, _moduleVisitor.makeOptionsSchema)({
|
||
|
maxDepth: {
|
||
|
anyOf: [
|
||
|
{
|
||
|
description: 'maximum dependency depth to traverse',
|
||
|
type: 'integer',
|
||
|
minimum: 1 },
|
||
|
|
||
|
{
|
||
|
'enum': ['∞'],
|
||
|
type: 'string' }] },
|
||
|
|
||
|
|
||
|
|
||
|
ignoreExternal: {
|
||
|
description: 'ignore external modules',
|
||
|
type: 'boolean',
|
||
|
'default': false },
|
||
|
|
||
|
allowUnsafeDynamicCyclicDependency: {
|
||
|
description: 'Allow cyclic dependency if there is at least one dynamic import in the chain',
|
||
|
type: 'boolean',
|
||
|
'default': false },
|
||
|
|
||
|
disableScc: {
|
||
|
description: 'When true, don\'t calculate a strongly-connected-components graph. SCC is used to reduce the time-complexity of cycle detection, but adds overhead.',
|
||
|
type: 'boolean',
|
||
|
'default': false } })] },
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
create: function () {function create(context) {
|
||
|
var myPath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename();
|
||
|
if (myPath === '<text>') {return {};} // can't cycle-check a non-file
|
||
|
|
||
|
var options = context.options[0] || {};
|
||
|
var maxDepth = typeof options.maxDepth === 'number' ? options.maxDepth : Infinity;
|
||
|
var ignoreModule = function () {function ignoreModule(name) {return options.ignoreExternal && (0, _importType.isExternalModule)(
|
||
|
name,
|
||
|
(0, _resolve2['default'])(name, context),
|
||
|
context);}return ignoreModule;}();
|
||
|
|
||
|
|
||
|
var scc = options.disableScc ? {} : _scc2['default'].get(myPath, context);
|
||
|
|
||
|
function checkSourceValue(sourceNode, importer) {
|
||
|
if (ignoreModule(sourceNode.value)) {
|
||
|
return; // ignore external modules
|
||
|
}
|
||
|
if (
|
||
|
options.allowUnsafeDynamicCyclicDependency && (
|
||
|
// Ignore `import()`
|
||
|
importer.type === 'ImportExpression'
|
||
|
// `require()` calls are always checked (if possible)
|
||
|
|| importer.type === 'CallExpression' && importer.callee.name !== 'require'))
|
||
|
|
||
|
{
|
||
|
return; // cycle via dynamic import allowed by config
|
||
|
}
|
||
|
|
||
|
if (
|
||
|
importer.type === 'ImportDeclaration' && (
|
||
|
// import type { Foo } (TS and Flow)
|
||
|
importer.importKind === 'type'
|
||
|
// import { type Foo } (Flow)
|
||
|
|| importer.specifiers.every(function (_ref) {var importKind = _ref.importKind;return importKind === 'type';})))
|
||
|
|
||
|
{
|
||
|
return; // ignore type imports
|
||
|
}
|
||
|
|
||
|
var imported = _builder2['default'].get(sourceNode.value, context);
|
||
|
|
||
|
if (imported == null) {
|
||
|
return; // no-unresolved territory
|
||
|
}
|
||
|
|
||
|
if (imported.path === myPath) {
|
||
|
return; // no-self-import territory
|
||
|
}
|
||
|
|
||
|
/* If we're in the same Strongly Connected Component,
|
||
|
* Then there exists a path from each node in the SCC to every other node in the SCC,
|
||
|
* Then there exists at least one path from them to us and from us to them,
|
||
|
* Then we have a cycle between us.
|
||
|
*/
|
||
|
var hasDependencyCycle = options.disableScc || scc[myPath] === scc[imported.path];
|
||
|
if (!hasDependencyCycle) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var untraversed = [{ mget: function () {function mget() {return imported;}return mget;}(), route: [] }];
|
||
|
function detectCycle(_ref2) {var mget = _ref2.mget,route = _ref2.route;
|
||
|
var m = mget();
|
||
|
if (m == null) {return;}
|
||
|
if (traversed.has(m.path)) {return;}
|
||
|
traversed.add(m.path);var _iteratorNormalCompletion = true;var _didIteratorError = false;var _iteratorError = undefined;try {
|
||
|
|
||
|
for (var _iterator = m.imports[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {var _ref3 = _step.value;var _ref4 = _slicedToArray(_ref3, 2);var path = _ref4[0];var _ref4$ = _ref4[1];var getter = _ref4$.getter;var declarations = _ref4$.declarations;
|
||
|
// If we're in different SCCs, we can't have a circular dependency
|
||
|
if (!options.disableScc && scc[myPath] !== scc[path]) {continue;}
|
||
|
|
||
|
if (traversed.has(path)) {continue;}
|
||
|
var toTraverse = [].concat(_toConsumableArray(declarations)).filter(function (_ref5) {var source = _ref5.source,isOnlyImportingTypes = _ref5.isOnlyImportingTypes;return !ignoreModule(source.value)
|
||
|
// Ignore only type imports
|
||
|
&& !isOnlyImportingTypes;});
|
||
|
|
||
|
|
||
|
/*
|
||
|
If cyclic dependency is allowed via dynamic import, skip checking if any module is imported dynamically
|
||
|
*/
|
||
|
if (options.allowUnsafeDynamicCyclicDependency && toTraverse.some(function (d) {return d.dynamic;})) {return;}
|
||
|
|
||
|
/*
|
||
|
Only report as a cycle if there are any import declarations that are considered by
|
||
|
the rule. For example:
|
||
|
a.ts:
|
||
|
import { foo } from './b' // should not be reported as a cycle
|
||
|
b.ts:
|
||
|
import type { Bar } from './a'
|
||
|
*/
|
||
|
|
||
|
|
||
|
if (path === myPath && toTraverse.length > 0) {return true;}
|
||
|
if (route.length + 1 < maxDepth) {var _iteratorNormalCompletion2 = true;var _didIteratorError2 = false;var _iteratorError2 = undefined;try {
|
||
|
for (var _iterator2 = toTraverse[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {var _ref6 = _step2.value;var source = _ref6.source;
|
||
|
untraversed.push({ mget: getter, route: route.concat(source) });
|
||
|
}} catch (err) {_didIteratorError2 = true;_iteratorError2 = err;} finally {try {if (!_iteratorNormalCompletion2 && _iterator2['return']) {_iterator2['return']();}} finally {if (_didIteratorError2) {throw _iteratorError2;}}}
|
||
|
}
|
||
|
}} catch (err) {_didIteratorError = true;_iteratorError = err;} finally {try {if (!_iteratorNormalCompletion && _iterator['return']) {_iterator['return']();}} finally {if (_didIteratorError) {throw _iteratorError;}}}
|
||
|
}
|
||
|
|
||
|
while (untraversed.length > 0) {
|
||
|
var next = untraversed.shift(); // bfs!
|
||
|
if (detectCycle(next)) {
|
||
|
var message = next.route.length > 0 ? 'Dependency cycle via ' + String(
|
||
|
routeString(next.route)) :
|
||
|
'Dependency cycle detected.';
|
||
|
context.report(importer, message);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Object.assign((0, _moduleVisitor2['default'])(checkSourceValue, context.options[0]), {
|
||
|
'Program:exit': function () {function ProgramExit() {
|
||
|
traversed.clear();
|
||
|
}return ProgramExit;}() });
|
||
|
|
||
|
}return create;}() };
|
||
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydWxlcy9uby1jeWNsZS5qcyJdLCJuYW1lcyI6WyJ0cmF2ZXJzZWQiLCJTZXQiLCJyb3V0ZVN0cmluZyIsInJvdXRlIiwibWFwIiwicyIsInZhbHVlIiwibG9jIiwic3RhcnQiLCJsaW5lIiwiam9pbiIsIm1vZHVsZSIsImV4cG9ydHMiLCJtZXRhIiwidHlwZSIsImRvY3MiLCJjYXRlZ29yeSIsImRlc2NyaXB0aW9uIiwidXJsIiwic2NoZW1hIiwibWF4RGVwdGgiLCJhbnlPZiIsIm1pbmltdW0iLCJpZ25vcmVFeHRlcm5hbCIsImFsbG93VW5zYWZlRHluYW1pY0N5Y2xpY0RlcGVuZGVuY3kiLCJkaXNhYmxlU2NjIiwiY3JlYXRlIiwiY29udGV4dCIsIm15UGF0aCIsImdldFBoeXNpY2FsRmlsZW5hbWUiLCJnZXRGaWxlbmFtZSIsIm9wdGlvbnMiLCJJbmZpbml0eSIsImlnbm9yZU1vZHVsZSIsIm5hbWUiLCJzY2MiLCJTdHJvbmdseUNvbm5lY3RlZENvbXBvbmVudHNCdWlsZGVyIiwiZ2V0IiwiY2hlY2tTb3VyY2VWYWx1ZSIsInNvdXJjZU5vZGUiLCJpbXBvcnRlciIsImNhbGxlZSIsImltcG9ydEtpbmQiLCJzcGVjaWZpZXJzIiwiZXZlcnkiLCJpbXBvcnRlZCIsIkV4cG9ydE1hcEJ1aWxkZXIiLCJwYXRoIiwiaGFzRGVwZW5kZW5jeUN5Y2xlIiwidW50cmF2ZXJzZWQiLCJtZ2V0IiwiZGV0ZWN0Q3ljbGUiLCJtIiwiaGFzIiwiYWRkIiwiaW1wb3J0cyIsImdldHRlciIsImRlY2xhcmF0aW9ucyIsInRvVHJhdmVyc2UiLCJmaWx0ZXIiLCJzb3VyY2UiLCJpc09ubHlJbXBvcnRpbmdUeXBlcyIsInNvbWUiLCJkIiwiZHluYW1pYyIsImxlbmd0aCIsInB1c2giLCJjb25jYXQiLCJuZXh0Iiwic2hpZnQiLCJtZXNzYWdlIiwicmVwb3J0IiwiT2JqZWN0IiwiYXNzaWduIiwiY2xlYXIiXSwibWFwcGluZ3MiOiJzb0JBQUE7Ozs7O0FBS0Esc0Q7QUFDQSwrQztBQUNBLDZCO0FBQ0E7QUFDQSxrRTtBQUNBLHFDOztBQUVBLElBQU1BLFlBQVksSUFBSUMsR0FBSixFQUFsQjs7QUFFQSxTQUFTQyxXQUFULENBQXFCQyxLQUFyQixFQUE0QjtBQUMxQixTQUFPQSxNQUFNQyxHQUFOLENBQVUsVUFBQ0MsQ0FBRCxpQkFBVUEsRUFBRUMsS0FBWixpQkFBcUJELEVBQUVFLEdBQUYsQ0FBTUMsS0FBTixDQUFZQyxJQUFqQyxHQUFWLEVBQW1EQyxJQUFuRCxDQUF3RCxJQUF4RCxDQUFQO0FBQ0Q7O0FBRURDLE9BQU9DLE9BQVAsR0FBaUI7QUFDZkMsUUFBTTtBQUNKQyxVQUFNLFlBREY7QUFFSkMsVUFBTTtBQUNKQyxnQkFBVSxpQkFETjtBQUVKQyxtQkFBYSxnRkFGVDtBQUdKQyxXQUFLLDBCQUFRLFVBQVIsQ0FIRCxFQUZGOztBQU9KQyxZQUFRLENBQUMsc0NBQWtCO0FBQ3pCQyxnQkFBVTtBQUNSQyxlQUFPO0FBQ0w7QUFDRUosdUJBQWEsc0NBRGY7QUFFRUgsZ0JBQU0sU0FGUjtBQUdFUSxtQkFBUyxDQUhYLEVBREs7O0FBTUw7QUFDRSxrQkFBTSxDQUFDLEdBQUQsQ0FEUjtBQUVFUixnQkFBTSxRQUZSLEVBTkssQ0FEQyxFQURlOzs7O0FBY3pCUyxzQkFBZ0I7QUFDZE4scUJBQWEseUJBREM7QUFFZEgsY0FBTSxTQUZRO0FBR2QsbUJBQVMsS0FISyxFQWRTOztBQW1CekJVLDBDQUFvQztBQUNsQ1AscUJBQWEsOEVBRHFCO0FBRWxDSCxjQUFNLFNBRjRCO0FBR2xDLG1CQUFTLEtBSHlCLEVBbkJYOztBQXdCekJXLGtCQUFZO0FBQ1ZSLHFCQUFhLHFKQURIO0FBRVZILGNBQU0sU0FGSTtBQUdWLG1CQUFTLEtBSEMsRUF4QmEsRUFBbEIsQ0FBRCxDQVBKLEVBRFM7Ozs7O0FBd0NmWSxRQXhDZSwrQkF3Q1JDLE9BeENRLEVBd0NDO0FBQ2QsVUFBTUMsU0FBU0QsUUFBUUUsbUJBQVIsR0FBOEJGLFFBQVFFLG1CQUFSLEVBQTlCLEdBQThERixRQUFRRyxXQUFSLEVBQTdFO0FBQ0EsVUFBSUYsV0FBVyxRQUFmLEVBQXlCLENBQUUsT0FBTyxFQUFQLENBQVksQ0FGekIsQ0FFMEI7O0FBRXhDLFVBQU1HLFVBQVVKLFFBQVFJLE9BQVIsQ0FBZ0IsQ0FBaEIsS0FBc0IsRUFBdEM7QUFDQSxVQUFNWCxXQUFXLE9BQU9XLFFBQVFYLFFBQWYsS0FBNEIsUUFBNUIsR0FBdUNXLFFBQVFYLFFBQS9DLEdBQTBEWSxRQUEzRTtBQUNBLFVBQU1DLDRCQUFlLFNBQWZBLFlBQWUsQ0FBQ0MsSUFBRCxVQUFVSCxRQUFRUixjQUFSLElBQTBCO0FBQ3ZEVyxjQUR1RDtBQUV2RCxvQ0FBUUEsSUFBUixFQUFjUCxPQUFkLENBRnVEO0FBR3ZEQSxpQkFIdUQsQ0FBcEMsRUFBZix1QkFBTjs7O0FBTUEsVUFBTVEsTUFBTUosUUFBUU4sVUFBUixHQUFxQixFQUFyQixHQUEwQlcsaUJBQW1DQyxHQUFuQyxDQUF1Q1QsTUFBdkMsRUFBK0NELE9BQS9DLENBQXRDOztBQUVBLGVBQVNXLGdCQUFULENBQTBCQyxVQUExQixFQUFzQ0MsUUFBdEMsRUFBZ0Q7QUFDOUMsWUFBSVAsYUFBYU0sV0FBV2pDLEtBQXhCLENBQUosRUFBb0M7QUFDbEMsaUJBRGtDLENBQzFCO0FBQ1Q7QUFDRDtBQUNFeUIsZ0JBQVFQLGtDQUFSO0FBQ0U7QUFDQWdCLGlCQUFTMUIsSUFBVCxLQUFrQjtBQUNsQjtBQURBLFdBRUcwQixTQUFTMUIsSUFBVCxLQUFrQixnQkFBbEIsSUFBc0MwQixTQUFTQyxNQUFULENBQWdCUCxJQUFoQixLQUF5QixTQUpwRSxDQURGOztBQU9FO0FBQ0EsaUJBREEsQ0FDUTtBQUNUOztBQUVEO0FBQ0VNLGlCQUFTMUIsSUFBVCxLQUFrQixtQkFBbEI7QUFDRTtBQUNBMEIsaUJBQVNFLFVBQVQsS0FBd0I7QUFDeEI7QUFEQSxXQUVHRixTQUFTRyxVQUFULENBQW9CQyxLQUFwQixDQUEwQixxQkFBR0YsVUFBSCxRQUFHQSxVQUFILFFBQW9CQSxlQUFlLE1BQW5DLEVBQTFCLENBSkwsQ0FERjs7QUFPRTtBQUNBLGlCQURBLENBQ1E7QUFDVDs7QUFFRCxZQUFNRyxXQUFXQyxxQkFBaUJULEdBQWpCLENBQXFCRSxXQUFXakMsS0FBaEMsRUFBdUNxQixPQUF2QyxDQUFqQjs7QUFFQSxZQUFJa0IsWUFBWSxJQUFoQixFQUFzQjtBQUNwQixpQkFEb0IsQ0FDWDtBQUNWOztBQUVELFlBQUlBLFNBQVNFLElBQVQsS0FBa0JuQixNQUF0QixFQUE4QjtBQUM1QixpQkFENEIsQ0FDbkI7QUFDVjs7QUFFRDs7Ozs7QUFLQSxZQUFNb0IscUJBQXFCakIsUUFBUU4sVUFBUixJQUFzQlUsSUFBSVAsTUFBSixNQUFnQk8sSUFBSVUsU0FBU0UsSUFBYixDQUFqRTtBQUNBLFlBQUksQ0FBQ0M
|