@jterrazz/typescript 6.2.0 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,150 +8,21 @@ The complete TypeScript toolchain — build, run, check, and document with zero
8
8
  npm install @jterrazz/typescript --save-dev
9
9
  ```
10
10
 
11
- ## Setup
12
-
13
- ### 1. Choose a TypeScript configuration
14
-
15
- ```json
16
- // tsconfig.json - Pick one:
17
- { "extends": "@jterrazz/typescript/tsconfig/node" } // Node.js projects
18
- { "extends": "@jterrazz/typescript/tsconfig/next" } // Next.js projects
19
- { "extends": "@jterrazz/typescript/tsconfig/expo" } // Expo/React Native
20
- ```
21
-
22
- ### 2. Create the lint and format configs
23
-
24
- ```ts
25
- // oxlint.config.ts
26
- import { oxlint } from '@jterrazz/typescript';
27
- import { defineConfig } from 'oxlint';
28
-
29
- export default defineConfig({
30
- extends: [oxlint.node],
31
- });
32
- ```
33
-
34
- ```ts
35
- // oxfmt.config.ts
36
- import { oxfmt } from '@jterrazz/typescript';
37
- import { defineConfig } from 'oxfmt';
38
-
39
- export default defineConfig(oxfmt);
40
- ```
41
-
42
- ### 3. Use the CLI
11
+ ## The CLI
43
12
 
44
13
  ```bash
45
14
  npx typescript build # Build application (ESM + types)
46
15
  npx typescript bundle # Bundle library (ESM + CJS + types)
47
16
  npx typescript start # Run the built application
48
17
  npx typescript dev # Build, run, and rebuild on changes
49
- npx typescript docs # Generate API reference + llms.txt
50
- npx typescript check # Check types, lint, formatting, and unused code
18
+ npx typescript docs # Compile the committed docs/reference tree from source
19
+ npx typescript check # Type-check, lint, format-check, and unused-code in parallel
51
20
  npx typescript fix # Auto-fix lint and formatting issues
52
21
  ```
53
22
 
54
- ## Building
55
-
56
- - **Blazing fast** — Powered by [tsdown](https://tsdown.dev) / [Rolldown](https://rolldown.rs) (Rust)
57
- - **Zero configuration** — Works out of the box
58
- - **Multiple outputs** — ESM + CommonJS + TypeScript declarations
59
- - **Source maps** — Full debugging support
60
-
61
- | Command | Output | Description |
62
- | ------------------- | ----------------- | ------------------------ |
63
- | `typescript build` | `dist/index.js` | ESM bundle |
64
- | | `dist/index.d.ts` | TypeScript declarations |
65
- | `typescript bundle` | `dist/index.js` | ESM bundle |
66
- | | `dist/index.cjs` | CommonJS bundle |
67
- | | `dist/index.d.ts` | TypeScript declarations |
68
- | `typescript start` | — | Runs `dist/index.js` |
69
- | `typescript dev` | `dist/index.js` | Watch + rebuild + run |
70
- | `typescript docs` | `.docs/` | API reference + llms.txt |
71
-
72
- ### Project structure
73
-
74
- ```
75
- your-project/
76
- ├── src/
77
- │ ├── index.ts # Main entry point
78
- │ └── instrumentation.ts # Optional instrumentation entry point
79
- ├── dist/ # Generated files
80
- └── tsconfig.json # Extends this package
81
- ```
82
-
83
- ## Quality checks
84
-
85
- `typescript check` runs four tools in parallel:
86
-
87
- | Tool | Purpose |
88
- | ------ | -------------------- |
89
- | tsc | Type checking |
90
- | oxlint | Linting |
91
- | oxfmt | Formatting |
92
- | knip | Unused code analysis |
93
-
94
- `typescript fix` runs tsc, oxlint (with `--fix`), and oxfmt in parallel (knip excluded).
95
-
96
- ### Lint presets
97
-
98
- | Preset | Use Case |
99
- | ------------- | --------------------------------- |
100
- | `oxlint.node` | Node.js (requires .js extensions) |
101
- | `oxlint.expo` | Expo / React Native |
102
- | `oxlint.next` | Next.js |
103
-
104
- ### Architecture enforcement
105
-
106
- Enforce hexagonal architecture boundaries with the additive `oxlint.hexagonal` preset:
107
-
108
- ```ts
109
- import { oxlint } from '@jterrazz/typescript';
110
- import { defineConfig } from 'oxlint';
111
-
112
- export default defineConfig({
113
- extends: [oxlint.node, oxlint.hexagonal],
114
- });
115
- ```
116
-
117
- Rules enforced:
118
-
119
- - `domain/` cannot import from other layers
120
- - `application/` cannot import infrastructure
121
- - `presentation/ui/` cannot import navigation
122
- - `features/` cannot import other features
123
-
124
- ### Unused code detection
125
-
126
- `typescript check` runs [Knip](https://knip.dev/) to detect unused files, exports, and dependencies. A base config is automatically merged with any project-local `knip.json`, handling common ecosystem patterns:
127
-
128
- - `@jterrazz/*` packages auto-ignored
129
- - Published libraries: `exports`/`types`/`files` rules auto-disabled
130
- - Convention paths (`fixtures/`, `expected/`, `docs/`) auto-ignored
131
- - Plugin dependencies (`*-plugin-*`, `@scope/*`) auto-ignored
132
-
133
- For fine-tuning, create a `knip.json` with only project-specific overrides.
134
-
135
- ## API docs generation
136
-
137
- `typescript docs` reads TSDoc from `src/index.ts` and generates:
138
-
139
- - **Typedoc markdown** — Full API reference under `.docs/`
140
- - **`llms.txt`** — Structured index following the [llms.txt standard](https://llmstxt.org/)
141
- - **`llms-full.txt`** — Complete reference in one file for LLM context windows
142
-
143
- No `typedoc.json` needed. Pair with the shared CI workflow to auto-deploy:
144
-
145
- ```yaml
146
- # .github/workflows/docs.yaml
147
- jobs:
148
- docs:
149
- uses: jterrazz/jterrazz-actions/.github/workflows/docs.yaml@main
150
- ```
151
-
152
23
  ## How it works
153
24
 
154
- The toolchain is fully compiled — no JavaScript in the hot path:
25
+ Fully compiled — no JavaScript in the hot path:
155
26
 
156
27
  | Step | Tool | Language |
157
28
  | ------------ | ---------------------------------------------------------------- | -------- |
@@ -164,6 +35,19 @@ The toolchain is fully compiled — no JavaScript in the hot path:
164
35
  | Unused code | [Knip](https://knip.dev) | Node |
165
36
  | API docs | [Typedoc](https://typedoc.org) | Node |
166
37
 
38
+ ## Documentation
39
+
40
+ The full corpus lives in [`docs/`](docs/):
41
+
42
+ - [Getting started](docs/01-getting-started.md) — install and configure a project.
43
+ - [Building](docs/02-building.md) — `build`, `bundle`, `start`, `dev`.
44
+ - [Quality checks](docs/03-quality-checks.md) — `check` / `fix` and their passes.
45
+ - [Lint presets](docs/04-lint-presets.md) — oxlint presets, `compose`, architecture, knip.
46
+ - [Docs pipeline](docs/05-docs-pipeline.md) — the `typescript docs` compiler.
47
+ - [Repo structure](docs/06-repo-structure.md) — pointer to the shared doctrine; what's TypeScript-specific here.
48
+
49
+ For agents: read the chapters and the generated [`docs/reference/`](docs/reference/) tree straight from the repo, plus the [`skills/jterrazz-typescript`](skills/jterrazz-typescript/SKILL.md) Claude Code skill (the toolchain). The repo-structure doctrine itself is a Claude Code skill too — `jterrazz-repo-structure`, shipped from [`jterrazz-studio`](https://github.com/jterrazz/jterrazz-studio).
50
+
167
51
  ## License
168
52
 
169
53
  MIT © [Jean-Baptiste Terrazzoni](https://github.com/jterrazz)
@@ -190,12 +190,24 @@ run_checks() {
190
190
  checker_pid=$!
191
191
  fi
192
192
 
193
+ # Docs (sync): only in check mode, and only once the project has generated
194
+ # its committed docs (docs/reference/ exists — opt-in by first generation).
195
+ # Delegates to docs.sh --check: regenerate into a temp dir, diff the
196
+ # committed projections. Never duplicates the compiler's logic.
197
+ local docs_pid=""
198
+ local docs_status=0
199
+ if [ "$FIX_MODE" = false ] && [ -d "docs/reference" ]; then
200
+ bash "$SCRIPT_DIR/docs.sh" "$(pwd)" "$PACKAGE_ROOT" --check > "$tmp_dir/docs.log" 2>&1 &
201
+ docs_pid=$!
202
+ fi
203
+
193
204
  # Wait and collect statuses
194
205
  wait $type_pid; local type_status=$?
195
206
  wait $lint_pid; local lint_status=$?
196
207
  wait $format_pid; local format_status=$?
197
208
  [ -n "$knip_pid" ] && { wait $knip_pid; knip_status=$?; }
198
209
  [ -n "$checker_pid" ] && { wait $checker_pid; checker_status=$?; }
210
+ [ -n "$docs_pid" ] && { wait $docs_pid; docs_status=$?; }
199
211
 
200
212
  # Print results — quiet on success, verbose on failure: a tool's captured log
201
213
  # is shown only when it failed, so green output stays byte-identical across
@@ -246,6 +258,16 @@ run_checks() {
246
258
  printf "${GREEN}✓ Passed${NC}\n"
247
259
  fi
248
260
  fi
261
+
262
+ if [ -n "$docs_pid" ]; then
263
+ printf "\n${CYAN_BG}${BRIGHT_WHITE} RUN ${NC} Docs (sync)\n\n"
264
+ if [ $docs_status -ne 0 ]; then
265
+ [ -s "$tmp_dir/docs.log" ] && cat "$tmp_dir/docs.log"
266
+ printf "${RED}✗ Failed with exit code %d${NC}\n" $docs_status
267
+ else
268
+ printf "${GREEN}✓ Passed${NC}\n"
269
+ fi
270
+ fi
249
271
  fi
250
272
 
251
273
  # Summary
@@ -255,7 +277,7 @@ run_checks() {
255
277
  printf "\n${CYAN_BG}${BRIGHT_WHITE} END ${NC} Finalizing quality checks\n\n"
256
278
  fi
257
279
 
258
- if [ $type_status -eq 0 ] && [ $lint_status -eq 0 ] && [ $format_status -eq 0 ] && [ $knip_status -eq 0 ] && [ $checker_status -eq 0 ]; then
280
+ if [ $type_status -eq 0 ] && [ $lint_status -eq 0 ] && [ $format_status -eq 0 ] && [ $knip_status -eq 0 ] && [ $checker_status -eq 0 ] && [ $docs_status -eq 0 ]; then
259
281
  printf "${GREEN}✓ All checks passed${NC}\n"
260
282
  exit 0
261
283
  else
@@ -1,15 +1,35 @@
1
1
  #!/bin/bash
2
- # Generate API reference + llms.txt from TypeScript source.
3
- # Called by: typescript docs
2
+ # Compile the committed docs projection from TypeScript source.
3
+ # Called by: typescript docs [--check] (via bin/typescript.sh) and the
4
+ # Docs (sync) pass in bin/commands/check.sh.
4
5
  #
5
- # Runs typedoc with sensible defaults (no typedoc.json needed),
6
- # then generates llms.txt (index) and llms-full.txt (full reference).
6
+ # Projection (COMMITTED, sync-checked) a single cross-layer compilation of
7
+ # source into docs:
8
+ # docs/reference/ typedoc markdown tree (stale members vanish on regen)
9
+ #
10
+ # Every generated file self-identifies with a first-line header. Generation is
11
+ # deterministic: LC_ALL=C sorts, explicit find | sort, no timestamps — two runs
12
+ # on an unchanged repo are byte-identical (E2E-tested). `--check` regenerates
13
+ # into a temp dir and diffs against the committed reference tree (never touching
14
+ # the committed tree), so it is safe to run in parallel from `typescript check`.
7
15
 
8
16
  set -e
9
17
 
18
+ RED='\033[0;31m'
19
+ GREEN='\033[0;32m'
20
+ NC='\033[0m'
21
+
10
22
  PROJECT_ROOT="$1"
11
23
  PACKAGE_ROOT="$2"
12
- OUT_DIR="$PROJECT_ROOT/.docs"
24
+ MODE="${3:-generate}" # "generate" | "--check"
25
+
26
+ # Header stamped as the first line of every generated file. It is an HTML
27
+ # comment: invisible when the markdown renders, and uniform across the whole
28
+ # reference tree.
29
+ GEN_HEADER="<!-- GENERATED by \`typescript docs\` — DO NOT EDIT -->"
30
+
31
+ # Byte-identical output regardless of the caller's locale.
32
+ export LC_ALL=C
13
33
 
14
34
  TYPEDOC=$(
15
35
  if [ -x "$PACKAGE_ROOT/node_modules/.bin/typedoc" ]; then
@@ -21,69 +41,104 @@ TYPEDOC=$(
21
41
  fi
22
42
  )
23
43
 
24
- # ── Step 1: Run typedoc ──
25
-
26
- "$TYPEDOC" \
27
- --entryPoints "$PROJECT_ROOT/src/index.ts" \
28
- --plugin typedoc-plugin-markdown \
29
- --out "$OUT_DIR" \
30
- --readme none \
31
- --entryFileName index.md \
32
- --hideBreadcrumbs true \
33
- --hidePageHeader true \
34
- --outputFileStrategy members \
35
- --useCodeBlocks true \
36
- --excludeInternal true \
37
- --excludePrivate true \
38
- --excludeProtected true \
39
- --githubPages false \
40
- --indexFormat table \
41
- --parametersFormat table \
42
- --enumMembersFormat table \
43
- --typeDeclarationFormat table \
44
- --tsconfig "$PROJECT_ROOT/tsconfig.json"
45
-
46
- # ── Step 2: Read package name ──
47
-
48
- PKG_NAME=$(node -e "console.log(require('$PROJECT_ROOT/package.json').name)")
49
- PKG_DESC=$(node -e "console.log(require('$PROJECT_ROOT/package.json').description || '')")
50
-
51
- # ── Step 3: Generate llms.txt ──
52
-
53
- {
54
- echo "# $PKG_NAME"
55
- echo ""
56
- if [ -n "$PKG_DESC" ]; then
57
- echo "> $PKG_DESC"
58
- echo ""
44
+ # Entry barrel: src/index.ts for source projects, or src/index.d.ts for
45
+ # JS-shipped packages that carry hand-written declarations (this toolchain
46
+ # itself is one).
47
+ ENTRY=""
48
+ if [ -f "$PROJECT_ROOT/src/index.ts" ]; then
49
+ ENTRY="$PROJECT_ROOT/src/index.ts"
50
+ elif [ -f "$PROJECT_ROOT/src/index.d.ts" ]; then
51
+ ENTRY="$PROJECT_ROOT/src/index.d.ts"
52
+ fi
53
+
54
+ # Guard: nothing to compile without an entry barrel AND a docs/ corpus. In
55
+ # --check mode a project that owns no docs is trivially in sync (exit 0).
56
+ if [ -z "$ENTRY" ] || [ ! -d "$PROJECT_ROOT/docs" ]; then
57
+ if [ "$MODE" = "--check" ]; then
58
+ exit 0
59
59
  fi
60
- echo "## API Reference"
61
- echo ""
62
- sed -n '/^## /,$ p' "$OUT_DIR/index.md"
63
- } > "$OUT_DIR/llms.txt"
64
-
65
- # ── Step 4: Generate llms-full.txt ──
66
-
67
- {
68
- echo "# $PKG_NAME — Full API Reference"
69
- echo ""
70
- if [ -n "$PKG_DESC" ]; then
71
- echo "> $PKG_DESC"
72
- echo ""
60
+ printf "${RED}Error: 'typescript docs' needs an entry barrel and a docs/ directory.${NC}\n" >&2
61
+ [ -z "$ENTRY" ] && printf " No src/index.ts or src/index.d.ts found.\n" >&2
62
+ [ ! -d "$PROJECT_ROOT/docs" ] && printf " No docs/ directory found — create it with your hand-written chapters.\n" >&2
63
+ exit 1
64
+ fi
65
+
66
+ # Prepend the generated-file header as the first line of a file.
67
+ prepend_header() {
68
+ local file="$1"
69
+ local tmp
70
+ tmp="$(mktemp)"
71
+ {
72
+ printf '%s\n' "$GEN_HEADER"
73
+ cat "$file"
74
+ } > "$tmp"
75
+ mv "$tmp" "$file"
76
+ }
77
+
78
+ # Generate the reference projection into <out_base>:
79
+ # <out_base>/reference/ typedoc tree
80
+ generate_docs() {
81
+ local OUT_BASE="$1"
82
+ local REF="$OUT_BASE/reference"
83
+
84
+ mkdir -p "$OUT_BASE"
85
+
86
+ # Step 1 — typedoc markdown tree. cleanOutputDir (default) wipes the target
87
+ # first, so members removed from the source vanish from the committed tree.
88
+ # --gitRevision main pins source links to the branch, not a commit SHA:
89
+ # a SHA link would name the commit BEFORE the one carrying the regenerated
90
+ # docs, so every docs-carrying commit would invalidate its own projection
91
+ # (bootstrap paradox — the sync check could never be green at HEAD).
92
+ "$TYPEDOC" \
93
+ --entryPoints "$ENTRY" \
94
+ --plugin typedoc-plugin-markdown \
95
+ --out "$REF" \
96
+ --gitRevision main \
97
+ --readme none \
98
+ --entryFileName index.md \
99
+ --hideBreadcrumbs true \
100
+ --hidePageHeader true \
101
+ --outputFileStrategy members \
102
+ --useCodeBlocks true \
103
+ --excludeInternal true \
104
+ --excludePrivate true \
105
+ --excludeProtected true \
106
+ --githubPages false \
107
+ --indexFormat table \
108
+ --parametersFormat table \
109
+ --enumMembersFormat table \
110
+ --typeDeclarationFormat table \
111
+ --tsconfig "$PROJECT_ROOT/tsconfig.json"
112
+
113
+ # Step 2 — stamp every reference markdown file with the generated header.
114
+ local f
115
+ while IFS= read -r f; do
116
+ prepend_header "$f"
117
+ done < <(find "$REF" -name '*.md' | LC_ALL=C sort)
118
+ }
119
+
120
+ # ── --check: regenerate into a temp dir, diff against the committed tree ──
121
+ if [ "$MODE" = "--check" ]; then
122
+ TMP_BASE=$(mktemp -d)
123
+ trap 'rm -rf "$TMP_BASE"' EXIT
124
+
125
+ # Typedoc chatter is noise here — the diff is the signal.
126
+ generate_docs "$TMP_BASE" > /dev/null 2>&1
127
+
128
+ OUT_OF_SYNC=()
129
+ diff -r -q "$TMP_BASE/reference" "$PROJECT_ROOT/docs/reference" > /dev/null 2>&1 || OUT_OF_SYNC+=("docs/reference/")
130
+
131
+ if [ ${#OUT_OF_SYNC[@]} -gt 0 ]; then
132
+ printf "${RED}Docs are out of sync:${NC}\n"
133
+ for p in "${OUT_OF_SYNC[@]}"; do
134
+ printf " ${RED}✗${NC} %s\n" "$p"
135
+ done
136
+ printf "Run 'typescript docs' to regenerate.\n"
137
+ exit 1
73
138
  fi
139
+ exit 0
140
+ fi
74
141
 
75
- for f in "$OUT_DIR"/index.md \
76
- "$OUT_DIR"/functions/*.md \
77
- "$OUT_DIR"/classes/*.md \
78
- "$OUT_DIR"/interfaces/*.md \
79
- "$OUT_DIR"/type-aliases/*.md \
80
- "$OUT_DIR"/variables/*.md; do
81
- [ -f "$f" ] || continue
82
- echo "---"
83
- echo ""
84
- cat "$f"
85
- echo ""
86
- done
87
- } > "$OUT_DIR/llms-full.txt"
88
-
89
- echo "Generated $OUT_DIR/llms.txt and $OUT_DIR/llms-full.txt"
142
+ # ── generate: write the committed projection in place ──
143
+ generate_docs "$PROJECT_ROOT/docs"
144
+ echo "Generated docs/reference/"
package/bin/typescript.sh CHANGED
@@ -88,11 +88,19 @@ case "$COMMAND" in
88
88
  ;;
89
89
 
90
90
  docs)
91
- printf "${CYAN_BG}${BRIGHT_WHITE} TYPESCRIPT ${NC} Generating API docs...\n\n"
91
+ if [ "${1:-}" = "--check" ]; then
92
+ printf "${CYAN_BG}${BRIGHT_WHITE} TYPESCRIPT ${NC} Checking docs are in sync...\n\n"
92
93
 
93
- bash "$SCRIPT_DIR/commands/docs.sh" "$PROJECT_ROOT" "$PACKAGE_ROOT"
94
+ bash "$SCRIPT_DIR/commands/docs.sh" "$PROJECT_ROOT" "$PACKAGE_ROOT" --check
94
95
 
95
- printf "\n${GREEN}Docs generated at .docs/${NC}\n"
96
+ printf "${GREEN}Docs are in sync${NC}\n"
97
+ else
98
+ printf "${CYAN_BG}${BRIGHT_WHITE} TYPESCRIPT ${NC} Generating API docs...\n\n"
99
+
100
+ bash "$SCRIPT_DIR/commands/docs.sh" "$PROJECT_ROOT" "$PACKAGE_ROOT"
101
+
102
+ printf "\n${GREEN}Docs generated at docs/${NC}\n"
103
+ fi
96
104
  ;;
97
105
 
98
106
  check|fix)
@@ -107,7 +115,7 @@ case "$COMMAND" in
107
115
  printf " bundle Bundle library (ESM + CJS + types)\n"
108
116
  printf " start Run the built application\n"
109
117
  printf " dev Build, run, and rebuild on changes\n"
110
- printf " docs Generate API reference + llms.txt from TSDoc\n"
118
+ printf " docs Generate the committed docs/reference tree; --check verifies sync\n"
111
119
  printf " check Check types, lint, formatting, and unused code\n"
112
120
  printf " fix Auto-fix lint and formatting issues\n\n"
113
121
  printf "Examples:\n"
@@ -116,6 +124,7 @@ case "$COMMAND" in
116
124
  printf " typescript start\n"
117
125
  printf " typescript dev\n"
118
126
  printf " typescript docs\n"
127
+ printf " typescript docs --check\n"
119
128
  printf " typescript check\n"
120
129
  printf " typescript fix\n"
121
130
  exit 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jterrazz/typescript",
3
- "version": "6.2.0",
3
+ "version": "7.1.0",
4
4
  "author": "Jean-Baptiste Terrazzoni <contact@jterrazz.com>",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,4 +9,7 @@ export default defineConfig({
9
9
  trailingComma: 'all',
10
10
  bracketSpacing: true,
11
11
  endOfLine: 'lf',
12
+ /* `typescript docs` writes the byte-for-byte typedoc tree to docs/reference/ —
13
+ * formatting it would fight the Docs (sync) pass, so skip it by default. */
14
+ ignorePatterns: ['docs/reference'],
12
15
  });
@@ -19,5 +19,17 @@ export default defineConfig({
19
19
  'react/jsx-curly-brace-presence': 'off',
20
20
  'react/jsx-max-depth': 'off',
21
21
  'unicorn/no-nested-ternary': 'off',
22
+ // -- Rules that fight the Next idiom itself, not bad code --
23
+ // A Next file interleaves `export const metadata`, types, and the
24
+ // Default export — exports-at-end is not how these files read.
25
+ 'import/exports-last': 'off',
26
+ // Immutable serialization maps (`.map((x) => ({ ...x, url }))`) are
27
+ // The dominant React idiom; the Object.assign form is strictly worse
28
+ // For the build-time collection sizes a site actually maps over.
29
+ 'oxc/no-map-spread': 'off',
30
+ // Client components are browser-only code that means `window`;
31
+ // Asking them to say `globalThis` trades clarity for portability
32
+ // They will never need.
33
+ 'unicorn/prefer-global-this': 'off',
22
34
  },
23
35
  });