freezedts 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +319 -0
  3. package/dist/cli.d.ts +13 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +121 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/excluded-dirs.d.ts +2 -0
  8. package/dist/excluded-dirs.d.ts.map +1 -0
  9. package/dist/excluded-dirs.js +2 -0
  10. package/dist/excluded-dirs.js.map +1 -0
  11. package/dist/generator/config.d.ts +18 -0
  12. package/dist/generator/config.d.ts.map +1 -0
  13. package/dist/generator/config.js +40 -0
  14. package/dist/generator/config.js.map +1 -0
  15. package/dist/generator/emitter.d.ts +3 -0
  16. package/dist/generator/emitter.d.ts.map +1 -0
  17. package/dist/generator/emitter.js +170 -0
  18. package/dist/generator/emitter.js.map +1 -0
  19. package/dist/generator/generator.d.ts +8 -0
  20. package/dist/generator/generator.d.ts.map +1 -0
  21. package/dist/generator/generator.js +188 -0
  22. package/dist/generator/generator.js.map +1 -0
  23. package/dist/generator/parser.d.ts +32 -0
  24. package/dist/generator/parser.d.ts.map +1 -0
  25. package/dist/generator/parser.js +154 -0
  26. package/dist/generator/parser.js.map +1 -0
  27. package/dist/generator/watcher.d.ts +11 -0
  28. package/dist/generator/watcher.d.ts.map +1 -0
  29. package/dist/generator/watcher.js +45 -0
  30. package/dist/generator/watcher.js.map +1 -0
  31. package/dist/index.d.ts +3 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +2 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/runtime/copy.d.ts +3 -0
  36. package/dist/runtime/copy.d.ts.map +1 -0
  37. package/dist/runtime/copy.js +43 -0
  38. package/dist/runtime/copy.js.map +1 -0
  39. package/dist/runtime/deepEqual.d.ts +2 -0
  40. package/dist/runtime/deepEqual.d.ts.map +1 -0
  41. package/dist/runtime/deepEqual.js +43 -0
  42. package/dist/runtime/deepEqual.js.map +1 -0
  43. package/dist/runtime/deepFreeze.d.ts +2 -0
  44. package/dist/runtime/deepFreeze.d.ts.map +1 -0
  45. package/dist/runtime/deepFreeze.js +35 -0
  46. package/dist/runtime/deepFreeze.js.map +1 -0
  47. package/dist/runtime/freezed.d.ts +16 -0
  48. package/dist/runtime/freezed.d.ts.map +1 -0
  49. package/dist/runtime/freezed.js +11 -0
  50. package/dist/runtime/freezed.js.map +1 -0
  51. package/dist/runtime/index.d.ts +6 -0
  52. package/dist/runtime/index.d.ts.map +1 -0
  53. package/dist/runtime/index.js +6 -0
  54. package/dist/runtime/index.js.map +1 -0
  55. package/package.json +53 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 James Gorman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,319 @@
1
+ # freezedts
2
+
3
+ Immutable class generation for TypeScript — deep copying, value equality, and runtime immutability via decorators and code generation.
4
+
5
+ A TypeScript port of Dart's [freezed](https://github.com/rrousselGit/freezed) package by Remi Rousselet.
6
+
7
+ ## Table of Contents
8
+
9
+ - [Motivation](#motivation)
10
+ - [Installation](#installation)
11
+ - [Running the Generator](#running-the-generator)
12
+ - [Creating Classes](#creating-classes)
13
+ - [Basic Usage](#basic-usage)
14
+ - [Field Configuration](#field-configuration)
15
+ - [Nested Freezed Types](#nested-freezed-types)
16
+ - [Collections](#collections)
17
+ - [Deep Copy](#deep-copy)
18
+ - [Deep Equality](#deep-equality)
19
+ - [Configuration](#configuration)
20
+ - [Per-Class Configuration](#per-class-configuration)
21
+ - [Project-Wide Configuration](#project-wide-configuration)
22
+ - [Resolution Order](#resolution-order)
23
+ - [Building the Library](#building-the-library)
24
+
25
+ ## Motivation
26
+
27
+ TypeScript has no native support for immutable classes with named parameters. Achieving truly immutable data classes requires significant boilerplate:
28
+
29
+ - `readonly` on every property
30
+ - `Object.freeze()` in every constructor
31
+ - Manual `copyWith` methods for creating modified copies
32
+ - Manual `equals` methods for structural comparison
33
+ - Recursive freezing of nested collections
34
+
35
+ freezedts eliminates this boilerplate. You write a class with a decorator, and the generator produces an abstract base class that handles immutability, deep copying, value equality, `toString()`, and collection freezing.
36
+
37
+ ```ts
38
+ // You write this:
39
+ @freezed()
40
+ class Person extends $Person {
41
+ constructor(params: { firstName: string; lastName: string; age: number }) {
42
+ super(params);
43
+ }
44
+ }
45
+ ```
46
+ The generator produces `$Person` with:
47
+ - readonly properties
48
+ - `Object.freeze(this)` in the constructor
49
+ - `person.with({ age: 31 })` for copying
50
+ - `person.equals(other)` for deep structural comparison
51
+ - `person.toString()` → `"Person(firstName: John, lastName: Smith, age: 30)"`
52
+ - Recursive freezing of arrays, Maps, and Sets
53
+
54
+
55
+ ## Installation
56
+
57
+ ```bash
58
+ npm install freezedts
59
+ ```
60
+
61
+ `freezedts` must be a runtime dependency. It has a small runtime footprint and
62
+ no transient runtime dependencies.
63
+
64
+ **Requirements:** TypeScript 6, ESM, TC39 stage 3 decorators.
65
+
66
+ ## Running the Generator
67
+
68
+ ```bash
69
+ # Generate .freezed.ts files for all source files in the current directory
70
+ npx freezedts
71
+
72
+ # Generate for a specific directory
73
+ npx freezedts src
74
+
75
+ # Watch mode — regenerate on file changes
76
+ npx freezedts --watch
77
+ npx freezedts -w src
78
+
79
+ # Use a custom config file
80
+ npx freezedts --config path/to/freezedts.config.yaml
81
+ npx freezedts -c custom.yaml -w src
82
+ ```
83
+
84
+ The generator scans for `.ts` files containing `@freezed()` classes and produces `.freezed.ts` files alongside them.
85
+
86
+ Only changed files are regenerated (mtime-based).
87
+
88
+ ## Creating Classes
89
+
90
+ ### Basic Usage
91
+
92
+ 1. Create your source file (e.g., `person.ts`):
93
+
94
+ ```ts
95
+ import { freezed } from 'freezedts';
96
+ import { $Person } from './person.freezed.ts';
97
+
98
+ @freezed()
99
+ class Person extends $Person {
100
+ constructor(params: { firstName: string; lastName: string; age: number }) {
101
+ super(params);
102
+ }
103
+ }
104
+ ```
105
+
106
+ 2. Run the generator:
107
+
108
+ ```bash
109
+ npx freezedts
110
+ ```
111
+
112
+ 3. The generated `person.freezed.ts` provides an abstract base class `$Person` with `readonly` properties, `Object.freeze(this)`, `with()`, `equals()`, and `toString()`.
113
+
114
+ Where a source file contains multiple `@freezed` classes, they will be generated to the same file.
115
+
116
+ ```ts
117
+ import { freezed } from 'freezedts';
118
+ import { $Person, $Address } from './person.freezed.ts';
119
+
120
+ @freezed()
121
+ class Person extends $Person {
122
+ ...
123
+ }
124
+
125
+ @freezed()
126
+ class Address extends $Address {
127
+ ...
128
+ }
129
+ ```
130
+
131
+
132
+ ### Field Configuration
133
+
134
+ Configure defaults and validation in the `@freezed()` decorator's `fields` option:
135
+
136
+ ```ts
137
+ @freezed({
138
+ fields: {
139
+ port: {
140
+ default: 3000,
141
+ assert: (v: number) => v > 0 && v < 65536,
142
+ message: 'port out of range',
143
+ },
144
+ host: { default: 'localhost' },
145
+ },
146
+ })
147
+ class ServerConfig extends $ServerConfig {
148
+ constructor(params: { name: string; host?: string; port?: number }) {
149
+ super(params);
150
+ }
151
+ }
152
+
153
+ const config = new ServerConfig({ name: 'api' });
154
+ config.host; // 'localhost'
155
+ config.port; // 3000
156
+
157
+ new ServerConfig({ name: 'api', port: -1 }); // throws: "port out of range"
158
+ ```
159
+
160
+ Field config options:
161
+
162
+ | Option | Type | Description |
163
+ |--------|------|-------------|
164
+ | `default` | `unknown` | Default value when the parameter is `undefined` |
165
+ | `assert` | `(value) => boolean` | Validation function run at construction time |
166
+ | `message` | <code>string &#124; undefined</code> | An optional error message when the assertion fails. If omitted, a basic error message will be generated. |
167
+
168
+ ### Collections
169
+
170
+ Arrays, Maps, and Sets are recursively frozen at construction time. Mutation attempts throw at runtime:
171
+
172
+ ```ts
173
+ @freezed()
174
+ class Team extends $Team {
175
+ constructor(params: { name: string; members: string[]; scores: number[] }) {
176
+ super(params);
177
+ }
178
+ }
179
+
180
+ const team = new Team({ name: 'Alpha', members: ['Alice', 'Bob'], scores: [10, 20] });
181
+ team.members.push('Charlie'); // throws TypeError
182
+ team.members[0] = 'Zara'; // throws TypeError
183
+ ```
184
+
185
+ ## Deep Copy
186
+
187
+ The `with()` method creates a new frozen instance with selective property overrides:
188
+
189
+ ```ts
190
+ const alice = new Person({ firstName: 'Alice', lastName: 'Smith', age: 30 });
191
+ const bob = alice.with({ firstName: 'Bob' });
192
+ // bob → Person(firstName: Bob, lastName: Smith, age: 30)
193
+ // alice is unchanged
194
+ ```
195
+
196
+ For nested `@freezed` types, `with` supports proxy-chained deep copies:
197
+
198
+ ```ts
199
+ @freezed()
200
+ class Assistant extends $Assistant {
201
+ constructor(params: { name: string }) { super(params); }
202
+ }
203
+
204
+ @freezed()
205
+ class Director extends $Director {
206
+ constructor(params: { name: string; assistant: Assistant }) { super(params); }
207
+ }
208
+
209
+ @freezed()
210
+ class Company extends $Company {
211
+ constructor(params: { name: string; director: Director }) { super(params); }
212
+ }
213
+
214
+ const co = new Company({
215
+ name: 'Acme',
216
+ director: new Director({
217
+ name: 'Jane',
218
+ assistant: new Assistant({ name: 'John' }),
219
+ }),
220
+ });
221
+
222
+ // Shallow copy
223
+ co.with({ name: 'NewCo' });
224
+
225
+ // Deep copy — update a nested freezed property
226
+ co.with.director({ name: 'Larry' });
227
+ co.with.director.assistant({ name: 'Sue' });
228
+ ```
229
+
230
+
231
+ ## Deep Equality
232
+
233
+ The `equals()` method performs structural comparison:
234
+
235
+ ```ts
236
+ const a = new Person({ firstName: 'Alice', lastName: 'Smith', age: 30 });
237
+ const b = new Person({ firstName: 'Alice', lastName: 'Smith', age: 30 });
238
+
239
+ a === b; // false (different instances)
240
+ a.equals(b); // true (same structure)
241
+ ```
242
+
243
+ Configure equality mode per class:
244
+
245
+ ```ts
246
+ @freezed({ equality: 'deep' }) // default — recursive structural comparison
247
+ class DeepPerson extends $DeepPerson { ... }
248
+
249
+ @freezed({ equality: 'shallow' }) // === for primitives, .equals() for nested freezed types
250
+ class ShallowPerson extends $ShallowPerson { ... }
251
+ ```
252
+
253
+ ## Configuration
254
+
255
+ ### Per-Class Configuration
256
+
257
+ Disable generation of specific methods via the `@freezed()` decorator:
258
+
259
+ ```ts
260
+ @freezed({ copyWith: false }) // skip with() generation
261
+ @freezed({ equal: false }) // skip equals() generation
262
+ @freezed({ toString: false }) // skip toString() generation
263
+ @freezed({ copyWith: false, equal: false, toString: false }) // only immutability
264
+ ```
265
+
266
+ ### Project-Wide Configuration
267
+
268
+ Create a `freezedts.config.yaml` in your project root:
269
+
270
+ ```yaml
271
+ freezed:
272
+ options:
273
+ # Format generated .freezed.ts files (can slow down generation)
274
+ format: true
275
+
276
+ # Disable with() generation for the entire project
277
+ copyWith: false
278
+
279
+ # Disable equals() generation for the entire project
280
+ equal: false
281
+
282
+ # Disable toString() generation for the entire project
283
+ toString: false
284
+ ```
285
+
286
+ All options default to `true` (enabled) except `format` which defaults to `false`.
287
+
288
+ ### Resolution Order
289
+
290
+ Per-class `@freezed()` options override project-wide `freezedts.config.yaml` defaults. If neither specifies a value, the built-in default applies (all features enabled).
291
+
292
+ ```
293
+ per-class @freezed() → freezedts.config.yaml → built-in defaults
294
+ (highest priority) (lowest priority)
295
+ ```
296
+
297
+ ## Building the Library
298
+
299
+ ```bash
300
+ # Install dependencies
301
+ npm install
302
+
303
+ # Build TypeScript to dist/
304
+ npm run build
305
+
306
+ # Run tests
307
+ npm test
308
+
309
+ # Run the generator on this project's source files
310
+ npm run generate
311
+
312
+ # Watch mode for tests
313
+ npm run test:watch
314
+ ```
315
+
316
+ The project uses:
317
+ - **TypeScript 6** with ES2022 target and Node16 module resolution
318
+ - **bun:test** for testing
319
+ - **ts-morph** for AST parsing and code generation
package/dist/cli.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ export declare function resolveSourceFiles(dir: string): string[];
3
+ export interface CliArgs {
4
+ watch: boolean;
5
+ dir: string;
6
+ config?: string;
7
+ }
8
+ export declare function filterChangedFiles(files: string[]): {
9
+ changed: string[];
10
+ skipped: number;
11
+ };
12
+ export declare function parseArgs(argv: string[]): CliArgs;
13
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAUA,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAwBxD;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAkB1F;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAkBjD"}
package/dist/cli.js ADDED
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+ import * as fs from 'node:fs';
3
+ import * as path from 'node:path';
4
+ import { generate } from './generator/generator.js';
5
+ import { createWatcher } from './generator/watcher.js';
6
+ import { loadConfig } from './generator/config.js';
7
+ import { EXCLUDED_DIRS } from './excluded-dirs.js';
8
+ export function resolveSourceFiles(dir) {
9
+ const results = [];
10
+ function walk(currentDir) {
11
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
12
+ for (const entry of entries) {
13
+ if (entry.isDirectory()) {
14
+ if (!EXCLUDED_DIRS.has(entry.name)) {
15
+ walk(path.join(currentDir, entry.name));
16
+ }
17
+ }
18
+ else if (entry.isFile() &&
19
+ entry.name.endsWith('.ts') &&
20
+ !entry.name.endsWith('.freezed.ts') &&
21
+ !entry.name.endsWith('.test.ts') &&
22
+ !entry.name.endsWith('.d.ts')) {
23
+ results.push(path.join(currentDir, entry.name));
24
+ }
25
+ }
26
+ }
27
+ walk(dir);
28
+ return results;
29
+ }
30
+ export function filterChangedFiles(files) {
31
+ const changed = [];
32
+ let skipped = 0;
33
+ for (const file of files) {
34
+ const outputPath = file.replace(/\.ts$/, '.freezed.ts');
35
+ try {
36
+ const sourceMtime = fs.statSync(file).mtimeMs;
37
+ const outputMtime = fs.statSync(outputPath).mtimeMs;
38
+ if (outputMtime >= sourceMtime) {
39
+ skipped++;
40
+ continue;
41
+ }
42
+ }
43
+ catch {
44
+ // Output file doesn't exist — needs generation
45
+ }
46
+ changed.push(file);
47
+ }
48
+ return { changed, skipped };
49
+ }
50
+ export function parseArgs(argv) {
51
+ const args = argv.slice(2);
52
+ let watch = false;
53
+ let dir = '.';
54
+ let config;
55
+ for (let i = 0; i < args.length; i++) {
56
+ const arg = args[i];
57
+ if (arg === '--watch' || arg === '-w') {
58
+ watch = true;
59
+ }
60
+ else if ((arg === '--config' || arg === '-c') && i + 1 < args.length) {
61
+ config = args[++i];
62
+ }
63
+ else if (!arg.startsWith('-')) {
64
+ dir = arg;
65
+ }
66
+ }
67
+ return { watch, dir, config };
68
+ }
69
+ function main() {
70
+ const args = parseArgs(process.argv);
71
+ const resolvedDir = path.resolve(args.dir);
72
+ const configPath = args.config ?? path.join(resolvedDir, 'freezedts.config.yaml');
73
+ const config = loadConfig(configPath);
74
+ console.log(`freezedts: scanning ${resolvedDir}`);
75
+ const allFiles = resolveSourceFiles(resolvedDir);
76
+ console.log(`freezedts: found ${allFiles.length} source file(s)`);
77
+ const { changed, skipped } = filterChangedFiles(allFiles);
78
+ const result = generate(changed, config);
79
+ if (skipped > 0) {
80
+ console.log(`freezedts: generated ${result.filesWritten} .freezed.ts file(s), ${skipped} unchanged`);
81
+ }
82
+ else {
83
+ console.log(`freezedts: generated ${result.filesWritten} .freezed.ts file(s)`);
84
+ }
85
+ if (result.warnings.length > 0) {
86
+ result.warnings.forEach((w) => console.warn(` warning: ${w}`));
87
+ }
88
+ if (result.errors.length > 0) {
89
+ console.error('freezedts: errors:');
90
+ result.errors.forEach((e) => console.error(` ${e}`));
91
+ if (!args.watch)
92
+ process.exit(1);
93
+ }
94
+ if (args.watch) {
95
+ console.log('freezedts: watching for changes... (press Ctrl+C to stop)');
96
+ createWatcher({
97
+ dir: resolvedDir,
98
+ onChange: (changedFiles) => {
99
+ const timestamp = new Date().toLocaleTimeString();
100
+ const watchResult = generate(changedFiles, config);
101
+ if (watchResult.filesWritten > 0) {
102
+ for (const f of changedFiles) {
103
+ const rel = path.relative(resolvedDir, f);
104
+ console.log(`[${timestamp}] ${rel} → regenerated`);
105
+ }
106
+ }
107
+ if (watchResult.warnings.length > 0) {
108
+ watchResult.warnings.forEach((w) => console.warn(` warning: ${w}`));
109
+ }
110
+ if (watchResult.errors.length > 0) {
111
+ watchResult.errors.forEach((e) => console.error(` error: ${e}`));
112
+ }
113
+ },
114
+ });
115
+ }
116
+ }
117
+ // Only run when executed directly, not when imported
118
+ if (import.meta.main) {
119
+ main();
120
+ }
121
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const EXCLUDED_DIRS: Set<string>;
2
+ //# sourceMappingURL=excluded-dirs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"excluded-dirs.d.ts","sourceRoot":"","sources":["../src/excluded-dirs.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,aAA4C,CAAC"}
@@ -0,0 +1,2 @@
1
+ export const EXCLUDED_DIRS = new Set(['node_modules', 'dist', '.git']);
2
+ //# sourceMappingURL=excluded-dirs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"excluded-dirs.js","sourceRoot":"","sources":["../src/excluded-dirs.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC"}
@@ -0,0 +1,18 @@
1
+ export interface ResolvedConfig {
2
+ format: boolean;
3
+ copyWith: boolean;
4
+ equal: boolean;
5
+ toString: boolean;
6
+ }
7
+ export declare const DEFAULTS: ResolvedConfig;
8
+ export declare function loadConfig(configPath: string): ResolvedConfig;
9
+ export declare function resolveClassOptions(cls: {
10
+ copyWith?: boolean;
11
+ equal?: boolean;
12
+ toString?: boolean;
13
+ }, config: ResolvedConfig): {
14
+ copyWith: boolean;
15
+ equal: boolean;
16
+ toString: boolean;
17
+ };
18
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/generator/config.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;CACnB;AAaD,eAAO,MAAM,QAAQ,EAAE,cAKtB,CAAC;AAEF,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc,CAsB7D;AAED,wBAAgB,mBAAmB,CACjC,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,EAChE,MAAM,EAAE,cAAc,GACrB;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAO1D"}
@@ -0,0 +1,40 @@
1
+ import * as fs from 'node:fs';
2
+ import { parse as parseYaml } from 'yaml';
3
+ export const DEFAULTS = {
4
+ format: false,
5
+ copyWith: true,
6
+ equal: true,
7
+ toString: true,
8
+ };
9
+ export function loadConfig(configPath) {
10
+ let raw;
11
+ try {
12
+ raw = fs.readFileSync(configPath, 'utf-8');
13
+ }
14
+ catch {
15
+ return { ...DEFAULTS };
16
+ }
17
+ let parsed;
18
+ try {
19
+ parsed = parseYaml(raw);
20
+ }
21
+ catch {
22
+ throw new Error(`freezedts: invalid YAML in config file: ${configPath}`);
23
+ }
24
+ const options = parsed?.freezed?.options;
25
+ return {
26
+ format: options?.format ?? DEFAULTS.format,
27
+ copyWith: options?.copyWith ?? DEFAULTS.copyWith,
28
+ equal: options?.equal ?? DEFAULTS.equal,
29
+ toString: options?.toString ?? DEFAULTS.toString,
30
+ };
31
+ }
32
+ export function resolveClassOptions(cls, config) {
33
+ return {
34
+ copyWith: cls.copyWith ?? config.copyWith,
35
+ equal: cls.equal ?? config.equal,
36
+ // Cannot use ?? because Object.prototype.toString would always be truthy
37
+ toString: Object.hasOwn(cls, 'toString') ? cls.toString : config.toString,
38
+ };
39
+ }
40
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/generator/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAoB1C,MAAM,CAAC,MAAM,QAAQ,GAAmB;IACtC,MAAM,EAAE,KAAK;IACb,QAAQ,EAAE,IAAI;IACd,KAAK,EAAE,IAAI;IACX,QAAQ,EAAE,IAAI;CACf,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,UAAkB;IAC3C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IACzB,CAAC;IAED,IAAI,MAAgC,CAAC;IACrC,IAAI,CAAC;QACH,MAAM,GAAG,SAAS,CAAC,GAAG,CAA6B,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,2CAA2C,UAAU,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC;IAEzC,OAAO;QACL,MAAM,EAAE,OAAO,EAAE,MAAM,IAAI,QAAQ,CAAC,MAAM;QAC1C,QAAQ,EAAE,OAAO,EAAE,QAAQ,IAAI,QAAQ,CAAC,QAAQ;QAChD,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,QAAQ,CAAC,KAAK;QACvC,QAAQ,EAAE,OAAO,EAAE,QAAQ,IAAI,QAAQ,CAAC,QAAQ;KACjD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,GAAgE,EAChE,MAAsB;IAEtB,OAAO;QACL,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ;QACzC,KAAK,EAAE,GAAG,CAAC,KAAK,IAAI,MAAM,CAAC,KAAK;QAChC,yEAAyE;QACzE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAS,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ;KAC3E,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ParsedFreezedClass } from './parser.js';
2
+ export declare function emitFreezedFile(classes: ParsedFreezedClass[]): string;
3
+ //# sourceMappingURL=emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../src/generator/emitter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAItD,wBAAgB,eAAe,CAAC,OAAO,EAAE,kBAAkB,EAAE,GAAG,MAAM,CA0BrE"}