Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
7 changes: 7 additions & 0 deletions packages/create-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ Each plugin exposes its own configuration keys that can be passed as CLI argumen
| **`--typescript.tsconfig`** | `string` | auto-detected | TypeScript config file |
| **`--typescript.categories`** | `boolean` | `true` | Add TypeScript categories |

#### Lighthouse

| Option | Type | Default | Description |
| ----------------------------- | ---------------------------------------------------------------- | ----------------------- | ------------------------------- |
| **`--lighthouse.urls`** | `string` | `http://localhost:4200` | Target URL(s) (comma-separated) |
Comment thread
hanna-skryl marked this conversation as resolved.
Outdated
| **`--lighthouse.categories`** | `('performance'` \| `'a11y'` \| `'best-practices'` \| `'seo')[]` | all | Lighthouse categories |

### Examples

Run interactively (default):
Expand Down
1 change: 1 addition & 0 deletions packages/create-cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"@code-pushup/coverage-plugin": "0.123.0",
"@code-pushup/eslint-plugin": "0.123.0",
"@code-pushup/js-packages-plugin": "0.123.0",
"@code-pushup/lighthouse-plugin": "0.123.0",
"@code-pushup/models": "0.123.0",
"@code-pushup/typescript-plugin": "0.123.0",
"@code-pushup/utils": "0.123.0",
Expand Down
4 changes: 3 additions & 1 deletion packages/create-cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { hideBin } from 'yargs/helpers';
import { coverageSetupBinding } from '@code-pushup/coverage-plugin';
import { eslintSetupBinding } from '@code-pushup/eslint-plugin';
import { jsPackagesSetupBinding } from '@code-pushup/js-packages-plugin';
import { lighthouseSetupBinding } from '@code-pushup/lighthouse-plugin';
import { typescriptSetupBinding } from '@code-pushup/typescript-plugin';
import { parsePluginSlugs, validatePluginSlugs } from './lib/setup/plugins.js';
import {
Expand All @@ -14,12 +15,13 @@ import {
} from './lib/setup/types.js';
import { runSetupWizard } from './lib/setup/wizard.js';

// TODO: create, import and pass remaining plugin bindings (lighthouse, jsdocs, axe)
// TODO: create, import and pass remaining plugin bindings (jsdocs, axe)
const bindings: PluginSetupBinding[] = [
eslintSetupBinding,
coverageSetupBinding,
jsPackagesSetupBinding,
typescriptSetupBinding,
lighthouseSetupBinding,
];

const argv = await yargs(hideBin(process.argv))
Expand Down
99 changes: 99 additions & 0 deletions packages/create-cli/src/lib/setup/codegen-categories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import type { CategoryRef } from '@code-pushup/models';
import { mergeDescriptions, singleQuote } from '@code-pushup/utils';
import type { CodeBuilder } from './codegen.js';
import type { CategoryCodegenConfig, PluginCodegenResult } from './types.js';

type MergedCategory = {
slug: string;
title: string;
description?: string;
docsUrl?: string;
refs: CategoryRef[];
refsExpressions: string[];
};

export function addCategories(
builder: CodeBuilder,
plugins: PluginCodegenResult[],
depth = 1,
): void {
const categories = mergeCategoriesBySlug(
plugins.flatMap(p => p.categories ?? []).map(toMergedCategory),
);
if (categories.length === 0) {
return;
}
builder.addLine('categories: [', depth);
categories.forEach(
({ slug, title, description, docsUrl, refs, refsExpressions }) => {
builder.addLine('{', depth + 1);
builder.addLine(`slug: '${slug}',`, depth + 2);
builder.addLine(`title: ${singleQuote(title)},`, depth + 2);
if (description) {
builder.addLine(`description: ${singleQuote(description)},`, depth + 2);
}
if (docsUrl) {
builder.addLine(`docsUrl: ${singleQuote(docsUrl)},`, depth + 2);
}
addCategoryRefs(builder, refs, refsExpressions, depth + 2);
builder.addLine('},', depth + 1);
},
);
builder.addLine('],', depth);
}

function toMergedCategory(category: CategoryCodegenConfig): MergedCategory {
return {
slug: category.slug,
title: category.title,
description: category.description,
docsUrl: category.docsUrl,
refs: 'refs' in category ? category.refs : [],
refsExpressions:
'refsExpression' in category ? [category.refsExpression] : [],
};
}

function mergeCategoriesBySlug(categories: MergedCategory[]): MergedCategory[] {
const map = categories.reduce((acc, category) => {
const existing = acc.get(category.slug);
acc.set(
category.slug,
existing ? mergeCategory(existing, category) : category,
);
return acc;
}, new Map<string, MergedCategory>());
return [...map.values()];
}

function mergeCategory(
existing: MergedCategory,
incoming: MergedCategory,
): MergedCategory {
return {
...existing,
description: mergeDescriptions(existing.description, incoming.description),
docsUrl: existing.docsUrl ?? incoming.docsUrl,
refs: [...existing.refs, ...incoming.refs],
refsExpressions: [...existing.refsExpressions, ...incoming.refsExpressions],
};
}

function addCategoryRefs(
builder: CodeBuilder,
refs: MergedCategory['refs'],
refsExpressions: MergedCategory['refsExpressions'],
depth: number,
): void {
builder.addLine('refs: [', depth);
builder.addLines(
refsExpressions.map(expr => `...${expr},`),
depth + 1,
);
builder.addLines(refs.map(formatCategoryRef), depth + 1);
builder.addLine('],', depth);
}

function formatCategoryRef(ref: CategoryRef): string {
return `{ type: '${ref.type}', plugin: '${ref.plugin}', slug: '${ref.slug}', weight: ${ref.weight} },`;
}
60 changes: 19 additions & 41 deletions packages/create-cli/src/lib/setup/codegen.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import path from 'node:path';
import type { CategoryRef } from '@code-pushup/models';
import {
mergeCategoriesBySlug,
singleQuote,
toUnixPath,
} from '@code-pushup/utils';
import { exists, toUnixPath } from '@code-pushup/utils';
import { addCategories } from './codegen-categories.js';
import type {
ConfigFileFormat,
ImportDeclarationStructure,
Expand All @@ -17,7 +13,7 @@ const CORE_CONFIG_IMPORT: ImportDeclarationStructure = {
isTypeOnly: true,
};

class CodeBuilder {
export class CodeBuilder {
private lines: string[] = [];

addLine(text: string, depth = 0): void {
Expand Down Expand Up @@ -45,6 +41,7 @@ export function generateConfigSource(
): string {
const builder = new CodeBuilder();
addImports(builder, collectImports(plugins, format));
addPluginDeclarations(builder, plugins);
if (format === 'ts') {
builder.addLine('export default {');
addPlugins(builder, plugins);
Expand All @@ -66,6 +63,7 @@ export function generatePresetSource(
): string {
const builder = new CodeBuilder();
addImports(builder, collectImports(plugins, format));
addPluginDeclarations(builder, plugins);
addPresetExport(builder, plugins, format);
return builder.toString();
}
Expand Down Expand Up @@ -137,6 +135,20 @@ function addImports(
}
}

function addPluginDeclarations(
builder: CodeBuilder,
plugins: PluginCodegenResult[],
): void {
const declarations = plugins
.map(({ pluginDeclaration }) => pluginDeclaration)
.filter(exists)
.map(d => `const ${d.identifier} = ${d.expression};`);
if (declarations.length > 0) {
builder.addLines(declarations);
builder.addEmptyLine();
}
}

function addPlugins(
builder: CodeBuilder,
plugins: PluginCodegenResult[],
Expand Down Expand Up @@ -183,37 +195,3 @@ function addPresetExport(
builder.addLine('};', 1);
builder.addLine('}');
}

function addCategories(
builder: CodeBuilder,
plugins: PluginCodegenResult[],
depth = 1,
): void {
const categories = mergeCategoriesBySlug(
plugins.flatMap(p => p.categories ?? []),
);
if (categories.length === 0) {
return;
}
builder.addLine('categories: [', depth);
categories.forEach(({ slug, title, description, docsUrl, refs }) => {
builder.addLine('{', depth + 1);
builder.addLine(`slug: '${slug}',`, depth + 2);
builder.addLine(`title: ${singleQuote(title)},`, depth + 2);
if (description) {
builder.addLine(`description: ${singleQuote(description)},`, depth + 2);
}
if (docsUrl) {
builder.addLine(`docsUrl: ${singleQuote(docsUrl)},`, depth + 2);
}
builder.addLine('refs: [', depth + 2);
builder.addLines(refs.map(formatCategoryRef), depth + 3);
builder.addLine('],', depth + 2);
builder.addLine('},', depth + 1);
});
builder.addLine('],', depth);
}

function formatCategoryRef(ref: CategoryRef): string {
return `{ type: '${ref.type}', plugin: '${ref.plugin}', slug: '${ref.slug}', weight: ${ref.weight} },`;
}
124 changes: 124 additions & 0 deletions packages/create-cli/src/lib/setup/codegen.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,130 @@ describe('generateConfigSource', () => {
expect(source).toContain("plugin: 'ts'");
});
});

describe('pluginDeclaration', () => {
it('should emit variable declaration between imports and config export', () => {
const plugin: PluginCodegenResult = {
imports: [
{
moduleSpecifier: '@code-pushup/lighthouse-plugin',
defaultImport: 'lighthousePlugin',
},
],
pluginDeclaration: {
identifier: 'lhPlugin',
expression: "lighthousePlugin('http://localhost:4200')",
},
pluginInit: ['lhPlugin,'],
};
expect(generateConfigSource([plugin], 'ts')).toMatchInlineSnapshot(`
"import lighthousePlugin from '@code-pushup/lighthouse-plugin';
import type { CoreConfig } from '@code-pushup/models';

const lhPlugin = lighthousePlugin('http://localhost:4200');

export default {
plugins: [
lhPlugin,
],
} satisfies CoreConfig;
"
`);
});
});

describe('expression refs', () => {
it('should generate config with expression refs and merged categories', () => {
expect(
generateConfigSource(
[
{
imports: [
{
moduleSpecifier: '@code-pushup/lighthouse-plugin',
defaultImport: 'lighthousePlugin',
namedImports: ['lighthouseGroupRefs'],
},
],
pluginDeclaration: {
identifier: 'lhPlugin',
expression: "lighthousePlugin('http://localhost:4200')",
},
pluginInit: ['lhPlugin,'],
categories: [
{
slug: 'a11y',
title: 'Accessibility',
refsExpression:
"lighthouseGroupRefs(lhPlugin, 'accessibility')",
},
{
slug: 'performance',
title: 'Performance',
refsExpression:
"lighthouseGroupRefs(lhPlugin, 'performance')",
},
],
},
{
imports: [
{
moduleSpecifier: '@code-pushup/axe-plugin',
defaultImport: 'axePlugin',
namedImports: ['axeGroupRefs'],
},
],
pluginDeclaration: {
identifier: 'axe',
expression: "axePlugin('http://localhost:4200')",
},
pluginInit: ['axe,'],
categories: [
{
slug: 'a11y',
title: 'Accessibility',
refsExpression: 'axeGroupRefs(axe)',
},
],
},
],
'ts',
),
).toMatchInlineSnapshot(`
"import axePlugin, { axeGroupRefs } from '@code-pushup/axe-plugin';
import lighthousePlugin, { lighthouseGroupRefs } from '@code-pushup/lighthouse-plugin';
import type { CoreConfig } from '@code-pushup/models';

const lhPlugin = lighthousePlugin('http://localhost:4200');
const axe = axePlugin('http://localhost:4200');

export default {
plugins: [
lhPlugin,
axe,
],
categories: [
{
slug: 'a11y',
title: 'Accessibility',
refs: [
...lighthouseGroupRefs(lhPlugin, 'accessibility'),
...axeGroupRefs(axe),
],
},
{
slug: 'performance',
title: 'Performance',
refs: [
...lighthouseGroupRefs(lhPlugin, 'performance'),
],
},
],
} satisfies CoreConfig;
"
`);
});
});
});

describe('generatePresetSource', () => {
Expand Down
1 change: 1 addition & 0 deletions packages/create-cli/src/lib/setup/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { PluginCodegenResult } from '@code-pushup/models';
import type { MonorepoTool } from '@code-pushup/utils';

export type {
CategoryCodegenConfig,
ImportDeclarationStructure,
PluginAnswer,
PluginCodegenResult,
Expand Down
2 changes: 2 additions & 0 deletions packages/models/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,11 @@ export {
type PluginUrls,
} from './lib/plugin-config.js';
export type {
CategoryCodegenConfig,
ImportDeclarationStructure,
PluginAnswer,
PluginCodegenResult,
PluginDeclarationStructure,
PluginPromptDescriptor,
PluginSetupBinding,
PluginSetupTree,
Expand Down
Loading
Loading