Skip to content

feat: website in the monorepo on Rspack, with a shared alphaTab core for webpack, Rspack, Vite and Turbopack - #2998

Merged
Danielku15 merged 521 commits into
developfrom
feature/website-monorepo
Oct 31, 2026
Merged

Danielku15 merged 521 commits into
developfrom
feature/website-monorepo

Conversation

@Danielku15

Copy link
Copy Markdown
Member

Note

AI-authored disclosure (alphatab-ai-authored-v1)

Portions of this content were authored by an AI agent. The agent has read
AGENTS.md and the human submitter accepts responsibility for
compliance with the rules in that document.

Issues

No issue: this is the website monorepo integration, agreed to land as a single PR. It also carries the bundler integration rework that the move to Rspack required.

Proposed changes

Why: The generated reference docs made the website's dev server slow and memory-hungry. Docusaurus' faster bundler, Rspack, was blocked because AlphaTabWebPackPlugin depended on webpack internals Rspack doesn't have. alphaTab's bundler integration also loaded the core more than once: webpack per runtime, Vite per worker and worklet. And it needed copied bundler internals to work at all.

alphaTab

  • Workers start with the standard new Worker(new URL(...), { type: 'module' }), which bundlers recognise natively. The existing fallbacks remain.
  • The worklet loads through alphaTabWorklet.addModule(...), which first adds the chunk files a webpack-style runtime lists (__webpack_require__.atwm).
  • New exports @coderline/alphatab/core, /worker and /worklet let apps own their worker and worklet entries, for bundlers without plugin support.
  • Turbopack is detected (Environment.isTurbopackBundled), so Turbopack bundles alphaTab's workers.
  • Minified entries now load the minified core.

@coderline/alphatab-webpack

  • A thin plugin for webpack 5 and Rspack 1/2 on public plugin APIs only.
  • Page, workers and worklet share one core: usedExports: 'global' in production, plus an alphaTabCore split chunk.
  • webWorkers: false now applies to workers.
  • Peer dependencies: webpack / @rspack/core, both optional.

@coderline/alphatab-vite

  • The worker and worklet are emitted as chunks of the main build and share the core chunk, instead of separate sub-builds.
  • Entry URLs are written relative to the referencing chunk, so relative base, SvelteKit and Nuxt work.
  • The emitted chunks are no longer manifest entries.
  • Vite 6 is supported.

Website

Docs

  • New page "Installing in web projects" and pages per bundler and per framework, based on the verified setups in alphaTabSamplesWeb.
  • Corrected the bundle-structure and worker-initialization pages.
  • 1.9 release notes.

Checklist

  • I consent that this change becomes part of alphaTab under its current or any future open source license
  • This PR is linked to an accepted issue (see above)
  • Changes are implemented
  • New tests were added
  • I have read AGENTS.md if an AI helped draft any part of this PR

AI authorship disclosure

  • No AI agent authored any part of this PR (description, code, tests, or commit messages)
  • An AI agent contributed to this PR. The AI-authored disclosure block
    (alphatab-ai-authored-v1) is present at the top of this body, and I have
    personally reviewed every change and can explain each one

Further details

  • This is a breaking change
  • Docs / release notes updated in website/

All 31 samples of alphaTabSamplesWeb (PR: ) were verified in headless Chromium against this build, in production and dev. Each one renders, plays with audio, loads its workers and worklet, and logs no errors. npm run build-website passes with no broken links.

Danielku15 and others added 30 commits November 30, 2025 22:59
…/alphaTabWebsite#155)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…bsite#156)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
# Conflicts:
#	docs/showcase/special-tracks.mdx
#	package-lock.json
#	package.json
# Conflicts:
#	.github/workflows/deploy-main.yml
…ebsite#165)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
(cherry picked from commit 9e5f6fcd57d7e63809e0cc8b3629fd3529ec4afd)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…bsite#172)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…#173)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ite#174)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…bsite#176)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…bsite#177)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…ebsite#178)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…haTabWebsite#179)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
A local .npmrc typically carries registry auth tokens.
TSDoc links cannot target parameters; the website's reference generator
could not resolve it.
…ence generator

A link to a parameter has no reference page. Unresolved links are now
reported with kind, location and text instead of a dump of the node.
The bundled declarations of @coderline/alphatab-language-server imported
'./../../alphatex/src/types'. packages/alphatex is a private workspace,
so the path does not exist in the npm package and consumers got `any`
for the documentation types.

The per-file declaration emit was rooted at each package's src/ folder
while the path aliases were rewritten relative to the source tree, so
imports of other workspace packages pointed outside of the emitted
tree, and their declarations were never written. API Extractor left
the unresolved imports as they were.

The emit is now rooted at the packages folder: dist/types mirrors the
workspace, the declarations of private workspace packages are written
next to the package's own, and API Extractor inlines them. Aliases of
published packages stay package imports. The language server keeps
@coderline/alphatab, which the alphaTex types reference, as an import.

Declared external types are checked across all entry points of a
package, since not every entry point of the language server uses
alphaTab.
API Extractor keeps imports it cannot resolve and inlines the text of
whatever it resolves, so a missing or mis-rooted declaration silently
became a broken path in the published types, and a published sibling
resolved through a path alias would ship its TypeScript source.

The bundling now fails when a declaration resolves to a TypeScript
source that is not declared in externalTypes, also when API Extractor
crashes on that source, and when a bundled or per-file declaration
keeps a relative import that does not resolve to a declaration file
shipped with the package.
Files that only re-exported or declared @internal members kept their
`export {}` and their now unused imports after filtering. In
@coderline/alphatab-vite this shipped bridge/typeUtils.d.ts, which
imports 'rollup', not a dependency of the package, and references the
removed GetHookContextMap type; the `./*` export makes it importable.

Such files are no longer written. Modules that never had exports, like
the monaco worker entry, keep their `export {}` declaration.
scripts/check-published-types.mts packs the web packages, installs the
tarballs into a project outside of the repository and type checks all
exported entry points and shipped declaration files with
skipLibCheck disabled. It also rejects relative imports that leave a
package or point into sources.

Build Web runs it after building the packages, before the dist cache is
saved, so a cached dist has passed the check. The cache key includes
the script, and the alphaTex sources, which the language server bundles.
…maid

The two installation pages were the only mermaid users; the plugin added
114 packages and about 168 MB to the install. The diagram is an inline
SVG that follows the site's light and dark colours.
… dev server

The generator deleted every reference page up front and streamed each
file in place, so a watching dev server saw ~800 pages vanish (ENOENT)
and could read half-written files. Pages are now written atomically and
only when their content changed; pages the run did not produce are
removed at the end.
The port type came from vscode-languageserver and named the WebWorker-only
DedicatedWorkerGlobalScope, so the browser entry, which also carries the
DOM-typed alphaTab API, could not type check in any project. The new
LanguageServerMessagePort lists only the members the server uses; every
declaration file is now part of the published-types check.
It is part of the shared player, not a temporary QA aid.
The changes job now selects the web jobs itself and emits the test_web
matrix as JSON, so a change in packages/react or packages/ui runs only the
Editor and Player leg plus the website build, and a playground-only change
runs lint alone. Core and web package changes still run all three legs.
The playground has no tests; installing it only cost time.
Dev server:
- Lazy compilation for the dev client, plus a loader that drops the
  require.resolveWeak() ids from Docusaurus' route registry; weakly
  referenced modules are built eagerly, which kept every page compiling
  at startup. Cold start 12.5 s -> 3.3 s, RSS after start ~2.1 GB ->
  0.65-0.9 GB.
- Eval source maps only for the site's own code, not for node_modules
  or prebuilt packages/*/dist.
- Gzip-compressed persistent cache, one in-memory generation in dev.

Build output:
- usedExports "global" so alphaTab's core ships as one chunk shared by
  the page, the alphaTab worker and the alphaTex language-server worker
  instead of one copy per runtime.
- Monaco registers only the editor features the alphaTex editor uses.
- Latin font subsets only.
- File format tooltips render through react-tooltip's render prop
  instead of react-dom/server in the browser.
- Generated Signature tokens use a compact string encoding.

alphaTex editor:
- Overflow widgets follow page and ancestor scrolling.
- Completion and hover widgets open below the cursor on scrolled pages:
  corrects Monaco's page-vs-viewport measurement
  (microsoft/monaco-editor#5512); a test pins the patched Monaco code.
…bpack, Rspack and Vite

alphaTab:
- Start workers with the standard `new Worker(new URL(..., import.meta.url))`
  form bundlers recognise; the blob, scriptFile and classic fallbacks stay.
- Load the worklet through `alphaTabWorklet.addModule`, which first adds the
  chunk files a webpack-style runtime lists via `__webpack_require__.atwm`
  (with a shim for `self`, `location` and `importScripts`).
- Keep the worker/worklet URLs as written in the library build instead of
  letting Vite bundle the worker and inline the worklet.

@coderline/alphatab-webpack:
- Rewritten as a thin plugin for webpack 5 and Rspack on public plugin APIs:
  worker syntax scoped to alphaTab's modules (works with `worker: false`
  hosts like Angular), the `__ALPHATAB_WEBPACK__` define, a page runtime
  module mapping the worklet to its chunk files, a worklet runtime module
  installing the pre-added chunks, `usedExports: 'global'` in production
  and an `alphaTabCore` split chunk so one core serves all runtimes.
- `webWorkers: false` now applies to workers (it checked `audioWorklets`).

@coderline/alphatab-vite:
- Emit the worker and worklet as chunks of the main build sharing the core
  chunk instead of separate sub-builds; exclude alphaTab from dependency
  pre-bundling in dev. Removes the copied Vite internals and the
  `workerPlugin` subpath.

Website:
- Build with Rspack (`rspackBundler`, persistent cache); lazy compilation in
  dev wired through Rspack's middleware; source maps from the active bundler.
- packages/ui: declare worker entry files as side effects (Rspack dropped the
  language-server worker's imports under `usedExports: 'global'`).
…chunk

The URLs no longer go through import.meta.ROLLUP_FILE_URL, which Rolldown
corrupts for relative base, renderBuiltUrl, SvelteKit and Nuxt 4. The emitted
worker and worklet chunks are no longer flagged as entries, and each build
environment tracks its own entries. Vite 6 joins the peer range.
Turbopack compiles import.meta.url to a file:// URL, so alphaTab took itself
for a classic script and could not start its workers. Environment detects
Turbopack via __turbopack_require__ (isTurbopackBundled, also printed by
printEnvironmentInfo) and alphaTab then creates its module workers, which
Turbopack bundles with the shared core. Turbopack bundles no Audio Worklets,
so there the worklet loads from core.scriptFile; a missing script file fails
with a clear error.
Installing in web projects chooses the setup by bundler and framework,
compares the alphaTab core copies each one loads and lists what every
setup needs. Bundler pages cover webpack with Rspack and Rsbuild, Vite,
and the core/worker/worklet recipe for esbuild, Rollup and Parcel;
framework pages cover React and Next.js including Turbopack, Vue and
Nuxt, Angular, Svelte and SvelteKit, and Solid, each from its verified
sample. The custom bundler page becomes a card pointing to the recipe,
and the 1.9 release notes describe the bundler changes.
Next.js prefixes the bundled workers with basePath but not URLs into public/, so the font directory, SoundFont, score and the worklet's core.scriptFile need the prefix. The section shows how to expose basePath to the client through next.config.ts.
…builder setup

alphaTab.min.mjs, its worker and its worklet import alphaTab.core.min.mjs,
so the setup copies and import-maps the minified set and names the
unminified set as the alternative for debugging.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant