183 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			183 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var _keywords = require('../parser/tokenizer/keywords');
 | 
						|
var _types = require('../parser/tokenizer/types');
 | 
						|
 | 
						|
 | 
						|
var _Transformer = require('./Transformer'); var _Transformer2 = _interopRequireDefault(_Transformer);
 | 
						|
 | 
						|
 class FlowTransformer extends _Transformer2.default {
 | 
						|
  constructor(
 | 
						|
     rootTransformer,
 | 
						|
     tokens,
 | 
						|
     isImportsTransformEnabled,
 | 
						|
  ) {
 | 
						|
    super();this.rootTransformer = rootTransformer;this.tokens = tokens;this.isImportsTransformEnabled = isImportsTransformEnabled;;
 | 
						|
  }
 | 
						|
 | 
						|
  process() {
 | 
						|
    if (
 | 
						|
      this.rootTransformer.processPossibleArrowParamEnd() ||
 | 
						|
      this.rootTransformer.processPossibleAsyncArrowWithTypeParams() ||
 | 
						|
      this.rootTransformer.processPossibleTypeRange()
 | 
						|
    ) {
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    if (this.tokens.matches1(_types.TokenType._enum)) {
 | 
						|
      this.processEnum();
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    if (this.tokens.matches2(_types.TokenType._export, _types.TokenType._enum)) {
 | 
						|
      this.processNamedExportEnum();
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    if (this.tokens.matches3(_types.TokenType._export, _types.TokenType._default, _types.TokenType._enum)) {
 | 
						|
      this.processDefaultExportEnum();
 | 
						|
      return true;
 | 
						|
    }
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Handle a declaration like:
 | 
						|
   * export enum E ...
 | 
						|
   *
 | 
						|
   * With this imports transform, this becomes:
 | 
						|
   * const E = [[enum]]; exports.E = E;
 | 
						|
   *
 | 
						|
   * otherwise, it becomes:
 | 
						|
   * export const E = [[enum]];
 | 
						|
   */
 | 
						|
  processNamedExportEnum() {
 | 
						|
    if (this.isImportsTransformEnabled) {
 | 
						|
      // export
 | 
						|
      this.tokens.removeInitialToken();
 | 
						|
      const enumName = this.tokens.identifierNameAtRelativeIndex(1);
 | 
						|
      this.processEnum();
 | 
						|
      this.tokens.appendCode(` exports.${enumName} = ${enumName};`);
 | 
						|
    } else {
 | 
						|
      this.tokens.copyToken();
 | 
						|
      this.processEnum();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Handle a declaration like:
 | 
						|
   * export default enum E
 | 
						|
   *
 | 
						|
   * With the imports transform, this becomes:
 | 
						|
   * const E = [[enum]]; exports.default = E;
 | 
						|
   *
 | 
						|
   * otherwise, it becomes:
 | 
						|
   * const E = [[enum]]; export default E;
 | 
						|
   */
 | 
						|
  processDefaultExportEnum() {
 | 
						|
    // export
 | 
						|
    this.tokens.removeInitialToken();
 | 
						|
    // default
 | 
						|
    this.tokens.removeToken();
 | 
						|
    const enumName = this.tokens.identifierNameAtRelativeIndex(1);
 | 
						|
    this.processEnum();
 | 
						|
    if (this.isImportsTransformEnabled) {
 | 
						|
      this.tokens.appendCode(` exports.default = ${enumName};`);
 | 
						|
    } else {
 | 
						|
      this.tokens.appendCode(` export default ${enumName};`);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Transpile flow enums to invoke the "flow-enums-runtime" library.
 | 
						|
   *
 | 
						|
   * Currently, the transpiled code always uses `require("flow-enums-runtime")`,
 | 
						|
   * but if future flexibility is needed, we could expose a config option for
 | 
						|
   * this string (similar to configurable JSX). Even when targeting ESM, the
 | 
						|
   * default behavior of babel-plugin-transform-flow-enums is to use require
 | 
						|
   * rather than injecting an import.
 | 
						|
   *
 | 
						|
   * Flow enums are quite a bit simpler than TS enums and have some convenient
 | 
						|
   * constraints:
 | 
						|
   * - Element initializers must be either always present or always absent. That
 | 
						|
   *   means that we can use fixed lookahead on the first element (if any) and
 | 
						|
   *   assume that all elements are like that.
 | 
						|
   * - The right-hand side of an element initializer must be a literal value,
 | 
						|
   *   not a complex expression and not referencing other elements. That means
 | 
						|
   *   we can simply copy a single token.
 | 
						|
   *
 | 
						|
   * Enums can be broken up into three basic cases:
 | 
						|
   *
 | 
						|
   * Mirrored enums:
 | 
						|
   * enum E {A, B}
 | 
						|
   *   ->
 | 
						|
   * const E = require("flow-enums-runtime").Mirrored(["A", "B"]);
 | 
						|
   *
 | 
						|
   * Initializer enums:
 | 
						|
   * enum E {A = 1, B = 2}
 | 
						|
   *   ->
 | 
						|
   * const E = require("flow-enums-runtime")({A: 1, B: 2});
 | 
						|
   *
 | 
						|
   * Symbol enums:
 | 
						|
   * enum E of symbol {A, B}
 | 
						|
   *   ->
 | 
						|
   * const E = require("flow-enums-runtime")({A: Symbol("A"), B: Symbol("B")});
 | 
						|
   *
 | 
						|
   * We can statically detect which of the three cases this is by looking at the
 | 
						|
   * "of" declaration (if any) and seeing if the first element has an initializer.
 | 
						|
   * Since the other transform details are so similar between the three cases, we
 | 
						|
   * use a single implementation and vary the transform within processEnumElement
 | 
						|
   * based on case.
 | 
						|
   */
 | 
						|
  processEnum() {
 | 
						|
    // enum E -> const E
 | 
						|
    this.tokens.replaceToken("const");
 | 
						|
    this.tokens.copyExpectedToken(_types.TokenType.name);
 | 
						|
 | 
						|
    let isSymbolEnum = false;
 | 
						|
    if (this.tokens.matchesContextual(_keywords.ContextualKeyword._of)) {
 | 
						|
      this.tokens.removeToken();
 | 
						|
      isSymbolEnum = this.tokens.matchesContextual(_keywords.ContextualKeyword._symbol);
 | 
						|
      this.tokens.removeToken();
 | 
						|
    }
 | 
						|
    const hasInitializers = this.tokens.matches3(_types.TokenType.braceL, _types.TokenType.name, _types.TokenType.eq);
 | 
						|
    this.tokens.appendCode(' = require("flow-enums-runtime")');
 | 
						|
 | 
						|
    const isMirrored = !isSymbolEnum && !hasInitializers;
 | 
						|
    this.tokens.replaceTokenTrimmingLeftWhitespace(isMirrored ? ".Mirrored([" : "({");
 | 
						|
 | 
						|
    while (!this.tokens.matches1(_types.TokenType.braceR)) {
 | 
						|
      // ... is allowed at the end and has no runtime behavior.
 | 
						|
      if (this.tokens.matches1(_types.TokenType.ellipsis)) {
 | 
						|
        this.tokens.removeToken();
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      this.processEnumElement(isSymbolEnum, hasInitializers);
 | 
						|
      if (this.tokens.matches1(_types.TokenType.comma)) {
 | 
						|
        this.tokens.copyToken();
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    this.tokens.replaceToken(isMirrored ? "]);" : "});");
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Process an individual enum element, producing either an array element or an
 | 
						|
   * object element based on what type of enum this is.
 | 
						|
   */
 | 
						|
  processEnumElement(isSymbolEnum, hasInitializers) {
 | 
						|
    if (isSymbolEnum) {
 | 
						|
      // Symbol enums never have initializers and are expanded to object elements.
 | 
						|
      // A, -> A: Symbol("A"),
 | 
						|
      const elementName = this.tokens.identifierName();
 | 
						|
      this.tokens.copyToken();
 | 
						|
      this.tokens.appendCode(`: Symbol("${elementName}")`);
 | 
						|
    } else if (hasInitializers) {
 | 
						|
      // Initializers are expanded to object elements.
 | 
						|
      // A = 1, -> A: 1,
 | 
						|
      this.tokens.copyToken();
 | 
						|
      this.tokens.replaceTokenTrimmingLeftWhitespace(":");
 | 
						|
      this.tokens.copyToken();
 | 
						|
    } else {
 | 
						|
      // Enum elements without initializers become string literal array elements.
 | 
						|
      // A, -> "A",
 | 
						|
      this.tokens.replaceToken(`"${this.tokens.identifierName()}"`);
 | 
						|
    }
 | 
						|
  }
 | 
						|
} exports.default = FlowTransformer;
 |