diff options
| author | Philipp Tanlak <philipp.tanlak@gmail.com> | 2025-11-24 20:54:57 +0100 |
|---|---|---|
| committer | Philipp Tanlak <philipp.tanlak@gmail.com> | 2025-11-24 20:57:48 +0100 |
| commit | b1e2c8fd5cb5dfa46bc440a12eafaf56cd844b1c (patch) | |
| tree | 49d360fd6cbc6a2754efe93524ac47ff0fbe0f7d /node_modules/sucrase/dist/transformers/FlowTransformer.js | |
Docs
Diffstat (limited to 'node_modules/sucrase/dist/transformers/FlowTransformer.js')
| -rw-r--r-- | node_modules/sucrase/dist/transformers/FlowTransformer.js | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/node_modules/sucrase/dist/transformers/FlowTransformer.js b/node_modules/sucrase/dist/transformers/FlowTransformer.js new file mode 100644 index 0000000..31c9744 --- /dev/null +++ b/node_modules/sucrase/dist/transformers/FlowTransformer.js @@ -0,0 +1,182 @@ +"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; |