astro-ghostcms/.pnpm-store/v3/files/9d/ce3dfd205408e2a12b1587a580d...

280 lines
10 KiB
Plaintext

"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._override) ||
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} = {}));`);
}
}
/**
* Transform an enum into equivalent JS. This has complexity in a few places:
* - TS allows string enums, numeric enums, and a mix of the two styles within an enum.
* - Enum keys are allowed to be referenced in later enum values.
* - Enum keys are allowed to be strings.
* - When enum values are omitted, they should follow an auto-increment behavior.
*/
processEnumBody(enumName) {
// Code that can be used to reference the previous enum member, or null if this is the first
// enum member.
let previousValueCode = null;
while (true) {
if (this.tokens.matches1(_types.TokenType.braceR)) {
break;
}
const {nameStringCode, variableName} = this.extractEnumKeyInfo(this.tokens.currentToken());
this.tokens.removeInitialToken();
if (
this.tokens.matches3(_types.TokenType.eq, _types.TokenType.string, _types.TokenType.comma) ||
this.tokens.matches3(_types.TokenType.eq, _types.TokenType.string, _types.TokenType.braceR)
) {
this.processStringLiteralEnumMember(enumName, nameStringCode, variableName);
} else if (this.tokens.matches1(_types.TokenType.eq)) {
this.processExplicitValueEnumMember(enumName, nameStringCode, variableName);
} else {
this.processImplicitValueEnumMember(
enumName,
nameStringCode,
variableName,
previousValueCode,
);
}
if (this.tokens.matches1(_types.TokenType.comma)) {
this.tokens.removeToken();
}
if (variableName != null) {
previousValueCode = variableName;
} else {
previousValueCode = `${enumName}[${nameStringCode}]`;
}
}
}
/**
* Detect name information about this enum key, which will be used to determine which code to emit
* and whether we should declare a variable as part of this declaration.
*
* Some cases to keep in mind:
* - Enum keys can be implicitly referenced later, e.g. `X = 1, Y = X`. In Sucrase, we implement
* this by declaring a variable `X` so that later expressions can use it.
* - In addition to the usual identifier key syntax, enum keys are allowed to be string literals,
* e.g. `"hello world" = 3,`. Template literal syntax is NOT allowed.
* - Even if the enum key is defined as a string literal, it may still be referenced by identifier
* later, e.g. `"X" = 1, Y = X`. That means that we need to detect whether or not a string
* literal is identifier-like and emit a variable if so, even if the declaration did not use an
* identifier.
* - Reserved keywords like `break` are valid enum keys, but are not valid to be referenced later
* and would be a syntax error if we emitted a variable, so we need to skip the variable
* declaration in those cases.
*
* The variableName return value captures these nuances: if non-null, we can and must emit a
* variable declaration, and if null, we can't and shouldn't.
*/
extractEnumKeyInfo(nameToken) {
if (nameToken.type === _types.TokenType.name) {
const name = this.tokens.identifierNameForToken(nameToken);
return {
nameStringCode: `"${name}"`,
variableName: _isIdentifier2.default.call(void 0, name) ? name : null,
};
} else if (nameToken.type === _types.TokenType.string) {
const name = this.tokens.stringValueForToken(nameToken);
return {
nameStringCode: this.tokens.code.slice(nameToken.start, nameToken.end),
variableName: _isIdentifier2.default.call(void 0, name) ? name : null,
};
} else {
throw new Error("Expected name or string at beginning of enum element.");
}
}
/**
* Handle an enum member where the RHS is just a string literal (not omitted, not a number, and
* not a complex expression). This is the typical form for TS string enums, and in this case, we
* do *not* create a reverse mapping.
*
* This is called after deleting the key token, when the token processor is at the equals sign.
*
* Example 1:
* someKey = "some value"
* ->
* const someKey = "some value"; MyEnum["someKey"] = someKey;
*
* Example 2:
* "some key" = "some value"
* ->
* MyEnum["some key"] = "some value";
*/
processStringLiteralEnumMember(
enumName,
nameStringCode,
variableName,
) {
if (variableName != null) {
this.tokens.appendCode(`const ${variableName}`);
// =
this.tokens.copyToken();
// value string
this.tokens.copyToken();
this.tokens.appendCode(`; ${enumName}[${nameStringCode}] = ${variableName};`);
} else {
this.tokens.appendCode(`${enumName}[${nameStringCode}]`);
// =
this.tokens.copyToken();
// value string
this.tokens.copyToken();
this.tokens.appendCode(";");
}
}
/**
* Handle an enum member initialized with an expression on the right-hand side (other than a
* string literal). In these cases, we should transform the expression and emit code that sets up
* a reverse mapping.
*
* The TypeScript implementation of this operation distinguishes between expressions that can be
* "constant folded" at compile time (i.e. consist of number literals and simple math operations
* on those numbers) and ones that are dynamic. For constant expressions, it emits the resolved
* numeric value, and auto-incrementing is only allowed in that case. Evaluating expressions at
* compile time would add significant complexity to Sucrase, so Sucrase instead leaves the
* expression as-is, and will later emit something like `MyEnum["previousKey"] + 1` to implement
* auto-incrementing.
*
* This is called after deleting the key token, when the token processor is at the equals sign.
*
* Example 1:
* someKey = 1 + 1
* ->
* const someKey = 1 + 1; MyEnum[MyEnum["someKey"] = someKey] = "someKey";
*
* Example 2:
* "some key" = 1 + 1
* ->
* MyEnum[MyEnum["some key"] = 1 + 1] = "some key";
*/
processExplicitValueEnumMember(
enumName,
nameStringCode,
variableName,
) {
const rhsEndIndex = this.tokens.currentToken().rhsEndIndex;
if (rhsEndIndex == null) {
throw new Error("Expected rhsEndIndex on enum assign.");
}
if (variableName != null) {
this.tokens.appendCode(`const ${variableName}`);
this.tokens.copyToken();
while (this.tokens.currentIndex() < rhsEndIndex) {
this.rootTransformer.processToken();
}
this.tokens.appendCode(
`; ${enumName}[${enumName}[${nameStringCode}] = ${variableName}] = ${nameStringCode};`,
);
} else {
this.tokens.appendCode(`${enumName}[${enumName}[${nameStringCode}]`);
this.tokens.copyToken();
while (this.tokens.currentIndex() < rhsEndIndex) {
this.rootTransformer.processToken();
}
this.tokens.appendCode(`] = ${nameStringCode};`);
}
}
/**
* Handle an enum member with no right-hand side expression. In this case, the value is the
* previous value plus 1, or 0 if there was no previous value. We should also always emit a
* reverse mapping.
*
* Example 1:
* someKey2
* ->
* const someKey2 = someKey1 + 1; MyEnum[MyEnum["someKey2"] = someKey2] = "someKey2";
*
* Example 2:
* "some key 2"
* ->
* MyEnum[MyEnum["some key 2"] = someKey1 + 1] = "some key 2";
*/
processImplicitValueEnumMember(
enumName,
nameStringCode,
variableName,
previousValueCode,
) {
let valueCode = previousValueCode != null ? `${previousValueCode} + 1` : "0";
if (variableName != null) {
this.tokens.appendCode(`const ${variableName} = ${valueCode}; `);
valueCode = variableName;
}
this.tokens.appendCode(
`${enumName}[${enumName}[${nameStringCode}] = ${valueCode}] = ${nameStringCode};`,
);
}
} exports.default = TypeScriptTransformer;