[eslint] fix and skip violations for cross-boundary imports (#136911)

This commit is contained in:
Spencer 2022-07-29 13:57:55 -05:00 committed by GitHub
parent 103aa675c7
commit bebec37f04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
379 changed files with 368 additions and 1168 deletions

View file

@ -34,7 +34,6 @@ RUNTIME_DEPS = [
"@npm//dedent",
"@npm//eslint",
"@npm//eslint-module-utils",
"@npm//micromatch",
]
js_library(

View file

@ -83,47 +83,6 @@ Disallows the usage of `this` into class property initializers and enforce to de
Disables the usage of a trailing slash in a node module import.
## no-restricted-paths
Defines a set of import paths valid to be imported for a given group of files.
```
module.exports = {
overrides: [
{
files: ['**/*.{js,mjs,ts,tsx}'],
rules: {
'@kbn/eslint/no-restricted-paths': [
'error',
{
basePath: __dirname,
zones: [
{
target: [
'(src|x-pack)/plugins/**/(public|server)/**/*',
'examples/**/*',
'!(src|x-pack)/**/*.test.*',
'!(x-pack/)?test/**/*',
],
from: [
'(src|x-pack)/plugins/**/(public|server)/**/*',
'!(src|x-pack)/plugins/**/(public|server)/mocks/index.{js,mjs,ts}',
'!(src|x-pack)/plugins/**/(public|server)/(index|mocks).{js,mjs,ts,tsx}',
'!(src|x-pack)/plugins/**/__stories__/index.{js,mjs,ts,tsx}',
'!(src|x-pack)/plugins/**/__fixtures__/index.{js,mjs,ts,tsx}',
],
allowSameFolder: true,
errorMessage: 'Plugins may only import from top-level public and server modules.',
},
],
},
],
},
}
]
}
```
## require-license-header
Requires a given license header text on a group of files.

View file

@ -10,7 +10,6 @@ module.exports = {
rules: {
'require-license-header': require('./rules/require_license_header'),
'disallow-license-headers': require('./rules/disallow_license_headers'),
'no-restricted-paths': require('./rules/no_restricted_paths'),
module_migration: require('./rules/module_migration'),
no_export_all: require('./rules/no_export_all'),
no_async_promise_body: require('./rules/no_async_promise_body'),

View file

@ -1,151 +0,0 @@
/* eslint-disable-line @kbn/eslint/require-license-header */
/*
* This product uses import/no-restricted-paths which is available under a
* "MIT" license.
*
* The MIT License (MIT)
*
* Copyright (c) 2015-present, Ben Mosher
* https://github.com/benmosher/eslint-plugin-import
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
const path = require('path');
const mm = require('micromatch');
const { getImportResolver } = require('@kbn/eslint-plugin-imports');
function isStaticRequire(node) {
return (
node &&
node.callee &&
node.callee.type === 'Identifier' &&
node.callee.name === 'require' &&
node.arguments.length === 1 &&
node.arguments[0].type === 'Literal' &&
typeof node.arguments[0].value === 'string'
);
}
function traverseToTopFolder(src, pattern) {
while (mm([src], pattern).length > 0) {
const srcIdx = src.lastIndexOf(path.sep);
src = src.slice(0, srcIdx);
}
return src;
}
function isSameFolderOrDescendent(src, imported, pattern) {
// to allow to exclude file by name in pattern (e.g., !**/index*) we start with file dirname and then traverse
const srcFileFolderRoot = traverseToTopFolder(path.dirname(src), pattern);
const importedFileFolderRoot = traverseToTopFolder(path.dirname(imported), pattern);
return srcFileFolderRoot === importedFileFolderRoot;
}
module.exports = {
meta: {
schema: [
{
type: 'object',
properties: {
zones: {
type: 'array',
minItems: 1,
items: {
type: 'object',
properties: {
target: {
anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }],
},
from: {
anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }],
},
allowSameFolder: { type: 'boolean' },
errorMessage: { type: 'string' },
},
additionalProperties: false,
},
},
basePath: { type: 'string' },
},
additionalProperties: false,
},
],
},
create(context) {
const resolver = getImportResolver(context);
const sourcePath = context.getPhysicalFilename
? context.getPhysicalFilename()
: context.getFilename();
const sourceDirname = path.dirname(sourcePath);
const options = context.options[0] || {};
const zones = options.zones || [];
const basePath = options.basePath;
if (!basePath || !path.isAbsolute(basePath)) {
throw new Error('basePath option must be specified and must be absolute');
}
function checkForRestrictedImportPath(importPath, node) {
const resolveReport = resolver.resolve(importPath, sourceDirname);
if (resolveReport?.type !== 'file' || resolveReport.nodeModule) {
return;
}
for (const { target, from, allowSameFolder, errorMessage = '' } of zones) {
const relativeSrcFile = path.relative(basePath, sourcePath);
const relativeImportFile = path.relative(basePath, resolveReport.absolute);
if (
!mm([relativeSrcFile], target).length ||
!mm([relativeImportFile], from).length ||
(allowSameFolder && isSameFolderOrDescendent(relativeSrcFile, relativeImportFile, from))
)
continue;
context.report({
node,
message: `Unexpected path "${importPath}" imported in restricted zone.${
errorMessage ? ' ' + errorMessage : ''
}`,
});
}
}
return {
ExportNamedDeclaration(node) {
if (!node.source) return;
checkForRestrictedImportPath(node.source.value, node.source);
},
ImportDeclaration(node) {
checkForRestrictedImportPath(node.source.value, node.source);
},
CallExpression(node) {
if (isStaticRequire(node)) {
const [firstArgument] = node.arguments;
checkForRestrictedImportPath(firstArgument.value, firstArgument);
}
},
};
},
};

View file

@ -1,400 +0,0 @@
/* eslint-disable-line @kbn/eslint/require-license-header */
/*
* This product uses import/no-restricted-paths which is available under a
* "MIT" license.
*
* The MIT License (MIT)
*
* Copyright (c) 2015-present, Ben Mosher
* https://github.com/benmosher/eslint-plugin-import
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
const path = require('path');
const { RuleTester } = require('eslint');
const { REPO_ROOT } = require('@kbn/utils');
const rule = require('./no_restricted_paths');
const ruleTester = new RuleTester({
parser: require.resolve('@babel/eslint-parser'),
parserOptions: {
sourceType: 'module',
ecmaVersion: 2018,
requireConfigFile: false,
},
});
ruleTester.run('@kbn/eslint/no-restricted-paths', rule, {
valid: [
{
code: 'import a from "../client/a.js"',
filename: path.join(__dirname, './__fixtures__/no_restricted_paths/server/b.js'),
options: [
{
basePath: __dirname,
zones: [
{
target: '__fixtures__/no_restricted_paths/server/**/*',
from: '__fixtures__/no_restricted_paths/other/**/*',
},
],
},
],
},
{
code: 'const a = require("../client/a.js")',
filename: path.join(__dirname, './__fixtures__/no_restricted_paths/server/b.js'),
options: [
{
basePath: __dirname,
zones: [
{
target: '__fixtures__/no_restricted_paths/server/**/*',
from: '__fixtures__/no_restricted_paths/other/**/*',
},
],
},
],
},
{
code: 'import b from "../server/b.js"',
filename: path.join(__dirname, './__fixtures__/no_restricted_paths/client/a.js'),
options: [
{
basePath: __dirname,
zones: [
{
target: '**/no_restricted_paths/client/**/*',
from: '**/no_restricted_paths/other/**/*',
},
],
},
],
},
// irrelevant function calls
{
code: 'notrequire("../server/b.js")',
options: [
{
basePath: __dirname,
},
],
},
{
code: 'notrequire("../server/b.js")',
filename: path.join(__dirname, './__fixtures__/no_restricted_paths/client/a.js'),
options: [
{
basePath: __dirname,
zones: [
{
target: '__fixtures__/no_restricted_paths/client/**/*',
from: '__fixtures__/no_restricted_paths/server/**/*',
},
],
},
],
},
// no config
{
code: 'require("../server/b.js")',
options: [
{
basePath: __dirname,
},
],
},
{
code: 'import b from "../server/b.js"',
options: [
{
basePath: __dirname,
},
],
},
// builtin (ignore)
{
code: 'require("os")',
options: [
{
basePath: __dirname,
},
],
},
{
code: 'const d = require("./deep/d.js")',
filename: path.join(__dirname, './__fixtures__/no_restricted_paths/server/b.js'),
options: [
{
basePath: __dirname,
zones: [
{
allowSameFolder: true,
target: '__fixtures__/no_restricted_paths/**/*',
from: '__fixtures__/no_restricted_paths/**/*',
},
],
},
],
},
{
code: 'const d = require("./deep/d.js")',
filename: path.join(__dirname, './__fixtures__/no_restricted_paths/server/b.js'),
options: [
{
basePath: __dirname,
zones: [
{
allowSameFolder: true,
target: '__fixtures__/no_restricted_paths/**/*',
from: [
'__fixtures__/no_restricted_paths/**/*',
'!__fixtures__/no_restricted_paths/server/b*',
],
},
],
},
],
},
{
// Check if dirs that start with 'index' work correctly.
code: 'import { X } from "./index_patterns"',
filename: path.join(__dirname, './__fixtures__/no_restricted_paths/server/b.js'),
options: [
{
basePath: __dirname,
zones: [
{
target: ['__fixtures__/no_restricted_paths/(public|server)/**/*'],
from: [
'__fixtures__/no_restricted_paths/server/**/*',
'!__fixtures__/no_restricted_paths/server/index.{ts,tsx}',
],
allowSameFolder: true,
},
],
},
],
},
],
invalid: [
{
code: 'export { b } from "../server/b.js"',
filename: path.join(__dirname, './__fixtures__/no_restricted_paths/client/a.js'),
options: [
{
basePath: __dirname,
zones: [
{
target: '__fixtures__/no_restricted_paths/client/**/*',
from: '__fixtures__/no_restricted_paths/server/**/*',
},
],
},
],
errors: [
{
message: 'Unexpected path "../server/b.js" imported in restricted zone.',
line: 1,
column: 19,
},
],
},
{
code: 'import b from "../server/b.js"',
filename: path.join(__dirname, './__fixtures__/no_restricted_paths/client/a.js'),
options: [
{
basePath: __dirname,
zones: [
{
target: '__fixtures__/no_restricted_paths/client/**/*',
from: '__fixtures__/no_restricted_paths/server/**/*',
},
],
},
],
errors: [
{
message: 'Unexpected path "../server/b.js" imported in restricted zone.',
line: 1,
column: 15,
},
],
},
{
code: 'import a from "../client/a"\nimport c from "./c"',
filename: path.join(__dirname, './__fixtures__/no_restricted_paths/server/b.js'),
options: [
{
basePath: __dirname,
zones: [
{
target: '__fixtures__/no_restricted_paths/server/**/*',
from: '__fixtures__/no_restricted_paths/client/**/*',
},
{
target: '__fixtures__/no_restricted_paths/server/**/*',
from: '__fixtures__/no_restricted_paths/server/c.js',
},
],
},
],
errors: [
{
message: 'Unexpected path "../client/a" imported in restricted zone.',
line: 1,
column: 15,
},
{
message: 'Unexpected path "./c" imported in restricted zone.',
line: 2,
column: 15,
},
],
},
{
code: 'const b = require("../server/b.js")',
filename: path.join(__dirname, './__fixtures__/no_restricted_paths/client/a.js'),
options: [
{
basePath: __dirname,
zones: [
{
target: '**/no_restricted_paths/client/**/*',
from: '**/no_restricted_paths/server/**/*',
},
],
},
],
errors: [
{
message: 'Unexpected path "../server/b.js" imported in restricted zone.',
line: 1,
column: 19,
},
],
},
{
code: 'const b = require("../server/b.js")',
filename: path.join(__dirname, './__fixtures__/no_restricted_paths/client/a.js'),
options: [
{
basePath: path.join(__dirname, '__fixtures__', 'no_restricted_paths'),
zones: [
{
target: 'client/**/*',
from: 'server/**/*',
},
],
},
],
errors: [
{
message: 'Unexpected path "../server/b.js" imported in restricted zone.',
line: 1,
column: 19,
},
],
},
{
code: 'const d = require("./deep/d.js")',
filename: path.join(__dirname, './__fixtures__/no_restricted_paths/server/b.js'),
options: [
{
basePath: __dirname,
zones: [
{
target: '__fixtures__/no_restricted_paths/**/*',
from: '__fixtures__/no_restricted_paths/**/*',
},
],
},
],
errors: [
{
message: 'Unexpected path "./deep/d.js" imported in restricted zone.',
line: 1,
column: 19,
},
],
},
{
// Does not allow to import deeply within Core, using "src/core/..." Webpack alias.
code: 'const d = require("src/core/server/saved_objects")',
filename: path.join(REPO_ROOT, './__fixtures__/no_restricted_paths/client/a.js'),
options: [
{
basePath: REPO_ROOT,
zones: [
{
target: '__fixtures__/no_restricted_paths/**/*',
from: 'src/core/server/**/*',
},
],
},
],
errors: [
{
message: 'Unexpected path "src/core/server/saved_objects" imported in restricted zone.',
line: 1,
column: 19,
},
],
},
{
// Don't use index*.
// It won't work with dirs that start with 'index'.
code: 'import { X } from "./index_patterns"',
filename: path.join(__dirname, './__fixtures__/no_restricted_paths/server/b.js'),
options: [
{
basePath: __dirname,
zones: [
{
target: ['__fixtures__/no_restricted_paths/(public|server)/**/*'],
from: [
'__fixtures__/no_restricted_paths/server/**/*',
'!__fixtures__/no_restricted_paths/server/index*',
],
allowSameFolder: true,
},
],
},
],
errors: [
{
message: 'Unexpected path "./index_patterns" imported in restricted zone.',
line: 1,
column: 19,
},
],
},
],
});