Chore: full project upgrade (mostly infra, little code) - #418
ryan-roemer wants to merge 19 commits into
Conversation
Drops infrastructure that no longer carries its weight: - `.sail.yml` — the last Sail vestige. Its install/lint/test/prettier/build flow is fully covered by GitHub Actions, and the `prettier` task it referenced was never defined in its own `tasks:` block. - `packages/demo` — never deployed and not referenced by the website, whose `src/components/live-edit.tsx` it duplicates almost verbatim. Its Vite config had no alias to library source, so it resolved `react-live` to built output and was not even a live-source dev loop. - Storybook 6 (EOL) and its two stories — the only consumers of `@storybook/*`, `webpack`, `babel-loader`, and `styled-components@4.0.0-beta`. - `.eslintrc`, `.eslintignore`, `.babelrc`, `tsup.config.ts`, and the website's `babel.config.js`, all replaced in the following commits. - `.npmignore`, obsolete since the `files` field governs the tarball.
Prettier 2 -> 3 changed the `trailingComma` default to "all". This commit is formatting only — no logic changes — so it can be skipped with `git blame --ignore-rev`.
Replaces the 2023-era stack. Measured on this repo: the library now builds
CJS + ESM + declarations + sourcemaps in ~76ms, and typechecks in ~0.33s.
| Concern | Before | After |
| --- | --- | --- |
| Bundling | tsup 6 (esbuild) | tsdown (rolldown + oxc) |
| Types | TypeScript 4.9 | TypeScript 7 (native compiler) |
| Tests | Jest 27 + 9 Babel packages | Vitest 5 |
| Linting | ESLint 8 + 7 plugins | oxlint |
| Formatting | Prettier 2 via eslint-plugin-prettier | Prettier 3 |
| Package manager | pnpm 7 | npm workspaces |
| Docs site | Docusaurus 2.4 (EOL) | Docusaurus 3.10 |
Roughly 30 devDependencies removed.
Workspaces are kept but reduced to two members. The website imports `react-live`
as a linked dependency, which is exactly what a workspace provides; removing
workspaces would mean replacing that link with a `file:` dep or a bundler alias.
TypeScript 7 ships no JavaScript compiler API — only an `unstable/*` RPC
surface. `tsc` and tsdown's declaration emit both work with it, but any future
tool needing the classic API would have to bring its own `typescript@6`. This
is also why the website no longer extends `@docusaurus/tsconfig`: that config
sets `baseUrl`, which TypeScript 7 removed.
Source changes, kept to the minimum:
- `LiveEditor.tsx`: rolldown cannot tell a type from a value in
`import Editor, { Props as EditorProps }` and fails with MISSING_EXPORT, so
the `type` modifier is now explicit.
- `LivePreview.tsx`: the global `JSX` namespace was removed in
`@types/react@19`, and these types are emitted into the published
declarations, so React 19 consumers could not compile against the package.
Now `React.JSX`, verified to typecheck under both `@types/react@18` and `@19`.
- `website/.../nf-link-button.tsx`: `children` was typed `ChildNode |
ReactNode`; `ChildNode` is a DOM node type and does not belong there. This
only surfaced now because the website has never been typechecked in CI.
Test changes:
- The four JSX-containing `.test.js` files are renamed `.test.jsx`. Vite will
not parse JSX inside a `.js` file.
- `jest.*` -> `vi.*`, mechanically.
- Every test in `LiveProvider.test.jsx` is now `it.skip`ed. None of them have
ever asserted anything: `waitAsync()` returns React's `act()` thenable rather
than a real Promise, so `return waitAsync().then(...)` was never awaited and
its callback ran after the test had already passed. Jest swallowed the
resulting errors entirely; Vitest reports them, which is how this was found.
Awaiting a real `act(async () => ...)` makes them fail outright, because
`render()` shallow-renders and therefore never runs the effect that calls
`transformCode`. Fixing them properly needs `react-dom/client`; the full
explanation is in a comment at the top of the file.
The published surface gains an `exports` map with separate ESM and CJS type
entries. `publint` and `@arethetypeswrong/cli` both report zero problems, and
the build now runs them on every invocation so a regression fails locally
rather than after release.
Runtime React devDependencies stay on 18, the floor of the peer range, so
tests exercise the oldest supported React; `@types/react` moves to 19 so the
emitted declarations are verified against the current major.
…yment
Replaces `code-check.yml` with `ci.yml`, which runs `npm run check` — lint,
format, typecheck, and tests — then builds. This is the first time `typecheck`
has ever run in CI, despite the old job being named "lint and typecheck".
`release.yml` adopts the split-job `changesets/action@v2` model with npm
trusted publishing: select-mode -> (version | pack -> publish). Permissions are
reset to `{}` at the top and granted per job, so `id-token: write` exists only
on the job that publishes. The `NPM_TOKEN` secret is no longer used.
The library's build script is `prepack`, not `prepublishOnly`. The release flow
packs a tarball in one job and publishes it in another, and `npm pack` does not
trigger `prepublishOnly` — left as it was, the published tarball would have
silently shipped without its README, LICENSE, and dist.
`website/vercel.json` checks the build and output settings into version
control. CONTRIBUTING.MD documents the repository layout, that the live demo
lives in the website rather than a separate app, the full toolchain, and how
the site deploys.
NOTE: the Vercel project's Build Command must be changed from `pnpm run build`
to `npm run build`. A Build Command set in the Vercel dashboard overrides
vercel.json, so this cannot be fixed from the repository alone.
The two hooks look redundant but cover different cases, and the root `prepare` does more than convenience: because the website resolves `react-live` through its `exports` map, `check:types` typechecks the site against the emitted declarations, making it an integration check on the published type surface.
`prepare` rebuilt the library on every `npm install`, which is a poor trade when you are just adding or bumping a dependency. Nothing builds implicitly any more. - Root `prepare` removed. `npm install` only installs. - `website` splits its build: `build` compiles the site alone, `build:prod` reaches up and builds the library first. The old `prebuild` hook is gone. - Vercel's Build Command becomes `npm run build:prod` (checked into `website/vercel.json`). - CI now builds before checking. `check:types` typechecks the website against the library's *emitted* declarations, so `dist` has to exist first -- this ordering keeps that integration check without an install-time build. The cost is one documented manual step: the library must be built once after cloning, or `check:types` and `start:docs` fail with `Cannot find module 'react-live'`. CONTRIBUTING.MD covers it.
Ports the scenarios from the deleted Storybook stories into real tests. Storybook was never wired into CI -- no test-runner, no storyshots -- so those scenarios had only ever been checked by eye. Coverage 38.46% -> 93.26% statements. Tests 15 (+4 skipped) -> 51 (0 skipped). Every React component was previously at 0%; only the transpile utilities were covered. The four skipped LiveProvider tests are replaced by 17 working ones. They had never asserted anything: they chained off React's act() thenable, which is not a real Promise, so the runner never awaited them. New: Editor (7), LivePreview/LiveEditor/withLive (8), ErrorBoundary (4). Two guards, both added after shipping the exact bugs they catch: - React act() warnings now fail the run (vitest.setup.js). The warning means a test asserted while a state update was in flight, so it may be passing for the wrong reason -- one such test asserted "no error is shown" before anything had happened, and would have passed even if the provider always errored. - CI runs with --reporter=verbose. Vitest's default reporter hides console output from *passing* test files, so a green run can silently print warnings. `CI=true npm test` reproduces it locally. Editor interaction stays uncovered: jsdom has no contentEditable editing model, so typing, caret-after-Enter, and tabMode need a real browser. CONTRIBUTING documents that boundary, and why Storybook's Vitest integration is not an option yet (addon-vitest peers vitest ^3 || ^4; this repo is on 5). Coverage is reported in CI, not enforced.
`stories` and `stories:test` now read as a pair -- the browser suite is the stories environment's tests. Adds a light dusting of the docs-site palette (stories/theme.js): a slim navy header, a grey sidebar with a green selection marker, Inter with a system fallback. No CSS framework, no bundled font files. The story canvas stays neutral so components are judged on their own rendering, and ?only=1 -- the mode the tests load -- now omits the global stylesheet too, so nothing the harness does can influence a result.
Closes the remaining gaps against the deleted Storybook set. 27 stories vs the original 14: StyledSubcomponents (className pass-through, without styled-components), ControlledEditor (LiveEditor lifting state via onChange -- the original's Sandbox, distinct from swapping in a textarea), WithLiveHocTypeScript, CustomLanguage, and TabIndentation / TabFocus with three browser tests for tabMode, documented in docs/api.md but covered by neither the original stories nor any test. The smoke test now awaits the transpile settling. LiveProvider transpiles in an effect, so it was asserting against the first paint and passing early for every async story; errors then surfaced after the test had finished, landing outside any test and appearing only intermittently. Stories that throw on purpose opt in with `expectsError: true`; only Live/RuntimeError does, and rendering all 27 confirms nothing else is suppressed. Not ported: Editor/PrismFromNpm. Despite the name it never passed a `prism` prop, and could not have -- Editor declares `prism` but never reads it.
… blog Keeps the dist filenames 4.1.x published (dist/index.js, index.mjs, index.d.ts) via tsdown outExtensions, so main/module/types resolve exactly as before for bundlers that ignore `exports`. attw and publint stay green, and a packed tarball installs and resolves in both CJS and ESM. Adds the jsdom suite's guards to the browser config, and records that React only emits act warnings when IS_REACT_ACT_ENVIRONMENT is set -- which it is not in a real browser, so that guard is inert there. Folds in regression tests for error recovery. getDerivedStateFromError makes hasError sticky, which is only safe because a new boundary class is built per transpile. docs/api.md claimed LiveEditor wraps react-simple-code-editor; it is not a dependency and never was. Documents the now-working `prism` prop. website/README.md was the untouched Docusaurus scaffold (yarn, Docusaurus 2, gh-pages deploy). The classic preset enables a blog by default and the config never disabled it, so every build emitted an empty /blog page. Pre-existing, not introduced here.
Replaces two `cond && expr` statements with `if` blocks, and disables no-array-index-key in Editor with the reasoning: for tokenised code, position is the identity, and content-derived keys would collide on duplicate lines and remount nodes inside a contentEditable on every edit. The five remaining warnings are render/effect behaviour in LiveProvider and Editor, tracked in #417. Notably the suggested fix for the effect-dependency warnings would break re-transpilation if applied literally.
Regenerate the lockfile to undo a split React tree, replace the removed react-test-renderer/shallow, and stop emitting JSX __self/__source debug props. Add a React 18 CI job; revert the Tailwind 4 bump for now.
🦋 Changeset detectedLatest commit: 8fb9d18 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
🟡 Changes recommended
The declared Node support conflicts with the toolchain, the React 18 job misses the minimum version, and the packaging script is not cross-platform.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Modernizes the project’s library, build, test, documentation, deployment, and release infrastructure in preparation for v5.
Changes:
- Migrates tooling to npm workspaces, tsdown, Vitest, Oxlint, Vite, and Docusaurus 3.
- Fixes React 19 typings, error handling, transpilation warnings, and custom Prism forwarding.
- Reworks CI, trusted publishing, stories, documentation, and deployment configuration.
File summaries
| File | Description |
|---|---|
website/vercel.json |
Adds Vercel build configuration. |
website/tsconfig.json |
Updates site TypeScript configuration. |
website/src/pages/index.tsx |
Changes installation command to npm. |
website/src/globals.d.ts |
Adds static-image declarations. |
website/src/components/landing/nf-link-button.tsx |
Simplifies children typing. |
website/README.md |
Rewrites site development and deployment guidance. |
website/package.json |
Upgrades Docusaurus and site tooling. |
website/docusaurus.config.js |
Updates Docusaurus configuration. |
website/babel.config.js |
Removes obsolete Babel configuration. |
README.md |
Fixes the contributing-guide link. |
pnpm-workspace.yaml |
Removes pnpm workspace configuration. |
packages/react-live/vitest.setup.js |
Adds React warning enforcement. |
packages/react-live/vitest.config.mts |
Configures jsdom tests and coverage. |
packages/react-live/vitest.browser.mts |
Configures Chromium tests. |
packages/react-live/vite.stories.mts |
Configures the story harness. |
packages/react-live/tsup.config.ts |
Removes tsup configuration. |
packages/react-live/tsdown.config.mts |
Adds tsdown build configuration. |
packages/react-live/tsconfig.json |
Modernizes library TypeScript settings. |
packages/react-live/stories/theme.js |
Adds story-harness styling. |
packages/react-live/stories/stories.browser.test.jsx |
Smoke-tests stories in Chromium. |
packages/react-live/stories/main.jsx |
Implements the story browser. |
packages/react-live/stories/live.stories.jsx |
Adds live-component scenarios. |
packages/react-live/stories/index.html |
Adds the Vite story entry page. |
packages/react-live/stories/editor.stories.jsx |
Adds editor scenarios. |
packages/react-live/src/utils/transpile/transform.ts |
Enables production Sucrase transforms. |
packages/react-live/src/utils/transpile/index.ts |
Simplifies the transform pipeline. |
packages/react-live/src/utils/transpile/evalCode.ts |
Applies formatting updates. |
packages/react-live/src/utils/transpile/errorBoundary.tsx |
Adds error-boundary state handling. |
packages/react-live/src/utils/transpile/compose.ts |
Applies formatting updates. |
packages/react-live/src/utils/test/transpile.test.jsx |
Migrates mocks to Vitest. |
packages/react-live/src/utils/test/renderer.js |
Replaces the removed shallow renderer. |
packages/react-live/src/utils/test/fixtures/optional-chain.test.jsx |
Updates snapshot formatting. |
packages/react-live/src/utils/test/errorBoundary.test.jsx |
Adds mounted boundary tests. |
packages/react-live/src/utils/test/errorBoundary.test.js |
Removes the former Jest test. |
packages/react-live/src/hoc/withLive.tsx |
Applies formatting updates. |
packages/react-live/src/components/Live/LiveProvider.test.jsx |
Expands provider behavior coverage. |
packages/react-live/src/components/Live/LiveProvider.test.js |
Removes obsolete provider tests. |
packages/react-live/src/components/Live/LiveProvider.stories.tsx |
Removes Storybook stories. |
packages/react-live/src/components/Live/LivePreview.tsx |
Uses the scoped React JSX namespace. |
packages/react-live/src/components/Live/LiveEditor.tsx |
Marks the editor props import as type-only. |
packages/react-live/src/components/Live/LiveEditor.stories.tsx |
Removes old editor stories. |
packages/react-live/src/components/Live/live-components.test.jsx |
Adds live-component tests. |
packages/react-live/src/components/Live/ErrorBoundary.test.jsx |
Adds preview boundary regression tests. |
packages/react-live/src/components/Live/editor-interaction.browser.test.jsx |
Adds real-browser editor tests. |
packages/react-live/src/components/Editor/index.tsx |
Forwards custom Prism instances. |
packages/react-live/src/components/Editor/index.test.jsx |
Adds editor rendering tests. |
packages/react-live/package.json |
Updates package metadata and tooling. |
packages/react-live/.storybook/preview.js |
Removes Storybook preview configuration. |
packages/react-live/.storybook/main.js |
Removes Storybook configuration. |
packages/react-live/.storybook/.babelrc |
Removes Storybook Babel configuration. |
packages/react-live/.babelrc |
Removes legacy Babel configuration. |
packages/demo/vite.config.ts |
Removes the duplicate demo. |
packages/demo/tsconfig.json |
Removes demo TypeScript configuration. |
packages/demo/tailwind.config.cjs |
Removes demo Tailwind configuration. |
packages/demo/src/vite-env.d.ts |
Removes demo Vite types. |
packages/demo/src/main.tsx |
Removes the demo entry point. |
packages/demo/src/index.css |
Removes demo styles. |
packages/demo/src/app.tsx |
Removes the demo application. |
packages/demo/postcss.config.cjs |
Removes demo PostCSS configuration. |
packages/demo/package.json |
Removes the demo package. |
package.json |
Migrates the workspace and scripts to npm. |
docs/introduction.mdx |
Corrects JSX examples. |
docs/api.md |
Updates editor implementation and Prism docs. |
CONTRIBUTING.MD |
Removes the old contributor guide. |
CONTRIBUTING.md |
Adds the new npm-based contributor guide. |
CODE_OF_CONDUCT.md |
Extracts the code of conduct. |
.sail.yml |
Removes obsolete Sail automation. |
.prettierrc |
Adds Prettier 3 configuration. |
.prettierignore |
Excludes generated files from formatting. |
.oxlintrc.json |
Adds Oxlint configuration. |
.nvmrc |
Selects the current Node LTS. |
.npmrc |
Removes pnpm-era peer settings. |
.npmignore |
Removes obsolete package exclusions. |
.gitignore |
Updates generated-file exclusions. |
.github/workflows/release.yml |
Rebuilds the trusted-publishing workflow. |
.github/workflows/code-check.yml |
Removes the former checks workflow. |
.github/workflows/ci.yml |
Adds unit, browser, and compatibility CI. |
.github/actions/setup/action.yml |
Removes the pnpm setup action. |
.eslintrc |
Removes ESLint configuration. |
.eslintignore |
Removes ESLint exclusions. |
.changeset/tame-moons-shake.md |
Records the boundary fix. |
.changeset/plenty-hoops-wonder.md |
Records the Sucrase fix. |
.changeset/olive-dolls-repeat.md |
Records Prism forwarding. |
.changeset/nervous-donkeys-smile.md |
Records v5 breaking changes. |
.changeset/config.json |
Updates Changesets configuration. |
Review details
- Files reviewed: 82/87 changed files
- Comments generated: 5
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| }, | ||
| "engines": { | ||
| "node": ">=18.0.0" | ||
| "node": ">=20.19.0" |
| npm pkg set \ | ||
| devDependencies.react="^${{ matrix.react }}" \ | ||
| devDependencies.react-dom="^${{ matrix.react }}" \ | ||
| "devDependencies.@types/react=^${{ matrix.react }}" \ | ||
| "devDependencies.@types/react-dom=^${{ matrix.react }}" \ | ||
| -w react-live | ||
| npm pkg set \ | ||
| dependencies.react="^${{ matrix.react }}" \ | ||
| dependencies.react-dom="^${{ matrix.react }}" \ | ||
| -w website |
| "lint:fix": "eslint --ext .js,.ts,.tsx src --fix" | ||
| "build": "tsdown", | ||
| "build:watch": "tsdown --watch", | ||
| "prepack": "cp ../../README.md ../../LICENSE . && npm run build", |
| </LiveProvider>, | ||
| ); | ||
| expect(await screen.findByText("inner")).toBeDefined(); | ||
| expect(container.querySelector("div")).toBeDefined(); |
| // live editor, so sucrase emits an empty filename. React ignores both | ||
| // props, and React 19 treats `__self` as the signature of an outdated | ||
| // JSX transform and warns about it, so there is nothing to lose. | ||
| production: true, |
Rebuilds the repo tooling and fixes four library bugs. We'll eventually release as
v5.Tasks
Library
LivePreviewoverloads useReact.JSXinstead of the globalJSXnamespace,which
@types/react@19removed. This is the breaking change.exportsmap. Published filenames are unchanged, somain/module/typesresolve as before; deep imports intodist/no longer resolve.es6→es2022.engines.node>= 0.12.0→>=20.19.0.Editor'sprismprop, which was typed but never passed through.production, dropping the__self/__sourcepropsand React 19's outdated-JSX-transform warning.
getDerivedStateFromErrorto the internal error boundary.Same exports, same props, same
react >=18peer range.Tooling
jest → vitest, eslint → oxlint, Prettier 3, Docusaurus 2 → 3.
packages/demo. Stories are now a plainVite app under
packages/react-live/stories, smoke-tested in Chromium.