@re-shell/cli
Version:
Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja
1,803 lines (1,573 loc) • 46.2 kB
JavaScript
"use strict";
/**
* Dart Backend Template Base Generator
* Shared functionality for all Dart web frameworks
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.DartBackendGenerator = void 0;
const backend_template_generator_1 = require("../shared/backend-template-generator");
const fs_1 = require("fs");
const path = __importStar(require("path"));
class DartBackendGenerator extends backend_template_generator_1.BackendTemplateGenerator {
constructor(framework) {
const config = {
language: 'Dart',
framework,
packageManager: 'pub',
buildTool: 'dart',
testFramework: 'test',
features: [
'Async/await support',
'Type-safe routing',
'Middleware pipeline',
'JSON serialization',
'WebSocket support',
'Database integration',
'Authentication & Authorization',
'Structured logging',
'Environment configuration',
'Docker support',
'Hot reload development',
'Null safety'
],
dependencies: {},
devDependencies: {},
scripts: {
'start': 'dart run bin/server.dart',
'dev': 'dart run --enable-vm-service --enable-asserts bin/server.dart',
'build': 'dart compile exe bin/server.dart -o build/server',
'test': 'dart test',
'test:coverage': 'dart test --coverage=coverage',
'analyze': 'dart analyze',
'format': 'dart format .',
'fix': 'dart fix --apply',
'clean': 'dart clean'
},
dockerConfig: {
baseImage: 'dart:stable-slim',
workDir: '/app',
exposedPorts: [8080],
buildSteps: [
'COPY pubspec.* ./',
'RUN dart pub get',
'COPY . .',
'RUN dart compile exe bin/server.dart -o bin/server'
],
runCommand: './bin/server',
multistage: true
},
envVars: {
'PORT': '8080',
'ENVIRONMENT': 'development',
'LOG_LEVEL': 'info',
'DATABASE_URL': 'postgresql://user:password@localhost:5432/dbname',
'JWT_SECRET': 'your-secret-key',
'REDIS_URL': 'redis://localhost:6379',
'API_PREFIX': '/api/v1'
}
};
super(config);
}
async generateLanguageFiles(projectPath, options) {
// Generate pubspec.yaml
await this.generatePubspec(projectPath, options);
// Generate analysis_options.yaml
await this.generateAnalysisOptions(projectPath);
// Generate .gitignore
await this.generateDartGitignore(projectPath);
// Create directory structure
const directories = [
'bin',
'lib',
'lib/src',
'lib/src/controllers',
'lib/src/services',
'lib/src/models',
'lib/src/middleware',
'lib/src/utils',
'lib/src/config',
'lib/src/database',
'lib/src/routes',
'test',
'test/unit',
'test/integration',
'test/fixtures'
];
for (const dir of directories) {
await fs_1.promises.mkdir(path.join(projectPath, dir), { recursive: true });
}
}
async generatePubspec(projectPath, options) {
const pubspecContent = `name: ${options.name}
description: A ${this.config.framework} server application built with Re-Shell CLI
version: 1.0.0
publish_to: none
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
${this.getFrameworkDependencies().map(dep => ' ' + dep).join('\n')}
dev_dependencies:
test: ^1.24.0
coverage: ^1.6.0
mockito: ^5.4.0
build_runner: ^2.4.0
lints: ^3.0.0
${this.getDevDependencies().join('\n ')}
executables:
server: server
`;
await fs_1.promises.writeFile(path.join(projectPath, 'pubspec.yaml'), pubspecContent);
}
async generateAnalysisOptions(projectPath) {
const analysisOptions = `include: package:lints/recommended.yaml
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
exclude:
- build/**
- '**.g.dart'
- '**.freezed.dart'
- test/.test_coverage.dart
errors:
missing_required_param: error
missing_return: error
todo: info
deprecated_member_use_from_same_package: info
linter:
rules:
- always_declare_return_types
- always_put_control_body_on_new_line
- always_put_required_named_parameters_first
- always_require_non_null_named_parameters
- annotate_overrides
- avoid_bool_literals_in_conditional_expressions
- avoid_catching_errors
- avoid_classes_with_only_static_members
- avoid_empty_else
- avoid_escaping_inner_quotes
- avoid_field_initializers_in_const_classes
- avoid_function_literals_in_foreach_calls
- avoid_init_to_null
- avoid_null_checks_in_equality_operators
- avoid_print
- avoid_private_typedef_functions
- avoid_redundant_argument_values
- avoid_relative_lib_imports
- avoid_return_types_on_setters
- avoid_returning_null_for_void
- avoid_setters_without_getters
- avoid_shadowing_type_parameters
- avoid_single_cascade_in_expression_statements
- avoid_slow_async_io
- avoid_types_as_parameter_names
- avoid_unnecessary_containers
- avoid_unused_constructor_parameters
- avoid_void_async
- await_only_futures
- camel_case_extensions
- camel_case_types
- cancel_subscriptions
- cast_nullable_to_non_nullable
- close_sinks
- collection_methods_unrelated_type
- constant_identifier_names
- control_flow_in_finally
- curly_braces_in_flow_control_structures
- depend_on_referenced_packages
- deprecated_consistency
- directives_ordering
- empty_catches
- empty_constructor_bodies
- empty_statements
- eol_at_end_of_file
- exhaustive_cases
- file_names
- flutter_style_todos
- hash_and_equals
- implementation_imports
- iterable_contains_unrelated_type
- join_return_with_assignment
- leading_newlines_in_multiline_strings
- library_names
- library_prefixes
- library_private_types_in_public_api
- lines_longer_than_80_chars
- list_remove_unrelated_type
- literal_only_boolean_expressions
- missing_whitespace_between_adjacent_strings
- no_adjacent_strings_in_list
- no_duplicate_case_values
- no_leading_underscores_for_library_prefixes
- no_leading_underscores_for_local_identifiers
- no_logic_in_create_state
- no_runtimeType_toString
- non_constant_identifier_names
- noop_primitive_operations
- null_check_on_nullable_type_parameter
- null_closures
- omit_local_variable_types
- one_member_abstracts
- only_throw_errors
- overridden_fields
- package_api_docs
- package_names
- package_prefixed_library_names
- parameter_assignments
- prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
- prefer_constructors_over_static_methods
- prefer_contains
- prefer_equal_for_default_values
- prefer_final_fields
- prefer_final_in_for_each
- prefer_final_locals
- prefer_for_elements_to_map_fromIterable
- prefer_function_declarations_over_variables
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
- prefer_int_literals
- prefer_interpolation_to_compose_strings
- prefer_is_empty
- prefer_is_not_empty
- prefer_is_not_operator
- prefer_iterable_whereType
- prefer_null_aware_method_calls
- prefer_null_aware_operators
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- prefer_void_to_null
- provide_deprecation_message
- public_member_api_docs
- recursive_getters
- require_trailing_commas
- secure_pubspec_urls
- sized_box_for_whitespace
- sized_box_shrink_expand
- slash_for_doc_comments
- sort_child_properties_last
- sort_constructors_first
- sort_pub_dependencies
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
- tighten_type_of_initializing_formals
- type_annotate_public_apis
- type_init_formals
- unawaited_futures
- unnecessary_await_in_return
- unnecessary_brace_in_string_interps
- unnecessary_const
- unnecessary_constructor_name
- unnecessary_getters_setters
- unnecessary_lambdas
- unnecessary_late
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_checks
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_overrides
- unnecessary_parenthesis
- unnecessary_raw_strings
- unnecessary_statements
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this
- unnecessary_to_list_in_spreads
- unrelated_type_equality_checks
- unsafe_html
- use_build_context_synchronously
- use_full_hex_values_for_flutter_colors
- use_function_type_syntax_for_parameters
- use_if_null_to_convert_nulls_to_bools
- use_is_even_rather_than_modulo
- use_key_in_widget_constructors
- use_late_for_private_fields_and_variables
- use_named_constants
- use_raw_strings
- use_rethrow_when_possible
- use_setters_to_change_properties
- use_string_buffers
- use_super_parameters
- use_test_throws_matchers
- use_to_and_as_if_applicable
- valid_regexps
- void_checks
`;
await fs_1.promises.writeFile(path.join(projectPath, 'analysis_options.yaml'), analysisOptions);
}
async generateDartGitignore(projectPath) {
const gitignore = `# Dart/Pub related
.dart_tool/
.packages
pubspec.lock
build/
doc/api/
# IDE
.idea/
.vscode/
*.iml
*.ipr
*.iws
.DS_Store
# Test coverage
coverage/
test/.test_coverage.dart
# Environment
.env
.env.*
# Logs
*.log
# Generated files
*.g.dart
*.freezed.dart
*.gr.dart
# Temporary files
*.tmp
*.temp
.cache/
# macOS
.DS_Store
# Linux
*~
# Windows
Thumbs.db
ehthumbs.db
Desktop.ini
# Build output
server
server.exe
*.exe
*.dll
*.so
*.dylib
`;
await fs_1.promises.writeFile(path.join(projectPath, '.gitignore'), gitignore);
}
async generateTestStructure(projectPath, options) {
// Test helper
const testHelper = `import 'dart:io';
import 'package:test/test.dart';
import 'package:http/http.dart' as http;
/// Base URL for test server
String get baseUrl => 'http://localhost:\${testPort}';
/// Test server port
int get testPort => int.parse(Platform.environment['TEST_PORT'] ?? '8081');
/// Create test HTTP client
http.Client createTestClient() {
return http.Client();
}
/// Test authentication helper
Future<String> getAuthToken(http.Client client, {
String email = 'test@example.com',
String password = 'password123',
}) async {
final response = await client.post(
Uri.parse('\$baseUrl/api/v1/auth/login'),
headers: {'Content-Type': 'application/json'},
body: '{"email": "\$email", "password": "\$password"}',
);
if (response.statusCode != 200) {
throw Exception('Failed to authenticate: \${response.body}');
}
final data = jsonDecode(response.body);
return data['token'];
}
/// Authenticated request helper
Future<http.Response> authenticatedRequest(
http.Client client,
String method,
String path, {
String? token,
Map<String, String>? headers,
String? body,
}) async {
token ??= await getAuthToken(client);
final uri = Uri.parse('\$baseUrl\$path');
final requestHeaders = {
'Authorization': 'Bearer \$token',
'Content-Type': 'application/json',
...?headers,
};
switch (method.toUpperCase()) {
case 'GET':
return client.get(uri, headers: requestHeaders);
case 'POST':
return client.post(uri, headers: requestHeaders, body: body);
case 'PUT':
return client.put(uri, headers: requestHeaders, body: body);
case 'DELETE':
return client.delete(uri, headers: requestHeaders);
default:
throw ArgumentError('Unsupported HTTP method: \$method');
}
}
/// Test data fixtures
class TestFixtures {
static Map<String, dynamic> get validUser => {
'email': 'test@example.com',
'password': 'password123',
'name': 'Test User',
};
static Map<String, dynamic> get invalidUser => {
'email': 'invalid-email',
'password': '123', // Too short
'name': '',
};
}
/// Custom test matchers
Matcher isValidationError() => allOf(
isA<Map>(),
containsPair('error', isA<Map>()),
);
Matcher hasStatus(int status) => allOf(
isA<http.Response>(),
predicate<http.Response>(
(r) => r.statusCode == status,
'has status code \$status',
),
);
`;
await fs_1.promises.writeFile(path.join(projectPath, 'test/test_helper.dart'), testHelper);
// Example unit test
const exampleUnitTest = `import 'package:test/test.dart';
import 'package:${options.name}/src/utils/validators.dart';
void main() {
group('Validators', () {
group('email validation', () {
test('accepts valid email addresses', () {
expect(isValidEmail('user@example.com'), isTrue);
expect(isValidEmail('user.name@example.co.uk'), isTrue);
expect(isValidEmail('user+tag@example.com'), isTrue);
});
test('rejects invalid email addresses', () {
expect(isValidEmail(''), isFalse);
expect(isValidEmail('invalid'), isFalse);
expect(isValidEmail('@example.com'), isFalse);
expect(isValidEmail('user@'), isFalse);
expect(isValidEmail('user @example.com'), isFalse);
});
});
group('password validation', () {
test('accepts valid passwords', () {
expect(isValidPassword('password123'), isTrue);
expect(isValidPassword('P@ssw0rd!'), isTrue);
});
test('rejects invalid passwords', () {
expect(isValidPassword(''), isFalse);
expect(isValidPassword('short'), isFalse);
expect(isValidPassword('1234567'), isFalse);
});
});
});
}
`;
await fs_1.promises.writeFile(path.join(projectPath, 'test/unit/validators_test.dart'), exampleUnitTest);
// Integration test example
const integrationTest = `import 'dart:convert';
import 'package:test/test.dart';
import 'package:http/http.dart' as http;
import '../test_helper.dart';
void main() {
late http.Client client;
setUpAll(() {
client = createTestClient();
});
tearDownAll(() {
client.close();
});
group('Health Check', () {
test('GET /health returns healthy status', () async {
final response = await client.get(Uri.parse('\$baseUrl/health'));
expect(response, hasStatus(200));
final data = jsonDecode(response.body);
expect(data['status'], equals('healthy'));
expect(data['timestamp'], isNotNull);
expect(data['version'], isNotNull);
});
test('GET /ready returns ready status', () async {
final response = await client.get(Uri.parse('\$baseUrl/ready'));
expect(response, hasStatus(200));
final data = jsonDecode(response.body);
expect(data['ready'], isTrue);
});
});
group('Authentication', () {
test('POST /auth/register creates new user', () async {
final user = {
'email': 'newuser\${DateTime.now().millisecondsSinceEpoch}@example.com',
'password': 'password123',
'name': 'New User',
};
final response = await client.post(
Uri.parse('\$baseUrl/api/v1/auth/register'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(user),
);
expect(response, hasStatus(201));
final data = jsonDecode(response.body);
expect(data['user']['email'], equals(user['email']));
expect(data['token'], isNotEmpty);
expect(data['refreshToken'], isNotEmpty);
});
test('POST /auth/login authenticates user', () async {
final response = await client.post(
Uri.parse('\$baseUrl/api/v1/auth/login'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'email': TestFixtures.validUser['email'],
'password': TestFixtures.validUser['password'],
}),
);
expect(response, hasStatus(200));
final data = jsonDecode(response.body);
expect(data['token'], isNotEmpty);
expect(data['user']['email'], equals(TestFixtures.validUser['email']));
});
test('POST /auth/login rejects invalid credentials', () async {
final response = await client.post(
Uri.parse('\$baseUrl/api/v1/auth/login'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({
'email': 'wrong@example.com',
'password': 'wrongpassword',
}),
);
expect(response, hasStatus(401));
});
});
}
`;
await fs_1.promises.writeFile(path.join(projectPath, 'test/integration/auth_test.dart'), integrationTest);
}
async generateHealthCheck(projectPath) {
const healthController = `import 'dart:convert';
import 'dart:io';
import 'package:${this.config.framework.toLowerCase()}/${this.config.framework.toLowerCase()}.dart';
/// Health check controller
class HealthController {
static final _startTime = DateTime.now();
/// Comprehensive health check
static Future<Response> health(Request request) async {
final checks = await _performHealthChecks();
final allHealthy = checks.values.every((check) => check);
final response = {
'status': allHealthy ? 'healthy' : 'degraded',
'timestamp': DateTime.now().toIso8601String(),
'version': _getVersion(),
'uptime': DateTime.now().difference(_startTime).inSeconds,
'environment': Platform.environment['ENVIRONMENT'] ?? 'development',
'checks': checks,
};
return Response.ok(
jsonEncode(response),
headers: {'Content-Type': 'application/json'},
);
}
/// Simple readiness check
static Future<Response> ready(Request request) async {
return Response.ok(
jsonEncode({
'ready': true,
'timestamp': DateTime.now().millisecondsSinceEpoch,
}),
headers: {'Content-Type': 'application/json'},
);
}
/// Liveness check
static Future<Response> live(Request request) async {
return Response.ok(
jsonEncode({'alive': true}),
headers: {'Content-Type': 'application/json'},
);
}
static Future<Map<String, bool>> _performHealthChecks() async {
final checks = <String, bool>{};
// Database check
checks['database'] = await _checkDatabase();
// Redis check
checks['redis'] = await _checkRedis();
// File system check
checks['filesystem'] = _checkFilesystem();
// Memory check
checks['memory'] = _checkMemory();
return checks;
}
static Future<bool> _checkDatabase() async {
try {
// TODO: Implement actual database connectivity check
return true;
} catch (e) {
return false;
}
}
static Future<bool> _checkRedis() async {
try {
// TODO: Implement actual Redis connectivity check
return true;
} catch (e) {
return false;
}
}
static bool _checkFilesystem() {
try {
final tempDir = Directory.systemTemp;
final testFile = File('\${tempDir.path}/health_check_\${DateTime.now().millisecondsSinceEpoch}');
testFile.writeAsStringSync('test');
testFile.deleteSync();
return true;
} catch (e) {
return false;
}
}
static bool _checkMemory() {
// Check if memory usage is reasonable
// This is a simplified check
return true;
}
static String _getVersion() {
// Read from pubspec.yaml or environment
return Platform.environment['VERSION'] ?? '1.0.0';
}
}
`;
await fs_1.promises.writeFile(path.join(projectPath, 'lib/src/controllers/health_controller.dart'), healthController);
}
async generateAPIDocs(projectPath) {
// Generate OpenAPI specification
const openAPISpec = `openapi: 3.0.0
info:
title: ${this.config.framework} API
description: API documentation for ${this.config.framework} microservice built with Dart
version: 1.0.0
contact:
name: API Support
email: support@example.com
license:
name: MIT
url: https://opensource.org/licenses/MIT
servers:
- url: http://localhost:8080
description: Development server
- url: https://api.example.com
description: Production server
tags:
- name: Health
description: Health check endpoints
- name: Auth
description: Authentication endpoints
- name: Users
description: User management
paths:
/health:
get:
tags:
- Health
summary: Health check
description: Returns the health status of the service
responses:
'200':
description: Service is healthy
content:
application/json:
schema:
$ref: '#/components/schemas/HealthResponse'
/ready:
get:
tags:
- Health
summary: Readiness check
description: Returns whether the service is ready to accept requests
responses:
'200':
description: Service is ready
content:
application/json:
schema:
type: object
properties:
ready:
type: boolean
timestamp:
type: integer
/live:
get:
tags:
- Health
summary: Liveness check
description: Returns whether the service is alive
responses:
'200':
description: Service is alive
content:
application/json:
schema:
type: object
properties:
alive:
type: boolean
/api/v1/auth/register:
post:
tags:
- Auth
summary: Register new user
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RegisterRequest'
responses:
'201':
description: User registered successfully
content:
application/json:
schema:
$ref: '#/components/schemas/AuthResponse'
'400':
description: Invalid registration data
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/api/v1/auth/login:
post:
tags:
- Auth
summary: User login
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
responses:
'200':
description: Login successful
content:
application/json:
schema:
$ref: '#/components/schemas/AuthResponse'
'401':
description: Invalid credentials
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/api/v1/auth/refresh:
post:
tags:
- Auth
summary: Refresh access token
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- refreshToken
properties:
refreshToken:
type: string
responses:
'200':
description: Token refreshed successfully
content:
application/json:
schema:
$ref: '#/components/schemas/AuthResponse'
'401':
description: Invalid refresh token
/api/v1/users/me:
get:
tags:
- Users
summary: Get current user profile
security:
- bearerAuth: []
responses:
'200':
description: User profile
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'401':
description: Unauthorized
put:
tags:
- Users
summary: Update current user profile
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateUserRequest'
responses:
'200':
description: User updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'400':
description: Invalid update data
'401':
description: Unauthorized
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
schemas:
HealthResponse:
type: object
properties:
status:
type: string
enum: [healthy, degraded, unhealthy]
timestamp:
type: string
format: date-time
version:
type: string
uptime:
type: integer
description: Uptime in seconds
environment:
type: string
checks:
type: object
additionalProperties:
type: boolean
RegisterRequest:
type: object
required:
- email
- password
- name
properties:
email:
type: string
format: email
password:
type: string
minLength: 8
name:
type: string
minLength: 2
LoginRequest:
type: object
required:
- email
- password
properties:
email:
type: string
format: email
password:
type: string
AuthResponse:
type: object
properties:
user:
$ref: '#/components/schemas/User'
token:
type: string
refreshToken:
type: string
expiresIn:
type: integer
description: Token expiration time in seconds
User:
type: object
properties:
id:
type: string
email:
type: string
format: email
name:
type: string
avatarUrl:
type: string
nullable: true
isActive:
type: boolean
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
UpdateUserRequest:
type: object
properties:
name:
type: string
minLength: 2
avatarUrl:
type: string
nullable: true
ErrorResponse:
type: object
properties:
error:
type: object
properties:
code:
type: string
message:
type: string
details:
type: object
additionalProperties:
type: string
PaginatedResponse:
type: object
properties:
data:
type: array
items: {}
pagination:
type: object
properties:
page:
type: integer
limit:
type: integer
total:
type: integer
pages:
type: integer
`;
await fs_1.promises.writeFile(path.join(projectPath, 'docs/openapi.yaml'), openAPISpec);
}
async generateDockerFiles(projectPath, options) {
// Multi-stage Dockerfile
const dockerfile = `# ================================
# Build Stage
# ================================
FROM dart:stable AS builder
# Install dependencies for compilation
RUN apt-get update && apt-get install -y --no-install-recommends \\
ca-certificates \\
&& rm -rf /var/lib/apt/lists/*
# Set working directory
WORKDIR /app
# Copy pubspec files
COPY pubspec.* ./
# Get dependencies
RUN dart pub get
# Copy source code
COPY . .
# Ensure the project is sound
RUN dart analyze --fatal-infos --fatal-warnings
# Compile to native executable
RUN dart compile exe bin/server.dart -o bin/server
# ================================
# Runtime Stage
# ================================
FROM debian:bookworm-slim
# Install runtime dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \\
ca-certificates \\
&& rm -rf /var/lib/apt/lists/*
# Create non-root user
RUN groupadd -r dartuser && useradd -r -g dartuser dartuser
# Set working directory
WORKDIR /app
# Copy compiled executable from builder
COPY --from=builder /app/bin/server /app/bin/server
# Copy any static assets if needed
COPY --from=builder /app/public ./public
# Set ownership
RUN chown -R dartuser:dartuser /app
# Switch to non-root user
USER dartuser
# Expose port
EXPOSE ${options.port || 8080}
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
CMD curl -f http://localhost:${options.port || 8080}/health || exit 1
# Run the server
CMD ["./bin/server"]
`;
await fs_1.promises.writeFile(path.join(projectPath, 'Dockerfile'), dockerfile);
// Docker compose for local development
const dockerCompose = `version: '3.8'
services:
app:
build:
context: .
target: builder
ports:
- "\${PORT:-8080}:8080"
environment:
- ENVIRONMENT=development
- DATABASE_URL=postgresql://postgres:postgres@db:5432/${options.name}
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
volumes:
- .:/app
- /app/.dart_tool
- /app/build
command: dart run --enable-vm-service bin/server.dart
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=${options.name}
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
adminer:
image: adminer
ports:
- "8081:8080"
depends_on:
- db
volumes:
postgres_data:
redis_data:
`;
await fs_1.promises.writeFile(path.join(projectPath, 'docker-compose.yml'), dockerCompose);
// .dockerignore
const dockerignore = `.dart_tool/
.packages
build/
coverage/
doc/api/
.git/
.gitignore
.dockerignore
Dockerfile
docker-compose.yml
README.md
.env
.env.*
*.log
test/
analysis_options.yaml
`;
await fs_1.promises.writeFile(path.join(projectPath, '.dockerignore'), dockerignore);
}
async generateDocumentation(projectPath, options) {
// README.md already generated by base class
// API documentation guide
const apiGuide = `# API Documentation
## Overview
This ${this.config.framework} API provides a robust foundation for building microservices with Dart.
## Base URL
- Development: \`http://localhost:8080\`
- Production: \`https://api.example.com\`
## Authentication
The API uses JWT (JSON Web Tokens) for authentication. Include the token in the Authorization header:
\`\`\`
Authorization: Bearer <your-jwt-token>
\`\`\`
### Obtaining a Token
1. Register a new account:
\`\`\`bash
curl -X POST http://localhost:8080/api/v1/auth/register \\
-H "Content-Type: application/json" \\
-d '{
"email": "user@example.com",
"password": "securepassword",
"name": "John Doe"
}'
\`\`\`
2. Login with credentials:
\`\`\`bash
curl -X POST http://localhost:8080/api/v1/auth/login \\
-H "Content-Type: application/json" \\
-d '{
"email": "user@example.com",
"password": "securepassword"
}'
\`\`\`
3. Refresh an expired token:
\`\`\`bash
curl -X POST http://localhost:8080/api/v1/auth/refresh \\
-H "Content-Type: application/json" \\
-d '{
"refreshToken": "<your-refresh-token>"
}'
\`\`\`
## Rate Limiting
API endpoints are rate-limited to prevent abuse:
- Anonymous requests: 100 requests per hour
- Authenticated requests: 1000 requests per hour
Rate limit information is included in response headers:
- \`X-RateLimit-Limit\`: Maximum requests allowed
- \`X-RateLimit-Remaining\`: Requests remaining
- \`X-RateLimit-Reset\`: Unix timestamp when limit resets
## Error Handling
The API returns consistent error responses:
\`\`\`json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid input data",
"details": {
"email": "Invalid email format",
"password": "Password must be at least 8 characters"
}
}
}
\`\`\`
### Error Codes
- \`VALIDATION_ERROR\`: Input validation failed
- \`AUTHENTICATION_ERROR\`: Authentication failed
- \`AUTHORIZATION_ERROR\`: Insufficient permissions
- \`NOT_FOUND\`: Resource not found
- \`CONFLICT\`: Resource conflict (e.g., duplicate email)
- \`RATE_LIMIT_ERROR\`: Too many requests
- \`INTERNAL_ERROR\`: Server error
## Pagination
List endpoints support pagination using query parameters:
\`\`\`
GET /api/v1/users?page=2&limit=20&sort=createdAt&order=desc
\`\`\`
Parameters:
- \`page\`: Page number (default: 1)
- \`limit\`: Items per page (default: 20, max: 100)
- \`sort\`: Field to sort by
- \`order\`: Sort order (asc/desc)
Response includes pagination metadata:
\`\`\`json
{
"data": [...],
"pagination": {
"page": 2,
"limit": 20,
"total": 100,
"pages": 5
}
}
\`\`\`
## Filtering
Most list endpoints support filtering:
\`\`\`
GET /api/v1/users?search=john&status=active&createdAfter=2023-01-01
\`\`\`
## WebSocket Support
WebSocket connections are available for real-time features:
\`\`\`javascript
const ws = new WebSocket('ws://localhost:8080/ws');
ws.onopen = () => {
console.log('Connected');
ws.send(JSON.stringify({
type: 'subscribe',
channel: 'updates'
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received:', data);
};
\`\`\`
## Development
### Running Locally
1. Install dependencies:
\`\`\`bash
dart pub get
\`\`\`
2. Run database migrations:
\`\`\`bash
dart run bin/migrate.dart
\`\`\`
3. Start the server:
\`\`\`bash
dart run bin/server.dart
\`\`\`
### Running Tests
\`\`\`bash
# All tests
dart test
# With coverage
dart test --coverage=coverage
# Specific test file
dart test test/unit/validators_test.dart
\`\`\`
### Code Quality
\`\`\`bash
# Analyze code
dart analyze
# Format code
dart format .
# Fix issues
dart fix --apply
\`\`\`
## Deployment
### Environment Variables
Required environment variables:
- \`PORT\`: Server port (default: 8080)
- \`ENVIRONMENT\`: Environment mode (development/staging/production)
- \`DATABASE_URL\`: PostgreSQL connection string
- \`JWT_SECRET\`: Secret key for JWT signing
- \`REDIS_URL\`: Redis connection string (optional)
### Docker Deployment
\`\`\`bash
# Build image
docker build -t ${options.name} .
# Run container
docker run -p 8080:8080 \\
-e DATABASE_URL=postgresql://user:pass@host:5432/db \\
-e JWT_SECRET=your-secret-key \\
${options.name}
\`\`\`
### Health Monitoring
- \`GET /health\`: Comprehensive health check with subsystem status
- \`GET /ready\`: Simple readiness check
- \`GET /live\`: Liveness probe for container orchestration
## Security Best Practices
1. **Environment Variables**: Never commit secrets to version control
2. **HTTPS**: Always use HTTPS in production
3. **Input Validation**: All inputs are validated and sanitized
4. **SQL Injection**: Use parameterized queries
5. **Rate Limiting**: Protect against abuse
6. **CORS**: Configure allowed origins appropriately
7. **Security Headers**: Enable security headers in production
`;
await fs_1.promises.writeFile(path.join(projectPath, 'docs/API.md'), apiGuide);
// Development guide
const devGuide = `# Development Guide
## Prerequisites
- Dart SDK 3.0+
- Docker & Docker Compose (optional)
- PostgreSQL 15+
- Redis 7+ (optional)
## Project Structure
\`\`\`
${options.name}/
├── bin/
│ └── server.dart # Application entry point
├── lib/
│ ├── ${options.name}.dart # Public API exports
│ └── src/
│ ├── controllers/ # Request handlers
│ ├── models/ # Data models
│ ├── services/ # Business logic
│ ├── middleware/ # Middleware functions
│ ├── database/ # Database connections
│ ├── routes/ # Route definitions
│ ├── utils/ # Utilities
│ └── config/ # Configuration
├── test/
│ ├── unit/ # Unit tests
│ ├── integration/ # Integration tests
│ └── test_helper.dart # Test utilities
├── docs/ # Documentation
├── pubspec.yaml # Dependencies
└── analysis_options.yaml # Linting rules
\`\`\`
## Getting Started
1. **Clone the repository**:
\`\`\`bash
git clone <repository-url>
cd ${options.name}
\`\`\`
2. **Install dependencies**:
\`\`\`bash
dart pub get
\`\`\`
3. **Set up environment**:
\`\`\`bash
cp .env.example .env
# Edit .env with your configuration
\`\`\`
4. **Start services** (Docker):
\`\`\`bash
docker-compose up -d db redis
\`\`\`
5. **Run migrations**:
\`\`\`bash
dart run bin/migrate.dart
\`\`\`
6. **Start the server**:
\`\`\`bash
dart run bin/server.dart
\`\`\`
## Development Workflow
### Hot Reload
During development, the server automatically reloads on file changes:
\`\`\`bash
dart run --enable-vm-service --enable-asserts bin/server.dart
\`\`\`
### Database Migrations
Create a new migration:
\`\`\`bash
dart run bin/migrate.dart create add_users_table
\`\`\`
Run pending migrations:
\`\`\`bash
dart run bin/migrate.dart up
\`\`\`
Rollback migrations:
\`\`\`bash
dart run bin/migrate.dart down
\`\`\`
### Testing
Run all tests:
\`\`\`bash
dart test
\`\`\`
Run with coverage:
\`\`\`bash
dart test --coverage=coverage
dart pub global run coverage:format_coverage \\
--lcov \\
--in=coverage \\
--out=coverage/lcov.info \\
--report-on=lib
\`\`\`
Generate HTML coverage report:
\`\`\`bash
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.html
\`\`\`
### Code Quality
Analyze code:
\`\`\`bash
dart analyze
\`\`\`
Format code:
\`\`\`bash
dart format .
\`\`\`
Fix issues automatically:
\`\`\`bash
dart fix --apply
\`\`\`
### Debugging
1. **VS Code**: Use the Dart extension and launch configuration
2. **IntelliJ IDEA**: Use the Dart plugin and debug configuration
3. **Command line**: Use \`dart run --enable-vm-service\` and connect debugger
### Performance Profiling
1. Run with Observatory:
\`\`\`bash
dart run --enable-vm-service --pause-isolates-on-start bin/server.dart
\`\`\`
2. Open Observatory in browser (URL shown in console)
3. Use DevTools for profiling:
\`\`\`bash
dart pub global activate devtools
dart pub global run devtools
\`\`\`
## Coding Standards
### Naming Conventions
- **Classes**: PascalCase (e.g., \`UserController\`)
- **Files**: snake_case (e.g., \`user_controller.dart\`)
- **Variables/Functions**: camelCase (e.g., \`getUserById\`)
- **Constants**: SCREAMING_SNAKE_CASE (e.g., \`MAX_RETRY_COUNT\`)
### Project Organization
- One class per file
- Group related files in subdirectories
- Keep controllers thin, logic in services
- Use dependency injection
### Error Handling
\`\`\`dart
try {
final result = await riskyOperation();
return Response.ok(jsonEncode(result));
} on ValidationException catch (e) {
return Response.badRequest(body: jsonEncode({
'error': {
'code': 'VALIDATION_ERROR',
'message': e.message,
'details': e.details,
}
}));
} catch (e, stackTrace) {
logger.error('Unexpected error', error: e, stackTrace: stackTrace);
return Response.internalServerError(body: jsonEncode({
'error': {
'code': 'INTERNAL_ERROR',
'message': 'An unexpected error occurred',
}
}));
}
\`\`\`
### Documentation
- Use dartdoc comments for public APIs
- Include examples in documentation
- Keep README.md up to date
\`\`\`dart
/// Authenticates a user with email and password.
///
/// Returns an [AuthResponse] containing the user data and tokens.
/// Throws [ValidationException] if input is invalid.
/// Throws [AuthenticationException] if credentials are incorrect.
///
/// Example:
/// \`\`\`dart
/// final response = await authService.login(
/// email: 'user@example.com',
/// password: 'securepassword',
/// );
/// \`\`\`
Future<AuthResponse> login({
required String email,
required String password,
}) async {
// Implementation
}
\`\`\`
## Troubleshooting
### Common Issues
1. **Port already in use**:
\`\`\`bash
lsof -i :8080
kill -9 <PID>
\`\`\`
2. **Database connection failed**:
- Check DATABASE_URL format
- Ensure PostgreSQL is running
- Verify credentials and database exists
3. **Dependency conflicts**:
\`\`\`bash
dart pub cache clean
rm pubspec.lock
dart pub get
\`\`\`
4. **Tests failing**:
- Ensure test database is set up
- Check for hardcoded ports/URLs
- Run tests individually to isolate issues
### Getting Help
1. Check the [Dart documentation](https://dart.dev/guides)
2. Review ${this.config.framework} [documentation](https://pub.dev/packages/${this.config.framework.toLowerCase()})
3. Search for issues in the repository
4. Ask in the team chat or create an issue
`;
await fs_1.promises.writeFile(path.join(projectPath, 'docs/DEVELOPMENT.md'), devGuide);
}
// Utility methods
getLanguageSpecificIgnorePatterns() {
return [
'.dart_tool/',
'.packages',
'build/',
'doc/api/',
'*.g.dart',
'*.freezed.dart',
'coverage/',
'.test_coverage.dart'
];
}
getLanguagePrerequisites() {
return 'Dart SDK 3.0+ (install via https://dart.dev/get-dart)';
}
getInstallCommand() {
return 'dart pub get';
}
getDevCommand() {
return 'dart run --enable-vm-service --enable-asserts bin/server.dart';
}
getProdCommand() {
return 'dart compile exe bin/server.dart -o build/server && ./build/server';
}
getTestCommand() {
return 'dart test';
}
getCoverageCommand() {
return 'dart test --coverage=coverage';
}
getLintCommand() {
return 'dart analyze';
}
getBuildCommand() {
return 'dart compile exe bin/server.dart -o build/server';
}
getSetupAction() {
return 'dart-lang/setup-dart@v1';
}
async generateBuildScript(projectPath, options) {
const buildScriptContent = `#!/bin/bash
# Build script for Dart ${this.config.framework} application
set -e
echo "Building Dart ${this.config.framework} application..."
# Get dependencies
echo "Installing dependencies..."
dart pub get
# Run tests
echo "Running tests..."
dart test
# Analyze code
echo "Analyzing code..."
dart analyze
# Format code
echo "Formatting code..."
dart format --fix .
# Build executable
echo "Building executable..."
dart compile exe bin/server.dart -o build/server
echo "Build complete!"
echo "Run ./build/server to start the application"
`;
await fs_1.promises.mkdir(path.join(projectPath, 'scripts'), { recursive: true });
await fs_1.promises.writeFile(path.join(projectPath, 'scripts', 'build.sh'), buildScriptContent);
await fs_1.promises.chmod(path.join(projectPath, 'scripts', 'build.sh'), 0o755);
}
}
exports.DartBackendGenerator = DartBackendGenerator;