coralite 0.35.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -82
- package/dist/lib/client-runtime.d.ts +3 -10
- package/dist/lib/client-runtime.d.ts.map +1 -1
- package/dist/lib/client-runtime.js +62 -0
- package/dist/lib/client-runtime.js.map +7 -0
- package/dist/lib/collection.js +194 -0
- package/dist/lib/collection.js.map +7 -0
- package/dist/lib/config.js +43 -0
- package/dist/lib/config.js.map +7 -0
- package/dist/lib/coralite-element.d.ts +249 -0
- package/dist/lib/coralite-element.d.ts.map +1 -0
- package/dist/lib/coralite-element.js +523 -0
- package/dist/lib/coralite-element.js.map +7 -0
- package/dist/lib/coralite.d.ts +73 -42
- package/dist/lib/coralite.d.ts.map +1 -1
- package/dist/lib/coralite.js +2064 -0
- package/dist/lib/coralite.js.map +7 -0
- package/dist/lib/dom.d.ts.map +1 -1
- package/dist/lib/dom.js +496 -0
- package/dist/lib/dom.js.map +7 -0
- package/dist/lib/errors.js +21 -0
- package/dist/lib/errors.js.map +7 -0
- package/dist/lib/html.js +149 -0
- package/dist/lib/html.js.map +7 -0
- package/dist/lib/index.js +13 -13293
- package/dist/lib/index.js.map +4 -4
- package/dist/lib/parse.d.ts.map +1 -1
- package/dist/lib/parse.js +502 -0
- package/dist/lib/parse.js.map +7 -0
- package/dist/lib/plugin.d.ts +23 -30
- package/dist/lib/plugin.d.ts.map +1 -1
- package/dist/lib/plugin.js +107 -0
- package/dist/lib/plugin.js.map +7 -0
- package/dist/lib/render-helpers.d.ts +8 -0
- package/dist/lib/render-helpers.d.ts.map +1 -1
- package/dist/lib/render-helpers.js +194 -0
- package/dist/lib/render-helpers.js.map +7 -0
- package/dist/lib/script-manager.d.ts.map +1 -1
- package/dist/lib/script-manager.js +610 -0
- package/dist/lib/script-manager.js.map +7 -0
- package/dist/lib/style-transform.js +68 -0
- package/dist/lib/style-transform.js.map +7 -0
- package/dist/lib/tags.d.ts +1 -0
- package/dist/lib/tags.d.ts.map +1 -1
- package/dist/lib/tags.js +255 -0
- package/dist/lib/tags.js.map +7 -0
- package/dist/lib/type-helper.js +62 -0
- package/dist/lib/type-helper.js.map +7 -0
- package/dist/lib/utils.d.ts +14 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +649 -0
- package/dist/lib/utils.js.map +7 -0
- package/dist/plugins/metadata.d.ts +1 -3
- package/dist/plugins/metadata.d.ts.map +1 -1
- package/dist/plugins/metadata.js +25 -23
- package/dist/plugins/refs.d.ts +1 -3
- package/dist/plugins/refs.d.ts.map +1 -1
- package/dist/plugins/refs.js +33 -3
- package/dist/plugins/static-assets.d.ts +1 -3
- package/dist/plugins/static-assets.d.ts.map +1 -1
- package/dist/plugins/static-assets.js +29 -27
- package/dist/plugins/testing.d.ts +1 -3
- package/dist/plugins/testing.d.ts.map +1 -1
- package/dist/plugins/testing.js +30 -8
- package/dist/types/component.d.ts +4 -0
- package/dist/types/component.d.ts.map +1 -1
- package/dist/types/core.d.ts +58 -0
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/plugin.d.ts +168 -70
- package/dist/types/plugin.d.ts.map +1 -1
- package/dist/types/script.d.ts +22 -22
- package/dist/types/script.d.ts.map +1 -1
- package/llms.txt +81 -263
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -6,16 +6,18 @@
|
|
|
6
6
|
|
|
7
7
|
## Build for the Web, With the Web
|
|
8
8
|
|
|
9
|
-
Coralite is a
|
|
10
|
-
|
|
11
|
-
- **
|
|
12
|
-
|
|
13
|
-
- **
|
|
14
|
-
|
|
15
|
-
- **
|
|
16
|
-
|
|
17
|
-
- **
|
|
18
|
-
|
|
9
|
+
Coralite is a **Native-First**, strictly Server-Side Rendered (SSR) framework for building fast, accessible, and future-proof websites.
|
|
10
|
+
|
|
11
|
+
- **True Native Web Components with a "Flat" API**
|
|
12
|
+
No vanilla boilerplate. Use a clean `defineComponent` flat-options API (`attributes`, `data`, `getters`, `script`) to build powerful Custom Elements without the `class extends HTMLElement` friction.
|
|
13
|
+
- **The "Smart State, Dumb Template" Paradigm**
|
|
14
|
+
Templates are strictly declarative and "dumb"—no logic loops or dot-notation in HTML. All logic lives in pure JavaScript `getters` which receive a safe, Read-Only Proxy.
|
|
15
|
+
- **Scoped CSS without Shadow DOM**
|
|
16
|
+
Enjoy perfect style encapsulation using standard CSS. The Coralite compiler automatically injects unique instance identifiers and nests rules, avoiding the accessibility and global styling headaches of Shadow DOM.
|
|
17
|
+
- **Native Async Race-Condition Immunity**
|
|
18
|
+
Coralite's reactive engine handles asynchronous `data()` and `getters` with built-in version locks, ensuring your DOM never renders stale data from out-of-order Promise resolutions.
|
|
19
|
+
- **Isomorphic, Two-Phase Curried Plugins**
|
|
20
|
+
Extend the engine with a strict, typed boundary. Plugins use a two-phase currying pattern to inject heavy context during initialization, leaving you with a clean, scoped API at runtime.
|
|
19
21
|
|
|
20
22
|
---
|
|
21
23
|
|
|
@@ -113,82 +115,63 @@ Styles defined in the `<style>` block are automatically **scoped** to the compon
|
|
|
113
115
|
|
|
114
116
|
```html
|
|
115
117
|
<template id="user-card">
|
|
116
|
-
<div class="card"
|
|
117
|
-
|
|
118
|
+
<div class="card">
|
|
119
|
+
<!-- Templates only render flat keys. Logic belongs in getters! -->
|
|
120
|
+
<h2 ref="title">{{ formatName }}</h2>
|
|
118
121
|
<p>{{ userMeta }}</p>
|
|
119
|
-
|
|
122
|
+
|
|
123
|
+
<slot></slot>
|
|
124
|
+
|
|
120
125
|
<p class="stats">Logins: {{ loginCount }}</p>
|
|
121
126
|
</div>
|
|
122
127
|
</template>
|
|
123
128
|
|
|
124
129
|
<style>
|
|
125
|
-
/* These styles are automatically scoped to this component */
|
|
130
|
+
/* These styles are automatically scoped to this component instance */
|
|
126
131
|
.card {
|
|
127
132
|
border: 1px solid #eaeaea;
|
|
128
133
|
padding: 1.5rem;
|
|
129
134
|
border-radius: 8px;
|
|
130
|
-
cursor: pointer;
|
|
131
|
-
}
|
|
132
|
-
h2 {
|
|
133
|
-
color: coral;
|
|
134
|
-
}
|
|
135
|
-
.stats {
|
|
136
|
-
font-size: 0.85em;
|
|
137
|
-
color: gray;
|
|
138
135
|
}
|
|
136
|
+
h2 { color: coral; }
|
|
139
137
|
</style>
|
|
140
138
|
|
|
141
139
|
<script type="module">
|
|
142
140
|
import { defineComponent } from 'coralite'
|
|
143
|
-
import
|
|
141
|
+
import { userService } from './services.js'
|
|
144
142
|
|
|
145
143
|
export default defineComponent({
|
|
146
|
-
//
|
|
144
|
+
// ATTRIBUTES: Coerced from HTML (String, Number, Boolean)
|
|
147
145
|
attributes: {
|
|
148
|
-
|
|
149
|
-
lastName: { type: String, default: '' },
|
|
146
|
+
userId: { type: Number, default: 0 },
|
|
150
147
|
role: { type: String, default: 'Guest' }
|
|
151
148
|
},
|
|
152
149
|
|
|
153
|
-
//
|
|
154
|
-
async data(
|
|
155
|
-
|
|
156
|
-
const stats = await db.fetchUserStats(context.attributes.firstName)
|
|
157
|
-
|
|
150
|
+
// DATA: Async server-side fetching (Stripped from client bundle)
|
|
151
|
+
async data({ state }) {
|
|
152
|
+
const user = await userService.getById(state.userId)
|
|
158
153
|
return {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
loginCount:
|
|
154
|
+
firstName: user.firstName,
|
|
155
|
+
lastName: user.lastName,
|
|
156
|
+
loginCount: user.loginCount
|
|
162
157
|
}
|
|
163
158
|
},
|
|
164
159
|
|
|
165
|
-
//
|
|
160
|
+
// GETTERS: Pure, sync derived state (Read-Only Proxy)
|
|
166
161
|
getters: {
|
|
167
|
-
// state = attributes + data
|
|
168
162
|
formatName: (state) => `${state.firstName} ${state.lastName}`.trim(),
|
|
169
|
-
userMeta: (state) => `Role: ${state.role} |
|
|
163
|
+
userMeta: (state) => `Role: ${state.role} | ID: ${state.userId}`
|
|
170
164
|
},
|
|
171
165
|
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
},
|
|
166
|
+
// SCRIPT: Client-side controller (Read/Write Proxy)
|
|
167
|
+
script({ state, refs, signal }) {
|
|
168
|
+
// Use the 'refs' utility to get the unique DOM element
|
|
169
|
+
const titleEl = refs('title')
|
|
178
170
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
// Use the refs dictionary provided by the core plugin to target the element
|
|
184
|
-
const cardEl = root.querySelector(`[ref="${refs.card}"]`)
|
|
185
|
-
|
|
186
|
-
cardEl.addEventListener('click', () => {
|
|
187
|
-
alert(`Hello from the browser, ${state.formatName}!`)
|
|
188
|
-
|
|
189
|
-
// automatically updates the DOM, and re-runs any dependent getters.
|
|
190
|
-
state.loginCount++
|
|
191
|
-
}, { signal })
|
|
171
|
+
titleEl.addEventListener('click', () => {
|
|
172
|
+
// Mutations automatically trigger DOM updates & getter re-evaluations
|
|
173
|
+
state.loginCount++
|
|
174
|
+
}, { signal }) // Always use 'signal' for auto-cleanup!
|
|
192
175
|
}
|
|
193
176
|
})
|
|
194
177
|
</script>
|
|
@@ -200,42 +183,33 @@ Styles defined in the `<style>` block are automatically **scoped** to the compon
|
|
|
200
183
|
|
|
201
184
|
## Extending the Engine (`definePlugin`)
|
|
202
185
|
|
|
203
|
-
Coralite is
|
|
204
|
-
|
|
205
|
-
Plugins in Coralite use a functional, immutable API. Instead of mutating shared state, your plugin hooks simply return the specific data patches or pages you want to add. Core utilities are available via `coralite/utils`, and global engine state is accessible through `this` binding.
|
|
186
|
+
Coralite uses an isomorphic plugin architecture. A plugin is divided into `server` (Node.js) and `client` (Browser) blocks, using a **Two-Phase Curried** API to safely inject context.
|
|
206
187
|
|
|
207
188
|
```javascript
|
|
208
189
|
import { definePlugin } from 'coralite'
|
|
209
|
-
import { parseHTML } from 'coralite/utils'
|
|
210
190
|
|
|
211
|
-
export default function
|
|
191
|
+
export default function myPlugin(options = {}) {
|
|
212
192
|
return definePlugin({
|
|
213
|
-
name: '
|
|
193
|
+
name: 'my-plugin',
|
|
214
194
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
siteTitle: options.title || 'My Coralite Site',
|
|
221
|
-
metaDescription: 'Generated by Coralite'
|
|
195
|
+
server: {
|
|
196
|
+
// Phase 1: Global Context | Phase 2: Component Arguments
|
|
197
|
+
exports: {
|
|
198
|
+
getData: (context) => (query) => {
|
|
199
|
+
return { custom: 'data' }
|
|
222
200
|
}
|
|
201
|
+
},
|
|
202
|
+
onBeforeComponentRender: ({ state }) => {
|
|
203
|
+
state.pluginAdded = true
|
|
223
204
|
}
|
|
224
205
|
},
|
|
225
206
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (basePageResult.path.filename === 'index.html') {
|
|
233
|
-
return [
|
|
234
|
-
{
|
|
235
|
-
path: { filename: 'sitemap.xml', pathname: '/sitemap.xml' },
|
|
236
|
-
content: '<xml>...</xml>'
|
|
237
|
-
}
|
|
238
|
-
]
|
|
207
|
+
client: {
|
|
208
|
+
// Injects a utility directly into the component's 'script' context
|
|
209
|
+
context: {
|
|
210
|
+
myHelper: (globalCtx) => (instanceCtx) => () => {
|
|
211
|
+
console.log('Hello from component', instanceCtx.instanceId)
|
|
212
|
+
}
|
|
239
213
|
}
|
|
240
214
|
}
|
|
241
215
|
})
|
|
@@ -261,6 +235,7 @@ Coralite plugins tap into specific hooks that execute strictly during the SSG bu
|
|
|
261
235
|
### Rendering Hooks
|
|
262
236
|
- **`onBeforePageRender`**: A state-reducing hook to patch the page context just before HTML serialization.
|
|
263
237
|
- **`onAfterPageRender`**: An aggregator hook that runs after a page is rendered, allowing plugins to append additional pages (e.g., RSS feeds, sitemaps) to the build output.
|
|
238
|
+
- **`no-hydration`**: An attribute that can be added to any component tag to completely exclude it from client-side hydration while still rendering its content on the server.
|
|
264
239
|
|
|
265
240
|
---
|
|
266
241
|
|
|
@@ -8,20 +8,13 @@
|
|
|
8
8
|
* @param {string} options.base - The base URL for assets.
|
|
9
9
|
* @param {string} options.sharedChunkPath - The filename of the shared chunk.
|
|
10
10
|
* @param {Object} options.chunkManifest - Manifest mapping component IDs to their chunk filenames.
|
|
11
|
-
* @param {
|
|
12
|
-
* @param {string} options.mode - Build mode ('development' or 'development').
|
|
13
|
-
* @param {Object} [options.renderContext] - Build-time render context.
|
|
11
|
+
* @param {string[]} [options.declarativeTags=[]] - The declarative tags used.
|
|
14
12
|
* @returns {string} The generated JavaScript runtime.
|
|
15
13
|
*/
|
|
16
|
-
export function generateClientRuntime({ base, sharedChunkPath, chunkManifest,
|
|
14
|
+
export function generateClientRuntime({ base, sharedChunkPath, chunkManifest, declarativeTags }: {
|
|
17
15
|
base: string;
|
|
18
16
|
sharedChunkPath: string;
|
|
19
17
|
chunkManifest: any;
|
|
20
|
-
|
|
21
|
-
[x: string]: InstanceContext;
|
|
22
|
-
};
|
|
23
|
-
mode: string;
|
|
24
|
-
renderContext?: any;
|
|
18
|
+
declarativeTags?: string[];
|
|
25
19
|
}): string;
|
|
26
|
-
import type { InstanceContext } from '../types/index.js';
|
|
27
20
|
//# sourceMappingURL=client-runtime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-runtime.d.ts","sourceRoot":"","sources":["../../lib/client-runtime.js"],"names":[],"mappings":"AAAA;;GAEG;AAEH
|
|
1
|
+
{"version":3,"file":"client-runtime.d.ts","sourceRoot":"","sources":["../../lib/client-runtime.js"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;;;;;GASG;AACH,iGANG;IAAwB,IAAI,EAApB,MAAM;IACU,eAAe,EAA/B,MAAM;IACU,aAAa;IACV,eAAe,GAAlC,MAAM,EAAE;CAChB,GAAU,MAAM,CA2DlB"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
function generateClientRuntime({
|
|
2
|
+
base,
|
|
3
|
+
sharedChunkPath,
|
|
4
|
+
chunkManifest,
|
|
5
|
+
declarativeTags = []
|
|
6
|
+
}) {
|
|
7
|
+
return `
|
|
8
|
+
import { getClientContext, getSetups, createCoraliteClass, globalClientHooks } from '${base}assets/js/${sharedChunkPath}';
|
|
9
|
+
|
|
10
|
+
(async () => {
|
|
11
|
+
if (!window.__coralite_ready__) {
|
|
12
|
+
window.__coralite_ready__ = new Promise(resolve => { window.__coralite_resolve_ready__ = resolve; });
|
|
13
|
+
}
|
|
14
|
+
globalThis.executableScripts = [];
|
|
15
|
+
globalThis.globalAbortController = new AbortController();
|
|
16
|
+
|
|
17
|
+
const componentManifest = ${JSON.stringify(chunkManifest)};
|
|
18
|
+
const loadCache = {};
|
|
19
|
+
|
|
20
|
+
const loadComponent = (componentId) => {
|
|
21
|
+
if (!componentManifest[componentId]) return Promise.resolve();
|
|
22
|
+
if (customElements.get(componentId)) return Promise.resolve();
|
|
23
|
+
if (loadCache[componentId]) return loadCache[componentId];
|
|
24
|
+
|
|
25
|
+
loadCache[componentId] = (async () => {
|
|
26
|
+
const module = await import('${base}assets/js/' + componentManifest[componentId]);
|
|
27
|
+
if (module.default && module.default.componentId) {
|
|
28
|
+
if (!customElements.get(module.default.componentId)) {
|
|
29
|
+
customElements.define(module.default.componentId, createCoraliteClass(module.default, getClientContext, globalClientHooks));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
})();
|
|
33
|
+
return loadCache[componentId];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const declarativeTags = ${JSON.stringify(declarativeTags)};
|
|
37
|
+
const allTags = Object.keys(componentManifest);
|
|
38
|
+
const imperativeTags = allTags.filter(tag => !declarativeTags.includes(tag));
|
|
39
|
+
|
|
40
|
+
const loadPromises = declarativeTags.map(tagName => loadComponent(tagName));
|
|
41
|
+
await Promise.all(loadPromises);
|
|
42
|
+
|
|
43
|
+
if (typeof window.__coralite_resolve_ready__ === 'function') {
|
|
44
|
+
window.__coralite_resolve_ready__();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (imperativeTags.length > 0) {
|
|
48
|
+
const lazyLoad = () => imperativeTags.forEach(tagName => loadComponent(tagName));
|
|
49
|
+
|
|
50
|
+
if ('requestIdleCallback' in window) {
|
|
51
|
+
requestIdleCallback(lazyLoad, { timeout: 500 });
|
|
52
|
+
} else {
|
|
53
|
+
setTimeout(lazyLoad, 1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
})();
|
|
57
|
+
`.trim();
|
|
58
|
+
}
|
|
59
|
+
export {
|
|
60
|
+
generateClientRuntime
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=client-runtime.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../lib/client-runtime.js"],
|
|
4
|
+
"sourcesContent": ["/**\n * @import { InstanceContext } from '../types/index.js'\n */\n\n/**\n * Generates the client-side runtime script for Coralite pages.\n *\n * @param {Object} options\n * @param {string} options.base - The base URL for assets.\n * @param {string} options.sharedChunkPath - The filename of the shared chunk.\n * @param {Object} options.chunkManifest - Manifest mapping component IDs to their chunk filenames.\n * @param {string[]} [options.declarativeTags=[]] - The declarative tags used.\n * @returns {string} The generated JavaScript runtime.\n */\nexport function generateClientRuntime ({\n base,\n sharedChunkPath,\n chunkManifest,\n declarativeTags = []\n}) {\n return `\nimport { getClientContext, getSetups, createCoraliteClass, globalClientHooks } from '${base}assets/js/${sharedChunkPath}';\n\n(async () => {\n if (!window.__coralite_ready__) {\n window.__coralite_ready__ = new Promise(resolve => { window.__coralite_resolve_ready__ = resolve; });\n }\n globalThis.executableScripts = [];\n globalThis.globalAbortController = new AbortController();\n\n const componentManifest = ${JSON.stringify(chunkManifest)};\n const loadCache = {};\n\n const loadComponent = (componentId) => {\n if (!componentManifest[componentId]) return Promise.resolve();\n if (customElements.get(componentId)) return Promise.resolve();\n if (loadCache[componentId]) return loadCache[componentId];\n\n loadCache[componentId] = (async () => {\n const module = await import('${base}assets/js/' + componentManifest[componentId]);\n if (module.default && module.default.componentId) {\n if (!customElements.get(module.default.componentId)) {\n customElements.define(module.default.componentId, createCoraliteClass(module.default, getClientContext, globalClientHooks));\n }\n }\n })();\n return loadCache[componentId];\n };\n\n const declarativeTags = ${JSON.stringify(declarativeTags)};\n const allTags = Object.keys(componentManifest);\n const imperativeTags = allTags.filter(tag => !declarativeTags.includes(tag));\n\n const loadPromises = declarativeTags.map(tagName => loadComponent(tagName));\n await Promise.all(loadPromises);\n\n if (typeof window.__coralite_resolve_ready__ === 'function') {\n window.__coralite_resolve_ready__();\n }\n\n if (imperativeTags.length > 0) {\n const lazyLoad = () => imperativeTags.forEach(tagName => loadComponent(tagName));\n \n if ('requestIdleCallback' in window) {\n requestIdleCallback(lazyLoad, { timeout: 500 });\n } else {\n setTimeout(lazyLoad, 1);\n }\n }\n})();\n`.trim()\n}\n"],
|
|
5
|
+
"mappings": "AAcO,SAAS,sBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA,kBAAkB,CAAC;AACrB,GAAG;AACD,SAAO;AAAA,uFAC8E,IAAI,aAAa,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BASzF,KAAK,UAAU,aAAa,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qCAStB,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAUb,KAAK,UAAU,eAAe,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBzD,KAAK;AACP;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { getHtmlFile } from "./html.js";
|
|
3
|
+
import { access } from "node:fs/promises";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
function CoraliteCollection(options = { rootDir: "" }) {
|
|
6
|
+
this.rootDir = path.join(options.rootDir);
|
|
7
|
+
if (!existsSync(this.rootDir)) {
|
|
8
|
+
throw new Error("Root directory was not found: " + this.rootDir);
|
|
9
|
+
}
|
|
10
|
+
this.list = [];
|
|
11
|
+
this.listByPath = /* @__PURE__ */ Object.create(null);
|
|
12
|
+
this.collection = /* @__PURE__ */ Object.create(null);
|
|
13
|
+
this._onSet = options.onSet;
|
|
14
|
+
this._onUpdate = options.onUpdate;
|
|
15
|
+
this._onDelete = options.onDelete;
|
|
16
|
+
}
|
|
17
|
+
CoraliteCollection.prototype.setItem = async function(value) {
|
|
18
|
+
if (typeof value === "string") {
|
|
19
|
+
value = await this._loadByPath(value);
|
|
20
|
+
}
|
|
21
|
+
if (!value || !value.path) {
|
|
22
|
+
throw new Error("Valid HTMLData object must be provided");
|
|
23
|
+
}
|
|
24
|
+
const pathname = value.path.pathname;
|
|
25
|
+
const dirname = value.path.dirname;
|
|
26
|
+
const originalValue = this.collection[pathname];
|
|
27
|
+
const documentValue = value;
|
|
28
|
+
if (!originalValue) {
|
|
29
|
+
if (typeof this._onSet === "function") {
|
|
30
|
+
const result = await this._onSet(value);
|
|
31
|
+
if (!result) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
documentValue.result = result.value;
|
|
35
|
+
if (result.state) {
|
|
36
|
+
documentValue.state = result.state;
|
|
37
|
+
}
|
|
38
|
+
if (result.type === "page" || result.type === "component") {
|
|
39
|
+
documentValue.type = result.type;
|
|
40
|
+
}
|
|
41
|
+
if (typeof result.id === "string" && result.id) {
|
|
42
|
+
this.collection[result.id] = documentValue;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
this.collection[pathname] = documentValue;
|
|
46
|
+
if (!this.listByPath[dirname]) {
|
|
47
|
+
this.listByPath[dirname] = [];
|
|
48
|
+
}
|
|
49
|
+
if (!this.listByPath[dirname].includes(documentValue)) {
|
|
50
|
+
this.listByPath[dirname].push(documentValue);
|
|
51
|
+
}
|
|
52
|
+
if (!this.list.includes(documentValue)) {
|
|
53
|
+
this.list.push(documentValue);
|
|
54
|
+
}
|
|
55
|
+
} else {
|
|
56
|
+
return await this.updateItem(value);
|
|
57
|
+
}
|
|
58
|
+
return documentValue;
|
|
59
|
+
};
|
|
60
|
+
CoraliteCollection.prototype.deleteItem = async function(value) {
|
|
61
|
+
if (!value) {
|
|
62
|
+
throw new Error("Valid pathname must be provided");
|
|
63
|
+
}
|
|
64
|
+
let pathname;
|
|
65
|
+
let dirname;
|
|
66
|
+
let valuesByPath;
|
|
67
|
+
let originalValue;
|
|
68
|
+
if (typeof value !== "string" && value.path) {
|
|
69
|
+
pathname = value.path.pathname;
|
|
70
|
+
dirname = value.path.dirname;
|
|
71
|
+
valuesByPath = this.listByPath[dirname];
|
|
72
|
+
originalValue = value;
|
|
73
|
+
} else if (typeof value === "string") {
|
|
74
|
+
pathname = value;
|
|
75
|
+
dirname = path.dirname(pathname);
|
|
76
|
+
valuesByPath = this.listByPath[dirname];
|
|
77
|
+
originalValue = this.collection[pathname];
|
|
78
|
+
} else {
|
|
79
|
+
throw new Error("Valid pathname must be provided");
|
|
80
|
+
}
|
|
81
|
+
if (!originalValue) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (!valuesByPath) {
|
|
85
|
+
for (const key in this.collection) {
|
|
86
|
+
if (this.collection[key] === originalValue) {
|
|
87
|
+
delete this.collection[key];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (typeof this._onDelete === "function") {
|
|
93
|
+
await this._onDelete(originalValue);
|
|
94
|
+
}
|
|
95
|
+
for (const key in this.collection) {
|
|
96
|
+
if (this.collection[key] === originalValue) {
|
|
97
|
+
delete this.collection[key];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const listIndex = this.list.indexOf(originalValue);
|
|
101
|
+
const pathIndex = valuesByPath.indexOf(originalValue);
|
|
102
|
+
if (listIndex !== -1) {
|
|
103
|
+
this.list.splice(listIndex, 1);
|
|
104
|
+
}
|
|
105
|
+
if (pathIndex !== -1) {
|
|
106
|
+
valuesByPath.splice(pathIndex, 1);
|
|
107
|
+
}
|
|
108
|
+
if (valuesByPath.length === 0) {
|
|
109
|
+
delete this.listByPath[dirname];
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
CoraliteCollection.prototype.updateItem = async function(value) {
|
|
113
|
+
if (typeof value === "string") {
|
|
114
|
+
value = await this._loadByPath(value);
|
|
115
|
+
}
|
|
116
|
+
if (!value || !value.path) {
|
|
117
|
+
throw new Error("Valid HTMLData object must be provided");
|
|
118
|
+
}
|
|
119
|
+
const pathname = value.path.pathname;
|
|
120
|
+
const originalValue = this.collection[pathname];
|
|
121
|
+
if (!originalValue) {
|
|
122
|
+
return await this.setItem(value);
|
|
123
|
+
}
|
|
124
|
+
if (typeof this._onUpdate === "function") {
|
|
125
|
+
const result = await this._onUpdate(value, originalValue);
|
|
126
|
+
if (!result) {
|
|
127
|
+
return originalValue;
|
|
128
|
+
}
|
|
129
|
+
if (result && typeof result === "object") {
|
|
130
|
+
if (result.value !== void 0) {
|
|
131
|
+
originalValue.result = result.value;
|
|
132
|
+
} else {
|
|
133
|
+
originalValue.result = result;
|
|
134
|
+
}
|
|
135
|
+
if (result.type === "page" || result.type === "component") {
|
|
136
|
+
originalValue.type = result.type;
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
originalValue.result = result;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (value.content !== void 0) {
|
|
143
|
+
originalValue.content = value.content;
|
|
144
|
+
}
|
|
145
|
+
if (value.path && value.path !== originalValue.path) {
|
|
146
|
+
originalValue.path = value.path;
|
|
147
|
+
}
|
|
148
|
+
if (value.type) {
|
|
149
|
+
originalValue.type = value.type;
|
|
150
|
+
}
|
|
151
|
+
if (value.state !== void 0) {
|
|
152
|
+
originalValue.state = value.state;
|
|
153
|
+
}
|
|
154
|
+
return originalValue;
|
|
155
|
+
};
|
|
156
|
+
CoraliteCollection.prototype.getItem = function(id) {
|
|
157
|
+
if (!this.collection[id] && id.endsWith("html")) {
|
|
158
|
+
id = path.join(this.rootDir, id);
|
|
159
|
+
}
|
|
160
|
+
return this.collection[id];
|
|
161
|
+
};
|
|
162
|
+
CoraliteCollection.prototype.getListByPath = function(dirname) {
|
|
163
|
+
const list = this.listByPath[dirname];
|
|
164
|
+
if (list) {
|
|
165
|
+
return list.slice();
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
CoraliteCollection.prototype._loadByPath = async function(filepath) {
|
|
169
|
+
try {
|
|
170
|
+
await access(filepath);
|
|
171
|
+
} catch {
|
|
172
|
+
try {
|
|
173
|
+
filepath = path.join(this.rootDir, filepath);
|
|
174
|
+
await access(filepath);
|
|
175
|
+
} catch {
|
|
176
|
+
throw new Error("Could not find collection item: " + filepath);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const content = await getHtmlFile(filepath);
|
|
180
|
+
return {
|
|
181
|
+
type: "page",
|
|
182
|
+
content,
|
|
183
|
+
path: {
|
|
184
|
+
pathname: filepath,
|
|
185
|
+
dirname: path.dirname(filepath),
|
|
186
|
+
filename: path.basename(filepath)
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
var collection_default = CoraliteCollection;
|
|
191
|
+
export {
|
|
192
|
+
collection_default as default
|
|
193
|
+
};
|
|
194
|
+
//# sourceMappingURL=collection.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../lib/collection.js"],
|
|
4
|
+
"sourcesContent": ["import path from 'node:path'\nimport { getHtmlFile } from './html.js'\nimport { access } from 'node:fs/promises'\nimport { existsSync } from 'node:fs'\n\n/**\n * @import {\n * CoraliteCollectionEventDelete,\n * CoraliteCollectionEventSet,\n * CoraliteCollectionEventUpdate,\n * CoraliteCollectionItem,\n * HTMLData } from '../types/index.js'\n */\n\n/**\n * Represents a collection of documents with methods to organize and retrieve them.\n * Maintains three views: flat list, path-based grouping, and ID lookup.\n * @class\n * @param {Object} [options={}] - Options for configuring the collection\n * @param {string} [options.rootDir=''] - The root directory path for the collection\n * @param {CoraliteCollectionEventSet} [options.onSet] - Event handler for when documents are set\n * @param {CoraliteCollectionEventUpdate} [options.onUpdate] - Event handler for when documents are updated\n * @param {CoraliteCollectionEventDelete} [options.onDelete] - Event handler for when documents are deleted\n */\nfunction CoraliteCollection (options = { rootDir: '' }) {\n /**\n * Root directory where collection items are located\n */\n this.rootDir = path.join(options.rootDir)\n\n if (!existsSync(this.rootDir)) {\n throw new Error('Root directory was not found: ' + this.rootDir)\n }\n\n /**\n * An array of HTMLData objects representing the list of documents.\n * @type {CoraliteCollectionItem[]}\n */\n this.list = []\n\n /**\n * An object mapping paths to arrays of HTMLData objects.\n * Used for grouping documents by their file path.\n * @type {Object.<string, CoraliteCollectionItem[]>}\n */\n this.listByPath = Object.create(null)\n\n /**\n * An object mapping unique identifiers to HTMLData objects.\n * Used for quick lookup of documents by their identifier.\n * @type {Object.<string, CoraliteCollectionItem>}\n */\n this.collection = Object.create(null)\n\n /**\n * Callback triggered when setting a new item\n * @type {CoraliteCollectionEventSet | undefined}\n */\n this._onSet = options.onSet\n\n /**\n * Callback triggered when updating an existing item\n * @type {CoraliteCollectionEventUpdate | undefined}\n */\n this._onUpdate = options.onUpdate\n\n /**\n * Callback triggered when deleting an item\n * @type {CoraliteCollectionEventDelete | undefined}\n */\n this._onDelete = options.onDelete\n}\n\n/**\n * Adds or updates an HTMLData object in the collection and associated lists.\n * If the item already exists, it will be updated in all views.\n * @param {HTMLData|string} value - The HTMLData object to be added or updated.\n * @returns {Promise<CoraliteCollectionItem>} The modified document\n */\nCoraliteCollection.prototype.setItem = async function (value) {\n if (typeof value === 'string') {\n value = await this._loadByPath(value)\n }\n\n if (!value || !value.path) {\n throw new Error('Valid HTMLData object must be provided')\n }\n\n const pathname = value.path.pathname\n const dirname = value.path.dirname\n const originalValue = this.collection[pathname]\n\n /** @type {CoraliteCollectionItem} */\n const documentValue = value\n\n if (!originalValue) {\n // handle pre-set hook if defined\n if (typeof this._onSet === 'function') {\n const result = await this._onSet(value)\n\n // abort adding item\n if (!result) {\n return\n }\n\n documentValue.result = result.value\n\n if (result.state) {\n documentValue.state = result.state\n }\n\n if (result.type === 'page' || result.type === 'component') {\n documentValue.type = result.type\n }\n\n // update collection using ID from hook result if available\n if (typeof result.id === 'string' && result.id) {\n this.collection[result.id] = documentValue\n }\n }\n\n // always update the collection with current pathname\n this.collection[pathname] = documentValue\n\n // initialize directory list if it doesn't exist\n if (!this.listByPath[dirname]) {\n this.listByPath[dirname] = []\n }\n\n // add to both directory-specific and general lists\n // check if already added to avoid duplicates\n if (!this.listByPath[dirname].includes(documentValue)) {\n this.listByPath[dirname].push(documentValue)\n }\n if (!this.list.includes(documentValue)) {\n this.list.push(documentValue)\n }\n } else {\n return await this.updateItem(value)\n }\n\n return documentValue\n}\n\n/**\n * Removes an HTMLData object from the collection and associated lists.\n * Accepts either an HTMLData object or a pathname string.\n * @param {HTMLData | string} value - The HTMLData object or a pathname to be removed.\n * @throws {Error} If invalid input is provided\n */\nCoraliteCollection.prototype.deleteItem = async function (value) {\n if (!value) {\n throw new Error('Valid pathname must be provided')\n }\n\n let pathname\n let dirname\n let valuesByPath\n let originalValue\n\n if (typeof value !== 'string' && value.path) {\n // if the input is an HTMLData object, extract its pathname and directory name\n pathname = value.path.pathname\n dirname = value.path.dirname\n valuesByPath = this.listByPath[dirname]\n originalValue = value\n } else if (typeof value === 'string') {\n // if the input is a string, use it as the pathname and determine the directory name\n pathname = value\n dirname = path.dirname(pathname)\n valuesByPath = this.listByPath[dirname]\n originalValue = this.collection[pathname]\n } else {\n throw new Error('Valid pathname must be provided')\n }\n\n if (!originalValue) {\n // item not found, nothing to delete\n return\n }\n\n if (!valuesByPath) {\n // directory list doesn't exist, but we still need to clean up collection\n // This can happen if the item was stored under a different ID\n for (const key in this.collection) {\n if (this.collection[key] === originalValue) {\n delete this.collection[key]\n }\n }\n return\n }\n\n if (typeof this._onDelete === 'function') {\n await this._onDelete(originalValue)\n }\n\n // remove the document from the collection\n // also check if it's stored under a different ID (from hook result)\n for (const key in this.collection) {\n if (this.collection[key] === originalValue) {\n delete this.collection[key]\n }\n }\n\n // find and remove the document from the list and by-path grouping\n const listIndex = this.list.indexOf(originalValue)\n const pathIndex = valuesByPath.indexOf(originalValue)\n\n if (listIndex !== -1) {\n this.list.splice(listIndex, 1)\n }\n if (pathIndex !== -1) {\n valuesByPath.splice(pathIndex, 1)\n }\n\n // clean up empty directory arrays\n if (valuesByPath.length === 0) {\n delete this.listByPath[dirname]\n }\n}\n\n/**\n * Updates an existing HTMLData object in the collection.\n * If the document does not exist, it will be added using the set method.\n * @param {CoraliteCollectionItem|string} value - The HTMLData object to be updated or added.\n * @throws {Error} If invalid input is provided\n */\nCoraliteCollection.prototype.updateItem = async function (value) {\n if (typeof value === 'string') {\n value = await this._loadByPath(value)\n }\n\n if (!value || !value.path) {\n throw new Error('Valid HTMLData object must be provided')\n }\n\n const pathname = value.path.pathname\n const originalValue = this.collection[pathname]\n\n if (!originalValue) {\n // if the document does not exist, add it using the set method\n return await this.setItem(value)\n }\n\n if (typeof this._onUpdate === 'function') {\n const result = await this._onUpdate(value, originalValue)\n\n // abort update\n if (!result) {\n return originalValue\n }\n\n // handle callback result\n if (result && typeof result === 'object') {\n // if result has a value property, use it\n if (result.value !== undefined) {\n originalValue.result = result.value\n } else {\n originalValue.result = result\n }\n\n // update type if provided\n if (result.type === 'page' || result.type === 'component') {\n originalValue.type = result.type\n }\n } else {\n originalValue.result = result\n }\n }\n\n // update core state\n if (value.content !== undefined) {\n originalValue.content = value.content\n }\n\n // update path information if it changed\n if (value.path && value.path !== originalValue.path) {\n originalValue.path = value.path\n }\n\n // update type if explicitly set\n if (value.type) {\n originalValue.type = value.type\n }\n\n // update any additional state\n if (value.state !== undefined) {\n originalValue.state = value.state\n }\n\n return originalValue\n}\n\n/**\n * Retrieves an item by its unique identifier\n * @param {string} id - Unique identifier of the item\n * @returns {CoraliteCollectionItem | undefined} The found item or undefined\n */\nCoraliteCollection.prototype.getItem = function (id) {\n if (!this.collection[id] && id.endsWith('html')) {\n id = path.join(this.rootDir, id)\n }\n\n return this.collection[id]\n}\n\n/**\n * Retrieves a list of items grouped by directory path\n * @param {string} dirname - Directory name to look up\n * @returns {CoraliteCollectionItem[] | undefined} A copy of the item list or undefined\n */\nCoraliteCollection.prototype.getListByPath = function (dirname) {\n const list = this.listByPath[dirname]\n\n if (list) {\n return list.slice()\n }\n}\n\n/**\n * Loads a collection item by its file path.\n *\n * @param {string} filepath - The path to the collection item file\n * @returns {Promise<HTMLData>} A promise that resolves to the loaded item object\n * @throws {Error} If the file cannot be found at either the provided path or within the root directory\n */\nCoraliteCollection.prototype._loadByPath = async function (filepath) {\n try {\n await access(filepath)\n } catch {\n try {\n filepath = path.join(this.rootDir, filepath)\n\n await access(filepath)\n } catch {\n throw new Error('Could not find collection item: ' + filepath)\n }\n }\n\n const content = await getHtmlFile(filepath)\n\n return {\n type: 'page',\n content,\n path: {\n pathname: filepath,\n dirname: path.dirname(filepath),\n filename: path.basename(filepath)\n }\n }\n}\n\nexport default CoraliteCollection\n"],
|
|
5
|
+
"mappings": "AAAA,OAAO,UAAU;AACjB,SAAS,mBAAmB;AAC5B,SAAS,cAAc;AACvB,SAAS,kBAAkB;AAqB3B,SAAS,mBAAoB,UAAU,EAAE,SAAS,GAAG,GAAG;AAItD,OAAK,UAAU,KAAK,KAAK,QAAQ,OAAO;AAExC,MAAI,CAAC,WAAW,KAAK,OAAO,GAAG;AAC7B,UAAM,IAAI,MAAM,mCAAmC,KAAK,OAAO;AAAA,EACjE;AAMA,OAAK,OAAO,CAAC;AAOb,OAAK,aAAa,uBAAO,OAAO,IAAI;AAOpC,OAAK,aAAa,uBAAO,OAAO,IAAI;AAMpC,OAAK,SAAS,QAAQ;AAMtB,OAAK,YAAY,QAAQ;AAMzB,OAAK,YAAY,QAAQ;AAC3B;AAQA,mBAAmB,UAAU,UAAU,eAAgB,OAAO;AAC5D,MAAI,OAAO,UAAU,UAAU;AAC7B,YAAQ,MAAM,KAAK,YAAY,KAAK;AAAA,EACtC;AAEA,MAAI,CAAC,SAAS,CAAC,MAAM,MAAM;AACzB,UAAM,IAAI,MAAM,wCAAwC;AAAA,EAC1D;AAEA,QAAM,WAAW,MAAM,KAAK;AAC5B,QAAM,UAAU,MAAM,KAAK;AAC3B,QAAM,gBAAgB,KAAK,WAAW,QAAQ;AAG9C,QAAM,gBAAgB;AAEtB,MAAI,CAAC,eAAe;AAElB,QAAI,OAAO,KAAK,WAAW,YAAY;AACrC,YAAM,SAAS,MAAM,KAAK,OAAO,KAAK;AAGtC,UAAI,CAAC,QAAQ;AACX;AAAA,MACF;AAEA,oBAAc,SAAS,OAAO;AAE9B,UAAI,OAAO,OAAO;AAChB,sBAAc,QAAQ,OAAO;AAAA,MAC/B;AAEA,UAAI,OAAO,SAAS,UAAU,OAAO,SAAS,aAAa;AACzD,sBAAc,OAAO,OAAO;AAAA,MAC9B;AAGA,UAAI,OAAO,OAAO,OAAO,YAAY,OAAO,IAAI;AAC9C,aAAK,WAAW,OAAO,EAAE,IAAI;AAAA,MAC/B;AAAA,IACF;AAGA,SAAK,WAAW,QAAQ,IAAI;AAG5B,QAAI,CAAC,KAAK,WAAW,OAAO,GAAG;AAC7B,WAAK,WAAW,OAAO,IAAI,CAAC;AAAA,IAC9B;AAIA,QAAI,CAAC,KAAK,WAAW,OAAO,EAAE,SAAS,aAAa,GAAG;AACrD,WAAK,WAAW,OAAO,EAAE,KAAK,aAAa;AAAA,IAC7C;AACA,QAAI,CAAC,KAAK,KAAK,SAAS,aAAa,GAAG;AACtC,WAAK,KAAK,KAAK,aAAa;AAAA,IAC9B;AAAA,EACF,OAAO;AACL,WAAO,MAAM,KAAK,WAAW,KAAK;AAAA,EACpC;AAEA,SAAO;AACT;AAQA,mBAAmB,UAAU,aAAa,eAAgB,OAAO;AAC/D,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACnD;AAEA,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AAEJ,MAAI,OAAO,UAAU,YAAY,MAAM,MAAM;AAE3C,eAAW,MAAM,KAAK;AACtB,cAAU,MAAM,KAAK;AACrB,mBAAe,KAAK,WAAW,OAAO;AACtC,oBAAgB;AAAA,EAClB,WAAW,OAAO,UAAU,UAAU;AAEpC,eAAW;AACX,cAAU,KAAK,QAAQ,QAAQ;AAC/B,mBAAe,KAAK,WAAW,OAAO;AACtC,oBAAgB,KAAK,WAAW,QAAQ;AAAA,EAC1C,OAAO;AACL,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACnD;AAEA,MAAI,CAAC,eAAe;AAElB;AAAA,EACF;AAEA,MAAI,CAAC,cAAc;AAGjB,eAAW,OAAO,KAAK,YAAY;AACjC,UAAI,KAAK,WAAW,GAAG,MAAM,eAAe;AAC1C,eAAO,KAAK,WAAW,GAAG;AAAA,MAC5B;AAAA,IACF;AACA;AAAA,EACF;AAEA,MAAI,OAAO,KAAK,cAAc,YAAY;AACxC,UAAM,KAAK,UAAU,aAAa;AAAA,EACpC;AAIA,aAAW,OAAO,KAAK,YAAY;AACjC,QAAI,KAAK,WAAW,GAAG,MAAM,eAAe;AAC1C,aAAO,KAAK,WAAW,GAAG;AAAA,IAC5B;AAAA,EACF;AAGA,QAAM,YAAY,KAAK,KAAK,QAAQ,aAAa;AACjD,QAAM,YAAY,aAAa,QAAQ,aAAa;AAEpD,MAAI,cAAc,IAAI;AACpB,SAAK,KAAK,OAAO,WAAW,CAAC;AAAA,EAC/B;AACA,MAAI,cAAc,IAAI;AACpB,iBAAa,OAAO,WAAW,CAAC;AAAA,EAClC;AAGA,MAAI,aAAa,WAAW,GAAG;AAC7B,WAAO,KAAK,WAAW,OAAO;AAAA,EAChC;AACF;AAQA,mBAAmB,UAAU,aAAa,eAAgB,OAAO;AAC/D,MAAI,OAAO,UAAU,UAAU;AAC7B,YAAQ,MAAM,KAAK,YAAY,KAAK;AAAA,EACtC;AAEA,MAAI,CAAC,SAAS,CAAC,MAAM,MAAM;AACzB,UAAM,IAAI,MAAM,wCAAwC;AAAA,EAC1D;AAEA,QAAM,WAAW,MAAM,KAAK;AAC5B,QAAM,gBAAgB,KAAK,WAAW,QAAQ;AAE9C,MAAI,CAAC,eAAe;AAElB,WAAO,MAAM,KAAK,QAAQ,KAAK;AAAA,EACjC;AAEA,MAAI,OAAO,KAAK,cAAc,YAAY;AACxC,UAAM,SAAS,MAAM,KAAK,UAAU,OAAO,aAAa;AAGxD,QAAI,CAAC,QAAQ;AACX,aAAO;AAAA,IACT;AAGA,QAAI,UAAU,OAAO,WAAW,UAAU;AAExC,UAAI,OAAO,UAAU,QAAW;AAC9B,sBAAc,SAAS,OAAO;AAAA,MAChC,OAAO;AACL,sBAAc,SAAS;AAAA,MACzB;AAGA,UAAI,OAAO,SAAS,UAAU,OAAO,SAAS,aAAa;AACzD,sBAAc,OAAO,OAAO;AAAA,MAC9B;AAAA,IACF,OAAO;AACL,oBAAc,SAAS;AAAA,IACzB;AAAA,EACF;AAGA,MAAI,MAAM,YAAY,QAAW;AAC/B,kBAAc,UAAU,MAAM;AAAA,EAChC;AAGA,MAAI,MAAM,QAAQ,MAAM,SAAS,cAAc,MAAM;AACnD,kBAAc,OAAO,MAAM;AAAA,EAC7B;AAGA,MAAI,MAAM,MAAM;AACd,kBAAc,OAAO,MAAM;AAAA,EAC7B;AAGA,MAAI,MAAM,UAAU,QAAW;AAC7B,kBAAc,QAAQ,MAAM;AAAA,EAC9B;AAEA,SAAO;AACT;AAOA,mBAAmB,UAAU,UAAU,SAAU,IAAI;AACnD,MAAI,CAAC,KAAK,WAAW,EAAE,KAAK,GAAG,SAAS,MAAM,GAAG;AAC/C,SAAK,KAAK,KAAK,KAAK,SAAS,EAAE;AAAA,EACjC;AAEA,SAAO,KAAK,WAAW,EAAE;AAC3B;AAOA,mBAAmB,UAAU,gBAAgB,SAAU,SAAS;AAC9D,QAAM,OAAO,KAAK,WAAW,OAAO;AAEpC,MAAI,MAAM;AACR,WAAO,KAAK,MAAM;AAAA,EACpB;AACF;AASA,mBAAmB,UAAU,cAAc,eAAgB,UAAU;AACnE,MAAI;AACF,UAAM,OAAO,QAAQ;AAAA,EACvB,QAAQ;AACN,QAAI;AACF,iBAAW,KAAK,KAAK,KAAK,SAAS,QAAQ;AAE3C,YAAM,OAAO,QAAQ;AAAA,IACvB,QAAQ;AACN,YAAM,IAAI,MAAM,qCAAqC,QAAQ;AAAA,IAC/D;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,YAAY,QAAQ;AAE1C,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,MAAM;AAAA,MACJ,UAAU;AAAA,MACV,SAAS,KAAK,QAAQ,QAAQ;AAAA,MAC9B,UAAU,KAAK,SAAS,QAAQ;AAAA,IAClC;AAAA,EACF;AACF;AAEA,IAAO,qBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
function defineConfig(options) {
|
|
2
|
+
if (!options || typeof options !== "object") {
|
|
3
|
+
throw new Error("Config must be an object");
|
|
4
|
+
}
|
|
5
|
+
const requiredProps = ["output", "components", "pages"];
|
|
6
|
+
for (const prop of requiredProps) {
|
|
7
|
+
if (!(prop in options)) {
|
|
8
|
+
throw new Error(`Missing required config property: "${prop}"`);
|
|
9
|
+
}
|
|
10
|
+
if (typeof options[prop] !== "string") {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`Config property "${prop}" must be a string, received ${typeof options[prop]}`
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
if (options[prop].trim() === "") {
|
|
16
|
+
throw new Error(`Config property "${prop}" cannot be empty`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if ("plugins" in options && options.plugins !== void 0) {
|
|
20
|
+
if (!Array.isArray(options.plugins)) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Config property "plugins" must be an array, received ${typeof options.plugins}`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
options.plugins.forEach((plugin, index) => {
|
|
26
|
+
if (typeof plugin !== "object" || plugin === null) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Plugin at index ${index} must be an object, received ${typeof plugin}`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
if (typeof plugin.name !== "string" || plugin.name.trim() === "") {
|
|
32
|
+
throw new Error(
|
|
33
|
+
`Plugin at index ${index} must have a valid "name" property (non-empty string)`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return options;
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
defineConfig
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../lib/config.js"],
|
|
4
|
+
"sourcesContent": ["/**\n * @import {CoraliteConfig} from '../types/index.js'\n */\n\n/**\n * Validates and defines a Coralite configuration object\n * @param {CoraliteConfig} options - The configuration options to validate and define\n * @returns {CoraliteConfig} The validated configuration object\n * @throws {Error} If the configuration is invalid\n */\nexport function defineConfig (options) {\n // Validate that options is an object\n if (!options || typeof options !== 'object') {\n throw new Error('Config must be an object')\n }\n\n // Validate required string state\n const requiredProps = ['output', 'components', 'pages']\n\n for (const prop of requiredProps) {\n // Check if property exists\n if (!(prop in options)) {\n throw new Error(`Missing required config property: \"${prop}\"`)\n }\n\n // Check if property is a string\n if (typeof options[prop] !== 'string') {\n throw new Error(\n `Config property \"${prop}\" must be a string, received ${typeof options[prop]}`\n )\n }\n\n // Check if property is not empty\n if (options[prop].trim() === '') {\n throw new Error(`Config property \"${prop}\" cannot be empty`)\n }\n }\n\n // Validate optional plugins property\n if ('plugins' in options && options.plugins !== undefined) {\n if (!Array.isArray(options.plugins)) {\n throw new Error(\n `Config property \"plugins\" must be an array, received ${typeof options.plugins}`\n )\n }\n\n // Validate each plugin in the array\n options.plugins.forEach((plugin, index) => {\n if (typeof plugin !== 'object' || plugin === null) {\n throw new Error(\n `Plugin at index ${index} must be an object, received ${typeof plugin}`\n )\n }\n\n if (typeof plugin.name !== 'string' || plugin.name.trim() === '') {\n throw new Error(\n `Plugin at index ${index} must have a valid \"name\" property (non-empty string)`\n )\n }\n })\n }\n\n return options\n}\n"],
|
|
5
|
+
"mappings": "AAUO,SAAS,aAAc,SAAS;AAErC,MAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,UAAM,IAAI,MAAM,0BAA0B;AAAA,EAC5C;AAGA,QAAM,gBAAgB,CAAC,UAAU,cAAc,OAAO;AAEtD,aAAW,QAAQ,eAAe;AAEhC,QAAI,EAAE,QAAQ,UAAU;AACtB,YAAM,IAAI,MAAM,sCAAsC,IAAI,GAAG;AAAA,IAC/D;AAGA,QAAI,OAAO,QAAQ,IAAI,MAAM,UAAU;AACrC,YAAM,IAAI;AAAA,QACR,oBAAoB,IAAI,gCAAgC,OAAO,QAAQ,IAAI,CAAC;AAAA,MAC9E;AAAA,IACF;AAGA,QAAI,QAAQ,IAAI,EAAE,KAAK,MAAM,IAAI;AAC/B,YAAM,IAAI,MAAM,oBAAoB,IAAI,mBAAmB;AAAA,IAC7D;AAAA,EACF;AAGA,MAAI,aAAa,WAAW,QAAQ,YAAY,QAAW;AACzD,QAAI,CAAC,MAAM,QAAQ,QAAQ,OAAO,GAAG;AACnC,YAAM,IAAI;AAAA,QACR,wDAAwD,OAAO,QAAQ,OAAO;AAAA,MAChF;AAAA,IACF;AAGA,YAAQ,QAAQ,QAAQ,CAAC,QAAQ,UAAU;AACzC,UAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,cAAM,IAAI;AAAA,UACR,mBAAmB,KAAK,gCAAgC,OAAO,MAAM;AAAA,QACvE;AAAA,MACF;AAEA,UAAI,OAAO,OAAO,SAAS,YAAY,OAAO,KAAK,KAAK,MAAM,IAAI;AAChE,cAAM,IAAI;AAAA,UACR,mBAAmB,KAAK;AAAA,QAC1B;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|