sucrase
Version:
Super-fast alternative to Babel for when you can target modern JS runtimes
157 lines (146 loc) • 5.91 kB
JavaScript
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }var _types = require('../parser/tokenizer/types');
var _isIdentifier = require('../util/isIdentifier'); var _isIdentifier2 = _interopRequireDefault(_isIdentifier);
var _Transformer = require('./Transformer'); var _Transformer2 = _interopRequireDefault(_Transformer);
class TypeScriptTransformer 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._public) ||
this.tokens.matches1(_types.TokenType._protected) ||
this.tokens.matches1(_types.TokenType._private) ||
this.tokens.matches1(_types.TokenType._abstract) ||
this.tokens.matches1(_types.TokenType._readonly) ||
this.tokens.matches1(_types.TokenType.nonNullAssertion)
) {
this.tokens.removeInitialToken();
return true;
}
if (this.tokens.matches1(_types.TokenType._enum) || this.tokens.matches2(_types.TokenType._const, _types.TokenType._enum)) {
this.processEnum();
return true;
}
if (
this.tokens.matches2(_types.TokenType._export, _types.TokenType._enum) ||
this.tokens.matches3(_types.TokenType._export, _types.TokenType._const, _types.TokenType._enum)
) {
this.processEnum(true);
return true;
}
return false;
}
processEnum(isExport = false) {
// We might have "export const enum", so just remove all relevant tokens.
this.tokens.removeInitialToken();
while (this.tokens.matches1(_types.TokenType._const) || this.tokens.matches1(_types.TokenType._enum)) {
this.tokens.removeToken();
}
const enumName = this.tokens.identifierName();
this.tokens.removeToken();
if (isExport && !this.isImportsTransformEnabled) {
this.tokens.appendCode("export ");
}
this.tokens.appendCode(`var ${enumName}; (function (${enumName})`);
this.tokens.copyExpectedToken(_types.TokenType.braceL);
this.processEnumBody(enumName);
this.tokens.copyExpectedToken(_types.TokenType.braceR);
if (isExport && this.isImportsTransformEnabled) {
this.tokens.appendCode(`)(${enumName} || (exports.${enumName} = ${enumName} = {}));`);
} else {
this.tokens.appendCode(`)(${enumName} || (${enumName} = {}));`);
}
}
/**
* Rather than try to compute the actual enum values at compile time, we just create variables for
* each one and let everything evaluate at runtime. There's some additional complexity due to
* handling string literal names, including ones that happen to be valid identifiers.
*/
processEnumBody(enumName) {
let isPreviousValidIdentifier = false;
let lastValueReference = null;
while (true) {
if (this.tokens.matches1(_types.TokenType.braceR)) {
break;
}
const nameToken = this.tokens.currentToken();
let name;
let nameStringCode;
if (nameToken.type === _types.TokenType.name) {
name = this.tokens.identifierNameForToken(nameToken);
nameStringCode = `"${name}"`;
} else if (nameToken.type === _types.TokenType.string) {
name = this.tokens.stringValueForToken(nameToken);
nameStringCode = this.tokens.code.slice(nameToken.start, nameToken.end);
} else {
throw new Error("Expected name or string at beginning of enum element.");
}
const isValidIdentifier = _isIdentifier2.default.call(void 0, name);
this.tokens.removeInitialToken();
let valueIsString;
let valueCode;
if (this.tokens.matches1(_types.TokenType.eq)) {
const rhsEndIndex = this.tokens.currentToken().rhsEndIndex;
if (rhsEndIndex == null) {
throw new Error("Expected rhsEndIndex on enum assign.");
}
this.tokens.removeToken();
if (
this.tokens.matches2(_types.TokenType.string, _types.TokenType.comma) ||
this.tokens.matches2(_types.TokenType.string, _types.TokenType.braceR)
) {
valueIsString = true;
}
const startToken = this.tokens.currentToken();
while (this.tokens.currentIndex() < rhsEndIndex) {
this.tokens.removeToken();
}
valueCode = this.tokens.code.slice(
startToken.start,
this.tokens.tokenAtRelativeIndex(-1).end,
);
} else {
valueIsString = false;
if (lastValueReference != null) {
if (isPreviousValidIdentifier) {
valueCode = `${lastValueReference} + 1`;
} else {
valueCode = `(${lastValueReference}) + 1`;
}
} else {
valueCode = "0";
}
}
if (this.tokens.matches1(_types.TokenType.comma)) {
this.tokens.removeToken();
}
let valueReference;
if (isValidIdentifier) {
this.tokens.appendCode(`const ${name} = ${valueCode}; `);
valueReference = name;
} else {
valueReference = valueCode;
}
if (valueIsString) {
this.tokens.appendCode(`${enumName}[${nameStringCode}] = ${valueReference};`);
} else {
this.tokens.appendCode(
`${enumName}[${enumName}[${nameStringCode}] = ${valueReference}] = ${nameStringCode};`,
);
}
lastValueReference = valueReference;
isPreviousValidIdentifier = isValidIdentifier;
}
}
} exports.default = TypeScriptTransformer;