diff --git a/docs/developer/best-practices/typescript.asciidoc b/docs/developer/best-practices/typescript.asciidoc index a2cda1e0b1e87..583a98f296de5 100644 --- a/docs/developer/best-practices/typescript.asciidoc +++ b/docs/developer/best-practices/typescript.asciidoc @@ -28,7 +28,7 @@ This architecture imposes several limitations to which we must comply: [discrete] ==== Prerequisites Since project refs rely on generated `d.ts` files, the migration order does matter. You can migrate your plugin only when all the plugin dependencies already have migrated. It creates a situation where commonly used plugins (such as `data` or `kibana_react`) have to migrate first. -https://github.com/elastic/kibana/issues/79343 is going to provide a tool for identifying a plugin dependency tree. +Run `node scripts/find_plugins_without_ts_refs.js --id your_plugin_id` to get a list of plugins that should be switched to TS project refs to unblock your plugin migration. [discrete] ==== Implementation diff --git a/packages/kbn-config/src/env.ts b/packages/kbn-config/src/env.ts index e4585056696f9..e7b4658262235 100644 --- a/packages/kbn-config/src/env.ts +++ b/packages/kbn-config/src/env.ts @@ -19,6 +19,7 @@ import { resolve, join } from 'path'; import loadJsonFile from 'load-json-file'; +import { getPluginSearchPaths } from './plugins'; import { PackageInfo, EnvironmentMode } from './types'; /** @internal */ @@ -114,21 +115,11 @@ export class Env { this.binDir = resolve(this.homeDir, 'bin'); this.logDir = resolve(this.homeDir, 'log'); - /** - * BEWARE: this needs to stay roughly synchronized with the @kbn/optimizer - * `packages/kbn-optimizer/src/optimizer_config.ts` determines the paths - * that should be searched for plugins to build - */ - this.pluginSearchPaths = [ - resolve(this.homeDir, 'src', 'plugins'), - ...(options.cliArgs.oss ? [] : [resolve(this.homeDir, 'x-pack', 'plugins')]), - resolve(this.homeDir, 'plugins'), - ...(options.cliArgs.runExamples ? [resolve(this.homeDir, 'examples')] : []), - ...(options.cliArgs.runExamples && !options.cliArgs.oss - ? [resolve(this.homeDir, 'x-pack', 'examples')] - : []), - resolve(this.homeDir, '..', 'kibana-extra'), - ]; + this.pluginSearchPaths = getPluginSearchPaths({ + rootDir: this.homeDir, + oss: options.cliArgs.oss, + examples: options.cliArgs.runExamples, + }); this.cliArgs = Object.freeze(options.cliArgs); this.configs = Object.freeze(options.configs); diff --git a/packages/kbn-config/src/index.ts b/packages/kbn-config/src/index.ts index f02514a92e606..68609c6d5c7c3 100644 --- a/packages/kbn-config/src/index.ts +++ b/packages/kbn-config/src/index.ts @@ -35,3 +35,4 @@ export { ObjectToConfigAdapter } from './object_to_config_adapter'; export { CliArgs, Env, RawPackageInfo } from './env'; export { EnvironmentMode, PackageInfo } from './types'; export { LegacyObjectToConfigAdapter, LegacyLoggingConfig } from './legacy'; +export { getPluginSearchPaths } from './plugins'; diff --git a/packages/kbn-config/src/plugins/index.ts b/packages/kbn-config/src/plugins/index.ts new file mode 100644 index 0000000000000..7d02f9fb984c2 --- /dev/null +++ b/packages/kbn-config/src/plugins/index.ts @@ -0,0 +1,19 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +export { getPluginSearchPaths } from './plugin_search_paths'; diff --git a/packages/kbn-config/src/plugins/plugin_search_paths.ts b/packages/kbn-config/src/plugins/plugin_search_paths.ts new file mode 100644 index 0000000000000..a7d151c3275c8 --- /dev/null +++ b/packages/kbn-config/src/plugins/plugin_search_paths.ts @@ -0,0 +1,36 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { resolve } from 'path'; + +interface SearchOptions { + rootDir: string; + oss: boolean; + examples: boolean; +} + +export function getPluginSearchPaths({ rootDir, oss, examples }: SearchOptions) { + return [ + resolve(rootDir, 'src', 'plugins'), + ...(oss ? [] : [resolve(rootDir, 'x-pack', 'plugins')]), + resolve(rootDir, 'plugins'), + ...(examples ? [resolve(rootDir, 'examples')] : []), + ...(examples && !oss ? [resolve(rootDir, 'x-pack', 'examples')] : []), + resolve(rootDir, '..', 'kibana-extra'), + ]; +} diff --git a/packages/kbn-dev-utils/src/index.ts b/packages/kbn-dev-utils/src/index.ts index 6a845825f0fd4..98385b49dafa9 100644 --- a/packages/kbn-dev-utils/src/index.ts +++ b/packages/kbn-dev-utils/src/index.ts @@ -40,7 +40,6 @@ export * from './axios'; export * from './stdio'; export * from './ci_stats_reporter'; export * from './plugin_list'; -export * from './simple_kibana_platform_plugin_discovery'; +export * from './plugins'; export * from './streams'; export * from './babel'; -export * from './parse_kibana_platform_plugin'; diff --git a/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts b/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts index e8f6735205b19..9782067e61343 100644 --- a/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts +++ b/packages/kbn-dev-utils/src/plugin_list/discover_plugins.ts @@ -24,7 +24,7 @@ import MarkdownIt from 'markdown-it'; import cheerio from 'cheerio'; import { REPO_ROOT } from '@kbn/utils'; -import { simpleKibanaPlatformPluginDiscovery } from '../simple_kibana_platform_plugin_discovery'; +import { simpleKibanaPlatformPluginDiscovery } from '../plugins'; import { extractAsciidocInfo } from './extract_asciidoc_info'; export interface Plugin { diff --git a/packages/kbn-dev-utils/src/plugins/index.ts b/packages/kbn-dev-utils/src/plugins/index.ts new file mode 100644 index 0000000000000..8705682f355c7 --- /dev/null +++ b/packages/kbn-dev-utils/src/plugins/index.ts @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './parse_kibana_platform_plugin'; +export * from './simple_kibana_platform_plugin_discovery'; diff --git a/packages/kbn-dev-utils/src/parse_kibana_platform_plugin.ts b/packages/kbn-dev-utils/src/plugins/parse_kibana_platform_plugin.ts similarity index 56% rename from packages/kbn-dev-utils/src/parse_kibana_platform_plugin.ts rename to packages/kbn-dev-utils/src/plugins/parse_kibana_platform_plugin.ts index 83d8c2684d7ca..16aaecb3e478d 100644 --- a/packages/kbn-dev-utils/src/parse_kibana_platform_plugin.ts +++ b/packages/kbn-dev-utils/src/plugins/parse_kibana_platform_plugin.ts @@ -23,12 +23,27 @@ import loadJsonFile from 'load-json-file'; export interface KibanaPlatformPlugin { readonly directory: string; readonly manifestPath: string; - readonly manifest: { - id: string; - ui: boolean; - server: boolean; - [key: string]: unknown; - }; + readonly manifest: Manifest; +} + +function isValidDepsDeclaration(input: unknown, type: string): string[] { + if (typeof input === 'undefined') return []; + if (Array.isArray(input) && input.every((i) => typeof i === 'string')) { + return input; + } + throw new TypeError(`The "${type}" in plugin manifest should be an array of strings.`); +} + +interface Manifest { + id: string; + ui: boolean; + server: boolean; + kibanaVersion: string; + version: string; + requiredPlugins: readonly string[]; + optionalPlugins: readonly string[]; + requiredBundles: readonly string[]; + extraPublicDirs: readonly string[]; } export function parseKibanaPlatformPlugin(manifestPath: string): KibanaPlatformPlugin { @@ -36,7 +51,7 @@ export function parseKibanaPlatformPlugin(manifestPath: string): KibanaPlatformP throw new TypeError('expected new platform manifest path to be absolute'); } - const manifest = loadJsonFile.sync(manifestPath); + const manifest: Partial = loadJsonFile.sync(manifestPath); if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) { throw new TypeError('expected new platform plugin manifest to be a JSON encoded object'); } @@ -45,6 +60,10 @@ export function parseKibanaPlatformPlugin(manifestPath: string): KibanaPlatformP throw new TypeError('expected new platform plugin manifest to have a string id'); } + if (typeof manifest.version !== 'string') { + throw new TypeError('expected new platform plugin manifest to have a string version'); + } + return { directory: Path.dirname(manifestPath), manifestPath, @@ -54,6 +73,12 @@ export function parseKibanaPlatformPlugin(manifestPath: string): KibanaPlatformP ui: !!manifest.ui, server: !!manifest.server, id: manifest.id, + version: manifest.version, + kibanaVersion: manifest.kibanaVersion || manifest.version, + requiredPlugins: isValidDepsDeclaration(manifest.requiredPlugins, 'requiredPlugins'), + optionalPlugins: isValidDepsDeclaration(manifest.optionalPlugins, 'optionalPlugins'), + requiredBundles: isValidDepsDeclaration(manifest.requiredBundles, 'requiredBundles'), + extraPublicDirs: isValidDepsDeclaration(manifest.extraPublicDirs, 'extraPublicDirs'), }, }; } diff --git a/packages/kbn-dev-utils/src/simple_kibana_platform_plugin_discovery.ts b/packages/kbn-dev-utils/src/plugins/simple_kibana_platform_plugin_discovery.ts similarity index 100% rename from packages/kbn-dev-utils/src/simple_kibana_platform_plugin_discovery.ts rename to packages/kbn-dev-utils/src/plugins/simple_kibana_platform_plugin_discovery.ts diff --git a/packages/kbn-optimizer/package.json b/packages/kbn-optimizer/package.json index c9e414dbc5177..63146fc7a1834 100644 --- a/packages/kbn-optimizer/package.json +++ b/packages/kbn-optimizer/package.json @@ -14,6 +14,7 @@ "@babel/core": "^7.11.6", "@kbn/babel-preset": "1.0.0", "@kbn/dev-utils": "1.0.0", + "@kbn/config": "1.0.0", "@kbn/std": "1.0.0", "@kbn/ui-shared-deps": "1.0.0", "autoprefixer": "^9.7.4", diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/kibana.json index 33f53e336598d..a5e9f34a22aa6 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/bar/kibana.json @@ -1,5 +1,6 @@ { "id": "bar", "ui": true, - "requiredBundles": ["foo"] + "requiredBundles": ["foo"], + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo/kibana.json index 256856181ccd8..27730df199887 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/foo/kibana.json @@ -1,4 +1,5 @@ { "id": "foo", - "ui": true + "ui": true, + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz/kibana.json index 6e4e9c70a115c..a8f991ee11465 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/plugins/nested/baz/kibana.json @@ -1,3 +1,4 @@ { - "id": "baz" + "id": "baz", + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz/kibana.json index b9e044523a6a5..d8a8b2e548e4a 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/test_plugins/test_baz/kibana.json @@ -1,3 +1,4 @@ { - "id": "test_baz" + "id": "test_baz", + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/__fixtures__/mock_repo/x-pack/baz/kibana.json b/packages/kbn-optimizer/src/__fixtures__/mock_repo/x-pack/baz/kibana.json index 10602d2e7981a..64ec7ff5ccf3e 100644 --- a/packages/kbn-optimizer/src/__fixtures__/mock_repo/x-pack/baz/kibana.json +++ b/packages/kbn-optimizer/src/__fixtures__/mock_repo/x-pack/baz/kibana.json @@ -1,4 +1,5 @@ { "id": "baz", - "ui": true + "ui": true, + "version": "8.0.0" } diff --git a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts index 8091f6aa90508..1443fccda04d8 100644 --- a/packages/kbn-optimizer/src/optimizer/optimizer_config.ts +++ b/packages/kbn-optimizer/src/optimizer/optimizer_config.ts @@ -19,6 +19,7 @@ import Path from 'path'; import Os from 'os'; +import { getPluginSearchPaths } from '@kbn/config'; import { Bundle, @@ -167,19 +168,14 @@ export class OptimizerConfig { throw new TypeError('outputRoot must be an absolute path'); } - /** - * BEWARE: this needs to stay roughly synchronized with - * `src/core/server/config/env.ts` which determines which paths - * should be searched for plugins to load - */ - const pluginScanDirs = options.pluginScanDirs || [ - Path.resolve(repoRoot, 'src/plugins'), - ...(oss ? [] : [Path.resolve(repoRoot, 'x-pack/plugins')]), - Path.resolve(repoRoot, 'plugins'), - ...(examples ? [Path.resolve('examples')] : []), - ...(examples && !oss ? [Path.resolve('x-pack/examples')] : []), - Path.resolve(repoRoot, '../kibana-extra'), - ]; + const pluginScanDirs = + options.pluginScanDirs || + getPluginSearchPaths({ + rootDir: repoRoot, + oss, + examples, + }); + if (!pluginScanDirs.every((p) => Path.isAbsolute(p))) { throw new TypeError('pluginScanDirs must all be absolute paths'); } diff --git a/scripts/find_plugin_circular_deps.js b/scripts/find_plugin_circular_deps.js new file mode 100644 index 0000000000000..6b0661cb841b4 --- /dev/null +++ b/scripts/find_plugin_circular_deps.js @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +require('../src/setup_node_env'); +require('../src/dev/run_find_plugin_circular_deps'); diff --git a/scripts/find_plugins_without_ts_refs.js b/scripts/find_plugins_without_ts_refs.js new file mode 100644 index 0000000000000..5f543a045f739 --- /dev/null +++ b/scripts/find_plugins_without_ts_refs.js @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +require('../src/setup_node_env'); +require('../src/dev/run_find_plugins_without_ts_refs'); diff --git a/src/dev/plugin_discovery/find_plugins.ts b/src/dev/plugin_discovery/find_plugins.ts new file mode 100644 index 0000000000000..4e7c34698c964 --- /dev/null +++ b/src/dev/plugin_discovery/find_plugins.ts @@ -0,0 +1,50 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import Path from 'path'; +import { REPO_ROOT } from '@kbn/dev-utils'; +import { getPluginSearchPaths } from '@kbn/config'; +import { KibanaPlatformPlugin, simpleKibanaPlatformPluginDiscovery } from '@kbn/dev-utils'; + +export interface SearchOptions { + oss: boolean; + examples: boolean; + extraPluginScanDirs: string[]; +} + +export function findPlugins({ + oss, + examples, + extraPluginScanDirs, +}: SearchOptions): Map { + const pluginSearchPaths = getPluginSearchPaths({ + rootDir: REPO_ROOT, + oss, + examples, + }); + + for (const extraScanDir of extraPluginScanDirs) { + if (!Path.isAbsolute(extraScanDir)) { + throw new TypeError('extraPluginScanDirs must all be absolute paths'); + } + pluginSearchPaths.push(extraScanDir); + } + + const plugins = simpleKibanaPlatformPluginDiscovery(pluginSearchPaths, []); + return new Map(plugins.map((p) => [p.manifest.id, p])); +} diff --git a/src/dev/plugin_discovery/get_plugin_deps.ts b/src/dev/plugin_discovery/get_plugin_deps.ts new file mode 100644 index 0000000000000..498feefd97094 --- /dev/null +++ b/src/dev/plugin_discovery/get_plugin_deps.ts @@ -0,0 +1,89 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import { KibanaPlatformPlugin } from '@kbn/dev-utils'; + +interface AllOptions { + id: string; + pluginMap: Map; +} + +interface CircularRefsError { + from: string; + to: string; + stack: string[]; +} + +export type SearchErrors = CircularRefsError; + +interface State { + deps: Set; + stack: string[]; + errors: Map; +} + +function traverse(pluginMap: Map, state: State, id: string) { + const plugin = pluginMap.get(id); + if (plugin === undefined) { + throw new Error(`Unknown plugin id: ${id}`); + } + + const prevIndex = state.stack.indexOf(id); + const isVisited = prevIndex > -1; + if (isVisited) { + const from = state.stack[state.stack.length - 1]; + const to = id; + const key = `circular-${[from, to].sort().join('-')}`; + + if (!state.errors.has(key)) { + const error: CircularRefsError = { + from, + to, + // provide sub-stack with circular refs only + stack: state.stack.slice(prevIndex), + }; + state.errors.set(key, error); + } + + return; + } + + state.stack.push(id); + new Set([ + ...plugin.manifest.requiredPlugins, + ...plugin.manifest.optionalPlugins, + ...plugin.manifest.requiredBundles, + ]).forEach((depId) => { + state.deps.add(pluginMap.get(depId)!); + traverse(pluginMap, state, depId); + }); + + state.stack.pop(); +} + +export function getPluginDeps({ pluginMap, id }: AllOptions): State { + const state: State = { + deps: new Set(), + errors: new Map(), + stack: [], + }; + + traverse(pluginMap, state, id); + + return state; +} diff --git a/src/dev/plugin_discovery/index.ts b/src/dev/plugin_discovery/index.ts new file mode 100644 index 0000000000000..4a4be65dfaef0 --- /dev/null +++ b/src/dev/plugin_discovery/index.ts @@ -0,0 +1,21 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export * from './find_plugins'; +export * from './get_plugin_deps'; diff --git a/src/dev/run_find_plugin_circular_deps.ts b/src/dev/run_find_plugin_circular_deps.ts new file mode 100644 index 0000000000000..501e2c4fed048 --- /dev/null +++ b/src/dev/run_find_plugin_circular_deps.ts @@ -0,0 +1,73 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { run } from '@kbn/dev-utils'; +import { findPlugins, getPluginDeps, SearchErrors } from './plugin_discovery'; + +interface AllOptions { + examples?: boolean; + extraPluginScanDirs?: string[]; +} + +run( + async ({ flags, log }) => { + const { examples = false, extraPluginScanDirs = [] } = flags as AllOptions; + + const pluginMap = findPlugins({ + oss: false, + examples, + extraPluginScanDirs, + }); + + const allErrors = new Map(); + for (const pluginId of pluginMap.keys()) { + const { errors } = getPluginDeps({ + pluginMap, + id: pluginId, + }); + + for (const [errorId, error] of errors) { + if (!allErrors.has(errorId)) { + allErrors.set(errorId, error); + } + } + } + + if (allErrors.size > 0) { + allErrors.forEach((error) => { + log.warning( + `Circular refs detected: ${[...error.stack, error.to].map((p) => `[${p}]`).join(' --> ')}` + ); + }); + } + }, + { + flags: { + boolean: ['examples'], + default: { + examples: false, + }, + allowUnexpected: false, + help: ` + --examples Include examples folder + --extraPluginScanDirs Include extra scan folder + `, + }, + } +); diff --git a/src/dev/run_find_plugins_without_ts_refs.ts b/src/dev/run_find_plugins_without_ts_refs.ts new file mode 100644 index 0000000000000..ad63884671e24 --- /dev/null +++ b/src/dev/run_find_plugins_without_ts_refs.ts @@ -0,0 +1,95 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Path from 'path'; +import Fs from 'fs'; +import { get } from 'lodash'; +import { run } from '@kbn/dev-utils'; +import { getPluginDeps, findPlugins } from './plugin_discovery'; + +interface AllOptions { + id?: string; + examples?: boolean; + extraPluginScanDirs?: string[]; +} + +run( + async ({ flags, log }) => { + const { examples = false, extraPluginScanDirs = [], id } = flags as AllOptions; + + if (!id) { + throw new Error('Plugin id required'); + } + + const pluginMap = findPlugins({ + oss: false, + examples, + extraPluginScanDirs, + }); + + const result = getPluginDeps({ + pluginMap, + id, + }); + + if (result.errors.size > 0) { + result.errors.forEach((error) => { + log.warning( + `Circular refs detected: ${[...error.stack, error.to].map((p) => `[${p}]`).join(' --> ')}` + ); + }); + } + + const notMigratedPlugins = [...result.deps].filter( + (plugin) => !isMigratedToTsProjectRefs(plugin.directory) + ); + if (notMigratedPlugins.length > 0) { + log.info( + `Dependencies haven't been migrated to TS project refs yet:\n${notMigratedPlugins + .map((p) => p.manifest.id) + .join('\n')}` + ); + } + }, + { + flags: { + boolean: ['examples'], + string: ['id'], + default: { + examples: false, + }, + allowUnexpected: false, + help: ` + --id Plugin id to perform deps search for + --examples Include examples folder + --extraPluginScanDirs Include extra scan folder + `, + }, + } +); + +function isMigratedToTsProjectRefs(dir: string): boolean { + try { + const path = Path.join(dir, 'tsconfig.json'); + const content = Fs.readFileSync(path, { encoding: 'utf8' }); + return get(JSON.parse(content), 'compilerOptions.composite', false); + } catch (e) { + return false; + } +}