summaryrefslogtreecommitdiff
path: root/node_modules/sucrase/dist/transformers/RootTransformer.js
diff options
context:
space:
mode:
authorPhilipp Tanlak <philipp.tanlak@gmail.com>2025-11-24 20:54:57 +0100
committerPhilipp Tanlak <philipp.tanlak@gmail.com>2025-11-24 20:57:48 +0100
commitb1e2c8fd5cb5dfa46bc440a12eafaf56cd844b1c (patch)
tree49d360fd6cbc6a2754efe93524ac47ff0fbe0f7d /node_modules/sucrase/dist/transformers/RootTransformer.js
Docs
Diffstat (limited to 'node_modules/sucrase/dist/transformers/RootTransformer.js')
-rw-r--r--node_modules/sucrase/dist/transformers/RootTransformer.js462
1 files changed, 462 insertions, 0 deletions
diff --git a/node_modules/sucrase/dist/transformers/RootTransformer.js b/node_modules/sucrase/dist/transformers/RootTransformer.js
new file mode 100644
index 0000000..70b33c8
--- /dev/null
+++ b/node_modules/sucrase/dist/transformers/RootTransformer.js
@@ -0,0 +1,462 @@
+"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 _getClassInfo = require('../util/getClassInfo'); var _getClassInfo2 = _interopRequireDefault(_getClassInfo);
+var _CJSImportTransformer = require('./CJSImportTransformer'); var _CJSImportTransformer2 = _interopRequireDefault(_CJSImportTransformer);
+var _ESMImportTransformer = require('./ESMImportTransformer'); var _ESMImportTransformer2 = _interopRequireDefault(_ESMImportTransformer);
+var _FlowTransformer = require('./FlowTransformer'); var _FlowTransformer2 = _interopRequireDefault(_FlowTransformer);
+var _JestHoistTransformer = require('./JestHoistTransformer'); var _JestHoistTransformer2 = _interopRequireDefault(_JestHoistTransformer);
+var _JSXTransformer = require('./JSXTransformer'); var _JSXTransformer2 = _interopRequireDefault(_JSXTransformer);
+var _NumericSeparatorTransformer = require('./NumericSeparatorTransformer'); var _NumericSeparatorTransformer2 = _interopRequireDefault(_NumericSeparatorTransformer);
+var _OptionalCatchBindingTransformer = require('./OptionalCatchBindingTransformer'); var _OptionalCatchBindingTransformer2 = _interopRequireDefault(_OptionalCatchBindingTransformer);
+var _OptionalChainingNullishTransformer = require('./OptionalChainingNullishTransformer'); var _OptionalChainingNullishTransformer2 = _interopRequireDefault(_OptionalChainingNullishTransformer);
+var _ReactDisplayNameTransformer = require('./ReactDisplayNameTransformer'); var _ReactDisplayNameTransformer2 = _interopRequireDefault(_ReactDisplayNameTransformer);
+var _ReactHotLoaderTransformer = require('./ReactHotLoaderTransformer'); var _ReactHotLoaderTransformer2 = _interopRequireDefault(_ReactHotLoaderTransformer);
+
+var _TypeScriptTransformer = require('./TypeScriptTransformer'); var _TypeScriptTransformer2 = _interopRequireDefault(_TypeScriptTransformer);
+
+
+
+
+
+
+
+
+ class RootTransformer {
+ __init() {this.transformers = []}
+
+
+ __init2() {this.generatedVariables = []}
+
+
+
+
+
+ constructor(
+ sucraseContext,
+ transforms,
+ enableLegacyBabel5ModuleInterop,
+ options,
+ ) {;RootTransformer.prototype.__init.call(this);RootTransformer.prototype.__init2.call(this);
+ this.nameManager = sucraseContext.nameManager;
+ this.helperManager = sucraseContext.helperManager;
+ const {tokenProcessor, importProcessor} = sucraseContext;
+ this.tokens = tokenProcessor;
+ this.isImportsTransformEnabled = transforms.includes("imports");
+ this.isReactHotLoaderTransformEnabled = transforms.includes("react-hot-loader");
+ this.disableESTransforms = Boolean(options.disableESTransforms);
+
+ if (!options.disableESTransforms) {
+ this.transformers.push(
+ new (0, _OptionalChainingNullishTransformer2.default)(tokenProcessor, this.nameManager),
+ );
+ this.transformers.push(new (0, _NumericSeparatorTransformer2.default)(tokenProcessor));
+ this.transformers.push(new (0, _OptionalCatchBindingTransformer2.default)(tokenProcessor, this.nameManager));
+ }
+
+ if (transforms.includes("jsx")) {
+ if (options.jsxRuntime !== "preserve") {
+ this.transformers.push(
+ new (0, _JSXTransformer2.default)(this, tokenProcessor, importProcessor, this.nameManager, options),
+ );
+ }
+ this.transformers.push(
+ new (0, _ReactDisplayNameTransformer2.default)(this, tokenProcessor, importProcessor, options),
+ );
+ }
+
+ let reactHotLoaderTransformer = null;
+ if (transforms.includes("react-hot-loader")) {
+ if (!options.filePath) {
+ throw new Error("filePath is required when using the react-hot-loader transform.");
+ }
+ reactHotLoaderTransformer = new (0, _ReactHotLoaderTransformer2.default)(tokenProcessor, options.filePath);
+ this.transformers.push(reactHotLoaderTransformer);
+ }
+
+ // Note that we always want to enable the imports transformer, even when the import transform
+ // itself isn't enabled, since we need to do type-only import pruning for both Flow and
+ // TypeScript.
+ if (transforms.includes("imports")) {
+ if (importProcessor === null) {
+ throw new Error("Expected non-null importProcessor with imports transform enabled.");
+ }
+ this.transformers.push(
+ new (0, _CJSImportTransformer2.default)(
+ this,
+ tokenProcessor,
+ importProcessor,
+ this.nameManager,
+ this.helperManager,
+ reactHotLoaderTransformer,
+ enableLegacyBabel5ModuleInterop,
+ Boolean(options.enableLegacyTypeScriptModuleInterop),
+ transforms.includes("typescript"),
+ transforms.includes("flow"),
+ Boolean(options.preserveDynamicImport),
+ Boolean(options.keepUnusedImports),
+ ),
+ );
+ } else {
+ this.transformers.push(
+ new (0, _ESMImportTransformer2.default)(
+ tokenProcessor,
+ this.nameManager,
+ this.helperManager,
+ reactHotLoaderTransformer,
+ transforms.includes("typescript"),
+ transforms.includes("flow"),
+ Boolean(options.keepUnusedImports),
+ options,
+ ),
+ );
+ }
+
+ if (transforms.includes("flow")) {
+ this.transformers.push(
+ new (0, _FlowTransformer2.default)(this, tokenProcessor, transforms.includes("imports")),
+ );
+ }
+ if (transforms.includes("typescript")) {
+ this.transformers.push(
+ new (0, _TypeScriptTransformer2.default)(this, tokenProcessor, transforms.includes("imports")),
+ );
+ }
+ if (transforms.includes("jest")) {
+ this.transformers.push(
+ new (0, _JestHoistTransformer2.default)(this, tokenProcessor, this.nameManager, importProcessor),
+ );
+ }
+ }
+
+ transform() {
+ this.tokens.reset();
+ this.processBalancedCode();
+ const shouldAddUseStrict = this.isImportsTransformEnabled;
+ // "use strict" always needs to be first, so override the normal transformer order.
+ let prefix = shouldAddUseStrict ? '"use strict";' : "";
+ for (const transformer of this.transformers) {
+ prefix += transformer.getPrefixCode();
+ }
+ prefix += this.helperManager.emitHelpers();
+ prefix += this.generatedVariables.map((v) => ` var ${v};`).join("");
+ for (const transformer of this.transformers) {
+ prefix += transformer.getHoistedCode();
+ }
+ let suffix = "";
+ for (const transformer of this.transformers) {
+ suffix += transformer.getSuffixCode();
+ }
+ const result = this.tokens.finish();
+ let {code} = result;
+ if (code.startsWith("#!")) {
+ let newlineIndex = code.indexOf("\n");
+ if (newlineIndex === -1) {
+ newlineIndex = code.length;
+ code += "\n";
+ }
+ return {
+ code: code.slice(0, newlineIndex + 1) + prefix + code.slice(newlineIndex + 1) + suffix,
+ // The hashbang line has no tokens, so shifting the tokens to account
+ // for prefix can happen normally.
+ mappings: this.shiftMappings(result.mappings, prefix.length),
+ };
+ } else {
+ return {
+ code: prefix + code + suffix,
+ mappings: this.shiftMappings(result.mappings, prefix.length),
+ };
+ }
+ }
+
+ processBalancedCode() {
+ let braceDepth = 0;
+ let parenDepth = 0;
+ while (!this.tokens.isAtEnd()) {
+ if (this.tokens.matches1(_types.TokenType.braceL) || this.tokens.matches1(_types.TokenType.dollarBraceL)) {
+ braceDepth++;
+ } else if (this.tokens.matches1(_types.TokenType.braceR)) {
+ if (braceDepth === 0) {
+ return;
+ }
+ braceDepth--;
+ }
+ if (this.tokens.matches1(_types.TokenType.parenL)) {
+ parenDepth++;
+ } else if (this.tokens.matches1(_types.TokenType.parenR)) {
+ if (parenDepth === 0) {
+ return;
+ }
+ parenDepth--;
+ }
+ this.processToken();
+ }
+ }
+
+ processToken() {
+ if (this.tokens.matches1(_types.TokenType._class)) {
+ this.processClass();
+ return;
+ }
+ for (const transformer of this.transformers) {
+ const wasProcessed = transformer.process();
+ if (wasProcessed) {
+ return;
+ }
+ }
+ this.tokens.copyToken();
+ }
+
+ /**
+ * Skip past a class with a name and return that name.
+ */
+ processNamedClass() {
+ if (!this.tokens.matches2(_types.TokenType._class, _types.TokenType.name)) {
+ throw new Error("Expected identifier for exported class name.");
+ }
+ const name = this.tokens.identifierNameAtIndex(this.tokens.currentIndex() + 1);
+ this.processClass();
+ return name;
+ }
+
+ processClass() {
+ const classInfo = _getClassInfo2.default.call(void 0, this, this.tokens, this.nameManager, this.disableESTransforms);
+
+ // Both static and instance initializers need a class name to use to invoke the initializer, so
+ // assign to one if necessary.
+ const needsCommaExpression =
+ (classInfo.headerInfo.isExpression || !classInfo.headerInfo.className) &&
+ classInfo.staticInitializerNames.length + classInfo.instanceInitializerNames.length > 0;
+
+ let className = classInfo.headerInfo.className;
+ if (needsCommaExpression) {
+ className = this.nameManager.claimFreeName("_class");
+ this.generatedVariables.push(className);
+ this.tokens.appendCode(` (${className} =`);
+ }
+
+ const classToken = this.tokens.currentToken();
+ const contextId = classToken.contextId;
+ if (contextId == null) {
+ throw new Error("Expected class to have a context ID.");
+ }
+ this.tokens.copyExpectedToken(_types.TokenType._class);
+ while (!this.tokens.matchesContextIdAndLabel(_types.TokenType.braceL, contextId)) {
+ this.processToken();
+ }
+
+ this.processClassBody(classInfo, className);
+
+ const staticInitializerStatements = classInfo.staticInitializerNames.map(
+ (name) => `${className}.${name}()`,
+ );
+ if (needsCommaExpression) {
+ this.tokens.appendCode(
+ `, ${staticInitializerStatements.map((s) => `${s}, `).join("")}${className})`,
+ );
+ } else if (classInfo.staticInitializerNames.length > 0) {
+ this.tokens.appendCode(` ${staticInitializerStatements.map((s) => `${s};`).join(" ")}`);
+ }
+ }
+
+ /**
+ * We want to just handle class fields in all contexts, since TypeScript supports them. Later,
+ * when some JS implementations support class fields, this should be made optional.
+ */
+ processClassBody(classInfo, className) {
+ const {
+ headerInfo,
+ constructorInsertPos,
+ constructorInitializerStatements,
+ fields,
+ instanceInitializerNames,
+ rangesToRemove,
+ } = classInfo;
+ let fieldIndex = 0;
+ let rangeToRemoveIndex = 0;
+ const classContextId = this.tokens.currentToken().contextId;
+ if (classContextId == null) {
+ throw new Error("Expected non-null context ID on class.");
+ }
+ this.tokens.copyExpectedToken(_types.TokenType.braceL);
+ if (this.isReactHotLoaderTransformEnabled) {
+ this.tokens.appendCode(
+ "__reactstandin__regenerateByEval(key, code) {this[key] = eval(code);}",
+ );
+ }
+
+ const needsConstructorInit =
+ constructorInitializerStatements.length + instanceInitializerNames.length > 0;
+
+ if (constructorInsertPos === null && needsConstructorInit) {
+ const constructorInitializersCode = this.makeConstructorInitCode(
+ constructorInitializerStatements,
+ instanceInitializerNames,
+ className,
+ );
+ if (headerInfo.hasSuperclass) {
+ const argsName = this.nameManager.claimFreeName("args");
+ this.tokens.appendCode(
+ `constructor(...${argsName}) { super(...${argsName}); ${constructorInitializersCode}; }`,
+ );
+ } else {
+ this.tokens.appendCode(`constructor() { ${constructorInitializersCode}; }`);
+ }
+ }
+
+ while (!this.tokens.matchesContextIdAndLabel(_types.TokenType.braceR, classContextId)) {
+ if (fieldIndex < fields.length && this.tokens.currentIndex() === fields[fieldIndex].start) {
+ let needsCloseBrace = false;
+ if (this.tokens.matches1(_types.TokenType.bracketL)) {
+ this.tokens.copyTokenWithPrefix(`${fields[fieldIndex].initializerName}() {this`);
+ } else if (this.tokens.matches1(_types.TokenType.string) || this.tokens.matches1(_types.TokenType.num)) {
+ this.tokens.copyTokenWithPrefix(`${fields[fieldIndex].initializerName}() {this[`);
+ needsCloseBrace = true;
+ } else {
+ this.tokens.copyTokenWithPrefix(`${fields[fieldIndex].initializerName}() {this.`);
+ }
+ while (this.tokens.currentIndex() < fields[fieldIndex].end) {
+ if (needsCloseBrace && this.tokens.currentIndex() === fields[fieldIndex].equalsIndex) {
+ this.tokens.appendCode("]");
+ }
+ this.processToken();
+ }
+ this.tokens.appendCode("}");
+ fieldIndex++;
+ } else if (
+ rangeToRemoveIndex < rangesToRemove.length &&
+ this.tokens.currentIndex() >= rangesToRemove[rangeToRemoveIndex].start
+ ) {
+ if (this.tokens.currentIndex() < rangesToRemove[rangeToRemoveIndex].end) {
+ this.tokens.removeInitialToken();
+ }
+ while (this.tokens.currentIndex() < rangesToRemove[rangeToRemoveIndex].end) {
+ this.tokens.removeToken();
+ }
+ rangeToRemoveIndex++;
+ } else if (this.tokens.currentIndex() === constructorInsertPos) {
+ this.tokens.copyToken();
+ if (needsConstructorInit) {
+ this.tokens.appendCode(
+ `;${this.makeConstructorInitCode(
+ constructorInitializerStatements,
+ instanceInitializerNames,
+ className,
+ )};`,
+ );
+ }
+ this.processToken();
+ } else {
+ this.processToken();
+ }
+ }
+ this.tokens.copyExpectedToken(_types.TokenType.braceR);
+ }
+
+ makeConstructorInitCode(
+ constructorInitializerStatements,
+ instanceInitializerNames,
+ className,
+ ) {
+ return [
+ ...constructorInitializerStatements,
+ ...instanceInitializerNames.map((name) => `${className}.prototype.${name}.call(this)`),
+ ].join(";");
+ }
+
+ /**
+ * Normally it's ok to simply remove type tokens, but we need to be more careful when dealing with
+ * arrow function return types since they can confuse the parser. In that case, we want to move
+ * the close-paren to the same line as the arrow.
+ *
+ * See https://github.com/alangpierce/sucrase/issues/391 for more details.
+ */
+ processPossibleArrowParamEnd() {
+ if (this.tokens.matches2(_types.TokenType.parenR, _types.TokenType.colon) && this.tokens.tokenAtRelativeIndex(1).isType) {
+ let nextNonTypeIndex = this.tokens.currentIndex() + 1;
+ // Look ahead to see if this is an arrow function or something else.
+ while (this.tokens.tokens[nextNonTypeIndex].isType) {
+ nextNonTypeIndex++;
+ }
+ if (this.tokens.matches1AtIndex(nextNonTypeIndex, _types.TokenType.arrow)) {
+ this.tokens.removeInitialToken();
+ while (this.tokens.currentIndex() < nextNonTypeIndex) {
+ this.tokens.removeToken();
+ }
+ this.tokens.replaceTokenTrimmingLeftWhitespace(") =>");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * An async arrow function might be of the form:
+ *
+ * async <
+ * T
+ * >() => {}
+ *
+ * in which case, removing the type parameters will cause a syntax error. Detect this case and
+ * move the open-paren earlier.
+ */
+ processPossibleAsyncArrowWithTypeParams() {
+ if (
+ !this.tokens.matchesContextual(_keywords.ContextualKeyword._async) &&
+ !this.tokens.matches1(_types.TokenType._async)
+ ) {
+ return false;
+ }
+ const nextToken = this.tokens.tokenAtRelativeIndex(1);
+ if (nextToken.type !== _types.TokenType.lessThan || !nextToken.isType) {
+ return false;
+ }
+
+ let nextNonTypeIndex = this.tokens.currentIndex() + 1;
+ // Look ahead to see if this is an arrow function or something else.
+ while (this.tokens.tokens[nextNonTypeIndex].isType) {
+ nextNonTypeIndex++;
+ }
+ if (this.tokens.matches1AtIndex(nextNonTypeIndex, _types.TokenType.parenL)) {
+ this.tokens.replaceToken("async (");
+ this.tokens.removeInitialToken();
+ while (this.tokens.currentIndex() < nextNonTypeIndex) {
+ this.tokens.removeToken();
+ }
+ this.tokens.removeToken();
+ // We ate a ( token, so we need to process the tokens in between and then the ) token so that
+ // we remain balanced.
+ this.processBalancedCode();
+ this.processToken();
+ return true;
+ }
+ return false;
+ }
+
+ processPossibleTypeRange() {
+ if (this.tokens.currentToken().isType) {
+ this.tokens.removeInitialToken();
+ while (this.tokens.currentToken().isType) {
+ this.tokens.removeToken();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ shiftMappings(
+ mappings,
+ prefixLength,
+ ) {
+ for (let i = 0; i < mappings.length; i++) {
+ const mapping = mappings[i];
+ if (mapping !== undefined) {
+ mappings[i] = mapping + prefixLength;
+ }
+ }
+ return mappings;
+ }
+} exports.default = RootTransformer;