@mnemonica/tactica 0.1.15 → 0.3.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/README.md +165 -5
- package/lib/analyzer.d.ts +59 -2
- package/lib/analyzer.js +346 -21
- package/lib/cli.d.ts +4 -1
- package/lib/cli.js +222 -7
- package/lib/creation-graph.d.ts +131 -0
- package/lib/creation-graph.js +568 -0
- package/lib/index.d.ts +8 -2
- package/lib/index.js +11 -3
- package/lib/module-graph.d.ts +109 -0
- package/lib/module-graph.js +690 -0
- package/lib/plugins.d.ts +44 -0
- package/lib/plugins.js +27 -0
- package/lib/scopes.d.ts +113 -0
- package/lib/scopes.js +582 -0
- package/lib/topologica-analyzer.js +13 -10
- package/lib/types.d.ts +320 -0
- package/lib/types.js +1 -1
- package/lib/writer.d.ts +14 -1
- package/lib/writer.js +61 -1
- package/package.json +7 -5
package/lib/plugins.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { InstrumentationKind } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Tactica plugin: supplies the framework vocabulary the analyzer matches
|
|
4
|
+
* syntactically. Tactica core is framework-blind — with no plugins loaded
|
|
5
|
+
* it detects no instrumentation points at all. Framework adapters (e.g. a
|
|
6
|
+
* server-framework integration package) ship a plugin; projects enable it
|
|
7
|
+
* through a `.tactica.js` / `tactica.config.js` config file.
|
|
8
|
+
*
|
|
9
|
+
* All keys are matched by identifier TEXT only (no import resolution, no
|
|
10
|
+
* type checker), so a plugin entry fires on any same-named identifier.
|
|
11
|
+
*/
|
|
12
|
+
export interface TacticaPlugin {
|
|
13
|
+
/** Plugin name, used in verbose logging */
|
|
14
|
+
name?: string;
|
|
15
|
+
/** Heritage interface identifier -> kind (`implements X`) */
|
|
16
|
+
instrumentationInterfaces?: Record<string, InstrumentationKind>;
|
|
17
|
+
/** Decorator identifier -> kind (`@X(Impl)` on classes/methods) */
|
|
18
|
+
useDecorators?: Record<string, InstrumentationKind>;
|
|
19
|
+
/** Provider token identifier -> kind (`{ provide: TOKEN, useClass: X }`) */
|
|
20
|
+
appTokens?: Record<string, InstrumentationKind>;
|
|
21
|
+
/**
|
|
22
|
+
* Enable middleware wiring detection: `consumer.apply(Mw).forRoutes(...)`
|
|
23
|
+
* inside a `configure()` method. Shape-based (no vocabulary keys), so it
|
|
24
|
+
* is opt-in per plugin.
|
|
25
|
+
*/
|
|
26
|
+
middlewareWiring?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The analyzer's merged view of all loaded plugins: one lookup table per
|
|
30
|
+
* detection channel, plus the middleware-wiring switch.
|
|
31
|
+
*/
|
|
32
|
+
export interface InstrumentationVocabulary {
|
|
33
|
+
interfaces: Record<string, InstrumentationKind>;
|
|
34
|
+
useDecorators: Record<string, InstrumentationKind>;
|
|
35
|
+
appTokens: Record<string, InstrumentationKind>;
|
|
36
|
+
middlewareWiring: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Merge plugins into one vocabulary. Later plugins override earlier ones on
|
|
40
|
+
* the same identifier key; middlewareWiring is an OR (any plugin enables it).
|
|
41
|
+
* An empty plugin list yields an empty vocabulary — the analyzer then
|
|
42
|
+
* collects zero instrumentation points.
|
|
43
|
+
*/
|
|
44
|
+
export declare function mergeTacticaPlugins(plugins: TacticaPlugin[]): InstrumentationVocabulary;
|
package/lib/plugins.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mergeTacticaPlugins = mergeTacticaPlugins;
|
|
4
|
+
/**
|
|
5
|
+
* Merge plugins into one vocabulary. Later plugins override earlier ones on
|
|
6
|
+
* the same identifier key; middlewareWiring is an OR (any plugin enables it).
|
|
7
|
+
* An empty plugin list yields an empty vocabulary — the analyzer then
|
|
8
|
+
* collects zero instrumentation points.
|
|
9
|
+
*/
|
|
10
|
+
function mergeTacticaPlugins(plugins) {
|
|
11
|
+
const merged = {
|
|
12
|
+
interfaces: {},
|
|
13
|
+
useDecorators: {},
|
|
14
|
+
appTokens: {},
|
|
15
|
+
middlewareWiring: false,
|
|
16
|
+
};
|
|
17
|
+
for (const plugin of plugins) {
|
|
18
|
+
Object.assign(merged.interfaces, plugin.instrumentationInterfaces);
|
|
19
|
+
Object.assign(merged.useDecorators, plugin.useDecorators);
|
|
20
|
+
Object.assign(merged.appTokens, plugin.appTokens);
|
|
21
|
+
if (plugin.middlewareWiring) {
|
|
22
|
+
merged.middlewareWiring = true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return merged;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9wbHVnaW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLFlBQVksQ0FBQzs7QUFnRGIsa0RBZ0JDO0FBdEJEOzs7OztHQUtHO0FBQ0gsU0FBZ0IsbUJBQW1CLENBQUUsT0FBd0I7SUFDNUQsTUFBTSxNQUFNLEdBQThCO1FBQ3pDLFVBQVUsRUFBUyxFQUFFO1FBQ3JCLGFBQWEsRUFBTSxFQUFFO1FBQ3JCLFNBQVMsRUFBVSxFQUFFO1FBQ3JCLGdCQUFnQixFQUFHLEtBQUs7S0FDeEIsQ0FBQztJQUNGLEtBQUssTUFBTSxNQUFNLElBQUksT0FBTyxFQUFFLENBQUM7UUFDOUIsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsVUFBVSxFQUFFLE1BQU0sQ0FBQyx5QkFBeUIsQ0FBQyxDQUFDO1FBQ25FLE1BQU0sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLGFBQWEsRUFBRSxNQUFNLENBQUMsYUFBYSxDQUFDLENBQUM7UUFDMUQsTUFBTSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsU0FBUyxFQUFFLE1BQU0sQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUNsRCxJQUFJLE1BQU0sQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBQzdCLE1BQU0sQ0FBQyxnQkFBZ0IsR0FBRyxJQUFJLENBQUM7UUFDaEMsQ0FBQztJQUNGLENBQUM7SUFDRCxPQUFPLE1BQU0sQ0FBQztBQUNmLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCB7IEluc3RydW1lbnRhdGlvbktpbmQgfSBmcm9tICcuL3R5cGVzJztcblxuLyoqXG4gKiBUYWN0aWNhIHBsdWdpbjogc3VwcGxpZXMgdGhlIGZyYW1ld29yayB2b2NhYnVsYXJ5IHRoZSBhbmFseXplciBtYXRjaGVzXG4gKiBzeW50YWN0aWNhbGx5LiBUYWN0aWNhIGNvcmUgaXMgZnJhbWV3b3JrLWJsaW5kIOKAlCB3aXRoIG5vIHBsdWdpbnMgbG9hZGVkXG4gKiBpdCBkZXRlY3RzIG5vIGluc3RydW1lbnRhdGlvbiBwb2ludHMgYXQgYWxsLiBGcmFtZXdvcmsgYWRhcHRlcnMgKGUuZy4gYVxuICogc2VydmVyLWZyYW1ld29yayBpbnRlZ3JhdGlvbiBwYWNrYWdlKSBzaGlwIGEgcGx1Z2luOyBwcm9qZWN0cyBlbmFibGUgaXRcbiAqIHRocm91Z2ggYSBgLnRhY3RpY2EuanNgIC8gYHRhY3RpY2EuY29uZmlnLmpzYCBjb25maWcgZmlsZS5cbiAqXG4gKiBBbGwga2V5cyBhcmUgbWF0Y2hlZCBieSBpZGVudGlmaWVyIFRFWFQgb25seSAobm8gaW1wb3J0IHJlc29sdXRpb24sIG5vXG4gKiB0eXBlIGNoZWNrZXIpLCBzbyBhIHBsdWdpbiBlbnRyeSBmaXJlcyBvbiBhbnkgc2FtZS1uYW1lZCBpZGVudGlmaWVyLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFRhY3RpY2FQbHVnaW4ge1xuXHQvKiogUGx1Z2luIG5hbWUsIHVzZWQgaW4gdmVyYm9zZSBsb2dnaW5nICovXG5cdG5hbWU/OiBzdHJpbmc7XG5cdC8qKiBIZXJpdGFnZSBpbnRlcmZhY2UgaWRlbnRpZmllciAtPiBraW5kIChgaW1wbGVtZW50cyBYYCkgKi9cblx0aW5zdHJ1bWVudGF0aW9uSW50ZXJmYWNlcz86IFJlY29yZDxzdHJpbmcsIEluc3RydW1lbnRhdGlvbktpbmQ+O1xuXHQvKiogRGVjb3JhdG9yIGlkZW50aWZpZXIgLT4ga2luZCAoYEBYKEltcGwpYCBvbiBjbGFzc2VzL21ldGhvZHMpICovXG5cdHVzZURlY29yYXRvcnM/OiBSZWNvcmQ8c3RyaW5nLCBJbnN0cnVtZW50YXRpb25LaW5kPjtcblx0LyoqIFByb3ZpZGVyIHRva2VuIGlkZW50aWZpZXIgLT4ga2luZCAoYHsgcHJvdmlkZTogVE9LRU4sIHVzZUNsYXNzOiBYIH1gKSAqL1xuXHRhcHBUb2tlbnM/OiBSZWNvcmQ8c3RyaW5nLCBJbnN0cnVtZW50YXRpb25LaW5kPjtcblx0LyoqXG5cdCAqIEVuYWJsZSBtaWRkbGV3YXJlIHdpcmluZyBkZXRlY3Rpb246IGBjb25zdW1lci5hcHBseShNdykuZm9yUm91dGVzKC4uLilgXG5cdCAqIGluc2lkZSBhIGBjb25maWd1cmUoKWAgbWV0aG9kLiBTaGFwZS1iYXNlZCAobm8gdm9jYWJ1bGFyeSBrZXlzKSwgc28gaXRcblx0ICogaXMgb3B0LWluIHBlciBwbHVnaW4uXG5cdCAqL1xuXHRtaWRkbGV3YXJlV2lyaW5nPzogYm9vbGVhbjtcbn1cblxuLyoqXG4gKiBUaGUgYW5hbHl6ZXIncyBtZXJnZWQgdmlldyBvZiBhbGwgbG9hZGVkIHBsdWdpbnM6IG9uZSBsb29rdXAgdGFibGUgcGVyXG4gKiBkZXRlY3Rpb24gY2hhbm5lbCwgcGx1cyB0aGUgbWlkZGxld2FyZS13aXJpbmcgc3dpdGNoLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEluc3RydW1lbnRhdGlvblZvY2FidWxhcnkge1xuXHRpbnRlcmZhY2VzOiBSZWNvcmQ8c3RyaW5nLCBJbnN0cnVtZW50YXRpb25LaW5kPjtcblx0dXNlRGVjb3JhdG9yczogUmVjb3JkPHN0cmluZywgSW5zdHJ1bWVudGF0aW9uS2luZD47XG5cdGFwcFRva2VuczogUmVjb3JkPHN0cmluZywgSW5zdHJ1bWVudGF0aW9uS2luZD47XG5cdG1pZGRsZXdhcmVXaXJpbmc6IGJvb2xlYW47XG59XG5cbi8qKlxuICogTWVyZ2UgcGx1Z2lucyBpbnRvIG9uZSB2b2NhYnVsYXJ5LiBMYXRlciBwbHVnaW5zIG92ZXJyaWRlIGVhcmxpZXIgb25lcyBvblxuICogdGhlIHNhbWUgaWRlbnRpZmllciBrZXk7IG1pZGRsZXdhcmVXaXJpbmcgaXMgYW4gT1IgKGFueSBwbHVnaW4gZW5hYmxlcyBpdCkuXG4gKiBBbiBlbXB0eSBwbHVnaW4gbGlzdCB5aWVsZHMgYW4gZW1wdHkgdm9jYWJ1bGFyeSDigJQgdGhlIGFuYWx5emVyIHRoZW5cbiAqIGNvbGxlY3RzIHplcm8gaW5zdHJ1bWVudGF0aW9uIHBvaW50cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIG1lcmdlVGFjdGljYVBsdWdpbnMgKHBsdWdpbnM6IFRhY3RpY2FQbHVnaW5bXSk6IEluc3RydW1lbnRhdGlvblZvY2FidWxhcnkge1xuXHRjb25zdCBtZXJnZWQ6IEluc3RydW1lbnRhdGlvblZvY2FidWxhcnkgPSB7XG5cdFx0aW50ZXJmYWNlcyAgICAgICA6IHt9LFxuXHRcdHVzZURlY29yYXRvcnMgICAgOiB7fSxcblx0XHRhcHBUb2tlbnMgICAgICAgIDoge30sXG5cdFx0bWlkZGxld2FyZVdpcmluZyA6IGZhbHNlLFxuXHR9O1xuXHRmb3IgKGNvbnN0IHBsdWdpbiBvZiBwbHVnaW5zKSB7XG5cdFx0T2JqZWN0LmFzc2lnbihtZXJnZWQuaW50ZXJmYWNlcywgcGx1Z2luLmluc3RydW1lbnRhdGlvbkludGVyZmFjZXMpO1xuXHRcdE9iamVjdC5hc3NpZ24obWVyZ2VkLnVzZURlY29yYXRvcnMsIHBsdWdpbi51c2VEZWNvcmF0b3JzKTtcblx0XHRPYmplY3QuYXNzaWduKG1lcmdlZC5hcHBUb2tlbnMsIHBsdWdpbi5hcHBUb2tlbnMpO1xuXHRcdGlmIChwbHVnaW4ubWlkZGxld2FyZVdpcmluZykge1xuXHRcdFx0bWVyZ2VkLm1pZGRsZXdhcmVXaXJpbmcgPSB0cnVlO1xuXHRcdH1cblx0fVxuXHRyZXR1cm4gbWVyZ2VkO1xufVxuIl19
|
package/lib/scopes.d.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import { ScopeAnalysis, UsageInfo } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Compile-time view over the analyzer's definitions, used to attach mnemonica
|
|
5
|
+
* type paths to variables. Name-based heuristics only — the no-getTypeChecker()
|
|
6
|
+
* precedent stays.
|
|
7
|
+
*/
|
|
8
|
+
export interface ScopeTypeResolver {
|
|
9
|
+
/** Resolve a bare constructor/type name to a mnemonica fullPath (undefined when unknown or ambiguous) */
|
|
10
|
+
resolveByName(name: string): string | undefined;
|
|
11
|
+
/** True when the dotted path is a known mnemonica type */
|
|
12
|
+
hasPath(fullPath: string): boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Local-scope walker (instrumentation walker plan, Phase 2).
|
|
16
|
+
*
|
|
17
|
+
* Tracks function/method/arrow scopes ONLY (decision 5: no block scopes),
|
|
18
|
+
* plus one synthetic 'module' scope per file — the plan requires module-scope
|
|
19
|
+
* instance creations to be labeled, not dropped. Variables carry isMutable
|
|
20
|
+
* (const vs let/var/parameter) and `reassignments`: each reassignment site of
|
|
21
|
+
* a mutable binding is a flow-termination point (decision 6) — downstream the
|
|
22
|
+
* walker stops following that binding there.
|
|
23
|
+
*
|
|
24
|
+
* Usage: addFile() per source file, then build(resolver) once definitions are
|
|
25
|
+
* known. findHolderScopeId(location) maps a usage location string to the
|
|
26
|
+
* innermost scope containing it (usages.json holderScopeId).
|
|
27
|
+
*/
|
|
28
|
+
export declare class LocalScopeWalker {
|
|
29
|
+
private scopes;
|
|
30
|
+
private spans;
|
|
31
|
+
private variables;
|
|
32
|
+
private pending;
|
|
33
|
+
/**
|
|
34
|
+
* Arrow/function-expression node -> name it is bound to (`const f = () => …`,
|
|
35
|
+
* `{ handler: () => … }`, class properties). Program source files can be
|
|
36
|
+
* UNBOUND (no node.parent pointers), so binding names travel through this
|
|
37
|
+
* map instead of parent lookups.
|
|
38
|
+
*/
|
|
39
|
+
private boundNames;
|
|
40
|
+
/**
|
|
41
|
+
* Track one source file. Re-adding the same file replaces its records,
|
|
42
|
+
* so a walker may safely be reused across passes.
|
|
43
|
+
*/
|
|
44
|
+
addFile(sourceFile: ts.SourceFile): void;
|
|
45
|
+
/**
|
|
46
|
+
* Resolve pending typePaths and return the analysis.
|
|
47
|
+
*/
|
|
48
|
+
build(resolver?: ScopeTypeResolver): ScopeAnalysis;
|
|
49
|
+
/**
|
|
50
|
+
* Map a usage location string ('abs/file.ts:line:col') to the innermost
|
|
51
|
+
* scope containing it. Module scope is the fallback, so every location
|
|
52
|
+
* inside a tracked file resolves to some scope.
|
|
53
|
+
*/
|
|
54
|
+
findHolderScopeId(location: string): string | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Remove every record belonging to one file (re-add support).
|
|
57
|
+
*/
|
|
58
|
+
private dropFile;
|
|
59
|
+
/**
|
|
60
|
+
* Attach holderScopeId to every usage whose location falls inside a
|
|
61
|
+
* tracked scope. Additive on UsageInfo; usages outside tracked files
|
|
62
|
+
* are left untouched.
|
|
63
|
+
*/
|
|
64
|
+
static attachHolderScopeIds(usages: Map<string, UsageInfo[]>, walker: LocalScopeWalker): void;
|
|
65
|
+
private visitNode;
|
|
66
|
+
private static scopeKindOf;
|
|
67
|
+
private hasBody;
|
|
68
|
+
private enterScope;
|
|
69
|
+
/**
|
|
70
|
+
* Decision 8 labeling: functions by name; methods as Class.method;
|
|
71
|
+
* arrows/functions bound to a variable or property take that name;
|
|
72
|
+
* anonymous holders are labeled file:line.
|
|
73
|
+
*/
|
|
74
|
+
private scopeName;
|
|
75
|
+
private spanOf;
|
|
76
|
+
/**
|
|
77
|
+
* Record the name an arrow/function-expression is bound to, without
|
|
78
|
+
* relying on node.parent (unbound program files): `const f = () => …`,
|
|
79
|
+
* `{ handler: () => … }`, `class C { run = () => … }`.
|
|
80
|
+
*/
|
|
81
|
+
private collectBoundName;
|
|
82
|
+
private collectVariableDeclarationList;
|
|
83
|
+
private recordVariable;
|
|
84
|
+
/**
|
|
85
|
+
* `a.b.c` → ['a', 'b', 'c'] (left-to-right); undefined-safe for
|
|
86
|
+
* non-identifier roots.
|
|
87
|
+
*/
|
|
88
|
+
private static unwrapPropertyAccess;
|
|
89
|
+
/**
|
|
90
|
+
* `lookup('A.B')`, `App.lookup('A.B')`, `lookup(source, 'A.B')` → the
|
|
91
|
+
* string-literal path plus the receiver's root identifier when there is
|
|
92
|
+
* one. Only literal paths are tracked: a computed path is data the static
|
|
93
|
+
* walker cannot follow, so it is skipped (the analyzer's usage pass still
|
|
94
|
+
* records the lookup call itself).
|
|
95
|
+
*/
|
|
96
|
+
private static unwrapLookupCall;
|
|
97
|
+
/**
|
|
98
|
+
* Cheap initializer classification for inferredType. Deliberately tiny:
|
|
99
|
+
* literal kinds and `new X` constructor names; everything else undefined.
|
|
100
|
+
*/
|
|
101
|
+
private static inferInitializerKind;
|
|
102
|
+
/**
|
|
103
|
+
* Reassignment of a let/var/parameter binding: a flow-termination point
|
|
104
|
+
* (decision 6). Recorded on the variable so the Phase 3 walker stops
|
|
105
|
+
* following that binding there.
|
|
106
|
+
*/
|
|
107
|
+
private collectReassignment;
|
|
108
|
+
/**
|
|
109
|
+
* Find a variable by name walking the scope chain outward.
|
|
110
|
+
*/
|
|
111
|
+
private findVariable;
|
|
112
|
+
private resolveVariableTypePath;
|
|
113
|
+
}
|