Skip to content

Commit 63b2ac8

Browse files
committed
type checking passes
1 parent b20be9a commit 63b2ac8

File tree

4 files changed

+127
-44
lines changed

4 files changed

+127
-44
lines changed

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ export default function macros(babel: typeof Babel): Babel.PluginObj<State> {
9191
});
9292
},
9393

94-
exit(path) {
95-
this.macroBuilder.expand(path);
94+
exit() {
95+
this.macroBuilder.expand();
9696
},
9797
},
9898

src/utils/babel-type-helpers.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import type { NodePath, types as t } from '@babel/core';
2+
3+
export function name(value: t.Identifier | t.StringLiteral): string {
4+
if (value.type === 'Identifier') {
5+
return value.name;
6+
} else {
7+
return value.value;
8+
}
9+
}
10+
11+
export type CallIdentifierExpression = t.CallExpression & { callee: t.Identifier };
12+
13+
export function isCallIdentifierExpression(exp: t.CallExpression): exp is CallIdentifierExpression {
14+
return exp.callee.type === 'Identifier';
15+
}
16+
17+
export type CallStatementPath = NodePath<
18+
t.ExpressionStatement & { expression: CallIdentifierExpression }
19+
>;
20+
export function isCallStatementPath(
21+
path: NodePath<t.ExpressionStatement>
22+
): path is CallStatementPath {
23+
return (
24+
path.node.expression.type === 'CallExpression' &&
25+
isCallIdentifierExpression(path.node.expression)
26+
);
27+
}

src/utils/builder.ts

Lines changed: 69 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,44 @@
1+
import type * as Babel from '@babel/core';
2+
import type { types as t } from '@babel/core';
3+
import type { NodePath } from '@babel/core';
4+
import { CallIdentifierExpression, CallStatementPath } from './babel-type-helpers';
5+
6+
export interface Options {
7+
module: boolean | undefined;
8+
global: string | undefined;
9+
assertPredicateIndex: number | undefined;
10+
isDebug: boolean;
11+
}
12+
13+
interface MacroExpressionOpts {
14+
validate?: (expression: CallIdentifierExpression, args: t.CallExpression['arguments']) => void;
15+
buildConsoleAPI?: (
16+
expression: CallIdentifierExpression,
17+
args: t.CallExpression['arguments']
18+
) => t.CallExpression;
19+
consoleAPI?: t.Identifier;
20+
predicate?: (
21+
expression: CallIdentifierExpression,
22+
args: t.CallExpression['arguments']
23+
) => t.CallExpression['arguments'][number] | undefined;
24+
}
25+
126
export default class Builder {
2-
constructor(t, options) {
3-
this.t = t;
27+
private module: boolean | undefined;
28+
private global: string | undefined;
29+
private assertPredicateIndex: number | undefined;
30+
private isDebug: boolean;
31+
32+
private expressions: [CallStatementPath, (debugIdentifier: t.Expression) => t.Expression][] = [];
33+
34+
constructor(
35+
readonly t: typeof Babel.types,
36+
options: Options
37+
) {
438
this.module = options.module;
539
this.global = options.global;
640
this.assertPredicateIndex = options.assertPredicateIndex;
741
this.isDebug = options.isDebug;
8-
this.expressions = [];
942
}
1043

1144
/**
@@ -25,11 +58,12 @@ export default class Builder {
2558
*
2659
* ($DEBUG && $GLOBAL_NS.assert($PREDICATE, $MESSAGE));
2760
*/
28-
assert(path) {
29-
let predicate;
30-
if (this.assertPredicateIndex !== undefined) {
61+
assert(path: CallStatementPath) {
62+
let predicate: MacroExpressionOpts['predicate'];
63+
const index = this.assertPredicateIndex;
64+
if (index !== undefined) {
3165
predicate = (expression, args) => {
32-
return args[this.assertPredicateIndex];
66+
return args[index];
3367
};
3468
}
3569

@@ -55,7 +89,7 @@ export default class Builder {
5589
*
5690
* ($DEBUG && $GLOBAL_NS.warn($MESSAGE));
5791
*/
58-
warn(path) {
92+
warn(path: CallStatementPath) {
5993
this._createMacroExpression(path);
6094
}
6195

@@ -76,13 +110,11 @@ export default class Builder {
76110
*
77111
* ($DEBUG && $GLOBAL_NS.log($MESSAGE));
78112
*/
79-
log(path) {
113+
log(path: CallStatementPath) {
80114
this._createMacroExpression(path);
81115
}
82116

83-
_createMacroExpression(path, _options) {
84-
let options = _options || {};
85-
117+
_createMacroExpression(path: CallStatementPath, options: MacroExpressionOpts = {}) {
86118
let t = this.t;
87119
let expression = path.node.expression;
88120
let callee = expression.callee;
@@ -103,10 +135,13 @@ export default class Builder {
103135
callExpression = this._createConsoleAPI(options.consoleAPI || callee, args);
104136
}
105137

106-
let prefixedIdentifiers = [];
138+
let prefixedIdentifiers: t.Expression[] = [];
107139

108140
if (options.predicate) {
109141
let predicate = options.predicate(expression, args) || t.identifier('false');
142+
if (!this.t.isExpression(predicate)) {
143+
throw new Error(`bug: this doesn't support ${predicate.type}`);
144+
}
110145
let negatedPredicate = t.unaryExpression('!', t.parenthesizedExpression(predicate));
111146
prefixedIdentifiers.push(negatedPredicate);
112147
}
@@ -142,7 +177,7 @@ export default class Builder {
142177
*
143178
* ($DEBUG && $PREDICATE && $GLOBAL_NS.deprecate($MESSAGE, $PREDICATE, { $ID, $URL, $UNTIL }));
144179
*/
145-
deprecate(path) {
180+
deprecate(path: CallStatementPath) {
146181
this._createMacroExpression(path, {
147182
predicate: (expression, args) => args[1],
148183

@@ -157,8 +192,14 @@ export default class Builder {
157192

158193
if (
159194
meta &&
195+
this.t.isObjectExpression(meta) &&
160196
meta.properties &&
161-
!meta.properties.some((prop) => prop.key.name === 'id' || prop.key.value === 'id')
197+
!meta.properties.some(
198+
(prop) =>
199+
this.t.isObjectProperty(prop) &&
200+
((this.t.isIdentifier(prop.key) && prop.key.name === 'id') ||
201+
(this.t.isStringLiteral(prop.key) && prop.key.value === 'id'))
202+
)
162203
) {
163204
throw new ReferenceError(`deprecate's meta information requires an "id" field.`);
164205
}
@@ -180,24 +221,27 @@ export default class Builder {
180221
}
181222
}
182223

183-
_getIdentifiers(args) {
184-
return args.filter((arg) => this.t.isIdentifier(arg));
185-
}
186-
187-
_createGlobalExternalHelper(identifier, args, ns) {
224+
_createGlobalExternalHelper(
225+
identifier: t.Identifier,
226+
args: t.CallExpression['arguments'],
227+
ns: string
228+
) {
188229
let t = this.t;
189230
return t.callExpression(t.memberExpression(t.identifier(ns), identifier), args);
190231
}
191232

192-
_createConsoleAPI(identifier, args) {
233+
_createConsoleAPI(identifier: t.Identifier, args: t.CallExpression['arguments']) {
193234
let t = this.t;
194235
return t.callExpression(t.memberExpression(t.identifier('console'), identifier), args);
195236
}
196237

197-
_buildLogicalExpressions(identifiers, callExpression) {
238+
_buildLogicalExpressions(
239+
identifiers: t.Expression[],
240+
callExpression: t.Expression
241+
): (debugIdentifier: t.Expression) => t.Expression {
198242
let t = this.t;
199243

200-
return (debugIdentifier) => {
244+
return (debugIdentifier: t.Expression) => {
201245
identifiers.unshift(debugIdentifier);
202246
identifiers.push(callExpression);
203247
let logicalExpressions;
@@ -212,7 +256,7 @@ export default class Builder {
212256
}
213257
}
214258

215-
return logicalExpressions;
259+
return logicalExpressions!;
216260
};
217261
}
218-
};
262+
}

src/utils/macros.ts

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import Builder from './builder';
22
import type * as Babel from '@babel/core';
3+
import type { types as t } from '@babel/core';
4+
35
import type { NormalizedOptions } from './normalize-options';
6+
import type { NodePath } from '@babel/core';
7+
import { isCallStatementPath, name } from './babel-type-helpers';
48

59
const SUPPORTED_MACROS = ['assert', 'deprecate', 'warn', 'log'];
10+
type SupportedMacro = 'assert' | 'deprecate' | 'warn' | 'log';
611

712
export default class Macros {
8-
private debugHelpers: NormalizedOptions["externalizeHelpers"];
9-
private localDebugBindings: unknown[] = [];
13+
private debugHelpers: NormalizedOptions['externalizeHelpers'];
14+
private localDebugBindings: NodePath<t.Identifier>[] = [];
1015
private builder: Builder;
1116

1217
constructor(babel: typeof Babel, options: NormalizedOptions) {
@@ -23,18 +28,25 @@ export default class Macros {
2328
* Injects the either the env-flags module with the debug binding or
2429
* adds the debug binding if missing from the env-flags module.
2530
*/
26-
expand(path) {
31+
expand() {
2732
this.builder.expandMacros();
2833

29-
this._cleanImports(path);
34+
this._cleanImports();
3035
}
3136

3237
/**
3338
* Collects the import bindings for the debug tools.
3439
*/
35-
collectDebugToolsSpecifiers(specifiers) {
40+
collectDebugToolsSpecifiers(
41+
specifiers: NodePath<
42+
t.ImportSpecifier | t.ImportDefaultSpecifier | t.ImportNamespaceSpecifier
43+
>[]
44+
) {
3645
specifiers.forEach((specifier) => {
37-
if (specifier.node.imported && SUPPORTED_MACROS.indexOf(specifier.node.imported.name) > -1) {
46+
if (
47+
specifier.node.type === 'ImportSpecifier' &&
48+
SUPPORTED_MACROS.indexOf(name(specifier.node.imported)) > -1
49+
) {
3850
this.localDebugBindings.push(specifier.get('local'));
3951
}
4052
});
@@ -43,34 +55,34 @@ export default class Macros {
4355
/**
4456
* Builds the expressions that the CallExpression will expand into.
4557
*/
46-
build(path) {
47-
let expression = path.node.expression;
48-
49-
if (
50-
this.builder.t.isCallExpression(expression) &&
51-
this.localDebugBindings.some((b) => b.node.name === expression.callee.name)
52-
) {
53-
let imported = path.scope.getBinding(expression.callee.name).path.node.imported.name;
58+
build(path: NodePath<t.ExpressionStatement>) {
59+
if (!isCallStatementPath(path)) {
60+
return;
61+
}
62+
if (this.localDebugBindings.some((b) => b.node.name === path.node.expression.callee.name)) {
63+
let imported = name(
64+
(path.scope.getBinding(path.node.expression.callee.name)!.path.node as t.ImportSpecifier).imported
65+
) as SupportedMacro;
5466
this.builder[`${imported}`](path);
5567
}
5668
}
5769

5870
_cleanImports() {
5971
if (!this.debugHelpers?.module) {
6072
if (this.localDebugBindings.length > 0) {
61-
let importPath = this.localDebugBindings[0].findParent((p) => p.isImportDeclaration());
73+
let importPath = this.localDebugBindings[0].findParent((p) => p.isImportDeclaration()) as NodePath<t.ImportDeclaration> | null;
6274
if (importPath === null) {
6375
// import declaration in question seems to have already been removed
6476
return;
6577
}
6678
let specifiers = importPath.get('specifiers');
6779

6880
if (specifiers.length === this.localDebugBindings.length) {
69-
this.localDebugBindings[0].parentPath.parentPath.remove();
81+
this.localDebugBindings[0].parentPath.parentPath!.remove();
7082
} else {
7183
this.localDebugBindings.forEach((binding) => binding.parentPath.remove());
7284
}
7385
}
7486
}
7587
}
76-
};
88+
}

0 commit comments

Comments
 (0)