Skip to content

Commit 419720b

Browse files
committed
working on final readme
1 parent 117a4b3 commit 419720b

14 files changed

Lines changed: 270 additions & 11 deletions

README.md

Lines changed: 270 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ npm install modular-openscriptjs
5050
<!DOCTYPE html>
5151
<html lang="en">
5252
<head>
53+
<meta charset="UTF-8" />
54+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
5355
<title>My OpenScript App</title>
5456
</head>
5557
<body>
@@ -80,6 +82,8 @@ async function init() {
8082
init();
8183
```
8284

85+
_Note: In visual applications, you typically define routes that render components into the `rootElement`._
86+
8387
### Vite Configuration
8488

8589
Create `vite.config.js` to enable the OpenScript plugin, which handles tasks like component auto-discovery.
@@ -91,54 +95,309 @@ import { openScriptComponentPlugin } from "modular-openscriptjs/plugin";
9195
export default defineConfig({
9296
plugins: [
9397
openScriptComponentPlugin({
94-
// componentsDir: 'src/components' // Optional
98+
// Optional: Configure components directory if different from 'src/components'
99+
// componentsDir: 'src/components'
95100
}),
96101
],
97102
});
98103
```
99104

100105
### OpenScript Configuration (`ojs.config.js`)
101106

102-
Configure core services like the Router and Broker in a dedicated file.
107+
Create an `ojs.config.js` file in your project root. This file is where you configure the core services of OpenScript, such as the Router and Broker.
103108

104109
```javascript
105110
import { app, registerNodeDisposalCallback } from "modular-openscriptjs";
106-
import { appEvents } from "./events.js";
111+
import { appEvents } from "./events.js"; // We will create this in the next section
112+
113+
/*----------------------------------
114+
| Do OpenScript Configurations Here
115+
|----------------------------------
116+
*/
107117

108118
const router = app("router");
109119
const broker = app("broker");
110120

111121
export function configureApp() {
112-
// Router Config
122+
/*-----------------------------------
123+
| Set the global runtime prefix.
124+
| This prefix will be appended
125+
| to every path before resolution.
126+
| So ensure when defining routes,
127+
| you have it as the main prefix.
128+
|------------------------------------
129+
*/
113130
router.runtimePrefix("");
131+
132+
/**----------------------------------
133+
*
134+
* Set the default route path here
135+
* ----------------------------------
136+
*/
114137
router.basePath("");
115138

116-
// Broker Config
139+
/*--------------------------------
140+
| Set the logs clearing interval
141+
| for the broker to remove stale
142+
| events. (milliseconds)
143+
|--------------------------------
144+
*/
117145
broker.CLEAR_LOGS_AFTER = 30000;
146+
147+
/*--------------------------------
148+
| Set how old an event must be
149+
| to be deleted from the broker's
150+
| event log during logs clearing
151+
|--------------------------------
152+
*/
118153
broker.TIME_TO_GC = 10000;
154+
155+
/*-------------------------------------------
156+
| Start the garbage
157+
| collector for the broker
158+
|-------------------------------------------
159+
*/
119160
broker.removeStaleEvents();
120161

121-
// Enable logs in dev
162+
/*------------------------------------------
163+
| Should the broker display events
164+
| in the console as they are fired
165+
|------------------------------------------
166+
*/
122167
if (/^(127\.0\.0\.1|localhost|.*\.test)$/.test(router.url().hostname)) {
123-
broker.withLogs(true);
168+
broker.withLogs(false); // Enable logs for development
124169
}
125170

126-
// Strict Event Registration
171+
/**
172+
* ---------------------------------------------
173+
* Should the broker require events registration.
174+
* This ensures that only registered events
175+
* can be listened to and fire by the broker.
176+
* ---------------------------------------------
177+
*/
127178
broker.requireEventsRegistration(true);
179+
180+
/**
181+
* ---------------------------------------------
182+
* Register events with the broker
183+
* ---------------------------------------------
184+
*/
185+
128186
broker.registerEvents(appEvents);
129187

130-
// Register appEvents in container for easy access
188+
/**
189+
* ---------------------------------------------
190+
* Register core services in IoC container
191+
* ---------------------------------------------
192+
*/
131193
app().value("appEvents", appEvents);
132194

133-
// Cleanup callback for third-party libs
195+
/**
196+
* ---------------------------------------------
197+
* Node Disposal Callback
198+
* ---------------------------------------------
199+
* Use this to clean up external library instances
200+
* attached to DOM nodes when they are removed.
201+
*/
134202
registerNodeDisposalCallback((node) => {
135-
// e.g. clean up tooltips
203+
// Example: Dispose Bootstrap tooltips/popovers
204+
// if bootstrap.Tooltip.getInstance(node) {
205+
// bootstrap.Tooltip.getInstance(node).dispose();
206+
// }
136207
});
137208
}
138209

210+
// execute configuration
139211
configureApp();
140212
```
141213

214+
> **Note**: `registerNodeDisposalCallback` is crucial for preventing memory leaks when using third-party libraries that attach instances to DOM elements (like Bootstrap, Tippy.js, etc.). The callback **MUST** be synchronous and stateless.
215+
216+
> **Note**: In the configuration above, we are using `appEvents` imported from `events.js`. We will cover the creation of `events.js` and how to handle events in the subsequent sections.
217+
218+
### Define Application Events
219+
220+
OpenScript uses a centralized event broker. It's best practice to define all your application events in a single file, typically `events.js` (or `src/events.js`).
221+
222+
If you configured `broker.requireEventsRegistration(true)` in your `ojs.config.js`, only events defined here and registered will be allowed.
223+
224+
Create a `src/events.js` file:
225+
226+
```javascript
227+
/**
228+
* Application Events
229+
* Structure: Nested object where keys become namespaced event names
230+
* Example: app.started becomes "app:started"
231+
* todo.added -> "todo:added"
232+
*/
233+
export const appEvents = {
234+
app: {
235+
started: true,
236+
ready: true,
237+
},
238+
239+
// Example for a Todo App
240+
todo: {
241+
added: true,
242+
deleted: true,
243+
completed: true,
244+
245+
// Nested events
246+
needs: {
247+
refresh: true,
248+
},
249+
},
250+
251+
ui: {
252+
modal: {
253+
opened: true,
254+
closed: true,
255+
},
256+
},
257+
};
258+
```
259+
260+
This structure allows you to use `appEvents.todo.added` to refer to the event in your code, providing strict typing and avoiding magic strings.
261+
262+
### Configure Contexts
263+
264+
Contexts are used to manage state and share data across your application. Create an `ojs.contexts.js` file (or `src/ojs.contexts.js`) to initialize them.
265+
266+
```javascript
267+
import { context, putContext, app } from "modular-openscriptjs";
268+
269+
// 1. Register Context Keys
270+
// This reserves the keys for your contexts.
271+
// The second argument is a provider name (can be arbitrary for simple apps).
272+
putContext(["global", "todo"], "AppContext");
273+
274+
// 2. Export Context Instances for usage in other files
275+
export const gc = context("global");
276+
export const tc = context("todo");
277+
278+
// 3. Setup Function to Initialize States
279+
export function setupContexts() {
280+
// Initialize Global Context
281+
gc.states({
282+
appName: "My OpenScript App",
283+
isAuthenticated: false,
284+
user: null,
285+
});
286+
287+
// Initialize Todo Context
288+
tc.states({
289+
todos: [],
290+
filter: "all",
291+
});
292+
293+
// Add listeners if needed
294+
tc.todos.listener((state) => {
295+
console.log("Todos updated:", state.value);
296+
});
297+
298+
// 4. Register in IoC Container (Optional but recommended)
299+
// This allows you to retrieve contexts using app("gc") anywhere.
300+
app().value("gc", gc);
301+
app().value("tc", tc);
302+
303+
console.log("Contexts initialized");
304+
}
305+
```
306+
307+
Don't forget to import and call `setupContexts()` in your `main.js`:
308+
309+
```javascript
310+
// in main.js
311+
import "./ojs.config.js"; // 1. Configuration first
312+
import { setupContexts } from "./ojs.contexts.js"; // 2. Then Contexts
313+
314+
// ... other imports
315+
316+
setupContexts(); // Initialize contexts before mounting app
317+
```
318+
319+
### Configure Routes
320+
321+
Create an `ojs.routes.js` file (or `src/ojs.routes.js`) to define your application's routes.
322+
323+
This file typically handles two things:
324+
325+
1. Importing your page components.
326+
2. Defining the routes in the router.
327+
3. Defining a render helper to mount components into the root element.
328+
329+
```javascript
330+
import { app, ojs } from "modular-openscriptjs";
331+
import App from "./components/App.js"; // Your main layout component
332+
import HomePage from "./components/HomePage.js";
333+
334+
// Register components with the Markup Engine if they aren't auto-discovered
335+
ojs(App, HomePage);
336+
337+
export function setupRoutes() {
338+
const router = app("router");
339+
const h = app("h");
340+
341+
// Get the root element (assuming it was set in Global Context or we get it directly)
342+
const rootElement = document.getElementById("app-root");
343+
344+
/**
345+
* Helper to render a component to the root element.
346+
* We use h.App (or your layout component) to wrap the page.
347+
*
348+
* @param {Component} component - The page component to render.
349+
*/
350+
const appRender = (component) => {
351+
// h.App refers to the App component registered above.
352+
// 'parent' option tells the engine where to render this component.
353+
return h.App(component, {
354+
parent: rootElement,
355+
resetParent: true, // Clear the parent content before rendering
356+
reconcileParent: true, // Efficiently update the DOM if possible
357+
});
358+
};
359+
360+
// Define Routes
361+
362+
// Default route (redirects to /home)
363+
router.default(() => router.to("home"));
364+
365+
router.on(
366+
"/",
367+
() => {
368+
appRender(h.HomePage());
369+
},
370+
"home",
371+
);
372+
373+
// Example of another route
374+
// router.on("/about", () => appRender(h.AboutPage()), "about");
375+
376+
console.log("Routes configured");
377+
}
378+
```
379+
380+
Now, update your `main.js` to include the routes:
381+
382+
```javascript
383+
// in main.js
384+
import "./ojs.config.js";
385+
import { setupContexts } from "./ojs.contexts.js";
386+
import { setupRoutes } from "./ojs.routes.js"; // Import routes setup
387+
388+
// ...
389+
390+
setupContexts();
391+
392+
// Setup routes before starting the router
393+
setupRoutes();
394+
395+
const router = app("router");
396+
router.listen();
397+
```
398+
399+
You are now set up with the basic structure of an OpenScript application!
400+
142401
---
143402

144403
## 2. Core Architecture
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)