Skip to content

feat(models): setup types and parser with zod #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 56 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
d067799
feat(models): setup types and parser with zod
BioPhoton Aug 30, 2023
46d4408
fix(models): add package.lock
BioPhoton Aug 30, 2023
0e5ef27
refactor(models): format
BioPhoton Aug 30, 2023
1369565
refactor(models): align package.json
BioPhoton Aug 30, 2023
63c1bcc
refactor(models): format
BioPhoton Aug 30, 2023
8096b85
refactor(models): package.lock
BioPhoton Aug 31, 2023
70fc405
Update packages/models/project.json
BioPhoton Aug 31, 2023
c885349
Update packages/models/project.json
BioPhoton Aug 31, 2023
337c9c2
refactor(models): remove budgets from initial code base
BioPhoton Aug 31, 2023
aaa17a5
Update packages/models/src/lib/category-config.ts
BioPhoton Aug 31, 2023
0a67872
refactor(models): remove budgets
BioPhoton Aug 31, 2023
48dd062
Update packages/models/src/lib/plugins.ts
BioPhoton Sep 1, 2023
3b35871
Update packages/models/src/lib/plugins.ts
BioPhoton Sep 1, 2023
34ceba1
Update packages/models/src/lib/upload.ts
BioPhoton Sep 1, 2023
71c5a4d
Update packages/models/src/lib/upload.ts
BioPhoton Sep 1, 2023
05486fd
refactor(models): add int and non negative validators
BioPhoton Sep 1, 2023
23cf79a
refactor(models): add cross field validators
BioPhoton Sep 4, 2023
d52de86
refactor(models): format files
BioPhoton Sep 4, 2023
b9e1244
refactor(models): implement feedback from PR, format
BioPhoton Sep 5, 2023
9bc21e6
Update packages/models/project.json
BioPhoton Sep 5, 2023
20ccf92
Update packages/models/src/lib/category-config.ts
BioPhoton Sep 5, 2023
401ed9a
Update packages/models/src/lib/implementation/schemas.ts
BioPhoton Sep 5, 2023
cbee718
Update packages/models/src/lib/category-config.ts
BioPhoton Sep 5, 2023
a000ee9
Update packages/models/src/lib/core-config.spec.ts
BioPhoton Sep 5, 2023
651498b
Update packages/models/src/lib/output.ts
BioPhoton Sep 5, 2023
4ec4bc4
Update packages/models/src/lib/output.ts
BioPhoton Sep 5, 2023
c0b4164
Update packages/models/src/lib/plugins.ts
BioPhoton Sep 5, 2023
462c5ed
Update packages/models/src/lib/plugins.ts
BioPhoton Sep 5, 2023
deb7209
Update packages/models/src/lib/core-config.spec.ts
BioPhoton Sep 5, 2023
d3cbc08
Update packages/models/src/lib/output.ts
BioPhoton Sep 5, 2023
82cc90f
Update packages/models/src/lib/output.ts
BioPhoton Sep 5, 2023
7fcdf05
Update packages/models/src/lib/core-config.ts
BioPhoton Sep 5, 2023
b7e985c
Update packages/models/src/lib/core-config.ts
BioPhoton Sep 5, 2023
fca30a4
Update packages/models/src/lib/implementation/utils.spec.ts
BioPhoton Sep 5, 2023
74e6594
Update packages/models/src/lib/implementation/utils.spec.ts
BioPhoton Sep 5, 2023
9a1a2c6
Merge remote-tracking branch 'origin/main' into add-models
BioPhoton Sep 5, 2023
4e0fd2e
refactor(models): add regex and validators and tests
BioPhoton Sep 5, 2023
66a5399
refactor(models): fix tests and lint errors
BioPhoton Sep 5, 2023
ce2362b
refactor(models): fix tests and comments
BioPhoton Sep 5, 2023
1c0e36e
refactor(models): add test case
BioPhoton Sep 5, 2023
9979695
Update schemas.ts
BioPhoton Sep 6, 2023
c000792
Update schemas.ts
BioPhoton Sep 6, 2023
acf20d6
Update schemas.ts
BioPhoton Sep 6, 2023
8b3c562
Update schemas.ts
BioPhoton Sep 6, 2023
ad2f7e9
Update utils.ts
BioPhoton Sep 6, 2023
ea3d7b1
Update utils.ts
BioPhoton Sep 6, 2023
5406c96
Update global-cli-options.ts
BioPhoton Sep 6, 2023
d6a82d6
refactor(models): merge suggestions, fix tests
BioPhoton Sep 6, 2023
7831a5a
Update packages/models/src/lib/category-config.spec.ts
BioPhoton Sep 6, 2023
2f7b498
Update packages/models/src/lib/implementation/schemas.ts
BioPhoton Sep 6, 2023
8f2e201
Update packages/models/src/lib/category-config.spec.ts
BioPhoton Sep 6, 2023
41b4edc
refactor(models): reinstall
BioPhoton Sep 6, 2023
9e97fa4
refactor(models): fix dependencies
BioPhoton Sep 6, 2023
c54d461
refactor(models): fix dependencies 2
BioPhoton Sep 6, 2023
d2ea0f4
refactor(models): fix dependencies 2
BioPhoton Sep 6, 2023
d6fd5e8
refactor(models): fix dependencies 3
BioPhoton Sep 6, 2023
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
19 changes: 18 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"dist/packages/*"
],
"dependencies": {
"bundle-require": "^4.0.1"
"bundle-require": "^4.0.1",
"zod": "^3.22.1"
},
"devDependencies": {
"@nx/esbuild": "16.7.4",
Expand Down
25 changes: 25 additions & 0 deletions packages/models/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": ["error"]
}
}
]
}
14 changes: 14 additions & 0 deletions packages/models/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# models

Model definitions and validators for the CLI configuration as well as plugin types and respective parser.

## Usage

```ts
import { CoreConfigSchema, pluginConfigSchema } from '@quality-metrics/models';

export default {
// ...
plugins: [pluginConfigSchema.parse({ ... })],
} satisfies CoreConfigSchema;
```
24 changes: 24 additions & 0 deletions packages/models/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "models",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/models/src",
"projectType": "library",
"targets": {
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["packages/models/**/*.ts"]
}
},
"test": {
"executor": "@nx/vite:test",
"outputs": ["{workspaceRoot}/coverage/packages/cli"],
"options": {
"passWithNoTests": true,
"reportsDirectory": "../../coverage/packages/cli"
}
}
},
"tags": []
}
18 changes: 18 additions & 0 deletions packages/models/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
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';
export {
categoryConfigSchema,
CategoryConfigSchema,
} from './lib/category-config';
export {
globalCliArgsSchema,
GlobalCliArgsSchema,
} from './lib/global-cli-options';
43 changes: 43 additions & 0 deletions packages/models/src/lib/category-config.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { describe, expect, it } from 'vitest';
import { mockCategory } from './implementation/helpers.mock';
import { categoryConfigSchema } from './category-config';

describe('categoryConfigSchema', () => {
it('should parse if configuration with audit refs is valid', () => {
const cfg = mockCategory({ auditRefOrGroupRef: ['test#a', 'test#b'] });
expect(() => categoryConfigSchema.parse(cfg)).not.toThrow();
});

it('should parse if configuration with group refs is valid', () => {
const cfg = mockCategory({ auditRefOrGroupRef: ['es-lint#group:base'] });
expect(() => categoryConfigSchema.parse(cfg)).not.toThrow();
});

it('should throw if category slug has a invalid pattern', () => {
const invalidCategorySlug = '-invalid-category-slug';
const cfg = mockCategory({ categorySlug: invalidCategorySlug });

expect(() => categoryConfigSchema.parse(cfg)).toThrow(
`slug has to follow the patter`,
);
});

it('should throw if audit ref in metrics is invalid', () => {
const invalidAuditRef = 'no-any';
const cfg = mockCategory({ auditRefOrGroupRef: [invalidAuditRef] });

expect(() => categoryConfigSchema.parse(cfg)).toThrow(
`ref has to follow the patter`,
);
});

it('should throw if duplicate refs to audits or groups in metrics are given', () => {
const duplicatedSlug = 'test#a';
const cfg = mockCategory({
auditRefOrGroupRef: [duplicatedSlug, duplicatedSlug],
});
expect(() => categoryConfigSchema.parse(cfg)).toThrow(
'the following audit or group refs are duplicates',
);
});
});
72 changes: 72 additions & 0 deletions packages/models/src/lib/category-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { z } from 'zod';
import {
refSchema,
descriptionSchema,
slugSchema,
titleSchema,
weightSchema,
} from './implementation/schemas';
import { errorItems, hasDuplicateStrings } from './implementation/utils';

/**
*
* Define Zod schema for the CategoryConfig type
*
* @example
*
* // Example data for the CategoryConfig type
* const data = {
* // ...
* };
*
* // Validate the data against the schema
* const validationResult = categoryConfigSchema.safeParse(data);
*
* if (validationResult.success) {
* console.log('Valid category config:', validationResult.data);
* } else {
* console.error('Invalid category config:', validationResult.error);
* }
*/
export const categoryConfigSchema = z.object(
{
slug: slugSchema(),
title: titleSchema('Display name for the category '),
description: descriptionSchema('Optional description in Markdown format'),
metrics: z
.array(
z.object(
{
ref: refSchema(
"Reference to a plugin's audit (e.g. 'eslint#max-lines') or group (e.g. 'lhci#group:performance')",
),
weight: weightSchema(),
},
{ description: 'Array of metrics associated with the category' },
),
)
// metrics have unique refs to audits or groups within a category
.refine(
metrics => !getDuplicateRefsInCategoryMetrics(metrics),
metrics => ({
message: duplicateRefsInCategoryMetricsErrorMsg(metrics),
}),
),
},
{
description: 'Weighted references to plugin-specific audits/categories',
},
);

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

// helper for validator: categories have unique refs to audits or groups
export function duplicateRefsInCategoryMetricsErrorMsg(metrics) {
const duplicateRefs = getDuplicateRefsInCategoryMetrics(metrics);
return `In the categories, the following audit or group refs are duplicates: ${errorItems(
duplicateRefs,
)}`;
}
function getDuplicateRefsInCategoryMetrics(metrics) {
return hasDuplicateStrings(metrics.map(({ ref }) => ref));
}
84 changes: 84 additions & 0 deletions packages/models/src/lib/core-config.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { describe, expect, it } from 'vitest';
import {
mockCategory,
mockConfig,
mockPluginConfig,
} from './implementation/helpers.mock';
import { coreConfigSchema } from './core-config';

/*
- plugin slug: es-lint
- audit slug: no-any
- group slug: basics
- audit: no-any
- category: best-practices

- from category to audit: es-lint#no-any
- from category to group: es-lint#group:basics
*/
describe('CoreConfig', () => {
it('should parse if configuration is valid', () => {
const cfg = mockConfig({ pluginSlug: 'test', auditSlug: ['a', 'b'] });
cfg.categories.push(
mockCategory({ auditRefOrGroupRef: ['test#a', 'test#b'] }),
);
expect(() => coreConfigSchema.parse(cfg)).not.toThrow();
});

it('should parse if configuration and groups are valid', () => {
const pluginSlug = 'plg';
const cfg = mockConfig();
cfg.plugins = [
mockPluginConfig({ pluginSlug, auditSlug: 'lcp', groupSlug: 'perf' }),
];
cfg.categories = [
mockCategory({ auditRefOrGroupRef: ['plg#lcp', 'plg#group:perf'] }),
];
// In the categories, the following plugin refs do not exist in the provided plugins: test#group:group-slug
expect(() => coreConfigSchema.parse(cfg)).not.toThrow();
});

it('should throw if the category slugs are not unique', () => {
const cfg = mockConfig({ pluginSlug: 'test', auditSlug: ['a', 'b'] });
const duplicatedSlug = 'test';
cfg.categories.push(
mockCategory({ categorySlug: 'test', auditRefOrGroupRef: ['test#a'] }),
mockCategory({ categorySlug: 'test', auditRefOrGroupRef: ['test#b'] }),
);
expect(() => coreConfigSchema.parse(cfg)).toThrow(
`In the categories, the following slugs are duplicated: ${duplicatedSlug}`,
);
});

it('should throw if ref in a category does not exist in audits', () => {
const cfg = mockConfig({ pluginSlug: 'test', auditSlug: ['a', 'b'] });
const missingSlug = 'missing-plugin-slug-in-category#auditref';
cfg.categories.push(
mockCategory({
categorySlug: 'test',
auditRefOrGroupRef: [`${missingSlug}`],
}),
);
expect(() => coreConfigSchema.parse(cfg)).toThrow(
`In the categories, the following plugin refs do not exist in the provided plugins: ${missingSlug}`,
);
});

it('should throw if ref in a category does not exist in groups', () => {
const cfg = mockConfig({
pluginSlug: 'test',
auditSlug: ['a', 'b'],
groupSlug: 'test#a',
});
const missingSlug = 'missing-plugin-slug-in-category#groups:auditref';
cfg.categories.push(
mockCategory({
categorySlug: 'test',
auditRefOrGroupRef: [`${missingSlug}`],
}),
);
expect(() => coreConfigSchema.parse(cfg)).toThrow(
`In the categories, the following plugin refs do not exist in the provided plugins: ${missingSlug}`,
);
});
});
Loading