Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions packages/cli/src/args.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,32 @@ describe('cli', () => {
expect(parseCliArgs().help).toBe(true)
})

it('should read the placeholder and select options', () => {
setArgs(
'--placeholder-width',
'24',
'--placeholder-format',
'jpg',
'--select-format',
'webp',
'--select-width',
'640'
)

const args = parseCliArgs()

expect(args.placeholderWidth).toBe(24)
expect(args.placeholderFormat).toBe('jpg')
expect(args.selectFormat).toBe('webp')
expect(args.selectWidth).toBe(640)
})

it('should read the negated placeholder flag', () => {
setArgs('--no-placeholder')

expect(parseCliArgs().placeholder).toBe(false)
})

it('should read the module format without validating it', () => {
setArgs('--module', 'typescript')

Expand Down
30 changes: 30 additions & 0 deletions packages/cli/src/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ export const usage = `srcset [...sources] [...options]
--no-scaling-up Do not generate images larger than the source.
--dest, -d Destination directory.
--module Generate an image module: ts, js, ts-dir or js-dir.
--placeholder Add the \`placeholder\` module export - a tiny variant inlined as a data-url.
--placeholder-width Placeholder width in pixels. Implies --placeholder.
--placeholder-format Placeholder format: webp or jpg. Implies --placeholder.
--select-id Module default export: the variant with this resource id.
--select-format Module default export: the variant of this format.
--select-width Module default export: the variant of this width, value less than or equal to 1 is treated as a multiplier.
--config, -c Config file path. Defaults to the \`srcset.config.js\` lookup.
--concurrency Concurrency limit.
`
Expand All @@ -36,6 +42,12 @@ export interface CliArgs {
scalingUp: boolean | undefined
dest: string | undefined
module: string | undefined
placeholder: boolean | undefined
placeholderWidth: number | undefined
placeholderFormat: string | undefined
selectId: string | undefined
selectFormat: string | undefined
selectWidth: number | undefined
config: string | undefined
concurrency: number | undefined
}
Expand All @@ -55,6 +67,12 @@ export function parseCliArgs(): CliArgs {
scalingUp,
dest,
module: moduleFormat,
placeholder,
placeholderWidth,
placeholderFormat,
selectId,
selectFormat,
selectWidth,
config,
concurrency
} = readOptions(
Expand All @@ -67,6 +85,12 @@ export function parseCliArgs(): CliArgs {
flag(autocase('scalingUp')),
option(alias('dest', 'd'), String),
option('module', String),
flag('placeholder'),
option(autocase('placeholderWidth'), Number),
option(autocase('placeholderFormat'), String),
option(autocase('selectId'), String),
option(autocase('selectFormat'), String),
option(autocase('selectWidth'), Number),
option(alias('config', 'c'), String),
option('concurrency', Number)
)
Expand Down Expand Up @@ -99,6 +123,12 @@ export function parseCliArgs(): CliArgs {
scalingUp,
dest,
module: moduleFormat,
placeholder,
placeholderWidth,
placeholderFormat,
selectId,
selectFormat,
selectWidth,
config,
concurrency
}
Expand Down
105 changes: 105 additions & 0 deletions packages/cli/src/options.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ const noArgs: CliArgs = {
scalingUp: undefined,
dest: undefined,
module: undefined,
placeholder: undefined,
placeholderWidth: undefined,
placeholderFormat: undefined,
selectId: undefined,
selectFormat: undefined,
selectWidth: undefined,
config: undefined,
concurrency: undefined
}
Expand Down Expand Up @@ -92,6 +98,105 @@ describe('cli', () => {
})).toThrow('Unknown module format: "typescript"')
})

it('should switch the placeholder on', () => {
expect(toCliOptions({
...noArgs,
placeholder: true
}, {
src: 'images/*.jpg',
dest: 'dist'
}).placeholder).toBe(true)
})

it('should read the placeholder options', () => {
expect(toCliOptions({
...noArgs,
placeholderWidth: 24,
placeholderFormat: 'jpg'
}, {
src: 'images/*.jpg',
dest: 'dist'
}).placeholder).toEqual({
width: 24,
format: 'jpg'
})
})

it('should let the placeholder options switch the placeholder on', () => {
expect(toCliOptions({
...noArgs,
placeholderWidth: 24
}, {
src: 'images/*.jpg',
dest: 'dist'
}).placeholder).toEqual({
width: 24
})
})

it('should keep the negated placeholder off', () => {
expect(toCliOptions({
...noArgs,
placeholder: false,
placeholderWidth: 24
}, {
src: 'images/*.jpg',
dest: 'dist',
placeholder: true
}).placeholder).toBe(false)
})

it('should reject an unknown placeholder format', () => {
expect(() => toCliOptions({
...noArgs,
placeholderFormat: 'png'
}, {
src: 'images/*.jpg',
dest: 'dist'
})).toThrow('Unknown placeholder format: "png"')
})

it('should fall back to the placeholder of the config', () => {
expect(toCliOptions(noArgs, {
src: 'images/*.jpg',
dest: 'dist',
placeholder: {
width: 8
}
}).placeholder).toEqual({
width: 8
})
})

it('should read the selection of the default export', () => {
expect(toCliOptions({
...noArgs,
selectFormat: 'webp',
selectWidth: 640
}, {
src: 'images/*.jpg',
dest: 'dist'
}).select).toEqual({
format: 'webp',
width: 640
})
})

it('should replace the selection of the config', () => {
expect(toCliOptions({
...noArgs,
selectId: 'jpg640'
}, {
src: 'images/*.jpg',
dest: 'dist',
select: {
format: 'avif'
}
}).select).toEqual({
id: 'jpg640'
})
})

it('should throw without sources', () => {
expect(() => toCliOptions(noArgs, {
dest: 'dist'
Expand Down
76 changes: 76 additions & 0 deletions packages/cli/src/options.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import type {
PlaceholderOptions,
SrcSetEntrySelect
} from '@srcset/bundler-utils'
import type { CliArgs } from './args.ts'
import type {
SrcSetCliOptions,
SrcSetModuleFormat
} from './types.ts'

const moduleFormats = new Set<SrcSetModuleFormat>(['ts', 'js', 'ts-dir', 'js-dir'])
const placeholderFormats = new Set<PlaceholderOptions['format']>(['webp', 'jpg'])

/**
* Read the module format option: the cli argument and the config file
Expand All @@ -24,6 +29,73 @@ export function toModuleFormat(value: string | undefined) {
return value as SrcSetModuleFormat
}

/**
* Read the placeholder options: a width or a format switches the placeholder
* on by itself, there is nothing else they could mean.
* @param args - Parsed command line arguments.
* @returns Placeholder options, or `undefined` when no option is set.
*/
function toPlaceholder(args: CliArgs) {
const {
placeholder,
placeholderWidth: width,
placeholderFormat
} = args

if (placeholderFormat !== undefined && !placeholderFormats.has(placeholderFormat as PlaceholderOptions['format'])) {
throw new Error(`Unknown placeholder format: "${placeholderFormat}". Use webp or jpg.`)
}

const format = placeholderFormat as PlaceholderOptions['format']

if (width === undefined && format === undefined) {
return placeholder
}

// An explicit `--no-placeholder` wins over the options of the same run.
return placeholder === false
? false
: {
...width !== undefined && {
width
},
...format !== undefined && {
format
}
}
}

/**
* Read the selection of the variant for the default export of the module.
* @param args - Parsed command line arguments.
* @returns Selection, or `undefined` when no option is set.
*/
function toSelect(args: CliArgs) {
const {
selectId: id,
selectFormat: format,
selectWidth: width
} = args

if (id === undefined && format === undefined && width === undefined) {
return undefined
}

const select: SrcSetEntrySelect = {
...id !== undefined && {
id
},
...format !== undefined && {
format
},
...width !== undefined && {
width
}
}

return select
}

/**
* Merge the command line arguments into the config file options:
* an argument wins, an option it does not carry falls back to the config.
Expand All @@ -32,12 +104,16 @@ export function toModuleFormat(value: string | undefined) {
* @returns Options of the run.
*/
export function toCliOptions(args: CliArgs, config: Partial<SrcSetCliOptions>): SrcSetCliOptions {
const placeholder = toPlaceholder(args)
const select = toSelect(args)
const options: SrcSetCliOptions = {
...config,
src: args.sources.length ? args.sources : config.src ?? [],
dest: args.dest ?? config.dest ?? '',
rules: args.rule ? [args.rule] : config.rules,
module: toModuleFormat(args.module ?? config.module),
placeholder: placeholder ?? config.placeholder,
select: select ?? config.select,
verbose: args.verbose ?? config.verbose,
skipOptimization: args.skipOptimization ?? config.skipOptimization,
scalingUp: args.scalingUp ?? config.scalingUp,
Expand Down