Files
copilotkit__copilotkit/packages/react-textarea/tsdown.config.ts
Alem Tuzlak d4a9bc3355 fix(react): resolve package types under bundler/node16/nodenext (#5264)
## Problem

The published declaration files for `@copilotkit/react-core`,
`@copilotkit/react-ui`, and `@copilotkit/react-textarea` contain imports
that TypeScript cannot resolve, so **`attw` (Are The Types Wrong)
reports `InternalResolutionError` across every resolution mode**
(`node10` / `node16` / `bundler`). In `@copilotkit/react-core` this was
being **masked in CI** by `--ignore-rules internal-resolution-error` on
the package's `attw` script — so the existing `check:packages` gate
looked green while consumers under `moduleResolution:
bundler`/`node16`/`nodenext` got broken types (the symptom reported in
#3324: `has no exported member 'useAgent'`, etc.).

Two distinct artifacts leaked into the emitted `.d.ts` / `.d.cts` /
`.d.mts` (neither affects the JS bundles):

1. **Side-effect CSS imports** — `import "./index.css"` is intentionally
kept in the JS so styles auto-load for bundler consumers, but
`rolldown-plugin-dts` also left it in the declarations, where TypeScript
can't resolve a `.css` as a typed module.
2. **Extensionless relative `./context` import** —
`@copilotkit/react-core/v2/headless` re-exports the externalized context
module; the JS bundle correctly externalizes it to
`@copilotkit/react-core/v2/context`, but the declaration kept the
relative `./context`, which is invalid in ESM declarations.

> Note: this is **not** the missing-`exports.types`-condition theory
from #3324. tsdown deliberately relies on co-located `.d.mts`/`.d.cts`
siblings; `@copilotkit/core` already resolves cleanly. The real defects
are the two leaked imports above.

## Fix

A small tsdown `build:done` hook post-processes the emitted declarations
**on disk** (after every format is written, so it catches both `.d.mts`
and `.d.cts`):

- strips side-effect CSS imports from declarations (JS keeps them);
- rewrites the relative `./context` import to the
`@copilotkit/react-core/v2/context` package path (matching how the JS
bundle externalizes it).

Also:
- **Removed the `--ignore-rules internal-resolution-error` band-aid**
from `react-core`'s `attw` script so the existing CI gate validates for
real.
- **Dropped the dead `codeSplitting` option** from the UMD configs —
tsdown never reads it (it's a rolldown-only key), and it was failing
`tsc` in the configs that type-check themselves. UMD output is unchanged
(single file).

## Verification

- All three packages build; **no CSS or relative-`./context` imports
remain in any declaration**, while the JS bundles still contain them
(styles auto-load preserved).
- `attw` + `publint` pass for all packages **with no suppression**
(`react-core`'s `/v2`, `/v2/headless`, `/v2/context` are green for
node16-cjs/esm/bundler).
- Unit tests pass.
- A standalone consumer project (real tarball install, `skipLibCheck:
false`) type-checks the public APIs — including `useAgent` /
`useFrontendTool` / `useConfigureSuggestions` — cleanly under **both
`bundler` and `nodenext`**, and the headless↔context class is nominally
identical.

## Out of scope (follow-ups)

- `@copilotkit/react-native`: its `--ignore-rules
internal-resolution-error` currently suppresses nothing (no IRE) and it
has a separate `NoResolution` flag.
- `@copilotkit/vue`: a large, genuine set of `.vue`/relative-import
declaration errors unrelated to this change.

Relates to #3324.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-09 18:42:07 +02:00

89 lines
2.8 KiB
TypeScript

/// <reference types="node" />
import { defineConfig } from "tsdown";
import fs from "fs";
import path from "path";
// Side-effect CSS imports are kept in the JS output so styles auto-load for
// bundler consumers, but rolldown-plugin-dts also leaves them in the emitted
// declaration files, where TypeScript cannot resolve a `.css` as a typed module
// (an `attw` InternalResolutionError). Strip them from declarations only, via the
// `build:done` hook so every format's declarations are post-processed on disk.
const stripCssTypeImports = (dir: string) => {
const cssImport = /^[ \t]*import\s+["'][^"']+\.css["'];?[ \t]*\r?\n/gm;
const walk = (current: string) => {
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
const full = path.join(current, entry.name);
if (entry.isDirectory()) {
walk(full);
} else if (/\.d\.[cm]?ts$/.test(entry.name)) {
const code = fs.readFileSync(full, "utf8");
const next = code.replace(cssImport, "");
if (next !== code) fs.writeFileSync(full, next);
}
}
};
if (fs.existsSync(dir)) walk(dir);
};
export default defineConfig([
{
entry: ["src/index.tsx"],
format: ["esm", "cjs"],
dts: true,
sourcemap: true,
target: "es2022",
outDir: "dist",
hooks: {
"build:done": () => stripCssTypeImports(path.resolve("dist")),
},
external: ["react", "react-dom"],
exports: {
customExports: (exports) => ({
...exports,
"./styles.css": "./dist/index.css",
}),
},
},
{
entry: ["src/index.tsx"],
format: ["umd"],
globalName: "CopilotKitReactTextarea",
sourcemap: true,
target: "es2018",
outDir: "dist",
external: [
"react",
"react-dom",
"@copilotkit/react-core",
"@copilotkit/shared",
"@copilotkit/runtime-client-gql",
],
outputOptions(options) {
options.codeSplitting = false;
options.entryFileNames = "[name].umd.js";
options.globals = {
react: "React",
"react-dom": "ReactDOM",
"react/jsx-runtime": "ReactJsxRuntime",
"@copilotkit/react-core": "CopilotKitReactCore",
"@copilotkit/shared": "CopilotKitShared",
"@copilotkit/runtime-client-gql": "CopilotKitRuntimeClientGQL",
slate: "Slate",
"slate-react": "SlateReact",
"slate-history": "SlateHistory",
"tailwind-merge": "tailwindMerge",
clsx: "clsx",
"@emotion/css": "emotionCss",
cmdk: "cmdk",
"@radix-ui/react-slot": "RadixReactSlot",
"class-variance-authority": "classVarianceAuthority",
"@radix-ui/react-label": "RadixReactLabel",
"@mui/material/Chip/Chip.js": "MuiChip",
"@mui/material/Avatar/Avatar.js": "MuiAvatar",
"lodash.merge": "lodashMerge",
};
return options;
},
},
]);