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 +48 -32
- package/README.md +37 -14
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/node/cmp-scan.js +80 -56
- package/dist/node/config-setup.js +5 -3
- package/dist/node/file-opener.js +15 -7
- package/package.json +23 -28
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": ".",
|
|
100
|
-
"editor": "cursor",
|
|
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(
|
|
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(
|
|
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,
|
|
96
|
-
"workspaceRoot": ".",
|
|
97
|
-
"editor": "cursor",
|
|
98
|
-
"fallbackEditor": "code",
|
|
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;
|
|
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"}
|
package/dist/node/cmp-scan.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
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(
|
|
35
|
+
function prefixWorkspaceRoot(globPattern, workspaceRoot) {
|
|
35
36
|
if (!workspaceRoot || workspaceRoot === '.' || workspaceRoot === './')
|
|
36
|
-
return
|
|
37
|
-
if (path.isAbsolute(
|
|
38
|
-
return
|
|
37
|
+
return globPattern;
|
|
38
|
+
if (path.isAbsolute(globPattern))
|
|
39
|
+
return globPattern;
|
|
39
40
|
const rootPosix = toPosix(workspaceRoot).replace(/\/+$/, '');
|
|
40
|
-
const globPosix = toPosix(
|
|
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
|
|
46
|
-
|
|
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
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
|
112
|
-
const
|
|
113
|
-
const
|
|
114
|
-
for (const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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
|
|
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);
|
package/dist/node/file-opener.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: ' +
|
|
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.
|
|
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
|
-
"
|
|
11
|
-
"open-in-editor",
|
|
7
|
+
"component",
|
|
12
8
|
"devtools",
|
|
13
|
-
"
|
|
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
|
-
"
|
|
20
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
53
|
-
"format": "
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
}
|