ngx-locatorjs 0.2.0 → 0.3.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 CHANGED
@@ -4,20 +4,24 @@
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
- - Cursor, VS Code, WebStorm 지원
11
+ - Antigravity IDE, Cursor, Zed, 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`에 런타임 훅 추가 (아래 예시 참고)
16
- 4. 파일 오프너 서버 + dev 서버 실행 (둘 다 켜진 상태 유지): `npx locatorjs-open-in-editor --watch` + `ng serve --proxy-config ngx-locatorjs.proxy.json`
18
+ 4. 파일 오프너 서버 + dev 서버 실행 (둘 다 켜진 상태 유지): `npx locatorjs-open-in-editor --watch` + `ng serve --proxy-config {proxyConfigPath}`
17
19
 
18
- `npm run start` 사용 시 `--` 뒤에 전달: `npm run start -- --proxy-config ngx-locatorjs.proxy.json`
20
+ `npm run start` 사용 시 `--` 뒤에 전달: `npm run start -- --proxy-config {proxyConfigPath}`
21
+ `{proxyConfigPath}`는 `npx locatorjs-config`가 선택/병합한 실제 프록시 파일 경로로 바꿔서 사용하세요.
19
22
 
20
23
  **Angular 코드 추가 (main.ts)**
24
+
21
25
  ```ts
22
26
  import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
23
27
  import { AppModule } from './app/app.module';
@@ -33,9 +37,7 @@ platformBrowserDynamic()
33
37
  .then(() => {
34
38
  if (!environment.production) {
35
39
  setTimeout(() => {
36
- import('ngx-locatorjs').then((m) =>
37
- m.installAngularLocator({ enableNetwork: true }),
38
- );
40
+ import('ngx-locatorjs').then((m) => m.installAngularLocator({ enableNetwork: true }));
39
41
  }, 1000);
40
42
  }
41
43
  })
@@ -43,6 +45,7 @@ platformBrowserDynamic()
43
45
  ```
44
46
 
45
47
  **Angular 코드 추가 (standalone: bootstrapApplication)**
48
+
46
49
  ```ts
47
50
  import { bootstrapApplication } from '@angular/platform-browser';
48
51
  import { appConfig } from './app/app.config';
@@ -52,9 +55,7 @@ bootstrapApplication(AppComponent, appConfig)
52
55
  .then(() => {
53
56
  setTimeout(() => {
54
57
  import('ngx-locatorjs')
55
- .then((m) =>
56
- m.installAngularLocator({ enableNetwork: true }),
57
- )
58
+ .then((m) => m.installAngularLocator({ enableNetwork: true }))
58
59
  .catch((err) => console.warn('[angular-locator] Failed to load:', err));
59
60
  }, 1000);
60
61
  })
@@ -62,56 +63,56 @@ bootstrapApplication(AppComponent, appConfig)
62
63
  ```
63
64
 
64
65
  **Angular dev server 예시**
66
+
65
67
  - CLI 실행
66
- `ng serve --proxy-config ngx-locatorjs.proxy.json`
68
+ `ng serve --proxy-config {proxyConfigPath}`
67
69
 
68
70
  - angular.json에 적용
69
- `"serve"` 옵션에 `"proxyConfig": "ngx-locatorjs.proxy.json"` 추가
71
+ `"serve"` 옵션에 `"proxyConfig": "{proxyConfigPath}"` 추가
70
72
 
71
73
  **컴포넌트 맵 스캔**
74
+
72
75
  - 수동 스캔
73
- `npx locatorjs-scan`
76
+ `npx locatorjs-scan`
74
77
 
75
78
  - 변경 감지 자동 스캔(선택)
76
- `nodemon --delay 2.5 -e ts,html -w src -w projects -w apps -w libs -x "npx locatorjs-scan"`
79
+ `nodemon --delay 2.5 -e ts,html -w src -w projects -w apps -w libs -x "npx locatorjs-scan"`
77
80
 
78
81
  **가능한 것**
82
+
79
83
  - Alt+클릭으로 템플릿 또는 컴포넌트 파일 열기 (개발 모드)
80
84
  - Alt 키 홀드 시 컴포넌트 하이라이트 및 툴팁 표시
81
85
  - 단일 Angular 앱, workspace, Nx 구조에서 동작
82
86
 
83
87
  **불가능/제한 사항**
88
+
84
89
  - SSR/SSG 환경에서는 동작하지 않음 (브라우저 DOM 기반)
85
90
 
86
91
  **ngx-locatorjs.config.json 가이드**
87
92
  파일 위치: 프로젝트 루트
88
93
 
89
94
  **중요**
95
+
90
96
  - `npx locatorjs-config`는 **실행한 현재 폴더를 기준**으로 설정합니다.
91
97
  - 기본값: `port: 4123`, `workspaceRoot: "."`.
92
98
  - 모노레포처럼 실제 Angular 앱이 하위 폴더에 있으면 `workspaceRoot`를 그 **상대 경로**로 수정하세요. (예: `apps/web`)
93
99
  - `.gitignore`가 있으면 `npx locatorjs-config`가 `.open-in-editor/`를 자동 추가합니다. 커밋하려면 해당 항목을 제거하세요.
94
100
 
95
101
  예시:
102
+
96
103
  ```json
97
104
  {
98
- "port": 4123, // 프록시 서버가 실행될 포트 주소
99
- "workspaceRoot": ".", // Angular 워크스페이스 루트
100
- "editor": "cursor", // 파일을 열 때 사용할 에디터 (`cursor`, `code`, `webstorm`)
101
- "fallbackEditor": "code", // 기본 에디터 실패 시 사용할 에디터
105
+ "port": 4123,
106
+ "workspaceRoot": ".",
107
+ "editor": "cursor",
108
+ "fallbackEditor": "code",
102
109
  "scan": {
103
- /**
104
- * 탐색할 컴포넌트의 경로 목록
105
- */
106
110
  "includeGlobs": [
107
111
  "src/**/*.{ts,tsx}",
108
112
  "projects/**/*.{ts,tsx}",
109
113
  "apps/**/*.{ts,tsx}",
110
114
  "libs/**/*.{ts,tsx}"
111
115
  ],
112
- /**
113
- * 스캔에서 제외할 경로 목록
114
- */
115
116
  "excludeGlobs": [
116
117
  "**/node_modules/**",
117
118
  "**/dist/**",
@@ -125,24 +126,40 @@ bootstrapApplication(AppComponent, appConfig)
125
126
  }
126
127
  ```
127
128
 
129
+ **필드 설명**
130
+
131
+ - `port`: 로컬 file-opener 서버 포트입니다.
132
+ - `workspaceRoot`: 명령 실행 위치 기준 Angular 워크스페이스 루트 상대 경로입니다.
133
+ - `editor`: 기본 에디터입니다 (`cursor`, `zed`, `antigravity`, `code`, `webstorm`).
134
+ - `fallbackEditor`: 기본 에디터 실행 실패 시 사용할 대체 에디터입니다.
135
+ - `scan.includeGlobs`: 컴포넌트 소스 파일 탐색 대상 glob 목록입니다.
136
+ - `scan.excludeGlobs`: 컴포넌트 스캔에서 제외할 glob 목록입니다.
137
+
128
138
  **프로젝트 구조별 includeGlobs 예시**
139
+
129
140
  1. 일반 Angular 앱
130
- `["src/app/**/*.ts"]`
141
+ `["src/app/**/*.ts"]`
131
142
  2. Angular Workspace (projects/)
132
- `["projects/**/*.{ts,tsx}"]`
143
+ `["projects/**/*.{ts,tsx}"]`
133
144
  3. Nx (apps/libs)
134
- `["apps/**/*.{ts,tsx}", "libs/**/*.{ts,tsx}"]`
145
+ `["apps/**/*.{ts,tsx}", "libs/**/*.{ts,tsx}"]`
135
146
 
136
147
  **환경변수 우선순위**
148
+
137
149
  1. `EDITOR_CMD` 예: `EDITOR_CMD="cursor --goto"`
138
150
  2. `LAUNCH_EDITOR` 예: `LAUNCH_EDITOR=code`
139
151
  3. `ngx-locatorjs.config.json`의 `editor`
140
152
  4. 자동 감지된 에디터
141
153
 
142
- **프록시 설정 (ngx-locatorjs.proxy.json)**
143
- `npx locatorjs-config` 실행 시 자동 생성됩니다. `angular.json`에 지정된 proxyConfig나 `proxy.conf.json`이 있으면 그 파일에 병합됩니다. 없으면 `ngx-locatorjs.proxy.json`을 생성합니다.
154
+ **프록시 설정 ({proxyConfigPath})**
155
+ `{proxyConfigPath}`는 `npx locatorjs-config` 실행 시 아래 규칙으로 결정됩니다.
156
+
157
+ - `angular.json`에 `proxyConfig`가 지정되어 있으면 그 파일에 병합합니다.
158
+ - 없고 `proxy.conf.json`이 있으면 그 파일에 병합합니다.
159
+ - 둘 다 없으면 `ngx-locatorjs.proxy.json`을 생성합니다.
144
160
 
145
161
  예시:
162
+
146
163
  ```json
147
164
  {
148
165
  "/__open-in-editor": {
@@ -164,26 +181,28 @@ bootstrapApplication(AppComponent, appConfig)
164
181
  ```
165
182
 
166
183
  **트러블슈팅**
184
+
167
185
  1. CORS 에러
168
- `ng serve --proxy-config ngx-locatorjs.proxy.json` 사용 여부 확인
186
+ `ng serve --proxy-config {proxyConfigPath}` 사용 여부 확인
169
187
  2. npm run 경고
170
- `npm run start -- --proxy-config ngx-locatorjs.proxy.json` 형태로 실행
188
+ `npm run start -- --proxy-config {proxyConfigPath}` 형태로 실행
171
189
  3. 네트워크 비활성
172
- `installAngularLocator({ enableNetwork: true })` 설정 확인
190
+ `installAngularLocator({ enableNetwork: true })` 설정 확인
173
191
  4. component-map.json not found
174
- `npx locatorjs-scan` 실행 후 `.open-in-editor/component-map.json` 생성 여부 확인
192
+ `npx locatorjs-scan` 실행 후 `.open-in-editor/component-map.json` 생성 여부 확인
175
193
  5. 컴포넌트 변경이 반영되지 않음
176
- `npx locatorjs-open-in-editor --watch` 사용 또는 `npx locatorjs-scan` 재실행
194
+ `npx locatorjs-open-in-editor --watch` 사용 또는 `npx locatorjs-scan` 재실행
177
195
  6. 스캔 결과가 비어있거나 컴포넌트가 누락됨
178
- `scan.includeGlobs` 경로 확인 후 재스캔. 실제 컴포넌트들이 위치한 경로를 입력해야 합니다.
196
+ `scan.includeGlobs` 경로 확인 후 재스캔. 실제 컴포넌트들이 위치한 경로를 입력해야 합니다.
179
197
  7. 잘못된 파일이 열리거나 매칭이 안 됨
180
- `workspaceRoot`가 Angular 앱 루트인지 확인
198
+ `workspaceRoot`가 Angular 앱 루트인지 확인
181
199
  8. 하이라이트가 안 보이거나 info가 null로 나옴
182
- `http://localhost:${port}/__cmp-map` 에서 컴포넌트 정보가 잘 나타나는지 확인
200
+ `http://localhost:${port}/__cmp-map` 에서 컴포넌트 정보가 잘 나타나는지 확인
183
201
  9. 포트 충돌
184
- `ngx-locatorjs.config.json`과 `ngx-locatorjs.proxy.json`에서 포트 일치 여부 확인
202
+ `ngx-locatorjs.config.json`과 `{proxyConfigPath}`에서 포트 일치 여부 확인
185
203
 
186
204
  **주의**
205
+
187
206
  - 개발 모드에서만 사용하세요. 프로덕션 번들에 포함되지 않도록 `environment.production` 체크를 권장합니다.
188
207
  - 네트워크 요청은 opt-in이며 localhost로만 제한됩니다. `enableNetwork: true`로 활성화하세요.
189
208
 
@@ -191,6 +210,7 @@ bootstrapApplication(AppComponent, appConfig)
191
210
  file-opener 서버와 Angular dev server를 한 번에 띄우려면 아래 방식 중 하나를 사용하세요.
192
211
 
193
212
  ### Option A: `concurrently`
213
+
194
214
  ```bash
195
215
  npm i -D concurrently
196
216
  ```
@@ -198,12 +218,13 @@ npm i -D concurrently
198
218
  ```json
199
219
  {
200
220
  "scripts": {
201
- "dev:locator": "concurrently -k -n opener,ng \"npx locatorjs-open-in-editor\" \"ng serve --proxy-config ngx-locatorjs.proxy.json\""
221
+ "dev:locator": "concurrently -k -n opener,ng \"npx locatorjs-open-in-editor\" \"ng serve --proxy-config {proxyConfigPath}\""
202
222
  }
203
223
  }
204
224
  ```
205
225
 
206
226
  ### Option B: `npm-run-all`
227
+
207
228
  ```bash
208
229
  npm i -D npm-run-all
209
230
  ```
@@ -212,8 +233,15 @@ npm i -D npm-run-all
212
233
  {
213
234
  "scripts": {
214
235
  "locator:opener": "npx locatorjs-open-in-editor",
215
- "dev:app": "ng serve --proxy-config ngx-locatorjs.proxy.json",
236
+ "dev:app": "ng serve --proxy-config {proxyConfigPath}",
216
237
  "dev:locator": "run-p locator:opener dev:app"
217
238
  }
218
239
  }
219
240
  ```
241
+
242
+ ** 고민 **
243
+
244
+ 처음 세팅할 때 귀찮은 단계가 많다고 느낍니다.
245
+ 설치하고, `npx locatorjs-config`를 돌리고, `main.ts`에 훅을 넣고, opener와 dev server를 proxy 설정과 함께 실행..
246
+
247
+ 이 흐름을 더 짧고 자연스럽게 만들고 싶습니다. 의견은 언제나 환영합니다.
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,29 +13,34 @@ 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
- - Supports **Cursor**, **VS Code**, **WebStorm**
20
+ - Supports **Antigravity IDE**, **Cursor**, **Zed**, **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`
29
33
  2. Generate config + proxy: `npx locatorjs-config`
30
34
  3. Add the runtime hook to `main.ts` (see the examples below)
31
- 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.json`
35
+ 4. Run the file-opener server and your dev server (keep both running): `npx locatorjs-open-in-editor --watch` + `ng serve --proxy-config {proxyConfigPath}`
32
36
 
33
- If you use `npm run start`, pass args after `--`: `npm run start -- --proxy-config ngx-locatorjs.proxy.json`
37
+ If you use `npm run start`, pass args after `--`: `npm run start -- --proxy-config {proxyConfigPath}`
38
+ Replace `{proxyConfigPath}` with the actual proxy file path selected/updated by `npx locatorjs-config`.
34
39
 
35
40
  ## Add to `main.ts`
36
41
 
37
42
  ### NgModule bootstrap
43
+
38
44
  ```ts
39
45
  import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
40
46
  import { AppModule } from './app/app.module';
@@ -51,8 +57,8 @@ platformBrowserDynamic()
51
57
  if (!environment.production) {
52
58
  setTimeout(() => {
53
59
  import('ngx-locatorjs')
54
- .then((m) =>
55
- m.installAngularLocator({ enableNetwork: true }), // required for network access (localhost-only)
60
+ .then(
61
+ (m) => m.installAngularLocator({ enableNetwork: true }), // required for network access (localhost-only)
56
62
  )
57
63
  .catch((err) => console.warn('[angular-locator] Failed to load:', err));
58
64
  }, 1000);
@@ -62,6 +68,7 @@ platformBrowserDynamic()
62
68
  ```
63
69
 
64
70
  ### Standalone bootstrap
71
+
65
72
  ```ts
66
73
  import { bootstrapApplication } from '@angular/platform-browser';
67
74
  import { appConfig } from './app/app.config';
@@ -71,8 +78,8 @@ bootstrapApplication(AppComponent, appConfig)
71
78
  .then(() => {
72
79
  setTimeout(() => {
73
80
  import('ngx-locatorjs')
74
- .then((m) =>
75
- m.installAngularLocator({ enableNetwork: true }), // required for network access (localhost-only)
81
+ .then(
82
+ (m) => m.installAngularLocator({ enableNetwork: true }), // required for network access (localhost-only)
76
83
  )
77
84
  .catch((err) => console.warn('[angular-locator] Failed to load:', err));
78
85
  }, 1000);
@@ -81,34 +88,31 @@ bootstrapApplication(AppComponent, appConfig)
81
88
  ```
82
89
 
83
90
  ## Config Guide (`ngx-locatorjs.config.json`)
91
+
84
92
  Location: project root
85
93
 
86
94
  **Important**
95
+
87
96
  - `npx locatorjs-config` uses the **current directory** as the base.
88
97
  - Defaults: `port: 4123`, `workspaceRoot: "."`.
89
98
  - In a monorepo, update `workspaceRoot` to the **relative path** of your Angular app (e.g. `apps/web`).
90
99
  - If `.gitignore` exists, `npx locatorjs-config` will append `.open-in-editor/`. Remove it if you want to commit the map.
91
100
 
92
101
  Example:
102
+
93
103
  ```json
94
104
  {
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
105
+ "port": 4123,
106
+ "workspaceRoot": ".",
107
+ "editor": "cursor",
108
+ "fallbackEditor": "code",
99
109
  "scan": {
100
- /**
101
- * Globs to include when scanning components
102
- */
103
110
  "includeGlobs": [
104
111
  "src/**/*.{ts,tsx}",
105
112
  "projects/**/*.{ts,tsx}",
106
113
  "apps/**/*.{ts,tsx}",
107
114
  "libs/**/*.{ts,tsx}"
108
115
  ],
109
- /**
110
- * Globs to exclude from scanning
111
- */
112
116
  "excludeGlobs": [
113
117
  "**/node_modules/**",
114
118
  "**/dist/**",
@@ -122,15 +126,31 @@ Example:
122
126
  }
123
127
  ```
124
128
 
129
+ ### Field Reference
130
+
131
+ - `port`: Port for the local file-opener server.
132
+ - `workspaceRoot`: Angular workspace root path (relative to where you run commands).
133
+ - `editor`: Preferred editor (`cursor`, `zed`, `antigravity`, `code`, `webstorm`).
134
+ - `fallbackEditor`: Fallback editor if the preferred editor cannot be launched.
135
+ - `scan.includeGlobs`: Globs used to find component source files.
136
+ - `scan.excludeGlobs`: Globs excluded from component scanning.
137
+
125
138
  ### Example includeGlobs
139
+
126
140
  - Simple app: `"src/app/**/*.ts"`
127
141
  - Angular workspace: `"projects/**/*.{ts,tsx}"`
128
142
  - Nx: `"apps/**/*.{ts,tsx}", "libs/**/*.{ts,tsx}"`
129
143
 
130
- ## Proxy (`ngx-locatorjs.proxy.json`)
131
- 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`.
144
+ ## Proxy (`{proxyConfigPath}`)
145
+
146
+ `{proxyConfigPath}` is decided by `npx locatorjs-config`:
147
+
148
+ - If `angular.json` already specifies `proxyConfig`, it updates that file.
149
+ - Else if `proxy.conf.json` exists, it updates that file.
150
+ - Otherwise it creates `ngx-locatorjs.proxy.json`.
132
151
 
133
152
  Example:
153
+
134
154
  ```json
135
155
  {
136
156
  "/__open-in-editor": {
@@ -152,30 +172,35 @@ Example:
152
172
  ```
153
173
 
154
174
  ## Environment Variable Priority
175
+
155
176
  1. `EDITOR_CMD` (example: `cursor --goto`)
156
177
  2. `LAUNCH_EDITOR` (example: `code`)
157
178
  3. `ngx-locatorjs.config.json` → `editor`
158
179
  4. auto-detected editor
159
180
 
160
181
  ## Troubleshooting
161
- - **CORS / JSON parse error**: ensure dev server uses `--proxy-config ngx-locatorjs.proxy.json`
162
- - **npm run shows "Unknown cli config --proxy-config"**: use `npm run start -- --proxy-config ngx-locatorjs.proxy.json`
182
+
183
+ - **CORS / JSON parse error**: ensure dev server uses `--proxy-config {proxyConfigPath}`
184
+ - **npm run shows "Unknown cli config --proxy-config"**: use `npm run start -- --proxy-config {proxyConfigPath}`
163
185
  - **Network disabled**: pass `enableNetwork: true` to `installAngularLocator`
164
186
  - **component-map.json not found**: run `npx locatorjs-scan`
165
187
  - **Component changes not reflected**: run `npx locatorjs-open-in-editor --watch` or re-run `npx locatorjs-scan`
166
188
  - **Map is empty or missing components**: check `scan.includeGlobs` and rerun the scan
167
189
  - **Wrong files open or nothing matches**: confirm `workspaceRoot` points to the actual Angular app root
168
190
  - **No highlight / info is null**: make sure `http://localhost:${port}/__cmp-map` is loading and includes your component class name
169
- - **Port conflict**: change port in both `ngx-locatorjs.config.json` and `ngx-locatorjs.proxy.json`
191
+ - **Port conflict**: change port in both `ngx-locatorjs.config.json` and `{proxyConfigPath}`
170
192
 
171
193
  ## Notes
194
+
172
195
  - Use only in development (guard with `environment.production`).
173
196
  - Network requests are opt-in and limited to localhost. Set `enableNetwork: true` to activate.
174
197
 
175
198
  ## One-Command Dev (Recommended)
199
+
176
200
  Running the file-opener server and Angular dev server separately is tedious. You can wire them into a single script.
177
201
 
178
202
  ### Option A: `concurrently`
203
+
179
204
  ```bash
180
205
  npm i -D concurrently
181
206
  ```
@@ -183,12 +208,13 @@ npm i -D concurrently
183
208
  ```json
184
209
  {
185
210
  "scripts": {
186
- "dev:locator": "concurrently -k -n opener,ng \"npx locatorjs-open-in-editor\" \"ng serve --proxy-config ngx-locatorjs.proxy.json\""
211
+ "dev:locator": "concurrently -k -n opener,ng \"npx locatorjs-open-in-editor\" \"ng serve --proxy-config {proxyConfigPath}\""
187
212
  }
188
213
  }
189
214
  ```
190
215
 
191
216
  ### Option B: `npm-run-all`
217
+
192
218
  ```bash
193
219
  npm i -D npm-run-all
194
220
  ```
@@ -197,16 +223,27 @@ npm i -D npm-run-all
197
223
  {
198
224
  "scripts": {
199
225
  "locator:opener": "npx locatorjs-open-in-editor",
200
- "dev:app": "ng serve --proxy-config ngx-locatorjs.proxy.json",
226
+ "dev:app": "ng serve --proxy-config {proxyConfigPath}",
201
227
  "dev:locator": "run-p locator:opener dev:app"
202
228
  }
203
229
  }
204
230
  ```
205
231
 
206
232
  ## What It Can Do
233
+
207
234
  - Open template or component files with Alt+click in development
208
235
  - Show component highlight and tooltip while holding Alt
209
236
  - Works with single apps, Angular workspace, and Nx layouts
210
237
 
211
238
  ## Limitations
239
+
212
240
  - Not supported in SSR/SSG runtime (browser DOM only)
241
+
242
+ ## Maintainer Note (Setup UX)
243
+
244
+ Current setup works, but the first-time flow still feels too manual. You install, run
245
+ `npx locatorjs-config`, add the bootstrap hook in `main.ts`, and then run opener + dev server
246
+ with proxy config.
247
+
248
+ This is an area we want to keep improving. Contributions that reduce setup friction are welcome,
249
+ especially around safer automation, clearer defaults, and better error guidance.
@@ -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;
@@ -202,6 +203,8 @@ function logDefaults() {
202
203
  function selectEditor() {
203
204
  const availableEditors = [
204
205
  { name: 'Cursor', value: 'cursor' },
206
+ { name: 'Zed', value: 'zed' },
207
+ { name: 'Antigravity IDE', value: 'antigravity' },
205
208
  { name: 'VS Code', value: 'code' },
206
209
  { name: 'WebStorm', value: 'webstorm' },
207
210
  ];
@@ -215,7 +218,7 @@ function selectEditor() {
215
218
  output: process.stdout,
216
219
  });
217
220
  return new Promise((resolve) => {
218
- rl.question('\nEnter number (1-3, default: 1 for Cursor): ', (answer) => {
221
+ rl.question('\nEnter number (1-5, default: 1 for Cursor): ', (answer) => {
219
222
  rl.close();
220
223
  const choice = parseInt(answer.trim(), 10) || 1;
221
224
  const selected = availableEditors[Math.max(0, Math.min(choice - 1, availableEditors.length - 1))];
@@ -253,7 +256,7 @@ function selectEditor() {
253
256
  renderMenu();
254
257
  break;
255
258
  case '\r':
256
- case '\n':
259
+ case '\n': {
257
260
  process.stdin.setRawMode(false);
258
261
  process.stdin.pause();
259
262
  process.stdin.removeListener('data', handleKeypress);
@@ -261,6 +264,7 @@ function selectEditor() {
261
264
  console.log(`\n✨ Selected: ${selected.name}`);
262
265
  resolve(selected.value);
263
266
  break;
267
+ }
264
268
  case '\u0003':
265
269
  console.log('\n\nCancelled. Setting Cursor as default.');
266
270
  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}',
@@ -46,12 +49,14 @@ function checkEditorCLI(editorName, cliCommand = editorName) {
46
49
  return editorCLICache[editorName];
47
50
  }
48
51
  const MAC_APP_NAMES = {
52
+ antigravity: 'Antigravity IDE',
49
53
  cursor: 'Cursor',
50
54
  code: 'Visual Studio Code',
55
+ zed: 'Zed',
51
56
  webstorm: 'WebStorm',
52
57
  };
53
58
  function detectAvailableEditors() {
54
- const editors = ['cursor', 'code', 'webstorm'];
59
+ const editors = ['cursor', 'zed', 'antigravity', 'code', 'webstorm'];
55
60
  const available = [];
56
61
  for (const editor of editors) {
57
62
  if (checkEditorCLI(editor)) {
@@ -61,9 +66,18 @@ function detectAvailableEditors() {
61
66
  return available;
62
67
  }
63
68
  const AVAILABLE_EDITORS = detectAvailableEditors();
64
- const DEFAULT_EDITOR = process.env.LAUNCH_EDITOR || cfg.editor || AVAILABLE_EDITORS[0]?.name || 'cursor';
65
- const FALLBACK_EDITOR = cfg.fallbackEditor || AVAILABLE_EDITORS[1]?.name || 'code';
69
+ const DEFAULT_EDITOR = process.env.LAUNCH_EDITOR || cfg.editor || 'cursor';
70
+ const FALLBACK_EDITOR = cfg.fallbackEditor ||
71
+ AVAILABLE_EDITORS.find((editor) => editor.name !== DEFAULT_EDITOR)?.name ||
72
+ 'code';
66
73
  const COMMAND_TEMPLATES = {
74
+ antigravity: (file) => {
75
+ if (checkEditorCLI('antigravity')) {
76
+ return ['antigravity', ['--goto', file]];
77
+ }
78
+ const filePath = file.split(':')[0];
79
+ return ['open', ['-a', MAC_APP_NAMES.antigravity, filePath]];
80
+ },
67
81
  cursor: (file) => {
68
82
  if (checkEditorCLI('cursor')) {
69
83
  return ['cursor', ['--goto', file]];
@@ -78,6 +92,13 @@ const COMMAND_TEMPLATES = {
78
92
  const filePath = file.split(':')[0];
79
93
  return ['open', ['-a', MAC_APP_NAMES.code, filePath]];
80
94
  },
95
+ zed: (file) => {
96
+ if (checkEditorCLI('zed')) {
97
+ return ['zed', [file]];
98
+ }
99
+ const filePath = file.split(':')[0];
100
+ return ['open', ['-a', MAC_APP_NAMES.zed, filePath]];
101
+ },
81
102
  webstorm: (file) => {
82
103
  if (checkEditorCLI('webstorm')) {
83
104
  const [filePath, line, col] = file.split(':');
@@ -100,7 +121,8 @@ function launchInEditor(fileWithPos, preferred = DEFAULT_EDITOR) {
100
121
  return true;
101
122
  }
102
123
  catch (e) {
103
- console.log(`[file-opener] EDITOR_CMD failed: ${e.message}`);
124
+ const message = e instanceof Error ? e.message : String(e);
125
+ console.log(`[file-opener] EDITOR_CMD failed: ${message}`);
104
126
  }
105
127
  }
106
128
  const tryRun = (editor) => {
@@ -113,7 +135,8 @@ function launchInEditor(fileWithPos, preferred = DEFAULT_EDITOR) {
113
135
  return true;
114
136
  }
115
137
  catch (e) {
116
- console.log(`[file-opener] ${editor} failed: ${e.message}`);
138
+ const message = e instanceof Error ? e.message : String(e);
139
+ console.log(`[file-opener] ${editor} failed: ${message}`);
117
140
  return false;
118
141
  }
119
142
  };
@@ -155,7 +178,8 @@ function findBestLineInFile(filePath, searchTerms) {
155
178
  return bestScore > 0 ? bestLine : 1;
156
179
  }
157
180
  catch (e) {
158
- console.warn(`[file-opener] Failed to search in file: ${e.message}`);
181
+ const message = e instanceof Error ? e.message : String(e);
182
+ console.warn(`[file-opener] Failed to search in file: ${message}`);
159
183
  return 1;
160
184
  }
161
185
  }
@@ -215,7 +239,8 @@ function startScanWatch() {
215
239
  watchers.push(watcher);
216
240
  }
217
241
  catch (err) {
218
- console.log(`[file-opener] failed to watch ${watchPath}: ${err?.message || err}`);
242
+ const message = err instanceof Error ? err.message : String(err);
243
+ console.log(`[file-opener] failed to watch ${watchPath}: ${message}`);
219
244
  throw err;
220
245
  }
221
246
  };
@@ -321,9 +346,10 @@ const server = http.createServer((req, res) => {
321
346
  res.end(`Opened at line ${bestLine}`);
322
347
  }
323
348
  catch (e) {
324
- console.warn(`[file-opener] Search error: ${e.message}`);
349
+ const message = e instanceof Error ? e.message : String(e);
350
+ console.warn(`[file-opener] Search error: ${message}`);
325
351
  res.statusCode = 500;
326
- res.end('Search failed: ' + e.message);
352
+ res.end('Search failed: ' + message);
327
353
  }
328
354
  return;
329
355
  }
@@ -347,7 +373,7 @@ server
347
373
  }
348
374
  })
349
375
  .on('error', (err) => {
350
- if (err.code === 'EADDRINUSE') {
376
+ if (isErrnoException(err) && err.code === 'EADDRINUSE') {
351
377
  console.log(`[file-opener] Port ${PORT} already in use - another file:opener is already running`);
352
378
  process.exit(0);
353
379
  }
package/package.json CHANGED
@@ -1,25 +1,34 @@
1
1
  {
2
2
  "name": "ngx-locatorjs",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
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": "^13.0.6"
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
  }