Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
# Optional Turborepo remote cache; tasks run with the local cache when unset.
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}

jobs:
check:
name: Check
Expand All @@ -39,4 +44,5 @@ jobs:
run: pnpm run check

- name: Inspect package contents
working-directory: packages/vinext
run: pnpm pack --dry-run
5 changes: 5 additions & 0 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ concurrency:
permissions:
contents: read

env:
# Optional Turborepo remote cache; tasks run with the local cache when unset.
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}

jobs:
playwright:
name: Playwright Tests
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pkg-pr-new.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ jobs:
- name: Build
run: pnpm build:package

- run: pnpm dlx pkg-pr-new publish --compact '.'
- run: pnpm dlx pkg-pr-new publish --compact './packages/vinext'
14 changes: 11 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ concurrency:

env:
PACKAGE_NAME: "@module-federation/vinext"
PACKAGE_DIR: packages/vinext

jobs:
publish:
Expand Down Expand Up @@ -91,6 +92,7 @@ jobs:
if: github.event_name == 'workflow_dispatch' && github.event.inputs.version == 'next'
run: |
pnpm exec changeset version --snapshot next
cd "$PACKAGE_DIR"
SNAPSHOT_VERSION="$(node -p "require('./package.json').version")"
BASE_VERSION="${SNAPSHOT_VERSION%%-*}"
NEXT_VERSION="${BASE_VERSION}-next.${GITHUB_RUN_ID}"
Expand All @@ -99,6 +101,7 @@ jobs:

- name: Verify tag matches package version
if: github.event_name == 'release'
working-directory: ${{ env.PACKAGE_DIR }}
env:
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
Expand All @@ -120,6 +123,7 @@ jobs:

- name: Set deterministic prerelease version
if: github.event_name == 'release' && github.event.release.prerelease
working-directory: ${{ env.PACKAGE_DIR }}
run: |
CURRENT_VERSION="$(node -p "require('./package.json').version")"
BASE_VERSION="${CURRENT_VERSION%%-*}"
Expand All @@ -133,8 +137,10 @@ jobs:
- name: Check formatting
run: pnpm run fmt.check

- name: Inspect package contents
run: pnpm pack --dry-run
- name: Pack package
working-directory: ${{ env.PACKAGE_DIR }}
# pnpm pack resolves catalog: and workspace: specifiers; npm publish does not.
run: pnpm pack --pack-destination "$RUNNER_TEMP/package"

- name: Compute npm dist-tag
id: dist
Expand All @@ -155,6 +161,7 @@ jobs:
echo "dist_tag=$DIST_TAG" >> "$GITHUB_OUTPUT"

- name: Resolve publish action
working-directory: ${{ env.PACKAGE_DIR }}
id: publish_action
env:
DIST_TAG: ${{ steps.dist.outputs.dist_tag }}
Expand Down Expand Up @@ -198,7 +205,7 @@ jobs:
if: steps.publish_action.outputs.action == 'publish'
env:
DIST_TAG: ${{ steps.dist.outputs.dist_tag }}
run: npm publish --provenance --tag "$DIST_TAG" --access public
run: npm publish "$RUNNER_TEMP"/package/*.tgz --provenance --tag "$DIST_TAG" --access public

- name: Skip npm publish
if: steps.publish_action.outputs.action == 'skip'
Expand All @@ -210,6 +217,7 @@ jobs:

- name: Post summary
if: always()
working-directory: ${{ env.PACKAGE_DIR }}
continue-on-error: true
env:
SOURCE_REF: ${{ github.event.release.tag_name || github.event.inputs.branch }}
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ dist/
*.log
reports/
.DS_Store
.turbo/
*.tsbuildinfo
11 changes: 11 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,14 @@ PR titles are validated by `.github/workflows/pr.yml` and must follow
- Titles starting with `Release ` are reserved for the PR generated by the
**Release Pull Request** workflow and skip validation. Don't use that prefix
for anything else.

<!-- BEGIN:turborepo-agent-rules -->

# This is NOT the Turborepo you know

Turborepo configuration, task behavior, and CLI commands can vary between installed versions and may differ from your training data. Resolve the `turbo` package from this file's directory or relevant workspace; in monorepos, it may not be visible from the repository root. For example, run `node -p "require.resolve('turbo/package.json')"` from a workspace that depends on `turbo`.

Read `docs/README.md` inside that installed package first, then read the relevant pages from its `docs/` directory before changing Turborepo configuration or commands. Heed deprecation notices. These bundled docs match the installed package version and are available without network access.

This block is written and re-added by `turbo` before repository-scoped commands when an AI agent is detected. In the Turborepo source repository, its template is defined in `crates/turborepo-cli/src/cli/agent_guidance.rs`. Removing the managed block while updates are enabled means a later qualifying invocation will add it again. Set `"agentGuidance": false` in the root `turbo.json` or `turbo.jsonc` to opt out; this does not remove an existing block. Keep the block committed with your work to avoid an uncommitted change on the next agent invocation.
<!-- END:turborepo-agent-rules -->
89 changes: 23 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,81 +1,38 @@
# @module-federation/vinext
# vinext Module Federation

Thin Module Federation wrapper for vinext, built on
`@module-federation/vite`.
Monorepo for [`@module-federation/vinext`](packages/vinext), a thin Module
Federation wrapper for vinext built on `@module-federation/vite`.

## Install
See the [package README](packages/vinext/README.md) for installation and
configuration.

```sh
pnpm add @module-federation/vinext
```

## Configure

Register federation before `vinext()` in `vite.config.ts`:

```ts
import { federation } from "@module-federation/vinext";
import { defineConfig } from "vite";
import vinext from "vinext";

export default defineConfig({
plugins: [
federation({
name: "host",
remotes: {
catalog: {
type: "module",
name: "catalog",
entry: "https://catalog.example.com/remoteEntry.js",
entryGlobalName: "catalog",
shareScope: "default",
},
},
}),
vinext(),
],
});
```

Remote example:
## Repository layout

```ts
federation({
name: "catalog",
exposes: {
"./ProductCard": "./app/product-card.tsx",
},
});
```

The wrapper defaults `filename` to `remoteEntry.js`, injects host startup into
the entry (vinext has no conventional HTML entry), and shares `react` and
`react-dom` as singletons. Explicit options override every default. All
`@module-federation/vite` options remain available.

The default export and `withModuleFederation` are aliases of `federation`.

## Example applications
| Path | Description |
| ------------------------------------ | ------------------------------------------------ |
| [`packages/vinext`](packages/vinext) | `@module-federation/vinext`, published to npm |
| [`apps/host`](apps/host) | Vinext/React 19 host, port 4173 |
| [`apps/remote`](apps/remote) | React 19 remote shared with the host, port 4174 |
| [`apps/island`](apps/island) | Isolated React 18 SSR island, port 4175 |
| [`e2e`](e2e) | Playwright tests against the production previews |

- Vinext host: `apps/host`, port 4173
- Shared React remote: `apps/remote`, port 4174
- Isolated React 18 island: `apps/island`, port 4175

```sh
pnpm dev
pnpm preview
```
Tasks run through [Turborepo](https://turborepo.com), which builds workspace
dependencies first and caches results in `.turbo/`.

## Development

```sh
pnpm install
pnpm run check
pnpm run build:package
pnpm run build
pnpm run test:e2e
pnpm dev # watch the package and run every app in dev mode
pnpm build # build the package and all apps
pnpm preview # build, then serve the production apps
pnpm check # typecheck, unit tests, and builds
pnpm test:e2e # Playwright against `pnpm preview`
```

Scope a task to one workspace with a filter, e.g.
`pnpm turbo run dev --filter=vinext-host` or `pnpm build:package`.

## Release

See [docs/releasing.md](docs/releasing.md).
Expand Down
3 changes: 2 additions & 1 deletion apps/host/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"scripts": {
"dev": "vite dev --port 4173",
"build": "vite build",
"preview": "vite preview --port 4173"
"preview": "vite preview --port 4173",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"next": "16.1.6",
Expand Down
3 changes: 2 additions & 1 deletion apps/island/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"scripts": {
"dev": "vite dev --port 4175",
"build": "vite build",
"preview": "vite preview --port 4175"
"preview": "vite preview --port 4175",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"react": "18.3.1",
Expand Down
3 changes: 2 additions & 1 deletion apps/remote/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"scripts": {
"dev": "vite dev --port 4174",
"build": "vite build",
"preview": "vite preview --port 4174"
"preview": "vite preview --port 4174",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"react": "19.2.8",
Expand Down
9 changes: 6 additions & 3 deletions docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ request.
3. Review the version bump and changelog in the generated `Release vX.Y.Z`
pull request, then merge it.
4. Create a GitHub release from `main` with the tag `X.Y.Z` (no leading `v`),
matching the version in `package.json`, and publish it.
matching the version in `packages/vinext/package.json`, and publish it.

Publishing the GitHub release runs the **Release** workflow, which checks that
the tag matches `package.json`, runs the full checks, and publishes to npm with
the `latest` tag.
the tag matches `packages/vinext/package.json`, runs the full checks, packs the
package with pnpm (resolving `catalog:` versions), and publishes the tarball to
npm with the `latest` tag.

## Publish a prerelease

Expand All @@ -58,3 +59,5 @@ Neither changes `main` or the next stable version.
- npm Trusted Publishing for `@module-federation/vinext`, pointing at this
repository, the `release.yml` workflow, and the `Publish` environment. No npm
token is needed.
- Optional: secret `TURBO_TOKEN` and variable `TURBO_TEAM` enable the Turborepo
remote cache in the **CI** and **E2E Tests** workflows.
67 changes: 15 additions & 52 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,67 +1,30 @@
{
"name": "@module-federation/vinext",
"version": "0.0.0",
"description": "Module Federation integration for vinext",
"homepage": "https://github.com/module-federation/vinext#readme",
"bugs": {
"url": "https://github.com/module-federation/vinext/issues"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/module-federation/vinext.git"
},
"files": [
"dist",
"README.md",
"LICENSE"
],
"name": "@module-federation/vinext-monorepo",
"private": true,
"type": "module",
"sideEffects": false,
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org"
},
"scripts": {
"prepare": "husky install",
"build": "turbo run build",
"build:package": "turbo run build --filter=@module-federation/vinext",
"check": "turbo run typecheck test build",
"dev": "turbo run dev",
"dev:host": "turbo run dev --filter=vinext-host",
"dev:island": "turbo run dev --filter=vinext-island",
"dev:remote": "turbo run dev --filter=vinext-remote",
"preview": "turbo run preview",
"test": "turbo run test",
"test:e2e": "playwright test --config=playwright.config.mjs",
"typecheck": "turbo run typecheck",
"fmt": "oxfmt *",
"fmt.check": "oxfmt --check *",
"build": "pnpm run build:apps",
"build:package": "tsc -p tsconfig.json",
"build:apps": "pnpm run build:package && pnpm --filter 'vinext-*' build",
"changeset": "changeset",
"check": "pnpm run typecheck && pnpm test && pnpm run build:apps",
"dev": "pnpm run build:package && pnpm --filter 'vinext-*' --parallel dev",
"dev:host": "pnpm run build:package && pnpm --filter vinext-host dev",
"dev:island": "pnpm --filter vinext-island dev",
"dev:remote": "pnpm --filter vinext-remote dev",
"preview": "pnpm run build:apps && pnpm --filter 'vinext-*' --parallel preview",
"test": "pnpm run build:package && node --test",
"test:e2e": "playwright test --config=playwright.config.mjs",
"typecheck": "tsc -p tsconfig.json --noEmit",
"prepack": "pnpm run build:package"
},
"dependencies": {
"@module-federation/vite": "catalog:"
"changeset": "changeset"
},
"devDependencies": {
"@changesets/cli": "^2.31.1",
"@playwright/test": "1.62.0",
"husky": "9.1.7",
"oxfmt": "0.63.0",
"typescript": "^7.0.2"
},
"peerDependencies": {
"vinext": ">=1.0.0-beta.3"
"turbo": "2.11.7"
},
"engines": {
"node": ">=22.22.2"
Expand Down
21 changes: 21 additions & 0 deletions packages/vinext/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Module Federation

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.
Loading
Loading