9148c358d0
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를 Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류. - 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로 자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거, 동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거. - 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming, Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business, Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로, 나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는 title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백). 원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지. - 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서. - 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는 지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지. - Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경. - 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
9.3 KiB
9.3 KiB
id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
| id | title | category | status | canonical_id | aliases | duplicate_of | source_trust_level | confidence_score | verification_status | tags | raw_sources | last_reinforced | github_commit | tech_stack | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| wiki-2026-0508-custom-eslint-rules-dev | Custom ESLint Rules Development | 10_Wiki/Topics | verified | self |
|
none | A | 0.9 | applied |
|
2026-05-10 | pending |
|
Custom ESLint Rules
매 한 줄
"매 team-specific rule 의 자동 enforcement". 매 AST visitor pattern + 매 fixer API. 매 architectural rule (no domain → infra), 매 anti-pattern (deprecated API), 매 convention (naming). 매 modern alternative: Semgrep (multi-language).
매 핵심
매 ESLint architecture
- Parser (espree, @typescript-eslint/parser): 매 source → 매 AST.
- Rule: 매 visit AST node + 매 report.
- Fixer: 매 auto-correct.
- Config: 매 enable / disable.
매 AST node type
- Program: 매 root.
- VariableDeclaration, FunctionDeclaration.
- CallExpression, MemberExpression.
- ArrowFunctionExpression, ObjectExpression, ArrayExpression.
- TSInterfaceDeclaration, TSTypeAliasDeclaration (TS).
매 use case
- Architectural: domain → infrastructure 의 forbid.
- Convention: naming, file structure.
- Deprecation: 매 old API 의 detect.
- Security: dangerous pattern (eval, dangerouslySetInnerHTML).
- Performance: anti-pattern (e.g., unnecessary re-render).
- Domain-specific: business rule.
매 selector syntax (Esquery)
Identifier[name="foo"]: 매 specific name.CallExpression > MemberExpression: 매 chain.:not(...): 매 exclusion.[callee.name="alert"]: 매 attribute match.
매 modern alternative: Semgrep
- 매 multi-language (Python, Go, Java, Rust, ...).
- 매 pattern-based (more readable).
- 매 ESLint custom 보다 매 fast write.
매 LLM-aided rule generation
- 매 plain English → 매 ESLint rule code.
- 매 example-based.
💻 패턴
Basic ESLint rule
// eslint-rule-no-console-log.js
module.exports = {
meta: {
type: 'suggestion',
docs: {
description: 'Disallow console.log calls',
recommended: true,
},
fixable: 'code',
schema: [],
messages: {
noConsoleLog: 'Avoid console.log in production. Use logger.',
},
},
create(context) {
return {
CallExpression(node) {
if (
node.callee.type === 'MemberExpression' &&
node.callee.object.name === 'console' &&
node.callee.property.name === 'log'
) {
context.report({
node,
messageId: 'noConsoleLog',
fix(fixer) {
// 매 auto-fix: 매 logger.info 의 replace
return fixer.replaceText(node.callee, 'logger.info');
},
});
}
},
};
},
};
Plugin structure
my-eslint-plugin/
├── package.json
├── lib/
│ ├── index.js # 매 plugin entry
│ └── rules/
│ ├── no-console-log.js
│ └── enforce-domain-isolation.js
└── tests/
└── no-console-log.test.js
// lib/index.js
module.exports = {
rules: {
'no-console-log': require('./rules/no-console-log'),
'enforce-domain-isolation': require('./rules/enforce-domain-isolation'),
},
configs: {
recommended: {
plugins: ['@my-org'],
rules: {
'@my-org/no-console-log': 'error',
'@my-org/enforce-domain-isolation': 'error',
},
},
},
};
Architectural rule (Clean Architecture)
// 매 src/domain/* 의 매 src/infrastructure/* 의 import 의 forbid
module.exports = {
meta: {
type: 'problem',
messages: {
noInfraInDomain: 'domain layer must not import from infrastructure',
},
},
create(context) {
return {
ImportDeclaration(node) {
const filename = context.getFilename();
if (!filename.includes('/domain/')) return;
const importPath = node.source.value;
if (importPath.includes('/infrastructure/')) {
context.report({
node,
messageId: 'noInfraInDomain',
});
}
},
};
},
};
Test (RuleTester)
const { RuleTester } = require('eslint');
const rule = require('./no-console-log');
const tester = new RuleTester();
tester.run('no-console-log', rule, {
valid: [
'logger.info("hi");',
'console.error("err");',
],
invalid: [
{
code: 'console.log("hi");',
output: 'logger.info("hi");',
errors: [{ messageId: 'noConsoleLog' }],
},
],
});
TypeScript-aware rule
import { TSESTree, ESLintUtils } from '@typescript-eslint/utils';
const createRule = ESLintUtils.RuleCreator(name => `https://docs.example.com/${name}`);
export default createRule({
name: 'no-any-type',
meta: {
type: 'suggestion',
docs: { description: 'Forbid `any` type', recommended: true },
schema: [],
messages: { noAny: 'Avoid `any` — use `unknown` or specific type.' },
},
defaultOptions: [],
create(context) {
return {
TSAnyKeyword(node) {
context.report({ node, messageId: 'noAny' });
},
};
},
});
Selector-based (esquery)
// 매 매 React.useState() 의 매 first arg 의 type check
module.exports = {
create(context) {
return {
'CallExpression[callee.object.name="React"][callee.property.name="useState"]'(node) {
const arg = node.arguments[0];
if (!arg) {
context.report({ node, message: 'useState requires initial value' });
}
},
};
},
};
Semgrep alternative
# .semgrep/no-console-log.yml
rules:
- id: no-console-log
pattern: console.log(...)
message: 'Avoid console.log in production'
severity: WARNING
languages: [javascript, typescript]
fix: logger.info(...)
- id: domain-no-infra
patterns:
- pattern: import $X from "$Y"
- metavariable-pattern:
metavariable: $Y
pattern: '*infrastructure*'
paths:
include:
- 'src/domain/**'
message: 'domain must not import infrastructure'
severity: ERROR
languages: [typescript]
LLM-generated rule
def generate_eslint_rule(description, examples):
prompt = f"""Generate an ESLint custom rule.
Description: {description}
Bad examples:
{format_examples(examples['bad'])}
Good examples (allowed):
{format_examples(examples['good'])}
Output: complete eslint rule .js file with autofix where possible.
Use selector syntax. Include test cases."""
return llm.generate(prompt)
Apply via flat config (ESLint 9+)
// eslint.config.js
import myPlugin from '@my-org/eslint-plugin';
export default [
{
plugins: { '@my-org': myPlugin },
rules: {
'@my-org/no-console-log': 'error',
'@my-org/enforce-domain-isolation': 'error',
},
},
];
Performance: visitor reuse
// 매 ❌ Bad — 매 매 node 마다 fileName check
create(context) {
return {
'ImportDeclaration'(node) {
const fn = context.getFilename();
if (!fn.includes('/domain/')) return;
// ...
},
};
}
// 매 ✅ Better — 매 file-level cache
create(context) {
const filename = context.getFilename();
if (!filename.includes('/domain/')) return {}; // 매 visitor 의 skip entire
return {
'ImportDeclaration'(node) { /* ... */ },
};
}
🤔 결정 기준
| 상황 | Tool |
|---|---|
| Single language (JS/TS) | ESLint custom |
| Multi-language | Semgrep |
| Cross-cutting (security) | CodeQL / Semgrep |
| Architectural | dependency-cruiser |
| Quick + readable pattern | Semgrep |
| AST manipulation needed | ESLint with autofix |
| Type-aware | typescript-eslint |
기본값: 매 single-lang JS/TS = ESLint. 매 multi-lang = Semgrep.
🔗 Graph
- 부모: ESLint-Static-Analysis · Code-Quality
- 변형: ESLint · Semgrep · CodeQL · dependency-cruiser
- 응용: AST · Code_Smells · CI_CD 파이프라인 및 IDE 통합 보안 · Architecture Anti-patterns
- Adjacent: Abstract_Syntax_Tree · Clean-Code-Principles · Quality_Code_Review_Modern
🤖 LLM 활용
언제: 매 team-wide convention enforcement. 매 architectural rule. 매 AI-generated code 의 normalize. 언제 X: 매 single-time check (just grep). 매 dynamic / runtime issue.
❌ 안티패턴
- No test (RuleTester): 매 rule 의 buggy.
- No fixer: 매 manual burden.
- Slow rule: 매 매 file performance ↓.
- Over-broad selector: 매 false positive.
- No documentation URL: 매 dev 의 confusion.
- Custom rule for what built-in exists: 매 wheel reinvent.
🧪 검증 / 중복
- Verified (ESLint docs, typescript-eslint, Semgrep docs).
- 신뢰도 A.
- Related: ESLint-Static-Analysis · Code_Smells · Architecture Anti-patterns · CI_CD 파이프라인 및 IDE 통합 보안 · Abstract_Syntax_Tree.
🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — AST + 매 basic / architectural / TS / Semgrep / LLM-gen code |