Compare commits
15 commits
108092aa52
...
1007479792
Author | SHA1 | Date | |
---|---|---|---|
|
1007479792 | ||
|
7bf27e2e59 | ||
|
23c9e2b107 | ||
|
16c1047bfe | ||
|
83fd901968 | ||
|
601a5e6210 | ||
|
cb16079d14 | ||
|
a0d5f68fad | ||
|
b42e1daa89 | ||
|
df19fbea36 | ||
|
a4beca5a4c | ||
|
999b7acb5a | ||
|
c7e69edcfc | ||
|
a63b2097a4 | ||
|
d17bfcf09a |
114 changed files with 6180 additions and 790 deletions
|
@ -1,7 +1,9 @@
|
|||
name: protevus_platform
|
||||
repository: https://github.com/protevus/platform
|
||||
packages:
|
||||
- apps/**
|
||||
- packages/**
|
||||
- helpers/tools/**
|
||||
- examples/**
|
||||
|
||||
command:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
scripts:
|
||||
_: &workflow_scripts
|
||||
_: &create_scripts
|
||||
clean:
|
||||
run: >
|
||||
melos exec -c 1 --fail-fast -- "
|
||||
|
|
24
.melos/create.yaml
Normal file
24
.melos/create.yaml
Normal file
|
@ -0,0 +1,24 @@
|
|||
scripts:
|
||||
_: &create_scripts
|
||||
create:
|
||||
name: Create new package or application
|
||||
description: |
|
||||
Creates a new Dart package or Flutter application in the appropriate directory.
|
||||
|
||||
Usage: melos run create -- --type dart|flutter --category type --name project_name
|
||||
|
||||
Available categories for Dart:
|
||||
- package : Basic Dart package
|
||||
- console : Command-line application
|
||||
- server : Server-side application
|
||||
- desktop : Desktop application
|
||||
- plugin : Dart plugin
|
||||
|
||||
Available categories for Flutter:
|
||||
- app : Mobile application
|
||||
- web : Web application
|
||||
- desktop : Desktop application
|
||||
- plugin : Flutter plugin
|
||||
- module : Flutter module
|
||||
- package : Flutter package
|
||||
run: dart run helpers/create_project.dart $MELOS_ARGS
|
13
.melos/template.yaml
Normal file
13
.melos/template.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
scripts:
|
||||
_: &template_scripts
|
||||
template:
|
||||
name: Create from template
|
||||
description: |
|
||||
Creates a new project from a template in the templates directory.
|
||||
|
||||
Usage: melos run template template_name:name type:dart|flutter name:project_name
|
||||
|
||||
Example:
|
||||
melos run template template_name:bloc_app type:flutter name:my_new_app
|
||||
melos run template template_name:core_package type:dart name:core_utils
|
||||
run: dart run helpers/create_from_template.dart $MELOS_ARGS
|
16
devbox.json
16
devbox.json
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/jetify-com/devbox/0.11.0/.schema/devbox.schema.json",
|
||||
"packages": [
|
||||
"dart@latest"
|
||||
],
|
||||
"shell": {
|
||||
"init_hook": [
|
||||
"echo 'Welcome to Protevus!' > /dev/null"
|
||||
],
|
||||
"scripts": {
|
||||
"test": [
|
||||
"echo \"Error: no test specified\" && exit 1"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
53
devbox.lock
53
devbox.lock
|
@ -1,53 +0,0 @@
|
|||
{
|
||||
"lockfile_version": "1",
|
||||
"packages": {
|
||||
"dart@latest": {
|
||||
"last_modified": "2024-06-03T07:19:07Z",
|
||||
"resolved": "github:NixOS/nixpkgs/4a4ecb0ab415c9fccfb005567a215e6a9564cdf5#dart",
|
||||
"source": "devbox-search",
|
||||
"version": "3.4.2",
|
||||
"systems": {
|
||||
"aarch64-darwin": {
|
||||
"outputs": [
|
||||
{
|
||||
"name": "out",
|
||||
"path": "/nix/store/9piqr817cdsgmz31m8q723lxhcpgqsa4-dart-3.4.2",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"store_path": "/nix/store/9piqr817cdsgmz31m8q723lxhcpgqsa4-dart-3.4.2"
|
||||
},
|
||||
"aarch64-linux": {
|
||||
"outputs": [
|
||||
{
|
||||
"name": "out",
|
||||
"path": "/nix/store/1j3h5yqxvgzakv5gir1ssg7wggwxhmsd-dart-3.4.2",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"store_path": "/nix/store/1j3h5yqxvgzakv5gir1ssg7wggwxhmsd-dart-3.4.2"
|
||||
},
|
||||
"x86_64-darwin": {
|
||||
"outputs": [
|
||||
{
|
||||
"name": "out",
|
||||
"path": "/nix/store/k8a6gkss3s19p5dhbzgbdqqk5b8qzd7d-dart-3.4.2",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"store_path": "/nix/store/k8a6gkss3s19p5dhbzgbdqqk5b8qzd7d-dart-3.4.2"
|
||||
},
|
||||
"x86_64-linux": {
|
||||
"outputs": [
|
||||
{
|
||||
"name": "out",
|
||||
"path": "/nix/store/wbj1csi5fk2w99aiglwgg1mv406pw4pn-dart-3.4.2",
|
||||
"default": true
|
||||
}
|
||||
],
|
||||
"store_path": "/nix/store/wbj1csi5fk2w99aiglwgg1mv406pw4pn-dart-3.4.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
328
docs/Melos.md
328
docs/Melos.md
|
@ -1,328 +0,0 @@
|
|||
# Protevus Platform Melos Configuration Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
The Protevus Platform uses Melos to manage our monorepo structure and automate various development tasks. This comprehensive guide outlines the features provided by our Melos configuration and provides detailed instructions on how to use them effectively.
|
||||
|
||||
## Repository Structure
|
||||
|
||||
- Project name: `protevus_platform`
|
||||
- Repository: `https://github.com/protevus/platform`
|
||||
- Package locations:
|
||||
- `packages/`: Core packages of the platform
|
||||
- `examples/`: Example applications and usage demonstrations
|
||||
|
||||
## Version Control Integration
|
||||
|
||||
- Version bump commit message format: "chore: Bump version to %v"
|
||||
- Changelog:
|
||||
- Version commits are linked in the changelog
|
||||
- A workspace-level changelog is generated
|
||||
- Main versioning branch: `main`
|
||||
|
||||
Example of how versioning works:
|
||||
```bash
|
||||
# To bump the version and generate changelog
|
||||
melos version --yes
|
||||
|
||||
# To bump a specific version
|
||||
melos version 1.2.3 --yes
|
||||
|
||||
# To bump a prerelease version
|
||||
melos version prerelease --preid beta --yes
|
||||
```
|
||||
|
||||
## IDE Integration
|
||||
|
||||
- IntelliJ integration is disabled to prevent conflicts with our custom setup.
|
||||
|
||||
## Available Scripts
|
||||
|
||||
### Static Analysis and Formatting
|
||||
|
||||
1. **Analyze**
|
||||
- Command: `melos run analyze`
|
||||
- Description: Runs `dart analyze` on all packages to identify potential issues.
|
||||
- Usage: `melos run analyze`
|
||||
- Example output:
|
||||
```
|
||||
Analyzing package_1...
|
||||
No issues found!
|
||||
Analyzing package_2...
|
||||
info • Unused import • lib/src/unused_file.dart:3:8 • unused_import
|
||||
```
|
||||
|
||||
2. **Format**
|
||||
- Command: `melos run format`
|
||||
- Description: Formats all Dart files in the repository using `dart format`.
|
||||
- Usage: `melos run format`
|
||||
- Example:
|
||||
```bash
|
||||
# Format and show which files were changed
|
||||
melos run format -- -o write --set-exit-if-changed
|
||||
```
|
||||
|
||||
### Code Generation
|
||||
|
||||
3. **Generate**
|
||||
- Command: `melos run generate`
|
||||
- Description: Runs `build_runner` for code generation in all packages.
|
||||
- Usage: `melos run generate`
|
||||
- Example output:
|
||||
```
|
||||
Running build_runner for package_1...
|
||||
[INFO] Generating build script...
|
||||
[INFO] Generating build script completed, took 304ms
|
||||
[INFO] Running build...
|
||||
[INFO] 5 outputs were generated
|
||||
[INFO] Running build completed, took 2.8s
|
||||
```
|
||||
|
||||
4. **Generate Custom**
|
||||
- Command: `melos run generate:custom`
|
||||
- Description: Runs code generation for specified packages.
|
||||
- Usage: `MELOS_SCOPE="package_name1,package_name2" melos run generate:custom`
|
||||
- Example:
|
||||
```bash
|
||||
MELOS_SCOPE="auth_package,user_package" melos run generate:custom
|
||||
```
|
||||
|
||||
5. **Generate Check**
|
||||
- Command: `melos run generate:check`
|
||||
- Description: Checks if code generation is needed in specified packages.
|
||||
- Usage: `MELOS_SCOPE="package_name" melos run generate:check`
|
||||
- Example output:
|
||||
```
|
||||
Checking auth_package...
|
||||
Package auth_package needs code generation.
|
||||
Checking user_package...
|
||||
Package user_package does not use build_runner.
|
||||
```
|
||||
|
||||
6. **Generate Dummy Test**
|
||||
- Command: `melos run generate:dummy:test`
|
||||
- Description: Generates a dummy test file in specified package(s).
|
||||
- Usage: `MELOS_SCOPE="package_name" melos run generate:dummy:test`
|
||||
- Example:
|
||||
```bash
|
||||
MELOS_SCOPE="new_feature" melos run generate:dummy:test
|
||||
# This will create a file: new_feature/test/dummy_test.dart
|
||||
```
|
||||
|
||||
### Publishing
|
||||
|
||||
7. **Publish**
|
||||
- Command: `melos run publish`
|
||||
- Description: Publishes all packages that have changed.
|
||||
- Usage: `melos run publish`
|
||||
- Example workflow:
|
||||
```bash
|
||||
# First, check what would be published
|
||||
melos run publish:check
|
||||
# If everything looks good, publish
|
||||
melos run publish
|
||||
```
|
||||
|
||||
8. **Publish Check**
|
||||
- Command: `melos run publish:check`
|
||||
- Description: Dry run to check which packages would be published.
|
||||
- Usage: `melos run publish:check`
|
||||
- Example output:
|
||||
```
|
||||
Would publish the following packages:
|
||||
- auth_package (1.0.0 -> 1.0.1)
|
||||
- user_package (2.1.0 -> 2.2.0)
|
||||
```
|
||||
|
||||
### Documentation
|
||||
|
||||
9. **Generate Docs**
|
||||
- Command: `melos run docs:generate`
|
||||
- Description: Generates dartdoc documentation for all packages.
|
||||
- Usage: `melos run docs:generate`
|
||||
|
||||
10. **Generate Custom Docs**
|
||||
- Command: `melos run docs:generate:custom`
|
||||
- Description: Generates documentation for specified packages.
|
||||
- Usage: `MELOS_SCOPE="package_name1,package_name2" melos run docs:generate:custom`
|
||||
- Example:
|
||||
```bash
|
||||
MELOS_SCOPE="core_package,utils_package" melos run docs:generate:custom
|
||||
```
|
||||
|
||||
11. **Serve Docs**
|
||||
- Command: `melos run docs:serve`
|
||||
- Description: Serves generated documentation using `dhttpd`.
|
||||
- Usage: `melos run docs:serve`
|
||||
- After running, visit `http://localhost:8080` in your browser
|
||||
|
||||
12. **Serve Custom Docs**
|
||||
- Command: `melos run docs:serve:custom`
|
||||
- Description: Serves documentation for specified packages.
|
||||
- Usage: `MELOS_SCOPE="package_name" DOC_PORT=8081 melos run docs:serve:custom`
|
||||
- Example:
|
||||
```bash
|
||||
MELOS_SCOPE="api_package" DOC_PORT=8082 melos run docs:serve:custom
|
||||
# Then visit http://localhost:8082 in your browser
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
13. **Test**
|
||||
- Command: `melos run test`
|
||||
- Description: Runs tests for all packages with fail-fast option.
|
||||
- Usage: `melos run test`
|
||||
- Example output:
|
||||
```
|
||||
Running tests for auth_package...
|
||||
00:01 +10: All tests passed!
|
||||
Running tests for user_package...
|
||||
00:02 +15: All tests passed!
|
||||
```
|
||||
|
||||
14. **Test Custom**
|
||||
- Command: `melos run test:custom`
|
||||
- Description: Runs tests for specified packages.
|
||||
- Usage: `MELOS_SCOPE="package_name1,package_name2" melos run test:custom`
|
||||
- Example:
|
||||
```bash
|
||||
MELOS_SCOPE="auth_package,user_package" melos run test:custom -- --coverage
|
||||
```
|
||||
|
||||
### Dependency Management
|
||||
|
||||
15. **Check Dependencies**
|
||||
- Command: `melos run deps:check`
|
||||
- Description: Checks for outdated dependencies in all packages.
|
||||
- Usage: `melos run deps:check`
|
||||
- Example output:
|
||||
```
|
||||
Checking dependencies for auth_package...
|
||||
2 dependencies are out of date
|
||||
Checking dependencies for user_package...
|
||||
All dependencies up to date
|
||||
```
|
||||
|
||||
16. **Upgrade Dependencies**
|
||||
- Command: `melos run deps:upgrade`
|
||||
- Description: Upgrades all dependencies to their latest versions.
|
||||
- Usage: `melos run deps:upgrade`
|
||||
|
||||
17. **Upgrade Custom Dependencies**
|
||||
- Command: `melos run deps:upgrade:custom`
|
||||
- Description: Upgrades dependencies for specified packages.
|
||||
- Usage: `MELOS_SCOPE="package_name1,package_name2" melos run deps:upgrade:custom`
|
||||
- Example:
|
||||
```bash
|
||||
MELOS_SCOPE="database_package,api_package" melos run deps:upgrade:custom
|
||||
```
|
||||
|
||||
### CI/CD
|
||||
|
||||
18. **CI Pipeline**
|
||||
- Command: `melos run ci`
|
||||
- Description: Runs the full CI pipeline (analyze and test).
|
||||
- Usage: `melos run ci`
|
||||
- This is typically used in CI/CD environments, e.g., in a GitHub Actions workflow:
|
||||
```yaml
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: dart-lang/setup-dart@v1
|
||||
- run: dart pub global activate melos
|
||||
- run: melos bootstrap
|
||||
- run: melos run ci
|
||||
```
|
||||
|
||||
### Debugging and Utility Scripts
|
||||
|
||||
19. **Debug Package Name**
|
||||
- Command: `melos run debug_pkg_name`
|
||||
- Description: Outputs the name of each package in the workspace.
|
||||
- Usage: `melos run debug_pkg_name`
|
||||
- Example output:
|
||||
```
|
||||
Package name is auth_package
|
||||
Package name is user_package
|
||||
Package name is core_package
|
||||
```
|
||||
|
||||
20. **Debug Package Path**
|
||||
- Command: `melos run debug_pkg_path`
|
||||
- Description: Outputs the path of each package in the workspace.
|
||||
- Usage: `melos run debug_pkg_path`
|
||||
- Example output:
|
||||
```
|
||||
Package path is /home/user/protevus_platform/packages/auth_package
|
||||
Package path is /home/user/protevus_platform/packages/user_package
|
||||
```
|
||||
|
||||
21. **Debug Reflectable**
|
||||
- Command: `melos run debug:reflectable`
|
||||
- Description: Finds `.reflectable.dart` files in specified packages.
|
||||
- Usage: `MELOS_SCOPE="package_name" melos run debug:reflectable`
|
||||
- Example:
|
||||
```bash
|
||||
MELOS_SCOPE="core_package" melos run debug:reflectable
|
||||
# Output: Checking for .reflectable.dart files in core_package
|
||||
# /home/user/protevus_platform/packages/core_package/lib/src/models.reflectable.dart
|
||||
```
|
||||
|
||||
22. **List Dart Files**
|
||||
- Command: `melos run list:dart:files`
|
||||
- Description: Lists all Dart files in specified package(s).
|
||||
- Usage: `MELOS_SCOPE="package_name" melos run list:dart:files`
|
||||
- Example:
|
||||
```bash
|
||||
MELOS_SCOPE="utils_package" melos run list:dart:files
|
||||
# Output: Listing all Dart files in utils_package:
|
||||
# /home/user/protevus_platform/packages/utils_package/lib/src/string_utils.dart
|
||||
# /home/user/protevus_platform/packages/utils_package/lib/src/date_utils.dart
|
||||
```
|
||||
|
||||
23. **Combine Config**
|
||||
- Command: `melos run combine_config`
|
||||
- Description: Combines Melos configuration files using a custom script.
|
||||
- Usage: `melos run combine_config`
|
||||
- This is useful for maintaining separate config files for different environments or CI/CD pipelines.
|
||||
|
||||
## Advanced Usage Examples
|
||||
|
||||
1. Running a subset of tests matching a specific pattern:
|
||||
```bash
|
||||
MELOS_SCOPE="auth_package" melos run test:custom -- --name "login"
|
||||
```
|
||||
|
||||
2. Generating documentation and immediately serving it:
|
||||
```bash
|
||||
melos run docs:generate && melos run docs:serve
|
||||
```
|
||||
|
||||
3. Checking for outdated dependencies, upgrading them, and then running tests:
|
||||
```bash
|
||||
melos run deps:check && melos run deps:upgrade && melos run test
|
||||
```
|
||||
|
||||
4. Performing a dry-run of publishing, then actually publishing if everything looks good:
|
||||
```bash
|
||||
melos run publish:check && melos run publish
|
||||
```
|
||||
|
||||
5. Running the full CI pipeline and then generating documentation if CI passes:
|
||||
```bash
|
||||
melos run ci && melos run docs:generate
|
||||
```
|
||||
|
||||
6. Upgrading dependencies for multiple packages and then running their tests:
|
||||
```bash
|
||||
MELOS_SCOPE="auth_package,user_package,core_package" melos run deps:upgrade:custom && MELOS_SCOPE="auth_package,user_package,core_package" melos run test:custom
|
||||
```
|
||||
|
||||
7. Generating code, running tests, and then checking if any files need to be formatted:
|
||||
```bash
|
||||
melos run generate && melos run test && melos run format -- -o none --set-exit-if-changed
|
||||
```
|
||||
|
||||
This documentation provides a comprehensive guide to using the Melos configuration in the Protevus Platform project. It covers all aspects of the development lifecycle and should help team members effectively manage the monorepo structure.
|
644
docs/melos_config.md
Normal file
644
docs/melos_config.md
Normal file
|
@ -0,0 +1,644 @@
|
|||
```markdown
|
||||
# Protevus Platform Melos Configuration Documentation
|
||||
|
||||
## Table of Contents
|
||||
- [Overview](#overview)
|
||||
- [Repository Structure](#repository-structure)
|
||||
- [Setup and Configuration](#setup-and-configuration)
|
||||
- [Version Control Integration](#version-control-integration)
|
||||
- [Project Management](#project-management)
|
||||
- [Development Workflow](#development-workflow)
|
||||
- [Testing and Coverage](#testing-and-coverage)
|
||||
- [Documentation](#documentation)
|
||||
- [Code Generation](#code-generation)
|
||||
- [Dependency Management](#dependency-management)
|
||||
- [CI/CD](#cicd)
|
||||
- [Debugging and Utilities](#debugging-and-utilities)
|
||||
- [Advanced Usage](#advanced-usage)
|
||||
- [Best Practices](#best-practices)
|
||||
|
||||
## Overview
|
||||
The Protevus Platform uses Melos to manage our monorepo structure and automate various development tasks. This comprehensive guide outlines the features provided by our Melos configuration and provides detailed instructions on how to use them effectively.
|
||||
|
||||
### Project Information
|
||||
- Project name: `protevus_platform`
|
||||
- Repository: `https://github.com/protevus/platform`
|
||||
|
||||
### Directory Structure
|
||||
```
|
||||
project/
|
||||
├── apps/ # Flutter applications
|
||||
├── packages/ # Dart/Flutter packages
|
||||
├── examples/ # Example projects
|
||||
├── templates/ # Project templates
|
||||
├── tools/ # Build and maintenance scripts
|
||||
└── config/ # Configuration files
|
||||
```
|
||||
|
||||
## Setup and Configuration
|
||||
|
||||
### Initial Setup
|
||||
```bash
|
||||
# Configure the entire development environment
|
||||
melos run configure
|
||||
```
|
||||
|
||||
This command performs a comprehensive setup:
|
||||
1. Bootstraps the workspace
|
||||
2. Generates code for platform_container_generator
|
||||
3. Creates dummy tests for specific packages
|
||||
4. Runs reflectable debugging
|
||||
5. Executes test suite
|
||||
6. Generates coverage reports
|
||||
7. Creates API documentation
|
||||
|
||||
### IDE Integration
|
||||
- IntelliJ integration is disabled to prevent conflicts with our custom setup.
|
||||
|
||||
### Configuration Management
|
||||
```bash
|
||||
# Combine configuration files
|
||||
melos run combine_config
|
||||
```
|
||||
|
||||
Combines multiple configuration files:
|
||||
- analyze.yaml
|
||||
- generate.yaml
|
||||
- publish.yaml
|
||||
- docs.yaml
|
||||
- coverage.yaml
|
||||
- clean.yaml
|
||||
- configure.yaml
|
||||
- test.yaml
|
||||
- create.yaml
|
||||
- dependencies.yaml
|
||||
- ci.yaml
|
||||
- debug.yaml
|
||||
- utils.yaml
|
||||
|
||||
## Version Control Integration
|
||||
|
||||
### Version Management
|
||||
- Version bump commit message format: "chore: Bump version to %v"
|
||||
- Main versioning branch: `main`
|
||||
- Changelog features:
|
||||
- Version commits are linked
|
||||
- Workspace-level changelog generation
|
||||
|
||||
Example version management:
|
||||
```bash
|
||||
# Standard version bump
|
||||
melos version --yes
|
||||
|
||||
# Specific version bump
|
||||
melos version 1.2.3 --yes
|
||||
|
||||
# Prerelease version
|
||||
melos version prerelease --preid beta --yes
|
||||
```
|
||||
|
||||
|
||||
## Project Management
|
||||
|
||||
### Creating New Projects
|
||||
|
||||
#### Standard Project Creation
|
||||
```bash
|
||||
melos run create -- --type <dart|flutter> --category <category> --name <name>
|
||||
```
|
||||
|
||||
##### Categories for Dart Projects:
|
||||
- `package`: Standard Dart package
|
||||
- `console`: Command-line application
|
||||
- `server`: Server-side application with built-in dependencies:
|
||||
- shelf_router
|
||||
- dotenv
|
||||
- logger
|
||||
- `desktop`: Desktop application with:
|
||||
- window_manager
|
||||
- screen_retriever
|
||||
- `plugin`: Dart plugin package
|
||||
|
||||
##### Categories for Flutter Projects:
|
||||
- `app`: Standard mobile application
|
||||
- `web`: Web-optimized application
|
||||
- `desktop`: Multi-platform desktop application
|
||||
- `plugin`: Flutter plugin
|
||||
- `module`: Add-to-app module
|
||||
- `package`: Flutter-specific package
|
||||
|
||||
#### Template-Based Project Creation
|
||||
```bash
|
||||
melos run template template_name:<template> type:<dart|flutter> name:<project_name>
|
||||
```
|
||||
|
||||
##### Template System Features:
|
||||
- Templates stored in `templates/` directory
|
||||
- Variable substitution using placeholders:
|
||||
- `{{PROJECT_NAME}}`: Raw project name
|
||||
- `{{PROJECT_NAME_SNAKE_CASE}}`: Snake case version
|
||||
- `{{PROJECT_NAME_PASCAL_CASE}}`: Pascal case version
|
||||
- `{{PROJECT_NAME_CAMEL_CASE}}`: Camel case version
|
||||
- `{{CREATION_TIMESTAMP}}`: Creation timestamp
|
||||
|
||||
Example template usage:
|
||||
```bash
|
||||
# Create a new Flutter app from the bloc_app template
|
||||
melos run template template_name:bloc_app type:flutter name:my_new_app
|
||||
|
||||
# Create a new Dart package from a core template
|
||||
melos run template template_name:core_package type:dart name:core_utils
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Static Analysis and Formatting
|
||||
|
||||
#### Code Analysis
|
||||
```bash
|
||||
# Run static analysis
|
||||
melos run analyze
|
||||
```
|
||||
|
||||
Example output:
|
||||
```
|
||||
Analyzing package_1...
|
||||
No issues found!
|
||||
Analyzing package_2...
|
||||
info • Unused import • lib/src/unused_file.dart:3:8 • unused_import
|
||||
```
|
||||
|
||||
#### Code Formatting
|
||||
```bash
|
||||
# Format all Dart files
|
||||
melos run format
|
||||
|
||||
# Format and check for changes
|
||||
melos run format -- -o write --set-exit-if-changed
|
||||
```
|
||||
|
||||
### Code Generation
|
||||
|
||||
#### Generate for All Packages
|
||||
```bash
|
||||
# Run build_runner for all packages
|
||||
melos run generate
|
||||
```
|
||||
|
||||
Example output:
|
||||
```
|
||||
Running build_runner for package_1...
|
||||
[INFO] Generating build script...
|
||||
[INFO] Generating build script completed, took 304ms
|
||||
[INFO] Running build...
|
||||
[INFO] 5 outputs were generated
|
||||
[INFO] Running build completed, took 2.8s
|
||||
```
|
||||
|
||||
#### Generate for Specific Packages
|
||||
```bash
|
||||
# Run code generation for specified packages
|
||||
MELOS_SCOPE="package_name1,package_name2" melos run generate:custom
|
||||
```
|
||||
|
||||
#### Check Generation Status
|
||||
```bash
|
||||
# Check if code generation is needed
|
||||
MELOS_SCOPE="package_name" melos run generate:check
|
||||
```
|
||||
|
||||
Example output:
|
||||
```
|
||||
Checking auth_package...
|
||||
Package auth_package needs code generation.
|
||||
Checking user_package...
|
||||
Package user_package does not use build_runner.
|
||||
```
|
||||
|
||||
#### Generate Dummy Tests
|
||||
```bash
|
||||
# Generate dummy test files
|
||||
MELOS_SCOPE="package_name" melos run generate:dummy:test
|
||||
```
|
||||
|
||||
## Testing and Coverage
|
||||
|
||||
### Running Tests
|
||||
|
||||
#### All Packages
|
||||
```bash
|
||||
# Run all tests
|
||||
melos run test
|
||||
```
|
||||
|
||||
Example output:
|
||||
```
|
||||
Running tests for auth_package...
|
||||
00:01 +10: All tests passed!
|
||||
Running tests for user_package...
|
||||
00:02 +15: All tests passed!
|
||||
```
|
||||
|
||||
#### Specific Packages
|
||||
```bash
|
||||
# Run tests for specific packages
|
||||
MELOS_SCOPE="package_name1,package_name2" melos run test:custom
|
||||
```
|
||||
|
||||
### Coverage Tools
|
||||
|
||||
#### Generate Coverage
|
||||
```bash
|
||||
# Generate coverage reports
|
||||
melos run coverage
|
||||
```
|
||||
|
||||
Features:
|
||||
- Adds coverage dependency temporarily
|
||||
- Generates LCOV reports
|
||||
- Removes coverage dependency after completion
|
||||
|
||||
#### Coverage Report
|
||||
```bash
|
||||
# Generate HTML coverage report
|
||||
melos run coverage_report
|
||||
```
|
||||
|
||||
Features:
|
||||
- Generates HTML reports from LCOV data
|
||||
- Creates detailed coverage visualization
|
||||
- Provides package-level coverage metrics
|
||||
|
||||
## Documentation
|
||||
|
||||
### API Documentation Generation
|
||||
|
||||
#### Generate for All Packages
|
||||
|
||||
```bash
|
||||
# Generate docs for all packages
|
||||
melos run docs:generate
|
||||
|
||||
|
||||
#### Generate for Specific Packages
|
||||
```bash
|
||||
# Generate docs for specific packages
|
||||
MELOS_SCOPE="package_name1,package_name2" melos run docs:generate:custom
|
||||
|
||||
# Example
|
||||
MELOS_SCOPE="core_package,utils_package" melos run docs:generate:custom
|
||||
```
|
||||
|
||||
### Serving Documentation
|
||||
|
||||
#### Serve All Documentation
|
||||
```bash
|
||||
# Serve generated documentation
|
||||
melos run docs:serve
|
||||
```
|
||||
After running, visit `http://localhost:8080` in your browser
|
||||
|
||||
#### Serve Specific Package Documentation
|
||||
```bash
|
||||
# Serve docs for specific packages with custom port
|
||||
MELOS_SCOPE="package_name" DOC_PORT=8081 melos run docs:serve:custom
|
||||
|
||||
# Example
|
||||
MELOS_SCOPE="api_package" DOC_PORT=8082 melos run docs:serve:custom
|
||||
```
|
||||
|
||||
## Publishing
|
||||
|
||||
### Publishing Packages
|
||||
|
||||
#### Check Publication Status
|
||||
```bash
|
||||
# Dry run to check what would be published
|
||||
melos run publish:check
|
||||
```
|
||||
|
||||
Example output:
|
||||
```
|
||||
Would publish the following packages:
|
||||
- auth_package (1.0.0 -> 1.0.1)
|
||||
- user_package (2.1.0 -> 2.2.0)
|
||||
```
|
||||
|
||||
#### Publish Packages
|
||||
```bash
|
||||
# Publish all changed packages
|
||||
melos run publish
|
||||
```
|
||||
|
||||
Recommended workflow:
|
||||
```bash
|
||||
# First, check what would be published
|
||||
melos run publish:check
|
||||
# If everything looks good, publish
|
||||
melos run publish
|
||||
```
|
||||
|
||||
## Dependency Management
|
||||
|
||||
### Checking Dependencies
|
||||
```bash
|
||||
# Check for outdated dependencies
|
||||
melos run deps:check
|
||||
```
|
||||
|
||||
Example output:
|
||||
```
|
||||
Checking dependencies for auth_package...
|
||||
2 dependencies are out of date
|
||||
Checking dependencies for user_package...
|
||||
All dependencies up to date
|
||||
```
|
||||
|
||||
### Upgrading Dependencies
|
||||
|
||||
#### All Packages
|
||||
```bash
|
||||
# Upgrade all dependencies
|
||||
melos run deps:upgrade
|
||||
```
|
||||
|
||||
#### Specific Packages
|
||||
```bash
|
||||
# Upgrade dependencies for specific packages
|
||||
MELOS_SCOPE="package_name1,package_name2" melos run deps:upgrade:custom
|
||||
|
||||
# Example
|
||||
MELOS_SCOPE="database_package,api_package" melos run deps:upgrade:custom
|
||||
```
|
||||
|
||||
## CI/CD
|
||||
|
||||
### Continuous Integration
|
||||
```bash
|
||||
# Run full CI pipeline
|
||||
melos run ci
|
||||
```
|
||||
|
||||
This command runs:
|
||||
1. Static analysis (`analyze`)
|
||||
2. All tests (`test`)
|
||||
|
||||
Example GitHub Actions workflow:
|
||||
```yaml
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: dart-lang/setup-dart@v1
|
||||
- run: dart pub global activate melos
|
||||
- run: melos bootstrap
|
||||
- run: melos run ci
|
||||
```
|
||||
|
||||
### Maintenance and Cleanup
|
||||
|
||||
#### Clean Command
|
||||
```bash
|
||||
# Clean all generated files and build artifacts
|
||||
melos run clean
|
||||
```
|
||||
|
||||
The clean command removes:
|
||||
- Build directories
|
||||
- Generated files:
|
||||
- `.g.dart`
|
||||
- `.freezed.dart`
|
||||
- `.mocks.dart`
|
||||
- `.gr.dart`
|
||||
- `.config.dart`
|
||||
- `.reflectable.dart`
|
||||
- And many more
|
||||
- Compilation artifacts:
|
||||
- `.g.aot`
|
||||
- `.g.ddc`
|
||||
- `.g.js`
|
||||
- `.g.js.map`
|
||||
- `.g.part`
|
||||
- `.g.sum`
|
||||
- `.g.txt`
|
||||
- Coverage files and reports
|
||||
- Documentation build files
|
||||
|
||||
|
||||
## Debugging and Utility Scripts
|
||||
|
||||
### Package Information
|
||||
|
||||
#### Debug Package Names
|
||||
```bash
|
||||
# Show all package names
|
||||
melos run debug_pkg_name
|
||||
```
|
||||
|
||||
Example output:
|
||||
```
|
||||
Package name is auth_package
|
||||
Package name is user_package
|
||||
Package name is core_package
|
||||
```
|
||||
|
||||
#### Debug Package Paths
|
||||
```bash
|
||||
# Show all package paths
|
||||
melos run debug_pkg_path
|
||||
```
|
||||
|
||||
Example output:
|
||||
```
|
||||
Package path is /home/user/protevus_platform/packages/auth_package
|
||||
Package path is /home/user/protevus_platform/packages/user_package
|
||||
```
|
||||
|
||||
### Code Analysis Tools
|
||||
|
||||
#### Debug Reflectable Files
|
||||
```bash
|
||||
# Find .reflectable.dart files
|
||||
MELOS_SCOPE="package_name" melos run debug:reflectable
|
||||
|
||||
# Example
|
||||
MELOS_SCOPE="core_package" melos run debug:reflectable
|
||||
```
|
||||
|
||||
Example output:
|
||||
```
|
||||
Checking for .reflectable.dart files in core_package
|
||||
/home/user/protevus_platform/packages/core_package/lib/src/models.reflectable.dart
|
||||
```
|
||||
|
||||
#### List Dart Files
|
||||
```bash
|
||||
# List all Dart files in package(s)
|
||||
MELOS_SCOPE="package_name" melos run list:dart:files
|
||||
```
|
||||
|
||||
Example output:
|
||||
```
|
||||
Listing all Dart files in utils_package:
|
||||
/home/user/protevus_platform/packages/utils_package/lib/src/string_utils.dart
|
||||
/home/user/protevus_platform/packages/utils_package/lib/src/date_utils.dart
|
||||
```
|
||||
|
||||
### Help System
|
||||
```bash
|
||||
# Display all available commands
|
||||
melos run help
|
||||
```
|
||||
|
||||
## Advanced Usage Examples
|
||||
|
||||
### Complex Workflows
|
||||
|
||||
#### Running Specific Test Patterns
|
||||
```bash
|
||||
# Run tests matching a pattern
|
||||
MELOS_SCOPE="auth_package" melos run test:custom -- --name "login"
|
||||
```
|
||||
|
||||
#### Documentation Workflow
|
||||
```bash
|
||||
# Generate and serve documentation
|
||||
melos run docs:generate && melos run docs:serve
|
||||
```
|
||||
|
||||
#### Dependency Update Workflow
|
||||
```bash
|
||||
# Check, upgrade, and test
|
||||
melos run deps:check && melos run deps:upgrade && melos run test
|
||||
```
|
||||
|
||||
#### Publishing Workflow
|
||||
```bash
|
||||
# Check and publish if everything is okay
|
||||
melos run publish:check && melos run publish
|
||||
```
|
||||
|
||||
#### CI and Documentation
|
||||
```bash
|
||||
# Run CI and generate docs if successful
|
||||
melos run ci && melos run docs:generate
|
||||
```
|
||||
|
||||
#### Multi-package Operations
|
||||
```bash
|
||||
# Upgrade dependencies and run tests for multiple packages
|
||||
MELOS_SCOPE="auth_package,user_package,core_package" melos run deps:upgrade:custom && \
|
||||
MELOS_SCOPE="auth_package,user_package,core_package" melos run test:custom
|
||||
```
|
||||
|
||||
#### Code Generation and Verification
|
||||
```bash
|
||||
# Generate code, test, and check formatting
|
||||
melos run generate && melos run test && melos run format -- -o none --set-exit-if-changed
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### Project Organization
|
||||
|
||||
1. **Directory Structure**
|
||||
- Place Flutter apps in `apps/`
|
||||
- Place packages in `packages/`
|
||||
- Keep examples in `examples/`
|
||||
- Store templates in `templates/`
|
||||
- Maintain tools in `tools/`
|
||||
|
||||
2. **Template Usage**
|
||||
- Use `.tmpl` extension for files requiring variable substitution
|
||||
- Document template variables in README files
|
||||
- Keep templates minimal and focused
|
||||
|
||||
3. **Configuration Management**
|
||||
- Keep configuration files modular in `config/` directory
|
||||
- Run `melos combine_config` after configuration changes
|
||||
- Use environment variables for flexible configuration
|
||||
|
||||
### Development Workflow
|
||||
|
||||
1. **Before Committing**
|
||||
```bash
|
||||
melos run format
|
||||
melos run analyze
|
||||
melos run test
|
||||
```
|
||||
|
||||
2. **After Dependency Changes**
|
||||
```bash
|
||||
melos bootstrap
|
||||
melos run deps:check
|
||||
```
|
||||
|
||||
3. **Before Publishing**
|
||||
```bash
|
||||
melos run publish:check
|
||||
melos run test
|
||||
melos run docs:generate
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
|
||||
1. **Testing**
|
||||
- Maintain high test coverage
|
||||
- Run `melos run coverage` regularly
|
||||
- Review coverage reports
|
||||
|
||||
2. **Documentation**
|
||||
- Document all public APIs
|
||||
- Keep README files updated
|
||||
- Generate and review documentation
|
||||
|
||||
3. **Code Generation**
|
||||
- Check generation status before commits
|
||||
- Keep generated files up to date
|
||||
- Validate generated code
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Key environment variables used across commands:
|
||||
- `MELOS_SCOPE`: Target specific packages
|
||||
- `DOC_PORT`: Custom documentation server port
|
||||
- `MELOS_ROOT_PATH`: Root directory path
|
||||
- `MELOS_PACKAGE_NAME`: Current package name
|
||||
- `MELOS_PACKAGE_PATH`: Current package path
|
||||
|
||||
## Support and Resources
|
||||
|
||||
### Getting Help
|
||||
1. Run `melos run help` for command documentation
|
||||
2. Check package-specific README files
|
||||
3. Review generated documentation
|
||||
4. Consult the Melos documentation
|
||||
|
||||
### Common Issues
|
||||
1. **Missing Dependencies**
|
||||
```bash
|
||||
melos bootstrap
|
||||
```
|
||||
|
||||
2. **Outdated Generated Code**
|
||||
```bash
|
||||
melos run generate
|
||||
```
|
||||
|
||||
3. **Configuration Issues**
|
||||
```bash
|
||||
melos run combine_config
|
||||
```
|
||||
|
||||
### Maintenance Tasks
|
||||
|
||||
Regular maintenance checklist:
|
||||
1. Update dependencies regularly
|
||||
2. Clean generated files periodically
|
||||
3. Review and update documentation
|
||||
4. Monitor test coverage
|
||||
5. Validate templates
|
||||
6. Check for outdated configurations
|
||||
|
||||
This completes the comprehensive documentation of the Protevus Platform Melos configuration system.
|
182
helpers/create_from_template.dart
Normal file
182
helpers/create_from_template.dart
Normal file
|
@ -0,0 +1,182 @@
|
|||
import 'dart:io';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
void main(List<String> args) async {
|
||||
// Parse command line arguments
|
||||
String? templateName;
|
||||
String? projectType;
|
||||
String? name;
|
||||
|
||||
for (var arg in args) {
|
||||
final parts = arg.split(':');
|
||||
if (parts.length == 2) {
|
||||
switch (parts[0]) {
|
||||
case 'template_name':
|
||||
templateName = parts[1];
|
||||
break;
|
||||
case 'type':
|
||||
projectType = parts[1];
|
||||
break;
|
||||
case 'name':
|
||||
name = parts[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print received arguments for debugging
|
||||
print('Received arguments:');
|
||||
print('Template Name: $templateName');
|
||||
print('Project Type: $projectType');
|
||||
print('Name: $name');
|
||||
|
||||
// Validate inputs
|
||||
if (templateName == null || projectType == null || name == null) {
|
||||
print('Error: Missing required arguments');
|
||||
print(
|
||||
'Usage: melos run template template_name:name type:dart|flutter name:project_name');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (projectType != 'dart' && projectType != 'flutter') {
|
||||
print('Error: type must be either "dart" or "flutter"');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Convert name to snake_case
|
||||
final snakeCaseName = name
|
||||
.replaceAllMapped(
|
||||
RegExp(r'[A-Z]'),
|
||||
(match) => '_${match.group(0)?.toLowerCase()}',
|
||||
)
|
||||
.toLowerCase()
|
||||
.replaceFirst(RegExp(r'^_'), '');
|
||||
|
||||
// Determine directories
|
||||
final templateDir = Directory('templates/$templateName');
|
||||
final targetBaseDir = (projectType == 'flutter') ? 'apps' : 'packages';
|
||||
final targetDir = Directory('$targetBaseDir/$snakeCaseName');
|
||||
|
||||
// Validate template exists
|
||||
if (!await templateDir.exists()) {
|
||||
print('Error: Template "$templateName" not found in templates directory');
|
||||
print('Available templates:');
|
||||
await for (var entity in Directory('templates').list()) {
|
||||
if (entity is Directory) {
|
||||
print(' - ${path.basename(entity.path)}');
|
||||
}
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Check if target directory already exists
|
||||
if (await targetDir.exists()) {
|
||||
print('Error: Target directory already exists at ${targetDir.path}');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
// Create target directory
|
||||
await targetDir.create(recursive: true);
|
||||
|
||||
// Copy template files
|
||||
await _copyDirectory(templateDir, targetDir);
|
||||
|
||||
// Process template files
|
||||
await _processTemplateFiles(targetDir, {
|
||||
'PROJECT_NAME': name,
|
||||
'PROJECT_NAME_SNAKE_CASE': snakeCaseName,
|
||||
'PROJECT_NAME_PASCAL_CASE': _toPascalCase(name),
|
||||
'PROJECT_NAME_CAMEL_CASE': _toCamelCase(name),
|
||||
'CREATION_TIMESTAMP': DateTime.now().toIso8601String(),
|
||||
});
|
||||
|
||||
// Update pubspec.yaml if it exists
|
||||
final pubspecFile = File('${targetDir.path}/pubspec.yaml');
|
||||
if (await pubspecFile.exists()) {
|
||||
await _updatePubspec(pubspecFile, name);
|
||||
}
|
||||
|
||||
print(
|
||||
'Successfully created project from template "$templateName" at ${targetDir.path}');
|
||||
print('Done! 🎉');
|
||||
print('To get started, cd into ${targetDir.path}');
|
||||
} catch (e) {
|
||||
print('Error: $e');
|
||||
// Cleanup on error
|
||||
if (await targetDir.exists()) {
|
||||
await targetDir.delete(recursive: true);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _copyDirectory(Directory source, Directory target) async {
|
||||
await for (var entity in source.list(recursive: false)) {
|
||||
final targetPath =
|
||||
path.join(target.path, path.relative(entity.path, from: source.path));
|
||||
|
||||
if (entity is Directory) {
|
||||
await Directory(targetPath).create(recursive: true);
|
||||
await _copyDirectory(entity, Directory(targetPath));
|
||||
} else if (entity is File) {
|
||||
await entity.copy(targetPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _processTemplateFiles(
|
||||
Directory directory, Map<String, String> replacements) async {
|
||||
await for (var entity in directory.list(recursive: true)) {
|
||||
if (entity is File) {
|
||||
if (path.extension(entity.path) == '.tmpl') {
|
||||
// Process template file
|
||||
String content = await entity.readAsString();
|
||||
for (var entry in replacements.entries) {
|
||||
content = content.replaceAll('{{${entry.key}}}', entry.value);
|
||||
}
|
||||
|
||||
// Write processed content to new file without .tmpl extension
|
||||
final newPath = entity.path.replaceAll('.tmpl', '');
|
||||
await File(newPath).writeAsString(content);
|
||||
await entity.delete(); // Remove template file
|
||||
} else {
|
||||
// Process regular file (only process certain file types)
|
||||
final ext = path.extension(entity.path);
|
||||
if (['.dart', '.yaml', '.md', '.json'].contains(ext)) {
|
||||
String content = await entity.readAsString();
|
||||
for (var entry in replacements.entries) {
|
||||
content = content.replaceAll('{{${entry.key}}}', entry.value);
|
||||
}
|
||||
await entity.writeAsString(content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _updatePubspec(File pubspecFile, String projectName) async {
|
||||
final content = await pubspecFile.readAsString();
|
||||
final yaml = loadYaml(content);
|
||||
|
||||
// Create new pubspec content with updated name
|
||||
final newContent = content.replaceFirst(
|
||||
RegExp(r'name:.*'),
|
||||
'name: $projectName',
|
||||
);
|
||||
|
||||
await pubspecFile.writeAsString(newContent);
|
||||
}
|
||||
|
||||
String _toPascalCase(String input) {
|
||||
return input
|
||||
.split(RegExp(r'[_\- ]'))
|
||||
.map((word) => word[0].toUpperCase() + word.substring(1).toLowerCase())
|
||||
.join('');
|
||||
}
|
||||
|
||||
String _toCamelCase(String input) {
|
||||
final pascal = _toPascalCase(input);
|
||||
return pascal[0].toLowerCase() + pascal.substring(1);
|
||||
}
|
121
helpers/create_project.dart
Normal file
121
helpers/create_project.dart
Normal file
|
@ -0,0 +1,121 @@
|
|||
import 'dart:io';
|
||||
|
||||
void main(List<String> args) async {
|
||||
// Parse command line arguments
|
||||
String? projectType;
|
||||
String? category;
|
||||
String? name;
|
||||
|
||||
for (var arg in args) {
|
||||
final parts = arg.split(':');
|
||||
if (parts.length == 2) {
|
||||
switch (parts[0]) {
|
||||
case 'project_type':
|
||||
case 'project-type':
|
||||
projectType = parts[1];
|
||||
break;
|
||||
case 'category':
|
||||
category = parts[1];
|
||||
break;
|
||||
case 'name':
|
||||
name = parts[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print received arguments for debugging
|
||||
print('Received arguments:');
|
||||
print('Project Type: $projectType');
|
||||
print('Category: $category');
|
||||
print('Name: $name');
|
||||
|
||||
// Validate inputs
|
||||
if (projectType == null || category == null || name == null) {
|
||||
print('Error: Missing required arguments');
|
||||
print(
|
||||
'Usage: melos run create project_type:dart|flutter category:type name:project_name');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (projectType != 'dart' && projectType != 'flutter') {
|
||||
print('Error: project_type must be either "dart" or "flutter"');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Determine base directory
|
||||
final baseDir = projectType == 'flutter' &&
|
||||
(category == 'app' || category == 'web' || category == 'desktop')
|
||||
? 'apps'
|
||||
: 'packages';
|
||||
|
||||
// Create project directory
|
||||
final projectDir = Directory('$baseDir/$name');
|
||||
if (await projectDir.exists()) {
|
||||
print('Error: Project directory already exists at ${projectDir.path}');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
// Ensure the base directory exists
|
||||
await Directory(baseDir).create(recursive: true);
|
||||
|
||||
// Create the project using the appropriate command
|
||||
final result = await Process.run(
|
||||
projectType,
|
||||
[
|
||||
'create',
|
||||
if (projectType == 'flutter') ...[
|
||||
'--org',
|
||||
'com.example',
|
||||
'--project-name',
|
||||
name,
|
||||
if (category == 'plugin') '--template=plugin',
|
||||
if (category == 'package') '--template=package',
|
||||
if (category == 'module') '--template=module',
|
||||
if (category == 'web') '--platforms=web',
|
||||
if (category == 'desktop') '--platforms=windows,macos,linux',
|
||||
] else ...[
|
||||
if (category == 'package') '--template=package',
|
||||
if (category == 'console') '--template=console',
|
||||
if (category == 'server') '--template=server-shelf',
|
||||
],
|
||||
projectDir.path,
|
||||
],
|
||||
);
|
||||
|
||||
if (result.exitCode != 0) {
|
||||
print('Error creating project:');
|
||||
print(result.stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
print('Successfully created $projectType project at ${projectDir.path}');
|
||||
|
||||
// Add additional dependencies based on category
|
||||
if (category == 'server') {
|
||||
await Process.run('dart', ['pub', 'add', 'shelf_router'],
|
||||
workingDirectory: projectDir.path);
|
||||
await Process.run('dart', ['pub', 'add', 'dotenv'],
|
||||
workingDirectory: projectDir.path);
|
||||
await Process.run('dart', ['pub', 'add', 'logger'],
|
||||
workingDirectory: projectDir.path);
|
||||
}
|
||||
|
||||
if (category == 'desktop') {
|
||||
await Process.run('dart', ['pub', 'add', 'window_manager'],
|
||||
workingDirectory: projectDir.path);
|
||||
await Process.run('dart', ['pub', 'add', 'screen_retriever'],
|
||||
workingDirectory: projectDir.path);
|
||||
}
|
||||
|
||||
// Format the project
|
||||
await Process.run('dart', ['format', projectDir.path]);
|
||||
|
||||
print('Done! 🎉');
|
||||
print('To get started, cd into ${projectDir.path}');
|
||||
} catch (e) {
|
||||
print('Error: $e');
|
||||
exit(1);
|
||||
}
|
||||
}
|
53
helpers/install_code_extensions.sh
Executable file
53
helpers/install_code_extensions.sh
Executable file
|
@ -0,0 +1,53 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Array of extensions to install
|
||||
extensions=(
|
||||
"nash.awesome-flutter-snippets" # Dart Data Class Generator
|
||||
"robert-brunhage.flutter-riverpod-snippets" # Flutter Riverpod Snippets
|
||||
"usernamehw.errorlens" # Error Lens
|
||||
"aaron-bond.better-comments" # Better Comments
|
||||
"plibither8.remove-comments" # Remove Comments
|
||||
"patbenatar.advanced-new-file" # Advanced New File
|
||||
"GitHub.copilot" # GitHub Copilot
|
||||
"dracula-theme.theme-dracula" # Dracula Theme (optional)
|
||||
"jsayol.firebase-explorer" # Firebase Explorer
|
||||
"pflannery.vscode-versionlens" # Version Lens
|
||||
"esentis.flutter-find-unused-assets-and-dart-files" # Find Unused Assets & Dart Files
|
||||
"humao.rest-client" # REST Client
|
||||
"rangav.vscode-thunder-client" # Thunder Client
|
||||
"ritwickdey.liveserver" # Live Server
|
||||
"Dart-Code.dart-code" # Dart SDK
|
||||
"Dart-Code.flutter" # Flutter SDK
|
||||
"ms-vscode.cpptools" # C/C++
|
||||
"ms-vscode.cpptools-extension-pack" # C/C++ Extension Pack
|
||||
"ms-vscode.cpptools-themes" # C/C++ Themes
|
||||
"twxs.cmake " # CMake
|
||||
"ms-vscode.cmake-tools" # CMake Tools
|
||||
"ms-vscode.makefile-tools" # Makefile Tools
|
||||
"saoudrizwan.claude-dev" # Claude Dev
|
||||
"Continue.continue" # Continue
|
||||
"DEVSENSE.phptools-vscode" # PHP Tools
|
||||
"DEVSENSE.composer-php-vscode" # Composer PHP
|
||||
"DEVSENSE.profiler-php-vscode" # Profiler PHP
|
||||
"ms-vscode.remote-explorer" # Remote - Containers
|
||||
"ms-vscode-remote.remote-ssh" # Remote - SSH
|
||||
"ms-vscode-remote.remote-ssh-edit" # Remote - SSH: Edit
|
||||
"ms-vscode-remote.remote-containers" # Remote - Containers
|
||||
"eamodio.gitlens" # GitLens
|
||||
"DEVSENSE.intelli-php-vscode" # IntelliPHP
|
||||
"blaugold.melos-code" # Melos
|
||||
"vscode-icons-team.vscode-icons" # VSCode Icons
|
||||
"redhat.vscode-yaml" # YAML
|
||||
"GitHub.vscode-github-actions" # GitHub Actions
|
||||
"ms-azuretools.vscode-docker" # Docker
|
||||
"ms-kubernetes-tools.vscode-kubernetes-tools" # Kubernetes
|
||||
)
|
||||
|
||||
# Install each extension
|
||||
echo "Installing VSCode extensions..."
|
||||
for extension in "${extensions[@]}"; do
|
||||
code --install-extension "$extension" --force
|
||||
echo "Installed: $extension"
|
||||
done
|
||||
|
||||
echo "All extensions have been installed successfully."
|
156
helpers/tools/converter/bin/extract_contracts.dart
Normal file
156
helpers/tools/converter/bin/extract_contracts.dart
Normal file
|
@ -0,0 +1,156 @@
|
|||
import 'dart:io';
|
||||
import 'package:args/args.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:converter/src/extractors/base_extractor.dart';
|
||||
import 'package:converter/src/extractors/php_extractor.dart';
|
||||
|
||||
/// Factory for creating language-specific extractors
|
||||
class ExtractorFactory {
|
||||
/// Create an appropriate extractor based on file extension
|
||||
static LanguageExtractor? createExtractor(String extension) {
|
||||
switch (extension.toLowerCase()) {
|
||||
case '.php':
|
||||
return PhpExtractor();
|
||||
// TODO: Add more extractors as they're implemented
|
||||
// case '.py':
|
||||
// return PythonExtractor();
|
||||
// case '.ts':
|
||||
// case '.js':
|
||||
// return TypeScriptExtractor();
|
||||
// case '.java':
|
||||
// return JavaExtractor();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Main contract extractor CLI
|
||||
class ContractExtractorCLI {
|
||||
final String sourcePath;
|
||||
final String outputPath;
|
||||
final bool verbose;
|
||||
|
||||
ContractExtractorCLI({
|
||||
required this.sourcePath,
|
||||
required this.outputPath,
|
||||
this.verbose = false,
|
||||
});
|
||||
|
||||
/// Run the extraction process
|
||||
Future<void> run() async {
|
||||
try {
|
||||
if (await FileSystemEntity.isDirectory(sourcePath)) {
|
||||
await _processDirectory(sourcePath);
|
||||
} else if (await FileSystemEntity.isFile(sourcePath)) {
|
||||
await _processFile(sourcePath);
|
||||
} else {
|
||||
throw Exception('Source path does not exist: $sourcePath');
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error: $e');
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Process a directory recursively
|
||||
Future<void> _processDirectory(String dirPath) async {
|
||||
final dir = Directory(dirPath);
|
||||
await for (final entity in dir.list(recursive: true)) {
|
||||
if (entity is File) {
|
||||
await _processFile(entity.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Process a single file
|
||||
Future<void> _processFile(String filePath) async {
|
||||
final extension = path.extension(filePath);
|
||||
final extractor = ExtractorFactory.createExtractor(extension);
|
||||
|
||||
if (extractor == null) {
|
||||
if (verbose) {
|
||||
print('Skipping unsupported file type: $filePath');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Calculate relative path to maintain directory structure
|
||||
final relativePath = path.relative(filePath, from: sourcePath);
|
||||
final destDir = path.join(outputPath, path.dirname(relativePath));
|
||||
|
||||
// Create destination directory
|
||||
await Directory(destDir).create(recursive: true);
|
||||
|
||||
// Extract contract
|
||||
final contract = await extractor.parseFile(filePath);
|
||||
final yamlContent = extractor.convertToYaml(contract);
|
||||
|
||||
// Write YAML contract
|
||||
final yamlFile = File(path.join(
|
||||
destDir,
|
||||
'${path.basenameWithoutExtension(filePath)}.yaml',
|
||||
));
|
||||
await yamlFile.writeAsString(yamlContent);
|
||||
|
||||
if (verbose) {
|
||||
print('Processed: $filePath');
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error processing $filePath: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main(List<String> arguments) async {
|
||||
final parser = ArgParser()
|
||||
..addOption(
|
||||
'source',
|
||||
abbr: 's',
|
||||
help: 'Source file or directory path',
|
||||
mandatory: true,
|
||||
)
|
||||
..addOption(
|
||||
'output',
|
||||
abbr: 'o',
|
||||
help: 'Output directory for YAML contracts',
|
||||
mandatory: true,
|
||||
)
|
||||
..addFlag(
|
||||
'verbose',
|
||||
abbr: 'v',
|
||||
help: 'Enable verbose output',
|
||||
defaultsTo: false,
|
||||
)
|
||||
..addFlag(
|
||||
'help',
|
||||
abbr: 'h',
|
||||
help: 'Show this help message',
|
||||
negatable: false,
|
||||
);
|
||||
|
||||
try {
|
||||
final results = parser.parse(arguments);
|
||||
|
||||
if (results['help'] as bool) {
|
||||
print('Usage: dart extract_contracts.dart [options]');
|
||||
print(parser.usage);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
final cli = ContractExtractorCLI(
|
||||
sourcePath: results['source'] as String,
|
||||
outputPath: results['output'] as String,
|
||||
verbose: results['verbose'] as bool,
|
||||
);
|
||||
|
||||
await cli.run();
|
||||
print('Contract extraction completed successfully.');
|
||||
} catch (e) {
|
||||
print('Error: $e');
|
||||
print('\nUsage: dart extract_contracts.dart [options]');
|
||||
print(parser.usage);
|
||||
exit(1);
|
||||
}
|
||||
}
|
108
helpers/tools/converter/example/extract_contracts_example.dart
Normal file
108
helpers/tools/converter/example/extract_contracts_example.dart
Normal file
|
@ -0,0 +1,108 @@
|
|||
import 'dart:io';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
void main() async {
|
||||
// Create a sample PHP file
|
||||
final samplePhp = '''
|
||||
<?php
|
||||
|
||||
namespace App\\Models;
|
||||
|
||||
use Illuminate\\Database\\Eloquent\\Model;
|
||||
use Illuminate\\Database\\Eloquent\\Factories\\HasFactory;
|
||||
use App\\Interfaces\\UserInterface;
|
||||
|
||||
/**
|
||||
* User model class.
|
||||
* Represents a user in the system.
|
||||
*/
|
||||
class User extends Model implements UserInterface {
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected array \$fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'password',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be hidden for serialization.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected array \$hidden = [
|
||||
'password',
|
||||
'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the user's full name.
|
||||
*
|
||||
* @param string \$title Optional title prefix
|
||||
* @return string
|
||||
*/
|
||||
public function getFullName(string \$title = ''): string {
|
||||
return trim(\$title . ' ' . \$this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user's password.
|
||||
*
|
||||
* @param string \$value
|
||||
* @return void
|
||||
*/
|
||||
public function setPasswordAttribute(string \$value): void {
|
||||
\$this->attributes['password'] = bcrypt(\$value);
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
// Create temporary directories
|
||||
final tempDir = Directory.systemTemp.createTempSync('contract_example');
|
||||
final sourceDir = Directory(path.join(tempDir.path, 'source'))..createSync();
|
||||
final outputDir = Directory(path.join(tempDir.path, 'output'))..createSync();
|
||||
|
||||
// Write sample PHP file
|
||||
final phpFile = File(path.join(sourceDir.path, 'User.php'));
|
||||
await phpFile.writeAsString(samplePhp);
|
||||
|
||||
// Run the contract extractor
|
||||
print('Extracting contracts from ${sourceDir.path}');
|
||||
print('Output directory: ${outputDir.path}');
|
||||
|
||||
final result = await Process.run(
|
||||
'dart',
|
||||
[
|
||||
'run',
|
||||
'bin/extract_contracts.dart',
|
||||
'--source',
|
||||
sourceDir.path,
|
||||
'--output',
|
||||
outputDir.path,
|
||||
'--verbose',
|
||||
],
|
||||
);
|
||||
|
||||
if (result.exitCode != 0) {
|
||||
print('Error: ${result.stderr}');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Read and display the generated YAML
|
||||
final yamlFile = File(path.join(outputDir.path, 'User.yaml'));
|
||||
if (await yamlFile.exists()) {
|
||||
print('\nGenerated YAML contract:');
|
||||
print('------------------------');
|
||||
print(await yamlFile.readAsString());
|
||||
} else {
|
||||
print('Error: YAML file was not generated');
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
tempDir.deleteSync(recursive: true);
|
||||
}
|
122
helpers/tools/converter/lib/src/extractors/base_extractor.dart
Normal file
122
helpers/tools/converter/lib/src/extractors/base_extractor.dart
Normal file
|
@ -0,0 +1,122 @@
|
|||
import 'dart:io';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'yaml_formatter.dart';
|
||||
|
||||
/// Base class for all language extractors
|
||||
abstract class LanguageExtractor {
|
||||
/// File extension this extractor handles (e.g., '.php', '.py')
|
||||
String get fileExtension;
|
||||
|
||||
/// Parse a source file and extract its components
|
||||
Future<Map<String, dynamic>> parseFile(String filePath);
|
||||
|
||||
/// Extract class-level documentation
|
||||
String? extractClassComment(String content);
|
||||
|
||||
/// Extract dependencies (imports, use statements, etc.)
|
||||
List<Map<String, String>> extractDependencies(String content);
|
||||
|
||||
/// Extract class properties/fields
|
||||
List<Map<String, dynamic>> extractProperties(String content);
|
||||
|
||||
/// Extract class methods
|
||||
List<Map<String, dynamic>> extractMethods(String content);
|
||||
|
||||
/// Extract implemented interfaces
|
||||
List<String> extractInterfaces(String content);
|
||||
|
||||
/// Extract used traits/mixins
|
||||
List<String> extractTraits(String content);
|
||||
|
||||
/// Convert extracted data to YAML format
|
||||
String convertToYaml(Map<String, dynamic> data) {
|
||||
return YamlFormatter.toYaml(data);
|
||||
}
|
||||
|
||||
/// Process a directory of source files
|
||||
Future<void> processDirectory(String sourceDir, String destDir) async {
|
||||
final sourceDirectory = Directory(sourceDir);
|
||||
|
||||
await for (final entity in sourceDirectory.list(recursive: true)) {
|
||||
if (entity is! File || !entity.path.endsWith(fileExtension)) continue;
|
||||
|
||||
final relativePath = path.relative(entity.path, from: sourceDir);
|
||||
final destPath = path.join(destDir, path.dirname(relativePath));
|
||||
|
||||
await Directory(destPath).create(recursive: true);
|
||||
|
||||
final data = await parseFile(entity.path);
|
||||
final yamlContent = convertToYaml(data);
|
||||
|
||||
final yamlFile = File(path.join(
|
||||
destPath, '${path.basenameWithoutExtension(entity.path)}.yaml'));
|
||||
|
||||
await yamlFile.writeAsString(yamlContent);
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse method parameters from a parameter string
|
||||
List<Map<String, String>> parseParameters(String paramsStr) {
|
||||
final params = <Map<String, String>>[];
|
||||
if (paramsStr.trim().isEmpty) return params;
|
||||
|
||||
for (final param in paramsStr.split(',')) {
|
||||
final parts = param.trim().split('=');
|
||||
final paramInfo = <String, String>{
|
||||
'name': parts[0].trim(),
|
||||
};
|
||||
|
||||
if (parts.length > 1) {
|
||||
paramInfo['default'] = parts[1].trim();
|
||||
}
|
||||
|
||||
params.add(paramInfo);
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/// Format a comment by removing common comment markers and whitespace
|
||||
String? formatComment(String? comment) {
|
||||
if (comment == null || comment.isEmpty) return null;
|
||||
|
||||
return comment
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.where((line) => line.isNotEmpty)
|
||||
.map((line) {
|
||||
// Remove common comment markers
|
||||
line = line.replaceAll(RegExp(r'^/\*+|\*+/$'), '');
|
||||
line = line.replaceAll(RegExp(r'^\s*\*\s*'), '');
|
||||
line = line.replaceAll(RegExp(r'^//\s*'), '');
|
||||
return line.trim();
|
||||
})
|
||||
.where((line) => line.isNotEmpty)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
/// Extract type information from a type string
|
||||
Map<String, dynamic> extractTypeInfo(String typeStr) {
|
||||
// Handle nullable types
|
||||
final isNullable = typeStr.endsWith('?');
|
||||
if (isNullable) {
|
||||
typeStr = typeStr.substring(0, typeStr.length - 1);
|
||||
}
|
||||
|
||||
// Handle generics
|
||||
final genericMatch = RegExp(r'^([\w\d_]+)<(.+)>$').firstMatch(typeStr);
|
||||
if (genericMatch != null) {
|
||||
return {
|
||||
'base_type': genericMatch.group(1),
|
||||
'generic_params':
|
||||
genericMatch.group(2)!.split(',').map((t) => t.trim()).toList(),
|
||||
'nullable': isNullable,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
'type': typeStr,
|
||||
'nullable': isNullable,
|
||||
};
|
||||
}
|
||||
}
|
157
helpers/tools/converter/lib/src/extractors/php_extractor.dart
Normal file
157
helpers/tools/converter/lib/src/extractors/php_extractor.dart
Normal file
|
@ -0,0 +1,157 @@
|
|||
import 'dart:io';
|
||||
import 'base_extractor.dart';
|
||||
|
||||
/// Extracts contract information from PHP source files
|
||||
class PhpExtractor extends LanguageExtractor {
|
||||
@override
|
||||
String get fileExtension => '.php';
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> parseFile(String filePath) async {
|
||||
final file = File(filePath);
|
||||
final content = await file.readAsString();
|
||||
|
||||
return {
|
||||
'name': filePath.split('/').last.split('.').first,
|
||||
'class_comment': extractClassComment(content),
|
||||
'dependencies': extractDependencies(content),
|
||||
'properties': extractProperties(content),
|
||||
'methods': extractMethods(content),
|
||||
'traits': extractTraits(content),
|
||||
'interfaces': extractInterfaces(content),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String? extractClassComment(String content) {
|
||||
final regex =
|
||||
RegExp(r'/\*\*(.*?)\*/\s*class', multiLine: true, dotAll: true);
|
||||
final match = regex.firstMatch(content);
|
||||
return formatComment(match?.group(1));
|
||||
}
|
||||
|
||||
@override
|
||||
List<Map<String, String>> extractDependencies(String content) {
|
||||
final regex = RegExp(r'use\s+([\w\\]+)(?:\s+as\s+(\w+))?;');
|
||||
final matches = regex.allMatches(content);
|
||||
|
||||
return matches.map((match) {
|
||||
final fullName = match.group(1)!;
|
||||
final alias = match.group(2);
|
||||
return {
|
||||
'name': alias ?? fullName.split('\\').last,
|
||||
'type': 'class', // Assuming class for now
|
||||
'source': fullName,
|
||||
};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
List<Map<String, dynamic>> extractProperties(String content) {
|
||||
final regex = RegExp(
|
||||
r'(?:/\*\*(.*?)\*/\s*)?(public|protected|private)\s+(?:readonly\s+)?(?:static\s+)?(?:[\w|]+\s+)?\$(\w+)(?:\s*=\s*[^;]+)?;',
|
||||
multiLine: true,
|
||||
dotAll: true,
|
||||
);
|
||||
final matches = regex.allMatches(content);
|
||||
|
||||
return matches.map((match) {
|
||||
return {
|
||||
'name': match.group(3), // Property name without $
|
||||
'visibility': match.group(2),
|
||||
'comment': formatComment(match.group(1)),
|
||||
};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
List<Map<String, dynamic>> extractMethods(String content) {
|
||||
final regex = RegExp(
|
||||
r'(?:/\*\*(.*?)\*/\s*)?(public|protected|private)\s+(?:static\s+)?function\s+(\w+)\s*\((.*?)\)(?:\s*:\s*(?:[\w|\\]+))?\s*{',
|
||||
multiLine: true,
|
||||
dotAll: true,
|
||||
);
|
||||
final matches = regex.allMatches(content);
|
||||
|
||||
return matches.map((match) {
|
||||
return {
|
||||
'name': match.group(3),
|
||||
'visibility': match.group(2),
|
||||
'parameters': _parseMethodParameters(match.group(4) ?? ''),
|
||||
'comment': formatComment(match.group(1)),
|
||||
};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
List<Map<String, String>> _parseMethodParameters(String params) {
|
||||
if (params.trim().isEmpty) return [];
|
||||
|
||||
final parameters = <Map<String, String>>[];
|
||||
final paramList = params.split(',');
|
||||
|
||||
for (var param in paramList) {
|
||||
param = param.trim();
|
||||
if (param.isEmpty) continue;
|
||||
|
||||
final paramInfo = <String, String>{};
|
||||
|
||||
// Handle type declaration and parameter name
|
||||
final typeAndName = param.split(RegExp(r'\$'));
|
||||
if (typeAndName.length > 1) {
|
||||
// Has type declaration
|
||||
final type = typeAndName[0].trim();
|
||||
if (type.isNotEmpty) {
|
||||
paramInfo['type'] = type;
|
||||
}
|
||||
|
||||
// Handle parameter name and default value
|
||||
final nameAndDefault = typeAndName[1].split('=');
|
||||
paramInfo['name'] = nameAndDefault[0].trim();
|
||||
|
||||
if (nameAndDefault.length > 1) {
|
||||
paramInfo['default'] = nameAndDefault[1].trim();
|
||||
}
|
||||
} else {
|
||||
// No type declaration, just name and possibly default value
|
||||
final nameAndDefault = param.replaceAll(r'$', '').split('=');
|
||||
paramInfo['name'] = nameAndDefault[0].trim();
|
||||
|
||||
if (nameAndDefault.length > 1) {
|
||||
paramInfo['default'] = nameAndDefault[1].trim();
|
||||
}
|
||||
}
|
||||
|
||||
parameters.add(paramInfo);
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> extractTraits(String content) {
|
||||
final regex = RegExp(r'use\s+([\w\\]+(?:\s*,\s*[\w\\]+)*)\s*;');
|
||||
final matches = regex.allMatches(content);
|
||||
final traits = <String>[];
|
||||
|
||||
for (final match in matches) {
|
||||
final traitList = match.group(1)!.split(',');
|
||||
traits.addAll(traitList.map((t) => t.trim()));
|
||||
}
|
||||
|
||||
return traits;
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> extractInterfaces(String content) {
|
||||
final regex = RegExp(r'implements\s+([\w\\]+(?:\s*,\s*[\w\\]+)*)');
|
||||
final matches = regex.allMatches(content);
|
||||
final interfaces = <String>[];
|
||||
|
||||
for (final match in matches) {
|
||||
final interfaceList = match.group(1)!.split(',');
|
||||
interfaces.addAll(interfaceList.map((i) => i.trim()));
|
||||
}
|
||||
|
||||
return interfaces;
|
||||
}
|
||||
}
|
223
helpers/tools/converter/lib/src/extractors/yaml_formatter.dart
Normal file
223
helpers/tools/converter/lib/src/extractors/yaml_formatter.dart
Normal file
|
@ -0,0 +1,223 @@
|
|||
/// Handles YAML formatting with proper comment preservation
|
||||
class YamlFormatter {
|
||||
/// Format a value for YAML output
|
||||
static String format(dynamic value, {int indent = 0}) {
|
||||
if (value == null) return 'null';
|
||||
|
||||
final indentStr = ' ' * indent;
|
||||
|
||||
if (value is String) {
|
||||
if (value.startsWith('#')) {
|
||||
// Handle comments - preserve only actual comment content
|
||||
return value
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.where((line) => line.isNotEmpty)
|
||||
.map((line) => '$indentStr$line')
|
||||
.join('\n');
|
||||
}
|
||||
// Escape special characters and handle multiline strings
|
||||
if (value.contains('\n') || value.contains('"')) {
|
||||
return '|\n${value.split('\n').map((line) => '$indentStr ${line.trim()}').join('\n')}';
|
||||
}
|
||||
return value.contains(' ') ? '"$value"' : value;
|
||||
}
|
||||
|
||||
if (value is num || value is bool) {
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
if (value is List) {
|
||||
if (value.isEmpty) return '[]';
|
||||
final buffer = StringBuffer('\n');
|
||||
for (final item in value) {
|
||||
buffer.writeln(
|
||||
'$indentStr- ${format(item, indent: indent + 2).trimLeft()}');
|
||||
}
|
||||
return buffer.toString().trimRight();
|
||||
}
|
||||
|
||||
if (value is Map) {
|
||||
if (value.isEmpty) return '{}';
|
||||
final buffer = StringBuffer('\n');
|
||||
value.forEach((key, val) {
|
||||
if (val != null) {
|
||||
final formattedValue = format(val, indent: indent + 2);
|
||||
if (formattedValue.contains('\n')) {
|
||||
buffer.writeln('$indentStr$key:$formattedValue');
|
||||
} else {
|
||||
buffer.writeln('$indentStr$key: $formattedValue');
|
||||
}
|
||||
// Add extra newline between top-level sections
|
||||
if (indent == 0) {
|
||||
buffer.writeln();
|
||||
}
|
||||
}
|
||||
});
|
||||
return buffer.toString().trimRight();
|
||||
}
|
||||
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
/// Extract the actual documentation from a comment block
|
||||
static String _extractDocumentation(String comment) {
|
||||
return comment
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.where((line) => line.isNotEmpty)
|
||||
.where(
|
||||
(line) => !line.contains('class ') && !line.contains('function '))
|
||||
.where((line) => !line.startsWith('@'))
|
||||
.where((line) => !line.contains('use ') && !line.contains('protected '))
|
||||
.where((line) => !line.contains('];') && !line.contains('['))
|
||||
.where((line) => !line.contains("'"))
|
||||
.where(
|
||||
(line) => !line.contains('private ') && !line.contains('public '))
|
||||
.where((line) => !line.contains('\$'))
|
||||
.map((line) => line.trim())
|
||||
.where((line) => line.isNotEmpty)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
/// Format method documentation
|
||||
static String formatMethodDoc(Map<String, dynamic> method) {
|
||||
final buffer = StringBuffer();
|
||||
|
||||
// Add main comment
|
||||
if (method['comment'] != null) {
|
||||
final mainComment = _extractDocumentation(method['comment'].toString());
|
||||
if (mainComment.isNotEmpty) {
|
||||
buffer.writeln(
|
||||
mainComment.split('\n').map((line) => '# $line').join('\n'));
|
||||
}
|
||||
}
|
||||
|
||||
// Add parameter documentation
|
||||
final params = method['parameters'] as List<Map<String, String>>?;
|
||||
if (params != null && params.isNotEmpty) {
|
||||
buffer.writeln('# Parameters:');
|
||||
for (final param in params) {
|
||||
final name = param['name'];
|
||||
final type = param['type'] ?? 'mixed';
|
||||
final defaultValue = param['default'];
|
||||
if (defaultValue != null) {
|
||||
buffer.writeln('# $name ($type = $defaultValue)');
|
||||
} else {
|
||||
buffer.writeln('# $name ($type)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.toString().trimRight();
|
||||
}
|
||||
|
||||
/// Format property documentation
|
||||
static String formatPropertyDoc(Map<String, dynamic> property) {
|
||||
final buffer = StringBuffer();
|
||||
|
||||
// Add main comment
|
||||
if (property['comment'] != null) {
|
||||
final mainComment = _extractDocumentation(property['comment'].toString());
|
||||
if (mainComment.isNotEmpty) {
|
||||
buffer.writeln(
|
||||
mainComment.split('\n').map((line) => '# $line').join('\n'));
|
||||
}
|
||||
}
|
||||
|
||||
// Add visibility
|
||||
if (property['visibility'] != null) {
|
||||
buffer.writeln('# Visibility: ${property["visibility"]}');
|
||||
}
|
||||
|
||||
return buffer.toString().trimRight();
|
||||
}
|
||||
|
||||
/// Convert a contract to YAML format
|
||||
static String toYaml(Map<String, dynamic> contract) {
|
||||
final formatted = <String, dynamic>{};
|
||||
|
||||
// Format class documentation
|
||||
if (contract['class_comment'] != null) {
|
||||
final doc = _extractDocumentation(contract['class_comment'] as String);
|
||||
if (doc.isNotEmpty) {
|
||||
formatted['documentation'] =
|
||||
doc.split('\n').map((line) => '# $line').join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
// Format dependencies (remove duplicates)
|
||||
if (contract['dependencies'] != null) {
|
||||
final deps = contract['dependencies'] as List;
|
||||
final uniqueDeps = <String, Map<String, String>>{};
|
||||
for (final dep in deps) {
|
||||
final source = dep['source'] as String;
|
||||
if (!uniqueDeps.containsKey(source)) {
|
||||
uniqueDeps[source] = dep as Map<String, String>;
|
||||
}
|
||||
}
|
||||
formatted['dependencies'] = uniqueDeps.values.toList();
|
||||
}
|
||||
|
||||
// Format properties with documentation
|
||||
if (contract['properties'] != null) {
|
||||
formatted['properties'] = (contract['properties'] as List).map((prop) {
|
||||
final doc = formatPropertyDoc(prop as Map<String, dynamic>);
|
||||
return {
|
||||
'name': prop['name'],
|
||||
'visibility': prop['visibility'],
|
||||
'documentation': doc,
|
||||
};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
// Format methods with documentation
|
||||
if (contract['methods'] != null) {
|
||||
formatted['methods'] = (contract['methods'] as List).map((method) {
|
||||
final doc = formatMethodDoc(method as Map<String, dynamic>);
|
||||
return {
|
||||
'name': method['name'],
|
||||
'visibility': method['visibility'],
|
||||
'parameters': method['parameters'],
|
||||
'documentation': doc,
|
||||
};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
// Format interfaces (remove duplicates)
|
||||
if (contract['interfaces'] != null) {
|
||||
formatted['interfaces'] =
|
||||
(contract['interfaces'] as List).toSet().toList();
|
||||
}
|
||||
|
||||
// Format traits (remove duplicates and filter out interfaces)
|
||||
if (contract['traits'] != null) {
|
||||
final traits = (contract['traits'] as List)
|
||||
.where((t) {
|
||||
// Filter out duplicates from dependencies
|
||||
if (contract['dependencies'] != null) {
|
||||
final deps = contract['dependencies'] as List;
|
||||
if (deps.any((d) => d['source'] == t)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Filter out interfaces
|
||||
if (formatted['interfaces'] != null) {
|
||||
final interfaces = formatted['interfaces'] as List;
|
||||
if (interfaces.contains(t)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.toSet()
|
||||
.toList();
|
||||
|
||||
if (traits.isNotEmpty) {
|
||||
formatted['traits'] = traits;
|
||||
}
|
||||
}
|
||||
|
||||
return format(formatted);
|
||||
}
|
||||
}
|
168
helpers/tools/converter/lib/src/utils/class_generator_utils.dart
Normal file
168
helpers/tools/converter/lib/src/utils/class_generator_utils.dart
Normal file
|
@ -0,0 +1,168 @@
|
|||
import 'name_utils.dart';
|
||||
import 'type_conversion_utils.dart';
|
||||
|
||||
/// Utility class for generating Dart class code
|
||||
class ClassGeneratorUtils {
|
||||
/// Generate a constructor for a class
|
||||
static String generateConstructor(
|
||||
String className, List<Map<String, dynamic>> properties) {
|
||||
final buffer = StringBuffer();
|
||||
|
||||
// Constructor signature
|
||||
buffer.writeln(' $className({');
|
||||
final params = <String>[];
|
||||
for (final prop in properties) {
|
||||
final propName = NameUtils.toDartName(prop['name'] as String);
|
||||
final propType =
|
||||
TypeConversionUtils.pythonToDartType(prop['type'] as String);
|
||||
final hasDefault = prop['has_default'] == true;
|
||||
|
||||
if (hasDefault) {
|
||||
params.add(' $propType? $propName,');
|
||||
} else {
|
||||
params.add(' required $propType $propName,');
|
||||
}
|
||||
}
|
||||
buffer.writeln(params.join('\n'));
|
||||
buffer.writeln(' }) {');
|
||||
|
||||
// Initialize properties in constructor body
|
||||
for (final prop in properties) {
|
||||
final propName = NameUtils.toDartName(prop['name'] as String);
|
||||
final propType =
|
||||
TypeConversionUtils.pythonToDartType(prop['type'] as String);
|
||||
final hasDefault = prop['has_default'] == true;
|
||||
|
||||
if (hasDefault) {
|
||||
final defaultValue = TypeConversionUtils.getDefaultValue(propType);
|
||||
buffer.writeln(' _$propName = $propName ?? $defaultValue;');
|
||||
} else {
|
||||
buffer.writeln(' _$propName = $propName;');
|
||||
}
|
||||
}
|
||||
buffer.writeln(' }');
|
||||
buffer.writeln();
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Generate property declarations and accessors
|
||||
static String generateProperties(List<Map<String, dynamic>> properties) {
|
||||
final buffer = StringBuffer();
|
||||
|
||||
for (final prop in properties) {
|
||||
final propName = NameUtils.toDartName(prop['name'] as String);
|
||||
final propType =
|
||||
TypeConversionUtils.pythonToDartType(prop['type'] as String);
|
||||
buffer.writeln(' late $propType _$propName;');
|
||||
|
||||
// Generate getter
|
||||
buffer.writeln(' $propType get $propName => _$propName;');
|
||||
|
||||
// Generate setter if not readonly
|
||||
final isReadonly = prop['is_readonly'];
|
||||
if (isReadonly != null && !isReadonly) {
|
||||
buffer.writeln(' set $propName($propType value) {');
|
||||
buffer.writeln(' _$propName = value;');
|
||||
buffer.writeln(' }');
|
||||
}
|
||||
buffer.writeln();
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Generate a method implementation
|
||||
static String generateMethod(Map<String, dynamic> method) {
|
||||
if (method['name'] == '__init__') return '';
|
||||
|
||||
final buffer = StringBuffer();
|
||||
final methodName = NameUtils.toDartName(method['name'] as String);
|
||||
final returnType =
|
||||
TypeConversionUtils.pythonToDartType(method['return_type'] as String);
|
||||
final methodDoc = method['docstring'] as String?;
|
||||
final isAsync = method['is_async'] == true;
|
||||
|
||||
if (methodDoc != null) {
|
||||
buffer.writeln(' /// ${methodDoc.replaceAll('\n', '\n /// ')}');
|
||||
}
|
||||
|
||||
// Method signature
|
||||
if (isAsync) {
|
||||
buffer.write(' Future<$returnType> $methodName(');
|
||||
} else {
|
||||
buffer.write(' $returnType $methodName(');
|
||||
}
|
||||
|
||||
// Parameters
|
||||
final params = method['arguments'] as List?;
|
||||
if (params != null && params.isNotEmpty) {
|
||||
final paramStrings = <String>[];
|
||||
|
||||
for (final param in params) {
|
||||
final paramName = NameUtils.toDartName(param['name'] as String);
|
||||
final paramType =
|
||||
TypeConversionUtils.pythonToDartType(param['type'] as String);
|
||||
final isOptional = param['is_optional'] == true;
|
||||
|
||||
if (isOptional) {
|
||||
paramStrings.add('[$paramType $paramName]');
|
||||
} else {
|
||||
paramStrings.add('$paramType $paramName');
|
||||
}
|
||||
}
|
||||
|
||||
buffer.write(paramStrings.join(', '));
|
||||
}
|
||||
|
||||
buffer.write(')');
|
||||
if (isAsync) buffer.write(' async');
|
||||
buffer.writeln(' {');
|
||||
buffer.writeln(' // TODO: Implement $methodName');
|
||||
if (returnType == 'void') {
|
||||
buffer.writeln(' throw UnimplementedError();');
|
||||
} else {
|
||||
buffer.writeln(' throw UnimplementedError();');
|
||||
}
|
||||
buffer.writeln(' }');
|
||||
buffer.writeln();
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Generate required interface implementations
|
||||
static String generateRequiredImplementations(
|
||||
List<String> bases, Map<String, dynamic> classContract) {
|
||||
final buffer = StringBuffer();
|
||||
|
||||
// Generate BaseChain implementations
|
||||
if (bases.contains('BaseChain')) {
|
||||
buffer.writeln(' late Map<String, dynamic>? _memory;');
|
||||
buffer.writeln(' Map<String, dynamic>? get memory => _memory;');
|
||||
buffer.writeln();
|
||||
buffer.writeln(' late bool _verbose;');
|
||||
buffer.writeln(' bool get verbose => _verbose;');
|
||||
buffer.writeln();
|
||||
|
||||
// Constructor with required properties
|
||||
buffer.writeln(' ${classContract['name']}({');
|
||||
buffer.writeln(' Map<String, dynamic>? memory,');
|
||||
buffer.writeln(' bool? verbose,');
|
||||
buffer.writeln(' }) {');
|
||||
buffer.writeln(' _memory = memory ?? {};');
|
||||
buffer.writeln(' _verbose = verbose ?? false;');
|
||||
buffer.writeln(' }');
|
||||
buffer.writeln();
|
||||
|
||||
// Required methods
|
||||
buffer.writeln(' @override');
|
||||
buffer.writeln(' void setMemory(Map<String, dynamic> memory) {');
|
||||
buffer.writeln(' // TODO: Implement setMemory');
|
||||
buffer.writeln(' throw UnimplementedError();');
|
||||
buffer.writeln(' }');
|
||||
buffer.writeln();
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
}
|
40
helpers/tools/converter/lib/src/utils/constructor_utils.dart
Normal file
40
helpers/tools/converter/lib/src/utils/constructor_utils.dart
Normal file
|
@ -0,0 +1,40 @@
|
|||
/// Utility functions for constructor generation
|
||||
class ConstructorUtils {
|
||||
/// Generate constructor parameter declarations
|
||||
static String generateParameters(List<Map<String, dynamic>> properties) {
|
||||
final buffer = StringBuffer();
|
||||
for (final prop in properties) {
|
||||
final propName = prop['name'] as String;
|
||||
final propType = prop['type'] as String;
|
||||
final hasDefault = prop['has_default'] == true;
|
||||
|
||||
if (hasDefault) {
|
||||
buffer.writeln(' $propType? $propName,');
|
||||
} else {
|
||||
buffer.writeln(' required $propType $propName,');
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Generate constructor initialization statements
|
||||
static String generateInitializers(List<Map<String, dynamic>> properties) {
|
||||
final buffer = StringBuffer();
|
||||
for (final prop in properties) {
|
||||
final propName = prop['name'] as String;
|
||||
final hasDefault = prop['has_default'] == true;
|
||||
if (hasDefault) {
|
||||
buffer.writeln(
|
||||
' _$propName = $propName ?? false;'); // TODO: Better default values
|
||||
} else {
|
||||
buffer.writeln(' _$propName = $propName;');
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Check if a method is a constructor (__init__)
|
||||
static bool isConstructor(Map<String, dynamic> method) {
|
||||
return method['name'] == '__init__';
|
||||
}
|
||||
}
|
23
helpers/tools/converter/lib/src/utils/name_utils.dart
Normal file
23
helpers/tools/converter/lib/src/utils/name_utils.dart
Normal file
|
@ -0,0 +1,23 @@
|
|||
/// Utility functions for name conversions
|
||||
class NameUtils {
|
||||
/// Convert Python method/property name to Dart style
|
||||
static String toDartName(String pythonName) {
|
||||
// Handle special Python method names
|
||||
if (pythonName.startsWith('__') && pythonName.endsWith('__')) {
|
||||
final name = pythonName.substring(2, pythonName.length - 2);
|
||||
if (name == 'init') return 'new';
|
||||
return name;
|
||||
}
|
||||
|
||||
// Convert snake_case to camelCase
|
||||
final parts = pythonName.split('_');
|
||||
if (parts.isEmpty) return pythonName;
|
||||
|
||||
return parts.first +
|
||||
parts
|
||||
.skip(1)
|
||||
.where((p) => p.isNotEmpty)
|
||||
.map((p) => p[0].toUpperCase() + p.substring(1))
|
||||
.join('');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/// Utility class for converting Python types to Dart types
|
||||
class TypeConversionUtils {
|
||||
/// Convert a Python type string to its Dart equivalent
|
||||
static String pythonToDartType(String pythonType) {
|
||||
final typeMap = {
|
||||
'str': 'String',
|
||||
'int': 'int',
|
||||
'float': 'double',
|
||||
'bool': 'bool',
|
||||
'List': 'List',
|
||||
'Dict': 'Map',
|
||||
'dict': 'Map<String, dynamic>',
|
||||
'Any': 'dynamic',
|
||||
'None': 'void',
|
||||
'Optional': 'dynamic',
|
||||
'Union': 'dynamic',
|
||||
'Callable': 'Function',
|
||||
};
|
||||
|
||||
// Handle generic types
|
||||
if (pythonType.contains('[')) {
|
||||
final match = RegExp(r'(\w+)\[(.*)\]').firstMatch(pythonType);
|
||||
if (match != null) {
|
||||
final baseType = match.group(1)!;
|
||||
final genericType = match.group(2)!;
|
||||
|
||||
if (baseType == 'List') {
|
||||
return 'List<${pythonToDartType(genericType)}>';
|
||||
} else if (baseType == 'Dict' || baseType == 'dict') {
|
||||
final types = genericType.split(',');
|
||||
if (types.length == 2) {
|
||||
return 'Map<${pythonToDartType(types[0].trim())}, ${pythonToDartType(types[1].trim())}>';
|
||||
}
|
||||
return 'Map<String, dynamic>';
|
||||
} else if (baseType == 'Optional') {
|
||||
final innerType = pythonToDartType(genericType);
|
||||
if (innerType == 'Map<String, dynamic>') {
|
||||
return 'Map<String, dynamic>?';
|
||||
}
|
||||
return '${innerType}?';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle raw types
|
||||
if (pythonType == 'dict') {
|
||||
return 'Map<String, dynamic>';
|
||||
} else if (pythonType == 'None') {
|
||||
return 'void';
|
||||
}
|
||||
|
||||
return typeMap[pythonType] ?? pythonType;
|
||||
}
|
||||
|
||||
/// Get a default value for a Dart type
|
||||
static String getDefaultValue(String dartType) {
|
||||
switch (dartType) {
|
||||
case 'bool':
|
||||
case 'bool?':
|
||||
return 'false';
|
||||
case 'int':
|
||||
case 'int?':
|
||||
return '0';
|
||||
case 'double':
|
||||
case 'double?':
|
||||
return '0.0';
|
||||
case 'String':
|
||||
case 'String?':
|
||||
return "''";
|
||||
case 'Map<String, dynamic>':
|
||||
case 'Map<String, dynamic>?':
|
||||
return '{}';
|
||||
case 'List':
|
||||
case 'List?':
|
||||
return '[]';
|
||||
default:
|
||||
if (dartType.startsWith('List<')) {
|
||||
return '[]';
|
||||
} else if (dartType.startsWith('Map<')) {
|
||||
return '{}';
|
||||
} else if (dartType.endsWith('?')) {
|
||||
return 'null';
|
||||
}
|
||||
return 'null';
|
||||
}
|
||||
}
|
||||
}
|
20
helpers/tools/converter/lib/src/utils/type_utils.dart
Normal file
20
helpers/tools/converter/lib/src/utils/type_utils.dart
Normal file
|
@ -0,0 +1,20 @@
|
|||
/// Utility functions for type casting
|
||||
class TypeUtils {
|
||||
/// Cast a List<dynamic> to List<Map<String, dynamic>>
|
||||
static List<Map<String, dynamic>> castToMapList(List<dynamic>? list) {
|
||||
if (list == null) return [];
|
||||
return list.map((item) => item as Map<String, dynamic>).toList();
|
||||
}
|
||||
|
||||
/// Cast a dynamic value to Map<String, dynamic>
|
||||
static Map<String, dynamic> castToMap(dynamic value) {
|
||||
if (value == null) return {};
|
||||
return value as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
/// Cast a List<dynamic> to List<String>
|
||||
static List<String> castToStringList(List<dynamic>? list) {
|
||||
if (list == null) return [];
|
||||
return list.map((item) => item.toString()).toList();
|
||||
}
|
||||
}
|
36
helpers/tools/converter/lib/src/utils/yaml_utils.dart
Normal file
36
helpers/tools/converter/lib/src/utils/yaml_utils.dart
Normal file
|
@ -0,0 +1,36 @@
|
|||
import 'package:yaml/yaml.dart';
|
||||
|
||||
/// Utility class for handling YAML conversions
|
||||
class YamlUtils {
|
||||
/// Convert YamlMap to regular Map recursively
|
||||
static Map<String, dynamic> convertYamlToMap(YamlMap yamlMap) {
|
||||
return Map<String, dynamic>.fromEntries(
|
||||
yamlMap.entries.map((entry) {
|
||||
if (entry.value is YamlMap) {
|
||||
return MapEntry(
|
||||
entry.key.toString(),
|
||||
convertYamlToMap(entry.value as YamlMap),
|
||||
);
|
||||
} else if (entry.value is YamlList) {
|
||||
return MapEntry(
|
||||
entry.key.toString(),
|
||||
convertYamlList(entry.value as YamlList),
|
||||
);
|
||||
}
|
||||
return MapEntry(entry.key.toString(), entry.value);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/// Convert YamlList to regular List recursively
|
||||
static List<dynamic> convertYamlList(YamlList yamlList) {
|
||||
return yamlList.map((item) {
|
||||
if (item is YamlMap) {
|
||||
return convertYamlToMap(item);
|
||||
} else if (item is YamlList) {
|
||||
return convertYamlList(item);
|
||||
}
|
||||
return item;
|
||||
}).toList();
|
||||
}
|
||||
}
|
402
helpers/tools/converter/pubspec.lock
Normal file
402
helpers/tools/converter/pubspec.lock
Normal file
|
@ -0,0 +1,402 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "45cfa8471b89fb6643fe9bf51bd7931a76b8f5ec2d65de4fb176dba8d4f22c77"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "73.0.0"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.2"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "4959fec185fe70cce007c57e9ab6983101dbe593d2bf8bbfb4453aaec0cf470a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.8.0"
|
||||
args:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: args
|
||||
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.12.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
coverage:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: coverage
|
||||
sha256: "88b0fddbe4c92910fefc09cc0248f5e7f0cd23e450ded4c28f16ab8ee8f83268"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.1"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: lints
|
||||
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
macros:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: macros
|
||||
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2-main.4"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16+1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
node_preamble:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: node_preamble
|
||||
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
path:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.2"
|
||||
shelf_packages_handler:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_packages_handler
|
||||
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
shelf_static:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_static
|
||||
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_map_stack_trace
|
||||
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
source_maps:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_maps
|
||||
sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.12"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.0"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
test:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: test
|
||||
sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.25.8"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.3"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.5"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.3.1"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket
|
||||
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.6"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
webkit_inspection_protocol:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webkit_inspection_protocol
|
||||
sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
yaml:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: yaml
|
||||
sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.5.0 <4.0.0"
|
15
helpers/tools/converter/pubspec.yaml
Normal file
15
helpers/tools/converter/pubspec.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
name: converter
|
||||
description: A Dart implementation of LangChain, providing tools and utilities for building applications powered by large language models (LLMs).
|
||||
version: 0.1.0
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
|
||||
dependencies:
|
||||
yaml: ^3.1.2
|
||||
path: ^1.8.3
|
||||
args: ^2.4.2
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^2.1.1
|
||||
test: ^1.24.6
|
95
helpers/tools/converter/test/code_generator_class_test.dart
Normal file
95
helpers/tools/converter/test/code_generator_class_test.dart
Normal file
|
@ -0,0 +1,95 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../tools/generate_dart_code.dart';
|
||||
|
||||
void main() {
|
||||
group('Class Generation', () {
|
||||
test('generates class with interface implementations', () {
|
||||
final classContract = {
|
||||
'name': 'SimpleChain',
|
||||
'docstring': 'A simple implementation of a chain.',
|
||||
'bases': ['BaseChain'],
|
||||
'methods': [
|
||||
{
|
||||
'name': 'run',
|
||||
'return_type': 'dict',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'inputs',
|
||||
'type': 'dict',
|
||||
'is_optional': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Execute the chain logic.',
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
final code = generateClass(classContract);
|
||||
|
||||
// Should include BaseChain implementations
|
||||
expect(code, contains('late Map<String, dynamic>? _memory;'));
|
||||
expect(code, contains('Map<String, dynamic>? get memory => _memory;'));
|
||||
expect(code, contains('late bool _verbose;'));
|
||||
expect(code, contains('bool get verbose => _verbose;'));
|
||||
|
||||
// Should include constructor with required properties
|
||||
expect(code, contains('SimpleChain({'));
|
||||
expect(code, contains('Map<String, dynamic>? memory,'));
|
||||
expect(code, contains('bool? verbose,'));
|
||||
expect(code, contains('_memory = memory ?? {};'));
|
||||
expect(code, contains('_verbose = verbose ?? false;'));
|
||||
|
||||
// Should include required method implementations
|
||||
expect(code, contains('@override'));
|
||||
expect(code, contains('void setMemory(Map<String, dynamic> memory)'));
|
||||
|
||||
// Should include additional methods
|
||||
expect(code, contains('Map<String, dynamic> run('));
|
||||
expect(code, contains('Map<String, dynamic> inputs'));
|
||||
expect(code, contains('/// Execute the chain logic.'));
|
||||
});
|
||||
|
||||
test('generates class with own properties and methods', () {
|
||||
final classContract = {
|
||||
'name': 'CustomClass',
|
||||
'docstring': 'A custom class.',
|
||||
'properties': [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'String',
|
||||
'has_default': false,
|
||||
}
|
||||
],
|
||||
'methods': [
|
||||
{
|
||||
'name': 'process',
|
||||
'return_type': 'String',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'input',
|
||||
'type': 'String',
|
||||
'is_optional': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Process input.',
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
final code = generateClass(classContract);
|
||||
|
||||
// Should include properties
|
||||
expect(code, contains('late String _modelName;'));
|
||||
expect(code, contains('String get modelName => _modelName;'));
|
||||
|
||||
// Should include constructor
|
||||
expect(code, contains('CustomClass({'));
|
||||
expect(code, contains('required String modelName'));
|
||||
expect(code, contains('_modelName = modelName;'));
|
||||
|
||||
// Should include methods
|
||||
expect(code, contains('String process(String input)'));
|
||||
expect(code, contains('/// Process input.'));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../tools/generate_dart_code.dart';
|
||||
import '../lib/src/utils/class_generator_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('Code Generator Integration', () {
|
||||
test('generates complete class with properties and methods', () {
|
||||
final classContract = {
|
||||
'name': 'TestModel',
|
||||
'docstring': 'A test model implementation.',
|
||||
'bases': ['BaseModel'],
|
||||
'properties': [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'String',
|
||||
'has_default': false,
|
||||
},
|
||||
{
|
||||
'name': 'is_loaded',
|
||||
'type': 'bool',
|
||||
'has_default': true,
|
||||
}
|
||||
],
|
||||
'methods': [
|
||||
{
|
||||
'name': '__init__',
|
||||
'return_type': 'None',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'String',
|
||||
'is_optional': false,
|
||||
'has_default': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Initialize the model.',
|
||||
},
|
||||
{
|
||||
'name': 'process_input',
|
||||
'return_type': 'String',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'input_text',
|
||||
'type': 'String',
|
||||
'is_optional': false,
|
||||
'has_default': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Process input text.',
|
||||
'is_async': true,
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
final code = generateClass(classContract);
|
||||
|
||||
// Class definition
|
||||
expect(code, contains('class TestModel implements BaseModel {'));
|
||||
expect(code, contains('/// A test model implementation.'));
|
||||
|
||||
// Properties
|
||||
expect(code, contains('late String _modelName;'));
|
||||
expect(code, contains('String get modelName => _modelName;'));
|
||||
expect(code, contains('late bool _isLoaded;'));
|
||||
expect(code, contains('bool get isLoaded => _isLoaded;'));
|
||||
|
||||
// Constructor
|
||||
expect(code, contains('TestModel({'));
|
||||
expect(code, contains('required String modelName,'));
|
||||
expect(code, contains('bool? isLoaded,'));
|
||||
expect(code, contains('_modelName = modelName;'));
|
||||
expect(code, contains('_isLoaded = isLoaded ?? false;'));
|
||||
|
||||
// Methods
|
||||
expect(code,
|
||||
contains('Future<String> processInput(String inputText) async {'));
|
||||
expect(code, contains('/// Process input text.'));
|
||||
|
||||
// No __init__ method
|
||||
expect(code, isNot(contains('__init__')));
|
||||
expect(code, isNot(contains('void new(')));
|
||||
});
|
||||
});
|
||||
}
|
63
helpers/tools/converter/test/code_generator_name_test.dart
Normal file
63
helpers/tools/converter/test/code_generator_name_test.dart
Normal file
|
@ -0,0 +1,63 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../tools/generate_dart_code.dart';
|
||||
|
||||
void main() {
|
||||
group('Code Generator Name Handling', () {
|
||||
test('converts Python method names to Dart style in interfaces', () {
|
||||
final interface = {
|
||||
'name': 'TestInterface',
|
||||
'docstring': 'Test interface.',
|
||||
'methods': [
|
||||
{
|
||||
'name': 'get_model_name',
|
||||
'return_type': 'str',
|
||||
'arguments': [],
|
||||
'docstring': 'Get model name.',
|
||||
},
|
||||
{
|
||||
'name': '__init__',
|
||||
'return_type': 'None',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'model_path',
|
||||
'type': 'str',
|
||||
'is_optional': false,
|
||||
'has_default': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Initialize.',
|
||||
}
|
||||
],
|
||||
'properties': [],
|
||||
};
|
||||
|
||||
final code = generateInterface(interface);
|
||||
expect(code, contains('String getModelName();'));
|
||||
expect(code, contains('void new(String modelPath);'));
|
||||
});
|
||||
|
||||
test('converts Python property names to Dart style in classes', () {
|
||||
final classContract = {
|
||||
'name': 'TestClass',
|
||||
'docstring': 'Test class.',
|
||||
'properties': [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'str',
|
||||
'has_default': false,
|
||||
},
|
||||
{
|
||||
'name': 'is_initialized',
|
||||
'type': 'bool',
|
||||
'has_default': true,
|
||||
}
|
||||
],
|
||||
'methods': [],
|
||||
};
|
||||
|
||||
final code = generateClass(classContract);
|
||||
expect(code, contains('String get modelName'));
|
||||
expect(code, contains('bool get isInitialized'));
|
||||
});
|
||||
});
|
||||
}
|
83
helpers/tools/converter/test/code_generator_test.dart
Normal file
83
helpers/tools/converter/test/code_generator_test.dart
Normal file
|
@ -0,0 +1,83 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../tools/generate_dart_code.dart';
|
||||
|
||||
void main() {
|
||||
group('Code Generator', () {
|
||||
test('generates constructor correctly', () {
|
||||
final classContract = {
|
||||
'name': 'TestClass',
|
||||
'docstring': 'Test class.',
|
||||
'properties': [
|
||||
{
|
||||
'name': 'name',
|
||||
'type': 'String',
|
||||
'has_default': false,
|
||||
},
|
||||
{
|
||||
'name': 'is_ready',
|
||||
'type': 'bool',
|
||||
'has_default': true,
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
final code = generateClass(classContract);
|
||||
expect(code, contains('TestClass({'));
|
||||
expect(code, contains('required String name,'));
|
||||
expect(code, contains('bool? isReady,'));
|
||||
expect(code, contains('_name = name;'));
|
||||
expect(code, contains('_isReady = isReady ?? false;'));
|
||||
});
|
||||
|
||||
test('handles async methods correctly', () {
|
||||
final classContract = {
|
||||
'name': 'TestClass',
|
||||
'docstring': 'Test class.',
|
||||
'methods': [
|
||||
{
|
||||
'name': 'process',
|
||||
'return_type': 'String',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'input',
|
||||
'type': 'String',
|
||||
'is_optional': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Process input.',
|
||||
'is_async': true,
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
final code = generateClass(classContract);
|
||||
expect(code, contains('Future<String> process(String input) async {'));
|
||||
expect(code, contains('/// Process input.'));
|
||||
});
|
||||
|
||||
test('initializes properties in constructor', () {
|
||||
final classContract = {
|
||||
'name': 'TestClass',
|
||||
'docstring': 'Test class.',
|
||||
'properties': [
|
||||
{
|
||||
'name': 'name',
|
||||
'type': 'String',
|
||||
'has_default': false,
|
||||
},
|
||||
{
|
||||
'name': 'is_ready',
|
||||
'type': 'bool',
|
||||
'has_default': true,
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
final code = generateClass(classContract);
|
||||
expect(code, contains('late String _name;'));
|
||||
expect(code, contains('late bool _isReady;'));
|
||||
expect(code, contains('_name = name;'));
|
||||
expect(code, contains('_isReady = isReady ?? false;'));
|
||||
});
|
||||
});
|
||||
}
|
181
helpers/tools/converter/test/extractors/php_extractor_test.dart
Normal file
181
helpers/tools/converter/test/extractors/php_extractor_test.dart
Normal file
|
@ -0,0 +1,181 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../../lib/src/extractors/php_extractor.dart';
|
||||
|
||||
void main() {
|
||||
group('PhpExtractor', () {
|
||||
final extractor = PhpExtractor();
|
||||
|
||||
test('extracts class comment', () {
|
||||
const phpCode = '''
|
||||
/**
|
||||
* User entity class.
|
||||
* Represents a user in the system.
|
||||
*/
|
||||
class User {
|
||||
}
|
||||
''';
|
||||
final comment = extractor.extractClassComment(phpCode);
|
||||
expect(comment, contains('User entity class'));
|
||||
expect(comment, contains('Represents a user in the system'));
|
||||
});
|
||||
|
||||
test('extracts dependencies', () {
|
||||
const phpCode = '''
|
||||
use App\\Models\\User;
|
||||
use Illuminate\\Support\\Str as StringHelper;
|
||||
use App\\Interfaces\\UserInterface;
|
||||
''';
|
||||
final deps = extractor.extractDependencies(phpCode);
|
||||
expect(deps, hasLength(3));
|
||||
expect(deps[0]['name'], equals('User'));
|
||||
expect(deps[1]['name'], equals('StringHelper'));
|
||||
expect(deps[2]['name'], equals('UserInterface'));
|
||||
expect(deps[1]['source'], equals('Illuminate\\Support\\Str'));
|
||||
});
|
||||
|
||||
test('extracts properties', () {
|
||||
const phpCode = '''
|
||||
class User {
|
||||
/**
|
||||
* The user's name.
|
||||
*/
|
||||
private string \$name;
|
||||
|
||||
/**
|
||||
* The user's email.
|
||||
*/
|
||||
protected string \$email;
|
||||
|
||||
/**
|
||||
* Is the user active?
|
||||
*/
|
||||
public bool \$isActive = false;
|
||||
}
|
||||
''';
|
||||
final props = extractor.extractProperties(phpCode);
|
||||
expect(props, hasLength(3));
|
||||
|
||||
expect(props[0]['name'], equals('name'));
|
||||
expect(props[0]['visibility'], equals('private'));
|
||||
expect(props[0]['comment'], contains("The user's name"));
|
||||
|
||||
expect(props[1]['name'], equals('email'));
|
||||
expect(props[1]['visibility'], equals('protected'));
|
||||
|
||||
expect(props[2]['name'], equals('isActive'));
|
||||
expect(props[2]['visibility'], equals('public'));
|
||||
});
|
||||
|
||||
test('extracts methods', () {
|
||||
const phpCode = '''
|
||||
class User {
|
||||
/**
|
||||
* Get the user's full name.
|
||||
* @param string \$title Optional title
|
||||
* @return string
|
||||
*/
|
||||
public function getFullName(string \$title = '') {
|
||||
return \$title . ' ' . \$this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user's email address.
|
||||
*/
|
||||
protected function setEmail(string \$email) {
|
||||
\$this->email = \$email;
|
||||
}
|
||||
}
|
||||
''';
|
||||
final methods = extractor.extractMethods(phpCode);
|
||||
expect(methods, hasLength(2));
|
||||
|
||||
expect(methods[0]['name'], equals('getFullName'));
|
||||
expect(methods[0]['visibility'], equals('public'));
|
||||
expect(methods[0]['parameters'], hasLength(1));
|
||||
expect(methods[0]['parameters'][0]['name'], equals('title'));
|
||||
expect(methods[0]['parameters'][0]['default'], equals("''"));
|
||||
expect(methods[0]['comment'], contains("Get the user's full name"));
|
||||
|
||||
expect(methods[1]['name'], equals('setEmail'));
|
||||
expect(methods[1]['visibility'], equals('protected'));
|
||||
expect(methods[1]['parameters'], hasLength(1));
|
||||
expect(methods[1]['parameters'][0]['name'], equals('email'));
|
||||
});
|
||||
|
||||
test('extracts interfaces', () {
|
||||
const phpCode = '''
|
||||
class User implements UserInterface, Authenticatable {
|
||||
}
|
||||
''';
|
||||
final interfaces = extractor.extractInterfaces(phpCode);
|
||||
expect(interfaces, hasLength(2));
|
||||
expect(interfaces[0], equals('UserInterface'));
|
||||
expect(interfaces[1], equals('Authenticatable'));
|
||||
});
|
||||
|
||||
test('extracts traits', () {
|
||||
const phpCode = '''
|
||||
class User {
|
||||
use HasFactory, Notifiable;
|
||||
}
|
||||
''';
|
||||
final traits = extractor.extractTraits(phpCode);
|
||||
expect(traits, hasLength(2));
|
||||
expect(traits[0], equals('HasFactory'));
|
||||
expect(traits[1], equals('Notifiable'));
|
||||
});
|
||||
|
||||
test('generates valid YAML output', () {
|
||||
const phpCode = '''
|
||||
/**
|
||||
* User entity class.
|
||||
*/
|
||||
class User implements UserInterface {
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The user's name.
|
||||
*/
|
||||
private string \$name;
|
||||
|
||||
/**
|
||||
* Get the user's name.
|
||||
*/
|
||||
public function getName(): string {
|
||||
return \$this->name;
|
||||
}
|
||||
}
|
||||
''';
|
||||
final contract = {
|
||||
'name': 'User',
|
||||
'class_comment': extractor.extractClassComment(phpCode),
|
||||
'dependencies': extractor.extractDependencies(phpCode),
|
||||
'properties': extractor.extractProperties(phpCode),
|
||||
'methods': extractor.extractMethods(phpCode),
|
||||
'traits': extractor.extractTraits(phpCode),
|
||||
'interfaces': extractor.extractInterfaces(phpCode),
|
||||
};
|
||||
|
||||
final yaml = extractor.convertToYaml(contract);
|
||||
|
||||
// Check required sections
|
||||
expect(yaml, contains('documentation:'));
|
||||
expect(yaml, contains('properties:'));
|
||||
expect(yaml, contains('methods:'));
|
||||
expect(yaml, contains('interfaces:'));
|
||||
|
||||
// Check content
|
||||
expect(yaml, contains('User entity class'));
|
||||
expect(yaml, contains('name: name'));
|
||||
expect(yaml, contains('visibility: private'));
|
||||
expect(yaml, contains('name: getName'));
|
||||
expect(yaml, contains('visibility: public'));
|
||||
expect(yaml, contains('UserInterface'));
|
||||
|
||||
// Verify formatting
|
||||
expect(yaml, isNot(contains('class User')));
|
||||
expect(yaml, isNot(contains('function')));
|
||||
expect(yaml, isNot(contains('private string')));
|
||||
});
|
||||
});
|
||||
}
|
112
helpers/tools/converter/test/fixtures/contracts.yaml
vendored
Normal file
112
helpers/tools/converter/test/fixtures/contracts.yaml
vendored
Normal file
|
@ -0,0 +1,112 @@
|
|||
interfaces:
|
||||
-
|
||||
name: "LLMProtocol"
|
||||
bases:
|
||||
- Protocol
|
||||
methods:
|
||||
-
|
||||
name: "generate"
|
||||
arguments:
|
||||
-
|
||||
name: "prompts"
|
||||
type: "List[str]"
|
||||
is_optional: false
|
||||
has_default: false
|
||||
return_type: "List[str]"
|
||||
docstring: "Generate completions for the prompts."
|
||||
decorators:
|
||||
-
|
||||
name: "abstractmethod"
|
||||
is_abstract: true
|
||||
-
|
||||
name: "model_name"
|
||||
arguments:
|
||||
return_type: "str"
|
||||
docstring: "Get the model name."
|
||||
decorators:
|
||||
-
|
||||
name: "property"
|
||||
-
|
||||
name: "abstractmethod"
|
||||
is_abstract: true
|
||||
properties:
|
||||
docstring: "Protocol for language models."
|
||||
decorators:
|
||||
is_interface: true
|
||||
classes:
|
||||
-
|
||||
name: "BaseChain"
|
||||
bases:
|
||||
- ABC
|
||||
methods:
|
||||
-
|
||||
name: "run"
|
||||
arguments:
|
||||
-
|
||||
name: "inputs"
|
||||
type: "dict"
|
||||
is_optional: false
|
||||
has_default: false
|
||||
return_type: "dict"
|
||||
docstring: "Run the chain on the inputs."
|
||||
decorators:
|
||||
-
|
||||
name: "abstractmethod"
|
||||
is_abstract: true
|
||||
-
|
||||
name: "set_memory"
|
||||
arguments:
|
||||
-
|
||||
name: "memory"
|
||||
type: "dict"
|
||||
is_optional: false
|
||||
has_default: false
|
||||
return_type: "None"
|
||||
docstring: "Set the memory for the chain."
|
||||
decorators:
|
||||
is_abstract: false
|
||||
properties:
|
||||
-
|
||||
name: "memory"
|
||||
type: "Optional[dict]"
|
||||
has_default: true
|
||||
-
|
||||
name: "verbose"
|
||||
type: "bool"
|
||||
has_default: true
|
||||
docstring: "Base class for chains."
|
||||
decorators:
|
||||
is_interface: false
|
||||
-
|
||||
name: "SimpleChain"
|
||||
bases:
|
||||
- BaseChain
|
||||
methods:
|
||||
-
|
||||
name: "__init__"
|
||||
arguments:
|
||||
-
|
||||
name: "llm"
|
||||
type: "LLMProtocol"
|
||||
is_optional: false
|
||||
has_default: false
|
||||
return_type: "None"
|
||||
docstring: "Initialize the chain."
|
||||
decorators:
|
||||
is_abstract: false
|
||||
-
|
||||
name: "run"
|
||||
arguments:
|
||||
-
|
||||
name: "inputs"
|
||||
type: "dict"
|
||||
is_optional: false
|
||||
has_default: false
|
||||
return_type: "dict"
|
||||
docstring: "Execute the chain logic."
|
||||
decorators:
|
||||
is_abstract: false
|
||||
properties:
|
||||
docstring: "A simple implementation of a chain."
|
||||
decorators:
|
||||
is_interface: false
|
46
helpers/tools/converter/test/fixtures/sample.py
vendored
Normal file
46
helpers/tools/converter/test/fixtures/sample.py
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
from typing import List, Optional, Protocol
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
class LLMProtocol(Protocol):
|
||||
"""Protocol for language models."""
|
||||
|
||||
@abstractmethod
|
||||
async def generate(self, prompts: List[str], **kwargs) -> List[str]:
|
||||
"""Generate completions for the prompts."""
|
||||
pass
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def model_name(self) -> str:
|
||||
"""Get the model name."""
|
||||
pass
|
||||
|
||||
class BaseChain(ABC):
|
||||
"""Base class for chains."""
|
||||
|
||||
memory: Optional[dict] = None
|
||||
verbose: bool = False
|
||||
|
||||
@abstractmethod
|
||||
async def run(self, inputs: dict) -> dict:
|
||||
"""Run the chain on the inputs."""
|
||||
pass
|
||||
|
||||
def set_memory(self, memory: dict) -> None:
|
||||
"""Set the memory for the chain."""
|
||||
self.memory = memory
|
||||
|
||||
class SimpleChain(BaseChain):
|
||||
"""A simple implementation of a chain."""
|
||||
|
||||
def __init__(self, llm: LLMProtocol):
|
||||
"""Initialize the chain."""
|
||||
self.llm = llm
|
||||
self.history: List[str] = []
|
||||
|
||||
async def run(self, inputs: dict) -> dict:
|
||||
"""Execute the chain logic."""
|
||||
prompt = inputs.get("prompt", "")
|
||||
result = await self.llm.generate([prompt])
|
||||
self.history.append(result[0])
|
||||
return {"output": result[0]}
|
91
helpers/tools/converter/test/python_parser_test.dart
Normal file
91
helpers/tools/converter/test/python_parser_test.dart
Normal file
|
@ -0,0 +1,91 @@
|
|||
import 'dart:io';
|
||||
import 'package:test/test.dart';
|
||||
import '../tools/python_parser.dart';
|
||||
|
||||
void main() {
|
||||
group('PythonParser', () {
|
||||
test('parses interface correctly', () async {
|
||||
final file = File('test/fixtures/sample.py');
|
||||
final classes = await PythonParser.parseFile(file);
|
||||
|
||||
final interface = classes.firstWhere((c) => c.isInterface);
|
||||
expect(interface.name, equals('LLMProtocol'));
|
||||
expect(interface.docstring, equals('Protocol for language models.'));
|
||||
|
||||
// Test generate method
|
||||
final generateMethod =
|
||||
interface.methods.firstWhere((m) => m.name == 'generate');
|
||||
expect(generateMethod.isAsync, isTrue);
|
||||
expect(generateMethod.isAbstract, isTrue);
|
||||
expect(generateMethod.docstring,
|
||||
equals('Generate completions for the prompts.'));
|
||||
expect(generateMethod.parameters.length, equals(1));
|
||||
expect(generateMethod.parameters.first.name, equals('prompts'));
|
||||
expect(generateMethod.parameters.first.type, equals('List[str]'));
|
||||
expect(generateMethod.returnType, equals('List[str]'));
|
||||
|
||||
// Test model_name property
|
||||
final modelNameMethod =
|
||||
interface.methods.firstWhere((m) => m.name == 'model_name');
|
||||
expect(modelNameMethod.isProperty, isTrue);
|
||||
expect(modelNameMethod.isAbstract, isTrue);
|
||||
expect(modelNameMethod.docstring, equals('Get the model name.'));
|
||||
expect(modelNameMethod.parameters.isEmpty, isTrue);
|
||||
expect(modelNameMethod.returnType, equals('str'));
|
||||
});
|
||||
|
||||
test('parses abstract class correctly', () async {
|
||||
final file = File('test/fixtures/sample.py');
|
||||
final classes = await PythonParser.parseFile(file);
|
||||
|
||||
final abstractClass = classes.firstWhere((c) => c.name == 'BaseChain');
|
||||
expect(abstractClass.docstring, equals('Base class for chains.'));
|
||||
|
||||
// Test properties
|
||||
expect(abstractClass.properties.length, equals(2));
|
||||
final memoryProp =
|
||||
abstractClass.properties.firstWhere((p) => p.name == 'memory');
|
||||
expect(memoryProp.type, equals('Optional[dict]'));
|
||||
expect(memoryProp.hasDefault, isTrue);
|
||||
|
||||
// Test run method
|
||||
final runMethod =
|
||||
abstractClass.methods.firstWhere((m) => m.name == 'run');
|
||||
expect(runMethod.isAsync, isTrue);
|
||||
expect(runMethod.isAbstract, isTrue);
|
||||
expect(runMethod.docstring, equals('Run the chain on the inputs.'));
|
||||
expect(runMethod.parameters.length, equals(1));
|
||||
expect(runMethod.parameters.first.name, equals('inputs'));
|
||||
expect(runMethod.parameters.first.type, equals('dict'));
|
||||
expect(runMethod.returnType, equals('dict'));
|
||||
});
|
||||
|
||||
test('parses concrete class correctly', () async {
|
||||
final file = File('test/fixtures/sample.py');
|
||||
final classes = await PythonParser.parseFile(file);
|
||||
|
||||
final concreteClass = classes.firstWhere((c) => c.name == 'SimpleChain');
|
||||
expect(concreteClass.docstring,
|
||||
equals('A simple implementation of a chain.'));
|
||||
|
||||
// Test constructor
|
||||
final constructor =
|
||||
concreteClass.methods.firstWhere((m) => m.name == '__init__');
|
||||
expect(constructor.docstring, equals('Initialize the chain.'));
|
||||
expect(constructor.parameters.length, equals(1));
|
||||
expect(constructor.parameters.first.name, equals('llm'));
|
||||
expect(constructor.parameters.first.type, equals('LLMProtocol'));
|
||||
|
||||
// Test run method
|
||||
final runMethod =
|
||||
concreteClass.methods.firstWhere((m) => m.name == 'run');
|
||||
expect(runMethod.isAsync, isTrue);
|
||||
expect(runMethod.isAbstract, isFalse);
|
||||
expect(runMethod.docstring, equals('Execute the chain logic.'));
|
||||
expect(runMethod.parameters.length, equals(1));
|
||||
expect(runMethod.parameters.first.name, equals('inputs'));
|
||||
expect(runMethod.parameters.first.type, equals('dict'));
|
||||
expect(runMethod.returnType, equals('dict'));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../../lib/src/utils/class_generator_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('ClassGeneratorUtils', () {
|
||||
test('generates constructor with property initialization', () {
|
||||
final properties = [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'String',
|
||||
'has_default': false,
|
||||
},
|
||||
{
|
||||
'name': 'is_ready',
|
||||
'type': 'bool',
|
||||
'has_default': true,
|
||||
}
|
||||
];
|
||||
|
||||
final code =
|
||||
ClassGeneratorUtils.generateConstructor('TestClass', properties);
|
||||
expect(code, contains('TestClass({'));
|
||||
expect(code, contains('required String modelName,'));
|
||||
expect(code, contains('bool? isReady,'));
|
||||
expect(code, contains('_modelName = modelName;'));
|
||||
expect(code, contains('_isReady = isReady ?? false;'));
|
||||
});
|
||||
|
||||
test('generates properties with getters and setters', () {
|
||||
final properties = [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'String',
|
||||
'is_readonly': true,
|
||||
},
|
||||
{
|
||||
'name': 'is_ready',
|
||||
'type': 'bool',
|
||||
'is_readonly': false,
|
||||
}
|
||||
];
|
||||
|
||||
final code = ClassGeneratorUtils.generateProperties(properties);
|
||||
expect(code, contains('late String _modelName;'));
|
||||
expect(code, contains('String get modelName => _modelName;'));
|
||||
expect(code, contains('late bool _isReady;'));
|
||||
expect(code, contains('bool get isReady => _isReady;'));
|
||||
expect(code, contains('set isReady(bool value)'));
|
||||
expect(code, isNot(contains('set modelName')));
|
||||
});
|
||||
|
||||
test('generates async method correctly', () {
|
||||
final method = {
|
||||
'name': 'process_input',
|
||||
'return_type': 'String',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'input',
|
||||
'type': 'String',
|
||||
'is_optional': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Process the input.',
|
||||
'is_async': true,
|
||||
};
|
||||
|
||||
final code = ClassGeneratorUtils.generateMethod(method);
|
||||
expect(code, contains('Future<String> processInput('));
|
||||
expect(code, contains('String input'));
|
||||
expect(code, contains('async {'));
|
||||
expect(code, contains('/// Process the input.'));
|
||||
});
|
||||
|
||||
test('skips generating constructor method', () {
|
||||
final method = {
|
||||
'name': '__init__',
|
||||
'return_type': 'void',
|
||||
'arguments': [],
|
||||
};
|
||||
|
||||
final code = ClassGeneratorUtils.generateMethod(method);
|
||||
expect(code, isEmpty);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../../lib/src/utils/constructor_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('ConstructorUtils', () {
|
||||
test('generates required parameters for non-default properties', () {
|
||||
final properties = [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'String',
|
||||
'has_default': false,
|
||||
}
|
||||
];
|
||||
|
||||
final params = ConstructorUtils.generateParameters(properties);
|
||||
expect(params, contains('required String model_name'));
|
||||
});
|
||||
|
||||
test('generates optional parameters for default properties', () {
|
||||
final properties = [
|
||||
{
|
||||
'name': 'is_ready',
|
||||
'type': 'bool',
|
||||
'has_default': true,
|
||||
}
|
||||
];
|
||||
|
||||
final params = ConstructorUtils.generateParameters(properties);
|
||||
expect(params, contains('bool? is_ready'));
|
||||
});
|
||||
|
||||
test('generates property initializers', () {
|
||||
final properties = [
|
||||
{
|
||||
'name': 'model_name',
|
||||
'type': 'String',
|
||||
'has_default': false,
|
||||
},
|
||||
{
|
||||
'name': 'is_ready',
|
||||
'type': 'bool',
|
||||
'has_default': true,
|
||||
}
|
||||
];
|
||||
|
||||
final inits = ConstructorUtils.generateInitializers(properties);
|
||||
expect(inits, contains('_model_name = model_name;'));
|
||||
expect(inits, contains('_is_ready = is_ready ?? false;'));
|
||||
});
|
||||
|
||||
test('identifies constructor methods', () {
|
||||
final initMethod = {
|
||||
'name': '__init__',
|
||||
'return_type': 'None',
|
||||
'arguments': [],
|
||||
};
|
||||
|
||||
final regularMethod = {
|
||||
'name': 'process',
|
||||
'return_type': 'String',
|
||||
'arguments': [],
|
||||
};
|
||||
|
||||
expect(ConstructorUtils.isConstructor(initMethod), isTrue);
|
||||
expect(ConstructorUtils.isConstructor(regularMethod), isFalse);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../../lib/src/utils/class_generator_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('Interface Implementation Generation', () {
|
||||
test('generates required BaseChain implementations', () {
|
||||
final bases = ['BaseChain'];
|
||||
final classContract = {
|
||||
'name': 'SimpleChain',
|
||||
'docstring': 'A simple implementation of a chain.',
|
||||
'methods': [
|
||||
{
|
||||
'name': 'run',
|
||||
'return_type': 'dict',
|
||||
'arguments': [
|
||||
{
|
||||
'name': 'inputs',
|
||||
'type': 'dict',
|
||||
'is_optional': false,
|
||||
}
|
||||
],
|
||||
'docstring': 'Execute the chain logic.',
|
||||
}
|
||||
],
|
||||
};
|
||||
|
||||
final code = ClassGeneratorUtils.generateRequiredImplementations(
|
||||
bases, classContract);
|
||||
|
||||
// Should include memory property
|
||||
expect(code, contains('late Map<String, dynamic>? _memory;'));
|
||||
expect(code, contains('Map<String, dynamic>? get memory => _memory;'));
|
||||
|
||||
// Should include verbose property
|
||||
expect(code, contains('late bool _verbose;'));
|
||||
expect(code, contains('bool get verbose => _verbose;'));
|
||||
|
||||
// Should include constructor with required properties
|
||||
expect(code, contains('SimpleChain({'));
|
||||
expect(code, contains('Map<String, dynamic>? memory,'));
|
||||
expect(code, contains('bool? verbose,'));
|
||||
expect(code, contains('_memory = memory ?? {};'));
|
||||
expect(code, contains('_verbose = verbose ?? false;'));
|
||||
|
||||
// Should include required method implementations
|
||||
expect(code, contains('@override'));
|
||||
expect(code, contains('void setMemory(Map<String, dynamic> memory)'));
|
||||
});
|
||||
|
||||
test('handles multiple interface implementations', () {
|
||||
final bases = ['BaseChain', 'Serializable'];
|
||||
final classContract = {
|
||||
'name': 'SimpleChain',
|
||||
'docstring': 'A simple implementation of a chain.',
|
||||
'methods': [],
|
||||
};
|
||||
|
||||
final code = ClassGeneratorUtils.generateRequiredImplementations(
|
||||
bases, classContract);
|
||||
|
||||
// Should include BaseChain implementations
|
||||
expect(code, contains('Map<String, dynamic>? get memory'));
|
||||
expect(code, contains('bool get verbose'));
|
||||
|
||||
// Should include constructor with all required properties
|
||||
expect(code, contains('SimpleChain({'));
|
||||
expect(code, contains('Map<String, dynamic>? memory,'));
|
||||
expect(code, contains('bool? verbose,'));
|
||||
});
|
||||
|
||||
test('handles no interface implementations', () {
|
||||
final bases = <String>[];
|
||||
final classContract = {
|
||||
'name': 'SimpleClass',
|
||||
'docstring': 'A simple class.',
|
||||
'methods': [],
|
||||
};
|
||||
|
||||
final code = ClassGeneratorUtils.generateRequiredImplementations(
|
||||
bases, classContract);
|
||||
expect(code, isEmpty);
|
||||
});
|
||||
});
|
||||
}
|
37
helpers/tools/converter/test/utils/name_utils_test.dart
Normal file
37
helpers/tools/converter/test/utils/name_utils_test.dart
Normal file
|
@ -0,0 +1,37 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../../lib/src/utils/name_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('NameUtils', () {
|
||||
test('converts snake_case to camelCase', () {
|
||||
expect(NameUtils.toDartName('hello_world'), equals('helloWorld'));
|
||||
expect(NameUtils.toDartName('get_model_name'), equals('getModelName'));
|
||||
expect(NameUtils.toDartName('set_memory'), equals('setMemory'));
|
||||
});
|
||||
|
||||
test('handles single word correctly', () {
|
||||
expect(NameUtils.toDartName('hello'), equals('hello'));
|
||||
expect(NameUtils.toDartName('test'), equals('test'));
|
||||
});
|
||||
|
||||
test('preserves existing camelCase', () {
|
||||
expect(NameUtils.toDartName('helloWorld'), equals('helloWorld'));
|
||||
expect(NameUtils.toDartName('getModelName'), equals('getModelName'));
|
||||
});
|
||||
|
||||
test('handles empty string', () {
|
||||
expect(NameUtils.toDartName(''), equals(''));
|
||||
});
|
||||
|
||||
test('handles special method names', () {
|
||||
expect(NameUtils.toDartName('__init__'), equals('new'));
|
||||
expect(NameUtils.toDartName('__str__'), equals('str'));
|
||||
expect(NameUtils.toDartName('__repr__'), equals('repr'));
|
||||
});
|
||||
|
||||
test('handles consecutive underscores', () {
|
||||
expect(NameUtils.toDartName('hello__world'), equals('helloWorld'));
|
||||
expect(NameUtils.toDartName('test___name'), equals('testName'));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../../lib/src/utils/type_conversion_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('TypeConversionUtils', () {
|
||||
group('pythonToDartType', () {
|
||||
test('converts basic Python types to Dart types', () {
|
||||
expect(TypeConversionUtils.pythonToDartType('str'), equals('String'));
|
||||
expect(TypeConversionUtils.pythonToDartType('int'), equals('int'));
|
||||
expect(TypeConversionUtils.pythonToDartType('bool'), equals('bool'));
|
||||
expect(TypeConversionUtils.pythonToDartType('None'), equals('void'));
|
||||
expect(TypeConversionUtils.pythonToDartType('dict'),
|
||||
equals('Map<String, dynamic>'));
|
||||
});
|
||||
|
||||
test('converts List types correctly', () {
|
||||
expect(TypeConversionUtils.pythonToDartType('List[str]'),
|
||||
equals('List<String>'));
|
||||
expect(TypeConversionUtils.pythonToDartType('List[int]'),
|
||||
equals('List<int>'));
|
||||
expect(TypeConversionUtils.pythonToDartType('List[dict]'),
|
||||
equals('List<Map<String, dynamic>>'));
|
||||
});
|
||||
|
||||
test('converts Dict types correctly', () {
|
||||
expect(TypeConversionUtils.pythonToDartType('Dict[str, Any]'),
|
||||
equals('Map<String, dynamic>'));
|
||||
expect(TypeConversionUtils.pythonToDartType('Dict[str, int]'),
|
||||
equals('Map<String, int>'));
|
||||
expect(TypeConversionUtils.pythonToDartType('dict'),
|
||||
equals('Map<String, dynamic>'));
|
||||
});
|
||||
|
||||
test('converts Optional types correctly', () {
|
||||
expect(TypeConversionUtils.pythonToDartType('Optional[str]'),
|
||||
equals('String?'));
|
||||
expect(TypeConversionUtils.pythonToDartType('Optional[int]'),
|
||||
equals('int?'));
|
||||
expect(TypeConversionUtils.pythonToDartType('Optional[dict]'),
|
||||
equals('Map<String, dynamic>?'));
|
||||
expect(TypeConversionUtils.pythonToDartType('Optional[List[str]]'),
|
||||
equals('List<String>?'));
|
||||
});
|
||||
|
||||
test('handles nested generic types', () {
|
||||
expect(TypeConversionUtils.pythonToDartType('List[Optional[str]]'),
|
||||
equals('List<String?>'));
|
||||
expect(TypeConversionUtils.pythonToDartType('Dict[str, List[int]]'),
|
||||
equals('Map<String, List<int>>'));
|
||||
expect(
|
||||
TypeConversionUtils.pythonToDartType(
|
||||
'Optional[Dict[str, List[int]]]'),
|
||||
equals('Map<String, List<int>>?'));
|
||||
});
|
||||
});
|
||||
|
||||
group('getDefaultValue', () {
|
||||
test('returns correct default values for basic types', () {
|
||||
expect(TypeConversionUtils.getDefaultValue('bool'), equals('false'));
|
||||
expect(TypeConversionUtils.getDefaultValue('int'), equals('0'));
|
||||
expect(TypeConversionUtils.getDefaultValue('double'), equals('0.0'));
|
||||
expect(TypeConversionUtils.getDefaultValue('String'), equals("''"));
|
||||
});
|
||||
|
||||
test('returns correct default values for nullable types', () {
|
||||
expect(TypeConversionUtils.getDefaultValue('bool?'), equals('false'));
|
||||
expect(TypeConversionUtils.getDefaultValue('int?'), equals('0'));
|
||||
expect(TypeConversionUtils.getDefaultValue('double?'), equals('0.0'));
|
||||
expect(TypeConversionUtils.getDefaultValue('String?'), equals("''"));
|
||||
});
|
||||
|
||||
test('returns correct default values for collection types', () {
|
||||
expect(TypeConversionUtils.getDefaultValue('List'), equals('[]'));
|
||||
expect(
|
||||
TypeConversionUtils.getDefaultValue('List<String>'), equals('[]'));
|
||||
expect(TypeConversionUtils.getDefaultValue('Map<String, dynamic>'),
|
||||
equals('{}'));
|
||||
expect(TypeConversionUtils.getDefaultValue('Map<String, int>'),
|
||||
equals('{}'));
|
||||
});
|
||||
|
||||
test('returns null for unknown types', () {
|
||||
expect(
|
||||
TypeConversionUtils.getDefaultValue('CustomType'), equals('null'));
|
||||
expect(TypeConversionUtils.getDefaultValue('UnknownType?'),
|
||||
equals('null'));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
45
helpers/tools/converter/test/utils/type_utils_test.dart
Normal file
45
helpers/tools/converter/test/utils/type_utils_test.dart
Normal file
|
@ -0,0 +1,45 @@
|
|||
import 'package:test/test.dart';
|
||||
import '../../lib/src/utils/type_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('TypeUtils', () {
|
||||
test('castToMapList handles null input', () {
|
||||
expect(TypeUtils.castToMapList(null), isEmpty);
|
||||
});
|
||||
|
||||
test('castToMapList converts List<dynamic> to List<Map<String, dynamic>>',
|
||||
() {
|
||||
final input = [
|
||||
{'name': 'test', 'value': 1},
|
||||
{'type': 'string', 'optional': true}
|
||||
];
|
||||
final result = TypeUtils.castToMapList(input);
|
||||
expect(result, isA<List<Map<String, dynamic>>>());
|
||||
expect(result.first['name'], equals('test'));
|
||||
expect(result.last['type'], equals('string'));
|
||||
});
|
||||
|
||||
test('castToMap handles null input', () {
|
||||
expect(TypeUtils.castToMap(null), isEmpty);
|
||||
});
|
||||
|
||||
test('castToMap converts dynamic to Map<String, dynamic>', () {
|
||||
final input = {'name': 'test', 'value': 1};
|
||||
final result = TypeUtils.castToMap(input);
|
||||
expect(result, isA<Map<String, dynamic>>());
|
||||
expect(result['name'], equals('test'));
|
||||
expect(result['value'], equals(1));
|
||||
});
|
||||
|
||||
test('castToStringList handles null input', () {
|
||||
expect(TypeUtils.castToStringList(null), isEmpty);
|
||||
});
|
||||
|
||||
test('castToStringList converts List<dynamic> to List<String>', () {
|
||||
final input = ['test', 1, true];
|
||||
final result = TypeUtils.castToStringList(input);
|
||||
expect(result, isA<List<String>>());
|
||||
expect(result, equals(['test', '1', 'true']));
|
||||
});
|
||||
});
|
||||
}
|
90
helpers/tools/converter/test/yaml_handling_test.dart
Normal file
90
helpers/tools/converter/test/yaml_handling_test.dart
Normal file
|
@ -0,0 +1,90 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
import '../lib/src/utils/yaml_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('YamlUtils', () {
|
||||
test('converts simple YAML to Map correctly', () {
|
||||
final yamlStr = '''
|
||||
interfaces:
|
||||
- name: TestInterface
|
||||
methods:
|
||||
- name: testMethod
|
||||
arguments:
|
||||
- name: arg1
|
||||
type: str
|
||||
return_type: str
|
||||
''';
|
||||
|
||||
final yaml = loadYaml(yamlStr) as YamlMap;
|
||||
final map = YamlUtils.convertYamlToMap(yaml);
|
||||
|
||||
expect(map, isA<Map<String, dynamic>>());
|
||||
expect(map['interfaces'], isA<List>());
|
||||
expect(map['interfaces'][0]['name'], equals('TestInterface'));
|
||||
expect(map['interfaces'][0]['methods'][0]['arguments'][0]['type'],
|
||||
equals('str'));
|
||||
});
|
||||
|
||||
test('handles nested YAML structures', () {
|
||||
final yamlStr = '''
|
||||
interfaces:
|
||||
- name: TestInterface
|
||||
properties:
|
||||
- name: prop1
|
||||
type: List[str]
|
||||
has_default: true
|
||||
methods:
|
||||
- name: testMethod
|
||||
arguments:
|
||||
- name: arg1
|
||||
type: Dict[str, Any]
|
||||
is_optional: true
|
||||
return_type: Optional[int]
|
||||
''';
|
||||
|
||||
final yaml = loadYaml(yamlStr) as YamlMap;
|
||||
final map = YamlUtils.convertYamlToMap(yaml);
|
||||
|
||||
expect(
|
||||
map['interfaces'][0]['properties'][0]['type'], equals('List[str]'));
|
||||
expect(map['interfaces'][0]['methods'][0]['arguments'][0]['type'],
|
||||
equals('Dict[str, Any]'));
|
||||
});
|
||||
|
||||
test('converts actual contract YAML correctly', () {
|
||||
final yamlStr = '''
|
||||
interfaces:
|
||||
- name: LLMProtocol
|
||||
bases:
|
||||
- Protocol
|
||||
methods:
|
||||
- name: generate
|
||||
arguments:
|
||||
- name: prompts
|
||||
type: List[str]
|
||||
is_optional: false
|
||||
has_default: false
|
||||
return_type: List[str]
|
||||
docstring: Generate completions for the prompts.
|
||||
decorators:
|
||||
- name: abstractmethod
|
||||
is_abstract: true
|
||||
properties: []
|
||||
docstring: Protocol for language models.
|
||||
is_interface: true
|
||||
''';
|
||||
|
||||
final yaml = loadYaml(yamlStr) as YamlMap;
|
||||
final map = YamlUtils.convertYamlToMap(yaml);
|
||||
|
||||
expect(map['interfaces'][0]['name'], equals('LLMProtocol'));
|
||||
expect(map['interfaces'][0]['bases'][0], equals('Protocol'));
|
||||
expect(map['interfaces'][0]['methods'][0]['name'], equals('generate'));
|
||||
expect(map['interfaces'][0]['methods'][0]['arguments'][0]['type'],
|
||||
equals('List[str]'));
|
||||
expect(map['interfaces'][0]['docstring'],
|
||||
equals('Protocol for language models.'));
|
||||
});
|
||||
});
|
||||
}
|
57
helpers/tools/converter/test/yaml_type_test.dart
Normal file
57
helpers/tools/converter/test/yaml_type_test.dart
Normal file
|
@ -0,0 +1,57 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
import '../lib/src/utils/yaml_utils.dart';
|
||||
import '../lib/src/utils/type_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('YAML Type Casting', () {
|
||||
test('converts YAML data to properly typed maps and lists', () {
|
||||
final yamlStr = '''
|
||||
classes:
|
||||
- name: TestClass
|
||||
properties:
|
||||
- name: model_name
|
||||
type: String
|
||||
has_default: false
|
||||
- name: is_ready
|
||||
type: bool
|
||||
has_default: true
|
||||
methods:
|
||||
- name: process
|
||||
return_type: String
|
||||
arguments:
|
||||
- name: input
|
||||
type: String
|
||||
is_optional: false
|
||||
''';
|
||||
|
||||
final yamlDoc = loadYaml(yamlStr) as YamlMap;
|
||||
final data = YamlUtils.convertYamlToMap(yamlDoc);
|
||||
|
||||
final classes = data['classes'] as List;
|
||||
final firstClass = classes.first as Map<String, dynamic>;
|
||||
|
||||
// Test properties casting
|
||||
final properties =
|
||||
TypeUtils.castToMapList(firstClass['properties'] as List?);
|
||||
expect(properties, isA<List<Map<String, dynamic>>>());
|
||||
expect(properties.first['name'], equals('model_name'));
|
||||
expect(properties.first['type'], equals('String'));
|
||||
expect(properties.first['has_default'], isFalse);
|
||||
|
||||
// Test methods casting
|
||||
final methods = TypeUtils.castToMapList(firstClass['methods'] as List?);
|
||||
expect(methods, isA<List<Map<String, dynamic>>>());
|
||||
expect(methods.first['name'], equals('process'));
|
||||
expect(methods.first['return_type'], equals('String'));
|
||||
|
||||
// Test nested arguments casting
|
||||
final arguments =
|
||||
TypeUtils.castToMapList(methods.first['arguments'] as List?);
|
||||
expect(arguments, isA<List<Map<String, dynamic>>>());
|
||||
expect(arguments.first['name'], equals('input'));
|
||||
expect(arguments.first['type'], equals('String'));
|
||||
expect(arguments.first['is_optional'], isFalse);
|
||||
});
|
||||
});
|
||||
}
|
255
helpers/tools/converter/tools/extract_contracts.dart
Normal file
255
helpers/tools/converter/tools/extract_contracts.dart
Normal file
|
@ -0,0 +1,255 @@
|
|||
import 'dart:io';
|
||||
import 'package:args/args.dart';
|
||||
import 'python_parser.dart';
|
||||
|
||||
/// Represents a Python class or interface contract
|
||||
class ContractDefinition {
|
||||
final String name;
|
||||
final List<String> bases;
|
||||
final List<MethodDefinition> methods;
|
||||
final List<PropertyDefinition> properties;
|
||||
final String? docstring;
|
||||
final List<Map<String, dynamic>> decorators;
|
||||
final bool isInterface;
|
||||
|
||||
ContractDefinition({
|
||||
required this.name,
|
||||
required this.bases,
|
||||
required this.methods,
|
||||
required this.properties,
|
||||
this.docstring,
|
||||
required this.decorators,
|
||||
required this.isInterface,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'bases': bases,
|
||||
'methods': methods.map((m) => m.toJson()).toList(),
|
||||
'properties': properties.map((p) => p.toJson()).toList(),
|
||||
if (docstring != null) 'docstring': docstring,
|
||||
'decorators': decorators,
|
||||
'is_interface': isInterface,
|
||||
};
|
||||
|
||||
/// Create ContractDefinition from PythonClass
|
||||
factory ContractDefinition.fromPythonClass(PythonClass pythonClass) {
|
||||
return ContractDefinition(
|
||||
name: pythonClass.name,
|
||||
bases: pythonClass.bases,
|
||||
methods: pythonClass.methods
|
||||
.map((m) => MethodDefinition(
|
||||
name: m.name,
|
||||
arguments: m.parameters
|
||||
.map((p) => ArgumentDefinition(
|
||||
name: p.name,
|
||||
type: p.type,
|
||||
isOptional: p.isOptional,
|
||||
hasDefault: p.hasDefault,
|
||||
))
|
||||
.toList(),
|
||||
returnType: m.returnType,
|
||||
docstring: m.docstring,
|
||||
decorators: m.decorators.map((d) => {'name': d}).toList(),
|
||||
isAbstract: m.isAbstract,
|
||||
))
|
||||
.toList(),
|
||||
properties: pythonClass.properties
|
||||
.map((p) => PropertyDefinition(
|
||||
name: p.name,
|
||||
type: p.type,
|
||||
hasDefault: p.hasDefault,
|
||||
))
|
||||
.toList(),
|
||||
docstring: pythonClass.docstring,
|
||||
decorators: pythonClass.decorators.map((d) => {'name': d}).toList(),
|
||||
isInterface: pythonClass.isInterface,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a method in a contract
|
||||
class MethodDefinition {
|
||||
final String name;
|
||||
final List<ArgumentDefinition> arguments;
|
||||
final String returnType;
|
||||
final String? docstring;
|
||||
final List<Map<String, dynamic>> decorators;
|
||||
final bool isAbstract;
|
||||
|
||||
MethodDefinition({
|
||||
required this.name,
|
||||
required this.arguments,
|
||||
required this.returnType,
|
||||
this.docstring,
|
||||
required this.decorators,
|
||||
required this.isAbstract,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'arguments': arguments.map((a) => a.toJson()).toList(),
|
||||
'return_type': returnType,
|
||||
if (docstring != null) 'docstring': docstring,
|
||||
'decorators': decorators,
|
||||
'is_abstract': isAbstract,
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents a method argument
|
||||
class ArgumentDefinition {
|
||||
final String name;
|
||||
final String type;
|
||||
final bool isOptional;
|
||||
final bool hasDefault;
|
||||
|
||||
ArgumentDefinition({
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.isOptional,
|
||||
required this.hasDefault,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'type': type,
|
||||
'is_optional': isOptional,
|
||||
'has_default': hasDefault,
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents a class property
|
||||
class PropertyDefinition {
|
||||
final String name;
|
||||
final String type;
|
||||
final bool hasDefault;
|
||||
|
||||
PropertyDefinition({
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.hasDefault,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'type': type,
|
||||
'has_default': hasDefault,
|
||||
};
|
||||
}
|
||||
|
||||
/// Main contract extractor class
|
||||
class ContractExtractor {
|
||||
final List<ContractDefinition> interfaces = [];
|
||||
final List<ContractDefinition> classes = [];
|
||||
|
||||
/// Process a Python source file and extract contracts
|
||||
Future<void> processFile(File file) async {
|
||||
try {
|
||||
final pythonClasses = await PythonParser.parseFile(file);
|
||||
|
||||
for (final pythonClass in pythonClasses) {
|
||||
final contract = ContractDefinition.fromPythonClass(pythonClass);
|
||||
if (pythonClass.isInterface) {
|
||||
interfaces.add(contract);
|
||||
} else {
|
||||
classes.add(contract);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('Error processing file ${file.path}: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Process all Python files in a directory recursively
|
||||
Future<void> processDirectory(String dirPath) async {
|
||||
final dir = Directory(dirPath);
|
||||
await for (final entity in dir.list(recursive: true)) {
|
||||
if (entity is File && entity.path.endsWith('.py')) {
|
||||
await processFile(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate YAML output
|
||||
Future<void> generateYaml(String outputPath) async {
|
||||
final output = {
|
||||
'interfaces': interfaces.map((i) => i.toJson()).toList(),
|
||||
'classes': classes.map((c) => c.toJson()).toList(),
|
||||
};
|
||||
|
||||
final yamlString = mapToYaml(output);
|
||||
final file = File(outputPath);
|
||||
await file.writeAsString(yamlString);
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a Map to YAML string with proper formatting
|
||||
String mapToYaml(Map<String, dynamic> map, {int indent = 0}) {
|
||||
final buffer = StringBuffer();
|
||||
final indentStr = ' ' * indent;
|
||||
|
||||
map.forEach((key, value) {
|
||||
if (value is Map) {
|
||||
buffer.writeln('$indentStr$key:');
|
||||
buffer
|
||||
.write(mapToYaml(value as Map<String, dynamic>, indent: indent + 2));
|
||||
} else if (value is List) {
|
||||
buffer.writeln('$indentStr$key:');
|
||||
for (var item in value) {
|
||||
if (item is Map) {
|
||||
buffer.writeln('$indentStr- ');
|
||||
buffer.write(
|
||||
mapToYaml(item as Map<String, dynamic>, indent: indent + 4));
|
||||
} else {
|
||||
buffer.writeln('$indentStr- $item');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value == null) {
|
||||
buffer.writeln('$indentStr$key: null');
|
||||
} else if (value is String) {
|
||||
// Handle multi-line strings
|
||||
if (value.contains('\n')) {
|
||||
buffer.writeln('$indentStr$key: |');
|
||||
value.split('\n').forEach((line) {
|
||||
buffer.writeln('$indentStr $line');
|
||||
});
|
||||
} else {
|
||||
buffer.writeln('$indentStr$key: "$value"');
|
||||
}
|
||||
} else {
|
||||
buffer.writeln('$indentStr$key: $value');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
void main(List<String> arguments) async {
|
||||
final parser = ArgParser()
|
||||
..addOption('source',
|
||||
abbr: 's',
|
||||
help: 'Source directory containing Python LangChain implementation',
|
||||
mandatory: true)
|
||||
..addOption('output',
|
||||
abbr: 'o', help: 'Output YAML file path', mandatory: true);
|
||||
|
||||
try {
|
||||
final results = parser.parse(arguments);
|
||||
final sourceDir = results['source'] as String;
|
||||
final outputFile = results['output'] as String;
|
||||
|
||||
final extractor = ContractExtractor();
|
||||
await extractor.processDirectory(sourceDir);
|
||||
await extractor.generateYaml(outputFile);
|
||||
|
||||
print('Contract extraction completed successfully.');
|
||||
print('Interfaces found: ${extractor.interfaces.length}');
|
||||
print('Classes found: ${extractor.classes.length}');
|
||||
} catch (e) {
|
||||
print('Error: $e');
|
||||
print('Usage: dart extract_contracts.dart --source <dir> --output <file>');
|
||||
exit(1);
|
||||
}
|
||||
}
|
209
helpers/tools/converter/tools/generate_dart_code.dart
Normal file
209
helpers/tools/converter/tools/generate_dart_code.dart
Normal file
|
@ -0,0 +1,209 @@
|
|||
import 'dart:io';
|
||||
import 'package:yaml/yaml.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:args/args.dart';
|
||||
import 'package:converter/src/utils/yaml_utils.dart';
|
||||
import 'package:converter/src/utils/name_utils.dart';
|
||||
import 'package:converter/src/utils/class_generator_utils.dart';
|
||||
import 'package:converter/src/utils/type_utils.dart';
|
||||
import 'package:converter/src/utils/type_conversion_utils.dart';
|
||||
|
||||
/// Generates a Dart interface from a contract
|
||||
String generateInterface(Map<String, dynamic> interface) {
|
||||
final buffer = StringBuffer();
|
||||
final name = interface['name'] as String;
|
||||
final docstring = interface['docstring'] as String?;
|
||||
|
||||
// Add documentation
|
||||
if (docstring != null) {
|
||||
buffer.writeln('/// ${docstring.replaceAll('\n', '\n/// ')}');
|
||||
}
|
||||
|
||||
// Begin interface definition
|
||||
buffer.writeln('abstract class $name {');
|
||||
|
||||
// Generate properties
|
||||
final properties = interface['properties'] as List?;
|
||||
if (properties != null) {
|
||||
for (final prop in properties) {
|
||||
final propName = NameUtils.toDartName(prop['name'] as String);
|
||||
final propType =
|
||||
TypeConversionUtils.pythonToDartType(prop['type'] as String);
|
||||
buffer.writeln(' $propType get $propName;');
|
||||
// Only generate setter if is_readonly is explicitly false
|
||||
final isReadonly = prop['is_readonly'];
|
||||
if (isReadonly != null && !isReadonly) {
|
||||
buffer.writeln(' set $propName($propType value);');
|
||||
}
|
||||
}
|
||||
if (properties.isNotEmpty) buffer.writeln();
|
||||
}
|
||||
|
||||
// Generate methods
|
||||
final methods = interface['methods'] as List?;
|
||||
if (methods != null) {
|
||||
for (final method in methods) {
|
||||
final methodName = NameUtils.toDartName(method['name'] as String);
|
||||
final returnType =
|
||||
TypeConversionUtils.pythonToDartType(method['return_type'] as String);
|
||||
final methodDoc = method['docstring'] as String?;
|
||||
final decorators = TypeUtils.castToMapList(method['decorators'] as List?);
|
||||
final isProperty = decorators.any((d) => d['name'] == 'property');
|
||||
|
||||
if (methodDoc != null) {
|
||||
buffer.writeln(' /// ${methodDoc.replaceAll('\n', '\n /// ')}');
|
||||
}
|
||||
|
||||
if (isProperty) {
|
||||
// Generate as a getter
|
||||
buffer.writeln(' $returnType get $methodName;');
|
||||
} else {
|
||||
// Generate as a method
|
||||
final isAsync = method['is_async'] == true;
|
||||
if (isAsync) {
|
||||
buffer.write(' Future<$returnType> $methodName(');
|
||||
} else {
|
||||
buffer.write(' $returnType $methodName(');
|
||||
}
|
||||
|
||||
// Generate parameters
|
||||
final params = method['arguments'] as List?;
|
||||
if (params != null && params.isNotEmpty) {
|
||||
final paramStrings = <String>[];
|
||||
|
||||
for (final param in params) {
|
||||
final paramName = NameUtils.toDartName(param['name'] as String);
|
||||
final paramType =
|
||||
TypeConversionUtils.pythonToDartType(param['type'] as String);
|
||||
final isOptional = param['is_optional'] == true;
|
||||
|
||||
if (isOptional) {
|
||||
paramStrings.add('[$paramType $paramName]');
|
||||
} else {
|
||||
paramStrings.add('$paramType $paramName');
|
||||
}
|
||||
}
|
||||
|
||||
buffer.write(paramStrings.join(', '));
|
||||
}
|
||||
|
||||
buffer.writeln(');');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer.writeln('}');
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Generates a Dart class implementation from a contract
|
||||
String generateClass(Map<String, dynamic> classContract) {
|
||||
final buffer = StringBuffer();
|
||||
final name = classContract['name'] as String;
|
||||
final bases = TypeUtils.castToStringList(classContract['bases'] as List?);
|
||||
final docstring = classContract['docstring'] as String?;
|
||||
|
||||
// Add documentation
|
||||
if (docstring != null) {
|
||||
buffer.writeln('/// ${docstring.replaceAll('\n', '\n/// ')}');
|
||||
}
|
||||
|
||||
// Begin class definition
|
||||
buffer.write('class $name');
|
||||
if (bases.isNotEmpty) {
|
||||
final implementsStr = bases.join(', ');
|
||||
buffer.write(' implements $implementsStr');
|
||||
}
|
||||
buffer.writeln(' {');
|
||||
|
||||
// Generate required interface implementations first
|
||||
if (bases.contains('BaseChain')) {
|
||||
buffer.write(ClassGeneratorUtils.generateRequiredImplementations(
|
||||
bases, classContract));
|
||||
}
|
||||
|
||||
// Generate properties from contract properties
|
||||
final properties =
|
||||
TypeUtils.castToMapList(classContract['properties'] as List?);
|
||||
if (properties.isNotEmpty) {
|
||||
buffer.write(ClassGeneratorUtils.generateProperties(properties));
|
||||
}
|
||||
|
||||
// Generate constructor
|
||||
if (properties.isNotEmpty || bases.contains('BaseChain')) {
|
||||
buffer.write(ClassGeneratorUtils.generateConstructor(name, properties));
|
||||
}
|
||||
|
||||
// Generate additional methods
|
||||
final methods = TypeUtils.castToMapList(classContract['methods'] as List?);
|
||||
if (methods.isNotEmpty) {
|
||||
for (final method in methods) {
|
||||
if (method['name'] != '__init__') {
|
||||
buffer.write(ClassGeneratorUtils.generateMethod(method));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer.writeln('}');
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/// Main code generator class
|
||||
class DartCodeGenerator {
|
||||
final String outputDir;
|
||||
|
||||
DartCodeGenerator(this.outputDir);
|
||||
|
||||
Future<void> generateFromYaml(String yamlPath) async {
|
||||
final file = File(yamlPath);
|
||||
final content = await file.readAsString();
|
||||
final yamlDoc = loadYaml(content) as YamlMap;
|
||||
final contracts = YamlUtils.convertYamlToMap(yamlDoc);
|
||||
|
||||
// Generate interfaces
|
||||
for (final interface in contracts['interfaces'] ?? []) {
|
||||
final code = generateInterface(interface as Map<String, dynamic>);
|
||||
final fileName = '${interface['name'].toString().toLowerCase()}.dart';
|
||||
final outputFile =
|
||||
File(path.join(outputDir, 'lib', 'src', 'interfaces', fileName));
|
||||
await outputFile.create(recursive: true);
|
||||
await outputFile.writeAsString(code);
|
||||
}
|
||||
|
||||
// Generate classes
|
||||
for (final classContract in contracts['classes'] ?? []) {
|
||||
final code = generateClass(classContract as Map<String, dynamic>);
|
||||
final fileName = '${classContract['name'].toString().toLowerCase()}.dart';
|
||||
final outputFile =
|
||||
File(path.join(outputDir, 'lib', 'src', 'implementations', fileName));
|
||||
await outputFile.create(recursive: true);
|
||||
await outputFile.writeAsString(code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main(List<String> arguments) async {
|
||||
final parser = ArgParser()
|
||||
..addOption('contracts',
|
||||
abbr: 'c', help: 'Path to the YAML contracts file', mandatory: true)
|
||||
..addOption('output',
|
||||
abbr: 'o',
|
||||
help: 'Output directory for generated Dart code',
|
||||
mandatory: true);
|
||||
|
||||
try {
|
||||
final results = parser.parse(arguments);
|
||||
final contractsFile = results['contracts'] as String;
|
||||
final outputDir = results['output'] as String;
|
||||
|
||||
final generator = DartCodeGenerator(outputDir);
|
||||
await generator.generateFromYaml(contractsFile);
|
||||
|
||||
print('Code generation completed successfully.');
|
||||
} catch (e) {
|
||||
print('Error: $e');
|
||||
print(
|
||||
'Usage: dart generate_dart_code.dart --contracts <file> --output <dir>');
|
||||
exit(1);
|
||||
}
|
||||
}
|
443
helpers/tools/converter/tools/python_parser.dart
Normal file
443
helpers/tools/converter/tools/python_parser.dart
Normal file
|
@ -0,0 +1,443 @@
|
|||
import 'dart:io';
|
||||
|
||||
/// Represents a Python method parameter
|
||||
class Parameter {
|
||||
final String name;
|
||||
final String type;
|
||||
final bool isOptional;
|
||||
final bool hasDefault;
|
||||
|
||||
Parameter({
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.isOptional,
|
||||
required this.hasDefault,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'type': type,
|
||||
'is_optional': isOptional,
|
||||
'has_default': hasDefault,
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents a Python method
|
||||
class Method {
|
||||
final String name;
|
||||
final List<Parameter> parameters;
|
||||
final String returnType;
|
||||
final String? docstring;
|
||||
final List<String> decorators;
|
||||
final bool isAsync;
|
||||
final bool isAbstract;
|
||||
final bool isProperty;
|
||||
|
||||
Method({
|
||||
required this.name,
|
||||
required this.parameters,
|
||||
required this.returnType,
|
||||
this.docstring,
|
||||
required this.decorators,
|
||||
required this.isAsync,
|
||||
required this.isAbstract,
|
||||
required this.isProperty,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'parameters': parameters.map((p) => p.toJson()).toList(),
|
||||
'return_type': returnType,
|
||||
if (docstring != null) 'docstring': docstring,
|
||||
'decorators': decorators,
|
||||
'is_async': isAsync,
|
||||
'is_abstract': isAbstract,
|
||||
'is_property': isProperty,
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents a Python class property
|
||||
class Property {
|
||||
final String name;
|
||||
final String type;
|
||||
final bool hasDefault;
|
||||
final String? defaultValue;
|
||||
|
||||
Property({
|
||||
required this.name,
|
||||
required this.type,
|
||||
required this.hasDefault,
|
||||
this.defaultValue,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'type': type,
|
||||
'has_default': hasDefault,
|
||||
if (defaultValue != null) 'default_value': defaultValue,
|
||||
};
|
||||
}
|
||||
|
||||
/// Represents a Python class
|
||||
class PythonClass {
|
||||
final String name;
|
||||
final List<String> bases;
|
||||
final List<Method> methods;
|
||||
final List<Property> properties;
|
||||
final String? docstring;
|
||||
final List<String> decorators;
|
||||
final bool isInterface;
|
||||
|
||||
PythonClass({
|
||||
required this.name,
|
||||
required this.bases,
|
||||
required this.methods,
|
||||
required this.properties,
|
||||
this.docstring,
|
||||
required this.decorators,
|
||||
required this.isInterface,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'name': name,
|
||||
'bases': bases,
|
||||
'methods': methods.map((m) => m.toJson()).toList(),
|
||||
'properties': properties.map((p) => p.toJson()).toList(),
|
||||
if (docstring != null) 'docstring': docstring,
|
||||
'decorators': decorators,
|
||||
'is_interface': isInterface,
|
||||
};
|
||||
}
|
||||
|
||||
/// Parser for Python source code
|
||||
class PythonParser {
|
||||
/// Check if a line looks like code
|
||||
static bool _isCodeLine(String line) {
|
||||
return line.startsWith('def ') ||
|
||||
line.startsWith('@') ||
|
||||
line.startsWith('class ') ||
|
||||
line.contains(' = ') ||
|
||||
line.contains('self.') ||
|
||||
line.contains('return ') ||
|
||||
line.contains('pass') ||
|
||||
line.contains('raise ') ||
|
||||
line.contains('yield ') ||
|
||||
line.contains('async ') ||
|
||||
line.contains('await ') ||
|
||||
(line.contains(':') && !line.startsWith('Note:')) ||
|
||||
line.trim().startsWith('"""') ||
|
||||
line.trim().endsWith('"""');
|
||||
}
|
||||
|
||||
/// Parse a docstring from Python code lines
|
||||
static String? _parseDocstring(
|
||||
List<String> lines, int startIndex, int baseIndent) {
|
||||
if (startIndex >= lines.length) return null;
|
||||
|
||||
final line = lines[startIndex].trim();
|
||||
if (!line.startsWith('"""')) return null;
|
||||
|
||||
// Handle single-line docstring
|
||||
if (line.endsWith('"""') && line.length > 6) {
|
||||
return line.substring(3, line.length - 3).trim();
|
||||
}
|
||||
|
||||
final docLines = <String>[];
|
||||
// Add first line content if it exists after the opening quotes
|
||||
var firstLineContent = line.substring(3).trim();
|
||||
if (firstLineContent.isNotEmpty && !_isCodeLine(firstLineContent)) {
|
||||
docLines.add(firstLineContent);
|
||||
}
|
||||
|
||||
var i = startIndex + 1;
|
||||
while (i < lines.length) {
|
||||
final currentLine = lines[i].trim();
|
||||
|
||||
// Stop at closing quotes
|
||||
if (currentLine.endsWith('"""')) {
|
||||
// Add the last line content if it exists before the closing quotes
|
||||
var lastLineContent =
|
||||
currentLine.substring(0, currentLine.length - 3).trim();
|
||||
if (lastLineContent.isNotEmpty && !_isCodeLine(lastLineContent)) {
|
||||
docLines.add(lastLineContent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Only add non-code lines
|
||||
if (currentLine.isNotEmpty && !_isCodeLine(currentLine)) {
|
||||
docLines.add(currentLine);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return docLines.isEmpty ? null : docLines.join('\n').trim();
|
||||
}
|
||||
|
||||
/// Get the indentation level of a line
|
||||
static int _getIndentation(String line) {
|
||||
return line.length - line.trimLeft().length;
|
||||
}
|
||||
|
||||
/// Parse method parameters from a parameter string
|
||||
static List<Parameter> _parseParameters(String paramsStr) {
|
||||
if (paramsStr.trim().isEmpty) return [];
|
||||
|
||||
final params = <Parameter>[];
|
||||
var depth = 0;
|
||||
var currentParam = StringBuffer();
|
||||
|
||||
// Handle nested brackets in parameter types
|
||||
for (var i = 0; i < paramsStr.length; i++) {
|
||||
final char = paramsStr[i];
|
||||
if (char == '[') depth++;
|
||||
if (char == ']') depth--;
|
||||
if (char == ',' && depth == 0) {
|
||||
final param = currentParam.toString().trim();
|
||||
if (param.isNotEmpty && param != 'self' && !param.startsWith('**')) {
|
||||
final paramObj = _parseParameter(param);
|
||||
if (paramObj != null) {
|
||||
params.add(paramObj);
|
||||
}
|
||||
}
|
||||
currentParam.clear();
|
||||
} else {
|
||||
currentParam.write(char);
|
||||
}
|
||||
}
|
||||
|
||||
final lastParam = currentParam.toString().trim();
|
||||
if (lastParam.isNotEmpty &&
|
||||
lastParam != 'self' &&
|
||||
!lastParam.startsWith('**')) {
|
||||
final paramObj = _parseParameter(lastParam);
|
||||
if (paramObj != null) {
|
||||
params.add(paramObj);
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/// Parse a single parameter
|
||||
static Parameter? _parseParameter(String param) {
|
||||
if (param.isEmpty) return null;
|
||||
|
||||
var name = param;
|
||||
var type = 'Any';
|
||||
var hasDefault = false;
|
||||
var isOptional = false;
|
||||
|
||||
// Check for type annotation
|
||||
if (param.contains(':')) {
|
||||
final parts = param.split(':');
|
||||
name = parts[0].trim();
|
||||
var typeStr = parts[1];
|
||||
|
||||
// Handle default value
|
||||
if (typeStr.contains('=')) {
|
||||
final typeParts = typeStr.split('=');
|
||||
typeStr = typeParts[0];
|
||||
hasDefault = true;
|
||||
isOptional = true;
|
||||
}
|
||||
|
||||
type = typeStr.trim();
|
||||
|
||||
// Handle Optional type
|
||||
if (type.startsWith('Optional[')) {
|
||||
type = type.substring(9, type.length - 1);
|
||||
isOptional = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for default value without type annotation
|
||||
if (param.contains('=')) {
|
||||
hasDefault = true;
|
||||
isOptional = true;
|
||||
if (!param.contains(':')) {
|
||||
name = param.split('=')[0].trim();
|
||||
}
|
||||
}
|
||||
|
||||
return Parameter(
|
||||
name: name,
|
||||
type: type,
|
||||
isOptional: isOptional,
|
||||
hasDefault: hasDefault,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parse a method definition
|
||||
static Method? _parseMethod(
|
||||
List<String> lines, int startIndex, List<String> decorators) {
|
||||
final line = lines[startIndex].trim();
|
||||
if (!line.startsWith('def ') && !line.startsWith('async def ')) return null;
|
||||
|
||||
final methodMatch =
|
||||
RegExp(r'(?:async\s+)?def\s+(\w+)\s*\((.*?)\)(?:\s*->\s*(.+?))?\s*:')
|
||||
.firstMatch(line);
|
||||
if (methodMatch == null) return null;
|
||||
|
||||
final name = methodMatch.group(1)!;
|
||||
final paramsStr = methodMatch.group(2) ?? '';
|
||||
var returnType = methodMatch.group(3) ?? 'None';
|
||||
returnType = returnType.trim();
|
||||
|
||||
final isAsync = line.contains('async ');
|
||||
final isAbstract = decorators.contains('abstractmethod');
|
||||
final isProperty = decorators.contains('property');
|
||||
|
||||
// Parse docstring if present
|
||||
var i = startIndex + 1;
|
||||
String? docstring;
|
||||
if (i < lines.length) {
|
||||
final nextLine = lines[i].trim();
|
||||
if (nextLine.startsWith('"""')) {
|
||||
docstring =
|
||||
_parseDocstring(lines, i, _getIndentation(lines[startIndex]));
|
||||
}
|
||||
}
|
||||
|
||||
return Method(
|
||||
name: name,
|
||||
parameters: _parseParameters(paramsStr),
|
||||
returnType: returnType,
|
||||
docstring: docstring,
|
||||
decorators: decorators,
|
||||
isAsync: isAsync,
|
||||
isAbstract: isAbstract,
|
||||
isProperty: isProperty,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parse a property definition
|
||||
static Property? _parseProperty(String line) {
|
||||
if (!line.contains(':') || line.contains('def ')) return null;
|
||||
|
||||
final propertyMatch =
|
||||
RegExp(r'(\w+)\s*:\s*(.+?)(?:\s*=\s*(.+))?$').firstMatch(line);
|
||||
if (propertyMatch == null) return null;
|
||||
|
||||
final name = propertyMatch.group(1)!;
|
||||
final type = propertyMatch.group(2)!;
|
||||
final defaultValue = propertyMatch.group(3);
|
||||
|
||||
return Property(
|
||||
name: name,
|
||||
type: type.trim(),
|
||||
hasDefault: defaultValue != null,
|
||||
defaultValue: defaultValue?.trim(),
|
||||
);
|
||||
}
|
||||
|
||||
/// Parse Python source code into a list of classes
|
||||
static Future<List<PythonClass>> parseFile(File file) async {
|
||||
final content = await file.readAsString();
|
||||
final lines = content.split('\n');
|
||||
final classes = <PythonClass>[];
|
||||
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
final line = lines[i];
|
||||
final trimmedLine = line.trim();
|
||||
|
||||
if (trimmedLine.startsWith('class ')) {
|
||||
final classMatch =
|
||||
RegExp(r'class\s+(\w+)(?:\((.*?)\))?:').firstMatch(trimmedLine);
|
||||
if (classMatch != null) {
|
||||
final className = classMatch.group(1)!;
|
||||
final basesStr = classMatch.group(2);
|
||||
final bases =
|
||||
basesStr?.split(',').map((b) => b.trim()).toList() ?? [];
|
||||
final isInterface = bases.any((b) => b.contains('Protocol'));
|
||||
|
||||
final classIndent = _getIndentation(line);
|
||||
var currentDecorators = <String>[];
|
||||
final methods = <Method>[];
|
||||
final properties = <Property>[];
|
||||
|
||||
// Parse class docstring
|
||||
var j = i + 1;
|
||||
String? docstring;
|
||||
if (j < lines.length && lines[j].trim().startsWith('"""')) {
|
||||
docstring = _parseDocstring(lines, j, classIndent);
|
||||
// Skip past docstring
|
||||
while (j < lines.length && !lines[j].trim().endsWith('"""')) {
|
||||
j++;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
// Parse class body
|
||||
while (j < lines.length) {
|
||||
final currentLine = lines[j];
|
||||
final currentIndent = _getIndentation(currentLine);
|
||||
final trimmedCurrentLine = currentLine.trim();
|
||||
|
||||
// Check if we're still in the class
|
||||
if (trimmedCurrentLine.isNotEmpty && currentIndent <= classIndent) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip empty lines
|
||||
if (trimmedCurrentLine.isEmpty) {
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse decorators
|
||||
if (trimmedCurrentLine.startsWith('@')) {
|
||||
currentDecorators
|
||||
.add(trimmedCurrentLine.substring(1).split('(')[0].trim());
|
||||
j++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse methods
|
||||
if (trimmedCurrentLine.startsWith('def ') ||
|
||||
trimmedCurrentLine.startsWith('async def ')) {
|
||||
final method =
|
||||
_parseMethod(lines, j, List.from(currentDecorators));
|
||||
if (method != null) {
|
||||
methods.add(method);
|
||||
currentDecorators = [];
|
||||
// Skip past method body
|
||||
while (j < lines.length - 1) {
|
||||
final nextLine = lines[j + 1];
|
||||
if (nextLine.trim().isEmpty ||
|
||||
_getIndentation(nextLine) <= currentIndent) {
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse properties
|
||||
final property = _parseProperty(trimmedCurrentLine);
|
||||
if (property != null) {
|
||||
properties.add(property);
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
|
||||
i = j - 1;
|
||||
|
||||
classes.add(PythonClass(
|
||||
name: className,
|
||||
bases: bases,
|
||||
methods: methods,
|
||||
properties: properties,
|
||||
docstring: docstring,
|
||||
decorators: [],
|
||||
isInterface: isInterface,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
}
|
152
helpers/tools/converter/tools/setup_langchain.sh
Normal file
152
helpers/tools/converter/tools/setup_langchain.sh
Normal file
|
@ -0,0 +1,152 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Exit on error
|
||||
set -e
|
||||
|
||||
# Script directory
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
PACKAGE_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
TEMP_DIR="$PACKAGE_DIR/temp"
|
||||
CONTRACTS_FILE="$TEMP_DIR/contracts.yaml"
|
||||
|
||||
# Colors for output
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Print step information
|
||||
print_step() {
|
||||
echo -e "${BLUE}=== $1 ===${NC}"
|
||||
}
|
||||
|
||||
# Ensure required commands are available
|
||||
check_requirements() {
|
||||
print_step "Checking requirements"
|
||||
|
||||
commands=("git" "dart" "pub")
|
||||
for cmd in "${commands[@]}"; do
|
||||
if ! command -v $cmd &> /dev/null; then
|
||||
echo "Error: $cmd is required but not installed."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Create necessary directories
|
||||
setup_directories() {
|
||||
print_step "Setting up directories"
|
||||
|
||||
mkdir -p "$TEMP_DIR"
|
||||
mkdir -p "$PACKAGE_DIR/lib/src/interfaces"
|
||||
mkdir -p "$PACKAGE_DIR/lib/src/implementations"
|
||||
}
|
||||
|
||||
# Clone Python LangChain repository
|
||||
clone_langchain() {
|
||||
print_step "Cloning Python LangChain repository"
|
||||
|
||||
if [ -d "$TEMP_DIR/langchain" ]; then
|
||||
echo "Updating existing LangChain repository..."
|
||||
cd "$TEMP_DIR/langchain"
|
||||
git pull
|
||||
else
|
||||
echo "Cloning LangChain repository..."
|
||||
git clone https://github.com/langchain-ai/langchain.git "$TEMP_DIR/langchain"
|
||||
fi
|
||||
}
|
||||
|
||||
# Extract contracts from Python code
|
||||
extract_contracts() {
|
||||
print_step "Extracting contracts from Python code"
|
||||
|
||||
cd "$PACKAGE_DIR"
|
||||
dart run "$SCRIPT_DIR/extract_contracts.dart" \
|
||||
--source "$TEMP_DIR/langchain/langchain" \
|
||||
--output "$CONTRACTS_FILE"
|
||||
}
|
||||
|
||||
# Generate Dart code from contracts
|
||||
generate_dart_code() {
|
||||
print_step "Generating Dart code from contracts"
|
||||
|
||||
cd "$PACKAGE_DIR"
|
||||
dart run "$SCRIPT_DIR/generate_dart_code.dart" \
|
||||
--contracts "$CONTRACTS_FILE" \
|
||||
--output "$PACKAGE_DIR"
|
||||
}
|
||||
|
||||
# Update package dependencies
|
||||
update_dependencies() {
|
||||
print_step "Updating package dependencies"
|
||||
|
||||
cd "$PACKAGE_DIR"
|
||||
|
||||
# Ensure required dependencies are in pubspec.yaml
|
||||
if ! grep -q "yaml:" pubspec.yaml; then
|
||||
echo "
|
||||
dependencies:
|
||||
yaml: ^3.1.0
|
||||
path: ^1.8.0
|
||||
args: ^2.3.0" >> pubspec.yaml
|
||||
fi
|
||||
|
||||
dart pub get
|
||||
}
|
||||
|
||||
# Create package exports file
|
||||
create_exports() {
|
||||
print_step "Creating package exports"
|
||||
|
||||
cat > "$PACKAGE_DIR/lib/langchain.dart" << EOL
|
||||
/// LangChain for Dart
|
||||
///
|
||||
/// This is a Dart implementation of LangChain, providing tools and utilities
|
||||
/// for building applications powered by large language models (LLMs).
|
||||
library langchain;
|
||||
|
||||
// Export interfaces
|
||||
export 'src/interfaces/llm.dart';
|
||||
export 'src/interfaces/chain.dart';
|
||||
export 'src/interfaces/prompt.dart';
|
||||
export 'src/interfaces/memory.dart';
|
||||
export 'src/interfaces/embeddings.dart';
|
||||
export 'src/interfaces/document.dart';
|
||||
export 'src/interfaces/vectorstore.dart';
|
||||
export 'src/interfaces/tool.dart';
|
||||
export 'src/interfaces/agent.dart';
|
||||
|
||||
// Export implementations
|
||||
export 'src/implementations/llm.dart';
|
||||
export 'src/implementations/chain.dart';
|
||||
export 'src/implementations/prompt.dart';
|
||||
export 'src/implementations/memory.dart';
|
||||
export 'src/implementations/embeddings.dart';
|
||||
export 'src/implementations/document.dart';
|
||||
export 'src/implementations/vectorstore.dart';
|
||||
export 'src/implementations/tool.dart';
|
||||
export 'src/implementations/agent.dart';
|
||||
EOL
|
||||
}
|
||||
|
||||
# Main execution
|
||||
main() {
|
||||
print_step "Starting LangChain setup"
|
||||
|
||||
check_requirements
|
||||
setup_directories
|
||||
clone_langchain
|
||||
extract_contracts
|
||||
generate_dart_code
|
||||
update_dependencies
|
||||
create_exports
|
||||
|
||||
echo -e "${GREEN}Setup completed successfully!${NC}"
|
||||
echo "Next steps:"
|
||||
echo "1. Review generated code in lib/src/"
|
||||
echo "2. Implement TODOs in the generated classes"
|
||||
echo "3. Add tests for the implementations"
|
||||
echo "4. Update the documentation"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main
|
53
helpers/uninstall_code_extensions.sh
Executable file
53
helpers/uninstall_code_extensions.sh
Executable file
|
@ -0,0 +1,53 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Array of extensions to uninstall
|
||||
extensions=(
|
||||
"nash.awesome-flutter-snippets" # Dart Data Class Generator
|
||||
"robert-brunhage.flutter-riverpod-snippets" # Flutter Riverpod Snippets
|
||||
"usernamehw.errorlens" # Error Lens
|
||||
"aaron-bond.better-comments" # Better Comments
|
||||
"styfle.remove-comments" # Remove Comments
|
||||
"patbenatar.advanced-new-file" # Advanced New File
|
||||
"GitHub.copilot" # GitHub Copilot
|
||||
"dracula-theme.theme-dracula" # Dracula Theme (optional)
|
||||
"firebase.vscode-firebase-explorer" # Firebase Explorer
|
||||
"pflannery.vscode-versionlens" # Version Lens
|
||||
"tgsup.find-unused-dart-files-and-assets" # Find Unused Dart Files & Assets
|
||||
"humao.rest-client" # REST Client
|
||||
"rangav.vscode-thunder-client" # Thunder Client
|
||||
"ritwickdey.liveserver" # Live Server
|
||||
"Dart-Code.dart-code" # Dart SDK
|
||||
"Dart-Code.flutter" # Flutter SDK
|
||||
"ms-vscode.cpptools" # C/C++
|
||||
"ms-vscode.cpptools-extension-pack" # C/C++ Extension Pack
|
||||
"ms-vscode.cpptools-themes" # C/C++ Themes
|
||||
"twxs.cmake " # CMake
|
||||
"ms-vscode.cmake-tools" # CMake Tools
|
||||
"ms-vscode.makefile-tools" # Makefile Tools
|
||||
"saoudrizwan.claude-dev" # Claude Dev
|
||||
"Continue.continue" # Continue
|
||||
"DEVSENSE.phptools-vscode" # PHP Tools
|
||||
"DEVSENSE.composer-php-vscode" # Composer PHP
|
||||
"DEVSENSE.profiler-php-vscode" # Profiler PHP
|
||||
"ms-vscode.remote-explorer" # Remote - Containers
|
||||
"ms-vscode-remote.remote-ssh" # Remote - SSH
|
||||
"ms-vscode-remote.remote-ssh-edit" # Remote - SSH: Edit
|
||||
"ms-vscode-remote.remote-containers" # Remote - Containers
|
||||
"eamodio.gitlens" # GitLens
|
||||
"DEVSENSE.intelli-php-vscode" # IntelliPHP
|
||||
"blaugold.melos-code" # Melos
|
||||
"vscode-icons-team.vscode-icons" # VSCode Icons
|
||||
"redhat.vscode-yaml" # YAML
|
||||
"GitHub.vscode-github-actions" # GitHub Actions
|
||||
"ms-azuretools.vscode-docker" # Docker
|
||||
"ms-kubernetes-tools.vscode-kubernetes-tools" # Kubernetes
|
||||
)
|
||||
|
||||
# Uninstall each extension
|
||||
echo "Uninstalling VSCode extensions..."
|
||||
for extension in "${extensions[@]}"; do
|
||||
code --uninstall-extension "$extension"
|
||||
echo "Uninstalled: $extension"
|
||||
done
|
||||
|
||||
echo "All specified extensions have been uninstalled successfully."
|
10
melos.yaml
10
melos.yaml
|
@ -11,7 +11,9 @@
|
|||
name: protevus_platform
|
||||
repository: https://github.com/protevus/platform
|
||||
packages:
|
||||
- apps/**
|
||||
- packages/**
|
||||
- helpers/tools/**
|
||||
- examples/**
|
||||
|
||||
command:
|
||||
|
@ -74,12 +76,20 @@ scripts:
|
|||
configure:
|
||||
run: "melos bootstrap && MELOS_SCOPE=\"platform_container_generator\" melos run generate:custom && MELOS_SCOPE=\"platform_model, platform_exceptions, platform_mocking\" melos run generate:dummy:test && MELOS_SCOPE=\"platform_container_generator\" melos run debug:reflectable && melos run test && melos run coverage && melos run coverage_report && melos run docs:generate\n"
|
||||
description: "Configure the development environment, generate code and dummy tests, run tests, generate coverage, and create API documentation"
|
||||
template:
|
||||
name: Create from template
|
||||
description: "Creates a new project from a template in the templates directory.\n\nUsage: melos run template template_name:name type:dart|flutter name:project_name\n\nExample:\n melos run template template_name:bloc_app type:flutter name:my_new_app\n melos run template template_name:core_package type:dart name:core_utils\n"
|
||||
run: dart run helpers/create_from_template.dart $MELOS_ARGS
|
||||
test:
|
||||
run: melos exec -c 1 --fail-fast -- "dart test"
|
||||
description: Run tests for all packages
|
||||
test:custom:
|
||||
run: melos exec --scope="$MELOS_SCOPE" -- dart test
|
||||
description: Run tests for specified packages (use with MELOS_SCOPE env var)
|
||||
create:
|
||||
name: Create new package or application
|
||||
description: "Creates a new Dart package or Flutter application in the appropriate directory.\n\nUsage: melos run create -- --type dart|flutter --category type --name project_name\n\nAvailable categories for Dart:\n - package : Basic Dart package\n - console : Command-line application\n - server : Server-side application\n - desktop : Desktop application\n - plugin : Dart plugin\n\nAvailable categories for Flutter:\n - app : Mobile application\n - web : Web application\n - desktop : Desktop application\n - plugin : Flutter plugin\n - module : Flutter module\n - package : Flutter package\n"
|
||||
run: dart run helpers/create_project.dart $MELOS_ARGS
|
||||
deps:check:
|
||||
run: melos exec -- "dart pub outdated"
|
||||
description: Check for outdated dependencies
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/// An easily-extensible web server framework in Dart.
|
||||
library platform_core;
|
||||
|
||||
export 'package:platform_exceptions/http_exception.dart';
|
||||
export 'package:platform_support/exceptions.dart';
|
||||
export 'package:platform_model/model.dart';
|
||||
export 'package:platform_route/route.dart';
|
||||
export 'src/core/core.dart';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io' show Cookie;
|
||||
import 'package:platform_exceptions/http_exception.dart';
|
||||
import 'package:platform_support/exceptions.dart';
|
||||
import 'package:platform_route/route.dart';
|
||||
import 'package:belatuk_combinator/belatuk_combinator.dart';
|
||||
import 'package:stack_trace/stack_trace.dart';
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:platform_exceptions/http_exception.dart';
|
||||
import 'package:platform_support/exceptions.dart';
|
||||
|
||||
import 'service.dart';
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
library angel_framework.http.metadata;
|
||||
|
||||
import 'package:platform_exceptions/http_exception.dart';
|
||||
import 'package:platform_support/exceptions.dart';
|
||||
|
||||
import 'hooked_service.dart' show HookedServiceEventListener;
|
||||
import 'request_context.dart';
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'dart:async';
|
|||
import 'dart:collection' show HashMap;
|
||||
import 'dart:convert';
|
||||
import 'package:platform_container/container.dart';
|
||||
import 'package:platform_exceptions/http_exception.dart';
|
||||
import 'package:platform_support/exceptions.dart';
|
||||
import 'package:platform_route/route.dart';
|
||||
import 'package:belatuk_combinator/belatuk_combinator.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
library platform_core.http.service;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:platform_exceptions/http_exception.dart';
|
||||
import 'package:platform_support/exceptions.dart';
|
||||
import 'package:belatuk_merge_map/belatuk_merge_map.dart';
|
||||
import 'package:quiver/core.dart';
|
||||
import '../util.dart';
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'package:platform_container/container.dart';
|
|||
import 'package:platform_core/core.dart';
|
||||
import 'package:collection/collection.dart' show IterableExtension;
|
||||
import 'package:http2/transport.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
final RegExp _comma = RegExp(r',\s*');
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'dart:io';
|
|||
import 'package:platform_core/core.dart' hide Header;
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:http2/transport.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'http2_request_context.dart';
|
||||
import 'http2_response_context.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
|
|
@ -389,20 +389,6 @@ packages:
|
|||
relative: true
|
||||
source: path
|
||||
version: "9.0.0"
|
||||
platform_exceptions:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../exceptions"
|
||||
relative: true
|
||||
source: path
|
||||
version: "9.0.0"
|
||||
platform_mocking:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../mocking"
|
||||
relative: true
|
||||
source: path
|
||||
version: "9.0.0"
|
||||
platform_model:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -417,6 +403,20 @@ packages:
|
|||
relative: true
|
||||
source: path
|
||||
version: "9.0.0"
|
||||
platform_support:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../support"
|
||||
relative: true
|
||||
source: path
|
||||
version: "9.0.0"
|
||||
platform_testing:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../testing"
|
||||
relative: true
|
||||
source: path
|
||||
version: "9.0.0"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -8,10 +8,10 @@ environment:
|
|||
sdk: '>=3.3.0 <4.0.0'
|
||||
dependencies:
|
||||
platform_container: ^9.0.0
|
||||
platform_exceptions: ^9.0.0
|
||||
platform_model: ^9.0.0
|
||||
platform_route: ^9.0.0
|
||||
platform_mocking: ^9.0.0
|
||||
platform_support: ^9.0.0
|
||||
platform_testing: ^9.0.0
|
||||
belatuk_merge_map: ^5.1.0
|
||||
belatuk_combinator: ^5.2.0
|
||||
belatuk_http_server: ^4.4.0
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:io';
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
final Uri endpoint = Uri.parse('http://example.com/accept');
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:convert';
|
|||
import 'dart:io';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:platform_container/mirrors.dart';
|
|||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'common.dart';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dart:convert';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:platform_core/http.dart';
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'common.dart';
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'dart:typed_data' show BytesBuilder;
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
Future<List<int>> getBody(MockHttpResponse rs) async {
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'dart:async';
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
final Uri endpoint = Uri.parse('http://example.com');
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:convert';
|
|||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'dart:convert';
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'dart:convert';
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'dart:io' show stderr;
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'dart:convert';
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
|
|
|
@ -5,7 +5,7 @@ import 'dart:io';
|
|||
import 'package:platform_container/mirrors.dart';
|
||||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import 'package:platform_container/mirrors.dart';
|
|||
import 'package:platform_core/core.dart';
|
||||
import 'package:platform_core/http.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
|
|
71
packages/exceptions/.gitignore
vendored
71
packages/exceptions/.gitignore
vendored
|
@ -1,71 +0,0 @@
|
|||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
.dart_tool
|
||||
.packages
|
||||
.pub/
|
||||
build/
|
||||
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
|
||||
### Dart template
|
||||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
|
||||
# SDK 1.20 and later (no longer creates packages directories)
|
||||
|
||||
# Older SDK versions
|
||||
# (Include if the minimum SDK version specified in pubsepc.yaml is earlier than 1.20)
|
||||
.project
|
||||
.buildlog
|
||||
**/packages/
|
||||
|
||||
|
||||
# Files created by dart2js
|
||||
# (Most Dart developers will use pub build to compile Dart, use/modify these
|
||||
# rules if you intend to use dart2js directly
|
||||
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
|
||||
# differentiate from explicit Javascript files)
|
||||
*.dart.js
|
||||
*.part.js
|
||||
*.js.deps
|
||||
*.js.map
|
||||
*.info.json
|
||||
|
||||
# Directory created by dartdoc
|
||||
|
||||
# Don't commit pubspec lock file
|
||||
# (Library packages only! Remove pattern if developing an application package)
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
|
||||
## VsCode
|
||||
.vscode/
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
/out/
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
|
@ -1,72 +0,0 @@
|
|||
|
||||
# Change Log
|
||||
|
||||
## 8.1.1
|
||||
|
||||
* Updated repository link
|
||||
|
||||
## 8.1.0
|
||||
|
||||
* Updated `lints` to 3.0.0
|
||||
|
||||
## 8.0.0
|
||||
|
||||
* Require Dart >= 3.0
|
||||
|
||||
## 7.0.0
|
||||
|
||||
* Require Dart >= 2.17
|
||||
|
||||
## 6.0.1
|
||||
|
||||
* Updated README
|
||||
|
||||
## 6.0.0
|
||||
|
||||
* Require Dart >= 2.16
|
||||
* [**Breaking**] `error` for `HttpException` is no longer mandatory
|
||||
|
||||
## 5.0.0
|
||||
|
||||
* Skipped release
|
||||
|
||||
## 4.0.0
|
||||
|
||||
* Skipped release
|
||||
|
||||
## 3.1.0
|
||||
|
||||
* Upgraded to `lints` linter
|
||||
|
||||
## 3.0.2
|
||||
|
||||
* Updated LICENSE link
|
||||
|
||||
## 3.0.1
|
||||
|
||||
* Updated README
|
||||
|
||||
## 3.0.0
|
||||
|
||||
* Migrated to support Dart >= 2.12 NNBD
|
||||
|
||||
## 2.0.0
|
||||
|
||||
* Migrated to work with Dart >= 2.12 Non NNBD
|
||||
|
||||
## 1.1.0
|
||||
|
||||
* Emit `is_error` and `status_code` in `toJson()`.
|
||||
* No more `camelCase` at all.
|
||||
|
||||
## 1.0.0+3
|
||||
|
||||
* Slightly relax the deserialization of `errors`.
|
||||
|
||||
## 1.0.0+2
|
||||
|
||||
* Added a backwards-compatible way to cast the `errors` List.
|
||||
|
||||
## 1.0.0+1
|
||||
|
||||
* Dart 2 updates.
|
|
@ -1,8 +0,0 @@
|
|||
# Protevus Http Exception
|
||||
|
||||
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_http_exception?include_prereleases)
|
||||
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
||||
[![License](https://img.shields.io/github/license/dart-backend/angel)](https://github.com/dart-backend/angel/tree/master/packages/http_exception/LICENSE)
|
||||
|
||||
Exception class that can be serialized to JSON and serialized to clients. Protevus's HTTP exception class.
|
|
@ -1 +0,0 @@
|
|||
include: package:lints/recommended.yaml
|
|
@ -1,11 +0,0 @@
|
|||
name: platform_exceptions
|
||||
version: 9.0.0
|
||||
description: Protevus Platform Exception package
|
||||
homepage: https://protevus.com
|
||||
documentation: https://docs.protevus.com
|
||||
repository: https://git.protevus.com/protevus/platform/src/branch/main/packages/exceptions
|
||||
environment:
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
dev_dependencies:
|
||||
lints: ^4.0.0
|
||||
test: ^1.25.8
|
71
packages/mocking/.gitignore
vendored
71
packages/mocking/.gitignore
vendored
|
@ -1,71 +0,0 @@
|
|||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
.dart_tool
|
||||
.packages
|
||||
.pub/
|
||||
build/
|
||||
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
|
||||
### Dart template
|
||||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
|
||||
# SDK 1.20 and later (no longer creates packages directories)
|
||||
|
||||
# Older SDK versions
|
||||
# (Include if the minimum SDK version specified in pubsepc.yaml is earlier than 1.20)
|
||||
.project
|
||||
.buildlog
|
||||
**/packages/
|
||||
|
||||
|
||||
# Files created by dart2js
|
||||
# (Most Dart developers will use pub build to compile Dart, use/modify these
|
||||
# rules if you intend to use dart2js directly
|
||||
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
|
||||
# differentiate from explicit Javascript files)
|
||||
*.dart.js
|
||||
*.part.js
|
||||
*.js.deps
|
||||
*.js.map
|
||||
*.info.json
|
||||
|
||||
# Directory created by dartdoc
|
||||
|
||||
# Don't commit pubspec lock file
|
||||
# (Library packages only! Remove pattern if developing an application package)
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
|
||||
## VsCode
|
||||
.vscode/
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
/out/
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
|
@ -1,12 +0,0 @@
|
|||
Primary Authors
|
||||
===============
|
||||
|
||||
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
|
||||
|
||||
Thomas is the current maintainer of the code base. He has refactored and migrated the
|
||||
code base to support NNBD.
|
||||
|
||||
* __[Tobe O](thosakwe@gmail.com)__
|
||||
|
||||
Tobe has written much of the original code prior to NNBD migration. He has moved on and
|
||||
is no longer involved with the project.
|
|
@ -1,29 +0,0 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, dukefirehawk.com
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -1 +0,0 @@
|
|||
include: package:lints/recommended.yaml
|
|
@ -1,6 +0,0 @@
|
|||
export 'src/connection_info.dart';
|
||||
export 'src/headers.dart';
|
||||
export 'src/lockable_headers.dart';
|
||||
export 'src/request.dart';
|
||||
export 'src/response.dart';
|
||||
export 'src/session.dart';
|
|
@ -1,25 +0,0 @@
|
|||
name: platform_mocking
|
||||
version: 9.0.0
|
||||
description: Protevus Platform Mocking for dart:io HttpRequests, HttpResponses, HttpHeaders, etc.
|
||||
homepage: https://protevus.com
|
||||
documentation: https://docs.protevus.com
|
||||
repository: https://git.protevus.com/protevus/platform/src/branch/main/packages/mocking
|
||||
environment:
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
dependencies:
|
||||
charcode: ^1.3.1
|
||||
dev_dependencies:
|
||||
http: ^1.2.2
|
||||
test: ^1.25.8
|
||||
lints: ^4.0.0
|
||||
# dependency_overrides:
|
||||
# platform_core:
|
||||
# path: ../framework
|
||||
# platform_route:
|
||||
# path: ../route
|
||||
# platform_model:
|
||||
# path: ../model
|
||||
# platform_exceptions:
|
||||
# path: ../exceptions
|
||||
# platform_container:
|
||||
# path: ../container/container
|
7
packages/support/.gitignore
vendored
Normal file
7
packages/support/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
|
||||
# Avoid committing pubspec.lock for library packages; see
|
||||
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
pubspec.lock
|
3
packages/support/CHANGELOG.md
Normal file
3
packages/support/CHANGELOG.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
## 1.0.0
|
||||
|
||||
- Initial version.
|
39
packages/support/README.md
Normal file
39
packages/support/README.md
Normal file
|
@ -0,0 +1,39 @@
|
|||
<!--
|
||||
This README describes the package. If you publish this package to pub.dev,
|
||||
this README's contents appear on the landing page for your package.
|
||||
|
||||
For information about how to write a good package README, see the guide for
|
||||
[writing package pages](https://dart.dev/tools/pub/writing-package-pages).
|
||||
|
||||
For general information about developing packages, see the Dart guide for
|
||||
[creating packages](https://dart.dev/guides/libraries/create-packages)
|
||||
and the Flutter guide for
|
||||
[developing packages and plugins](https://flutter.dev/to/develop-packages).
|
||||
-->
|
||||
|
||||
TODO: Put a short description of the package here that helps potential users
|
||||
know whether this package might be useful for them.
|
||||
|
||||
## Features
|
||||
|
||||
TODO: List what your package can do. Maybe include images, gifs, or videos.
|
||||
|
||||
## Getting started
|
||||
|
||||
TODO: List prerequisites and provide or point to information on how to
|
||||
start using the package.
|
||||
|
||||
## Usage
|
||||
|
||||
TODO: Include short and useful examples for package users. Add longer examples
|
||||
to `/example` folder.
|
||||
|
||||
```dart
|
||||
const like = 'sample';
|
||||
```
|
||||
|
||||
## Additional information
|
||||
|
||||
TODO: Tell users more about the package: where to find more information, how to
|
||||
contribute to the package, how to file issues, what response they can expect
|
||||
from the package authors, and more.
|
30
packages/support/analysis_options.yaml
Normal file
30
packages/support/analysis_options.yaml
Normal file
|
@ -0,0 +1,30 @@
|
|||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:platform_exceptions/http_exception.dart';
|
||||
import 'package:platform_support/src/exceptions/http_exception.dart';
|
||||
|
||||
void main() =>
|
||||
throw PlatformHttpException.notFound(message: "Can't find that page!");
|
8
packages/support/lib/exceptions.dart
Normal file
8
packages/support/lib/exceptions.dart
Normal file
|
@ -0,0 +1,8 @@
|
|||
/// Support for doing something awesome.
|
||||
///
|
||||
/// More dartdocs go here.
|
||||
library;
|
||||
|
||||
export 'src/exceptions/http_exception.dart';
|
||||
|
||||
// TODO: Export any libraries intended for clients of this package.
|
15
packages/support/pubspec.yaml
Normal file
15
packages/support/pubspec.yaml
Normal file
|
@ -0,0 +1,15 @@
|
|||
name: platform_support
|
||||
description: Protevus Platform support package.
|
||||
version: 9.0.0
|
||||
# repository: https://github.com/my_org/my_repo
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.4
|
||||
|
||||
# Add regular dependencies here.
|
||||
dependencies:
|
||||
# path: ^1.8.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^4.0.0
|
||||
test: ^1.24.0
|
7
packages/testing/.gitignore
vendored
Normal file
7
packages/testing/.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
|
||||
# Avoid committing pubspec.lock for library packages; see
|
||||
# https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
pubspec.lock
|
30
packages/testing/analysis_options.yaml
Normal file
30
packages/testing/analysis_options.yaml
Normal file
|
@ -0,0 +1,30 @@
|
|||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
|
@ -1,5 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'package:platform_mocking/mocking.dart';
|
||||
import 'package:platform_testing/http.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
var rq =
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue