coralite-scripts 0.23.0 → 0.25.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/bin/index.js +5 -1
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -1
- package/libs/load-config.js +24 -7
- package/libs/server.js +133 -99
- package/package.json +3 -2
- package/types/index.js +1 -0
- package/libs/build-html.js +0 -30
package/bin/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env -S node --experimental-vm-modules --experimental-import-meta-resolve
|
|
2
2
|
|
|
3
3
|
import loadConfig from '../libs/load-config.js'
|
|
4
4
|
import { Command, Argument } from 'commander'
|
|
@@ -34,6 +34,10 @@ const options = program.opts()
|
|
|
34
34
|
const mode = program.args[0]
|
|
35
35
|
const config = await loadConfig()
|
|
36
36
|
|
|
37
|
+
if (!config) {
|
|
38
|
+
process.exit(1)
|
|
39
|
+
}
|
|
40
|
+
|
|
37
41
|
if (mode === 'dev') {
|
|
38
42
|
await server(config, options)
|
|
39
43
|
} else if (mode === 'build') {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -23,6 +23,10 @@ export type CoraliteScriptBaseConfig = {
|
|
|
23
23
|
* - Postcss plugins.
|
|
24
24
|
*/
|
|
25
25
|
cssPlugins?: import("postcss").AcceptedPlugin[];
|
|
26
|
+
/**
|
|
27
|
+
* - Set build mode for the coralite instance.
|
|
28
|
+
*/
|
|
29
|
+
mode?: "production" | "development";
|
|
26
30
|
};
|
|
27
31
|
export type CoraliteScriptConfig = CoraliteScriptBaseConfig & CoraliteConfig;
|
|
28
32
|
export type CoraliteScriptOptions = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../types/index.js"],"names":[],"mappings":";;;;;;YAOc,MAAM;;;;aAEjB;QAA0B,IAAI,EAAnB,MAAM;KACjB;aACA;QAA2C,IAAI,EAApC,KAAK,GAAG,MAAM,GAAG,MAAM;QACR,KAAK,EAApB,MAAM;KACjB;;;;kBAAW,QAAQ,OAAO,CAAC;;;;iBAChB,OAAO,SAAS,EAAE,cAAc,EAAE;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../types/index.js"],"names":[],"mappings":";;;;;;YAOc,MAAM;;;;aAEjB;QAA0B,IAAI,EAAnB,MAAM;KACjB;aACA;QAA2C,IAAI,EAApC,KAAK,GAAG,MAAM,GAAG,MAAM;QACR,KAAK,EAApB,MAAM;KACjB;;;;kBAAW,QAAQ,OAAO,CAAC;;;;iBAChB,OAAO,SAAS,EAAE,cAAc,EAAE;;;;WAClC,YAAY,GAAG,aAAa;;mCAI7B,wBAAwB,GAAG,cAAc;;;;;UAKxC,OAAO;;;;YACP,OAAO;;;;cACP,OAAO;;6BAxBK,MAAM;oCADC,gBAAgB"}
|
package/libs/load-config.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { join } from 'path'
|
|
2
2
|
import { access } from 'fs/promises'
|
|
3
3
|
import { pathToFileURL } from 'url'
|
|
4
|
+
import { displayError } from './build-utils.js'
|
|
5
|
+
import { defineConfig } from './config.js'
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* @import {CoraliteScriptConfig} from '../types/index.js'
|
|
@@ -9,7 +11,7 @@ import { pathToFileURL } from 'url'
|
|
|
9
11
|
/**
|
|
10
12
|
* Loads the configuration for the Coralite project.
|
|
11
13
|
*
|
|
12
|
-
* @returns {Promise<CoraliteScriptConfig>} The configuration object containing path settings or
|
|
14
|
+
* @returns {Promise<CoraliteScriptConfig|null>} The configuration object containing path settings or null if no config found or invalid
|
|
13
15
|
*
|
|
14
16
|
* @example
|
|
15
17
|
* ```js
|
|
@@ -23,18 +25,33 @@ async function loadConfig () {
|
|
|
23
25
|
|
|
24
26
|
try {
|
|
25
27
|
await access(configPath)
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (error.code === 'ENOENT') {
|
|
30
|
+
displayError('Configuration file not found', `Could not find coralite.config.js at ${configPath}`)
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
displayError('Failed to access configuration file', error)
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
26
36
|
|
|
37
|
+
try {
|
|
27
38
|
const config = await import(configPath.toString())
|
|
28
39
|
|
|
29
|
-
if (config.default) {
|
|
30
|
-
|
|
40
|
+
if (!config.default) {
|
|
41
|
+
displayError('Config file must export a default object')
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
return defineConfig(config.default)
|
|
47
|
+
} catch (err) {
|
|
48
|
+
displayError('Invalid configuration', err.message)
|
|
49
|
+
return null
|
|
31
50
|
}
|
|
32
51
|
} catch (error) {
|
|
33
|
-
|
|
34
|
-
|
|
52
|
+
displayError('Failed to load configuration file', error)
|
|
53
|
+
return null
|
|
35
54
|
}
|
|
36
|
-
|
|
37
|
-
return null
|
|
38
55
|
}
|
|
39
56
|
|
|
40
57
|
export default loadConfig
|
package/libs/server.js
CHANGED
|
@@ -36,7 +36,8 @@ async function server (config, options) {
|
|
|
36
36
|
const coralite = new Coralite({
|
|
37
37
|
templates: config.templates,
|
|
38
38
|
pages: config.pages,
|
|
39
|
-
plugins: config.plugins
|
|
39
|
+
plugins: config.plugins,
|
|
40
|
+
mode: 'development'
|
|
40
41
|
})
|
|
41
42
|
await coralite.initialise()
|
|
42
43
|
displaySuccess('Coralite initialized successfully')
|
|
@@ -156,21 +157,83 @@ async function server (config, options) {
|
|
|
156
157
|
})
|
|
157
158
|
.get(/(.*)/, async (req, res) => {
|
|
158
159
|
// extract the requested path and its extension.
|
|
159
|
-
|
|
160
|
-
const extension = extname(
|
|
161
|
-
|
|
162
|
-
//
|
|
163
|
-
if (
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
160
|
+
const reqPath = req.path
|
|
161
|
+
const extension = extname(reqPath)
|
|
162
|
+
|
|
163
|
+
// Only handle HTML requests or extension-less requests (assumed to be pages)
|
|
164
|
+
if (extension && extension !== '.html') {
|
|
165
|
+
return res.sendStatus(404)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const resolveSource = async () => {
|
|
169
|
+
const candidates = []
|
|
170
|
+
|
|
171
|
+
// Ensure relative path doesn't start with / for joining
|
|
172
|
+
const relPath = reqPath.startsWith('/') ? reqPath.slice(1) : reqPath
|
|
173
|
+
|
|
174
|
+
if (reqPath.endsWith('/')) {
|
|
175
|
+
const key = join(relPath, 'index.html')
|
|
176
|
+
candidates.push({
|
|
177
|
+
path: join(config.pages, key),
|
|
178
|
+
key
|
|
179
|
+
})
|
|
180
|
+
} else if (extension === '.html') {
|
|
181
|
+
const key = relPath
|
|
182
|
+
candidates.push({
|
|
183
|
+
path: join(config.pages, key),
|
|
184
|
+
key
|
|
185
|
+
})
|
|
168
186
|
} else {
|
|
169
|
-
|
|
187
|
+
// No extension, no trailing slash
|
|
188
|
+
const key1 = relPath + '.html'
|
|
189
|
+
candidates.push({
|
|
190
|
+
path: join(config.pages, key1),
|
|
191
|
+
key: key1
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
const key2 = join(relPath, 'index.html')
|
|
195
|
+
candidates.push({
|
|
196
|
+
path: join(config.pages, key2),
|
|
197
|
+
key: key2
|
|
198
|
+
})
|
|
170
199
|
}
|
|
200
|
+
|
|
201
|
+
for (const candidate of candidates) {
|
|
202
|
+
try {
|
|
203
|
+
await access(candidate.path, constants.R_OK)
|
|
204
|
+
// Normalize key for consistency (use forward slashes)
|
|
205
|
+
const normalizedKey = candidate.key.split(sep).join('/')
|
|
206
|
+
return {
|
|
207
|
+
pathname: candidate.path,
|
|
208
|
+
key: normalizedKey
|
|
209
|
+
}
|
|
210
|
+
} catch {
|
|
211
|
+
// continue
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Fallback check memoryPageSource
|
|
216
|
+
for (const candidate of candidates) {
|
|
217
|
+
const normalizedKey = candidate.key.split(sep).join('/')
|
|
218
|
+
if (memoryPageSource.has(normalizedKey)) {
|
|
219
|
+
return {
|
|
220
|
+
pathname: memoryPageSource.get(normalizedKey),
|
|
221
|
+
key: normalizedKey
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return null
|
|
171
227
|
}
|
|
172
228
|
|
|
173
|
-
const
|
|
229
|
+
const result = await resolveSource()
|
|
230
|
+
|
|
231
|
+
if (!result) {
|
|
232
|
+
res.sendStatus(404)
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const { pathname, key: cacheKey } = result
|
|
174
237
|
|
|
175
238
|
if (pageCache.has(cacheKey)) {
|
|
176
239
|
res.send(pageCache.get(cacheKey))
|
|
@@ -178,99 +241,70 @@ async function server (config, options) {
|
|
|
178
241
|
}
|
|
179
242
|
|
|
180
243
|
try {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
if (
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
244
|
+
const start = process.hrtime()
|
|
245
|
+
let duration, dash = colours.gray(' ─ ')
|
|
246
|
+
|
|
247
|
+
let rebuildScript = '\n<script>\n'
|
|
248
|
+
rebuildScript += " const eventSource = new EventSource('/_/rebuild');\n"
|
|
249
|
+
rebuildScript += ' eventSource.onmessage = function(event) {\n'
|
|
250
|
+
rebuildScript += " if (event.data === 'connected') return;\n"
|
|
251
|
+
rebuildScript += ' // Reload page when file changes\n'
|
|
252
|
+
rebuildScript += ' location.reload()\n'
|
|
253
|
+
rebuildScript += ' }\n'
|
|
254
|
+
rebuildScript += ' </script>\n'
|
|
255
|
+
rebuildScript += '</body>\n'
|
|
256
|
+
|
|
257
|
+
await coralite.pages.setItem(pathname)
|
|
258
|
+
// build the HTML for this page using the built-in compiler.
|
|
259
|
+
const documents = await coralite.build(pathname, (result) => {
|
|
260
|
+
// inject a script to enable live reload via Server-Sent Events
|
|
261
|
+
const injectedHtml = result.html.replace(/<\/body>/i, rebuildScript)
|
|
262
|
+
|
|
263
|
+
const relPath = relative(config.pages, result.path.pathname)
|
|
264
|
+
const normalizedKey = relPath.split(sep).join('/')
|
|
265
|
+
|
|
266
|
+
// map in memory page to source
|
|
267
|
+
if (normalizedKey !== pathname) {
|
|
268
|
+
memoryPageSource.set(normalizedKey, pathname)
|
|
269
|
+
}
|
|
194
270
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
// check if it is a known in memory page source
|
|
199
|
-
const cacheKey = path.startsWith('/') ? path.slice(1) : path
|
|
200
|
-
if (memoryPageSource.has(cacheKey)) {
|
|
201
|
-
pathname = memoryPageSource.get(cacheKey)
|
|
202
|
-
} else {
|
|
203
|
-
res.sendStatus(404)
|
|
204
|
-
return
|
|
205
|
-
}
|
|
271
|
+
// only cache pages that were out of scope of the initial page request
|
|
272
|
+
if (normalizedKey !== cacheKey) {
|
|
273
|
+
pageCache.set(normalizedKey, injectedHtml)
|
|
206
274
|
}
|
|
207
275
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
let rebuildScript = '\n<script>\n'
|
|
213
|
-
rebuildScript += " const eventSource = new EventSource('/_/rebuild');\n"
|
|
214
|
-
rebuildScript += ' eventSource.onmessage = function(event) {\n'
|
|
215
|
-
rebuildScript += " if (event.data === 'connected') return;\n"
|
|
216
|
-
rebuildScript += ' // Reload page when file changes\n'
|
|
217
|
-
rebuildScript += ' location.reload()\n'
|
|
218
|
-
rebuildScript += ' }\n'
|
|
219
|
-
rebuildScript += ' </script>\n'
|
|
220
|
-
rebuildScript += '</body>\n'
|
|
221
|
-
|
|
222
|
-
await coralite.pages.setItem(pathname)
|
|
223
|
-
// build the HTML for this page using the built-in compiler.
|
|
224
|
-
const documents = await coralite.build(pathname, (result) => {
|
|
225
|
-
// inject a script to enable live reload via Server-Sent Events
|
|
226
|
-
const injectedHtml = result.html.replace(/<\/body>/i, rebuildScript)
|
|
227
|
-
|
|
228
|
-
const relPath = relative(config.pages, result.path.pathname)
|
|
229
|
-
const normalizedKey = relPath.split(sep).join('/')
|
|
230
|
-
|
|
231
|
-
// map in memory page to source
|
|
232
|
-
if (normalizedKey !== pathname) {
|
|
233
|
-
memoryPageSource.set(normalizedKey, pathname)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// only cache pages that were out of scope of the initial page request
|
|
237
|
-
if (normalizedKey !== cacheKey) {
|
|
238
|
-
pageCache.set(normalizedKey, injectedHtml)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
return {
|
|
242
|
-
path: result.path,
|
|
243
|
-
html: injectedHtml,
|
|
244
|
-
duration: result.duration
|
|
245
|
-
}
|
|
246
|
-
})
|
|
247
|
-
|
|
248
|
-
// prints time and path to the file that has been changed or added.
|
|
249
|
-
duration = process.hrtime(start)
|
|
250
|
-
process.stdout.write(toTime() + colours.bgGreen(' Compiled HTML ') + dash + toMS(duration) + dash + path + '\n')
|
|
251
|
-
|
|
252
|
-
// find the document that matches the request path
|
|
253
|
-
const doc = documents.find(doc => {
|
|
254
|
-
const relPath = relative(config.pages, doc.path.pathname)
|
|
255
|
-
const normalizedKey = relPath.split(sep).join('/')
|
|
256
|
-
return normalizedKey === cacheKey
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
if (doc) {
|
|
260
|
-
res.send(doc.html)
|
|
261
|
-
} else {
|
|
262
|
-
res.sendStatus(404)
|
|
263
|
-
}
|
|
264
|
-
} catch (error) {
|
|
265
|
-
// If headers haven't been sent, send 500
|
|
266
|
-
if (!res.headersSent) {
|
|
267
|
-
res.status(500).send(error.message)
|
|
268
|
-
}
|
|
269
|
-
displayError('Request processing failed', error)
|
|
276
|
+
return {
|
|
277
|
+
path: result.path,
|
|
278
|
+
html: injectedHtml,
|
|
279
|
+
duration: result.duration
|
|
270
280
|
}
|
|
281
|
+
})
|
|
282
|
+
|
|
283
|
+
// prints time and path to the file that has been changed or added.
|
|
284
|
+
duration = process.hrtime(start)
|
|
285
|
+
process.stdout.write(toTime() + colours.bgGreen(' Compiled HTML ') + dash + toMS(duration) + dash + '/' + cacheKey + '\n')
|
|
286
|
+
|
|
287
|
+
// find the document that matches the request path
|
|
288
|
+
const doc = documents.find(doc => {
|
|
289
|
+
const relPath = relative(config.pages, doc.path.pathname)
|
|
290
|
+
const normalizedKey = relPath.split(sep).join('/')
|
|
291
|
+
return normalizedKey === cacheKey
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
if (doc) {
|
|
295
|
+
res.send(doc.html)
|
|
296
|
+
} else {
|
|
297
|
+
res.sendStatus(404)
|
|
298
|
+
}
|
|
299
|
+
} catch (error) {
|
|
300
|
+
// If headers haven't been sent, send 500
|
|
301
|
+
if (!res.headersSent) {
|
|
302
|
+
res.status(500).send(error.message)
|
|
271
303
|
}
|
|
304
|
+
displayError('Request processing failed', error)
|
|
272
305
|
}
|
|
273
|
-
}
|
|
306
|
+
}
|
|
307
|
+
)
|
|
274
308
|
|
|
275
309
|
// watch for file changes
|
|
276
310
|
const watcher = chokidar.watch(watchPath, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coralite-scripts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.0",
|
|
4
4
|
"description": "Configuration and scripts for Create Coralite.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"chokidar": "^4.0.3",
|
|
52
52
|
"commander": "^14.0.0",
|
|
53
|
+
"cross-env": "^10.1.0",
|
|
53
54
|
"express": "^5.1.0",
|
|
54
55
|
"kleur": "^4.1.5",
|
|
55
56
|
"local-access": "^1.1.0",
|
|
@@ -57,7 +58,7 @@
|
|
|
57
58
|
"portfinder": "^1.0.38",
|
|
58
59
|
"postcss": "^8.5.6",
|
|
59
60
|
"sass": "^1.91.0",
|
|
60
|
-
"coralite": "0.
|
|
61
|
+
"coralite": "0.25.0"
|
|
61
62
|
},
|
|
62
63
|
"scripts": {
|
|
63
64
|
"build": "premove dist && pnpm build-types",
|
package/types/index.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* @property {string} styles.input - The path to the main stylesheet file to process.
|
|
14
14
|
* @property {Options<'async'>} [sassOptions] - Additional options passed to the Sass compiler.
|
|
15
15
|
* @property {import('postcss').AcceptedPlugin[]} [cssPlugins] - Postcss plugins.
|
|
16
|
+
* @property {'production' | 'development'} [mode='production'] - Set build mode for the coralite instance.
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
19
|
/**
|
package/libs/build-html.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import Coralite from 'coralite'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @import {CoraliteScriptConfig} from '../types/index.js'
|
|
5
|
-
* @import {CoraliteResult} from 'coralite/types'
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @param {CoraliteScriptConfig} config
|
|
10
|
-
* @returns {Promise<CoraliteResult[]>}
|
|
11
|
-
*/
|
|
12
|
-
async function buildHTML (config) {
|
|
13
|
-
// start coralite
|
|
14
|
-
const coralite = new Coralite({
|
|
15
|
-
templates: config.templates,
|
|
16
|
-
pages: config.pages,
|
|
17
|
-
plugins: config.plugins
|
|
18
|
-
})
|
|
19
|
-
await coralite.initialise()
|
|
20
|
-
|
|
21
|
-
// compile website
|
|
22
|
-
const documents = await coralite.compile()
|
|
23
|
-
|
|
24
|
-
// save documents
|
|
25
|
-
await coralite.save(documents, config.output)
|
|
26
|
-
|
|
27
|
-
return documents
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export default buildHTML
|