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
This proposal introduces a method for plugin developers to define their configuration options using a config.ts (or config.js) file within the /src/robo directory. By exporting an object that describes the expected configuration options, we can automatically generate TypeScript interfaces for these configurations. This approach ensures that pure JavaScript plugins remain compatible with TypeScript projects, providing type safety and improved tooling support.
Goals
Standardize Configuration Definition: Allow plugin developers to define their configuration schemas in a consistent and structured manner.
Automatic Type Generation: Generate TypeScript interfaces (.d.ts files) from the configuration schemas to provide type safety.
Enhance Developer Experience: Improve code completion, validation, and documentation for users of plugins.
Maintain JavaScript Compatibility: Ensure that plugins written in pure JavaScript remain fully compatible with TypeScript projects.
Optional Zod Integration: Allow the use of Zod as an optional dependency for schema validation for those who prefer it.
Implementation
1. Defining the Configuration Schema
Plugin developers can create a config.ts or config.js file in the /src/robo directory. For example:
// /src/robo/config.tsimport{Config}from'robo.js'exportdefaultConfig.define((c)=>({databaseUrl: c.string().description('The URL of the database'),port: c.number().default(3000).description('Port number to run the server on'),features: c.object({enableLogging: c.boolean().default(false).description('Enable logging feature'),enableCache: c.boolean().default(true).description('Enable caching feature')})}))
Explanation
Config.define: A function that accepts a callback where c is a schema builder object containing type methods.
Type Methods: The c object provides methods like string(), number(), boolean(), and object().
Field Definitions: Each field is defined using the type methods, with optional methods for defaults and descriptions.
Avoiding Prototype Pollution: By moving type methods into the c object, we prevent pollution of the Config prototype.
The .define Result
The Config.define function processes the schema defined in the callback and returns a structured schema object. This object can be used for:
Runtime Validation: Optionally validate user configurations at runtime.
Type Generation: Generate TypeScript definitions for type safety.
CLI Prompts: Provide default values and descriptions for interactive prompts.
An example of the schema object structure:
{databaseUrl: {type: 'string',description: 'The URL of the database',required: true},port: {type: 'number',default: 3000,description: 'Port number to run the server on'},features: {type: 'object',properties: {enableLogging: {type: 'boolean',default: false,description: 'Enable logging feature'},enableCache: {type: 'boolean',default: true,description: 'Enable caching feature'},},},}
2. Updating the Build Process
The robo build plugin command needs to be enhanced to handle the new configuration schema:
Check for Configuration File: After compiling the plugin, check if .robo/build/robo/config.js exists.
Import the Configuration Schema: Import the exported schema object from the configuration file.
Generate Type Definitions: Use the schema object to construct a TypeScript definition file config.d.ts.
Here's a detailed implementation of the generateTypeDefinition function, including how the schema is processed:
importfsfrom'node:fs'importpathfrom'node:path'functiongenerateTypeDefinition(schema,indent=0){constindentation=' '.repeat(indent)lettypeDef=''if(schema.type==='object'&&schema.properties){typeDef+='{\n'for(const[key,value]ofObject.entries(schema.properties)){constoptional=value.required===false||value.default!==undefined ? '?' : ''typeDef+=`${indentation}${key}${optional}: ${generateTypeDefinition(value,indent+1)};\n`}typeDef+=`${indentation}}`}else{typeDef+=schemaToTypeString(schema)}returntypeDef}functionschemaToTypeString(schema){switch(schema.type){case'string':
return'string'case'number':
return'number'case'boolean':
return'boolean'case'array':
return`${schemaToTypeString(schema.items)}[]`default:
return'any'}}// Load the compiled config.jsconstconfigPath=path.join('.robo','build','robo','config.js')constconfigModule=awaitimport(configPath)constschema=configModule.default// Generate the TypeScript definitionconsttypeDefinition=`export interface Config ${generateTypeDefinition(schema)}\n`// Write to config.d.tsconstconfigDtsPath=path.join('.robo','build','config.d.ts')fs.writeFileSync(configDtsPath,typeDefinition)
Be aware that the above is just a simplified example. The actual implementation may require additional error handling, validation, and type definitions.
3. Enhancing TypeScript Support for Users
Users can now get type safety when configuring the plugin:
For better usability, the build process should also:
Check for index.d.ts: If .robo/build/index.d.ts exists, append the following line:
exporttype{Config}from'../config'
Sample Implementation
constindexDtsPath=path.join('.robo','build','index.d.ts')constexportStatement=`\nexport type { Config } from '../config';\n`if(fs.existsSync(indexDtsPath)){fs.appendFileSync(indexDtsPath,exportStatement)}else{// If index.d.ts doesn't exist, create itfs.writeFileSync(indexDtsPath,exportStatement.trim())}
Resulting Usage: Users can now import the Config type directly from the plugin:
Ensure that the Config API is fluent and intuitive:
// /src/robo/config.tsimport{Config}from'robo.js'exportdefaultConfig.define((c)=>({apiKey: c.string().required().description('API key for authentication').prompt('Please enter your API key'),retries: c.number().default(3).description('Number of retry attempts')}))
7. Detailed Schema and Type Generation
Schema Builder (c Object)
The c object provides methods to define each configuration field:
c.string(): Defines a string type.
c.number(): Defines a number type.
c.boolean(): Defines a boolean type.
c.object(properties): Defines an object with specified properties.
Common Methods:
.required(): Marks the field as required.
.default(value): Sets a default value.
.description(text): Adds a description.
.prompt(text): Sets a prompt message for CLI interactions.
Example of the Schema Object
{apiKey: {type: 'string',required: true,description: 'API key for authentication',prompt: 'Please enter your API key',},retries: {type: 'number',default: 3,description: 'Number of retry attempts',},}
Improved generateTypeDefinition Function
The function now handles optional fields and nested objects accurately:
Note: Remember, the above code examples are simplified for explanation purposes and to define the API design. The actual implementation may require additional error handling, validation, and type definitions.
The text was updated successfully, but these errors were encountered:
Pkmmte
changed the title
feat(robo/cli): standardized plugin options schema
feat(robo/cli): Config Schema Definition and Type Generation for Plugins
Oct 13, 2024
Description
This proposal introduces a method for plugin developers to define their configuration options using a
config.ts
(orconfig.js
) file within the/src/robo
directory. By exporting an object that describes the expected configuration options, we can automatically generate TypeScript interfaces for these configurations. This approach ensures that pure JavaScript plugins remain compatible with TypeScript projects, providing type safety and improved tooling support.Goals
.d.ts
files) from the configuration schemas to provide type safety.Implementation
1. Defining the Configuration Schema
Plugin developers can create a
config.ts
orconfig.js
file in the/src/robo
directory. For example:Explanation
c
is a schema builder object containing type methods.c
object provides methods likestring()
,number()
,boolean()
, andobject()
.c
object, we prevent pollution of theConfig
prototype.The
.define
ResultThe
Config.define
function processes the schema defined in the callback and returns a structured schema object. This object can be used for:An example of the schema object structure:
2. Updating the Build Process
The
robo build plugin
command needs to be enhanced to handle the new configuration schema:.robo/build/robo/config.js
exists.config.d.ts
.Generating
config.d.ts
An example of the generated
config.d.ts
:Detailed
generateTypeDefinition
FunctionHere's a detailed implementation of the
generateTypeDefinition
function, including how the schema is processed:Be aware that the above is just a simplified example. The actual implementation may require additional error handling, validation, and type definitions.
3. Enhancing TypeScript Support for Users
Users can now get type safety when configuring the plugin:
4. Appending to
index.d.ts
For better usability, the build process should also:
index.d.ts
: If.robo/build/index.d.ts
exists, append the following line:Sample Implementation
Config
type directly from the plugin:5. Optional Zod Integration
For developers familiar with Zod, we can allow its usage:
6. Fluent API Design
Ensure that the
Config
API is fluent and intuitive:7. Detailed Schema and Type Generation
Schema Builder (
c
Object)The
c
object provides methods to define each configuration field:Example of the Schema Object
Improved
generateTypeDefinition
FunctionThe function now handles optional fields and nested objects accurately:
8. File System Operations
Include file system operations to read and write necessary files:
9. Example Usage of the Config API
Plugin Developer's
config.ts
:Generated
config.d.ts
:User's Configuration File:
Note: Remember, the above code examples are simplified for explanation purposes and to define the API design. The actual implementation may require additional error handling, validation, and type definitions.
The text was updated successfully, but these errors were encountered: