@adaptive-ds/assets-optimizer 0.1.0 → 0.2.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/README.md +36 -15
- package/bun.lock +12 -12
- package/changelogs/2026-04-18_v0.2.0.md +32 -0
- package/changelogs/2026-04-18_v0.2.1.md +5 -0
- package/dist/AssetsOptimizeOptions.d.ts +17 -0
- package/dist/AssetsOptimizeOptions.d.ts.map +1 -0
- package/dist/AssetsOptimizeOptions.js +1 -0
- package/dist/AssetsOptimizeResult.d.ts +12 -0
- package/dist/AssetsOptimizeResult.d.ts.map +1 -0
- package/dist/AssetsOptimizeResult.js +1 -0
- package/dist/assetsOptimize.d.ts +4 -0
- package/dist/assetsOptimize.d.ts.map +1 -0
- package/dist/assetsOptimize.js +61 -0
- package/dist/image/ExpectedImage.d.ts +5 -0
- package/dist/image/ExpectedImage.d.ts.map +1 -0
- package/dist/image/ExpectedImage.js +1 -0
- package/dist/image/ImageFormat.d.ts +2 -0
- package/dist/image/ImageFormat.d.ts.map +1 -0
- package/dist/image/ImageFormat.js +1 -0
- package/dist/image/ProcessImagesOptions.d.ts +10 -0
- package/dist/image/ProcessImagesOptions.d.ts.map +1 -0
- package/dist/image/ProcessImagesOptions.js +1 -0
- package/dist/image/TransformSpec.d.ts +8 -0
- package/dist/image/TransformSpec.d.ts.map +1 -0
- package/dist/image/TransformSpec.js +1 -0
- package/dist/image/buildExpectedImages.d.ts +4 -0
- package/dist/image/buildExpectedImages.d.ts.map +1 -0
- package/dist/image/buildExpectedImages.js +89 -0
- package/dist/image/createOutputHash.d.ts +2 -0
- package/dist/image/createOutputHash.d.ts.map +1 -0
- package/dist/image/createOutputHash.js +4 -0
- package/dist/image/createOutputHashForFile.d.ts +2 -0
- package/dist/image/createOutputHashForFile.d.ts.map +1 -0
- package/dist/image/createOutputHashForFile.js +14 -0
- package/dist/image/createRootImageTransform.d.ts +3 -0
- package/dist/image/createRootImageTransform.d.ts.map +1 -0
- package/dist/image/createRootImageTransform.js +33 -0
- package/dist/image/defaultQuality.d.ts +2 -0
- package/dist/image/defaultQuality.d.ts.map +1 -0
- package/dist/image/defaultQuality.js +1 -0
- package/dist/image/imageFormats.d.ts +3 -0
- package/dist/image/imageFormats.d.ts.map +1 -0
- package/dist/image/imageFormats.js +1 -0
- package/dist/image/isImageFormat.d.ts +3 -0
- package/dist/image/isImageFormat.d.ts.map +1 -0
- package/dist/image/isImageFormat.js +5 -0
- package/dist/image/parseTransformSpec.d.ts +3 -0
- package/dist/image/parseTransformSpec.d.ts.map +1 -0
- package/dist/image/parseTransformSpec.js +26 -0
- package/dist/image/processImage.d.ts +3 -0
- package/dist/image/processImage.d.ts.map +1 -0
- package/dist/image/processImage.js +28 -0
- package/dist/image/processImages.d.ts +3 -0
- package/dist/image/processImages.d.ts.map +1 -0
- package/dist/image/processImages.js +19 -0
- package/dist/image/supportedSourceExtensions.d.ts +2 -0
- package/dist/image/supportedSourceExtensions.d.ts.map +1 -0
- package/dist/image/supportedSourceExtensions.js +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/list/AssetListTypes.d.ts +13 -0
- package/dist/list/AssetListTypes.d.ts.map +1 -0
- package/dist/list/AssetListTypes.js +1 -0
- package/dist/list/formatGeneratedCodeFile.d.ts +3 -0
- package/dist/list/formatGeneratedCodeFile.d.ts.map +1 -0
- package/dist/list/formatGeneratedCodeFile.js +32 -0
- package/dist/list/generateImageList.d.ts +3 -0
- package/dist/list/generateImageList.d.ts.map +1 -0
- package/dist/list/generateImageList.js +85 -0
- package/dist/list/generateVideoList.d.ts +3 -0
- package/dist/list/generateVideoList.d.ts.map +1 -0
- package/dist/list/generateVideoList.js +87 -0
- package/dist/list/getAssetKey.d.ts +2 -0
- package/dist/list/getAssetKey.d.ts.map +1 -0
- package/dist/list/getAssetKey.js +10 -0
- package/dist/list/loadExistingAssetList.d.ts +2 -0
- package/dist/list/loadExistingAssetList.d.ts.map +1 -0
- package/dist/list/loadExistingAssetList.js +23 -0
- package/dist/list/sortAssetMap.d.ts +2 -0
- package/dist/list/sortAssetMap.d.ts.map +1 -0
- package/dist/list/sortAssetMap.js +8 -0
- package/dist/rclone/bisync.d.ts +9 -0
- package/dist/rclone/bisync.d.ts.map +1 -0
- package/dist/rclone/bisync.js +17 -0
- package/dist/rclone/runRclone.d.ts +2 -0
- package/dist/rclone/runRclone.d.ts.map +1 -0
- package/dist/rclone/runRclone.js +13 -0
- package/dist/shared/assertNever.d.ts +2 -0
- package/dist/shared/assertNever.d.ts.map +1 -0
- package/dist/shared/assertNever.js +3 -0
- package/dist/shared/dirExists.d.ts +2 -0
- package/dist/shared/dirExists.d.ts.map +1 -0
- package/dist/shared/dirExists.js +10 -0
- package/dist/shared/getOwnPackageName.d.ts +2 -0
- package/dist/shared/getOwnPackageName.d.ts.map +1 -0
- package/dist/shared/getOwnPackageName.js +21 -0
- package/dist/shared/isMissingDirError.d.ts +2 -0
- package/dist/shared/isMissingDirError.d.ts.map +1 -0
- package/dist/shared/isMissingDirError.js +3 -0
- package/dist/shared/listLocalFiles.d.ts +2 -0
- package/dist/shared/listLocalFiles.d.ts.map +1 -0
- package/dist/shared/listLocalFiles.js +13 -0
- package/dist/shared/logger.d.ts +13 -0
- package/dist/shared/logger.d.ts.map +1 -0
- package/dist/shared/logger.js +47 -0
- package/dist/shared/printSummary.d.ts +4 -0
- package/dist/shared/printSummary.d.ts.map +1 -0
- package/dist/shared/printSummary.js +25 -0
- package/dist/shared/walkFiles.d.ts +2 -0
- package/dist/shared/walkFiles.d.ts.map +1 -0
- package/dist/shared/walkFiles.js +17 -0
- package/dist/video/ProcessVideosOptions.d.ts +11 -0
- package/dist/video/ProcessVideosOptions.d.ts.map +1 -0
- package/dist/video/ProcessVideosOptions.js +1 -0
- package/dist/video/createVideoArgs.d.ts +3 -0
- package/dist/video/createVideoArgs.d.ts.map +1 -0
- package/dist/video/createVideoArgs.js +77 -0
- package/dist/video/createVideoPreviewArgs.d.ts +2 -0
- package/dist/video/createVideoPreviewArgs.d.ts.map +1 -0
- package/dist/video/createVideoPreviewArgs.js +18 -0
- package/dist/video/createVideoPreviewPath.d.ts +2 -0
- package/dist/video/createVideoPreviewPath.d.ts.map +1 -0
- package/dist/video/createVideoPreviewPath.js +3 -0
- package/dist/video/ensureVideoPreviews.d.ts +4 -0
- package/dist/video/ensureVideoPreviews.d.ts.map +1 -0
- package/dist/video/ensureVideoPreviews.js +30 -0
- package/dist/video/getAvailableVideoEncoders.d.ts +3 -0
- package/dist/video/getAvailableVideoEncoders.d.ts.map +1 -0
- package/dist/video/getAvailableVideoEncoders.js +36 -0
- package/dist/video/processLocalVideos.d.ts +4 -0
- package/dist/video/processLocalVideos.d.ts.map +1 -0
- package/dist/video/processLocalVideos.js +26 -0
- package/dist/video/processVideos.d.ts +3 -0
- package/dist/video/processVideos.d.ts.map +1 -0
- package/dist/video/processVideos.js +10 -0
- package/dist/video/runFfmpeg.d.ts +3 -0
- package/dist/video/runFfmpeg.d.ts.map +1 -0
- package/dist/video/runFfmpeg.js +26 -0
- package/dist/video/supportedVideoSourceExtensions.d.ts +2 -0
- package/dist/video/supportedVideoSourceExtensions.d.ts.map +1 -0
- package/dist/video/supportedVideoSourceExtensions.js +1 -0
- package/package.json +1 -1
- package/assets-optimizer.code-workspace +0 -8
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const imageFormats = new Set(["jpg", "png", "webp", "avif"]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isImageFormat.d.ts","sourceRoot":"","sources":["../../src/image/isImageFormat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAGnD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,WAAW,CAEjE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parseTransformSpec.d.ts","sourceRoot":"","sources":["../../src/image/parseTransformSpec.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEvD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA4BxE"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { isImageFormat } from "./isImageFormat.js";
|
|
2
|
+
export function parseTransformSpec(dirName) {
|
|
3
|
+
const match = /^(?<width>\d+)x(?<height>\d+)_(?<format>jpg|png|webp|avif)$/.exec(dirName);
|
|
4
|
+
if (!match?.groups) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const { width: widthValue, height: heightValue, format: formatValue } = match.groups;
|
|
8
|
+
if (!widthValue || !heightValue || !formatValue) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const width = Number.parseInt(widthValue, 10);
|
|
12
|
+
const height = Number.parseInt(heightValue, 10);
|
|
13
|
+
if (!isImageFormat(formatValue)) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const format = formatValue;
|
|
17
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
width,
|
|
22
|
+
height,
|
|
23
|
+
format,
|
|
24
|
+
normalized: `${width}x${height}_${format}`,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processImage.d.ts","sourceRoot":"","sources":["../../src/image/processImage.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEvD,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,aAAa,EACxB,OAAO,SAAiB,GACvB,OAAO,CAAC,IAAI,CAAC,CA0Bf"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import sharp from "sharp";
|
|
2
|
+
import { assertNever } from "../shared/assertNever.js";
|
|
3
|
+
import { defaultQuality } from "./defaultQuality.js";
|
|
4
|
+
export async function processImage(sourceBuffer, outputPath, transform, quality = defaultQuality) {
|
|
5
|
+
let pipeline = sharp(sourceBuffer, { animated: false }).rotate().resize({
|
|
6
|
+
width: transform.width,
|
|
7
|
+
height: transform.height,
|
|
8
|
+
fit: "inside",
|
|
9
|
+
withoutEnlargement: true,
|
|
10
|
+
});
|
|
11
|
+
switch (transform.format) {
|
|
12
|
+
case "jpg":
|
|
13
|
+
pipeline = pipeline.jpeg({ quality });
|
|
14
|
+
break;
|
|
15
|
+
case "png":
|
|
16
|
+
pipeline = pipeline.png({ quality });
|
|
17
|
+
break;
|
|
18
|
+
case "webp":
|
|
19
|
+
pipeline = pipeline.webp({ quality });
|
|
20
|
+
break;
|
|
21
|
+
case "avif":
|
|
22
|
+
pipeline = pipeline.avif({ quality });
|
|
23
|
+
break;
|
|
24
|
+
default:
|
|
25
|
+
assertNever(transform.format);
|
|
26
|
+
}
|
|
27
|
+
await pipeline.toFile(outputPath);
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"processImages.d.ts","sourceRoot":"","sources":["../../src/image/processImages.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAA;AAErE,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBhF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { listLocalFiles } from "../shared/listLocalFiles.js";
|
|
4
|
+
import { buildExpectedImages } from "./buildExpectedImages.js";
|
|
5
|
+
export async function processImages(options) {
|
|
6
|
+
const { allowRootImageFiles, imageOptimizedDir, imageOriginalsDir, result } = options;
|
|
7
|
+
await fs.mkdir(imageOriginalsDir, { recursive: true });
|
|
8
|
+
await fs.mkdir(imageOptimizedDir, { recursive: true });
|
|
9
|
+
const expectedImages = await buildExpectedImages(imageOriginalsDir, imageOptimizedDir, result, allowRootImageFiles);
|
|
10
|
+
const expectedFileNames = new Set(expectedImages.map((image) => image.fileName));
|
|
11
|
+
const localOptimizedFiles = await listLocalFiles(imageOptimizedDir);
|
|
12
|
+
for (const localFile of localOptimizedFiles) {
|
|
13
|
+
const relativeFile = path.relative(imageOptimizedDir, localFile);
|
|
14
|
+
if (!expectedFileNames.has(relativeFile) && !relativeFile.startsWith(".")) {
|
|
15
|
+
await fs.rm(localFile, { force: true });
|
|
16
|
+
result.deletedLocal.push(relativeFile);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supportedSourceExtensions.d.ts","sourceRoot":"","sources":["../../src/image/supportedSourceExtensions.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,yBAAyB,aAAgF,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const supportedSourceExtensions = new Set([".jpg", ".jpeg", ".png", ".webp", ".avif", ".tif", ".tiff", ".gif"]);
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export * from "./AssetsOptimizeOptions.js";
|
|
2
|
+
export * from "./AssetsOptimizeResult.js";
|
|
3
|
+
export * from "./image/buildExpectedImages.js";
|
|
4
|
+
export * from "./image/createOutputHash.js";
|
|
5
|
+
export * from "./image/defaultQuality.js";
|
|
6
|
+
export * from "./image/ExpectedImage.js";
|
|
7
|
+
export * from "./image/ImageFormat.js";
|
|
8
|
+
export * from "./image/parseTransformSpec.js";
|
|
9
|
+
export * from "./image/processImage.js";
|
|
10
|
+
export * from "./image/processImages.js";
|
|
11
|
+
export * from "./image/ProcessImagesOptions.js";
|
|
12
|
+
export * from "./image/supportedSourceExtensions.js";
|
|
13
|
+
export * from "./image/TransformSpec.js";
|
|
14
|
+
export * from "./list/AssetListTypes.js";
|
|
15
|
+
export * from "./list/generateImageList.js";
|
|
16
|
+
export * from "./list/generateVideoList.js";
|
|
17
|
+
export * from "./assetsOptimize.js";
|
|
18
|
+
export * from "./rclone/bisync.js";
|
|
19
|
+
export * from "./rclone/runRclone.js";
|
|
20
|
+
export * from "./shared/listLocalFiles.js";
|
|
21
|
+
export * from "./shared/printSummary.js";
|
|
22
|
+
export * from "./shared/walkFiles.js";
|
|
23
|
+
export * from "./video/createVideoPreviewPath.js";
|
|
24
|
+
export * from "./video/ensureVideoPreviews.js";
|
|
25
|
+
export * from "./video/processVideos.js";
|
|
26
|
+
export * from "./video/ProcessVideosOptions.js";
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,2BAA2B,CAAA;AACzC,cAAc,gCAAgC,CAAA;AAC9C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,2BAA2B,CAAA;AACzC,cAAc,0BAA0B,CAAA;AACxC,cAAc,wBAAwB,CAAA;AACtC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,yBAAyB,CAAA;AACvC,cAAc,0BAA0B,CAAA;AACxC,cAAc,iCAAiC,CAAA;AAC/C,cAAc,sCAAsC,CAAA;AACpD,cAAc,0BAA0B,CAAA;AACxC,cAAc,0BAA0B,CAAA;AACxC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,6BAA6B,CAAA;AAC3C,cAAc,qBAAqB,CAAA;AACnC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA;AACrC,cAAc,4BAA4B,CAAA;AAC1C,cAAc,0BAA0B,CAAA;AACxC,cAAc,uBAAuB,CAAA;AACrC,cAAc,mCAAmC,CAAA;AACjD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,0BAA0B,CAAA;AACxC,cAAc,iCAAiC,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export * from "./AssetsOptimizeOptions.js";
|
|
2
|
+
export * from "./AssetsOptimizeResult.js";
|
|
3
|
+
export * from "./image/buildExpectedImages.js";
|
|
4
|
+
export * from "./image/createOutputHash.js";
|
|
5
|
+
export * from "./image/defaultQuality.js";
|
|
6
|
+
export * from "./image/ExpectedImage.js";
|
|
7
|
+
export * from "./image/ImageFormat.js";
|
|
8
|
+
export * from "./image/parseTransformSpec.js";
|
|
9
|
+
export * from "./image/processImage.js";
|
|
10
|
+
export * from "./image/processImages.js";
|
|
11
|
+
export * from "./image/ProcessImagesOptions.js";
|
|
12
|
+
export * from "./image/supportedSourceExtensions.js";
|
|
13
|
+
export * from "./image/TransformSpec.js";
|
|
14
|
+
export * from "./list/AssetListTypes.js";
|
|
15
|
+
export * from "./list/generateImageList.js";
|
|
16
|
+
export * from "./list/generateVideoList.js";
|
|
17
|
+
export * from "./assetsOptimize.js";
|
|
18
|
+
export * from "./rclone/bisync.js";
|
|
19
|
+
export * from "./rclone/runRclone.js";
|
|
20
|
+
export * from "./shared/listLocalFiles.js";
|
|
21
|
+
export * from "./shared/printSummary.js";
|
|
22
|
+
export * from "./shared/walkFiles.js";
|
|
23
|
+
export * from "./video/createVideoPreviewPath.js";
|
|
24
|
+
export * from "./video/ensureVideoPreviews.js";
|
|
25
|
+
export * from "./video/processVideos.js";
|
|
26
|
+
export * from "./video/ProcessVideosOptions.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ImageType {
|
|
2
|
+
path: string;
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
alt: string;
|
|
6
|
+
mimeType?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface VideoType {
|
|
9
|
+
path: string;
|
|
10
|
+
mimeType?: string;
|
|
11
|
+
image: ImageType;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=AssetListTypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssetListTypes.d.ts","sourceRoot":"","sources":["../../src/list/AssetListTypes.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,SAAS,CAAA;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatGeneratedCodeFile.d.ts","sourceRoot":"","sources":["../../src/list/formatGeneratedCodeFile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAEjD,wBAAsB,uBAAuB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiChG"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export async function formatGeneratedCodeFile(outputPath, logger) {
|
|
2
|
+
const command = ["bunx", "biome", "check", "--write", outputPath];
|
|
3
|
+
logger?.cli(command.join(" "));
|
|
4
|
+
const process = Bun.spawn(command, {
|
|
5
|
+
stdout: "pipe",
|
|
6
|
+
stderr: "pipe",
|
|
7
|
+
});
|
|
8
|
+
const exitCode = await process.exited;
|
|
9
|
+
if (exitCode !== 0) {
|
|
10
|
+
const stderr = await new Response(process.stderr).text();
|
|
11
|
+
const message = `Failed to format generated file ${outputPath}: ${stderr}`;
|
|
12
|
+
if (logger) {
|
|
13
|
+
logger.warn(message);
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
console.warn(message);
|
|
17
|
+
}
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (logger?.isEnabled(3)) {
|
|
21
|
+
const [stdout, stderr] = await Promise.all([
|
|
22
|
+
new Response(process.stdout).text(),
|
|
23
|
+
new Response(process.stderr).text(),
|
|
24
|
+
]);
|
|
25
|
+
if (stdout.trim()) {
|
|
26
|
+
logger.verbose(stdout.trim());
|
|
27
|
+
}
|
|
28
|
+
if (stderr.trim()) {
|
|
29
|
+
logger.verbose(stderr.trim());
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateImageList.d.ts","sourceRoot":"","sources":["../../src/list/generateImageList.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAUjD,wBAAsB,iBAAiB,CACrC,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,mBAAmB,CAAC,EAAE,MAAM,EAC5B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAWf"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { imageSize } from "image-size";
|
|
4
|
+
import { getOwnPackageName } from "../shared/getOwnPackageName.js";
|
|
5
|
+
import { walkFiles } from "../shared/walkFiles.js";
|
|
6
|
+
import { formatGeneratedCodeFile } from "./formatGeneratedCodeFile.js";
|
|
7
|
+
import { getAssetKey } from "./getAssetKey.js";
|
|
8
|
+
import { loadExistingAssetList } from "./loadExistingAssetList.js";
|
|
9
|
+
import { sortAssetMap } from "./sortAssetMap.js";
|
|
10
|
+
const IMAGE_EXTENSIONS = new Set([".jpg", ".jpeg", ".png", ".gif", ".webp", ".avif", ".tiff", ".svg"]);
|
|
11
|
+
export async function generateImageList(imageDirectory, outputPath, imageTypeImportPath, logger) {
|
|
12
|
+
const resolvedImageTypeImportPath = imageTypeImportPath ?? (await getOwnPackageName(import.meta.url));
|
|
13
|
+
const existingImages = await loadExistingAssetList(outputPath, "imageList");
|
|
14
|
+
const imageMap = await processImageFiles(imageDirectory, existingImages, logger);
|
|
15
|
+
const sorted = sortAssetMap(imageMap);
|
|
16
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
17
|
+
await Bun.write(outputPath, createGeneratedImageListContent(sorted, resolvedImageTypeImportPath));
|
|
18
|
+
await formatGeneratedCodeFile(outputPath, logger);
|
|
19
|
+
logger?.files(`generated image list: ${outputPath}`);
|
|
20
|
+
logger?.summary(`Generated ${Object.keys(sorted).length} images to ${outputPath}`);
|
|
21
|
+
}
|
|
22
|
+
async function processImageFiles(directory, existingImages, logger) {
|
|
23
|
+
const imageMap = {};
|
|
24
|
+
for (const filePath of await walkFiles(directory)) {
|
|
25
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
26
|
+
if (!IMAGE_EXTENSIONS.has(extension)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const buffer = await fs.readFile(filePath);
|
|
31
|
+
const dimensions = imageSize(buffer);
|
|
32
|
+
if (!dimensions.width || !dimensions.height) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
const relativePath = path.relative(directory, filePath);
|
|
36
|
+
const key = normalizeGeneratedImageKey(getAssetKey(filePath));
|
|
37
|
+
const fileName = path.basename(filePath, extension);
|
|
38
|
+
imageMap[key] = {
|
|
39
|
+
path: relativePath,
|
|
40
|
+
width: dimensions.width,
|
|
41
|
+
height: dimensions.height,
|
|
42
|
+
alt: existingImages[key]?.alt || formatGeneratedImageAlt(fileName),
|
|
43
|
+
mimeType: getImageMimeType(extension),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
logger?.error(`Error processing ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return imageMap;
|
|
51
|
+
}
|
|
52
|
+
function normalizeGeneratedImageKey(key) {
|
|
53
|
+
return key.replace(/_[0-9a-f]{8}$/i, "");
|
|
54
|
+
}
|
|
55
|
+
function formatGeneratedImageAlt(fileName) {
|
|
56
|
+
return normalizeGeneratedImageKey(fileName).replace(/[-_]/g, " ");
|
|
57
|
+
}
|
|
58
|
+
function createGeneratedImageListContent(imageMap, imageTypeImportPath) {
|
|
59
|
+
return `import type { ImageType } from "${imageTypeImportPath}"
|
|
60
|
+
|
|
61
|
+
// Auto-generated, manual changes will be lost, generated by optimizeAssets from @adaptive-ds/assets-optimizer
|
|
62
|
+
export const imageList = ${JSON.stringify(imageMap, null, 2)} as const satisfies Record<string, ImageType>
|
|
63
|
+
`;
|
|
64
|
+
}
|
|
65
|
+
function getImageMimeType(extension) {
|
|
66
|
+
switch (extension) {
|
|
67
|
+
case ".jpg":
|
|
68
|
+
case ".jpeg":
|
|
69
|
+
return "image/jpeg";
|
|
70
|
+
case ".png":
|
|
71
|
+
return "image/png";
|
|
72
|
+
case ".gif":
|
|
73
|
+
return "image/gif";
|
|
74
|
+
case ".webp":
|
|
75
|
+
return "image/webp";
|
|
76
|
+
case ".avif":
|
|
77
|
+
return "image/avif";
|
|
78
|
+
case ".tiff":
|
|
79
|
+
return "image/tiff";
|
|
80
|
+
case ".svg":
|
|
81
|
+
return "image/svg+xml";
|
|
82
|
+
default:
|
|
83
|
+
return "application/octet-stream";
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generateVideoList.d.ts","sourceRoot":"","sources":["../../src/list/generateVideoList.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAUjD,wBAAsB,iBAAiB,CACrC,eAAe,EAAE,MAAM,EACvB,UAAU,EAAE,MAAM,EAClB,mBAAmB,CAAC,EAAE,MAAM,EAC5B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAUf"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { imageSize } from "image-size";
|
|
4
|
+
import { getOwnPackageName } from "../shared/getOwnPackageName.js";
|
|
5
|
+
import { isMissingDirError } from "../shared/isMissingDirError.js";
|
|
6
|
+
import { walkFiles } from "../shared/walkFiles.js";
|
|
7
|
+
import { createVideoPreviewPath } from "../video/createVideoPreviewPath.js";
|
|
8
|
+
import { supportedVideoSourceExtensions } from "../video/supportedVideoSourceExtensions.js";
|
|
9
|
+
import { formatGeneratedCodeFile } from "./formatGeneratedCodeFile.js";
|
|
10
|
+
import { getAssetKey } from "./getAssetKey.js";
|
|
11
|
+
import { loadExistingAssetList } from "./loadExistingAssetList.js";
|
|
12
|
+
import { sortAssetMap } from "./sortAssetMap.js";
|
|
13
|
+
export async function generateVideoList(videosDirectory, outputPath, videoTypeImportPath, logger) {
|
|
14
|
+
const resolvedVideoTypeImportPath = videoTypeImportPath ?? (await getOwnPackageName(import.meta.url));
|
|
15
|
+
const existingVideos = await loadExistingAssetList(outputPath, "videoList");
|
|
16
|
+
const videoMap = sortAssetMap(await processVideoFiles(videosDirectory, existingVideos));
|
|
17
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
18
|
+
await Bun.write(outputPath, createGeneratedVideoListContent(videoMap, resolvedVideoTypeImportPath));
|
|
19
|
+
await formatGeneratedCodeFile(outputPath, logger);
|
|
20
|
+
logger?.files(`generated video list: ${outputPath}`);
|
|
21
|
+
logger?.summary(`Generated ${Object.keys(videoMap).length} videos to ${outputPath}`);
|
|
22
|
+
}
|
|
23
|
+
async function processVideoFiles(directory, existingVideos) {
|
|
24
|
+
const videoMap = {};
|
|
25
|
+
let filePaths;
|
|
26
|
+
try {
|
|
27
|
+
filePaths = await walkFiles(directory);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (isMissingDirError(error)) {
|
|
31
|
+
return videoMap;
|
|
32
|
+
}
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
for (const filePath of filePaths) {
|
|
36
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
37
|
+
if (!supportedVideoSourceExtensions.has(extension)) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const relativePath = path.relative(directory, filePath);
|
|
41
|
+
const key = getAssetKey(filePath);
|
|
42
|
+
const previewPath = createVideoPreviewPath(filePath);
|
|
43
|
+
const previewBuffer = await fs.readFile(previewPath);
|
|
44
|
+
const previewDimensions = imageSize(previewBuffer);
|
|
45
|
+
if (!previewDimensions.width || !previewDimensions.height) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const fileName = path.basename(filePath, extension);
|
|
49
|
+
const existingPreview = existingVideos[key]?.image;
|
|
50
|
+
videoMap[key] = {
|
|
51
|
+
path: relativePath,
|
|
52
|
+
mimeType: getVideoMimeType(extension),
|
|
53
|
+
image: {
|
|
54
|
+
path: path.relative(directory, previewPath),
|
|
55
|
+
width: previewDimensions.width,
|
|
56
|
+
height: previewDimensions.height,
|
|
57
|
+
alt: existingPreview?.alt || fileName.replace(/[-_]/g, " "),
|
|
58
|
+
mimeType: "image/jpeg",
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return videoMap;
|
|
63
|
+
}
|
|
64
|
+
function createGeneratedVideoListContent(videoMap, videoTypeImportPath) {
|
|
65
|
+
return `import type { VideoType } from "${videoTypeImportPath}"
|
|
66
|
+
|
|
67
|
+
// Auto-generated, manual changes will be lost, generated by optimizeAssets from @adaptive-ds/assets-optimizer
|
|
68
|
+
export const videoList = ${JSON.stringify(videoMap, null, 2)} as const satisfies Record<string, VideoType>
|
|
69
|
+
`;
|
|
70
|
+
}
|
|
71
|
+
function getVideoMimeType(extension) {
|
|
72
|
+
switch (extension) {
|
|
73
|
+
case ".mp4":
|
|
74
|
+
case ".m4v":
|
|
75
|
+
return "video/mp4";
|
|
76
|
+
case ".mov":
|
|
77
|
+
return "video/quicktime";
|
|
78
|
+
case ".webm":
|
|
79
|
+
return "video/webm";
|
|
80
|
+
case ".avi":
|
|
81
|
+
return "video/x-msvideo";
|
|
82
|
+
case ".mkv":
|
|
83
|
+
return "video/x-matroska";
|
|
84
|
+
default:
|
|
85
|
+
return "application/octet-stream";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getAssetKey.d.ts","sourceRoot":"","sources":["../../src/list/getAssetKey.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUpD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
export function getAssetKey(filePath) {
|
|
3
|
+
const extension = path.extname(filePath);
|
|
4
|
+
const fileName = path.basename(filePath, extension);
|
|
5
|
+
let key = fileName.replace(/-/g, "_");
|
|
6
|
+
if (/^\d/.test(key)) {
|
|
7
|
+
key = `i${key}`;
|
|
8
|
+
}
|
|
9
|
+
return key;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadExistingAssetList.d.ts","sourceRoot":"","sources":["../../src/list/loadExistingAssetList.ts"],"names":[],"mappings":"AAGA,wBAAsB,qBAAqB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAgB/G"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
export async function loadExistingAssetList(filePath, exportName) {
|
|
4
|
+
try {
|
|
5
|
+
await fs.access(filePath);
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return {};
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const moduleUrl = `${pathToFileURL(filePath).href}?t=${Date.now()}`;
|
|
12
|
+
const existingModule = (await import(moduleUrl));
|
|
13
|
+
const existingList = existingModule[exportName];
|
|
14
|
+
return isRecord(existingList) ? existingList : {};
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
console.warn(`Failed to load existing ${exportName} from ${filePath}:`, error);
|
|
18
|
+
return {};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function isRecord(value) {
|
|
22
|
+
return typeof value === "object" && value !== null;
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sortAssetMap.d.ts","sourceRoot":"","sources":["../../src/list/sortAssetMap.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAU9E"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface BisyncOptions {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
resync?: boolean;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* https://rclone.org/bisync/#limitations
|
|
7
|
+
*/
|
|
8
|
+
export declare function bisync(localPath: string, remotePath: string, options?: BisyncOptions): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=bisync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bisync.d.ts","sourceRoot":"","sources":["../../src/rclone/bisync.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAa9G"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { dirExists } from "../shared/dirExists.js";
|
|
3
|
+
import { runRclone } from "./runRclone.js";
|
|
4
|
+
/**
|
|
5
|
+
* https://rclone.org/bisync/#limitations
|
|
6
|
+
*/
|
|
7
|
+
export async function bisync(localPath, remotePath, options = {}) {
|
|
8
|
+
const cwd = options.cwd ?? process.cwd();
|
|
9
|
+
const localDirExists = await dirExists(localPath);
|
|
10
|
+
await fs.mkdir(localPath, { recursive: true });
|
|
11
|
+
await runRclone(["mkdir", remotePath], cwd);
|
|
12
|
+
const args = ["bisync", localPath, remotePath, "--create-empty-src-dirs", "--resilient", "--recover"];
|
|
13
|
+
if (options.resync === true || !localDirExists) {
|
|
14
|
+
args.push("--resync");
|
|
15
|
+
}
|
|
16
|
+
await runRclone(args, cwd);
|
|
17
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runRclone.d.ts","sourceRoot":"","sources":["../../src/rclone/runRclone.ts"],"names":[],"mappings":"AAAA,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAclF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export async function runRclone(args, cwd = process.cwd()) {
|
|
2
|
+
const command = ["rclone", ...args];
|
|
3
|
+
console.log(command.join(" "));
|
|
4
|
+
const proc = Bun.spawn(command, {
|
|
5
|
+
cwd,
|
|
6
|
+
stdout: "inherit",
|
|
7
|
+
stderr: "inherit",
|
|
8
|
+
});
|
|
9
|
+
const exitCode = await proc.exited;
|
|
10
|
+
if (exitCode !== 0) {
|
|
11
|
+
throw new Error(`rclone ${args.join(" ")} failed with exit code ${exitCode}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assertNever.d.ts","sourceRoot":"","sources":["../../src/shared/assertNever.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAE/C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dirExists.d.ts","sourceRoot":"","sources":["../../src/shared/dirExists.ts"],"names":[],"mappings":"AAEA,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO7D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getOwnPackageName.d.ts","sourceRoot":"","sources":["../../src/shared/getOwnPackageName.ts"],"names":[],"mappings":"AAIA,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoB1E"}
|