@lightnet/cli 4.8.0 โ 4.8.1
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/CHANGELOG.md +6 -0
- package/package.json +1 -1
- package/src/check-files.js +51 -55
- package/src/index.js +2 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @lightnet/cli
|
|
2
2
|
|
|
3
|
+
## 4.8.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#454](https://github.com/LightNetDev/LightNet/pull/454) [`5de5b93`](https://github.com/LightNetDev/LightNet/commit/5de5b931575a9e864c5acfdf991e0e9b2980030b) - Rename check-files scope `thumbnails` to `images`.
|
|
8
|
+
|
|
3
9
|
## 4.8.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/package.json
CHANGED
package/src/check-files.js
CHANGED
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
import { cancelPrompt } from "./support/prompt-cancel.js"
|
|
25
25
|
import { createR2FileStorage } from "./support/r2.js"
|
|
26
26
|
|
|
27
|
-
const supportedScopes = new Set(["content-files", "
|
|
27
|
+
const supportedScopes = new Set(["content-files", "images"])
|
|
28
28
|
|
|
29
29
|
const mediaDir = "src/content/media"
|
|
30
30
|
const mediaImagesDir = "src/content/media/images"
|
|
@@ -83,7 +83,7 @@ export async function checkFiles(options, runtime = {}) {
|
|
|
83
83
|
|
|
84
84
|
const scopes = parseScopes(options.scope)
|
|
85
85
|
const includeContentFiles = scopes.has("content-files")
|
|
86
|
-
const
|
|
86
|
+
const includeImages = scopes.has("images")
|
|
87
87
|
|
|
88
88
|
intro("check-files")
|
|
89
89
|
|
|
@@ -102,9 +102,9 @@ export async function checkFiles(options, runtime = {}) {
|
|
|
102
102
|
/** @type {string[]} */
|
|
103
103
|
let orphanedContentFiles = []
|
|
104
104
|
/** @type {string[]} */
|
|
105
|
-
let
|
|
105
|
+
let orphanedMediaImages = []
|
|
106
106
|
/** @type {string[]} */
|
|
107
|
-
let
|
|
107
|
+
let orphanedCategoryImages = []
|
|
108
108
|
/** @type {string[]} */
|
|
109
109
|
let removedItems = []
|
|
110
110
|
/** @type {string[]} */
|
|
@@ -112,8 +112,8 @@ export async function checkFiles(options, runtime = {}) {
|
|
|
112
112
|
|
|
113
113
|
/** @type {FileStorage|undefined} */
|
|
114
114
|
let contentFileStorage = undefined
|
|
115
|
-
const
|
|
116
|
-
const
|
|
115
|
+
const mediaImageStorage = files(cwd, { rootDir: mediaImagesDir })
|
|
116
|
+
const categoryImageStorage = files(cwd, { rootDir: categoryImagesDir })
|
|
117
117
|
|
|
118
118
|
if (includeContentFiles) {
|
|
119
119
|
contentFileStorage = options.r2
|
|
@@ -153,7 +153,7 @@ export async function checkFiles(options, runtime = {}) {
|
|
|
153
153
|
orphanedContentFiles = r2Result.orphanedFiles
|
|
154
154
|
if (r2Result.referenceCount === 0) {
|
|
155
155
|
warnings.push(
|
|
156
|
-
'No R2-backed content file references found. Use "--scope=
|
|
156
|
+
'No R2-backed content file references found. Use "--scope=images" or check your "--r2" setup.',
|
|
157
157
|
)
|
|
158
158
|
}
|
|
159
159
|
} else {
|
|
@@ -177,21 +177,21 @@ export async function checkFiles(options, runtime = {}) {
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
180
|
-
if (
|
|
181
|
-
const
|
|
182
|
-
error: "
|
|
183
|
-
start: "Checking
|
|
184
|
-
stop: (result) =>
|
|
180
|
+
if (includeImages) {
|
|
181
|
+
const imagesResult = await runSpinner({
|
|
182
|
+
error: "Image check failed.",
|
|
183
|
+
start: "Checking images",
|
|
184
|
+
stop: (result) => formatImageCheckSummary(result),
|
|
185
185
|
task: () =>
|
|
186
|
-
|
|
186
|
+
validateImages(
|
|
187
187
|
mediaItems,
|
|
188
188
|
categories,
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
mediaImageStorage,
|
|
190
|
+
categoryImageStorage,
|
|
191
191
|
),
|
|
192
192
|
})
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
orphanedMediaImages = imagesResult.orphanedMediaImages
|
|
194
|
+
orphanedCategoryImages = imagesResult.orphanedCategoryImages
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
if (options.fix || options.fixWithoutConfirm) {
|
|
@@ -212,7 +212,7 @@ export async function checkFiles(options, runtime = {}) {
|
|
|
212
212
|
contentDeletions.push(deletion)
|
|
213
213
|
deletions.push(deletion)
|
|
214
214
|
}
|
|
215
|
-
for (const filePath of
|
|
215
|
+
for (const filePath of orphanedMediaImages) {
|
|
216
216
|
const deletion = {
|
|
217
217
|
displayPath: filePath,
|
|
218
218
|
target: filePath,
|
|
@@ -220,7 +220,7 @@ export async function checkFiles(options, runtime = {}) {
|
|
|
220
220
|
localDeletions.push(deletion)
|
|
221
221
|
deletions.push(deletion)
|
|
222
222
|
}
|
|
223
|
-
for (const filePath of
|
|
223
|
+
for (const filePath of orphanedCategoryImages) {
|
|
224
224
|
const deletion = {
|
|
225
225
|
displayPath: filePath,
|
|
226
226
|
target: filePath,
|
|
@@ -257,22 +257,22 @@ export async function checkFiles(options, runtime = {}) {
|
|
|
257
257
|
task: async () => {
|
|
258
258
|
const [
|
|
259
259
|
deletedContentTargets,
|
|
260
|
-
|
|
261
|
-
|
|
260
|
+
deletedMediaImageTargets,
|
|
261
|
+
deletedCategoryImageTargets,
|
|
262
262
|
] = await Promise.all([
|
|
263
263
|
contentFileStorage
|
|
264
264
|
? contentFileStorage.delete(
|
|
265
265
|
contentDeletions.map((deletion) => deletion.target),
|
|
266
266
|
)
|
|
267
267
|
: [],
|
|
268
|
-
|
|
268
|
+
mediaImageStorage.delete(
|
|
269
269
|
localDeletions
|
|
270
270
|
.filter((deletion) =>
|
|
271
271
|
deletion.target.startsWith(mediaImagesDir),
|
|
272
272
|
)
|
|
273
273
|
.map((deletion) => deletion.target),
|
|
274
274
|
),
|
|
275
|
-
|
|
275
|
+
categoryImageStorage.delete(
|
|
276
276
|
localDeletions
|
|
277
277
|
.filter((deletion) =>
|
|
278
278
|
deletion.target.startsWith(categoryImagesDir),
|
|
@@ -283,8 +283,8 @@ export async function checkFiles(options, runtime = {}) {
|
|
|
283
283
|
return {
|
|
284
284
|
deletedContentTargets,
|
|
285
285
|
deletedLocalTargets: [
|
|
286
|
-
...
|
|
287
|
-
...
|
|
286
|
+
...deletedMediaImageTargets,
|
|
287
|
+
...deletedCategoryImageTargets,
|
|
288
288
|
],
|
|
289
289
|
}
|
|
290
290
|
},
|
|
@@ -304,10 +304,10 @@ export async function checkFiles(options, runtime = {}) {
|
|
|
304
304
|
orphanedContentFiles = orphanedContentFiles.filter(
|
|
305
305
|
(item) => !deletedTargets.has(item),
|
|
306
306
|
)
|
|
307
|
-
|
|
307
|
+
orphanedMediaImages = orphanedMediaImages.filter(
|
|
308
308
|
(item) => !deletedTargets.has(item),
|
|
309
309
|
)
|
|
310
|
-
|
|
310
|
+
orphanedCategoryImages = orphanedCategoryImages.filter(
|
|
311
311
|
(item) => !deletedTargets.has(item),
|
|
312
312
|
)
|
|
313
313
|
}
|
|
@@ -318,8 +318,8 @@ export async function checkFiles(options, runtime = {}) {
|
|
|
318
318
|
missingContentFiles.length > 0 ||
|
|
319
319
|
wrongTypeR2ContentFiles.length > 0 ||
|
|
320
320
|
orphanedContentFiles.length > 0 ||
|
|
321
|
-
|
|
322
|
-
|
|
321
|
+
orphanedMediaImages.length > 0 ||
|
|
322
|
+
orphanedCategoryImages.length > 0
|
|
323
323
|
|
|
324
324
|
for (const warning of warnings) {
|
|
325
325
|
log.warn(warning)
|
|
@@ -344,8 +344,8 @@ export async function checkFiles(options, runtime = {}) {
|
|
|
344
344
|
formatContentFilePath(filePath, options),
|
|
345
345
|
),
|
|
346
346
|
)
|
|
347
|
-
printSection("Orphaned media
|
|
348
|
-
printSection("Orphaned category
|
|
347
|
+
printSection("Orphaned media images", orphanedMediaImages)
|
|
348
|
+
printSection("Orphaned category images", orphanedCategoryImages)
|
|
349
349
|
printSection("Removed items", removedItems)
|
|
350
350
|
|
|
351
351
|
outro(hasIssues ? "Issues found. ๐ง" : "Cleanup complete. ๐งน")
|
|
@@ -458,17 +458,16 @@ function formatContentCheckSummary(result) {
|
|
|
458
458
|
/**
|
|
459
459
|
* @param {{
|
|
460
460
|
* fileCount: number
|
|
461
|
-
*
|
|
462
|
-
*
|
|
461
|
+
* orphanedCategoryImages: string[]
|
|
462
|
+
* orphanedMediaImages: string[]
|
|
463
463
|
* referenceCount: number
|
|
464
464
|
* }} result
|
|
465
465
|
*/
|
|
466
|
-
function
|
|
467
|
-
const
|
|
468
|
-
result.
|
|
469
|
-
result.orphanedCategoryThumbnails.length
|
|
466
|
+
function formatImageCheckSummary(result) {
|
|
467
|
+
const orphanedImages =
|
|
468
|
+
result.orphanedMediaImages.length + result.orphanedCategoryImages.length
|
|
470
469
|
|
|
471
|
-
return `
|
|
470
|
+
return `Images: ${formatCount(result.referenceCount, "reference")}, ${formatCount(result.fileCount, "file")}; orphaned: ${orphanedImages} (${result.orphanedMediaImages.length} media, ${result.orphanedCategoryImages.length} categories).`
|
|
472
471
|
}
|
|
473
472
|
|
|
474
473
|
/**
|
|
@@ -502,7 +501,7 @@ function formatDeletionProgress(action, count) {
|
|
|
502
501
|
* @param {string|undefined} rawScope
|
|
503
502
|
*/
|
|
504
503
|
function parseScopes(rawScope) {
|
|
505
|
-
const values = (rawScope ?? "content-files,
|
|
504
|
+
const values = (rawScope ?? "content-files,images")
|
|
506
505
|
.split(",")
|
|
507
506
|
.map((value) => value.trim())
|
|
508
507
|
.filter(Boolean)
|
|
@@ -510,7 +509,7 @@ function parseScopes(rawScope) {
|
|
|
510
509
|
if (values.length === 0) {
|
|
511
510
|
throw new CliError(
|
|
512
511
|
// intentionally kept as a CLI-facing validation error
|
|
513
|
-
'Expected "--scope" to include at least one of "content-files" or "
|
|
512
|
+
'Expected "--scope" to include at least one of "content-files" or "images".',
|
|
514
513
|
)
|
|
515
514
|
}
|
|
516
515
|
|
|
@@ -518,7 +517,7 @@ function parseScopes(rawScope) {
|
|
|
518
517
|
for (const scope of scopes) {
|
|
519
518
|
if (!supportedScopes.has(scope)) {
|
|
520
519
|
throw new CliError(
|
|
521
|
-
`Unsupported scope "${scope}". Allowed values: content-files,
|
|
520
|
+
`Unsupported scope "${scope}". Allowed values: content-files, images.`,
|
|
522
521
|
)
|
|
523
522
|
}
|
|
524
523
|
}
|
|
@@ -540,31 +539,28 @@ async function assertLightNetSiteRoot(cwd) {
|
|
|
540
539
|
/**
|
|
541
540
|
* @param {MediaItem[]} mediaItems
|
|
542
541
|
* @param {Category[]} categories
|
|
543
|
-
* @param {FileStorage}
|
|
544
|
-
* @param {FileStorage}
|
|
542
|
+
* @param {FileStorage} mediaImageStorage
|
|
543
|
+
* @param {FileStorage} categoryImageStorage
|
|
545
544
|
*/
|
|
546
|
-
async function
|
|
545
|
+
async function validateImages(
|
|
547
546
|
mediaItems,
|
|
548
547
|
categories,
|
|
549
|
-
|
|
550
|
-
|
|
548
|
+
mediaImageStorage,
|
|
549
|
+
categoryImageStorage,
|
|
551
550
|
) {
|
|
552
551
|
const mediaReferences = new Set(
|
|
553
552
|
mediaItems
|
|
554
|
-
.map((item) =>
|
|
553
|
+
.map((item) => toImagePath("media", item.image))
|
|
555
554
|
.filter((value) => value !== undefined),
|
|
556
555
|
)
|
|
557
556
|
const categoryReferences = new Set(
|
|
558
557
|
categories
|
|
559
|
-
.map((item) =>
|
|
558
|
+
.map((item) => toImagePath("categories", item.image))
|
|
560
559
|
.filter((value) => value !== undefined),
|
|
561
560
|
)
|
|
562
561
|
|
|
563
562
|
const [initializedMediaStorage, initializedCategoryStorage] =
|
|
564
|
-
await Promise.all([
|
|
565
|
-
mediaThumbnailStorage.init(),
|
|
566
|
-
categoryThumbnailStorage.init(),
|
|
567
|
-
])
|
|
563
|
+
await Promise.all([mediaImageStorage.init(), categoryImageStorage.init()])
|
|
568
564
|
const [mediaFiles, categoryFiles] = await Promise.all([
|
|
569
565
|
initializedMediaStorage.list(),
|
|
570
566
|
initializedCategoryStorage.list(),
|
|
@@ -572,10 +568,10 @@ async function validateThumbnails(
|
|
|
572
568
|
|
|
573
569
|
return {
|
|
574
570
|
fileCount: mediaFiles.length + categoryFiles.length,
|
|
575
|
-
|
|
571
|
+
orphanedMediaImages: mediaFiles.filter(
|
|
576
572
|
(file) => !mediaReferences.has(file),
|
|
577
573
|
),
|
|
578
|
-
|
|
574
|
+
orphanedCategoryImages: categoryFiles.filter(
|
|
579
575
|
(file) => !categoryReferences.has(file),
|
|
580
576
|
),
|
|
581
577
|
referenceCount: mediaReferences.size + categoryReferences.size,
|
|
@@ -700,7 +696,7 @@ function addFileReference(references, filePath, displayPath, sourceFileName) {
|
|
|
700
696
|
* @param {"media"|"categories"} kind
|
|
701
697
|
* @param {string|undefined} image
|
|
702
698
|
*/
|
|
703
|
-
function
|
|
699
|
+
function toImagePath(kind, image) {
|
|
704
700
|
if (typeof image !== "string") {
|
|
705
701
|
return undefined
|
|
706
702
|
}
|
package/src/index.js
CHANGED
|
@@ -37,7 +37,7 @@ program
|
|
|
37
37
|
program
|
|
38
38
|
.command("check-files")
|
|
39
39
|
.description(
|
|
40
|
-
"check for missing and orphaned content files and
|
|
40
|
+
"check for missing and orphaned content files and images in a LightNet site",
|
|
41
41
|
)
|
|
42
42
|
.option("--fix", "remove orphaned files")
|
|
43
43
|
.option("--fix-without-confirm", "remove orphaned files without confirmation")
|
|
@@ -45,10 +45,7 @@ program
|
|
|
45
45
|
"--r2",
|
|
46
46
|
"validate remote content files in Cloudflare R2 instead of public/files",
|
|
47
47
|
)
|
|
48
|
-
.option(
|
|
49
|
-
"--scope <values>",
|
|
50
|
-
"comma-separated scopes: content-files,thumbnails",
|
|
51
|
-
)
|
|
48
|
+
.option("--scope <values>", "comma-separated scopes: content-files,images")
|
|
52
49
|
.action(async (options) => {
|
|
53
50
|
try {
|
|
54
51
|
const checkSuccessful = await checkFiles(options)
|