Skip to content

Commit dacaaf7

Browse files
feat(utils): add package utils for cli independent logic (#39)
Closes #32 This PR focuses on the CLI independent logic for the collect command. It includes: - source code + JsDocs - unit tests - test helper - mock data - adoptions for libs (make them buildable) --------- Co-authored-by: Matěj Chalk <[email protected]>
1 parent adec416 commit dacaaf7

38 files changed

+1520
-394
lines changed

package-lock.json

+314-110
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cli/.eslintrc.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"@nx/dependency-checks": [
2222
"error",
2323
{
24-
"ignoredDependencies": ["vite", "@nx/vite"]
24+
"ignoredDependencies": ["vite", "vitest", "@nx/vite"]
2525
}
2626
]
2727
}

packages/models/.eslintrc.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@
1818
"files": ["*.json"],
1919
"parser": "jsonc-eslint-parser",
2020
"rules": {
21-
"@nx/dependency-checks": ["error"]
21+
"@nx/dependency-checks": [
22+
"error",
23+
{
24+
"ignoredDependencies": ["vite", "vitest", "@nx/vite"]
25+
}
26+
]
2227
}
2328
}
2429
]

packages/models/package.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "@quality-metrics/models",
3+
"version": "0.0.1",
4+
"dependencies": {
5+
"zod": "^3.22.1"
6+
}
7+
}

packages/models/project.json

+21-3
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,37 @@
44
"sourceRoot": "packages/models/src",
55
"projectType": "library",
66
"targets": {
7+
"build": {
8+
"executor": "@nx/esbuild:esbuild",
9+
"outputs": ["{options.outputPath}"],
10+
"options": {
11+
"outputPath": "dist/packages/models",
12+
"main": "packages/models/src/index.ts",
13+
"tsConfig": "packages/models/tsconfig.lib.json",
14+
"assets": ["packages/models/*.md"],
15+
"esbuildConfig": "esbuild.config.js"
16+
}
17+
},
18+
"publish": {
19+
"command": "node tools/scripts/publish.mjs models {args.ver} {args.tag}",
20+
"dependsOn": ["build"]
21+
},
722
"lint": {
823
"executor": "@nx/linter:eslint",
924
"outputs": ["{options.outputFile}"],
1025
"options": {
11-
"lintFilePatterns": ["packages/models/**/*.ts"]
26+
"lintFilePatterns": [
27+
"packages/models/**/*.ts",
28+
"packages/models/package.json"
29+
]
1230
}
1331
},
1432
"test": {
1533
"executor": "@nx/vite:test",
16-
"outputs": ["{workspaceRoot}/coverage/packages/cli"],
34+
"outputs": ["{workspaceRoot}/coverage/packages/models"],
1735
"options": {
1836
"passWithNoTests": true,
19-
"reportsDirectory": "../../coverage/packages/cli"
37+
"reportsDirectory": "../../coverage/packages/models"
2038
}
2139
}
2240
},

packages/models/src/index.ts

+18-15
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
export { coreConfigSchema, CoreConfigSchema } from './lib/core-config';
2-
export { uploadConfigSchema, UploadConfigSchema } from './lib/upload-config';
3-
export { pluginConfigSchema, PluginConfigSchema } from './lib/plugin-config';
41
export {
5-
runnerOutputSchema,
6-
RunnerOutputSchema,
7-
runnerOutputAuditRefsPresentInPluginConfigs,
8-
PluginsOutputSchema,
9-
} from './lib/output';
10-
export { persistConfigSchema, PersistConfigSchema } from './lib/persist-config';
2+
unrefinedCoreConfigSchema,
3+
refineCoreConfig,
4+
coreConfigSchema,
5+
CoreConfig,
6+
} from './lib/core-config';
7+
export { uploadConfigSchema, UploadConfig } from './lib/upload-config';
118
export {
12-
categoryConfigSchema,
13-
CategoryConfigSchema,
14-
} from './lib/category-config';
9+
pluginConfigSchema,
10+
PluginConfig,
11+
RunnerOutput,
12+
runnerOutputSchema,
13+
} from './lib/plugin-config';
1514
export {
16-
globalCliArgsSchema,
17-
GlobalCliArgsSchema,
18-
} from './lib/global-cli-options';
15+
runnerOutputAuditRefsPresentInPluginConfigs,
16+
reportSchema,
17+
Report,
18+
} from './lib/report';
19+
export { persistConfigSchema, PersistConfig } from './lib/persist-config';
20+
export { categoryConfigSchema, CategoryConfig } from './lib/category-config';
21+
export { globalCliArgsSchema, GlobalCliArgs } from './lib/global-cli-options';

packages/models/src/lib/category-config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export const categoryConfigSchema = z.object(
5858
},
5959
);
6060

61-
export type CategoryConfigSchema = z.infer<typeof categoryConfigSchema>;
61+
export type CategoryConfig = z.infer<typeof categoryConfigSchema>;
6262

6363
// helper for validator: categories have unique refs to audits or groups
6464
export function duplicateRefsInCategoryMetricsErrorMsg(metrics) {

packages/models/src/lib/core-config.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ describe('CoreConfig', () => {
7373
const missingSlug = 'missing-plugin-slug-in-category#groups:auditref';
7474
cfg.categories.push(
7575
mockCategory({
76-
categorySlug: 'test',
76+
categorySlug: 'test-slug',
7777
auditRefOrGroupRef: [`${missingSlug}`],
7878
}),
7979
);

packages/models/src/lib/core-config.ts

+42-31
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { z } from 'zod';
1+
import { Schema, z } from 'zod';
22
import { pluginConfigSchema } from './plugin-config';
33
import { categoryConfigSchema } from './category-config';
44
import { uploadConfigSchema } from './upload-config';
@@ -28,37 +28,49 @@ import {
2828
* console.error('Invalid plugin config:', validationResult.error);
2929
* }
3030
*/
31-
export const coreConfigSchema = z
32-
.object({
33-
plugins: z.array(pluginConfigSchema, {
34-
description:
35-
'List of plugins to be used (official, community-provided, or custom)',
36-
}),
37-
/** portal configuration for persisting results */
38-
persist: persistConfigSchema,
39-
/** portal configuration for uploading results */
40-
upload: uploadConfigSchema.optional(),
41-
categories: z
42-
.array(categoryConfigSchema, {
43-
description: 'Categorization of individual audits',
44-
})
45-
// categories slugs are unique
31+
export const unrefinedCoreConfigSchema = z.object({
32+
plugins: z.array(pluginConfigSchema, {
33+
description:
34+
'List of plugins to be used (official, community-provided, or custom)',
35+
}),
36+
/** portal configuration for persisting results */
37+
persist: persistConfigSchema,
38+
/** portal configuration for uploading results */
39+
upload: uploadConfigSchema.optional(),
40+
categories: z
41+
.array(categoryConfigSchema, {
42+
description: 'Categorization of individual audits',
43+
})
44+
// categories slugs are unique
45+
.refine(
46+
categoryCfg => !getDuplicateSlugCategories(categoryCfg),
47+
categoryCfg => ({
48+
message: duplicateSlugCategoriesErrorMsg(categoryCfg),
49+
}),
50+
),
51+
});
52+
53+
export const coreConfigSchema = refineCoreConfig(unrefinedCoreConfigSchema);
54+
55+
/**
56+
* Add refinements to coreConfigSchema
57+
* workaround for zod issue: https://github.com/colinhacks/zod/issues/454
58+
*
59+
*/
60+
export function refineCoreConfig(schema: Schema): Schema {
61+
return (
62+
schema
63+
// categories point to existing audit or group refs
4664
.refine(
47-
categoryCfg => !getDuplicateSlugCategories(categoryCfg),
48-
categoryCfg => ({
49-
message: duplicateSlugCategoriesErrorMsg(categoryCfg),
65+
coreCfg => !getMissingRefsForCategories(coreCfg),
66+
coreCfg => ({
67+
message: missingRefsForCategoriesErrorMsg(coreCfg),
5068
}),
51-
),
52-
})
53-
// categories point to existing audit or group refs
54-
.refine(
55-
coreCfg => !getMissingRefsForCategories(coreCfg),
56-
coreCfg => ({
57-
message: missingRefsForCategoriesErrorMsg(coreCfg),
58-
}),
69+
)
5970
);
71+
}
6072

61-
export type CoreConfigSchema = z.infer<typeof coreConfigSchema>;
73+
export type CoreConfig = z.infer<typeof coreConfigSchema>;
6274

6375
// helper for validator: categories point to existing audit or group refs
6476
function missingRefsForCategoriesErrorMsg(coreCfg) {
@@ -89,9 +101,8 @@ function getMissingRefsForCategories(coreCfg) {
89101
if (Array.isArray(missingAuditRefs) && missingAuditRefs.length > 0) {
90102
missingRefs.push(...missingAuditRefs);
91103
}
92-
const groupRefsFromCategory = coreCfg.categories.flatMap(
93-
({ metrics }) =>
94-
metrics.filter(({ ref }) => isGroupRef(ref)).map(({ ref }) => ref), // plg#group:perf
104+
const groupRefsFromCategory = coreCfg.categories.flatMap(({ metrics }) =>
105+
metrics.filter(({ ref }) => isGroupRef(ref)).map(({ ref }) => ref),
95106
);
96107
const groupRefsFromPlugins = coreCfg.plugins.flatMap(({ groups, meta }) => {
97108
return groups.map(({ slug }) => `${meta.slug}#group:${slug}`);

packages/models/src/lib/global-cli-options.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,4 @@ export const globalCliArgsSchema = z.object({
4040
.default('code-pushup.config.js'),
4141
});
4242

43-
export type GlobalCliArgsSchema = z.infer<typeof globalCliArgsSchema>;
43+
export type GlobalCliArgs = z.infer<typeof globalCliArgsSchema>;

packages/models/src/lib/implementation/helpers.mock.ts

+15-20
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
1-
import { PluginConfigSchema } from '../plugin-config';
2-
import { RunnerOutputSchema } from '../output';
3-
import { CoreConfigSchema } from '../core-config';
4-
import { CategoryConfigSchema } from '../category-config';
5-
import { UploadConfigSchema } from '../upload-config';
6-
import { PersistConfigSchema } from '../persist-config';
1+
import { PluginConfig, RunnerOutput } from '../plugin-config';
2+
import { CoreConfig } from '../core-config';
3+
import { CategoryConfig } from '../category-config';
4+
import { UploadConfig } from '../upload-config';
5+
import { PersistConfig } from '../persist-config';
76

87
export function mockConfig(opt?: {
98
pluginSlug?: string | string[];
109
auditSlug?: string | string[];
1110
groupSlug?: string | string[];
12-
}): CoreConfigSchema {
11+
}): CoreConfig {
1312
let { pluginSlug, auditSlug, groupSlug } = opt || {};
1413
pluginSlug = pluginSlug || 'mock-plugin-slug';
1514
auditSlug = auditSlug || 'mock-audit-slug';
@@ -27,7 +26,7 @@ export function mockPluginConfig(opt?: {
2726
pluginSlug?: string;
2827
auditSlug?: string | string[];
2928
groupSlug?: string | string[];
30-
}): PluginConfigSchema {
29+
}): PluginConfig {
3130
const { groupSlug } = opt || {};
3231
let { pluginSlug, auditSlug } = opt || {};
3332
pluginSlug = pluginSlug || 'mock-plugin-slug';
@@ -58,7 +57,7 @@ export function mockPluginConfig(opt?: {
5857
slug: `${pluginSlug}#${slug}`,
5958
value: parseFloat('0.' + idx),
6059
})),
61-
} satisfies RunnerOutputSchema)}' > ${outputPath}`,
60+
} satisfies RunnerOutput)}' > ${outputPath}`,
6261
],
6362
outputPath: outputPath,
6463
},
@@ -71,7 +70,7 @@ export function mockPluginConfig(opt?: {
7170

7271
export function mockAuditConfig(opt?: {
7372
auditSlug?: string;
74-
}): PluginConfigSchema['audits'][0] {
73+
}): PluginConfig['audits'][0] {
7574
let { auditSlug } = opt || {};
7675
auditSlug = auditSlug || 'mock-audit-slug';
7776

@@ -85,7 +84,7 @@ export function mockAuditConfig(opt?: {
8584
}
8685
export function mockMetric(opt?: {
8786
auditRef?: string;
88-
}): CategoryConfigSchema['metrics'][0] {
87+
}): CategoryConfig['metrics'][0] {
8988
const { auditRef } = opt || {};
9089
const ref = auditRef || 'mock-plugin-slug#mock-audit-slug';
9190

@@ -98,7 +97,7 @@ export function mockMetric(opt?: {
9897
export function mockGroupConfig(opt?: {
9998
groupSlug?: string;
10099
auditSlug?: string | string[];
101-
}): PluginConfigSchema['groups'][0] {
100+
}): PluginConfig['groups'][0] {
102101
let { groupSlug, auditSlug } = opt || {};
103102
groupSlug = groupSlug || 'mock-group-slug';
104103
auditSlug = auditSlug || 'mock-audit-slug';
@@ -116,7 +115,7 @@ export function mockGroupConfig(opt?: {
116115
export function mockCategory(opt?: {
117116
categorySlug?: string;
118117
auditRefOrGroupRef?: string | string[];
119-
}): CategoryConfigSchema {
118+
}): CategoryConfig {
120119
let { auditRefOrGroupRef, categorySlug } = opt || {};
121120
categorySlug = categorySlug || 'mock-category-slug';
122121
auditRefOrGroupRef = auditRefOrGroupRef || 'mock-plugin-slug#mock-audit-slug';
@@ -133,18 +132,14 @@ export function mockCategory(opt?: {
133132
};
134133
}
135134

136-
export function mockUploadConfig(
137-
opt?: Partial<UploadConfigSchema>,
138-
): UploadConfigSchema {
135+
export function mockUploadConfig(opt?: Partial<UploadConfig>): UploadConfig {
139136
return {
140137
apiKey: 'm0ck-API-k3y',
141138
server: 'http://test.server.io',
142139
...opt,
143140
};
144141
}
145-
export function mockPersistConfig(
146-
opt?: Partial<PersistConfigSchema>,
147-
): PersistConfigSchema {
142+
export function mockPersistConfig(opt?: Partial<PersistConfig>): PersistConfig {
148143
return {
149144
outputPath: 'mock-output-path.json',
150145
...opt,
@@ -153,7 +148,7 @@ export function mockPersistConfig(
153148

154149
export function mockRunnerOutput(opt?: {
155150
auditSlug: string | string[];
156-
}): RunnerOutputSchema {
151+
}): RunnerOutput {
157152
let { auditSlug } = opt || {};
158153
auditSlug = auditSlug || 'mock-audit-output-slug';
159154
const audits = Array.isArray(auditSlug)

packages/models/src/lib/output.spec.ts

-53
This file was deleted.

0 commit comments

Comments
 (0)