diff --git a/CHANGELOG.md b/CHANGELOG.md index ed2cd218..bf70f4f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,47 @@ merges to `main`; this file is the human-curated breaking-change ledger. For per-release auto-generated notes (every commit, every fix), see [GitHub Releases](https://github.com/decocms/deco-start/releases). +## 5.1.1 — Restore generate-sections/generate-loaders, drop broken ESM scripts + +Patch release fixing build-time-script regressions from `5.1.0`. + +### Fixed + +- **`generate-sections` and `generate-loaders` are back.** Both scripts still + existed in source but were accidentally dropped from the `tsup` build + entries in `5.1.0` (PR #164 oversight). Combined with `files: ["dist"]`, + this hid them from consumers. Re-added as `dist/scripts/generate-sections.cjs` + and `dist/scripts/generate-loaders.cjs`, with `./scripts/generate-sections` + and `./scripts/generate-loaders` entries restored in `package.json` exports. +- **`dist/scripts/*.js` (ESM) no longer ships.** The ESM bundles were broken + by design: `tsup` was configured with `platform: "neutral"`, which in ESM + mode wraps externalized `require()` calls in a `__require` shim that + throws `Dynamic require of "fs" is not supported` as soon as `ts-morph` + loads. CLI scripts are now CJS-only (`format: ["cjs"]`, `platform: "node"`) + — invoke via `node node_modules/@decocms/start/dist/scripts/.cjs`. +- **`generate-invoke` resolution + error message.** `resolveAppsDir()` now + walks up `node_modules` from CWD (handles npm/bun hoisting in monorepos) + and distinguishes "`@decocms/apps` not installed" from "installed but + `vtex/invoke.ts` missing" — the latter is the common case because the + published `@decocms/apps` tarball does not ship the dev-time source file + the script parses. The error now tells consumers to point `--apps-dir` at + a local checkout of `decocms/apps-start`, or to skip regeneration and + keep using the committed `src/server/invoke.gen.ts`. + +### Notes + +- `5.1.0` was published as a minor but contained these regressions, which + acted as a hard breaking change for any site that ran the build-time + generators as part of its build pipeline. `5.1.1` is the recommended + upgrade path; consumers should update their `package.json` script paths + to invoke the compiled `.cjs` files (e.g. + `node node_modules/@decocms/start/dist/scripts/generate-schema.cjs`). +- The published `@decocms/apps` tarball does not ship `vtex/invoke.ts`, + so `generate-invoke` only works against a local checkout of + `decocms/apps-start`. Existing sites that already have a committed + `invoke.gen.ts` can skip regeneration entirely — runtime imports the + pre-generated file, not the script. + ## 5.0.0 — Drop in-Worker OTLP, converge on Cloudflare-native observability ### Breaking — Observability transport rewritten diff --git a/package.json b/package.json index 480672ae..51dd2c3d 100644 --- a/package.json +++ b/package.json @@ -326,38 +326,39 @@ }, "./scripts/generate-blocks": { "types": "./dist/scripts/generate-blocks.d.ts", - "import": "./dist/scripts/generate-blocks.js", - "require": "./dist/scripts/generate-blocks.cjs" + "default": "./dist/scripts/generate-blocks.cjs" }, "./scripts/generate-schema": { "types": "./dist/scripts/generate-schema.d.ts", - "import": "./dist/scripts/generate-schema.js", - "require": "./dist/scripts/generate-schema.cjs" + "default": "./dist/scripts/generate-schema.cjs" }, "./scripts/generate-invoke": { "types": "./dist/scripts/generate-invoke.d.ts", - "import": "./dist/scripts/generate-invoke.js", - "require": "./dist/scripts/generate-invoke.cjs" + "default": "./dist/scripts/generate-invoke.cjs" + }, + "./scripts/generate-sections": { + "types": "./dist/scripts/generate-sections.d.ts", + "default": "./dist/scripts/generate-sections.cjs" + }, + "./scripts/generate-loaders": { + "types": "./dist/scripts/generate-loaders.d.ts", + "default": "./dist/scripts/generate-loaders.cjs" }, "./scripts/migrate": { "types": "./dist/scripts/migrate.d.ts", - "import": "./dist/scripts/migrate.js", - "require": "./dist/scripts/migrate.cjs" + "default": "./dist/scripts/migrate.cjs" }, "./scripts/migrate-post-cleanup": { "types": "./dist/scripts/migrate-post-cleanup.d.ts", - "import": "./dist/scripts/migrate-post-cleanup.js", - "require": "./dist/scripts/migrate-post-cleanup.cjs" + "default": "./dist/scripts/migrate-post-cleanup.cjs" }, "./scripts/migrate-to-cf-observability": { "types": "./dist/scripts/migrate-to-cf-observability.d.ts", - "import": "./dist/scripts/migrate-to-cf-observability.js", - "require": "./dist/scripts/migrate-to-cf-observability.cjs" + "default": "./dist/scripts/migrate-to-cf-observability.cjs" }, "./scripts/tailwind-lint": { "types": "./dist/scripts/tailwind-lint.d.ts", - "import": "./dist/scripts/tailwind-lint.js", - "require": "./dist/scripts/tailwind-lint.cjs" + "default": "./dist/scripts/tailwind-lint.cjs" }, "./vite": { "import": "./dist/tanstack/vite/plugin.js", diff --git a/scripts/generate-invoke.ts b/scripts/generate-invoke.ts index 73be8db6..b5efd475 100644 --- a/scripts/generate-invoke.ts +++ b/scripts/generate-invoke.ts @@ -35,15 +35,45 @@ function resolveAppsDir(): string { const explicit = arg("apps-dir", ""); if (explicit) return path.resolve(cwd, explicit); - // Try common locations - const candidates = [ - path.resolve(cwd, "node_modules/@decocms/apps"), - path.resolve(cwd, "../apps-start"), - ]; + // Walk up from CWD collecting every node_modules/@decocms/apps along the way + // (handles npm/bun hoisting in monorepos), plus a couple of dev fallbacks. + const candidates: string[] = []; + let dir = cwd; + while (true) { + candidates.push(path.join(dir, "node_modules/@decocms/apps")); + const parent = path.dirname(dir); + if (parent === dir) break; + dir = parent; + } + candidates.push(path.resolve(cwd, "../apps-start")); + candidates.push(path.resolve(cwd, "../decocms-apps")); + + // First pass: a candidate that has vtex/invoke.ts is fully usable. for (const c of candidates) { if (fs.existsSync(path.join(c, "vtex/invoke.ts"))) return c; } - throw new Error("Could not find @decocms/apps. Use --apps-dir to specify its location."); + + // Second pass: a candidate exists as @decocms/apps but the source file we + // need to parse is missing. The published @decocms/apps tarball does not + // include vtex/invoke.ts — it is a dev-time source-of-truth file. Surface a + // distinct, actionable error so consumers don't chase a non-existent + // "package not installed" bug. + for (const c of candidates) { + if (fs.existsSync(path.join(c, "package.json"))) { + throw new Error( + `Found @decocms/apps at ${c} but it is missing vtex/invoke.ts.\n` + + `generate-invoke parses the TS source-of-truth, which is not shipped\n` + + `in the published npm tarball. Point --apps-dir at a local checkout of\n` + + `the decocms/apps-start repo (e.g. --apps-dir ../apps-start), or skip\n` + + `regeneration and use the existing src/server/invoke.gen.ts.`, + ); + } + } + + throw new Error( + "Could not find @decocms/apps in node_modules (walked up from " + + `${cwd}). Install it, or pass --apps-dir .`, + ); } const appsDir = resolveAppsDir(); diff --git a/tsup.config.ts b/tsup.config.ts index 75e65ee3..67af9d94 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -133,23 +133,33 @@ export default defineConfig([ "scripts/generate-blocks.ts", "scripts/generate-schema.ts", "scripts/generate-invoke.ts", + "scripts/generate-sections.ts", + "scripts/generate-loaders.ts", "scripts/migrate.ts", "scripts/migrate-post-cleanup.ts", "scripts/migrate-to-cf-observability.ts", "scripts/htmx-analyze.ts", "scripts/tailwind-lint.ts", ], - format: ["esm", "cjs"], + // Scripts are CLI tools invoked via `node …/foo.cjs` — CJS only. ESM bundles + // of ts-morph (which inlines TypeScript) leave `require("fs")` callsites + // intact; in an ESM context those go through a __require shim that throws + // "Dynamic require of fs is not supported". package.json `"type": "module"` + // means a bare .js file would be loaded as ESM, so we don't ship one. + format: ["cjs"], dts: false, splitting: false, sourcemap: true, clean: false, outDir: "dist/scripts", target: "es2022", + // platform: "node" auto-externalizes Node built-ins and emits proper + // require() for bundled CJS deps. Avoids the dynamic-require shim that + // platform: "neutral" produces. external: sharedExternal, esbuildOptions(opts) { opts.jsx = "automatic"; - opts.platform = "neutral"; + opts.platform = "node"; opts.outbase = "scripts"; }, ignoreWatch: ["**/*.test.ts", "**/*.test.tsx"],