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 | Works | What 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. +
+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
+ * `