From 08d0c8d84b54074f3818a839a450247951499f60 Mon Sep 17 00:00:00 2001 From: Alexander Kachkaev Date: Fri, 6 Feb 2026 22:58:43 +0000 Subject: [PATCH 1/2] feat(image): add outputName option for custom asset naming Add a new `outputName` option to `s.image()` that allows customizing the output filename template per-schema. This is more flexible than a simple prefix as it allows full control over the output name pattern. Features: - Supports all existing placeholders: [name], [hash], [hash:N], [ext] - Supports subdirectories (e.g., 'logos/[name]-[hash:6].[ext]') - Automatically creates subdirectories in the output assets folder --- src/output.ts | 12 ++++++++---- src/schemas/image.ts | 19 ++++++++++++------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/output.ts b/src/output.ts index 45509f35..ffb7a741 100644 --- a/src/output.ts +++ b/src/output.ts @@ -1,5 +1,5 @@ -import { copyFile, writeFile } from 'node:fs/promises' -import { join, relative } from 'node:path' +import { copyFile, mkdir, writeFile } from 'node:fs/promises' +import { dirname, join, relative } from 'node:path' import { logger } from './logger' @@ -96,8 +96,12 @@ export const outputAssets = async (dest: string, assets: Map): P logger.log(`skipped copy '${name}' with same content`) return } - await copyFile(from, join(dest, name)) - // logger.log(`copied '${name}' from '${from}'`) + const destPath = join(dest, name) + const destDir = dirname(destPath) + if (destDir !== dest) { + await mkdir(destDir, { recursive: true }) + } + await copyFile(from, destPath) emitted.set(name, from) count++ }) diff --git a/src/schemas/image.ts b/src/schemas/image.ts index be17b396..acc5f0d2 100644 --- a/src/schemas/image.ts +++ b/src/schemas/image.ts @@ -12,17 +12,21 @@ export interface ImageOptions { * @default undefined */ absoluteRoot?: string - // /** - // * allow remote url - // * @default false - // */ - // allowRemoteUrl?: boolean + /** + * Custom output name template for the asset + * Supports placeholders: [name], [hash], [hash:N], [ext] + * Can include subdirectories (e.g., 'logos/[name]-[hash:6].[ext]') + * @default undefined (uses global output.name from config) + * @example 'logo-[name]-[hash:6].[ext]' + * @example 'logos/[name]-[hash:6].[ext]' + */ + outputName?: string } /** * Image schema */ -export const image = ({ absoluteRoot }: ImageOptions = {}) => +export const image = ({ absoluteRoot, outputName }: ImageOptions = {}) => string().transform(async (value, { meta, addIssue }) => { try { if (absoluteRoot && /^\//.test(value)) { @@ -44,7 +48,8 @@ export const image = ({ absoluteRoot }: ImageOptions = {}) => const { output } = meta.config // process asset as relative path - return await processAsset(value, meta.path, output.name, output.base, true) + const assetName = outputName ?? output.name + return await processAsset(value, meta.path, assetName, output.base, true) } catch (err) { const message = err instanceof Error ? err.message : String(err) addIssue({ fatal: true, code: 'custom', message }) From 816ac69764cccaac5e132d48c187612947f20cbf Mon Sep 17 00:00:00 2001 From: Alexander Kachkaev Date: Fri, 6 Feb 2026 23:26:56 +0000 Subject: [PATCH 2/2] Restore comment --- src/output.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/output.ts b/src/output.ts index ffb7a741..4d667b42 100644 --- a/src/output.ts +++ b/src/output.ts @@ -102,6 +102,7 @@ export const outputAssets = async (dest: string, assets: Map): P await mkdir(destDir, { recursive: true }) } await copyFile(from, destPath) + // logger.log(`copied '${name}' from '${from}'`) emitted.set(name, from) count++ })