Skip to content
Open
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
2 changes: 1 addition & 1 deletion src/diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ function diffArgsCheck(
const { output } = translateOptions({
...options,
tempDir,
});
} as DiffCommandOptions & { tempDir: string });
if (typeof output !== 'string') {
throw new Error('Output path is required.');
}
Expand Down
49 changes: 31 additions & 18 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,27 +103,40 @@ export async function question(query: string, password?: boolean) {

export function translateOptions<T extends Record<string, unknown>>(
options: T,
map?: Record<string, string>,
): T & Record<string, unknown> {
const ret: Record<string, unknown> = {};
for (const key in options) {
const value = options[key];
if (typeof value === 'string') {
ret[key] = value.replace(/\$\{(\w+)\}/g, (placeholder, name) => {
const replacement = options[name] ?? process.env[name];
if (
typeof replacement === 'string' ||
typeof replacement === 'number' ||
typeof replacement === 'boolean'
) {
return String(replacement);
}
return placeholder;
});
} else {
ret[key] = value;
if (!map) {
// Existing logic for template replacement if no map is provided
const ret: Record<string, unknown> = {};
for (const key in options) {
const value = options[key];
if (typeof value === 'string') {
ret[key] = value.replace(/\$\{(\w+)\}/g, (placeholder, name) => {
const replacement = options[name] ?? process.env[name];
if (
typeof replacement === 'string' ||
typeof replacement === 'number' ||
typeof replacement === 'boolean'
) {
return String(replacement);
}
return placeholder;
});
} else {
ret[key] = value;
}
}
return ret as T & Record<string, unknown>;
}

const result: Record<string, unknown> = { ...options };
for (const [key, value] of Object.entries(map)) {
if (result[key] !== undefined) {
result[value] = result[key];
delete result[key];
}
}
return ret as T & Record<string, unknown>;
return result as T;
}

export async function getApkInfo(fn: string) {
Expand Down
91 changes: 91 additions & 0 deletions tests/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,97 @@ describe('translateOptions', () => {
const result = translateOptions(options);
expect(result.output).toBe('v42.ppk');
});

test('handles boolean values in template', () => {
const options = {
flag: false,
output: `is-false-${templateVar('flag')}.ppk`,
};
const result = translateOptions(options);
expect(result.output).toBe('is-false-false.ppk');
});

test('handles zero numeric values in template', () => {
const options = {
count: 0,
output: `v${templateVar('count')}.ppk`,
};
const result = translateOptions(options);
expect(result.output).toBe('v0.ppk');
});

test('handles empty string values in template', () => {
const options = {
empty: '',
output: `pre-${templateVar('empty')}-post.ppk`,
};
const result = translateOptions(options);
expect(result.output).toBe('pre--post.ppk');
});

test('ignores object values in template and leaves placeholder', () => {
const options = {
obj: { key: 'value' },
output: `obj-${templateVar('obj')}.ppk`,
};
const result = translateOptions(options);
expect(result.output).toBe(`obj-${templateVar('obj')}.ppk`);
});

test('ignores array values in template and leaves placeholder', () => {
const options = {
arr: [1, 2, 3],
output: `arr-${templateVar('arr')}.ppk`,
};
const result = translateOptions(options);
expect(result.output).toBe(`arr-${templateVar('arr')}.ppk`);
});

test('replaces multiple identical placeholders', () => {
const options = {
val: 'test',
output: `${templateVar('val')}-${templateVar('val')}.ppk`,
};
const result = translateOptions(options);
expect(result.output).toBe('test-test.ppk');
});

describe('translateOptions with map', () => {
test('maps keys according to the provided map', () => {
const options = { oldKey: 'value', keepKey: 'keep' };
const map = { oldKey: 'newKey' };
const result = translateOptions(options, map);
expect(result).toEqual({ newKey: 'value', keepKey: 'keep' });
});

test('ignores missing keys in options', () => {
const options = { existingKey: 'value' };
const map = { missingKey: 'newKey' };
const result = translateOptions(options, map);
expect(result).toEqual({ existingKey: 'value' });
});

test('maps multiple keys', () => {
const options = { a: 1, b: 2, c: 3 };
const map = { a: 'alpha', b: 'beta' };
const result = translateOptions(options, map);
expect(result).toEqual({ alpha: 1, beta: 2, c: 3 });
});

test('handles mapping to existing keys by overwriting', () => {
const options = { a: 1, b: 2 };
const map = { a: 'b' };
const result = translateOptions(options, map);
expect(result).toEqual({ b: 1 });
});

test('handles undefined values', () => {
const options = { a: undefined, b: null };
const map = { a: 'newA', b: 'newB' };
const result = translateOptions(options, map);
expect(result).toEqual({ a: undefined, newB: null });
});
});
});

describe('isNonInteractive', () => {
Expand Down