feat: Implement core view package components
This commit is contained in:
parent
f22dd8c495
commit
4b46d00dd4
4 changed files with 165 additions and 0 deletions
13
packages/view/lib/src/engine.dart
Normal file
13
packages/view/lib/src/engine.dart
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import 'view.dart';
|
||||||
|
|
||||||
|
/// Abstract engine interface for view rendering
|
||||||
|
abstract class Engine {
|
||||||
|
/// Get the evaluated contents of the view
|
||||||
|
Future<String> get(View view);
|
||||||
|
|
||||||
|
/// Add a piece of shared data to the engine
|
||||||
|
void share(String key, dynamic value);
|
||||||
|
|
||||||
|
/// Get all of the shared data for the engine
|
||||||
|
Map<String, dynamic> getShared();
|
||||||
|
}
|
18
packages/view/lib/src/exceptions.dart
Normal file
18
packages/view/lib/src/exceptions.dart
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/// Base exception for view related errors
|
||||||
|
class ViewException implements Exception {
|
||||||
|
final String message;
|
||||||
|
ViewException(this.message);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'ViewException: $message';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exception thrown when a view is not found
|
||||||
|
class ViewNotFoundException extends ViewException {
|
||||||
|
ViewNotFoundException(String message) : super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exception thrown when there's an error compiling the view
|
||||||
|
class ViewCompileException extends ViewException {
|
||||||
|
ViewCompileException(String message) : super(message);
|
||||||
|
}
|
82
packages/view/lib/src/factory.dart
Normal file
82
packages/view/lib/src/factory.dart
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'engine.dart';
|
||||||
|
import 'finder.dart';
|
||||||
|
import 'view.dart';
|
||||||
|
import 'exceptions.dart';
|
||||||
|
|
||||||
|
/// The view factory implementation
|
||||||
|
class Factory {
|
||||||
|
/// The view finder implementation
|
||||||
|
final ViewFinder finder;
|
||||||
|
|
||||||
|
/// The engine resolver callback
|
||||||
|
final FutureOr<Engine> Function(String) engineResolver;
|
||||||
|
|
||||||
|
/// The extension to engine mappings
|
||||||
|
final Map<String, String> extensions;
|
||||||
|
|
||||||
|
/// The shared data for the factory
|
||||||
|
final Map<String, dynamic> _shared = {};
|
||||||
|
|
||||||
|
/// The view composers
|
||||||
|
final Map<String, List<Function(View)>> _composers = {};
|
||||||
|
|
||||||
|
/// The view creators
|
||||||
|
final Map<String, List<Function(View)>> _creators = {};
|
||||||
|
|
||||||
|
Factory(this.finder, this.engineResolver, [this.extensions = const {}]);
|
||||||
|
|
||||||
|
/// Create a new view instance
|
||||||
|
Future<View> make(String path, [Map<String, dynamic> data = const {}]) async {
|
||||||
|
String normalizedPath = _normalizePath(path);
|
||||||
|
|
||||||
|
// Determine the engine for this view
|
||||||
|
String extension = path.split('.').last;
|
||||||
|
String engineName = extensions[extension] ?? 'file';
|
||||||
|
Engine engine = await engineResolver(engineName);
|
||||||
|
|
||||||
|
// Create the view instance
|
||||||
|
View view = View(this, engine, normalizedPath, {..._shared, ...data});
|
||||||
|
|
||||||
|
// Call creators
|
||||||
|
_callCreators(view);
|
||||||
|
|
||||||
|
// Call composers
|
||||||
|
_callComposers(view);
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a piece of shared data to the factory
|
||||||
|
void share(String key, dynamic value) {
|
||||||
|
_shared[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a view composer
|
||||||
|
void composer(String path, Function(View) callback) {
|
||||||
|
_composers.putIfAbsent(path, () => []).add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a view creator
|
||||||
|
void creator(String path, Function(View) callback) {
|
||||||
|
_creators.putIfAbsent(path, () => []).add(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call the creators for a given view
|
||||||
|
void _callCreators(View view) {
|
||||||
|
List<Function(View)>? creators = _creators[view.path];
|
||||||
|
creators?.forEach((creator) => creator(view));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call the composers for a given view
|
||||||
|
void _callComposers(View view) {
|
||||||
|
List<Function(View)>? composers = _composers[view.path];
|
||||||
|
composers?.forEach((composer) => composer(view));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Normalize the given view path
|
||||||
|
String _normalizePath(String path) {
|
||||||
|
return path.replaceAll('.', '/');
|
||||||
|
}
|
||||||
|
}
|
52
packages/view/lib/src/finder.dart
Normal file
52
packages/view/lib/src/finder.dart
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'exceptions.dart';
|
||||||
|
|
||||||
|
/// The view finder implementation
|
||||||
|
class ViewFinder {
|
||||||
|
/// The paths to search for views
|
||||||
|
final List<String> paths;
|
||||||
|
|
||||||
|
/// The file extensions to search for
|
||||||
|
final List<String> extensions;
|
||||||
|
|
||||||
|
/// Cache of found views
|
||||||
|
final Map<String, String> _cache = {};
|
||||||
|
|
||||||
|
ViewFinder(this.paths, [this.extensions = const ['.blade.php', '.php', '.html']]);
|
||||||
|
|
||||||
|
/// Find the given view in the filesystem
|
||||||
|
String find(String name) {
|
||||||
|
if (_cache.containsKey(name)) {
|
||||||
|
return _cache[name]!;
|
||||||
|
}
|
||||||
|
|
||||||
|
String result = _findInPaths(name);
|
||||||
|
_cache[name] = result;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the given view in the filesystem paths
|
||||||
|
String _findInPaths(String name) {
|
||||||
|
for (String location in paths) {
|
||||||
|
for (String extension in extensions) {
|
||||||
|
String viewPath = path.join(location, '$name$extension');
|
||||||
|
if (File(viewPath).existsSync()) {
|
||||||
|
return viewPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw ViewNotFoundException('View [$name] not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a location to the finder
|
||||||
|
void addLocation(String location) {
|
||||||
|
paths.add(location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flush the cache of located views
|
||||||
|
void flush() {
|
||||||
|
_cache.clear();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue