Initial commit
This commit is contained in:
@@ -0,0 +1,96 @@
|
|||||||
|
---
|
||||||
|
name: "agent-browser"
|
||||||
|
description: "브라우저 자동화 CLI를 사용해 웹사이트를 탐색하고 조작합니다. 페이지 이동, 폼 입력, 클릭, 스크린샷, 데이터 추출, 웹앱 테스트가 필요할 때 호출합니다."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Agent Browser
|
||||||
|
|
||||||
|
에이전트를 위한 빠른 브라우저 자동화 CLI 가이드입니다. 웹사이트 탐색, 폼 입력, 버튼 클릭, 스크린샷 촬영, 데이터 추출, 웹앱 테스트, 탐색적 QA 등에 사용합니다.
|
||||||
|
|
||||||
|
## 언제 사용할지
|
||||||
|
|
||||||
|
다음과 같은 요청에서 우선 사용합니다.
|
||||||
|
|
||||||
|
- 웹사이트를 열어 달라고 할 때
|
||||||
|
- 폼을 입력하거나 버튼을 클릭해야 할 때
|
||||||
|
- 페이지 스크린샷이 필요할 때
|
||||||
|
- 웹페이지 데이터를 추출하거나 스크래핑할 때
|
||||||
|
- 웹앱을 실제 브라우저에서 테스트할 때
|
||||||
|
- 로그인, QA, 버그 재현, 탐색적 테스트가 필요할 때
|
||||||
|
|
||||||
|
Electron 기반 데스크톱 앱이나 Slack 자동화 같은 확장 작업에도 활용할 수 있습니다.
|
||||||
|
|
||||||
|
## 시작 방법
|
||||||
|
|
||||||
|
`agent-browser` 명령을 바로 사용하기 전에, 설치된 버전에 맞는 실제 가이드를 CLI에서 불러옵니다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
agent-browser skills get core
|
||||||
|
```
|
||||||
|
|
||||||
|
전체 명령과 템플릿까지 포함해 보고 싶다면:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
agent-browser skills get core --full
|
||||||
|
```
|
||||||
|
|
||||||
|
이 스킬 문서는 시작점 역할만 합니다. 실제 사용 절차와 최신 명령 형식은 항상 CLI가 제공하는 가이드를 기준으로 확인합니다.
|
||||||
|
|
||||||
|
## 특수 가이드
|
||||||
|
|
||||||
|
작업 성격에 따라 아래 보조 가이드를 불러올 수 있습니다.
|
||||||
|
|
||||||
|
- `agent-browser skills get electron`
|
||||||
|
- `agent-browser skills get slack`
|
||||||
|
- `agent-browser skills get dogfood`
|
||||||
|
- `agent-browser skills get vercel-sandbox`
|
||||||
|
- `agent-browser skills get agentcore`
|
||||||
|
|
||||||
|
사용 가능한 전체 목록은 아래 명령으로 확인합니다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
agent-browser skills list
|
||||||
|
```
|
||||||
|
|
||||||
|
## 기본 사용 흐름
|
||||||
|
|
||||||
|
1. 먼저 `agent-browser skills get core`로 현재 버전 가이드를 읽습니다.
|
||||||
|
2. 작업이 웹 브라우저인지, Electron인지, Slack인지 성격을 구분합니다.
|
||||||
|
3. 필요하면 해당 특수 가이드를 추가로 읽습니다.
|
||||||
|
4. 세션을 열고 탐색, 선택, 입력, 클릭, 추출 같은 실제 작업을 진행합니다.
|
||||||
|
5. 스크린샷, 상태 저장, 기록 기능이 필요하면 그 옵션을 함께 사용합니다.
|
||||||
|
|
||||||
|
## 장점
|
||||||
|
|
||||||
|
- 빠른 네이티브 CLI 기반 동작
|
||||||
|
- Chrome/Chromium 제어
|
||||||
|
- 접근성 트리 기반 스냅샷과 안정적인 요소 참조
|
||||||
|
- 세션 유지, 인증 저장, 상태 지속성 지원
|
||||||
|
- 영상 기록 및 다양한 특수 자동화 시나리오 지원
|
||||||
|
|
||||||
|
## 운영 원칙
|
||||||
|
|
||||||
|
- 내장 웹 도구보다 `agent-browser`가 더 적합한 작업이면 이를 우선 사용합니다.
|
||||||
|
- 현재 설치된 버전의 CLI가 제공하는 가이드를 기준으로 작업합니다.
|
||||||
|
- 오래된 문서 기억보다, 실행 중인 버전의 `skills get` 결과를 신뢰합니다.
|
||||||
|
|
||||||
|
## 언제 특히 유용한가
|
||||||
|
|
||||||
|
- 실제 브라우저 상호작용이 길거나 반복적일 때
|
||||||
|
- 접근성 트리 기준의 안정적인 요소 선택이 필요할 때
|
||||||
|
- 상태 지속, 인증, 녹화, 세션 관리가 중요한 테스트일 때
|
||||||
|
- 단순 스크래핑이 아니라 탐색적 QA나 실제 사용자 흐름 재현이 필요할 때
|
||||||
|
|
||||||
|
## 안티 패턴
|
||||||
|
|
||||||
|
- 설치된 버전을 확인하지 않고 예전 명령 기억으로 바로 실행하는 방식
|
||||||
|
- 일반 웹 도구로 충분한 작업인데 굳이 복잡한 세션을 여는 방식
|
||||||
|
- 작업 성격이 특수 가이드 대상인데 core만 읽고 끝내는 방식
|
||||||
|
|
||||||
|
## 기대 결과
|
||||||
|
|
||||||
|
이 스킬을 적용한 결과는 다음을 만족해야 합니다.
|
||||||
|
|
||||||
|
- 현재 설치된 버전 기준으로 정확한 사용 절차를 따른다
|
||||||
|
- 작업 유형에 맞는 특수 가이드를 선택한다
|
||||||
|
- 브라우저 자동화가 실제 목표에 맞게 안정적으로 수행된다
|
||||||
@@ -0,0 +1,191 @@
|
|||||||
|
---
|
||||||
|
name: "brainstorming"
|
||||||
|
description: "구현 전에 아이디어를 질문과 설계안으로 구체화합니다. 기능 추가, 동작 변경, 신규 개발처럼 창의적 작업을 시작하기 전에 반드시 호출합니다."
|
||||||
|
---
|
||||||
|
|
||||||
|
# 아이디어를 설계로 구체화하기
|
||||||
|
|
||||||
|
자연스러운 협업형 대화를 통해 아이디어를 완성도 높은 설계와 명세로 바꿉니다.
|
||||||
|
|
||||||
|
먼저 현재 프로젝트 문맥을 이해한 뒤, 질문을 한 번에 하나씩 하면서 아이디어를 구체화합니다. 무엇을 만들지 충분히 이해했다면 설계를 제시하고 사용자 승인을 받습니다.
|
||||||
|
|
||||||
|
<HARD-GATE>
|
||||||
|
설계를 제시하고 사용자 승인을 받기 전까지는 구현 스킬 호출, 코드 작성, 프로젝트 스캐폴딩, 기타 구현 행동을 해서는 안 됩니다. 이 규칙은 작업이 얼마나 단순해 보이든 항상 적용됩니다.
|
||||||
|
</HARD-GATE>
|
||||||
|
|
||||||
|
## 안티 패턴: "이건 너무 단순해서 설계가 필요 없다"
|
||||||
|
|
||||||
|
모든 작업은 이 과정을 거쳐야 합니다. 할 일 목록, 단일 함수 유틸리티, 설정 변경도 예외가 아닙니다. "단순한" 작업일수록 검증되지 않은 가정 때문에 불필요한 재작업이 생기기 쉽습니다. 설계는 짧아도 되지만, 반드시 제시하고 승인을 받아야 합니다.
|
||||||
|
|
||||||
|
## 체크리스트
|
||||||
|
|
||||||
|
아래 항목별로 작업을 만들고, 순서대로 완료해야 합니다.
|
||||||
|
|
||||||
|
1. **프로젝트 문맥 탐색** - 파일, 문서, 최근 커밋 확인
|
||||||
|
2. **시각 보조 도구 제안** - 시각적 질문이 예상되면 별도 메시지로 제안
|
||||||
|
3. **명확화 질문 진행** - 한 번에 하나씩 질문하며 목적, 제약, 성공 기준 파악
|
||||||
|
4. **2~3개 접근법 제안** - 트레이드오프와 추천안 포함
|
||||||
|
5. **설계 제시** - 복잡도에 맞춰 섹션별로 설명하고 승인 받기
|
||||||
|
6. **설계 문서 작성** - `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md`에 저장
|
||||||
|
7. **명세 자체 점검** - placeholder, 모순, 모호성, 범위 확인
|
||||||
|
8. **사용자 문서 검토** - 구현 전 설계 문서 검토 요청
|
||||||
|
9. **구현 단계 전환** - 다음 단계로 `writing-plans` 스킬 호출
|
||||||
|
|
||||||
|
## 프로세스 흐름
|
||||||
|
|
||||||
|
기본 흐름은 아래와 같습니다.
|
||||||
|
|
||||||
|
1. 현재 프로젝트 문맥을 탐색합니다.
|
||||||
|
2. 필요한 경우 시각 보조 도구를 별도 메시지로 제안합니다.
|
||||||
|
3. 질문을 한 번에 하나씩 하며 요구를 구체화합니다.
|
||||||
|
4. 2~3개의 접근법을 비교하고 추천안을 제시합니다.
|
||||||
|
5. 설계를 섹션 단위로 설명하며 단계적으로 승인을 받습니다.
|
||||||
|
6. 승인된 설계를 문서로 저장합니다.
|
||||||
|
7. 문서를 스스로 점검해 빈칸, 모순, 모호함을 제거합니다.
|
||||||
|
8. 사용자에게 문서 검토를 요청합니다.
|
||||||
|
9. 승인이 끝나면 다음 단계로 `writing-plans`를 호출합니다.
|
||||||
|
|
||||||
|
## 프로세스
|
||||||
|
|
||||||
|
**아이디어 이해**
|
||||||
|
|
||||||
|
- 먼저 현재 프로젝트 상태를 확인합니다.
|
||||||
|
- 요청이 여러 독립 하위 시스템을 포함하면, 세부 질문 전에 먼저 분해가 필요한지 판단합니다.
|
||||||
|
- 하나의 명세로 다루기 너무 크다면 하위 프로젝트로 나누고, 첫 번째 하위 프로젝트만 일반 설계 흐름으로 진행합니다.
|
||||||
|
- 적절한 범위라면 질문을 한 번에 하나씩 하며 구체화합니다.
|
||||||
|
- 가능하면 객관식 질문을 우선 사용하되, 필요하면 서술형도 사용합니다.
|
||||||
|
- 각 메시지에는 질문 하나만 포함합니다.
|
||||||
|
- 목적, 제약 조건, 성공 기준을 이해하는 데 집중합니다.
|
||||||
|
- 너무 일찍 구현 방향으로 뛰어들지 말고, 범위와 기대 결과를 먼저 고정합니다.
|
||||||
|
- "간단해 보인다"는 이유로 질문 단계를 생략하지 않습니다.
|
||||||
|
|
||||||
|
**접근법 탐색**
|
||||||
|
|
||||||
|
- 서로 다른 2~3개의 접근법을 제안합니다.
|
||||||
|
- 각 접근법의 장단점과 추천 이유를 함께 설명합니다.
|
||||||
|
- 추천안을 먼저 제시하고, 왜 그 선택이 적합한지 설명합니다.
|
||||||
|
- 접근법은 기능 관점뿐 아니라 유지보수, 테스트 용이성, 사용자 경험까지 함께 비교합니다.
|
||||||
|
|
||||||
|
**설계 제시**
|
||||||
|
|
||||||
|
- 무엇을 만들지 충분히 이해했다면 설계를 제시합니다.
|
||||||
|
- 각 섹션은 복잡도에 맞게 짧게 또는 자세히 설명합니다.
|
||||||
|
- 각 섹션 뒤에는 지금까지 맞는지 확인을 요청합니다.
|
||||||
|
- 아키텍처, 구성요소, 데이터 흐름, 오류 처리, 테스트를 다룹니다.
|
||||||
|
- 이해되지 않는 부분이 있으면 다시 질문하고 명확히 합니다.
|
||||||
|
- 한 번에 모든 내용을 밀어 넣기보다, 사용자가 따라올 수 있는 단위로 나눠 검증합니다.
|
||||||
|
|
||||||
|
**격리와 명확성을 고려한 설계**
|
||||||
|
|
||||||
|
- 시스템을 명확한 목적을 가진 작은 단위로 나눕니다.
|
||||||
|
- 각 단위는 잘 정의된 인터페이스로 통신하고, 독립적으로 이해 및 테스트 가능해야 합니다.
|
||||||
|
- 각 단위에 대해 "무엇을 하는가, 어떻게 사용하는가, 무엇에 의존하는가"를 설명할 수 있어야 합니다.
|
||||||
|
- 내부 구현을 몰라도 역할을 이해할 수 있어야 하며, 내부를 바꿔도 소비자가 깨지지 않아야 합니다.
|
||||||
|
- 파일이 지나치게 커지면 책임이 과도하게 섞였다는 신호로 봅니다.
|
||||||
|
|
||||||
|
**기존 코드베이스에서 작업할 때**
|
||||||
|
|
||||||
|
- 변경 제안 전에 현재 구조와 패턴을 먼저 탐색합니다.
|
||||||
|
- 현재 작업에 영향을 주는 구조적 문제는 설계에 포함해 개선할 수 있습니다.
|
||||||
|
- 현재 목표와 무관한 리팩터링은 제안하지 않습니다.
|
||||||
|
|
||||||
|
## 설계 제시 규칙
|
||||||
|
|
||||||
|
- 섹션별로 끊어서 설명하고, 각 섹션 뒤에 확인을 받습니다.
|
||||||
|
- 설계는 복잡도에 맞춰 짧게 또는 자세히 조절합니다.
|
||||||
|
- 구조, 데이터 흐름, 오류 처리, 테스트 전략을 빠뜨리지 않습니다.
|
||||||
|
- 사용자가 "지금까지 맞다"고 확인하기 전까지 다음 단계로 밀어붙이지 않습니다.
|
||||||
|
|
||||||
|
## 금지 사항
|
||||||
|
|
||||||
|
- 설계 승인 전에 구현 스킬 호출
|
||||||
|
- 설계 승인 전에 코드 작성
|
||||||
|
- 질문 여러 개를 한 번에 던지는 방식
|
||||||
|
- 대안 비교 없이 단일 해법만 제시하는 방식
|
||||||
|
- 현재 목표와 무관한 대형 리팩터링 제안
|
||||||
|
- 사용자 검토 없이 바로 구현 계획 또는 구현으로 넘어가는 방식
|
||||||
|
|
||||||
|
## 설계 후 단계
|
||||||
|
|
||||||
|
**문서화**
|
||||||
|
|
||||||
|
- 승인된 설계를 `docs/superpowers/specs/YYYY-MM-DD-<topic>-design.md`에 작성합니다.
|
||||||
|
- 사용자가 다른 문서 위치를 선호하면 그 경로를 우선합니다.
|
||||||
|
- 가능하면 명확하고 간결한 문체를 유지합니다.
|
||||||
|
|
||||||
|
**명세 자체 점검**
|
||||||
|
|
||||||
|
설계 문서를 작성한 뒤 아래 항목을 빠르게 확인합니다.
|
||||||
|
|
||||||
|
1. **Placeholder 점검** - `TBD`, `TODO`, 미완성 섹션, 모호한 요구사항이 있는지 확인
|
||||||
|
2. **내부 일관성 점검** - 섹션끼리 모순되지 않는지 확인
|
||||||
|
3. **범위 점검** - 단일 구현 계획으로 충분한 범위인지 확인
|
||||||
|
4. **모호성 점검** - 여러 해석이 가능한 요구사항을 하나로 명확히 고정
|
||||||
|
|
||||||
|
문제가 있으면 문서 안에서 바로 수정하고 진행합니다.
|
||||||
|
|
||||||
|
**사용자 검토 게이트**
|
||||||
|
|
||||||
|
설계 검토가 끝나면, 구현 계획으로 넘어가기 전에 사용자에게 작성된 명세 문서를 검토해 달라고 요청합니다.
|
||||||
|
|
||||||
|
예시 문구:
|
||||||
|
|
||||||
|
> 명세를 `<path>` 에 작성했습니다. 구현 계획을 쓰기 전에 변경하고 싶은 점이 있는지 검토해 주세요.
|
||||||
|
|
||||||
|
사용자가 승인할 때까지 기다립니다. 변경 요청이 있으면 수정하고 다시 점검합니다.
|
||||||
|
|
||||||
|
**구현 전환**
|
||||||
|
|
||||||
|
- 다음 단계에서는 `writing-plans` 스킬을 호출합니다.
|
||||||
|
- 다른 구현 스킬을 먼저 호출하지 않습니다.
|
||||||
|
|
||||||
|
## 기대 결과
|
||||||
|
|
||||||
|
이 스킬의 결과물은 다음을 만족해야 합니다.
|
||||||
|
|
||||||
|
- 사용자 의도가 구조화된 설계 문장으로 정리되어 있어야 합니다.
|
||||||
|
- 구현 전에 범위와 성공 기준이 합의되어 있어야 합니다.
|
||||||
|
- 설계 문서만 읽어도 다음 단계에서 무엇을 구현할지 알 수 있어야 합니다.
|
||||||
|
- 설계 문서는 placeholder 없이 구체적이어야 합니다.
|
||||||
|
|
||||||
|
## 핵심 원칙
|
||||||
|
|
||||||
|
- **한 번에 한 질문** - 여러 질문을 한꺼번에 던지지 않습니다.
|
||||||
|
- **객관식 우선** - 답하기 쉬운 형식을 우선합니다.
|
||||||
|
- **YAGNI 준수** - 불필요한 기능은 설계에서 제거합니다.
|
||||||
|
- **대안 비교** - 항상 2~3개의 접근법을 제시합니다.
|
||||||
|
- **점진적 검증** - 설계를 제시하고 승인받은 뒤 다음으로 진행합니다.
|
||||||
|
- **유연한 명확화** - 이해가 부족하면 다시 질문합니다.
|
||||||
|
|
||||||
|
## 안티 패턴 신호
|
||||||
|
|
||||||
|
다음 생각이 들면 흐름을 다시 점검합니다.
|
||||||
|
|
||||||
|
- 이건 너무 간단해서 설계가 필요 없다
|
||||||
|
- 구현하면서 알아가도 된다
|
||||||
|
- 사용자 의도는 아마 이럴 것이다
|
||||||
|
- 설계 문서는 나중에 써도 된다
|
||||||
|
- 구현 스킬부터 불러도 큰 문제는 없다
|
||||||
|
|
||||||
|
이런 생각은 대부분 브레인스토밍 단계를 건너뛰려는 신호입니다.
|
||||||
|
|
||||||
|
## 시각 보조 도구
|
||||||
|
|
||||||
|
목업, 다이어그램, 시각 옵션을 보여주는 브라우저 기반 보조 수단입니다. 이는 별도 모드가 아니라 필요할 때 사용하는 도구입니다.
|
||||||
|
|
||||||
|
**제안 시점**
|
||||||
|
|
||||||
|
앞으로의 질문이 시각 요소를 포함할 가능성이 높다면, 아래 문구를 반드시 **단독 메시지**로 제안합니다.
|
||||||
|
|
||||||
|
> 지금 논의하는 내용 중 일부는 웹 브라우저에서 직접 보여드리면 더 이해하기 쉬울 수 있습니다. 진행하면서 목업, 다이어그램, 비교안 같은 시각 자료를 준비할 수 있습니다. 아직 새 기능이라 토큰 사용량이 많을 수 있는데, 사용해 보시겠어요? (로컬 URL 열기 필요)
|
||||||
|
|
||||||
|
이 제안 메시지에는 다른 질문, 요약, 설명을 섞지 않습니다. 사용자의 답변을 기다린 뒤 계속 진행합니다.
|
||||||
|
|
||||||
|
**질문별 판단**
|
||||||
|
|
||||||
|
사용자가 수락했더라도 모든 질문을 브라우저로 처리하지는 않습니다. 매 질문마다 "읽는 것보다 보는 것이 더 이해하기 쉬운가?"를 기준으로 판단합니다.
|
||||||
|
|
||||||
|
- 브라우저 사용: 목업, 와이어프레임, 레이아웃 비교, 아키텍처 다이어그램 같은 시각 중심 내용
|
||||||
|
- 터미널 사용: 요구사항 질문, 개념적 선택지, 트레이드오프 설명, 범위 결정 같은 텍스트 중심 내용
|
||||||
|
|
||||||
|
자세한 사용법은 [visual-companion.md](./visual-companion.md)를 참고합니다.
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
# 시각 보조 도구 가이드
|
||||||
|
|
||||||
|
브레인스토밍 중 목업, 다이어그램, 비교안을 보여주기 위한 브라우저 기반 시각 보조 가이드입니다.
|
||||||
|
|
||||||
|
## 언제 사용할지
|
||||||
|
|
||||||
|
세션 단위가 아니라 질문 단위로 판단합니다. 기준은 하나입니다. **읽는 것보다 보는 것이 더 이해하기 쉬운가?**
|
||||||
|
|
||||||
|
**브라우저를 사용할 때**
|
||||||
|
|
||||||
|
- UI 목업, 와이어프레임, 레이아웃, 내비게이션 구조
|
||||||
|
- 시스템 구성도, 데이터 흐름도, 관계도
|
||||||
|
- 레이아웃이나 스타일의 시각적 비교
|
||||||
|
- 간격, 시각적 위계, 분위기 같은 디자인 완성도 논의
|
||||||
|
- 상태 전이, 흐름도, 엔터티 관계처럼 공간적 관계가 중요한 설명
|
||||||
|
|
||||||
|
**터미널을 사용할 때**
|
||||||
|
|
||||||
|
- 요구사항과 범위 질문
|
||||||
|
- 개념적 A/B/C 선택
|
||||||
|
- 장단점 비교와 트레이드오프 정리
|
||||||
|
- API 설계, 데이터 모델링, 구조 선택 같은 기술 의사결정
|
||||||
|
- 시각 취향이 아니라 언어 설명이 필요한 명확화 질문
|
||||||
|
|
||||||
|
UI 주제에 대한 질문이라고 해서 자동으로 시각 질문이 되는 것은 아닙니다. "어떤 종류의 위저드를 원하나요?"는 개념 질문이고, "이 위저드 레이아웃 중 무엇이 더 맞나요?"는 시각 질문입니다.
|
||||||
|
|
||||||
|
## 진행 방식
|
||||||
|
|
||||||
|
서버는 HTML 파일이 저장되는 디렉터리를 감시하고 가장 최근 파일을 브라우저에 제공합니다. 화면 내용은 `screen_dir`에 기록하고, 사용자의 클릭/선택 결과는 `state_dir/events`에 저장됩니다.
|
||||||
|
|
||||||
|
**콘텐츠 조각과 전체 문서**
|
||||||
|
|
||||||
|
- HTML이 `<!DOCTYPE` 또는 `<html`로 시작하면 전체 문서로 그대로 제공합니다.
|
||||||
|
- 그렇지 않으면 서버가 기본 프레임 템플릿으로 감싸서 보여줍니다.
|
||||||
|
- 기본적으로는 전체 HTML 문서보다 **콘텐츠 조각** 작성 방식을 사용합니다.
|
||||||
|
|
||||||
|
## 세션 시작
|
||||||
|
|
||||||
|
- 서버를 시작하고 응답에서 `url`, `screen_dir`, `state_dir`를 저장합니다.
|
||||||
|
- 사용자에게 URL을 열어 달라고 안내합니다.
|
||||||
|
- 가능하면 프로젝트 루트를 기준으로 실행해 결과물이 프로젝트 안에 유지되게 합니다.
|
||||||
|
- 환경상 URL 접근이 어렵다면 호스트 설정을 조정합니다.
|
||||||
|
|
||||||
|
## 반복 루프
|
||||||
|
|
||||||
|
1. 서버가 살아 있는지 확인하고 `screen_dir`에 새 HTML 파일을 씁니다.
|
||||||
|
2. 사용자에게 URL과 현재 화면에 무엇이 보이는지 짧게 설명합니다.
|
||||||
|
3. 사용자가 터미널에서 응답하면 `state_dir/events`를 읽어 브라우저 상호작용을 확인합니다.
|
||||||
|
4. 피드백에 따라 현재 화면을 수정하거나 다음 질문으로 넘어갑니다.
|
||||||
|
5. 다음 단계가 시각 자료가 아니라면 대기 화면을 밀어 넣어 오래된 화면이 남지 않게 합니다.
|
||||||
|
|
||||||
|
## 작성 원칙
|
||||||
|
|
||||||
|
- 파일명은 의미 있게 짓습니다. 예: `layout.html`, `visual-style.html`
|
||||||
|
- 같은 파일명을 재사용하지 않습니다.
|
||||||
|
- 한 화면에는 2~4개 선택지만 둡니다.
|
||||||
|
- 질문 자체를 화면에 명확히 적습니다.
|
||||||
|
- 레이아웃 검토에는 와이어프레임, 완성도 검토에는 더 실제적인 시안을 사용합니다.
|
||||||
|
- 필요한 경우 실제 콘텐츠를 사용하고, 불필요한 정교함은 피합니다.
|
||||||
|
|
||||||
|
## 이벤트 해석
|
||||||
|
|
||||||
|
브라우저 클릭 이벤트는 `state_dir/events`에 JSON Lines 형식으로 기록됩니다. 마지막 선택이 최종 선택인 경우가 많지만, 선택 흐름 전체가 사용자의 망설임이나 선호를 보여줄 수 있습니다.
|
||||||
|
|
||||||
|
## 정리
|
||||||
|
|
||||||
|
- 세션 종료 시 서버를 중지합니다.
|
||||||
|
- 프로젝트 기반 세션이라면 결과물은 이후 참고를 위해 남겨둘 수 있습니다.
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
---
|
||||||
|
name: "find-skills"
|
||||||
|
description: "필요한 기능을 제공하는 커뮤니티 스킬을 검색하고 설치하도록 돕습니다. 특정 작업용 스킬이 있는지 찾거나 에이전트 기능을 확장하고 싶을 때 호출합니다."
|
||||||
|
---
|
||||||
|
|
||||||
|
# 스킬 찾기
|
||||||
|
|
||||||
|
이 스킬은 공개 에이전트 스킬 생태계에서 적절한 스킬을 찾고 설치하는 과정을 안내합니다.
|
||||||
|
|
||||||
|
## 언제 사용할지
|
||||||
|
|
||||||
|
다음과 같은 요청에서 사용합니다.
|
||||||
|
|
||||||
|
- X를 어떻게 하냐고 묻는데, 이미 해당 작업용 스킬이 있을 가능성이 있을 때
|
||||||
|
- 특정 도메인용 스킬을 찾아 달라고 할 때
|
||||||
|
- 전문 기능을 제공할 수 있는 스킬이 있는지 궁금해할 때
|
||||||
|
- 에이전트 기능을 확장하고 싶다고 할 때
|
||||||
|
- 도구, 템플릿, 워크플로를 찾고 싶다고 할 때
|
||||||
|
|
||||||
|
## Skills CLI
|
||||||
|
|
||||||
|
Skills CLI는 공개 스킬 생태계를 위한 패키지 관리자입니다.
|
||||||
|
|
||||||
|
주요 명령:
|
||||||
|
|
||||||
|
- `npx skills find [query]` - 키워드로 스킬 검색
|
||||||
|
- `npx skills add <package>` - GitHub 등에서 스킬 설치
|
||||||
|
- `npx skills check` - 업데이트 확인
|
||||||
|
- `npx skills update` - 설치된 스킬 업데이트
|
||||||
|
|
||||||
|
브라우징 사이트:
|
||||||
|
|
||||||
|
- `https://skills.sh/`
|
||||||
|
|
||||||
|
## 추천 절차
|
||||||
|
|
||||||
|
### 1. 요구사항 이해
|
||||||
|
|
||||||
|
- 도메인이 무엇인지 파악합니다.
|
||||||
|
- 실제로 하고 싶은 작업이 무엇인지 파악합니다.
|
||||||
|
- 흔한 문제인지, 이미 스킬이 있을 가능성이 높은지 판단합니다.
|
||||||
|
- 검색 전에 사용자가 원하는 것이 "기능", "워크플로", "템플릿", "도메인 지식" 중 무엇인지 구분합니다.
|
||||||
|
|
||||||
|
### 2. 먼저 인기 스킬 확인
|
||||||
|
|
||||||
|
- 검색 전에 leaderboard나 인기 목록에서 검증된 스킬이 있는지 먼저 봅니다.
|
||||||
|
- 설치 수가 많고 널리 알려진 출처를 우선합니다.
|
||||||
|
|
||||||
|
### 3. 검색
|
||||||
|
|
||||||
|
필요하면 아래 명령으로 검색합니다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx skills find [query]
|
||||||
|
```
|
||||||
|
|
||||||
|
예:
|
||||||
|
|
||||||
|
- React 성능 최적화 -> `npx skills find react performance`
|
||||||
|
- PR 리뷰 -> `npx skills find pr review`
|
||||||
|
- changelog 생성 -> `npx skills find changelog`
|
||||||
|
|
||||||
|
### 4. 품질 검증
|
||||||
|
|
||||||
|
검색 결과만 보고 바로 추천하지 않습니다. 아래를 반드시 확인합니다.
|
||||||
|
|
||||||
|
- 설치 수
|
||||||
|
- 출처 신뢰도
|
||||||
|
- GitHub 저장소 평판
|
||||||
|
|
||||||
|
가능하면 공식 또는 널리 알려진 조직의 스킬을 우선합니다.
|
||||||
|
|
||||||
|
추가로 아래도 함께 봅니다.
|
||||||
|
|
||||||
|
- 최근에도 관리되고 있는지
|
||||||
|
- 설명이 실제 사용 시점을 명확히 말하는지
|
||||||
|
- 사용자가 원하는 작업과 과하게 어긋나지 않는지
|
||||||
|
|
||||||
|
### 5. 사용자에게 제시
|
||||||
|
|
||||||
|
다음 정보를 함께 제공합니다.
|
||||||
|
|
||||||
|
- 스킬 이름과 역할
|
||||||
|
- 설치 수와 출처
|
||||||
|
- 설치 명령
|
||||||
|
- 더 알아볼 링크
|
||||||
|
|
||||||
|
### 6. 설치 제안
|
||||||
|
|
||||||
|
사용자가 원하면 설치까지 진행할 수 있습니다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npx skills add <owner/repo@skill> -g -y
|
||||||
|
```
|
||||||
|
|
||||||
|
## 검색 절차 요약
|
||||||
|
|
||||||
|
1. 사용자가 하려는 일을 짧게 재정의합니다.
|
||||||
|
2. 인기 스킬에서 먼저 후보를 봅니다.
|
||||||
|
3. 부족하면 `npx skills find`로 구체 검색을 합니다.
|
||||||
|
4. 검색 결과를 설치 수와 출처 기준으로 걸러냅니다.
|
||||||
|
5. 사용자에게 1~3개 정도의 실질적인 후보를 제시합니다.
|
||||||
|
6. 원하면 설치까지 이어집니다.
|
||||||
|
|
||||||
|
## 검색 팁
|
||||||
|
|
||||||
|
- 너무 넓은 키워드보다 구체적인 키워드를 사용합니다.
|
||||||
|
- 검색 결과가 약하면 동의어를 바꿔가며 다시 찾습니다.
|
||||||
|
- 테스트, 디자인, 문서화, 배포, 코드리뷰, 생산성 같은 범주를 기준으로 접근합니다.
|
||||||
|
|
||||||
|
## 추천 시 주의점
|
||||||
|
|
||||||
|
- 검색 결과만 나왔다고 바로 추천하지 않습니다.
|
||||||
|
- 설치 수가 지나치게 적고 출처가 불분명하면 주의 표시를 합니다.
|
||||||
|
- 사용자가 당장 원하는 문제를 일반 역량으로 더 빨리 해결할 수 있다면 그 점도 함께 안내합니다.
|
||||||
|
|
||||||
|
## 찾지 못했을 때
|
||||||
|
|
||||||
|
적절한 스킬이 없다면 다음 원칙을 따릅니다.
|
||||||
|
|
||||||
|
- 스킬을 찾지 못했다고 명확히 알립니다.
|
||||||
|
- 일반 역량으로 직접 도와줄 수 있다고 제안합니다.
|
||||||
|
- 반복 작업이라면 새 스킬을 직접 만들 수 있다고 안내합니다.
|
||||||
|
|
||||||
|
## 기대 결과
|
||||||
|
|
||||||
|
이 스킬의 결과물은 다음을 만족해야 합니다.
|
||||||
|
|
||||||
|
- 사용자가 어떤 스킬을 왜 추천받았는지 이해할 수 있어야 합니다.
|
||||||
|
- 추천 스킬은 최소한의 품질 검증을 통과해야 합니다.
|
||||||
|
- 설치 명령과 더 알아볼 경로가 함께 제공되어야 합니다.
|
||||||
|
- 적절한 스킬이 없을 경우에도 다음 행동이 제시되어야 합니다.
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
---
|
||||||
|
name: "frontend-design"
|
||||||
|
description: "개성 있고 완성도 높은 프론트엔드 UI를 설계하고 구현합니다. 웹 페이지, 컴포넌트, 대시보드, 랜딩페이지, 스타일 개선 요청이 있을 때 호출합니다."
|
||||||
|
---
|
||||||
|
|
||||||
|
# 개성 있는 프로덕션급 프론트엔드 디자인
|
||||||
|
|
||||||
|
이 스킬은 흔한 "AI가 만든 티 나는" 결과물을 피하고, 높은 디자인 완성도를 가진 프론트엔드 인터페이스를 만들기 위한 가이드입니다. 미학적 디테일과 창의적 선택에 집중하면서 실제로 동작하는 코드를 구현합니다.
|
||||||
|
|
||||||
|
사용자는 컴포넌트, 페이지, 애플리케이션, 포스터, 웹 인터페이스 등의 프론트엔드 요구사항을 제공합니다. 여기에는 목적, 대상 사용자, 기술 제약이 함께 포함될 수 있습니다.
|
||||||
|
|
||||||
|
## 디자인 사고
|
||||||
|
|
||||||
|
코드를 작성하기 전에 문맥을 이해하고, 분명하고 대담한 미학 방향을 먼저 정합니다.
|
||||||
|
|
||||||
|
- **목적** - 이 인터페이스가 해결하는 문제는 무엇인지, 누가 사용하는지 파악합니다.
|
||||||
|
- **톤** - 극단적인 방향성을 고릅니다. 예: 극단적 미니멀리즘, 맥시멀리즘, 레트로 퓨처리즘, 유기적/자연주의, 고급/정제, 장난감 같은 유쾌함, 에디토리얼/매거진, 브루탈리즘, 아르데코, 소프트/파스텔, 산업적/실용주의 등
|
||||||
|
- **제약** - 프레임워크, 성능, 접근성 같은 기술 조건을 확인합니다.
|
||||||
|
- **차별점** - 이 결과물을 잊히지 않게 만드는 핵심 한 가지가 무엇인지 정의합니다.
|
||||||
|
|
||||||
|
**중요**: 개념적 방향을 명확히 정하고 정밀하게 실행해야 합니다. 대담한 맥시멀리즘이든 정제된 미니멀리즘이든 모두 가능하지만, 핵심은 강도가 아니라 의도성입니다.
|
||||||
|
|
||||||
|
방향을 정할 때는 "멋있어 보이는 것"만이 아니라 "왜 이 제품과 맥락에 맞는가"까지 설명할 수 있어야 합니다.
|
||||||
|
|
||||||
|
그 다음, HTML/CSS/JS, React, Vue 등 적절한 기술로 아래 기준을 만족하는 실제 동작 코드를 구현합니다.
|
||||||
|
|
||||||
|
- 프로덕션 수준의 완성도와 기능성
|
||||||
|
- 시각적으로 강렬하고 기억에 남는 결과
|
||||||
|
- 일관된 미학 관점
|
||||||
|
- 세부까지 정교하게 다듬어진 표현
|
||||||
|
|
||||||
|
## 프론트엔드 미학 가이드라인
|
||||||
|
|
||||||
|
다음 요소에 집중합니다.
|
||||||
|
|
||||||
|
- **타이포그래피** - 아름답고, 독특하고, 흥미로운 폰트를 선택합니다. Arial, Inter 같은 너무 흔한 폰트는 피합니다. 개성 있는 디스플레이 폰트와 정제된 본문 폰트를 조합합니다.
|
||||||
|
- **색상과 테마** - 일관된 미학 방향에 확실히 맞춥니다. CSS 변수로 일관성을 유지합니다. 소심하게 균등 분배된 색상보다, 지배적인 메인 컬러와 날카로운 포인트 컬러가 더 강한 인상을 줍니다.
|
||||||
|
- **모션** - 애니메이션과 마이크로 인터랙션을 활용합니다. HTML에서는 가능하면 CSS 중심으로 해결하고, React에서는 Motion 계열 라이브러리를 활용할 수 있습니다. 페이지 로드 연출, 스태거드 리빌, 스크롤 트리거, 의외성 있는 호버 상태처럼 임팩트 있는 순간에 집중합니다.
|
||||||
|
- **공간 구성** - 예상 가능한 배치보다 비대칭, 겹침, 대각선 흐름, 그리드를 깨는 요소, 넉넉한 여백 또는 의도된 밀도를 적극 활용합니다.
|
||||||
|
- **배경과 시각 디테일** - 단색 배경으로 끝내지 말고 분위기와 깊이를 만듭니다. 그래디언트 메시, 노이즈 텍스처, 기하학 패턴, 레이어 투명도, 강한 그림자, 장식 테두리, 커스텀 커서, 그레인 오버레이 등 맥락에 맞는 효과를 활용합니다.
|
||||||
|
- **맥락 적합성** - 금융, 의료, 패션, 교육처럼 도메인별 기대치를 반영합니다. 시각적으로 강하더라도 제품 신뢰를 해치면 실패입니다.
|
||||||
|
- **디테일 완성도** - 버튼 상태, 입력 포커스, 빈 상태, 에러 상태, 로딩 상태까지 같은 미학 언어로 정리합니다.
|
||||||
|
|
||||||
|
## 반드시 피할 것
|
||||||
|
|
||||||
|
다음과 같은 흔한 AI 스타일은 사용하지 않습니다.
|
||||||
|
|
||||||
|
- Inter, Roboto, Arial, 시스템 폰트에 과도하게 의존하는 구성
|
||||||
|
- 흰 배경 위 보라색 그라데이션 같은 진부한 색 조합
|
||||||
|
- 너무 예측 가능한 레이아웃과 컴포넌트 패턴
|
||||||
|
- 맥락 고유성이 없는 쿠키커터식 디자인
|
||||||
|
|
||||||
|
항상 맥락에 맞게 창의적으로 해석하고, 예상 밖이지만 설득력 있는 선택을 합니다. 모든 디자인은 서로 달라야 하며, 결과물마다 라이트/다크 테마, 폰트, 분위기, 스타일이 달라질 수 있어야 합니다. 여러 세대의 결과물이 공통된 안전한 선택으로 수렴해서는 안 됩니다.
|
||||||
|
|
||||||
|
다음 같은 패턴도 피합니다.
|
||||||
|
|
||||||
|
- 의미 없는 화려함만 있고 정보 위계가 약한 구성
|
||||||
|
- 디자인 방향 없이 라이브러리 기본 스타일만 얹은 화면
|
||||||
|
- 모든 요소에 같은 그림자, 같은 반경, 같은 간격을 기계적으로 적용한 화면
|
||||||
|
- 제품 목적보다 Dribbble식 비주얼 과시에 치우친 화면
|
||||||
|
|
||||||
|
## 구현 복잡도와 미학의 일치
|
||||||
|
|
||||||
|
구현 복잡도는 미학 방향과 맞아야 합니다.
|
||||||
|
|
||||||
|
- 맥시멀한 디자인이라면 풍부한 애니메이션, 시각 효과, 정교한 레이어링이 필요할 수 있습니다.
|
||||||
|
- 미니멀하거나 정제된 디자인이라면 절제, 정밀한 간격, 타이포그래피, 미묘한 디테일에 집중해야 합니다.
|
||||||
|
|
||||||
|
우아함은 단순함 자체가 아니라, 선택한 비전을 얼마나 정확하게 실행했는지에서 나옵니다.
|
||||||
|
|
||||||
|
## 작업 절차
|
||||||
|
|
||||||
|
이 스킬을 적용할 때는 보통 아래 순서를 따릅니다.
|
||||||
|
|
||||||
|
1. 제품 맥락과 사용자층을 확인합니다.
|
||||||
|
2. 대담한 미학 방향을 한 문장으로 정의합니다.
|
||||||
|
3. 색상, 폰트, 레이아웃, 모션 원칙을 결정합니다.
|
||||||
|
4. 그 방향에 맞는 실제 UI를 구현합니다.
|
||||||
|
5. 상태 변화, 반응형, 접근성, 디테일 완성도를 점검합니다.
|
||||||
|
|
||||||
|
## 산출물 기대치
|
||||||
|
|
||||||
|
최종 결과물은 다음을 만족해야 합니다.
|
||||||
|
|
||||||
|
- 실제로 동작하는 코드여야 합니다.
|
||||||
|
- 디자인 방향이 한눈에 읽혀야 합니다.
|
||||||
|
- 타이포그래피와 색상 시스템이 의도적으로 구성되어야 합니다.
|
||||||
|
- 상태 변화와 미세 상호작용까지 마감되어야 합니다.
|
||||||
|
- 흔한 AI 스타일이 아니라, 맥락에 맞는 인상적인 화면이어야 합니다.
|
||||||
|
|
||||||
|
## 핵심 원칙
|
||||||
|
|
||||||
|
- 프론트엔드 결과물은 항상 실제로 동작해야 합니다.
|
||||||
|
- 미학 방향은 분명해야 하며, 애매한 절충안으로 흐르지 않습니다.
|
||||||
|
- 흔한 AI 스타일 대신 맥락에 맞는 독창성을 우선합니다.
|
||||||
|
- 디테일, 간격, 타이포그래피, 모션, 색상 일관성을 끝까지 다듬습니다.
|
||||||
|
- 과감한 선택을 두려워하지 말고, 명확한 방향성을 완성도 있게 구현합니다.
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
name: "karpathy-guidelines"
|
||||||
|
description: "LLM 코딩 실수를 줄이는 행동 지침입니다. 코드 작성, 리뷰, 리팩터링 시 과설계와 과추정을 줄이고 검증 가능한 목표를 세워야 할 때 호출합니다."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Karpathy 가이드라인
|
||||||
|
|
||||||
|
이 스킬은 LLM이 코드 작업에서 자주 하는 실수를 줄이기 위한 행동 규칙입니다. 속도보다 신중함을 약간 더 우선합니다.
|
||||||
|
|
||||||
|
사소한 작업에서는 판단의 여지가 있지만, 코드 작성, 리뷰, 리팩터링처럼 실수가 누적되기 쉬운 작업에서는 이 규칙을 기본 행동으로 삼습니다.
|
||||||
|
|
||||||
|
## 1. 코딩 전에 먼저 생각하기
|
||||||
|
|
||||||
|
- 추정하지 않습니다.
|
||||||
|
- 헷갈리는 점을 숨기지 않습니다.
|
||||||
|
- 가정을 명시적으로 드러냅니다.
|
||||||
|
- 해석이 여러 가지면 조용히 하나를 고르지 말고 선택지를 보여줍니다.
|
||||||
|
- 더 단순한 방법이 있으면 먼저 제안합니다.
|
||||||
|
- 불명확하면 멈추고 무엇이 불명확한지 말합니다.
|
||||||
|
|
||||||
|
핵심은 "모르는 상태에서 자신감 있게 진행하지 않는 것"입니다.
|
||||||
|
|
||||||
|
## 2. 단순함 우선
|
||||||
|
|
||||||
|
문제를 해결하는 최소한의 코드만 작성합니다.
|
||||||
|
|
||||||
|
- 요청하지 않은 기능은 넣지 않습니다.
|
||||||
|
- 한 번만 쓰는 코드에 추상화를 만들지 않습니다.
|
||||||
|
- 요구되지 않은 유연성이나 설정 가능성은 추가하지 않습니다.
|
||||||
|
- 실제로 불가능한 시나리오까지 과한 예외 처리를 만들지 않습니다.
|
||||||
|
- 200줄이 50줄로 될 수 있다면 다시 단순화합니다.
|
||||||
|
|
||||||
|
항상 스스로 묻습니다. "시니어 엔지니어가 이걸 과하다고 말하지 않을까?"
|
||||||
|
|
||||||
|
단순함은 기능 부족이 아니라, 요구에 정확히 맞는 최소 해법을 의미합니다.
|
||||||
|
|
||||||
|
## 3. 수술하듯 수정하기
|
||||||
|
|
||||||
|
필요한 부분만 건드리고, 내 변경으로 생긴 부산물만 정리합니다.
|
||||||
|
|
||||||
|
- 인접 코드, 주석, 포맷을 괜히 손보지 않습니다.
|
||||||
|
- 고장 나지 않은 부분을 리팩터링하지 않습니다.
|
||||||
|
- 기존 스타일을 따릅니다.
|
||||||
|
- 내 변경 때문에 쓰이지 않게 된 import, 변수, 함수만 제거합니다.
|
||||||
|
- 원래부터 있던 죽은 코드는 함부로 지우지 않고 필요하면 언급만 합니다.
|
||||||
|
|
||||||
|
모든 변경 줄은 사용자 요청과 직접 연결되어야 합니다.
|
||||||
|
|
||||||
|
관련 없는 개선 욕구는 분리해야 합니다. 지금 작업의 일부가 아니라면 메모만 하고 건드리지 않습니다.
|
||||||
|
|
||||||
|
## 4. 목표 기반 실행
|
||||||
|
|
||||||
|
작업은 검증 가능한 목표로 바꿉니다.
|
||||||
|
|
||||||
|
- 유효성 검사 추가 -> 잘못된 입력 테스트를 쓰고 통과시킨다
|
||||||
|
- 버그 수정 -> 재현 테스트를 만들고 통과시킨다
|
||||||
|
- 리팩터링 -> 변경 전후 테스트가 모두 통과하는지 확인한다
|
||||||
|
|
||||||
|
여러 단계 작업이라면 짧은 계획과 검증 기준을 함께 둡니다.
|
||||||
|
|
||||||
|
예:
|
||||||
|
|
||||||
|
1. 단계 수행 -> 어떤 체크로 검증할지 명시
|
||||||
|
2. 다음 단계 수행 -> 어떤 체크로 검증할지 명시
|
||||||
|
3. 최종 검증 -> 통과 조건 명시
|
||||||
|
|
||||||
|
강한 성공 기준이 있을수록 중간 판단을 줄일 수 있고, 사용자의 재확인 없이도 정확하게 루프를 돌 수 있습니다.
|
||||||
|
|
||||||
|
## 자주 막아야 할 실수
|
||||||
|
|
||||||
|
- 애매한 요구를 임의로 해석해 구현해 버리는 것
|
||||||
|
- 한 번만 쓸 로직에 구조를 과하게 씌우는 것
|
||||||
|
- 요청과 직접 관련 없는 주변 코드까지 손대는 것
|
||||||
|
- "작동하면 됐다" 수준으로 검증 없이 마무리하는 것
|
||||||
|
|
||||||
|
## 기대 결과
|
||||||
|
|
||||||
|
이 스킬을 적용한 결과는 다음을 만족해야 합니다.
|
||||||
|
|
||||||
|
- 가정이 숨겨지지 않는다
|
||||||
|
- 코드가 필요한 만큼만 단순하다
|
||||||
|
- 변경 범위가 요청과 정확히 맞닿아 있다
|
||||||
|
- 성공 기준이 검증 가능한 문장으로 바뀌어 있다
|
||||||
|
|
||||||
|
## 핵심 요약
|
||||||
|
|
||||||
|
- 먼저 생각하고, 모르면 묻습니다.
|
||||||
|
- 최소 코드로 끝냅니다.
|
||||||
|
- 꼭 필요한 줄만 바꿉니다.
|
||||||
|
- 검증 가능한 성공 기준을 세웁니다.
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
---
|
||||||
|
name: "systematic-debugging"
|
||||||
|
description: "버그, 테스트 실패, 예기치 않은 동작을 체계적으로 진단합니다. 수정안을 제시하기 전에 반드시 근본 원인을 먼저 찾아야 할 때 호출합니다."
|
||||||
|
---
|
||||||
|
|
||||||
|
# 체계적 디버깅
|
||||||
|
|
||||||
|
무작위 수정은 시간을 낭비하고 새 버그를 만듭니다. 빠른 땜질은 증상만 가릴 뿐입니다. 이 스킬의 핵심은 **수정 전에 반드시 근본 원인을 찾는 것**입니다.
|
||||||
|
|
||||||
|
## 철칙
|
||||||
|
|
||||||
|
근본 원인 조사 없이 수정하지 않습니다.
|
||||||
|
|
||||||
|
다시 말해, 1단계를 끝내지 않았다면 수정안 제시는 아직 허용되지 않습니다.
|
||||||
|
|
||||||
|
## 언제 사용할지
|
||||||
|
|
||||||
|
다음과 같은 모든 기술 문제에서 사용합니다.
|
||||||
|
|
||||||
|
- 테스트 실패
|
||||||
|
- 운영 중 버그
|
||||||
|
- 예기치 않은 동작
|
||||||
|
- 성능 문제
|
||||||
|
- 빌드 실패
|
||||||
|
- 통합 문제
|
||||||
|
|
||||||
|
특히 아래 상황에서 반드시 사용합니다.
|
||||||
|
|
||||||
|
- 시간이 촉박할 때
|
||||||
|
- "일단 이것만 빨리 고치면 될 것 같을 때"
|
||||||
|
- 이미 여러 수정안을 시도했을 때
|
||||||
|
- 이전 수정이 실패했을 때
|
||||||
|
- 문제를 완전히 이해하지 못한 상태일 때
|
||||||
|
|
||||||
|
다음 상황에서도 생략하지 않습니다.
|
||||||
|
|
||||||
|
- 문제가 너무 단순해 보일 때
|
||||||
|
- 급하게 고쳐 달라는 압박이 있을 때
|
||||||
|
- 지금 보이는 증상만 없애면 될 것처럼 느껴질 때
|
||||||
|
|
||||||
|
## 4단계 프로세스
|
||||||
|
|
||||||
|
### 1단계: 근본 원인 조사
|
||||||
|
|
||||||
|
수정 전에 반드시 아래를 수행합니다.
|
||||||
|
|
||||||
|
- 오류 메시지, 경고, 스택 트레이스를 끝까지 읽습니다.
|
||||||
|
- 재현 절차를 명확히 만들고, 반복 재현 가능한지 확인합니다.
|
||||||
|
- 최근 변경 사항, 설정 차이, 의존성 변경, 환경 차이를 확인합니다.
|
||||||
|
- 다중 컴포넌트 시스템이라면 경계마다 로그와 계측을 추가해 어디서 깨지는지 증거를 모읍니다.
|
||||||
|
- 호출 스택이 깊다면 잘못된 값이 어디서 시작됐는지 거꾸로 추적합니다.
|
||||||
|
- 재현이 안정적이지 않다면, 추측 대신 로그와 관찰 지점을 늘립니다.
|
||||||
|
- 레이어가 여러 개인 시스템이라면 각 경계에서 입력, 출력, 상태 전파를 확인합니다.
|
||||||
|
|
||||||
|
### 2단계: 패턴 분석
|
||||||
|
|
||||||
|
- 같은 코드베이스에서 정상 동작하는 유사 사례를 찾습니다.
|
||||||
|
- 참조 구현이 있다면 끝까지 읽고 패턴을 정확히 이해합니다.
|
||||||
|
- 정상 사례와 문제 사례의 차이를 작은 것까지 모두 나열합니다.
|
||||||
|
- 필요한 설정, 환경, 전제 조건을 파악합니다.
|
||||||
|
- "이 정도 차이는 중요하지 않겠지"라고 넘기지 않습니다.
|
||||||
|
|
||||||
|
### 3단계: 가설과 검증
|
||||||
|
|
||||||
|
- "원인은 X이며 이유는 Y다"라는 단일 가설을 명확히 적습니다.
|
||||||
|
- 가설을 검증할 수 있는 최소 변경만 적용합니다.
|
||||||
|
- 한 번에 변수 하나만 바꿉니다.
|
||||||
|
- 실패하면 수정안을 겹쳐 쌓지 말고 새 가설로 돌아갑니다.
|
||||||
|
- 모르는 것은 모른다고 인정하고 추가 조사 또는 도움 요청을 합니다.
|
||||||
|
|
||||||
|
이 단계의 핵심은 과학적 방법입니다.
|
||||||
|
|
||||||
|
- 한 번에 하나의 가설만 세웁니다.
|
||||||
|
- 한 번에 하나의 변수만 바꿉니다.
|
||||||
|
- 실패하면 더 많은 수정으로 덮지 않고, 얻은 새 증거를 바탕으로 다시 분석합니다.
|
||||||
|
|
||||||
|
### 4단계: 구현
|
||||||
|
|
||||||
|
- 먼저 실패하는 테스트나 최소 재현 케이스를 만듭니다.
|
||||||
|
- 확인된 근본 원인만 겨냥해 한 번에 한 수정만 적용합니다.
|
||||||
|
- 테스트가 통과하는지, 다른 것이 깨지지 않았는지 검증합니다.
|
||||||
|
- 수정이 실패하면 즉시 멈추고 다시 1단계로 돌아갑니다.
|
||||||
|
- 세 번 이상 실패했다면 개별 버그가 아니라 구조적 문제일 가능성을 의심합니다.
|
||||||
|
|
||||||
|
세 번 이상 수정이 연속 실패했다면 아래를 질문합니다.
|
||||||
|
|
||||||
|
- 우리가 증상을 계속 쫓고 있지는 않은가
|
||||||
|
- 현재 구조 자체가 문제를 유발하고 있지는 않은가
|
||||||
|
- 고칠 문제가 아니라 패턴을 바꿔야 하는 상황은 아닌가
|
||||||
|
|
||||||
|
## 중단 신호
|
||||||
|
|
||||||
|
다음 생각이 들면 멈추고 다시 1단계로 돌아갑니다.
|
||||||
|
|
||||||
|
- 일단 빨리 고치고 나중에 조사하자
|
||||||
|
- X를 바꿔보면 될 것 같다
|
||||||
|
- 여러 개를 같이 바꾸면 빠를 것 같다
|
||||||
|
- 테스트는 나중에 쓰자
|
||||||
|
- 완전히 이해는 못 했지만 아마 맞을 것이다
|
||||||
|
- 한 번만 더 고쳐보자
|
||||||
|
- 여기 문제들은 대충 이런 것들이다
|
||||||
|
- 로그 없이 감으로 수정 방향을 정하자
|
||||||
|
- 여기저기 같이 바꾸면 하나쯤 맞을 것이다
|
||||||
|
|
||||||
|
이 신호들은 대부분 조사보다 추측이 앞서고 있다는 뜻입니다.
|
||||||
|
|
||||||
|
## 안티 패턴
|
||||||
|
|
||||||
|
- 증상만 감추는 빠른 땜질
|
||||||
|
- 여러 수정안을 한 번에 넣는 방식
|
||||||
|
- 재현 없이 감으로 고치는 방식
|
||||||
|
- 비교 대상 없이 현재 코드만 들여다보는 방식
|
||||||
|
- 세 번 이상 실패했는데도 구조를 의심하지 않는 방식
|
||||||
|
|
||||||
|
## 핵심 원칙
|
||||||
|
|
||||||
|
- 증상이 아니라 원인을 수정합니다.
|
||||||
|
- 계측과 증거 없이 추측하지 않습니다.
|
||||||
|
- 한 번에 하나만 바꿉니다.
|
||||||
|
- 재현, 비교, 검증이 없는 수정은 완료가 아닙니다.
|
||||||
|
- 여러 번 실패하면 구조 자체를 의심합니다.
|
||||||
|
|
||||||
|
## 기대 결과
|
||||||
|
|
||||||
|
이 스킬을 적용한 결과는 다음을 만족해야 합니다.
|
||||||
|
|
||||||
|
- 문제의 재현 절차가 설명 가능해야 합니다.
|
||||||
|
- 근본 원인을 뒷받침하는 증거가 있어야 합니다.
|
||||||
|
- 수정안은 하나의 원인에 대응해야 합니다.
|
||||||
|
- 수정 후에는 재현 테스트 또는 검증 절차가 통과해야 합니다.
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
---
|
||||||
|
name: "ui-ux-pro-max"
|
||||||
|
description: "웹과 모바일 전반의 UI/UX 설계 지능을 제공합니다. 새 페이지 설계, 컴포넌트 리팩터링, 디자인 시스템, 접근성, 상호작용 품질 점검이 필요할 때 호출합니다."
|
||||||
|
---
|
||||||
|
|
||||||
|
# UI/UX Pro Max
|
||||||
|
|
||||||
|
웹과 모바일 애플리케이션을 위한 종합 UI/UX 설계 가이드입니다. 다양한 스타일, 색상 팔레트, 폰트 조합, 제품 유형별 추천, UX 가이드라인, 차트 유형 관점을 바탕으로 더 완성도 높은 인터페이스를 설계하도록 돕습니다.
|
||||||
|
|
||||||
|
## 언제 적용할지
|
||||||
|
|
||||||
|
다음과 같은 작업에서는 이 스킬을 우선 사용합니다.
|
||||||
|
|
||||||
|
- 새 페이지를 설계할 때
|
||||||
|
- 버튼, 모달, 폼, 테이블, 차트 같은 UI 컴포넌트를 만들거나 리팩터링할 때
|
||||||
|
- 색상 체계, 타이포그래피, 간격, 레이아웃 시스템을 정할 때
|
||||||
|
- UI 코드의 사용성, 접근성, 시각 일관성을 검토할 때
|
||||||
|
- 내비게이션, 애니메이션, 반응형 동작을 설계할 때
|
||||||
|
- 제품 수준의 스타일, 정보 위계, 브랜드 표현을 결정할 때
|
||||||
|
- 인터페이스의 명확성, 품질감, 사용성을 개선할 때
|
||||||
|
|
||||||
|
## 반드시 사용할 상황
|
||||||
|
|
||||||
|
아래 상황에서는 이 스킬 사용을 기본값으로 둡니다.
|
||||||
|
|
||||||
|
- 랜딩 페이지, 대시보드, 어드민, SaaS, 모바일 앱 등 새 화면 구조를 만들 때
|
||||||
|
- 버튼, 모달, 폼, 테이블, 차트 같은 핵심 컴포넌트를 설계 또는 개편할 때
|
||||||
|
- 색상 체계와 타이포그래피 시스템을 결정할 때
|
||||||
|
- 접근성, 상호작용 품질, 시각 일관성 리뷰를 수행할 때
|
||||||
|
- 반응형 레이아웃과 내비게이션 구조를 정할 때
|
||||||
|
|
||||||
|
다음 경우에는 보조적으로 권장됩니다.
|
||||||
|
|
||||||
|
- UI가 "어딘가 덜 프로페셔널해 보이는데 이유가 분명하지 않을 때"
|
||||||
|
- 사용성 피드백을 받았을 때
|
||||||
|
- 출시 전 UI 품질을 정리할 때
|
||||||
|
- 웹, iOS, Android 간 디자인 정렬이 필요할 때
|
||||||
|
- 디자인 시스템 또는 재사용 가능한 컴포넌트 라이브러리를 만들 때
|
||||||
|
|
||||||
|
## 권장 상황
|
||||||
|
|
||||||
|
- UI가 덜 프로페셔널해 보이지만 원인이 명확하지 않을 때
|
||||||
|
- 제품 피드백에서 "불편하다", "헷갈린다"는 의견이 반복될 때
|
||||||
|
- 출시 직전 품질 점검에서 디자인 완성도를 끌어올려야 할 때
|
||||||
|
|
||||||
|
## 불필요한 상황
|
||||||
|
|
||||||
|
아래 작업에서는 일반적으로 이 스킬이 우선순위가 아닙니다.
|
||||||
|
|
||||||
|
- 순수 백엔드 로직만 다루는 작업
|
||||||
|
- API 계약, DB 스키마 설계만 다루는 작업
|
||||||
|
- UI와 무관한 인프라/DevOps 작업
|
||||||
|
- 시각 요소가 없는 자동화 스크립트 작업
|
||||||
|
|
||||||
|
다음 작업에는 일반적으로 필요하지 않습니다.
|
||||||
|
|
||||||
|
- 순수 백엔드 로직 개발
|
||||||
|
- API 또는 데이터베이스 설계만 하는 작업
|
||||||
|
- 인터페이스와 무관한 성능 최적화
|
||||||
|
- 인프라, DevOps, 비시각 자동화 작업
|
||||||
|
|
||||||
|
판단 기준은 단순합니다. 작업이 기능이 **어떻게 보이고, 느껴지고, 움직이고, 상호작용되는지**를 바꾼다면 이 스킬을 사용합니다.
|
||||||
|
|
||||||
|
## 핵심 원칙
|
||||||
|
|
||||||
|
- 접근성을 최우선으로 봅니다. 대비, 키보드 탐색, 레이블, 상태 표현을 먼저 확인합니다.
|
||||||
|
- 시각적 품질보다 정보 구조와 사용 흐름을 먼저 정리합니다.
|
||||||
|
- 제품 맥락에 맞는 스타일을 선택하고, 유행하는 패턴을 무조건 복제하지 않습니다.
|
||||||
|
- 타이포그래피, 색상, 간격, 그림자, 애니메이션, 반응형 규칙을 시스템으로 다룹니다.
|
||||||
|
- 웹과 모바일을 포함한 여러 플랫폼 간 일관성을 유지하되, 플랫폼 고유 상호작용은 존중합니다.
|
||||||
|
- 차트나 데이터 시각화는 미적인 취향보다 데이터 전달 목적에 맞게 선택합니다.
|
||||||
|
|
||||||
|
## 우선순위별 검토 항목
|
||||||
|
|
||||||
|
1. **접근성** - 대비, 포커스, 라벨, 키보드 접근성, 보조기기 친화성
|
||||||
|
2. **상호작용 품질** - 클릭 영역, 터치 친화성, 상태 변화, 피드백
|
||||||
|
3. **정보 위계** - 가장 중요한 내용이 첫눈에 보이는지 확인
|
||||||
|
4. **레이아웃 시스템** - 그리드, 여백, 정렬, 반응형 구조
|
||||||
|
5. **타이포그래피** - 역할이 명확한 글자 크기, 무게, 줄 간격, 폰트 조합
|
||||||
|
6. **색상 시스템** - 브랜드 적합성, 상태 색, 강조 규칙, 다크/라이트 확장성
|
||||||
|
7. **모션과 전환** - 의미 있는 움직임만 남기고 산만한 효과는 제거
|
||||||
|
8. **시각 디테일** - 경계선, 그림자, 반경, 배경 질감, 컴포넌트 완성도
|
||||||
|
|
||||||
|
## 워크플로
|
||||||
|
|
||||||
|
1. 제품 유형과 목표 사용자, 핵심 과업을 정의합니다.
|
||||||
|
2. 정보 위계와 화면 구조를 먼저 설계합니다.
|
||||||
|
3. 스타일 방향, 색상 전략, 타이포그래피 전략을 정합니다.
|
||||||
|
4. 컴포넌트와 페이지를 같은 시스템 규칙으로 맞춥니다.
|
||||||
|
5. 접근성, 반응형, 상태 표현, 상호작용 완성도를 점검합니다.
|
||||||
|
6. 필요한 경우 리뷰 결과를 우선순위별로 정리해 개선합니다.
|
||||||
|
|
||||||
|
## 작업 방식
|
||||||
|
|
||||||
|
- 먼저 제품 유형과 대상 사용자, 핵심 화면 목적을 확인합니다.
|
||||||
|
- 그 다음 스타일 방향, 컬러 전략, 타이포그래피 방향, 레이아웃 전략을 제안합니다.
|
||||||
|
- 필요한 경우 2~3개의 시각 방향을 비교하고 추천안을 제시합니다.
|
||||||
|
- 구현에 들어갈 때는 컴포넌트 단위 규칙과 페이지 단위 위계를 함께 설계합니다.
|
||||||
|
- 리뷰 작업이라면 문제를 단순 나열하지 말고, 우선순위와 수정 이유까지 설명합니다.
|
||||||
|
|
||||||
|
## 안티 패턴
|
||||||
|
|
||||||
|
- 정보 구조를 정하기 전에 시각 효과만 먼저 쌓는 방식
|
||||||
|
- 모든 화면을 같은 템플릿으로 찍어내는 방식
|
||||||
|
- 접근성 점검 없이 색상과 애니메이션만 조정하는 방식
|
||||||
|
- 컴포넌트 규칙 없이 페이지별 임시 스타일을 누적하는 방식
|
||||||
|
|
||||||
|
## 산출물 기준
|
||||||
|
|
||||||
|
이 스킬을 사용한 결과물은 다음을 만족해야 합니다.
|
||||||
|
|
||||||
|
- 보기 좋을 뿐 아니라 읽기 쉽고 사용하기 쉬워야 합니다.
|
||||||
|
- 단일 화면이 아니라 전체 시스템 관점에서 일관성이 있어야 합니다.
|
||||||
|
- 컴포넌트와 페이지의 관계가 명확해야 합니다.
|
||||||
|
- 접근성, 반응형, 상태 표현을 빠뜨리지 않아야 합니다.
|
||||||
|
- 색상, 폰트, 간격, 인터랙션이 의도적으로 선택되어야 합니다.
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
name: "using-superpowers"
|
||||||
|
description: "응답 전에 관련 스킬을 먼저 확인하고 호출하는 운영 규칙입니다. 어떤 작업이든 적절한 스킬 적용 여부를 먼저 판단해야 할 때 호출합니다."
|
||||||
|
---
|
||||||
|
|
||||||
|
# 스킬 사용 운영 규칙
|
||||||
|
|
||||||
|
이 스킬은 대화나 작업을 시작할 때, 어떤 스킬을 먼저 확인하고 어떻게 적용할지에 대한 기본 운영 원칙을 정의합니다.
|
||||||
|
|
||||||
|
## 최우선 규칙
|
||||||
|
|
||||||
|
관련 스킬이 조금이라도 적용될 가능성이 있다면, 응답이나 행동 전에 먼저 스킬을 호출합니다.
|
||||||
|
|
||||||
|
질문에 답하기 전, 코드 읽기 전, 구현 시작 전, 명확화 질문을 하기 전에도 먼저 검토합니다.
|
||||||
|
|
||||||
|
단 1%의 가능성이라도 있다면 먼저 확인하는 쪽이 기본값입니다.
|
||||||
|
|
||||||
|
## 우선순위
|
||||||
|
|
||||||
|
스킬 지시보다 사용자 지시가 항상 우선합니다.
|
||||||
|
|
||||||
|
1. 사용자 명시 지시
|
||||||
|
2. 스킬 지시
|
||||||
|
3. 시스템 기본 동작
|
||||||
|
|
||||||
|
즉, 스킬이 어떤 워크플로를 권장하더라도 사용자가 다른 방식을 명확히 요구하면 사용자 지시를 따릅니다.
|
||||||
|
|
||||||
|
## 기본 흐름
|
||||||
|
|
||||||
|
- 사용자 메시지를 받으면 먼저 관련 스킬 가능성을 판단합니다.
|
||||||
|
- 관련 가능성이 있으면 즉시 스킬을 호출합니다.
|
||||||
|
- 호출된 스킬에 체크리스트가 있으면 작업 목록을 만들고 순서대로 따릅니다.
|
||||||
|
- 그 다음에야 응답, 질문, 구현, 조사 같은 행동을 합니다.
|
||||||
|
|
||||||
|
중요한 점은, "먼저 조금만 확인하고 나서 스킬을 쓰자"가 아니라 "스킬을 먼저 확인하고 나서 어떻게 확인할지 결정한다"는 순서입니다.
|
||||||
|
|
||||||
|
## 스킬 우선 적용 순서
|
||||||
|
|
||||||
|
여러 스킬이 동시에 맞을 수 있다면 아래 순서를 기준으로 봅니다.
|
||||||
|
|
||||||
|
1. **프로세스 스킬** - 작업 방식을 정하는 스킬
|
||||||
|
2. **구현 스킬** - 실제 구현이나 산출물을 만드는 스킬
|
||||||
|
|
||||||
|
예:
|
||||||
|
|
||||||
|
- "무언가를 만들자" -> brainstorming 먼저, 그다음 구현 스킬
|
||||||
|
- "버그를 고치자" -> debugging 먼저, 그다음 도메인 스킬
|
||||||
|
|
||||||
|
즉, 프로세스를 정하는 스킬이 항상 구현 스킬보다 먼저입니다.
|
||||||
|
|
||||||
|
## 흔한 자기합리화 경고
|
||||||
|
|
||||||
|
다음 생각이 들면 흐름을 다시 점검합니다.
|
||||||
|
|
||||||
|
- 이건 간단하니까 스킬이 필요 없겠다
|
||||||
|
- 먼저 코드 좀 보고 나서 생각하자
|
||||||
|
- 빠르게 확인만 하고 나중에 스킬을 쓰자
|
||||||
|
- 이 정도는 기억으로 처리해도 되겠다
|
||||||
|
- 질문이니까 작업이 아니다
|
||||||
|
- 스킬이 너무 과한 것 같다
|
||||||
|
- 이건 그냥 간단한 확인이다
|
||||||
|
- 이전에 본 적 있는 스킬이라 다시 안 읽어도 된다
|
||||||
|
|
||||||
|
이런 생각은 대부분 스킬 적용을 건너뛰려는 신호입니다.
|
||||||
|
|
||||||
|
## 적용 절차
|
||||||
|
|
||||||
|
실제 적용 절차는 아래와 같습니다.
|
||||||
|
|
||||||
|
1. 사용자 메시지를 받습니다.
|
||||||
|
2. 관련 스킬 가능성을 먼저 판단합니다.
|
||||||
|
3. 조금이라도 관련 있다면 스킬을 호출합니다.
|
||||||
|
4. 스킬의 체크리스트나 절차를 작업 흐름에 반영합니다.
|
||||||
|
5. 그 다음 응답, 조사, 구현을 진행합니다.
|
||||||
|
|
||||||
|
## 원칙
|
||||||
|
|
||||||
|
- 스킬은 선택이 아니라 작업 방식의 일부로 본다
|
||||||
|
- 단순한 작업일수록 오히려 프로세스를 지켜 과잉 추정을 줄인다
|
||||||
|
- 기억에 의존하지 말고 현재 스킬 내용을 기준으로 판단한다
|
||||||
|
- 관련성이 아주 낮아 보여도, 가능성이 있으면 먼저 확인한다
|
||||||
|
|
||||||
|
## 기대 결과
|
||||||
|
|
||||||
|
이 스킬의 목적은 다음 상태를 만드는 것입니다.
|
||||||
|
|
||||||
|
- 관련 스킬이 빠지지 않는다
|
||||||
|
- 작업 접근 순서가 일관된다
|
||||||
|
- 응답 전에 방법론이 먼저 고정된다
|
||||||
|
- 임의 판단과 과잉 추정이 줄어든다
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
---
|
||||||
|
name: "webapp-testing"
|
||||||
|
description: "Playwright 기반으로 로컬 웹앱을 점검하고 테스트합니다. 프론트엔드 동작 검증, UI 디버깅, 스크린샷, 브라우저 로그 확인이 필요할 때 호출합니다."
|
||||||
|
---
|
||||||
|
|
||||||
|
# 웹앱 테스트
|
||||||
|
|
||||||
|
이 스킬은 로컬 웹 애플리케이션을 브라우저에서 실제로 검증하기 위한 Playwright 기반 테스트 가이드입니다.
|
||||||
|
|
||||||
|
## 기본 원칙
|
||||||
|
|
||||||
|
- 동적 웹앱은 렌더링이 끝나기 전에 DOM을 섣불리 읽지 않습니다.
|
||||||
|
- 브라우저 조작 전에 먼저 화면 상태를 관찰합니다.
|
||||||
|
- 가능하면 기본 제공 스크립트를 블랙박스로 활용합니다.
|
||||||
|
- 정적인 추측보다 브라우저에서 직접 확인한 셀렉터와 상태를 신뢰합니다.
|
||||||
|
|
||||||
|
## 사용 가능한 보조 스크립트
|
||||||
|
|
||||||
|
- `scripts/with_server.py` - 서버 실행과 종료를 관리합니다.
|
||||||
|
|
||||||
|
이 스크립트는 먼저 `--help`로 사용법을 확인한 뒤 사용합니다. 소스를 먼저 읽기보다, 가능한 한 도구처럼 호출하는 방식을 우선합니다.
|
||||||
|
|
||||||
|
## 언제 사용할지
|
||||||
|
|
||||||
|
- 로컬 웹앱의 실제 동작을 확인해야 할 때
|
||||||
|
- 버튼, 폼, 내비게이션, 모달 같은 UI 상호작용을 점검할 때
|
||||||
|
- 스크린샷이나 브라우저 로그가 필요할 때
|
||||||
|
- 동적 렌더링 이후의 DOM을 기준으로 문제를 확인해야 할 때
|
||||||
|
|
||||||
|
## 접근 방식 결정
|
||||||
|
|
||||||
|
### 정적 HTML인 경우
|
||||||
|
|
||||||
|
- 파일을 직접 읽어 셀렉터를 먼저 확인합니다.
|
||||||
|
- 그 셀렉터를 바탕으로 Playwright 스크립트를 작성합니다.
|
||||||
|
- 직접 확인이 부족하면 동적 앱처럼 취급합니다.
|
||||||
|
|
||||||
|
### 동적 웹앱인 경우
|
||||||
|
|
||||||
|
- 서버가 안 떠 있다면 `with_server.py`로 서버 라이프사이클을 관리합니다.
|
||||||
|
- 서버가 이미 떠 있다면 브라우저에서 먼저 정찰한 뒤 조작합니다.
|
||||||
|
|
||||||
|
## 정찰 후 행동 패턴
|
||||||
|
|
||||||
|
이 스킬의 기본 패턴은 "정찰 후 행동"입니다.
|
||||||
|
|
||||||
|
1. 페이지에 접속합니다.
|
||||||
|
2. `networkidle` 상태까지 기다립니다.
|
||||||
|
3. 스크린샷을 찍거나 렌더링된 DOM을 확인합니다.
|
||||||
|
4. 그 상태를 기준으로 셀렉터를 식별합니다.
|
||||||
|
5. 그 다음 클릭, 입력, 검증 같은 행동을 수행합니다.
|
||||||
|
|
||||||
|
정적 분석으로 셀렉터를 추측하는 대신, 실제 렌더링 결과를 기준으로 선택자를 확정합니다.
|
||||||
|
|
||||||
|
## 서버 관리 예시
|
||||||
|
|
||||||
|
단일 서버:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/with_server.py --server "npm run dev" --port 5173 -- python your_automation.py
|
||||||
|
```
|
||||||
|
|
||||||
|
복수 서버:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/with_server.py --server "cd backend && python server.py" --port 3000 --server "cd frontend && npm run dev" --port 5173 -- python your_automation.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## 자동화 스크립트 원칙
|
||||||
|
|
||||||
|
- `sync_playwright()`를 기본으로 사용합니다.
|
||||||
|
- Chromium은 headless 모드로 실행합니다.
|
||||||
|
- 페이지 이동 후 `page.wait_for_load_state('networkidle')`를 호출합니다.
|
||||||
|
- 작업이 끝나면 브라우저를 닫습니다.
|
||||||
|
|
||||||
|
## with_server.py 사용 원칙
|
||||||
|
|
||||||
|
- 먼저 `python scripts/with_server.py --help`로 옵션을 확인합니다.
|
||||||
|
- 서버가 여러 개인 경우에도 이 스크립트로 생명주기를 한 번에 관리합니다.
|
||||||
|
- Playwright 스크립트 안에는 서버 시작 로직을 넣지 않습니다.
|
||||||
|
- 자동화 스크립트는 브라우저 조작에만 집중합니다.
|
||||||
|
|
||||||
|
## 좋은 습관
|
||||||
|
|
||||||
|
- 먼저 관찰하고 나중에 조작합니다.
|
||||||
|
- `text=`, `role=`, CSS 선택자, ID 등 설명력 있는 셀렉터를 씁니다.
|
||||||
|
- 필요한 경우 `wait_for_selector()` 같은 명시적 대기를 사용합니다.
|
||||||
|
- 스크린샷과 콘솔 로그 수집을 적극 활용합니다.
|
||||||
|
- 재현이 불안정하면 관찰 스텝을 늘리고, 행동 스텝은 줄여 원인을 좁힙니다.
|
||||||
|
|
||||||
|
## 흔한 실수
|
||||||
|
|
||||||
|
- JS가 끝나기 전에 DOM을 확인하는 것
|
||||||
|
- 서버 실행과 테스트 로직을 뒤섞는 것
|
||||||
|
- 셀렉터 확인 없이 곧바로 클릭부터 시도하는 것
|
||||||
|
- 렌더링 전 HTML만 보고 동적 UI 동작을 단정하는 것
|
||||||
|
|
||||||
|
## 기대 결과
|
||||||
|
|
||||||
|
이 스킬을 적용한 결과물은 다음을 만족해야 합니다.
|
||||||
|
|
||||||
|
- 서버 실행과 테스트 로직이 분리되어 있어야 합니다.
|
||||||
|
- 브라우저에서 실제 보이는 상태를 기준으로 상호작용이 설계되어야 합니다.
|
||||||
|
- 셀렉터와 검증 조건이 관찰 결과를 기반으로 정해져야 합니다.
|
||||||
|
- 필요 시 스크린샷, 콘솔 로그, DOM 확인 결과를 남길 수 있어야 합니다.
|
||||||
|
|
||||||
|
## 목표
|
||||||
|
|
||||||
|
이 스킬의 목적은 브라우저에서 실제 사용자 흐름을 검증하고, 화면 기준으로 문제를 발견하며, 재현 가능한 자동화 스크립트를 만드는 것입니다.
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
---
|
||||||
|
name: "writing-plans"
|
||||||
|
description: "명세가 정해진 다단계 작업을 실행 계획으로 바꿉니다. 코드를 건드리기 전에 구체적인 구현 계획과 작업 순서를 작성해야 할 때 호출합니다."
|
||||||
|
---
|
||||||
|
|
||||||
|
# 구현 계획 작성
|
||||||
|
|
||||||
|
이 스킬은 명세나 요구사항이 있는 작업을, 실제 엔지니어가 바로 수행할 수 있는 상세 구현 계획으로 바꾸기 위한 가이드입니다.
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
계획은 코드베이스 맥락이 거의 없는 개발자도 이해할 수 있도록 충분히 구체적이어야 합니다. 어떤 파일을 수정할지, 어떤 테스트를 작성할지, 무엇을 어떤 순서로 검증할지까지 모두 포함해야 합니다.
|
||||||
|
|
||||||
|
핵심 원칙은 다음과 같습니다.
|
||||||
|
|
||||||
|
- DRY
|
||||||
|
- YAGNI
|
||||||
|
- TDD
|
||||||
|
- 잦은 커밋
|
||||||
|
- 작은 단계
|
||||||
|
|
||||||
|
계획 문서는 기본적으로 `docs/superpowers/plans/YYYY-MM-DD-<feature-name>.md` 에 저장합니다.
|
||||||
|
|
||||||
|
시작할 때는 현재 구현 계획 작성 단계에 들어왔음을 명확히 선언합니다.
|
||||||
|
|
||||||
|
## 범위 점검
|
||||||
|
|
||||||
|
- 명세가 여러 독립 하위 시스템을 포함한다면 계획도 분리합니다.
|
||||||
|
- 하나의 계획은 자체적으로 구현, 테스트, 검증 가능한 범위를 가져야 합니다.
|
||||||
|
- 하나의 계획만으로 독립적으로 완료 가능한 단위를 만드는 것이 목표입니다.
|
||||||
|
|
||||||
|
## 파일 구조 먼저 정의
|
||||||
|
|
||||||
|
작업을 나누기 전에 어떤 파일을 만들고 수정할지 먼저 정리합니다.
|
||||||
|
|
||||||
|
- 각 파일은 하나의 명확한 책임만 가지게 설계합니다.
|
||||||
|
- 함께 바뀌는 파일은 가까이 두고, 책임 기준으로 나눕니다.
|
||||||
|
- 기존 코드베이스의 패턴을 따릅니다.
|
||||||
|
- 지나치게 커진 파일을 다뤄야 한다면 계획 안에 분리 작업을 포함할 수 있습니다.
|
||||||
|
- 분해 결정은 이 단계에서 고정하는 것이 좋습니다. 계획 후반에 파일 책임이 바뀌면 전체 작업이 흔들립니다.
|
||||||
|
|
||||||
|
## 작업 단위 규칙
|
||||||
|
|
||||||
|
각 단계는 2~5분 안에 수행 가능한 하나의 행동이어야 합니다.
|
||||||
|
|
||||||
|
- 실패 테스트 작성
|
||||||
|
- 테스트를 실행해 실패 확인
|
||||||
|
- 최소 구현 작성
|
||||||
|
- 테스트를 실행해 통과 확인
|
||||||
|
- 커밋
|
||||||
|
|
||||||
|
이처럼 한 단계에는 한 행동만 담습니다.
|
||||||
|
|
||||||
|
좋은 계획은 "무엇을 할지"만 적지 않고, "어떻게 검증할지"까지 함께 적습니다.
|
||||||
|
|
||||||
|
## 계획 문서 헤더
|
||||||
|
|
||||||
|
모든 계획 문서는 다음 구조로 시작합니다.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# [기능명] 구현 계획
|
||||||
|
> **에이전트 작업자용:** 각 작업은 체크박스(`- [ ]`)로 추적합니다.
|
||||||
|
**목표:** [이 기능이 무엇을 만드는지 한 문장]
|
||||||
|
**아키텍처:** [접근 방식 2~3문장]
|
||||||
|
**기술 스택:** [핵심 기술]
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
## 작업 구조
|
||||||
|
|
||||||
|
각 작업은 아래 내용을 포함해야 합니다.
|
||||||
|
|
||||||
|
- 대상 파일 경로
|
||||||
|
- 생성/수정/테스트 파일 구분
|
||||||
|
- 실제 테스트 코드 예시
|
||||||
|
- 실행 명령과 기대 결과
|
||||||
|
- 최소 구현 코드 예시
|
||||||
|
- 커밋 명령 예시
|
||||||
|
|
||||||
|
가능하다면 각 작업은 독립적으로 읽혀야 합니다. 작업을 순서대로 읽지 않아도 필요한 정보가 빠지지 않아야 합니다.
|
||||||
|
|
||||||
|
## 금지 사항
|
||||||
|
|
||||||
|
다음과 같은 placeholder는 허용하지 않습니다.
|
||||||
|
|
||||||
|
- TBD
|
||||||
|
- TODO
|
||||||
|
- 나중에 구현
|
||||||
|
- 적절한 에러 처리 추가
|
||||||
|
- 위 내용을 테스트 작성
|
||||||
|
- Task N과 유사
|
||||||
|
- 방법 설명만 있고 실제 코드나 명령이 없는 단계
|
||||||
|
|
||||||
|
모든 단계는 실행에 필요한 실제 내용을 포함해야 합니다.
|
||||||
|
|
||||||
|
다음 표현도 피합니다.
|
||||||
|
|
||||||
|
- 적당히 구현
|
||||||
|
- 필요하면 보완
|
||||||
|
- 상황에 맞게 처리
|
||||||
|
- 일반적인 에러 처리 추가
|
||||||
|
- 위 단계 참고
|
||||||
|
|
||||||
|
이런 표현은 실행 정보를 숨기기 때문에 계획 품질을 떨어뜨립니다.
|
||||||
|
|
||||||
|
## 자체 검토
|
||||||
|
|
||||||
|
계획 작성 후 아래를 스스로 점검합니다.
|
||||||
|
|
||||||
|
1. 명세의 모든 요구사항이 작업에 반영됐는가
|
||||||
|
2. placeholder나 빈칸이 남지 않았는가
|
||||||
|
3. 함수명, 타입, 시그니처, 속성명이 작업 간 일관적인가
|
||||||
|
|
||||||
|
빠진 요구사항이 있으면 즉시 작업을 추가합니다.
|
||||||
|
|
||||||
|
추가로 아래도 확인합니다.
|
||||||
|
|
||||||
|
- 각 작업이 실제로 끝나는 단위인지
|
||||||
|
- 테스트나 검증 단계가 빠지지 않았는지
|
||||||
|
- 파일 경로가 모호하지 않은지
|
||||||
|
- 한 작업에 여러 행동이 뭉쳐 있지 않은지
|
||||||
|
|
||||||
|
## 완료 후 인계
|
||||||
|
|
||||||
|
계획 저장 후에는 실행 방식을 사용자에게 선택하게 합니다.
|
||||||
|
|
||||||
|
- 서브에이전트 기반 실행
|
||||||
|
- 현재 세션에서 인라인 실행
|
||||||
|
|
||||||
|
핵심은, 구현 전에 계획이 먼저 완성되고 검토 가능해야 한다는 점입니다.
|
||||||
|
|
||||||
|
## 기대 결과
|
||||||
|
|
||||||
|
이 스킬의 결과물은 다음을 만족해야 합니다.
|
||||||
|
|
||||||
|
- 구현자가 문맥 없이도 작업을 시작할 수 있어야 합니다.
|
||||||
|
- 각 작업은 체크리스트처럼 따라갈 수 있어야 합니다.
|
||||||
|
- 테스트와 검증 명령이 포함되어 있어야 합니다.
|
||||||
|
- 문서 안에 실행 가능한 수준의 구체성이 있어야 합니다.
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
# skillDesk
|
||||||
|
|
||||||
|
TRAE에서 바로 사용할 수 있도록 정리한 에이전트 스킬 모음 저장소입니다.
|
||||||
|
`skillText.md`에 정리된 인기 스킬 10개를 현재 프로젝트의 `.trae/skills` 구조로 옮기고, 한국어로 확장 설명을 추가했습니다.
|
||||||
|
|
||||||
|
## 프로젝트 목적
|
||||||
|
|
||||||
|
이 저장소는 다음 목적을 가집니다.
|
||||||
|
|
||||||
|
- 자주 쓰이는 에이전트 스킬 10개를 현재 프로젝트 안에서 바로 참조 가능하게 정리
|
||||||
|
- 원문에 가까운 규칙, 사용 시점, 워크플로를 한국어로 복원
|
||||||
|
- TRAE에서 실제로 검증할 수 있는 프롬프트형 테스트 시나리오 제공
|
||||||
|
|
||||||
|
## 포함된 스킬
|
||||||
|
|
||||||
|
- [brainstorming](./.trae/skills/brainstorming/SKILL.md)
|
||||||
|
- [frontend-design](./.trae/skills/frontend-design/SKILL.md)
|
||||||
|
- [ui-ux-pro-max](./.trae/skills/ui-ux-pro-max/SKILL.md)
|
||||||
|
- [systematic-debugging](./.trae/skills/systematic-debugging/SKILL.md)
|
||||||
|
- [writing-plans](./.trae/skills/writing-plans/SKILL.md)
|
||||||
|
- [find-skills](./.trae/skills/find-skills/SKILL.md)
|
||||||
|
- [using-superpowers](./.trae/skills/using-superpowers/SKILL.md)
|
||||||
|
- [karpathy-guidelines](./.trae/skills/karpathy-guidelines/SKILL.md)
|
||||||
|
- [webapp-testing](./.trae/skills/webapp-testing/SKILL.md)
|
||||||
|
- [agent-browser](./.trae/skills/agent-browser/SKILL.md)
|
||||||
|
|
||||||
|
## 디렉터리 구조
|
||||||
|
|
||||||
|
```text
|
||||||
|
skillDesk/
|
||||||
|
├── .trae/
|
||||||
|
│ └── skills/
|
||||||
|
│ ├── brainstorming/
|
||||||
|
│ ├── frontend-design/
|
||||||
|
│ ├── ui-ux-pro-max/
|
||||||
|
│ ├── systematic-debugging/
|
||||||
|
│ ├── writing-plans/
|
||||||
|
│ ├── find-skills/
|
||||||
|
│ ├── using-superpowers/
|
||||||
|
│ ├── karpathy-guidelines/
|
||||||
|
│ ├── webapp-testing/
|
||||||
|
│ └── agent-browser/
|
||||||
|
├── docs/
|
||||||
|
│ ├── superpowers/
|
||||||
|
│ │ ├── specs/
|
||||||
|
│ │ └── plans/
|
||||||
|
│ └── test-scenarios.md
|
||||||
|
├── README.md
|
||||||
|
└── skillText.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 문서 구성
|
||||||
|
|
||||||
|
- [skillText.md](./skillText.md): 10개 인기 스킬 소개 번역본
|
||||||
|
- 각 `SKILL.md`: 실제 프로젝트용 스킬 설명과 사용 규칙
|
||||||
|
- [테스트 시나리오](./docs/test-scenarios.md): TRAE에서 바로 실행해 볼 수 있는 검증 프롬프트 모음
|
||||||
|
- [설계 문서](./docs/superpowers/specs/2026-06-09-skill-docs-design.md): 문서 확장 설계
|
||||||
|
- [구현 계획](./docs/superpowers/plans/2026-06-09-skill-docs-expansion.md): 실행 계획 기록
|
||||||
|
|
||||||
|
## TRAE에서 사용하는 방법
|
||||||
|
|
||||||
|
1. 이 프로젝트를 TRAE에서 엽니다.
|
||||||
|
2. `.trae/skills/` 아래 스킬들이 인식되는 환경에서 작업합니다.
|
||||||
|
3. 작업 요청을 입력하면, 적절한 스킬이 먼저 호출되는지 확인합니다.
|
||||||
|
4. 스킬별 동작을 검증하려면 [테스트 시나리오](./docs/test-scenarios.md)의 프롬프트를 그대로 사용합니다.
|
||||||
|
|
||||||
|
## 추천 사용 흐름
|
||||||
|
|
||||||
|
작업 성격에 따라 아래 순서를 추천합니다.
|
||||||
|
|
||||||
|
- 신규 기능/설계: `brainstorming` -> `writing-plans` -> 구현 스킬
|
||||||
|
- UI 작업: `brainstorming` -> `frontend-design` 또는 `ui-ux-pro-max`
|
||||||
|
- 버그 수정: `systematic-debugging` -> 도메인 스킬
|
||||||
|
- 브라우저 검증: `webapp-testing` 또는 `agent-browser`
|
||||||
|
- 스킬 탐색: `find-skills`
|
||||||
|
- 전반 운영 규칙: `using-superpowers`, `karpathy-guidelines`
|
||||||
|
|
||||||
|
## 문서 확장 기준
|
||||||
|
|
||||||
|
이번 저장소의 스킬 문서는 다음 기준으로 정리했습니다.
|
||||||
|
|
||||||
|
- 원문 핵심 규칙, 단계, 금지 사항은 최대한 유지
|
||||||
|
- 한국어 가독성을 위해 구조와 문장을 재편집
|
||||||
|
- 단순 번역보다 실제 프로젝트에서 바로 읽히는 운영 문서 형태로 정리
|
||||||
|
- frontmatter `description`은 짧고 실사용 판단에 유리하게 유지
|
||||||
|
|
||||||
|
## 빠른 확인 포인트
|
||||||
|
|
||||||
|
- 설계 없이 구현으로 바로 가는가 -> `brainstorming`
|
||||||
|
- 계획 없이 바로 코드를 바꾸려는가 -> `writing-plans`
|
||||||
|
- 버그를 감으로 고치려는가 -> `systematic-debugging`
|
||||||
|
- UI가 평범하고 맥락이 약한가 -> `frontend-design`, `ui-ux-pro-max`
|
||||||
|
- 적절한 스킬이 있는지부터 모르겠는가 -> `find-skills`
|
||||||
|
|
||||||
|
## 테스트 시나리오
|
||||||
|
|
||||||
|
실제 TRAE 검증용 프롬프트는 아래 문서에 정리되어 있습니다.
|
||||||
|
|
||||||
|
- [docs/test-scenarios.md](./docs/test-scenarios.md)
|
||||||
|
|
||||||
|
## SQLite + FastAPI + Vue CRUD 예제 실행
|
||||||
|
|
||||||
|
현재 저장소에는 SQLite의 `messages` 테이블 데이터를 FastAPI가 읽고 쓰며, Vue 화면에서 조회, 추가, 수정, 삭제할 수 있는 최소 CRUD 예제가 포함되어 있습니다.
|
||||||
|
|
||||||
|
### 1. 백엔드 실행
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install -r backend/requirements.txt
|
||||||
|
python backend/init_db.py
|
||||||
|
uvicorn backend.main:app --host 127.0.0.1 --port 8000 --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 프론트엔드 실행
|
||||||
|
|
||||||
|
새 터미널에서 아래 명령을 실행합니다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m http.server 5173 -d frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 확인
|
||||||
|
|
||||||
|
- 백엔드 API 목록 조회: `http://127.0.0.1:8000/api/messages`
|
||||||
|
- 프론트엔드 화면: `http://127.0.0.1:5173`
|
||||||
|
- 기존 단일 조회 호환 API: `http://127.0.0.1:8000/api/message`
|
||||||
|
|
||||||
|
정상 동작 시 Vue 화면에서 초기 데이터 `hello world`를 포함한 메시지 목록이 보이고, 새 메시지 추가, 기존 메시지 수정, 기존 메시지 삭제가 모두 가능합니다.
|
||||||
|
|
||||||
|
### 4. 포트 충돌 시 대체 실행
|
||||||
|
|
||||||
|
이미 `8000` 또는 `5173` 포트를 다른 프로그램이 사용 중이면 아래처럼 대체 포트를 사용합니다.
|
||||||
|
|
||||||
|
#### 백엔드 대체 포트 예시
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn backend.main:app --host 127.0.0.1 --port 8001 --reload
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 프론트엔드 대체 포트 예시
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m http.server 5174 -d frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 프론트 API 포트 맞추기
|
||||||
|
|
||||||
|
프론트가 다른 백엔드 포트를 보도록 하려면 [index.html](file:///Users/woozooni/Documents/trae_projects/skillDesk/frontend/index.html#L8-L15) 의 `window.APP_CONFIG`에서 `apiPort` 값을 백엔드 실행 포트와 동일하게 수정합니다.
|
||||||
|
|
||||||
|
예를 들어 백엔드를 `8001`로 실행했다면 아래처럼 맞춥니다.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
window.APP_CONFIG = {
|
||||||
|
apiHost: 'http://127.0.0.1',
|
||||||
|
apiPort: '8001',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
이 경우 접속 주소는 아래와 같습니다.
|
||||||
|
|
||||||
|
- 백엔드 API 목록 조회: `http://127.0.0.1:8001/api/messages`
|
||||||
|
- 프론트엔드 화면: `http://127.0.0.1:5174`
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,19 @@
|
|||||||
|
import sqlite3 # SQLite 데이터베이스를 다루기 위한 모듈을 가져온다.
|
||||||
|
from pathlib import Path # 파일 경로를 안전하게 계산하기 위한 모듈을 가져온다.
|
||||||
|
|
||||||
|
DB_PATH = Path(__file__).resolve().parent / "app.db" # 현재 파일 기준으로 데이터베이스 파일 경로를 정한다.
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None: # 데이터베이스를 초기화하는 메인 함수를 정의한다.
|
||||||
|
connection = sqlite3.connect(DB_PATH) # SQLite 데이터베이스에 연결한다.
|
||||||
|
cursor = connection.cursor() # SQL 실행을 위한 커서를 만든다.
|
||||||
|
cursor.execute("CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT NOT NULL)") # 메시지 테이블이 없으면 생성한다.
|
||||||
|
cursor.execute("DELETE FROM messages") # 예제를 단순하게 유지하기 위해 기존 메시지를 모두 지운다.
|
||||||
|
cursor.execute("INSERT INTO messages (content) VALUES (?)", ("hello world",)) # hello world 예제 데이터를 한 건 추가한다.
|
||||||
|
connection.commit() # 변경 내용을 데이터베이스에 저장한다.
|
||||||
|
connection.close() # 데이터베이스 연결을 닫는다.
|
||||||
|
print(f"Database initialized at: {DB_PATH}") # 초기화된 데이터베이스 경로를 출력한다.
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__": # 현재 파일을 직접 실행했을 때만 초기화 함수를 호출한다.
|
||||||
|
main() # 데이터베이스 초기화를 수행한다.
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import sqlite3 # SQLite 데이터베이스를 읽고 쓰기 위한 모듈을 가져온다.
|
||||||
|
from pathlib import Path # 데이터베이스 파일 경로를 계산하기 위한 모듈을 가져온다.
|
||||||
|
|
||||||
|
from fastapi import FastAPI, HTTPException # FastAPI 앱과 예외 응답 도구를 가져온다.
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware # 프론트엔드 연동을 위한 CORS 미들웨어를 가져온다.
|
||||||
|
from pydantic import BaseModel # 요청 본문을 검증하기 위한 기본 모델 클래스를 가져온다.
|
||||||
|
|
||||||
|
DB_PATH = Path(__file__).resolve().parent / "app.db" # 현재 파일 기준으로 데이터베이스 파일 경로를 정한다.
|
||||||
|
app = FastAPI(title="SQLite CRUD API") # FastAPI 애플리케이션 인스턴스를 만든다.
|
||||||
|
app.add_middleware(CORSMiddleware, allow_origins=["http://127.0.0.1:5173", "http://localhost:5173"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"]) # Vue 개발 서버에서 API를 호출할 수 있도록 CORS를 허용한다.
|
||||||
|
|
||||||
|
|
||||||
|
class MessagePayload(BaseModel): # 메시지 생성과 수정을 위한 요청 모델을 정의한다.
|
||||||
|
content: str # 요청 본문에서 메시지 내용을 문자열로 받는다.
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_database_exists() -> None: # 데이터베이스 파일 존재 여부를 확인하는 함수를 정의한다.
|
||||||
|
if not DB_PATH.exists(): # 데이터베이스 파일이 아직 생성되지 않았는지 확인한다.
|
||||||
|
raise HTTPException(status_code=500, detail="Database file does not exist. Run backend/init_db.py first.") # 초기화 스크립트 실행이 필요하다는 오류를 반환한다.
|
||||||
|
|
||||||
|
|
||||||
|
def get_connection() -> sqlite3.Connection: # SQLite 연결 객체를 공통으로 만드는 함수를 정의한다.
|
||||||
|
ensure_database_exists() # 데이터베이스 파일이 존재하는지 먼저 확인한다.
|
||||||
|
connection = sqlite3.connect(DB_PATH) # SQLite 데이터베이스에 연결한다.
|
||||||
|
connection.row_factory = sqlite3.Row # 컬럼 이름으로 접근할 수 있도록 Row 팩토리를 지정한다.
|
||||||
|
return connection # 설정이 끝난 연결 객체를 반환한다.
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_content(content: str) -> str: # 메시지 내용을 공통 규칙으로 정리하는 함수를 정의한다.
|
||||||
|
normalized_content = content.strip() # 앞뒤 공백을 제거해 실제 입력값만 남긴다.
|
||||||
|
if not normalized_content: # 공백만 있거나 빈 문자열인 경우를 확인한다.
|
||||||
|
raise HTTPException(status_code=400, detail="Content must not be empty") # 빈 메시지는 허용하지 않는다는 오류를 반환한다.
|
||||||
|
return normalized_content # 검증을 통과한 메시지 내용을 반환한다.
|
||||||
|
|
||||||
|
|
||||||
|
def serialize_message(row: sqlite3.Row) -> dict[str, int | str]: # SQLite 행 데이터를 JSON 응답용 딕셔너리로 바꾸는 함수를 정의한다.
|
||||||
|
return {"id": row["id"], "content": row["content"]} # id와 content만 꺼내서 반환한다.
|
||||||
|
|
||||||
|
|
||||||
|
def read_first_message() -> str: # 데이터베이스에서 첫 번째 메시지를 읽는 함수를 정의한다.
|
||||||
|
with get_connection() as connection: # 데이터베이스 연결을 열고 자동으로 닫히게 한다.
|
||||||
|
row = connection.execute("SELECT content FROM messages ORDER BY id ASC LIMIT 1").fetchone() # 가장 먼저 저장된 메시지 한 건을 조회한다.
|
||||||
|
if row is None: # 조회된 메시지가 없는 경우를 확인한다.
|
||||||
|
raise HTTPException(status_code=404, detail="Message not found") # 메시지가 없다는 404 오류를 반환한다.
|
||||||
|
return row["content"] # 조회한 메시지 내용을 반환한다.
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/message") # 기존 예제와 호환되도록 첫 번째 메시지를 반환하는 GET 엔드포인트를 유지한다.
|
||||||
|
def get_message() -> dict[str, str]: # JSON 응답 형태의 딕셔너리를 반환하는 함수를 정의한다.
|
||||||
|
return {"message": read_first_message()} # 데이터베이스에서 읽은 첫 번째 메시지를 JSON으로 반환한다.
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/messages") # 전체 메시지 목록을 반환하는 GET 엔드포인트를 정의한다.
|
||||||
|
def list_messages() -> list[dict[str, int | str]]: # 메시지 목록을 JSON 배열 형태로 반환하는 함수를 정의한다.
|
||||||
|
with get_connection() as connection: # 데이터베이스 연결을 열고 자동으로 닫히게 한다.
|
||||||
|
rows = connection.execute("SELECT id, content FROM messages ORDER BY id ASC").fetchall() # 저장된 메시지를 id 오름차순으로 모두 조회한다.
|
||||||
|
return [serialize_message(row) for row in rows] # 조회된 모든 행을 직렬화해 반환한다.
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/api/messages", status_code=201) # 새 메시지를 저장하는 POST 엔드포인트를 정의한다.
|
||||||
|
def create_message(payload: MessagePayload) -> dict[str, int | str]: # 생성된 메시지 정보를 반환하는 함수를 정의한다.
|
||||||
|
normalized_content = normalize_content(payload.content) # 요청 본문의 메시지 내용을 공통 규칙으로 정리한다.
|
||||||
|
with get_connection() as connection: # 데이터베이스 연결을 열고 자동으로 닫히게 한다.
|
||||||
|
cursor = connection.execute("INSERT INTO messages (content) VALUES (?)", (normalized_content,)) # 정리된 메시지 내용을 테이블에 저장한다.
|
||||||
|
connection.commit() # INSERT 결과를 데이터베이스에 반영한다.
|
||||||
|
return {"id": int(cursor.lastrowid), "content": normalized_content} # 생성된 id와 메시지 내용을 응답으로 반환한다.
|
||||||
|
|
||||||
|
|
||||||
|
@app.put("/api/messages/{message_id}") # 기존 메시지를 수정하는 PUT 엔드포인트를 정의한다.
|
||||||
|
def update_message(message_id: int, payload: MessagePayload) -> dict[str, int | str]: # 수정된 메시지 정보를 반환하는 함수를 정의한다.
|
||||||
|
normalized_content = normalize_content(payload.content) # 요청 본문의 메시지 내용을 공통 규칙으로 정리한다.
|
||||||
|
with get_connection() as connection: # 데이터베이스 연결을 열고 자동으로 닫히게 한다.
|
||||||
|
cursor = connection.execute("UPDATE messages SET content = ? WHERE id = ?", (normalized_content, message_id)) # 지정한 id의 메시지 내용을 새 값으로 수정한다.
|
||||||
|
connection.commit() # UPDATE 결과를 데이터베이스에 반영한다.
|
||||||
|
if cursor.rowcount == 0: # 실제로 수정된 행이 없는 경우를 확인한다.
|
||||||
|
raise HTTPException(status_code=404, detail="Message not found") # 없는 메시지라는 404 오류를 반환한다.
|
||||||
|
return {"id": message_id, "content": normalized_content} # 수정된 id와 메시지 내용을 응답으로 반환한다.
|
||||||
|
|
||||||
|
|
||||||
|
@app.delete("/api/messages/{message_id}") # 기존 메시지를 삭제하는 DELETE 엔드포인트를 정의한다.
|
||||||
|
def delete_message(message_id: int) -> dict[str, bool]: # 삭제 성공 여부를 반환하는 함수를 정의한다.
|
||||||
|
with get_connection() as connection: # 데이터베이스 연결을 열고 자동으로 닫히게 한다.
|
||||||
|
cursor = connection.execute("DELETE FROM messages WHERE id = ?", (message_id,)) # 지정한 id의 메시지를 테이블에서 삭제한다.
|
||||||
|
connection.commit() # DELETE 결과를 데이터베이스에 반영한다.
|
||||||
|
if cursor.rowcount == 0: # 실제로 삭제된 행이 없는 경우를 확인한다.
|
||||||
|
raise HTTPException(status_code=404, detail="Message not found") # 없는 메시지라는 404 오류를 반환한다.
|
||||||
|
return {"success": True} # 삭제가 성공했음을 JSON으로 반환한다.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fastapi
|
||||||
|
uvicorn
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# SQLite + FastAPI + Vue 전역 설정 분리 구현 계획
|
||||||
|
> **에이전트 작업자용:** 각 작업은 체크박스(`- [ ]`)로 추적합니다.
|
||||||
|
**목표:** 프론트엔드가 `index.html`의 전역 설정 객체에서 API 호스트와 포트를 읽어 오도록 바꿔, 포트 변경 시 `main.js`를 수정하지 않아도 되게 만든다.
|
||||||
|
**아키텍처:** `frontend/index.html`에 `window.APP_CONFIG`를 정의하고, `frontend/src/main.js`는 이 전역 객체를 읽어 기본값과 함께 `API_BASE_URL`을 조합한다. README는 설정 위치와 포트 충돌 대응 절차를 `index.html` 기준으로 다시 설명한다.
|
||||||
|
**기술 스택:** HTML, Vue 3 ESM, JavaScript, Markdown
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] **작업 1: HTML 전역 설정 객체 추가**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/index.html` (수정)
|
||||||
|
- **할 일:** Vue 앱 스크립트가 실행되기 전에 `window.APP_CONFIG` 객체를 선언하고, 기본 `apiHost`와 `apiPort`를 넣는다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
window.APP_CONFIG = {
|
||||||
|
apiHost: 'http://127.0.0.1',
|
||||||
|
apiPort: '8000',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 -m http.server 5173 -d frontend`
|
||||||
|
- **기대 결과:** 브라우저가 `index.html`을 로드하면 Vue 앱 시작 전 전역 설정 객체가 준비된다.
|
||||||
|
|
||||||
|
- [ ] **작업 2: main.js가 전역 설정과 기본값을 읽도록 변경**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/src/main.js` (수정)
|
||||||
|
- **할 일:** 기존 `API_HOST`, `API_PORT` 상수를 `window.APP_CONFIG` 기반으로 바꾸고, 설정이 없을 때 기본값 `127.0.0.1:8000`이 유지되게 한다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```javascript
|
||||||
|
const appConfig = window.APP_CONFIG ?? {}
|
||||||
|
const apiHost = appConfig.apiHost || 'http://127.0.0.1'
|
||||||
|
const apiPort = appConfig.apiPort || '8000'
|
||||||
|
const API_BASE_URL = `${apiHost}:${apiPort}/api`
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 -m py_compile backend/main.py`
|
||||||
|
- **기대 결과:** `main.js`를 수정하지 않고 `index.html` 값만 바꿔 다른 백엔드 포트로 연결할 수 있다.
|
||||||
|
|
||||||
|
- [ ] **작업 3: 기존 CRUD fetch 흐름 유지 확인**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/src/main.js` (검토)
|
||||||
|
- **할 일:** 생성, 조회, 수정, 삭제 fetch 호출이 모두 새 `API_BASE_URL`을 그대로 사용하도록 유지한다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```javascript
|
||||||
|
const response = await fetch(`${API_BASE_URL}/messages`)
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 -m py_compile backend/main.py`
|
||||||
|
- **기대 결과:** 설정 방식만 바뀌고 CRUD 기능 경로는 그대로 유지된다.
|
||||||
|
|
||||||
|
- [ ] **작업 4: README 설정 위치 설명 갱신**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/README.md` (수정)
|
||||||
|
- **할 일:** 기존 `main.js`의 `API_PORT` 수정 안내를 `index.html`의 `window.APP_CONFIG.apiPort` 수정 안내로 바꾼다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```markdown
|
||||||
|
`frontend/index.html`의 `window.APP_CONFIG`에서 `apiPort` 값을 백엔드 실행 포트와 맞춥니다.
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 -m py_compile backend/init_db.py backend/main.py`
|
||||||
|
- **기대 결과:** README만 읽어도 설정 수정 위치가 `index.html`이라는 점을 알 수 있다.
|
||||||
|
|
||||||
|
- [ ] **작업 5: 포트 충돌 대응 예시를 전역 설정 방식으로 보강**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/README.md` (수정)
|
||||||
|
- **할 일:** 백엔드를 `8001`, 프론트를 `5174`로 실행할 때 `apiPort: '8001'` 예시를 함께 적는다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```html
|
||||||
|
window.APP_CONFIG = {
|
||||||
|
apiHost: 'http://127.0.0.1',
|
||||||
|
apiPort: '8001',
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 -m py_compile backend/init_db.py backend/main.py`
|
||||||
|
- **기대 결과:** 사용자가 포트 충돌 시 실행 명령과 전역 설정 값을 한 번에 맞출 수 있다.
|
||||||
|
|
||||||
|
- [ ] **작업 6: 진단 및 수동 검증**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/index.html` (진단), `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/src/main.js` (진단), `/Users/woozooni/Documents/trae_projects/skillDesk/README.md` (진단)
|
||||||
|
- **할 일:** IDE 진단을 확인하고, 필요 시 즉시 수정한다. 가능하면 프론트 정적 서버를 열어 설정 객체가 페이지 로드 전에 선언되는 구조도 점검한다.
|
||||||
|
- **검증 도구:** IDE 진단 확인
|
||||||
|
- **기대 결과:** 새 오류 없이 전역 설정 분리 구조가 문서와 코드에 모두 반영된다.
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# SQLite + FastAPI + Vue 포트 설정 분리 구현 계획
|
||||||
|
> **에이전트 작업자용:** 각 작업은 체크박스(`- [ ]`)로 추적합니다.
|
||||||
|
**목표:** CRUD 예제의 프론트 API 주소와 실행 포트 안내를 분리해 포트 충돌 시 최소 수정으로 다시 실행할 수 있게 만든다.
|
||||||
|
**아키텍처:** 프론트엔드는 `frontend/src/main.js`에서 API 주소를 `API_HOST`, `API_PORT`, `API_BASE_URL` 상수로 나누어 관리한다. 실행 포트 선택은 백엔드와 프론트 모두 명령줄에서 유지하고, README에 기본 포트와 대체 포트 예시를 함께 적어 사용자가 충돌 상황에서 바로 대응할 수 있게 한다.
|
||||||
|
**기술 스택:** Vue 3 ESM, FastAPI, Python http.server, Markdown
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] **작업 1: 프론트 API 주소 상수 구조 변경**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/src/main.js` (수정)
|
||||||
|
- **할 일:** 현재 하나의 문자열로 선언된 `API_BASE_URL`을 `API_HOST`, `API_PORT`, `API_BASE_URL`로 분리한다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```javascript
|
||||||
|
const API_HOST = 'http://127.0.0.1'
|
||||||
|
const API_PORT = '8000'
|
||||||
|
const API_BASE_URL = `${API_HOST}:${API_PORT}/api`
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 -m py_compile backend/main.py`
|
||||||
|
- **기대 결과:** 프론트 코드에서 백엔드 포트를 한 줄만 수정해 다른 포트로 연결할 수 있다.
|
||||||
|
|
||||||
|
- [ ] **작업 2: 프론트 주석과 설정 의도 정리**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/src/main.js` (수정)
|
||||||
|
- **할 일:** 사용자 규칙에 맞춰 각 상수와 조합 이유를 한글 주석으로 설명하고, 기존 fetch 호출이 새 상수를 그대로 사용하도록 유지한다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```javascript
|
||||||
|
const API_HOST = 'http://127.0.0.1' // 백엔드 API 호스트 주소를 별도 상수로 분리한다.
|
||||||
|
const API_PORT = '8000' // 백엔드 포트 충돌 시 이 값만 바꾸면 되도록 한다.
|
||||||
|
const API_BASE_URL = `${API_HOST}:${API_PORT}/api` // 실제 fetch 요청에 사용할 기본 API 주소를 조합한다.
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 -m py_compile backend/main.py`
|
||||||
|
- **기대 결과:** 프론트 상수 구조만 읽어도 포트 변경 지점을 쉽게 찾을 수 있다.
|
||||||
|
|
||||||
|
- [ ] **작업 3: README 기본 실행 예시를 명시적 포트 기준으로 보강**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/README.md` (수정)
|
||||||
|
- **할 일:** 기존 실행 안내에 기본 백엔드 포트 `8000`, 프론트 포트 `5173`을 명령 예시로 명확히 적는다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```markdown
|
||||||
|
uvicorn backend.main:app --host 127.0.0.1 --port 8000
|
||||||
|
python3 -m http.server 5173 -d frontend
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 -m py_compile backend/init_db.py backend/main.py`
|
||||||
|
- **기대 결과:** README만 보고 기본 실행 주소를 바로 이해할 수 있다.
|
||||||
|
|
||||||
|
- [ ] **작업 4: README에 포트 충돌 대응 섹션 추가**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/README.md` (수정)
|
||||||
|
- **할 일:** `8001`, `5174` 같은 대체 포트 예시와 함께, 프론트에서 `API_PORT`를 어디서 바꾸는지 안내한다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```markdown
|
||||||
|
uvicorn backend.main:app --host 127.0.0.1 --port 8001
|
||||||
|
python3 -m http.server 5174 -d frontend
|
||||||
|
`frontend/src/main.js`의 `API_PORT`도 `8001`로 맞춥니다.
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 -m py_compile backend/init_db.py backend/main.py`
|
||||||
|
- **기대 결과:** 포트 충돌 시 사용자가 다음 명령과 코드 수정 위치를 바로 알 수 있다.
|
||||||
|
|
||||||
|
- [ ] **작업 5: 문법 및 진단 확인**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/src/main.js` (진단), `/Users/woozooni/Documents/trae_projects/skillDesk/README.md` (진단)
|
||||||
|
- **할 일:** IDE 진단으로 새 오류를 확인하고, 필요 시 즉시 수정한다.
|
||||||
|
- **검증 도구:** IDE 진단 확인
|
||||||
|
- **기대 결과:** 수정 파일에 새 오류가 없다.
|
||||||
|
|
||||||
|
- [ ] **작업 6: 수동 확인 절차 정리**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/README.md` (검토)
|
||||||
|
- **할 일:** 사용자가 직접 확인할 수 있도록 기본 포트 실행과 대체 포트 실행 시 어떤 주소를 열어야 하는지 최종 점검한다.
|
||||||
|
- **실행 명령:**
|
||||||
|
```bash
|
||||||
|
uvicorn backend.main:app --host 127.0.0.1 --port 8001
|
||||||
|
python3 -m http.server 5174 -d frontend
|
||||||
|
```
|
||||||
|
- **기대 결과:** `frontend/src/main.js`의 `API_PORT`를 `8001`로 맞추면 `http://127.0.0.1:5174`에서 정상 동작해야 한다는 실행 흐름이 README에 반영된다.
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
# Skill 문서 확장 구현 계획
|
||||||
|
> **에이전트 작업자용:** REQUIRED SUB-SKILL: 현재 세션에서는 인라인으로 실행한다. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
**Goal:** 10개 스킬 문서를 원문에 더 가깝게 확장하고, README와 TRAE 테스트 시나리오 문서를 추가한다.
|
||||||
|
**Architecture:** 기존 `.trae/skills/*/SKILL.md`를 스킬 성격별로 확장하고, 저장소 루트에는 탐색용 README를, `docs/`에는 실행 가능한 테스트 시나리오 문서를 둔다. 검증은 Markdown 진단과 문서 간 링크/명칭 일관성 확인으로 마무리한다.
|
||||||
|
**Tech Stack:** Markdown, Trae skill frontmatter, VS Code diagnostics
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: 프로세스 스킬 문서 확장
|
||||||
|
**Files:**
|
||||||
|
- Modify: `.trae/skills/brainstorming/SKILL.md`
|
||||||
|
- Modify: `.trae/skills/systematic-debugging/SKILL.md`
|
||||||
|
- Modify: `.trae/skills/writing-plans/SKILL.md`
|
||||||
|
- Modify: `.trae/skills/using-superpowers/SKILL.md`
|
||||||
|
- Modify: `.trae/skills/karpathy-guidelines/SKILL.md`
|
||||||
|
- Test: 문서 진단 확인
|
||||||
|
|
||||||
|
- [ ] **Step 1: 현재 프로세스 스킬 문서를 다시 읽어 섹션 공통 틀을 고정**
|
||||||
|
Read:
|
||||||
|
```text
|
||||||
|
.trae/skills/brainstorming/SKILL.md
|
||||||
|
.trae/skills/systematic-debugging/SKILL.md
|
||||||
|
.trae/skills/writing-plans/SKILL.md
|
||||||
|
.trae/skills/using-superpowers/SKILL.md
|
||||||
|
.trae/skills/karpathy-guidelines/SKILL.md
|
||||||
|
```
|
||||||
|
Expected: 각 문서에 공통으로 들어갈 섹션(개요, 적용 시점, 워크플로, 금지 사항, 기대 결과)을 정할 수 있다.
|
||||||
|
|
||||||
|
- [ ] **Step 2: brainstorming 문서를 원문 흐름에 가깝게 확장**
|
||||||
|
Include:
|
||||||
|
```markdown
|
||||||
|
## 체크리스트
|
||||||
|
## 프로세스 흐름
|
||||||
|
## 설계 제시 규칙
|
||||||
|
## 문서화와 사용자 검토 게이트
|
||||||
|
## 시각 보조 도구 사용 기준
|
||||||
|
```
|
||||||
|
Expected: 설계 전 구현 금지, 한 번에 한 질문, 2~3개 접근법 제안, writing-plans로 전환 규칙이 모두 드러난다.
|
||||||
|
|
||||||
|
- [ ] **Step 3: systematic-debugging 문서를 4단계 절차 중심으로 확장**
|
||||||
|
Include:
|
||||||
|
```markdown
|
||||||
|
## 철칙
|
||||||
|
## 1단계: 근본 원인 조사
|
||||||
|
## 2단계: 패턴 분석
|
||||||
|
## 3단계: 가설과 검증
|
||||||
|
## 4단계: 구현
|
||||||
|
## 중단 신호
|
||||||
|
```
|
||||||
|
Expected: 수정 전에 조사, 증거 기반 추적, 최소 변경 검증, 3회 이상 실패 시 구조 재검토가 명확해진다.
|
||||||
|
|
||||||
|
- [ ] **Step 4: writing-plans 문서를 계획 품질 기준 중심으로 확장**
|
||||||
|
Include:
|
||||||
|
```markdown
|
||||||
|
## 파일 구조 먼저 정의
|
||||||
|
## 작업 단위 규칙
|
||||||
|
## 계획 문서 헤더
|
||||||
|
## 금지 사항
|
||||||
|
## 자체 검토
|
||||||
|
## 완료 후 인계
|
||||||
|
```
|
||||||
|
Expected: placeholder 금지, 작은 단계, 명확한 파일 경로, 검증 가능한 명령이 강조된다.
|
||||||
|
|
||||||
|
- [ ] **Step 5: using-superpowers와 karpathy-guidelines를 운영 규칙 중심으로 확장**
|
||||||
|
Include:
|
||||||
|
```markdown
|
||||||
|
## 최우선 규칙
|
||||||
|
## 스킬 우선 적용 순서
|
||||||
|
## 흔한 자기합리화 경고
|
||||||
|
## 코딩 전에 먼저 생각하기
|
||||||
|
## 단순함 우선
|
||||||
|
## 수술하듯 수정하기
|
||||||
|
## 목표 기반 실행
|
||||||
|
```
|
||||||
|
Expected: 스킬 선적용 원칙과 과설계 방지 원칙이 각각 독립적으로 읽혀야 한다.
|
||||||
|
|
||||||
|
- [ ] **Step 6: 진단 확인**
|
||||||
|
Run: Markdown diagnostics for the five edited files
|
||||||
|
Expected: 진단 오류 없음
|
||||||
|
|
||||||
|
### Task 2: UI/구현/탐색 스킬 문서 확장
|
||||||
|
**Files:**
|
||||||
|
- Modify: `.trae/skills/frontend-design/SKILL.md`
|
||||||
|
- Modify: `.trae/skills/ui-ux-pro-max/SKILL.md`
|
||||||
|
- Modify: `.trae/skills/find-skills/SKILL.md`
|
||||||
|
- Modify: `.trae/skills/webapp-testing/SKILL.md`
|
||||||
|
- Modify: `.trae/skills/agent-browser/SKILL.md`
|
||||||
|
- Test: 문서 진단 확인
|
||||||
|
|
||||||
|
- [ ] **Step 1: 현재 다섯 문서를 다시 읽고 성격별 확장 포인트 정리**
|
||||||
|
Read:
|
||||||
|
```text
|
||||||
|
.trae/skills/frontend-design/SKILL.md
|
||||||
|
.trae/skills/ui-ux-pro-max/SKILL.md
|
||||||
|
.trae/skills/find-skills/SKILL.md
|
||||||
|
.trae/skills/webapp-testing/SKILL.md
|
||||||
|
.trae/skills/agent-browser/SKILL.md
|
||||||
|
```
|
||||||
|
Expected: 디자인형, 탐색형, 테스트형, 자동화형 문서에 필요한 추가 섹션이 정리된다.
|
||||||
|
|
||||||
|
- [ ] **Step 2: frontend-design 문서를 미학 방향성과 금지 패턴 중심으로 확장**
|
||||||
|
Include:
|
||||||
|
```markdown
|
||||||
|
## 디자인 사고
|
||||||
|
## 미학 가이드라인
|
||||||
|
## 반드시 피할 것
|
||||||
|
## 구현 복잡도와 미학의 일치
|
||||||
|
## 산출물 기대치
|
||||||
|
```
|
||||||
|
Expected: 흔한 AI 스타일 회피, 타이포그래피/색상/모션/공간 구성 원칙이 상세해진다.
|
||||||
|
|
||||||
|
- [ ] **Step 3: ui-ux-pro-max 문서를 적용 조건과 우선순위 점검 기준 중심으로 확장**
|
||||||
|
Include:
|
||||||
|
```markdown
|
||||||
|
## 언제 적용할지
|
||||||
|
## 반드시 사용할 상황
|
||||||
|
## 권장 상황
|
||||||
|
## 불필요한 상황
|
||||||
|
## 우선순위별 검토 항목
|
||||||
|
## 산출물 기준
|
||||||
|
```
|
||||||
|
Expected: 웹/모바일 UI 품질 관리용 종합 규칙처럼 읽힌다.
|
||||||
|
|
||||||
|
- [ ] **Step 4: find-skills, webapp-testing, agent-browser 문서를 실행 절차 중심으로 확장**
|
||||||
|
Include:
|
||||||
|
```markdown
|
||||||
|
## 검색 절차
|
||||||
|
## 품질 검증
|
||||||
|
## 정찰 후 행동 패턴
|
||||||
|
## with_server.py 사용 원칙
|
||||||
|
## agent-browser skills get core
|
||||||
|
## 특수 가이드
|
||||||
|
```
|
||||||
|
Expected: 사용자가 바로 명령과 판단 기준을 가져다 쓸 수 있다.
|
||||||
|
|
||||||
|
- [ ] **Step 5: 진단 확인**
|
||||||
|
Run: Markdown diagnostics for the five edited files
|
||||||
|
Expected: 진단 오류 없음
|
||||||
|
|
||||||
|
### Task 3: README와 테스트 시나리오 문서 작성
|
||||||
|
**Files:**
|
||||||
|
- Create: `README.md`
|
||||||
|
- Create: `docs/test-scenarios.md`
|
||||||
|
- Test: 문서 진단 확인
|
||||||
|
|
||||||
|
- [ ] **Step 1: README 초안 작성**
|
||||||
|
Include:
|
||||||
|
```markdown
|
||||||
|
# skillDesk
|
||||||
|
## 프로젝트 소개
|
||||||
|
## 포함된 스킬
|
||||||
|
## 디렉터리 구조
|
||||||
|
## TRAE에서 사용하는 방법
|
||||||
|
## 추천 사용 흐름
|
||||||
|
## 테스트 시나리오
|
||||||
|
```
|
||||||
|
Expected: 저장소 목적, 10개 스킬, 사용 흐름을 처음 보는 사람도 이해할 수 있다.
|
||||||
|
|
||||||
|
- [ ] **Step 2: 테스트 시나리오 문서 작성**
|
||||||
|
Include:
|
||||||
|
```markdown
|
||||||
|
# TRAE 테스트 시나리오
|
||||||
|
## 공통 사용법
|
||||||
|
## brainstorming 시나리오
|
||||||
|
## frontend-design 시나리오
|
||||||
|
## ...
|
||||||
|
## agent-browser 시나리오
|
||||||
|
```
|
||||||
|
Expected: 각 스킬당 최소 1개 프롬프트, 기대 동작, 확인 포인트가 담긴다.
|
||||||
|
|
||||||
|
- [ ] **Step 3: README와 테스트 문서 간 링크 연결**
|
||||||
|
Add:
|
||||||
|
```markdown
|
||||||
|
[테스트 시나리오](./docs/test-scenarios.md)
|
||||||
|
[brainstorming](./.trae/skills/brainstorming/SKILL.md)
|
||||||
|
```
|
||||||
|
Expected: README에서 주요 문서로 빠르게 이동할 수 있다.
|
||||||
|
|
||||||
|
- [ ] **Step 4: 진단 확인**
|
||||||
|
Run: Markdown diagnostics for `README.md` and `docs/test-scenarios.md`
|
||||||
|
Expected: 진단 오류 없음
|
||||||
|
|
||||||
|
### Task 4: 최종 검증과 정리
|
||||||
|
**Files:**
|
||||||
|
- Modify: 전체 문서 일관성 점검 결과에 따라 필요한 파일
|
||||||
|
- Test: 전체 신규/수정 Markdown 파일 진단 확인
|
||||||
|
|
||||||
|
- [ ] **Step 1: 문서 명칭과 링크 일관성 점검**
|
||||||
|
Check:
|
||||||
|
```text
|
||||||
|
README.md
|
||||||
|
docs/test-scenarios.md
|
||||||
|
.trae/skills/*/SKILL.md
|
||||||
|
```
|
||||||
|
Expected: 스킬 이름, 링크 경로, 설명 용어가 서로 충돌하지 않는다.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Markdown 진단 일괄 확인**
|
||||||
|
Run: diagnostics for all touched markdown files
|
||||||
|
Expected: 진단 오류 없음
|
||||||
|
|
||||||
|
- [ ] **Step 3: 결과 요약 작성**
|
||||||
|
Include:
|
||||||
|
```text
|
||||||
|
확장한 스킬 목록
|
||||||
|
추가한 문서 목록
|
||||||
|
검증 결과
|
||||||
|
```
|
||||||
|
Expected: 사용자가 바로 산출물을 훑어볼 수 있다.
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
# SQLite + FastAPI + Vue CRUD 구현 계획
|
||||||
|
> **에이전트 작업자용:** 각 작업은 체크박스(`- [ ]`)로 추적합니다.
|
||||||
|
**목표:** SQLite의 `messages` 테이블 데이터를 FastAPI CRUD API로 조작하고 Vue 화면에서 생성, 조회, 수정, 삭제할 수 있게 만든다.
|
||||||
|
**아키텍처:** 백엔드는 `sqlite3`와 FastAPI로 단순 CRUD 엔드포인트를 제공하고, 프론트엔드는 브라우저용 Vue ESM 번들로 목록형 CRUD UI를 구현한다. 구현은 기존 파일 구조를 유지하면서 최소한의 상태만 추가하고, 마지막에 API와 화면 흐름을 직접 검증한다.
|
||||||
|
**기술 스택:** Python 3, FastAPI, sqlite3, Vue 3 ESM, 정적 HTML
|
||||||
|
---
|
||||||
|
|
||||||
|
- [ ] **작업 1: DB 초기화 스크립트 유지 여부 점검**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/backend/init_db.py` (수정)
|
||||||
|
- **할 일:** `messages` 테이블 구조가 CRUD 요구사항에 충분한지 확인하고, 초기 데이터 `hello world` 1건을 유지하는 방식으로 정리한다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```python
|
||||||
|
cursor.execute(
|
||||||
|
"CREATE TABLE IF NOT EXISTS messages (id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT NOT NULL)"
|
||||||
|
)
|
||||||
|
cursor.execute("DELETE FROM messages")
|
||||||
|
cursor.execute("INSERT INTO messages (content) VALUES (?)", ("hello world",))
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 backend/init_db.py`
|
||||||
|
- **기대 결과:** `backend/app.db`가 다시 생성되거나 갱신되고, `messages` 테이블에 `hello world` 1건이 들어간다.
|
||||||
|
|
||||||
|
- [ ] **작업 2: FastAPI용 요청 모델과 DB 헬퍼 추가**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/backend/main.py` (수정)
|
||||||
|
- **할 일:** SQLite 연결 헬퍼, 메시지 직렬화 헬퍼, 입력 검증용 Pydantic 모델을 추가한다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```python
|
||||||
|
class MessagePayload(BaseModel):
|
||||||
|
content: str
|
||||||
|
|
||||||
|
@field_validator("content")
|
||||||
|
@classmethod
|
||||||
|
def validate_content(cls, value: str) -> str:
|
||||||
|
stripped_value = value.strip()
|
||||||
|
if not stripped_value:
|
||||||
|
raise ValueError("Content must not be empty")
|
||||||
|
return stripped_value
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 -m py_compile backend/main.py`
|
||||||
|
- **기대 결과:** API 함수들이 공통 검증 로직과 연결 헬퍼를 재사용할 준비가 된다.
|
||||||
|
|
||||||
|
- [ ] **작업 3: 메시지 목록 조회 API 구현**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/backend/main.py` (수정)
|
||||||
|
- **할 일:** `GET /api/messages` 엔드포인트를 추가해 전체 메시지 목록을 `id` 오름차순으로 반환한다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```python
|
||||||
|
@app.get("/api/messages")
|
||||||
|
def list_messages() -> list[dict[str, object]]:
|
||||||
|
with get_connection() as connection:
|
||||||
|
rows = connection.execute(
|
||||||
|
"SELECT id, content FROM messages ORDER BY id ASC"
|
||||||
|
).fetchall()
|
||||||
|
return [serialize_message(row) for row in rows]
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 -c "from backend.main import list_messages; print(list_messages())"`
|
||||||
|
- **기대 결과:** `hello world`가 포함된 리스트가 출력된다.
|
||||||
|
|
||||||
|
- [ ] **작업 4: 메시지 생성 API 구현**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/backend/main.py` (수정)
|
||||||
|
- **할 일:** `POST /api/messages` 엔드포인트를 추가해 메시지를 저장하고 생성된 `id`와 `content`를 반환한다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```python
|
||||||
|
@app.post("/api/messages", status_code=201)
|
||||||
|
def create_message(payload: MessagePayload) -> dict[str, object]:
|
||||||
|
with get_connection() as connection:
|
||||||
|
cursor = connection.execute(
|
||||||
|
"INSERT INTO messages (content) VALUES (?)",
|
||||||
|
(payload.content,),
|
||||||
|
)
|
||||||
|
connection.commit()
|
||||||
|
return {"id": cursor.lastrowid, "content": payload.content}
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 - <<'PY'\nfrom backend.main import create_message, MessagePayload\nprint(create_message(MessagePayload(content='created from plan')))\nPY`
|
||||||
|
- **기대 결과:** 새 `id`와 저장된 `content`가 출력된다.
|
||||||
|
|
||||||
|
- [ ] **작업 5: 메시지 수정 API 구현**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/backend/main.py` (수정)
|
||||||
|
- **할 일:** `PUT /api/messages/{id}` 엔드포인트를 추가해 해당 메시지를 수정하고, 없는 `id`는 404를 반환한다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```python
|
||||||
|
@app.put("/api/messages/{message_id}")
|
||||||
|
def update_message(message_id: int, payload: MessagePayload) -> dict[str, object]:
|
||||||
|
with get_connection() as connection:
|
||||||
|
cursor = connection.execute(
|
||||||
|
"UPDATE messages SET content = ? WHERE id = ?",
|
||||||
|
(payload.content, message_id),
|
||||||
|
)
|
||||||
|
connection.commit()
|
||||||
|
if cursor.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="Message not found")
|
||||||
|
return {"id": message_id, "content": payload.content}
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 - <<'PY'\nfrom backend.main import update_message, MessagePayload\nprint(update_message(1, MessagePayload(content='updated hello world')))\nPY`
|
||||||
|
- **기대 결과:** 수정된 메시지 객체가 출력된다.
|
||||||
|
|
||||||
|
- [ ] **작업 6: 메시지 삭제 API 구현**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/backend/main.py` (수정)
|
||||||
|
- **할 일:** `DELETE /api/messages/{id}` 엔드포인트를 추가해 해당 메시지를 삭제하고 성공 여부를 반환한다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```python
|
||||||
|
@app.delete("/api/messages/{message_id}")
|
||||||
|
def delete_message(message_id: int) -> dict[str, bool]:
|
||||||
|
with get_connection() as connection:
|
||||||
|
cursor = connection.execute(
|
||||||
|
"DELETE FROM messages WHERE id = ?",
|
||||||
|
(message_id,),
|
||||||
|
)
|
||||||
|
connection.commit()
|
||||||
|
if cursor.rowcount == 0:
|
||||||
|
raise HTTPException(status_code=404, detail="Message not found")
|
||||||
|
return {"success": True}
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 - <<'PY'\nfrom backend.main import delete_message\nprint(delete_message(1))\nPY`
|
||||||
|
- **기대 결과:** `{'success': True}`가 출력된다.
|
||||||
|
|
||||||
|
- [ ] **작업 7: Vue 상태를 목록형 CRUD 구조로 변경**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/src/main.js` (수정)
|
||||||
|
- **할 일:** 단일 `message` 상태를 `messages`, `newContent`, `editingId`, `editingContent` 상태로 교체하고 목록 조회 함수를 만든다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```javascript
|
||||||
|
const messages = ref([])
|
||||||
|
const newContent = ref('')
|
||||||
|
const editingId = ref(null)
|
||||||
|
const editingContent = ref('')
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 -m py_compile backend/main.py`
|
||||||
|
- **기대 결과:** 프론트 로직이 CRUD API 구조와 맞는 상태 이름으로 정리된다.
|
||||||
|
|
||||||
|
- [ ] **작업 8: Vue 생성/수정/삭제 액션 구현**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/src/main.js` (수정)
|
||||||
|
- **할 일:** `fetch`를 사용해 생성, 수정, 삭제 요청을 보내고 성공 후 목록을 다시 불러오거나 상태를 갱신한다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```javascript
|
||||||
|
const createMessage = async () => {
|
||||||
|
await fetch(`${API_BASE_URL}/messages`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ content: newContent.value.trim() }),
|
||||||
|
})
|
||||||
|
await loadMessages()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 backend/init_db.py`
|
||||||
|
- **기대 결과:** 프론트에서 각 버튼이 API 호출 함수와 연결된다.
|
||||||
|
|
||||||
|
- [ ] **작업 9: Vue 템플릿을 목록형 CRUD UI로 교체**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/src/main.js` (수정)
|
||||||
|
- **할 일:** 입력 폼, 목록, 인라인 편집, 빈 상태, 에러 상태를 모두 표시하는 템플릿으로 교체한다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```html
|
||||||
|
<form @submit.prevent="createMessage">
|
||||||
|
<input v-model="newContent" />
|
||||||
|
<button type="submit">추가</button>
|
||||||
|
</form>
|
||||||
|
<li v-for="message in messages" :key="message.id">
|
||||||
|
<span>{{ message.content }}</span>
|
||||||
|
</li>
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 -m http.server 5173 -d frontend`
|
||||||
|
- **기대 결과:** 브라우저에서 목록형 CRUD 화면이 렌더링된다.
|
||||||
|
|
||||||
|
- [ ] **작업 10: README 실행/검증 문구를 CRUD 기준으로 갱신**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/README.md` (수정)
|
||||||
|
- **할 일:** 단일 조회 설명을 목록형 CRUD 예제 설명으로 바꾸고, 확인 포인트를 CRUD 흐름에 맞게 수정한다.
|
||||||
|
- **최소 구현 예시:**
|
||||||
|
```markdown
|
||||||
|
- 백엔드 API: `http://127.0.0.1:8000/api/messages`
|
||||||
|
- 프론트엔드 화면: `http://127.0.0.1:5173`
|
||||||
|
- 정상 동작 시 메시지 추가, 수정, 삭제가 모두 가능하다.
|
||||||
|
```
|
||||||
|
- **검증 명령:** `python3 -m py_compile backend/init_db.py backend/main.py`
|
||||||
|
- **기대 결과:** README만 읽어도 CRUD 예제 실행과 확인 방법을 이해할 수 있다.
|
||||||
|
|
||||||
|
- [ ] **작업 11: 백엔드 문법과 API 흐름 검증**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/backend/init_db.py` (검증), `/Users/woozooni/Documents/trae_projects/skillDesk/backend/main.py` (검증)
|
||||||
|
- **할 일:** DB를 초기화하고, Python에서 API 함수를 직접 호출해 생성, 조회, 수정, 삭제 흐름이 모두 동작하는지 확인한다.
|
||||||
|
- **실행 명령:**
|
||||||
|
```bash
|
||||||
|
python3 backend/init_db.py
|
||||||
|
python3 - <<'PY'
|
||||||
|
from backend.main import MessagePayload, create_message, delete_message, list_messages, update_message
|
||||||
|
print("LIST-1", list_messages())
|
||||||
|
created = create_message(MessagePayload(content="plan create"))
|
||||||
|
print("CREATED", created)
|
||||||
|
print("LIST-2", list_messages())
|
||||||
|
updated = update_message(created["id"], MessagePayload(content="plan update"))
|
||||||
|
print("UPDATED", updated)
|
||||||
|
print("LIST-3", list_messages())
|
||||||
|
print("DELETED", delete_message(created["id"]))
|
||||||
|
print("LIST-4", list_messages())
|
||||||
|
PY
|
||||||
|
```
|
||||||
|
- **기대 결과:** 각 단계 출력에 따라 CRUD가 순서대로 성공한다.
|
||||||
|
|
||||||
|
- [ ] **작업 12: 진단 점검 및 인라인 실행 정리**
|
||||||
|
- **대상 파일:** `/Users/woozooni/Documents/trae_projects/skillDesk/backend/main.py` (진단), `/Users/woozooni/Documents/trae_projects/skillDesk/frontend/src/main.js` (진단), `/Users/woozooni/Documents/trae_projects/skillDesk/README.md` (진단)
|
||||||
|
- **할 일:** 진단 도구로 오류를 확인하고 바로 수정한 뒤, 변경 파일과 실행 방법을 사용자에게 인계한다.
|
||||||
|
- **검증 도구:** IDE 진단 확인
|
||||||
|
- **기대 결과:** 새 오류 없이 CRUD 예제를 실행할 수 있는 상태로 마무리된다.
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
# SQLite + FastAPI + Vue Hello World 구현 계획
|
||||||
|
> **에이전트 작업자용:** 현재 세션에서 인라인으로 실행한다. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
**Goal:** SQLite에 저장된 `'hello world'` 값을 FastAPI API를 통해 Vue 화면에 표시하는 최소 실행 예제를 만든다.
|
||||||
|
**Architecture:** `backend/`에는 SQLite 초기화 스크립트와 FastAPI API를 두고, `frontend/`에는 Vite 기반 Vue 앱을 둔다. Vue는 `/api/message`를 호출해 DB 값을 받아 렌더링한다.
|
||||||
|
**Tech Stack:** Python, FastAPI, sqlite3, uvicorn, Vue 3, Vite
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: 백엔드 기본 구조 만들기
|
||||||
|
**Files:**
|
||||||
|
- Create: `backend/requirements.txt`
|
||||||
|
- Create: `backend/init_db.py`
|
||||||
|
- Create: `backend/main.py`
|
||||||
|
- Test: `python3 -m py_compile backend/init_db.py backend/main.py`
|
||||||
|
|
||||||
|
- [ ] **Step 1: requirements 파일 작성**
|
||||||
|
Content:
|
||||||
|
```text
|
||||||
|
fastapi
|
||||||
|
uvicorn
|
||||||
|
```
|
||||||
|
Expected: 백엔드 설치 의존성이 명확해진다.
|
||||||
|
|
||||||
|
- [ ] **Step 2: SQLite 초기화 스크립트 작성**
|
||||||
|
Code should:
|
||||||
|
```python
|
||||||
|
import sqlite3
|
||||||
|
from pathlib import Path
|
||||||
|
```
|
||||||
|
Behavior:
|
||||||
|
- `app.db` 생성
|
||||||
|
- `messages` 테이블 생성
|
||||||
|
- 기존 데이터 삭제 후 `'hello world'` 삽입
|
||||||
|
Expected: `python3 backend/init_db.py` 실행 시 DB 파일과 샘플 데이터가 생성된다.
|
||||||
|
|
||||||
|
- [ ] **Step 3: FastAPI 앱 작성**
|
||||||
|
Code should:
|
||||||
|
```python
|
||||||
|
from fastapi import FastAPI, HTTPException
|
||||||
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
```
|
||||||
|
Behavior:
|
||||||
|
- `GET /api/message`
|
||||||
|
- SQLite에서 첫 메시지 조회
|
||||||
|
- 없으면 404 반환
|
||||||
|
Expected: 앱이 JSON으로 `{ "message": "hello world" }`를 반환할 수 있다.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Python 문법 확인**
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
python3 -m py_compile backend/init_db.py backend/main.py
|
||||||
|
```
|
||||||
|
Expected: 출력 없이 종료
|
||||||
|
|
||||||
|
### Task 2: 프론트엔드 기본 구조 만들기
|
||||||
|
**Files:**
|
||||||
|
- Create: `frontend/package.json`
|
||||||
|
- Create: `frontend/vite.config.js`
|
||||||
|
- Create: `frontend/index.html`
|
||||||
|
- Create: `frontend/src/main.js`
|
||||||
|
- Test: `npm run build`
|
||||||
|
|
||||||
|
- [ ] **Step 1: package.json 작성**
|
||||||
|
Content should include:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "vite build"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Expected: Vue/Vite 개발 및 빌드 명령이 준비된다.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Vite 프록시 설정 작성**
|
||||||
|
Behavior:
|
||||||
|
- `/api` 요청을 `http://127.0.0.1:8000`으로 프록시
|
||||||
|
Expected: 프론트엔드에서 상대 경로로 API 호출 가능
|
||||||
|
|
||||||
|
- [ ] **Step 3: index.html 작성**
|
||||||
|
Behavior:
|
||||||
|
- `#app` 마운트 포인트 제공
|
||||||
|
Expected: Vue 앱이 정상적으로 마운트 가능
|
||||||
|
|
||||||
|
- [ ] **Step 4: Vue 메인 앱 작성**
|
||||||
|
Behavior:
|
||||||
|
- 로딩 상태 표시
|
||||||
|
- `/api/message` fetch
|
||||||
|
- 성공 시 메시지 표시
|
||||||
|
- 실패 시 에러 문구 표시
|
||||||
|
Expected: 브라우저에서 `'hello world'`를 볼 수 있는 최소 화면이 구성된다.
|
||||||
|
|
||||||
|
- [ ] **Step 5: 프론트엔드 빌드 확인**
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
Expected: 빌드 성공
|
||||||
|
|
||||||
|
### Task 3: 실행 문서 정리
|
||||||
|
**Files:**
|
||||||
|
- Modify: `README.md`
|
||||||
|
- Test: 문서 진단 확인
|
||||||
|
|
||||||
|
- [ ] **Step 1: README에 예제 실행 섹션 추가**
|
||||||
|
Include:
|
||||||
|
```markdown
|
||||||
|
## SQLite + FastAPI + Vue 예제 실행
|
||||||
|
```
|
||||||
|
Details:
|
||||||
|
- Python 가상환경 생성
|
||||||
|
- 백엔드 의존성 설치
|
||||||
|
- DB 초기화
|
||||||
|
- FastAPI 실행
|
||||||
|
- 프론트엔드 설치/실행
|
||||||
|
- 접속 URL 안내
|
||||||
|
Expected: 처음 보는 사용자도 따라 실행 가능
|
||||||
|
|
||||||
|
### Task 4: 기본 동작 검증
|
||||||
|
**Files:**
|
||||||
|
- Modify: 필요 시 문제 있는 파일
|
||||||
|
- Test: DB 초기화, Python 문법, 프론트엔드 빌드
|
||||||
|
|
||||||
|
- [ ] **Step 1: DB 초기화 실행**
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
python3 backend/init_db.py
|
||||||
|
```
|
||||||
|
Expected: `backend/app.db` 생성
|
||||||
|
|
||||||
|
- [ ] **Step 2: FastAPI 의존성 설치 후 앱 import 확인**
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
python3 -m venv .venv
|
||||||
|
.venv/bin/pip install -r backend/requirements.txt
|
||||||
|
.venv/bin/python -c "from backend.main import app; print(app.title)"
|
||||||
|
```
|
||||||
|
Expected: 앱 타이틀 출력
|
||||||
|
|
||||||
|
- [ ] **Step 3: 프론트엔드 설치 및 빌드**
|
||||||
|
Run:
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
Expected: 빌드 성공
|
||||||
|
|
||||||
|
- [ ] **Step 4: 결과 요약 정리**
|
||||||
|
Include:
|
||||||
|
```text
|
||||||
|
생성 파일
|
||||||
|
실행 방법
|
||||||
|
검증 결과
|
||||||
|
```
|
||||||
|
Expected: 사용자가 바로 실행을 이어갈 수 있다.
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
# SQLite + FastAPI + Vue 전역 설정 분리 설계
|
||||||
|
|
||||||
|
## 목표
|
||||||
|
|
||||||
|
프론트엔드가 백엔드 API 주소를 코드 내부 상수에 직접 고정하지 않고, `index.html`의 전역 설정 객체를 통해 읽도록 바꿔서 포트 변경 시 자바스크립트 로직을 수정하지 않아도 되게 만든다.
|
||||||
|
|
||||||
|
## 배경
|
||||||
|
|
||||||
|
현재 예제는 `frontend/src/main.js`에 `API_HOST`, `API_PORT`, `API_BASE_URL` 상수가 직접 선언되어 있다.
|
||||||
|
|
||||||
|
이 구조는 이전보다 단순하지만, 포트를 바꿀 때 여전히 `main.js`를 수정해야 한다. 예제 사용성 관점에서는 HTML 설정만 바꾸고 Vue 로직은 그대로 두는 쪽이 더 이해하기 쉽다.
|
||||||
|
|
||||||
|
## 범위
|
||||||
|
|
||||||
|
이번 작업은 다음을 포함한다.
|
||||||
|
|
||||||
|
- `frontend/index.html`에 전역 설정 객체 추가
|
||||||
|
- `frontend/src/main.js`가 전역 설정 객체를 읽도록 변경
|
||||||
|
- 설정이 없을 때 기본값 `127.0.0.1:8000` 사용
|
||||||
|
- README에 설정 위치를 `index.html` 기준으로 설명
|
||||||
|
|
||||||
|
이번 작업은 다음을 포함하지 않는다.
|
||||||
|
|
||||||
|
- 별도 `config.js` 파일 추가
|
||||||
|
- `.env` 기반 설정 시스템 도입
|
||||||
|
- URL 쿼리 파라미터 기반 포트 주입
|
||||||
|
- 백엔드 설정 시스템 변경
|
||||||
|
|
||||||
|
## 구조
|
||||||
|
|
||||||
|
### 1. HTML 전역 설정
|
||||||
|
|
||||||
|
`frontend/index.html`에 아래 형태의 전역 객체를 둔다.
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
window.APP_CONFIG = {
|
||||||
|
apiHost: 'http://127.0.0.1',
|
||||||
|
apiPort: '8000',
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
이 값은 프론트엔드 애플리케이션이 시작되기 전에 정의되어야 한다.
|
||||||
|
|
||||||
|
### 2. 프론트엔드 설정 읽기
|
||||||
|
|
||||||
|
`frontend/src/main.js`는 `window.APP_CONFIG`를 읽어 아래 값을 만든다.
|
||||||
|
|
||||||
|
- `apiHost`
|
||||||
|
- `apiPort`
|
||||||
|
- `API_BASE_URL`
|
||||||
|
|
||||||
|
설정이 없거나 일부 속성이 빠져 있으면 기본값을 사용한다.
|
||||||
|
|
||||||
|
예시 구조:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const appConfig = window.APP_CONFIG ?? {}
|
||||||
|
const apiHost = appConfig.apiHost || 'http://127.0.0.1'
|
||||||
|
const apiPort = appConfig.apiPort || '8000'
|
||||||
|
const API_BASE_URL = `${apiHost}:${apiPort}/api`
|
||||||
|
```
|
||||||
|
|
||||||
|
## 데이터 흐름
|
||||||
|
|
||||||
|
1. 브라우저가 `index.html`을 먼저 읽는다.
|
||||||
|
2. `window.APP_CONFIG`가 전역으로 설정된다.
|
||||||
|
3. `main.js`가 이 값을 읽어 실제 API 주소를 조합한다.
|
||||||
|
4. Vue 애플리케이션은 조합된 API 주소로 CRUD 요청을 보낸다.
|
||||||
|
|
||||||
|
## 기본값 처리
|
||||||
|
|
||||||
|
전역 설정 객체가 없더라도 예제가 깨지지 않도록 기본값을 유지한다.
|
||||||
|
|
||||||
|
- 기본 호스트: `http://127.0.0.1`
|
||||||
|
- 기본 포트: `8000`
|
||||||
|
|
||||||
|
즉, 설정 객체는 선택 사항이지만 존재하면 우선 적용된다.
|
||||||
|
|
||||||
|
## README 문서화 방식
|
||||||
|
|
||||||
|
README에는 아래 내용을 포함한다.
|
||||||
|
|
||||||
|
- 기본 실행 예시
|
||||||
|
- `index.html`의 `window.APP_CONFIG` 위치 설명
|
||||||
|
- 포트 충돌 시 `apiPort`를 어떻게 바꾸는지 설명
|
||||||
|
- 프론트 정적 서버 포트는 여전히 실행 명령에서 따로 바꾼다는 점
|
||||||
|
|
||||||
|
## 오류 처리
|
||||||
|
|
||||||
|
- 전역 설정이 없으면 기본값으로 동작한다.
|
||||||
|
- 전역 설정의 값이 잘못되어 연결에 실패하면 기존 fetch 에러 문구를 보여준다.
|
||||||
|
- README에 백엔드 포트와 `apiPort`가 같아야 한다는 점을 분명히 적는다.
|
||||||
|
|
||||||
|
## 검증 기준
|
||||||
|
|
||||||
|
다음 조건을 만족하면 성공이다.
|
||||||
|
|
||||||
|
- `main.js`를 수정하지 않고 `index.html`의 `apiPort` 값만 바꿔 다른 백엔드 포트로 연결할 수 있다.
|
||||||
|
- 전역 설정이 없어도 기본값으로 기존 CRUD 기능이 유지된다.
|
||||||
|
- README만 읽어도 설정 위치와 수정 방법을 이해할 수 있다.
|
||||||
|
|
||||||
|
## 구현 원칙
|
||||||
|
|
||||||
|
- 기존 CRUD 기능은 그대로 유지한다.
|
||||||
|
- 설정 시스템은 예제 규모에 맞게 가장 단순한 수준으로 둔다.
|
||||||
|
- 포트 변경 지점은 한 곳으로 모은다.
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
# SQLite + FastAPI + Vue 포트 설정 분리 설계
|
||||||
|
|
||||||
|
## 목표
|
||||||
|
|
||||||
|
현재 CRUD 예제의 백엔드와 프론트 실행 포트를 문서와 코드에서 더 분리해, 로컬 포트 충돌이 있어도 최소 수정으로 다시 실행할 수 있게 만든다.
|
||||||
|
|
||||||
|
## 배경
|
||||||
|
|
||||||
|
기존 예제는 아래 주소를 기본값으로 사용한다.
|
||||||
|
|
||||||
|
- 백엔드 API: `http://127.0.0.1:8000`
|
||||||
|
- 프론트 정적 서버: `http://127.0.0.1:5173`
|
||||||
|
|
||||||
|
실행 중 실제로 `8000`, `5173` 포트가 이미 사용 중이어서 새 서버를 같은 포트에 띄우지 못했다. 따라서 포트를 바꾸는 방법이 코드와 문서에 더 명확히 드러나야 한다.
|
||||||
|
|
||||||
|
## 범위
|
||||||
|
|
||||||
|
이번 작업은 다음을 포함한다.
|
||||||
|
|
||||||
|
- 프론트엔드의 API 주소 상수 구조를 분리
|
||||||
|
- 백엔드 포트 변경 방법을 README에 명시
|
||||||
|
- 프론트 정적 서버 포트 변경 방법을 README에 명시
|
||||||
|
- 기본 포트와 대체 포트 예시를 함께 제공
|
||||||
|
|
||||||
|
이번 작업은 다음을 포함하지 않는다.
|
||||||
|
|
||||||
|
- 별도 설정 파일 도입
|
||||||
|
- `.env` 기반 환경변수 시스템 추가
|
||||||
|
- 프론트 번들러 도입
|
||||||
|
- 런타임 자동 포트 탐지
|
||||||
|
|
||||||
|
## 접근 방식
|
||||||
|
|
||||||
|
### 1. 프론트 상수 분리
|
||||||
|
|
||||||
|
`frontend/src/main.js`의 API 주소를 하나의 긴 문자열 대신 아래 상수로 분리한다.
|
||||||
|
|
||||||
|
- `API_HOST`
|
||||||
|
- `API_PORT`
|
||||||
|
- `API_BASE_URL`
|
||||||
|
|
||||||
|
예시 구조:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const API_HOST = 'http://127.0.0.1'
|
||||||
|
const API_PORT = '8000'
|
||||||
|
const API_BASE_URL = `${API_HOST}:${API_PORT}/api`
|
||||||
|
```
|
||||||
|
|
||||||
|
이렇게 하면 백엔드 포트를 `8001` 같은 값으로 바꿀 때 한 줄만 수정하면 된다.
|
||||||
|
|
||||||
|
### 2. 백엔드 실행 포트는 명령에서 제어
|
||||||
|
|
||||||
|
백엔드는 FastAPI 코드 내부에서 포트를 고정하지 않는다. 실행 포트는 `uvicorn` 명령의 `--port` 옵션으로 제어한다.
|
||||||
|
|
||||||
|
기본 실행:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn backend.main:app --host 127.0.0.1 --port 8000
|
||||||
|
```
|
||||||
|
|
||||||
|
대체 실행:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
uvicorn backend.main:app --host 127.0.0.1 --port 8001
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 프론트 정적 서버 포트는 명령에서 제어
|
||||||
|
|
||||||
|
프론트 정적 서버도 코드가 아니라 실행 명령에서 포트를 선택한다.
|
||||||
|
|
||||||
|
기본 실행:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m http.server 5173 -d frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
대체 실행:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -m http.server 5174 -d frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
## README 문서화 방식
|
||||||
|
|
||||||
|
README에는 아래 내용을 포함한다.
|
||||||
|
|
||||||
|
- 기본 포트 실행 예시
|
||||||
|
- 포트 충돌 시 대체 실행 예시
|
||||||
|
- 프론트에서 `API_PORT` 값을 어디서 바꾸는지 설명
|
||||||
|
- 백엔드와 프론트 포트는 서로 독립적으로 바꿀 수 있다는 점
|
||||||
|
|
||||||
|
## 데이터 흐름
|
||||||
|
|
||||||
|
1. 사용자가 백엔드를 원하는 포트로 실행한다.
|
||||||
|
2. 프론트는 `API_HOST`와 `API_PORT`를 조합해 API 주소를 만든다.
|
||||||
|
3. 프론트 정적 서버는 별도 포트에서 독립적으로 제공된다.
|
||||||
|
4. 포트 충돌 시 백엔드와 프론트 포트를 각각 따로 변경할 수 있다.
|
||||||
|
|
||||||
|
## 오류 처리
|
||||||
|
|
||||||
|
- 프론트가 잘못된 백엔드 포트를 가리키면 기존처럼 fetch 에러 문구를 보여준다.
|
||||||
|
- README에 포트 불일치 점검 순서를 넣어 사용자가 원인을 쉽게 찾을 수 있게 한다.
|
||||||
|
|
||||||
|
## 검증 기준
|
||||||
|
|
||||||
|
다음 조건을 만족하면 성공이다.
|
||||||
|
|
||||||
|
- 프론트 코드에서 백엔드 포트를 한 곳만 바꿔도 API 주소가 함께 변경된다.
|
||||||
|
- README만 읽어도 기본 포트와 대체 포트 실행 방법을 이해할 수 있다.
|
||||||
|
- 기존 CRUD 기능은 변경 없이 유지된다.
|
||||||
|
- 포트 충돌 상황에서 사용자가 다음 행동을 바로 선택할 수 있다.
|
||||||
|
|
||||||
|
## 구현 원칙
|
||||||
|
|
||||||
|
- 기존 CRUD 구조를 유지한다.
|
||||||
|
- 설정 분리를 위해 새 시스템을 과하게 도입하지 않는다.
|
||||||
|
- 실행 문서는 실제 충돌 상황을 해결하는 데 바로 도움이 되도록 쓴다.
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
# skillDesk 문서 확장 설계
|
||||||
|
|
||||||
|
## 목표
|
||||||
|
|
||||||
|
현재 프로젝트의 10개 스킬 문서를 원문에 더 가깝게 확장하고, 사용자가 바로 이해하고 검증할 수 있도록 루트 README와 TRAE 실사용 테스트 시나리오 문서를 추가한다.
|
||||||
|
|
||||||
|
## 범위
|
||||||
|
|
||||||
|
이번 작업은 다음 세 가지를 포함한다.
|
||||||
|
|
||||||
|
1. `.trae/skills/*/SKILL.md` 10개 문서 확장
|
||||||
|
2. 루트 `README.md` 신규 작성
|
||||||
|
3. `docs/test-scenarios.md` 신규 작성
|
||||||
|
|
||||||
|
이번 작업은 다음을 포함하지 않는다.
|
||||||
|
|
||||||
|
- 실제 스킬 실행 엔진 개발
|
||||||
|
- 자동 테스트 실행 스크립트 작성
|
||||||
|
- 외부 설치 자동화
|
||||||
|
- Git 커밋 자동 생성
|
||||||
|
|
||||||
|
## 현재 상태
|
||||||
|
|
||||||
|
- 루트에는 [skillText.md](file:///Users/woozooni/Documents/trae_projects/skillDesk/skillText.md) 가 있고, 10개 스킬 목록의 한국어 번역이 있다.
|
||||||
|
- `.trae/skills/` 아래에 10개 스킬 디렉터리와 `SKILL.md`가 생성되어 있다.
|
||||||
|
- 현재 스킬 문서는 핵심 요약형에 가깝고, 원문 세부 규칙과 절차 복원 수준은 낮다.
|
||||||
|
- 루트 `README.md`는 아직 없다.
|
||||||
|
- 테스트 시나리오 문서도 아직 없다.
|
||||||
|
|
||||||
|
## 설계 방향
|
||||||
|
|
||||||
|
### 1. 스킬 문서 확장
|
||||||
|
|
||||||
|
각 `SKILL.md`는 현재의 짧은 요약형에서 벗어나, 원문에 더 가까운 아래 구조를 갖는다.
|
||||||
|
|
||||||
|
- 언제 사용하는지
|
||||||
|
- 반드시 지켜야 하는 규칙
|
||||||
|
- 단계별 작업 흐름
|
||||||
|
- 권장/비권장 행동
|
||||||
|
- 결과물 기대치
|
||||||
|
- 필요 시 예시 프롬프트 또는 운영 팁
|
||||||
|
|
||||||
|
모든 스킬을 원문 그대로 기계 번역하는 방식은 피한다. 대신 다음 원칙을 따른다.
|
||||||
|
|
||||||
|
- 원문 핵심 규칙, 체크리스트, 금지 사항은 최대한 보존한다.
|
||||||
|
- 한국어 사용성과 현재 프로젝트 맥락을 고려해 불필요한 외부 의존 서술은 줄인다.
|
||||||
|
- 지나치게 긴 원문은 구조화해서 읽기 쉽게 재편집한다.
|
||||||
|
- frontmatter `description`은 짧고 명확하게 유지한다.
|
||||||
|
|
||||||
|
### 2. README 구조
|
||||||
|
|
||||||
|
루트 `README.md`는 프로젝트 안내문 역할을 하며 아래 내용을 포함한다.
|
||||||
|
|
||||||
|
- 프로젝트 소개
|
||||||
|
- 포함된 10개 스킬 목록
|
||||||
|
- 디렉터리 구조 설명
|
||||||
|
- TRAE에서 사용하는 방법
|
||||||
|
- 추천 사용 흐름
|
||||||
|
- 문서 확장 기준 설명
|
||||||
|
- 테스트 시나리오 문서 위치 안내
|
||||||
|
|
||||||
|
README는 "이 저장소가 무엇인지, 어떻게 써야 하는지"를 처음 보는 사람도 바로 이해할 수 있게 작성한다.
|
||||||
|
|
||||||
|
### 3. 테스트 시나리오 문서
|
||||||
|
|
||||||
|
`docs/test-scenarios.md`는 실제 TRAE에서 복붙 가능한 검증 시나리오 모음으로 작성한다.
|
||||||
|
|
||||||
|
각 시나리오는 아래 형식을 따른다.
|
||||||
|
|
||||||
|
- 시나리오 목적
|
||||||
|
- 사전 조건
|
||||||
|
- 입력 프롬프트
|
||||||
|
- 기대 동작
|
||||||
|
- 확인 포인트
|
||||||
|
|
||||||
|
10개 스킬 각각 최소 1개 이상의 대표 시나리오를 둔다. 필요하면 공통 시나리오도 추가한다.
|
||||||
|
|
||||||
|
## 정보 구조
|
||||||
|
|
||||||
|
### 스킬 문서 일관성 규칙
|
||||||
|
|
||||||
|
모든 `SKILL.md`는 아래 섹션 순서를 가능한 한 맞춘다.
|
||||||
|
|
||||||
|
1. 개요
|
||||||
|
2. 언제 사용할지
|
||||||
|
3. 핵심 규칙
|
||||||
|
4. 단계 또는 워크플로
|
||||||
|
5. 금지 사항 또는 안티 패턴
|
||||||
|
6. 기대 결과
|
||||||
|
7. 예시 또는 운영 팁
|
||||||
|
|
||||||
|
스킬마다 성격이 다르므로 완전히 동일한 틀을 강제하지는 않지만, 사용자 관점에서 탐색성이 유지되도록 한다.
|
||||||
|
|
||||||
|
### README와 테스트 문서 연결
|
||||||
|
|
||||||
|
- README에서 테스트 시나리오 문서를 직접 링크한다.
|
||||||
|
- README에서 스킬별 문서 위치도 빠르게 찾을 수 있게 정리한다.
|
||||||
|
- 테스트 문서에서는 각 스킬 파일명을 명시해 사용자가 대응 관계를 이해할 수 있게 한다.
|
||||||
|
|
||||||
|
## 구현 단위
|
||||||
|
|
||||||
|
### 단위 A: 기존 스킬 문서 확장
|
||||||
|
|
||||||
|
대상 파일:
|
||||||
|
|
||||||
|
- `.trae/skills/brainstorming/SKILL.md`
|
||||||
|
- `.trae/skills/frontend-design/SKILL.md`
|
||||||
|
- `.trae/skills/ui-ux-pro-max/SKILL.md`
|
||||||
|
- `.trae/skills/systematic-debugging/SKILL.md`
|
||||||
|
- `.trae/skills/writing-plans/SKILL.md`
|
||||||
|
- `.trae/skills/find-skills/SKILL.md`
|
||||||
|
- `.trae/skills/using-superpowers/SKILL.md`
|
||||||
|
- `.trae/skills/karpathy-guidelines/SKILL.md`
|
||||||
|
- `.trae/skills/webapp-testing/SKILL.md`
|
||||||
|
- `.trae/skills/agent-browser/SKILL.md`
|
||||||
|
|
||||||
|
접근 방식:
|
||||||
|
|
||||||
|
- 이미 있는 한국어 문서를 기반으로 구조를 넓힌다.
|
||||||
|
- 원문에서 확인한 사용 시점, 워크플로, 금지 사항을 최대한 반영한다.
|
||||||
|
- 서로 중복되는 규칙은 표현은 달리하되 의미를 맞춘다.
|
||||||
|
|
||||||
|
### 단위 B: README 작성
|
||||||
|
|
||||||
|
대상 파일:
|
||||||
|
|
||||||
|
- `README.md`
|
||||||
|
|
||||||
|
접근 방식:
|
||||||
|
|
||||||
|
- 저장소 소개에서 시작해 사용 흐름으로 내려가는 구조를 사용한다.
|
||||||
|
- 표 또는 목록으로 10개 스킬을 빠르게 훑을 수 있게 한다.
|
||||||
|
- 문서 탐색 링크를 충분히 제공한다.
|
||||||
|
|
||||||
|
### 단위 C: 테스트 시나리오 작성
|
||||||
|
|
||||||
|
대상 파일:
|
||||||
|
|
||||||
|
- `docs/test-scenarios.md`
|
||||||
|
|
||||||
|
접근 방식:
|
||||||
|
|
||||||
|
- 각 스킬당 1개 이상 총 10개 이상의 대표 프롬프트를 작성한다.
|
||||||
|
- 실제 TRAE 사용자가 바로 실행할 수 있는 형태로 구체화한다.
|
||||||
|
- 기대 결과를 너무 추상적으로 쓰지 않고, 어떤 종류의 응답이 나와야 하는지 명시한다.
|
||||||
|
|
||||||
|
## 품질 기준
|
||||||
|
|
||||||
|
완료 판단 기준은 다음과 같다.
|
||||||
|
|
||||||
|
- 10개 스킬 문서가 현재보다 명확히 더 상세해진다.
|
||||||
|
- README만 읽어도 프로젝트 구조와 사용법을 이해할 수 있다.
|
||||||
|
- 테스트 시나리오 문서만으로 실제 검증을 시작할 수 있다.
|
||||||
|
- 문서 간 링크와 역할 분담이 자연스럽다.
|
||||||
|
- Markdown 진단 오류가 없다.
|
||||||
|
|
||||||
|
## 리스크와 대응
|
||||||
|
|
||||||
|
### 리스크 1: 문서가 과도하게 길어짐
|
||||||
|
|
||||||
|
대응:
|
||||||
|
|
||||||
|
- 핵심 규칙은 유지하되, 반복 표현은 줄인다.
|
||||||
|
- 표와 목록을 적극 사용한다.
|
||||||
|
|
||||||
|
### 리스크 2: 원문 충실도와 한국어 가독성 충돌
|
||||||
|
|
||||||
|
대응:
|
||||||
|
|
||||||
|
- 규칙과 단계는 원문에 가깝게 유지한다.
|
||||||
|
- 설명 문장은 한국어 사용성을 우선해 재구성한다.
|
||||||
|
|
||||||
|
### 리스크 3: 테스트 시나리오가 추상적이 됨
|
||||||
|
|
||||||
|
대응:
|
||||||
|
|
||||||
|
- 실제 입력 프롬프트를 그대로 넣는다.
|
||||||
|
- 기대 응답의 구조와 체크 포인트를 함께 적는다.
|
||||||
|
|
||||||
|
## 검증 계획
|
||||||
|
|
||||||
|
- 수정 후 각 Markdown 파일에 대해 진단을 확인한다.
|
||||||
|
- README, 테스트 문서, 스킬 문서 간 링크와 명칭 일관성을 점검한다.
|
||||||
|
- 시나리오가 실제 스킬 설명과 어긋나지 않는지 교차 검토한다.
|
||||||
|
|
||||||
|
## 최종 산출물
|
||||||
|
|
||||||
|
- 확장된 10개 `SKILL.md`
|
||||||
|
- 루트 `README.md`
|
||||||
|
- `docs/test-scenarios.md`
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
# SQLite + FastAPI + Vue CRUD 예제 설계
|
||||||
|
|
||||||
|
## 목표
|
||||||
|
|
||||||
|
기존의 단일 `'hello world'` 조회 예제를 확장해, SQLite에 저장된 메시지를 Vue 화면에서 생성(Create), 조회(Read), 수정(Update), 삭제(Delete)할 수 있는 목록형 CRUD 예제로 만든다.
|
||||||
|
|
||||||
|
## 범위
|
||||||
|
|
||||||
|
이번 작업은 다음을 포함한다.
|
||||||
|
|
||||||
|
- SQLite `messages` 테이블 유지
|
||||||
|
- 메시지 목록 조회 API 추가
|
||||||
|
- 메시지 생성 API 추가
|
||||||
|
- 메시지 수정 API 추가
|
||||||
|
- 메시지 삭제 API 추가
|
||||||
|
- Vue 화면에서 목록형 CRUD UI 구현
|
||||||
|
- 초기 데이터로 `hello world` 1건 유지
|
||||||
|
- 실행 문서 업데이트
|
||||||
|
|
||||||
|
이번 작업은 다음을 포함하지 않는다.
|
||||||
|
|
||||||
|
- 인증/권한
|
||||||
|
- 검색/정렬/페이지네이션
|
||||||
|
- 다중 테이블 관계
|
||||||
|
- 복잡한 폼 검증
|
||||||
|
- 배포 설정
|
||||||
|
|
||||||
|
## 구조
|
||||||
|
|
||||||
|
기존 분리형 구조를 그대로 유지한다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
skillDesk/
|
||||||
|
├── backend/
|
||||||
|
│ ├── init_db.py
|
||||||
|
│ ├── main.py
|
||||||
|
│ ├── requirements.txt
|
||||||
|
│ └── app.db
|
||||||
|
├── frontend/
|
||||||
|
│ ├── index.html
|
||||||
|
│ └── src/
|
||||||
|
│ └── main.js
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 데이터 모델
|
||||||
|
|
||||||
|
테이블명: `messages`
|
||||||
|
|
||||||
|
컬럼:
|
||||||
|
|
||||||
|
- `id` INTEGER PRIMARY KEY AUTOINCREMENT
|
||||||
|
- `content` TEXT NOT NULL
|
||||||
|
|
||||||
|
메시지 단위는 매우 단순하게 유지한다. 별도 제목, 작성일, 상태 컬럼은 추가하지 않는다.
|
||||||
|
|
||||||
|
## API 설계
|
||||||
|
|
||||||
|
### 1. 목록 조회
|
||||||
|
|
||||||
|
`GET /api/messages`
|
||||||
|
|
||||||
|
응답 예시:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{ "id": 1, "content": "hello world" }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 생성
|
||||||
|
|
||||||
|
`POST /api/messages`
|
||||||
|
|
||||||
|
요청 예시:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"content": "new message"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
응답 예시:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"content": "new message"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 수정
|
||||||
|
|
||||||
|
`PUT /api/messages/{id}`
|
||||||
|
|
||||||
|
요청 예시:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"content": "updated message"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
응답 예시:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"content": "updated message"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 삭제
|
||||||
|
|
||||||
|
`DELETE /api/messages/{id}`
|
||||||
|
|
||||||
|
응답 예시:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 백엔드 설계
|
||||||
|
|
||||||
|
- FastAPI는 기존 CORS 설정을 유지한다.
|
||||||
|
- sqlite3 표준 라이브러리를 사용한다.
|
||||||
|
- 입력 모델은 최소한으로 `content` 문자열만 받는다.
|
||||||
|
- 빈 문자열은 거부한다.
|
||||||
|
- 존재하지 않는 id에 대한 수정/삭제는 404로 반환한다.
|
||||||
|
|
||||||
|
## 프론트엔드 설계
|
||||||
|
|
||||||
|
### 화면 구성
|
||||||
|
|
||||||
|
- 상단 제목과 설명
|
||||||
|
- 새 메시지 입력 폼
|
||||||
|
- 메시지 목록 영역
|
||||||
|
- 각 항목의 수정 / 삭제 버튼
|
||||||
|
- 수정 모드일 때 인라인 입력창과 저장 / 취소 버튼
|
||||||
|
- 로딩 / 에러 / 빈 목록 상태 표시
|
||||||
|
|
||||||
|
### 사용자 흐름
|
||||||
|
|
||||||
|
1. 페이지 진입 시 목록을 불러온다.
|
||||||
|
2. 입력창에 값을 넣고 생성 버튼을 누르면 메시지가 추가된다.
|
||||||
|
3. 각 항목의 수정 버튼을 누르면 해당 줄이 편집 모드로 바뀐다.
|
||||||
|
4. 저장 시 수정 API를 호출하고 목록을 갱신한다.
|
||||||
|
5. 삭제 시 삭제 API를 호출하고 목록을 갱신한다.
|
||||||
|
|
||||||
|
### 상태
|
||||||
|
|
||||||
|
다음 상태를 최소로 둔다.
|
||||||
|
|
||||||
|
- 전체 목록
|
||||||
|
- 새 메시지 입력값
|
||||||
|
- 현재 편집 중인 메시지 id
|
||||||
|
- 편집 중 입력값
|
||||||
|
- 로딩 상태
|
||||||
|
- 에러 상태
|
||||||
|
|
||||||
|
## 데이터 흐름
|
||||||
|
|
||||||
|
1. `init_db.py`가 DB를 만들고 `hello world`를 기본 데이터로 넣는다.
|
||||||
|
2. Vue는 페이지 로드 시 `GET /api/messages`를 호출한다.
|
||||||
|
3. 사용자가 생성/수정/삭제를 수행하면 해당 API를 호출한다.
|
||||||
|
4. 요청 성공 후 목록을 다시 불러오거나 로컬 상태를 갱신한다.
|
||||||
|
|
||||||
|
## 오류 처리
|
||||||
|
|
||||||
|
- 빈 메시지 생성/수정은 프론트엔드와 백엔드 모두에서 막는다.
|
||||||
|
- 없는 메시지 수정/삭제는 404로 처리한다.
|
||||||
|
- API 실패 시 화면에 간단한 오류 문구를 표시한다.
|
||||||
|
|
||||||
|
## 검증 기준
|
||||||
|
|
||||||
|
다음 조건을 만족하면 성공이다.
|
||||||
|
|
||||||
|
- 초기 화면에 `hello world`가 포함된 목록이 표시된다.
|
||||||
|
- 새 메시지를 추가할 수 있다.
|
||||||
|
- 기존 메시지를 수정할 수 있다.
|
||||||
|
- 기존 메시지를 삭제할 수 있다.
|
||||||
|
- 삭제 후 목록이 즉시 반영된다.
|
||||||
|
- README 실행 방법이 최신 흐름에 맞게 정리된다.
|
||||||
|
|
||||||
|
## 구현 원칙
|
||||||
|
|
||||||
|
- 기존 예제 구조를 최대한 유지한다.
|
||||||
|
- 불필요한 라이브러리는 추가하지 않는다.
|
||||||
|
- CRUD 흐름이 한눈에 보이는 최소 UI를 만든다.
|
||||||
|
- 과도한 추상화보다 이해하기 쉬운 코드를 우선한다.
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
# SQLite + FastAPI + Vue Hello World 예제 설계
|
||||||
|
|
||||||
|
## 목표
|
||||||
|
|
||||||
|
SQLite 데이터베이스에 저장된 문자열 `'hello world'`를 Python FastAPI 백엔드가 읽고, Vue 프론트엔드가 API를 호출해 화면에 표시하는 최소 동작 예제를 만든다.
|
||||||
|
|
||||||
|
## 범위
|
||||||
|
|
||||||
|
이번 예제는 다음 범위를 포함한다.
|
||||||
|
|
||||||
|
- SQLite DB 파일 생성
|
||||||
|
- 메시지 테이블 생성과 샘플 데이터 삽입
|
||||||
|
- FastAPI에서 SQLite 데이터를 읽는 API 구현
|
||||||
|
- Vue에서 API를 호출해 화면에 문자열 출력
|
||||||
|
- 실행 방법 문서화
|
||||||
|
|
||||||
|
이번 예제는 다음 범위를 포함하지 않는다.
|
||||||
|
|
||||||
|
- 사용자 입력 폼
|
||||||
|
- CRUD 전체 기능
|
||||||
|
- 인증/권한
|
||||||
|
- 상태관리 라이브러리
|
||||||
|
- 배포 구성
|
||||||
|
|
||||||
|
## 구조
|
||||||
|
|
||||||
|
예제는 프론트엔드와 백엔드를 분리한 최소 구조로 만든다.
|
||||||
|
|
||||||
|
```text
|
||||||
|
skillDesk/
|
||||||
|
├── backend/
|
||||||
|
│ ├── main.py
|
||||||
|
│ ├── init_db.py
|
||||||
|
│ ├── requirements.txt
|
||||||
|
│ └── app.db
|
||||||
|
├── frontend/
|
||||||
|
│ ├── package.json
|
||||||
|
│ ├── vite.config.js
|
||||||
|
│ └── src/
|
||||||
|
│ ├── App.vue
|
||||||
|
│ └── main.js
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## 데이터 흐름
|
||||||
|
|
||||||
|
1. `backend/init_db.py`가 SQLite 데이터베이스 파일을 만들고 `messages` 테이블을 생성한다.
|
||||||
|
2. 초기 데이터로 `'hello world'` 한 건을 삽입한다.
|
||||||
|
3. `backend/main.py`의 `GET /api/message` 엔드포인트가 SQLite에서 첫 메시지를 읽는다.
|
||||||
|
4. Vue 앱이 페이지 로드 시 `/api/message`를 호출한다.
|
||||||
|
5. 응답 JSON의 메시지를 화면에 렌더링한다.
|
||||||
|
|
||||||
|
## 백엔드 설계
|
||||||
|
|
||||||
|
### 기술 선택
|
||||||
|
|
||||||
|
- Python
|
||||||
|
- FastAPI
|
||||||
|
- sqlite3 표준 라이브러리
|
||||||
|
- uvicorn
|
||||||
|
|
||||||
|
### 엔드포인트
|
||||||
|
|
||||||
|
`GET /api/message`
|
||||||
|
|
||||||
|
응답 예시:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "hello world"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### DB 스키마
|
||||||
|
|
||||||
|
테이블명: `messages`
|
||||||
|
|
||||||
|
컬럼:
|
||||||
|
|
||||||
|
- `id` INTEGER PRIMARY KEY AUTOINCREMENT
|
||||||
|
- `content` TEXT NOT NULL
|
||||||
|
|
||||||
|
### CORS
|
||||||
|
|
||||||
|
개발 중 Vue dev server에서 FastAPI API를 호출할 수 있도록 최소 CORS 설정을 추가한다.
|
||||||
|
|
||||||
|
## 프론트엔드 설계
|
||||||
|
|
||||||
|
### 기술 선택
|
||||||
|
|
||||||
|
- Vue 3
|
||||||
|
- Vite
|
||||||
|
|
||||||
|
### 동작
|
||||||
|
|
||||||
|
- 앱이 마운트되면 FastAPI API를 호출한다.
|
||||||
|
- 로딩 중에는 간단한 로딩 문구를 표시한다.
|
||||||
|
- 성공하면 DB에서 받은 `'hello world'`를 표시한다.
|
||||||
|
- 실패하면 간단한 에러 문구를 표시한다.
|
||||||
|
|
||||||
|
### 화면 구성
|
||||||
|
|
||||||
|
예제는 최소 화면만 구성한다.
|
||||||
|
|
||||||
|
- 제목
|
||||||
|
- API에서 받은 메시지 출력 영역
|
||||||
|
- 로딩/에러 상태 문구
|
||||||
|
|
||||||
|
## 실행 흐름
|
||||||
|
|
||||||
|
1. Python 가상환경 생성 및 의존성 설치
|
||||||
|
2. SQLite 초기화 스크립트 실행
|
||||||
|
3. FastAPI 서버 실행
|
||||||
|
4. Vue 의존성 설치
|
||||||
|
5. Vue 개발 서버 실행
|
||||||
|
6. 브라우저에서 메시지 표시 확인
|
||||||
|
|
||||||
|
## 검증 기준
|
||||||
|
|
||||||
|
다음 조건을 만족하면 예제가 성공이다.
|
||||||
|
|
||||||
|
- SQLite DB 파일이 생성된다.
|
||||||
|
- `messages` 테이블에 `'hello world'`가 저장된다.
|
||||||
|
- `GET /api/message` 호출 시 JSON 응답이 반환된다.
|
||||||
|
- Vue 화면에서 `'hello world'`가 보인다.
|
||||||
|
- 백엔드와 프론트엔드 실행 방법이 README에 정리된다.
|
||||||
|
|
||||||
|
## 오류 처리
|
||||||
|
|
||||||
|
최소 예제이므로 복잡한 예외 처리 대신 아래 수준만 포함한다.
|
||||||
|
|
||||||
|
- DB에 메시지가 없으면 기본 에러 응답 반환
|
||||||
|
- 프론트엔드 fetch 실패 시 에러 문구 표시
|
||||||
|
|
||||||
|
## 구현 원칙
|
||||||
|
|
||||||
|
- 예제는 최대한 짧고 이해하기 쉬워야 한다.
|
||||||
|
- 구조는 실제 앱 구조와 유사하게 분리한다.
|
||||||
|
- 과도한 추상화는 하지 않는다.
|
||||||
|
- 의존성은 최소화한다.
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
# TRAE 테스트 시나리오
|
||||||
|
|
||||||
|
이 문서는 현재 저장소에 포함된 10개 스킬을 실제 TRAE에서 바로 검증해 볼 수 있도록 만든 프롬프트형 시나리오 모음입니다.
|
||||||
|
|
||||||
|
## 공통 사용법
|
||||||
|
|
||||||
|
- 각 시나리오의 **입력 프롬프트**를 그대로 복사해서 TRAE에 넣습니다.
|
||||||
|
- 기대 동작은 "정답 문장"이 아니라 "어떤 종류의 흐름이 나와야 하는지"를 의미합니다.
|
||||||
|
- 결과가 다소 다르더라도, 핵심 스킬 호출과 절차가 맞으면 통과로 봅니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. brainstorming
|
||||||
|
|
||||||
|
- **목적:** 구현 전에 설계 중심 흐름으로 들어가는지 확인
|
||||||
|
- **사전 조건:** 현재 프로젝트가 열려 있어야 함
|
||||||
|
- **입력 프롬프트:** `간단한 할일 관리 위젯을 추가하고 싶은데 바로 구현하지 말고 먼저 어떻게 설계할지 같이 정리해줘`
|
||||||
|
- **기대 동작:** 바로 코드 작성으로 들어가지 않고, 질문 또는 설계 흐름으로 진입해야 함
|
||||||
|
- **확인 포인트:** 한 번에 한 질문, 설계 승인 전 구현 금지, 이후 `writing-plans`로 이어질 여지가 보여야 함
|
||||||
|
|
||||||
|
## 2. frontend-design
|
||||||
|
|
||||||
|
- **목적:** 평범한 UI 생성이 아니라 강한 미학 방향을 먼저 잡는지 확인
|
||||||
|
- **사전 조건:** 프론트엔드 결과물을 요청할 수 있는 문맥
|
||||||
|
- **입력 프롬프트:** `패션 브랜드용 랜딩페이지 히어로 섹션을 만들어줘. 흔한 AI 스타일 말고 강한 개성이 있었으면 좋겠어`
|
||||||
|
- **기대 동작:** 톤, 타이포그래피, 색상, 레이아웃 같은 미학 방향을 언급해야 함
|
||||||
|
- **확인 포인트:** "AI 티 나는 화면" 회피, 대담한 스타일 방향, 실제 구현 코드 의도 설명
|
||||||
|
|
||||||
|
## 3. ui-ux-pro-max
|
||||||
|
|
||||||
|
- **목적:** UI 미감뿐 아니라 UX 품질 기준까지 함께 다루는지 확인
|
||||||
|
- **사전 조건:** UI 구조 개선 요청 가능
|
||||||
|
- **입력 프롬프트:** `관리자 대시보드가 뭔가 덜 프로페셔널해 보여. 정보 위계, 접근성, 상호작용 품질 관점에서 개선 방향을 제안해줘`
|
||||||
|
- **기대 동작:** 접근성, 위계, 상태 표현, 레이아웃, 상호작용 품질을 우선순위 있게 다뤄야 함
|
||||||
|
- **확인 포인트:** 단순 색상 추천이 아니라 시스템 수준의 개선 기준 제시
|
||||||
|
|
||||||
|
## 4. systematic-debugging
|
||||||
|
|
||||||
|
- **목적:** 버그 수정 전에 근본 원인 조사 흐름을 밟는지 확인
|
||||||
|
- **사전 조건:** 버그 상황을 설명할 수 있어야 함
|
||||||
|
- **입력 프롬프트:** `로그인 후 가끔 대시보드가 빈 화면으로 떠. 일단 추측하지 말고 체계적으로 원인부터 찾는 방식으로 진행해줘`
|
||||||
|
- **기대 동작:** 재현, 최근 변경, 로그/증거 수집, 가설 검증 순서를 제시해야 함
|
||||||
|
- **확인 포인트:** 즉시 수정안부터 제안하지 않는지, 원인 조사 단계가 분리되는지
|
||||||
|
|
||||||
|
## 5. writing-plans
|
||||||
|
|
||||||
|
- **목적:** 구현 전에 체크리스트형 계획을 만드는지 확인
|
||||||
|
- **사전 조건:** 간단한 요구사항 또는 명세가 있어야 함
|
||||||
|
- **입력 프롬프트:** `사용자 프로필 편집 기능을 추가해야 해. 코드는 아직 건드리지 말고 파일 단위와 테스트 단위까지 포함한 구현 계획만 작성해줘`
|
||||||
|
- **기대 동작:** 파일 경로, 작업 순서, 검증 기준, 테스트 단계가 포함된 계획을 작성해야 함
|
||||||
|
- **확인 포인트:** TODO 수준이 아니라 실행 가능한 단계인지, 작은 단계로 분해되는지
|
||||||
|
|
||||||
|
## 6. find-skills
|
||||||
|
|
||||||
|
- **목적:** 바로 구현보다 적절한 커뮤니티 스킬 탐색 흐름을 타는지 확인
|
||||||
|
- **사전 조건:** 특정 작업용 스킬을 찾는 상황
|
||||||
|
- **입력 프롬프트:** `PR 리뷰를 더 체계적으로 해주는 스킬이 있는지 찾아줘`
|
||||||
|
- **기대 동작:** 필요한 도메인을 파악하고, 인기/품질 기준을 거쳐 후보를 제시해야 함
|
||||||
|
- **확인 포인트:** 검색만이 아니라 설치 수, 출처, 설치 명령까지 제공하는지
|
||||||
|
|
||||||
|
## 7. using-superpowers
|
||||||
|
|
||||||
|
- **목적:** 응답 전에 관련 스킬을 먼저 확인하는 운영 규칙이 작동하는지 확인
|
||||||
|
- **사전 조건:** 다른 스킬이 분명히 필요한 작업 요청
|
||||||
|
- **입력 프롬프트:** `새로운 대시보드 페이지를 설계하고 구현해줘`
|
||||||
|
- **기대 동작:** 바로 구현하기보다 먼저 관련 스킬을 확인하고 적용해야 함
|
||||||
|
- **확인 포인트:** 프로세스 스킬 우선, 구현 스킬 후순위 원칙이 드러나는지
|
||||||
|
|
||||||
|
## 8. karpathy-guidelines
|
||||||
|
|
||||||
|
- **목적:** 과설계와 과추정을 줄이는 행동 원칙이 반영되는지 확인
|
||||||
|
- **사전 조건:** 코드 변경 또는 리팩터링 맥락
|
||||||
|
- **입력 프롬프트:** `이 기능 리팩터링하되 너무 크게 뜯어고치지 말고 필요한 최소 변경만으로 접근해줘`
|
||||||
|
- **기대 동작:** 가정 명시, 최소 변경, 검증 기준 중심으로 접근해야 함
|
||||||
|
- **확인 포인트:** 주변 코드까지 과하게 손대지 않는지, 목표 기반으로 설명하는지
|
||||||
|
|
||||||
|
## 9. webapp-testing
|
||||||
|
|
||||||
|
- **목적:** 로컬 웹앱 테스트에서 정찰 후 행동 패턴을 따르는지 확인
|
||||||
|
- **사전 조건:** 로컬 웹앱 또는 정적 HTML을 테스트할 수 있는 상황
|
||||||
|
- **입력 프롬프트:** `이 로컬 웹앱의 회원가입 폼을 Playwright 방식으로 점검해줘. 바로 클릭하지 말고 먼저 화면 상태부터 확인해줘`
|
||||||
|
- **기대 동작:** `networkidle` 대기, 스크린샷 또는 DOM 확인, 셀렉터 파악 후 행동 순서를 보여야 함
|
||||||
|
- **확인 포인트:** 먼저 관찰하고 나중에 상호작용하는지
|
||||||
|
|
||||||
|
## 10. agent-browser
|
||||||
|
|
||||||
|
- **목적:** 일반 브라우저 도구보다 agent-browser 흐름이 필요한 상황을 인식하는지 확인
|
||||||
|
- **사전 조건:** 브라우저 자동화 요청
|
||||||
|
- **입력 프롬프트:** `특정 사이트에 로그인해서 여러 페이지를 돌아다니며 상태를 확인하고 스크린샷도 남겨줘`
|
||||||
|
- **기대 동작:** `agent-browser skills get core` 같은 시작 절차를 우선 언급해야 함
|
||||||
|
- **확인 포인트:** 설치된 버전 기준 가이드를 먼저 확인하는지, 특수 가이드 가능성도 보는지
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 교차 시나리오 1: 설계 -> 계획
|
||||||
|
|
||||||
|
- **목적:** `brainstorming`에서 `writing-plans`로 자연스럽게 이어지는지 확인
|
||||||
|
- **입력 프롬프트:** `알림 센터 기능을 추가하고 싶어. 먼저 설계부터 하고, 내가 승인하면 계획까지 이어가자`
|
||||||
|
- **기대 동작:** 처음에는 설계 흐름, 승인 후에는 계획 문서 흐름으로 전환
|
||||||
|
- **확인 포인트:** 설계와 구현 계획 단계가 섞이지 않는지
|
||||||
|
|
||||||
|
## 교차 시나리오 2: 디버깅 -> 검증
|
||||||
|
|
||||||
|
- **목적:** 버그 분석 이후 실제 검증 단계로 이어질 수 있는지 확인
|
||||||
|
- **입력 프롬프트:** `폼 제출 버그를 먼저 근본 원인부터 찾고, 수정 전후 동작을 브라우저에서 검증할 수 있게 정리해줘`
|
||||||
|
- **기대 동작:** `systematic-debugging` 중심으로 조사하고, 필요 시 `webapp-testing` 또는 브라우저 검증 흐름으로 이어짐
|
||||||
|
- **확인 포인트:** 원인 분석과 검증이 분리되면서도 연결되는지
|
||||||
|
|
||||||
|
## 교차 시나리오 3: UI 설계 -> 품질 향상
|
||||||
|
|
||||||
|
- **목적:** `frontend-design`과 `ui-ux-pro-max`가 역할을 나눠 쓰이는지 확인
|
||||||
|
- **입력 프롬프트:** `이 SaaS 설정 페이지를 더 개성 있게 만들고 싶어. 동시에 접근성과 정보 위계도 챙겨줘`
|
||||||
|
- **기대 동작:** 미학 방향과 UX 품질 기준을 함께 다루되, 역할이 구분되어야 함
|
||||||
|
- **확인 포인트:** 스타일 강화와 UX 품질 점검이 모두 보이는지
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<!doctype html> <!-- 문서 타입을 HTML5로 선언한다. -->
|
||||||
|
<html lang="ko"> <!-- 문서 기본 언어를 한국어로 설정한다. -->
|
||||||
|
<head> <!-- 문서 메타데이터 영역을 시작한다. -->
|
||||||
|
<meta charset="UTF-8" /> <!-- 문자 인코딩을 UTF-8로 지정한다. -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <!-- 모바일 화면 크기에 맞춰 레이아웃이 동작하도록 설정한다. -->
|
||||||
|
<title>SQLite + FastAPI + Vue Hello World</title> <!-- 브라우저 탭 제목을 지정한다. -->
|
||||||
|
</head> <!-- 문서 메타데이터 영역을 닫는다. -->
|
||||||
|
<body> <!-- 사용자에게 보이는 본문 영역을 시작한다. -->
|
||||||
|
<div id="app"></div> <!-- Vue 애플리케이션이 마운트될 루트 요소를 배치한다. -->
|
||||||
|
<script> <!-- Vue 앱이 읽을 전역 설정 객체를 먼저 선언한다. -->
|
||||||
|
window.APP_CONFIG = { // 백엔드 API 연결 정보를 전역 설정 객체로 저장한다.
|
||||||
|
apiHost: 'http://127.0.0.1', // 기본 백엔드 호스트 주소를 설정한다.
|
||||||
|
apiPort: '8000', // 기본 백엔드 포트 번호를 설정한다.
|
||||||
|
} // 전역 설정 객체 정의를 마무리한다.
|
||||||
|
</script> <!-- 전역 설정 객체 선언 스크립트를 닫는다. -->
|
||||||
|
<script type="module" src="/src/main.js"></script> <!-- Vue 진입 스크립트를 불러온다. -->
|
||||||
|
</body> <!-- 사용자에게 보이는 본문 영역을 닫는다. -->
|
||||||
|
</html> <!-- HTML 문서를 마무리한다. -->
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../esbuild/bin/esbuild
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../nanoid/bin/nanoid.cjs
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../@babel/parser/bin/babel-parser.js
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../rollup/dist/bin/rollup
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../vite/bin/vite.js
|
||||||
+443
@@ -0,0 +1,443 @@
|
|||||||
|
{
|
||||||
|
"name": "sqlite-fastapi-vue-hello",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"node_modules/@babel/helper-string-parser": {
|
||||||
|
"version": "7.29.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
|
||||||
|
"integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/helper-validator-identifier": {
|
||||||
|
"version": "7.29.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
|
||||||
|
"integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/parser": {
|
||||||
|
"version": "7.29.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
|
||||||
|
"integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/types": "^7.29.7"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"parser": "bin/babel-parser.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@babel/types": {
|
||||||
|
"version": "7.29.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
|
||||||
|
"integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/helper-string-parser": "^7.29.7",
|
||||||
|
"@babel/helper-validator-identifier": "^7.29.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@esbuild/darwin-x64": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@jridgewell/sourcemap-codec": {
|
||||||
|
"version": "1.5.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
||||||
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@types/estree": {
|
||||||
|
"version": "1.0.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
|
||||||
|
"integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-core": {
|
||||||
|
"version": "3.5.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.35.tgz",
|
||||||
|
"integrity": "sha512-BUmHaR1J+O+CKZ9uJucdVTEr1LHsdyvv7vG3eNRhK3CczEHeMd/LtsHAuD7PbrxvI2envCY2v7HI1vC1aBRzKw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/parser": "^7.29.3",
|
||||||
|
"@vue/shared": "3.5.35",
|
||||||
|
"entities": "^7.0.1",
|
||||||
|
"estree-walker": "^2.0.2",
|
||||||
|
"source-map-js": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-dom": {
|
||||||
|
"version": "3.5.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.35.tgz",
|
||||||
|
"integrity": "sha512-k+bprkXxuqhVajgTx5mUHuir7TwQzUKOWR40ng1ncAqQRPnrLngGGgqVEEhOnTMlc8btHYVKmrP8s5Qyg0hvYA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-core": "3.5.35",
|
||||||
|
"@vue/shared": "3.5.35"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-sfc": {
|
||||||
|
"version": "3.5.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.35.tgz",
|
||||||
|
"integrity": "sha512-G5VPMcXTSywXBgtFOZOnHKBxKSrwXUcvY1iaF5/hRcy7t0J6CH/d8ha9F4nzi00Fax1eLV0QHM7v4mQu68jydw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/parser": "^7.29.3",
|
||||||
|
"@vue/compiler-core": "3.5.35",
|
||||||
|
"@vue/compiler-dom": "3.5.35",
|
||||||
|
"@vue/compiler-ssr": "3.5.35",
|
||||||
|
"@vue/shared": "3.5.35",
|
||||||
|
"estree-walker": "^2.0.2",
|
||||||
|
"magic-string": "^0.30.21",
|
||||||
|
"postcss": "^8.5.15",
|
||||||
|
"source-map-js": "^1.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/compiler-ssr": {
|
||||||
|
"version": "3.5.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.35.tgz",
|
||||||
|
"integrity": "sha512-rGhAeXgdM7/ffTJGXT69rCCdTmjDewnFuUZfBQQHTdcEBeWdT5HCGY60y2ytLJr9/Dsu7IntUi5z/w0h6Rjnzw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-dom": "3.5.35",
|
||||||
|
"@vue/shared": "3.5.35"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/reactivity": {
|
||||||
|
"version": "3.5.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.35.tgz",
|
||||||
|
"integrity": "sha512-tVc+SsHConvh/Lz64qq1pP3rYArBmK42xonovEcxY74SQtvctZodG/zhq54P5dr38cVuw25d27cPNRdlMidpGQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/shared": "3.5.35"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/runtime-core": {
|
||||||
|
"version": "3.5.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.35.tgz",
|
||||||
|
"integrity": "sha512-A/xFNX9loIcWDygeQuNCfKuh0CoYBzxhqEMNah5TSFg9Z53DrFYEN2qi5CU9necjM1OWYegYREUTHmXTmhfXtg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/reactivity": "3.5.35",
|
||||||
|
"@vue/shared": "3.5.35"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/runtime-dom": {
|
||||||
|
"version": "3.5.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.35.tgz",
|
||||||
|
"integrity": "sha512-odrJ1C391dbGnyDRh8U+rnP7J2amIEzfmRk5vXy7xi3aZhEXofTvpi0T4HJb6jlNqQZTNPR5MPHSB3RHNkIORA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/reactivity": "3.5.35",
|
||||||
|
"@vue/runtime-core": "3.5.35",
|
||||||
|
"@vue/shared": "3.5.35",
|
||||||
|
"csstype": "^3.2.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/server-renderer": {
|
||||||
|
"version": "3.5.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.35.tgz",
|
||||||
|
"integrity": "sha512-NkebSOYdB97wi8OQcO3HqzZSlymJi/aWsN/7h74OSVhRTm6qGs3Jp3e0rCXynmWwSlKeRrnlIug+ilYoHBmQDA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-ssr": "3.5.35",
|
||||||
|
"@vue/shared": "3.5.35"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "3.5.35"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@vue/shared": {
|
||||||
|
"version": "3.5.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.35.tgz",
|
||||||
|
"integrity": "sha512-zSbjL7gRXwks2ZQLRGCajBtBXEOXW9Ddhn/HvSdrGkE2dqGnumzW8XtusRrxrE9LvqtiqDXQ+A60Hp6mvdYxfA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/csstype": {
|
||||||
|
"version": "3.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
||||||
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/entities": {
|
||||||
|
"version": "7.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
|
||||||
|
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/esbuild": {
|
||||||
|
"version": "0.21.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
|
||||||
|
"integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
|
||||||
|
"dev": true,
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"esbuild": "bin/esbuild"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@esbuild/aix-ppc64": "0.21.5",
|
||||||
|
"@esbuild/android-arm": "0.21.5",
|
||||||
|
"@esbuild/android-arm64": "0.21.5",
|
||||||
|
"@esbuild/android-x64": "0.21.5",
|
||||||
|
"@esbuild/darwin-arm64": "0.21.5",
|
||||||
|
"@esbuild/darwin-x64": "0.21.5",
|
||||||
|
"@esbuild/freebsd-arm64": "0.21.5",
|
||||||
|
"@esbuild/freebsd-x64": "0.21.5",
|
||||||
|
"@esbuild/linux-arm": "0.21.5",
|
||||||
|
"@esbuild/linux-arm64": "0.21.5",
|
||||||
|
"@esbuild/linux-ia32": "0.21.5",
|
||||||
|
"@esbuild/linux-loong64": "0.21.5",
|
||||||
|
"@esbuild/linux-mips64el": "0.21.5",
|
||||||
|
"@esbuild/linux-ppc64": "0.21.5",
|
||||||
|
"@esbuild/linux-riscv64": "0.21.5",
|
||||||
|
"@esbuild/linux-s390x": "0.21.5",
|
||||||
|
"@esbuild/linux-x64": "0.21.5",
|
||||||
|
"@esbuild/netbsd-x64": "0.21.5",
|
||||||
|
"@esbuild/openbsd-x64": "0.21.5",
|
||||||
|
"@esbuild/sunos-x64": "0.21.5",
|
||||||
|
"@esbuild/win32-arm64": "0.21.5",
|
||||||
|
"@esbuild/win32-ia32": "0.21.5",
|
||||||
|
"@esbuild/win32-x64": "0.21.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/estree-walker": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/magic-string": {
|
||||||
|
"version": "0.30.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||||
|
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/nanoid": {
|
||||||
|
"version": "3.3.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
|
||||||
|
"integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"nanoid": "bin/nanoid.cjs"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/picocolors": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/postcss": {
|
||||||
|
"version": "8.5.15",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz",
|
||||||
|
"integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"nanoid": "^3.3.12",
|
||||||
|
"picocolors": "^1.1.1",
|
||||||
|
"source-map-js": "^1.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/rollup": {
|
||||||
|
"version": "4.61.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz",
|
||||||
|
"integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/estree": "1.0.9"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"rollup": "dist/bin/rollup"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@rollup/rollup-android-arm-eabi": "4.61.1",
|
||||||
|
"@rollup/rollup-android-arm64": "4.61.1",
|
||||||
|
"@rollup/rollup-darwin-arm64": "4.61.1",
|
||||||
|
"@rollup/rollup-darwin-x64": "4.61.1",
|
||||||
|
"@rollup/rollup-freebsd-arm64": "4.61.1",
|
||||||
|
"@rollup/rollup-freebsd-x64": "4.61.1",
|
||||||
|
"@rollup/rollup-linux-arm-gnueabihf": "4.61.1",
|
||||||
|
"@rollup/rollup-linux-arm-musleabihf": "4.61.1",
|
||||||
|
"@rollup/rollup-linux-arm64-gnu": "4.61.1",
|
||||||
|
"@rollup/rollup-linux-arm64-musl": "4.61.1",
|
||||||
|
"@rollup/rollup-linux-loong64-gnu": "4.61.1",
|
||||||
|
"@rollup/rollup-linux-loong64-musl": "4.61.1",
|
||||||
|
"@rollup/rollup-linux-ppc64-gnu": "4.61.1",
|
||||||
|
"@rollup/rollup-linux-ppc64-musl": "4.61.1",
|
||||||
|
"@rollup/rollup-linux-riscv64-gnu": "4.61.1",
|
||||||
|
"@rollup/rollup-linux-riscv64-musl": "4.61.1",
|
||||||
|
"@rollup/rollup-linux-s390x-gnu": "4.61.1",
|
||||||
|
"@rollup/rollup-linux-x64-gnu": "4.61.1",
|
||||||
|
"@rollup/rollup-linux-x64-musl": "4.61.1",
|
||||||
|
"@rollup/rollup-openbsd-x64": "4.61.1",
|
||||||
|
"@rollup/rollup-openharmony-arm64": "4.61.1",
|
||||||
|
"@rollup/rollup-win32-arm64-msvc": "4.61.1",
|
||||||
|
"@rollup/rollup-win32-ia32-msvc": "4.61.1",
|
||||||
|
"@rollup/rollup-win32-x64-gnu": "4.61.1",
|
||||||
|
"@rollup/rollup-win32-x64-msvc": "4.61.1",
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/source-map-js": {
|
||||||
|
"version": "1.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite": {
|
||||||
|
"version": "5.4.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
|
||||||
|
"integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"esbuild": "^0.21.3",
|
||||||
|
"postcss": "^8.4.43",
|
||||||
|
"rollup": "^4.20.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"vite": "bin/vite.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.0.0 || >=20.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/node": "^18.0.0 || >=20.0.0",
|
||||||
|
"less": "*",
|
||||||
|
"lightningcss": "^1.21.0",
|
||||||
|
"sass": "*",
|
||||||
|
"sass-embedded": "*",
|
||||||
|
"stylus": "*",
|
||||||
|
"sugarss": "*",
|
||||||
|
"terser": "^5.4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/node": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"less": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"lightningcss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sass-embedded": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"stylus": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"sugarss": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"terser": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vue": {
|
||||||
|
"version": "3.5.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.35.tgz",
|
||||||
|
"integrity": "sha512-cx89fnr+0kVGHiNFG6y6s0bdjypJRFNZn6x3WPstNdQR1bi1mbB7h4v5IBGTsPJU3nK1+0Iqj3Zf+hZWMieR4Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/compiler-dom": "3.5.35",
|
||||||
|
"@vue/compiler-sfc": "3.5.35",
|
||||||
|
"@vue/runtime-dom": "3.5.35",
|
||||||
|
"@vue/server-renderer": "3.5.35",
|
||||||
|
"@vue/shared": "3.5.35"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
# @babel/helper-string-parser
|
||||||
|
|
||||||
|
> A utility package to parse strings
|
||||||
|
|
||||||
|
See our website [@babel/helper-string-parser](https://babeljs.io/docs/babel-helper-string-parser) for more information.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Using npm:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install --save @babel/helper-string-parser
|
||||||
|
```
|
||||||
|
|
||||||
|
or using yarn:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn add @babel/helper-string-parser
|
||||||
|
```
|
||||||
+295
@@ -0,0 +1,295 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.readCodePoint = readCodePoint;
|
||||||
|
exports.readInt = readInt;
|
||||||
|
exports.readStringContents = readStringContents;
|
||||||
|
var _isDigit = function isDigit(code) {
|
||||||
|
return code >= 48 && code <= 57;
|
||||||
|
};
|
||||||
|
const forbiddenNumericSeparatorSiblings = {
|
||||||
|
decBinOct: new Set([46, 66, 69, 79, 95, 98, 101, 111]),
|
||||||
|
hex: new Set([46, 88, 95, 120])
|
||||||
|
};
|
||||||
|
const isAllowedNumericSeparatorSibling = {
|
||||||
|
bin: ch => ch === 48 || ch === 49,
|
||||||
|
oct: ch => ch >= 48 && ch <= 55,
|
||||||
|
dec: ch => ch >= 48 && ch <= 57,
|
||||||
|
hex: ch => ch >= 48 && ch <= 57 || ch >= 65 && ch <= 70 || ch >= 97 && ch <= 102
|
||||||
|
};
|
||||||
|
function readStringContents(type, input, pos, lineStart, curLine, errors) {
|
||||||
|
const initialPos = pos;
|
||||||
|
const initialLineStart = lineStart;
|
||||||
|
const initialCurLine = curLine;
|
||||||
|
let out = "";
|
||||||
|
let firstInvalidLoc = null;
|
||||||
|
let chunkStart = pos;
|
||||||
|
const {
|
||||||
|
length
|
||||||
|
} = input;
|
||||||
|
for (;;) {
|
||||||
|
if (pos >= length) {
|
||||||
|
errors.unterminated(initialPos, initialLineStart, initialCurLine);
|
||||||
|
out += input.slice(chunkStart, pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const ch = input.charCodeAt(pos);
|
||||||
|
if (isStringEnd(type, ch, input, pos)) {
|
||||||
|
out += input.slice(chunkStart, pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ch === 92) {
|
||||||
|
out += input.slice(chunkStart, pos);
|
||||||
|
const res = readEscapedChar(input, pos, lineStart, curLine, type === "template", errors);
|
||||||
|
if (res.ch === null && !firstInvalidLoc) {
|
||||||
|
firstInvalidLoc = {
|
||||||
|
pos,
|
||||||
|
lineStart,
|
||||||
|
curLine
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
out += res.ch;
|
||||||
|
}
|
||||||
|
({
|
||||||
|
pos,
|
||||||
|
lineStart,
|
||||||
|
curLine
|
||||||
|
} = res);
|
||||||
|
chunkStart = pos;
|
||||||
|
} else if (ch === 8232 || ch === 8233) {
|
||||||
|
++pos;
|
||||||
|
++curLine;
|
||||||
|
lineStart = pos;
|
||||||
|
} else if (ch === 10 || ch === 13) {
|
||||||
|
if (type === "template") {
|
||||||
|
out += input.slice(chunkStart, pos) + "\n";
|
||||||
|
++pos;
|
||||||
|
if (ch === 13 && input.charCodeAt(pos) === 10) {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
++curLine;
|
||||||
|
chunkStart = lineStart = pos;
|
||||||
|
} else {
|
||||||
|
errors.unterminated(initialPos, initialLineStart, initialCurLine);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
pos,
|
||||||
|
str: out,
|
||||||
|
firstInvalidLoc,
|
||||||
|
lineStart,
|
||||||
|
curLine,
|
||||||
|
containsInvalid: !!firstInvalidLoc
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function isStringEnd(type, ch, input, pos) {
|
||||||
|
if (type === "template") {
|
||||||
|
return ch === 96 || ch === 36 && input.charCodeAt(pos + 1) === 123;
|
||||||
|
}
|
||||||
|
return ch === (type === "double" ? 34 : 39);
|
||||||
|
}
|
||||||
|
function readEscapedChar(input, pos, lineStart, curLine, inTemplate, errors) {
|
||||||
|
const throwOnInvalid = !inTemplate;
|
||||||
|
pos++;
|
||||||
|
const res = ch => ({
|
||||||
|
pos,
|
||||||
|
ch,
|
||||||
|
lineStart,
|
||||||
|
curLine
|
||||||
|
});
|
||||||
|
const ch = input.charCodeAt(pos++);
|
||||||
|
switch (ch) {
|
||||||
|
case 110:
|
||||||
|
return res("\n");
|
||||||
|
case 114:
|
||||||
|
return res("\r");
|
||||||
|
case 120:
|
||||||
|
{
|
||||||
|
let code;
|
||||||
|
({
|
||||||
|
code,
|
||||||
|
pos
|
||||||
|
} = readHexChar(input, pos, lineStart, curLine, 2, false, throwOnInvalid, errors));
|
||||||
|
return res(code === null ? null : String.fromCharCode(code));
|
||||||
|
}
|
||||||
|
case 117:
|
||||||
|
{
|
||||||
|
let code;
|
||||||
|
({
|
||||||
|
code,
|
||||||
|
pos
|
||||||
|
} = readCodePoint(input, pos, lineStart, curLine, throwOnInvalid, errors));
|
||||||
|
return res(code === null ? null : String.fromCodePoint(code));
|
||||||
|
}
|
||||||
|
case 116:
|
||||||
|
return res("\t");
|
||||||
|
case 98:
|
||||||
|
return res("\b");
|
||||||
|
case 118:
|
||||||
|
return res("\u000b");
|
||||||
|
case 102:
|
||||||
|
return res("\f");
|
||||||
|
case 13:
|
||||||
|
if (input.charCodeAt(pos) === 10) {
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
case 10:
|
||||||
|
lineStart = pos;
|
||||||
|
++curLine;
|
||||||
|
case 8232:
|
||||||
|
case 8233:
|
||||||
|
return res("");
|
||||||
|
case 56:
|
||||||
|
case 57:
|
||||||
|
if (inTemplate) {
|
||||||
|
return res(null);
|
||||||
|
} else {
|
||||||
|
errors.strictNumericEscape(pos - 1, lineStart, curLine);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if (ch >= 48 && ch <= 55) {
|
||||||
|
const startPos = pos - 1;
|
||||||
|
const match = /^[0-7]+/.exec(input.slice(startPos, pos + 2));
|
||||||
|
let octalStr = match[0];
|
||||||
|
let octal = parseInt(octalStr, 8);
|
||||||
|
if (octal > 255) {
|
||||||
|
octalStr = octalStr.slice(0, -1);
|
||||||
|
octal = parseInt(octalStr, 8);
|
||||||
|
}
|
||||||
|
pos += octalStr.length - 1;
|
||||||
|
const next = input.charCodeAt(pos);
|
||||||
|
if (octalStr !== "0" || next === 56 || next === 57) {
|
||||||
|
if (inTemplate) {
|
||||||
|
return res(null);
|
||||||
|
} else {
|
||||||
|
errors.strictNumericEscape(startPos, lineStart, curLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res(String.fromCharCode(octal));
|
||||||
|
}
|
||||||
|
return res(String.fromCharCode(ch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function readHexChar(input, pos, lineStart, curLine, len, forceLen, throwOnInvalid, errors) {
|
||||||
|
const initialPos = pos;
|
||||||
|
let n;
|
||||||
|
({
|
||||||
|
n,
|
||||||
|
pos
|
||||||
|
} = readInt(input, pos, lineStart, curLine, 16, len, forceLen, false, errors, !throwOnInvalid));
|
||||||
|
if (n === null) {
|
||||||
|
if (throwOnInvalid) {
|
||||||
|
errors.invalidEscapeSequence(initialPos, lineStart, curLine);
|
||||||
|
} else {
|
||||||
|
pos = initialPos - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
code: n,
|
||||||
|
pos
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function readInt(input, pos, lineStart, curLine, radix, len, forceLen, allowNumSeparator, errors, bailOnError) {
|
||||||
|
const start = pos;
|
||||||
|
const forbiddenSiblings = radix === 16 ? forbiddenNumericSeparatorSiblings.hex : forbiddenNumericSeparatorSiblings.decBinOct;
|
||||||
|
const isAllowedSibling = radix === 16 ? isAllowedNumericSeparatorSibling.hex : radix === 10 ? isAllowedNumericSeparatorSibling.dec : radix === 8 ? isAllowedNumericSeparatorSibling.oct : isAllowedNumericSeparatorSibling.bin;
|
||||||
|
let invalid = false;
|
||||||
|
let total = 0;
|
||||||
|
for (let i = 0, e = len == null ? Infinity : len; i < e; ++i) {
|
||||||
|
const code = input.charCodeAt(pos);
|
||||||
|
let val;
|
||||||
|
if (code === 95 && allowNumSeparator !== "bail") {
|
||||||
|
const prev = input.charCodeAt(pos - 1);
|
||||||
|
const next = input.charCodeAt(pos + 1);
|
||||||
|
if (!allowNumSeparator) {
|
||||||
|
if (bailOnError) return {
|
||||||
|
n: null,
|
||||||
|
pos
|
||||||
|
};
|
||||||
|
errors.numericSeparatorInEscapeSequence(pos, lineStart, curLine);
|
||||||
|
} else if (Number.isNaN(next) || !isAllowedSibling(next) || forbiddenSiblings.has(prev) || forbiddenSiblings.has(next)) {
|
||||||
|
if (bailOnError) return {
|
||||||
|
n: null,
|
||||||
|
pos
|
||||||
|
};
|
||||||
|
errors.unexpectedNumericSeparator(pos, lineStart, curLine);
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (code >= 97) {
|
||||||
|
val = code - 97 + 10;
|
||||||
|
} else if (code >= 65) {
|
||||||
|
val = code - 65 + 10;
|
||||||
|
} else if (_isDigit(code)) {
|
||||||
|
val = code - 48;
|
||||||
|
} else {
|
||||||
|
val = Infinity;
|
||||||
|
}
|
||||||
|
if (val >= radix) {
|
||||||
|
if (val <= 9 && bailOnError) {
|
||||||
|
return {
|
||||||
|
n: null,
|
||||||
|
pos
|
||||||
|
};
|
||||||
|
} else if (val <= 9 && errors.invalidDigit(pos, lineStart, curLine, radix)) {
|
||||||
|
val = 0;
|
||||||
|
} else if (forceLen) {
|
||||||
|
val = 0;
|
||||||
|
invalid = true;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++pos;
|
||||||
|
total = total * radix + val;
|
||||||
|
}
|
||||||
|
if (pos === start || len != null && pos - start !== len || invalid) {
|
||||||
|
return {
|
||||||
|
n: null,
|
||||||
|
pos
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
n: total,
|
||||||
|
pos
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function readCodePoint(input, pos, lineStart, curLine, throwOnInvalid, errors) {
|
||||||
|
const ch = input.charCodeAt(pos);
|
||||||
|
let code;
|
||||||
|
if (ch === 123) {
|
||||||
|
++pos;
|
||||||
|
({
|
||||||
|
code,
|
||||||
|
pos
|
||||||
|
} = readHexChar(input, pos, lineStart, curLine, input.indexOf("}", pos) - pos, true, throwOnInvalid, errors));
|
||||||
|
++pos;
|
||||||
|
if (code !== null && code > 0x10ffff) {
|
||||||
|
if (throwOnInvalid) {
|
||||||
|
errors.invalidCodePoint(pos, lineStart, curLine);
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
code: null,
|
||||||
|
pos
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
({
|
||||||
|
code,
|
||||||
|
pos
|
||||||
|
} = readHexChar(input, pos, lineStart, curLine, 4, false, throwOnInvalid, errors));
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
code,
|
||||||
|
pos
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+31
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "@babel/helper-string-parser",
|
||||||
|
"version": "7.29.7",
|
||||||
|
"description": "A utility package to parse strings",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/babel/babel.git",
|
||||||
|
"directory": "packages/babel-helper-string-parser"
|
||||||
|
},
|
||||||
|
"homepage": "https://babel.dev/docs/en/next/babel-helper-string-parser",
|
||||||
|
"license": "MIT",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"main": "./lib/index.js",
|
||||||
|
"devDependencies": {
|
||||||
|
"charcodes": "^0.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
},
|
||||||
|
"author": "The Babel Team (https://babel.dev/team)",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./lib/index.d.ts",
|
||||||
|
"default": "./lib/index.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
# @babel/helper-validator-identifier
|
||||||
|
|
||||||
|
> Validate identifier/keywords name
|
||||||
|
|
||||||
|
See our website [@babel/helper-validator-identifier](https://babeljs.io/docs/babel-helper-validator-identifier) for more information.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Using npm:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install --save @babel/helper-validator-identifier
|
||||||
|
```
|
||||||
|
|
||||||
|
or using yarn:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn add @babel/helper-validator-identifier
|
||||||
|
```
|
||||||
+70
@@ -0,0 +1,70 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.isIdentifierChar = isIdentifierChar;
|
||||||
|
exports.isIdentifierName = isIdentifierName;
|
||||||
|
exports.isIdentifierStart = isIdentifierStart;
|
||||||
|
let nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u052f\u0531-\u0556\u0559\u0560-\u0588\u05d0-\u05ea\u05ef-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u0860-\u086a\u0870-\u0887\u0889-\u088f\u08a0-\u08c9\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u09fc\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0af9\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c58-\u0c5a\u0c5c\u0c5d\u0c60\u0c61\u0c80\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cdc-\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d04-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d54-\u0d56\u0d5f-\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e86-\u0e8a\u0e8c-\u0ea3\u0ea5\u0ea7-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f8\u1700-\u1711\u171f-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1878\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4c\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1c80-\u1c8a\u1c90-\u1cba\u1cbd-\u1cbf\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1cfa\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2118-\u211d\u2124\u2126\u2128\u212a-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309b-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312f\u3131-\u318e\u31a0-\u31bf\u31f0-\u31ff\u3400-\u4dbf\u4e00-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua69d\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua7dc\ua7f1-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua8fd\ua8fe\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\ua9e0-\ua9e4\ua9e6-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa7e-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab69\uab70-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc";
|
||||||
|
let nonASCIIidentifierChars = "\xb7\u0300-\u036f\u0387\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u0669\u0670\u06d6-\u06dc\u06df-\u06e4\u06e7\u06e8\u06ea-\u06ed\u06f0-\u06f9\u0711\u0730-\u074a\u07a6-\u07b0\u07c0-\u07c9\u07eb-\u07f3\u07fd\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0859-\u085b\u0897-\u089f\u08ca-\u08e1\u08e3-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09cb-\u09cd\u09d7\u09e2\u09e3\u09e6-\u09ef\u09fe\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2\u0ae3\u0ae6-\u0aef\u0afa-\u0aff\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b55-\u0b57\u0b62\u0b63\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c00-\u0c04\u0c3c\u0c3e-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0c66-\u0c6f\u0c81-\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0ce6-\u0cef\u0cf3\u0d00-\u0d03\u0d3b\u0d3c\u0d3e-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57\u0d62\u0d63\u0d66-\u0d6f\u0d81-\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0de6-\u0def\u0df2\u0df3\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0e50-\u0e59\u0eb1\u0eb4-\u0ebc\u0ec8-\u0ece\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e\u0f3f\u0f71-\u0f84\u0f86\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u102b-\u103e\u1040-\u1049\u1056-\u1059\u105e-\u1060\u1062-\u1064\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u1369-\u1371\u1712-\u1715\u1732-\u1734\u1752\u1753\u1772\u1773\u17b4-\u17d3\u17dd\u17e0-\u17e9\u180b-\u180d\u180f-\u1819\u18a9\u1920-\u192b\u1930-\u193b\u1946-\u194f\u19d0-\u19da\u1a17-\u1a1b\u1a55-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1ab0-\u1abd\u1abf-\u1add\u1ae0-\u1aeb\u1b00-\u1b04\u1b34-\u1b44\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1b82\u1ba1-\u1bad\u1bb0-\u1bb9\u1be6-\u1bf3\u1c24-\u1c37\u1c40-\u1c49\u1c50-\u1c59\u1cd0-\u1cd2\u1cd4-\u1ce8\u1ced\u1cf4\u1cf7-\u1cf9\u1dc0-\u1dff\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2cef-\u2cf1\u2d7f\u2de0-\u2dff\u302a-\u302f\u3099\u309a\u30fb\ua620-\ua629\ua66f\ua674-\ua67d\ua69e\ua69f\ua6f0\ua6f1\ua802\ua806\ua80b\ua823-\ua827\ua82c\ua880\ua881\ua8b4-\ua8c5\ua8d0-\ua8d9\ua8e0-\ua8f1\ua8ff-\ua909\ua926-\ua92d\ua947-\ua953\ua980-\ua983\ua9b3-\ua9c0\ua9d0-\ua9d9\ua9e5\ua9f0-\ua9f9\uaa29-\uaa36\uaa43\uaa4c\uaa4d\uaa50-\uaa59\uaa7b-\uaa7d\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uaaeb-\uaaef\uaaf5\uaaf6\uabe3-\uabea\uabec\uabed\uabf0-\uabf9\ufb1e\ufe00-\ufe0f\ufe20-\ufe2f\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f\uff65";
|
||||||
|
const nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
|
||||||
|
const nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
|
||||||
|
nonASCIIidentifierStartChars = nonASCIIidentifierChars = null;
|
||||||
|
const astralIdentifierStartCodes = [0, 11, 2, 25, 2, 18, 2, 1, 2, 14, 3, 13, 35, 122, 70, 52, 268, 28, 4, 48, 48, 31, 14, 29, 6, 37, 11, 29, 3, 35, 5, 7, 2, 4, 43, 157, 19, 35, 5, 35, 5, 39, 9, 51, 13, 10, 2, 14, 2, 6, 2, 1, 2, 10, 2, 14, 2, 6, 2, 1, 4, 51, 13, 310, 10, 21, 11, 7, 25, 5, 2, 41, 2, 8, 70, 5, 3, 0, 2, 43, 2, 1, 4, 0, 3, 22, 11, 22, 10, 30, 66, 18, 2, 1, 11, 21, 11, 25, 7, 25, 39, 55, 7, 1, 65, 0, 16, 3, 2, 2, 2, 28, 43, 28, 4, 28, 36, 7, 2, 27, 28, 53, 11, 21, 11, 18, 14, 17, 111, 72, 56, 50, 14, 50, 14, 35, 39, 27, 10, 22, 251, 41, 7, 1, 17, 5, 57, 28, 11, 0, 9, 21, 43, 17, 47, 20, 28, 22, 13, 52, 58, 1, 3, 0, 14, 44, 33, 24, 27, 35, 30, 0, 3, 0, 9, 34, 4, 0, 13, 47, 15, 3, 22, 0, 2, 0, 36, 17, 2, 24, 20, 1, 64, 6, 2, 0, 2, 3, 2, 14, 2, 9, 8, 46, 39, 7, 3, 1, 3, 21, 2, 6, 2, 1, 2, 4, 4, 0, 19, 0, 13, 4, 31, 9, 2, 0, 3, 0, 2, 37, 2, 0, 26, 0, 2, 0, 45, 52, 19, 3, 21, 2, 31, 47, 21, 1, 2, 0, 185, 46, 42, 3, 37, 47, 21, 0, 60, 42, 14, 0, 72, 26, 38, 6, 186, 43, 117, 63, 32, 7, 3, 0, 3, 7, 2, 1, 2, 23, 16, 0, 2, 0, 95, 7, 3, 38, 17, 0, 2, 0, 29, 0, 11, 39, 8, 0, 22, 0, 12, 45, 20, 0, 19, 72, 200, 32, 32, 8, 2, 36, 18, 0, 50, 29, 113, 6, 2, 1, 2, 37, 22, 0, 26, 5, 2, 1, 2, 31, 15, 0, 24, 43, 261, 18, 16, 0, 2, 12, 2, 33, 125, 0, 80, 921, 103, 110, 18, 195, 2637, 96, 16, 1071, 18, 5, 26, 3994, 6, 582, 6842, 29, 1763, 568, 8, 30, 18, 78, 18, 29, 19, 47, 17, 3, 32, 20, 6, 18, 433, 44, 212, 63, 33, 24, 3, 24, 45, 74, 6, 0, 67, 12, 65, 1, 2, 0, 15, 4, 10, 7381, 42, 31, 98, 114, 8702, 3, 2, 6, 2, 1, 2, 290, 16, 0, 30, 2, 3, 0, 15, 3, 9, 395, 2309, 106, 6, 12, 4, 8, 8, 9, 5991, 84, 2, 70, 2, 1, 3, 0, 3, 1, 3, 3, 2, 11, 2, 0, 2, 6, 2, 64, 2, 3, 3, 7, 2, 6, 2, 27, 2, 3, 2, 4, 2, 0, 4, 6, 2, 339, 3, 24, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 30, 2, 24, 2, 7, 1845, 30, 7, 5, 262, 61, 147, 44, 11, 6, 17, 0, 322, 29, 19, 43, 485, 27, 229, 29, 3, 0, 208, 30, 2, 2, 2, 1, 2, 6, 3, 4, 10, 1, 225, 6, 2, 3, 2, 1, 2, 14, 2, 196, 60, 67, 8, 0, 1205, 3, 2, 26, 2, 1, 2, 0, 3, 0, 2, 9, 2, 3, 2, 0, 2, 0, 7, 0, 5, 0, 2, 0, 2, 0, 2, 2, 2, 1, 2, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 1, 2, 0, 3, 3, 2, 6, 2, 3, 2, 3, 2, 0, 2, 9, 2, 16, 6, 2, 2, 4, 2, 16, 4421, 42719, 33, 4381, 3, 5773, 3, 7472, 16, 621, 2467, 541, 1507, 4938, 6, 8489];
|
||||||
|
const astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 574, 3, 9, 9, 7, 9, 32, 4, 318, 1, 78, 5, 71, 10, 50, 3, 123, 2, 54, 14, 32, 10, 3, 1, 11, 3, 46, 10, 8, 0, 46, 9, 7, 2, 37, 13, 2, 9, 6, 1, 45, 0, 13, 2, 49, 13, 9, 3, 2, 11, 83, 11, 7, 0, 3, 0, 158, 11, 6, 9, 7, 3, 56, 1, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 68, 8, 2, 0, 3, 0, 2, 3, 2, 4, 2, 0, 15, 1, 83, 17, 10, 9, 5, 0, 82, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 7, 19, 58, 14, 5, 9, 243, 14, 166, 9, 71, 5, 2, 1, 3, 3, 2, 0, 2, 1, 13, 9, 120, 6, 3, 6, 4, 0, 29, 9, 41, 6, 2, 3, 9, 0, 10, 10, 47, 15, 199, 7, 137, 9, 54, 7, 2, 7, 17, 9, 57, 21, 2, 13, 123, 5, 4, 0, 2, 1, 2, 6, 2, 0, 9, 9, 49, 4, 2, 1, 2, 4, 9, 9, 55, 9, 266, 3, 10, 1, 2, 0, 49, 6, 4, 4, 14, 10, 5350, 0, 7, 14, 11465, 27, 2343, 9, 87, 9, 39, 4, 60, 6, 26, 9, 535, 9, 470, 0, 2, 54, 8, 3, 82, 0, 12, 1, 19628, 1, 4178, 9, 519, 45, 3, 22, 543, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 513, 54, 5, 49, 9, 0, 15, 0, 23, 4, 2, 14, 1361, 6, 2, 16, 3, 6, 2, 1, 2, 4, 101, 0, 161, 6, 10, 9, 357, 0, 62, 13, 499, 13, 245, 1, 2, 9, 233, 0, 3, 0, 8, 1, 6, 0, 475, 6, 110, 6, 6, 9, 4759, 9, 787719, 239];
|
||||||
|
function isInAstralSet(code, set) {
|
||||||
|
let pos = 0x10000;
|
||||||
|
for (let i = 0, length = set.length; i < length; i += 2) {
|
||||||
|
pos += set[i];
|
||||||
|
if (pos > code) return false;
|
||||||
|
pos += set[i + 1];
|
||||||
|
if (pos >= code) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function isIdentifierStart(code) {
|
||||||
|
if (code < 65) return code === 36;
|
||||||
|
if (code <= 90) return true;
|
||||||
|
if (code < 97) return code === 95;
|
||||||
|
if (code <= 122) return true;
|
||||||
|
if (code <= 0xffff) {
|
||||||
|
return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
|
||||||
|
}
|
||||||
|
return isInAstralSet(code, astralIdentifierStartCodes);
|
||||||
|
}
|
||||||
|
function isIdentifierChar(code) {
|
||||||
|
if (code < 48) return code === 36;
|
||||||
|
if (code < 58) return true;
|
||||||
|
if (code < 65) return false;
|
||||||
|
if (code <= 90) return true;
|
||||||
|
if (code < 97) return code === 95;
|
||||||
|
if (code <= 122) return true;
|
||||||
|
if (code <= 0xffff) {
|
||||||
|
return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
|
||||||
|
}
|
||||||
|
return isInAstralSet(code, astralIdentifierStartCodes) || isInAstralSet(code, astralIdentifierCodes);
|
||||||
|
}
|
||||||
|
function isIdentifierName(name) {
|
||||||
|
let isFirst = true;
|
||||||
|
for (let i = 0; i < name.length; i++) {
|
||||||
|
let cp = name.charCodeAt(i);
|
||||||
|
if ((cp & 0xfc00) === 0xd800 && i + 1 < name.length) {
|
||||||
|
const trail = name.charCodeAt(++i);
|
||||||
|
if ((trail & 0xfc00) === 0xdc00) {
|
||||||
|
cp = 0x10000 + ((cp & 0x3ff) << 10) + (trail & 0x3ff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isFirst) {
|
||||||
|
isFirst = false;
|
||||||
|
if (!isIdentifierStart(cp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!isIdentifierChar(cp)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !isFirst;
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=identifier.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+57
@@ -0,0 +1,57 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "isIdentifierChar", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _identifier.isIdentifierChar;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "isIdentifierName", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _identifier.isIdentifierName;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "isIdentifierStart", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _identifier.isIdentifierStart;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "isKeyword", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _keyword.isKeyword;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "isReservedWord", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _keyword.isReservedWord;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "isStrictBindOnlyReservedWord", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _keyword.isStrictBindOnlyReservedWord;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "isStrictBindReservedWord", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _keyword.isStrictBindReservedWord;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Object.defineProperty(exports, "isStrictReservedWord", {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _keyword.isStrictReservedWord;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var _identifier = require("./identifier.js");
|
||||||
|
var _keyword = require("./keyword.js");
|
||||||
|
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["_identifier","require","_keyword"],"sources":["../src/index.ts"],"sourcesContent":["export {\n isIdentifierName,\n isIdentifierChar,\n isIdentifierStart,\n} from \"./identifier.ts\";\nexport {\n isReservedWord,\n isStrictBindOnlyReservedWord,\n isStrictBindReservedWord,\n isStrictReservedWord,\n isKeyword,\n} from \"./keyword.ts\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,WAAA,GAAAC,OAAA;AAKA,IAAAC,QAAA,GAAAD,OAAA","ignoreList":[]}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.isKeyword = isKeyword;
|
||||||
|
exports.isReservedWord = isReservedWord;
|
||||||
|
exports.isStrictBindOnlyReservedWord = isStrictBindOnlyReservedWord;
|
||||||
|
exports.isStrictBindReservedWord = isStrictBindReservedWord;
|
||||||
|
exports.isStrictReservedWord = isStrictReservedWord;
|
||||||
|
const reservedWords = {
|
||||||
|
keyword: ["break", "case", "catch", "continue", "debugger", "default", "do", "else", "finally", "for", "function", "if", "return", "switch", "throw", "try", "var", "const", "while", "with", "new", "this", "super", "class", "extends", "export", "import", "null", "true", "false", "in", "instanceof", "typeof", "void", "delete"],
|
||||||
|
strict: ["implements", "interface", "let", "package", "private", "protected", "public", "static", "yield"],
|
||||||
|
strictBind: ["eval", "arguments"]
|
||||||
|
};
|
||||||
|
const keywords = new Set(reservedWords.keyword);
|
||||||
|
const reservedWordsStrictSet = new Set(reservedWords.strict);
|
||||||
|
const reservedWordsStrictBindSet = new Set(reservedWords.strictBind);
|
||||||
|
function isReservedWord(word, inModule) {
|
||||||
|
return inModule && word === "await" || word === "enum";
|
||||||
|
}
|
||||||
|
function isStrictReservedWord(word, inModule) {
|
||||||
|
return isReservedWord(word, inModule) || reservedWordsStrictSet.has(word);
|
||||||
|
}
|
||||||
|
function isStrictBindOnlyReservedWord(word) {
|
||||||
|
return reservedWordsStrictBindSet.has(word);
|
||||||
|
}
|
||||||
|
function isStrictBindReservedWord(word, inModule) {
|
||||||
|
return isStrictReservedWord(word, inModule) || isStrictBindOnlyReservedWord(word);
|
||||||
|
}
|
||||||
|
function isKeyword(word) {
|
||||||
|
return keywords.has(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=keyword.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["reservedWords","keyword","strict","strictBind","keywords","Set","reservedWordsStrictSet","reservedWordsStrictBindSet","isReservedWord","word","inModule","isStrictReservedWord","has","isStrictBindOnlyReservedWord","isStrictBindReservedWord","isKeyword"],"sources":["../src/keyword.ts"],"sourcesContent":["const reservedWords = {\n keyword: [\n \"break\",\n \"case\",\n \"catch\",\n \"continue\",\n \"debugger\",\n \"default\",\n \"do\",\n \"else\",\n \"finally\",\n \"for\",\n \"function\",\n \"if\",\n \"return\",\n \"switch\",\n \"throw\",\n \"try\",\n \"var\",\n \"const\",\n \"while\",\n \"with\",\n \"new\",\n \"this\",\n \"super\",\n \"class\",\n \"extends\",\n \"export\",\n \"import\",\n \"null\",\n \"true\",\n \"false\",\n \"in\",\n \"instanceof\",\n \"typeof\",\n \"void\",\n \"delete\",\n ],\n strict: [\n \"implements\",\n \"interface\",\n \"let\",\n \"package\",\n \"private\",\n \"protected\",\n \"public\",\n \"static\",\n \"yield\",\n ],\n strictBind: [\"eval\", \"arguments\"],\n};\nconst keywords = new Set(reservedWords.keyword);\nconst reservedWordsStrictSet = new Set(reservedWords.strict);\nconst reservedWordsStrictBindSet = new Set(reservedWords.strictBind);\n\n/**\n * Checks if word is a reserved word in non-strict mode\n */\nexport function isReservedWord(word: string, inModule: boolean): boolean {\n return (inModule && word === \"await\") || word === \"enum\";\n}\n\n/**\n * Checks if word is a reserved word in non-binding strict mode\n *\n * Includes non-strict reserved words\n */\nexport function isStrictReservedWord(word: string, inModule: boolean): boolean {\n return isReservedWord(word, inModule) || reservedWordsStrictSet.has(word);\n}\n\n/**\n * Checks if word is a reserved word in binding strict mode, but it is allowed as\n * a normal identifier.\n */\nexport function isStrictBindOnlyReservedWord(word: string): boolean {\n return reservedWordsStrictBindSet.has(word);\n}\n\n/**\n * Checks if word is a reserved word in binding strict mode\n *\n * Includes non-strict reserved words and non-binding strict reserved words\n */\nexport function isStrictBindReservedWord(\n word: string,\n inModule: boolean,\n): boolean {\n return (\n isStrictReservedWord(word, inModule) || isStrictBindOnlyReservedWord(word)\n );\n}\n\nexport function isKeyword(word: string): boolean {\n return keywords.has(word);\n}\n"],"mappings":";;;;;;;;;;AAAA,MAAMA,aAAa,GAAG;EACpBC,OAAO,EAAE,CACP,OAAO,EACP,MAAM,EACN,OAAO,EACP,UAAU,EACV,UAAU,EACV,SAAS,EACT,IAAI,EACJ,MAAM,EACN,SAAS,EACT,KAAK,EACL,UAAU,EACV,IAAI,EACJ,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,KAAK,EACL,KAAK,EACL,OAAO,EACP,OAAO,EACP,MAAM,EACN,KAAK,EACL,MAAM,EACN,OAAO,EACP,OAAO,EACP,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,MAAM,EACN,MAAM,EACN,OAAO,EACP,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,MAAM,EACN,QAAQ,CACT;EACDC,MAAM,EAAE,CACN,YAAY,EACZ,WAAW,EACX,KAAK,EACL,SAAS,EACT,SAAS,EACT,WAAW,EACX,QAAQ,EACR,QAAQ,EACR,OAAO,CACR;EACDC,UAAU,EAAE,CAAC,MAAM,EAAE,WAAW;AAClC,CAAC;AACD,MAAMC,QAAQ,GAAG,IAAIC,GAAG,CAACL,aAAa,CAACC,OAAO,CAAC;AAC/C,MAAMK,sBAAsB,GAAG,IAAID,GAAG,CAACL,aAAa,CAACE,MAAM,CAAC;AAC5D,MAAMK,0BAA0B,GAAG,IAAIF,GAAG,CAACL,aAAa,CAACG,UAAU,CAAC;AAK7D,SAASK,cAAcA,CAACC,IAAY,EAAEC,QAAiB,EAAW;EACvE,OAAQA,QAAQ,IAAID,IAAI,KAAK,OAAO,IAAKA,IAAI,KAAK,MAAM;AAC1D;AAOO,SAASE,oBAAoBA,CAACF,IAAY,EAAEC,QAAiB,EAAW;EAC7E,OAAOF,cAAc,CAACC,IAAI,EAAEC,QAAQ,CAAC,IAAIJ,sBAAsB,CAACM,GAAG,CAACH,IAAI,CAAC;AAC3E;AAMO,SAASI,4BAA4BA,CAACJ,IAAY,EAAW;EAClE,OAAOF,0BAA0B,CAACK,GAAG,CAACH,IAAI,CAAC;AAC7C;AAOO,SAASK,wBAAwBA,CACtCL,IAAY,EACZC,QAAiB,EACR;EACT,OACEC,oBAAoB,CAACF,IAAI,EAAEC,QAAQ,CAAC,IAAIG,4BAA4B,CAACJ,IAAI,CAAC;AAE9E;AAEO,SAASM,SAASA,CAACN,IAAY,EAAW;EAC/C,OAAOL,QAAQ,CAACQ,GAAG,CAACH,IAAI,CAAC;AAC3B","ignoreList":[]}
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"name": "@babel/helper-validator-identifier",
|
||||||
|
"version": "7.29.7",
|
||||||
|
"description": "Validate identifier/keywords name",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/babel/babel.git",
|
||||||
|
"directory": "packages/babel-helper-validator-identifier"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"main": "./lib/index.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./lib/index.d.ts",
|
||||||
|
"default": "./lib/index.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@unicode/unicode-17.0.0": "^1.6.10",
|
||||||
|
"charcodes": "^0.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.9.0"
|
||||||
|
},
|
||||||
|
"author": "The Babel Team (https://babel.dev/team)",
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
||||||
+1073
File diff suppressed because it is too large
Load Diff
+19
@@ -0,0 +1,19 @@
|
|||||||
|
Copyright (C) 2012-2014 by various contributors (see AUTHORS)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
# @babel/parser
|
||||||
|
|
||||||
|
> A JavaScript parser
|
||||||
|
|
||||||
|
See our website [@babel/parser](https://babeljs.io/docs/babel-parser) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20parser%22+is%3Aopen) associated with this package.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Using npm:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install --save-dev @babel/parser
|
||||||
|
```
|
||||||
|
|
||||||
|
or using yarn:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn add @babel/parser --dev
|
||||||
|
```
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/* eslint-disable no-var, unicorn/prefer-node-protocol */
|
||||||
|
|
||||||
|
var parser = require("..");
|
||||||
|
var fs = require("fs");
|
||||||
|
|
||||||
|
var filename = process.argv[2];
|
||||||
|
if (!filename) {
|
||||||
|
console.error("no filename specified");
|
||||||
|
} else {
|
||||||
|
var file = fs.readFileSync(filename, "utf8");
|
||||||
|
var ast = parser.parse(file);
|
||||||
|
|
||||||
|
console.log(JSON.stringify(ast, null, " "));
|
||||||
|
}
|
||||||
+14599
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+50
@@ -0,0 +1,50 @@
|
|||||||
|
{
|
||||||
|
"name": "@babel/parser",
|
||||||
|
"version": "7.29.7",
|
||||||
|
"description": "A JavaScript parser",
|
||||||
|
"author": "The Babel Team (https://babel.dev/team)",
|
||||||
|
"homepage": "https://babel.dev/docs/en/next/babel-parser",
|
||||||
|
"bugs": "https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A+parser+%28babylon%29%22+is%3Aopen",
|
||||||
|
"license": "MIT",
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"babel",
|
||||||
|
"javascript",
|
||||||
|
"parser",
|
||||||
|
"tc39",
|
||||||
|
"ecmascript",
|
||||||
|
"@babel/parser"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/babel/babel.git",
|
||||||
|
"directory": "packages/babel-parser"
|
||||||
|
},
|
||||||
|
"main": "./lib/index.js",
|
||||||
|
"types": "./typings/babel-parser.d.ts",
|
||||||
|
"files": [
|
||||||
|
"bin",
|
||||||
|
"lib",
|
||||||
|
"typings/babel-parser.d.ts",
|
||||||
|
"index.cjs"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0.0"
|
||||||
|
},
|
||||||
|
"# dependencies": "This package doesn't actually have runtime dependencies. @babel/types is only needed for type definitions.",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/types": "^7.29.7"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/code-frame": "^7.29.7",
|
||||||
|
"@babel/helper-check-duplicate-nodes": "^7.29.7",
|
||||||
|
"@babel/helper-fixtures": "^7.29.7",
|
||||||
|
"@babel/helper-string-parser": "^7.29.7",
|
||||||
|
"@babel/helper-validator-identifier": "^7.29.7",
|
||||||
|
"charcodes": "^0.2.0"
|
||||||
|
},
|
||||||
|
"bin": "./bin/babel-parser.js",
|
||||||
|
"type": "commonjs"
|
||||||
|
}
|
||||||
+262
@@ -0,0 +1,262 @@
|
|||||||
|
// This file is auto-generated! Do not modify it directly.
|
||||||
|
// Run `yarn gulp bundle-dts` to re-generate it.
|
||||||
|
/* eslint-disable @typescript-eslint/consistent-type-imports, @typescript-eslint/no-redundant-type-constituents */
|
||||||
|
import { File, Expression } from '@babel/types';
|
||||||
|
|
||||||
|
declare class Position {
|
||||||
|
line: number;
|
||||||
|
column: number;
|
||||||
|
index: number;
|
||||||
|
constructor(line: number, col: number, index: number);
|
||||||
|
}
|
||||||
|
|
||||||
|
type SyntaxPlugin = "flow" | "typescript" | "jsx" | "pipelineOperator" | "placeholders";
|
||||||
|
type ParseErrorCode = "BABEL_PARSER_SYNTAX_ERROR" | "BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED";
|
||||||
|
interface ParseErrorSpecification<ErrorDetails> {
|
||||||
|
code: ParseErrorCode;
|
||||||
|
reasonCode: string;
|
||||||
|
syntaxPlugin?: SyntaxPlugin;
|
||||||
|
missingPlugin?: string | string[];
|
||||||
|
loc: Position;
|
||||||
|
details: ErrorDetails;
|
||||||
|
pos: number;
|
||||||
|
}
|
||||||
|
type ParseError$1<ErrorDetails> = SyntaxError & ParseErrorSpecification<ErrorDetails>;
|
||||||
|
|
||||||
|
type BABEL_8_BREAKING = false;
|
||||||
|
type IF_BABEL_7<V> = false extends BABEL_8_BREAKING ? V : never;
|
||||||
|
|
||||||
|
type Plugin$1 =
|
||||||
|
| "asyncDoExpressions"
|
||||||
|
| IF_BABEL_7<"asyncGenerators">
|
||||||
|
| IF_BABEL_7<"bigInt">
|
||||||
|
| IF_BABEL_7<"classPrivateMethods">
|
||||||
|
| IF_BABEL_7<"classPrivateProperties">
|
||||||
|
| IF_BABEL_7<"classProperties">
|
||||||
|
| IF_BABEL_7<"classStaticBlock">
|
||||||
|
| IF_BABEL_7<"decimal">
|
||||||
|
| "decorators-legacy"
|
||||||
|
| "deferredImportEvaluation"
|
||||||
|
| "decoratorAutoAccessors"
|
||||||
|
| "destructuringPrivate"
|
||||||
|
| IF_BABEL_7<"deprecatedImportAssert">
|
||||||
|
| "doExpressions"
|
||||||
|
| IF_BABEL_7<"dynamicImport">
|
||||||
|
| IF_BABEL_7<"explicitResourceManagement">
|
||||||
|
| "exportDefaultFrom"
|
||||||
|
| IF_BABEL_7<"exportNamespaceFrom">
|
||||||
|
| "flow"
|
||||||
|
| "flowComments"
|
||||||
|
| "functionBind"
|
||||||
|
| "functionSent"
|
||||||
|
| "importMeta"
|
||||||
|
| "jsx"
|
||||||
|
| IF_BABEL_7<"jsonStrings">
|
||||||
|
| IF_BABEL_7<"logicalAssignment">
|
||||||
|
| IF_BABEL_7<"importAssertions">
|
||||||
|
| IF_BABEL_7<"importReflection">
|
||||||
|
| "moduleBlocks"
|
||||||
|
| IF_BABEL_7<"moduleStringNames">
|
||||||
|
| IF_BABEL_7<"nullishCoalescingOperator">
|
||||||
|
| IF_BABEL_7<"numericSeparator">
|
||||||
|
| IF_BABEL_7<"objectRestSpread">
|
||||||
|
| IF_BABEL_7<"optionalCatchBinding">
|
||||||
|
| IF_BABEL_7<"optionalChaining">
|
||||||
|
| "partialApplication"
|
||||||
|
| "placeholders"
|
||||||
|
| IF_BABEL_7<"privateIn">
|
||||||
|
| IF_BABEL_7<"regexpUnicodeSets">
|
||||||
|
| "sourcePhaseImports"
|
||||||
|
| "throwExpressions"
|
||||||
|
| IF_BABEL_7<"topLevelAwait">
|
||||||
|
| "v8intrinsic"
|
||||||
|
| ParserPluginWithOptions[0];
|
||||||
|
|
||||||
|
type ParserPluginWithOptions =
|
||||||
|
| ["decorators", DecoratorsPluginOptions]
|
||||||
|
| ["discardBinding", { syntaxType: "void" }]
|
||||||
|
| ["estree", { classFeatures?: boolean }]
|
||||||
|
| IF_BABEL_7<["importAttributes", { deprecatedAssertSyntax: boolean }]>
|
||||||
|
| IF_BABEL_7<["moduleAttributes", { version: "may-2020" }]>
|
||||||
|
| ["optionalChainingAssign", { version: "2023-07" }]
|
||||||
|
| ["pipelineOperator", PipelineOperatorPluginOptions]
|
||||||
|
| ["recordAndTuple", RecordAndTuplePluginOptions]
|
||||||
|
| ["flow", FlowPluginOptions]
|
||||||
|
| ["typescript", TypeScriptPluginOptions];
|
||||||
|
|
||||||
|
type PluginConfig = Plugin$1 | ParserPluginWithOptions;
|
||||||
|
|
||||||
|
interface DecoratorsPluginOptions {
|
||||||
|
decoratorsBeforeExport?: boolean;
|
||||||
|
allowCallParenthesized?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PipelineOperatorPluginOptions {
|
||||||
|
proposal: BABEL_8_BREAKING extends false
|
||||||
|
? "minimal" | "fsharp" | "hack" | "smart"
|
||||||
|
: "fsharp" | "hack";
|
||||||
|
topicToken?: "%" | "#" | "@@" | "^^" | "^";
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RecordAndTuplePluginOptions {
|
||||||
|
syntaxType: "bar" | "hash";
|
||||||
|
}
|
||||||
|
|
||||||
|
type FlowPluginOptions = BABEL_8_BREAKING extends true
|
||||||
|
? {
|
||||||
|
all?: boolean;
|
||||||
|
enums?: boolean;
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
all?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface TypeScriptPluginOptions {
|
||||||
|
dts?: boolean;
|
||||||
|
disallowAmbiguousJSXLike?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Plugin = PluginConfig;
|
||||||
|
|
||||||
|
type SourceType = "script" | "commonjs" | "module" | "unambiguous";
|
||||||
|
interface Options {
|
||||||
|
/**
|
||||||
|
* By default, import and export declarations can only appear at a program's top level.
|
||||||
|
* Setting this option to true allows them anywhere where a statement is allowed.
|
||||||
|
*/
|
||||||
|
allowImportExportEverywhere?: boolean;
|
||||||
|
/**
|
||||||
|
* By default, await use is not allowed outside of an async function.
|
||||||
|
* Set this to true to accept such code.
|
||||||
|
*/
|
||||||
|
allowAwaitOutsideFunction?: boolean;
|
||||||
|
/**
|
||||||
|
* By default, a return statement at the top level raises an error.
|
||||||
|
* Set this to true to accept such code.
|
||||||
|
*/
|
||||||
|
allowReturnOutsideFunction?: boolean;
|
||||||
|
/**
|
||||||
|
* By default, new.target use is not allowed outside of a function or class.
|
||||||
|
* Set this to true to accept such code.
|
||||||
|
*/
|
||||||
|
allowNewTargetOutsideFunction?: boolean;
|
||||||
|
/**
|
||||||
|
* By default, super calls are not allowed outside of a method.
|
||||||
|
* Set this to true to accept such code.
|
||||||
|
*/
|
||||||
|
allowSuperOutsideMethod?: boolean;
|
||||||
|
/**
|
||||||
|
* By default, exported identifiers must refer to a declared variable.
|
||||||
|
* Set this to true to allow export statements to reference undeclared variables.
|
||||||
|
*/
|
||||||
|
allowUndeclaredExports?: boolean;
|
||||||
|
/**
|
||||||
|
* By default, yield use is not allowed outside of a generator function.
|
||||||
|
* Set this to true to accept such code.
|
||||||
|
*/
|
||||||
|
allowYieldOutsideFunction?: boolean;
|
||||||
|
/**
|
||||||
|
* By default, Babel parser JavaScript code according to Annex B syntax.
|
||||||
|
* Set this to `false` to disable such behavior.
|
||||||
|
*/
|
||||||
|
annexB?: boolean;
|
||||||
|
/**
|
||||||
|
* By default, Babel attaches comments to adjacent AST nodes.
|
||||||
|
* When this option is set to false, comments are not attached.
|
||||||
|
* It can provide up to 30% performance improvement when the input code has many comments.
|
||||||
|
* @babel/eslint-parser will set it for you.
|
||||||
|
* It is not recommended to use attachComment: false with Babel transform,
|
||||||
|
* as doing so removes all the comments in output code, and renders annotations such as
|
||||||
|
* /* istanbul ignore next *\/ nonfunctional.
|
||||||
|
*/
|
||||||
|
attachComment?: boolean;
|
||||||
|
/**
|
||||||
|
* By default, Babel always throws an error when it finds some invalid code.
|
||||||
|
* When this option is set to true, it will store the parsing error and
|
||||||
|
* try to continue parsing the invalid input file.
|
||||||
|
*/
|
||||||
|
errorRecovery?: boolean;
|
||||||
|
/**
|
||||||
|
* Indicate the mode the code should be parsed in.
|
||||||
|
* Can be one of "script", "commonjs", "module", or "unambiguous". Defaults to "script".
|
||||||
|
* "unambiguous" will make @babel/parser attempt to guess, based on the presence
|
||||||
|
* of ES6 import or export statements.
|
||||||
|
* Files with ES6 imports and exports are considered "module" and are otherwise "script".
|
||||||
|
*
|
||||||
|
* Use "commonjs" to parse code that is intended to be run in a CommonJS environment such as Node.js.
|
||||||
|
*/
|
||||||
|
sourceType?: SourceType;
|
||||||
|
/**
|
||||||
|
* Correlate output AST nodes with their source filename.
|
||||||
|
* Useful when generating code and source maps from the ASTs of multiple input files.
|
||||||
|
*/
|
||||||
|
sourceFilename?: string;
|
||||||
|
/**
|
||||||
|
* By default, all source indexes start from 0.
|
||||||
|
* You can provide a start index to alternatively start with.
|
||||||
|
* Useful for integration with other source tools.
|
||||||
|
*/
|
||||||
|
startIndex?: number;
|
||||||
|
/**
|
||||||
|
* By default, the first line of code parsed is treated as line 1.
|
||||||
|
* You can provide a line number to alternatively start with.
|
||||||
|
* Useful for integration with other source tools.
|
||||||
|
*/
|
||||||
|
startLine?: number;
|
||||||
|
/**
|
||||||
|
* By default, the parsed code is treated as if it starts from line 1, column 0.
|
||||||
|
* You can provide a column number to alternatively start with.
|
||||||
|
* Useful for integration with other source tools.
|
||||||
|
*/
|
||||||
|
startColumn?: number;
|
||||||
|
/**
|
||||||
|
* Array containing the plugins that you want to enable.
|
||||||
|
*/
|
||||||
|
plugins?: Plugin[];
|
||||||
|
/**
|
||||||
|
* Should the parser work in strict mode.
|
||||||
|
* Defaults to true if sourceType === 'module'. Otherwise, false.
|
||||||
|
*/
|
||||||
|
strictMode?: boolean;
|
||||||
|
/**
|
||||||
|
* Adds a ranges property to each node: [node.start, node.end]
|
||||||
|
*/
|
||||||
|
ranges?: boolean;
|
||||||
|
/**
|
||||||
|
* Adds all parsed tokens to a tokens property on the File node.
|
||||||
|
*/
|
||||||
|
tokens?: boolean;
|
||||||
|
/**
|
||||||
|
* By default, the parser adds information about parentheses by setting
|
||||||
|
* `extra.parenthesized` to `true` as needed.
|
||||||
|
* When this option is `true` the parser creates `ParenthesizedExpression`
|
||||||
|
* AST nodes instead of using the `extra` property.
|
||||||
|
*/
|
||||||
|
createParenthesizedExpressions?: boolean;
|
||||||
|
/**
|
||||||
|
* The default is false in Babel 7 and true in Babel 8
|
||||||
|
* Set this to true to parse it as an `ImportExpression` node.
|
||||||
|
* Otherwise `import(foo)` is parsed as `CallExpression(Import, [Identifier(foo)])`.
|
||||||
|
*/
|
||||||
|
createImportExpressions?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParserOptions = Partial<Options>;
|
||||||
|
type ParseError = ParseError$1<object>;
|
||||||
|
type ParseResult<Result extends File | Expression = File> = Result & {
|
||||||
|
comments: File["comments"];
|
||||||
|
errors: null | ParseError[];
|
||||||
|
tokens?: File["tokens"];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Parse the provided code as an entire ECMAScript program.
|
||||||
|
*/
|
||||||
|
declare function parse(input: string, options?: ParserOptions): ParseResult<File>;
|
||||||
|
declare function parseExpression(input: string, options?: ParserOptions): ParseResult<Expression>;
|
||||||
|
|
||||||
|
declare const tokTypes: {
|
||||||
|
// todo(flow->ts) real token type
|
||||||
|
[name: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { DecoratorsPluginOptions, FlowPluginOptions, ParseError, ParseResult, ParserOptions, PluginConfig as ParserPlugin, ParserPluginWithOptions, PipelineOperatorPluginOptions, RecordAndTuplePluginOptions, TypeScriptPluginOptions, parse, parseExpression, tokTypes };
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2014-present Sebastian McKenzie and other contributors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
# @babel/types
|
||||||
|
|
||||||
|
> Babel Types is a Lodash-esque utility library for AST nodes
|
||||||
|
|
||||||
|
See our website [@babel/types](https://babeljs.io/docs/babel-types) for more information or the [issues](https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22pkg%3A%20types%22+is%3Aopen) associated with this package.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Using npm:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm install --save-dev @babel/types
|
||||||
|
```
|
||||||
|
|
||||||
|
or using yarn:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn add @babel/types --dev
|
||||||
|
```
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = assertNode;
|
||||||
|
var _isNode = require("../validators/isNode.js");
|
||||||
|
function assertNode(node) {
|
||||||
|
if (!(0, _isNode.default)(node)) {
|
||||||
|
var _node$type;
|
||||||
|
const type = (_node$type = node == null ? void 0 : node.type) != null ? _node$type : JSON.stringify(node);
|
||||||
|
throw new TypeError(`Not a valid node of type "${type}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=assertNode.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["_isNode","require","assertNode","node","isNode","_node$type","type","JSON","stringify","TypeError"],"sources":["../../src/asserts/assertNode.ts"],"sourcesContent":["import isNode from \"../validators/isNode.ts\";\nimport type * as t from \"../index.ts\";\n\nexport default function assertNode(node?: any): asserts node is t.Node {\n if (!isNode(node)) {\n const type = node?.type ?? JSON.stringify(node);\n throw new TypeError(`Not a valid node of type \"${type}\"`);\n }\n}\n"],"mappings":";;;;;;AAAA,IAAAA,OAAA,GAAAC,OAAA;AAGe,SAASC,UAAUA,CAACC,IAAU,EAA0B;EACrE,IAAI,CAAC,IAAAC,eAAM,EAACD,IAAI,CAAC,EAAE;IAAA,IAAAE,UAAA;IACjB,MAAMC,IAAI,IAAAD,UAAA,GAAGF,IAAI,oBAAJA,IAAI,CAAEG,IAAI,YAAAD,UAAA,GAAIE,IAAI,CAACC,SAAS,CAACL,IAAI,CAAC;IAC/C,MAAM,IAAIM,SAAS,CAAC,6BAA6BH,IAAI,GAAG,CAAC;EAC3D;AACF","ignoreList":[]}
|
||||||
+1251
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+3
@@ -0,0 +1,3 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+18
@@ -0,0 +1,18 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = createFlowUnionType;
|
||||||
|
var _index = require("../generated/index.js");
|
||||||
|
var _removeTypeDuplicates = require("../../modifications/flow/removeTypeDuplicates.js");
|
||||||
|
function createFlowUnionType(types) {
|
||||||
|
const flattened = (0, _removeTypeDuplicates.default)(types);
|
||||||
|
if (flattened.length === 1) {
|
||||||
|
return flattened[0];
|
||||||
|
} else {
|
||||||
|
return (0, _index.unionTypeAnnotation)(flattened);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=createFlowUnionType.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["_index","require","_removeTypeDuplicates","createFlowUnionType","types","flattened","removeTypeDuplicates","length","unionTypeAnnotation"],"sources":["../../../src/builders/flow/createFlowUnionType.ts"],"sourcesContent":["import { unionTypeAnnotation } from \"../generated/index.ts\";\nimport removeTypeDuplicates from \"../../modifications/flow/removeTypeDuplicates.ts\";\nimport type * as t from \"../../index.ts\";\n\n/**\n * Takes an array of `types` and flattens them, removing duplicates and\n * returns a `UnionTypeAnnotation` node containing them.\n */\nexport default function createFlowUnionType<T extends t.FlowType>(\n types: [T] | T[],\n): T | t.UnionTypeAnnotation {\n const flattened = removeTypeDuplicates(types);\n\n if (flattened.length === 1) {\n return flattened[0] as T;\n } else {\n return unionTypeAnnotation(flattened);\n }\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,qBAAA,GAAAD,OAAA;AAOe,SAASE,mBAAmBA,CACzCC,KAAgB,EACW;EAC3B,MAAMC,SAAS,GAAG,IAAAC,6BAAoB,EAACF,KAAK,CAAC;EAE7C,IAAIC,SAAS,CAACE,MAAM,KAAK,CAAC,EAAE;IAC1B,OAAOF,SAAS,CAAC,CAAC,CAAC;EACrB,CAAC,MAAM;IACL,OAAO,IAAAG,0BAAmB,EAACH,SAAS,CAAC;EACvC;AACF","ignoreList":[]}
|
||||||
Generated
Vendored
+31
@@ -0,0 +1,31 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = void 0;
|
||||||
|
var _index = require("../generated/index.js");
|
||||||
|
var _default = exports.default = createTypeAnnotationBasedOnTypeof;
|
||||||
|
function createTypeAnnotationBasedOnTypeof(type) {
|
||||||
|
switch (type) {
|
||||||
|
case "string":
|
||||||
|
return (0, _index.stringTypeAnnotation)();
|
||||||
|
case "number":
|
||||||
|
return (0, _index.numberTypeAnnotation)();
|
||||||
|
case "undefined":
|
||||||
|
return (0, _index.voidTypeAnnotation)();
|
||||||
|
case "boolean":
|
||||||
|
return (0, _index.booleanTypeAnnotation)();
|
||||||
|
case "function":
|
||||||
|
return (0, _index.genericTypeAnnotation)((0, _index.identifier)("Function"));
|
||||||
|
case "object":
|
||||||
|
return (0, _index.genericTypeAnnotation)((0, _index.identifier)("Object"));
|
||||||
|
case "symbol":
|
||||||
|
return (0, _index.genericTypeAnnotation)((0, _index.identifier)("Symbol"));
|
||||||
|
case "bigint":
|
||||||
|
return (0, _index.anyTypeAnnotation)();
|
||||||
|
}
|
||||||
|
throw new Error("Invalid typeof value: " + type);
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=createTypeAnnotationBasedOnTypeof.js.map
|
||||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["_index","require","_default","exports","default","createTypeAnnotationBasedOnTypeof","type","stringTypeAnnotation","numberTypeAnnotation","voidTypeAnnotation","booleanTypeAnnotation","genericTypeAnnotation","identifier","anyTypeAnnotation","Error"],"sources":["../../../src/builders/flow/createTypeAnnotationBasedOnTypeof.ts"],"sourcesContent":["import {\n anyTypeAnnotation,\n stringTypeAnnotation,\n numberTypeAnnotation,\n voidTypeAnnotation,\n booleanTypeAnnotation,\n genericTypeAnnotation,\n identifier,\n} from \"../generated/index.ts\";\nimport type * as t from \"../../index.ts\";\n\nexport default createTypeAnnotationBasedOnTypeof as {\n (type: \"string\"): t.StringTypeAnnotation;\n (type: \"number\"): t.NumberTypeAnnotation;\n (type: \"undefined\"): t.VoidTypeAnnotation;\n (type: \"boolean\"): t.BooleanTypeAnnotation;\n (type: \"function\"): t.GenericTypeAnnotation;\n (type: \"object\"): t.GenericTypeAnnotation;\n (type: \"symbol\"): t.GenericTypeAnnotation;\n (type: \"bigint\"): t.AnyTypeAnnotation;\n};\n\n/**\n * Create a type annotation based on typeof expression.\n */\nfunction createTypeAnnotationBasedOnTypeof(type: string): t.FlowType {\n switch (type) {\n case \"string\":\n return stringTypeAnnotation();\n case \"number\":\n return numberTypeAnnotation();\n case \"undefined\":\n return voidTypeAnnotation();\n case \"boolean\":\n return booleanTypeAnnotation();\n case \"function\":\n return genericTypeAnnotation(identifier(\"Function\"));\n case \"object\":\n return genericTypeAnnotation(identifier(\"Object\"));\n case \"symbol\":\n return genericTypeAnnotation(identifier(\"Symbol\"));\n case \"bigint\":\n // todo: use BigInt annotation when Flow supports BigInt\n // https://github.com/facebook/flow/issues/6639\n return anyTypeAnnotation();\n }\n throw new Error(\"Invalid typeof value: \" + type);\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAQ+B,IAAAC,QAAA,GAAAC,OAAA,CAAAC,OAAA,GAGhBC,iCAAiC;AAchD,SAASA,iCAAiCA,CAACC,IAAY,EAAc;EACnE,QAAQA,IAAI;IACV,KAAK,QAAQ;MACX,OAAO,IAAAC,2BAAoB,EAAC,CAAC;IAC/B,KAAK,QAAQ;MACX,OAAO,IAAAC,2BAAoB,EAAC,CAAC;IAC/B,KAAK,WAAW;MACd,OAAO,IAAAC,yBAAkB,EAAC,CAAC;IAC7B,KAAK,SAAS;MACZ,OAAO,IAAAC,4BAAqB,EAAC,CAAC;IAChC,KAAK,UAAU;MACb,OAAO,IAAAC,4BAAqB,EAAC,IAAAC,iBAAU,EAAC,UAAU,CAAC,CAAC;IACtD,KAAK,QAAQ;MACX,OAAO,IAAAD,4BAAqB,EAAC,IAAAC,iBAAU,EAAC,QAAQ,CAAC,CAAC;IACpD,KAAK,QAAQ;MACX,OAAO,IAAAD,4BAAqB,EAAC,IAAAC,iBAAU,EAAC,QAAQ,CAAC,CAAC;IACpD,KAAK,QAAQ;MAGX,OAAO,IAAAC,wBAAiB,EAAC,CAAC;EAC9B;EACA,MAAM,IAAIC,KAAK,CAAC,wBAAwB,GAAGR,IAAI,CAAC;AAClD","ignoreList":[]}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
var _lowercase = require("./lowercase.js");
|
||||||
|
Object.keys(_lowercase).forEach(function (key) {
|
||||||
|
if (key === "default" || key === "__esModule") return;
|
||||||
|
if (key in exports && exports[key] === _lowercase[key]) return;
|
||||||
|
Object.defineProperty(exports, key, {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _lowercase[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var _uppercase = require("./uppercase.js");
|
||||||
|
Object.keys(_uppercase).forEach(function (key) {
|
||||||
|
if (key === "default" || key === "__esModule") return;
|
||||||
|
if (key in exports && exports[key] === _uppercase[key]) return;
|
||||||
|
Object.defineProperty(exports, key, {
|
||||||
|
enumerable: true,
|
||||||
|
get: function () {
|
||||||
|
return _uppercase[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+2902
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+272
@@ -0,0 +1,272 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.JSXIdentifier = exports.JSXFragment = exports.JSXExpressionContainer = exports.JSXEmptyExpression = exports.JSXElement = exports.JSXClosingFragment = exports.JSXClosingElement = exports.JSXAttribute = exports.IntersectionTypeAnnotation = exports.InterpreterDirective = exports.InterfaceTypeAnnotation = exports.InterfaceExtends = exports.InterfaceDeclaration = exports.InferredPredicate = exports.IndexedAccessType = exports.ImportSpecifier = exports.ImportNamespaceSpecifier = exports.ImportExpression = exports.ImportDefaultSpecifier = exports.ImportDeclaration = exports.ImportAttribute = exports.Import = exports.IfStatement = exports.Identifier = exports.GenericTypeAnnotation = exports.FunctionTypeParam = exports.FunctionTypeAnnotation = exports.FunctionExpression = exports.FunctionDeclaration = exports.ForStatement = exports.ForOfStatement = exports.ForInStatement = exports.File = exports.ExpressionStatement = exports.ExportSpecifier = exports.ExportNamespaceSpecifier = exports.ExportNamedDeclaration = exports.ExportDefaultSpecifier = exports.ExportDefaultDeclaration = exports.ExportAllDeclaration = exports.ExistsTypeAnnotation = exports.EnumSymbolBody = exports.EnumStringMember = exports.EnumStringBody = exports.EnumNumberMember = exports.EnumNumberBody = exports.EnumDefaultedMember = exports.EnumDeclaration = exports.EnumBooleanMember = exports.EnumBooleanBody = exports.EmptyTypeAnnotation = exports.EmptyStatement = exports.DoWhileStatement = exports.DoExpression = exports.DirectiveLiteral = exports.Directive = exports.Decorator = exports.DeclaredPredicate = exports.DeclareVariable = exports.DeclareTypeAlias = exports.DeclareOpaqueType = exports.DeclareModuleExports = exports.DeclareModule = exports.DeclareInterface = exports.DeclareFunction = exports.DeclareExportDeclaration = exports.DeclareExportAllDeclaration = exports.DeclareClass = exports.DecimalLiteral = exports.DebuggerStatement = exports.ContinueStatement = exports.ConditionalExpression = exports.ClassProperty = exports.ClassPrivateProperty = exports.ClassPrivateMethod = exports.ClassMethod = exports.ClassImplements = exports.ClassExpression = exports.ClassDeclaration = exports.ClassBody = exports.ClassAccessorProperty = exports.CatchClause = exports.CallExpression = exports.BreakStatement = exports.BooleanTypeAnnotation = exports.BooleanLiteralTypeAnnotation = exports.BooleanLiteral = exports.BlockStatement = exports.BindExpression = exports.BinaryExpression = exports.BigIntLiteral = exports.AwaitExpression = exports.AssignmentPattern = exports.AssignmentExpression = exports.ArrowFunctionExpression = exports.ArrayTypeAnnotation = exports.ArrayPattern = exports.ArrayExpression = exports.ArgumentPlaceholder = exports.AnyTypeAnnotation = void 0;
|
||||||
|
exports.TSNumberKeyword = exports.TSNullKeyword = exports.TSNonNullExpression = exports.TSNeverKeyword = exports.TSNamespaceExportDeclaration = exports.TSNamedTupleMember = exports.TSModuleDeclaration = exports.TSModuleBlock = exports.TSMethodSignature = exports.TSMappedType = exports.TSLiteralType = exports.TSIntrinsicKeyword = exports.TSIntersectionType = exports.TSInterfaceDeclaration = exports.TSInterfaceBody = exports.TSInstantiationExpression = exports.TSInferType = exports.TSIndexedAccessType = exports.TSIndexSignature = exports.TSImportType = exports.TSImportEqualsDeclaration = exports.TSFunctionType = exports.TSExternalModuleReference = exports.TSExpressionWithTypeArguments = exports.TSExportAssignment = exports.TSEnumMember = exports.TSEnumDeclaration = exports.TSEnumBody = exports.TSDeclareMethod = exports.TSDeclareFunction = exports.TSConstructorType = exports.TSConstructSignatureDeclaration = exports.TSConditionalType = exports.TSCallSignatureDeclaration = exports.TSBooleanKeyword = exports.TSBigIntKeyword = exports.TSAsExpression = exports.TSArrayType = exports.TSAnyKeyword = exports.SymbolTypeAnnotation = exports.SwitchStatement = exports.SwitchCase = exports.Super = exports.StringTypeAnnotation = exports.StringLiteralTypeAnnotation = exports.StringLiteral = exports.StaticBlock = exports.SpreadProperty = exports.SpreadElement = exports.SequenceExpression = exports.ReturnStatement = exports.RestProperty = exports.RestElement = exports.RegexLiteral = exports.RegExpLiteral = exports.RecordExpression = exports.QualifiedTypeIdentifier = exports.Program = exports.PrivateName = exports.Placeholder = exports.PipelineTopicExpression = exports.PipelinePrimaryTopicReference = exports.PipelineBareFunction = exports.ParenthesizedExpression = exports.OptionalMemberExpression = exports.OptionalIndexedAccessType = exports.OptionalCallExpression = exports.OpaqueType = exports.ObjectTypeSpreadProperty = exports.ObjectTypeProperty = exports.ObjectTypeInternalSlot = exports.ObjectTypeIndexer = exports.ObjectTypeCallProperty = exports.ObjectTypeAnnotation = exports.ObjectProperty = exports.ObjectPattern = exports.ObjectMethod = exports.ObjectExpression = exports.NumericLiteral = exports.NumberTypeAnnotation = exports.NumberLiteralTypeAnnotation = exports.NumberLiteral = exports.NullableTypeAnnotation = exports.NullLiteralTypeAnnotation = exports.NullLiteral = exports.Noop = exports.NewExpression = exports.ModuleExpression = exports.MixedTypeAnnotation = exports.MetaProperty = exports.MemberExpression = exports.LogicalExpression = exports.LabeledStatement = exports.JSXText = exports.JSXSpreadChild = exports.JSXSpreadAttribute = exports.JSXOpeningFragment = exports.JSXOpeningElement = exports.JSXNamespacedName = exports.JSXMemberExpression = void 0;
|
||||||
|
exports.YieldExpression = exports.WithStatement = exports.WhileStatement = exports.VoidTypeAnnotation = exports.VoidPattern = exports.Variance = exports.VariableDeclarator = exports.VariableDeclaration = exports.V8IntrinsicIdentifier = exports.UpdateExpression = exports.UnionTypeAnnotation = exports.UnaryExpression = exports.TypeofTypeAnnotation = exports.TypeParameterInstantiation = exports.TypeParameterDeclaration = exports.TypeParameter = exports.TypeCastExpression = exports.TypeAnnotation = exports.TypeAlias = exports.TupleTypeAnnotation = exports.TupleExpression = exports.TryStatement = exports.TopicReference = exports.ThrowStatement = exports.ThisTypeAnnotation = exports.ThisExpression = exports.TemplateLiteral = exports.TemplateElement = exports.TaggedTemplateExpression = exports.TSVoidKeyword = exports.TSUnknownKeyword = exports.TSUnionType = exports.TSUndefinedKeyword = exports.TSTypeReference = exports.TSTypeQuery = exports.TSTypePredicate = exports.TSTypeParameterInstantiation = exports.TSTypeParameterDeclaration = exports.TSTypeParameter = exports.TSTypeOperator = exports.TSTypeLiteral = exports.TSTypeAssertion = exports.TSTypeAnnotation = exports.TSTypeAliasDeclaration = exports.TSTupleType = exports.TSThisType = exports.TSTemplateLiteralType = exports.TSSymbolKeyword = exports.TSStringKeyword = exports.TSSatisfiesExpression = exports.TSRestType = exports.TSQualifiedName = exports.TSPropertySignature = exports.TSParenthesizedType = exports.TSParameterProperty = exports.TSOptionalType = exports.TSObjectKeyword = void 0;
|
||||||
|
var b = require("./lowercase.js");
|
||||||
|
var _deprecationWarning = require("../../utils/deprecationWarning.js");
|
||||||
|
function alias(lowercase) {
|
||||||
|
return b[lowercase];
|
||||||
|
}
|
||||||
|
const ArrayExpression = exports.ArrayExpression = alias("arrayExpression"),
|
||||||
|
AssignmentExpression = exports.AssignmentExpression = alias("assignmentExpression"),
|
||||||
|
BinaryExpression = exports.BinaryExpression = alias("binaryExpression"),
|
||||||
|
InterpreterDirective = exports.InterpreterDirective = alias("interpreterDirective"),
|
||||||
|
Directive = exports.Directive = alias("directive"),
|
||||||
|
DirectiveLiteral = exports.DirectiveLiteral = alias("directiveLiteral"),
|
||||||
|
BlockStatement = exports.BlockStatement = alias("blockStatement"),
|
||||||
|
BreakStatement = exports.BreakStatement = alias("breakStatement"),
|
||||||
|
CallExpression = exports.CallExpression = alias("callExpression"),
|
||||||
|
CatchClause = exports.CatchClause = alias("catchClause"),
|
||||||
|
ConditionalExpression = exports.ConditionalExpression = alias("conditionalExpression"),
|
||||||
|
ContinueStatement = exports.ContinueStatement = alias("continueStatement"),
|
||||||
|
DebuggerStatement = exports.DebuggerStatement = alias("debuggerStatement"),
|
||||||
|
DoWhileStatement = exports.DoWhileStatement = alias("doWhileStatement"),
|
||||||
|
EmptyStatement = exports.EmptyStatement = alias("emptyStatement"),
|
||||||
|
ExpressionStatement = exports.ExpressionStatement = alias("expressionStatement"),
|
||||||
|
File = exports.File = alias("file"),
|
||||||
|
ForInStatement = exports.ForInStatement = alias("forInStatement"),
|
||||||
|
ForStatement = exports.ForStatement = alias("forStatement"),
|
||||||
|
FunctionDeclaration = exports.FunctionDeclaration = alias("functionDeclaration"),
|
||||||
|
FunctionExpression = exports.FunctionExpression = alias("functionExpression"),
|
||||||
|
Identifier = exports.Identifier = alias("identifier"),
|
||||||
|
IfStatement = exports.IfStatement = alias("ifStatement"),
|
||||||
|
LabeledStatement = exports.LabeledStatement = alias("labeledStatement"),
|
||||||
|
StringLiteral = exports.StringLiteral = alias("stringLiteral"),
|
||||||
|
NumericLiteral = exports.NumericLiteral = alias("numericLiteral"),
|
||||||
|
NullLiteral = exports.NullLiteral = alias("nullLiteral"),
|
||||||
|
BooleanLiteral = exports.BooleanLiteral = alias("booleanLiteral"),
|
||||||
|
RegExpLiteral = exports.RegExpLiteral = alias("regExpLiteral"),
|
||||||
|
LogicalExpression = exports.LogicalExpression = alias("logicalExpression"),
|
||||||
|
MemberExpression = exports.MemberExpression = alias("memberExpression"),
|
||||||
|
NewExpression = exports.NewExpression = alias("newExpression"),
|
||||||
|
Program = exports.Program = alias("program"),
|
||||||
|
ObjectExpression = exports.ObjectExpression = alias("objectExpression"),
|
||||||
|
ObjectMethod = exports.ObjectMethod = alias("objectMethod"),
|
||||||
|
ObjectProperty = exports.ObjectProperty = alias("objectProperty"),
|
||||||
|
RestElement = exports.RestElement = alias("restElement"),
|
||||||
|
ReturnStatement = exports.ReturnStatement = alias("returnStatement"),
|
||||||
|
SequenceExpression = exports.SequenceExpression = alias("sequenceExpression"),
|
||||||
|
ParenthesizedExpression = exports.ParenthesizedExpression = alias("parenthesizedExpression"),
|
||||||
|
SwitchCase = exports.SwitchCase = alias("switchCase"),
|
||||||
|
SwitchStatement = exports.SwitchStatement = alias("switchStatement"),
|
||||||
|
ThisExpression = exports.ThisExpression = alias("thisExpression"),
|
||||||
|
ThrowStatement = exports.ThrowStatement = alias("throwStatement"),
|
||||||
|
TryStatement = exports.TryStatement = alias("tryStatement"),
|
||||||
|
UnaryExpression = exports.UnaryExpression = alias("unaryExpression"),
|
||||||
|
UpdateExpression = exports.UpdateExpression = alias("updateExpression"),
|
||||||
|
VariableDeclaration = exports.VariableDeclaration = alias("variableDeclaration"),
|
||||||
|
VariableDeclarator = exports.VariableDeclarator = alias("variableDeclarator"),
|
||||||
|
WhileStatement = exports.WhileStatement = alias("whileStatement"),
|
||||||
|
WithStatement = exports.WithStatement = alias("withStatement"),
|
||||||
|
AssignmentPattern = exports.AssignmentPattern = alias("assignmentPattern"),
|
||||||
|
ArrayPattern = exports.ArrayPattern = alias("arrayPattern"),
|
||||||
|
ArrowFunctionExpression = exports.ArrowFunctionExpression = alias("arrowFunctionExpression"),
|
||||||
|
ClassBody = exports.ClassBody = alias("classBody"),
|
||||||
|
ClassExpression = exports.ClassExpression = alias("classExpression"),
|
||||||
|
ClassDeclaration = exports.ClassDeclaration = alias("classDeclaration"),
|
||||||
|
ExportAllDeclaration = exports.ExportAllDeclaration = alias("exportAllDeclaration"),
|
||||||
|
ExportDefaultDeclaration = exports.ExportDefaultDeclaration = alias("exportDefaultDeclaration"),
|
||||||
|
ExportNamedDeclaration = exports.ExportNamedDeclaration = alias("exportNamedDeclaration"),
|
||||||
|
ExportSpecifier = exports.ExportSpecifier = alias("exportSpecifier"),
|
||||||
|
ForOfStatement = exports.ForOfStatement = alias("forOfStatement"),
|
||||||
|
ImportDeclaration = exports.ImportDeclaration = alias("importDeclaration"),
|
||||||
|
ImportDefaultSpecifier = exports.ImportDefaultSpecifier = alias("importDefaultSpecifier"),
|
||||||
|
ImportNamespaceSpecifier = exports.ImportNamespaceSpecifier = alias("importNamespaceSpecifier"),
|
||||||
|
ImportSpecifier = exports.ImportSpecifier = alias("importSpecifier"),
|
||||||
|
ImportExpression = exports.ImportExpression = alias("importExpression"),
|
||||||
|
MetaProperty = exports.MetaProperty = alias("metaProperty"),
|
||||||
|
ClassMethod = exports.ClassMethod = alias("classMethod"),
|
||||||
|
ObjectPattern = exports.ObjectPattern = alias("objectPattern"),
|
||||||
|
SpreadElement = exports.SpreadElement = alias("spreadElement"),
|
||||||
|
Super = exports.Super = alias("super"),
|
||||||
|
TaggedTemplateExpression = exports.TaggedTemplateExpression = alias("taggedTemplateExpression"),
|
||||||
|
TemplateElement = exports.TemplateElement = alias("templateElement"),
|
||||||
|
TemplateLiteral = exports.TemplateLiteral = alias("templateLiteral"),
|
||||||
|
YieldExpression = exports.YieldExpression = alias("yieldExpression"),
|
||||||
|
AwaitExpression = exports.AwaitExpression = alias("awaitExpression"),
|
||||||
|
Import = exports.Import = alias("import"),
|
||||||
|
BigIntLiteral = exports.BigIntLiteral = alias("bigIntLiteral"),
|
||||||
|
ExportNamespaceSpecifier = exports.ExportNamespaceSpecifier = alias("exportNamespaceSpecifier"),
|
||||||
|
OptionalMemberExpression = exports.OptionalMemberExpression = alias("optionalMemberExpression"),
|
||||||
|
OptionalCallExpression = exports.OptionalCallExpression = alias("optionalCallExpression"),
|
||||||
|
ClassProperty = exports.ClassProperty = alias("classProperty"),
|
||||||
|
ClassAccessorProperty = exports.ClassAccessorProperty = alias("classAccessorProperty"),
|
||||||
|
ClassPrivateProperty = exports.ClassPrivateProperty = alias("classPrivateProperty"),
|
||||||
|
ClassPrivateMethod = exports.ClassPrivateMethod = alias("classPrivateMethod"),
|
||||||
|
PrivateName = exports.PrivateName = alias("privateName"),
|
||||||
|
StaticBlock = exports.StaticBlock = alias("staticBlock"),
|
||||||
|
ImportAttribute = exports.ImportAttribute = alias("importAttribute"),
|
||||||
|
AnyTypeAnnotation = exports.AnyTypeAnnotation = alias("anyTypeAnnotation"),
|
||||||
|
ArrayTypeAnnotation = exports.ArrayTypeAnnotation = alias("arrayTypeAnnotation"),
|
||||||
|
BooleanTypeAnnotation = exports.BooleanTypeAnnotation = alias("booleanTypeAnnotation"),
|
||||||
|
BooleanLiteralTypeAnnotation = exports.BooleanLiteralTypeAnnotation = alias("booleanLiteralTypeAnnotation"),
|
||||||
|
NullLiteralTypeAnnotation = exports.NullLiteralTypeAnnotation = alias("nullLiteralTypeAnnotation"),
|
||||||
|
ClassImplements = exports.ClassImplements = alias("classImplements"),
|
||||||
|
DeclareClass = exports.DeclareClass = alias("declareClass"),
|
||||||
|
DeclareFunction = exports.DeclareFunction = alias("declareFunction"),
|
||||||
|
DeclareInterface = exports.DeclareInterface = alias("declareInterface"),
|
||||||
|
DeclareModule = exports.DeclareModule = alias("declareModule"),
|
||||||
|
DeclareModuleExports = exports.DeclareModuleExports = alias("declareModuleExports"),
|
||||||
|
DeclareTypeAlias = exports.DeclareTypeAlias = alias("declareTypeAlias"),
|
||||||
|
DeclareOpaqueType = exports.DeclareOpaqueType = alias("declareOpaqueType"),
|
||||||
|
DeclareVariable = exports.DeclareVariable = alias("declareVariable"),
|
||||||
|
DeclareExportDeclaration = exports.DeclareExportDeclaration = alias("declareExportDeclaration"),
|
||||||
|
DeclareExportAllDeclaration = exports.DeclareExportAllDeclaration = alias("declareExportAllDeclaration"),
|
||||||
|
DeclaredPredicate = exports.DeclaredPredicate = alias("declaredPredicate"),
|
||||||
|
ExistsTypeAnnotation = exports.ExistsTypeAnnotation = alias("existsTypeAnnotation"),
|
||||||
|
FunctionTypeAnnotation = exports.FunctionTypeAnnotation = alias("functionTypeAnnotation"),
|
||||||
|
FunctionTypeParam = exports.FunctionTypeParam = alias("functionTypeParam"),
|
||||||
|
GenericTypeAnnotation = exports.GenericTypeAnnotation = alias("genericTypeAnnotation"),
|
||||||
|
InferredPredicate = exports.InferredPredicate = alias("inferredPredicate"),
|
||||||
|
InterfaceExtends = exports.InterfaceExtends = alias("interfaceExtends"),
|
||||||
|
InterfaceDeclaration = exports.InterfaceDeclaration = alias("interfaceDeclaration"),
|
||||||
|
InterfaceTypeAnnotation = exports.InterfaceTypeAnnotation = alias("interfaceTypeAnnotation"),
|
||||||
|
IntersectionTypeAnnotation = exports.IntersectionTypeAnnotation = alias("intersectionTypeAnnotation"),
|
||||||
|
MixedTypeAnnotation = exports.MixedTypeAnnotation = alias("mixedTypeAnnotation"),
|
||||||
|
EmptyTypeAnnotation = exports.EmptyTypeAnnotation = alias("emptyTypeAnnotation"),
|
||||||
|
NullableTypeAnnotation = exports.NullableTypeAnnotation = alias("nullableTypeAnnotation"),
|
||||||
|
NumberLiteralTypeAnnotation = exports.NumberLiteralTypeAnnotation = alias("numberLiteralTypeAnnotation"),
|
||||||
|
NumberTypeAnnotation = exports.NumberTypeAnnotation = alias("numberTypeAnnotation"),
|
||||||
|
ObjectTypeAnnotation = exports.ObjectTypeAnnotation = alias("objectTypeAnnotation"),
|
||||||
|
ObjectTypeInternalSlot = exports.ObjectTypeInternalSlot = alias("objectTypeInternalSlot"),
|
||||||
|
ObjectTypeCallProperty = exports.ObjectTypeCallProperty = alias("objectTypeCallProperty"),
|
||||||
|
ObjectTypeIndexer = exports.ObjectTypeIndexer = alias("objectTypeIndexer"),
|
||||||
|
ObjectTypeProperty = exports.ObjectTypeProperty = alias("objectTypeProperty"),
|
||||||
|
ObjectTypeSpreadProperty = exports.ObjectTypeSpreadProperty = alias("objectTypeSpreadProperty"),
|
||||||
|
OpaqueType = exports.OpaqueType = alias("opaqueType"),
|
||||||
|
QualifiedTypeIdentifier = exports.QualifiedTypeIdentifier = alias("qualifiedTypeIdentifier"),
|
||||||
|
StringLiteralTypeAnnotation = exports.StringLiteralTypeAnnotation = alias("stringLiteralTypeAnnotation"),
|
||||||
|
StringTypeAnnotation = exports.StringTypeAnnotation = alias("stringTypeAnnotation"),
|
||||||
|
SymbolTypeAnnotation = exports.SymbolTypeAnnotation = alias("symbolTypeAnnotation"),
|
||||||
|
ThisTypeAnnotation = exports.ThisTypeAnnotation = alias("thisTypeAnnotation"),
|
||||||
|
TupleTypeAnnotation = exports.TupleTypeAnnotation = alias("tupleTypeAnnotation"),
|
||||||
|
TypeofTypeAnnotation = exports.TypeofTypeAnnotation = alias("typeofTypeAnnotation"),
|
||||||
|
TypeAlias = exports.TypeAlias = alias("typeAlias"),
|
||||||
|
TypeAnnotation = exports.TypeAnnotation = alias("typeAnnotation"),
|
||||||
|
TypeCastExpression = exports.TypeCastExpression = alias("typeCastExpression"),
|
||||||
|
TypeParameter = exports.TypeParameter = alias("typeParameter"),
|
||||||
|
TypeParameterDeclaration = exports.TypeParameterDeclaration = alias("typeParameterDeclaration"),
|
||||||
|
TypeParameterInstantiation = exports.TypeParameterInstantiation = alias("typeParameterInstantiation"),
|
||||||
|
UnionTypeAnnotation = exports.UnionTypeAnnotation = alias("unionTypeAnnotation"),
|
||||||
|
Variance = exports.Variance = alias("variance"),
|
||||||
|
VoidTypeAnnotation = exports.VoidTypeAnnotation = alias("voidTypeAnnotation"),
|
||||||
|
EnumDeclaration = exports.EnumDeclaration = alias("enumDeclaration"),
|
||||||
|
EnumBooleanBody = exports.EnumBooleanBody = alias("enumBooleanBody"),
|
||||||
|
EnumNumberBody = exports.EnumNumberBody = alias("enumNumberBody"),
|
||||||
|
EnumStringBody = exports.EnumStringBody = alias("enumStringBody"),
|
||||||
|
EnumSymbolBody = exports.EnumSymbolBody = alias("enumSymbolBody"),
|
||||||
|
EnumBooleanMember = exports.EnumBooleanMember = alias("enumBooleanMember"),
|
||||||
|
EnumNumberMember = exports.EnumNumberMember = alias("enumNumberMember"),
|
||||||
|
EnumStringMember = exports.EnumStringMember = alias("enumStringMember"),
|
||||||
|
EnumDefaultedMember = exports.EnumDefaultedMember = alias("enumDefaultedMember"),
|
||||||
|
IndexedAccessType = exports.IndexedAccessType = alias("indexedAccessType"),
|
||||||
|
OptionalIndexedAccessType = exports.OptionalIndexedAccessType = alias("optionalIndexedAccessType"),
|
||||||
|
JSXAttribute = exports.JSXAttribute = alias("jsxAttribute"),
|
||||||
|
JSXClosingElement = exports.JSXClosingElement = alias("jsxClosingElement"),
|
||||||
|
JSXElement = exports.JSXElement = alias("jsxElement"),
|
||||||
|
JSXEmptyExpression = exports.JSXEmptyExpression = alias("jsxEmptyExpression"),
|
||||||
|
JSXExpressionContainer = exports.JSXExpressionContainer = alias("jsxExpressionContainer"),
|
||||||
|
JSXSpreadChild = exports.JSXSpreadChild = alias("jsxSpreadChild"),
|
||||||
|
JSXIdentifier = exports.JSXIdentifier = alias("jsxIdentifier"),
|
||||||
|
JSXMemberExpression = exports.JSXMemberExpression = alias("jsxMemberExpression"),
|
||||||
|
JSXNamespacedName = exports.JSXNamespacedName = alias("jsxNamespacedName"),
|
||||||
|
JSXOpeningElement = exports.JSXOpeningElement = alias("jsxOpeningElement"),
|
||||||
|
JSXSpreadAttribute = exports.JSXSpreadAttribute = alias("jsxSpreadAttribute"),
|
||||||
|
JSXText = exports.JSXText = alias("jsxText"),
|
||||||
|
JSXFragment = exports.JSXFragment = alias("jsxFragment"),
|
||||||
|
JSXOpeningFragment = exports.JSXOpeningFragment = alias("jsxOpeningFragment"),
|
||||||
|
JSXClosingFragment = exports.JSXClosingFragment = alias("jsxClosingFragment"),
|
||||||
|
Noop = exports.Noop = alias("noop"),
|
||||||
|
Placeholder = exports.Placeholder = alias("placeholder"),
|
||||||
|
V8IntrinsicIdentifier = exports.V8IntrinsicIdentifier = alias("v8IntrinsicIdentifier"),
|
||||||
|
ArgumentPlaceholder = exports.ArgumentPlaceholder = alias("argumentPlaceholder"),
|
||||||
|
BindExpression = exports.BindExpression = alias("bindExpression"),
|
||||||
|
Decorator = exports.Decorator = alias("decorator"),
|
||||||
|
DoExpression = exports.DoExpression = alias("doExpression"),
|
||||||
|
ExportDefaultSpecifier = exports.ExportDefaultSpecifier = alias("exportDefaultSpecifier"),
|
||||||
|
RecordExpression = exports.RecordExpression = alias("recordExpression"),
|
||||||
|
TupleExpression = exports.TupleExpression = alias("tupleExpression"),
|
||||||
|
DecimalLiteral = exports.DecimalLiteral = alias("decimalLiteral"),
|
||||||
|
ModuleExpression = exports.ModuleExpression = alias("moduleExpression"),
|
||||||
|
TopicReference = exports.TopicReference = alias("topicReference"),
|
||||||
|
PipelineTopicExpression = exports.PipelineTopicExpression = alias("pipelineTopicExpression"),
|
||||||
|
PipelineBareFunction = exports.PipelineBareFunction = alias("pipelineBareFunction"),
|
||||||
|
PipelinePrimaryTopicReference = exports.PipelinePrimaryTopicReference = alias("pipelinePrimaryTopicReference"),
|
||||||
|
VoidPattern = exports.VoidPattern = alias("voidPattern"),
|
||||||
|
TSParameterProperty = exports.TSParameterProperty = alias("tsParameterProperty"),
|
||||||
|
TSDeclareFunction = exports.TSDeclareFunction = alias("tsDeclareFunction"),
|
||||||
|
TSDeclareMethod = exports.TSDeclareMethod = alias("tsDeclareMethod"),
|
||||||
|
TSQualifiedName = exports.TSQualifiedName = alias("tsQualifiedName"),
|
||||||
|
TSCallSignatureDeclaration = exports.TSCallSignatureDeclaration = alias("tsCallSignatureDeclaration"),
|
||||||
|
TSConstructSignatureDeclaration = exports.TSConstructSignatureDeclaration = alias("tsConstructSignatureDeclaration"),
|
||||||
|
TSPropertySignature = exports.TSPropertySignature = alias("tsPropertySignature"),
|
||||||
|
TSMethodSignature = exports.TSMethodSignature = alias("tsMethodSignature"),
|
||||||
|
TSIndexSignature = exports.TSIndexSignature = alias("tsIndexSignature"),
|
||||||
|
TSAnyKeyword = exports.TSAnyKeyword = alias("tsAnyKeyword"),
|
||||||
|
TSBooleanKeyword = exports.TSBooleanKeyword = alias("tsBooleanKeyword"),
|
||||||
|
TSBigIntKeyword = exports.TSBigIntKeyword = alias("tsBigIntKeyword"),
|
||||||
|
TSIntrinsicKeyword = exports.TSIntrinsicKeyword = alias("tsIntrinsicKeyword"),
|
||||||
|
TSNeverKeyword = exports.TSNeverKeyword = alias("tsNeverKeyword"),
|
||||||
|
TSNullKeyword = exports.TSNullKeyword = alias("tsNullKeyword"),
|
||||||
|
TSNumberKeyword = exports.TSNumberKeyword = alias("tsNumberKeyword"),
|
||||||
|
TSObjectKeyword = exports.TSObjectKeyword = alias("tsObjectKeyword"),
|
||||||
|
TSStringKeyword = exports.TSStringKeyword = alias("tsStringKeyword"),
|
||||||
|
TSSymbolKeyword = exports.TSSymbolKeyword = alias("tsSymbolKeyword"),
|
||||||
|
TSUndefinedKeyword = exports.TSUndefinedKeyword = alias("tsUndefinedKeyword"),
|
||||||
|
TSUnknownKeyword = exports.TSUnknownKeyword = alias("tsUnknownKeyword"),
|
||||||
|
TSVoidKeyword = exports.TSVoidKeyword = alias("tsVoidKeyword"),
|
||||||
|
TSThisType = exports.TSThisType = alias("tsThisType"),
|
||||||
|
TSFunctionType = exports.TSFunctionType = alias("tsFunctionType"),
|
||||||
|
TSConstructorType = exports.TSConstructorType = alias("tsConstructorType"),
|
||||||
|
TSTypeReference = exports.TSTypeReference = alias("tsTypeReference"),
|
||||||
|
TSTypePredicate = exports.TSTypePredicate = alias("tsTypePredicate"),
|
||||||
|
TSTypeQuery = exports.TSTypeQuery = alias("tsTypeQuery"),
|
||||||
|
TSTypeLiteral = exports.TSTypeLiteral = alias("tsTypeLiteral"),
|
||||||
|
TSArrayType = exports.TSArrayType = alias("tsArrayType"),
|
||||||
|
TSTupleType = exports.TSTupleType = alias("tsTupleType"),
|
||||||
|
TSOptionalType = exports.TSOptionalType = alias("tsOptionalType"),
|
||||||
|
TSRestType = exports.TSRestType = alias("tsRestType"),
|
||||||
|
TSNamedTupleMember = exports.TSNamedTupleMember = alias("tsNamedTupleMember"),
|
||||||
|
TSUnionType = exports.TSUnionType = alias("tsUnionType"),
|
||||||
|
TSIntersectionType = exports.TSIntersectionType = alias("tsIntersectionType"),
|
||||||
|
TSConditionalType = exports.TSConditionalType = alias("tsConditionalType"),
|
||||||
|
TSInferType = exports.TSInferType = alias("tsInferType"),
|
||||||
|
TSParenthesizedType = exports.TSParenthesizedType = alias("tsParenthesizedType"),
|
||||||
|
TSTypeOperator = exports.TSTypeOperator = alias("tsTypeOperator"),
|
||||||
|
TSIndexedAccessType = exports.TSIndexedAccessType = alias("tsIndexedAccessType"),
|
||||||
|
TSMappedType = exports.TSMappedType = alias("tsMappedType"),
|
||||||
|
TSTemplateLiteralType = exports.TSTemplateLiteralType = alias("tsTemplateLiteralType"),
|
||||||
|
TSLiteralType = exports.TSLiteralType = alias("tsLiteralType"),
|
||||||
|
TSExpressionWithTypeArguments = exports.TSExpressionWithTypeArguments = alias("tsExpressionWithTypeArguments"),
|
||||||
|
TSInterfaceDeclaration = exports.TSInterfaceDeclaration = alias("tsInterfaceDeclaration"),
|
||||||
|
TSInterfaceBody = exports.TSInterfaceBody = alias("tsInterfaceBody"),
|
||||||
|
TSTypeAliasDeclaration = exports.TSTypeAliasDeclaration = alias("tsTypeAliasDeclaration"),
|
||||||
|
TSInstantiationExpression = exports.TSInstantiationExpression = alias("tsInstantiationExpression"),
|
||||||
|
TSAsExpression = exports.TSAsExpression = alias("tsAsExpression"),
|
||||||
|
TSSatisfiesExpression = exports.TSSatisfiesExpression = alias("tsSatisfiesExpression"),
|
||||||
|
TSTypeAssertion = exports.TSTypeAssertion = alias("tsTypeAssertion"),
|
||||||
|
TSEnumBody = exports.TSEnumBody = alias("tsEnumBody"),
|
||||||
|
TSEnumDeclaration = exports.TSEnumDeclaration = alias("tsEnumDeclaration"),
|
||||||
|
TSEnumMember = exports.TSEnumMember = alias("tsEnumMember"),
|
||||||
|
TSModuleDeclaration = exports.TSModuleDeclaration = alias("tsModuleDeclaration"),
|
||||||
|
TSModuleBlock = exports.TSModuleBlock = alias("tsModuleBlock"),
|
||||||
|
TSImportType = exports.TSImportType = alias("tsImportType"),
|
||||||
|
TSImportEqualsDeclaration = exports.TSImportEqualsDeclaration = alias("tsImportEqualsDeclaration"),
|
||||||
|
TSExternalModuleReference = exports.TSExternalModuleReference = alias("tsExternalModuleReference"),
|
||||||
|
TSNonNullExpression = exports.TSNonNullExpression = alias("tsNonNullExpression"),
|
||||||
|
TSExportAssignment = exports.TSExportAssignment = alias("tsExportAssignment"),
|
||||||
|
TSNamespaceExportDeclaration = exports.TSNamespaceExportDeclaration = alias("tsNamespaceExportDeclaration"),
|
||||||
|
TSTypeAnnotation = exports.TSTypeAnnotation = alias("tsTypeAnnotation"),
|
||||||
|
TSTypeParameterInstantiation = exports.TSTypeParameterInstantiation = alias("tsTypeParameterInstantiation"),
|
||||||
|
TSTypeParameterDeclaration = exports.TSTypeParameterDeclaration = alias("tsTypeParameterDeclaration"),
|
||||||
|
TSTypeParameter = exports.TSTypeParameter = alias("tsTypeParameter");
|
||||||
|
const NumberLiteral = exports.NumberLiteral = b.numberLiteral,
|
||||||
|
RegexLiteral = exports.RegexLiteral = b.regexLiteral,
|
||||||
|
RestProperty = exports.RestProperty = b.restProperty,
|
||||||
|
SpreadProperty = exports.SpreadProperty = b.spreadProperty;
|
||||||
|
|
||||||
|
//# sourceMappingURL=uppercase.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+12
@@ -0,0 +1,12 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.buildUndefinedNode = buildUndefinedNode;
|
||||||
|
var _index = require("./generated/index.js");
|
||||||
|
function buildUndefinedNode() {
|
||||||
|
return (0, _index.unaryExpression)("void", (0, _index.numericLiteral)(0), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=productions.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["_index","require","buildUndefinedNode","unaryExpression","numericLiteral"],"sources":["../../src/builders/productions.ts"],"sourcesContent":["import { numericLiteral, unaryExpression } from \"./generated/index.ts\";\n\nexport function buildUndefinedNode() {\n return unaryExpression(\"void\", numericLiteral(0), true);\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAEO,SAASC,kBAAkBA,CAAA,EAAG;EACnC,OAAO,IAAAC,sBAAe,EAAC,MAAM,EAAE,IAAAC,qBAAc,EAAC,CAAC,CAAC,EAAE,IAAI,CAAC;AACzD","ignoreList":[]}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = buildChildren;
|
||||||
|
var _index = require("../../validators/generated/index.js");
|
||||||
|
var _cleanJSXElementLiteralChild = require("../../utils/react/cleanJSXElementLiteralChild.js");
|
||||||
|
function buildChildren(node) {
|
||||||
|
const elements = [];
|
||||||
|
for (let i = 0; i < node.children.length; i++) {
|
||||||
|
let child = node.children[i];
|
||||||
|
if ((0, _index.isJSXText)(child)) {
|
||||||
|
(0, _cleanJSXElementLiteralChild.default)(child, elements);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ((0, _index.isJSXExpressionContainer)(child)) child = child.expression;
|
||||||
|
if ((0, _index.isJSXEmptyExpression)(child)) continue;
|
||||||
|
elements.push(child);
|
||||||
|
}
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=buildChildren.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["_index","require","_cleanJSXElementLiteralChild","buildChildren","node","elements","i","children","length","child","isJSXText","cleanJSXElementLiteralChild","isJSXExpressionContainer","expression","isJSXEmptyExpression","push"],"sources":["../../../src/builders/react/buildChildren.ts"],"sourcesContent":["import {\n isJSXText,\n isJSXExpressionContainer,\n isJSXEmptyExpression,\n} from \"../../validators/generated/index.ts\";\nimport cleanJSXElementLiteralChild from \"../../utils/react/cleanJSXElementLiteralChild.ts\";\nimport type * as t from \"../../index.ts\";\n\ntype ReturnedChild =\n | t.JSXSpreadChild\n | t.JSXElement\n | t.JSXFragment\n | t.Expression;\n\nexport default function buildChildren(\n node: t.JSXElement | t.JSXFragment,\n): ReturnedChild[] {\n const elements = [];\n\n for (let i = 0; i < node.children.length; i++) {\n let child: any = node.children[i];\n\n if (isJSXText(child)) {\n cleanJSXElementLiteralChild(child, elements);\n continue;\n }\n\n if (isJSXExpressionContainer(child)) child = child.expression;\n if (isJSXEmptyExpression(child)) continue;\n\n elements.push(child);\n }\n\n return elements;\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAKA,IAAAC,4BAAA,GAAAD,OAAA;AASe,SAASE,aAAaA,CACnCC,IAAkC,EACjB;EACjB,MAAMC,QAAQ,GAAG,EAAE;EAEnB,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGF,IAAI,CAACG,QAAQ,CAACC,MAAM,EAAEF,CAAC,EAAE,EAAE;IAC7C,IAAIG,KAAU,GAAGL,IAAI,CAACG,QAAQ,CAACD,CAAC,CAAC;IAEjC,IAAI,IAAAI,gBAAS,EAACD,KAAK,CAAC,EAAE;MACpB,IAAAE,oCAA2B,EAACF,KAAK,EAAEJ,QAAQ,CAAC;MAC5C;IACF;IAEA,IAAI,IAAAO,+BAAwB,EAACH,KAAK,CAAC,EAAEA,KAAK,GAAGA,KAAK,CAACI,UAAU;IAC7D,IAAI,IAAAC,2BAAoB,EAACL,KAAK,CAAC,EAAE;IAEjCJ,QAAQ,CAACU,IAAI,CAACN,KAAK,CAAC;EACtB;EAEA,OAAOJ,QAAQ;AACjB","ignoreList":[]}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = createTSUnionType;
|
||||||
|
var _index = require("../generated/index.js");
|
||||||
|
var _removeTypeDuplicates = require("../../modifications/typescript/removeTypeDuplicates.js");
|
||||||
|
var _index2 = require("../../validators/generated/index.js");
|
||||||
|
function createTSUnionType(typeAnnotations) {
|
||||||
|
const types = typeAnnotations.map(type => {
|
||||||
|
return (0, _index2.isTSTypeAnnotation)(type) ? type.typeAnnotation : type;
|
||||||
|
});
|
||||||
|
const flattened = (0, _removeTypeDuplicates.default)(types);
|
||||||
|
if (flattened.length === 1) {
|
||||||
|
return flattened[0];
|
||||||
|
} else {
|
||||||
|
return (0, _index.tsUnionType)(flattened);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=createTSUnionType.js.map
|
||||||
Generated
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["_index","require","_removeTypeDuplicates","_index2","createTSUnionType","typeAnnotations","types","map","type","isTSTypeAnnotation","typeAnnotation","flattened","removeTypeDuplicates","length","tsUnionType"],"sources":["../../../src/builders/typescript/createTSUnionType.ts"],"sourcesContent":["import { tsUnionType } from \"../generated/index.ts\";\nimport removeTypeDuplicates from \"../../modifications/typescript/removeTypeDuplicates.ts\";\nimport { isTSTypeAnnotation } from \"../../validators/generated/index.ts\";\nimport type * as t from \"../../index.ts\";\n\n/**\n * Takes an array of `types` and flattens them, removing duplicates and\n * returns a `UnionTypeAnnotation` node containing them.\n */\nexport default function createTSUnionType(\n typeAnnotations: (t.TSTypeAnnotation | t.TSType)[],\n): t.TSType {\n const types = typeAnnotations.map(type => {\n return isTSTypeAnnotation(type) ? type.typeAnnotation : type;\n });\n const flattened = removeTypeDuplicates(types);\n\n if (flattened.length === 1) {\n return flattened[0];\n } else {\n return tsUnionType(flattened);\n }\n}\n"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AACA,IAAAC,qBAAA,GAAAD,OAAA;AACA,IAAAE,OAAA,GAAAF,OAAA;AAOe,SAASG,iBAAiBA,CACvCC,eAAkD,EACxC;EACV,MAAMC,KAAK,GAAGD,eAAe,CAACE,GAAG,CAACC,IAAI,IAAI;IACxC,OAAO,IAAAC,0BAAkB,EAACD,IAAI,CAAC,GAAGA,IAAI,CAACE,cAAc,GAAGF,IAAI;EAC9D,CAAC,CAAC;EACF,MAAMG,SAAS,GAAG,IAAAC,6BAAoB,EAACN,KAAK,CAAC;EAE7C,IAAIK,SAAS,CAACE,MAAM,KAAK,CAAC,EAAE;IAC1B,OAAOF,SAAS,CAAC,CAAC,CAAC;EACrB,CAAC,MAAM;IACL,OAAO,IAAAG,kBAAW,EAACH,SAAS,CAAC;EAC/B;AACF","ignoreList":[]}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = validateNode;
|
||||||
|
var _validate = require("../validators/validate.js");
|
||||||
|
var _index = require("../index.js");
|
||||||
|
function validateNode(node) {
|
||||||
|
if (node == null || typeof node !== "object") return;
|
||||||
|
const fields = _index.NODE_FIELDS[node.type];
|
||||||
|
if (!fields) return;
|
||||||
|
const keys = _index.BUILDER_KEYS[node.type];
|
||||||
|
for (const key of keys) {
|
||||||
|
const field = fields[key];
|
||||||
|
if (field != null) (0, _validate.validateInternal)(field, node, key, node[key]);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=validateNode.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["_validate","require","_index","validateNode","node","fields","NODE_FIELDS","type","keys","BUILDER_KEYS","key","field","validateInternal"],"sources":["../../src/builders/validateNode.ts"],"sourcesContent":["import { validateInternal } from \"../validators/validate.ts\";\nimport type * as t from \"../index.ts\";\nimport { BUILDER_KEYS, NODE_FIELDS } from \"../index.ts\";\n\nexport default function validateNode<N extends t.Node>(node: N) {\n if (node == null || typeof node !== \"object\") return;\n const fields = NODE_FIELDS[node.type];\n if (!fields) return;\n\n // todo: because keys not in BUILDER_KEYS are not validated - this actually allows invalid nodes in some cases\n const keys = BUILDER_KEYS[node.type] as (keyof N & string)[];\n for (const key of keys) {\n const field = fields[key];\n if (field != null) validateInternal(field, node, key, node[key]);\n }\n return node;\n}\n"],"mappings":";;;;;;AAAA,IAAAA,SAAA,GAAAC,OAAA;AAEA,IAAAC,MAAA,GAAAD,OAAA;AAEe,SAASE,YAAYA,CAAmBC,IAAO,EAAE;EAC9D,IAAIA,IAAI,IAAI,IAAI,IAAI,OAAOA,IAAI,KAAK,QAAQ,EAAE;EAC9C,MAAMC,MAAM,GAAGC,kBAAW,CAACF,IAAI,CAACG,IAAI,CAAC;EACrC,IAAI,CAACF,MAAM,EAAE;EAGb,MAAMG,IAAI,GAAGC,mBAAY,CAACL,IAAI,CAACG,IAAI,CAAyB;EAC5D,KAAK,MAAMG,GAAG,IAAIF,IAAI,EAAE;IACtB,MAAMG,KAAK,GAAGN,MAAM,CAACK,GAAG,CAAC;IACzB,IAAIC,KAAK,IAAI,IAAI,EAAE,IAAAC,0BAAgB,EAACD,KAAK,EAAEP,IAAI,EAAEM,GAAG,EAAEN,IAAI,CAACM,GAAG,CAAC,CAAC;EAClE;EACA,OAAON,IAAI;AACb","ignoreList":[]}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = clone;
|
||||||
|
var _cloneNode = require("./cloneNode.js");
|
||||||
|
function clone(node) {
|
||||||
|
return (0, _cloneNode.default)(node, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=clone.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["_cloneNode","require","clone","node","cloneNode"],"sources":["../../src/clone/clone.ts"],"sourcesContent":["import cloneNode from \"./cloneNode.ts\";\nimport type * as t from \"../index.ts\";\n\n/**\n * Create a shallow clone of a `node`, including only\n * properties belonging to the node.\n * @deprecated Use t.cloneNode instead.\n */\nexport default function clone<T extends t.Node>(node: T): T {\n return cloneNode(node, /* deep */ false);\n}\n"],"mappings":";;;;;;AAAA,IAAAA,UAAA,GAAAC,OAAA;AAQe,SAASC,KAAKA,CAAmBC,IAAO,EAAK;EAC1D,OAAO,IAAAC,kBAAS,EAACD,IAAI,EAAa,KAAK,CAAC;AAC1C","ignoreList":[]}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = cloneDeep;
|
||||||
|
var _cloneNode = require("./cloneNode.js");
|
||||||
|
function cloneDeep(node) {
|
||||||
|
return (0, _cloneNode.default)(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=cloneDeep.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["_cloneNode","require","cloneDeep","node","cloneNode"],"sources":["../../src/clone/cloneDeep.ts"],"sourcesContent":["import cloneNode from \"./cloneNode.ts\";\nimport type * as t from \"../index.ts\";\n\n/**\n * Create a deep clone of a `node` and all of it's child nodes\n * including only properties belonging to the node.\n * @deprecated Use t.cloneNode instead.\n */\nexport default function cloneDeep<T extends t.Node>(node: T): T {\n return cloneNode(node);\n}\n"],"mappings":";;;;;;AAAA,IAAAA,UAAA,GAAAC,OAAA;AAQe,SAASC,SAASA,CAAmBC,IAAO,EAAK;EAC9D,OAAO,IAAAC,kBAAS,EAACD,IAAI,CAAC;AACxB","ignoreList":[]}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = cloneDeepWithoutLoc;
|
||||||
|
var _cloneNode = require("./cloneNode.js");
|
||||||
|
function cloneDeepWithoutLoc(node) {
|
||||||
|
return (0, _cloneNode.default)(node, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=cloneDeepWithoutLoc.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["_cloneNode","require","cloneDeepWithoutLoc","node","cloneNode"],"sources":["../../src/clone/cloneDeepWithoutLoc.ts"],"sourcesContent":["import cloneNode from \"./cloneNode.ts\";\nimport type * as t from \"../index.ts\";\n/**\n * Create a deep clone of a `node` and all of it's child nodes\n * including only properties belonging to the node.\n * excluding `_private` and location properties.\n */\nexport default function cloneDeepWithoutLoc<T extends t.Node>(node: T): T {\n return cloneNode(node, /* deep */ true, /* withoutLoc */ true);\n}\n"],"mappings":";;;;;;AAAA,IAAAA,UAAA,GAAAC,OAAA;AAOe,SAASC,mBAAmBA,CAAmBC,IAAO,EAAK;EACxE,OAAO,IAAAC,kBAAS,EAACD,IAAI,EAAa,IAAI,EAAmB,IAAI,CAAC;AAChE","ignoreList":[]}
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = cloneNode;
|
||||||
|
var _index = require("../definitions/index.js");
|
||||||
|
var _index2 = require("../validators/generated/index.js");
|
||||||
|
const {
|
||||||
|
hasOwn
|
||||||
|
} = {
|
||||||
|
hasOwn: Function.call.bind(Object.prototype.hasOwnProperty)
|
||||||
|
};
|
||||||
|
function cloneIfNode(obj, deep, withoutLoc, commentsCache) {
|
||||||
|
if (obj && typeof obj.type === "string") {
|
||||||
|
return cloneNodeInternal(obj, deep, withoutLoc, commentsCache);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
function cloneIfNodeOrArray(obj, deep, withoutLoc, commentsCache) {
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
return obj.map(node => cloneIfNode(node, deep, withoutLoc, commentsCache));
|
||||||
|
}
|
||||||
|
return cloneIfNode(obj, deep, withoutLoc, commentsCache);
|
||||||
|
}
|
||||||
|
function cloneNode(node, deep = true, withoutLoc = false) {
|
||||||
|
return cloneNodeInternal(node, deep, withoutLoc, new Map());
|
||||||
|
}
|
||||||
|
function cloneNodeInternal(node, deep = true, withoutLoc = false, commentsCache) {
|
||||||
|
if (!node) return node;
|
||||||
|
const {
|
||||||
|
type
|
||||||
|
} = node;
|
||||||
|
const newNode = {
|
||||||
|
type: node.type
|
||||||
|
};
|
||||||
|
if ((0, _index2.isIdentifier)(node)) {
|
||||||
|
newNode.name = node.name;
|
||||||
|
if (hasOwn(node, "optional") && typeof node.optional === "boolean") {
|
||||||
|
newNode.optional = node.optional;
|
||||||
|
}
|
||||||
|
if (hasOwn(node, "typeAnnotation")) {
|
||||||
|
newNode.typeAnnotation = deep ? cloneIfNodeOrArray(node.typeAnnotation, true, withoutLoc, commentsCache) : node.typeAnnotation;
|
||||||
|
}
|
||||||
|
if (hasOwn(node, "decorators")) {
|
||||||
|
newNode.decorators = deep ? cloneIfNodeOrArray(node.decorators, true, withoutLoc, commentsCache) : node.decorators;
|
||||||
|
}
|
||||||
|
} else if (!hasOwn(_index.NODE_FIELDS, type)) {
|
||||||
|
throw new Error(`Unknown node type: "${type}"`);
|
||||||
|
} else {
|
||||||
|
for (const field of Object.keys(_index.NODE_FIELDS[type])) {
|
||||||
|
if (hasOwn(node, field)) {
|
||||||
|
if (deep) {
|
||||||
|
newNode[field] = (0, _index2.isFile)(node) && field === "comments" ? maybeCloneComments(node.comments, deep, withoutLoc, commentsCache) : cloneIfNodeOrArray(node[field], true, withoutLoc, commentsCache);
|
||||||
|
} else {
|
||||||
|
newNode[field] = node[field];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasOwn(node, "loc")) {
|
||||||
|
if (withoutLoc) {
|
||||||
|
newNode.loc = null;
|
||||||
|
} else {
|
||||||
|
newNode.loc = node.loc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasOwn(node, "leadingComments")) {
|
||||||
|
newNode.leadingComments = maybeCloneComments(node.leadingComments, deep, withoutLoc, commentsCache);
|
||||||
|
}
|
||||||
|
if (hasOwn(node, "innerComments")) {
|
||||||
|
newNode.innerComments = maybeCloneComments(node.innerComments, deep, withoutLoc, commentsCache);
|
||||||
|
}
|
||||||
|
if (hasOwn(node, "trailingComments")) {
|
||||||
|
newNode.trailingComments = maybeCloneComments(node.trailingComments, deep, withoutLoc, commentsCache);
|
||||||
|
}
|
||||||
|
if (hasOwn(node, "extra")) {
|
||||||
|
newNode.extra = Object.assign({}, node.extra);
|
||||||
|
}
|
||||||
|
return newNode;
|
||||||
|
}
|
||||||
|
function maybeCloneComments(comments, deep, withoutLoc, commentsCache) {
|
||||||
|
if (!comments || !deep) {
|
||||||
|
return comments;
|
||||||
|
}
|
||||||
|
return comments.map(comment => {
|
||||||
|
const cache = commentsCache.get(comment);
|
||||||
|
if (cache) return cache;
|
||||||
|
const {
|
||||||
|
type,
|
||||||
|
value,
|
||||||
|
loc
|
||||||
|
} = comment;
|
||||||
|
const ret = {
|
||||||
|
type,
|
||||||
|
value,
|
||||||
|
loc
|
||||||
|
};
|
||||||
|
if (withoutLoc) {
|
||||||
|
ret.loc = null;
|
||||||
|
}
|
||||||
|
commentsCache.set(comment, ret);
|
||||||
|
return ret;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=cloneNode.js.map
|
||||||
+1
File diff suppressed because one or more lines are too long
+12
@@ -0,0 +1,12 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = cloneWithoutLoc;
|
||||||
|
var _cloneNode = require("./cloneNode.js");
|
||||||
|
function cloneWithoutLoc(node) {
|
||||||
|
return (0, _cloneNode.default)(node, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=cloneWithoutLoc.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["_cloneNode","require","cloneWithoutLoc","node","cloneNode"],"sources":["../../src/clone/cloneWithoutLoc.ts"],"sourcesContent":["import cloneNode from \"./cloneNode.ts\";\nimport type * as t from \"../index.ts\";\n\n/**\n * Create a shallow clone of a `node` excluding `_private` and location properties.\n */\nexport default function cloneWithoutLoc<T extends t.Node>(node: T): T {\n return cloneNode(node, /* deep */ false, /* withoutLoc */ true);\n}\n"],"mappings":";;;;;;AAAA,IAAAA,UAAA,GAAAC,OAAA;AAMe,SAASC,eAAeA,CAAmBC,IAAO,EAAK;EACpE,OAAO,IAAAC,kBAAS,EAACD,IAAI,EAAa,KAAK,EAAmB,IAAI,CAAC;AACjE","ignoreList":[]}
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = addComment;
|
||||||
|
var _addComments = require("./addComments.js");
|
||||||
|
function addComment(node, type, content, line) {
|
||||||
|
return (0, _addComments.default)(node, type, [{
|
||||||
|
type: line ? "CommentLine" : "CommentBlock",
|
||||||
|
value: content
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=addComment.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["_addComments","require","addComment","node","type","content","line","addComments","value"],"sources":["../../src/comments/addComment.ts"],"sourcesContent":["import addComments from \"./addComments.ts\";\nimport type * as t from \"../index.ts\";\n\n/**\n * Add comment of certain type to a node.\n */\nexport default function addComment<T extends t.Node>(\n node: T,\n type: t.CommentTypeShorthand,\n content: string,\n line?: boolean,\n): T {\n return addComments(node, type, [\n {\n type: line ? \"CommentLine\" : \"CommentBlock\",\n value: content,\n } as t.Comment,\n ]);\n}\n"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAMe,SAASC,UAAUA,CAChCC,IAAO,EACPC,IAA4B,EAC5BC,OAAe,EACfC,IAAc,EACX;EACH,OAAO,IAAAC,oBAAW,EAACJ,IAAI,EAAEC,IAAI,EAAE,CAC7B;IACEA,IAAI,EAAEE,IAAI,GAAG,aAAa,GAAG,cAAc;IAC3CE,KAAK,EAAEH;EACT,CAAC,CACF,CAAC;AACJ","ignoreList":[]}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = addComments;
|
||||||
|
function addComments(node, type, comments) {
|
||||||
|
if (!comments || !node) return node;
|
||||||
|
const key = `${type}Comments`;
|
||||||
|
if (node[key]) {
|
||||||
|
if (type === "leading") {
|
||||||
|
node[key] = comments.concat(node[key]);
|
||||||
|
} else {
|
||||||
|
node[key].push(...comments);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node[key] = comments;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=addComments.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["addComments","node","type","comments","key","concat","push"],"sources":["../../src/comments/addComments.ts"],"sourcesContent":["import type * as t from \"../index.ts\";\n\n/**\n * Add comments of certain type to a node.\n */\nexport default function addComments<T extends t.Node>(\n node: T,\n type: t.CommentTypeShorthand,\n comments: t.Comment[],\n): T {\n if (!comments || !node) return node;\n\n const key = `${type}Comments` as const;\n\n if (node[key]) {\n if (type === \"leading\") {\n node[key] = comments.concat(node[key]);\n } else {\n node[key].push(...comments);\n }\n } else {\n node[key] = comments;\n }\n\n return node;\n}\n"],"mappings":";;;;;;AAKe,SAASA,WAAWA,CACjCC,IAAO,EACPC,IAA4B,EAC5BC,QAAqB,EAClB;EACH,IAAI,CAACA,QAAQ,IAAI,CAACF,IAAI,EAAE,OAAOA,IAAI;EAEnC,MAAMG,GAAG,GAAG,GAAGF,IAAI,UAAmB;EAEtC,IAAID,IAAI,CAACG,GAAG,CAAC,EAAE;IACb,IAAIF,IAAI,KAAK,SAAS,EAAE;MACtBD,IAAI,CAACG,GAAG,CAAC,GAAGD,QAAQ,CAACE,MAAM,CAACJ,IAAI,CAACG,GAAG,CAAC,CAAC;IACxC,CAAC,MAAM;MACLH,IAAI,CAACG,GAAG,CAAC,CAACE,IAAI,CAAC,GAAGH,QAAQ,CAAC;IAC7B;EACF,CAAC,MAAM;IACLF,IAAI,CAACG,GAAG,CAAC,GAAGD,QAAQ;EACtB;EAEA,OAAOF,IAAI;AACb","ignoreList":[]}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports.default = inheritInnerComments;
|
||||||
|
var _inherit = require("../utils/inherit.js");
|
||||||
|
function inheritInnerComments(child, parent) {
|
||||||
|
(0, _inherit.default)("innerComments", child, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
//# sourceMappingURL=inheritInnerComments.js.map
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"names":["_inherit","require","inheritInnerComments","child","parent","inherit"],"sources":["../../src/comments/inheritInnerComments.ts"],"sourcesContent":["import inherit from \"../utils/inherit.ts\";\nimport type * as t from \"../index.ts\";\n\nexport default function inheritInnerComments(\n child: t.Node,\n parent: t.Node,\n): void {\n inherit(\"innerComments\", child, parent);\n}\n"],"mappings":";;;;;;AAAA,IAAAA,QAAA,GAAAC,OAAA;AAGe,SAASC,oBAAoBA,CAC1CC,KAAa,EACbC,MAAc,EACR;EACN,IAAAC,gBAAO,EAAC,eAAe,EAAEF,KAAK,EAAEC,MAAM,CAAC;AACzC","ignoreList":[]}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user