b3a7f98e5a
CI / E2E Cloudflare (4/8) (push) Failing after 0s
CI / E2E Cloudflare (8/8) (push) Failing after 1s
CI / Lint (push) Failing after 1s
Auto Extract / Extract (push) Failing after 4s
CI / Version Check (push) Failing after 9s
CI / Integration Tests (push) Failing after 1s
CI / E2E tests (1/8) (push) Failing after 1s
CI / E2E tests (2/8) (push) Failing after 2s
CI / E2E tests (3/8) (push) Failing after 2s
CI / Browser Tests (push) Failing after 1s
CI / E2E tests (5/8) (push) Failing after 1s
CI / E2E Cloudflare (2/8) (push) Failing after 2s
CI / Typecheck (push) Failing after 1s
CI / Changeset Validation (push) Failing after 2s
CI / E2E Cloudflare (5/8) (push) Failing after 1s
CI / E2E Cloudflare (6/8) (push) Failing after 1s
CI / E2E Cloudflare (7/8) (push) Failing after 1s
CodeQL / Analyze (javascript-typescript) (push) Failing after 1s
Format / Format (push) Failing after 0s
CodeQL / Analyze (actions) (push) Failing after 4s
CI / E2E tests (4/8) (push) Failing after 1s
CI / E2E tests (6/8) (push) Failing after 1s
CI / E2E tests (7/8) (push) Failing after 2s
CI / E2E tests (8/8) (push) Failing after 1s
CI / E2E Cloudflare (1/8) (push) Failing after 1s
CI / E2E Cloudflare (3/8) (push) Failing after 2s
Preview Releases / Publish Preview (push) Failing after 0s
zizmor / Run zizmor (push) Failing after 1s
Release / Release (push) Failing after 2s
CI / Smoke Tests (push) Failing after 5m36s
CI / Tests (push) Failing after 6m36s
Release / Sync Templates (push) Has been skipped
CI / E2E Tests (push) Has been cancelled
173 lines
6.1 KiB
TypeScript
173 lines
6.1 KiB
TypeScript
import { existsSync } from "node:fs";
|
|
import { basename, isAbsolute } from "node:path";
|
|
|
|
import type { AstroConfig } from "astro";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import { createViteConfig } from "../../../src/astro/integration/vite-config.js";
|
|
|
|
describe("createViteConfig admin aliasing", () => {
|
|
const monorepoDemoRoot = new URL("../../../../../demos/simple/", import.meta.url);
|
|
const externalProjectRoot = new URL("file:///workspace/emdash-site/");
|
|
const siblingProjectRoot = new URL("../../../../../../emdash-site/", import.meta.url);
|
|
const adminSourcePattern = /[/\\]packages[/\\]admin[/\\]src$/;
|
|
const adminDistPattern = /[/\\]packages[/\\]admin[/\\]dist$/;
|
|
|
|
function buildConfig(root: URL, command: "dev" | "build" | "preview" | "sync" = "dev") {
|
|
return createViteConfig(
|
|
{
|
|
serializableConfig: {},
|
|
resolvedConfig: {} as never,
|
|
pluginDescriptors: [],
|
|
astroConfig: {
|
|
root,
|
|
adapter: { name: "@astrojs/node" },
|
|
} as AstroConfig,
|
|
},
|
|
command,
|
|
);
|
|
}
|
|
|
|
function getAdminAliasReplacement(config: ReturnType<typeof createViteConfig>) {
|
|
const aliases = Array.isArray(config.resolve?.alias) ? config.resolve.alias : [];
|
|
const adminAlias = aliases.find(
|
|
(alias) =>
|
|
typeof alias === "object" &&
|
|
alias !== null &&
|
|
"find" in alias &&
|
|
alias.find === "@emdash-cms/admin" &&
|
|
"replacement" in alias,
|
|
);
|
|
|
|
if (!adminAlias || typeof adminAlias.replacement !== "string") {
|
|
throw new Error("Missing @emdash-cms/admin alias");
|
|
}
|
|
|
|
return adminAlias.replacement;
|
|
}
|
|
|
|
it("uses raw admin source for local monorepo dev", () => {
|
|
const config = buildConfig(monorepoDemoRoot);
|
|
const replacement = getAdminAliasReplacement(config);
|
|
|
|
expect(basename(replacement)).toBe("src");
|
|
expect(replacement).toMatch(adminSourcePattern);
|
|
});
|
|
|
|
it("uses built admin dist for external app dev", () => {
|
|
const config = buildConfig(externalProjectRoot);
|
|
const replacement = getAdminAliasReplacement(config);
|
|
|
|
expect(basename(replacement)).toBe("dist");
|
|
expect(replacement).toMatch(adminDistPattern);
|
|
});
|
|
|
|
it("uses built admin dist for sibling paths with a matching prefix", () => {
|
|
const config = buildConfig(siblingProjectRoot);
|
|
const replacement = getAdminAliasReplacement(config);
|
|
|
|
expect(basename(replacement)).toBe("dist");
|
|
expect(replacement).toMatch(adminDistPattern);
|
|
});
|
|
|
|
it("uses built admin dist outside dev", () => {
|
|
const config = buildConfig(monorepoDemoRoot, "build");
|
|
const replacement = getAdminAliasReplacement(config);
|
|
|
|
expect(basename(replacement)).toBe("dist");
|
|
expect(replacement).toMatch(adminDistPattern);
|
|
});
|
|
});
|
|
|
|
describe("createViteConfig use-sync-external-store shim aliasing", () => {
|
|
const externalProjectRoot = new URL("file:///workspace/emdash-site/");
|
|
|
|
function buildConfig(adapter: string) {
|
|
return createViteConfig(
|
|
{
|
|
serializableConfig: {},
|
|
resolvedConfig: {} as never,
|
|
pluginDescriptors: [],
|
|
astroConfig: {
|
|
root: externalProjectRoot,
|
|
adapter: { name: adapter },
|
|
} as AstroConfig,
|
|
},
|
|
"dev",
|
|
);
|
|
}
|
|
|
|
function getAlias(config: ReturnType<typeof createViteConfig>, find: string) {
|
|
const aliases = Array.isArray(config.resolve?.alias) ? config.resolve.alias : [];
|
|
return aliases.find(
|
|
(alias) =>
|
|
typeof alias === "object" && alias !== null && "find" in alias && alias.find === find,
|
|
);
|
|
}
|
|
|
|
function getAliasReplacement(config: ReturnType<typeof createViteConfig>, find: string) {
|
|
const alias = getAlias(config, find);
|
|
if (!alias || typeof alias !== "object" || !("replacement" in alias)) {
|
|
throw new Error(`Missing alias for ${find}`);
|
|
}
|
|
if (typeof alias.replacement !== "string") {
|
|
throw new Error(`Alias replacement for ${find} is not a string`);
|
|
}
|
|
return alias.replacement;
|
|
}
|
|
|
|
// Regression: with pnpm + React 18+, @tiptap/react pulls in
|
|
// `use-sync-external-store/shim` (CJS). Vite can't pre-bundle from the
|
|
// virtual store, so browsers get raw CJS and InlinePortableTextEditor
|
|
// fails to hydrate. The aliases redirect the shim to ESM files that use
|
|
// React's built-in hook without loading the warning-only package main
|
|
// entry on React 18+.
|
|
for (const adapter of ["@astrojs/node", "@astrojs/cloudflare"] as const) {
|
|
it(`redirects use-sync-external-store/shim to React-backed ESM shim files on ${adapter}`, () => {
|
|
const config = buildConfig(adapter);
|
|
|
|
const withSelectorPath = getAliasReplacement(
|
|
config,
|
|
"use-sync-external-store/shim/with-selector.js",
|
|
);
|
|
const withSelectorBarePath = getAliasReplacement(
|
|
config,
|
|
"use-sync-external-store/shim/with-selector",
|
|
);
|
|
const indexPath = getAliasReplacement(config, "use-sync-external-store/shim/index.js");
|
|
const shimPath = getAliasReplacement(config, "use-sync-external-store/shim");
|
|
|
|
expect(isAbsolute(withSelectorPath)).toBe(true);
|
|
expect(existsSync(withSelectorPath)).toBe(true);
|
|
expect(withSelectorBarePath).toBe(withSelectorPath);
|
|
expect(basename(withSelectorPath)).toBe("use-sync-external-store-with-selector.js");
|
|
expect(isAbsolute(indexPath)).toBe(true);
|
|
expect(existsSync(indexPath)).toBe(true);
|
|
expect(shimPath).toBe(indexPath);
|
|
expect(basename(indexPath)).toBe("use-sync-external-store.js");
|
|
});
|
|
|
|
it(`lists the more-specific shim aliases before the directory alias on ${adapter}`, () => {
|
|
const config = buildConfig(adapter);
|
|
const aliases = Array.isArray(config.resolve?.alias) ? config.resolve.alias : [];
|
|
|
|
const findIndex = (find: string) =>
|
|
aliases.findIndex(
|
|
(alias) =>
|
|
typeof alias === "object" && alias !== null && "find" in alias && alias.find === find,
|
|
);
|
|
|
|
const withSelectorIdx = findIndex("use-sync-external-store/shim/with-selector.js");
|
|
const withSelectorBareIdx = findIndex("use-sync-external-store/shim/with-selector");
|
|
const indexIdx = findIndex("use-sync-external-store/shim/index.js");
|
|
const shimIdx = findIndex("use-sync-external-store/shim");
|
|
|
|
expect(withSelectorIdx).toBeGreaterThanOrEqual(0);
|
|
expect(withSelectorBareIdx).toBeGreaterThan(withSelectorIdx);
|
|
expect(indexIdx).toBeGreaterThanOrEqual(0);
|
|
expect(shimIdx).toBeGreaterThan(withSelectorBareIdx);
|
|
expect(shimIdx).toBeGreaterThan(indexIdx);
|
|
});
|
|
}
|
|
});
|