Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/rude-seas-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"emdash": patch
---

fix: exclude unused @tiptap collaboration deps from optimizeDeps so fresh installs do not fail
18 changes: 16 additions & 2 deletions packages/core/src/astro/integration/vite-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,8 @@ export function createViteConfig(
// during pre-bundling and can't resolve them. Vite's exclude
// uses prefix matching (id.startsWith(m + "/")), so
// "virtual:emdash" matches all "virtual:emdash/*" imports.
exclude: ["virtual:emdash"],
// tiptap deps not used in this repo; excluded so optimizeDeps does not fail to resolve them on fresh installs (issue #771).
exclude: ["virtual:emdash", "@tiptap/extension-collaboration", "@tiptap/y-tiptap"],
include: [
// EmDash direct deps
"emdash > @portabletext/toolkit",
Expand Down Expand Up @@ -424,7 +425,20 @@ export function createViteConfig(
include: useSource
? ["@astrojs/react/client.js"]
: ["@emdash-cms/admin", "@astrojs/react/client.js"],
exclude: cloudflare ? ["virtual:emdash"] : [...NODE_NATIVE_EXTERNALS, "virtual:emdash"],
exclude: cloudflare
? [
// tiptap deps not used in this repo; excluded so optimizeDeps does not fail to resolve them on fresh installs (issue #771).
"virtual:emdash",
"@tiptap/extension-collaboration",
"@tiptap/y-tiptap",
]
Comment on lines +431 to +434
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of repeating the array, could you define it as a constant used in both places?

: [
...NODE_NATIVE_EXTERNALS,
// tiptap deps not used in this repo; excluded so optimizeDeps does not fail to resolve them on fresh installs (issue #771).
"virtual:emdash",
"@tiptap/extension-collaboration",
"@tiptap/y-tiptap",
],
},
};
}
76 changes: 76 additions & 0 deletions packages/core/tests/unit/astro/vite-config-optimizedeps.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { AstroConfig } from "astro";
import { describe, expect, it } from "vitest";

import type { EmDashConfig } from "../../../src/astro/integration/runtime.js";
import { createViteConfig } from "../../../src/astro/integration/vite-config.js";

/**
* Regression for #771: a fresh `npm create emdash@latest && yarn dev` failed
* dependency optimization on `@tiptap/extension-collaboration` and
* `@tiptap/y-tiptap`. Neither package is imported in source code or declared
* as a dependency, but Vite's esbuild dep scanner follows non-static
* `import()` calls inside `@tiptap/react` / `@tiptap/starter-kit` and tries
* to resolve them. The fix: list both packages in `optimizeDeps.exclude` so
* the scanner skips them.
*
* Both branches of the integration's vite config (Cloudflare and non-
* Cloudflare) need the exclusion. The test pins both branches so a future
* refactor can't quietly drop the entries from one path.
*/
describe("vite-config optimizeDeps exclude (#771)", () => {
function makeOptions(adapter: "cloudflare" | "node") {
const astroConfig: Partial<AstroConfig> = {
adapter:
adapter === "cloudflare"
? { name: "@astrojs/cloudflare", hooks: {} }
: { name: "@astrojs/node", hooks: {} },
};
const emdashConfig: Partial<EmDashConfig> = {};
return {
astroConfig: astroConfig as AstroConfig,
emdashConfig: emdashConfig as EmDashConfig,
plugins: [] as PluginDescriptor[],
};
}

it("excludes @tiptap/extension-collaboration and @tiptap/y-tiptap on the Node path", () => {
const config = createViteConfig(
makeOptions("node") as Parameters<typeof createViteConfig>[0],
"dev",
);
const exclude = config.optimizeDeps?.exclude ?? [];
expect(exclude).toContain("@tiptap/extension-collaboration");
expect(exclude).toContain("@tiptap/y-tiptap");
// Sanity-check existing entries are still present so we did not
// regress the original optimizeDeps shape.
expect(exclude).toContain("virtual:emdash");
});

it("excludes @tiptap/extension-collaboration and @tiptap/y-tiptap on the Cloudflare ssr path", () => {
const config = createViteConfig(
makeOptions("cloudflare") as Parameters<typeof createViteConfig>[0],
"dev",
);
// On Cloudflare the exclusion shows up in two places: the
// adapter-specific ssr.optimizeDeps block, and the top-level
// optimizeDeps.exclude ternary. Both must carry the entries so a
// future refactor that drops one path is still caught.
const ssr = config.ssr as { optimizeDeps?: { exclude?: string[] } } | undefined;
const ssrExclude = ssr?.optimizeDeps?.exclude ?? [];
expect(ssrExclude).toContain("@tiptap/extension-collaboration");
expect(ssrExclude).toContain("@tiptap/y-tiptap");
expect(ssrExclude).toContain("virtual:emdash");

const topLevelExclude = config.optimizeDeps?.exclude ?? [];
expect(topLevelExclude).toContain("@tiptap/extension-collaboration");
expect(topLevelExclude).toContain("@tiptap/y-tiptap");
});
});

// Re-declare here to avoid pulling the runtime module's broader type surface
// into a test file. The shape only needs to be assignable; the test does not
// inspect the plugins list.
type PluginDescriptor = {
name?: string;
package?: string;
};
Loading