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
174 changes: 173 additions & 1 deletion packages/url-loader/src/constants/qualifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,11 +285,183 @@ export const effects: Record<string, Qualifier> = {
} as const;

export const flags: Record<string, Qualifier> = {
animated: {
prefix: 'fl',
qualifier: 'animated'
},
anyFormat: {
prefix: 'fl',
qualifier: 'any_format'
},
apng: {
prefix: 'fl',
qualifier: 'apng'
},
attachment: {
prefix: 'fl',
qualifier: 'attachment'
},
awebp: {
prefix: 'fl',
qualifier: 'awebp'
},
clip: {
prefix: 'fl',
qualifier: 'clip'
},
clipEvenodd: {
prefix: 'fl',
qualifier: 'clip_evenodd'
},
cutter: {
prefix: 'fl',
qualifier: 'cutter'
},
draco: {
prefix: 'fl',
qualifier: 'draco'
},
forceIcc: {
prefix: 'fl',
qualifier: 'force_icc'
},
forceStrip: {
prefix: 'fl',
qualifier: 'force_strip'
},
getinfo: {
prefix: 'fl',
qualifier: 'getinfo'
},
group4: {
prefix: 'fl',
qualifier: 'group4'
},
hlsv3: {
prefix: 'fl',
qualifier: 'hlsv3'
},
ignoreAspectRatio: {
prefix: 'fl',
qualifier: 'ignore_aspect_ratio'
},
ignoreMaskChannels: {
prefix: 'fl',
qualifier: 'ignore_mask_channels'
},
immutableCache: {
prefix: 'fl',
qualifier: 'immutable_cache'
},
keepAttribution: {
prefix: 'fl',
qualifier: 'keep_attribution'
},
keepDar: {
prefix: 'fl',
qualifier: 'keep_dar'
},
keepIptc: {
prefix: 'fl',
qualifier: 'keep_iptc'
},
layerApply: {
prefix: 'fl',
qualifier: 'layer_apply'
},
lossy: {
prefix: 'fl',
qualifier: 'lossy'
},
mono: {
prefix: 'fl',
qualifier: 'mono'
},
noOverflow: {
prefix: 'fl',
qualifier: 'no_overflow'
},
noStream: {
prefix: 'fl',
qualifier: 'no_stream'
},
png8: {
prefix: 'fl',
qualifier: 'png8'
},
png24: {
prefix: 'fl',
qualifier: 'png24'
},
png32: {
prefix: 'fl',
qualifier: 'png32'
},
preserveTransparency: {
prefix: 'fl',
qualifier: 'preserve_transparency'
},
progressive: {
prefix: 'fl',
qualifier: 'progressive'
},
rasterize: {
prefix: 'fl',
qualifier: 'rasterize'
},
regionRelative: {
prefix: 'fl',
qualifier: 'region_relative'
},
relative: {
prefix: 'fl',
qualifier: 'relative',
location: 'primary'
}
},
replaceImage: {
prefix: 'fl',
qualifier: 'replace_image'
},
sanitize: {
prefix: 'fl',
qualifier: 'sanitize'
},
splice: {
prefix: 'fl',
qualifier: 'splice'
},
streamingAttachment: {
prefix: 'fl',
qualifier: 'streaming_attachment'
},
stripProfile: {
prefix: 'fl',
qualifier: 'strip_profile'
},
textDisallowOverflow: {
prefix: 'fl',
qualifier: 'text_disallow_overflow'
},
textNoTrim: {
prefix: 'fl',
qualifier: 'text_no_trim'
},
tif8Lzw: {
prefix: 'fl',
qualifier: 'tif8_lzw'
},
tiled: {
prefix: 'fl',
qualifier: 'tiled'
},
truncateTs: {
prefix: 'fl',
qualifier: 'truncate_ts'
},
waveform: {
prefix: 'fl',
qualifier: 'waveform'
},
} as const;

export const video: Record<string, Qualifier> = {
Expand Down
36 changes: 36 additions & 0 deletions packages/url-loader/src/plugins/flags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { PluginSettings } from '../types/plugins';

import { flags as qualifiersFlags } from '../constants/qualifiers';

export const props = ['flags'];
export const assetTypes = ['image', 'images', 'video', 'videos'];

const supportedFlags = Object.entries(qualifiersFlags).map(([_, { qualifier }]) => qualifier);

export function plugin(props: PluginSettings) {
const { cldAsset, options } = props;
const { flags = [] } = options;

// First iteration of adding flags follows the same pattern
// as the top level option from Cloudinary URL Gen SDK where
// each flag is individually added as its own segment via
// the addFlag method. Flags can have additional context and
// may warrant case-by-case applications

if ( Array.isArray(flags) && flags.length > 0 ) {
flags.forEach(flag => {
if ( !supportedFlags.includes(flag) ) return;
cldAsset.addFlag(flag)
});
} else if ( typeof flags === 'object' ) {
Object.entries(flags).forEach(([qualifier, value]) => {
if ( !supportedFlags.includes(qualifier) ) return;
// The addFlag method encodes some characters, specifically
// the "." character which breaks some use cases like
// du_2.5
cldAsset.addTransformation(`fl_${qualifier}:${value}`);
});
}

return {};
}
1 change: 1 addition & 0 deletions packages/url-loader/src/types/asset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface AssetOptions {
crop?: string;
deliveryType?: string;
effects?: Array<any>;
flags?: Array<string> | object;
format?: string;
gravity?: string;
height?: string | number;
Expand Down
58 changes: 58 additions & 0 deletions packages/url-loader/tests/plugins/flags.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Cloudinary } from '@cloudinary/url-gen';

import * as flagsPlugin from '../../src/plugins/flags';

const { plugin } = flagsPlugin

const cld = new Cloudinary({
cloud: {
cloudName: 'test-cloud-name'
}
});

describe('Plugins', () => {
describe('Flags', () => {
it('should include a flag on an image', () => {
const src = 'turtle';
const assetType = 'image';
const cldVideo = cld.image(src);
const flags = ['keep_iptc'];
plugin({ cldAsset: cldVideo, options: {
assetType,
src,
flags
} });
expect(cldVideo.toURL()).toContain(`${assetType}/upload/fl_keep_iptc/${src}`);
});

it('should add multiple flags to a video', () => {
const src = 'turtle.mp4';
const assetType = 'video';
const cldVideo = cld.video(src);
const flags = ['no_stream', 'splice']
plugin({ cldAsset: cldVideo, options: {
assetType,
src,
flags
} });
expect(cldVideo.toURL()).toContain(`${assetType}/upload/fl_no_stream/fl_splice/${src}`);
});

it('should add custom flag definitions via object syntax', () => {
const src = 'turtle';
const assetType = 'image';
const cldVideo = cld.image(src);
const flags = {
splice: 'transition_(name_circleopen;du_2.5)',
attachment: 'space_jellyfish'
}
const flagsString = Object.entries(flags).map(([q, v]) => `fl_${q}:${v}`).join('/');
plugin({ cldAsset: cldVideo, options: {
assetType,
src,
flags
} });
expect(cldVideo.toURL()).toContain(`${assetType}/upload/${flagsString}/${src}`);
});
});
});