mirror of
https://github.com/joelwmale/webhook-action.git
synced 2024-11-29 19:05:22 +01:00
907 lines
98 KiB
JavaScript
907 lines
98 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
var _ExportMap = require('../ExportMap');var _ExportMap2 = _interopRequireDefault(_ExportMap);
|
||
|
var _ignore = require('eslint-module-utils/ignore');
|
||
|
var _resolve = require('eslint-module-utils/resolve');var _resolve2 = _interopRequireDefault(_resolve);
|
||
|
var _docsUrl = require('../docsUrl');var _docsUrl2 = _interopRequireDefault(_docsUrl);
|
||
|
var _path = require('path');
|
||
|
var _readPkgUp = require('read-pkg-up');var _readPkgUp2 = _interopRequireDefault(_readPkgUp);
|
||
|
var _object = require('object.values');var _object2 = _interopRequireDefault(_object);
|
||
|
var _arrayIncludes = require('array-includes');var _arrayIncludes2 = _interopRequireDefault(_arrayIncludes);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);}} /**
|
||
|
* @fileOverview Ensures that modules contain exports and/or all
|
||
|
* modules are consumed within other modules.
|
||
|
* @author René Fermann
|
||
|
*/ // eslint/lib/util/glob-util has been moved to eslint/lib/util/glob-utils with version 5.3
|
||
|
// and has been moved to eslint/lib/cli-engine/file-enumerator in version 6
|
||
|
let listFilesToProcess;try {const FileEnumerator = require('eslint/lib/cli-engine/file-enumerator').FileEnumerator;
|
||
|
listFilesToProcess = function (src, extensions) {
|
||
|
const e = new FileEnumerator({
|
||
|
extensions: extensions });
|
||
|
|
||
|
return Array.from(e.iterateFiles(src), (_ref) => {let filePath = _ref.filePath,ignored = _ref.ignored;return {
|
||
|
ignored,
|
||
|
filename: filePath };});
|
||
|
|
||
|
};
|
||
|
} catch (e1) {
|
||
|
// Prevent passing invalid options (extensions array) to old versions of the function.
|
||
|
// https://github.com/eslint/eslint/blob/v5.16.0/lib/util/glob-utils.js#L178-L280
|
||
|
// https://github.com/eslint/eslint/blob/v5.2.0/lib/util/glob-util.js#L174-L269
|
||
|
let originalListFilesToProcess;
|
||
|
try {
|
||
|
originalListFilesToProcess = require('eslint/lib/util/glob-utils').listFilesToProcess;
|
||
|
listFilesToProcess = function (src, extensions) {
|
||
|
return originalListFilesToProcess(src, {
|
||
|
extensions: extensions });
|
||
|
|
||
|
};
|
||
|
} catch (e2) {
|
||
|
originalListFilesToProcess = require('eslint/lib/util/glob-util').listFilesToProcess;
|
||
|
|
||
|
listFilesToProcess = function (src, extensions) {
|
||
|
const patterns = src.reduce((carry, pattern) => {
|
||
|
return carry.concat(extensions.map(extension => {
|
||
|
return (/\*\*|\*\./.test(pattern) ? pattern : `${pattern}/**/*${extension}`);
|
||
|
}));
|
||
|
}, src.slice());
|
||
|
|
||
|
return originalListFilesToProcess(patterns);
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const EXPORT_DEFAULT_DECLARATION = 'ExportDefaultDeclaration';
|
||
|
const EXPORT_NAMED_DECLARATION = 'ExportNamedDeclaration';
|
||
|
const EXPORT_ALL_DECLARATION = 'ExportAllDeclaration';
|
||
|
const IMPORT_DECLARATION = 'ImportDeclaration';
|
||
|
const IMPORT_NAMESPACE_SPECIFIER = 'ImportNamespaceSpecifier';
|
||
|
const IMPORT_DEFAULT_SPECIFIER = 'ImportDefaultSpecifier';
|
||
|
const VARIABLE_DECLARATION = 'VariableDeclaration';
|
||
|
const FUNCTION_DECLARATION = 'FunctionDeclaration';
|
||
|
const CLASS_DECLARATION = 'ClassDeclaration';
|
||
|
const INTERFACE_DECLARATION = 'InterfaceDeclaration';
|
||
|
const TYPE_ALIAS = 'TypeAlias';
|
||
|
const TS_INTERFACE_DECLARATION = 'TSInterfaceDeclaration';
|
||
|
const TS_TYPE_ALIAS_DECLARATION = 'TSTypeAliasDeclaration';
|
||
|
const TS_ENUM_DECLARATION = 'TSEnumDeclaration';
|
||
|
const DEFAULT = 'default';
|
||
|
|
||
|
function forEachDeclarationIdentifier(declaration, cb) {
|
||
|
if (declaration) {
|
||
|
if (
|
||
|
declaration.type === FUNCTION_DECLARATION ||
|
||
|
declaration.type === CLASS_DECLARATION ||
|
||
|
declaration.type === INTERFACE_DECLARATION ||
|
||
|
declaration.type === TYPE_ALIAS ||
|
||
|
declaration.type === TS_INTERFACE_DECLARATION ||
|
||
|
declaration.type === TS_TYPE_ALIAS_DECLARATION ||
|
||
|
declaration.type === TS_ENUM_DECLARATION)
|
||
|
{
|
||
|
cb(declaration.id.name);
|
||
|
} else if (declaration.type === VARIABLE_DECLARATION) {
|
||
|
declaration.declarations.forEach((_ref2) => {let id = _ref2.id;
|
||
|
cb(id.name);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* List of imports per file.
|
||
|
*
|
||
|
* Represented by a two-level Map to a Set of identifiers. The upper-level Map
|
||
|
* keys are the paths to the modules containing the imports, while the
|
||
|
* lower-level Map keys are the paths to the files which are being imported
|
||
|
* from. Lastly, the Set of identifiers contains either names being imported
|
||
|
* or a special AST node name listed above (e.g ImportDefaultSpecifier).
|
||
|
*
|
||
|
* For example, if we have a file named foo.js containing:
|
||
|
*
|
||
|
* import { o2 } from './bar.js';
|
||
|
*
|
||
|
* Then we will have a structure that looks like:
|
||
|
*
|
||
|
* Map { 'foo.js' => Map { 'bar.js' => Set { 'o2' } } }
|
||
|
*
|
||
|
* @type {Map<string, Map<string, Set<string>>>}
|
||
|
*/
|
||
|
const importList = new Map();
|
||
|
|
||
|
/**
|
||
|
* List of exports per file.
|
||
|
*
|
||
|
* Represented by a two-level Map to an object of metadata. The upper-level Map
|
||
|
* keys are the paths to the modules containing the exports, while the
|
||
|
* lower-level Map keys are the specific identifiers or special AST node names
|
||
|
* being exported. The leaf-level metadata object at the moment only contains a
|
||
|
* `whereUsed` propoerty, which contains a Set of paths to modules that import
|
||
|
* the name.
|
||
|
*
|
||
|
* For example, if we have a file named bar.js containing the following exports:
|
||
|
*
|
||
|
* const o2 = 'bar';
|
||
|
* export { o2 };
|
||
|
*
|
||
|
* And a file named foo.js containing the following import:
|
||
|
*
|
||
|
* import { o2 } from './bar.js';
|
||
|
*
|
||
|
* Then we will have a structure that looks like:
|
||
|
*
|
||
|
* Map { 'bar.js' => Map { 'o2' => { whereUsed: Set { 'foo.js' } } } }
|
||
|
*
|
||
|
* @type {Map<string, Map<string, object>>}
|
||
|
*/
|
||
|
const exportList = new Map();
|
||
|
|
||
|
const ignoredFiles = new Set();
|
||
|
const filesOutsideSrc = new Set();
|
||
|
|
||
|
const isNodeModule = path => {
|
||
|
return (/\/(node_modules)\//.test(path));
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* read all files matching the patterns in src and ignoreExports
|
||
|
*
|
||
|
* return all files matching src pattern, which are not matching the ignoreExports pattern
|
||
|
*/
|
||
|
const resolveFiles = (src, ignoreExports, context) => {
|
||
|
const extensions = Array.from((0, _ignore.getFileExtensions)(context.settings));
|
||
|
|
||
|
const srcFiles = new Set();
|
||
|
const srcFileList = listFilesToProcess(src, extensions);
|
||
|
|
||
|
// prepare list of ignored files
|
||
|
const ignoredFilesList = listFilesToProcess(ignoreExports, extensions);
|
||
|
ignoredFilesList.forEach((_ref3) => {let filename = _ref3.filename;return ignoredFiles.add(filename);});
|
||
|
|
||
|
// prepare list of source files, don't consider files from node_modules
|
||
|
srcFileList.filter((_ref4) => {let filename = _ref4.filename;return !isNodeModule(filename);}).forEach((_ref5) => {let filename = _ref5.filename;
|
||
|
srcFiles.add(filename);
|
||
|
});
|
||
|
return srcFiles;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* parse all source files and build up 2 maps containing the existing imports and exports
|
||
|
*/
|
||
|
const prepareImportsAndExports = (srcFiles, context) => {
|
||
|
const exportAll = new Map();
|
||
|
srcFiles.forEach(file => {
|
||
|
const exports = new Map();
|
||
|
const imports = new Map();
|
||
|
const currentExports = _ExportMap2.default.get(file, context);
|
||
|
if (currentExports) {const
|
||
|
dependencies = currentExports.dependencies,reexports = currentExports.reexports,localImportList = currentExports.imports,namespace = currentExports.namespace;
|
||
|
|
||
|
// dependencies === export * from
|
||
|
const currentExportAll = new Set();
|
||
|
dependencies.forEach(getDependency => {
|
||
|
const dependency = getDependency();
|
||
|
if (dependency === null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
currentExportAll.add(dependency.path);
|
||
|
});
|
||
|
exportAll.set(file, currentExportAll);
|
||
|
|
||
|
reexports.forEach((value, key) => {
|
||
|
if (key === DEFAULT) {
|
||
|
exports.set(IMPORT_DEFAULT_SPECIFIER, { whereUsed: new Set() });
|
||
|
} else {
|
||
|
exports.set(key, { whereUsed: new Set() });
|
||
|
}
|
||
|
const reexport = value.getImport();
|
||
|
if (!reexport) {
|
||
|
return;
|
||
|
}
|
||
|
let localImport = imports.get(reexport.path);
|
||
|
let currentValue;
|
||
|
if (value.local === DEFAULT) {
|
||
|
currentValue = IMPORT_DEFAULT_SPECIFIER;
|
||
|
} else {
|
||
|
currentValue = value.local;
|
||
|
}
|
||
|
if (typeof localImport !== 'undefined') {
|
||
|
localImport = new Set([].concat(_toConsumableArray(localImport), [currentValue]));
|
||
|
} else {
|
||
|
localImport = new Set([currentValue]);
|
||
|
}
|
||
|
imports.set(reexport.path, localImport);
|
||
|
});
|
||
|
|
||
|
localImportList.forEach((value, key) => {
|
||
|
if (isNodeModule(key)) {
|
||
|
return;
|
||
|
}
|
||
|
let localImport = imports.get(key);
|
||
|
if (typeof localImport !== 'undefined') {
|
||
|
localImport = new Set([].concat(_toConsumableArray(localImport), _toConsumableArray(value.importedSpecifiers)));
|
||
|
} else {
|
||
|
localImport = value.importedSpecifiers;
|
||
|
}
|
||
|
imports.set(key, localImport);
|
||
|
});
|
||
|
importList.set(file, imports);
|
||
|
|
||
|
// build up export list only, if file is not ignored
|
||
|
if (ignoredFiles.has(file)) {
|
||
|
return;
|
||
|
}
|
||
|
namespace.forEach((value, key) => {
|
||
|
if (key === DEFAULT) {
|
||
|
exports.set(IMPORT_DEFAULT_SPECIFIER, { whereUsed: new Set() });
|
||
|
} else {
|
||
|
exports.set(key, { whereUsed: new Set() });
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
exports.set(EXPORT_ALL_DECLARATION, { whereUsed: new Set() });
|
||
|
exports.set(IMPORT_NAMESPACE_SPECIFIER, { whereUsed: new Set() });
|
||
|
exportList.set(file, exports);
|
||
|
});
|
||
|
exportAll.forEach((value, key) => {
|
||
|
value.forEach(val => {
|
||
|
const currentExports = exportList.get(val);
|
||
|
const currentExport = currentExports.get(EXPORT_ALL_DECLARATION);
|
||
|
currentExport.whereUsed.add(key);
|
||
|
});
|
||
|
});
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* traverse through all imports and add the respective path to the whereUsed-list
|
||
|
* of the corresponding export
|
||
|
*/
|
||
|
const determineUsage = () => {
|
||
|
importList.forEach((listValue, listKey) => {
|
||
|
listValue.forEach((value, key) => {
|
||
|
const exports = exportList.get(key);
|
||
|
if (typeof exports !== 'undefined') {
|
||
|
value.forEach(currentImport => {
|
||
|
let specifier;
|
||
|
if (currentImport === IMPORT_NAMESPACE_SPECIFIER) {
|
||
|
specifier = IMPORT_NAMESPACE_SPECIFIER;
|
||
|
} else if (currentImport === IMPORT_DEFAULT_SPECIFIER) {
|
||
|
specifier = IMPORT_DEFAULT_SPECIFIER;
|
||
|
} else {
|
||
|
specifier = currentImport;
|
||
|
}
|
||
|
if (typeof specifier !== 'undefined') {
|
||
|
const exportStatement = exports.get(specifier);
|
||
|
if (typeof exportStatement !== 'undefined') {const
|
||
|
whereUsed = exportStatement.whereUsed;
|
||
|
whereUsed.add(listKey);
|
||
|
exports.set(specifier, { whereUsed });
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
};
|
||
|
|
||
|
const getSrc = src => {
|
||
|
if (src) {
|
||
|
return src;
|
||
|
}
|
||
|
return [process.cwd()];
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* prepare the lists of existing imports and exports - should only be executed once at
|
||
|
* the start of a new eslint run
|
||
|
*/
|
||
|
let srcFiles;
|
||
|
let lastPrepareKey;
|
||
|
const doPreparation = (src, ignoreExports, context) => {
|
||
|
const prepareKey = JSON.stringify({
|
||
|
src: (src || []).sort(),
|
||
|
ignoreExports: (ignoreExports || []).sort(),
|
||
|
extensions: Array.from((0, _ignore.getFileExtensions)(context.settings)).sort() });
|
||
|
|
||
|
if (prepareKey === lastPrepareKey) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
importList.clear();
|
||
|
exportList.clear();
|
||
|
ignoredFiles.clear();
|
||
|
filesOutsideSrc.clear();
|
||
|
|
||
|
srcFiles = resolveFiles(getSrc(src), ignoreExports, context);
|
||
|
prepareImportsAndExports(srcFiles, context);
|
||
|
determineUsage();
|
||
|
lastPrepareKey = prepareKey;
|
||
|
};
|
||
|
|
||
|
const newNamespaceImportExists = specifiers =>
|
||
|
specifiers.some((_ref6) => {let type = _ref6.type;return type === IMPORT_NAMESPACE_SPECIFIER;});
|
||
|
|
||
|
const newDefaultImportExists = specifiers =>
|
||
|
specifiers.some((_ref7) => {let type = _ref7.type;return type === IMPORT_DEFAULT_SPECIFIER;});
|
||
|
|
||
|
const fileIsInPkg = file => {var _readPkgUp$sync =
|
||
|
_readPkgUp2.default.sync({ cwd: file, normalize: false });const path = _readPkgUp$sync.path,pkg = _readPkgUp$sync.pkg;
|
||
|
const basePath = (0, _path.dirname)(path);
|
||
|
|
||
|
const checkPkgFieldString = pkgField => {
|
||
|
if ((0, _path.join)(basePath, pkgField) === file) {
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const checkPkgFieldObject = pkgField => {
|
||
|
const pkgFieldFiles = (0, _object2.default)(pkgField).map(value => (0, _path.join)(basePath, value));
|
||
|
if ((0, _arrayIncludes2.default)(pkgFieldFiles, file)) {
|
||
|
return true;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const checkPkgField = pkgField => {
|
||
|
if (typeof pkgField === 'string') {
|
||
|
return checkPkgFieldString(pkgField);
|
||
|
}
|
||
|
|
||
|
if (typeof pkgField === 'object') {
|
||
|
return checkPkgFieldObject(pkgField);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
if (pkg.private === true) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (pkg.bin) {
|
||
|
if (checkPkgField(pkg.bin)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pkg.browser) {
|
||
|
if (checkPkgField(pkg.browser)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pkg.main) {
|
||
|
if (checkPkgFieldString(pkg.main)) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
module.exports = {
|
||
|
meta: {
|
||
|
type: 'suggestion',
|
||
|
docs: { url: (0, _docsUrl2.default)('no-unused-modules') },
|
||
|
schema: [{
|
||
|
properties: {
|
||
|
src: {
|
||
|
description: 'files/paths to be analyzed (only for unused exports)',
|
||
|
type: 'array',
|
||
|
minItems: 1,
|
||
|
items: {
|
||
|
type: 'string',
|
||
|
minLength: 1 } },
|
||
|
|
||
|
|
||
|
ignoreExports: {
|
||
|
description:
|
||
|
'files/paths for which unused exports will not be reported (e.g module entry points)',
|
||
|
type: 'array',
|
||
|
minItems: 1,
|
||
|
items: {
|
||
|
type: 'string',
|
||
|
minLength: 1 } },
|
||
|
|
||
|
|
||
|
missingExports: {
|
||
|
description: 'report modules without any exports',
|
||
|
type: 'boolean' },
|
||
|
|
||
|
unusedExports: {
|
||
|
description: 'report exports without any usage',
|
||
|
type: 'boolean' } },
|
||
|
|
||
|
|
||
|
not: {
|
||
|
properties: {
|
||
|
unusedExports: { enum: [false] },
|
||
|
missingExports: { enum: [false] } } },
|
||
|
|
||
|
|
||
|
anyOf: [{
|
||
|
not: {
|
||
|
properties: {
|
||
|
unusedExports: { enum: [true] } } },
|
||
|
|
||
|
|
||
|
required: ['missingExports'] },
|
||
|
{
|
||
|
not: {
|
||
|
properties: {
|
||
|
missingExports: { enum: [true] } } },
|
||
|
|
||
|
|
||
|
required: ['unusedExports'] },
|
||
|
{
|
||
|
properties: {
|
||
|
unusedExports: { enum: [true] } },
|
||
|
|
||
|
required: ['unusedExports'] },
|
||
|
{
|
||
|
properties: {
|
||
|
missingExports: { enum: [true] } },
|
||
|
|
||
|
required: ['missingExports'] }] }] },
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
create: context => {var _ref8 =
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
context.options[0] || {};const src = _ref8.src;var _ref8$ignoreExports = _ref8.ignoreExports;const ignoreExports = _ref8$ignoreExports === undefined ? [] : _ref8$ignoreExports,missingExports = _ref8.missingExports,unusedExports = _ref8.unusedExports;
|
||
|
|
||
|
if (unusedExports) {
|
||
|
doPreparation(src, ignoreExports, context);
|
||
|
}
|
||
|
|
||
|
const file = context.getFilename();
|
||
|
|
||
|
const checkExportPresence = node => {
|
||
|
if (!missingExports) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (ignoredFiles.has(file)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const exportCount = exportList.get(file);
|
||
|
const exportAll = exportCount.get(EXPORT_ALL_DECLARATION);
|
||
|
const namespaceImports = exportCount.get(IMPORT_NAMESPACE_SPECIFIER);
|
||
|
|
||
|
exportCount.delete(EXPORT_ALL_DECLARATION);
|
||
|
exportCount.delete(IMPORT_NAMESPACE_SPECIFIER);
|
||
|
if (exportCount.size < 1) {
|
||
|
// node.body[0] === 'undefined' only happens, if everything is commented out in the file
|
||
|
// being linted
|
||
|
context.report(node.body[0] ? node.body[0] : node, 'No exports found');
|
||
|
}
|
||
|
exportCount.set(EXPORT_ALL_DECLARATION, exportAll);
|
||
|
exportCount.set(IMPORT_NAMESPACE_SPECIFIER, namespaceImports);
|
||
|
};
|
||
|
|
||
|
const checkUsage = (node, exportedValue) => {
|
||
|
if (!unusedExports) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (ignoredFiles.has(file)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (fileIsInPkg(file)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (filesOutsideSrc.has(file)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// make sure file to be linted is included in source files
|
||
|
if (!srcFiles.has(file)) {
|
||
|
srcFiles = resolveFiles(getSrc(src), ignoreExports, context);
|
||
|
if (!srcFiles.has(file)) {
|
||
|
filesOutsideSrc.add(file);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
exports = exportList.get(file);
|
||
|
|
||
|
// special case: export * from
|
||
|
const exportAll = exports.get(EXPORT_ALL_DECLARATION);
|
||
|
if (typeof exportAll !== 'undefined' && exportedValue !== IMPORT_DEFAULT_SPECIFIER) {
|
||
|
if (exportAll.whereUsed.size > 0) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// special case: namespace import
|
||
|
const namespaceImports = exports.get(IMPORT_NAMESPACE_SPECIFIER);
|
||
|
if (typeof namespaceImports !== 'undefined') {
|
||
|
if (namespaceImports.whereUsed.size > 0) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// exportsList will always map any imported value of 'default' to 'ImportDefaultSpecifier'
|
||
|
const exportsKey = exportedValue === DEFAULT ? IMPORT_DEFAULT_SPECIFIER : exportedValue;
|
||
|
|
||
|
const exportStatement = exports.get(exportsKey);
|
||
|
|
||
|
const value = exportsKey === IMPORT_DEFAULT_SPECIFIER ? DEFAULT : exportsKey;
|
||
|
|
||
|
if (typeof exportStatement !== 'undefined') {
|
||
|
if (exportStatement.whereUsed.size < 1) {
|
||
|
context.report(
|
||
|
node,
|
||
|
`exported declaration '${value}' not used within other modules`);
|
||
|
|
||
|
}
|
||
|
} else {
|
||
|
context.report(
|
||
|
node,
|
||
|
`exported declaration '${value}' not used within other modules`);
|
||
|
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* only useful for tools like vscode-eslint
|
||
|
*
|
||
|
* update lists of existing exports during runtime
|
||
|
*/
|
||
|
const updateExportUsage = node => {
|
||
|
if (ignoredFiles.has(file)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let exports = exportList.get(file);
|
||
|
|
||
|
// new module has been created during runtime
|
||
|
// include it in further processing
|
||
|
if (typeof exports === 'undefined') {
|
||
|
exports = new Map();
|
||
|
}
|
||
|
|
||
|
const newExports = new Map();
|
||
|
const newExportIdentifiers = new Set();
|
||
|
|
||
|
node.body.forEach((_ref9) => {let type = _ref9.type,declaration = _ref9.declaration,specifiers = _ref9.specifiers;
|
||
|
if (type === EXPORT_DEFAULT_DECLARATION) {
|
||
|
newExportIdentifiers.add(IMPORT_DEFAULT_SPECIFIER);
|
||
|
}
|
||
|
if (type === EXPORT_NAMED_DECLARATION) {
|
||
|
if (specifiers.length > 0) {
|
||
|
specifiers.forEach(specifier => {
|
||
|
if (specifier.exported) {
|
||
|
newExportIdentifiers.add(specifier.exported.name);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
forEachDeclarationIdentifier(declaration, name => {
|
||
|
newExportIdentifiers.add(name);
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// old exports exist within list of new exports identifiers: add to map of new exports
|
||
|
exports.forEach((value, key) => {
|
||
|
if (newExportIdentifiers.has(key)) {
|
||
|
newExports.set(key, value);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// new export identifiers added: add to map of new exports
|
||
|
newExportIdentifiers.forEach(key => {
|
||
|
if (!exports.has(key)) {
|
||
|
newExports.set(key, { whereUsed: new Set() });
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// preserve information about namespace imports
|
||
|
let exportAll = exports.get(EXPORT_ALL_DECLARATION);
|
||
|
let namespaceImports = exports.get(IMPORT_NAMESPACE_SPECIFIER);
|
||
|
|
||
|
if (typeof namespaceImports === 'undefined') {
|
||
|
namespaceImports = { whereUsed: new Set() };
|
||
|
}
|
||
|
|
||
|
newExports.set(EXPORT_ALL_DECLARATION, exportAll);
|
||
|
newExports.set(IMPORT_NAMESPACE_SPECIFIER, namespaceImports);
|
||
|
exportList.set(file, newExports);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* only useful for tools like vscode-eslint
|
||
|
*
|
||
|
* update lists of existing imports during runtime
|
||
|
*/
|
||
|
const updateImportUsage = node => {
|
||
|
if (!unusedExports) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let oldImportPaths = importList.get(file);
|
||
|
if (typeof oldImportPaths === 'undefined') {
|
||
|
oldImportPaths = new Map();
|
||
|
}
|
||
|
|
||
|
const oldNamespaceImports = new Set();
|
||
|
const newNamespaceImports = new Set();
|
||
|
|
||
|
const oldExportAll = new Set();
|
||
|
const newExportAll = new Set();
|
||
|
|
||
|
const oldDefaultImports = new Set();
|
||
|
const newDefaultImports = new Set();
|
||
|
|
||
|
const oldImports = new Map();
|
||
|
const newImports = new Map();
|
||
|
oldImportPaths.forEach((value, key) => {
|
||
|
if (value.has(EXPORT_ALL_DECLARATION)) {
|
||
|
oldExportAll.add(key);
|
||
|
}
|
||
|
if (value.has(IMPORT_NAMESPACE_SPECIFIER)) {
|
||
|
oldNamespaceImports.add(key);
|
||
|
}
|
||
|
if (value.has(IMPORT_DEFAULT_SPECIFIER)) {
|
||
|
oldDefaultImports.add(key);
|
||
|
}
|
||
|
value.forEach(val => {
|
||
|
if (val !== IMPORT_NAMESPACE_SPECIFIER &&
|
||
|
val !== IMPORT_DEFAULT_SPECIFIER) {
|
||
|
oldImports.set(val, key);
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
|
||
|
node.body.forEach(astNode => {
|
||
|
let resolvedPath;
|
||
|
|
||
|
// support for export { value } from 'module'
|
||
|
if (astNode.type === EXPORT_NAMED_DECLARATION) {
|
||
|
if (astNode.source) {
|
||
|
resolvedPath = (0, _resolve2.default)(astNode.source.raw.replace(/('|")/g, ''), context);
|
||
|
astNode.specifiers.forEach(specifier => {
|
||
|
const name = specifier.local.name;
|
||
|
if (specifier.local.name === DEFAULT) {
|
||
|
newDefaultImports.add(resolvedPath);
|
||
|
} else {
|
||
|
newImports.set(name, resolvedPath);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (astNode.type === EXPORT_ALL_DECLARATION) {
|
||
|
resolvedPath = (0, _resolve2.default)(astNode.source.raw.replace(/('|")/g, ''), context);
|
||
|
newExportAll.add(resolvedPath);
|
||
|
}
|
||
|
|
||
|
if (astNode.type === IMPORT_DECLARATION) {
|
||
|
resolvedPath = (0, _resolve2.default)(astNode.source.raw.replace(/('|")/g, ''), context);
|
||
|
if (!resolvedPath) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (isNodeModule(resolvedPath)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (newNamespaceImportExists(astNode.specifiers)) {
|
||
|
newNamespaceImports.add(resolvedPath);
|
||
|
}
|
||
|
|
||
|
if (newDefaultImportExists(astNode.specifiers)) {
|
||
|
newDefaultImports.add(resolvedPath);
|
||
|
}
|
||
|
|
||
|
astNode.specifiers.forEach(specifier => {
|
||
|
if (specifier.type === IMPORT_DEFAULT_SPECIFIER ||
|
||
|
specifier.type === IMPORT_NAMESPACE_SPECIFIER) {
|
||
|
return;
|
||
|
}
|
||
|
newImports.set(specifier.imported.name, resolvedPath);
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
|
||
|
newExportAll.forEach(value => {
|
||
|
if (!oldExportAll.has(value)) {
|
||
|
let imports = oldImportPaths.get(value);
|
||
|
if (typeof imports === 'undefined') {
|
||
|
imports = new Set();
|
||
|
}
|
||
|
imports.add(EXPORT_ALL_DECLARATION);
|
||
|
oldImportPaths.set(value, imports);
|
||
|
|
||
|
let exports = exportList.get(value);
|
||
|
let currentExport;
|
||
|
if (typeof exports !== 'undefined') {
|
||
|
currentExport = exports.get(EXPORT_ALL_DECLARATION);
|
||
|
} else {
|
||
|
exports = new Map();
|
||
|
exportList.set(value, exports);
|
||
|
}
|
||
|
|
||
|
if (typeof currentExport !== 'undefined') {
|
||
|
currentExport.whereUsed.add(file);
|
||
|
} else {
|
||
|
const whereUsed = new Set();
|
||
|
whereUsed.add(file);
|
||
|
exports.set(EXPORT_ALL_DECLARATION, { whereUsed });
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
oldExportAll.forEach(value => {
|
||
|
if (!newExportAll.has(value)) {
|
||
|
const imports = oldImportPaths.get(value);
|
||
|
imports.delete(EXPORT_ALL_DECLARATION);
|
||
|
|
||
|
const exports = exportList.get(value);
|
||
|
if (typeof exports !== 'undefined') {
|
||
|
const currentExport = exports.get(EXPORT_ALL_DECLARATION);
|
||
|
if (typeof currentExport !== 'undefined') {
|
||
|
currentExport.whereUsed.delete(file);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
newDefaultImports.forEach(value => {
|
||
|
if (!oldDefaultImports.has(value)) {
|
||
|
let imports = oldImportPaths.get(value);
|
||
|
if (typeof imports === 'undefined') {
|
||
|
imports = new Set();
|
||
|
}
|
||
|
imports.add(IMPORT_DEFAULT_SPECIFIER);
|
||
|
oldImportPaths.set(value, imports);
|
||
|
|
||
|
let exports = exportList.get(value);
|
||
|
let currentExport;
|
||
|
if (typeof exports !== 'undefined') {
|
||
|
currentExport = exports.get(IMPORT_DEFAULT_SPECIFIER);
|
||
|
} else {
|
||
|
exports = new Map();
|
||
|
exportList.set(value, exports);
|
||
|
}
|
||
|
|
||
|
if (typeof currentExport !== 'undefined') {
|
||
|
currentExport.whereUsed.add(file);
|
||
|
} else {
|
||
|
const whereUsed = new Set();
|
||
|
whereUsed.add(file);
|
||
|
exports.set(IMPORT_DEFAULT_SPECIFIER, { whereUsed });
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
oldDefaultImports.forEach(value => {
|
||
|
if (!newDefaultImports.has(value)) {
|
||
|
const imports = oldImportPaths.get(value);
|
||
|
imports.delete(IMPORT_DEFAULT_SPECIFIER);
|
||
|
|
||
|
const exports = exportList.get(value);
|
||
|
if (typeof exports !== 'undefined') {
|
||
|
const currentExport = exports.get(IMPORT_DEFAULT_SPECIFIER);
|
||
|
if (typeof currentExport !== 'undefined') {
|
||
|
currentExport.whereUsed.delete(file);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
newNamespaceImports.forEach(value => {
|
||
|
if (!oldNamespaceImports.has(value)) {
|
||
|
let imports = oldImportPaths.get(value);
|
||
|
if (typeof imports === 'undefined') {
|
||
|
imports = new Set();
|
||
|
}
|
||
|
imports.add(IMPORT_NAMESPACE_SPECIFIER);
|
||
|
oldImportPaths.set(value, imports);
|
||
|
|
||
|
let exports = exportList.get(value);
|
||
|
let currentExport;
|
||
|
if (typeof exports !== 'undefined') {
|
||
|
currentExport = exports.get(IMPORT_NAMESPACE_SPECIFIER);
|
||
|
} else {
|
||
|
exports = new Map();
|
||
|
exportList.set(value, exports);
|
||
|
}
|
||
|
|
||
|
if (typeof currentExport !== 'undefined') {
|
||
|
currentExport.whereUsed.add(file);
|
||
|
} else {
|
||
|
const whereUsed = new Set();
|
||
|
whereUsed.add(file);
|
||
|
exports.set(IMPORT_NAMESPACE_SPECIFIER, { whereUsed });
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
oldNamespaceImports.forEach(value => {
|
||
|
if (!newNamespaceImports.has(value)) {
|
||
|
const imports = oldImportPaths.get(value);
|
||
|
imports.delete(IMPORT_NAMESPACE_SPECIFIER);
|
||
|
|
||
|
const exports = exportList.get(value);
|
||
|
if (typeof exports !== 'undefined') {
|
||
|
const currentExport = exports.get(IMPORT_NAMESPACE_SPECIFIER);
|
||
|
if (typeof currentExport !== 'undefined') {
|
||
|
currentExport.whereUsed.delete(file);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
newImports.forEach((value, key) => {
|
||
|
if (!oldImports.has(key)) {
|
||
|
let imports = oldImportPaths.get(value);
|
||
|
if (typeof imports === 'undefined') {
|
||
|
imports = new Set();
|
||
|
}
|
||
|
imports.add(key);
|
||
|
oldImportPaths.set(value, imports);
|
||
|
|
||
|
let exports = exportList.get(value);
|
||
|
let currentExport;
|
||
|
if (typeof exports !== 'undefined') {
|
||
|
currentExport = exports.get(key);
|
||
|
} else {
|
||
|
exports = new Map();
|
||
|
exportList.set(value, exports);
|
||
|
}
|
||
|
|
||
|
if (typeof currentExport !== 'undefined') {
|
||
|
currentExport.whereUsed.add(file);
|
||
|
} else {
|
||
|
const whereUsed = new Set();
|
||
|
whereUsed.add(file);
|
||
|
exports.set(key, { whereUsed });
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
oldImports.forEach((value, key) => {
|
||
|
if (!newImports.has(key)) {
|
||
|
const imports = oldImportPaths.get(value);
|
||
|
imports.delete(key);
|
||
|
|
||
|
const exports = exportList.get(value);
|
||
|
if (typeof exports !== 'undefined') {
|
||
|
const currentExport = exports.get(key);
|
||
|
if (typeof currentExport !== 'undefined') {
|
||
|
currentExport.whereUsed.delete(file);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
|
||
|
return {
|
||
|
'Program:exit': node => {
|
||
|
updateExportUsage(node);
|
||
|
updateImportUsage(node);
|
||
|
checkExportPresence(node);
|
||
|
},
|
||
|
'ExportDefaultDeclaration': node => {
|
||
|
checkUsage(node, IMPORT_DEFAULT_SPECIFIER);
|
||
|
},
|
||
|
'ExportNamedDeclaration': node => {
|
||
|
node.specifiers.forEach(specifier => {
|
||
|
checkUsage(node, specifier.exported.name);
|
||
|
});
|
||
|
forEachDeclarationIdentifier(node.declaration, name => {
|
||
|
checkUsage(node, name);
|
||
|
});
|
||
|
} };
|
||
|
|
||
|
} };
|
||
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9ydWxlcy9uby11bnVzZWQtbW9kdWxlcy5qcyJdLCJuYW1lcyI6WyJsaXN0RmlsZXNUb1Byb2Nlc3MiLCJGaWxlRW51bWVyYXRvciIsInJlcXVpcmUiLCJzcmMiLCJleHRlbnNpb25zIiwiZSIsIkFycmF5IiwiZnJvbSIsIml0ZXJhdGVGaWxlcyIsImZpbGVQYXRoIiwiaWdub3JlZCIsImZpbGVuYW1lIiwiZTEiLCJvcmlnaW5hbExpc3RGaWxlc1RvUHJvY2VzcyIsImUyIiwicGF0dGVybnMiLCJyZWR1Y2UiLCJjYXJyeSIsInBhdHRlcm4iLCJjb25jYXQiLCJtYXAiLCJleHRlbnNpb24iLCJ0ZXN0Iiwic2xpY2UiLCJFWFBPUlRfREVGQVVMVF9ERUNMQVJBVElPTiIsIkVYUE9SVF9OQU1FRF9ERUNMQVJBVElPTiIsIkVYUE9SVF9BTExfREVDTEFSQVRJT04iLCJJTVBPUlRfREVDTEFSQVRJT04iLCJJTVBPUlRfTkFNRVNQQUNFX1NQRUNJRklFUiIsIklNUE9SVF9ERUZBVUxUX1NQRUNJRklFUiIsIlZBUklBQkxFX0RFQ0xBUkFUSU9OIiwiRlVOQ1RJT05fREVDTEFSQVRJT04iLCJDTEFTU19ERUNMQVJBVElPTiIsIklOVEVSRkFDRV9ERUNMQVJBVElPTiIsIlRZUEVfQUxJQVMiLCJUU19JTlRFUkZBQ0VfREVDTEFSQVRJT04iLCJUU19UWVBFX0FMSUFTX0RFQ0xBUkFUSU9OIiwiVFNfRU5VTV9ERUNMQVJBVElPTiIsIkRFRkFVTFQiLCJmb3JFYWNoRGVjbGFyYXRpb25JZGVudGlmaWVyIiwiZGVjbGFyYXRpb24iLCJjYiIsInR5cGUiLCJpZCIsIm5hbWUiLCJkZWNsYXJhdGlvbnMiLCJmb3JFYWNoIiwiaW1wb3J0TGlzdCIsIk1hcCIsImV4cG9ydExpc3QiLCJpZ25vcmVkRmlsZXMiLCJTZXQiLCJmaWxlc091dHNpZGVTcmMiLCJpc05vZGVNb2R1bGUiLCJwYXRoIiwicmVzb2x2ZUZpbGVzIiwiaWdub3JlRXhwb3J0cyIsImNvbnRleHQiLCJzZXR0aW5ncyIsInNyY0ZpbGVzIiwic3JjRmlsZUxpc3QiLCJpZ25vcmVkRmlsZXNMaXN0IiwiYWRkIiwiZmlsdGVyIiwicHJlcGFyZUltcG9ydHNBbmRFeHBvcnRzIiwiZXhwb3J0QWxsIiwiZmlsZSIsImV4cG9ydHMiLCJpbXBvcnRzIiwiY3VycmVudEV4cG9ydHMiLCJFeHBvcnRzIiwiZ2V0IiwiZGVwZW5kZW5jaWVzIiwicmVleHBvcnRzIiwibG9jYWxJbXBvcnRMaXN0IiwibmFtZXNwYWNlIiwiY3VycmVudEV4cG9ydEFsbCIsImdldERlcGVuZGVuY3kiLCJkZXBlbmRlbmN5Iiwic2V0IiwidmFsdWUiLCJrZXkiLCJ3aGVyZVVzZWQiLCJyZWV4cG9ydCIsImdldEltcG9ydCIsImxvY2FsSW1wb3J0IiwiY3VycmVudFZhbHVlIiwibG9jYWwiLCJpbXBvcnRlZFNwZWNpZmllcnMiLCJoYXMiLCJ2YWwiLCJjdXJyZW50RXhwb3J0IiwiZGV0ZXJtaW5lVXNhZ2UiLCJsaXN0VmFsdWUiLCJsaXN0S2V5IiwiY3VycmVudEltcG9ydCIsInNwZWNpZmllciIsImV4cG9ydFN0YXRlbWVudCIsImdldFNyYyIsInByb2Nlc3MiLCJjd2QiLCJsYXN0UHJlcGFyZUtleSIsImRvUHJlcGFyYXRpb24iLCJwcmVwYXJlS2V5IiwiSlNPTiIsInN0cmluZ2lmeSIsInNvcnQiLCJjbGVhciIsIm5ld05hbWVzcGFjZUltcG9ydEV4aXN0cyIsInNwZWNpZmllcnMiLCJzb21lIiwibmV3RGVmYXVsdEltcG9ydEV4aXN0cyIsImZpbGVJc0luUGtnIiwicmVhZFBrZ1VwIiwic3luYyIsIm5vcm1hbGl6ZSIsInBrZyIsImJhc2VQYXRoIiwiY2hlY2tQa2dGaWVsZFN0cmluZyIsInBrZ0ZpZWxkIiwiY2hlY2tQa2dGaWVsZE9iamVjdCIsInBrZ0ZpZWxkRmlsZXMiLCJjaGVja1BrZ0ZpZWxkIiwicHJpdmF0ZSIsImJpbiIsImJyb3dzZXIiLCJtYWluIiwibW9kdWxlIiwibWV0YSIsImRvY3MiLCJ1cmwiLCJzY2hlbWEiLCJwcm9wZXJ0aWVzIiwiZGVzY3JpcHRpb24iLCJtaW5JdGVtcyIsIml0ZW1zIiwibWluTGVuZ3RoIiwibWlzc2luZ0V4cG9ydHMiLCJ1bnVzZWRFeHBvcnRzIiwibm90IiwiZW51bSIsImFueU9mIiwicmVxdWlyZWQiLCJjcmVhdGUiLCJvcHRpb25zIiwiZ2V0RmlsZW5hbWUiLCJjaGVja0V4cG9ydFByZXNlbmNlIiwibm9kZSIsImV4cG9ydENvdW50IiwibmFtZXNwYWNlSW1wb3J0cyIsImRlbGV0ZSIsInNpemUiLCJyZXBvcnQiLCJib2R5IiwiY2hlY2tVc2FnZSIsImV4cG9ydGVkVmFsdWUiLCJleHBvcnRzS2V5IiwidXBkYXRlRXhwb3J0VXNhZ2UiLCJuZXdFeHBvcnRzIiwibmV3RXhwb3J0SWRlbnRpZmllcnMiLCJsZW5ndGgiLCJleHBvcnRlZCIsInVwZGF0ZUltcG9ydFVzYWdlIiwib2xkSW1wb3J0UGF0aHMiLCJvbGROYW1lc3BhY2VJbXBvcnRzIiwibmV3TmFtZXNwYWNlSW1wb3J0cyIsIm9sZEV4cG9ydEFsbCIsIm5ld0V4cG9ydEFsbCIsIm9sZERlZmF1bHRJbXBvcnRzIiwibmV3RGVmYXVsdEltcG9ydHMiLCJvbGRJbXBvcnRzIiwibmV3SW1wb3J0cyIsImFzdE5vZGUiLCJyZXNvbHZlZFBhdGgiLCJzb3VyY2UiLCJyYXciLCJyZXBsYWNlIiwiaW1wb3J0ZWQiXSwibWFwcGluZ3MiOiI7Ozs7OztBQU1BLHlDO0FBQ0E7QUFDQSxzRDtBQUNBLHFDO0FBQ0E7QUFDQSx3QztBQUNBLHVDO0FBQ0EsK0MsbVZBYkE7Ozs7c1lBZUE7QUFDQTtBQUNBLElBQUlBLGtCQUFKLENBQ0EsSUFBSSxDQUNGLE1BQU1DLGlCQUFpQkMsUUFBUSx1Q0FBUixFQUFpREQsY0FBeEU7QUFDQUQsdUJBQXFCLFVBQVVHLEdBQVYsRUFBZUMsVUFBZixFQUEyQjtBQUM5QyxVQUFNQyxJQUFJLElBQUlKLGNBQUosQ0FBbUI7QUFDM0JHLGtCQUFZQSxVQURlLEVBQW5CLENBQVY7O0FBR0EsV0FBT0UsTUFBTUMsSUFBTixDQUFXRixFQUFFRyxZQUFGLENBQWVMLEdBQWYsQ0FBWCxFQUFnQyxlQUFHTSxRQUFILFFBQUdBLFFBQUgsQ0FBYUMsT0FBYixRQUFhQSxPQUFiLFFBQTRCO0FBQ2pFQSxlQURpRTtBQUVqRUMsa0JBQVVGLFFBRnVELEVBQTVCLEVBQWhDLENBQVA7O0FBSUQsR0FSRDtBQVNELENBWEQsQ0FXRSxPQUFPRyxFQUFQLEVBQVc7QUFDWDtBQUNBO0FBQ0E7QUFDQSxNQUFJQywwQkFBSjtBQUNBLE1BQUk7QUFDRkEsaUNBQTZCWCxRQUFRLDRCQUFSLEVBQXNDRixrQkFBbkU7QUFDQUEseUJBQXFCLFVBQVVHLEdBQVYsRUFBZUMsVUFBZixFQUEyQjtBQUM5QyxhQUFPUywyQkFBMkJWLEd
|