Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2c35200
chore: adjust .gitignore
BioPhoton Aug 16, 2025
5006bf5
chore: adjust cp preset
BioPhoton Aug 16, 2025
fd9b743
chore: adjust nx.json default targets
BioPhoton Aug 16, 2025
a253902
feat: add artifact utils logic to eslint plugin
BioPhoton Aug 16, 2025
f11b093
feat(models): add runner options
BioPhoton Aug 16, 2025
6175c5b
feat(models): add runner options
BioPhoton Aug 16, 2025
1c1e201
feat(plugin-eslint): add artifacts options
BioPhoton Aug 16, 2025
e93232f
fix(plugin-eslint): artifacts options
BioPhoton Aug 16, 2025
dfeecab
fix(plugin-eslint): cleanup runner config removal
BioPhoton Aug 16, 2025
2877a89
docs(plugin-eslint): add artifacts docs
BioPhoton Aug 16, 2025
74b6bcf
test(plugin-eslint): fix unit tests
BioPhoton Aug 16, 2025
b85199b
fix(plugin-eslint): fix build
BioPhoton Aug 16, 2025
e4fe8a9
fix(plugin-eslint): fix unit-test
BioPhoton Aug 16, 2025
84742a7
docs(models): update models docs
BioPhoton Aug 16, 2025
8de7b2b
fix: fix lint
BioPhoton Aug 16, 2025
eb8a7ef
feat: add custom formatter
BioPhoton Aug 20, 2025
3182a47
chore: add lint-reporter targets
Aug 21, 2025
e4f6f37
fix(plugin-eslint): refactor formatter to ts
Aug 21, 2025
3ab7674
fix(plugin-eslint): add glob handling
Aug 21, 2025
42abfcf
fix(plugin-eslint): adjust default lint targets
Aug 21, 2025
d43d85f
Merge branch 'main' into feat/plugin-eslint/artifacts-caching-poc
BioPhoton Aug 21, 2025
3c522c7
wip
Aug 21, 2025
a559959
Merge remote-tracking branch 'origin/main' into feat/plugin-eslint/ar…
BioPhoton Aug 21, 2025
00e3fe7
docs(plugin-eslint): add formatter docs
BioPhoton Aug 21, 2025
5d40c59
docs(plugin-eslint): fix code fro new loader
BioPhoton Aug 21, 2025
79170da
docs(plugin-eslint): fix code fro new loader 2
BioPhoton Aug 21, 2025
486e099
chore: add lint target with formatter
BioPhoton Aug 21, 2025
146132a
fix(plugin-eslint): fix lint
BioPhoton Aug 21, 2025
d2de7bb
test(plugin-eslint): fix unit tests
BioPhoton Aug 21, 2025
690ba8e
test(models): add globPathSchema
BioPhoton Aug 21, 2025
3db86dc
fix: add regex for eslint report
BioPhoton Aug 21, 2025
330e480
fix: add regex for eslint report
BioPhoton Aug 21, 2025
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ testem.log
Thumbs.db

# generated Code PushUp reports
/.code-pushup
.code-pushup

# Nx workspace cache
.nx
28 changes: 28 additions & 0 deletions code-pushup.preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import eslintPlugin, {
eslintConfigFromAllNxProjects,
eslintConfigFromNxProject,
} from './packages/plugin-eslint/src/index.js';
import type { ESLintTarget } from './packages/plugin-eslint/src/lib/config.js';
import { nxProjectsToConfig } from './packages/plugin-eslint/src/lib/nx/projects-to-config.js';
import jsPackagesPlugin from './packages/plugin-js-packages/src/index.js';
import jsDocsPlugin from './packages/plugin-jsdocs/src/index.js';
import type { JsDocsPluginTransformedConfig } from './packages/plugin-jsdocs/src/lib/config.js';
Expand Down Expand Up @@ -156,6 +158,17 @@ export const jsDocsCoreConfig = (
),
});

export async function eslintConfigFromPublishableNxProjects(): Promise<
ESLintTarget[]
> {
const { createProjectGraphAsync } = await import('@nx/devkit');
const projectGraph = await createProjectGraphAsync({ exitOnError: false });
return nxProjectsToConfig(
projectGraph,
project => project.tags?.includes('publishable') ?? false,
);
}

export const eslintCoreConfigNx = async (
projectName?: string,
): Promise<CoreConfig> => ({
Expand All @@ -164,6 +177,21 @@ export const eslintCoreConfigNx = async (
await (projectName
? eslintConfigFromNxProject(projectName)
: eslintConfigFromAllNxProjects()),
{
artifacts: {
generateArtifactsCommand: {
command: 'npx',
args: [
'nx',
'run-many',
'-t',
'lint-reporter',
'--projects=tag:publishable',
],
},
artifactsPaths: 'packages/*/.code-pushup/eslint/eslint-report.json',
},
},
),
],
categories: eslintCategories,
Expand Down
34 changes: 33 additions & 1 deletion nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,48 @@
"lint": {
"inputs": ["default", "{workspaceRoot}/eslint.config.?(c)js"],
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"cache": true,
"options": {
"errorOnUnmatchedPattern": false,
"maxWarnings": 0,
"lintFilePatterns": [
"{projectRoot}/**/*.ts",
"{projectRoot}/package.json"
]
}
},
"lint-reporter": {
"inputs": ["default", "{workspaceRoot}/eslint.config.?(c)js"],
"outputs": ["{projectRoot}/.eslint/eslint-report*.json"],
"cache": true,
"executor": "@nx/linter:eslint",
"options": {
"errorOnUnmatchedPattern": false,
"maxWarnings": 0,
"format": "json",
"outputFile": "{projectRoot}/.eslint/eslint-report.json",
"lintFilePatterns": [
"{projectRoot}/**/*.ts",
"{projectRoot}/package.json"
]
}
},
"lint-formatter": {
"inputs": ["default", "{workspaceRoot}/eslint.config.?(c)js"],
"outputs": ["{projectRoot}/.eslint/eslint-report*.json"],
"cache": true,
"executor": "nx:run-commands",
"options": {
"command": "nx lint {projectName}",
"args": [
"--format",
"./packages/plugin-eslint/dist/src/lib/formatter/multiple-formats.js"
],
"env": {
"ESLINT_FORMATTER_CONFIG": "{\"outputDir\":\"{projectRoot}/.eslint\"}"
}
}
},
"nxv-pkg-install": {
"parallelism": false
},
Expand Down
1 change: 1 addition & 0 deletions packages/ci/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"targets": {
"build": {},
"lint": {},
"lint-reporter": {},
"unit-test": {},
"int-test": {}
},
Expand Down
1 change: 1 addition & 0 deletions packages/cli/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"targets": {
"build": {},
"lint": {},
"lint-reporter": {},
"unit-test": {},
"int-test": {},
"run-help": {
Expand Down
1 change: 1 addition & 0 deletions packages/core/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"targets": {
"build": {},
"lint": {},
"lint-reporter": {},
"unit-test": {},
"int-test": {}
},
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/lib/implementation/execute-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ export async function executePlugin(
? // IF not null, take the result from cache
((await readRunnerResults(pluginMeta.slug, outputDir)) ??
// ELSE execute the plugin runner
(await executePluginRunner(pluginConfig)))
: await executePluginRunner(pluginConfig);
(await executePluginRunner(pluginConfig, persist)))
: await executePluginRunner(pluginConfig, persist);

if (cacheWrite) {
await writeRunnerResults(pluginMeta.slug, outputDir, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ describe('executePlugin', () => {

await expect(
executePlugin(MINIMAL_PLUGIN_CONFIG_MOCK, {
persist: { outputDir: 'dummy-path-result-is-mocked' },
persist: { outputDir: MEMFS_VOLUME },
cache: { read: true, write: false },
}),
).resolves.toStrictEqual({
Expand All @@ -122,6 +122,7 @@ describe('executePlugin', () => {

expect(executePluginRunnerSpy).toHaveBeenCalledWith(
MINIMAL_PLUGIN_CONFIG_MOCK,
{ outputDir: MEMFS_VOLUME },
);
});
});
Expand Down Expand Up @@ -322,8 +323,8 @@ describe('executePlugins', () => {
{
...MINIMAL_PLUGIN_CONFIG_MOCK,
runner: {
command: 'node',
args: ['-v'],
command: 'echo',
args: ['16'],
outputFile: 'output.json',
outputTransform: (outputs: unknown): Promise<AuditOutputs> =>
Promise.resolve([
Expand All @@ -337,7 +338,7 @@ describe('executePlugins', () => {
},
},
],
persist: { outputDir: '.code-pushup' },
persist: { outputDir: MEMFS_VOLUME },
cache: { read: false, write: false },
},
{ progress: false },
Expand Down
13 changes: 9 additions & 4 deletions packages/core/src/lib/implementation/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { writeFile } from 'node:fs/promises';
import path from 'node:path';
import {
type AuditOutputs,
type PersistConfig,
type PluginConfig,
type RunnerConfig,
type RunnerFunction,
Expand All @@ -14,6 +15,7 @@ import {
executeProcess,
fileExists,
isVerbose,
objectToCliArgs,
readJsonFile,
removeDirectoryIfExists,
ui,
Expand All @@ -32,12 +34,13 @@ export type ValidatedRunnerResult = Omit<RunnerResult, 'audits'> & {

export async function executeRunnerConfig(
cfg: RunnerConfig,
config: Required<Pick<PersistConfig, 'outputDir'>>,
): Promise<RunnerResult> {
const { args, command, outputFile, outputTransform } = cfg;

const { duration, date } = await executeProcess({
command,
args,
args: [...(args ?? []), ...objectToCliArgs(config)],
observer: {
onStdout: stdout => {
if (isVerbose()) {
Expand Down Expand Up @@ -66,12 +69,13 @@ export async function executeRunnerConfig(

export async function executeRunnerFunction(
runner: RunnerFunction,
config: PersistConfig,
): Promise<RunnerResult> {
const date = new Date().toISOString();
const start = performance.now();

// execute plugin runner
const audits = await runner();
const audits = await runner(config);

// create runner result
return {
Expand All @@ -96,12 +100,13 @@ export class AuditOutputsMissingAuditError extends Error {

export async function executePluginRunner(
pluginConfig: Pick<PluginConfig, 'audits' | 'runner'>,
persist: Required<Pick<PersistConfig, 'outputDir'>>,
): Promise<Omit<RunnerResult, 'audits'> & { audits: AuditOutputs }> {
const { audits: pluginConfigAudits, runner } = pluginConfig;
const runnerResult: RunnerResult =
typeof runner === 'object'
? await executeRunnerConfig(runner)
: await executeRunnerFunction(runner);
? await executeRunnerConfig(runner, persist)
: await executeRunnerFunction(runner, persist);
const { audits: unvalidatedAuditOutputs, ...executionMeta } = runnerResult;

const result = auditOutputsSchema.safeParse(unvalidatedAuditOutputs);
Expand Down
1 change: 1 addition & 0 deletions packages/create-cli/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"targets": {
"build": {},
"lint": {},
"lint-reporter": {},
"unit-test": {},
"exec-node": {
"dependsOn": ["build"],
Expand Down
2 changes: 1 addition & 1 deletion packages/models/docs/models-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1418,7 +1418,7 @@ _Function._

_Parameters:_

- _none_
1. [PersistConfig](#persistconfig)

_Returns:_

Expand Down
1 change: 1 addition & 0 deletions packages/models/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
]
},
"lint": {},
"lint-reporter": {},
"unit-test": {}
},
"tags": ["scope:shared", "type:util", "publishable"]
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 @@ -57,6 +57,7 @@ export {
export {
fileNameSchema,
filePathSchema,
globPathSchema,
materialIconSchema,
type MaterialIcon,
} from './lib/implementation/schemas.js';
Expand Down Expand Up @@ -145,4 +146,5 @@ export { uploadConfigSchema, type UploadConfig } from './lib/upload-config.js';
export {
artifactGenerationCommandSchema,
pluginArtifactOptionsSchema,
type PluginArtifactOptions,
} from './lib/configuration.js';
10 changes: 9 additions & 1 deletion packages/models/src/lib/configuration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { z } from 'zod';
import { filePathSchema, globPathSchema } from './implementation/schemas.js';

/**
* Generic schema for a tool command configuration, reusable across plugins.
Expand All @@ -13,7 +14,14 @@ export const artifactGenerationCommandSchema = z.union([

export const pluginArtifactOptionsSchema = z.object({
generateArtifactsCommand: artifactGenerationCommandSchema.optional(),
artifactsPaths: z.union([z.string(), z.array(z.string()).min(1)]),
artifactsPaths: z
.union([
filePathSchema,
z.array(filePathSchema).min(1),
globPathSchema,
z.array(globPathSchema).min(1),
])
.describe('File paths or glob patterns for artifact files'),
});

export type PluginArtifactOptions = z.infer<typeof pluginArtifactOptionsSchema>;
18 changes: 18 additions & 0 deletions packages/models/src/lib/implementation/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,24 @@ export const filePathSchema = z
.trim()
.min(1, { message: 'The path is invalid' });

/**
* Regex for glob patterns - validates file paths and glob patterns
* Allows normal paths and paths with glob metacharacters: *, **, {}, [], !, ?
* Excludes invalid path characters: <>"|
*/
const globRegex = /^!?[^<>"|]+$/;

/** Schema for a glob pattern (supports wildcards like *, **, {}, !, etc.) */
export const globPathSchema = z
.string()
.trim()
.min(1, { message: 'The glob pattern is invalid' })
.regex(globRegex, {
message:
'The path must be a valid file path or glob pattern (supports *, **, {}, [], !, ?)',
})
.describe('File path or glob pattern (supports *, **, {}, !, etc.)');

/** Schema for a fileNameSchema */
export const fileNameSchema = z
.string()
Expand Down
2 changes: 2 additions & 0 deletions packages/models/src/lib/runner-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { z } from 'zod/v4';
import { auditOutputsSchema } from './audit-output.js';
import { convertAsyncZodFunctionToSchema } from './implementation/function.js';
import { filePathSchema } from './implementation/schemas.js';
import { persistConfigSchema } from './persist-config.js';

export const outputTransformSchema = convertAsyncZodFunctionToSchema(
z.function({
Expand All @@ -25,6 +26,7 @@ export type RunnerConfig = z.infer<typeof runnerConfigSchema>;

export const runnerFunctionSchema = convertAsyncZodFunctionToSchema(
z.function({
input: [persistConfigSchema],
output: z.union([auditOutputsSchema, z.promise(auditOutputsSchema)]),
}),
);
Expand Down
9 changes: 9 additions & 0 deletions packages/nx-plugin/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@
]
}
},
"lint-reporter": {
"options": {
"lintFilePatterns": [
"packages/nx-plugin/**/*.ts",
"packages/nx-plugin/package.json",
"packages/nx-plugin/generators.json"
]
}
},
"unit-test": {},
"int-test": {}
},
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-coverage/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"targets": {
"build": {},
"lint": {},
"lint-reporter": {},
"unit-test": {},
"int-test": {}
},
Expand Down
Loading
Loading