Skip to content

Commit

Permalink
refactor(reporter): Add statistic to reporter interface
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrii Kucherenko committed Nov 15, 2018
1 parent 9112844 commit 4be729f
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 41 deletions.
110 changes: 107 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,29 @@ The jscpd tool implements [Rabin-Karp](https://en.wikipedia.org/wiki/Rabin%E2%80
- Generate JSON report
- Integrate with CI systems, use thresholds for level of duplications
- The powerful [API](docs/api.md) for extend functionality and usage

## What is new in jscpd v1.0.0?

- Powerful development [API](docs/api.md)
- Supports more formats (moved source code tokenizer from CodeMirror to Prism.js)
- Add blamed lines to JSON report
- Default config file is `.jscpd.json`, no more `.cpd.yaml`
- Detect different formats in one file, like js scripts in html tags
- Allow to use multiple cli options for parameters like `jscpd --ignore tests,build`
- Allow multiple paths for detection like `jscpd ./src ./tests ./docs`
- Statistic of detection
- Use patterns form `.gitignore` for ignoring detection

## Getting started

### Usage
```bash
$ npx jscpd@1.0.0-rc.3 /path/to/source
$ npx jscpd@1.0.0-rc.4 /path/to/source
```

or

```bash
$ npm install -g jscpd@1.0.0-rc.3
$ npm install -g jscpd@1.0.0-rc.4

$ jscpd /path/to/code
```
Expand Down Expand Up @@ -236,6 +247,99 @@ cpd.detectInFiles(['./src', './tests'])
[Progamming API](docs/api.md)


## Reporters

### PMD CPD XML
```xml
<?xml version="1.0" encoding="utf-8"?>
<pmd-cpd>
<duplication lines="10">
<file path="/path/to/file" line="1">
<codefragment><![CDATA[ ...first code fragment... ]]></codefragment>
</file>
<file path="/path/to/file" line="5">
<codefragment><![CDATA[ ...second code fragment...}]]></codefragment>
</file>
<codefragment><![CDATA[ ...duplicated fragment... ]]></codefragment>
</duplication>
</pmd-cpd>
```

### JSON reporters
```json
{
"duplications": [{
"format": "javascript",
"lines": 27,
"fragment": "...code fragment... ",
"tokens": 0,
"firstFile": {
"name": "tests/fixtures/javascript/file2.js",
"start": 1,
"end": 27,
"startLoc": {
"line": 1,
"column": 1
},
"endLoc": {
"line": 27,
"column": 2
}
},
"secondFile": {
"name": "tests/fixtures/javascript/file1.js",
"start": 1,
"end": 24,
"startLoc": {
"line": 1,
"column": 1
},
"endLoc": {
"line": 24,
"column": 2
}
}
}],
"statistic": {
"detectionDate": "2018-11-09T15:32:02.397Z",
"formats": {
"javascript": {
"sources": {
"/path/to/file": {
"lines": 24,
"sources": 1,
"clones": 1,
"duplicatedLines": 26,
"percentage": 45.33,
"newDuplicatedLines": 0,
"newClones": 0
}
},
"total": {
"lines": 297,
"sources": 1,
"clones": 1,
"duplicatedLines": 26,
"percentage": 45.33,
"newDuplicatedLines": 0,
"newClones": 0
}
}
},
"total": {
"lines": 297,
"sources": 6,
"clones": 5,
"duplicatedLines": 26,
"percentage": 45.33,
"newDuplicatedLines": 0,
"newClones": 0
},
"threshold": 10
}
}
```

## Contributors

This project exists thanks to all the people who contribute.
Expand Down
8 changes: 2 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
export {
MATCH_SOURCE_EVENT,
CLONE_FOUND_EVENT,
SOURCE_SKIPPED_EVENT,
END_EVENT
} from './events';
export { MATCH_SOURCE_EVENT, CLONE_FOUND_EVENT, SOURCE_SKIPPED_EVENT, END_EVENT } from './events';
export { IReporter } from './interfaces/reporter.interface';
export { IMode } from './interfaces/mode.type';
export { IOptions } from './interfaces/options.interface';
export { IClone } from './interfaces/clone.interface';
export { IStatistic } from './interfaces/statistic.interface';
export { JSCPD, getStoreManager } from './jscpd';
3 changes: 2 additions & 1 deletion src/interfaces/reporter.interface.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import EventEmitter = require('eventemitter3');
import { IClone } from './clone.interface';
import { IStatistic } from './statistic.interface';

export interface IReporter {
attach(eventEmitter: EventEmitter): void;
report(clones: IClone[]): void;
report(clones?: IClone[], statistic?: IStatistic): void;
}
6 changes: 4 additions & 2 deletions src/jscpd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import { IListener } from './interfaces/listener.interface';
import { IOptions } from './interfaces/options.interface';
import { IReporter } from './interfaces/reporter.interface';
import { ISourceOptions } from './interfaces/source-options.interface';
import { IStatistic } from './interfaces/statistic.interface';
import { IToken } from './interfaces/token/token.interface';
import { getRegisteredListeners, registerListenerByName } from './listeners';
import { getModeHandler } from './modes';
import { getRegisteredReporters, registerReportersByName } from './reporters';
import { SOURCES_DB } from './stores/models';
import { SOURCES_DB, STATISTIC_DB } from './stores/models';
import { StoreManager, StoresManager } from './stores/stores-manager';
import { createTokensMaps, tokenize } from './tokenizer';
import { getFormatByFile } from './tokenizer/formats';
Expand Down Expand Up @@ -194,8 +195,9 @@ export class JSCPD {
}

private generateReports(clones: IClone[]) {
const statistic: IStatistic = StoresManager.getStore(STATISTIC_DB).get(getOption('executionId', this.options));
Object.values(getRegisteredReporters()).map((reporter: IReporter) => {
reporter.report(clones);
reporter.report(clones, statistic);
});
}
}
17 changes: 7 additions & 10 deletions src/reporters/console.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
import Table from 'cli-table3';
import { bold, red } from 'colors/safe';
import { IOptions, IReporter } from '..';
import { IClone, IOptions, IReporter } from '..';
import { CLONE_FOUND_EVENT, JscpdEventEmitter } from '../events';
import { IClone } from '../interfaces/clone.interface';
import { IStatisticRow } from '../interfaces/statistic.interface';
import { STATISTIC_DB } from '../stores/models';
import { StoresManager } from '../stores/stores-manager';
import { IStatistic, IStatisticRow } from '../interfaces/statistic.interface';
import { getPathConsoleString, getSourceLocation } from '../utils';
import { getOption } from '../utils/options';

export class ConsoleReporter implements IReporter {
constructor(protected options: IOptions) {}
constructor(protected options: IOptions) {
}

public attach(eventEmitter: JscpdEventEmitter): void {
eventEmitter.on(CLONE_FOUND_EVENT, this.cloneFound.bind(this));
}

public report() {
const statistic = StoresManager.getStore(STATISTIC_DB).get(getOption('executionId', this.options));
public report(...args: [any, IStatistic]) {
const [, statistic]: [any, IStatistic] = args;
if (statistic) {
const table: any[] = new Table({
head: ['Format', 'Files analyzed', 'Total lines', 'Clones found', 'Duplicated lines', '%']
});
Object.keys(statistic.formats)
.filter(format => statistic.formats[format].sources as boolean)
.filter(format => statistic.formats[format].sources)
.forEach((format: string) => {
table.push(this.convertStatisticToArray(format, statistic.formats[format].total));
});
Expand Down
9 changes: 2 additions & 7 deletions src/reporters/json.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { writeFileSync } from 'fs';
import { ensureDirSync } from 'fs-extra';
import { IOptions, IReporter } from '..';
import { IClone, IOptions, IReporter } from '..';
import { IBlamedLines } from '../interfaces/blame.interface';
import { IClone } from '../interfaces/clone.interface';
import { IStatistic } from '../interfaces/statistic.interface';
import { ITokenLocation } from '../interfaces/token/token-location.interface';
import { STATISTIC_DB } from '../stores/models';
import { StoresManager } from '../stores/stores-manager';
import { getPath } from '../utils';
import { getOption } from '../utils/options';

Expand Down Expand Up @@ -48,9 +45,7 @@ export class JsonReporter implements IReporter {

public attach(): void {}

public report(clones: IClone[]): void {
const statistic: IStatistic = StoresManager.getStore(STATISTIC_DB).get(getOption('executionId', this.options));

public report(clones: IClone[], statistic: IStatistic): void {
if (statistic) {
this.json.statistics = statistic;
}
Expand Down
20 changes: 8 additions & 12 deletions src/reporters/silent.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
import { bold } from 'colors/safe';
import { IOptions, IReporter } from '..';
import { IClone, IReporter } from '..';
import { IStatistic } from '../interfaces/statistic.interface';
import { STATISTIC_DB } from '../stores/models';
import { StoresManager } from '../stores/stores-manager';
import { getOption } from '../utils/options';

export class SilentReporter implements IReporter {
constructor(private options: IOptions) {}

public attach(): void {}
public attach(): void {
}

public report() {
const statistic: IStatistic = StoresManager.getStore(STATISTIC_DB).get(getOption('executionId', this.options));
public report(clones: IClone[], statistic: IStatistic) {
if (statistic) {
console.log(
`Duplications detection: Found ${bold(statistic.total.clones.toString())} ` +
`exact clones with ${bold(statistic.total.duplicatedLines.toString())}(${statistic.total.percentage}%) ` +
`duplicated lines in ${bold(statistic.total.sources.toString())} ` +
`(${Object.keys(statistic.formats).length} formats) files.`
`Duplications detection: Found ${bold(clones.length.toString())} ` +
`exact clones with ${bold(statistic.total.duplicatedLines.toString())}(${statistic.total.percentage}%) ` +
`duplicated lines in ${bold(statistic.total.sources.toString())} ` +
`(${Object.keys(statistic.formats).length} formats) files.`
);
}
}
Expand Down

0 comments on commit 4be729f

Please sign in to comment.