Skip to content

Commit

Permalink
feat(utils): add package utils for cli independent logic (#39)
Browse files Browse the repository at this point in the history
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 <34691111+matejchalk@users.noreply.github.com>
  • Loading branch information
BioPhoton and matejchalk committed Sep 7, 2023
1 parent adec416 commit dacaaf7
Show file tree
Hide file tree
Showing 38 changed files with 1,520 additions and 394 deletions.
424 changes: 314 additions & 110 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/cli/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"@nx/dependency-checks": [
"error",
{
"ignoredDependencies": ["vite", "@nx/vite"]
"ignoredDependencies": ["vite", "vitest", "@nx/vite"]
}
]
}
Expand Down
7 changes: 6 additions & 1 deletion packages/models/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": ["error"]
"@nx/dependency-checks": [
"error",
{
"ignoredDependencies": ["vite", "vitest", "@nx/vite"]
}
]
}
}
]
Expand Down
7 changes: 7 additions & 0 deletions packages/models/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "@quality-metrics/models",
"version": "0.0.1",
"dependencies": {
"zod": "^3.22.1"
}
}
24 changes: 21 additions & 3 deletions packages/models/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,37 @@
"sourceRoot": "packages/models/src",
"projectType": "library",
"targets": {
"build": {
"executor": "@nx/esbuild:esbuild",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/packages/models",
"main": "packages/models/src/index.ts",
"tsConfig": "packages/models/tsconfig.lib.json",
"assets": ["packages/models/*.md"],
"esbuildConfig": "esbuild.config.js"
}
},
"publish": {
"command": "node tools/scripts/publish.mjs models {args.ver} {args.tag}",
"dependsOn": ["build"]
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["packages/models/**/*.ts"]
"lintFilePatterns": [
"packages/models/**/*.ts",
"packages/models/package.json"
]
}
},
"test": {
"executor": "@nx/vite:test",
"outputs": ["{workspaceRoot}/coverage/packages/cli"],
"outputs": ["{workspaceRoot}/coverage/packages/models"],
"options": {
"passWithNoTests": true,
"reportsDirectory": "../../coverage/packages/cli"
"reportsDirectory": "../../coverage/packages/models"
}
}
},
Expand Down
33 changes: 18 additions & 15 deletions packages/models/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
export { coreConfigSchema, CoreConfigSchema } from './lib/core-config';
export { uploadConfigSchema, UploadConfigSchema } from './lib/upload-config';
export { pluginConfigSchema, PluginConfigSchema } from './lib/plugin-config';
export {
runnerOutputSchema,
RunnerOutputSchema,
runnerOutputAuditRefsPresentInPluginConfigs,
PluginsOutputSchema,
} from './lib/output';
export { persistConfigSchema, PersistConfigSchema } from './lib/persist-config';
unrefinedCoreConfigSchema,
refineCoreConfig,
coreConfigSchema,
CoreConfig,
} from './lib/core-config';
export { uploadConfigSchema, UploadConfig } from './lib/upload-config';
export {
categoryConfigSchema,
CategoryConfigSchema,
} from './lib/category-config';
pluginConfigSchema,
PluginConfig,
RunnerOutput,
runnerOutputSchema,
} from './lib/plugin-config';
export {
globalCliArgsSchema,
GlobalCliArgsSchema,
} from './lib/global-cli-options';
runnerOutputAuditRefsPresentInPluginConfigs,
reportSchema,
Report,
} from './lib/report';
export { persistConfigSchema, PersistConfig } from './lib/persist-config';
export { categoryConfigSchema, CategoryConfig } from './lib/category-config';
export { globalCliArgsSchema, GlobalCliArgs } from './lib/global-cli-options';
2 changes: 1 addition & 1 deletion packages/models/src/lib/category-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const categoryConfigSchema = z.object(
},
);

export type CategoryConfigSchema = z.infer<typeof categoryConfigSchema>;
export type CategoryConfig = z.infer<typeof categoryConfigSchema>;

// helper for validator: categories have unique refs to audits or groups
export function duplicateRefsInCategoryMetricsErrorMsg(metrics) {
Expand Down
2 changes: 1 addition & 1 deletion packages/models/src/lib/core-config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe('CoreConfig', () => {
const missingSlug = 'missing-plugin-slug-in-category#groups:auditref';
cfg.categories.push(
mockCategory({
categorySlug: 'test',
categorySlug: 'test-slug',
auditRefOrGroupRef: [`${missingSlug}`],
}),
);
Expand Down
73 changes: 42 additions & 31 deletions packages/models/src/lib/core-config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { z } from 'zod';
import { Schema, z } from 'zod';
import { pluginConfigSchema } from './plugin-config';
import { categoryConfigSchema } from './category-config';
import { uploadConfigSchema } from './upload-config';
Expand Down Expand Up @@ -28,37 +28,49 @@ import {
* console.error('Invalid plugin config:', validationResult.error);
* }
*/
export const coreConfigSchema = z
.object({
plugins: z.array(pluginConfigSchema, {
description:
'List of plugins to be used (official, community-provided, or custom)',
}),
/** portal configuration for persisting results */
persist: persistConfigSchema,
/** portal configuration for uploading results */
upload: uploadConfigSchema.optional(),
categories: z
.array(categoryConfigSchema, {
description: 'Categorization of individual audits',
})
// categories slugs are unique
export const unrefinedCoreConfigSchema = z.object({
plugins: z.array(pluginConfigSchema, {
description:
'List of plugins to be used (official, community-provided, or custom)',
}),
/** portal configuration for persisting results */
persist: persistConfigSchema,
/** portal configuration for uploading results */
upload: uploadConfigSchema.optional(),
categories: z
.array(categoryConfigSchema, {
description: 'Categorization of individual audits',
})
// categories slugs are unique
.refine(
categoryCfg => !getDuplicateSlugCategories(categoryCfg),
categoryCfg => ({
message: duplicateSlugCategoriesErrorMsg(categoryCfg),
}),
),
});

export const coreConfigSchema = refineCoreConfig(unrefinedCoreConfigSchema);

/**
* Add refinements to coreConfigSchema
* workaround for zod issue: https://github.com/colinhacks/zod/issues/454
*
*/
export function refineCoreConfig(schema: Schema): Schema {
return (
schema
// categories point to existing audit or group refs
.refine(
categoryCfg => !getDuplicateSlugCategories(categoryCfg),
categoryCfg => ({
message: duplicateSlugCategoriesErrorMsg(categoryCfg),
coreCfg => !getMissingRefsForCategories(coreCfg),
coreCfg => ({
message: missingRefsForCategoriesErrorMsg(coreCfg),
}),
),
})
// categories point to existing audit or group refs
.refine(
coreCfg => !getMissingRefsForCategories(coreCfg),
coreCfg => ({
message: missingRefsForCategoriesErrorMsg(coreCfg),
}),
)
);
}

export type CoreConfigSchema = z.infer<typeof coreConfigSchema>;
export type CoreConfig = z.infer<typeof coreConfigSchema>;

// helper for validator: categories point to existing audit or group refs
function missingRefsForCategoriesErrorMsg(coreCfg) {
Expand Down Expand Up @@ -89,9 +101,8 @@ function getMissingRefsForCategories(coreCfg) {
if (Array.isArray(missingAuditRefs) && missingAuditRefs.length > 0) {
missingRefs.push(...missingAuditRefs);
}
const groupRefsFromCategory = coreCfg.categories.flatMap(
({ metrics }) =>
metrics.filter(({ ref }) => isGroupRef(ref)).map(({ ref }) => ref), // plg#group:perf
const groupRefsFromCategory = coreCfg.categories.flatMap(({ metrics }) =>
metrics.filter(({ ref }) => isGroupRef(ref)).map(({ ref }) => ref),
);
const groupRefsFromPlugins = coreCfg.plugins.flatMap(({ groups, meta }) => {
return groups.map(({ slug }) => `${meta.slug}#group:${slug}`);
Expand Down
2 changes: 1 addition & 1 deletion packages/models/src/lib/global-cli-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ export const globalCliArgsSchema = z.object({
.default('code-pushup.config.js'),
});

export type GlobalCliArgsSchema = z.infer<typeof globalCliArgsSchema>;
export type GlobalCliArgs = z.infer<typeof globalCliArgsSchema>;
35 changes: 15 additions & 20 deletions packages/models/src/lib/implementation/helpers.mock.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { PluginConfigSchema } from '../plugin-config';
import { RunnerOutputSchema } from '../output';
import { CoreConfigSchema } from '../core-config';
import { CategoryConfigSchema } from '../category-config';
import { UploadConfigSchema } from '../upload-config';
import { PersistConfigSchema } from '../persist-config';
import { PluginConfig, RunnerOutput } from '../plugin-config';
import { CoreConfig } from '../core-config';
import { CategoryConfig } from '../category-config';
import { UploadConfig } from '../upload-config';
import { PersistConfig } from '../persist-config';

export function mockConfig(opt?: {
pluginSlug?: string | string[];
auditSlug?: string | string[];
groupSlug?: string | string[];
}): CoreConfigSchema {
}): CoreConfig {
let { pluginSlug, auditSlug, groupSlug } = opt || {};
pluginSlug = pluginSlug || 'mock-plugin-slug';
auditSlug = auditSlug || 'mock-audit-slug';
Expand All @@ -27,7 +26,7 @@ export function mockPluginConfig(opt?: {
pluginSlug?: string;
auditSlug?: string | string[];
groupSlug?: string | string[];
}): PluginConfigSchema {
}): PluginConfig {
const { groupSlug } = opt || {};
let { pluginSlug, auditSlug } = opt || {};
pluginSlug = pluginSlug || 'mock-plugin-slug';
Expand Down Expand Up @@ -58,7 +57,7 @@ export function mockPluginConfig(opt?: {
slug: `${pluginSlug}#${slug}`,
value: parseFloat('0.' + idx),
})),
} satisfies RunnerOutputSchema)}' > ${outputPath}`,
} satisfies RunnerOutput)}' > ${outputPath}`,
],
outputPath: outputPath,
},
Expand All @@ -71,7 +70,7 @@ export function mockPluginConfig(opt?: {

export function mockAuditConfig(opt?: {
auditSlug?: string;
}): PluginConfigSchema['audits'][0] {
}): PluginConfig['audits'][0] {
let { auditSlug } = opt || {};
auditSlug = auditSlug || 'mock-audit-slug';

Expand All @@ -85,7 +84,7 @@ export function mockAuditConfig(opt?: {
}
export function mockMetric(opt?: {
auditRef?: string;
}): CategoryConfigSchema['metrics'][0] {
}): CategoryConfig['metrics'][0] {
const { auditRef } = opt || {};
const ref = auditRef || 'mock-plugin-slug#mock-audit-slug';

Expand All @@ -98,7 +97,7 @@ export function mockMetric(opt?: {
export function mockGroupConfig(opt?: {
groupSlug?: string;
auditSlug?: string | string[];
}): PluginConfigSchema['groups'][0] {
}): PluginConfig['groups'][0] {
let { groupSlug, auditSlug } = opt || {};
groupSlug = groupSlug || 'mock-group-slug';
auditSlug = auditSlug || 'mock-audit-slug';
Expand All @@ -116,7 +115,7 @@ export function mockGroupConfig(opt?: {
export function mockCategory(opt?: {
categorySlug?: string;
auditRefOrGroupRef?: string | string[];
}): CategoryConfigSchema {
}): CategoryConfig {
let { auditRefOrGroupRef, categorySlug } = opt || {};
categorySlug = categorySlug || 'mock-category-slug';
auditRefOrGroupRef = auditRefOrGroupRef || 'mock-plugin-slug#mock-audit-slug';
Expand All @@ -133,18 +132,14 @@ export function mockCategory(opt?: {
};
}

export function mockUploadConfig(
opt?: Partial<UploadConfigSchema>,
): UploadConfigSchema {
export function mockUploadConfig(opt?: Partial<UploadConfig>): UploadConfig {
return {
apiKey: 'm0ck-API-k3y',
server: 'http://test.server.io',
...opt,
};
}
export function mockPersistConfig(
opt?: Partial<PersistConfigSchema>,
): PersistConfigSchema {
export function mockPersistConfig(opt?: Partial<PersistConfig>): PersistConfig {
return {
outputPath: 'mock-output-path.json',
...opt,
Expand All @@ -153,7 +148,7 @@ export function mockPersistConfig(

export function mockRunnerOutput(opt?: {
auditSlug: string | string[];
}): RunnerOutputSchema {
}): RunnerOutput {
let { auditSlug } = opt || {};
auditSlug = auditSlug || 'mock-audit-output-slug';
const audits = Array.isArray(auditSlug)
Expand Down
53 changes: 0 additions & 53 deletions packages/models/src/lib/output.spec.ts

This file was deleted.

Loading

0 comments on commit dacaaf7

Please sign in to comment.