fix: add strict types to build scripts and remove all any usage

Replace all implicit and explicit any types in build scripts with
proper TypeScript Compiler API types (Decorator, ClassDeclaration,
MethodDeclaration, Identifier, SourceFile, etc.). Add PackageJson
and InjectableClassEntry interfaces. Fix return types, null checks,
and type assertions throughout all transformer scripts.
This commit is contained in:
Daniel Sogl
2026-03-21 16:11:27 -07:00
parent 6453f2ab78
commit 62956e429c
9 changed files with 160 additions and 108 deletions
@@ -23,7 +23,7 @@ export const EMIT_PATH = resolve(ROOT, 'injectable-classes.json');
*/
export function extractInjectables() {
return (ctx: TransformationContext) => {
return (tsSourceFile) => {
return (tsSourceFile: any) => {
if (tsSourceFile.fileName.indexOf('src/@awesome-cordova-plugins/plugins') > -1) {
visitEachChild(
tsSourceFile,
@@ -36,7 +36,7 @@ export function extractInjectables() {
if (isInjectable) {
injectableClasses.push({
file: tsSourceFile.path,
className: (node as ClassDeclaration).name.text,
className: (node as ClassDeclaration).name!.text,
dirName: tsSourceFile.path.split(/[\\\/]+/).reverse()[1],
});
}
+21 -17
View File
@@ -1,20 +1,24 @@
import { factory, SourceFile, SyntaxKind, TransformationContext } from 'typescript';
import { factory, Identifier, ImportSpecifier, SourceFile, SyntaxKind, TransformationContext } from 'typescript';
import { getMethodsForDecorator } from '../helpers';
function transformImports(file: SourceFile, ctx: TransformationContext, ngcBuild?: boolean) {
function transformImports(file: SourceFile, _ctx: TransformationContext, ngcBuild?: boolean) {
// remove angular imports
if (!ngcBuild) {
// @ts-expect-error
file.statements = (file.statements as any).filter(
(s: any) => !(s.kind === SyntaxKind.ImportDeclaration && s.moduleSpecifier.text === '@angular/core')
);
// @ts-expect-error — mutating readonly statements for transformer pipeline
file.statements = (
file.statements as unknown as Array<{ kind: number; moduleSpecifier?: { text: string } }>
).filter((s) => !(s.kind === SyntaxKind.ImportDeclaration && s.moduleSpecifier?.text === '@angular/core'));
}
// find the @awesome-cordova-plugins/core import statement
const importStatement = (file.statements as any).find((s: any) => {
return s.kind === SyntaxKind.ImportDeclaration && s.moduleSpecifier.text === '@awesome-cordova-plugins/core';
});
const importStatement = (file.statements as unknown as Array<Record<string, unknown>>).find((s) => {
return (
(s as { kind: number }).kind === SyntaxKind.ImportDeclaration &&
(s as { moduleSpecifier: { text: string } }).moduleSpecifier.text === '@awesome-cordova-plugins/core'
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}) as Record<string, any> | undefined;
// we're only interested in files containing @awesome-cordova-plugins/core import statement
if (!importStatement) return file;
@@ -27,7 +31,7 @@ function transformImports(file: SourceFile, ctx: TransformationContext, ngcBuild
const keep: string[] = ['getPromise', 'checkAvailability'];
let m;
let m: RegExpExecArray | null;
while ((m = decoratorRegex.exec(file.text)) !== null) {
if (m.index === decoratorRegex.lastIndex) {
@@ -37,27 +41,27 @@ function transformImports(file: SourceFile, ctx: TransformationContext, ngcBuild
}
if (decorators.length) {
let methods = [];
let methods: string[] = [];
decorators.forEach((d) => (methods = getMethodsForDecorator(d).concat(methods)));
const methodElements = methods.map((m) => factory.createIdentifier(m));
const methodNames = methodElements.map((el) => el.escapedText);
const methodElements = methods.map((name: string) => factory.createIdentifier(name));
const methodNames = methodElements.map((el: Identifier) => el.escapedText);
importStatement.importClause.namedBindings.elements = [
factory.createIdentifier('AwesomeCordovaNativePlugin'),
...methodElements,
...importStatement.importClause.namedBindings.elements.filter(
(el) => keep.indexOf(el.name.text) !== -1 && methodNames.indexOf(el.name.text) === -1
(el: ImportSpecifier) => keep.indexOf(el.name.text) !== -1 && methodNames.indexOf(el.name.text) === -1
),
];
if (ngcBuild) {
importStatement.importClause.namedBindings.elements = importStatement.importClause.namedBindings.elements.map(
(binding) => {
(binding: Identifier & { name?: { text: string } }) => {
if (binding.escapedText) {
binding.name = {
text: binding.escapedText,
text: binding.escapedText as string,
};
}
return binding;
@@ -71,7 +75,7 @@ function transformImports(file: SourceFile, ctx: TransformationContext, ngcBuild
export function importsTransformer(ngcBuild?: boolean) {
return (ctx: TransformationContext) => {
return (tsSourceFile) => {
return (tsSourceFile: SourceFile) => {
return transformImports(tsSourceFile, ctx, ngcBuild);
};
};
+24 -11
View File
@@ -1,32 +1,45 @@
import { canHaveDecorators, ClassDeclaration, factory, getDecorators as tsGetDecorators, SyntaxKind } from 'typescript';
import {
canHaveDecorators,
ClassDeclaration,
ClassElement,
ConstructorDeclaration,
factory,
getDecorators as tsGetDecorators,
MethodDeclaration,
SyntaxKind,
} from 'typescript';
import { transformMethod } from './methods';
import { transformProperty } from './properties';
export function transformMembers(cls: ClassDeclaration) {
export function transformMembers(cls: ClassDeclaration): ClassElement[] {
const propertyIndices: number[] = [];
const members = cls.members.map((member: any, index: number) => {
// only process decorated members
const members = cls.members.map((member, index) => {
const memberDecorators = canHaveDecorators(member) ? tsGetDecorators(member) : undefined;
if (!memberDecorators || !memberDecorators.length) return member;
switch (member.kind) {
case SyntaxKind.MethodDeclaration:
return transformMethod(member);
return transformMethod(member as MethodDeclaration) ?? member;
case SyntaxKind.PropertyDeclaration:
propertyIndices.push(index);
return member;
case SyntaxKind.Constructor:
return factory.createConstructorDeclaration(undefined, member.parameters, member.body);
case SyntaxKind.Constructor: {
const ctor = member as ConstructorDeclaration;
return factory.createConstructorDeclaration(undefined, ctor.parameters, ctor.body);
}
default:
return member; // in case anything gets here by accident...
return member;
}
});
}) as ClassElement[];
propertyIndices.forEach((i: number) => {
const [getter, setter] = transformProperty(members, i) as any;
members.push(getter, setter);
const result = transformProperty(members, i);
if (Array.isArray(result)) {
const [getter, setter] = result;
members.push(getter, setter);
}
});
propertyIndices.reverse().forEach((i) => members.splice(i, 1));
+16 -10
View File
@@ -1,4 +1,4 @@
import { Expression, factory, MethodDeclaration, SyntaxKind } from 'typescript';
import { Expression, factory, Identifier, MethodDeclaration, SyntaxKind } from 'typescript';
import { Logger } from '../../logger';
import {
@@ -12,9 +12,11 @@ import {
export function transformMethod(method: MethodDeclaration) {
if (!method) return;
const decorator = getDecorator(method),
decoratorName = getDecoratorName(decorator),
decoratorArgs = getDecoratorArgs(decorator);
const decorator = getDecorator(method);
if (!decorator) return;
const decoratorName = getDecoratorName(decorator);
const decoratorArgs = getDecoratorArgs(decorator);
try {
return factory.createMethodDeclaration(
@@ -27,13 +29,17 @@ export function transformMethod(method: MethodDeclaration) {
method.type,
factory.createBlock([factory.createReturnStatement(getMethodBlock(method, decoratorName, decoratorArgs))])
);
} catch (e) {
Logger.error('Error transforming method: ' + (method.name as any).text);
Logger.error(e.message);
} catch (e: unknown) {
Logger.error('Error transforming method: ' + (method.name as Identifier).text);
Logger.error(e instanceof Error ? e.message : String(e));
}
}
function getMethodBlock(method: MethodDeclaration, decoratorName: string, decoratorArgs: any): Expression {
function getMethodBlock(
method: MethodDeclaration,
decoratorName: string,
decoratorArgs: Record<string, string | number | boolean | string[]>
): Expression {
const decoratorMethod = getMethodsForDecorator(decoratorName)[0];
switch (decoratorName) {
@@ -46,14 +52,14 @@ function getMethodBlock(method: MethodDeclaration, decoratorName: string, decora
SyntaxKind.EqualsEqualsEqualsToken,
factory.createTrue()
),
method.body
method.body!
),
]);
default:
return factory.createCallExpression(factory.createIdentifier(decoratorMethod), undefined, [
factory.createThis(),
factory.createStringLiteral(decoratorArgs?.methodName || (method.name as any).text),
factory.createStringLiteral((decoratorArgs?.methodName as string) || (method.name as Identifier).text),
convertValueToLiteral(decoratorArgs),
factory.createIdentifier('arguments'),
]);
+10 -9
View File
@@ -1,10 +1,12 @@
import {
canHaveDecorators,
canHaveModifiers,
ClassDeclaration,
Decorator,
factory,
getDecorators as tsGetDecorators,
getModifiers as tsGetModifiers,
Identifier,
SourceFile,
SyntaxKind,
TransformationContext,
@@ -16,16 +18,15 @@ import { Logger } from '../../logger';
import { convertValueToLiteral, getDecorator, getDecoratorArgs, getDecoratorName } from '../helpers';
import { transformMembers } from './members';
function transformClass(cls: any, ngcBuild?: boolean) {
Logger.profile('transformClass: ' + cls.name.text);
function transformClass(cls: ClassDeclaration, ngcBuild?: boolean) {
Logger.profile('transformClass: ' + cls.name!.text);
const pluginStatics = [];
const dec: Decorator = getDecorator(cls);
const dec = getDecorator(cls);
if (dec) {
const pluginDecoratorArgs = getDecoratorArgs(dec);
// add plugin decorator args as static properties of the plugin's class
for (const prop in pluginDecoratorArgs) {
pluginStatics.push(
factory.createPropertyDeclaration(
@@ -45,7 +46,7 @@ function transformClass(cls: any, ngcBuild?: boolean) {
? clsDecorators.filter((d: Decorator) => getDecoratorName(d) === 'Injectable')
: [];
cls = factory.createClassDeclaration(
const result = factory.createClassDeclaration(
[...keepDecorators, factory.createToken(SyntaxKind.ExportKeyword)],
cls.name,
cls.typeParameters,
@@ -53,8 +54,8 @@ function transformClass(cls: any, ngcBuild?: boolean) {
[...transformMembers(cls), ...pluginStatics]
);
Logger.profile('transformClass: ' + cls.name.text);
return cls;
Logger.profile('transformClass: ' + (result.name as Identifier).text);
return result;
}
function transformClasses(file: SourceFile, ctx: TransformationContext, ngcBuild?: boolean) {
@@ -68,7 +69,7 @@ function transformClasses(file: SourceFile, ctx: TransformationContext, ngcBuild
) {
return node;
}
return transformClass(node, ngcBuild);
return transformClass(node as ClassDeclaration, ngcBuild);
},
ctx
);
@@ -76,7 +77,7 @@ function transformClasses(file: SourceFile, ctx: TransformationContext, ngcBuild
export function pluginClassTransformer(ngcBuild?: boolean): TransformerFactory<SourceFile> {
return (ctx: TransformationContext) => {
return (tsSourceFile) => {
return (tsSourceFile: SourceFile) => {
if (tsSourceFile.fileName.indexOf('src/@awesome-cordova-plugins/plugins') > -1) {
return transformClasses(tsSourceFile, ctx, ngcBuild);
}
+17 -5
View File
@@ -1,11 +1,21 @@
import { factory, PropertyDeclaration } from 'typescript';
import {
ClassElement,
factory,
GetAccessorDeclaration,
Identifier,
PropertyDeclaration,
SetAccessorDeclaration,
} from 'typescript';
import { getDecorator, getDecoratorName } from '../helpers';
export function transformProperty(members: any[], index: number) {
export function transformProperty(
members: ClassElement[],
index: number
): [GetAccessorDeclaration, SetAccessorDeclaration] | PropertyDeclaration {
const property = members[index] as PropertyDeclaration,
decorator = getDecorator(property),
decoratorName = getDecoratorName(decorator);
decoratorName = decorator ? getDecoratorName(decorator) : undefined;
let type: 'cordova' | 'instance';
@@ -22,6 +32,8 @@ export function transformProperty(members: any[], index: number) {
return property;
}
const propertyName = (property.name as Identifier).text;
const getter = factory.createGetAccessorDeclaration(
undefined,
property.name,
@@ -31,7 +43,7 @@ export function transformProperty(members: any[], index: number) {
factory.createReturnStatement(
factory.createCallExpression(factory.createIdentifier(type + 'PropertyGet'), undefined, [
factory.createThis(),
factory.createStringLiteral((property.name as any).text),
factory.createStringLiteral(propertyName),
])
),
])
@@ -45,7 +57,7 @@ export function transformProperty(members: any[], index: number) {
factory.createExpressionStatement(
factory.createCallExpression(factory.createIdentifier(type + 'PropertySet'), undefined, [
factory.createThis(),
factory.createStringLiteral((property.name as any).text),
factory.createStringLiteral(propertyName),
factory.createIdentifier('value'),
])
),