| 1 | "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var _keywords = require('../parser/tokenizer/keywords');
|
|---|
| 2 | var _types = require('../parser/tokenizer/types');
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 | var _Transformer = require('./Transformer'); var _Transformer2 = _interopRequireDefault(_Transformer);
|
|---|
| 6 |
|
|---|
| 7 | class FlowTransformer extends _Transformer2.default {
|
|---|
| 8 | constructor(
|
|---|
| 9 | rootTransformer,
|
|---|
| 10 | tokens,
|
|---|
| 11 | isImportsTransformEnabled,
|
|---|
| 12 | ) {
|
|---|
| 13 | super();this.rootTransformer = rootTransformer;this.tokens = tokens;this.isImportsTransformEnabled = isImportsTransformEnabled;;
|
|---|
| 14 | }
|
|---|
| 15 |
|
|---|
| 16 | process() {
|
|---|
| 17 | if (
|
|---|
| 18 | this.rootTransformer.processPossibleArrowParamEnd() ||
|
|---|
| 19 | this.rootTransformer.processPossibleAsyncArrowWithTypeParams() ||
|
|---|
| 20 | this.rootTransformer.processPossibleTypeRange()
|
|---|
| 21 | ) {
|
|---|
| 22 | return true;
|
|---|
| 23 | }
|
|---|
| 24 | if (this.tokens.matches1(_types.TokenType._enum)) {
|
|---|
| 25 | this.processEnum();
|
|---|
| 26 | return true;
|
|---|
| 27 | }
|
|---|
| 28 | if (this.tokens.matches2(_types.TokenType._export, _types.TokenType._enum)) {
|
|---|
| 29 | this.processNamedExportEnum();
|
|---|
| 30 | return true;
|
|---|
| 31 | }
|
|---|
| 32 | if (this.tokens.matches3(_types.TokenType._export, _types.TokenType._default, _types.TokenType._enum)) {
|
|---|
| 33 | this.processDefaultExportEnum();
|
|---|
| 34 | return true;
|
|---|
| 35 | }
|
|---|
| 36 | return false;
|
|---|
| 37 | }
|
|---|
| 38 |
|
|---|
| 39 | /**
|
|---|
| 40 | * Handle a declaration like:
|
|---|
| 41 | * export enum E ...
|
|---|
| 42 | *
|
|---|
| 43 | * With this imports transform, this becomes:
|
|---|
| 44 | * const E = [[enum]]; exports.E = E;
|
|---|
| 45 | *
|
|---|
| 46 | * otherwise, it becomes:
|
|---|
| 47 | * export const E = [[enum]];
|
|---|
| 48 | */
|
|---|
| 49 | processNamedExportEnum() {
|
|---|
| 50 | if (this.isImportsTransformEnabled) {
|
|---|
| 51 | // export
|
|---|
| 52 | this.tokens.removeInitialToken();
|
|---|
| 53 | const enumName = this.tokens.identifierNameAtRelativeIndex(1);
|
|---|
| 54 | this.processEnum();
|
|---|
| 55 | this.tokens.appendCode(` exports.${enumName} = ${enumName};`);
|
|---|
| 56 | } else {
|
|---|
| 57 | this.tokens.copyToken();
|
|---|
| 58 | this.processEnum();
|
|---|
| 59 | }
|
|---|
| 60 | }
|
|---|
| 61 |
|
|---|
| 62 | /**
|
|---|
| 63 | * Handle a declaration like:
|
|---|
| 64 | * export default enum E
|
|---|
| 65 | *
|
|---|
| 66 | * With the imports transform, this becomes:
|
|---|
| 67 | * const E = [[enum]]; exports.default = E;
|
|---|
| 68 | *
|
|---|
| 69 | * otherwise, it becomes:
|
|---|
| 70 | * const E = [[enum]]; export default E;
|
|---|
| 71 | */
|
|---|
| 72 | processDefaultExportEnum() {
|
|---|
| 73 | // export
|
|---|
| 74 | this.tokens.removeInitialToken();
|
|---|
| 75 | // default
|
|---|
| 76 | this.tokens.removeToken();
|
|---|
| 77 | const enumName = this.tokens.identifierNameAtRelativeIndex(1);
|
|---|
| 78 | this.processEnum();
|
|---|
| 79 | if (this.isImportsTransformEnabled) {
|
|---|
| 80 | this.tokens.appendCode(` exports.default = ${enumName};`);
|
|---|
| 81 | } else {
|
|---|
| 82 | this.tokens.appendCode(` export default ${enumName};`);
|
|---|
| 83 | }
|
|---|
| 84 | }
|
|---|
| 85 |
|
|---|
| 86 | /**
|
|---|
| 87 | * Transpile flow enums to invoke the "flow-enums-runtime" library.
|
|---|
| 88 | *
|
|---|
| 89 | * Currently, the transpiled code always uses `require("flow-enums-runtime")`,
|
|---|
| 90 | * but if future flexibility is needed, we could expose a config option for
|
|---|
| 91 | * this string (similar to configurable JSX). Even when targeting ESM, the
|
|---|
| 92 | * default behavior of babel-plugin-transform-flow-enums is to use require
|
|---|
| 93 | * rather than injecting an import.
|
|---|
| 94 | *
|
|---|
| 95 | * Flow enums are quite a bit simpler than TS enums and have some convenient
|
|---|
| 96 | * constraints:
|
|---|
| 97 | * - Element initializers must be either always present or always absent. That
|
|---|
| 98 | * means that we can use fixed lookahead on the first element (if any) and
|
|---|
| 99 | * assume that all elements are like that.
|
|---|
| 100 | * - The right-hand side of an element initializer must be a literal value,
|
|---|
| 101 | * not a complex expression and not referencing other elements. That means
|
|---|
| 102 | * we can simply copy a single token.
|
|---|
| 103 | *
|
|---|
| 104 | * Enums can be broken up into three basic cases:
|
|---|
| 105 | *
|
|---|
| 106 | * Mirrored enums:
|
|---|
| 107 | * enum E {A, B}
|
|---|
| 108 | * ->
|
|---|
| 109 | * const E = require("flow-enums-runtime").Mirrored(["A", "B"]);
|
|---|
| 110 | *
|
|---|
| 111 | * Initializer enums:
|
|---|
| 112 | * enum E {A = 1, B = 2}
|
|---|
| 113 | * ->
|
|---|
| 114 | * const E = require("flow-enums-runtime")({A: 1, B: 2});
|
|---|
| 115 | *
|
|---|
| 116 | * Symbol enums:
|
|---|
| 117 | * enum E of symbol {A, B}
|
|---|
| 118 | * ->
|
|---|
| 119 | * const E = require("flow-enums-runtime")({A: Symbol("A"), B: Symbol("B")});
|
|---|
| 120 | *
|
|---|
| 121 | * We can statically detect which of the three cases this is by looking at the
|
|---|
| 122 | * "of" declaration (if any) and seeing if the first element has an initializer.
|
|---|
| 123 | * Since the other transform details are so similar between the three cases, we
|
|---|
| 124 | * use a single implementation and vary the transform within processEnumElement
|
|---|
| 125 | * based on case.
|
|---|
| 126 | */
|
|---|
| 127 | processEnum() {
|
|---|
| 128 | // enum E -> const E
|
|---|
| 129 | this.tokens.replaceToken("const");
|
|---|
| 130 | this.tokens.copyExpectedToken(_types.TokenType.name);
|
|---|
| 131 |
|
|---|
| 132 | let isSymbolEnum = false;
|
|---|
| 133 | if (this.tokens.matchesContextual(_keywords.ContextualKeyword._of)) {
|
|---|
| 134 | this.tokens.removeToken();
|
|---|
| 135 | isSymbolEnum = this.tokens.matchesContextual(_keywords.ContextualKeyword._symbol);
|
|---|
| 136 | this.tokens.removeToken();
|
|---|
| 137 | }
|
|---|
| 138 | const hasInitializers = this.tokens.matches3(_types.TokenType.braceL, _types.TokenType.name, _types.TokenType.eq);
|
|---|
| 139 | this.tokens.appendCode(' = require("flow-enums-runtime")');
|
|---|
| 140 |
|
|---|
| 141 | const isMirrored = !isSymbolEnum && !hasInitializers;
|
|---|
| 142 | this.tokens.replaceTokenTrimmingLeftWhitespace(isMirrored ? ".Mirrored([" : "({");
|
|---|
| 143 |
|
|---|
| 144 | while (!this.tokens.matches1(_types.TokenType.braceR)) {
|
|---|
| 145 | // ... is allowed at the end and has no runtime behavior.
|
|---|
| 146 | if (this.tokens.matches1(_types.TokenType.ellipsis)) {
|
|---|
| 147 | this.tokens.removeToken();
|
|---|
| 148 | break;
|
|---|
| 149 | }
|
|---|
| 150 | this.processEnumElement(isSymbolEnum, hasInitializers);
|
|---|
| 151 | if (this.tokens.matches1(_types.TokenType.comma)) {
|
|---|
| 152 | this.tokens.copyToken();
|
|---|
| 153 | }
|
|---|
| 154 | }
|
|---|
| 155 |
|
|---|
| 156 | this.tokens.replaceToken(isMirrored ? "]);" : "});");
|
|---|
| 157 | }
|
|---|
| 158 |
|
|---|
| 159 | /**
|
|---|
| 160 | * Process an individual enum element, producing either an array element or an
|
|---|
| 161 | * object element based on what type of enum this is.
|
|---|
| 162 | */
|
|---|
| 163 | processEnumElement(isSymbolEnum, hasInitializers) {
|
|---|
| 164 | if (isSymbolEnum) {
|
|---|
| 165 | // Symbol enums never have initializers and are expanded to object elements.
|
|---|
| 166 | // A, -> A: Symbol("A"),
|
|---|
| 167 | const elementName = this.tokens.identifierName();
|
|---|
| 168 | this.tokens.copyToken();
|
|---|
| 169 | this.tokens.appendCode(`: Symbol("${elementName}")`);
|
|---|
| 170 | } else if (hasInitializers) {
|
|---|
| 171 | // Initializers are expanded to object elements.
|
|---|
| 172 | // A = 1, -> A: 1,
|
|---|
| 173 | this.tokens.copyToken();
|
|---|
| 174 | this.tokens.replaceTokenTrimmingLeftWhitespace(":");
|
|---|
| 175 | this.tokens.copyToken();
|
|---|
| 176 | } else {
|
|---|
| 177 | // Enum elements without initializers become string literal array elements.
|
|---|
| 178 | // A, -> "A",
|
|---|
| 179 | this.tokens.replaceToken(`"${this.tokens.identifierName()}"`);
|
|---|
| 180 | }
|
|---|
| 181 | }
|
|---|
| 182 | } exports.default = FlowTransformer;
|
|---|