ngx-locatorjs 0.2.0 → 0.2.1

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.ko.md CHANGED
@@ -4,12 +4,14 @@
4
4
  이 프로젝트는 [locatorjs.com](https://www.locatorjs.com/)에서 영감을 받았습니다.
5
5
 
6
6
  **기능**
7
+
7
8
  - Alt+클릭: 템플릿(.html) 열기
8
9
  - Alt+Shift+클릭: 컴포넌트(.ts) 열기
9
10
  - Alt 키 홀드: 컴포넌트 하이라이트 + 툴팁 표시
10
11
  - Cursor, VS Code, WebStorm 지원
11
12
 
12
13
  **필수 단계 (1~4 반드시 수행)**
14
+
13
15
  1. 패키지 설치: `npm i -D ngx-locatorjs`
14
16
  2. 설정/프록시 생성: `npx locatorjs-config`
15
17
  3. `main.ts`에 런타임 훅 추가 (아래 예시 참고)
@@ -18,6 +20,7 @@
18
20
  `npm run start` 사용 시 `--` 뒤에 전달: `npm run start -- --proxy-config ngx-locatorjs.proxy.json`
19
21
 
20
22
  **Angular 코드 추가 (main.ts)**
23
+
21
24
  ```ts
22
25
  import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
23
26
  import { AppModule } from './app/app.module';
@@ -33,9 +36,7 @@ platformBrowserDynamic()
33
36
  .then(() => {
34
37
  if (!environment.production) {
35
38
  setTimeout(() => {
36
- import('ngx-locatorjs').then((m) =>
37
- m.installAngularLocator({ enableNetwork: true }),
38
- );
39
+ import('ngx-locatorjs').then((m) => m.installAngularLocator({ enableNetwork: true }));
39
40
  }, 1000);
40
41
  }
41
42
  })
@@ -43,6 +44,7 @@ platformBrowserDynamic()
43
44
  ```
44
45
 
45
46
  **Angular 코드 추가 (standalone: bootstrapApplication)**
47
+
46
48
  ```ts
47
49
  import { bootstrapApplication } from '@angular/platform-browser';
48
50
  import { appConfig } from './app/app.config';
@@ -52,9 +54,7 @@ bootstrapApplication(AppComponent, appConfig)
52
54
  .then(() => {
53
55
  setTimeout(() => {
54
56
  import('ngx-locatorjs')
55
- .then((m) =>
56
- m.installAngularLocator({ enableNetwork: true }),
57
- )
57
+ .then((m) => m.installAngularLocator({ enableNetwork: true }))
58
58
  .catch((err) => console.warn('[angular-locator] Failed to load:', err));
59
59
  }, 1000);
60
60
  })
@@ -62,56 +62,56 @@ bootstrapApplication(AppComponent, appConfig)
62
62
  ```
63
63
 
64
64
  **Angular dev server 예시**
65
+
65
66
  - CLI 실행
66
- `ng serve --proxy-config ngx-locatorjs.proxy.json`
67
+ `ng serve --proxy-config ngx-locatorjs.proxy.json`
67
68
 
68
69
  - angular.json에 적용
69
- `"serve"` 옵션에 `"proxyConfig": "ngx-locatorjs.proxy.json"` 추가
70
+ `"serve"` 옵션에 `"proxyConfig": "ngx-locatorjs.proxy.json"` 추가
70
71
 
71
72
  **컴포넌트 맵 스캔**
73
+
72
74
  - 수동 스캔
73
- `npx locatorjs-scan`
75
+ `npx locatorjs-scan`
74
76
 
75
77
  - 변경 감지 자동 스캔(선택)
76
- `nodemon --delay 2.5 -e ts,html -w src -w projects -w apps -w libs -x "npx locatorjs-scan"`
78
+ `nodemon --delay 2.5 -e ts,html -w src -w projects -w apps -w libs -x "npx locatorjs-scan"`
77
79
 
78
80
  **가능한 것**
81
+
79
82
  - Alt+클릭으로 템플릿 또는 컴포넌트 파일 열기 (개발 모드)
80
83
  - Alt 키 홀드 시 컴포넌트 하이라이트 및 툴팁 표시
81
84
  - 단일 Angular 앱, workspace, Nx 구조에서 동작
82
85
 
83
86
  **불가능/제한 사항**
87
+
84
88
  - SSR/SSG 환경에서는 동작하지 않음 (브라우저 DOM 기반)
85
89
 
86
90
  **ngx-locatorjs.config.json 가이드**
87
91
  파일 위치: 프로젝트 루트
88
92
 
89
93
  **중요**
94
+
90
95
  - `npx locatorjs-config`는 **실행한 현재 폴더를 기준**으로 설정합니다.
91
96
  - 기본값: `port: 4123`, `workspaceRoot: "."`.
92
97
  - 모노레포처럼 실제 Angular 앱이 하위 폴더에 있으면 `workspaceRoot`를 그 **상대 경로**로 수정하세요. (예: `apps/web`)
93
98
  - `.gitignore`가 있으면 `npx locatorjs-config`가 `.open-in-editor/`를 자동 추가합니다. 커밋하려면 해당 항목을 제거하세요.
94
99
 
95
100
  예시:
101
+
96
102
  ```json
97
103
  {
98
- "port": 4123, // 프록시 서버가 실행될 포트 주소
99
- "workspaceRoot": ".", // Angular 워크스페이스 루트
100
- "editor": "cursor", // 파일을 열 때 사용할 에디터 (`cursor`, `code`, `webstorm`)
101
- "fallbackEditor": "code", // 기본 에디터 실패 시 사용할 에디터
104
+ "port": 4123,
105
+ "workspaceRoot": ".",
106
+ "editor": "cursor",
107
+ "fallbackEditor": "code",
102
108
  "scan": {
103
- /**
104
- * 탐색할 컴포넌트의 경로 목록
105
- */
106
109
  "includeGlobs": [
107
110
  "src/**/*.{ts,tsx}",
108
111
  "projects/**/*.{ts,tsx}",
109
112
  "apps/**/*.{ts,tsx}",
110
113
  "libs/**/*.{ts,tsx}"
111
114
  ],
112
- /**
113
- * 스캔에서 제외할 경로 목록
114
- */
115
115
  "excludeGlobs": [
116
116
  "**/node_modules/**",
117
117
  "**/dist/**",
@@ -125,15 +125,26 @@ bootstrapApplication(AppComponent, appConfig)
125
125
  }
126
126
  ```
127
127
 
128
+ **필드 설명**
129
+
130
+ - `port`: 로컬 file-opener 서버 포트입니다.
131
+ - `workspaceRoot`: 명령 실행 위치 기준 Angular 워크스페이스 루트 상대 경로입니다.
132
+ - `editor`: 기본 에디터입니다 (`cursor`, `code`, `webstorm`).
133
+ - `fallbackEditor`: 기본 에디터 실행 실패 시 사용할 대체 에디터입니다.
134
+ - `scan.includeGlobs`: 컴포넌트 소스 파일 탐색 대상 glob 목록입니다.
135
+ - `scan.excludeGlobs`: 컴포넌트 스캔에서 제외할 glob 목록입니다.
136
+
128
137
  **프로젝트 구조별 includeGlobs 예시**
138
+
129
139
  1. 일반 Angular 앱
130
- `["src/app/**/*.ts"]`
140
+ `["src/app/**/*.ts"]`
131
141
  2. Angular Workspace (projects/)
132
- `["projects/**/*.{ts,tsx}"]`
142
+ `["projects/**/*.{ts,tsx}"]`
133
143
  3. Nx (apps/libs)
134
- `["apps/**/*.{ts,tsx}", "libs/**/*.{ts,tsx}"]`
144
+ `["apps/**/*.{ts,tsx}", "libs/**/*.{ts,tsx}"]`
135
145
 
136
146
  **환경변수 우선순위**
147
+
137
148
  1. `EDITOR_CMD` 예: `EDITOR_CMD="cursor --goto"`
138
149
  2. `LAUNCH_EDITOR` 예: `LAUNCH_EDITOR=code`
139
150
  3. `ngx-locatorjs.config.json`의 `editor`
@@ -143,6 +154,7 @@ bootstrapApplication(AppComponent, appConfig)
143
154
  `npx locatorjs-config` 실행 시 자동 생성됩니다. `angular.json`에 지정된 proxyConfig나 `proxy.conf.json`이 있으면 그 파일에 병합됩니다. 없으면 `ngx-locatorjs.proxy.json`을 생성합니다.
144
155
 
145
156
  예시:
157
+
146
158
  ```json
147
159
  {
148
160
  "/__open-in-editor": {
@@ -164,26 +176,28 @@ bootstrapApplication(AppComponent, appConfig)
164
176
  ```
165
177
 
166
178
  **트러블슈팅**
179
+
167
180
  1. CORS 에러
168
- `ng serve --proxy-config ngx-locatorjs.proxy.json` 사용 여부 확인
181
+ `ng serve --proxy-config ngx-locatorjs.proxy.json` 사용 여부 확인
169
182
  2. npm run 경고
170
- `npm run start -- --proxy-config ngx-locatorjs.proxy.json` 형태로 실행
183
+ `npm run start -- --proxy-config ngx-locatorjs.proxy.json` 형태로 실행
171
184
  3. 네트워크 비활성
172
- `installAngularLocator({ enableNetwork: true })` 설정 확인
185
+ `installAngularLocator({ enableNetwork: true })` 설정 확인
173
186
  4. component-map.json not found
174
- `npx locatorjs-scan` 실행 후 `.open-in-editor/component-map.json` 생성 여부 확인
187
+ `npx locatorjs-scan` 실행 후 `.open-in-editor/component-map.json` 생성 여부 확인
175
188
  5. 컴포넌트 변경이 반영되지 않음
176
- `npx locatorjs-open-in-editor --watch` 사용 또는 `npx locatorjs-scan` 재실행
189
+ `npx locatorjs-open-in-editor --watch` 사용 또는 `npx locatorjs-scan` 재실행
177
190
  6. 스캔 결과가 비어있거나 컴포넌트가 누락됨
178
- `scan.includeGlobs` 경로 확인 후 재스캔. 실제 컴포넌트들이 위치한 경로를 입력해야 합니다.
191
+ `scan.includeGlobs` 경로 확인 후 재스캔. 실제 컴포넌트들이 위치한 경로를 입력해야 합니다.
179
192
  7. 잘못된 파일이 열리거나 매칭이 안 됨
180
- `workspaceRoot`가 Angular 앱 루트인지 확인
193
+ `workspaceRoot`가 Angular 앱 루트인지 확인
181
194
  8. 하이라이트가 안 보이거나 info가 null로 나옴
182
- `http://localhost:${port}/__cmp-map` 에서 컴포넌트 정보가 잘 나타나는지 확인
195
+ `http://localhost:${port}/__cmp-map` 에서 컴포넌트 정보가 잘 나타나는지 확인
183
196
  9. 포트 충돌
184
- `ngx-locatorjs.config.json`과 `ngx-locatorjs.proxy.json`에서 포트 일치 여부 확인
197
+ `ngx-locatorjs.config.json`과 `ngx-locatorjs.proxy.json`에서 포트 일치 여부 확인
185
198
 
186
199
  **주의**
200
+
187
201
  - 개발 모드에서만 사용하세요. 프로덕션 번들에 포함되지 않도록 `environment.production` 체크를 권장합니다.
188
202
  - 네트워크 요청은 opt-in이며 localhost로만 제한됩니다. `enableNetwork: true`로 활성화하세요.
189
203
 
@@ -191,6 +205,7 @@ bootstrapApplication(AppComponent, appConfig)
191
205
  file-opener 서버와 Angular dev server를 한 번에 띄우려면 아래 방식 중 하나를 사용하세요.
192
206
 
193
207
  ### Option A: `concurrently`
208
+
194
209
  ```bash
195
210
  npm i -D concurrently
196
211
  ```
@@ -204,6 +219,7 @@ npm i -D concurrently
204
219
  ```
205
220
 
206
221
  ### Option B: `npm-run-all`
222
+
207
223
  ```bash
208
224
  npm i -D npm-run-all
209
225
  ```
package/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  한국어 문서: [README.ko.md](README.ko.md)
6
6
 
7
7
  Open Angular component files directly from the browser with **Alt + Click** during development. This package provides:
8
+
8
9
  - Browser runtime for Alt+click / hover UI
9
10
  - CLI tools to scan Angular components and open files in your editor
10
11
  - Config + proxy setup guidance
@@ -12,17 +13,20 @@ Open Angular component files directly from the browser with **Alt + Click** duri
12
13
  Inspired by [locatorjs.com](https://www.locatorjs.com/).
13
14
 
14
15
  ## Features
16
+
15
17
  - **Alt + Click**: open template (.html)
16
18
  - **Alt + Shift + Click**: open component (.ts)
17
19
  - **Hold Alt**: highlight component + tooltip
18
20
  - Supports **Cursor**, **VS Code**, **WebStorm**
19
21
 
20
22
  ## Install
23
+
21
24
  ```bash
22
25
  npm i -D ngx-locatorjs
23
26
  ```
24
27
 
25
28
  ## Required Steps (Do This First)
29
+
26
30
  You must complete steps 1–4 for this to work.
27
31
 
28
32
  1. Install the package: `npm i -D ngx-locatorjs`
@@ -35,6 +39,7 @@ If you use `npm run start`, pass args after `--`: `npm run start -- --proxy-conf
35
39
  ## Add to `main.ts`
36
40
 
37
41
  ### NgModule bootstrap
42
+
38
43
  ```ts
39
44
  import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
40
45
  import { AppModule } from './app/app.module';
@@ -51,8 +56,8 @@ platformBrowserDynamic()
51
56
  if (!environment.production) {
52
57
  setTimeout(() => {
53
58
  import('ngx-locatorjs')
54
- .then((m) =>
55
- m.installAngularLocator({ enableNetwork: true }), // required for network access (localhost-only)
59
+ .then(
60
+ (m) => m.installAngularLocator({ enableNetwork: true }), // required for network access (localhost-only)
56
61
  )
57
62
  .catch((err) => console.warn('[angular-locator] Failed to load:', err));
58
63
  }, 1000);
@@ -62,6 +67,7 @@ platformBrowserDynamic()
62
67
  ```
63
68
 
64
69
  ### Standalone bootstrap
70
+
65
71
  ```ts
66
72
  import { bootstrapApplication } from '@angular/platform-browser';
67
73
  import { appConfig } from './app/app.config';
@@ -71,8 +77,8 @@ bootstrapApplication(AppComponent, appConfig)
71
77
  .then(() => {
72
78
  setTimeout(() => {
73
79
  import('ngx-locatorjs')
74
- .then((m) =>
75
- m.installAngularLocator({ enableNetwork: true }), // required for network access (localhost-only)
80
+ .then(
81
+ (m) => m.installAngularLocator({ enableNetwork: true }), // required for network access (localhost-only)
76
82
  )
77
83
  .catch((err) => console.warn('[angular-locator] Failed to load:', err));
78
84
  }, 1000);
@@ -81,34 +87,31 @@ bootstrapApplication(AppComponent, appConfig)
81
87
  ```
82
88
 
83
89
  ## Config Guide (`ngx-locatorjs.config.json`)
90
+
84
91
  Location: project root
85
92
 
86
93
  **Important**
94
+
87
95
  - `npx locatorjs-config` uses the **current directory** as the base.
88
96
  - Defaults: `port: 4123`, `workspaceRoot: "."`.
89
97
  - In a monorepo, update `workspaceRoot` to the **relative path** of your Angular app (e.g. `apps/web`).
90
98
  - If `.gitignore` exists, `npx locatorjs-config` will append `.open-in-editor/`. Remove it if you want to commit the map.
91
99
 
92
100
  Example:
101
+
93
102
  ```json
94
103
  {
95
- "port": 4123, // Port for the local file-opener server
96
- "workspaceRoot": ".", // Angular workspace root
97
- "editor": "cursor", // Editor to open files (`cursor`, `code`, `webstorm`)
98
- "fallbackEditor": "code", // Fallback editor if the default fails
104
+ "port": 4123,
105
+ "workspaceRoot": ".",
106
+ "editor": "cursor",
107
+ "fallbackEditor": "code",
99
108
  "scan": {
100
- /**
101
- * Globs to include when scanning components
102
- */
103
109
  "includeGlobs": [
104
110
  "src/**/*.{ts,tsx}",
105
111
  "projects/**/*.{ts,tsx}",
106
112
  "apps/**/*.{ts,tsx}",
107
113
  "libs/**/*.{ts,tsx}"
108
114
  ],
109
- /**
110
- * Globs to exclude from scanning
111
- */
112
115
  "excludeGlobs": [
113
116
  "**/node_modules/**",
114
117
  "**/dist/**",
@@ -122,15 +125,27 @@ Example:
122
125
  }
123
126
  ```
124
127
 
128
+ ### Field Reference
129
+
130
+ - `port`: Port for the local file-opener server.
131
+ - `workspaceRoot`: Angular workspace root path (relative to where you run commands).
132
+ - `editor`: Preferred editor (`cursor`, `code`, `webstorm`).
133
+ - `fallbackEditor`: Fallback editor if the preferred editor cannot be launched.
134
+ - `scan.includeGlobs`: Globs used to find component source files.
135
+ - `scan.excludeGlobs`: Globs excluded from component scanning.
136
+
125
137
  ### Example includeGlobs
138
+
126
139
  - Simple app: `"src/app/**/*.ts"`
127
140
  - Angular workspace: `"projects/**/*.{ts,tsx}"`
128
141
  - Nx: `"apps/**/*.{ts,tsx}", "libs/**/*.{ts,tsx}"`
129
142
 
130
143
  ## Proxy (`ngx-locatorjs.proxy.json`)
144
+
131
145
  Generated by `npx locatorjs-config`. If a proxy file is already referenced in `angular.json` or `proxy.conf.json` exists, it will merge entries there. Otherwise it creates `ngx-locatorjs.proxy.json`.
132
146
 
133
147
  Example:
148
+
134
149
  ```json
135
150
  {
136
151
  "/__open-in-editor": {
@@ -152,12 +167,14 @@ Example:
152
167
  ```
153
168
 
154
169
  ## Environment Variable Priority
170
+
155
171
  1. `EDITOR_CMD` (example: `cursor --goto`)
156
172
  2. `LAUNCH_EDITOR` (example: `code`)
157
173
  3. `ngx-locatorjs.config.json` → `editor`
158
174
  4. auto-detected editor
159
175
 
160
176
  ## Troubleshooting
177
+
161
178
  - **CORS / JSON parse error**: ensure dev server uses `--proxy-config ngx-locatorjs.proxy.json`
162
179
  - **npm run shows "Unknown cli config --proxy-config"**: use `npm run start -- --proxy-config ngx-locatorjs.proxy.json`
163
180
  - **Network disabled**: pass `enableNetwork: true` to `installAngularLocator`
@@ -169,13 +186,16 @@ Example:
169
186
  - **Port conflict**: change port in both `ngx-locatorjs.config.json` and `ngx-locatorjs.proxy.json`
170
187
 
171
188
  ## Notes
189
+
172
190
  - Use only in development (guard with `environment.production`).
173
191
  - Network requests are opt-in and limited to localhost. Set `enableNetwork: true` to activate.
174
192
 
175
193
  ## One-Command Dev (Recommended)
194
+
176
195
  Running the file-opener server and Angular dev server separately is tedious. You can wire them into a single script.
177
196
 
178
197
  ### Option A: `concurrently`
198
+
179
199
  ```bash
180
200
  npm i -D concurrently
181
201
  ```
@@ -189,6 +209,7 @@ npm i -D concurrently
189
209
  ```
190
210
 
191
211
  ### Option B: `npm-run-all`
212
+
192
213
  ```bash
193
214
  npm i -D npm-run-all
194
215
  ```
@@ -204,9 +225,11 @@ npm i -D npm-run-all
204
225
  ```
205
226
 
206
227
  ## What It Can Do
228
+
207
229
  - Open template or component files with Alt+click in development
208
230
  - Show component highlight and tooltip while holding Alt
209
231
  - Works with single apps, Angular workspace, and Nx layouts
210
232
 
211
233
  ## Limitations
234
+
212
235
  - Not supported in SSR/SSG runtime (browser DOM only)
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA,KAAK,OAAO,GAAG;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,KAAK,MAAM,GAAG;IACZ,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC7C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AA4EF,iBAAe,SAAS,CAAC,YAAY,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAwB9D;AA+eD,wBAAsB,qBAAqB,CAAC,OAAO,GAAE,qBAA0B,iBAa9E;AAED,wBAAsB,mBAAmB,kBASxC;AAED,wBAAsB,mBAAmB,kBAExC;AAED,wBAAgB,yBAAyB,YAExC;AAED,eAAO,MAAM,SAAS;;CAErB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA,KAAK,OAAO,GAAG;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,KAAK,MAAM,GAAG;IACZ,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC1C,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CAChD,CAAC;AAkBF,MAAM,MAAM,uBAAuB,GAAG;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAC7C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AA4EF,iBAAe,SAAS,CAAC,YAAY,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CA2B9D;AA+eD,wBAAsB,qBAAqB,CAAC,OAAO,GAAE,qBAA0B,iBAa9E;AAED,wBAAsB,mBAAmB,kBASxC;AAED,wBAAsB,mBAAmB,kBAExC;AAED,wBAAgB,yBAAyB,YAExC;AAED,eAAO,MAAM,SAAS;;CAErB,CAAC"}
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { Project, SyntaxKind } from 'ts-morph';
2
+ import ts from 'typescript';
3
3
  import fs from 'fs';
4
4
  import path from 'path';
5
+ import { glob } from 'glob';
5
6
  const DEFAULT_INCLUDE_GLOBS = [
6
7
  'src/**/*.{ts,tsx}',
7
8
  'projects/**/*.{ts,tsx}',
@@ -31,28 +32,71 @@ function readConfig() {
31
32
  function toPosix(p) {
32
33
  return p.replace(/\\/g, '/');
33
34
  }
34
- function prefixWorkspaceRoot(glob, workspaceRoot) {
35
+ function prefixWorkspaceRoot(globPattern, workspaceRoot) {
35
36
  if (!workspaceRoot || workspaceRoot === '.' || workspaceRoot === './')
36
- return glob;
37
- if (path.isAbsolute(glob))
38
- return glob;
37
+ return globPattern;
38
+ if (path.isAbsolute(globPattern))
39
+ return globPattern;
39
40
  const rootPosix = toPosix(workspaceRoot).replace(/\/+$/, '');
40
- const globPosix = toPosix(glob).replace(/^\/+/, '');
41
+ const globPosix = toPosix(globPattern).replace(/^\/+/, '');
41
42
  if (globPosix.startsWith(rootPosix + '/'))
42
43
  return globPosix;
43
44
  return `${rootPosix}/${globPosix}`;
44
45
  }
45
- function globToNeedle(glob) {
46
- return toPosix(glob).replace(/\*\*/g, '').replace(/\*/g, '');
46
+ function extractTemplateUrl(node) {
47
+ for (const prop of node.properties) {
48
+ if (ts.isPropertyAssignment(prop) &&
49
+ ts.isIdentifier(prop.name) &&
50
+ prop.name.text === 'templateUrl') {
51
+ const initializer = prop.initializer;
52
+ if (ts.isStringLiteral(initializer)) {
53
+ return initializer.text;
54
+ }
55
+ }
56
+ }
57
+ return undefined;
58
+ }
59
+ function findComponentDecorator(node) {
60
+ if (!node.modifiers)
61
+ return undefined;
62
+ for (const modifier of node.modifiers) {
63
+ if (ts.isDecorator(modifier)) {
64
+ const expr = modifier.expression;
65
+ if (ts.isCallExpression(expr) && ts.isIdentifier(expr.expression)) {
66
+ if (expr.expression.text === 'Component') {
67
+ return modifier;
68
+ }
69
+ }
70
+ }
71
+ }
72
+ return undefined;
47
73
  }
48
- function isExcluded(filePath, excludeGlobs) {
49
- const normalized = toPosix(filePath);
50
- return excludeGlobs.some((pattern) => {
51
- const needle = globToNeedle(pattern);
52
- if (!needle)
53
- return false;
54
- return normalized.includes(needle);
55
- });
74
+ function parseSourceFile(filePath, sourceCode) {
75
+ const sourceFile = ts.createSourceFile(filePath, sourceCode, ts.ScriptTarget.Latest, true);
76
+ const components = [];
77
+ function visit(node) {
78
+ if (ts.isClassDeclaration(node) && node.name) {
79
+ const componentDecorator = findComponentDecorator(node);
80
+ if (componentDecorator) {
81
+ const expr = componentDecorator.expression;
82
+ const firstArg = expr.arguments[0];
83
+ if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
84
+ const templateUrl = extractTemplateUrl(firstArg);
85
+ const className = node.name.text;
86
+ const absTs = path.resolve(root, filePath);
87
+ const absTpl = templateUrl ? path.resolve(path.dirname(absTs), templateUrl) : undefined;
88
+ components.push({
89
+ className,
90
+ filePath: absTs,
91
+ templateUrl: absTpl,
92
+ });
93
+ }
94
+ }
95
+ }
96
+ ts.forEachChild(node, visit);
97
+ }
98
+ visit(sourceFile);
99
+ return components;
56
100
  }
57
101
  async function main() {
58
102
  const cfg = readConfig();
@@ -88,14 +132,16 @@ async function main() {
88
132
  }
89
133
  return stats;
90
134
  }
91
- const project = new Project({
92
- skipAddingFilesFromTsConfig: true,
93
- });
94
- project.addSourceFilesAtPaths(effectiveIncludeGlobs);
95
- const sourceFiles = project
96
- .getSourceFiles()
97
- .filter((sf) => !isExcluded(sf.getFilePath(), excludeGlobs));
98
- const filePaths = sourceFiles.map((sf) => sf.getFilePath());
135
+ const allFiles = [];
136
+ for (const pattern of effectiveIncludeGlobs) {
137
+ const files = await glob(pattern, {
138
+ ignore: excludeGlobs,
139
+ absolute: true,
140
+ cwd: root,
141
+ });
142
+ allFiles.push(...files);
143
+ }
144
+ const filePaths = [...new Set(allFiles)];
99
145
  const currentStats = getFileStats(filePaths);
100
146
  const previousCache = loadCache();
101
147
  const hasChanges = filePaths.some((filePath) => !previousCache[filePath] || previousCache[filePath] !== currentStats[filePath]);
@@ -108,38 +154,16 @@ async function main() {
108
154
  }
109
155
  const detailByFilePath = {};
110
156
  const filePathsByClassName = {};
111
- for (const sf of sourceFiles) {
112
- const filePath = sf.getFilePath();
113
- const classes = sf.getClasses();
114
- for (const cls of classes) {
115
- const decorators = cls.getDecorators();
116
- const comp = decorators.find((d) => d.getName() === 'Component');
117
- if (!comp)
118
- continue;
119
- const arg = comp.getCallExpression()?.getArguments()[0];
120
- if (!arg || !arg.asKind(SyntaxKind.ObjectLiteralExpression))
121
- continue;
122
- const obj = arg.asKindOrThrow(SyntaxKind.ObjectLiteralExpression);
123
- const templateUrlProp = obj.getProperty('templateUrl');
124
- const templateUrl = templateUrlProp
125
- ?.asKind(SyntaxKind.PropertyAssignment)
126
- ?.getInitializer()
127
- ?.getText()
128
- .replace(/^`|^'|^"|"|'|`$/g, '');
129
- const className = cls.getName();
130
- if (!className)
131
- continue;
132
- const absTs = path.resolve(root, filePath);
133
- const absTpl = templateUrl ? path.resolve(path.dirname(absTs), templateUrl) : undefined;
134
- detailByFilePath[absTs] = {
135
- className,
136
- filePath: absTs,
137
- templateUrl: absTpl,
138
- };
139
- if (!filePathsByClassName[className])
140
- filePathsByClassName[className] = [];
141
- if (!filePathsByClassName[className].includes(absTs)) {
142
- filePathsByClassName[className].push(absTs);
157
+ for (const filePath of filePaths) {
158
+ const sourceCode = fs.readFileSync(filePath, 'utf8');
159
+ const components = parseSourceFile(filePath, sourceCode);
160
+ for (const cmp of components) {
161
+ detailByFilePath[cmp.filePath] = cmp;
162
+ if (!filePathsByClassName[cmp.className]) {
163
+ filePathsByClassName[cmp.className] = [];
164
+ }
165
+ if (!filePathsByClassName[cmp.className].includes(cmp.filePath)) {
166
+ filePathsByClassName[cmp.className].push(cmp.filePath);
143
167
  }
144
168
  }
145
169
  }
@@ -94,7 +94,8 @@ async function startSetup() {
94
94
  }
95
95
  }
96
96
  catch (error) {
97
- console.error('\n❌ Setup failed:', error?.message || error);
97
+ const message = error instanceof Error ? error.message : String(error);
98
+ console.error('\n❌ Setup failed:', message);
98
99
  process.exit(1);
99
100
  }
100
101
  }
@@ -156,7 +157,7 @@ function findProxyConfigFromAngularJson() {
156
157
  const angularJson = JSON.parse(fs.readFileSync(angularJsonPath, 'utf8'));
157
158
  const projects = angularJson?.projects ?? {};
158
159
  for (const project of Object.values(projects)) {
159
- const targets = project?.architect || project?.targets;
160
+ const targets = project.architect || project.targets;
160
161
  const serve = targets?.serve;
161
162
  if (!serve)
162
163
  continue;
@@ -253,7 +254,7 @@ function selectEditor() {
253
254
  renderMenu();
254
255
  break;
255
256
  case '\r':
256
- case '\n':
257
+ case '\n': {
257
258
  process.stdin.setRawMode(false);
258
259
  process.stdin.pause();
259
260
  process.stdin.removeListener('data', handleKeypress);
@@ -261,6 +262,7 @@ function selectEditor() {
261
262
  console.log(`\n✨ Selected: ${selected.name}`);
262
263
  resolve(selected.value);
263
264
  break;
265
+ }
264
266
  case '\u0003':
265
267
  console.log('\n\nCancelled. Setting Cursor as default.');
266
268
  process.stdin.setRawMode(false);
@@ -16,6 +16,9 @@ if (!fs.existsSync(configPath)) {
16
16
  console.log('Or manually create the config file.');
17
17
  process.exit(1);
18
18
  }
19
+ function isErrnoException(error) {
20
+ return typeof error === 'object' && error !== null && 'code' in error;
21
+ }
19
22
  const DEFAULT_INCLUDE_GLOBS = [
20
23
  'src/**/*.{ts,tsx}',
21
24
  'projects/**/*.{ts,tsx}',
@@ -100,7 +103,8 @@ function launchInEditor(fileWithPos, preferred = DEFAULT_EDITOR) {
100
103
  return true;
101
104
  }
102
105
  catch (e) {
103
- console.log(`[file-opener] EDITOR_CMD failed: ${e.message}`);
106
+ const message = e instanceof Error ? e.message : String(e);
107
+ console.log(`[file-opener] EDITOR_CMD failed: ${message}`);
104
108
  }
105
109
  }
106
110
  const tryRun = (editor) => {
@@ -113,7 +117,8 @@ function launchInEditor(fileWithPos, preferred = DEFAULT_EDITOR) {
113
117
  return true;
114
118
  }
115
119
  catch (e) {
116
- console.log(`[file-opener] ${editor} failed: ${e.message}`);
120
+ const message = e instanceof Error ? e.message : String(e);
121
+ console.log(`[file-opener] ${editor} failed: ${message}`);
117
122
  return false;
118
123
  }
119
124
  };
@@ -155,7 +160,8 @@ function findBestLineInFile(filePath, searchTerms) {
155
160
  return bestScore > 0 ? bestLine : 1;
156
161
  }
157
162
  catch (e) {
158
- console.warn(`[file-opener] Failed to search in file: ${e.message}`);
163
+ const message = e instanceof Error ? e.message : String(e);
164
+ console.warn(`[file-opener] Failed to search in file: ${message}`);
159
165
  return 1;
160
166
  }
161
167
  }
@@ -215,7 +221,8 @@ function startScanWatch() {
215
221
  watchers.push(watcher);
216
222
  }
217
223
  catch (err) {
218
- console.log(`[file-opener] failed to watch ${watchPath}: ${err?.message || err}`);
224
+ const message = err instanceof Error ? err.message : String(err);
225
+ console.log(`[file-opener] failed to watch ${watchPath}: ${message}`);
219
226
  throw err;
220
227
  }
221
228
  };
@@ -321,9 +328,10 @@ const server = http.createServer((req, res) => {
321
328
  res.end(`Opened at line ${bestLine}`);
322
329
  }
323
330
  catch (e) {
324
- console.warn(`[file-opener] Search error: ${e.message}`);
331
+ const message = e instanceof Error ? e.message : String(e);
332
+ console.warn(`[file-opener] Search error: ${message}`);
325
333
  res.statusCode = 500;
326
- res.end('Search failed: ' + e.message);
334
+ res.end('Search failed: ' + message);
327
335
  }
328
336
  return;
329
337
  }
@@ -347,7 +355,7 @@ server
347
355
  }
348
356
  })
349
357
  .on('error', (err) => {
350
- if (err.code === 'EADDRINUSE') {
358
+ if (isErrnoException(err) && err.code === 'EADDRINUSE') {
351
359
  console.log(`[file-opener] Port ${PORT} already in use - another file:opener is already running`);
352
360
  process.exit(0);
353
361
  }
package/package.json CHANGED
@@ -1,25 +1,34 @@
1
1
  {
2
2
  "name": "ngx-locatorjs",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "LocatorJs open-in-editor tools for Angular projects",
5
- "author": "antepost24",
6
- "type": "module",
7
- "license": "MIT",
8
5
  "keywords": [
9
6
  "angular",
10
- "locatorjs",
11
- "open-in-editor",
7
+ "component",
12
8
  "devtools",
13
- "component"
9
+ "locatorjs",
10
+ "open-in-editor"
14
11
  ],
12
+ "homepage": "https://github.com/Ea-st-ring/ngx-locator#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/Ea-st-ring/ngx-locator/issues"
15
+ },
16
+ "license": "MIT",
17
+ "author": "antepost24",
15
18
  "repository": {
16
19
  "type": "git",
17
20
  "url": "git+https://github.com/Ea-st-ring/ngx-locator.git"
18
21
  },
19
- "bugs": {
20
- "url": "https://github.com/Ea-st-ring/ngx-locator/issues"
22
+ "bin": {
23
+ "locatorjs-config": "dist/node/config-setup.js",
24
+ "locatorjs-open-in-editor": "dist/node/file-opener.js",
25
+ "locatorjs-scan": "dist/node/cmp-scan.js"
21
26
  },
22
- "homepage": "https://github.com/Ea-st-ring/ngx-locator#readme",
27
+ "files": [
28
+ "dist",
29
+ "README.md"
30
+ ],
31
+ "type": "module",
23
32
  "main": "dist/browser/index.js",
24
33
  "types": "dist/browser/index.d.ts",
25
34
  "exports": {
@@ -34,35 +43,21 @@
34
43
  "default": "./dist/browser/auto.js"
35
44
  }
36
45
  },
37
- "bin": {
38
- "locatorjs-open-in-editor": "dist/node/file-opener.js",
39
- "locatorjs-config": "dist/node/config-setup.js",
40
- "locatorjs-scan": "dist/node/cmp-scan.js"
41
- },
42
- "files": [
43
- "dist",
44
- "README.md"
45
- ],
46
46
  "scripts": {
47
47
  "build": "npm run build:browser && npm run build:node",
48
48
  "build:browser": "tsc -p tsconfig.browser.json",
49
49
  "build:node": "tsc -p tsconfig.node.json",
50
50
  "clean": "rm -rf dist",
51
51
  "prepare": "npm run build",
52
- "lint": "eslint \"src/**/*.{ts,tsx}\"",
53
- "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
54
- "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\""
52
+ "format": "oxfmt",
53
+ "format:check": "oxfmt --check"
55
54
  },
56
55
  "dependencies": {
57
- "ts-morph": "^27.0.0"
56
+ "glob": "^11.0.0"
58
57
  },
59
58
  "devDependencies": {
60
- "@typescript-eslint/eslint-plugin": "^7.18.0",
61
- "@typescript-eslint/parser": "^7.18.0",
62
59
  "@types/node": "^18.18.0",
63
- "eslint": "^8.57.1",
64
- "eslint-config-prettier": "^9.1.0",
65
- "prettier": "^3.2.5",
60
+ "oxfmt": "^0.35.0",
66
61
  "typescript": "^5.5.4"
67
62
  }
68
63
  }