typescript-to-lua
Version:
A generic TypeScript to Lua transpiler. Write your code in TypeScript and publish Lua!
200 lines • 9.12 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ContextType = void 0;
exports.getCallContextType = getCallContextType;
exports.getFunctionContextType = getFunctionContextType;
const ts = require("typescript");
const annotations_1 = require("./annotations");
const typescript_1 = require("./typescript");
var ContextType;
(function (ContextType) {
ContextType[ContextType["None"] = 0] = "None";
ContextType[ContextType["Void"] = 1] = "Void";
ContextType[ContextType["NonVoid"] = 2] = "NonVoid";
ContextType[ContextType["Mixed"] = 3] = "Mixed";
})(ContextType || (exports.ContextType = ContextType = {}));
function hasNoSelfAncestor(declaration) {
const scopeDeclaration = (0, typescript_1.findFirstNodeAbove)(declaration, (node) => ts.isSourceFile(node) || ts.isModuleDeclaration(node));
if (!scopeDeclaration) {
return false;
}
else if (ts.isSourceFile(scopeDeclaration)) {
return (0, annotations_1.getFileAnnotations)(scopeDeclaration).has(annotations_1.AnnotationKind.NoSelfInFile);
}
else if ((0, annotations_1.getNodeAnnotations)(scopeDeclaration).has(annotations_1.AnnotationKind.NoSelf)) {
return true;
}
else {
return hasNoSelfAncestor(scopeDeclaration);
}
}
function getExplicitThisParameter(signatureDeclaration) {
const param = signatureDeclaration.parameters[0];
if (param && ts.isIdentifier(param.name) && ts.identifierToKeywordKind(param.name) === ts.SyntaxKind.ThisKeyword) {
return param;
}
}
const callContextTypes = new WeakMap();
function getCallContextType(context, callExpression) {
const known = callContextTypes.get(callExpression);
if (known !== undefined)
return known;
const signature = context.checker.getResolvedSignature(callExpression);
const signatureDeclaration = signature === null || signature === void 0 ? void 0 : signature.getDeclaration();
let contextType = ContextType.None;
if (signatureDeclaration) {
contextType = computeDeclarationContextType(context, signatureDeclaration);
}
else {
// No signature declaration could be resolved, so instead try to see if the declaration is in a
// noSelfInFile file
const declarations = findRootDeclarations(context, callExpression);
contextType = declarations.some(d => (0, annotations_1.getFileAnnotations)(d.getSourceFile()).has(annotations_1.AnnotationKind.NoSelfInFile))
? ContextType.Void
: context.options.noImplicitSelf
? ContextType.Void
: ContextType.NonVoid;
}
callContextTypes.set(callExpression, contextType);
return contextType;
}
const signatureDeclarationContextTypes = new WeakMap();
function getSignatureContextType(context, signatureDeclaration) {
const known = signatureDeclarationContextTypes.get(signatureDeclaration);
if (known !== undefined)
return known;
const contextType = computeDeclarationContextType(context, signatureDeclaration);
signatureDeclarationContextTypes.set(signatureDeclaration, contextType);
return contextType;
}
function findRootDeclarations(context, callExpression) {
var _a, _b;
const calledExpression = ts.isTaggedTemplateExpression(callExpression)
? callExpression.tag
: ts.isJsxSelfClosingElement(callExpression)
? callExpression.tagName
: ts.isJsxOpeningElement(callExpression)
? callExpression.tagName
: ts.isCallExpression(callExpression)
? callExpression.expression
: undefined;
if (!calledExpression)
return [];
const calledSymbol = context.checker.getSymbolAtLocation(calledExpression);
if (calledSymbol === undefined)
return [];
return ((_b = (_a = calledSymbol.getDeclarations()) === null || _a === void 0 ? void 0 : _a.flatMap(d => {
var _a;
if (ts.isImportSpecifier(d)) {
const aliasSymbol = context.checker.getAliasedSymbol(calledSymbol);
return (_a = aliasSymbol.getDeclarations()) !== null && _a !== void 0 ? _a : [];
}
else {
return [d];
}
})) !== null && _b !== void 0 ? _b : []);
}
function computeDeclarationContextType(context, signatureDeclaration) {
const thisParameter = getExplicitThisParameter(signatureDeclaration);
if (thisParameter) {
// Explicit 'this'
return thisParameter.type && thisParameter.type.kind === ts.SyntaxKind.VoidKeyword
? ContextType.Void
: ContextType.NonVoid;
}
// noSelf declaration on function signature
if ((0, annotations_1.getNodeAnnotations)(signatureDeclaration).has(annotations_1.AnnotationKind.NoSelf)) {
return ContextType.Void;
}
if (ts.isMethodSignature(signatureDeclaration) ||
ts.isMethodDeclaration(signatureDeclaration) ||
ts.isConstructSignatureDeclaration(signatureDeclaration) ||
ts.isConstructorDeclaration(signatureDeclaration) ||
(signatureDeclaration.parent && ts.isPropertyDeclaration(signatureDeclaration.parent)) ||
(signatureDeclaration.parent && ts.isPropertySignature(signatureDeclaration.parent)) ||
(signatureDeclaration.parent && ts.isIndexSignatureDeclaration(signatureDeclaration.parent))) {
// Class/interface methods only respect @noSelf on their parent
const scopeDeclaration = (0, typescript_1.findFirstNodeAbove)(signatureDeclaration, (n) => ts.isClassDeclaration(n) || ts.isClassExpression(n) || ts.isInterfaceDeclaration(n));
if (scopeDeclaration !== undefined && (0, annotations_1.getNodeAnnotations)(scopeDeclaration).has(annotations_1.AnnotationKind.NoSelf)) {
return ContextType.Void;
}
return ContextType.NonVoid;
}
if (signatureDeclaration.parent && ts.isTypeParameterDeclaration(signatureDeclaration.parent)) {
return ContextType.NonVoid;
}
// When using --noImplicitSelf and the signature is defined in a file targeted by the program apply the @noSelf rule.
const program = context.program;
const options = program.getCompilerOptions();
if (options.noImplicitSelf) {
const sourceFile = program.getSourceFile(signatureDeclaration.getSourceFile().fileName);
if (sourceFile !== undefined &&
!program.isSourceFileDefaultLibrary(sourceFile) &&
!program.isSourceFileFromExternalLibrary(sourceFile)) {
return ContextType.Void;
}
}
// Walk up to find @noSelf or @noSelfInFile
if (hasNoSelfAncestor(signatureDeclaration)) {
return ContextType.Void;
}
return ContextType.NonVoid;
}
function reduceContextTypes(contexts) {
let type = ContextType.None;
for (const context of contexts) {
type |= context;
if (type === ContextType.Mixed)
break;
}
return type;
}
function getSignatureDeclarations(context, signature) {
if (signature.compositeSignatures) {
return signature.compositeSignatures.flatMap(s => getSignatureDeclarations(context, s));
}
const signatureDeclaration = signature.getDeclaration();
if (signatureDeclaration === undefined) {
return [];
}
let inferredType;
if (ts.isMethodDeclaration(signatureDeclaration) &&
ts.isObjectLiteralExpression(signatureDeclaration.parent) &&
!getExplicitThisParameter(signatureDeclaration)) {
inferredType = context.checker.getContextualTypeForObjectLiteralElement(signatureDeclaration);
}
else if ((ts.isFunctionExpression(signatureDeclaration) || ts.isArrowFunction(signatureDeclaration)) &&
!getExplicitThisParameter(signatureDeclaration)) {
// Infer type of function expressions/arrow functions
inferredType = (0, typescript_1.inferAssignedType)(context, signatureDeclaration);
}
if (inferredType) {
const inferredSignatures = (0, typescript_1.getAllCallSignatures)(inferredType);
if (inferredSignatures.length > 0) {
return inferredSignatures.map(s => s.getDeclaration());
}
}
return [signatureDeclaration];
}
const typeContextTypes = new WeakMap();
function getFunctionContextType(context, type) {
const known = typeContextTypes.get(type);
if (known !== undefined)
return known;
const contextType = computeFunctionContextType(context, type);
typeContextTypes.set(type, contextType);
return contextType;
}
function computeFunctionContextType(context, type) {
if (type.isTypeParameter()) {
const constraint = type.getConstraint();
if (constraint)
return getFunctionContextType(context, constraint);
}
const signatures = context.checker.getSignaturesOfType(type, ts.SignatureKind.Call);
if (signatures.length === 0) {
return ContextType.None;
}
return reduceContextTypes(signatures.flatMap(s => getSignatureDeclarations(context, s)).map(s => getSignatureContextType(context, s)));
}
//# sourceMappingURL=function-context.js.map