kibana/src/platform/packages/private/kbn-repo-packages/modern/package.js
Gerard Soldevila adb4bdf8d8
SKA: Extract list of Kibana solutions into a dedicated package (#213353)
## Summary

The intent is to have a centralised place to store the list of Kibana
solutions and serverless project types.
To that end, this PR creates a `@kbn/projects-solutions-groups` package.
It also adds the new solution type `'chat'`.

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
2025-03-20 10:20:07 +01:00

253 lines
7.3 KiB
JavaScript

/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/
const { inspect } = require('util');
const Path = require('path');
const { readPackageJson } = require('./parse_package_json');
const { PLUGIN_CATEGORY } = require('./plugin_category_info');
const { readPackageManifest } = require('./parse_package_manifest');
/**
* Normalize a path for operating systems which use backslashes
* @param {string} path
*/
const normalize = (path) => (Path.sep !== '/' ? path.split('\\').join('/') : path);
/**
* Representation of a Bazel Package in the Kibana repository
* @class
*/
class Package {
/**
* Create a Package object from a package directory. Reads some files from the package and returns
* a Promise for a Package instance.
* @param {string} repoRoot
* @param {string} path
*/
static fromManifest(repoRoot, path) {
const manifest = readPackageManifest(repoRoot, path);
const dir = Path.dirname(path);
return new Package(repoRoot, dir, manifest, readPackageJson(Path.resolve(dir, 'package.json')));
}
/**
* Sort an array of packages
* @param {Package} a
* @param {Package} b
*/
static sorter(a, b) {
return a.manifest.id.localeCompare(b.manifest.id, 'en');
}
/**
* @private
*/
constructor(
/**
* Absolute path to the root of the repository
* @type {string}
*/
repoRoot,
/**
* Absolute path to the package directory
* @type {string}
*/
dir,
/**
* Parsed kibana.jsonc manifest from the package
* @type {import('./types').KibanaPackageManifest}
*/
manifest,
/**
* Parsed package.json file from the package
* @type {import('./types').ParsedPackageJson | undefined}
*/
pkg
) {
/**
* Absolute path to this package directory
* @type {string}
* @readonly
*/
this.directory = dir;
/**
* repo relative path to the root of the package
* @type {string}
* @readonly
*/
this.normalizedRepoRelativeDir = normalize(Path.relative(repoRoot, dir));
/**
* copy of the parsed kibana.jsonc manifest of the package
*
* @type {import('./types').KibanaPackageManifest}
* @readonly
* @deprecated rather than accessing this directly the necessary information about the package should be reflected on the Package class.
*/
this.manifest = manifest;
/**
* copy of the parsed package.json file in the package
* @type {import('./types').ParsedPackageJson | undefined}
* @readonly
* @deprecated rather than accessing this directly the necessary information about the package should be reflected on the Package class.
*/
this.pkg = pkg;
/**
* the name/import id of the package
* @type {string}
* @readonly
*/
this.name = manifest.id;
/**
* the name/import id of the package
* @type {string}
* @readonly
*/
this.id = manifest.id;
const { group, visibility } = this.determineGroupAndVisibility();
/**
* the group to which this package belongs
* @type {import('@kbn/projects-solutions-groups').ModuleGroup}
* @readonly
*/
this.group = group;
/**
* the visibility of this package, i.e. whether it can be accessed by everybody or only modules in the same group
* @type {import('@kbn/projects-solutions-groups').ModuleVisibility}
* @readonly
*/
this.visibility = visibility;
}
/**
* Is this package highlighted as a "dev only" package? If so it will always
* be listed in the devDependencies and will never end up in the build
* @returns {boolean}
*/
isDevOnly() {
return (
!!this.manifest.devOnly ||
this.manifest.type === 'functional-tests' ||
this.manifest.type === 'test-helper'
);
}
/**
* Does this package expose a plugin, is it of one of the plugin types?
* @readonly
* @returns {this is import('./types').PluginPackage}
*/
isPlugin() {
return this.manifest.type === 'plugin';
}
/**
* Returns the group to which this package belongs
* @readonly
* @returns {import('@kbn/projects-solutions-groups').ModuleGroup}
*/
getGroup() {
return this.group;
}
/**
* Returns the package visibility, i.e. whether it can be accessed by everybody or only packages in the same group
* @readonly
* @returns {import('@kbn/projects-solutions-groups').ModuleVisibility}
*/
getVisibility() {
return this.visibility;
}
/**
* Returns true if the package represents some type of plugin
* @returns {import('./types').PluginCategoryInfo}
*/
getPluginCategories() {
if (!this.isPlugin()) {
throw new Error('package is not a plugin, check pkg.isPlugin before calling this method');
}
const preCalculated = this.manifest.plugin[PLUGIN_CATEGORY];
if (preCalculated) {
return { ...preCalculated };
}
const dir = this.normalizedRepoRelativeDir;
const oss = !dir.startsWith('x-pack/');
const example = dir.startsWith('examples/') || dir.startsWith('x-pack/examples/');
const testPlugin = dir.startsWith('src/platform/test/') || dir.startsWith('x-pack/test/');
return {
oss,
example,
testPlugin,
};
}
determineGroupAndVisibility() {
const dir = this.normalizedRepoRelativeDir;
/** @type {import('@kbn/projects-solutions-groups').ModuleGroup} */
let group = 'common';
/** @type {import('@kbn/projects-solutions-groups').ModuleVisibility} */
let visibility = 'shared';
if (dir.startsWith('src/platform/') || dir.startsWith('x-pack/platform/')) {
group = 'platform';
visibility =
/src\/platform\/[^\/]+\/shared/.test(dir) || /x-pack\/platform\/[^\/]+\/shared/.test(dir)
? 'shared'
: 'private';
// BOOKMARK - List of Kibana solutions
} else if (dir.startsWith('x-pack/solutions/search/')) {
group = 'search';
visibility = 'private';
} else if (dir.startsWith('x-pack/solutions/security/')) {
group = 'security';
visibility = 'private';
} else if (dir.startsWith('x-pack/solutions/observability/')) {
group = 'observability';
visibility = 'private';
} else if (dir.startsWith('x-pack/solutions/chat/')) {
group = 'chat';
visibility = 'private';
} else {
group = this.manifest.group ?? 'common';
// if the group is 'private-only', enforce it
// BOOKMARK - List of Kibana solutions - FIXME we could use KIBANA_SOLUTIONS array here once we modernize this / get rid of Bazel
visibility = ['search', 'security', 'observability', 'chat'].includes(group)
? 'private'
: this.manifest.visibility ?? 'shared';
}
return { group, visibility };
}
/**
* Custom inspect handler so that logging variables in scripts/generate doesn't
* print all the BUILD.bazel files
*/
[inspect.custom]() {
return `${this.isPlugin() ? `PluginPackage` : `Package`}<${this.normalizedRepoRelativeDir}>`;
}
}
module.exports = {
Package,
};