Skip to content

Latest commit

 

History

History
431 lines (339 loc) · 9.75 KB

File metadata and controls

431 lines (339 loc) · 9.75 KB

Getting Started with Synetics

This guide explains the minimal setup required to launch a Synetics application properly.

Required Dependencies

{
  "dependencies": {
    "@synetics/synetics.dev": "^0.9.0"
  },
  "devDependencies": {
    "@synetics/vite-plugin": "^0.8.0",
    "typescript": "^5.3.3",
    "vite": "^5.0.8"
  }
}

Why These Dependencies?

  • synetics.dev - Core framework with reactivity, hooks, context, and components
  • vite-plugin - Transforms JSX and enables deferred children for Context.Provider
  • vite - Build tool and dev server
  • typescript - Type safety and JSX transformation

Minimal File Structure

my-app/
├── index.html           # HTML entry point
├── package.json         # Dependencies
├── tsconfig.json        # TypeScript config
├── vite.config.ts       # Vite + Synetics plugin
└── src/
    ├── main.tsx         # Application bootstrap
    └── App.tsx          # Root component

1. HTML Entry Point (index.html)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My Synetics App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

Critical: The mount point (#app) must exist in the DOM before the script runs.

2. Vite Configuration (vite.config.ts)

import { defineConfig } from 'vite';
import { syneticsPlugin } from '@synetics/vite-plugin';

export default defineConfig({
  plugins: [syneticsPlugin()],
  esbuild: {
    jsxFactory: 'h',
    jsxFragment: 'Fragment',
    jsxInject: `import { h, Fragment } from '@synetics/synetics.dev'`,
  },
  optimizeDeps: {
    include: ['@synetics/synetics.dev'],
  },
});

Required Configuration:

  1. syneticsPlugin() - Transforms Context.Provider to defer children evaluation
  2. jsxFactory: 'h' - Uses Synetics's JSX pragma
  3. jsxInject - Auto-imports JSX functions in every file
  4. optimizeDeps - Pre-bundles Synetics for faster dev server

Without syneticsPlugin(), Context.Provider will fail with "must be used within Provider" errors.

3. TypeScript Configuration (tsconfig.json)

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "jsx": "preserve",
    "jsxFactory": "h",
    "jsxFragmentFactory": "Fragment",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Critical: "jsx": "preserve" - Vite handles JSX transformation.

4. Application Bootstrap (src/main.tsx)

import { AppContextProvider, bootstrapApp } from '@synetics/synetics.dev';
import { App } from './App';

// 1. Create application root with lifecycle hooks
const appRoot = bootstrapApp()
  .root('#app')
  .onMount((element) => {
    console.log('App mounted successfully', element);
  })
  .onError((error) => {
    console.error('App error:', error);
  })
  .build();

// 2. Wrap application with context provider
const app = (
  <AppContextProvider
    root={appRoot}
    context={{
      appName: 'My Synetics App',
      version: '1.0.0',
    }}
  >
    <App />
  </AppContextProvider>
);

// 3. Append to DOM (AppContextProvider handles root.mount() internally)
document.getElementById('app')?.appendChild(app);

Bootstrap Pattern Explained

Why both bootstrapApp() AND AppContextProvider?

They serve different purposes:

Component Purpose Responsibility
bootstrapApp() Creates application root Lifecycle hooks (onMount, onError), configuration
AppContextProvider Wraps app with context Provides AppContext, calls root.mount()

Execution Flow:

bootstrapApp()          ← Configure lifecycle hooks
    ↓
appRoot.build()         ← Build root configuration
    ↓
AppContextProvider      ← Wrap app + provide context
    ↓
root.mount()            ← Mount to DOM (called internally)
    ↓
Component Tree          ← Access context via useAppContext()

What is AppContext?

interface IAppContext {
  appName: string;
  version: string;
  [key: string]: any; // Your custom context properties
}

Access anywhere in your component tree:

import { useAppContext } from '@synetics/synetics.dev';

export const MyComponent = () => {
  const context = useAppContext();
  return <div>App: {context.appName}</div>;
};

5. Root Component (src/App.tsx)

import { createSignal, useAppContext } from '@synetics/synetics.dev';

export const App = function App() {
  const context = useAppContext();
  const [count, setCount] = createSignal(0);

  return (
    <div style={{ padding: '2rem' }}>
      <h1>Welcome to {context.appName}</h1>
      <button onClick={() => setCount(count() + 1)}>
        Count: {count()}
      </button>
    </div>
  );
};

Key Points:

  • Use createSignal() for reactive state
  • Access signal value with count() (getter)
  • Update with setCount(newValue) (setter)
  • Use useAppContext() to access application context

Running the Application

# Install dependencies
npm install
# or
pnpm install

# Start dev server
npm run dev

# Build for production
npm run build

# Preview production build
npm run preview

Add to package.json:

{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }
}

What Makes This Setup Complete?

✅ Framework Components Available

Once configured, you have access to all framework components:

import {
  // Reactivity
  createSignal,
  createEffect,
  createMemo,
  batch,

  // Hooks
  useState,
  useEffect,
  useMemo,
  useRef,

  // Context
  createContext,
  useContext,
  useAppContext,
  AppContextProvider,

  // Lifecycle
  bootstrapApp,
  onMount,
  onCleanup,

  // Portal
  Portal,
  PortalSlot,

  // Error Boundaries
  Tryer,
  Catcher,

  // State Management
  createStore,
  dispatch,
  select,

  // Resources (async)
  createResource,
} from '@synetics/synetics.dev';

✅ Context System Works

With syneticsPlugin(), Context.Provider children are wrapped in arrow functions at compile time:

// Your code:
<Context.Provider value={data}>
  <Child />
</Context.Provider>

// Transformed by plugin:
Context.Provider({
  value: data,
  children: () => jsx(Child, {})  // ← Deferred evaluation
})

This ensures Provider registers context before Child tries to access it via useContext().

✅ Proper Lifecycle Management

const appRoot = bootstrapApp()
  .root('#app')
  .onMount((element) => {
    // Called when app successfully mounts
    // Perfect for: analytics, feature flags, initial data fetch
  })
  .onError((error) => {
    // Called when app encounters errors
    // Perfect for: error reporting, fallback UI
  })
  .build();

Common Mistakes to Avoid

❌ Forgetting syneticsPlugin()

// ❌ WRONG - Context won't work
export default defineConfig({
  // Missing plugins: [syneticsPlugin()]
});

Result: useContext() throws "must be used within Provider"

❌ Using mount() Instead of AppContextProvider

// ❌ OLD WAY - No context, no lifecycle
import { mount } from '@synetics/synetics.dev';
mount(App, '#app');

Result: No AppContext, no lifecycle hooks, no proper initialization

❌ Skipping bootstrapApp()

// ❌ WRONG - AppContextProvider needs a root
const app = (
  <AppContextProvider
    root={undefined}  // ← Missing root!
    context={{}}
  >
    <App />
  </AppContextProvider>
);

Result: AppContextProvider has no mount target

❌ Wrong JSX Configuration

// ❌ WRONG - React JSX, not Synetics
{
  "compilerOptions": {
    "jsx": "react",           // ❌ Should be "preserve"
    "jsxFactory": "React.createElement"  // ❌ Should be "h"
  }
}

Result: JSX transformed incorrectly, runtime errors

Quick Start with CLI

Instead of manual setup, use the Synetics CLI:

npm install -g @synetics/cli

synetics create my-app --template basic
cd my-app
npm install
npm run dev

The CLI generates all configuration files with proper setup.

Summary: Minimal Checklist

  • Install @synetics/synetics.dev and @synetics/vite-plugin
  • Configure Vite with syneticsPlugin()
  • Set TypeScript jsx: "preserve" and jsxFactory: "h"
  • Create HTML with <div id="app">
  • Bootstrap app with bootstrapApp().root('#app').build()
  • Wrap app with <AppContextProvider root={appRoot} context={{...}}>
  • Append to DOM: document.getElementById('app')?.appendChild(app)

That's it! You now have a fully functional Synetics application with:

  • Fine-grained reactivity
  • Context system
  • Lifecycle management
  • Type safety
  • Hot module replacement

Next Steps

Need Help?