@code-pushup/utils 0.46.0 → 0.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -63,7 +63,7 @@ function missingRefsForCategoriesErrorMsg(categories, plugins) {
63
63
  }
64
64
 
65
65
  // packages/models/src/lib/implementation/schemas.ts
66
- var primitiveValueSchema = z.union([z.string(), z.number()]);
66
+ var tableCellValueSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]).default(null);
67
67
  function executionMetaSchema(options = {
68
68
  descriptionDate: "Execution start date and time",
69
69
  descriptionDuration: "Execution duration in ms"
@@ -227,10 +227,10 @@ var tableColumnObjectSchema = z4.object({
227
227
  label: z4.string().optional(),
228
228
  align: tableAlignmentSchema.optional()
229
229
  });
230
- var tableRowObjectSchema = z4.record(primitiveValueSchema, {
230
+ var tableRowObjectSchema = z4.record(tableCellValueSchema, {
231
231
  description: "Object row"
232
232
  });
233
- var tableRowPrimitiveSchema = z4.array(primitiveValueSchema, {
233
+ var tableRowPrimitiveSchema = z4.array(tableCellValueSchema, {
234
234
  description: "Primitive row"
235
235
  });
236
236
  var tableSharedSchema = z4.object({
@@ -777,9 +777,9 @@ function formatBytes(bytes, decimals = 2) {
777
777
  function pluralizeToken(token, times) {
778
778
  return `${times} ${Math.abs(times) === 1 ? token : pluralize(token)}`;
779
779
  }
780
- function formatDuration(duration) {
780
+ function formatDuration(duration, granularity = 0) {
781
781
  if (duration < 1e3) {
782
- return `${duration} ms`;
782
+ return `${granularity ? duration.toFixed(granularity) : duration} ms`;
783
783
  }
784
784
  return `${(duration / 1e3).toFixed(2)} s`;
785
785
  }
@@ -795,12 +795,25 @@ function formatDate(date) {
795
795
  timeZoneName: "short"
796
796
  }).replace(/\u202F/g, " ");
797
797
  }
798
- function truncateText(text, maxChars) {
798
+ function truncateText(text, options) {
799
+ const {
800
+ maxChars,
801
+ position = "end",
802
+ ellipsis = "..."
803
+ } = typeof options === "number" ? { maxChars: options } : options;
799
804
  if (text.length <= maxChars) {
800
805
  return text;
801
806
  }
802
- const ellipsis = "...";
803
- return text.slice(0, maxChars - ellipsis.length) + ellipsis;
807
+ const maxLength = maxChars - ellipsis.length;
808
+ switch (position) {
809
+ case "start":
810
+ return ellipsis + text.slice(-maxLength).trim();
811
+ case "middle":
812
+ const halfMaxChars = Math.floor(maxLength / 2);
813
+ return text.slice(0, halfMaxChars).trim() + ellipsis + text.slice(-halfMaxChars).trim();
814
+ case "end":
815
+ return text.slice(0, maxLength).trim() + ellipsis;
816
+ }
804
817
  }
805
818
  function truncateTitle(text) {
806
819
  return truncateText(text, MAX_TITLE_LENGTH);
@@ -986,20 +999,12 @@ function logMultipleFileResults(fileResults, messagePrefix) {
986
999
  failedTransform
987
1000
  );
988
1001
  }
989
- var NoExportError = class extends Error {
990
- constructor(filepath) {
991
- super(`No default export found in ${filepath}`);
1002
+ async function importModule(options) {
1003
+ const { mod } = await bundleRequire(options);
1004
+ if (typeof mod === "object" && "default" in mod) {
1005
+ return mod.default;
992
1006
  }
993
- };
994
- async function importEsmModule(options) {
995
- const { mod } = await bundleRequire({
996
- format: "esm",
997
- ...options
998
- });
999
- if (!("default" in mod)) {
1000
- throw new NoExportError(options.filepath);
1001
- }
1002
- return mod.default;
1007
+ return mod;
1003
1008
  }
1004
1009
  function pluginWorkDir(slug) {
1005
1010
  return join("node_modules", ".code-pushup", slug);
@@ -1064,7 +1069,7 @@ function code(text) {
1064
1069
 
1065
1070
  // packages/utils/src/lib/text-formats/html/link.ts
1066
1071
  function link2(href, text) {
1067
- return `<a href="${href}">${text || href}"</a>`;
1072
+ return `<a href="${href}">${text || href}</a>`;
1068
1073
  }
1069
1074
 
1070
1075
  // packages/utils/src/lib/transform.ts
@@ -1171,7 +1176,7 @@ function toOrdinal(value) {
1171
1176
  return `${value}th`;
1172
1177
  }
1173
1178
 
1174
- // packages/utils/src/lib/table.ts
1179
+ // packages/utils/src/lib/text-formats/table.ts
1175
1180
  function rowToStringArray({ rows, columns = [] }) {
1176
1181
  if (Array.isArray(rows.at(0)) && typeof columns.at(0) === "object") {
1177
1182
  throw new TypeError(
@@ -1184,14 +1189,19 @@ function rowToStringArray({ rows, columns = [] }) {
1184
1189
  }
1185
1190
  const objectRow = row;
1186
1191
  if (columns.length === 0 || typeof columns.at(0) === "string") {
1187
- return Object.values(objectRow).map(String);
1192
+ return Object.values(objectRow).map(
1193
+ (value) => value == null ? "" : String(value)
1194
+ );
1188
1195
  }
1189
1196
  return columns.map(
1190
- ({ key }) => String(objectRow[key])
1197
+ ({ key }) => objectRow[key] == null ? "" : String(objectRow[key])
1191
1198
  );
1192
1199
  });
1193
1200
  }
1194
- function columnsToStringArray({ rows, columns = [] }) {
1201
+ function columnsToStringArray({
1202
+ rows,
1203
+ columns = []
1204
+ }) {
1195
1205
  const firstRow = rows.at(0);
1196
1206
  const primitiveRows = Array.isArray(firstRow);
1197
1207
  if (typeof columns.at(0) === "string" && !primitiveRows) {
@@ -1231,10 +1241,8 @@ function getColumnAlignmentForIndex(targetIdx, columns = []) {
1231
1241
  return "center";
1232
1242
  }
1233
1243
  }
1234
- function getColumnAlignments({
1235
- rows,
1236
- columns = []
1237
- }) {
1244
+ function getColumnAlignments(tableData) {
1245
+ const { rows, columns = [] } = tableData;
1238
1246
  if (rows.at(0) == null) {
1239
1247
  throw new Error("first row can`t be undefined.");
1240
1248
  }
@@ -1244,10 +1252,17 @@ function getColumnAlignments({
1244
1252
  (_, idx) => getColumnAlignmentForIndex(idx, columns)
1245
1253
  );
1246
1254
  }
1247
- const firstObject = rows.at(0);
1248
- return Object.keys(firstObject).map(
1249
- (key, idx) => getColumnAlignmentForKeyAndIndex(key, idx, columns)
1250
- );
1255
+ const biggestRow = [...rows].sort((a, b) => Object.keys(a).length - Object.keys(b).length).at(-1);
1256
+ if (columns.length > 0) {
1257
+ return columns.map(
1258
+ (column, idx) => typeof column === "string" ? column : getColumnAlignmentForKeyAndIndex(
1259
+ column.key,
1260
+ idx,
1261
+ columns
1262
+ )
1263
+ );
1264
+ }
1265
+ return Object.keys(biggestRow ?? {}).map((_) => "center");
1251
1266
  }
1252
1267
 
1253
1268
  // packages/utils/src/lib/text-formats/html/table.ts
@@ -1344,7 +1359,10 @@ function section(...contents) {
1344
1359
  return `${lines(...contents)}${NEW_LINE}`;
1345
1360
  }
1346
1361
  function lines(...contents) {
1347
- return `${contents.filter(Boolean).join(NEW_LINE)}`;
1362
+ const filteredContent = contents.filter(
1363
+ (value) => value != null && value !== "" && value !== false
1364
+ );
1365
+ return `${filteredContent.join(NEW_LINE)}`;
1348
1366
  }
1349
1367
 
1350
1368
  // packages/utils/src/lib/text-formats/md/table.ts
@@ -1858,6 +1876,113 @@ function groupByStatus(results) {
1858
1876
  );
1859
1877
  }
1860
1878
 
1879
+ // packages/utils/src/lib/merge-configs.ts
1880
+ function mergeConfigs(config, ...configs) {
1881
+ return configs.reduce(
1882
+ (acc, obj) => ({
1883
+ ...acc,
1884
+ ...mergeCategories(acc.categories, obj.categories),
1885
+ ...mergePlugins(acc.plugins, obj.plugins),
1886
+ ...mergePersist(acc.persist, obj.persist),
1887
+ ...mergeUpload(acc.upload, obj.upload)
1888
+ }),
1889
+ config
1890
+ );
1891
+ }
1892
+ function mergeCategories(a, b) {
1893
+ if (!a && !b) {
1894
+ return {};
1895
+ }
1896
+ const mergedMap = /* @__PURE__ */ new Map();
1897
+ const addToMap = (categories) => {
1898
+ categories.forEach((newObject) => {
1899
+ if (mergedMap.has(newObject.slug)) {
1900
+ const existingObject = mergedMap.get(
1901
+ newObject.slug
1902
+ );
1903
+ mergedMap.set(newObject.slug, {
1904
+ ...existingObject,
1905
+ ...newObject,
1906
+ refs: mergeByUniqueCategoryRefCombination(
1907
+ existingObject?.refs,
1908
+ newObject.refs
1909
+ )
1910
+ });
1911
+ } else {
1912
+ mergedMap.set(newObject.slug, newObject);
1913
+ }
1914
+ });
1915
+ };
1916
+ if (a) {
1917
+ addToMap(a);
1918
+ }
1919
+ if (b) {
1920
+ addToMap(b);
1921
+ }
1922
+ return { categories: [...mergedMap.values()] };
1923
+ }
1924
+ function mergePlugins(a, b) {
1925
+ if (!a && !b) {
1926
+ return { plugins: [] };
1927
+ }
1928
+ const mergedMap = /* @__PURE__ */ new Map();
1929
+ const addToMap = (plugins) => {
1930
+ plugins.forEach((newObject) => {
1931
+ mergedMap.set(newObject.slug, newObject);
1932
+ });
1933
+ };
1934
+ if (a) {
1935
+ addToMap(a);
1936
+ }
1937
+ if (b) {
1938
+ addToMap(b);
1939
+ }
1940
+ return { plugins: [...mergedMap.values()] };
1941
+ }
1942
+ function mergePersist(a, b) {
1943
+ if (!a && !b) {
1944
+ return {};
1945
+ }
1946
+ if (a) {
1947
+ return b ? { persist: { ...a, ...b } } : {};
1948
+ } else {
1949
+ return { persist: b };
1950
+ }
1951
+ }
1952
+ function mergeByUniqueCategoryRefCombination(a, b) {
1953
+ const map = /* @__PURE__ */ new Map();
1954
+ const addToMap = (refs) => {
1955
+ refs.forEach((ref) => {
1956
+ const uniqueIdentification = `${ref.type}:${ref.plugin}:${ref.slug}`;
1957
+ if (map.has(uniqueIdentification)) {
1958
+ map.set(uniqueIdentification, {
1959
+ ...map.get(uniqueIdentification),
1960
+ ...ref
1961
+ });
1962
+ } else {
1963
+ map.set(uniqueIdentification, ref);
1964
+ }
1965
+ });
1966
+ };
1967
+ if (a) {
1968
+ addToMap(a);
1969
+ }
1970
+ if (b) {
1971
+ addToMap(b);
1972
+ }
1973
+ return [...map.values()];
1974
+ }
1975
+ function mergeUpload(a, b) {
1976
+ if (!a && !b) {
1977
+ return {};
1978
+ }
1979
+ if (a) {
1980
+ return b ? { upload: { ...a, ...b } } : {};
1981
+ } else {
1982
+ return { upload: b };
1983
+ }
1984
+ }
1985
+
1861
1986
  // packages/utils/src/lib/progress.ts
1862
1987
  import chalk3 from "chalk";
1863
1988
  import { MultiProgressBars } from "multi-progress-bars";
@@ -2263,8 +2388,8 @@ function formatDiffCategoriesSection(diff) {
2263
2388
  }
2264
2389
  const columns = [
2265
2390
  { key: "category", label: "\u{1F3F7}\uFE0F Category", align: "left" },
2266
- { key: "after", label: hasChanges ? "\u2B50 Current score" : "\u2B50 Score" },
2267
- { key: "before", label: "\u2B50 Previous score" },
2391
+ { key: "before", label: hasChanges ? "\u2B50 Previous score" : "\u2B50 Score" },
2392
+ { key: "after", label: "\u2B50 Current score" },
2268
2393
  { key: "change", label: "\u{1F504} Score change" }
2269
2394
  ];
2270
2395
  return lines5(
@@ -2293,7 +2418,7 @@ function formatDiffCategoriesSection(diff) {
2293
2418
  change: "\u2013"
2294
2419
  }))
2295
2420
  ].map(
2296
- (row) => hasChanges ? row : { category: row.category, after: row.after }
2421
+ (row) => hasChanges ? row : { category: row.category, before: row.before }
2297
2422
  )
2298
2423
  }),
2299
2424
  added.length > 0 && section5(italicMd("(\\*) New category."))
@@ -2309,8 +2434,8 @@ function formatDiffGroupsSection(diff) {
2309
2434
  columns: [
2310
2435
  { key: "plugin", label: "\u{1F50C} Plugin", align: "left" },
2311
2436
  { key: "group", label: "\u{1F5C3}\uFE0F Group", align: "left" },
2312
- { key: "after", label: "\u2B50 Current score" },
2313
2437
  { key: "before", label: "\u2B50 Previous score" },
2438
+ { key: "after", label: "\u2B50 Current score" },
2314
2439
  { key: "change", label: "\u{1F504} Score change" }
2315
2440
  ],
2316
2441
  rows: sortChanges(diff.groups.changed).map((group) => ({
@@ -2330,8 +2455,8 @@ function formatDiffAuditsSection(diff) {
2330
2455
  columns: [
2331
2456
  { key: "plugin", label: "\u{1F50C} Plugin", align: "left" },
2332
2457
  { key: "audit", label: "\u{1F6E1}\uFE0F Audit", align: "left" },
2333
- { key: "after", label: "\u{1F4CF} Current value" },
2334
2458
  { key: "before", label: "\u{1F4CF} Previous value" },
2459
+ { key: "after", label: "\u{1F4CF} Current value" },
2335
2460
  { key: "change", label: "\u{1F504} Value change" }
2336
2461
  ],
2337
2462
  rows: sortChanges(diff.audits.changed).map((audit) => ({
@@ -2741,7 +2866,7 @@ export {
2741
2866
  groupByStatus,
2742
2867
  guardAgainstLocalChanges,
2743
2868
  html,
2744
- importEsmModule,
2869
+ importModule,
2745
2870
  isPromiseFulfilledResult,
2746
2871
  isPromiseRejectedResult,
2747
2872
  isSemver,
@@ -2754,6 +2879,7 @@ export {
2754
2879
  logStdoutSummary,
2755
2880
  matchArrayItemsByKey,
2756
2881
  md,
2882
+ mergeConfigs,
2757
2883
  normalizeSemver,
2758
2884
  objectFromEntries,
2759
2885
  objectToCliArgs,
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@code-pushup/utils",
3
- "version": "0.46.0",
3
+ "version": "0.48.0",
4
4
  "dependencies": {
5
- "@code-pushup/models": "0.46.0",
5
+ "@code-pushup/models": "0.48.0",
6
6
  "bundle-require": "^4.0.1",
7
7
  "esbuild": "^0.19.2",
8
8
  "chalk": "^5.3.0",
package/src/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { exists } from '@code-pushup/models';
2
2
  export { Diff, comparePairs, matchArrayItemsByKey } from './lib/diff';
3
3
  export { ProcessConfig, ProcessError, ProcessObserver, ProcessResult, executeProcess, } from './lib/execute-process';
4
- export { CrawlFileSystemOptions, FileResult, MultipleFileResults, crawlFileSystem, directoryExists, ensureDirectoryExists, fileExists, filePathToCliArg, findLineNumberInText, importEsmModule, logMultipleFileResults, pluginWorkDir, readJsonFile, readTextFile, removeDirectoryIfExists, } from './lib/file-system';
4
+ export { CrawlFileSystemOptions, FileResult, MultipleFileResults, crawlFileSystem, directoryExists, ensureDirectoryExists, fileExists, filePathToCliArg, findLineNumberInText, importModule, logMultipleFileResults, pluginWorkDir, readJsonFile, readTextFile, removeDirectoryIfExists, } from './lib/file-system';
5
5
  export { filterItemRefsBy } from './lib/filter';
6
6
  export { formatBytes, formatDuration, pluralize, pluralizeToken, slugify, truncateDescription, truncateIssueMessage, truncateText, truncateTitle, } from './lib/formatting';
7
7
  export { formatGitPath, getGitRoot, guardAgainstLocalChanges, safeCheckout, toGitPath, } from './lib/git/git';
@@ -10,6 +10,7 @@ export { groupByStatus } from './lib/group-by-status';
10
10
  export { isPromiseFulfilledResult, isPromiseRejectedResult, } from './lib/guards';
11
11
  export { logMultipleResults } from './lib/log-results';
12
12
  export { CliUi, Column, link, ui } from './lib/logging';
13
+ export { mergeConfigs } from './lib/merge-configs';
13
14
  export { ProgressBar, getProgressBar } from './lib/progress';
14
15
  export { CODE_PUSHUP_DOMAIN, FOOTER_PREFIX, README_LINK, TERMINAL_WIDTH, } from './lib/reports/constants';
15
16
  export { listAuditsFromAllPlugins, listGroupsFromAllPlugins, } from './lib/reports/flatten-plugins';
@@ -8,10 +8,7 @@ export declare function removeDirectoryIfExists(dir: string): Promise<void>;
8
8
  export type FileResult = readonly [string] | readonly [string, number];
9
9
  export type MultipleFileResults = PromiseSettledResult<FileResult>[];
10
10
  export declare function logMultipleFileResults(fileResults: MultipleFileResults, messagePrefix: string): void;
11
- export declare class NoExportError extends Error {
12
- constructor(filepath: string);
13
- }
14
- export declare function importEsmModule<T = unknown>(options: Options): Promise<T>;
11
+ export declare function importModule<T = unknown>(options: Options): Promise<T>;
15
12
  export declare function pluginWorkDir(slug: string): string;
16
13
  export type CrawlFileSystemOptions<T> = {
17
14
  directory: string;
@@ -2,9 +2,13 @@ export declare function slugify(text: string): string;
2
2
  export declare function pluralize(text: string, amount?: number): string;
3
3
  export declare function formatBytes(bytes: number, decimals?: number): string;
4
4
  export declare function pluralizeToken(token: string, times: number): string;
5
- export declare function formatDuration(duration: number): string;
5
+ export declare function formatDuration(duration: number, granularity?: number): string;
6
6
  export declare function formatDate(date: Date): string;
7
- export declare function truncateText(text: string, maxChars: number): string;
7
+ export declare function truncateText(text: string, options: number | {
8
+ maxChars: number;
9
+ position?: 'start' | 'middle' | 'end';
10
+ ellipsis?: string;
11
+ }): string;
8
12
  export declare function truncateTitle(text: string): string;
9
13
  export declare function truncateDescription(text: string): string;
10
14
  export declare function truncateIssueMessage(text: string): string;
@@ -0,0 +1,2 @@
1
+ import { CoreConfig } from '@code-pushup/models';
2
+ export declare function mergeConfigs(config: CoreConfig, ...configs: Partial<CoreConfig>[]): Partial<CoreConfig>;
@@ -1,2 +1,2 @@
1
1
  export declare function section(...contents: (string | undefined | boolean)[]): string;
2
- export declare function lines(...contents: (string | undefined | boolean)[]): string;
2
+ export declare function lines(...contents: (string | undefined | boolean | number)[]): string;
@@ -1,6 +1,6 @@
1
1
  import { Table, TableAlignment, TableColumnObject, TableColumnPrimitive } from '@code-pushup/models';
2
2
  export declare function rowToStringArray({ rows, columns }: Table): string[][];
3
- export declare function columnsToStringArray({ rows, columns }: Table): string[];
3
+ export declare function columnsToStringArray({ rows, columns, }: Pick<Table, 'columns' | 'rows'>): string[];
4
4
  export declare function getColumnAlignmentForKeyAndIndex(targetKey: string, targetIdx: number, columns?: TableColumnObject[]): TableAlignment;
5
5
  export declare function getColumnAlignmentForIndex(targetIdx: number, columns?: TableColumnPrimitive[]): TableAlignment;
6
- export declare function getColumnAlignments({ rows, columns, }: Table): TableAlignment[];
6
+ export declare function getColumnAlignments(tableData: Table): TableAlignment[];
@@ -1,6 +0,0 @@
1
- import { Table, TableAlignment, TableColumnObject, TableColumnPrimitive } from '@code-pushup/models';
2
- export declare function rowToStringArray({ rows, columns }: Table): string[][];
3
- export declare function columnsToStringArray({ rows, columns }: Table): string[];
4
- export declare function getColumnAlignmentForKeyAndIndex(targetKey: string, targetIdx: number, columns?: TableColumnObject[]): TableAlignment;
5
- export declare function getColumnAlignmentForIndex(targetIdx: number, columns?: TableColumnPrimitive[]): TableAlignment;
6
- export declare function getColumnAlignments({ rows, columns, }: Table): TableAlignment[];