@angular-modernizer/api 0.1.3 → 0.2.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.
Files changed (31) hide show
  1. package/dist/index.d.ts +4 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +3 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/investigation/call-graph-builder.d.ts +95 -8
  6. package/dist/investigation/call-graph-builder.d.ts.map +1 -1
  7. package/dist/investigation/call-graph-builder.js +424 -81
  8. package/dist/investigation/call-graph-builder.js.map +1 -1
  9. package/dist/investigation/codebase-searcher.d.ts +68 -0
  10. package/dist/investigation/codebase-searcher.d.ts.map +1 -1
  11. package/dist/investigation/codebase-searcher.js +154 -10
  12. package/dist/investigation/codebase-searcher.js.map +1 -1
  13. package/dist/investigation/template-usage-finder.d.ts +64 -0
  14. package/dist/investigation/template-usage-finder.d.ts.map +1 -0
  15. package/dist/investigation/template-usage-finder.js +279 -0
  16. package/dist/investigation/template-usage-finder.js.map +1 -0
  17. package/dist/investigation/template-usage-scanner.d.ts +69 -0
  18. package/dist/investigation/template-usage-scanner.d.ts.map +1 -0
  19. package/dist/investigation/template-usage-scanner.js +375 -0
  20. package/dist/investigation/template-usage-scanner.js.map +1 -0
  21. package/dist/investigation/usage-finder.d.ts +51 -5
  22. package/dist/investigation/usage-finder.d.ts.map +1 -1
  23. package/dist/investigation/usage-finder.js +129 -36
  24. package/dist/investigation/usage-finder.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/index.ts +16 -0
  27. package/src/investigation/call-graph-builder.ts +528 -103
  28. package/src/investigation/codebase-searcher.ts +240 -11
  29. package/src/investigation/template-usage-finder.ts +389 -0
  30. package/src/investigation/template-usage-scanner.ts +529 -0
  31. package/src/investigation/usage-finder.ts +194 -54
@@ -0,0 +1,279 @@
1
+ /**
2
+ * @angular-modernizer/api - Template Usage Finder
3
+ *
4
+ * Project-level companion to `scanTemplate`: resolves what a symbol means
5
+ * in Angular templates (component/directive selector, pipe name, class
6
+ * member, input/output binding) and scans every component template
7
+ * (external `templateUrl` files and inline `template:` literals).
8
+ *
9
+ * @remarks
10
+ * Member hits are `verified` when they read the member from the component
11
+ * context (`foo`, `this.foo`) inside the template of the declaring class or
12
+ * one of its subclasses. Same-name reads elsewhere (other components, or
13
+ * `x.foo`) are reported as `template-unverified`: they may or may not refer
14
+ * to the searched member.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const finder = new TemplateUsageFinder(project);
19
+ * const usages = finder.findTemplateUsages('UserCardComponent', declarations);
20
+ * // [{ file: '/src/app/list.component.html', usageType: 'template', templateKind: 'element', ... }]
21
+ * ```
22
+ */
23
+ import { Node, } from 'ts-morph';
24
+ import { dirname, resolve } from 'node:path';
25
+ import { scanTemplate, } from './template-usage-scanner.js';
26
+ const COMPONENT_DECORATORS = new Set(['Component', 'ExportComponent']);
27
+ const DIRECTIVE_DECORATORS = new Set([
28
+ 'Component',
29
+ 'ExportComponent',
30
+ 'Directive',
31
+ ]);
32
+ const SIGNAL_BINDING_FACTORIES = new Set(['input', 'output', 'model']);
33
+ function findDecorator(cls, names) {
34
+ return cls.getDecorators().find((d) => names.has(d.getName()));
35
+ }
36
+ /** Reads a string-valued property (`selector`, `name`, `templateUrl`) from decorator metadata. */
37
+ function readDecoratorString(decorator, property) {
38
+ const arg = decorator.getArguments()[0];
39
+ if (arg === undefined || !Node.isObjectLiteralExpression(arg)) {
40
+ return undefined;
41
+ }
42
+ const prop = arg.getProperty(property);
43
+ if (prop === undefined || !Node.isPropertyAssignment(prop)) {
44
+ return undefined;
45
+ }
46
+ const init = prop.getInitializer();
47
+ if (init !== undefined &&
48
+ (Node.isStringLiteral(init) || Node.isNoSubstitutionTemplateLiteral(init))) {
49
+ return { value: init.getLiteralText(), node: init };
50
+ }
51
+ return undefined;
52
+ }
53
+ function lineInfo(text, offset) {
54
+ const before = text.slice(0, offset);
55
+ const line = before.split('\n').length;
56
+ const lineStart = before.lastIndexOf('\n') + 1;
57
+ const lineEnd = text.indexOf('\n', offset);
58
+ const snippet = text.slice(lineStart, lineEnd === -1 ? undefined : lineEnd);
59
+ return { line, col: offset - lineStart + 1, snippet: snippet.trim() };
60
+ }
61
+ /** Input/output names under which a member can be bound from a parent template. */
62
+ function bindingNamesOf(member) {
63
+ const names = [];
64
+ const name = Node.hasName(member) ? member.getName() : undefined;
65
+ if (Node.isDecoratable(member)) {
66
+ for (const decorator of member.getDecorators()) {
67
+ const decoratorName = decorator.getName();
68
+ if (decoratorName !== 'Input' && decoratorName !== 'Output') {
69
+ continue;
70
+ }
71
+ const alias = decorator.getArguments()[0];
72
+ const aliasText = alias !== undefined && Node.isStringLiteral(alias)
73
+ ? alias.getLiteralText()
74
+ : undefined;
75
+ names.push(aliasText ?? name ?? '');
76
+ }
77
+ }
78
+ if (Node.isPropertyDeclaration(member)) {
79
+ const init = member.getInitializer();
80
+ const callee = init !== undefined && Node.isCallExpression(init)
81
+ ? init.getExpression().getText().split('.')[0]
82
+ : undefined;
83
+ if (callee !== undefined && SIGNAL_BINDING_FACTORIES.has(callee)) {
84
+ names.push(name ?? '');
85
+ if (callee === 'model' && name !== undefined) {
86
+ names.push(`${name}Change`);
87
+ }
88
+ }
89
+ }
90
+ return names.filter((n) => n !== '');
91
+ }
92
+ function inheritsFrom(cls, owners) {
93
+ const seen = new Set();
94
+ let current = cls;
95
+ while (current !== undefined && !seen.has(current)) {
96
+ if (owners.has(current)) {
97
+ return true;
98
+ }
99
+ seen.add(current);
100
+ current = current.getBaseClass();
101
+ }
102
+ return false;
103
+ }
104
+ /**
105
+ * Finds usages of a symbol inside Angular component templates.
106
+ */
107
+ export class TemplateUsageFinder {
108
+ project;
109
+ constructor(project) {
110
+ this.project = project;
111
+ }
112
+ /**
113
+ * @param symbol - Searched name (class, member, or pipe class name).
114
+ * @param declarations - Declaration nodes named `symbol` (as found by `UsageFinder`).
115
+ * @returns Template usages, unsorted.
116
+ */
117
+ findTemplateUsages(symbol, declarations) {
118
+ const selectorTargets = [];
119
+ const pipeNames = [];
120
+ const members = [];
121
+ for (const decl of declarations) {
122
+ if (Node.isClassDeclaration(decl)) {
123
+ const directive = findDecorator(decl, DIRECTIVE_DECORATORS);
124
+ const selector = directive === undefined
125
+ ? undefined
126
+ : readDecoratorString(directive, 'selector')?.value;
127
+ if (selector !== undefined) {
128
+ selectorTargets.push(selector);
129
+ }
130
+ const pipe = findDecorator(decl, new Set(['Pipe']));
131
+ const pipeName = pipe === undefined ? undefined : readDecoratorString(pipe, 'name')?.value;
132
+ if (pipeName !== undefined) {
133
+ pipeNames.push(pipeName);
134
+ }
135
+ continue;
136
+ }
137
+ // Parameter properties live in the constructor; other members directly in the class.
138
+ const ownerClass = Node.isParameterDeclaration(decl)
139
+ ? decl.getParent().getParent()
140
+ : decl.getParent();
141
+ const isMember = ownerClass !== undefined &&
142
+ Node.isClassDeclaration(ownerClass) &&
143
+ (Node.isMethodDeclaration(decl) ||
144
+ Node.isPropertyDeclaration(decl) ||
145
+ Node.isGetAccessorDeclaration(decl) ||
146
+ Node.isSetAccessorDeclaration(decl) ||
147
+ (Node.isParameterDeclaration(decl) && decl.isParameterProperty()));
148
+ if (isMember && Node.isClassDeclaration(ownerClass)) {
149
+ members.push({ owner: ownerClass, bindingNames: bindingNamesOf(decl) });
150
+ }
151
+ }
152
+ const hasClassTarget = selectorTargets.length > 0 || pipeNames.length > 0;
153
+ const scanMembers = members.length > 0 || declarations.length === 0;
154
+ if (!hasClassTarget && !scanMembers) {
155
+ return [];
156
+ }
157
+ const queries = this.buildQueries(symbol, selectorTargets, pipeNames, members, scanMembers);
158
+ const memberOwners = new Set(members.map((m) => m.owner));
159
+ const usages = [];
160
+ for (const template of this.collectTemplates()) {
161
+ for (const query of queries) {
162
+ for (const hit of scanTemplate(template.content, query)) {
163
+ usages.push(this.toUsage(template, hit, memberOwners));
164
+ }
165
+ }
166
+ }
167
+ return usages;
168
+ }
169
+ buildQueries(symbol, selectors, pipeNames, members, scanMembers) {
170
+ const queries = [];
171
+ for (const selector of selectors) {
172
+ queries.push({ selector });
173
+ }
174
+ for (const pipeName of pipeNames) {
175
+ queries.push({ pipeName });
176
+ }
177
+ if (scanMembers) {
178
+ queries.push({ memberName: symbol });
179
+ }
180
+ for (const member of members) {
181
+ const directive = findDecorator(member.owner, DIRECTIVE_DECORATORS);
182
+ const bindingSelector = directive === undefined
183
+ ? undefined
184
+ : readDecoratorString(directive, 'selector')?.value;
185
+ if (bindingSelector === undefined) {
186
+ continue;
187
+ }
188
+ for (const bindingName of member.bindingNames) {
189
+ queries.push({ bindingName, bindingSelector });
190
+ }
191
+ }
192
+ return queries;
193
+ }
194
+ toUsage(template, hit, memberOwners) {
195
+ const position = template.locate(hit.offset);
196
+ const isMemberHit = hit.implicitReceiver !== undefined;
197
+ const verified = !isMemberHit ||
198
+ (hit.implicitReceiver === true && inheritsFrom(template.owner, memberOwners));
199
+ const component = template.owner.getName() ?? '<anonymous>';
200
+ return {
201
+ file: template.file,
202
+ line: position.line,
203
+ col: position.col,
204
+ snippet: position.snippet,
205
+ templateKind: hit.kind,
206
+ templateMatch: verified ? 'verified' : 'template-unverified',
207
+ component,
208
+ ...(verified
209
+ ? {}
210
+ : {
211
+ note: hit.implicitReceiver === true
212
+ ? `Same-name member used in the template of ${component}, which does not declare or inherit the searched member.`
213
+ : `Same-name property accessed on another object ('x.${hit.name}') in the template of ${component}; receiver type not checked.`,
214
+ }),
215
+ };
216
+ }
217
+ /** All component templates in the project (inline and external). */
218
+ collectTemplates() {
219
+ const templates = [];
220
+ for (const sf of this.project.getSourceFiles()) {
221
+ if (sf.isDeclarationFile() || sf.isInNodeModules()) {
222
+ continue;
223
+ }
224
+ for (const cls of sf.getClasses()) {
225
+ const component = findDecorator(cls, COMPONENT_DECORATORS);
226
+ if (component === undefined) {
227
+ continue;
228
+ }
229
+ const template = this.loadTemplate(sf, cls, component);
230
+ if (template !== undefined) {
231
+ templates.push(template);
232
+ }
233
+ }
234
+ }
235
+ return templates;
236
+ }
237
+ loadTemplate(sf, owner, decorator) {
238
+ const inline = readDecoratorString(decorator, 'template');
239
+ if (inline !== undefined) {
240
+ // Raw text between the delimiters keeps offsets aligned with the .ts file.
241
+ const literalStart = inline.node.getStart() + 1;
242
+ const content = inline.node.getText().slice(1, -1);
243
+ return {
244
+ owner,
245
+ file: sf.getFilePath(),
246
+ content,
247
+ locate: (offset) => {
248
+ const pos = literalStart + offset;
249
+ const { line, column } = sf.getLineAndColumnAtPos(pos);
250
+ const snippet = sf.getFullText().split('\n')[line - 1] ?? '';
251
+ return { line, col: column, snippet: snippet.trim() };
252
+ },
253
+ };
254
+ }
255
+ const templateUrl = readDecoratorString(decorator, 'templateUrl');
256
+ if (templateUrl === undefined) {
257
+ return undefined;
258
+ }
259
+ const file = resolve(dirname(sf.getFilePath()), templateUrl.value).replaceAll('\\', '/');
260
+ const fs = this.project.getFileSystem();
261
+ let content;
262
+ try {
263
+ if (!fs.fileExistsSync(file)) {
264
+ return undefined;
265
+ }
266
+ content = fs.readFileSync(file);
267
+ }
268
+ catch {
269
+ return undefined;
270
+ }
271
+ return {
272
+ owner,
273
+ file,
274
+ content,
275
+ locate: (offset) => lineInfo(content, offset),
276
+ };
277
+ }
278
+ }
279
+ //# sourceMappingURL=template-usage-finder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-usage-finder.js","sourceRoot":"","sources":["../../src/investigation/template-usage-finder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @angular-modernizer/api - Template Usage Scanner
3
+ *
4
+ * Parses a single Angular template with `@angular/compiler` and reports
5
+ * where a selector, pipe, class member, or input/output binding is used.
6
+ * Positions are returned as offsets into the template text so callers can
7
+ * map them to external `.html` files or to inline `template:` literals.
8
+ *
9
+ * @remarks
10
+ * Pure function over template text; it knows nothing about ts-morph. The
11
+ * project-level lookup (which templates belong to which class, selector and
12
+ * pipe resolution) lives in `TemplateUsageFinder`.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const hits = scanTemplate('<app-user (saved)="onSave()"></app-user>', {
17
+ * selector: 'app-user',
18
+ * memberName: 'onSave',
19
+ * });
20
+ * // [{ kind: 'element', ... }, { kind: 'method-call', implicitReceiver: true, ... }]
21
+ * ```
22
+ */
23
+ /** How a template refers to the searched symbol. */
24
+ export type TemplateUsageKind = 'element' | 'attribute' | 'pipe' | 'property-read' | 'property-write' | 'method-call' | 'input-binding' | 'output-binding';
25
+ /** What to look for in a template. All fields are optional and combinable. */
26
+ export interface TemplateQuery {
27
+ /** Component/directive CSS selector (may be a selector list). */
28
+ selector?: string;
29
+ /** Pipe name as used in templates (`{{ x | name }}`). */
30
+ pipeName?: string;
31
+ /** Class member name read, written, or called in expressions. */
32
+ memberName?: string;
33
+ /** Input/output name bound as `[name]`, `name="..."` or `(name)`. */
34
+ bindingName?: string;
35
+ /**
36
+ * Restricts `bindingName` matches to elements matching this selector.
37
+ * When omitted, bindings on any element match.
38
+ */
39
+ bindingSelector?: string;
40
+ }
41
+ /** A single match inside a template. */
42
+ export interface TemplateHit {
43
+ /** 0-based offset into the template text. */
44
+ offset: number;
45
+ /** Kind of usage. */
46
+ kind: TemplateUsageKind;
47
+ /** Matched name (selector, pipe, member, or binding name). */
48
+ name: string;
49
+ /**
50
+ * For member hits: `true` when the expression reads the member from the
51
+ * component context (`foo`, `this.foo`), `false` for `x.foo`.
52
+ */
53
+ implicitReceiver?: boolean;
54
+ }
55
+ /**
56
+ * Cheap pre-check: can the template possibly contain a hit for `query`?
57
+ * Avoids parsing templates that do not mention any searched token.
58
+ */
59
+ export declare function templateMayMatch(content: string, query: TemplateQuery): boolean;
60
+ /**
61
+ * Scans one template for usages described by `query`.
62
+ *
63
+ * @param content - Raw template text.
64
+ * @param query - What to look for.
65
+ * @returns Hits ordered by offset. Returns `[]` when the template cannot be
66
+ * parsed at all.
67
+ */
68
+ export declare function scanTemplate(content: string, query: TemplateQuery): TemplateHit[];
69
+ //# sourceMappingURL=template-usage-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-usage-scanner.d.ts","sourceRoot":"","sources":["../../src/investigation/template-usage-scanner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AA+BH,oDAAoD;AACpD,MAAM,MAAM,iBAAiB,GACzB,SAAS,GACT,WAAW,GACX,MAAM,GACN,eAAe,GACf,gBAAgB,GAChB,aAAa,GACb,eAAe,GACf,gBAAgB,CAAC;AAErB,8EAA8E;AAC9E,MAAM,WAAW,aAAa;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,qEAAqE;IACrE,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wCAAwC;AACxC,MAAM,WAAW,WAAW;IAC1B,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IAEf,qBAAqB;IACrB,IAAI,EAAE,iBAAiB,CAAC;IAExB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAmWD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CA8B/E;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,WAAW,EAAE,CA6BjF"}