Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Project/functions/createProjectData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export function createProjectData(
const projectData: ProjectData = {
tsConfigPath,
includePath,
isSkippingPackages: projectOptions.skipPackages,
isPackage,
isPublishing: projectOptions.publish,
logTruthyChanges,
Expand Down
1 change: 1 addition & 0 deletions src/Shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export interface ProjectOptions {

export interface ProjectData {
includePath: string;
isSkippingPackages: boolean;
isPackage: boolean;
isPublishing: boolean;
logTruthyChanges: boolean;
Expand Down
2 changes: 1 addition & 1 deletion src/TSTransformer/classes/TransformState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export class TransformState {
return this._context === CompliationContext.Shared;
}

public get isPublish() {
public isPublish() {
return this.data.isPublishing;
}

Expand Down
19 changes: 12 additions & 7 deletions src/TSTransformer/nodes/class/transformClassConstructor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import { transformIdentifierDefined } from "TSTransformer/nodes/expressions/tran
import { transformParameters } from "TSTransformer/nodes/transformParameters";
import { transformPropertyName } from "TSTransformer/nodes/transformPropertyName";
import { transformStatementList } from "TSTransformer/nodes/transformStatementList";
import { isRootAirshipBehaviourClass, isRootAirshipSingletonClass } from "TSTransformer/util/extendsAirshipBehaviour";
import {
isAirshipSingletonClass,
isRootAirshipBehaviourClass,
isRootAirshipSingletonClass,
} from "TSTransformer/util/extendsAirshipBehaviour";
import { getExtendsNode } from "TSTransformer/util/getExtendsNode";
import { getStatements } from "TSTransformer/util/getStatements";
import ts from "typescript";
Expand Down Expand Up @@ -67,9 +71,10 @@ export function transformClassConstructor(

let bodyStatements = originNode ? getStatements(originNode.body) : [];

const isAirshipSingleton = isRootAirshipSingletonClass(state, node);
const isAirshipBehaviour = isRootAirshipBehaviourClass(state, node);
let removeFirstSuper = isAirshipBehaviour || isAirshipSingleton;
const isRootSingletonClass = isRootAirshipSingletonClass(state, node);
const isAirshipSingleton = isAirshipSingletonClass(state, node);
const isRootAirshipBehaviour = isRootAirshipBehaviourClass(state, node);
let removeFirstSuper = isRootAirshipBehaviour || isAirshipSingleton;

let parameters = luau.list.make<luau.AnyIdentifier>();
let hasDotDotDot = false;
Expand All @@ -82,7 +87,7 @@ export function transformClassConstructor(
luau.list.pushList(statements, paramStatements);
parameters = constructorParams;
hasDotDotDot = constructorHasDotDotDot;
} else if (getExtendsNode(node) && !isAirshipBehaviour && !isAirshipSingleton) {
} else if (getExtendsNode(node) && !isRootAirshipBehaviour && !isRootSingletonClass) {
// if extends + no constructor:
// - add ... to params
// - add super.constructor(self, ...)
Expand Down Expand Up @@ -143,7 +148,7 @@ export function transformClassConstructor(

if (
ts.isIdentifier(member.name) &&
(isAirshipSingleton || isAirshipBehaviour) &&
(isAirshipSingleton || isRootAirshipBehaviour) &&
isAirshipBehaviourReserved(member.name.text)
) {
DiagnosticService.addDiagnostic(errors.noReservedAirshipIdentifier(member.name));
Expand Down Expand Up @@ -177,7 +182,7 @@ export function transformClassConstructor(
}
}

if (isAirshipSingleton) {
if (isAirshipSingleton && !node.modifiers?.some(value => ts.isAbstractModifier(value))) {
createAirshipSingletonBoilerplate(state, node, name, statements);
}

Expand Down
20 changes: 14 additions & 6 deletions src/TSTransformer/nodes/statements/transformImportDeclaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { TransformState } from "TSTransformer";
import { transformVariable } from "TSTransformer/nodes/statements/transformVariableStatement";
import { cleanModuleName } from "TSTransformer/util/cleanModuleName";
import { createImportExpression } from "TSTransformer/util/createImportExpression";
import { isAirshipSingletonType } from "TSTransformer/util/extendsAirshipBehaviour";
import { isAirshipSingletonType, isClassInheritingSymbol } from "TSTransformer/util/extendsAirshipBehaviour";
import { getOriginalSymbolOfNode } from "TSTransformer/util/getOriginalSymbolOfNode";
import { getSourceFileFromModuleSpecifier } from "TSTransformer/util/getSourceFileFromModuleSpecifier";
import { isSymbolOfValue } from "TSTransformer/util/isSymbolOfValue";
Expand Down Expand Up @@ -76,23 +76,24 @@ function shouldSkipSingletonImport(
symbol: ts.Symbol,
) {
const linkedModuleSingletonIds = state.airshipBuildState.singletonTypes.get(module.fileName);
if (!linkedModuleSingletonIds) {
if (state.isPublish() && !linkedModuleSingletonIds) {
return false;
}

if (!symbol.valueDeclaration) return false;
const valueDeclaration = symbol.valueDeclaration;
if (!valueDeclaration) return false;

// get the value type of the import
const valueType = state.typeChecker.getTypeAtLocation(symbol!.valueDeclaration!);
const valueType = state.typeChecker.getTypeAtLocation(valueDeclaration);

if (!valueType) {
return false;
}

const typeUniqueId = state.airshipBuildState.getUniqueIdForType(state, valueType, module);

// Need to ensure we keep the import or strip it... Don't like this TBH
if (!linkedModuleSingletonIds.has(typeUniqueId)) {
if (state.isPublish() && !linkedModuleSingletonIds!.has(typeUniqueId)) {
// Need to ensure we keep the import or strip it... Don't like this TBH
return false;
}

Expand All @@ -101,6 +102,13 @@ function shouldSkipSingletonImport(
// Ensure we're only using only a macro on the singleton
let shouldSkip = true;
ts.forEachChildRecursively(importDeclaration.getSourceFile(), node => {
// If we have an inheriting singleton, we need to call the constructor of the base singleton
if (ts.isClassLike(node) && isClassInheritingSymbol(state, node, symbol)) {
shouldSkip = false;
return "skip";
}

// Any static accesses of singletons
if (ts.isPropertyAccessExpression(node) && isStaticAirshipSingletonPropertyAccess(state, node, typeName)) {
shouldSkip = false;
return "skip";
Expand Down
59 changes: 44 additions & 15 deletions src/TSTransformer/util/extendsAirshipBehaviour.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ import { getExtendsNode } from "TSTransformer/util/getExtendsNode";
import { getOriginalSymbolOfNode } from "TSTransformer/util/getOriginalSymbolOfNode";
import ts from "typescript";

export function isRootAirshipBehaviourClassNoState(
singletonSymbol: ts.Symbol,
typeChecker: ts.TypeChecker,
node: ts.ClassLikeDeclaration,
) {
const extendsNode = getExtendsNode(node);
if (extendsNode) {
const symbol = getOriginalSymbolOfNode(typeChecker, extendsNode.expression);
if (symbol === singletonSymbol) {
return true;
}
}

return false;
}

export function isRootAirshipBehaviourClass(state: TransformState, node: ts.ClassLikeDeclaration) {
const extendsNode = getExtendsNode(node);
if (extendsNode) {
Expand All @@ -25,10 +41,11 @@ export function isRootAirshipSingletonClass(state: TransformState, node: ts.Clas
if (extendsNode) {
const airshipSingletonSymbol = state.services.airshipSymbolManager.getAirshipSingletonSymbolOrThrow();

const symbol = getOriginalSymbolOfNode(state.typeChecker, extendsNode.expression);
if (symbol === airshipSingletonSymbol) {
return true;
}
return isRootAirshipBehaviourClassNoState(airshipSingletonSymbol, state.typeChecker, node);
// const symbol = getOriginalSymbolOfNode(state.typeChecker, extendsNode.expression);
// if (symbol === airshipSingletonSymbol) {
// return true;
// }
}

return false;
Expand Down Expand Up @@ -86,7 +103,7 @@ export function isAirshipSingletonClassNoState(
}

// Get the root inheriting symbol (Should match AirshipBehaviour for this to be "extending" AirshipBehaviour)
const baseTypeDeclaration = inheritance[inheritance.length - 1];
const baseTypeDeclaration = inheritance[inheritance.length - 2] ?? inheritance[inheritance.length - 1];
if (baseTypeDeclaration !== undefined) {
return baseTypeDeclaration === airshipBehaviourSymbol;
}
Expand All @@ -102,6 +119,7 @@ export function isAirshipSingletonClass(state: TransformState, node: ts.ClassLik

// check if the immediate extends is AirshipBehaviour
let type = state.typeChecker.getTypeAtLocation(node);

if (type.isNullableType()) {
type = type.getNonNullableType();
}
Expand All @@ -111,17 +129,11 @@ export function isAirshipSingletonClass(state: TransformState, node: ts.ClassLik
return true;
}

// Get the inheritance tree, otherwise
const inheritance = getAncestorTypeSymbols(type, state.typeChecker);
if (inheritance.length === 0) {
return false;
}
const extendsClasses = getTypesOfClasses(state.typeChecker, getExtendsClasses(state.typeChecker, node));
if (extendsClasses.length === 0) return false;

// Get the root inheriting symbol (Should match AirshipBehaviour for this to be "extending" AirshipBehaviour)
const baseTypeDeclaration = inheritance[inheritance.length - 1];
if (baseTypeDeclaration !== undefined) {
return baseTypeDeclaration === airshipBehaviourSymbol;
}
const baseClass = extendsClasses[extendsClasses.length - 2] ?? extendsClasses[extendsClasses.length - 1];
return baseClass.symbol === airshipBehaviourSymbol;
}

return false;
Expand Down Expand Up @@ -167,6 +179,23 @@ export function isAirshipBehaviourType(state: TransformState, type: ts.Type) {
}
}

export const enum SingletonQueryType {
IsRootSingleton,
IsAnySingleton,
}

export function isClassInheritingSymbol(state: TransformState, node: ts.ClassLikeDeclaration, symbol: ts.Symbol) {
const type = state.typeChecker.getTypeAtLocation(node);

// Get the inheritance tree, otherwise
const inheritance = getAncestorTypeSymbols(type, state.typeChecker);
if (inheritance.length === 0) {
return false;
}

return inheritance.some(value => value === symbol);
}

export function isAirshipSingletonType(state: TransformState, type: ts.Type) {
const airshipBehaviourSymbol = state.services.airshipSymbolManager.getAirshipSingletonSymbolOrThrow();

Expand Down