coralite-scripts 0.19.0 → 0.21.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 +0 -10
- package/bin/index.js +104 -41
- package/libs/build-utils.js +3 -20
- package/libs/server.js +84 -25
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -4,16 +4,6 @@ Welcome to **Coralite starter script**, a lightweight Static Site Generator (SSG
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
## Prerequisites
|
|
8
|
-
|
|
9
|
-
Ensure you have:
|
|
10
|
-
- Node.js ≥ 20.x
|
|
11
|
-
- npm or pnpm installed
|
|
12
|
-
|
|
13
|
-
> Coralite uses experimental ES modules (`--experimental-vm-modules`) — make sure your Node version supports it.
|
|
14
|
-
|
|
15
|
-
---
|
|
16
|
-
|
|
17
7
|
## Project Structure
|
|
18
8
|
|
|
19
9
|
Coralite expects a standard folder layout:
|
package/bin/index.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import loadConfig from '../libs/load-config.js'
|
|
4
4
|
import { Command, Argument } from 'commander'
|
|
5
5
|
import server from '../libs/server.js'
|
|
6
6
|
import colours from 'kleur'
|
|
7
|
-
import
|
|
8
|
-
import pkg from '../package.json' with { type: 'json'}
|
|
7
|
+
import pkg from '../package.json' with { type: 'json' }
|
|
9
8
|
import buildSass from '../libs/build-sass.js'
|
|
10
|
-
import { join } from 'node:path'
|
|
11
|
-
import { deleteDirectoryRecursive, copyDirectory, toMS, toTime } from '../libs/build-utils.js'
|
|
9
|
+
import { join, relative } from 'node:path'
|
|
10
|
+
import { deleteDirectoryRecursive, copyDirectory, toMS, toTime, displayError } from '../libs/build-utils.js'
|
|
12
11
|
import buildCSS from '../libs/build-css.js'
|
|
12
|
+
import { Coralite } from 'coralite'
|
|
13
|
+
import { mkdir, writeFile } from 'node:fs/promises'
|
|
14
|
+
import ora from 'ora'
|
|
13
15
|
|
|
14
16
|
// remove all Node warnings before doing anything else
|
|
15
17
|
process.removeAllListeners('warning')
|
|
@@ -37,56 +39,117 @@ if (mode === 'dev') {
|
|
|
37
39
|
} else if (mode === 'build') {
|
|
38
40
|
const PAD = ' '
|
|
39
41
|
const border = '─'.repeat(Math.min(process.stdout.columns, 36) / 2)
|
|
42
|
+
const dash = colours.gray(' ─ ')
|
|
43
|
+
|
|
44
|
+
if (options.verbose) {
|
|
45
|
+
// log the response time and status code
|
|
46
|
+
process.stdout.write('\n' + PAD + colours.yellow('Compiling Coralite... \n\n'))
|
|
47
|
+
process.stdout.write(border + colours.inverse(` LOGS `) + border + '\n\n')
|
|
48
|
+
} else {
|
|
49
|
+
process.stdout.write('\n' + PAD + colours.yellow('Compiling Coralite... \n\n'))
|
|
50
|
+
}
|
|
40
51
|
|
|
41
|
-
// log the response time and status code
|
|
42
|
-
process.stdout.write('\n' + PAD + colours.yellow('Compiling Coralite... \n\n'))
|
|
43
|
-
process.stdout.write(border + colours.inverse(` LOGS `) + border + '\n\n')
|
|
44
52
|
// delete old output files
|
|
45
53
|
deleteDirectoryRecursive(config.output)
|
|
46
54
|
|
|
47
55
|
const start = process.hrtime()
|
|
48
|
-
|
|
49
|
-
const
|
|
56
|
+
// start coralite
|
|
57
|
+
const coralite = new Coralite({
|
|
58
|
+
templates: config.templates,
|
|
59
|
+
pages: config.pages,
|
|
60
|
+
plugins: config.plugins
|
|
61
|
+
})
|
|
62
|
+
await coralite.initialise()
|
|
63
|
+
|
|
64
|
+
let spinner
|
|
65
|
+
let pageCount = 0
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
if (!options.verbose) {
|
|
69
|
+
spinner = ora('Building pages...').start()
|
|
70
|
+
}
|
|
50
71
|
|
|
51
|
-
|
|
52
|
-
|
|
72
|
+
// compile website
|
|
73
|
+
await coralite.build(async (result) => {
|
|
74
|
+
const relDir = relative(config.pages, result.path.dirname)
|
|
75
|
+
const outDir = join(config.output, relDir)
|
|
76
|
+
const outFile = join(outDir, result.path.filename)
|
|
53
77
|
|
|
54
|
-
|
|
55
|
-
|
|
78
|
+
await mkdir(outDir, { recursive: true })
|
|
79
|
+
await writeFile(outFile, result.html)
|
|
56
80
|
|
|
57
|
-
|
|
81
|
+
if (options.verbose) {
|
|
82
|
+
process.stdout.write(toTime() + toMS(result.duration) + dash + result.path.pathname + '\n')
|
|
83
|
+
} else {
|
|
84
|
+
pageCount++
|
|
85
|
+
spinner.text = `Building pages... (${pageCount} completed)`
|
|
86
|
+
}
|
|
58
87
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
88
|
+
return outFile
|
|
89
|
+
})
|
|
62
90
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
input: config.styles.input,
|
|
67
|
-
output: join(config.output, 'css'),
|
|
68
|
-
options: config.sassOptions,
|
|
69
|
-
start
|
|
70
|
-
})
|
|
91
|
+
if (!options.verbose) {
|
|
92
|
+
spinner.succeed(`Pages built (${pageCount} completed)`)
|
|
93
|
+
}
|
|
71
94
|
|
|
72
|
-
|
|
73
|
-
const result = results[i]
|
|
95
|
+
const publicDir = config.public
|
|
74
96
|
|
|
75
|
-
|
|
97
|
+
if (publicDir) {
|
|
98
|
+
if (!options.verbose) {
|
|
99
|
+
spinner = ora('Copying public directory...').start()
|
|
76
100
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
output: join(config.output, 'css'),
|
|
81
|
-
plugins: config.cssPlugins,
|
|
82
|
-
start
|
|
83
|
-
})
|
|
84
|
-
|
|
85
|
-
for (let i = 0; i < results.length; i++) {
|
|
86
|
-
const result = results[i]
|
|
87
|
-
|
|
88
|
-
process.stdout.write(toTime() + toMS(result.duration) + dash + result.output + '\n')
|
|
101
|
+
await copyDirectory(publicDir, config.output)
|
|
102
|
+
if (!options.verbose) {
|
|
103
|
+
spinner.succeed('Public directory copied')
|
|
89
104
|
}
|
|
90
105
|
}
|
|
106
|
+
|
|
107
|
+
if (config.styles) {
|
|
108
|
+
if (!options.verbose) {
|
|
109
|
+
spinner = ora('Building styles...').start()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (config.styles.type === 'sass' || config.styles.type === 'scss') {
|
|
113
|
+
const results = await buildSass({
|
|
114
|
+
input: config.styles.input,
|
|
115
|
+
output: join(config.output, 'css'),
|
|
116
|
+
options: config.sassOptions,
|
|
117
|
+
start
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
for (let i = 0; i < results.length; i++) {
|
|
121
|
+
const result = results[i]
|
|
122
|
+
|
|
123
|
+
if (options.verbose) {
|
|
124
|
+
process.stdout.write(toTime() + toMS(result.duration) + dash + result.output + '\n')
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} else if (config.styles.type === 'css') {
|
|
128
|
+
const results = await buildCSS({
|
|
129
|
+
input: config.styles.input,
|
|
130
|
+
output: join(config.output, 'css'),
|
|
131
|
+
plugins: config.cssPlugins,
|
|
132
|
+
start
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
for (let i = 0; i < results.length; i++) {
|
|
136
|
+
const result = results[i]
|
|
137
|
+
|
|
138
|
+
if (options.verbose) {
|
|
139
|
+
process.stdout.write(toTime() + toMS(result.duration) + dash + result.output + '\n')
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!options.verbose) {
|
|
145
|
+
spinner.succeed('Styles built')
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} catch (error) {
|
|
149
|
+
if (spinner) {
|
|
150
|
+
spinner.fail('Build failed')
|
|
151
|
+
}
|
|
152
|
+
displayError('Build failed', error)
|
|
153
|
+
process.exit(1)
|
|
91
154
|
}
|
|
92
155
|
}
|
package/libs/build-utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import colours from 'kleur'
|
|
2
2
|
import fs from 'node:fs'
|
|
3
3
|
import path from 'node:path'
|
|
4
|
+
import { cp } from 'node:fs/promises'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Creates current time in format [HH:MM:SS].mmm (milliseconds), colored with ANSI colors, and formatted as bold white string for better readability of logs or console output
|
|
@@ -53,26 +54,8 @@ export function toCode (code) {
|
|
|
53
54
|
* @param {string} src - The source directory path to copy from
|
|
54
55
|
* @param {string} dest - The destination directory path to copy to
|
|
55
56
|
*/
|
|
56
|
-
export function copyDirectory (src, dest) {
|
|
57
|
-
|
|
58
|
-
if (!fs.existsSync(dest)) {
|
|
59
|
-
fs.mkdirSync(dest, { recursive: true })
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Read source directory contents
|
|
63
|
-
const entries = fs.readdirSync(src)
|
|
64
|
-
|
|
65
|
-
for (const entry of entries) {
|
|
66
|
-
const srcPath = path.join(src, entry)
|
|
67
|
-
const destPath = path.join(dest, entry)
|
|
68
|
-
|
|
69
|
-
// Check if it's a file or directory and copy accordingly
|
|
70
|
-
if (fs.statSync(srcPath).isDirectory()) {
|
|
71
|
-
copyDirectory(srcPath, destPath) // Recursive call for subdirectories
|
|
72
|
-
} else {
|
|
73
|
-
fs.copyFileSync(srcPath, destPath) // Copy files
|
|
74
|
-
}
|
|
75
|
-
}
|
|
57
|
+
export async function copyDirectory (src, dest) {
|
|
58
|
+
await cp(src, dest, { recursive: true })
|
|
76
59
|
}
|
|
77
60
|
|
|
78
61
|
/**
|
package/libs/server.js
CHANGED
|
@@ -4,7 +4,7 @@ import localAccess from 'local-access'
|
|
|
4
4
|
import chokidar from 'chokidar'
|
|
5
5
|
import buildSass from './build-sass.js'
|
|
6
6
|
import { displayError, displayInfo, displaySuccess, toCode, toMS, toTime } from './build-utils.js'
|
|
7
|
-
import { extname, join, normalize } from 'path'
|
|
7
|
+
import { extname, join, normalize, relative, sep } from 'path'
|
|
8
8
|
import { readFile, access, constants } from 'fs/promises'
|
|
9
9
|
import Coralite from 'coralite'
|
|
10
10
|
import buildCSS from './build-css.js'
|
|
@@ -28,6 +28,8 @@ async function server (config, options) {
|
|
|
28
28
|
|
|
29
29
|
// track active connections.
|
|
30
30
|
const clients = new Set()
|
|
31
|
+
const pageCache = new Map()
|
|
32
|
+
const memoryPageSource = new Map()
|
|
31
33
|
|
|
32
34
|
// start coralite
|
|
33
35
|
displayInfo('Initializing Coralite...')
|
|
@@ -165,6 +167,13 @@ async function server (config, options) {
|
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
169
|
|
|
170
|
+
const cacheKey = path.startsWith('/') ? path.slice(1) : path
|
|
171
|
+
|
|
172
|
+
if (pageCache.has(cacheKey)) {
|
|
173
|
+
res.send(pageCache.get(cacheKey))
|
|
174
|
+
return
|
|
175
|
+
}
|
|
176
|
+
|
|
168
177
|
try {
|
|
169
178
|
// first attempt to read the file directly.
|
|
170
179
|
await access(path)
|
|
@@ -175,7 +184,7 @@ async function server (config, options) {
|
|
|
175
184
|
if (!path.endsWith('.html')) {
|
|
176
185
|
res.sendStatus(404)
|
|
177
186
|
} else {
|
|
178
|
-
|
|
187
|
+
let pathname = join(config.pages, path)
|
|
179
188
|
|
|
180
189
|
try {
|
|
181
190
|
// if that fails, try reading from pages directory.
|
|
@@ -183,31 +192,79 @@ async function server (config, options) {
|
|
|
183
192
|
// check if page source file exists and is readable
|
|
184
193
|
await access(pathname, constants.R_OK)
|
|
185
194
|
} catch {
|
|
186
|
-
|
|
195
|
+
// check if it is a known in memory page source
|
|
196
|
+
const cacheKey = path.startsWith('/') ? path.slice(1) : path
|
|
197
|
+
if (memoryPageSource.has(cacheKey)) {
|
|
198
|
+
pathname = memoryPageSource.get(cacheKey)
|
|
199
|
+
} else {
|
|
200
|
+
res.sendStatus(404)
|
|
201
|
+
return
|
|
202
|
+
}
|
|
187
203
|
}
|
|
188
204
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
205
|
+
try {
|
|
206
|
+
const start = process.hrtime()
|
|
207
|
+
let duration, dash = colours.gray(' ─ ')
|
|
208
|
+
|
|
209
|
+
let rebuildScript = '\n<script>\n'
|
|
210
|
+
rebuildScript += " const eventSource = new EventSource('/_/rebuild');\n"
|
|
211
|
+
rebuildScript += ' eventSource.onmessage = function(event) {\n'
|
|
212
|
+
rebuildScript += " if (event.data === 'connected') return;\n"
|
|
213
|
+
rebuildScript += ' // Reload page when file changes\n'
|
|
214
|
+
rebuildScript += ' location.reload()\n'
|
|
215
|
+
rebuildScript += ' }\n'
|
|
216
|
+
rebuildScript += ' </script>\n'
|
|
217
|
+
rebuildScript += '</body>\n'
|
|
218
|
+
|
|
219
|
+
await coralite.pages.setItem(pathname)
|
|
220
|
+
// build the HTML for this page using the built-in compiler.
|
|
221
|
+
const documents = await coralite.build(pathname, (result) => {
|
|
222
|
+
// inject a script to enable live reload via Server-Sent Events
|
|
223
|
+
const injectedHtml = result.html.replace(/<\/body>/i, rebuildScript)
|
|
224
|
+
|
|
225
|
+
const relPath = relative(config.pages, result.path.pathname)
|
|
226
|
+
const normalizedKey = relPath.split(sep).join('/')
|
|
227
|
+
|
|
228
|
+
// map in memory page to source
|
|
229
|
+
if (normalizedKey !== pathname) {
|
|
230
|
+
memoryPageSource.set(normalizedKey, pathname)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// only cache pages that were out of scope of the initial page request
|
|
234
|
+
if (normalizedKey !== cacheKey) {
|
|
235
|
+
pageCache.set(normalizedKey, injectedHtml)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
path: result.path,
|
|
240
|
+
html: injectedHtml,
|
|
241
|
+
duration: result.duration
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
// prints time and path to the file that has been changed or added.
|
|
246
|
+
duration = process.hrtime(start)
|
|
247
|
+
process.stdout.write(toTime() + colours.bgGreen(' Compiled HTML ') + dash + toMS(duration) + dash + path + '\n')
|
|
248
|
+
|
|
249
|
+
// find the document that matches the request path
|
|
250
|
+
const doc = documents.find(doc => {
|
|
251
|
+
const relPath = relative(config.pages, doc.path.pathname)
|
|
252
|
+
const normalizedKey = relPath.split(sep).join('/')
|
|
253
|
+
return normalizedKey === cacheKey
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
if (doc) {
|
|
257
|
+
res.send(doc.html)
|
|
258
|
+
} else {
|
|
259
|
+
res.sendStatus(404)
|
|
260
|
+
}
|
|
261
|
+
} catch (error) {
|
|
262
|
+
// If headers haven't been sent, send 500
|
|
263
|
+
if (!res.headersSent) {
|
|
264
|
+
res.status(500).send(error.message)
|
|
265
|
+
}
|
|
266
|
+
displayError('Request processing failed', error)
|
|
267
|
+
}
|
|
211
268
|
}
|
|
212
269
|
}
|
|
213
270
|
})
|
|
@@ -235,6 +292,8 @@ async function server (config, options) {
|
|
|
235
292
|
compileTimeout = setTimeout(async () => {
|
|
236
293
|
if (isCompiling || pendingChanges.size === 0) return
|
|
237
294
|
|
|
295
|
+
pageCache.clear()
|
|
296
|
+
|
|
238
297
|
isCompiling = true
|
|
239
298
|
const start = process.hrtime()
|
|
240
299
|
let dash = colours.gray(' ─ ')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coralite-scripts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Configuration and scripts for Create Coralite.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -53,10 +53,11 @@
|
|
|
53
53
|
"express": "^5.1.0",
|
|
54
54
|
"kleur": "^4.1.5",
|
|
55
55
|
"local-access": "^1.1.0",
|
|
56
|
+
"ora": "^9.1.0",
|
|
56
57
|
"portfinder": "^1.0.38",
|
|
57
58
|
"postcss": "^8.5.6",
|
|
58
59
|
"sass": "^1.91.0",
|
|
59
|
-
"coralite": "0.
|
|
60
|
+
"coralite": "0.21.0"
|
|
60
61
|
},
|
|
61
62
|
"scripts": {
|
|
62
63
|
"build": "premove dist && pnpm build-types",
|