async loadTriggers()

in src/emulator/functionsEmulator.ts [452:608]


  async loadTriggers(emulatableBackend: EmulatableBackend, force = false): Promise<void> {
    // Before loading any triggers we need to make sure there are no 'stale' workers
    // in the pool that would cause us to run old code.
    this.workerPool.refresh();

    if (!emulatableBackend.nodeBinary) {
      throw new FirebaseError(
        `No node binary for ${emulatableBackend.functionsDir}. This should never happen.`
      );
    }
    const worker = this.invokeRuntime(
      this.getBaseBundle(emulatableBackend),
      {
        nodeBinary: emulatableBackend.nodeBinary,
        extensionTriggers: emulatableBackend.predefinedTriggers,
      },
      // Don't include user envs when parsing triggers.
      {
        ...this.getSystemEnvs(),
        ...this.getEmulatorEnvs(),
        FIREBASE_CONFIG: this.getFirebaseConfig(),
        ...emulatableBackend.env,
      }
    );

    const triggerParseEvent = await EmulatorLog.waitForLog(
      worker.runtime.events,
      "SYSTEM",
      "triggers-parsed"
    );
    // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
    const parsedDefinitions = triggerParseEvent.data
      .triggerDefinitions as ParsedTriggerDefinition[];

    const triggerDefinitions: EmulatedTriggerDefinition[] =
      emulatedFunctionsByRegion(parsedDefinitions);

    // When force is true we set up all triggers, otherwise we only set up
    // triggers which have a unique function name
    const toSetup = triggerDefinitions.filter((definition) => {
      if (force) {
        return true;
      }

      // We want to add a trigger if we don't already have an enabled trigger
      // with the same entryPoint / trigger.
      const anyEnabledMatch = Object.values(this.triggers).some((record) => {
        const sameEntryPoint = record.def.entryPoint === definition.entryPoint;

        // If they both have event triggers, make sure they match
        const sameEventTrigger =
          JSON.stringify(record.def.eventTrigger) === JSON.stringify(definition.eventTrigger);

        if (sameEntryPoint && !sameEventTrigger) {
          this.logger.log(
            "DEBUG",
            `Definition for trigger ${definition.entryPoint} changed from ${JSON.stringify(
              record.def.eventTrigger
            )} to ${JSON.stringify(definition.eventTrigger)}`
          );
        }

        return record.enabled && sameEntryPoint && sameEventTrigger;
      });
      return !anyEnabledMatch;
    });

    for (const definition of toSetup) {
      // Skip function with invalid id.
      try {
        functionIdsAreValid([definition]);
      } catch (e: any) {
        this.logger.logLabeled(
          "WARN",
          `functions[${definition.id}]`,
          `Invalid function id: ${e.message}`
        );
        continue;
      }

      let added = false;
      let url: string | undefined = undefined;

      if (definition.httpsTrigger) {
        const { host, port } = this.getInfo();
        added = true;
        url = FunctionsEmulator.getHttpFunctionUrl(
          host,
          port,
          this.args.projectId,
          definition.name,
          definition.region
        );
      } else if (definition.eventTrigger) {
        const service: string = getFunctionService(definition);
        const key = this.getTriggerKey(definition);
        const signature = getSignatureType(definition);

        switch (service) {
          case Constants.SERVICE_FIRESTORE:
            added = await this.addFirestoreTrigger(
              this.args.projectId,
              key,
              definition.eventTrigger
            );
            break;
          case Constants.SERVICE_REALTIME_DATABASE:
            added = await this.addRealtimeDatabaseTrigger(
              this.args.projectId,
              key,
              definition.eventTrigger
            );
            break;
          case Constants.SERVICE_PUBSUB:
            added = await this.addPubsubTrigger(
              definition.name,
              key,
              definition.eventTrigger,
              signature,
              definition.schedule
            );
            break;
          case Constants.SERVICE_AUTH:
            added = this.addAuthTrigger(this.args.projectId, key, definition.eventTrigger);
            break;
          case Constants.SERVICE_STORAGE:
            added = this.addStorageTrigger(this.args.projectId, key, definition.eventTrigger);
            break;
          default:
            this.logger.log("DEBUG", `Unsupported trigger: ${JSON.stringify(definition)}`);
            break;
        }
      } else {
        this.logger.log(
          "WARN",
          `Trigger trigger "${definition.name}" has has neither "httpsTrigger" or "eventTrigger" member`
        );
      }

      const ignored = !added;
      this.addTriggerRecord(definition, { backend: emulatableBackend, ignored, url });

      const type = definition.httpsTrigger
        ? "http"
        : Constants.getServiceName(getFunctionService(definition));

      if (ignored) {
        const msg = `function ignored because the ${type} emulator does not exist or is not running.`;
        this.logger.logLabeled("BULLET", `functions[${definition.id}]`, msg);
      } else {
        const msg = url
          ? `${clc.bold(type)} function initialized (${url}).`
          : `${clc.bold(type)} function initialized.`;
        this.logger.logLabeled("SUCCESS", `functions[${definition.id}]`, msg);
      }
    }
  }