본문으로 건너뛰기
언어

@plott/webkit-config0.4.0

앱 공통 설정 — ESLint flat config(TypeScript·React Hooks·접근성·Next·webkit 규칙), tsconfig 프리셋, Playwright E2E 프리셋

설치

pnpm
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·Hooksoxlint 내장 규칙(React 컴파일러 규칙 포함)각 ESLint 플러그인
설정 모양JSON 파일 하나를 extendseslint.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

jsonc
// .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" }}
sh
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 를 끕니다. 같은 규칙의 다른 제한 구문은 그대로 켜져 있습니다.

json
{  "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-syntaxerror아래 «기본으로 지키는 것» 의 selector 목록. 앱의 selector 는 ["error", { "extra": [{ "selector": "…", "message": "…" }], "disable": ["date-input"], "messages": { "date-input": "…" } }] 로 이어 붙이거나 id 별로 끄고 문구를 바꿉니다
webkit/no-dangerous-htmlerrordangerouslySetInnerHTML. 허용 파일은 overrides 로 끕니다
webkit/safe-storagewarnlocalStorage·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-memberimport 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-listeneronmessage = … 처럼 핸들러 «하나» 를 일부러 두는 곳이 있습니다(교체·해제가 쉽습니다)
typescript/no-unnecessary-type-conversion경계의 String(x)·!!x 는 JS 호출자가 타입을 어길 때의 방어이고 동작이 같습니다
typescript/no-generated-empty-object-type조건부 타입이 {} 로 풀리는 것은 «값이 없음» 을 나타내려고 쓰는 모양입니다
typescript/consistent-returnuseEffect 콜백은 정리 함수를 돌려주거나 아무것도 돌려주지 않는 것이 React 관용구입니다
unicorn/require-post-message-target-originBroadcastChannel·MessagePort·웹뷰 브리지의 postMessage 는 origin 인자가 없습니다
eslint/no-newnew 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 의 extendsJSON 프리셋
@plott/webkit-config/oxlintoxlint 가 jsPlugins 로 읽음(프리셋이 이미 적어 둠)플러그인 기본 export: 규칙 restricted-syntax·navigation-seam·no-dangerous-html·safe-storage
@plott/webkit-config/eslinteslint.config.mjsrecommended(options), 조각 webkitRules·webkitRulesWith·restrictedSyntax·restrictions·restrictionIds·defaultMessages·defaultBannedNames·navigationSeamBlocks·browserSafetyBlocks·a11yPinned·eslintCommentsBlock·defaultIgnores
@plott/webkit-config/eslint/kreslint.config.mjskoMessages(규칙 문구의 한국어 세트. recommended({ messages: koMessages }))
@plott/webkit-config/tsconfig/next.jsonNext 앱의 tsconfig.jsonJSON 프리셋
@plott/webkit-config/tsconfig/library.json패키지(라이브러리)의 tsconfig.jsonJSON 프리셋
@plott/webkit-config/playwrightplaywright.config.tse2eConfig(options), e2ePort(options)

ESLint

eslint.config.mjs:

js
import { recommended } from "@plott/webkit-config/eslint";
export default recommended({  next: true,  typeChecked: true,  tsconfigRootDir: import.meta.dirname,  navigationSeam: { allowIn: ["src/navigation/**"] },});
옵션기본값뜻
reacttrueReact Hooks 규칙과 접근성(jsx-a11y) 규칙
nextfalseNext 의 core-web-vitals 규칙. @next/eslint-plugin-next 는 선택 peer 이고 이 옵션이 true 일 때만 읽습니다(설치돼 있지 않으면 설치하라는 오류)
typeCheckedfalse타입 정보를 쓰는 규칙(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 문자열). 기본 목록 뒤에 이어 붙입니다
appRouterOnlytruenext: 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-googlenext/font/google 가져오기
date-only-literalnew Date("YYYY-MM-DD")·Date.parse("YYYY-MM-DD")
utc-date-slicetoISOString() 을 잘라 날짜 만들기(네 가지 모양)
locale-date-stringtoLocaleDateString·toLocaleTimeString
locale-string-no-args인자 없는 toLocaleString()
date-input<input type="date"> 와 그 무리
placeholder-replace번역 문구의 {자리표시자} 를 replace·replaceAll 로 바꾸기

앱의 규칙을 더할 때는 extraRestrictedSyntax 를 씁니다.

js
export default recommended({  extraRestrictedSyntax: [{ selector: "CallExpression[callee.name='eval']", message: "eval 금지" }],});

같은 규칙 id(no-restricted-syntax)를 뒤의 설정 블록에서 따로 선언하면 목록 전체가 그 블록의 것으로 바뀌어 webkit 규칙이 사라집니다. 손으로 조립하는 앱은 그 블록에 restrictedSyntax 를 이어 붙여 한 항목으로 선언합니다.

js
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

jsonc
// 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 이 켜져 있고, 빌드용 설정이 하나 더 필요합니다.

jsonc
// tsconfig.json — 타입 검사 (에디터·typecheck){ "extends": "@plott/webkit-config/tsconfig/library.json", "compilerOptions": { "types": ["node"] }, "include": ["src", "test"] }
jsonc
// 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 를 해 둡니다.

ts
import { e2eConfig } from "@plott/webkit-config/playwright";
export default e2eConfig({  command: "pnpm exec next start -p {port}",  healthPath: "/api/health",});
옵션기본값뜻
command(필수)서버를 띄우는 명령. {port} 자리에 포트가 들어갑니다
portOffset0포트 = E2E_PORT_BASE + portOffset. 서버가 둘인 앱이 구분하는 데 씁니다
defaultPortBase3100E2E_PORT_BASE 가 없을 때의 기본 포트
healthPath/서버가 떴는지 보는 경로
testDir./e2e테스트 폴더
env없음서버에 넘길 환경 변수(프로세스 환경 위에 덮고, PORT 도 채웁니다)
serverTimeout120000서버가 뜰 때까지 기다리는 시간(ms)
cwd없음서버를 띄우는 작업 폴더
projectsdesktop·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 에 넣어 주는 명령입니다. 읽는 사람은 앱을 세팅하는 프론트엔드 개발자입니다.

sh
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 입니다.
  • 에이전트 스킬 설치는 에이전트 안내 문서 를 봅니다.