From b4f0657d09f23c4dab5b159050ba9d5c8137290f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 5 Aug 2026 16:00:26 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=A6=20feat:=20add=20cereale/min,=20and?= =?UTF-8?q?=20a=20verified=20framework=20guide?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **cereale/min** — the library flattened into one minified ES module, 25.5 KB / 8.6 KB gzipped, for import maps, +``` diff --git a/README.md b/README.md index b308c8d..64395ae 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,20 @@ inside a native binary with no standalone transform API. | swc | ✅ | `jsc.transform.decoratorVersion: "2022-03"` | | **oxc** | ❌ | Used by **Vite 8** and **Vitest 4** — see below | +### Frameworks + +**[FRAMEWORKS.md](FRAMEWORKS.md)** has a setup recipe for each, every one of them run before it +was written. The short version: + +| | | | +| --- | --- | --- | +| **Angular** 21 | ✅ | Flip the scaffolded `experimentalDecorators` to `false`. Angular does not need it — `ngtsc` erases its own decorators itself | +| **React, Vue, Svelte, Solid, Astro, Nuxt** | ✅ | Any Vite 8 app: add the `cereale/vite` plugin below | +| **Bun** | ✅ | No configuration | +| **Node** + `tsc` | ✅ | Just the flag | +| **Next.js** 16 | ⚠️ | Not inline: it derives both the SWC parser *and* the transform from one flag, so decorators either compile legacy or fail to parse. Keep your models in a package compiled by `tsc` | +| **NestJS** 11 | ⚠️ | Not inline: its DI needs `emitDecoratorMetadata`. Same precompiled-package route — verified working alongside `@Injectable()` in a program with both legacy flags on | + **If you are on Vite 8 or Vitest 4**, oxc leaves decorator syntax in the output without reporting anything: `vitest` prints `0 test` next to a bare `SyntaxError`, and `vite build` reports success while emitting a bundle that throws the moment it is imported. Cereale ships diff --git a/docs/index.html b/docs/index.html index adc51bf..7367112 100644 --- a/docs/index.html +++ b/docs/index.html @@ -271,6 +271,7 @@ header.nav { .panel p { color: var(--text-muted); font-size: .9375rem; margin: 0; } .tick { color: var(--ok); font-weight: 700; } .cross { color: var(--bad); font-weight: 700; } +.warn-mark { color: var(--warn); font-weight: 700; } .compare { display: grid; gap: 1rem; } @media (min-width: 860px) { .compare { grid-template-columns: 1fr 1fr; } } @@ -693,6 +694,26 @@ Break the cycle with @JsonIgnore() on the back-reference, or supply a that fixes it. +
+ + + + + + + + + + + +
Framework support
FrameworkWorksWhat it takes
Angular 21✓Flip the scaffolded experimentalDecorators to false — Angular does not need it
React, Vue, Svelte…✓Any Vite 8 app — add the plugin below
Bun✓Nothing
Node + tsc✓Just the flag
Next.js 16~Not inline — keep models in a package compiled by tsc
NestJS 11~Not inline — its DI needs emitDecoratorMetadata; same precompiled route
+
+

+ Each of these was set up and run before it was written down. + FRAMEWORKS.md + has the full recipe for every one, including the two that need the precompiled route. +

+
vite.config.ts
import { defineConfig } from 'vite';
diff --git a/docs/meta.js b/docs/meta.js
index 752d437..e7ae56a 100644
--- a/docs/meta.js
+++ b/docs/meta.js
@@ -1,5 +1,5 @@
 // Generated by scripts/build-docs.mjs — do not edit.
 window.CEREALE_META = {
-  "version": "0.3.0",
+  "version": "0.4.0",
   "node": ">=20.0.0"
 };
diff --git a/package.json b/package.json
index 6fb0ce8..621b179 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "cereale",
-  "version": "0.3.0",
+  "version": "0.4.0",
   "description": "Strongly typed JSON mapping and validation for TypeScript classes \u2014 validated domain objects, not validated data. A zero-dependency replacement for class-validator + class-transformer, on TC39 standard decorators.",
   "type": "module",
   "main": "./dist/cjs/index.js",
@@ -16,21 +16,27 @@
       "types": "./dist/esm/vite.d.ts",
       "import": "./dist/esm/vite.js",
       "require": "./dist/cjs/vite.js"
+    },
+    "./min": {
+      "types": "./dist/esm/index.d.ts",
+      "default": "./dist/cereale.min.js"
     }
   },
   "sideEffects": [
     "./dist/esm/metadata.js",
-    "./dist/cjs/metadata.js"
+    "./dist/cjs/metadata.js",
+    "./dist/cereale.min.js"
   ],
   "files": [
     "dist",
     "src",
+    "FRAMEWORKS.md",
     "CHANGELOG.md",
     "!src/**/*.test.ts",
     "!src/example.ts"
   ],
   "scripts": {
-    "build": "rm -rf dist && tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && echo '{\"type\": \"commonjs\"}' > dist/cjs/package.json",
+    "build": "rm -rf dist && tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && echo '{\"type\": \"commonjs\"}' > dist/cjs/package.json && node scripts/build-bundle.mjs",
     "build:docs": "node scripts/build-docs.mjs",
     "demo": "node --no-warnings=ExperimentalWarning --loader ts-node/esm src/example.ts",
     "type-check": "tsc --noEmit",
diff --git a/scripts/build-bundle.mjs b/scripts/build-bundle.mjs
new file mode 100644
index 0000000..d8bfd6d
--- /dev/null
+++ b/scripts/build-bundle.mjs
@@ -0,0 +1,60 @@
+/**
+ * Flattens the ESM build into a single minified module.
+ *
+ * This is an *addition*, not a replacement. `dist/esm` stays the default `import`, because
+ * measuring says the flat file buys a consumer nothing: bundled through esbuild the two come
+ * out within 2 bytes of each other, through rollup+terser the flat one is ~165 bytes smaller,
+ * and unused decorators tree-shake out of both. What the per-module build keeps is readable
+ * stack traces for anyone who does not load source maps.
+ *
+ * Where the single file does earn its place is everywhere a bundler is not involved: a
+ * `