You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -32,17 +32,76 @@ We will use a CSV format for recipe marketplace data with the following structur
32
32
33
33
-**`displayName`**: Human-readable recipe display name
34
34
-**`description`**: Recipe description
35
+
-**`recipeCount`**: Integer count of recipes (direct + transitive), defaults to 1
35
36
-**`estimatedEffortPerOccurrence`**: ISO-8601 duration format (e.g., PT5M, PT1H)
36
37
-**`category1, category2, ..., categoryN`**: Zero or more category columns, read **left to right** with **left representing the deepest level category**
37
-
-**`option1Name, option1DisplayName, option1Description`**: First recipe option
38
-
-**`option2Name, option2DisplayName, option2Description`**: Second recipe option
39
-
-**`optionNName, optionNDisplayName, optionNDescription`**: Additional options following the same pattern
38
+
-**`category1Description, category2Description, ..., categoryNDescription`**: Optional descriptions for each category level
39
+
-**`options`**: JSON array of `OptionDescriptor` objects (see JSON Format section below)
40
+
-**`dataTables`**: JSON array of `DataTableDescriptor` objects (see JSON Format section below)
40
41
41
42
### Optional Bundle Columns
42
43
43
-
-**`version`**: Package version (optional, allows version-independent recipe catalogs)
44
+
-**`version`**: Resolved package version (optional, allows version-independent recipe catalogs)
45
+
-**`requestedVersion`**: Version constraint as requested (e.g., `LATEST`, `0.2.0-SNAPSHOT`)
44
46
-**`team`**: Optional team identifier for marketplace partitioning
45
47
48
+
### Metadata Columns
49
+
50
+
Any unrecognized columns are preserved as metadata in `RecipeListing.getMetadata()` as a `Map<String, Object>`, allowing forward compatibility with future extensions.
51
+
52
+
### JSON Format for Options
53
+
54
+
The `options` column contains a JSON array of option descriptors:
55
+
56
+
```json
57
+
[
58
+
{
59
+
"name": "groupId",
60
+
"type": "String",
61
+
"displayName": "Group ID",
62
+
"description": "The group ID of the dependency.",
63
+
"example": "org.openrewrite",
64
+
"required": true
65
+
},
66
+
{
67
+
"name": "artifactId",
68
+
"type": "String",
69
+
"displayName": "Artifact ID",
70
+
"description": "The artifact ID of the dependency.",
71
+
"required": false
72
+
}
73
+
]
74
+
```
75
+
76
+
### JSON Format for DataTables
77
+
78
+
The `dataTables` column contains a JSON array of data table descriptors:
"description": "Lists all dependencies found in the project.",
86
+
"columns": [
87
+
{
88
+
"name": "groupId",
89
+
"type": "String",
90
+
"displayName": "Group ID",
91
+
"description": "The dependency group."
92
+
}
93
+
]
94
+
}
95
+
]
96
+
```
97
+
98
+
The following data tables are excluded during writing as they are internal infrastructure tables:
99
+
-`org.openrewrite.table.SearchResults`
100
+
-`org.openrewrite.table.SourcesFileResults`
101
+
-`org.openrewrite.table.SourcesFileErrors`
102
+
-`org.openrewrite.table.RecipeRunStats`
103
+
-`org.openrewrite.table.ParseFailures`
104
+
46
105
### Category Structure
47
106
48
107
The `RecipeMarketplace` is a recursive data structure where each category is itself a marketplace. Categories are determined by column headers starting with "category":
@@ -67,31 +126,53 @@ Since `version` is optional, marketplaces can represent version-independent reci
67
126
### Implementation
68
127
69
128
-**Data Model**:
70
-
-`RecipeBundle`: Simple data class containing ecosystem, packageName, version (optional), and team (optional)
71
-
-`RecipeListing`: Represents a recipe with metadata (name, displayName, description, options) and an associated `RecipeBundle`
129
+
-`RecipeBundle`: Data class containing:
130
+
-`packageEcosystem`: The package ecosystem (e.g., `maven`, `npm`, `yaml`)
131
+
-`packageName`: The package identifier
132
+
-`requestedVersion`: Version constraint as requested (optional)
133
+
-`version`: Resolved version (optional)
134
+
-`team`: Team identifier (optional)
135
+
-`RecipeListing`: Represents a recipe with metadata:
- Dynamically determines required category and option columns based on marketplace content
82
-
- Always includes `ecosystem` and `packageName` columns
83
-
- Only includes `version` column if at least one recipe has version information
84
-
- Only includes `team` column if at least one recipe has team information
154
+
- Dynamically determines required columns based on marketplace content
155
+
- Always includes `ecosystem`, `packageName`, and `name` columns
156
+
- Only includes optional columns if at least one recipe has data for them
157
+
- Uses Jackson for JSON serialization with `NON_DEFAULT` inclusion
158
+
- Places `options` and `dataTables` columns last for human readability
85
159
86
160
-**Bundle Resolution**: Two-phase resolution system
87
161
-`RecipeBundleResolver`: Interface with `getEcosystem()` and `resolve(RecipeBundle)` methods
88
162
- Ecosystem-specific resolvers are registered with the `RecipeMarketplace`
89
-
- Examples: `MavenRecipeBundleResolver` in `rewrite-maven`, `NpmRecipeBundleResolver` in `rewrite-javascript`
163
+
- Implementations:
164
+
-`MavenRecipeBundleResolver` in `rewrite-maven`
165
+
-`NpmRecipeBundleResolver` in `rewrite-javascript`
166
+
-`YamlRecipeBundleResolver` in `rewrite-core`
90
167
-`RecipeBundleReader`: Interface returned by resolvers with methods:
91
168
-`getBundle()`: Returns the associated `RecipeBundle`
92
169
-`read()`: Reads the bundle and returns a `RecipeMarketplace`
93
170
-`describe(RecipeListing)`: Returns a `RecipeDescriptor` for a listing
94
171
-`prepare(RecipeListing, Map<String, Object>)`: Creates a configured `Recipe` instance
172
+
- Implementations:
173
+
-`MavenRecipeBundleReader`: Downloads Maven artifacts, reads `META-INF/rewrite/recipes.csv` from JAR, falls back to classpath scanning
174
+
-`NpmRecipeBundleReader`: Uses `JavaScriptRewriteRpc` for remote communication with npm packages
175
+
-`YamlRecipeBundleReader`: Reads recipes from YAML files by path or URI
95
176
-`RecipeListing.resolve()`: Convenience method that finds the appropriate resolver and returns a `RecipeBundleReader`
96
177
97
178
-**Validators**: Tools for ensuring marketplace quality and completeness
@@ -124,16 +205,18 @@ Since `version` is optional, marketplaces can represent version-independent reci
124
205
125
206
### Negative
126
207
127
-
1.**CSV limitations**: No native support for nested structures (mitigated by column naming conventions)
128
-
2.**Sparse data**: Recipes with few options result in many empty cells in CSVs with high option counts
208
+
1.**CSV limitations**: No native support for nested structures (mitigated by JSON columns and column naming conventions)
209
+
2.**JSON in CSV cells**: Options and dataTables are stored as JSON strings, which can be harder to edit manually in spreadsheet tools
129
210
3.**Always requires bundle metadata**: Unlike earlier designs, ecosystem and packageName are always required, even for basic recipe catalogs
130
211
131
212
### Trade-offs
132
213
133
214
-**Left-to-right category ordering** (left = deepest): This matches the `moderne-organizations-format` convention but may be counterintuitive to some users who expect left-to-right to represent root-to-leaf
134
215
-**Two-phase resolution**: Separating RecipeBundleResolver and RecipeBundleReader provides flexibility but adds complexity compared to a single interface
135
-
-**Dynamic columns**: Provides flexibility but means schema varies between files, making generic CSV processing tools less effective
216
+
-**JSON for complex data**: Using JSON for options and dataTables provides a fixed column structure but requires JSON parsing/writing
217
+
-**Dynamic category columns**: The number of category columns varies between files based on hierarchy depth
136
218
-**Resolver configuration**: RecipeBundleResolvers must be registered with the RecipeMarketplace instance before calling RecipeListing.resolve(), describe(), or prepare()
219
+
-**Requested vs resolved version**: Supporting both `requestedVersion` (constraint) and `version` (resolved) adds flexibility but also complexity in version handling
0 commit comments