Skip to content

Commit 242435e

Browse files
committed
feat(utils): interpolate variables in strings
1 parent 3fb3b43 commit 242435e

File tree

5 files changed

+67
-4
lines changed

5 files changed

+67
-4
lines changed

packages/ci/src/lib/monorepo/handlers/nx.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import path from 'node:path';
22
import {
33
executeProcess,
44
fileExists,
5+
interpolate,
56
stringifyError,
67
toArray,
78
} from '@code-pushup/utils';
@@ -32,7 +33,7 @@ export const nxHandler: MonorepoToolHandler = {
3233
'nx',
3334
'show',
3435
'projects',
35-
...toArray(nxProjectsFilter).map(arg => arg.replaceAll('{task}', task)),
36+
...toArray(nxProjectsFilter).map(arg => interpolate(arg, { task })),
3637
'--json',
3738
],
3839
cwd,

packages/utils/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export {
7676
isPromiseFulfilledResult,
7777
isPromiseRejectedResult,
7878
} from './lib/guards.js';
79+
export { interpolate } from './lib/interpolate.js';
7980
export { logMultipleResults } from './lib/log-results.js';
8081
export { link, ui, type CliUi, type Column } from './lib/logging.js';
8182
export { mergeConfigs } from './lib/merge-configs.js';
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function interpolate(
2+
text: string,
3+
variables: Record<string, string>,
4+
): string {
5+
return Object.entries(variables).reduce(
6+
(acc, [key, value]) => acc.replaceAll(`{${key}}`, value),
7+
text,
8+
);
9+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { interpolate } from './interpolate.js';
2+
3+
describe('interpolate', () => {
4+
it('should replace variable in string', () => {
5+
expect(
6+
interpolate('.code-pushup/{projectName}', { projectName: 'utils' }),
7+
).toBe('.code-pushup/utils');
8+
});
9+
10+
it('should replace multiple variables', () => {
11+
expect(
12+
interpolate('{workspaceRoot}/coverage/{projectRoot}/lcov.info', {
13+
workspaceRoot: '/home/matej/Projects/code-pushup-cli',
14+
projectRoot: 'packages/ci',
15+
}),
16+
).toBe(
17+
'/home/matej/Projects/code-pushup-cli/coverage/packages/ci/lcov.info',
18+
);
19+
});
20+
21+
it('should replace same variable multiple times', () => {
22+
expect(
23+
interpolate('.code-pushup/{projectName}/{projectName}-report.json', {
24+
projectName: 'utils',
25+
}),
26+
).toBe('.code-pushup/utils/utils-report.json');
27+
});
28+
29+
it('should not replace missing variables', () => {
30+
expect(interpolate('{projectRoot}/.code-pushup', {})).toBe(
31+
'{projectRoot}/.code-pushup',
32+
);
33+
});
34+
35+
it('should support empty string interpolation', () => {
36+
expect(interpolate('{prefix}report.json', { prefix: '' })).toBe(
37+
'report.json',
38+
);
39+
});
40+
41+
it('should support strings with only variable', () => {
42+
expect(interpolate('{projectName}', { projectName: 'utils' })).toBe(
43+
'utils',
44+
);
45+
});
46+
47+
it('should leave strings without variables unchanged', () => {
48+
expect(interpolate('.code-pushup', { projectName: 'utils' })).toBe(
49+
'.code-pushup',
50+
);
51+
});
52+
});

packages/utils/src/lib/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
export type ExcludeNullableProps<T> = {
1+
export type ExcludeNullableProps<T> = Prettify<{
22
[P in keyof T]: NonNullable<T[P]>;
3-
};
3+
}>;
44

55
export type ItemOrArray<T> = T | T[];
66

@@ -14,7 +14,7 @@ export type WithRequired<T, K extends keyof T> = Prettify<
1414
Omit<T, K> & Required<Pick<T, K>>
1515
>;
1616

17-
export type Prettify<T> = { [K in keyof T]: T[K] };
17+
export type Prettify<T> = { [K in keyof T]: T[K] } & {};
1818

1919
export type CamelCaseToKebabCase<T extends string> =
2020
T extends `${infer First}${infer Rest}`

0 commit comments

Comments
 (0)