Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ PleaseAI 프로젝트를 위한 공유 코드 스타일 설정 모노레포 —

## `@pleaseai/eslint-config`

### 설치
### eslint-config 설치

```sh
bun add -D @pleaseai/eslint-config eslint
```

### 사용법
### eslint-config 사용법

```ts
// eslint.config.ts
Expand Down Expand Up @@ -51,12 +51,34 @@ export default pleaseai(
)
```

### 기본값
### eslint-config 기본값

- `stylistic`: `indent: 2`, `quotes: 'single'`, `semi: false`
- `typescript: true`
- `gitignore: true`

## `@pleaseai/ast-grep-config`

### ast-grep-config 설치

```sh
bun add -D @pleaseai/ast-grep-config @ast-grep/cli
```

### ast-grep-config 사용법

```yaml
# sgconfig.yml
ruleDirs:
- node_modules/@pleaseai/ast-grep-config/rules
```

```sh
bunx ast-grep scan
```

모든 규칙은 경고(warning)로 배포됩니다. 규칙 목록과 위반을 고치는 방법은 [ast-grep Config 가이드](https://code-style.pages.dev/ko/ast-grep-config)를 참고하세요.

## 개발

로컬에서 저장소를 설정하고 빌드·린트하는 방법은 [CONTRIBUTING.md의 Getting started](./CONTRIBUTING.md#getting-started)(영문)를 따르세요.
Expand Down
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ See the [code-style documentation site](https://code-style.pages.dev) for full g

## `@pleaseai/eslint-config`

### Installation
### eslint-config installation

```sh
bun add -D @pleaseai/eslint-config eslint
```

### Usage
### eslint-config usage

```ts
// eslint.config.ts
Expand Down Expand Up @@ -51,12 +51,34 @@ export default pleaseai(
)
```

### Defaults
### eslint-config defaults

- `stylistic`: `indent: 2`, `quotes: 'single'`, `semi: false`
- `typescript: true`
- `gitignore: true`

## `@pleaseai/ast-grep-config`

### ast-grep-config installation

```sh
bun add -D @pleaseai/ast-grep-config @ast-grep/cli
```

### ast-grep-config usage

```yaml
# sgconfig.yml
ruleDirs:
- node_modules/@pleaseai/ast-grep-config/rules
```

```sh
bunx ast-grep scan
```

Every rule ships as a warning. See the [ast-grep Config guide](https://code-style.pages.dev/en/ast-grep-config) for the rule list and how to fix each violation.

## Development

To set up the repo, build, and lint locally, follow [Getting started in CONTRIBUTING.md](./CONTRIBUTING.md#getting-started).
Expand Down
184 changes: 184 additions & 0 deletions apps/docs/src/content/docs/en/ast-grep-config.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
---
title: "ast-grep Config"
description: "ast-grep rules that keep a codebase navigable for coding agents."
sidebar:
order: 5
---
`@pleaseai/ast-grep-config` is a set of [ast-grep](https://ast-grep.github.io/) rules that keep a codebase navigable for coding agents: public symbols you can find by name, error types in a designated file, and no tests inside source files. It enforces layer 2 (structural rules) of [ADR-0022](https://github.com/chatbot-pf/engineering-standards/blob/main/docs/adr/0022-agent-navigable-code-organization-standard.md) for TypeScript, Dart, Kotlin, Java, and Rust.

Every rule ships as a **warning**. Nothing here fails a build unless you raise a rule's severity in your own config.

## Installation

<CodeGroup titles={["bun", "pnpm", "npm"]}>

```bash
bun add -D @pleaseai/ast-grep-config @ast-grep/cli
```

```bash
pnpm add -D @pleaseai/ast-grep-config @ast-grep/cli
```

```bash
npm install -D @pleaseai/ast-grep-config @ast-grep/cli
```

</CodeGroup>

[`@ast-grep/cli`](https://ast-grep.github.io/) is a peer dependency that provides the `ast-grep` binary. Install it next to this package so `ast-grep` is on your project's bin path.

With bun, add `@ast-grep/cli` to `trustedDependencies` so its postinstall step runs. Otherwise `ast-grep` prints a fallback warning on every call (`please-style check` is unaffected).

```json title="package.json"
{
"trustedDependencies": ["@ast-grep/cli"]
}
```

<Aside type="tip" href="/en/cli">

Run `bunx @pleaseai/code-style init` and select **ast-grep** — the CLI installs both packages and writes `sgconfig.yml` for you.

</Aside>

## Usage

Point your project's `sgconfig.yml` at the rules:

```yaml title="sgconfig.yml"
ruleDirs:
- node_modules/@pleaseai/ast-grep-config/rules
```

```bash
bunx ast-grep scan
```

Or scan with the package's own config without adding one:

```bash
bunx ast-grep scan -c node_modules/@pleaseai/ast-grep-config/sgconfig.yml
```

## The Five Principles

| Slug | Principle |
| --- | --- |
| `code-greppable-public-symbols` | Public symbols are named declarations, so one name search finds the definition and every use. |
| `code-filename-matches-primary-symbol` | A file with one public symbol is named after it (normalized to the language's file-name convention). |
| `code-error-types-in-dedicated-module` | A module's error types live in its designated error file or package. |
| `test-path-derivable-from-source` | Tests live under the toolchain's test root, at a path mirrored from the source they test. |
| `test-helpers-in-dedicated-location` | Helpers shared by several tests live in the designated helper location. |

The checks run in two layers:

- **Layer 2** — the ast-grep rules in this package, run with `ast-grep scan`.
- **Layer 3** — [`please-style check`](/en/cli#check), a path checker for what ast-grep cannot see (file names, test paths, and which helpers are shared). It runs the extraction rules in this package's `extract/` directory. `extract/` is not listed in `ruleDirs`, so its matches never appear in `ast-grep scan` output.

## Rules

| Rule | Slug | Language |
| --- | --- | --- |
| [`ts-no-default-export`](https://github.com/pleaseai/code-style/blob/main/packages/ast-grep-config/rules/typescript/ts-no-default-export.md) | `code-greppable-public-symbols` | TypeScript |
| [`tsx-no-default-export`](https://github.com/pleaseai/code-style/blob/main/packages/ast-grep-config/rules/tsx/tsx-no-default-export.md) | `code-greppable-public-symbols` | TSX |
| [`rust-no-glob-reexport`](https://github.com/pleaseai/code-style/blob/main/packages/ast-grep-config/rules/rust/rust-no-glob-reexport.md) | `code-greppable-public-symbols` | Rust |
| [`ts-error-outside-errors-file`](https://github.com/pleaseai/code-style/blob/main/packages/ast-grep-config/rules/typescript/ts-error-outside-errors-file.md) | `code-error-types-in-dedicated-module` | TypeScript |
| [`tsx-error-outside-errors-file`](https://github.com/pleaseai/code-style/blob/main/packages/ast-grep-config/rules/tsx/tsx-error-outside-errors-file.md) | `code-error-types-in-dedicated-module` | TSX |
| [`dart-error-outside-errors-file`](https://github.com/pleaseai/code-style/blob/main/packages/ast-grep-config/rules/dart/dart-error-outside-errors-file.md) | `code-error-types-in-dedicated-module` | Dart |
| [`kotlin-error-outside-errors-file`](https://github.com/pleaseai/code-style/blob/main/packages/ast-grep-config/rules/kotlin/kotlin-error-outside-errors-file.md) | `code-error-types-in-dedicated-module` | Kotlin |
| [`java-error-outside-error-package`](https://github.com/pleaseai/code-style/blob/main/packages/ast-grep-config/rules/java/java-error-outside-error-package.md) | `code-error-types-in-dedicated-module` | Java |
| [`rust-error-outside-error-file`](https://github.com/pleaseai/code-style/blob/main/packages/ast-grep-config/rules/rust/rust-error-outside-error-file.md) | `code-error-types-in-dedicated-module` | Rust |
| [`ts-no-in-source-test`](https://github.com/pleaseai/code-style/blob/main/packages/ast-grep-config/rules/typescript/ts-no-in-source-test.md) | `test-path-derivable-from-source` | TypeScript |
| [`tsx-no-in-source-test`](https://github.com/pleaseai/code-style/blob/main/packages/ast-grep-config/rules/tsx/tsx-no-in-source-test.md) | `test-path-derivable-from-source` | TSX |

Each rule has a README next to it with the reason, a wrong and a right example, and the steps to fix a violation. The warning message names the slug and the README path inside `node_modules`, so an agent can fix the violation from the message alone:

```text
[code-greppable-public-symbols] Default export hides the symbol's name from
grep. Export a named declaration instead. Fix guide:
node_modules/@pleaseai/ast-grep-config/rules/typescript/ts-no-default-export.md
```

### No default exports

`ts-no-default-export` and `tsx-no-default-export` flag `export default …`, `export { foo as default }`, and `export { default } from './x'`. A default import lets every importer pick its own local name, so a search for the declaration's name misses those import sites. A named import must mention the exported name.

```ts
// Wrong
export default class UserService {}

// Right
export class UserService {}
```

### No glob re-exports

`rust-no-glob-reexport` flags any `use` with a visibility modifier (`pub`, `pub(crate)`, `pub(super)`) that contains a `*`. Private glob imports such as `use super::*;` are not matched. Replace the glob with an explicit list of the items it exports today:

```rust
// Wrong
pub use crate::model::*;

// Right
pub use crate::model::{Account, User};
```

### Error types in a dedicated module

ast-grep has no type information, so the error rules match by name. For the class-based languages, a class is flagged when its supertype ends in `Error` or `Exception`, or when its own name ends in `Error` or `Exception` and it also has a supertype:

- TypeScript / TSX: the `extends` superclass only. `implements` does not match, and a class named `*Error` or `*Exception` is flagged only when it has an `extends` clause.
- Java: the superclass only (`Throwable` also counts). `implements` does not match.
- Dart: the superclass or any implemented type. A class named `*Error` or `*Exception` needs a superclass or `implements` clause.
- Kotlin: any supertype (`Throwable` also counts), on `class` and `object` declarations.

Rust flags a `struct` or `enum` whose name ends in `Error` (not `Exception`) or that has `#[derive(…Error)]`, plus any `impl Error for T` (including `std::error::Error` and `core::error::Error`). Each language has its own designated location:

| Language | Error types live in | Also exempt |
| --- | --- | --- |
| TypeScript / TSX | `errors.ts` in the module directory | `*.d.ts`, `*.test.*`, `*.spec.*`, `test/`, `tests/`, `__tests__/` |
| Dart / Flutter | `errors.dart` in the library directory | `*_test.dart`, `test/`, `integration_test/`, `test_driver/` |
| Kotlin | `Errors.kt` in the package directory | `src/test/`, `src/testFixtures/`, source sets ending in `Test` |
| Java | an `error/` subpackage of the module | `src/test/`, `src/testFixtures/`, `src/androidTest/` |
| Rust | `error.rs` next to the module root (or an `error/` module) | integration tests under `tests/` |

Only the location is checked — the error file may contain helpers and imports.

### No in-source tests

`ts-no-in-source-test` and `tsx-no-in-source-test` flag Vitest [in-source tests](https://vitest.dev/guide/in-source.html) (`if (import.meta.vitest) { … }`). A `test/**` glob cannot find them, and production builds need a bundler `define` to strip them. Move the block into a test file under the package's test root, at a path mirrored from the source: `src/math/add.ts` becomes `test/math/add.test.ts`.

## Framework Files

Files whose shape a framework dictates are excluded from `ts-no-default-export` and `tsx-no-default-export` through their `ignores` lists:

- Type declarations: `*.d.ts`, `*.d.mts`, `*.d.cts`
- Config files: `*.config.ts` and `*.config.*.ts` (for example `vite.config.prod.ts`), in their `.mts`, `.cts`, and `.tsx` forms too
- Storybook stories (`*.stories.ts`, `*.stories.tsx`, …), `.vitepress/`, and `.storybook/`
- Nuxt 4: `app/pages/`, `app/layouts/`, `app/middleware/`, `app/plugins/`, plus `app/app.config.ts` and `app/router.options.ts`
- Nitro: `server/api/`, `server/routes/`, `server/middleware/`, `server/plugins/`, `server/tasks/`

The full lists are in [`ts-no-default-export.yml`](https://github.com/pleaseai/code-style/blob/main/packages/ast-grep-config/rules/typescript/ts-no-default-export.yml) and [`tsx-no-default-export.yml`](https://github.com/pleaseai/code-style/blob/main/packages/ast-grep-config/rules/tsx/tsx-no-default-export.yml). No other rule exempts these paths, except type declarations, which `ts-error-outside-errors-file` and `tsx-error-outside-errors-file` also skip.

Nuxt 3 root directories (`pages/`, `layouts/`, `middleware/`, and `plugins/` without `app/`) are **not** excluded: a glob such as `**/plugins/**` would also exempt every unrelated directory named `plugins`. ast-grep cannot override a shipped rule's `ignores`, so scope the rules from the command line instead — turn them off in the main scan and run them once more with your framework directories excluded:

```bash
ast-grep scan --off=ts-no-default-export --off=tsx-no-default-export
ast-grep scan --filter '^(ts|tsx)-no-default-export$' \
--globs '!pages/**' --globs '!layouts/**' \
--globs '!middleware/**' --globs '!plugins/**' --globs '!app.config.ts'
Comment thread
amondnet marked this conversation as resolved.
```

Put both commands in one package script (for example `lint:structure`) so CI and agents run the same thing. After migrating to the Nuxt 4 `app/` layout, replace the two-step workaround with a single plain `ast-grep scan`. Keeping only the first command would leave both rules turned off for every file.

<Aside type="caution">

Do not add `ast-grep-ignore` comments to silence a rule — they are suppressions that review flags. If a framework requires a default export somewhere else, propose the path for the allowlist in `@pleaseai/ast-grep-config`.

</Aside>

## Compatibility

- `@ast-grep/cli` `^0.45.3`
- Node.js `>=22.0.0`
6 changes: 6 additions & 0 deletions apps/docs/src/content/docs/en/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ Shared `.editorconfig` for consistent editor settings.

</Card>

<Card title="ast-grep Config" icon="ph:tree-structure" href="/en/ast-grep-config">

ast-grep rules that keep a codebase navigable for coding agents.

</Card>

<Card title="CLI" icon="ph:terminal" href="/en/cli">

One-command setup for eslint, prettier, editorconfig, and the `AGENTS.md` rules block.
Expand Down
Loading
Loading