commit 47b729f6c7671251b9324cb22c583177bc7a5266 Author: Aric Camarata Date: Thu May 28 12:30:48 2026 -0400 chore: scaffold @acamarata/tsconfig 0.1.0 Four JSON tsconfig presets for acamarata packages: base (strict/ES2022/bundler), library (declaration output), node (CommonJS), react (jsx:react-jsx/DOM). JSON-only package — no build step. Includes validation script and CI workflow. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..7be6941 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,64 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + name: Validate JSON (Node ${{ matrix.node-version }}) + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20, 22, 24] + steps: + - uses: actions/checkout@v4 + + - name: Setup Node ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'pnpm' + + - name: Enable corepack + run: corepack enable + + - name: Install dependencies + run: pnpm install + + - name: Validate JSON configs + run: pnpm test + + pack-check: + name: Pack check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node 24 + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: 'pnpm' + + - name: Enable corepack + run: corepack enable + + - name: Install dependencies + run: pnpm install + + - name: Pack + run: npm pack + + - name: Verify pack contents + run: | + tar -tf *.tgz | sort + tar -tf *.tgz | grep -q "package/tsconfig.base.json" || (echo "MISSING: tsconfig.base.json" && exit 1) + tar -tf *.tgz | grep -q "package/tsconfig.library.json" || (echo "MISSING: tsconfig.library.json" && exit 1) + tar -tf *.tgz | grep -q "package/tsconfig.node.json" || (echo "MISSING: tsconfig.node.json" && exit 1) + tar -tf *.tgz | grep -q "package/tsconfig.react.json" || (echo "MISSING: tsconfig.react.json" && exit 1) + tar -tf *.tgz | grep -q "package/README.md" || (echo "MISSING: README.md" && exit 1) + tar -tf *.tgz | grep -q "package/LICENSE" || (echo "MISSING: LICENSE" && exit 1) + echo "Pack check passed" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6a241d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +node_modules/ +.DS_Store +*.tgz +.claude/ +.vscode/* +.idea/ +.codex/ +.cursor/ +.aider/ +.aider.chat.history.md +.continue/ +.windsurf/ +.gemini/ +.codeium/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..e69de29 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..64e5bc1 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,19 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.1.0] - 2026-05-28 + +### Added + +- `tsconfig.base.json`: strict base config with ES2022 target, bundler module resolution, `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes` +- `tsconfig.library.json`: extends base, adds declaration output for publishable libraries (ESNext module, `declarationMap`, `sourceMap`) +- `tsconfig.node.json`: extends base, targets CommonJS for Node.js tools and scripts +- `tsconfig.react.json`: extends base, adds `jsx: react-jsx` and DOM lib for React applications +- JSON validation script (`scripts/validate-json.mjs`) to verify all variants parse and contain expected keys +- CI workflow validating JSON on Node 20, 22, and 24 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f8c021c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Aric Camarata + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4466f65 --- /dev/null +++ b/README.md @@ -0,0 +1,66 @@ +# @acamarata/tsconfig + +Shared TypeScript configs for acamarata packages. Four variants covering the common cases: a strict base, a publishable library, a Node.js tool, and a React app. + +## Install + +```sh +pnpm add -D @acamarata/tsconfig +``` + +## Variants + +| Config | Extends | Use case | +|--------|---------|----------| +| `tsconfig.base.json` | — | Strict base. All other variants extend this. Use it directly only if none of the others fit. | +| `tsconfig.library.json` | base | Publishable npm libraries. ESNext modules, `declaration: true`, `declarationMap: true`, `sourceMap: true`. | +| `tsconfig.node.json` | base | Node.js tools and scripts. CommonJS output, `@types/node` included. | +| `tsconfig.react.json` | base | React apps. Adds `jsx: react-jsx` and the DOM lib. | + +## Usage + +In your `tsconfig.json`, set `extends` to the variant that matches your project: + +```json +{ + "extends": "@acamarata/tsconfig/tsconfig.library.json", + "compilerOptions": { + "rootDir": "src" + }, + "include": ["src"] +} +``` + +For a Node.js CLI tool: + +```json +{ + "extends": "@acamarata/tsconfig/tsconfig.node.json", + "compilerOptions": { + "rootDir": "src" + }, + "include": ["src"] +} +``` + +For a React app: + +```json +{ + "extends": "@acamarata/tsconfig/tsconfig.react.json", + "include": ["src"] +} +``` + +## What the base config enables + +- `strict: true` — the full strict family of checks +- `skipLibCheck: true` — skips checking declaration files in `node_modules` +- `noUncheckedIndexedAccess: true` — array index access includes `undefined` in the type +- `exactOptionalPropertyTypes: true` — distinguishes `{ x?: string }` from `{ x: string | undefined }` +- `target: ES2022` — modern output; no polyfill overhead for `async/await`, `class fields`, `at()` +- `moduleResolution: bundler` — resolves `.ts` extensions naturally in bundler pipelines (tsup, Vite, esbuild) + +## License + +MIT diff --git a/package.json b/package.json new file mode 100644 index 0000000..d916a1e --- /dev/null +++ b/package.json @@ -0,0 +1,48 @@ +{ + "name": "@acamarata/tsconfig", + "version": "0.1.0", + "description": "Shared TypeScript configs for acamarata packages. Four variants: base, library, node, and react.", + "author": "Aric Camarata", + "license": "MIT", + "exports": { + "./tsconfig.base.json": "./tsconfig.base.json", + "./tsconfig.library.json": "./tsconfig.library.json", + "./tsconfig.node.json": "./tsconfig.node.json", + "./tsconfig.react.json": "./tsconfig.react.json", + "./package.json": "./package.json" + }, + "files": [ + "tsconfig.base.json", + "tsconfig.library.json", + "tsconfig.node.json", + "tsconfig.react.json", + "README.md", + "CHANGELOG.md", + "LICENSE" + ], + "scripts": { + "test": "node scripts/validate-json.mjs" + }, + "engines": { + "node": ">=20" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/acamarata/tsconfig.git" + }, + "homepage": "https://github.com/acamarata/tsconfig#readme", + "bugs": { + "url": "https://github.com/acamarata/tsconfig/issues" + }, + "keywords": [ + "typescript", + "tsconfig", + "config", + "acamarata" + ], + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "packageManager": "pnpm@10.11.1" +} diff --git a/scripts/validate-json.mjs b/scripts/validate-json.mjs new file mode 100644 index 0000000..e3a481e --- /dev/null +++ b/scripts/validate-json.mjs @@ -0,0 +1,56 @@ +/** + * Purpose: Validate that all tsconfig JSON files parse without error and contain expected keys. + * Inputs: none — reads tsconfig.*.json from package root + * Outputs: pass/fail per file to stdout; exits 1 on any failure + * Constraints: runs with Node built-in fs — no deps + */ +import { readFileSync } from 'fs'; + +const variants = [ + { file: 'tsconfig.base.json', requiredKeys: ['compilerOptions'] }, + { file: 'tsconfig.library.json', requiredKeys: ['extends', 'compilerOptions'] }, + { file: 'tsconfig.node.json', requiredKeys: ['extends', 'compilerOptions'] }, + { file: 'tsconfig.react.json', requiredKeys: ['extends', 'compilerOptions'] }, +]; + +const compilerOptionChecks = { + 'tsconfig.base.json': ['strict', 'skipLibCheck', 'noUncheckedIndexedAccess', 'exactOptionalPropertyTypes'], + 'tsconfig.library.json': ['declaration', 'declarationMap', 'sourceMap', 'outDir', 'module'], + 'tsconfig.node.json': ['declaration', 'declarationMap', 'sourceMap', 'outDir', 'module'], + 'tsconfig.react.json': ['declaration', 'declarationMap', 'sourceMap', 'outDir', 'jsx'], +}; + +let failed = false; + +for (const { file, requiredKeys } of variants) { + try { + const content = readFileSync(new URL(`../${file}`, import.meta.url), 'utf8'); + const parsed = JSON.parse(content); + + for (const key of requiredKeys) { + if (!(key in parsed)) { + console.error(`FAIL ${file}: missing top-level key "${key}"`); + failed = true; + } + } + + const expectedCompilerOptions = compilerOptionChecks[file] ?? []; + for (const opt of expectedCompilerOptions) { + if (!(opt in (parsed.compilerOptions ?? {}))) { + console.error(`FAIL ${file}: missing compilerOptions.${opt}`); + failed = true; + } + } + + if (!failed) { + console.log(`PASS ${file}`); + } + } catch (err) { + console.error(`FAIL ${file}: ${err.message}`); + failed = true; + } +} + +if (failed) { + process.exit(1); +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..7b9a273 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "target": "ES2022", + "moduleResolution": "bundler", + "strict": true, + "skipLibCheck": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "allowImportingTsExtensions": false + } +} diff --git a/tsconfig.library.json b/tsconfig.library.json new file mode 100644 index 0000000..0714ea5 --- /dev/null +++ b/tsconfig.library.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "ESNext", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "dist" + } +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..c7ae331 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "CommonJS", + "target": "ES2022", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "dist", + "types": ["node"] + } +} diff --git a/tsconfig.react.json b/tsconfig.react.json new file mode 100644 index 0000000..e5274ed --- /dev/null +++ b/tsconfig.react.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.base.json", + "compilerOptions": { + "module": "ESNext", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "dist", + "jsx": "react-jsx", + "lib": ["ES2022", "DOM", "DOM.Iterable"] + } +}