async _exportPlatform(platform) {
    await this.hasInitialized;
    const platformConfig = this.getPlatformConfig(platform);

    /** @type {PreprocessedTokens | TransformedTokens} */
    let tokens = structuredClone(this.tokens);
    /** @type {Map<string, Token> | Map<string, TransformedToken>} */
    let tokenMap = structuredClone(this.tokenMap);
    /** @type {Token[] | TransformedToken[]} */
    let allTokens = /** */ structuredClone(this.allTokens);
    const usesDtcg = this.usesDtcg;
    const opts = this.options;

    if (platformConfig.preprocessors && platformConfig.preprocessors.length > 0) {
      tokens = await preprocess(
        tokens,
        platformConfig.preprocessors,
        this.hooks.preprocessors,
        platformConfig,
      );
      tokenMap = convertTokenData(tokens, { output: 'map', usesDtcg });
    }

    if (this.shouldRunExpansion(platformConfig.expand)) {
      tokenMap = expandTokens(tokenMap, opts, platformConfig);
    }

    /**
     * @type {Set<string>}
     * list keeping paths of props with applied value transformations
     */
    const transformedPropRefs = new Set();
    /**
     * list keeping paths of props that had references in it, and therefore
     * could not (yet) have transformed
     * @type {Set<string>}
     */
    const deferredPropValueTransforms = new Set();

    const transformationContext = {
      transformedPropRefs,
      deferredPropValueTransforms,
    };

    let deferredPropCount = 0;
    let finished = false;

    while (!finished) {
      // We keep up transforming and resolving until all props are resolved
      // and every defined transformation was executed. Remember: transformations
      // can only be executed, if the value to be transformed, has no references
      // in it. So resolving may lead to enable further transformations, and sub
      // sequent resolving may enable even more transformations - and so on.
      // So we keep this loop running until sub sequent transformations are ineffective.
      //
      // Take the following example:
      //
      // color.brand = {
      //   value: "{color.base.green}"
      // }
      //
      // color.background.button.primary.base = {
      //   value: "{color.brand}",
      //   color: {
      //     desaturate: 0.5
      //   }
      // }
      //
      // color.background.button.primary.hover = {
      //   value: "{color.background.button.primary.base}",
      //   color: {
      //     darken: 0.2
      //   }
      // }
      //
      // As you can see 'color.background.button.primary.hover' is a variation
      // of 'color.background.button.primary.base' which is a variation of
      // 'color.base.green'. These transitive references are solved by running
      // this loop until all tokens are transformed and resolved.

      // We need to transform the object before we resolve the
      // variable names because if a value contains concatenated
      // values like "1px solid {color.border.base}" we want to
      // transform the original value (color.border.base) before
      // replacing that value in the string.
      await transformMap(tokenMap, platformConfig, opts, transformationContext);

      resolveMap(/** @type {Map<string, TransformedToken>} */ (tokenMap), {
        // for deep reference chains and thousands of tokens, this is the line of code that bottlenecks
        // because we are shallow copying large arrays of strings inside a while loop that runs
        // X times where X is the maximum reference depth within a token dictionary
        ignorePaths: deferredPropValueTransforms,
        usesDtcg,
      });

      const newDeferredPropCount = deferredPropValueTransforms.size;

      // nothing left to transform -> ready
      if (newDeferredPropCount === 0) {
        finished = true;
        // or deferred count doesn't go down, that means there
        // is a circular reference -> ready (but errored)
      } else if (deferredPropCount === newDeferredPropCount) {
        // if we didn't resolve any deferred references then we have a circular reference
        // the resolveObject method will find the circular references
        // we do this in case there are multiple circular references
        resolveMap(/** @type {Map<string, TransformedToken>} */ (tokenMap), {
          usesDtcg,
        });
        finished = true;
      } else {
        // neither of these things, keep going.
        deferredPropCount = newDeferredPropCount;
      }
    }

    const refWarningCount = GroupMessages.count(PROPERTY_REFERENCE_WARNINGS);
    if (refWarningCount > 0) {
      const warnings = GroupMessages.flush(PROPERTY_REFERENCE_WARNINGS).join('\n');
      let err = `\nReference Errors:\nSome token references (${refWarningCount}) could not be found.\n`;

      if (this.log.verbosity === verbose) {
        err += `\n${warnings}\n`;
      } else {
        err += `${verbosityInfo}\n`;
      }

      if (this.log.errors?.brokenReferences === throwBrokenReference) {
        throw new Error(err);
      } else if (this.log.verbosity !== silent) {
        console.error(err);
      }
    }

    const transformErrorCount = GroupMessages.count(TRANSFORM_ERRORS);
    if (transformErrorCount > 0) {
      const warnings = GroupMessages.flush(TRANSFORM_ERRORS).join('\n');
      let err = `\nSome token transformations (${transformErrorCount}) could not be applied correctly.\n`;
      if (this.log.verbosity === verbose) {
        err += `\n${warnings}\n`;
      } else {
        err += `${verbosityInfo}\n`;
      }

      if (this.log.warnings === 'error') {
        throw new Error(err);
      } else if (this.log.warnings !== disabled && this.log.verbosity !== silent) {
        // eslint-disable-next-line no-console
        console.log(chalk.rgb(255, 140, 0).bold(err));
      }
    }

    const unknownPropsWarningCount = GroupMessages.count(UNKNOWN_CSS_FONT_PROPS_WARNINGS);
    if (unknownPropsWarningCount > 0) {
      const warnings = GroupMessages.flush(UNKNOWN_CSS_FONT_PROPS_WARNINGS).join('\n');
      let err = `\nUnknown CSS Font Shorthand properties found for ${unknownPropsWarningCount} tokens, CSS output for Font values will be missing some typography token properties as a result:\n`;

      if (this.log.verbosity === verbose) {
        err += `\n${warnings}\n`;
      } else {
        err += `${verbosityInfo}\n`;
      }

      if (this.log.warnings === error) {
        throw new Error(err);
      } else if (this.log.warnings !== disabled && this.log.verbosity !== silent) {
        // eslint-disable-next-line no-console
        console.log(chalk.rgb(255, 140, 0).bold(err));
      }
    }

    allTokens = convertTokenData(tokenMap, { output: 'array', usesDtcg });
    tokens = /** @type {TransformedTokens} */ (
      convertTokenData(allTokens, { output: 'object', usesDtcg })
    );
    return {
      tokenMap: /** @type {Map<string,TransformedToken>} */ (tokenMap),
      allTokens: /** @type {TransformedToken[]} */ (allTokens),
      tokens,
    };
  }
@param {string} platform
@returns {Promise<Dictionary>}