@greenlabs/ppx-spice 0.3.1 → 0.3.2

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.
package/AGENTS.md ADDED
@@ -0,0 +1,232 @@
1
+ # AGENTS.md
2
+
3
+ This document provides guidelines for AI coding agents working on the ppx_spice project.
4
+
5
+ ## Project Overview
6
+
7
+ ppx_spice is a ReScript PPX that generates JSON (de)serializers. It is written in OCaml
8
+ and uses ppxlib for AST manipulation. The PPX processes ReScript type definitions annotated
9
+ with `@spice` and generates corresponding `_encode` and `_decode` functions.
10
+
11
+ ## Build Commands
12
+
13
+ All OCaml build commands must be run from the `src/` directory:
14
+
15
+ ```bash
16
+ # Navigate to source directory
17
+ cd src
18
+
19
+ # Create opam switch (first time setup)
20
+ opam switch create spice 4.14.0
21
+
22
+ # Install dependencies
23
+ opam install . --deps-only
24
+
25
+ # Build the PPX
26
+ dune build
27
+
28
+ # Build with static linking (for releases)
29
+ dune build --profile static
30
+
31
+ # Clean build artifacts
32
+ dune clean
33
+ ```
34
+
35
+ ## Test Commands
36
+
37
+ Tests are written in ReScript and located in the `test/` directory:
38
+
39
+ ```bash
40
+ # Navigate to test directory
41
+ cd test
42
+
43
+ # Install dependencies
44
+ pnpm install
45
+
46
+ # Build ReScript code
47
+ pnpm res:build
48
+
49
+ # Clean and rebuild
50
+ pnpm res:clean && pnpm res:build
51
+
52
+ # Run all tests
53
+ pnpm test
54
+
55
+ # Run tests in watch mode
56
+ pnpm test:watch
57
+
58
+ # Run a single test file
59
+ pta './test/__tests__/spec/<test_name>_test.mjs' | tap-difflet
60
+
61
+ # Example: Run only records tests
62
+ pta './test/__tests__/spec/records_test.mjs' | tap-difflet
63
+ ```
64
+
65
+ ### Development Workflow
66
+
67
+ 1. Make changes to OCaml code in `src/ppx/`
68
+ 2. Run `dune build` in `src/`
69
+ 3. Navigate to `test/` and run `pnpm res:clean && pnpm res:build`
70
+ 4. Run `pnpm test` to verify changes
71
+
72
+ ## Project Structure
73
+
74
+ ```
75
+ ppx_spice/
76
+ ├── src/ppx/ # OCaml PPX implementation
77
+ │ ├── ppx_spice.ml # Entry point, registers transformation
78
+ │ ├── codecs.ml # Primitive type codec generation
79
+ │ ├── records.ml # Record encoder/decoder generation
80
+ │ ├── variants.ml # Variant type handling
81
+ │ ├── polyvariants.ml # Polymorphic variant handling
82
+ │ ├── structure.ml # Implementation (.ml) processing
83
+ │ ├── signature.ml # Interface (.mli) processing
84
+ │ └── utils.ml # Shared utilities
85
+ ├── src/bin/bin.ml # Executable entry point
86
+ ├── test/src/ # ReScript test source types
87
+ └── test/test/__tests__/ # Test specifications
88
+ ```
89
+
90
+ ### PPX Integration (rescript.json)
91
+ ```json
92
+ {
93
+ "ppx-flags": ["@greenlabs/ppx-spice/ppx"],
94
+ "warnings": { "error": true, "number": "-48" }
95
+ }
96
+ ```
97
+
98
+ ## Code Style Guidelines
99
+
100
+ ### OCaml Code Style
101
+
102
+ #### Formatting
103
+ - Use 2 spaces for indentation
104
+ - Use LF line endings
105
+ - Insert final newline at end of files
106
+ - Maximum line length: ~100 characters (soft limit)
107
+
108
+ #### Imports
109
+ Always open modules in this order at the top of each file:
110
+ ```ocaml
111
+ open Ppxlib
112
+ open Parsetree
113
+ open Ast_helper
114
+ open Utils (* Local utilities last *)
115
+ ```
116
+
117
+ #### Type Definitions
118
+ - Define types at the top of the module, after opens
119
+ - Use record types for complex data structures:
120
+ ```ocaml
121
+ type parsed_decl = {
122
+ name : string;
123
+ key : expression;
124
+ field : expression;
125
+ codecs : expression option * expression option;
126
+ }
127
+ ```
128
+
129
+ #### Naming Conventions
130
+ - Module names: PascalCase (`Records`, `Variants`, `Polyvariants`)
131
+ - Function names: snake_case (`generate_encoder`, `parse_decl`)
132
+ - Type names: snake_case (`parsed_decl`, `generator_settings`)
133
+ - Variables: snake_case (`type_name`, `param_names`)
134
+ - Constants/suffixes: snake_case with descriptive names
135
+ - `encoder_func_suffix = "_encode"`
136
+ - `decoder_func_suffix = "_decode"`
137
+
138
+ #### Error Handling
139
+ - Use `Result` type for operations that can fail
140
+ - Use `fail` function from `Utils` to raise location-aware errors:
141
+ ```ocaml
142
+ let fail loc message = Location.raise_errorf ~loc "%s" message
143
+
144
+ (* Usage *)
145
+ | Ptyp_any -> fail ptyp_loc "Can't generate codecs for `any` type"
146
+ ```
147
+
148
+ #### Pattern Matching
149
+ - Prefer exhaustive pattern matching
150
+ - Use `_` prefix for intentionally unused variables
151
+ - Group related cases together:
152
+ ```ocaml
153
+ match ptype_kind with
154
+ | Ptype_abstract -> (* handle abstract *)
155
+ | Ptype_variant decls -> (* handle variant *)
156
+ | Ptype_record decls -> (* handle record *)
157
+ | _ -> fail ptype_loc "This type is not handled by spice"
158
+ ```
159
+
160
+ #### PPX-Specific Patterns
161
+ - Use ppxlib metaquot for AST construction: `[%expr ...]`, `[%pat? ...]`, `[%type: ...]`
162
+ - Always attach `res.arity` attribute for ReScript function compatibility
163
+ - Use `Utils.expr_func ~arity:1` for single-argument functions
164
+
165
+ ### ReScript Test Code Style
166
+
167
+ #### Test Structure
168
+ ```rescript
169
+ open Zora
170
+
171
+ zoraBlock("descriptive test block name", t => {
172
+ // Setup
173
+ let sampleJson = ...
174
+ let expected = ...
175
+
176
+ // Execute
177
+ let result = SomeModule.function(input)
178
+
179
+ // Assert
180
+ t->test("assertion description", async t => {
181
+ t->equal(result, expected, "message")
182
+ })
183
+ })
184
+ ```
185
+
186
+ #### Naming
187
+ - Test files: `<module>_test.res` (e.g., `records_test.res`)
188
+ - Test blocks: Descriptive phrases in quotes
189
+
190
+ ## Dependencies
191
+
192
+ ### OCaml Dependencies (from opam)
193
+ - `ocaml` >= 4.14.0, <= 4.14.2
194
+ - `dune` >= 2.8
195
+ - `ppxlib` = 0.28.0
196
+
197
+ ### ReScript Dependencies (test only)
198
+ - `rescript` ^12.0.0
199
+ - `@dusty-phillips/rescript-zora` ^4.0.0
200
+
201
+ ## Common Patterns
202
+
203
+ ### Generated Function Names
204
+ The PPX generates functions with these naming patterns:
205
+ - Encoder: `<type_name>_encode`
206
+ - Decoder: `<type_name>_decode`
207
+
208
+ ### Attribute Handling
209
+ ```ocaml
210
+ (* Check for attribute *)
211
+ match get_attribute_by_name attributes "spice.key" with
212
+ | Ok (Some attr) -> (* attribute present *)
213
+ | Ok None -> (* attribute absent *)
214
+ | Error s -> fail loc s
215
+ ```
216
+
217
+ ### Codec Generation Flow
218
+ 1. `ppx_spice.ml` registers the transformation
219
+ 2. `structure.ml` / `signature.ml` process type declarations
220
+ 3. `codecs.ml` handles primitive types
221
+ 4. `records.ml`, `variants.ml`, `polyvariants.ml` handle complex types
222
+ 5. Generated AST is returned to the compiler
223
+
224
+ ## CI/CD
225
+
226
+ GitHub Actions workflows are in `.github/workflows/`:
227
+ - `build_linux.yml` - Linux build with Alpine container
228
+ - `build_macos.yml` - macOS build
229
+ - `build_windows.yml` - Windows build
230
+ - `publish.yml` - NPM package publishing
231
+
232
+ Builds use OCaml 4.14.2 with static linking for portable binaries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.3.2
4
+
5
+ - Fixes O(n) nested match decoder replaces O(n²) tuple pattern matching to prevent the hang with the recode with many fields https://github.com/green-labs/ppx_spice/pull/111
6
+
3
7
  ## 0.3.1
4
8
 
5
9
  - Fixes [#107](https://github.com/green-labs/ppx_spice/issues/107) Arrays being reversed by Spice.arrayFromJson
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greenlabs/ppx-spice",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "packageManager": "yarn@1.22.22",
5
5
  "description": "ReScript PPX which generate JSON (de)serializer",
6
6
  "license": "MIT",
package/postInstall.js CHANGED
@@ -38,7 +38,9 @@ switch (process.platform) {
38
38
  installMacLinuxBinary("ppx-linux.exe");
39
39
  break;
40
40
  case "darwin":
41
- installMacLinuxBinary("ppx-osx.exe");
41
+ // Detect macOS architecture: arm64 for Apple Silicon, x64 for Intel
42
+ const macBinary = process.arch === "arm64" ? "ppx-osx-arm64.exe" : "ppx-osx-x64.exe";
43
+ installMacLinuxBinary(macBinary);
42
44
  break;
43
45
  case "win32":
44
46
  installWindowsBinary();
package/ppx-linux.exe CHANGED
Binary file
index f6ea8b6..663b50e
Binary file
index 4639571..46e7115
Binary file
package/ppx-windows.exe CHANGED
Binary file