export function rewriteExtend()

in src/schema/firefox-schemas-import.js [350:421]


export function rewriteExtend(schemas, schemaId) {
  const definitions = {};
  const refs = {};
  const types = {};
  schemas.forEach((extendSchema) => {
    const extendId = extendSchema.namespace;
    const { max_manifest_version, min_manifest_version } = extendSchema;
    const extendDefinitions = {};
    const extendTypes = {};
    (extendSchema.types || []).forEach((type) => {
      const { $extend, id, ...rest } = type;
      if ($extend) {
        // Throw an explicit error if we are going to deep merge a set of properties which may be
        // overriding an existing one, instead of extending the type with new ones as expected.
        if (rest.properties) {
          const newProps = Object.keys(rest.properties);
          if (
            !newProps.every(
              // Make sure that the value set of each property name that we are going to merge
              // is null or undefined.
              (k) => extendDefinitions?.[$extend]?.properties?.[k] == null
            )
          ) {
            throw new Error(oneLine`Unsupported schema format:
              detected multiple extend schema entries overwriting existing properties
              while processing "${schemaId}" namespace
            `);
          }
        }
        // Move the $extend into definitions.
        //
        // NOTE: some schema files, like browser_action.json, may contain more than one extends
        // and so here we use deepmerge to merge the multiple extended definitions instead
        // of overwriting it with the last one processed.
        extendDefinitions[$extend] = deepmerge(
          extendDefinitions[$extend] ?? {},
          rest
        );
        // Remember the location of this file so we can $ref it later.
        refs[`${schemaId}#/definitions/${$extend}`] = {
          namespace: extendId,
          type: $extend,
        };
      } else if (id) {
        // Move this type into types.
        extendTypes[id] = rest;
        types[id] = rest;
      } else {
        throw new Error('cannot handle extend, $extend or id is required');
      }
    });
    Object.keys(extendDefinitions).forEach((id) => {
      // Update $refs to point to the right namespace.
      const definition = extendDefinitions[id];
      if (extendId === 'manifest') {
        // if the definition is extending the manifest types, we have to
        // propagate the min/max_manifest_version keyword to the definitions
        // attributes (which make sure we will collect validation errors if
        // the manifest property is part of an API namespace unsupported on
        // the addon manifest version.
        propagateManifestVersionRestrictions({
          definition,
          max_manifest_version,
          min_manifest_version,
        });
      }
      definitions[id] = rewriteExtendRefs(definition, extendId, extendTypes);
    });
  });

  return { definitions, refs, types };
}