@gaonjs/cli 0.63.1 → 0.65.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/dist/commands/check.js +34 -18
- package/dist/commands/g.d.ts +1 -0
- package/dist/commands/g.js +11 -2
- package/dist/commands/gen.js +1 -1
- package/dist/dev.d.ts +13 -7
- package/dist/dev.js +30 -22
- package/dist/doctor/fixers/i18n-layout.d.ts +2 -2
- package/dist/doctor/fixers/i18n-layout.js +119 -25
- package/dist/doctor/fixers/index.js +7 -2
- package/dist/doctor/fixers/types.d.ts +17 -2
- package/dist/doctor/i18n-app-scope.d.ts +3 -0
- package/dist/doctor/i18n-app-scope.js +94 -65
- package/dist/doctor/i18n-layout.d.ts +1 -1
- package/dist/doctor/i18n-layout.js +157 -32
- package/dist/doctor/i18n-server-scope.d.ts +2 -0
- package/dist/doctor/i18n-server-scope.js +124 -0
- package/dist/doctor/locale-parity.js +29 -68
- package/dist/doctor/types.d.ts +1 -1
- package/dist/doctor.d.ts +2 -1
- package/dist/doctor.js +36 -10
- package/dist/i18n-config.d.ts +9 -9
- package/dist/i18n-config.js +8 -15
- package/dist/messages-gen.d.ts +10 -10
- package/dist/messages-gen.js +42 -54
- package/dist/scaffold/app.d.ts +10 -1
- package/dist/scaffold/app.js +12 -2
- package/dist/templates/project/AGENTS.md.tpl +7 -6
- package/dist/templates/project/agents/async.md.tpl +9 -9
- package/dist/templates/project/agents/data.md.tpl +37 -12
- package/dist/templates/project/agents/frontend.md.tpl +14 -9
- package/dist/templates/project/agents/i18n.md.tpl +185 -74
- package/dist/templates/project/agents/mail.md.tpl +3 -1
- package/dist/templates/project/agents/realtime.md.tpl +54 -31
- package/dist/templates/project/agents/seal.md.tpl +22 -4
- package/dist/templates/project/agents/security.md.tpl +1 -1
- package/dist/templates/project/agents/storage.md.tpl +23 -15
- package/dist/templates/project/agents/testing.md.tpl +5 -5
- package/dist/templates/project/agents/web.md.tpl +24 -11
- package/dist/templates/project/{locales → apps/web/locales}/en/frontend.json.tpl +1 -0
- package/dist/templates/project/{locales → apps/web/locales}/ko/frontend.json.tpl +1 -0
- package/dist/templates/project/gaon.config.ts.tpl +6 -5
- package/package.json +7 -7
- package/dist/templates/project/apps/web/locales/en.json.tpl +0 -3
- package/dist/templates/project/apps/web/locales/ko.json.tpl +0 -3
- /package/dist/templates/project/{locales → domain/locales}/en/backend.json.tpl +0 -0
- /package/dist/templates/project/{locales → domain/locales}/ko/backend.json.tpl +0 -0
|
@@ -74,7 +74,9 @@ npm i @gaonjs/seal # pnpm add @gaonjs/seal · yarn add @gaonjs/seal 동
|
|
|
74
74
|
2) 앱 설정에서 켠다:
|
|
75
75
|
|
|
76
76
|
```ts
|
|
77
|
-
// apps
|
|
77
|
+
// apps/web/app.config.ts — 앱 wire 전체 봉인(요청/응답 JSON + 최초 문서 data-page).
|
|
78
|
+
import { defineAppConfig } from 'gaonjs/config'
|
|
79
|
+
|
|
78
80
|
export default defineAppConfig({
|
|
79
81
|
seal: true, // 또는 { except: ['/webhooks/*'], strictQuery: true } — except = 외부가 seal 을 모르는 경로만 평문 통과 · strictQuery = 평문 쿼리도 거부(결정 354)
|
|
80
82
|
})
|
|
@@ -82,10 +84,15 @@ export default defineAppConfig({
|
|
|
82
84
|
3) 클라이언트를 배선한다:
|
|
83
85
|
|
|
84
86
|
```ts
|
|
85
|
-
// apps
|
|
87
|
+
// apps/web/main.ts — seal 클라이언트를 정적 import 해 createGaonApp 에 넘긴다.
|
|
86
88
|
import { createGaonApp } from 'gaonjs/vue'
|
|
87
89
|
import * as sealClient from '@gaonjs/seal/client' // 정적 import (사용자 vite 가 wasm 포함 번들)
|
|
88
|
-
|
|
90
|
+
import { routes } from './.gaon/routes.manifest.js'
|
|
91
|
+
|
|
92
|
+
const pages = import.meta.glob('./pages/**/*.vue')
|
|
93
|
+
const layouts = import.meta.glob('./layouts/*.vue', { eager: true })
|
|
94
|
+
|
|
95
|
+
void createGaonApp({ pages, layouts, routes, sealClient })
|
|
89
96
|
```
|
|
90
97
|
|
|
91
98
|
- **왜 main.ts 배선이 필요한가 (결정 124 · §3.1)**: `@gaonjs/vue` 는 선택 플러그인 seal 을 **몰라야** 한다
|
|
@@ -194,16 +201,27 @@ seal 앱 응답에만 `script-src` 에 `'wasm-unsafe-eval'` 을 **자동 주입*
|
|
|
194
201
|
|
|
195
202
|
```ts
|
|
196
203
|
// apps/web/app.config.ts — 앱 wire 전체 봉인.
|
|
204
|
+
import { defineAppConfig } from 'gaonjs/config'
|
|
205
|
+
|
|
197
206
|
export default defineAppConfig({ seal: true })
|
|
198
207
|
```
|
|
199
208
|
```ts
|
|
200
209
|
// apps/web/main.ts — seal 클라이언트 정적 import 를 createGaonApp 에 주입(결정 124 · doctor 가 강제·fixer 자동 배선).
|
|
201
210
|
import { createGaonApp } from 'gaonjs/vue'
|
|
202
211
|
import * as sealClient from '@gaonjs/seal/client'
|
|
203
|
-
|
|
212
|
+
import { routes } from './.gaon/routes.manifest.js'
|
|
213
|
+
|
|
214
|
+
const pages = import.meta.glob('./pages/**/*.vue')
|
|
215
|
+
const layouts = import.meta.glob('./layouts/*.vue', { eager: true })
|
|
216
|
+
|
|
217
|
+
void createGaonApp({ pages, layouts, routes, sealClient })
|
|
204
218
|
```
|
|
205
219
|
```ts
|
|
220
|
+
// apps/web/controllers/posts.ts
|
|
206
221
|
// 컨트롤러는 그대로 — seal 을 전혀 모른다(요청/응답 JSON·최초 문서 data-page·WS 프레임이 자동 봉인).
|
|
222
|
+
import { controller } from 'gaonjs/web'
|
|
223
|
+
import { Post } from '../../../domain/models/Post.js'
|
|
224
|
+
|
|
207
225
|
export default controller({
|
|
208
226
|
async index() {
|
|
209
227
|
return this.render('Posts/Index', { posts: await Post.latest().all() })
|
|
@@ -52,21 +52,26 @@
|
|
|
52
52
|
### 2. 설정 (`gaon.config.ts`)
|
|
53
53
|
|
|
54
54
|
```ts
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
55
|
+
// gaon.config.ts
|
|
56
|
+
import { defineConfig } from 'gaonjs/config'
|
|
57
|
+
|
|
58
|
+
export default defineConfig({
|
|
59
|
+
storage: process.env.STORAGE_ENDPOINT
|
|
60
|
+
? {
|
|
61
|
+
default: 'main',
|
|
62
|
+
disks: {
|
|
63
|
+
main: {
|
|
64
|
+
driver: 's3', // 's3' | 'local'
|
|
65
|
+
bucket: process.env.STORAGE_BUCKET ?? 'myapp',
|
|
66
|
+
endpoint: process.env.STORAGE_ENDPOINT, // MinIO/R2 = 필수 · AWS S3 = 생략
|
|
67
|
+
accessKeyId: process.env.STORAGE_ACCESS_KEY,
|
|
68
|
+
secretAccessKey: process.env.STORAGE_SECRET_KEY,
|
|
69
|
+
// publicUrl: 'https://cdn.example.com', // 있으면 url()이 공개 URL
|
|
70
|
+
},
|
|
66
71
|
},
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
}
|
|
73
|
+
: undefined,
|
|
74
|
+
})
|
|
70
75
|
```
|
|
71
76
|
|
|
72
77
|
- dev 는 compose 의 `createbuckets` 가 버킷을 만들어 **첫 업로드부터 동작**한다
|
|
@@ -86,6 +91,9 @@ storage: process.env.STORAGE_ENDPOINT
|
|
|
86
91
|
|
|
87
92
|
```ts
|
|
88
93
|
// apps/web/controllers/profile.ts
|
|
94
|
+
import { controller } from 'gaonjs/web'
|
|
95
|
+
import { Storage } from 'gaonjs/storage'
|
|
96
|
+
|
|
89
97
|
export default controller({
|
|
90
98
|
async updateAvatar() {
|
|
91
99
|
const f = this.file('avatar') // UploadedFile | undefined
|
|
@@ -139,7 +147,7 @@ export default controller({
|
|
|
139
147
|
|
|
140
148
|
## 정본 예시
|
|
141
149
|
|
|
142
|
-
```ts
|
|
150
|
+
```ts fragment
|
|
143
151
|
// 저장 → 공개/서명 URL 얻기(드라이버 무관 · 같은 코드). url() 은 async.
|
|
144
152
|
// 화면에 표시할 파일이면 키를 public/ 아래에 둔다 — 로컬 디스크의 서빙 범위가
|
|
145
153
|
// publicPrefix(기본 'public/')로 한정되기 때문이다(결정 401 · 밖이면 404).
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
|
|
57
57
|
### 3. 실 NATS 접속 관례
|
|
58
58
|
|
|
59
|
-
```ts
|
|
59
|
+
```ts test
|
|
60
60
|
import { connectNats } from 'gaonjs/async'
|
|
61
61
|
|
|
62
62
|
// 인자 없이 부르면 env NATS_URL(없으면 nats://127.0.0.1:4222)에 붙는다.
|
|
@@ -147,7 +147,7 @@ describe('PostPublished (실 NATS JetStream)', () => {
|
|
|
147
147
|
표준 하네스(`connectTestDatabase` · `config.nats` + `main` 커넥션)가 배선하므로, `gaon test`
|
|
148
148
|
로 도는 통합 테스트에서 그냥 성립한다.
|
|
149
149
|
|
|
150
|
-
```ts
|
|
150
|
+
```ts test
|
|
151
151
|
// service() 가 emit 하는 흐름도 같은 한 줄로 확증된다(트리거만 서비스 호출로 바꾸면 됨).
|
|
152
152
|
await expectEventProcessed(PostPublished, () => PublishPost.call({ postId: 1n }), { nats })
|
|
153
153
|
```
|
|
@@ -190,7 +190,7 @@ DB 테스트는 손으로 커넥션을 배선하지 않는다 — `gaon test`
|
|
|
190
190
|
|
|
191
191
|
스캐폴드가 심어 주는 `test/setup.ts`(수정 불필요):
|
|
192
192
|
|
|
193
|
-
```ts
|
|
193
|
+
```ts test
|
|
194
194
|
import { afterAll, afterEach, beforeAll } from 'vitest'
|
|
195
195
|
import { connectTestDatabase, truncateAllConnections, type TestDbHandle } from 'gaonjs/testing'
|
|
196
196
|
|
|
@@ -273,7 +273,7 @@ describe('AuditLog(실 mongod)', () => {
|
|
|
273
273
|
서비스가 반환하기 **전에** 실행되므로, 테스트는 서비스 호출을 `await` 한 직후 몽고
|
|
274
274
|
문서를 단언하면 된다(별도 대기·폴링 불필요).
|
|
275
275
|
|
|
276
|
-
```ts
|
|
276
|
+
```ts test
|
|
277
277
|
// SQL 커밋 → 몽고 감사 로그 순서를 한 흐름으로 확증한다.
|
|
278
278
|
const user = await SignUp.call({ email: 'a@b.c', password: 'x' }) // main(SQL) 커밋 + afterCommit
|
|
279
279
|
expect(await AuditLog.countDocuments({ actorId: String(user.id) })).toBe(1)
|
|
@@ -298,7 +298,7 @@ expect(await AuditLog.countDocuments({ actorId: 'dup' })).toBe(0)
|
|
|
298
298
|
정본 = "직렬화 결과 어디에도 hidden 컬럼명이 없다" 를 **재귀로** 단언한다
|
|
299
299
|
(배열·중첩 관계 전부 훑는 헬퍼). 관계를 포함한 픽스처에 적용한다:
|
|
300
300
|
|
|
301
|
-
```ts
|
|
301
|
+
```ts test
|
|
302
302
|
// 직렬화 결과(JSON-safe)에 hidden 컬럼명이 어느 깊이에도 없음을 확인.
|
|
303
303
|
function assertNoHiddenLeak(value: unknown, hidden: readonly string[]): void {
|
|
304
304
|
const walk = (v: unknown): void => {
|
|
@@ -231,7 +231,7 @@ handler 안에서 파싱). **결정 342 이후 헤더 부착은 프레임웍 자
|
|
|
231
231
|
(참고: 지원 Content-Type 은 `application/json` · `multipart/form-data` 뿐이라
|
|
232
232
|
`x-www-form-urlencoded` 로 폼을 보내면 415 로 거부된다 · `inertia.ts` · §아래 415.)
|
|
233
233
|
|
|
234
|
-
```vue
|
|
234
|
+
```vue fragment
|
|
235
235
|
<script setup lang="ts">
|
|
236
236
|
import { useForm } from 'gaonjs/vue'
|
|
237
237
|
const form = useForm({ avatar: null as File | null })
|
|
@@ -259,8 +259,10 @@ redirect 로 처리한다 — 전체 페이지 리로드도, 별도 REST 엔드
|
|
|
259
259
|
폼 API 는 **`gaonjs/vue` 의 `useForm`·`router`** 뿐이다(결정 64) —
|
|
260
260
|
`@inertiajs/vue3` 를 직접 import 하지 않는다(파사드가 The One Way).
|
|
261
261
|
|
|
262
|
-
```ts
|
|
262
|
+
```ts fragment
|
|
263
263
|
// 로그인 폼 — 제출은 Inertia SPA 방식, 서버는 redirect 로 답한다.
|
|
264
|
+
import { pageProps, router, useForm } from 'gaonjs/vue'
|
|
265
|
+
|
|
264
266
|
// pageProps 는 반응형 — 변수로 받아 props.x 로 접근한다(구조분해 금지 · 결정 99).
|
|
265
267
|
// CSRF 토큰은 프레임웍이 자동 부착한다(결정 342) — _csrf 바디도, 수동 헤더도 없다.
|
|
266
268
|
const props = pageProps<'web:session#new'>()
|
|
@@ -282,7 +284,7 @@ Inertia 폼(`useForm(...).post()`)의 검증 실패는 컨트롤러가 손으로
|
|
|
282
284
|
방문의 `errors` 로 `form.errors.<필드>` 를 **자동으로 채운다**. 입력값은
|
|
283
285
|
`useForm` 이 그대로 보존한다(재제출 방지). 컨트롤러는 성공 경로만 쓴다:
|
|
284
286
|
|
|
285
|
-
```ts
|
|
287
|
+
```ts controller-action
|
|
286
288
|
// 컨트롤러 — 검증 실패 분기를 손으로 쓰지 않는다(결정 109).
|
|
287
289
|
async create() {
|
|
288
290
|
const data = this.params(Post.form.pick('title', 'body')) // 실패 시 프레임웍이 303 back
|
|
@@ -291,10 +293,18 @@ async create() {
|
|
|
291
293
|
}
|
|
292
294
|
```
|
|
293
295
|
|
|
294
|
-
```vue
|
|
296
|
+
```vue fragment
|
|
295
297
|
<!-- 페이지 — form.errors.<필드> 는 서버 검증 실패 시 자동으로 채워진다. -->
|
|
296
|
-
<
|
|
297
|
-
|
|
298
|
+
<script setup lang="ts">
|
|
299
|
+
import { useForm } from 'gaonjs/vue'
|
|
300
|
+
|
|
301
|
+
const form = useForm({ title: '', body: '' })
|
|
302
|
+
</script>
|
|
303
|
+
|
|
304
|
+
<template>
|
|
305
|
+
<input v-model="form.title" />
|
|
306
|
+
<p v-if="form.errors.title">{{ form.errors.title }}</p>
|
|
307
|
+
</template>
|
|
298
308
|
```
|
|
299
309
|
|
|
300
310
|
순수 JSON/API 앱(X-Inertia 아님·세션 없음)은 기존대로 **422 JSON** 을 받는다.
|
|
@@ -302,8 +312,9 @@ async create() {
|
|
|
302
312
|
#### 검증 사유 로케일화 — 예약 namespace `validation.*` (결정 183)
|
|
303
313
|
|
|
304
314
|
검증 실패의 **필드별 사유**(`form.errors.<필드>` 로 엔드유저에 노출되는 부분)는 요청
|
|
305
|
-
로케일로 번역된다 — `i18n` 이 설정돼 있고
|
|
306
|
-
`validation.<code>`** 로 번역을
|
|
315
|
+
로케일로 번역된다 — `i18n` 이 설정돼 있고 프로젝트가 `domain/locales/<로케일>/backend.json`
|
|
316
|
+
의 **예약 namespace `validation.<code>`** 로 번역을 제공하면(결정 459 — 검증 문안은 화면
|
|
317
|
+
밖 문구라 도메인이 소유한다). 프레임웍은 **안정적 코드만** 노출하고 번역은
|
|
307
318
|
앱 몫이다(The One Way: 코드는 프레임웍, 문안은 앱). 키가 없으면 내장 fallback(한국어)로
|
|
308
319
|
떨어져 거동이 보존된다. i18n 미설정이면 항상 fallback.
|
|
309
320
|
|
|
@@ -320,7 +331,7 @@ async create() {
|
|
|
320
331
|
| `invalid` | — | 그 외 coerce 예외(비-CoerceError) |
|
|
321
332
|
|
|
322
333
|
```json
|
|
323
|
-
// locales/en.json —
|
|
334
|
+
// domain/locales/en/backend.json — 검증 문안을 로케일별로 준다(i18next {{max}} 보간).
|
|
324
335
|
{ "validation": {
|
|
325
336
|
"required": "This field is required.",
|
|
326
337
|
"too_long": "At most {{max}} characters (got {{len}})."
|
|
@@ -381,6 +392,8 @@ async create() {
|
|
|
381
392
|
|
|
382
393
|
```ts
|
|
383
394
|
// apps/web/app.config.ts
|
|
395
|
+
import { defineAppConfig } from 'gaonjs/config'
|
|
396
|
+
|
|
384
397
|
export default defineAppConfig({
|
|
385
398
|
sharedProps: (ctx) => ({ locale: ctx.session?.locale ?? 'en', theme: 'dark' }),
|
|
386
399
|
})
|
|
@@ -414,7 +427,7 @@ shared.locale // 로그인/로그아웃·플래시로
|
|
|
414
427
|
메서드(스코프)**, 대등한 조합이면 `domain/services/`(`agents/data.md` §8). 컨트롤러가
|
|
415
428
|
`pluck`·교집합·사이드바 질의를 인라인 조립하기 시작하면 서비스/스코프로 옮긴다.
|
|
416
429
|
|
|
417
|
-
```ts
|
|
430
|
+
```ts fragment
|
|
418
431
|
// ❌ 컨트롤러가 검색 교집합·태그 필터를 인라인 조립
|
|
419
432
|
// ✅ const rows = await Post.searchPublished(term).latest().offset(o).limit(n).all()
|
|
420
433
|
// ✅ 페이지네이션은 스코프 체인 종단 paginate — 컨트롤러는 여전히 한 줄(결정 119)
|
|
@@ -450,7 +463,7 @@ shared.locale // 로그인/로그아웃·플래시로
|
|
|
450
463
|
base64·해시를 손으로 짜지 말고** `gaonjs/web` 의 헬퍼를 쓴다(The One Way · §7 인증).
|
|
451
464
|
bcrypt(cost 10)로 해싱하며 상수시간 비교를 제공한다.
|
|
452
465
|
|
|
453
|
-
```ts
|
|
466
|
+
```ts fragment
|
|
454
467
|
import { hashPassword, verifyPassword } from 'gaonjs/web'
|
|
455
468
|
|
|
456
469
|
// 가입 — 서비스에서 평문을 해싱해 hidden 컬럼(passwordDigest)에 저장.
|
|
@@ -54,11 +54,12 @@ export default defineConfig({
|
|
|
54
54
|
}
|
|
55
55
|
: undefined,
|
|
56
56
|
|
|
57
|
-
// 다국어(§7 · 결정
|
|
58
|
-
// locales/<로케일>/frontend.json — 화면 문구. 클라 t()(gaonjs/vue)
|
|
59
|
-
// locales/<로케일>/backend.json —
|
|
60
|
-
//
|
|
61
|
-
// 요청 로케일은 세션>쿠키>Accept-Language 로
|
|
57
|
+
// 다국어(§7 · 결정 459). 문구의 **소유자**가 위치를 정한다 — 화면이면 그 앱, 화면 밖이면 domain:
|
|
58
|
+
// apps/<앱>/locales/<로케일>/frontend.json — 그 앱 화면 문구. 클라 t()(gaonjs/vue) · 그 앱 청크로만 나간다.
|
|
59
|
+
// apps/<앱>/locales/<로케일>/backend.json — 그 앱 컨트롤러 전용 서버 문구(선택).
|
|
60
|
+
// domain/locales/<로케일>/backend.json — 메일·잡·검증 등 도메인 공통 **서버 전용**. 클라로 나가지 않는다.
|
|
61
|
+
// 위치는 관례로 고정이라 dir 설정이 없다. 요청 로케일은 세션>쿠키>Accept-Language 로
|
|
62
|
+
// 자동 협상된다(결정 159 · this.setLocale 로 전환).
|
|
62
63
|
i18n: { fallbackLng: 'ko', supportedLngs: ['ko', 'en'] },
|
|
63
64
|
|
|
64
65
|
// 웹 서버 리슨 옵션. --port · env PORT 로 덮을 수 있다.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gaonjs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.65.0",
|
|
4
4
|
"description": "Gaon CLI — 스캐폴딩·제너레이터·마이그레이션·dev/serve/work/hub·doctor·check (bin: gaon)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -33,14 +33,14 @@
|
|
|
33
33
|
"typescript": "^5.9.0",
|
|
34
34
|
"vite": "^7.0.0",
|
|
35
35
|
"@gaonjs/async": "0.22.0",
|
|
36
|
-
"@gaonjs/config": "0.
|
|
36
|
+
"@gaonjs/config": "0.26.0",
|
|
37
37
|
"@gaonjs/core": "0.3.0",
|
|
38
|
-
"@gaonjs/
|
|
39
|
-
"@gaonjs/
|
|
40
|
-
"@gaonjs/
|
|
41
|
-
"@gaonjs/
|
|
38
|
+
"@gaonjs/i18n": "0.5.0",
|
|
39
|
+
"@gaonjs/mail": "0.5.4",
|
|
40
|
+
"@gaonjs/web": "0.33.0",
|
|
41
|
+
"@gaonjs/data": "0.26.2"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
|
-
"build": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json && node -e \"const fs=require('fs');fs.cpSync('src/templates','dist/templates',{recursive:true,filter:(s)=>!s.endsWith('.ts')});fs.rmSync('dist/templates/index.ts',{force:true})\""
|
|
44
|
+
"build": "node ../../node_modules/typescript/bin/tsc -p tsconfig.json && node -e \"const fs=require('fs');fs.rmSync('dist/templates/project',{recursive:true,force:true});fs.cpSync('src/templates','dist/templates',{recursive:true,filter:(s)=>!s.endsWith('.ts')});fs.rmSync('dist/templates/index.ts',{force:true})\""
|
|
45
45
|
}
|
|
46
46
|
}
|
|
File without changes
|
|
File without changes
|