@plott/webkit-config0.4.0
앱 공통 설정 — ESLint flat config(TypeScript·React Hooks·접근성·Next·webkit 규칙), tsconfig 프리셋, Playwright E2E 프리셋
설치
pnpm add @plott/webkit-config@0.4.0진입점(exports)
| import | 타입 | 파일 |
|---|---|---|
@plott/webkit-config/eslint | ./dist/eslint/index.d.ts | ./dist/eslint/index.js |
@plott/webkit-config/eslint/kr | ./dist/eslint/kr.d.ts | ./dist/eslint/kr.js |
@plott/webkit-config/oxlint | ./dist/oxlint/plugin.d.ts | ./dist/oxlint/plugin.js |
@plott/webkit-config/oxlint/recommended.json | ./oxlint/recommended.json | |
@plott/webkit-config/playwright | ./dist/playwright/index.d.ts | ./dist/playwright/index.js |
@plott/webkit-config/tsconfig/next.json | ./tsconfig/next.json | |
@plott/webkit-config/tsconfig/library.json | ./tsconfig/library.json |
앱의 공통 설정입니다 — oxlint 프리셋(린트), ESLint 규칙 묶음(flat config), tsconfig 프리셋, Playwright E2E 프리셋. 읽는 사람은 새 앱을 세팅하는 프론트엔드 개발자입니다.
린트는 oxlint 와 ESLint 중 하나
oxlint 프리셋 (/oxlint) | ESLint 프리셋 (/eslint) | |
|---|---|---|
| 권장 | 새 앱의 기본 | oxlint 로 못 옮기는 사정이 있을 때의 대비책 |
| 속도 | 네이티브 실행이라 빠릅니다(타입 정보를 쓰는 규칙 포함) | 자바스크립트 실행이라 느립니다(타입 정보를 쓰면 더 느림) |
| webkit 규칙 | 같은 규칙(webkit/*, 아래 JS 플러그인) | 같은 규칙 |
| 접근성·Next·Hooks | oxlint 내장 규칙(React 컴파일러 규칙 포함) | 각 ESLint 플러그인 |
| 설정 모양 | JSON 파일 하나를 extends | eslint.config.mjs 에서 함수 호출 |
webkit 규칙은 한 곳(src/eslint/rules.ts)의 selector 목록에서 나오고, 두 프리셋이 그 목록을 그대로 씁니다. oxlint 에는 no-restricted-syntax 가 없어서 같은 selector 를 읽는 규칙(webkit/restricted-syntax)을 JS 플러그인으로 제공합니다. oxlint 의 JS 플러그인 기능은 아직 알파(alpha)라서 oxlint 판을 올릴 때는 pnpm test(이 패키지의 oxlint 시험)로 확인합니다.
oxlint
// .oxlintrc.json{ "extends": ["./node_modules/@plott/webkit-config/oxlint/recommended.json"], "overrides": [ // 이음새 파일(로케일을 붙여 주는 래퍼)에서만 next/link 를 가져와도 된다 { "files": ["src/navigation/**"], "rules": { "webkit/navigation-seam": "off" } }, // 정제를 한 곳에서 하는 컴포넌트에서만 dangerouslySetInnerHTML 을 쓴다 { "files": ["src/components/safe-html.tsx"], "rules": { "webkit/no-dangerous-html": "off" } } ], "rules": { "webkit/navigation-seam": "error" }}oxlint --type-aware --deny-warnings src--type-aware 는 oxlint-tsgolint 가 있어야 합니다(앱의 devDependency). 프리셋은 correctness 를 error, suspicious 를 warn 으로 두고 --deny-warnings 가 warn 도 실패로 만듭니다.
프리셋은 스타일시트 부수효과 import(import "./globals.css"; css·scss·sass·less·styl·pcss)를 import/no-unassigned-import 에서 빼고, 그 밖의 값 없는 import 는 warn 으로 그대로 보고합니다. 가로로 스크롤되는 영역에 키보드 초점이 필요하면 이름을 붙인 <section aria-label="…" tabIndex={0}> 로 씁니다(WAI-ARIA 의 이름 붙은 region). 이름 붙은 section 은 랜드마크라서 한 페이지에 같은 이름이 둘 있으면 axe landmark-unique 가 실패합니다. 같은 컴포넌트가 한 페이지에 여러 번 나오면 고정 문구 대신 각 영역의 제목을 aria-labelledby 로 가리킵니다. jsx-a11y/no-noninteractive-tabindex 가 section 과 role="tabpanel" 만 허용하고 div·p·role="region" 의 tabIndex 는 계속 보고합니다. ESLint 프리셋도 같은 허용을 둡니다.
URL 템플릿(…/r/{name}.json)처럼 번역 문구가 아닌 템플릿을 replace 로 채우는 파일은 구문으로 번역 문구와 구별되지 않아 placeholder-replace 에 걸립니다. 그 파일에서만 overrides 로 이 id 를 끕니다. 같은 규칙의 다른 제한 구문은 그대로 켜져 있습니다.
{ "extends": ["./node_modules/@plott/webkit-config/oxlint/recommended.json"], "overrides": [ { "files": ["lib/registry.ts"], "rules": { "webkit/restricted-syntax": ["error", { "disable": ["placeholder-replace"] }] } } ]}suspicious 범주의 react/iframe-missing-sandbox 는 sandbox 가 없는 <iframe> 과 allow-scripts 와 allow-same-origin 을 함께 준 sandbox 를 모두 보고합니다. 같은 출처의 앱 페이지는 둘 중 하나라도 빠지면 스크립트나 저장소가 동작하지 않으므로, 앱이 자기 페이지를 띄우는 파일에서만 overrides 로 이 규칙을 끕니다. 다른 출처의 페이지를 띄우는 iframe 은 sandbox 를 둡니다.
| 규칙 | 기본 | 뜻 |
|---|---|---|
webkit/restricted-syntax | error | 아래 «기본으로 지키는 것» 의 selector 목록. 앱의 selector 는 ["error", { "extra": [{ "selector": "…", "message": "…" }], "disable": ["date-input"], "messages": { "date-input": "…" } }] 로 이어 붙이거나 id 별로 끄고 문구를 바꿉니다 |
webkit/no-dangerous-html | error | dangerouslySetInnerHTML. 허용 파일은 overrides 로 끕니다 |
webkit/safe-storage | warn | localStorage·sessionStorage 직접 접근 |
webkit/navigation-seam | 꺼짐 | next/link·next/navigation 직접 사용(켜면 이음새 파일만 overrides 로 끕니다) |
프리셋은 correctness 를 error, suspicious 를 warn 으로 두고 env 는 browser·node·es2024 입니다. dist, .next, coverage, node_modules, next-env.d.ts, playwright-report, test-results 는 검사하지 않습니다. 아래 규칙은 버그가 아니라 취향에 가까워 끕니다. 켜면 같은 문장을 고치는 일이 대부분입니다.
| 끈 규칙 | 끄는 이유 |
|---|---|
react/react-in-jsx-scope | 새 JSX 변환(react-jsx)은 React 를 import 하지 않아도 됩니다 |
typescript/no-unsafe-type-assertion | 경계(unknown → 구체 타입)의 단언은 라이브러리 코드에서 흔하고, 대안인 타입 가드도 같은 단언을 한 번 더 씁니다 |
typescript/no-unnecessary-type-parameters | 추론만 돕는 제네릭은 공개 API 에서 의도한 모양입니다 |
unicorn/consistent-function-scoping | 클로저가 일부러 안에 있는 코드가 대부분입니다 |
import/no-named-as-default-member | import fc from "fast-check" 뒤 fc.assert 같은 기본 export 멤버 접근은 표준 ESM 관용구입니다 |
unicorn/no-array-sort, unicorn/no-array-reverse | 새 배열을 만든 뒤 정렬·뒤집는 것은 변형이 밖으로 새지 않습니다 |
eslint/no-underscore-dangle, eslint/no-shadow | 취향 규칙입니다 |
unicorn/prefer-add-event-listener | onmessage = … 처럼 핸들러 «하나» 를 일부러 두는 곳이 있습니다(교체·해제가 쉽습니다) |
typescript/no-unnecessary-type-conversion | 경계의 String(x)·!!x 는 JS 호출자가 타입을 어길 때의 방어이고 동작이 같습니다 |
typescript/no-generated-empty-object-type | 조건부 타입이 {} 로 풀리는 것은 «값이 없음» 을 나타내려고 쓰는 모양입니다 |
typescript/consistent-return | useEffect 콜백은 정리 함수를 돌려주거나 아무것도 돌려주지 않는 것이 React 관용구입니다 |
unicorn/require-post-message-target-origin | BroadcastChannel·MessagePort·웹뷰 브리지의 postMessage 는 origin 인자가 없습니다 |
eslint/no-new | new Intl.Locale(x) 처럼 생성자를 불러 입력을 검증하는 관용구가 있습니다 |
ESLint
ESLint 는 9 버전을 씁니다. 접근성 플러그인 eslint-plugin-jsx-a11y 6.10.2 의 peer 범위가 ESLint 9 까지입니다. 앱의 eslint 도 9 로 맞춥니다. E2E 를 쓰는 앱만 @playwright/test 가 필요합니다(없어도 나머지는 동작합니다).
이 패키지는 린트 도구를 설치해 주지 않습니다(모두 선택 peer). oxlint 프리셋과 tsconfig·Playwright 프리셋만 쓰는 앱은 ESLint 도구를 받지 않습니다. @plott/webkit-config/eslint 를 쓰는 앱은 아래를 devDependencies 에 직접 설치합니다. 없으면 진입점을 불러 오는 순간 Cannot find package 로 멈춥니다.
| 설치할 것 | 범위 |
|---|---|
eslint | ^9.39.5 |
@eslint/js | ^9.39.5 |
typescript-eslint | ^8.72.1 |
typescript | >=5.9.3 <6.1 |
eslint-plugin-react-hooks | ^7.1.1 |
eslint-plugin-jsx-a11y | ^6.10.2 |
@eslint-community/eslint-plugin-eslint-comments | ^4.8.1 |
eslint-config-prettier | ^10.1.8 |
globals | ^17.13.0 |
@next/eslint-plugin-next (next: true 일 때만) | ^16.0.0 |
진입점
| import | 용도 | 주요 export |
|---|---|---|
@plott/webkit-config/oxlint/recommended.json | .oxlintrc.json 의 extends | JSON 프리셋 |
@plott/webkit-config/oxlint | oxlint 가 jsPlugins 로 읽음(프리셋이 이미 적어 둠) | 플러그인 기본 export: 규칙 restricted-syntax·navigation-seam·no-dangerous-html·safe-storage |
@plott/webkit-config/eslint | eslint.config.mjs | recommended(options), 조각 webkitRules·webkitRulesWith·restrictedSyntax·restrictions·restrictionIds·defaultMessages·defaultBannedNames·navigationSeamBlocks·browserSafetyBlocks·a11yPinned·eslintCommentsBlock·defaultIgnores |
@plott/webkit-config/eslint/kr | eslint.config.mjs | koMessages(규칙 문구의 한국어 세트. recommended({ messages: koMessages })) |
@plott/webkit-config/tsconfig/next.json | Next 앱의 tsconfig.json | JSON 프리셋 |
@plott/webkit-config/tsconfig/library.json | 패키지(라이브러리)의 tsconfig.json | JSON 프리셋 |
@plott/webkit-config/playwright | playwright.config.ts | e2eConfig(options), e2ePort(options) |
ESLint
eslint.config.mjs:
import { recommended } from "@plott/webkit-config/eslint";
export default recommended({ next: true, typeChecked: true, tsconfigRootDir: import.meta.dirname, navigationSeam: { allowIn: ["src/navigation/**"] },});| 옵션 | 기본값 | 뜻 |
|---|---|---|
react | true | React Hooks 규칙과 접근성(jsx-a11y) 규칙 |
next | false | Next 의 core-web-vitals 규칙. @next/eslint-plugin-next 는 선택 peer 이고 이 옵션이 true 일 때만 읽습니다(설치돼 있지 않으면 설치하라는 오류) |
typeChecked | false | 타입 정보를 쓰는 규칙(projectService). 타입 검사를 하므로 느립니다 |
tsconfigRootDir | 없음 | typeChecked 가 tsconfig.json 을 찾는 기준 폴더. 보통 import.meta.dirname |
ignores | 없음 | 기본 제외 목록에 더할 glob |
navigationSeam | 없음 | { allowIn: string[], banned? }. 아래 «로케일 이음새» |
messages | 영어 문구(defaultMessages) | 규칙 문구를 id 별로 바꿉니다. 모르는 id 나 빈 문구는 던집니다. 한국어 세트는 koMessages |
disableRestrictions | 없음 | 끌 제한 구문의 id 목록. 아래 표 |
dangerousHtml | 없음 | { allowIn: string[] }. dangerouslySetInnerHTML 을 써도 되는 파일(정제를 한 곳에서 하는 컴포넌트). 그 밖에서는 error |
extraRestrictedSyntax | 없음 | 앱의 no-restricted-syntax selector({ selector, message } 또는 selector 문자열). 기본 목록 뒤에 이어 붙입니다 |
appRouterOnly | true | next: true 일 때 @next/next/no-html-link-for-pages 를 끕니다. pages 폴더가 없으면 그 규칙은 «Pages directory cannot be found» 경고만 내고 검사를 못 합니다. pages 폴더가 있는 앱은 false |
기본으로 제외하는 곳은 dist, .next, node_modules, coverage, playwright-report, test-results, next-env.d.ts 입니다.
제한 구문은 id 로 끌 수 있습니다(recommended({ disableRestrictions: ["date-input"] }), 모르는 id 는 던집니다). 같은 id 의 selector 는 함께 꺼집니다. 문구 id 는 아래 id 에 dangerous-html·safe-storage·navigation-seam·navigation-seam-namespace 를 더한 것입니다.
| id | 막는 것 |
|---|---|
next-font-google | next/font/google 가져오기 |
date-only-literal | new Date("YYYY-MM-DD")·Date.parse("YYYY-MM-DD") |
utc-date-slice | toISOString() 을 잘라 날짜 만들기(네 가지 모양) |
locale-date-string | toLocaleDateString·toLocaleTimeString |
locale-string-no-args | 인자 없는 toLocaleString() |
date-input | <input type="date"> 와 그 무리 |
placeholder-replace | 번역 문구의 {자리표시자} 를 replace·replaceAll 로 바꾸기 |
앱의 규칙을 더할 때는 extraRestrictedSyntax 를 씁니다.
export default recommended({ extraRestrictedSyntax: [{ selector: "CallExpression[callee.name='eval']", message: "eval 금지" }],});같은 규칙 id(no-restricted-syntax)를 뒤의 설정 블록에서 따로 선언하면 목록 전체가 그 블록의 것으로 바뀌어 webkit 규칙이 사라집니다. 손으로 조립하는 앱은 그 블록에 restrictedSyntax 를 이어 붙여 한 항목으로 선언합니다.
import { recommended, restrictedSyntax } from "@plott/webkit-config/eslint";
export default [ ...recommended(), { rules: { "no-restricted-syntax": ["error", ...restrictedSyntax, { selector: "…", message: "…" }], }, },];기본으로 지키는 것
- 날짜를
toISOString().slice(0, 10)·.substring(0, 10)·.substr(0, 10)·.split("T")[0]·.split("T").at(0)·.split("T").shift()로 자르지 않습니다. UTC 날짜라서 서버(UTC)와 브라우저(UTC+9 같은 양의 오프셋)가 하루 어긋난 날짜를 그립니다.@plott/webkit-time의today(zone)·dateIn을 씁니다. toLocaleDateString·toLocaleTimeString을 부르지 않고, 인자 없는toLocaleString()도 부르지 않습니다. Node 의 ICU 와 브라우저가 다르게 그려 하이드레이션(서버가 그린 화면에 브라우저가 이어 붙는 과정)이 깨지고, 시간대도 실행 환경을 따라갑니다.@plott/webkit-format을 씁니다. 로케일 인자를 준toLocaleString("ko-KR")같은 호출(숫자 포맷 등)은 막지 않습니다.<input type="date">·"time"·"datetime-local"·"month"·"week"를 쓰지 않습니다. Chrome 은 0001년을 받아 주고 iOS 에서는 칸이 넘칩니다. 날짜 선택 컴포넌트를 씁니다. 글자(리터럴)로 적은type만 잡습니다.type={someVar}처럼 변수로 주는 것은 따라가지 않습니다.next/font/google을 가져오지 않습니다. 빌드할 때 구글 서버에서 글꼴을 받아 오므로 네트워크가 막히거나 불안정한 빌드(Docker 등)에서 간헐적으로 실패할 수 있습니다.next/font/local로 글꼴 파일을 저장소에 둡니다.new Date("2026-01-02")·Date.parse("2026-01-02")처럼 날짜만 있는 글자 리터럴을 쓰지 않습니다. UTC 자정으로 읽혀 시간대에 따라 하루 어긋납니다.@plott/webkit-time을 씁니다. 변수로 준 문자열은 따라가지 않습니다.dangerouslySetInnerHTML을 쓰지 않습니다(XSS). 정제를 한 곳에서 하는 컴포넌트만dangerousHtml.allowIn으로 허용합니다.localStorage·sessionStorage에 바로 접근하면 경고합니다(warn). 사파리 개인 창·차단된 사이트 데이터에서는 접근만 해도 던집니다.@plott/webkit-browser의safeStorage를 씁니다.- 번역 문구의
{자리표시자}를replace·replaceAll로 바꾸지 않습니다(문자열"{a}", 정규식/\{a\}/g, 템플릿`{${k}}`모두). 하나라도 빠뜨리면{duration}이 화면에 그대로 남습니다. 메시지 라이브러리의 치환 기능을 씁니다./\s{2,}/같은 수량자 정규식과/\p{Nd}/u같은 유니코드 속성 정규식은 막지 않습니다. - 접근성 다섯 규칙(
alt-text,aria-props,click-events-have-key-events,no-static-element-interactions,role-has-required-aria-props)은 항상 error 입니다. eslint-disable에는 이유를 적습니다(// eslint-disable-next-line 규칙 -- 이유). 이유가 없으면 나중에 아무도 왜 껐는지 모릅니다. 더는 필요 없어진eslint-disable도 오류입니다. 남아 있으면 진짜 오류를 가립니다.- 안 쓰는 변수·인자는
_로 시작하면 허용합니다. - 표기 규칙은 Prettier 가 갖습니다.
eslint-config-prettier를 마지막에 두어 스타일 규칙을 모두 끕니다.
규칙을 정확히 잡지 못하는 모양은 일부러 잡지 않습니다. 예를 들어 toISOString() 을 변수에 담아 둔 뒤 자르는 코드는 정적으로 따라가지 않으므로 규칙에 걸리지 않습니다.
로케일 이음새 (navigationSeam)
navigationSeam 을 주면 next/link, next/navigation 의 redirect·permanentRedirect·useRouter·usePathname, next/cache 의 revalidatePath 를 직접 가져오는 것을 막습니다. 이 목록(defaultBannedNames)은 navigationSeam.banned 로 바꿉니다. { "모듈 이름": "all" | ["이름", …] } 모양이고, 주면 기본 목록을 대신하며 모양이 틀리면 던집니다. 로케일을 붙여 주는 앱 안의 래퍼(이음새)를 거치지 않으면 경로에서 로케일이 빠지기 때문입니다. allowIn 에 든 파일(이음새 자신)에서만 허용하고, 타입만 가져오는 import type(인라인 type 지정자 포함)은 어디서나 통과합니다. 규칙 id 는 webkit/navigation-seam 입니다(eslint-disable 에도 이 이름).
import * as nav from "next/navigation" 는 쓰는 이름을 따라가서 판단합니다. nav.notFound 만 쓰면 통과하고, nav.redirect 처럼 막힌 이름을 쓰면 그 자리를 막습니다. nav 를 값으로 넘기거나 const { redirect } = nav 처럼 구조 분해해 이름을 따라갈 수 없으면 import 를 막고, 필요한 이름을 직접 가져오라고 안내합니다. next/link 는 모듈 전체가 대상이라 네임스페이스로도 막습니다. export … from 도 같은 규칙을 받습니다.
규칙 이름
- 규칙 이름 공간과 ESLint 설정 블록 이름의 접두는
webkit입니다(webkit/restricted-syntax,webkit/navigation-seam,webkit/no-dangerous-html,webkit/safe-storage, 블록webkit/ignores·webkit/typescript·webkit/rules등).eslint-disable·oxlint-disable주석과overrides의 규칙 이름도 이 이름을 씁니다. - 옵션 타입은
RecommendedOptions입니다.
tsconfig
// apps/web/tsconfig.json{ "extends": "@plott/webkit-config/tsconfig/next.json", "compilerOptions": { "paths": { "@/*": ["./*"] }, "types": ["node"] }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"]}next.json 은 Next 16 이 요구하는 설정(moduleResolution: bundler, jsx: react-jsx, plugins: [{ "name": "next" }], noEmit, incremental, isolatedModules)에 strict, noUncheckedIndexedAccess, skipLibCheck 를 더한 것입니다. include·exclude·paths 는 앱마다 달라 프리셋에 넣지 않습니다(extends 한 설정의 include 는 상대 경로 기준이 프리셋 쪽이 되므로 앱에서 적습니다).
패키지는 library.json 을 씁니다. 타입 검사용이라 noEmit 이 켜져 있고, 빌드용 설정이 하나 더 필요합니다.
// tsconfig.json — 타입 검사 (에디터·typecheck){ "extends": "@plott/webkit-config/tsconfig/library.json", "compilerOptions": { "types": ["node"] }, "include": ["src", "test"] }// tsconfig.build.json — 내보내기{ "extends": "@plott/webkit-config/tsconfig/library.json", "compilerOptions": { "noEmit": false, "allowImportingTsExtensions": false, "rewriteRelativeImportExtensions": true, "declaration": true, "declarationMap": true, "sourceMap": true, "outDir": "dist", "rootDir": "src", "types": ["node"] }, "include": ["src"]}Playwright
playwright.config.ts 의 command 는 next start 를 쓰므로 먼저 next build 를 해 둡니다.
import { e2eConfig } from "@plott/webkit-config/playwright";
export default e2eConfig({ command: "pnpm exec next start -p {port}", healthPath: "/api/health",});| 옵션 | 기본값 | 뜻 |
|---|---|---|
command | (필수) | 서버를 띄우는 명령. {port} 자리에 포트가 들어갑니다 |
portOffset | 0 | 포트 = E2E_PORT_BASE + portOffset. 서버가 둘인 앱이 구분하는 데 씁니다 |
defaultPortBase | 3100 | E2E_PORT_BASE 가 없을 때의 기본 포트 |
healthPath | / | 서버가 떴는지 보는 경로 |
testDir | ./e2e | 테스트 폴더 |
env | 없음 | 서버에 넘길 환경 변수(프로세스 환경 위에 덮고, PORT 도 채웁니다) |
serverTimeout | 120000 | 서버가 뜰 때까지 기다리는 시간(ms) |
cwd | 없음 | 서버를 띄우는 작업 폴더 |
projects | desktop·mobile | 통째로 바꿉니다 |
use | 없음 | 기본 use(baseURL·trace) 위에 얹습니다. 로케일과 시간대는 기본값이 없으니 use: { locale, timezoneId } 로 직접 줍니다 |
기본으로 지키는 것
- 서버는 Playwright 가 직접 띄우고
reuseExistingServer는false입니다. 같은 포트에서 다른 세션의 서버가 떠 있으면 그 서버가 테스트에 답해 엉뚱한 화면을 검사하게 됩니다. 겹치면 실패하는 편이 낫습니다. - 포트는 워크트리(작업 폴더 사본)마다 달라야 하므로
E2E_PORT_BASE환경 변수로 정합니다. 예:E2E_PORT_BASE=3200 pnpm exec playwright test. - 브라우저의 로케일과 시간대는 호출자가
use: { locale, timezoneId }로 정합니다(기본값 없음). 정하지 않으면 실행 환경의 시간대에 기대 날짜 검사가 환경마다 달라지므로 앱의 설정에 적습니다. - 실패한 테스트만 트레이스를 남깁니다(
retain-on-failure). - CI(
CI환경 변수가 있을 때)에서는test.only를 금지하고(forbidOnly) 실패한 테스트를 한 번 더 돌립니다(retries: 1). 로컬은 재시도하지 않습니다. - 기본 프로젝트는
desktop(Desktop Chrome)과mobile(Pixel 7)입니다.
코딩 에이전트 안내 블록 — webkit-agents
Claude Code·Codex·Cursor 같은 코딩 에이전트(coding agent)가 앱에서 날짜·형식·API 호출을 직접 짜지 않고 @plott/webkit-* 를 쓰게 하는 안내를 앱의 AGENTS.md 에 넣어 주는 명령입니다. 읽는 사람은 앱을 세팅하는 프론트엔드 개발자입니다.
pnpm exec webkit-agents # 앱 루트에서. AGENTS.md 가 없으면 만듭니다pnpm exec webkit-agents --check # CI 에서. 블록이 낡았거나 없으면 종료 코드 1pnpm exec webkit-agents ../my-app # 다른 폴더의 앱node_modules/@plott/webkit-*에 깔린 패키지를 찾아<!-- webkit:start -->와<!-- webkit:end -->사이를 씁니다. 표지 밖의 글은 건드리지 않고, 표지가 없으면 파일 끝에 덧붙입니다.- 블록에는 패키지마다 한 줄 설명과
node_modules안의 README 경로, «손으로 만들지 말고 이것을 쓴다» 규칙이 들어갑니다. 규칙 줄은 각 패키지package.json의webkit.use(쓸 일)와webkit.never(하지 말 것)로 만들고,webkit.use가 없는 패키지는 목록에만 오릅니다. 찾는 범위(@plott)는 이 패키지 자신의 이름에서 읽습니다. 패키지를 더하거나 뺀 뒤 다시 돌립니다. 몇 번을 돌려도 결과가 같습니다. - 표지가 짝이 맞지 않으면 파일을 고치지 않고 종료 코드 2 로 멈춥니다.
webkit-패키지가 하나도 없을 때도 2 입니다. - 에이전트 스킬 설치는 에이전트 안내 문서 를 봅니다.