fix: validate sandbox plugin exports and fix plugin packaging (#363)
* fix(webhook-notifier): add build step and export built files
The webhook-notifier plugin exported raw TypeScript source from its
package.json exports (./sandbox pointed to src/sandbox-entry.ts).
When the Vite plugin resolved this at site build time, it embedded
unbuilt TypeScript into the sandbox module, causing "Unexpected token
'{'" errors at runtime.
Add a tsdown build step (matching sandboxed-test's pattern) and update
the exports map to point to dist/*.mjs.
Fixes #150
* fix(core): reject unbuilt source in sandbox module generator and bundle validator
Add two validation checks to prevent plugins with misconfigured exports
from silently breaking site builds:
1. generateSandboxedPluginsModule() now throws a clear error if a
sandbox entrypoint resolves to a TypeScript/JSX source file instead
of pre-built JavaScript. This catches the problem at site build time
with an actionable message.
2. The `emdash bundle` command now validates that all package.json
exports point to built files (.js/.mjs), not source (.ts/.tsx/.jsx).
This catches the misconfiguration at plugin publish time, before
consumers are affected.
Fixes #150
* chore: add changeset for sandbox source validation
* fix: use slash syntax for e18e rule override in oxlintrc
The test file override for e18e/prefer-static-regex used parenthesis
syntax ("e18e(prefer-static-regex)") which is the diagnostic display
format, not the config format. Changed to slash syntax to match the
top-level rule declarations so the override actually takes effect.
* test: add tests for sandbox source validation
Add tests for both validation checks:
- generateSandboxedPluginsModule: verifies it embeds pre-built JS,
rejects .ts/.tsx/.mts source files, and includes the plugin ID in
error messages.
- findSourceExports: verifies it flags .ts/.tsx/.mts/.cts/.jsx exports,
accepts .mjs/.js exports, and handles conditional export maps.
Also extracts findSourceExports() from the inline bundle.ts validation
into bundle-utils.ts so it can be tested without the CLI harness.
* fix(atproto, audit-log): add build step and export built files
Same issue as webhook-notifier — both plugins exported raw TypeScript
source from their package.json sandbox exports. Add tsdown build steps
and update exports to point to dist/*.mjs.
* refactor(smoke): replace sequential per-site astro builds with recursive pnpm build
The build verification section was running `astro build` individually
and sequentially for every demo and template (~12 sites). Replace with
a single `pnpm run --recursive --filter {./demos/*} --filter
{./templates/*} build` which pnpm parallelizes automatically.
This commit is contained in:
@@ -181,39 +181,37 @@ async function fetchWithRetry(url: string, retries = 10, delayMs = 1500): Promis
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Build verification — runs `astro build` for every site to catch adapter
|
||||
// and bundling errors that dev mode doesn't surface.
|
||||
// Build verification — runs a single recursive `pnpm build` across all demos
|
||||
// and templates in parallel, then verifies each site produced output.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe.sequential("Site build verification", () => {
|
||||
const BUILD_TIMEOUT = 120_000;
|
||||
describe("Site build verification", () => {
|
||||
it("all demos and templates build successfully", { timeout: 300_000 }, async () => {
|
||||
await ensureBuilt();
|
||||
|
||||
for (const site of SITE_MATRIX) {
|
||||
if (site.mode === "typecheck") continue;
|
||||
|
||||
it(`${site.name} builds successfully`, { timeout: BUILD_TIMEOUT + 30_000 }, async () => {
|
||||
await ensureBuilt();
|
||||
|
||||
try {
|
||||
await execAsync("pnpm", ["exec", "astro", "build"], {
|
||||
cwd: site.dir,
|
||||
timeout: BUILD_TIMEOUT,
|
||||
try {
|
||||
await execAsync(
|
||||
"pnpm",
|
||||
["run", "--recursive", "--filter", "{./demos/*}", "--filter", "{./templates/*}", "build"],
|
||||
{
|
||||
cwd: WORKSPACE_ROOT,
|
||||
timeout: 240_000,
|
||||
env: {
|
||||
...process.env,
|
||||
CI: "true",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
const stderr =
|
||||
error instanceof Error && "stderr" in error ? (error as { stderr: string }).stderr : "";
|
||||
const stdout =
|
||||
error instanceof Error && "stdout" in error ? (error as { stdout: string }).stdout : "";
|
||||
throw new Error(`${site.name} build failed:\n\n${stderr || stdout}`.slice(0, 5000), {
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (error) {
|
||||
const stderr =
|
||||
error instanceof Error && "stderr" in error ? (error as { stderr: string }).stderr : "";
|
||||
const stdout =
|
||||
error instanceof Error && "stdout" in error ? (error as { stdout: string }).stdout : "";
|
||||
throw new Error(`Site builds failed:\n\n${stderr || stdout}`.slice(0, 5000), {
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user