mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 18:51:07 -04:00
[eslint] fix and skip violations for cross-boundary imports (#136911)
This commit is contained in:
parent
103aa675c7
commit
bebec37f04
379 changed files with 368 additions and 1168 deletions
|
@ -34,7 +34,6 @@ RUNTIME_DEPS = [
|
|||
"@npm//dedent",
|
||||
"@npm//eslint",
|
||||
"@npm//eslint-module-utils",
|
||||
"@npm//micromatch",
|
||||
]
|
||||
|
||||
js_library(
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
|
@ -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,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue