@mnemonica/tactica 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +475 -0
- package/lib/analyzer.d.ts +75 -0
- package/lib/analyzer.js +478 -0
- package/lib/analyzer.js.map +1 -0
- package/lib/cli.d.ts +29 -0
- package/lib/cli.js +327 -0
- package/lib/cli.js.map +1 -0
- package/lib/generator.d.ts +42 -0
- package/lib/generator.js +187 -0
- package/lib/generator.js.map +1 -0
- package/lib/graph.d.ts +25 -0
- package/lib/graph.js +81 -0
- package/lib/graph.js.map +1 -0
- package/lib/index.d.ts +15 -0
- package/lib/index.js +33 -0
- package/lib/index.js.map +1 -0
- package/lib/plugin.d.ts +8 -0
- package/lib/plugin.js +128 -0
- package/lib/plugin.js.map +1 -0
- package/lib/types.d.ts +72 -0
- package/lib/types.js +3 -0
- package/lib/types.js.map +1 -0
- package/lib/writer.d.ts +40 -0
- package/lib/writer.js +139 -0
- package/lib/writer.js.map +1 -0
- package/package.json +63 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 Mythographica
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
# @mnemonica/tactica
|
|
2
|
+
|
|
3
|
+
**TypeScript Language Service Plugin for Mnemonica**
|
|
4
|
+
|
|
5
|
+
Tactica generates type definitions for Mnemonica's dynamic nested constructors, enabling TypeScript to understand runtime type hierarchies created through `define()` and `decorate()` calls.
|
|
6
|
+
|
|
7
|
+
## The Problem
|
|
8
|
+
|
|
9
|
+
Mnemonica enables powerful instance-level inheritance:
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
const UserType = define('UserType', function (this: { name: string }) {
|
|
13
|
+
this.name = '';
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const AdminType = UserType.define('AdminType', function (this: { role: string }) {
|
|
17
|
+
this.role = 'admin';
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const user = new UserType();
|
|
21
|
+
const admin = new user.AdminType(); // Works at runtime!
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
But TypeScript doesn't know that `user.AdminType` exists because `UserType.define()` is a runtime operation.
|
|
25
|
+
|
|
26
|
+
## The Solution
|
|
27
|
+
|
|
28
|
+
Tactica analyzes your TypeScript source files and generates declaration files that tell TypeScript about the nested constructor hierarchy.
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install --save-dev @mnemonica/tactica
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
### 1. As a TypeScript Language Service Plugin (Recommended)
|
|
39
|
+
|
|
40
|
+
Add to your `tsconfig.json`:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"compilerOptions": {
|
|
45
|
+
"plugins": [
|
|
46
|
+
{
|
|
47
|
+
"name": "@mnemonica/tactica",
|
|
48
|
+
"outputDir": ".tactica",
|
|
49
|
+
"include": ["src/**/*.ts"],
|
|
50
|
+
"exclude": ["**/*.test.ts", "**/*.spec.ts"]
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Then include the generated types in your project:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"compilerOptions": {
|
|
62
|
+
"typeRoots": ["./node_modules/@types", "./.tactica"]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 2. As a CLI Tool
|
|
68
|
+
|
|
69
|
+
Generate types once:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx tactica
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Watch mode for development:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx tactica --watch
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
With custom options:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
npx tactica --project ./src/tsconfig.json --output ./types/mnemonica
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 3. As a Module
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { MnemonicaAnalyzer, TypesGenerator, TypesWriter } from '@mnemonica/tactica';
|
|
91
|
+
import * as ts from 'typescript';
|
|
92
|
+
|
|
93
|
+
const program = ts.createProgram(['./src/index.ts'], {});
|
|
94
|
+
const analyzer = new MnemonicaAnalyzer(program);
|
|
95
|
+
|
|
96
|
+
for (const sourceFile of program.getSourceFiles()) {
|
|
97
|
+
if (!sourceFile.isDeclarationFile) {
|
|
98
|
+
analyzer.analyzeFile(sourceFile);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const generator = new TypesGenerator(analyzer.getGraph());
|
|
103
|
+
|
|
104
|
+
// Generate types.ts (exportable type aliases - default mode)
|
|
105
|
+
const generatedTypes = generator.generateTypesFile();
|
|
106
|
+
const writer = new TypesWriter('.tactica');
|
|
107
|
+
writer.writeTypesFile(generatedTypes);
|
|
108
|
+
|
|
109
|
+
// Or generate index.d.ts (global augmentation - legacy mode)
|
|
110
|
+
const generatedGlobal = generator.generateGlobalAugmentation();
|
|
111
|
+
writer.writeGlobalAugmentation(generatedGlobal);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Configuration Options
|
|
115
|
+
|
|
116
|
+
### Plugin Options (tsconfig.json)
|
|
117
|
+
|
|
118
|
+
| Option | Type | Default | Description |
|
|
119
|
+
|--------|------|---------|-------------|
|
|
120
|
+
| `outputDir` | string | `.tactica` | Directory for generated types |
|
|
121
|
+
| `include` | string[] | `['**/*.ts']` | File patterns to include |
|
|
122
|
+
| `exclude` | string[] | `['**/*.d.ts']` | File patterns to exclude |
|
|
123
|
+
| `verbose` | boolean | `false` | Enable verbose logging |
|
|
124
|
+
|
|
125
|
+
### CLI Options
|
|
126
|
+
|
|
127
|
+
| Option | Short | Description |
|
|
128
|
+
|--------|-------|-------------|
|
|
129
|
+
| `--watch` | `-w` | Watch mode - regenerate on file changes |
|
|
130
|
+
| `--project` | `-p` | Path to tsconfig.json |
|
|
131
|
+
| `--output` | `-o` | Output directory (default: `.tactica`) |
|
|
132
|
+
| `--include` | `-i` | Include patterns (comma-separated) |
|
|
133
|
+
| `--exclude` | `-e` | Exclude patterns (comma-separated) |
|
|
134
|
+
| `--module-augmentation` | `-m` | Generate global augmentation (legacy mode) |
|
|
135
|
+
| `--verbose` | `-v` | Enable verbose logging |
|
|
136
|
+
| `--help` | `-h` | Show help message |
|
|
137
|
+
|
|
138
|
+
**Examples:**
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# Default mode - generates .tactica/types.ts
|
|
142
|
+
npx tactica
|
|
143
|
+
|
|
144
|
+
# Global augmentation mode - generates .tactica/index.d.ts
|
|
145
|
+
npx tactica --module-augmentation
|
|
146
|
+
|
|
147
|
+
# Watch mode with custom output directory
|
|
148
|
+
npx tactica --watch --output ./custom-types
|
|
149
|
+
|
|
150
|
+
# Exclude test files
|
|
151
|
+
npx tactica --exclude "*.test.ts,*.spec.ts"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Generated Output
|
|
155
|
+
|
|
156
|
+
By default, Tactica generates `.tactica/types.ts` with exported type aliases:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// Generated by @mnemonica/tactica - DO NOT EDIT
|
|
160
|
+
export type UserTypeInstance = {
|
|
161
|
+
name: string;
|
|
162
|
+
email: string;
|
|
163
|
+
AdminType: TypeConstructor<AdminTypeInstance>;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export type AdminTypeInstance = UserTypeInstance & {
|
|
167
|
+
role: string;
|
|
168
|
+
permissions: string[];
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Output Modes
|
|
173
|
+
|
|
174
|
+
**Default mode** (`npx tactica`):
|
|
175
|
+
- Generates `.tactica/types.ts` - Exportable type aliases
|
|
176
|
+
- Import types explicitly: `import type { UserTypeInstance } from './.tactica/types'`
|
|
177
|
+
- Include in tsconfig.json: `"include": ["src/**/*.ts", ".tactica/types.ts"]`
|
|
178
|
+
- **Recommended for new projects** - explicit imports, better tree-shaking
|
|
179
|
+
|
|
180
|
+
**Global mode** (`npx tactica --module-augmentation`):
|
|
181
|
+
- Generates `.tactica/index.d.ts` - Global type declarations
|
|
182
|
+
- Types are available without imports (via `declare global`)
|
|
183
|
+
- Add to tsconfig.json: `"typeRoots": ["./node_modules/@types", "./.tactica"]`
|
|
184
|
+
- Use triple-slash reference: `/// <reference types="./.tactica/index" />`
|
|
185
|
+
|
|
186
|
+
**Choosing a mode:**
|
|
187
|
+
- Use **Default mode** for new projects - explicit imports are clearer and work better with tree-shaking
|
|
188
|
+
- Use **Global mode** if you want types available without imports (legacy behavior)
|
|
189
|
+
|
|
190
|
+
## What Gets Analyzed
|
|
191
|
+
|
|
192
|
+
### 1. define() Calls
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
// Root type
|
|
196
|
+
const UserType = define('UserType', function (this: { name: string }) {
|
|
197
|
+
this.name = '';
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// Nested type
|
|
201
|
+
const AdminType = UserType.define('AdminType', function (this: { role: string }) {
|
|
202
|
+
this.role = 'admin';
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### 2. @decorate() Decorator
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
@decorate()
|
|
210
|
+
class User {
|
|
211
|
+
name: string = '';
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
@decorate(User)
|
|
215
|
+
class Admin {
|
|
216
|
+
role: string = 'admin';
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
#### Why Type Casting is Necessary for @decorate()
|
|
221
|
+
|
|
222
|
+
When using `@decorate()` on classes, TypeScript cannot automatically infer that instances have nested type constructors (like `user.Admin`). This is because:
|
|
223
|
+
|
|
224
|
+
1. **`define()` types work automatically**: When you use `define()`, the returned constructor has the correct type signature with nested constructors.
|
|
225
|
+
|
|
226
|
+
2. **`@decorate()` classes need casting**: When you decorate a class, TypeScript sees the class itself, not the augmented type that mnemonica creates at runtime.
|
|
227
|
+
|
|
228
|
+
**The Solution:** Cast to the instance type to access nested constructors:
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
@decorate()
|
|
232
|
+
class Order {
|
|
233
|
+
orderId: string = '';
|
|
234
|
+
total: number = 0;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
@decorate(Order)
|
|
238
|
+
class AugmentedOrder {
|
|
239
|
+
addition: string = 'extra';
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Cast to OrderInstance to access AugmentedOrder constructor
|
|
243
|
+
const order = new Order() as OrderInstance;
|
|
244
|
+
const augmented = new order.AugmentedOrder(); // ✅ Works!
|
|
245
|
+
|
|
246
|
+
// The 'augmented' variable is automatically typed as AugmentedOrderInstance
|
|
247
|
+
console.log(augmented.orderId); // From Order
|
|
248
|
+
console.log(augmented.addition); // From AugmentedOrder
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Generated types (like `OrderInstance`, `AugmentedOrderInstance`) are automatically available globally - no imports needed!
|
|
252
|
+
|
|
253
|
+
#### @decorate() with Options
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
@decorate({
|
|
257
|
+
blockErrors: true,
|
|
258
|
+
strictChain: false,
|
|
259
|
+
exposeInstanceMethods: true
|
|
260
|
+
})
|
|
261
|
+
class ConfigurableClass {
|
|
262
|
+
value: string = '';
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### 3. Object.assign Pattern
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
const UserType = define('UserType', function (this: any, data: any) {
|
|
270
|
+
Object.assign(this, data);
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### 4. Typeomatica Integration
|
|
275
|
+
|
|
276
|
+
Tactica works seamlessly with Typeomatica patterns:
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import { decorate } from 'mnemonica';
|
|
280
|
+
import { Strict, BaseClass } from 'typeomatica';
|
|
281
|
+
|
|
282
|
+
// @Strict decorator alongside @decorate
|
|
283
|
+
@decorate()
|
|
284
|
+
@Strict({ someProp: 123 })
|
|
285
|
+
class StrictDecorated {
|
|
286
|
+
someProp!: number;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// BaseClass with Object.setPrototypeOf
|
|
290
|
+
@decorate()
|
|
291
|
+
class MyBaseClass {
|
|
292
|
+
base_field = 555;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
Object.setPrototypeOf(MyBaseClass.prototype, new BaseClass({ strict: true }));
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### 5. ConstructorFunction Pattern
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
import { define, ConstructorFunction } from 'mnemonica';
|
|
302
|
+
|
|
303
|
+
const MyFn = function (this: any) {
|
|
304
|
+
this.field = 123;
|
|
305
|
+
} as ConstructorFunction<{ field: number }>;
|
|
306
|
+
|
|
307
|
+
const MyFnType = define('MyFnType', MyFn);
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## CLI Features
|
|
311
|
+
|
|
312
|
+
### Tree Output
|
|
313
|
+
|
|
314
|
+
The CLI displays type hierarchy as a tree:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
$ npx tactica
|
|
318
|
+
|
|
319
|
+
Type Hierarchy (Trie):
|
|
320
|
+
└── UserTypeInstance
|
|
321
|
+
└── AdminTypeInstance
|
|
322
|
+
└── SuperAdminTypeInstance
|
|
323
|
+
└── ProductTypeInstance
|
|
324
|
+
├── DigitalProductTypeInstance
|
|
325
|
+
└── PhysicalProductTypeInstance
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Code Coverage
|
|
329
|
+
|
|
330
|
+
Run tests with coverage:
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
npm run test:coverage
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Integration with Your Workflow
|
|
337
|
+
|
|
338
|
+
### .gitignore
|
|
339
|
+
|
|
340
|
+
Tactica automatically adds `.tactica/` to your `.gitignore` if not already present.
|
|
341
|
+
|
|
342
|
+
### IDE Support
|
|
343
|
+
|
|
344
|
+
With the Language Service Plugin:
|
|
345
|
+
- **VS Code**: Automatic type updates on file save
|
|
346
|
+
- **WebStorm**: Works with TypeScript service
|
|
347
|
+
- **Vim/Neovim**: Works with coc.nvim, nvim-lspconfig
|
|
348
|
+
|
|
349
|
+
## API Reference
|
|
350
|
+
|
|
351
|
+
### MnemonicaAnalyzer
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
class MnemonicaAnalyzer {
|
|
355
|
+
constructor(program?: ts.Program);
|
|
356
|
+
analyzeFile(sourceFile: ts.SourceFile): AnalyzeResult;
|
|
357
|
+
analyzeSource(sourceCode: string, fileName?: string): AnalyzeResult;
|
|
358
|
+
getGraph(): TypeGraphImpl;
|
|
359
|
+
}
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### TypeGraphImpl
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
class TypeGraphImpl implements TypeGraph {
|
|
366
|
+
roots: Map<string, TypeNode>;
|
|
367
|
+
allTypes: Map<string, TypeNode>;
|
|
368
|
+
addRoot(node: TypeNode): void;
|
|
369
|
+
addChild(parent: TypeNode, child: TypeNode): void;
|
|
370
|
+
findType(fullPath: string): TypeNode | undefined;
|
|
371
|
+
getAllTypes(): TypeNode[];
|
|
372
|
+
*bfs(): Generator<TypeNode>;
|
|
373
|
+
*dfs(node?: TypeNode): Generator<TypeNode>;
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### TypesGenerator
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
class TypesGenerator {
|
|
381
|
+
constructor(graph: TypeGraphImpl);
|
|
382
|
+
generate(): GeneratedTypes;
|
|
383
|
+
generateSingleType(node: TypeNode): string;
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### TypesWriter
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
class TypesWriter {
|
|
391
|
+
constructor(outputDir?: string);
|
|
392
|
+
write(generated: GeneratedTypes): string;
|
|
393
|
+
writeTo(filename: string, content: string): string;
|
|
394
|
+
clean(): void;
|
|
395
|
+
getOutputDir(): string;
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
## How It Works
|
|
400
|
+
|
|
401
|
+
1. **Parse**: TypeScript AST is parsed to find `define()` and `decorate()` calls
|
|
402
|
+
2. **Analyze**: The analyzer extracts type names, properties, and hierarchy
|
|
403
|
+
3. **Graph**: A Trie (tree) structure represents the type hierarchy
|
|
404
|
+
4. **Generate**: TypeScript declarations are generated from the graph
|
|
405
|
+
5. **Output**: Files are written to `.tactica/` directory
|
|
406
|
+
|
|
407
|
+
```
|
|
408
|
+
Type Hierarchy (Trie)
|
|
409
|
+
├── UserType
|
|
410
|
+
│ ├── properties: { name: string }
|
|
411
|
+
│ └── AdminType
|
|
412
|
+
│ ├── properties: { role: string }
|
|
413
|
+
│ └── SuperAdminType
|
|
414
|
+
│ └── properties: { permissions: string[] }
|
|
415
|
+
└── OrderType
|
|
416
|
+
└── properties: { items: Item[] }
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
## Troubleshooting
|
|
420
|
+
|
|
421
|
+
### Types not updating
|
|
422
|
+
|
|
423
|
+
1. Check that the plugin is loaded in `tsconfig.json`
|
|
424
|
+
2. Restart TypeScript service (VS Code: Command Palette → "TypeScript: Restart TS Server")
|
|
425
|
+
3. Verify file patterns in `include`/`exclude` config
|
|
426
|
+
|
|
427
|
+
### Generated types have errors
|
|
428
|
+
|
|
429
|
+
1. Ensure all mnemonica types have explicit type annotations
|
|
430
|
+
2. Check that `define()` calls use string literals for type names
|
|
431
|
+
3. Verify property types are valid TypeScript
|
|
432
|
+
|
|
433
|
+
### Plugin not working
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
# Test with CLI first
|
|
437
|
+
npx tactica --verbose
|
|
438
|
+
|
|
439
|
+
# Check for parsing errors
|
|
440
|
+
npx tactica --verbose 2>&1 | grep -i error
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
## Related Projects
|
|
444
|
+
|
|
445
|
+
- [mnemonica](https://github.com/wentout/mnemonica) - Instance inheritance system
|
|
446
|
+
- [@mnemonica/topologica](https://github.com/mythographica/topologica) - Filesystem-based type discovery
|
|
447
|
+
- [typeomatica](https://github.com/wentout/typeomatica) - Runtime type checking with `@Strict` decorator and `BaseClass`
|
|
448
|
+
|
|
449
|
+
## Testing
|
|
450
|
+
|
|
451
|
+
Tactica includes comprehensive test coverage:
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
# Run all tests
|
|
455
|
+
npm test
|
|
456
|
+
|
|
457
|
+
# Run with coverage
|
|
458
|
+
npm run test:coverage
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
Test suites include:
|
|
462
|
+
- **Analyzer tests** - Core AST parsing functionality
|
|
463
|
+
- **Generator tests** - TypeScript declaration generation
|
|
464
|
+
- **Writer tests** - File I/O operations
|
|
465
|
+
- **Integration tests** - End-to-end workflows
|
|
466
|
+
- **Example tests** - Patterns from `tactica-test/` project
|
|
467
|
+
- **Typeomatica tests** - Combined mnemonica + typeomatica patterns
|
|
468
|
+
|
|
469
|
+
## License
|
|
470
|
+
|
|
471
|
+
MIT
|
|
472
|
+
|
|
473
|
+
## Contributing
|
|
474
|
+
|
|
475
|
+
Contributions welcome! Please read the [Contributing Guide](CONTRIBUTING.md) for details.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import { AnalyzeResult } from './types';
|
|
3
|
+
import { TypeGraphImpl } from './graph';
|
|
4
|
+
/**
|
|
5
|
+
* AST Analyzer for finding Mnemonica define() and decorate() calls
|
|
6
|
+
*/
|
|
7
|
+
export declare class MnemonicaAnalyzer {
|
|
8
|
+
private errors;
|
|
9
|
+
private graph;
|
|
10
|
+
constructor(program?: ts.Program);
|
|
11
|
+
/**
|
|
12
|
+
* Analyze a source file for Mnemonica type definitions
|
|
13
|
+
*/
|
|
14
|
+
analyzeFile(sourceFile: ts.SourceFile): AnalyzeResult;
|
|
15
|
+
/**
|
|
16
|
+
* Analyze source code string
|
|
17
|
+
*/
|
|
18
|
+
analyzeSource(sourceCode: string, fileName?: string): AnalyzeResult;
|
|
19
|
+
/**
|
|
20
|
+
* Get the type graph
|
|
21
|
+
*/
|
|
22
|
+
getGraph(): TypeGraphImpl;
|
|
23
|
+
/**
|
|
24
|
+
* Visit a node in the AST
|
|
25
|
+
*/
|
|
26
|
+
private visitNode;
|
|
27
|
+
/**
|
|
28
|
+
* Check if a node is a define() call
|
|
29
|
+
*/
|
|
30
|
+
private isDefineCall;
|
|
31
|
+
/**
|
|
32
|
+
* Check if a node is a @decorate() decorator
|
|
33
|
+
*/
|
|
34
|
+
private isDecorateDecorator;
|
|
35
|
+
/**
|
|
36
|
+
* Process a define() call
|
|
37
|
+
*/
|
|
38
|
+
private processDefineCall;
|
|
39
|
+
/**
|
|
40
|
+
* Process a @decorate() decorator
|
|
41
|
+
*/
|
|
42
|
+
private processDecorateDecorator;
|
|
43
|
+
/**
|
|
44
|
+
* Extract type name from define() call arguments
|
|
45
|
+
*/
|
|
46
|
+
private extractTypeName;
|
|
47
|
+
/**
|
|
48
|
+
* Find parent type node for nested define calls
|
|
49
|
+
*/
|
|
50
|
+
private findParentType;
|
|
51
|
+
/**
|
|
52
|
+
* Get property chain from nested access
|
|
53
|
+
*/
|
|
54
|
+
private getPropertyChain;
|
|
55
|
+
/**
|
|
56
|
+
* Extract properties from constructor function
|
|
57
|
+
*/
|
|
58
|
+
private extractProperties;
|
|
59
|
+
/**
|
|
60
|
+
* Extract property assignment from statement
|
|
61
|
+
*/
|
|
62
|
+
private extractPropertyFromStatement;
|
|
63
|
+
/**
|
|
64
|
+
* Extract properties from class declaration
|
|
65
|
+
*/
|
|
66
|
+
private extractClassProperties;
|
|
67
|
+
/**
|
|
68
|
+
* Infer TypeScript type from type node
|
|
69
|
+
*/
|
|
70
|
+
private inferType;
|
|
71
|
+
/**
|
|
72
|
+
* Infer type from initializer
|
|
73
|
+
*/
|
|
74
|
+
private inferTypeFromInitializer;
|
|
75
|
+
}
|