@ng-annotate/angular 0.3.5 → 0.3.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-annotate/angular",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "schematics": "./schematics/collection.json",
5
5
  "description": "Angular library for ng-annotate-mcp — browser overlay for annotating components and routing instructions to an AI agent",
6
6
  "keywords": [
@@ -1,8 +1,68 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
3
36
  exports.default = default_1;
4
37
  const schematics_1 = require("@angular-devkit/schematics");
5
38
  const tasks_1 = require("@angular-devkit/schematics/tasks");
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ /** Insert `newImport` on the line after the last import statement (handles multi-line imports and blank lines between groups). */
42
+ function insertAfterLastImport(content, newImport) {
43
+ const lines = content.split('\n');
44
+ let lastImportLine = -1;
45
+ let inImport = false;
46
+ for (let i = 0; i < lines.length; i++) {
47
+ if (/^import\s/.test(lines[i]))
48
+ inImport = true;
49
+ if (inImport) {
50
+ lastImportLine = i;
51
+ if (lines[i].includes(';'))
52
+ inImport = false;
53
+ }
54
+ }
55
+ if (lastImportLine < 0)
56
+ return newImport + '\n' + content;
57
+ lines.splice(lastImportLine + 1, 0, newImport);
58
+ return lines.join('\n');
59
+ }
60
+ /** Insert `newProvider` as the first item in the `providers: [...]` array, preserving indentation style. */
61
+ function insertIntoProviders(content, newProvider) {
62
+ return content.replace(/^(\s*)providers\s*:\s*\[/m, (match, indent) => {
63
+ return `${indent}providers: [\n${indent} ${newProvider},`;
64
+ });
65
+ }
6
66
  const MIN_ANGULAR_MAJOR = 21;
7
67
  function checkAngularVersion() {
8
68
  return (tree) => {
@@ -34,9 +94,7 @@ function addVitePlugin() {
34
94
  context.logger.info('@ng-annotate/vite-plugin vite plugin already present, skipping.');
35
95
  return;
36
96
  }
37
- // Insert import after the last existing import line
38
- content = content.replace(/(^import .+$(\r?\n)?)+/m, (match) => match + "import { ngAnnotateMcp } from '@ng-annotate/vite-plugin';\n");
39
- // Insert spread into plugins array (handles `plugins: [` or `plugins:[`)
97
+ content = insertAfterLastImport(content, "import { ngAnnotateMcp } from '@ng-annotate/vite-plugin';");
40
98
  content = content.replace(/plugins\s*:\s*\[/, 'plugins: [...ngAnnotateMcp(), ');
41
99
  tree.overwrite(viteConfigPath, content);
42
100
  context.logger.info(`✅ Added ngAnnotateMcp() to ${viteConfigPath}`);
@@ -61,20 +119,49 @@ function addProviders() {
61
119
  context.logger.info('provideNgAnnotate already present, skipping.');
62
120
  return;
63
121
  }
64
- // Insert import after the last existing import line
65
- content = content.replace(/(^import .+$(\r?\n)?)+/m, (match) => match + "import { provideNgAnnotate } from '@ng-annotate/angular';\n");
66
- // Insert into providers array
67
- content = content.replace(/providers\s*:\s*\[/, 'providers: [\n provideNgAnnotate(),');
122
+ content = insertAfterLastImport(content, "import { provideNgAnnotate } from '@ng-annotate/angular';");
123
+ content = insertIntoProviders(content, 'provideNgAnnotate()');
68
124
  tree.overwrite(appConfigPath, content);
69
125
  context.logger.info(`✅ Added provideNgAnnotate() to ${appConfigPath}`);
70
126
  };
71
127
  }
128
+ /** Walk up from startDir until we find a .git folder; returns that directory or null. */
129
+ function findGitRoot(startDir) {
130
+ let dir = path.resolve(startDir);
131
+ while (true) {
132
+ if (fs.existsSync(path.join(dir, '.git')))
133
+ return dir;
134
+ const parent = path.dirname(dir);
135
+ if (parent === dir)
136
+ return null;
137
+ dir = parent;
138
+ }
139
+ }
140
+ /**
141
+ * Write a config file outside the schematic Tree (e.g. to a parent git root).
142
+ * Uses fs directly — intentionally bypasses Tree so it works for out-of-project paths.
143
+ */
144
+ function writeOutsideTree(absPath, content, context) {
145
+ const dir = path.dirname(absPath);
146
+ if (!fs.existsSync(dir))
147
+ fs.mkdirSync(dir, { recursive: true });
148
+ if (fs.existsSync(absPath)) {
149
+ context.logger.info(`${path.basename(absPath)} already exists at workspace root, skipping.`);
150
+ return;
151
+ }
152
+ fs.writeFileSync(absPath, content, 'utf-8');
153
+ context.logger.info(`✅ Created ${path.basename(absPath)} at workspace root (${dir})`);
154
+ }
72
155
  function addMcpConfig(options) {
73
156
  return (tree, context) => {
74
- const projectRoot = process.cwd().replace(/\\/g, '/');
75
- const env = { NG_ANNOTATE_PROJECT_ROOT: projectRoot };
157
+ const projectRoot = process.cwd();
158
+ const env = { NG_ANNOTATE_PROJECT_ROOT: projectRoot.replace(/\\/g, '/') };
76
159
  const isWindows = process.platform === 'win32';
77
160
  const { aiTool } = options;
161
+ // Detect monorepo: if git root is a parent of the Angular project, VS Code is likely
162
+ // opened there — write configs to both the project folder and the git root.
163
+ const gitRoot = findGitRoot(projectRoot);
164
+ const isSubproject = gitRoot !== null && path.resolve(gitRoot) !== path.resolve(projectRoot);
78
165
  if (aiTool === 'other') {
79
166
  const claudeConfig = JSON.stringify({
80
167
  mcpServers: {
@@ -104,41 +191,47 @@ function addMcpConfig(options) {
104
191
  }
105
192
  // .mcp.json — Claude Code (needs cmd /c on Windows to invoke npx.cmd)
106
193
  if (aiTool === 'claude-code' || aiTool === 'both') {
194
+ const mcpConfig = JSON.stringify({
195
+ mcpServers: {
196
+ 'ng-annotate': isWindows
197
+ ? { command: 'cmd', args: ['/c', 'npx', '-y', '@ng-annotate/mcp-server'], env }
198
+ : { command: 'npx', args: ['-y', '@ng-annotate/mcp-server'], env },
199
+ },
200
+ }, null, 2) + '\n';
107
201
  if (!tree.exists('.mcp.json')) {
108
- const mcpConfig = {
109
- mcpServers: {
110
- 'ng-annotate': isWindows
111
- ? { command: 'cmd', args: ['/c', 'npx', '-y', '@ng-annotate/mcp-server'], env }
112
- : { command: 'npx', args: ['-y', '@ng-annotate/mcp-server'], env },
113
- },
114
- };
115
- tree.create('.mcp.json', JSON.stringify(mcpConfig, null, 2) + '\n');
202
+ tree.create('.mcp.json', mcpConfig);
116
203
  context.logger.info('✅ Created .mcp.json');
117
204
  }
118
205
  else {
119
206
  context.logger.info('.mcp.json already exists, skipping.');
120
207
  }
208
+ if (isSubproject) {
209
+ writeOutsideTree(path.join(gitRoot, '.mcp.json'), mcpConfig, context);
210
+ }
121
211
  }
122
212
  // .vscode/mcp.json — VS Code Copilot
123
213
  if (aiTool === 'vscode' || aiTool === 'both') {
214
+ const vscodeMcpConfig = JSON.stringify({
215
+ servers: {
216
+ 'ng-annotate': {
217
+ type: 'stdio',
218
+ command: 'npx',
219
+ args: ['-y', '@ng-annotate/mcp-server'],
220
+ env,
221
+ },
222
+ },
223
+ }, null, 2) + '\n';
124
224
  const vscodeMcpPath = '.vscode/mcp.json';
125
225
  if (!tree.exists(vscodeMcpPath)) {
126
- const vscodeMcpConfig = {
127
- servers: {
128
- 'ng-annotate': {
129
- type: 'stdio',
130
- command: 'npx',
131
- args: ['-y', '@ng-annotate/mcp-server'],
132
- env,
133
- },
134
- },
135
- };
136
- tree.create(vscodeMcpPath, JSON.stringify(vscodeMcpConfig, null, 2) + '\n');
226
+ tree.create(vscodeMcpPath, vscodeMcpConfig);
137
227
  context.logger.info('✅ Created .vscode/mcp.json');
138
228
  }
139
229
  else {
140
230
  context.logger.info('.vscode/mcp.json already exists, skipping.');
141
231
  }
232
+ if (isSubproject) {
233
+ writeOutsideTree(path.join(gitRoot, '.vscode', 'mcp.json'), vscodeMcpConfig, context);
234
+ }
142
235
  }
143
236
  };
144
237
  }
@@ -1,5 +1,33 @@
1
1
  import { Rule, SchematicContext, Tree, chain, SchematicsException } from '@angular-devkit/schematics';
2
2
  import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
3
+ import * as fs from 'fs';
4
+ import * as path from 'path';
5
+
6
+ /** Insert `newImport` on the line after the last import statement (handles multi-line imports and blank lines between groups). */
7
+ function insertAfterLastImport(content: string, newImport: string): string {
8
+ const lines = content.split('\n');
9
+ let lastImportLine = -1;
10
+ let inImport = false;
11
+
12
+ for (let i = 0; i < lines.length; i++) {
13
+ if (/^import\s/.test(lines[i])) inImport = true;
14
+ if (inImport) {
15
+ lastImportLine = i;
16
+ if (lines[i].includes(';')) inImport = false;
17
+ }
18
+ }
19
+
20
+ if (lastImportLine < 0) return newImport + '\n' + content;
21
+ lines.splice(lastImportLine + 1, 0, newImport);
22
+ return lines.join('\n');
23
+ }
24
+
25
+ /** Insert `newProvider` as the first item in the `providers: [...]` array, preserving indentation style. */
26
+ function insertIntoProviders(content: string, newProvider: string): string {
27
+ return content.replace(/^(\s*)providers\s*:\s*\[/m, (match, indent) => {
28
+ return `${indent}providers: [\n${indent} ${newProvider},`;
29
+ });
30
+ }
3
31
 
4
32
  interface Options {
5
33
  aiTool: 'claude-code' | 'vscode' | 'both' | 'other';
@@ -49,13 +77,7 @@ function addVitePlugin(): Rule {
49
77
  return;
50
78
  }
51
79
 
52
- // Insert import after the last existing import line
53
- content = content.replace(
54
- /(^import .+$(\r?\n)?)+/m,
55
- (match) => match + "import { ngAnnotateMcp } from '@ng-annotate/vite-plugin';\n",
56
- );
57
-
58
- // Insert spread into plugins array (handles `plugins: [` or `plugins:[`)
80
+ content = insertAfterLastImport(content, "import { ngAnnotateMcp } from '@ng-annotate/vite-plugin';");
59
81
  content = content.replace(/plugins\s*:\s*\[/, 'plugins: [...ngAnnotateMcp(), ');
60
82
 
61
83
  tree.overwrite(viteConfigPath, content);
@@ -88,27 +110,53 @@ function addProviders(): Rule {
88
110
  return;
89
111
  }
90
112
 
91
- // Insert import after the last existing import line
92
- content = content.replace(
93
- /(^import .+$(\r?\n)?)+/m,
94
- (match) => match + "import { provideNgAnnotate } from '@ng-annotate/angular';\n",
95
- );
96
-
97
- // Insert into providers array
98
- content = content.replace(/providers\s*:\s*\[/, 'providers: [\n provideNgAnnotate(),');
113
+ content = insertAfterLastImport(content, "import { provideNgAnnotate } from '@ng-annotate/angular';");
114
+ content = insertIntoProviders(content, 'provideNgAnnotate()');
99
115
 
100
116
  tree.overwrite(appConfigPath, content);
101
117
  context.logger.info(`✅ Added provideNgAnnotate() to ${appConfigPath}`);
102
118
  };
103
119
  }
104
120
 
121
+ /** Walk up from startDir until we find a .git folder; returns that directory or null. */
122
+ function findGitRoot(startDir: string): string | null {
123
+ let dir = path.resolve(startDir);
124
+ while (true) {
125
+ if (fs.existsSync(path.join(dir, '.git'))) return dir;
126
+ const parent = path.dirname(dir);
127
+ if (parent === dir) return null;
128
+ dir = parent;
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Write a config file outside the schematic Tree (e.g. to a parent git root).
134
+ * Uses fs directly — intentionally bypasses Tree so it works for out-of-project paths.
135
+ */
136
+ function writeOutsideTree(absPath: string, content: string, context: SchematicContext): void {
137
+ const dir = path.dirname(absPath);
138
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
139
+ if (fs.existsSync(absPath)) {
140
+ context.logger.info(`${path.basename(absPath)} already exists at workspace root, skipping.`);
141
+ return;
142
+ }
143
+ fs.writeFileSync(absPath, content, 'utf-8');
144
+ context.logger.info(`✅ Created ${path.basename(absPath)} at workspace root (${dir})`);
145
+ }
146
+
105
147
  function addMcpConfig(options: Options): Rule {
106
148
  return (tree: Tree, context: SchematicContext) => {
107
- const projectRoot = process.cwd().replace(/\\/g, '/');
108
- const env = { NG_ANNOTATE_PROJECT_ROOT: projectRoot };
149
+ const projectRoot = process.cwd();
150
+ const env = { NG_ANNOTATE_PROJECT_ROOT: projectRoot.replace(/\\/g, '/') };
109
151
  const isWindows = process.platform === 'win32';
110
152
  const { aiTool } = options;
111
153
 
154
+ // Detect monorepo: if git root is a parent of the Angular project, VS Code is likely
155
+ // opened there — write configs to both the project folder and the git root.
156
+ const gitRoot = findGitRoot(projectRoot);
157
+ const isSubproject =
158
+ gitRoot !== null && path.resolve(gitRoot) !== path.resolve(projectRoot);
159
+
112
160
  if (aiTool === 'other') {
113
161
  const claudeConfig = JSON.stringify(
114
162
  {
@@ -149,40 +197,60 @@ function addMcpConfig(options: Options): Rule {
149
197
 
150
198
  // .mcp.json — Claude Code (needs cmd /c on Windows to invoke npx.cmd)
151
199
  if (aiTool === 'claude-code' || aiTool === 'both') {
152
- if (!tree.exists('.mcp.json')) {
153
- const mcpConfig = {
154
- mcpServers: {
155
- 'ng-annotate': isWindows
156
- ? { command: 'cmd', args: ['/c', 'npx', '-y', '@ng-annotate/mcp-server'], env }
157
- : { command: 'npx', args: ['-y', '@ng-annotate/mcp-server'], env },
200
+ const mcpConfig =
201
+ JSON.stringify(
202
+ {
203
+ mcpServers: {
204
+ 'ng-annotate': isWindows
205
+ ? { command: 'cmd', args: ['/c', 'npx', '-y', '@ng-annotate/mcp-server'], env }
206
+ : { command: 'npx', args: ['-y', '@ng-annotate/mcp-server'], env },
207
+ },
158
208
  },
159
- };
160
- tree.create('.mcp.json', JSON.stringify(mcpConfig, null, 2) + '\n');
209
+ null,
210
+ 2,
211
+ ) + '\n';
212
+
213
+ if (!tree.exists('.mcp.json')) {
214
+ tree.create('.mcp.json', mcpConfig);
161
215
  context.logger.info('✅ Created .mcp.json');
162
216
  } else {
163
217
  context.logger.info('.mcp.json already exists, skipping.');
164
218
  }
219
+
220
+ if (isSubproject) {
221
+ writeOutsideTree(path.join(gitRoot!, '.mcp.json'), mcpConfig, context);
222
+ }
165
223
  }
166
224
 
167
225
  // .vscode/mcp.json — VS Code Copilot
168
226
  if (aiTool === 'vscode' || aiTool === 'both') {
169
- const vscodeMcpPath = '.vscode/mcp.json';
170
- if (!tree.exists(vscodeMcpPath)) {
171
- const vscodeMcpConfig = {
172
- servers: {
173
- 'ng-annotate': {
174
- type: 'stdio',
175
- command: 'npx',
176
- args: ['-y', '@ng-annotate/mcp-server'],
177
- env,
227
+ const vscodeMcpConfig =
228
+ JSON.stringify(
229
+ {
230
+ servers: {
231
+ 'ng-annotate': {
232
+ type: 'stdio',
233
+ command: 'npx',
234
+ args: ['-y', '@ng-annotate/mcp-server'],
235
+ env,
236
+ },
178
237
  },
179
238
  },
180
- };
181
- tree.create(vscodeMcpPath, JSON.stringify(vscodeMcpConfig, null, 2) + '\n');
239
+ null,
240
+ 2,
241
+ ) + '\n';
242
+
243
+ const vscodeMcpPath = '.vscode/mcp.json';
244
+ if (!tree.exists(vscodeMcpPath)) {
245
+ tree.create(vscodeMcpPath, vscodeMcpConfig);
182
246
  context.logger.info('✅ Created .vscode/mcp.json');
183
247
  } else {
184
248
  context.logger.info('.vscode/mcp.json already exists, skipping.');
185
249
  }
250
+
251
+ if (isSubproject) {
252
+ writeOutsideTree(path.join(gitRoot!, '.vscode', 'mcp.json'), vscodeMcpConfig, context);
253
+ }
186
254
  }
187
255
  };
188
256
  }