Animated Circular Progress Bar0
Font.Lokio
CLI

Lokio Syntax

Referensi lengkap sintaks Lokio: output, kondisi, loop, dan komentar di template


Contoh Nyata#

Kumpulan template siap pakai untuk berbagai stack. Tinggal copy ke lokio/templates/ dan sesuaikan.


React / Next.js — Component#

configs.yaml#

- name: component
  description: React functional component dengan TypeScript
  path: component.lokio
  output: src/components/<%= pascalCase(name) %>/<%= pascalCase(name) %>.tsx
  parameters:
    - name: name
      type: string
      required: true
      prompt: "Nama component:"
    - name: withProps
      type: boolean
      required: true
      prompt: "Butuh props interface?"
      default: true

component.lokio#

<%# @lokio required danger "Harus export default" export default %>
 
<% if (withProps === 'TRUE') { %>
interface <%= pascalCase(name) %>Props {
  // tambahkan props di sini
}
 
export default function <%= pascalCase(name) %>({ }: <%= pascalCase(name) %>Props) {
<% } else { %>
export default function <%= pascalCase(name) %>() {
<% } %>
  return (
    <div>
      <h1><%= titleCase(name) %></h1>
    </div>
  );
}

Generate:

lokio g component UserCard

Hasil src/components/UserCard/UserCard.tsx:

interface UserCardProps {
  // tambahkan props di sini
}
 
export default function UserCard({ }: UserCardProps) {
  return (
    <div>
      <h1>User Card</h1>
    </div>
  );
}

React / Next.js — Page + Hook (Multi-file)#

configs.yaml#

- name: page
  description: Next.js page dengan custom hook
  files:
    - path: page/index.lokio
      output: src/app/<%= kebabCase(name) %>/page.tsx
    - path: page/hook.lokio
      output: src/hooks/use<%= pascalCase(name) %>.ts
  parameters:
    - name: name
      type: string
      required: true
      prompt: "Nama halaman:"

page/index.lokio#

'use client';
 
import { use<%= pascalCase(name) %> } from '@/hooks/use<%= pascalCase(name) %>';
 
export default function <%= pascalCase(name) %>Page() {
  const { data, isLoading } = use<%= pascalCase(name) %>();
 
  if (isLoading) return <p>Loading...</p>;
 
  return (
    <main>
      <h1><%= titleCase(name) %></h1>
    </main>
  );
}

page/hook.lokio#

import { useState, useEffect } from 'react';
 
export function use<%= pascalCase(name) %>() {
  const [data, setData] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
 
  useEffect(() => {
    // fetch data here
    setIsLoading(false);
  }, []);
 
  return { data, isLoading };
}

Generate:

lokio g page dashboard

NestJS — Service + Controller (Multi-file)#

configs.yaml#

- name: module
  description: NestJS module lengkap (controller + service)
  files:
    - path: nestjs/service.lokio
      output: src/<%= kebabCase(name) %>/<%= kebabCase(name) %>.service.ts
    - path: nestjs/controller.lokio
      output: src/<%= kebabCase(name) %>/<%= kebabCase(name) %>.controller.ts
  parameters:
    - name: name
      type: string
      required: true
      prompt: "Nama module:"
    - name: type
      type: options
      required: true
      prompt: "Tipe endpoint:"
      options:
        - REST
        - GraphQL
      default: REST
  hooks:
    after_gen:
      - bun run build:check

nestjs/service.lokio#

import { Injectable } from '@nestjs/common';
 
@Injectable()
export class <%= pascalCase(name) %>Service {
  async findAll(): Promise<<%= pascalCase(name) %>[]> {
    return [];
  }
 
  async findOne(id: number): Promise<<%= pascalCase(name) %> | null> {
    return null;
  }
 
  async create(data: Partial<<%= pascalCase(name) %>>): Promise<<%= pascalCase(name) %>> {
    return data as <%= pascalCase(name) %>;
  }
 
  async update(id: number, data: Partial<<%= pascalCase(name) %>>): Promise<<%= pascalCase(name) %>> {
    return { id, ...data } as <%= pascalCase(name) %>;
  }
 
  async remove(id: number): Promise<void> {
    // delete logic here
  }
}

nestjs/controller.lokio#

<% if (type === 'REST') { %>
import { Controller, Get, Post, Put, Delete, Param, Body } from '@nestjs/common';
import { <%= pascalCase(name) %>Service } from './<%= kebabCase(name) %>.service';
 
@Controller('<%= kebabCase(name) %>')
export class <%= pascalCase(name) %>Controller {
  constructor(private readonly service: <%= pascalCase(name) %>Service) {}
 
  @Get()
  findAll() {
    return this.service.findAll();
  }
 
  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.service.findOne(+id);
  }
 
  @Post()
  create(@Body() body: any) {
    return this.service.create(body);
  }
 
  @Put(':id')
  update(@Param('id') id: string, @Body() body: any) {
    return this.service.update(+id, body);
  }
 
  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.service.remove(+id);
  }
}
<% } else { %>
import { Resolver, Query, Mutation, Args } from '@nestjs/graphql';
import { <%= pascalCase(name) %>Service } from './<%= kebabCase(name) %>.service';
 
@Resolver()
export class <%= pascalCase(name) %>Resolver {
  constructor(private readonly service: <%= pascalCase(name) %>Service) {}
 
  @Query(() => [String])
  <%= camelCase(plural(name)) %>() {
    return this.service.findAll();
  }
 
  @Mutation(() => String)
  create<%= pascalCase(name) %>() {
    return this.service.create({});
  }
}
<% } %>

Go — HTTP Handler#

configs.yaml#

- name: handler
  description: Go HTTP handler
  path: go/handler.lokio
  output: internal/handler/<%= snakeCase(name) %>_handler.go
  parameters:
    - name: name
      type: string
      required: true
      prompt: "Nama handler:"
    - name: withMiddleware
      type: boolean
      required: false
      prompt: "Butuh middleware auth?"
      default: false

go/handler.lokio#

package handler
 
import (
  "encoding/json"
  "net/http"
)
 
type <%= pascalCase(name) %>Handler struct {
  // dependencies
}
 
func New<%= pascalCase(name) %>Handler() *<%= pascalCase(name) %>Handler {
  return &<%= pascalCase(name) %>Handler{}
}
 
func (h *<%= pascalCase(name) %>Handler) GetAll(w http.ResponseWriter, r *http.Request) {
<% if (withMiddleware === 'TRUE') { %>
  // auth check
  if r.Header.Get("Authorization") == "" {
    http.Error(w, "Unauthorized", http.StatusUnauthorized)
    return
  }
<% } %>
  w.Header().Set("Content-Type", "application/json")
  json.NewEncoder(w).Encode(map[string]any{
    "data": []any{},
  })
}
 
func (h *<%= pascalCase(name) %>Handler) RegisterRoutes(mux *http.ServeMux) {
  mux.HandleFunc("GET /<%= kebabCase(name) %>", h.GetAll)
}

Flutter — Screen + ViewModel#

configs.yaml#

- name: screen
  description: Flutter screen dengan ChangeNotifier ViewModel
  files:
    - path: flutter/screen.lokio
      output: lib/features/<%= snakeCase(name) %>/view/<%= snakeCase(name) %>_screen.dart
    - path: flutter/viewmodel.lokio
      output: lib/features/<%= snakeCase(name) %>/viewmodel/<%= snakeCase(name) %>_viewmodel.dart
  parameters:
    - name: name
      type: string
      required: true
      prompt: "Nama screen:"
  hooks:
    after_gen:
      - flutter pub get

flutter/screen.lokio#

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../viewmodel/<%= snakeCase(name) %>_viewmodel.dart';
 
class <%= pascalCase(name) %>Screen extends StatelessWidget {
  const <%= pascalCase(name) %>Screen({super.key});
 
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => <%= pascalCase(name) %>ViewModel(),
      child: Scaffold(
        appBar: AppBar(title: const Text('<%= titleCase(name) %>')),
        body: Consumer<<%= pascalCase(name) %>ViewModel>(
          builder: (context, vm, _) {
            if (vm.isLoading) {
              return const Center(child: CircularProgressIndicator());
            }
            return const Center(child: Text('<%= titleCase(name) %>'));
          },
        ),
      ),
    );
  }
}

flutter/viewmodel.lokio#

import 'package:flutter/foundation.dart';
 
class <%= pascalCase(name) %>ViewModel extends ChangeNotifier {
  bool _isLoading = false;
  bool get isLoading => _isLoading;
 
  <%= pascalCase(name) %>ViewModel() {
    _init();
  }
 
  Future<void> _init() async {
    _isLoading = true;
    notifyListeners();
 
    // load data here
 
    _isLoading = false;
    notifyListeners();
  }
}

Batch Generate — Buat Banyak Service Sekaligus#

Buat file services.json:

[
  { "name": "User" },
  { "name": "Product" },
  { "name": "Order" },
  { "name": "Payment" },
  { "name": "Notification" }
]

Generate semua:

lokio gen service --batch services.json
✓ [1] name=User → src/services/UserService.ts
✓ [2] name=Product → src/services/ProductService.ts
✓ [3] name=Order → src/services/OrderService.ts
✓ [4] name=Payment → src/services/PaymentService.ts
✓ [5] name=Notification → src/services/NotificationService.ts
Batch complete! (5 succeeded, 0 failed)

lokio.yaml — Referensi Konfigurasi Lengkap#

project:
  name: my-awesome-project
  version: 1.0.0
 
# Opsional: Enterprise Hub
enterprise:
  enabled: true
  code: ENT-xxxxxx
  repo: my-awesome-project
 
# Opsional: konfigurasi lokio check
check:
  danger_mode: warn       # "warn" atau "block"
  templates:              # opsional: hanya validasi template ini
    - service
    - controller

lokio/configs.yaml — Referensi Template Lengkap#

templates:
  # Single-file template
  - name: service
    description: NestJS service class
    path: service.lokio                             # file blueprint
    output: src/services/<%= pascalCase(name) %>Service.ts   # output path
    parameters:
      - name: name
        type: string
        required: true
        prompt: "Nama service:"
      - name: type
        type: options
        required: true
        prompt: "Tipe:"
        options: [basic, repository]
        default: basic
    hooks:
      after_gen:
        - bun run format
 
  # Multi-file template
  - name: module
    description: NestJS module lengkap
    files:
      - path: module/service.lokio
        output: src/<%= kebabCase(name) %>/<%= kebabCase(name) %>.service.ts
      - path: module/controller.lokio
        output: src/<%= kebabCase(name) %>/<%= kebabCase(name) %>.controller.ts
    parameters:
      - name: name
        type: string
        required: true
        prompt: "Nama module:"