/**
 * @license
 * Copyright Google LLC All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */
import { ChangeDetectionStrategy } from '../change_detection/constants';
import { formatRuntimeError } from '../errors';
import { ViewEncapsulation } from '../metadata/view';
import { noSideEffects } from '../util/closure';
import { EMPTY_ARRAY, EMPTY_OBJ } from '../util/empty';
import { initNgDevMode } from '../util/ng_dev_mode';
import { stringify } from '../util/stringify';
import { NG_COMP_DEF, NG_DIR_DEF, NG_MOD_DEF, NG_PIPE_DEF } from './fields';
import { stringifyCSSSelectorList } from './node_selector_matcher';
/**
 * Create a component definition object.
 *
 *
 * # Example
 * ```
 * class MyComponent {
 *   // Generated by Angular Template Compiler
 *   // [Symbol] syntax will not be supported by TypeScript until v2.7
 *   static ɵcmp = defineComponent({
 *     ...
 *   });
 * }
 * ```
 * @codeGenApi
 */
export function ɵɵdefineComponent(componentDefinition) {
    return noSideEffects(() => {
        // Initialize ngDevMode. This must be the first statement in ɵɵdefineComponent.
        // See the `initNgDevMode` docstring for more information.
        (typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode();
        const baseDef = getNgDirectiveDef(componentDefinition);
        const def = {
            ...baseDef,
            decls: componentDefinition.decls,
            vars: componentDefinition.vars,
            template: componentDefinition.template,
            consts: componentDefinition.consts || null,
            ngContentSelectors: componentDefinition.ngContentSelectors,
            onPush: componentDefinition.changeDetection === ChangeDetectionStrategy.OnPush,
            directiveDefs: null,
            pipeDefs: null,
            dependencies: baseDef.standalone && componentDefinition.dependencies || null,
            getStandaloneInjector: null,
            signals: componentDefinition.signals ?? false,
            data: componentDefinition.data || {},
            encapsulation: componentDefinition.encapsulation || ViewEncapsulation.Emulated,
            styles: componentDefinition.styles || EMPTY_ARRAY,
            _: null,
            schemas: componentDefinition.schemas || null,
            tView: null,
            id: '',
        };
        initFeatures(def);
        const dependencies = componentDefinition.dependencies;
        def.directiveDefs = extractDefListOrFactory(dependencies, /* pipeDef */ false);
        def.pipeDefs = extractDefListOrFactory(dependencies, /* pipeDef */ true);
        def.id = getComponentId(def);
        return def;
    });
}
/**
 * Generated next to NgModules to monkey-patch directive and pipe references onto a component's
 * definition, when generating a direct reference in the component file would otherwise create an
 * import cycle.
 *
 * See [this explanation](https://hackmd.io/Odw80D0pR6yfsOjg_7XCJg?view) for more details.
 *
 * @codeGenApi
 */
export function ɵɵsetComponentScope(type, directives, pipes) {
    const def = type.ɵcmp;
    def.directiveDefs = extractDefListOrFactory(directives, /* pipeDef */ false);
    def.pipeDefs = extractDefListOrFactory(pipes, /* pipeDef */ true);
}
export function extractDirectiveDef(type) {
    return getComponentDef(type) || getDirectiveDef(type);
}
function nonNull(value) {
    return value !== null;
}
/**
 * @codeGenApi
 */
export function ɵɵdefineNgModule(def) {
    return noSideEffects(() => {
        const res = {
            type: def.type,
            bootstrap: def.bootstrap || EMPTY_ARRAY,
            declarations: def.declarations || EMPTY_ARRAY,
            imports: def.imports || EMPTY_ARRAY,
            exports: def.exports || EMPTY_ARRAY,
            transitiveCompileScopes: null,
            schemas: def.schemas || null,
            id: def.id || null,
        };
        return res;
    });
}
/**
 * Adds the module metadata that is necessary to compute the module's transitive scope to an
 * existing module definition.
 *
 * Scope metadata of modules is not used in production builds, so calls to this function can be
 * marked pure to tree-shake it from the bundle, allowing for all referenced declarations
 * to become eligible for tree-shaking as well.
 *
 * @codeGenApi
 */
export function ɵɵsetNgModuleScope(type, scope) {
    return noSideEffects(() => {
        const ngModuleDef = getNgModuleDef(type, true);
        ngModuleDef.declarations = scope.declarations || EMPTY_ARRAY;
        ngModuleDef.imports = scope.imports || EMPTY_ARRAY;
        ngModuleDef.exports = scope.exports || EMPTY_ARRAY;
    });
}
/**
 * Inverts an inputs or outputs lookup such that the keys, which were the
 * minified keys, are part of the values, and the values are parsed so that
 * the publicName of the property is the new key
 *
 * e.g. for
 *
 * ```
 * class Comp {
 *   @Input()
 *   propName1: string;
 *
 *   @Input('publicName2')
 *   declaredPropName2: number;
 * }
 * ```
 *
 * will be serialized as
 *
 * ```
 * {
 *   propName1: 'propName1',
 *   declaredPropName2: ['publicName2', 'declaredPropName2'],
 * }
 * ```
 *
 * which is than translated by the minifier as:
 *
 * ```
 * {
 *   minifiedPropName1: 'propName1',
 *   minifiedPropName2: ['publicName2', 'declaredPropName2'],
 * }
 * ```
 *
 * becomes: (public name => minifiedName)
 *
 * ```
 * {
 *  'propName1': 'minifiedPropName1',
 *  'publicName2': 'minifiedPropName2',
 * }
 * ```
 *
 * Optionally the function can take `secondary` which will result in: (public name => declared name)
 *
 * ```
 * {
 *  'propName1': 'propName1',
 *  'publicName2': 'declaredPropName2',
 * }
 * ```
 *

 */
function invertObject(obj, secondary) {
    if (obj == null)
        return EMPTY_OBJ;
    const newLookup = {};
    for (const minifiedKey in obj) {
        if (obj.hasOwnProperty(minifiedKey)) {
            let publicName = obj[minifiedKey];
            let declaredName = publicName;
            if (Array.isArray(publicName)) {
                declaredName = publicName[1];
                publicName = publicName[0];
            }
            newLookup[publicName] = minifiedKey;
            if (secondary) {
                (secondary[publicName] = declaredName);
            }
        }
    }
    return newLookup;
}
/**
 * Create a directive definition object.
 *
 * # Example
 * ```ts
 * class MyDirective {
 *   // Generated by Angular Template Compiler
 *   // [Symbol] syntax will not be supported by TypeScript until v2.7
 *   static ɵdir = ɵɵdefineDirective({
 *     ...
 *   });
 * }
 * ```
 *
 * @codeGenApi
 */
export function ɵɵdefineDirective(directiveDefinition) {
    return noSideEffects(() => {
        const def = getNgDirectiveDef(directiveDefinition);
        initFeatures(def);
        return def;
    });
}
/**
 * Create a pipe definition object.
 *
 * # Example
 * ```
 * class MyPipe implements PipeTransform {
 *   // Generated by Angular Template Compiler
 *   static ɵpipe = definePipe({
 *     ...
 *   });
 * }
 * ```
 * @param pipeDef Pipe definition generated by the compiler
 *
 * @codeGenApi
 */
export function ɵɵdefinePipe(pipeDef) {
    return {
        type: pipeDef.type,
        name: pipeDef.name,
        factory: null,
        pure: pipeDef.pure !== false,
        standalone: pipeDef.standalone === true,
        onDestroy: pipeDef.type.prototype.ngOnDestroy || null
    };
}
/**
 * The following getter methods retrieve the definition from the type. Currently the retrieval
 * honors inheritance, but in the future we may change the rule to require that definitions are
 * explicit. This would require some sort of migration strategy.
 */
export function getComponentDef(type) {
    return type[NG_COMP_DEF] || null;
}
export function getDirectiveDef(type) {
    return type[NG_DIR_DEF] || null;
}
export function getPipeDef(type) {
    return type[NG_PIPE_DEF] || null;
}
/**
 * Checks whether a given Component, Directive or Pipe is marked as standalone.
 * This will return false if passed anything other than a Component, Directive, or Pipe class
 * See [this guide](/guide/standalone-components) for additional information:
 *
 * @param type A reference to a Component, Directive or Pipe.
 * @publicApi
 */
export function isStandalone(type) {
    const def = getComponentDef(type) || getDirectiveDef(type) || getPipeDef(type);
    return def !== null ? def.standalone : false;
}
export function getNgModuleDef(type, throwNotFound) {
    const ngModuleDef = type[NG_MOD_DEF] || null;
    if (!ngModuleDef && throwNotFound === true) {
        throw new Error(`Type ${stringify(type)} does not have 'ɵmod' property.`);
    }
    return ngModuleDef;
}
function getNgDirectiveDef(directiveDefinition) {
    const declaredInputs = {};
    return {
        type: directiveDefinition.type,
        providersResolver: null,
        factory: null,
        hostBindings: directiveDefinition.hostBindings || null,
        hostVars: directiveDefinition.hostVars || 0,
        hostAttrs: directiveDefinition.hostAttrs || null,
        contentQueries: directiveDefinition.contentQueries || null,
        declaredInputs,
        inputTransforms: null,
        inputConfig: directiveDefinition.inputs || EMPTY_OBJ,
        exportAs: directiveDefinition.exportAs || null,
        standalone: directiveDefinition.standalone === true,
        signals: directiveDefinition.signals === true,
        selectors: directiveDefinition.selectors || EMPTY_ARRAY,
        viewQuery: directiveDefinition.viewQuery || null,
        features: directiveDefinition.features || null,
        setInput: null,
        findHostDirectiveDefs: null,
        hostDirectives: null,
        inputs: invertObject(directiveDefinition.inputs, declaredInputs),
        outputs: invertObject(directiveDefinition.outputs),
    };
}
function initFeatures(definition) {
    definition.features?.forEach((fn) => fn(definition));
}
function extractDefListOrFactory(dependencies, pipeDef) {
    if (!dependencies) {
        return null;
    }
    const defExtractor = pipeDef ? getPipeDef : extractDirectiveDef;
    return () => (typeof dependencies === 'function' ? dependencies() : dependencies)
        .map(dep => defExtractor(dep))
        .filter(nonNull);
}
/**
 * A map that contains the generated component IDs and type.
 */
export const GENERATED_COMP_IDS = new Map();
/**
 * A method can returns a component ID from the component definition using a variant of DJB2 hash
 * algorithm.
 */
function getComponentId(componentDef) {
    let hash = 0;
    // We cannot rely solely on the component selector as the same selector can be used in different
    // modules.
    //
    // `componentDef.style` is not used, due to it causing inconsistencies. Ex: when server
    // component styles has no sourcemaps and browsers do.
    //
    // Example:
    // https://github.com/angular/components/blob/d9f82c8f95309e77a6d82fd574c65871e91354c2/src/material/core/option/option.ts#L248
    // https://github.com/angular/components/blob/285f46dc2b4c5b127d356cb7c4714b221f03ce50/src/material/legacy-core/option/option.ts#L32
    const hashSelectors = [
        componentDef.selectors,
        componentDef.ngContentSelectors,
        componentDef.hostVars,
        componentDef.hostAttrs,
        componentDef.consts,
        componentDef.vars,
        componentDef.decls,
        componentDef.encapsulation,
        componentDef.standalone,
        componentDef.signals,
        componentDef.exportAs,
        JSON.stringify(componentDef.inputs),
        JSON.stringify(componentDef.outputs),
        // We cannot use 'componentDef.type.name' as the name of the symbol will change and will not
        // match in the server and browser bundles.
        Object.getOwnPropertyNames(componentDef.type.prototype),
        !!componentDef.contentQueries,
        !!componentDef.viewQuery,
    ].join('|');
    for (const char of hashSelectors) {
        hash = Math.imul(31, hash) + char.charCodeAt(0) << 0;
    }
    // Force positive number hash.
    // 2147483647 = equivalent of Integer.MAX_VALUE.
    hash += 2147483647 + 1;
    const compId = 'c' + hash;
    if (typeof ngDevMode === 'undefined' || ngDevMode) {
        if (GENERATED_COMP_IDS.has(compId)) {
            const previousCompDefType = GENERATED_COMP_IDS.get(compId);
            if (previousCompDefType !== componentDef.type) {
                console.warn(formatRuntimeError(-912 /* RuntimeErrorCode.COMPONENT_ID_COLLISION */, `Component ID generation collision detected. Components '${previousCompDefType.name}' and '${componentDef.type.name}' with selector '${stringifyCSSSelectorList(componentDef
                    .selectors)}' generated the same component ID. To fix this, you can change the selector of one of those components or add an extra host attribute to force a different ID.`));
            }
        }
        else {
            GENERATED_COMP_IDS.set(compId, componentDef.type);
        }
    }
    return compId;
}
//# sourceMappingURL=data:application/json;base64,[encoded data, 43388 characters]