fix(create-wcb): add tsconfig.json for tsc

This commit is contained in:
ayo 2026-07-25 12:11:40 +02:00
parent 751067d367
commit 2adc4e8d43
5 changed files with 102 additions and 3 deletions

View file

@ -16,10 +16,11 @@ This file provides guidance to AI coding agents when working with code in this r
- `pnpm build``tsc` emits `.d.ts` types, then `copy:source` uses esbuild to minify/bundle `src/*.js` + `src/utils/*` into `dist/` as ESM. **`dist/` is what gets published; `src/` is shipped as the readable source.**
- `pnpm size-limit` — enforces the per-file byte budgets declared in `package.json` `size-limit` (each `dist/` file has its own, e.g. `WebComponent.js` ≤ 2.05 KB, `html.js` ≤ 0.6 KB, each util ≤ 0.50.8 KB). Keep additions tiny; this gate is a core project value. `size-change-log.md` is the running record of how each change moved the base-class bundle and why; its latest row's min+brotli figure is the single source of truth for the base-class size, so `docs/src/content/docs/guides/library-size.md` must be updated in lockstep whenever a new row lands.
- `pnpm test:types` — builds, then type-checks the TS type tests (`test/types/typed-props.test-d.ts`) and the `demo/examples/typed-props/` example. `this.props` is typed by passing the props shape as a class type argument: `class X extends WebComponent<typeof props>`.
- `pnpm test:template` — scaffolds `create/template/` into a temp directory with the real scaffolder, links this repo in as `web-component-base`, and type-checks it with the template's own `tsconfig.json` (`scripts/check-template.js`). It resolves through the root `exports` map, so it catches a template that can't resolve the package — the failure mode that leaves every `this.props` access an error while `vite build` still exits 0.
- `pnpm docs` — run the Astro docs site (`docs/` workspace) locally.
- `pnpm demo` — run the Vite examples showcase (`demo/` workspace) locally; `pnpm demo:build` builds it.
- `pnpm -F storybook dev` — run the Storybook testing ground (`storybook/`) locally; `pnpm -F storybook build` builds it. Both run `cem analyze` first to regenerate `custom-elements.json`. The CEM config imports the plugin from the **published subpath** (`web-component-base/cem-plugin` → `dist/`), so run `pnpm build` at the root first.
- `pnpm test:e2e` — run the browser e2e specs (`test/e2e/`) via Vitest browser mode (Playwright). Defaults to Chromium; `pnpm test:e2e:firefox` / `:webkit` / `:all` target the other engines (the `E2E_BROWSERS` env var, comma-separated, selects instances). `pnpm test:all` runs unit + types + e2e across all engines.
- `pnpm test:e2e` — run the browser e2e specs (`test/e2e/`) via Vitest browser mode (Playwright). Defaults to Chromium; `pnpm test:e2e:firefox` / `:webkit` / `:all` target the other engines (the `E2E_BROWSERS` env var, comma-separated, selects instances). `pnpm test:all` runs unit + types + template + e2e across all engines.
- `pnpm create wcb` (the `create/` workspace package) scaffolds a new component project from `create/template/`. `create/` is a separate publishable package, not part of the `web-component-base` bundle.
pnpm is mandatory (a `preinstall` `only-allow pnpm` guard enforces it). This is a pnpm workspace with three sub-packages: `docs/` (Astro docs site), `demo/` (Vite examples showcase, which consumes the root `web-component-base` package as a `workspace:*` dependency), and `storybook/` (Storybook testing ground — it writes no components of its own, it builds stories over the `demo/examples/` components so the CEM plugin is exercised against real ones). The runnable example sources live in `demo/examples/` and import the package by name (`web-component-base`), not via relative `src/` paths.
@ -56,7 +57,7 @@ Any change to observable behavior ships as one unit — code alone is an incompl
3. **Documentation** — the guide under `docs/src/content/docs/guides/` that doubles as the behavioral spec. For a breaking change, also update the `README.md` banner with the migration consumers have to perform.
4. **Size budget**`pnpm size-limit` stays green. If an addition genuinely needs more headroom, raise the budget in `package.json` deliberately and say so in the change description; never let it drift silently. Whenever the change moves the base-class bundle, record it as a new row in `size-change-log.md` **and** update the headline size in `docs/src/content/docs/guides/library-size.md` to match that row's new min+brotli figure — the two must never disagree.
Verify with `pnpm test:all` (unit + types + e2e across all engines) before calling the change done.
Verify with `pnpm test:all` (unit + types + template + e2e across all engines) before calling the change done.
## Testing notes

View file

@ -45,6 +45,7 @@ describe('create-wcb', () => {
'src/my-button.ts',
'custom-elements-manifest.config.mjs',
'vite-lib.config.ts',
'tsconfig.json',
'pnpm-workspace.yaml',
'README.md',
])

View file

@ -0,0 +1,21 @@
// TypeScript config for the component source. `npm run build:lib` runs
// vite-plugin-dts against this to emit `.d.ts` files next to the bundles.
//
// `moduleResolution: "bundler"` is the load-bearing line. Without it tsc falls
// back to classic resolution, never looks in node_modules, and the
// `web-component-base` import fails which in turn makes every `this.props`
// access an error, because the base class it resolves to is an error type.
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"strict": true,
"skipLibCheck": true,
"declaration": true,
"emitDeclarationOnly": true,
"outDir": "dist"
},
"include": ["src"]
}

View file

@ -43,7 +43,8 @@
"test:e2e:webkit": "E2E_BROWSERS=webkit vitest --run --config vitest.e2e.config.mjs",
"test:e2e:all": "vitest --run --config vitest.e2e.config.mjs",
"test:types": "pnpm run build && tsc -p test/types/tsconfig.json && tsc -p demo/examples/typed-props/tsconfig.json",
"test:all": "pnpm test && pnpm test:types && pnpm test:e2e:all",
"test:template": "pnpm run build && node ./scripts/check-template.js",
"test:all": "pnpm test && pnpm test:types && pnpm test:template && pnpm test:e2e:all",
"demo": "pnpm -F demo dev",
"demo:build": "pnpm -F demo build",
"docs": "pnpm -F docs start",

75
scripts/check-template.js Normal file
View file

@ -0,0 +1,75 @@
/**
* @license MIT <https://opensource.org/licenses/MIT>
*
* Type-checks the `create/` scaffold the way a user gets it: runs the real
* scaffolder into a temp directory, links this repo in as
* `web-component-base`, and runs `tsc` with the template's own
* `tsconfig.json`.
*
* The link resolves through the root `package.json` `exports` map, so this
* exercises the same module resolution a scaffolded project does which is
* what catches a template that ships no `tsconfig.json`, or one whose
* `moduleResolution` can't read an `exports` map. Both leave the
* `web-component-base` import unresolvable and every `this.props` access an
* error, while `vite build` still exits 0.
*
* Run via `pnpm test:template` (part of `pnpm test:all`). Needs `dist/` built.
*/
import { execFileSync } from 'node:child_process'
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
const repoRoot = fileURLToPath(new URL('..', import.meta.url))
const scaffolder = path.join(repoRoot, 'create', 'index.js')
const tsc = path.join(repoRoot, 'node_modules', '.bin', 'tsc')
const PROJECT = 'template-check'
if (!fs.existsSync(path.join(repoRoot, 'dist', 'index.d.ts'))) {
console.error('check-template: dist/index.d.ts missing — run `pnpm build`')
process.exit(1)
}
const workDir = fs.mkdtempSync(path.join(os.tmpdir(), 'wcb-template-'))
const root = path.join(workDir, PROJECT)
try {
execFileSync(process.execPath, [scaffolder, PROJECT], {
cwd: workDir,
stdio: 'pipe',
})
// Stand in for `npm install` of the peer dependency: the scaffold resolves
// `web-component-base` to this repo, exports map and all.
fs.mkdirSync(path.join(root, 'node_modules'), { recursive: true })
fs.symlinkSync(
repoRoot,
path.join(root, 'node_modules', 'web-component-base'),
'dir'
)
// The template emits declarations during `build:lib`; here we only want the
// diagnostics, and `emitDeclarationOnly` may not be combined with `noEmit`.
execFileSync(
tsc,
['-p', 'tsconfig.json', '--emitDeclarationOnly', 'false', '--noEmit'],
{
cwd: root,
stdio: 'inherit',
}
)
console.log('check-template: scaffold type-checks clean')
} catch {
console.error(
`\ncheck-template: the scaffolded template does not type-check.\n` +
`A user running \`npm create wcb@latest\` sees these errors on their\n` +
`first \`npm run build:lib\`. Check create/template/tsconfig.json.`
)
process.exit(1)
} finally {
fs.rmSync(workDir, { recursive: true, force: true })
}