diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 250afa6..99ad018 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,7 @@ jobs: node --input-type=commonjs -e "const m = require('./dist/cjs/index.js'); if (typeof m.toInstance !== 'function') throw new Error('CJS entry point broken');" node --input-type=module -e "import { standardDecorators } from './dist/esm/vite.js'; if (standardDecorators().enforce !== 'pre') throw new Error('ESM cereale/vite broken');" node --input-type=commonjs -e "const { standardDecorators } = require('./dist/cjs/vite.js'); if (standardDecorators().enforce !== 'pre') throw new Error('CJS cereale/vite broken');" + node --input-type=module -e "import * as m from './dist/cereale.min.js'; if (typeof m.toInstanceSync !== 'function') throw new Error('flat bundle broken');" - name: Run Demo run: npm run demo - name: Published types stand alone diff --git a/CHANGELOG.md b/CHANGELOG.md index f11a858..b5460af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,63 @@ All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.4.0] - 2026-08-05 + +### `cereale/min` — one file, no bundler + +The whole library flattened into a single 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
+ * `