export async function parseManifest()

in src/core/packages/plugins/server-internal/src/discovery/plugin_manifest_parser.ts [69:222]


export async function parseManifest(
  pluginPath: string,
  packageInfo: PackageInfo
): Promise<PluginManifest> {
  const manifestPath = resolve(pluginPath, MANIFEST_FILE_NAME);

  let manifestContent;
  try {
    manifestContent = await fsReadFileAsync(manifestPath);
  } catch (err) {
    throw PluginDiscoveryError.missingManifest(manifestPath, err);
  }

  let manifest: Partial<PluginManifest>;
  try {
    manifest = JSON.parse(manifestContent.toString());
  } catch (err) {
    throw PluginDiscoveryError.invalidManifest(manifestPath, err);
  }

  if (!manifest || typeof manifest !== 'object') {
    throw PluginDiscoveryError.invalidManifest(
      manifestPath,
      new Error('Plugin manifest must contain a JSON encoded object.')
    );
  }

  if (!manifest.id || typeof manifest.id !== 'string') {
    throw PluginDiscoveryError.invalidManifest(
      manifestPath,
      new Error('Plugin manifest must contain an "id" property.')
    );
  }

  // Plugin id can be used as a config path or as a logger context and having dots
  // in there may lead to various issues, so we forbid that.
  if (manifest.id.includes('.')) {
    throw PluginDiscoveryError.invalidManifest(
      manifestPath,
      new Error('Plugin "id" must not include `.` characters.')
    );
  }

  if (!isCamelCase(manifest.id)) {
    throw PluginDiscoveryError.invalidManifest(
      manifestPath,
      new Error(`Plugin "id" must be camelCase, but found: ${manifest.id}.`)
    );
  }

  if (!manifest.version || typeof manifest.version !== 'string') {
    throw PluginDiscoveryError.invalidManifest(
      manifestPath,
      new Error(`Plugin manifest for "${manifest.id}" must contain a "version" property.`)
    );
  }

  if (!manifest.owner || !manifest.owner.name || typeof manifest.owner.name !== 'string') {
    throw PluginDiscoveryError.invalidManifest(
      manifestPath,
      new Error(
        `Plugin manifest for "${manifest.id}" must contain an "owner" property, which includes a nested "name" property.`
      )
    );
  }

  if (manifest.configPath !== undefined && !isConfigPath(manifest.configPath)) {
    throw PluginDiscoveryError.invalidManifest(
      manifestPath,
      new Error(
        `The "configPath" in plugin manifest for "${manifest.id}" should either be a string or an array of strings.`
      )
    );
  }

  if (
    manifest.extraPublicDirs &&
    (!Array.isArray(manifest.extraPublicDirs) ||
      !manifest.extraPublicDirs.every((dir) => typeof dir === 'string'))
  ) {
    throw PluginDiscoveryError.invalidManifest(
      manifestPath,
      new Error(
        `The "extraPublicDirs" in plugin manifest for "${manifest.id}" should be an array of strings.`
      )
    );
  }

  const expectedKibanaVersion =
    typeof manifest.kibanaVersion === 'string' && manifest.kibanaVersion
      ? manifest.kibanaVersion
      : manifest.version;
  if (!isVersionCompatible(expectedKibanaVersion, packageInfo.version)) {
    throw PluginDiscoveryError.incompatibleVersion(
      manifestPath,
      new Error(
        `Plugin "${manifest.id}" is only compatible with Kibana version "${expectedKibanaVersion}", but used Kibana version is "${packageInfo.version}".`
      )
    );
  }

  const includesServerPlugin = typeof manifest.server === 'boolean' ? manifest.server : false;
  const includesUiPlugin = typeof manifest.ui === 'boolean' ? manifest.ui : false;
  if (!includesServerPlugin && !includesUiPlugin) {
    throw PluginDiscoveryError.invalidManifest(
      manifestPath,
      new Error(
        `Both "server" and "ui" are missing or set to "false" in plugin manifest for "${manifest.id}", but at least one of these must be set to "true".`
      )
    );
  }

  const unknownManifestKeys = Object.keys(manifest).filter(
    (key) => !KNOWN_MANIFEST_FIELDS.has(key)
  );
  if (unknownManifestKeys.length > 0) {
    throw PluginDiscoveryError.invalidManifest(
      manifestPath,
      new Error(
        `Manifest for plugin "${manifest.id}" contains the following unrecognized properties: ${unknownManifestKeys}.`
      )
    );
  }

  const type = manifest.type ?? PluginType.standard;
  if (type !== PluginType.preboot && type !== PluginType.standard) {
    throw PluginDiscoveryError.invalidManifest(
      manifestPath,
      new Error(
        `The "type" in manifest for plugin "${manifest.id}" is set to "${type}", but it should either be "standard" or "preboot".`
      )
    );
  }

  return {
    id: manifest.id,
    version: manifest.version,
    kibanaVersion: expectedKibanaVersion,
    type,
    configPath: manifest.configPath || snakeCase(manifest.id),
    requiredPlugins: Array.isArray(manifest.requiredPlugins) ? manifest.requiredPlugins : [],
    optionalPlugins: Array.isArray(manifest.optionalPlugins) ? manifest.optionalPlugins : [],
    requiredBundles: Array.isArray(manifest.requiredBundles) ? manifest.requiredBundles : [],
    runtimePluginDependencies: Array.isArray(manifest.runtimePluginDependencies)
      ? manifest.runtimePluginDependencies
      : [],
    ui: includesUiPlugin,
    server: includesServerPlugin,
    extraPublicDirs: manifest.extraPublicDirs,
    owner: manifest.owner!,
    description: manifest.description,
    enabledOnAnonymousPages: manifest.enabledOnAnonymousPages,
  };
}