coralite-scripts 0.38.5 → 0.39.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 CHANGED
@@ -3,14 +3,10 @@
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
- import colours from 'kleur'
7
6
  import pkg from '../package.json' with { type: 'json' }
8
- import buildStyles from '../libs/build-styles.js'
9
- import { join, relative, dirname } from 'node:path'
10
- import { deleteDirectoryRecursive, copyDirectory, toMS, toTime, displayError, displayWarning, displayInfo } from '../libs/build-utils.js'
11
- import { createCoralite } from 'coralite'
12
- import { mkdir, writeFile } from 'node:fs/promises'
13
- import ora from 'ora'
7
+ import { join } from 'node:path'
8
+ import { mkdir } from 'node:fs/promises'
9
+ import { buildCommand } from '../libs/commands/build.js'
14
10
 
15
11
  // remove all Node warnings before doing anything else
16
12
  process.removeAllListeners('warning')
@@ -22,7 +18,8 @@ program
22
18
  .description(pkg.description)
23
19
  .version(pkg.version)
24
20
  .addArgument(new Argument('<mode>', 'Run mode: dev (development server) or build (production compilation)').choices(['dev', 'build']).default('dev'))
25
- .option('-v --verbose', 'Enable verbose logging output')
21
+ .option('-v, --verbose', 'Enable verbose logging output')
22
+ .option('-c, --clean', 'Clear the output directory before building')
26
23
 
27
24
  program.parse(process.argv)
28
25
  program.on('error', (err) => {
@@ -31,7 +28,7 @@ program.on('error', (err) => {
31
28
 
32
29
  const options = program.opts()
33
30
  const mode = program.args[0]
34
- const config = await loadConfig()
31
+ const config = await loadConfig(process.cwd())
35
32
 
36
33
  if (!config) {
37
34
  process.exit(1)
@@ -43,159 +40,9 @@ if (mode === 'dev') {
43
40
 
44
41
  await server(config, options)
45
42
  } else if (mode === 'build') {
46
- const PAD = ' '
47
- const border = '─'.repeat(Math.min(process.stdout.columns, 36) / 2)
48
- const dash = colours.gray(' ─ ')
49
-
50
- if (options.verbose) {
51
- // log the response time and status code
52
- process.stdout.write('\n' + PAD + colours.yellow('Compiling Coralite... \n\n'))
53
- process.stdout.write(border + colours.inverse(` LOGS `) + border + '\n\n')
54
- } else {
55
- process.stdout.write('\n' + PAD + colours.yellow('Compiling Coralite... \n\n'))
56
- }
57
-
58
- // delete old output files
59
- deleteDirectoryRecursive(config.output)
60
-
61
- // start coralite
62
- const coralite = await createCoralite({
63
- components: config.components,
64
- pages: config.pages,
65
- plugins: config.plugins,
66
- assets: config.assets,
67
- externalStyles: config.styles?.input?.map(input => {
68
- const ext = input.split('.').pop()
69
- return '/assets/css/' + input.split('/').pop().replace(`.${ext}`, '.css')
70
- }),
71
- baseURL: config.baseURL,
72
- output: config.output,
73
- mode: 'production',
74
- onError: ({ level, message, error }) => {
75
- if (level === 'ERR') {
76
- displayError(message, error)
77
- } else if (level === 'WARN') {
78
- displayWarning(message)
79
- } else {
80
- displayInfo(message)
81
- }
82
- }
83
- })
84
-
85
- let spinner
86
- let pageCount = 0
87
- let skippedCount = 0
88
-
89
43
  try {
90
- let componentCount = 0
91
-
92
- if (!options.verbose) {
93
- spinner = ora('Building pages...').start()
94
- }
95
-
96
- const updateSpinnerText = () => {
97
- if (skippedCount > 0) {
98
- spinner.text = `Building pages... (${pageCount} completed, ${skippedCount} skipped)`
99
- } else {
100
- spinner.text = `Building pages... (${pageCount} completed)`
101
- }
102
- }
103
-
104
- // compile website
105
- await coralite.build(async (result) => {
106
- if (result.status === 'skipped') {
107
- skippedCount++
108
- if (!options.verbose) {
109
- updateSpinnerText()
110
- }
111
- return
112
- }
113
-
114
- const relativeDir = relative(config.pages, result.path.dirname)
115
- const outDir = join(config.output, relativeDir)
116
- const outFile = join(outDir, result.path.filename)
117
-
118
- await mkdir(outDir, { recursive: true })
119
- await writeFile(outFile, result.content)
120
-
121
- if (options.verbose) {
122
- process.stdout.write(toTime() + toMS(result.duration) + dash + result.path.pathname + '\n')
123
- } else {
124
- pageCount++
125
- updateSpinnerText()
126
- }
127
- })
128
-
129
- // Write ESM script assets generated during the build phase
130
- if (coralite.outputFiles) {
131
- const assetsDir = join(config.output, 'assets', 'js')
132
-
133
- const assetWrites = Object.values(coralite.outputFiles).map(async (file) => {
134
- const outFile = join(assetsDir, file.hashedPath)
135
- await mkdir(dirname(outFile), { recursive: true })
136
- await writeFile(outFile, file.text)
137
- if (options.verbose) {
138
- const dash = colours.gray(' ─ ')
139
- process.stdout.write(toTime() + toMS(0) + dash + outFile + '\n')
140
- }
141
- })
142
-
143
- await Promise.all(assetWrites)
144
- }
145
-
146
- if (!options.verbose) {
147
- if (skippedCount > 0) {
148
- spinner.succeed(`Pages built (${pageCount} completed, ${skippedCount} skipped)`)
149
- } else {
150
- spinner.succeed(`Pages built (${pageCount} completed)`)
151
- }
152
- if (componentCount > 0) {
153
- ora(`Components built (${componentCount} completed)`).succeed()
154
- }
155
- }
156
-
157
- const publicDir = config.public
158
-
159
- if (publicDir) {
160
- if (!options.verbose) {
161
- spinner = ora('Copying public directory...').start()
162
- }
163
- await copyDirectory(publicDir, config.output)
164
- if (!options.verbose) {
165
- spinner.succeed('Public directory copied')
166
- }
167
- }
168
-
169
- if (config.styles && config.styles.input) {
170
- if (!options.verbose) {
171
- spinner = ora('Building styles...').start()
172
- }
173
-
174
- const results = await buildStyles({
175
- input: config.styles.input,
176
- output: join(config.output, 'assets', 'css'),
177
- processors: config.styles.processors,
178
- minify: mode === 'build',
179
- sourcemap: mode !== 'build'
180
- })
181
-
182
- for (let i = 0; i < results.length; i++) {
183
- const result = results[i]
184
-
185
- if (options.verbose) {
186
- process.stdout.write(toTime() + toMS(result.duration) + dash + result.output + '\n')
187
- }
188
- }
189
-
190
- if (!options.verbose) {
191
- spinner.succeed('Styles built')
192
- }
193
- }
194
- } catch (error) {
195
- if (spinner) {
196
- spinner.fail('Build failed')
197
- }
198
- displayError('Build failed', error)
44
+ await buildCommand(config, options)
45
+ } catch {
199
46
  process.exit(1)
200
47
  }
201
48
  }
@@ -0,0 +1,260 @@
1
+ import colours from 'kleur'
2
+ import buildStyles from '../build-styles.js'
3
+ import { join, relative, dirname } from 'node:path'
4
+ import { existsSync, readdirSync, rmdirSync, statSync, unlinkSync } from 'node:fs'
5
+ import { deleteDirectoryRecursive, copyDirectory, toMS, toTime, displayError, displayWarning, displayInfo } from '../build-utils.js'
6
+ import { createCoralite } from 'coralite'
7
+ import { mkdir, writeFile } from 'node:fs/promises'
8
+ import ora from 'ora'
9
+
10
+ /**
11
+ * @param {import('../../types/index.js').CoraliteScriptConfig} config - The configuration object.
12
+ * @param {any} options - The CLI options.
13
+ * @param {any} logger - The logger object.
14
+ */
15
+ export async function buildCommand (config, options, logger = null) {
16
+ const mode = 'build'
17
+ const PAD = ' '
18
+ const border = '─'.repeat(Math.min(process.stdout.columns || 36, 36) / 2)
19
+ const dash = colours.gray(' ─ ')
20
+
21
+ const log = (msg) => {
22
+ if (logger && logger.write) {
23
+ logger.write(msg)
24
+ } else {
25
+ process.stdout.write(msg)
26
+ }
27
+ }
28
+
29
+ const createSpinner = (text) => {
30
+ if (logger && logger.spinner) {
31
+ return logger.spinner(text)
32
+ }
33
+ return ora(text)
34
+ }
35
+
36
+ const internalLogger = {
37
+ info: (msg) => {
38
+ return logger && logger.info ? logger.info(msg) : displayInfo(msg)
39
+ },
40
+ warn: (msg) => {
41
+ return logger && logger.warn ? logger.warn(msg) : displayWarning(msg)
42
+ },
43
+ error: (msg, err) => {
44
+ return logger && logger.error ? logger.error(msg, err) : displayError(msg, err)
45
+ }
46
+ }
47
+
48
+ if (options.verbose) {
49
+ log('\n' + PAD + colours.yellow('Compiling Coralite... \n\n'))
50
+ log(border + colours.inverse(` LOGS `) + border + '\n\n')
51
+ } else {
52
+ log('\n' + PAD + colours.yellow('Compiling Coralite... \n\n'))
53
+ }
54
+
55
+ if (options.clean) {
56
+ deleteDirectoryRecursive(config.output)
57
+ }
58
+
59
+ const validFiles = new Set()
60
+
61
+ const coralite = await createCoralite({
62
+ components: config.components,
63
+ pages: config.pages,
64
+ plugins: config.plugins,
65
+ assets: config.assets,
66
+ externalStyles: config.styles?.input?.map(input => {
67
+ const ext = input.split('.').pop()
68
+ return '/assets/css/' + input.split('/').pop().replace(`.${ext}`, '.css')
69
+ }),
70
+ baseURL: config.baseURL,
71
+ output: config.output,
72
+ mode: 'production',
73
+ onError: ({ level, message, error }) => {
74
+ if (level === 'ERR') {
75
+ internalLogger.error(message, error)
76
+ } else if (level === 'WARN') {
77
+ internalLogger.warn(message)
78
+ } else {
79
+ internalLogger.info(message)
80
+ }
81
+ }
82
+ })
83
+
84
+ let spinner
85
+ let pageCount = 0
86
+ let skippedCount = 0
87
+
88
+ try {
89
+ const componentCount = 0
90
+
91
+ if (!options.verbose) {
92
+ spinner = createSpinner('Building pages...').start()
93
+ }
94
+
95
+ const updateSpinnerText = () => {
96
+ if (skippedCount > 0) {
97
+ spinner.text = `Building pages... (${pageCount} completed, ${skippedCount} skipped)`
98
+ } else {
99
+ spinner.text = `Building pages... (${pageCount} completed)`
100
+ }
101
+ }
102
+
103
+ await coralite.build(async (result) => {
104
+ const relativeDir = relative(config.pages, result.path.dirname)
105
+ const outDir = join(config.output, relativeDir)
106
+ const outFile = join(outDir, result.path.filename)
107
+
108
+ validFiles.add(outFile)
109
+
110
+ if (result.status === 'skipped') {
111
+ skippedCount++
112
+ if (!options.verbose) {
113
+ updateSpinnerText()
114
+ }
115
+ return
116
+ }
117
+
118
+ await mkdir(outDir, { recursive: true })
119
+ await writeFile(outFile, result.content)
120
+
121
+ if (options.verbose) {
122
+ log(toTime() + toMS(result.duration) + dash + result.path.pathname + '\n')
123
+ } else {
124
+ pageCount++
125
+ updateSpinnerText()
126
+ }
127
+ })
128
+
129
+ if (coralite.outputFiles) {
130
+ const assetsJsDir = join(config.output, 'assets', 'js')
131
+ const assetsCssDir = join(config.output, 'assets', 'css')
132
+
133
+ const assetWrites = Object.values(coralite.outputFiles).map(async (file) => {
134
+ const isCSS = (file.path || file.hashedPath)?.endsWith('.css')
135
+ const baseAssetsDir = isCSS ? assetsCssDir : assetsJsDir
136
+ const outFile = join(baseAssetsDir, file.hashedPath)
137
+
138
+ validFiles.add(outFile)
139
+
140
+ await mkdir(dirname(outFile), { recursive: true })
141
+ await writeFile(outFile, file.text)
142
+ if (options.verbose) {
143
+ const dash = colours.gray(' ─ ')
144
+ log(toTime() + toMS(0) + dash + outFile + '\n')
145
+ }
146
+ })
147
+
148
+ await Promise.all(assetWrites)
149
+ }
150
+
151
+ if (!options.verbose) {
152
+ if (skippedCount > 0) {
153
+ spinner.succeed(`Pages built (${pageCount} completed, ${skippedCount} skipped)`)
154
+ } else {
155
+ spinner.succeed(`Pages built (${pageCount} completed)`)
156
+ }
157
+ if (componentCount > 0) {
158
+ createSpinner(`Components built (${componentCount} completed)`).succeed()
159
+ }
160
+ }
161
+
162
+ const publicDir = config.public
163
+
164
+ if (publicDir) {
165
+ if (!options.verbose) {
166
+ spinner = createSpinner('Copying public directory...').start()
167
+ }
168
+ await copyDirectory(publicDir, config.output)
169
+
170
+ const trackPublicFiles = (dir, baseDir) => {
171
+ const files = readdirSync(dir)
172
+ for (const file of files) {
173
+ const fullPath = join(dir, file)
174
+ const stat = statSync(fullPath)
175
+
176
+ if (stat.isDirectory()) {
177
+ trackPublicFiles(fullPath, baseDir)
178
+ } else {
179
+ const relativePath = relative(baseDir, fullPath)
180
+ validFiles.add(join(config.output, relativePath))
181
+ }
182
+ }
183
+ }
184
+ trackPublicFiles(publicDir, publicDir)
185
+
186
+ if (!options.verbose) {
187
+ spinner.succeed('Public directory copied')
188
+ }
189
+ }
190
+
191
+ if (config.styles && config.styles.input) {
192
+ if (!options.verbose) {
193
+ spinner = createSpinner('Building styles...').start()
194
+ }
195
+
196
+ const results = await buildStyles({
197
+ input: config.styles.input,
198
+ output: join(config.output, 'assets', 'css'),
199
+ processors: config.styles.processors,
200
+ minify: mode === 'build',
201
+ sourcemap: mode !== 'build'
202
+ })
203
+
204
+ for (let i = 0; i < results.length; i++) {
205
+ const result = results[i]
206
+ validFiles.add(result.output)
207
+
208
+ if (options.verbose) {
209
+ log(toTime() + toMS(result.duration) + dash + result.output + '\n')
210
+ }
211
+ }
212
+
213
+ if (!options.verbose) {
214
+ spinner.succeed('Styles built')
215
+ }
216
+ }
217
+
218
+ if (!options.clean) {
219
+ if (!options.verbose) {
220
+ spinner = createSpinner('Cleaning up stale files...').start()
221
+ }
222
+
223
+ const cleanupStaleFiles = (dir) => {
224
+ if (!existsSync(dir)) {
225
+ return
226
+ }
227
+
228
+ const files = readdirSync(dir)
229
+ for (const file of files) {
230
+ const fullPath = join(dir, file)
231
+ const stat = statSync(fullPath)
232
+
233
+ if (stat.isDirectory()) {
234
+ cleanupStaleFiles(fullPath)
235
+ if (readdirSync(fullPath).length === 0) {
236
+ rmdirSync(fullPath)
237
+ }
238
+ } else if (!validFiles.has(fullPath)) {
239
+ unlinkSync(fullPath)
240
+
241
+ if (options.verbose) {
242
+ log(toTime() + colours.gray('Deleted stale file: ') + fullPath + '\n')
243
+ }
244
+ }
245
+ }
246
+ }
247
+ cleanupStaleFiles(config.output)
248
+
249
+ if (!options.verbose) {
250
+ spinner.succeed('Cleanup complete')
251
+ }
252
+ }
253
+ } catch (error) {
254
+ if (spinner) {
255
+ spinner.fail('Build failed')
256
+ }
257
+ internalLogger.error('Build failed', error)
258
+ throw error
259
+ }
260
+ }
@@ -19,9 +19,10 @@ import { defineConfig } from './config.js'
19
19
  *
20
20
  * const config = await loadConfig()
21
21
  * ```
22
+ * @param {string} [cwd=process.cwd()] - The current working directory.
22
23
  */
23
- async function loadConfig () {
24
- const configPath = pathToFileURL(join(process.cwd(), 'coralite.config.js'))
24
+ async function loadConfig (cwd = process.cwd()) {
25
+ const configPath = pathToFileURL(join(cwd, 'coralite.config.js'))
25
26
 
26
27
  try {
27
28
  await access(configPath)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coralite-scripts",
3
- "version": "0.38.5",
3
+ "version": "0.39.0",
4
4
  "description": "Configuration and scripts for Create Coralite.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -61,7 +61,7 @@
61
61
  "portfinder": "^1.0.38",
62
62
  "postcss": "^8.5.15",
63
63
  "sass": "^1.101.0",
64
- "coralite": "0.38.5"
64
+ "coralite": "0.39.0"
65
65
  },
66
66
  "scripts": {
67
67
  "build": "premove dist && pnpm build-types",