ngx-locatorjs 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +50 -61
- package/README.md +46 -57
- package/dist/browser/index.d.ts +1 -0
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +24 -5
- package/dist/node/cmp-scan.js +60 -19
- package/dist/node/config-setup.js +344 -67
- package/dist/node/file-opener.js +193 -42
- package/package.json +5 -2
package/README.ko.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# ngx-locatorjs (Open-in-Editor)
|
|
2
2
|
|
|
3
|
+
Angular 인기가 적어서인지 [LocatorJs](https://www.locatorjs.com/)와 같은 개발 도구가 없어 불편했습니다. 그래서 CODEX와 함께 만들어보았습니다.
|
|
3
4
|
브라우저에서 Alt+클릭으로 Angular 컴포넌트 파일을 에디터에서 바로 여는 개발용 도구입니다. Angular 프로젝트 어디에나 npm 패키지로 설치해 사용할 수 있습니다.
|
|
4
|
-
이 프로젝트는 [locatorjs.com](https://www.locatorjs.com/)에서 영감을 받았습니다.
|
|
5
5
|
|
|
6
6
|
**기능**
|
|
7
7
|
|
|
@@ -15,10 +15,9 @@
|
|
|
15
15
|
1. 패키지 설치: `npm i -D ngx-locatorjs`
|
|
16
16
|
2. 설정/프록시 생성: `npx locatorjs-config`
|
|
17
17
|
3. `main.ts`에 런타임 훅 추가 (아래 예시 참고)
|
|
18
|
-
4. 파일 오프너 서버 + dev 서버 실행 (둘 다 켜진 상태 유지): `npx locatorjs-open-in-editor --watch` + `ng serve --proxy-config
|
|
18
|
+
4. 파일 오프너 서버 + dev 서버 실행 (둘 다 켜진 상태 유지): `npx locatorjs-open-in-editor --watch` + `ng serve --proxy-config ngx-locatorjs.proxy.cjs`
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
`{proxyConfigPath}`는 `npx locatorjs-config`가 선택/병합한 실제 프록시 파일 경로로 바꿔서 사용하세요.
|
|
20
|
+
기존 0.3.0 버전에서 업그레이드했다면 `npx locatorjs-config`를 다시 실행해 `authToken`과 proxy 헤더를 재생성하고, `--proxy-config`(또는 `angular.json`)를 `ngx-locatorjs.proxy.cjs`로 맞추세요.
|
|
22
21
|
|
|
23
22
|
**Angular 코드 추가 (main.ts)**
|
|
24
23
|
|
|
@@ -65,10 +64,10 @@ bootstrapApplication(AppComponent, appConfig)
|
|
|
65
64
|
**Angular dev server 예시**
|
|
66
65
|
|
|
67
66
|
- CLI 실행
|
|
68
|
-
`ng serve --proxy-config
|
|
67
|
+
`ng serve --proxy-config ngx-locatorjs.proxy.cjs`
|
|
69
68
|
|
|
70
69
|
- angular.json에 적용
|
|
71
|
-
`"serve"` 옵션에 `"proxyConfig": "
|
|
70
|
+
`"serve"` 옵션에 `"proxyConfig": "ngx-locatorjs.proxy.cjs"` 추가
|
|
72
71
|
|
|
73
72
|
**컴포넌트 맵 스캔**
|
|
74
73
|
|
|
@@ -94,7 +93,7 @@ bootstrapApplication(AppComponent, appConfig)
|
|
|
94
93
|
**중요**
|
|
95
94
|
|
|
96
95
|
- `npx locatorjs-config`는 **실행한 현재 폴더를 기준**으로 설정합니다.
|
|
97
|
-
- 기본값: `port: 4123`, `workspaceRoot: "."`.
|
|
96
|
+
- 기본값: `host: "127.0.0.1"`, `port: 4123`, `workspaceRoot: "."`.
|
|
98
97
|
- 모노레포처럼 실제 Angular 앱이 하위 폴더에 있으면 `workspaceRoot`를 그 **상대 경로**로 수정하세요. (예: `apps/web`)
|
|
99
98
|
- `.gitignore`가 있으면 `npx locatorjs-config`가 `.open-in-editor/`를 자동 추가합니다. 커밋하려면 해당 항목을 제거하세요.
|
|
100
99
|
|
|
@@ -102,25 +101,27 @@ bootstrapApplication(AppComponent, appConfig)
|
|
|
102
101
|
|
|
103
102
|
```json
|
|
104
103
|
{
|
|
104
|
+
"host": "127.0.0.1",
|
|
105
105
|
"port": 4123,
|
|
106
106
|
"workspaceRoot": ".",
|
|
107
107
|
"editor": "cursor",
|
|
108
108
|
"fallbackEditor": "code",
|
|
109
|
+
"authToken": "locatorjs-config가 자동 생성",
|
|
109
110
|
"scan": {
|
|
110
111
|
"includeGlobs": [
|
|
111
|
-
"src/**/*.{ts,tsx}",
|
|
112
|
-
"projects/**/*.{ts,tsx}",
|
|
113
|
-
"apps/**/*.{ts,tsx}",
|
|
114
|
-
"libs/**/*.{ts,tsx}"
|
|
112
|
+
"src/**/*.{ts,tsx,js,jsx}",
|
|
113
|
+
"projects/**/*.{ts,tsx,js,jsx}",
|
|
114
|
+
"apps/**/*.{ts,tsx,js,jsx}",
|
|
115
|
+
"libs/**/*.{ts,tsx,js,jsx}"
|
|
115
116
|
],
|
|
116
117
|
"excludeGlobs": [
|
|
117
118
|
"**/node_modules/**",
|
|
118
119
|
"**/dist/**",
|
|
119
120
|
"**/.angular/**",
|
|
120
121
|
"**/coverage/**",
|
|
121
|
-
"**/*.spec.ts",
|
|
122
|
-
"**/*.test.ts",
|
|
123
|
-
"**/*.e2e.ts"
|
|
122
|
+
"**/*.spec.{ts,tsx,js,jsx}",
|
|
123
|
+
"**/*.test.{ts,tsx,js,jsx}",
|
|
124
|
+
"**/*.e2e.{ts,tsx,js,jsx}"
|
|
124
125
|
]
|
|
125
126
|
}
|
|
126
127
|
}
|
|
@@ -129,20 +130,35 @@ bootstrapApplication(AppComponent, appConfig)
|
|
|
129
130
|
**필드 설명**
|
|
130
131
|
|
|
131
132
|
- `port`: 로컬 file-opener 서버 포트입니다.
|
|
133
|
+
- `host`: file-opener 서버 바인드 주소입니다. 로컬 전용으로 `127.0.0.1` 사용을 권장합니다.
|
|
132
134
|
- `workspaceRoot`: 명령 실행 위치 기준 Angular 워크스페이스 루트 상대 경로입니다.
|
|
133
|
-
- `editor`: 기본 에디터입니다 (`cursor`, `
|
|
135
|
+
- `editor`: 기본 에디터입니다 (`cursor`, `code`, `webstorm`).
|
|
134
136
|
- `fallbackEditor`: 기본 에디터 실행 실패 시 사용할 대체 에디터입니다.
|
|
137
|
+
- `authToken`: open-in-editor 서버 요청 검증 토큰입니다. `locatorjs-config`가 자동 생성합니다.
|
|
135
138
|
- `scan.includeGlobs`: 컴포넌트 소스 파일 탐색 대상 glob 목록입니다.
|
|
136
139
|
- `scan.excludeGlobs`: 컴포넌트 스캔에서 제외할 glob 목록입니다.
|
|
137
140
|
|
|
141
|
+
**authToken 동작 방식 (0.5.0)**
|
|
142
|
+
|
|
143
|
+
- `locatorjs-config`가 프로젝트별 `authToken`을 `ngx-locatorjs.config.json`에 자동 생성합니다.
|
|
144
|
+
- `ngx-locatorjs.proxy.cjs`가 해당 토큰을 읽어 opener 라우트로 `x-locatorjs-token` 헤더를 전달합니다.
|
|
145
|
+
- Angular proxy 없이 opener 엔드포인트를 직접 호출할 경우 `installAngularLocator(...)`에 `authToken`을 전달해야 인증됩니다.
|
|
146
|
+
- `401 Unauthorized`가 발생하면 `npx locatorjs-config`를 다시 실행해 config/proxy를 재생성하세요.
|
|
147
|
+
|
|
148
|
+
**자동 스캔 범위 탐색 (0.5.0)**
|
|
149
|
+
|
|
150
|
+
- `locatorjs-config`가 스캔 루트를 자동으로 구성합니다.
|
|
151
|
+
- 기본 탐색 확장자는 `ts/tsx/js/jsx`를 포함합니다.
|
|
152
|
+
- 생성된 `scan.includeGlobs` / `scan.excludeGlobs`는 언제든 수동 수정 가능합니다.
|
|
153
|
+
|
|
138
154
|
**프로젝트 구조별 includeGlobs 예시**
|
|
139
155
|
|
|
140
156
|
1. 일반 Angular 앱
|
|
141
157
|
`["src/app/**/*.ts"]`
|
|
142
158
|
2. Angular Workspace (projects/)
|
|
143
|
-
`["projects/**/*.{ts,tsx}"]`
|
|
159
|
+
`["projects/**/*.{ts,tsx,js,jsx}"]`
|
|
144
160
|
3. Nx (apps/libs)
|
|
145
|
-
`["apps/**/*.{ts,tsx}", "libs/**/*.{ts,tsx}"]`
|
|
161
|
+
`["apps/**/*.{ts,tsx,js,jsx}", "libs/**/*.{ts,tsx,js,jsx}"]`
|
|
146
162
|
|
|
147
163
|
**환경변수 우선순위**
|
|
148
164
|
|
|
@@ -151,41 +167,25 @@ bootstrapApplication(AppComponent, appConfig)
|
|
|
151
167
|
3. `ngx-locatorjs.config.json`의 `editor`
|
|
152
168
|
4. 자동 감지된 에디터
|
|
153
169
|
|
|
154
|
-
**프록시 설정 (
|
|
155
|
-
`
|
|
156
|
-
|
|
157
|
-
- `angular.json`에 `proxyConfig`가 지정되어 있으면 그 파일에 병합합니다.
|
|
158
|
-
- 없고 `proxy.conf.json`이 있으면 그 파일에 병합합니다.
|
|
159
|
-
- 둘 다 없으면 `ngx-locatorjs.proxy.json`을 생성합니다.
|
|
170
|
+
**프록시 설정 (ngx-locatorjs.proxy.cjs)**
|
|
171
|
+
`npx locatorjs-config` 실행 시 자동 생성됩니다. 이 파일은 실행 시점에 `ngx-locatorjs.config.json`을 읽어 target을 구성하므로, 포트를 바꾸려면 `ngx-locatorjs.config.json`의 `port`만 수정하면 됩니다.
|
|
160
172
|
|
|
161
173
|
예시:
|
|
162
174
|
|
|
163
|
-
```
|
|
164
|
-
{
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
},
|
|
170
|
-
"/__open-in-editor-search": {
|
|
171
|
-
"target": "http://localhost:4123",
|
|
172
|
-
"secure": false,
|
|
173
|
-
"changeOrigin": true
|
|
174
|
-
},
|
|
175
|
-
"/__cmp-map": {
|
|
176
|
-
"target": "http://localhost:4123",
|
|
177
|
-
"secure": false,
|
|
178
|
-
"changeOrigin": true
|
|
179
|
-
}
|
|
180
|
-
}
|
|
175
|
+
```js
|
|
176
|
+
module.exports = {
|
|
177
|
+
'/__open-in-editor': { target, secure: false, changeOrigin: true, headers },
|
|
178
|
+
'/__open-in-editor-search': { target, secure: false, changeOrigin: true, headers },
|
|
179
|
+
'/__cmp-map': { target, secure: false, changeOrigin: true, headers },
|
|
180
|
+
};
|
|
181
181
|
```
|
|
182
182
|
|
|
183
183
|
**트러블슈팅**
|
|
184
184
|
|
|
185
185
|
1. CORS 에러
|
|
186
|
-
`ng serve --proxy-config
|
|
186
|
+
`ng serve --proxy-config ngx-locatorjs.proxy.cjs` 사용 여부 확인
|
|
187
187
|
2. npm run 경고
|
|
188
|
-
`npm run start -- --proxy-config
|
|
188
|
+
`npm run start -- --proxy-config ngx-locatorjs.proxy.cjs` 형태로 실행
|
|
189
189
|
3. 네트워크 비활성
|
|
190
190
|
`installAngularLocator({ enableNetwork: true })` 설정 확인
|
|
191
191
|
4. component-map.json not found
|
|
@@ -199,31 +199,20 @@ bootstrapApplication(AppComponent, appConfig)
|
|
|
199
199
|
8. 하이라이트가 안 보이거나 info가 null로 나옴
|
|
200
200
|
`http://localhost:${port}/__cmp-map` 에서 컴포넌트 정보가 잘 나타나는지 확인
|
|
201
201
|
9. 포트 충돌
|
|
202
|
-
`ngx-locatorjs.config.json
|
|
202
|
+
`ngx-locatorjs.config.json`의 `port`만 수정하면 됩니다
|
|
203
|
+
10. opener 라우트에서 401 발생
|
|
204
|
+
`npx locatorjs-config` 재실행으로 토큰/프록시 헤더를 다시 생성하거나, 프록시 없이 직접 호출 시 `installAngularLocator`에 `authToken` 전달
|
|
203
205
|
|
|
204
206
|
**주의**
|
|
205
207
|
|
|
206
208
|
- 개발 모드에서만 사용하세요. 프로덕션 번들에 포함되지 않도록 `environment.production` 체크를 권장합니다.
|
|
207
209
|
- 네트워크 요청은 opt-in이며 localhost로만 제한됩니다. `enableNetwork: true`로 활성화하세요.
|
|
210
|
+
- opener 서버는 기본적으로 loopback(`127.0.0.1`)에만 바인드되며, `workspaceRoot` 바깥 파일은 열 수 없습니다.
|
|
208
211
|
|
|
209
212
|
**원 커맨드 실행 (추천)**
|
|
210
|
-
file-opener 서버와 Angular dev server를 한 번에 띄우려면 아래
|
|
211
|
-
|
|
212
|
-
### Option A: `concurrently`
|
|
213
|
-
|
|
214
|
-
```bash
|
|
215
|
-
npm i -D concurrently
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
```json
|
|
219
|
-
{
|
|
220
|
-
"scripts": {
|
|
221
|
-
"dev:locator": "concurrently -k -n opener,ng \"npx locatorjs-open-in-editor\" \"ng serve --proxy-config {proxyConfigPath}\""
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
```
|
|
213
|
+
file-opener 서버와 Angular dev server를 한 번에 띄우려면 아래 방식을 사용하세요.
|
|
225
214
|
|
|
226
|
-
###
|
|
215
|
+
### `npm-run-all`
|
|
227
216
|
|
|
228
217
|
```bash
|
|
229
218
|
npm i -D npm-run-all
|
|
@@ -233,7 +222,7 @@ npm i -D npm-run-all
|
|
|
233
222
|
{
|
|
234
223
|
"scripts": {
|
|
235
224
|
"locator:opener": "npx locatorjs-open-in-editor",
|
|
236
|
-
"dev:app": "ng serve --proxy-config
|
|
225
|
+
"dev:app": "ng serve --proxy-config ngx-locatorjs.proxy.cjs",
|
|
237
226
|
"dev:locator": "run-p locator:opener dev:app"
|
|
238
227
|
}
|
|
239
228
|
}
|
package/README.md
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
한국어 문서: [README.ko.md](README.ko.md)
|
|
6
6
|
|
|
7
|
+
Angular doesn’t have many developer tools like LocatorJs, likely due to lower popularity, which has been inconvenient. So I built one together with CODEX.
|
|
7
8
|
Open Angular component files directly from the browser with **Alt + Click** during development. This package provides:
|
|
8
9
|
|
|
9
10
|
- Browser runtime for Alt+click / hover UI
|
|
@@ -32,10 +33,9 @@ You must complete steps 1–4 for this to work.
|
|
|
32
33
|
1. Install the package: `npm i -D ngx-locatorjs`
|
|
33
34
|
2. Generate config + proxy: `npx locatorjs-config`
|
|
34
35
|
3. Add the runtime hook to `main.ts` (see the examples below)
|
|
35
|
-
4. Run the file-opener server and your dev server (keep both running): `npx locatorjs-open-in-editor --watch` + `ng serve --proxy-config
|
|
36
|
+
4. Run the file-opener server and your dev server (keep both running): `npx locatorjs-open-in-editor --watch` + `ng serve --proxy-config ngx-locatorjs.proxy.cjs`
|
|
36
37
|
|
|
37
|
-
If you
|
|
38
|
-
Replace `{proxyConfigPath}` with the actual proxy file path selected/updated by `npx locatorjs-config`.
|
|
38
|
+
If you are upgrading from an older version, run `npx locatorjs-config` again so `authToken` and proxy headers are generated, then use `ngx-locatorjs.proxy.cjs` in your `--proxy-config` or `angular.json`.
|
|
39
39
|
|
|
40
40
|
## Add to `main.ts`
|
|
41
41
|
|
|
@@ -94,7 +94,7 @@ Location: project root
|
|
|
94
94
|
**Important**
|
|
95
95
|
|
|
96
96
|
- `npx locatorjs-config` uses the **current directory** as the base.
|
|
97
|
-
- Defaults: `port: 4123`, `workspaceRoot: "."`.
|
|
97
|
+
- Defaults: `host: "127.0.0.1"`, `port: 4123`, `workspaceRoot: "."`.
|
|
98
98
|
- In a monorepo, update `workspaceRoot` to the **relative path** of your Angular app (e.g. `apps/web`).
|
|
99
99
|
- If `.gitignore` exists, `npx locatorjs-config` will append `.open-in-editor/`. Remove it if you want to commit the map.
|
|
100
100
|
|
|
@@ -102,25 +102,27 @@ Example:
|
|
|
102
102
|
|
|
103
103
|
```json
|
|
104
104
|
{
|
|
105
|
+
"host": "127.0.0.1",
|
|
105
106
|
"port": 4123,
|
|
106
107
|
"workspaceRoot": ".",
|
|
107
108
|
"editor": "cursor",
|
|
108
109
|
"fallbackEditor": "code",
|
|
110
|
+
"authToken": "generated-by-locatorjs-config",
|
|
109
111
|
"scan": {
|
|
110
112
|
"includeGlobs": [
|
|
111
|
-
"src/**/*.{ts,tsx}",
|
|
112
|
-
"projects/**/*.{ts,tsx}",
|
|
113
|
-
"apps/**/*.{ts,tsx}",
|
|
114
|
-
"libs/**/*.{ts,tsx}"
|
|
113
|
+
"src/**/*.{ts,tsx,js,jsx}",
|
|
114
|
+
"projects/**/*.{ts,tsx,js,jsx}",
|
|
115
|
+
"apps/**/*.{ts,tsx,js,jsx}",
|
|
116
|
+
"libs/**/*.{ts,tsx,js,jsx}"
|
|
115
117
|
],
|
|
116
118
|
"excludeGlobs": [
|
|
117
119
|
"**/node_modules/**",
|
|
118
120
|
"**/dist/**",
|
|
119
121
|
"**/.angular/**",
|
|
120
122
|
"**/coverage/**",
|
|
121
|
-
"**/*.spec.ts",
|
|
122
|
-
"**/*.test.ts",
|
|
123
|
-
"**/*.e2e.ts"
|
|
123
|
+
"**/*.spec.{ts,tsx,js,jsx}",
|
|
124
|
+
"**/*.test.{ts,tsx,js,jsx}",
|
|
125
|
+
"**/*.e2e.{ts,tsx,js,jsx}"
|
|
124
126
|
]
|
|
125
127
|
}
|
|
126
128
|
}
|
|
@@ -129,46 +131,45 @@ Example:
|
|
|
129
131
|
### Field Reference
|
|
130
132
|
|
|
131
133
|
- `port`: Port for the local file-opener server.
|
|
134
|
+
- `host`: Bind address for the file-opener server. Keep `127.0.0.1` for local-only access.
|
|
132
135
|
- `workspaceRoot`: Angular workspace root path (relative to where you run commands).
|
|
133
|
-
- `editor`: Preferred editor (`cursor`, `
|
|
136
|
+
- `editor`: Preferred editor (`cursor`, `code`, `webstorm`).
|
|
134
137
|
- `fallbackEditor`: Fallback editor if the preferred editor cannot be launched.
|
|
138
|
+
- `authToken`: Request token validated by the open-in-editor server. Generated automatically by `locatorjs-config`.
|
|
135
139
|
- `scan.includeGlobs`: Globs used to find component source files.
|
|
136
140
|
- `scan.excludeGlobs`: Globs excluded from component scanning.
|
|
137
141
|
|
|
142
|
+
### authToken Behavior (0.5.0)
|
|
143
|
+
|
|
144
|
+
- `locatorjs-config` generates a per-project `authToken` in `ngx-locatorjs.config.json`.
|
|
145
|
+
- `ngx-locatorjs.proxy.cjs` reads that token and forwards it as `x-locatorjs-token` to opener routes.
|
|
146
|
+
- If you skip Angular proxy and call opener endpoints directly, pass `authToken` to `installAngularLocator(...)` so requests are authorized.
|
|
147
|
+
- If token mismatch occurs (`401 Unauthorized`), regenerate config/proxy: `npx locatorjs-config`.
|
|
148
|
+
|
|
149
|
+
### Auto Scan Scope Discovery (0.5.0)
|
|
150
|
+
|
|
151
|
+
- `locatorjs-config` now auto-detects scan roots.
|
|
152
|
+
- Generated defaults now include `ts/tsx/js/jsx`.
|
|
153
|
+
- You can always edit the generated `scan.includeGlobs` / `scan.excludeGlobs` manually.
|
|
154
|
+
|
|
138
155
|
### Example includeGlobs
|
|
139
156
|
|
|
140
157
|
- Simple app: `"src/app/**/*.ts"`
|
|
141
|
-
- Angular workspace: `"projects/**/*.{ts,tsx}"`
|
|
142
|
-
- Nx: `"apps/**/*.{ts,tsx}", "libs/**/*.{ts,tsx}"`
|
|
143
|
-
|
|
144
|
-
## Proxy (`{proxyConfigPath}`)
|
|
158
|
+
- Angular workspace: `"projects/**/*.{ts,tsx,js,jsx}"`
|
|
159
|
+
- Nx: `"apps/**/*.{ts,tsx,js,jsx}", "libs/**/*.{ts,tsx,js,jsx}"`
|
|
145
160
|
|
|
146
|
-
|
|
161
|
+
## Proxy (`ngx-locatorjs.proxy.cjs`)
|
|
147
162
|
|
|
148
|
-
-
|
|
149
|
-
- Else if `proxy.conf.json` exists, it updates that file.
|
|
150
|
-
- Otherwise it creates `ngx-locatorjs.proxy.json`.
|
|
163
|
+
Generated by `npx locatorjs-config`. This dynamic proxy module reads `ngx-locatorjs.config.json` at runtime, so changing `port` in config updates proxy target automatically.
|
|
151
164
|
|
|
152
165
|
Example:
|
|
153
166
|
|
|
154
|
-
```
|
|
155
|
-
{
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
},
|
|
161
|
-
"/__open-in-editor-search": {
|
|
162
|
-
"target": "http://localhost:4123",
|
|
163
|
-
"secure": false,
|
|
164
|
-
"changeOrigin": true
|
|
165
|
-
},
|
|
166
|
-
"/__cmp-map": {
|
|
167
|
-
"target": "http://localhost:4123",
|
|
168
|
-
"secure": false,
|
|
169
|
-
"changeOrigin": true
|
|
170
|
-
}
|
|
171
|
-
}
|
|
167
|
+
```js
|
|
168
|
+
module.exports = {
|
|
169
|
+
'/__open-in-editor': { target, secure: false, changeOrigin: true, headers },
|
|
170
|
+
'/__open-in-editor-search': { target, secure: false, changeOrigin: true, headers },
|
|
171
|
+
'/__cmp-map': { target, secure: false, changeOrigin: true, headers },
|
|
172
|
+
};
|
|
172
173
|
```
|
|
173
174
|
|
|
174
175
|
## Environment Variable Priority
|
|
@@ -180,40 +181,28 @@ Example:
|
|
|
180
181
|
|
|
181
182
|
## Troubleshooting
|
|
182
183
|
|
|
183
|
-
- **CORS / JSON parse error**: ensure dev server uses `--proxy-config
|
|
184
|
-
- **npm run shows "Unknown cli config --proxy-config"**: use `npm run start -- --proxy-config
|
|
184
|
+
- **CORS / JSON parse error**: ensure dev server uses `--proxy-config ngx-locatorjs.proxy.cjs`
|
|
185
|
+
- **npm run shows "Unknown cli config --proxy-config"**: use `npm run start -- --proxy-config ngx-locatorjs.proxy.cjs`
|
|
185
186
|
- **Network disabled**: pass `enableNetwork: true` to `installAngularLocator`
|
|
186
187
|
- **component-map.json not found**: run `npx locatorjs-scan`
|
|
187
188
|
- **Component changes not reflected**: run `npx locatorjs-open-in-editor --watch` or re-run `npx locatorjs-scan`
|
|
188
189
|
- **Map is empty or missing components**: check `scan.includeGlobs` and rerun the scan
|
|
189
190
|
- **Wrong files open or nothing matches**: confirm `workspaceRoot` points to the actual Angular app root
|
|
190
191
|
- **No highlight / info is null**: make sure `http://localhost:${port}/__cmp-map` is loading and includes your component class name
|
|
191
|
-
- **Port conflict**: change port in
|
|
192
|
+
- **Port conflict**: change `port` in `ngx-locatorjs.config.json` only
|
|
193
|
+
- **401 Unauthorized from opener routes**: regenerate config/proxy (`npx locatorjs-config`) or pass `authToken` to `installAngularLocator` if you call the opener without Angular proxy headers
|
|
192
194
|
|
|
193
195
|
## Notes
|
|
194
196
|
|
|
195
197
|
- Use only in development (guard with `environment.production`).
|
|
196
198
|
- Network requests are opt-in and limited to localhost. Set `enableNetwork: true` to activate.
|
|
199
|
+
- Opener server accepts files only inside `workspaceRoot` and binds to loopback by default.
|
|
197
200
|
|
|
198
201
|
## One-Command Dev (Recommended)
|
|
199
202
|
|
|
200
203
|
Running the file-opener server and Angular dev server separately is tedious. You can wire them into a single script.
|
|
201
204
|
|
|
202
|
-
###
|
|
203
|
-
|
|
204
|
-
```bash
|
|
205
|
-
npm i -D concurrently
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
```json
|
|
209
|
-
{
|
|
210
|
-
"scripts": {
|
|
211
|
-
"dev:locator": "concurrently -k -n opener,ng \"npx locatorjs-open-in-editor\" \"ng serve --proxy-config {proxyConfigPath}\""
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### Option B: `npm-run-all`
|
|
205
|
+
### `npm-run-all`
|
|
217
206
|
|
|
218
207
|
```bash
|
|
219
208
|
npm i -D npm-run-all
|
|
@@ -223,7 +212,7 @@ npm i -D npm-run-all
|
|
|
223
212
|
{
|
|
224
213
|
"scripts": {
|
|
225
214
|
"locator:opener": "npx locatorjs-open-in-editor",
|
|
226
|
-
"dev:app": "ng serve --proxy-config
|
|
215
|
+
"dev:app": "ng serve --proxy-config ngx-locatorjs.proxy.cjs",
|
|
227
216
|
"dev:locator": "run-p locator:opener dev:app"
|
|
228
217
|
}
|
|
229
218
|
}
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -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;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;
|
|
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,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AA+FF,iBAAe,SAAS,CAAC,YAAY,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CA4B9D;AAmfD,wBAAsB,qBAAqB,CAAC,OAAO,GAAE,qBAA0B,iBAc9E;AAED,wBAAsB,mBAAmB,kBASxC;AAED,wBAAsB,mBAAmB,kBAExC;AAED,wBAAgB,yBAAyB,YAExC;AAED,eAAO,MAAM,SAAS;;CAErB,CAAC"}
|
package/dist/browser/index.js
CHANGED
|
@@ -11,6 +11,7 @@ const DEFAULT_OPTIONS = {
|
|
|
11
11
|
enableClick: true,
|
|
12
12
|
showTooltip: true,
|
|
13
13
|
showClickFeedback: true,
|
|
14
|
+
authToken: null,
|
|
14
15
|
debug: false,
|
|
15
16
|
};
|
|
16
17
|
let OPTIONS = DEFAULT_OPTIONS;
|
|
@@ -41,6 +42,22 @@ function assertNetworkAllowed(url) {
|
|
|
41
42
|
}
|
|
42
43
|
return resolved.toString();
|
|
43
44
|
}
|
|
45
|
+
function attachAuthToken(url) {
|
|
46
|
+
if (!OPTIONS.authToken)
|
|
47
|
+
return url;
|
|
48
|
+
const resolved = new URL(url, window.location.href);
|
|
49
|
+
if (!resolved.searchParams.has('token')) {
|
|
50
|
+
resolved.searchParams.set('token', OPTIONS.authToken);
|
|
51
|
+
}
|
|
52
|
+
return resolved.toString();
|
|
53
|
+
}
|
|
54
|
+
function getAuthHeaders() {
|
|
55
|
+
if (!OPTIONS.authToken)
|
|
56
|
+
return undefined;
|
|
57
|
+
return {
|
|
58
|
+
'x-locatorjs-token': OPTIONS.authToken,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
44
61
|
function normalizeMap(map) {
|
|
45
62
|
if (!map.filePathsByClassName || Object.keys(map.filePathsByClassName).length === 0) {
|
|
46
63
|
const rebuilt = {};
|
|
@@ -59,12 +76,13 @@ async function ensureMap(forceRefresh = false) {
|
|
|
59
76
|
if (CMP_MAP && !forceRefresh)
|
|
60
77
|
return CMP_MAP;
|
|
61
78
|
const timestamp = Date.now();
|
|
62
|
-
const res = await fetch(assertNetworkAllowed(`${OPTIONS.endpoints.componentMap}?t=${timestamp}`), {
|
|
79
|
+
const res = await fetch(attachAuthToken(assertNetworkAllowed(`${OPTIONS.endpoints.componentMap}?t=${timestamp}`)), {
|
|
63
80
|
cache: 'no-store',
|
|
64
81
|
headers: {
|
|
65
82
|
'Cache-Control': 'no-cache, no-store, must-revalidate',
|
|
66
83
|
Pragma: 'no-cache',
|
|
67
84
|
Expires: '0',
|
|
85
|
+
...getAuthHeaders(),
|
|
68
86
|
},
|
|
69
87
|
});
|
|
70
88
|
const text = await res.text();
|
|
@@ -198,9 +216,9 @@ function getNearestComponent(el) {
|
|
|
198
216
|
};
|
|
199
217
|
}
|
|
200
218
|
async function openFile(absPath, line = 1, col = 1) {
|
|
201
|
-
const url = assertNetworkAllowed(`${OPTIONS.endpoints.openInEditor}?file=${encodeURIComponent(absPath)}&line=${line}&col=${col}`);
|
|
219
|
+
const url = attachAuthToken(assertNetworkAllowed(`${OPTIONS.endpoints.openInEditor}?file=${encodeURIComponent(absPath)}&line=${line}&col=${col}`));
|
|
202
220
|
try {
|
|
203
|
-
await fetch(url);
|
|
221
|
+
await fetch(url, { headers: getAuthHeaders() });
|
|
204
222
|
}
|
|
205
223
|
catch (e) {
|
|
206
224
|
if (OPTIONS.debug) {
|
|
@@ -209,9 +227,9 @@ async function openFile(absPath, line = 1, col = 1) {
|
|
|
209
227
|
}
|
|
210
228
|
}
|
|
211
229
|
async function openFileWithSearch(absPath, searchTerms) {
|
|
212
|
-
const url = assertNetworkAllowed(`${OPTIONS.endpoints.openInEditorSearch}?file=${encodeURIComponent(absPath)}&search=${encodeURIComponent(JSON.stringify(searchTerms))}`);
|
|
230
|
+
const url = attachAuthToken(assertNetworkAllowed(`${OPTIONS.endpoints.openInEditorSearch}?file=${encodeURIComponent(absPath)}&search=${encodeURIComponent(JSON.stringify(searchTerms))}`));
|
|
213
231
|
try {
|
|
214
|
-
await fetch(url);
|
|
232
|
+
await fetch(url, { headers: getAuthHeaders() });
|
|
215
233
|
}
|
|
216
234
|
catch (e) {
|
|
217
235
|
if (OPTIONS.debug) {
|
|
@@ -524,6 +542,7 @@ export async function installAngularLocator(options = {}) {
|
|
|
524
542
|
const mergedOptions = {
|
|
525
543
|
...DEFAULT_OPTIONS,
|
|
526
544
|
...options,
|
|
545
|
+
authToken: options.authToken ?? DEFAULT_OPTIONS.authToken,
|
|
527
546
|
endpoints: {
|
|
528
547
|
...DEFAULT_ENDPOINTS,
|
|
529
548
|
...options.endpoints,
|
package/dist/node/cmp-scan.js
CHANGED
|
@@ -4,19 +4,19 @@ import fs from 'fs';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { glob } from 'glob';
|
|
6
6
|
const DEFAULT_INCLUDE_GLOBS = [
|
|
7
|
-
'src/**/*.{ts,tsx}',
|
|
8
|
-
'projects/**/*.{ts,tsx}',
|
|
9
|
-
'apps/**/*.{ts,tsx}',
|
|
10
|
-
'libs/**/*.{ts,tsx}',
|
|
7
|
+
'src/**/*.{ts,tsx,js,jsx}',
|
|
8
|
+
'projects/**/*.{ts,tsx,js,jsx}',
|
|
9
|
+
'apps/**/*.{ts,tsx,js,jsx}',
|
|
10
|
+
'libs/**/*.{ts,tsx,js,jsx}',
|
|
11
11
|
];
|
|
12
12
|
const DEFAULT_EXCLUDE_GLOBS = [
|
|
13
13
|
'**/node_modules/**',
|
|
14
14
|
'**/dist/**',
|
|
15
15
|
'**/.angular/**',
|
|
16
16
|
'**/coverage/**',
|
|
17
|
-
'**/*.spec.ts',
|
|
18
|
-
'**/*.test.ts',
|
|
19
|
-
'**/*.e2e.ts',
|
|
17
|
+
'**/*.spec.{ts,tsx,js,jsx}',
|
|
18
|
+
'**/*.test.{ts,tsx,js,jsx}',
|
|
19
|
+
'**/*.e2e.{ts,tsx,js,jsx}',
|
|
20
20
|
];
|
|
21
21
|
const root = process.cwd();
|
|
22
22
|
const CONFIG_FILENAME = 'ngx-locatorjs.config.json';
|
|
@@ -110,10 +110,31 @@ async function main() {
|
|
|
110
110
|
fs.mkdirSync(outDir, { recursive: true });
|
|
111
111
|
function loadCache() {
|
|
112
112
|
try {
|
|
113
|
-
|
|
113
|
+
const raw = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
|
|
114
|
+
if (!raw || typeof raw !== 'object')
|
|
115
|
+
return { version: 2, files: {} };
|
|
116
|
+
const typed = raw;
|
|
117
|
+
if (typed.version === 2 && typed.files && typeof typed.files === 'object') {
|
|
118
|
+
return {
|
|
119
|
+
version: 2,
|
|
120
|
+
files: typed.files,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
// Backward compatibility for old cache shape: { [filePath]: mtimeMs }
|
|
124
|
+
const files = {};
|
|
125
|
+
for (const [filePath, mtime] of Object.entries(raw)) {
|
|
126
|
+
if (typeof mtime === 'number') {
|
|
127
|
+
files[filePath] = {
|
|
128
|
+
// Force one-time reparsing after legacy-cache migration.
|
|
129
|
+
mtimeMs: -1,
|
|
130
|
+
components: [],
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return { version: 2, files };
|
|
114
135
|
}
|
|
115
136
|
catch {
|
|
116
|
-
return {};
|
|
137
|
+
return { version: 2, files: {} };
|
|
117
138
|
}
|
|
118
139
|
}
|
|
119
140
|
function saveCache(cache) {
|
|
@@ -144,19 +165,36 @@ async function main() {
|
|
|
144
165
|
const filePaths = [...new Set(allFiles)];
|
|
145
166
|
const currentStats = getFileStats(filePaths);
|
|
146
167
|
const previousCache = loadCache();
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
168
|
+
const previousFiles = previousCache.files;
|
|
169
|
+
const previousPaths = Object.keys(previousFiles);
|
|
170
|
+
const changedOrNewPaths = filePaths.filter((filePath) => {
|
|
171
|
+
const currentMtime = currentStats[filePath];
|
|
172
|
+
const cached = previousFiles[filePath];
|
|
173
|
+
return !cached || cached.mtimeMs !== currentMtime;
|
|
174
|
+
});
|
|
175
|
+
const deletedPaths = previousPaths.filter((cachedPath) => !currentStats[cachedPath]);
|
|
176
|
+
if (changedOrNewPaths.length === 0 && deletedPaths.length === 0 && fs.existsSync(outFile)) {
|
|
153
177
|
process.exit(0);
|
|
154
178
|
}
|
|
155
|
-
const
|
|
156
|
-
const filePathsByClassName = {};
|
|
179
|
+
const nextFiles = {};
|
|
157
180
|
for (const filePath of filePaths) {
|
|
181
|
+
const previous = previousFiles[filePath];
|
|
182
|
+
const currentMtime = currentStats[filePath];
|
|
183
|
+
if (previous && previous.mtimeMs === currentMtime) {
|
|
184
|
+
nextFiles[filePath] = previous;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
158
187
|
const sourceCode = fs.readFileSync(filePath, 'utf8');
|
|
159
188
|
const components = parseSourceFile(filePath, sourceCode);
|
|
189
|
+
nextFiles[filePath] = {
|
|
190
|
+
mtimeMs: currentMtime,
|
|
191
|
+
components,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
const detailByFilePath = {};
|
|
195
|
+
const filePathsByClassName = {};
|
|
196
|
+
for (const entry of Object.values(nextFiles)) {
|
|
197
|
+
const components = entry.components;
|
|
160
198
|
for (const cmp of components) {
|
|
161
199
|
detailByFilePath[cmp.filePath] = cmp;
|
|
162
200
|
if (!filePathsByClassName[cmp.className]) {
|
|
@@ -173,8 +211,11 @@ async function main() {
|
|
|
173
211
|
filePathsByClassName,
|
|
174
212
|
};
|
|
175
213
|
fs.writeFileSync(outFile, JSON.stringify(out, null, 2));
|
|
176
|
-
saveCache(
|
|
177
|
-
|
|
214
|
+
saveCache({
|
|
215
|
+
version: 2,
|
|
216
|
+
files: nextFiles,
|
|
217
|
+
});
|
|
218
|
+
console.log(`[cmp-scan] ✅ Saved ${Object.keys(detailByFilePath).length} components to ${path.relative(root, outFile)} (updated ${changedOrNewPaths.length}, removed ${deletedPaths.length})`);
|
|
178
219
|
}
|
|
179
220
|
main().catch((err) => {
|
|
180
221
|
console.error('[cmp-scan] Failed:', err);
|