# Quarc Framework
**Lightweight Angular-inspired framework for embedded devices with limited memory**
Quarc is a modern frontend framework designed specifically for devices with constrained resources such as ESP32, Arduino, routers, and other embedded systems. Built with Angular's familiar syntax and patterns, it enables developers to create efficient, feature-rich web applications without the overhead of traditional frameworks.
## 🎯 Key Features
- **Angular-Inspired Syntax** - Familiar decorators, components, routing, and dependency injection
- **Minimal Bundle Size** - Optimized for devices with limited memory (5-25KB typical)
- **Native Web Components** - Uses browser's native Custom Elements API
- **Reactive Signals** - Built-in signal-based reactivity system
- **Advanced Build System** - Compile-time transformations minimize runtime overhead
- **Lazy Loading** - Load components on-demand to reduce initial bundle size
- **Router with Code Splitting** - Full-featured routing with lazy-loaded routes
- **Plugin Architecture** - Build and deploy plugins as separate bundles
- **External Scripts Support** - Load optional enhancements from remote servers
- **Dependency Injection** - Familiar DI system for services and components
## 🚀 Why Quarc?
If you've worked with Angular, you already know Quarc. The framework uses the same patterns and syntax, so there's **zero learning curve** for Angular developers. However, unlike Angular, Quarc is built from the ground up for resource-constrained environments:
- **Compile-time Processing** - Templates, styles, and decorators are processed during build, not runtime
- **Tree-shaking Friendly** - Only bundle what you actually use
- **No Virtual DOM** - Direct DOM manipulation for better performance and smaller size
- **Minimal Runtime** - Core framework is just a few kilobytes
## 📦 Installation
```bash
npm install @quarc/core @quarc/cli @quarc/router @quarc/platform-browser
```
## 🏗️ Project Structure
```
my-app/
├── src/
│ ├── app/
│ │ ├── app.component.ts
│ │ ├── app.component.html
│ │ ├── app.component.scss
│ │ └── routes.ts
│ ├── main.ts
│ └── main.scss
├── quarc.json
├── package.json
└── tsconfig.json
```
## 🎨 Quick Start
### 1. Create a Component
```typescript
import { Component, signal } from '@quarc/core';
@Component({
selector: 'app-counter',
templateUrl: './counter.component.html',
styleUrl: './counter.component.scss',
})
export class CounterComponent {
public count = signal(0);
public increment(): void {
this.count.update(c => c + 1);
}
}
```
**counter.component.html:**
```html
Count: {{ count() }}
```
### 2. Bootstrap Your Application
```typescript
import { bootstrapApplication } from '@quarc/platform-browser';
import { AppComponent } from './app/app.component';
import { provideRouter } from '@quarc/router';
import { routes } from './app/routes';
bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes)
]
});
```
### 3. Configure Build
**quarc.json:**
```json
{
"environment": "development",
"build": {
"minifyNames": false,
"styles": ["src/main.scss"],
"limits": {
"total": {
"warning": "50 KB",
"error": "200 KB"
},
"main": {
"warning": "20 KB",
"error": "60 KB"
}
}
},
"serve": {
"proxy": {
"/api/*": {
"target": "http://localhost:3000",
"changeOrigin": true
}
}
},
"environments": {
"development": {
"minifyNames": true,
"generateSourceMaps": false,
"compressed": true,
"devServer": {
"port": 4200
}
},
"production": {
"treatWarningsAsErrors": true,
"minifyNames": true,
"generateSourceMaps": false,
"compressed": true
}
}
}
```
### 4. Build and Serve
```bash
# Development server with hot reload
qu serve
# Production build
qu build --env production
```
## 📚 Core Concepts
### Components
Components are the building blocks of Quarc applications. They use the same `@Component` decorator as Angular:
```typescript
import { Component, OnInit, OnDestroy } from '@quarc/core';
@Component({
selector: 'my-component',
templateUrl: './my-component.html',
styleUrl: './my-component.scss',
imports: [ChildComponent], // Automatic dependency registration
})
export class MyComponent implements OnInit, OnDestroy {
ngOnInit() {
console.log('Component initialized');
}
ngOnDestroy() {
console.log('Component destroyed');
}
}
```
### Signals (Reactive State)
Quarc includes a built-in signals system for reactive state management:
```typescript
import { signal, computed, effect } from '@quarc/core';
export class DataComponent {
// Writable signal
public count = signal(0);
public name = signal('John');
// Computed signal (derived state)
public displayName = computed(() => `${this.name()} (${this.count()})`);
constructor() {
// Effect runs when dependencies change
effect(() => {
console.log('Count changed:', this.count());
});
}
public updateCount(): void {
this.count.update(c => c + 1);
}
}
```
### Template Syntax
Quarc supports Angular's template syntax including:
```html
@if (isLoggedIn) {
Welcome back!
} @else {
Please log in
}
@for (item of items(); track item.id) {
{{ item.name }}
}
```
### Routing
Full-featured routing with lazy loading support:
```typescript
import { Route } from '@quarc/router';
export const routes: Route[] = [
{
path: '',
component: HomeComponent,
},
{
path: 'about',
loadComponent: () => import('./about/about.component')
.then(m => m.AboutComponent),
},
{
path: 'user/:id',
component: UserComponent,
},
];
```
**Using Router in Components:**
```typescript
import { Router, ActivatedRoute } from '@quarc/router';
export class MyComponent {
constructor(
private router: Router,
private route: ActivatedRoute
) {
// Get route params
this.route.params.subscribe(params => {
console.log('User ID:', params['id']);
});
}
public navigate(): void {
this.router.navigate(['/about']);
}
}
```
### Dependency Injection
Familiar DI system for services:
```typescript
import { Injectable } from '@quarc/core';
@Injectable()
export class DataService {
public getData(): Promise {
return fetch('/api/data').then(r => r.json());
}
}
@Component({
selector: 'app-root',
template: '
{{ data }}
',
providers: [DataService], // Provide at component level
})
export class AppComponent {
public data: any;
constructor(private dataService: DataService) {
this.dataService.getData().then(d => this.data = d);
}
}
```
**Note:** Services are injected via constructor parameters. The framework uses compile-time metadata generation for dependency resolution.
### Directives
Directives are supported with `@Directive` decorator:
```typescript
import { Directive } from '@quarc/core';
@Directive({
selector: '[appHighlight]',
host: {
'(mouseenter)': 'onMouseEnter()',
'(mouseleave)': 'onMouseLeave()'
}
})
export class HighlightDirective {
constructor(private element: HTMLElement) {}
public onMouseEnter(): void {
this.element.style.backgroundColor = 'yellow';
}
public onMouseLeave(): void {
this.element.style.backgroundColor = '';
}
}
```
**Note:** `@HostBinding` and `@HostListener` decorators exist for TypeScript compatibility but are processed at compile-time. Use the `host` property in directive options for runtime behavior.
## 🔧 Advanced Features
### Development Server Proxy
The development server supports proxying HTTP requests to a backend server. This is useful when your frontend needs to communicate with an API during development.
**Configuration in quarc.json:**
```json
{
"serve": {
"proxy": {
"/api/*": {
"target": "http://192.168.1.100:8080",
"changeOrigin": true
},
"/auth/*": {
"target": "https://auth.example.com",
"changeOrigin": true,
"pathRewrite": {
"^/auth": "/api/v1/auth"
}
}
}
}
}
```
**Proxy Options:**
- **Pattern matching**: Use wildcards (`*`) to match request paths
- `/api/*` matches `/api/users`, `/api/data`, etc.
- `/v1/*/data` matches `/v1/users/data`, `/v1/products/data`, etc.
- **target**: Backend server URL (required)
- Can be HTTP or HTTPS
- Include protocol and host, optionally port
- **changeOrigin**: Set to `true` to change the `Host` header to match the target (recommended for most cases)
- **pathRewrite**: Object with regex patterns to rewrite request paths
- Key: regex pattern to match
- Value: replacement string
**Example Use Cases:**
```json
{
"serve": {
"proxy": {
"/api/*": {
"target": "http://localhost:3000",
"changeOrigin": true
}
}
}
}
```
When your app makes a request to `http://localhost:4200/api/users`, it will be proxied to `http://localhost:3000/api/users`.
**Perfect for ESP32 Development:**
When developing for ESP32, you can proxy API requests to the device:
```json
{
"serve": {
"proxy": {
"/api/*": {
"target": "http://192.168.1.50",
"changeOrigin": true
}
}
}
}
```
This allows you to develop your frontend locally while testing against the actual ESP32 backend.
### Lazy Loading
Components are automatically lazy-loaded when using route-based code splitting:
```typescript
// Only loaded when route is activated
{
path: 'dashboard',
loadComponent: () => import('./dashboard/dashboard.component')
.then(m => m.DashboardComponent),
}
```
### Automatic Dependency Registration
When you import components, they're automatically registered as Web Components:
```typescript
@Component({
selector: 'parent',
template: '',
imports: [ChildComponent], // Automatically registered
})
export class ParentComponent {}
```
See [DEPENDENCY_REGISTRATION.md](./DEPENDENCY_REGISTRATION.md) for details.
### View Encapsulation
Quarc supports three encapsulation modes for component styles:
```typescript
import { ViewEncapsulation } from '@quarc/core';
// Shadow DOM - True encapsulation using native Shadow DOM
@Component({
selector: 'shadow-component',
template: '