@cspell/cspell-junit-reporter 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +110 -0
- package/dist/esm/CSpellJUnitReporterSettings.d.ts +24 -0
- package/dist/esm/CSpellJUnitReporterSettings.js +2 -0
- package/dist/esm/index.d.ts +9 -0
- package/dist/esm/index.js +68 -0
- package/dist/esm/utils/buildJUnitXml.d.ts +47 -0
- package/dist/esm/utils/buildJUnitXml.js +99 -0
- package/dist/esm/utils/escapeXml.d.ts +9 -0
- package/dist/esm/utils/escapeXml.js +27 -0
- package/dist/esm/utils/validateSettings.d.ts +6 -0
- package/dist/esm/utils/validateSettings.js +29 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 cspell
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# `@cspell/cspell-junit-reporter`
|
|
2
|
+
|
|
3
|
+
> CSpell reporter with JUnit XML output
|
|
4
|
+
|
|
5
|
+
Emits a [JUnit](https://github.com/testmoapp/junitxml) compatible XML report of a cspell run, suitable for
|
|
6
|
+
consumption by CI systems (GitHub Actions, GitLab, Jenkins, CircleCI, etc.) that understand the JUnit test
|
|
7
|
+
result format.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Install it as a development package in the repository that will use it.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install -SD @cspell/cspell-junit-reporter
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Using Command Line
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
cspell . --reporter @cspell/cspell-junit-reporter
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Using CSpell Configuration
|
|
26
|
+
|
|
27
|
+
Add this to `cspell.yaml`:
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
reporters: [['@cspell/cspell-junit-reporter', { outFile: 'cspell-junit.xml' }]]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
or `cspell.json`
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"reporters": [["@cspell/cspell-junit-reporter", { "outFile": "cspell-junit.xml" }]]
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Output format
|
|
42
|
+
|
|
43
|
+
`@cspell/cspell-junit-reporter` maps a cspell run onto JUnit XML as follows:
|
|
44
|
+
|
|
45
|
+
- one `<testsuites>` root element for the whole run
|
|
46
|
+
- one `<testsuite>` per file checked, named after the file
|
|
47
|
+
- one `<testcase>` per spelling issue found in a file, containing a `<failure>` with the misspelled word,
|
|
48
|
+
location, and message
|
|
49
|
+
- a file with no issues gets a single passing `<testcase>` so the suite is never empty
|
|
50
|
+
- a skipped file gets a single `<testcase>` containing a `<skipped>` element
|
|
51
|
+
- processing errors that are not tied to a specific spelling issue (for example, a bad config file) are
|
|
52
|
+
reported in a dedicated `cspell-errors` testsuite using `<error>` instead of `<failure>`
|
|
53
|
+
|
|
54
|
+
All file paths, words, and messages are escaped so the output is valid XML even when they contain
|
|
55
|
+
`& < > " '` characters.
|
|
56
|
+
|
|
57
|
+
Example output for a single file with one issue:
|
|
58
|
+
|
|
59
|
+
```xml
|
|
60
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
61
|
+
<testsuites name="cspell" tests="1" failures="1" errors="0" time="0.001">
|
|
62
|
+
<testsuite name="src/example.ts" tests="1" failures="1" errors="0" time="0.001">
|
|
63
|
+
<testcase name="teh (3:5)" classname="src/example.ts">
|
|
64
|
+
<failure type="spelling" message="Unknown word: "teh"">Unknown word: "teh"</failure>
|
|
65
|
+
</testcase>
|
|
66
|
+
</testsuite>
|
|
67
|
+
</testsuites>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Settings
|
|
71
|
+
|
|
72
|
+
Possible settings:
|
|
73
|
+
|
|
74
|
+
- `outFile` (default: stdout) - path for the XML file to emit
|
|
75
|
+
- `suiteName` (default: `cspell`) - name used for the root `<testsuites>` element
|
|
76
|
+
|
|
77
|
+
<details>
|
|
78
|
+
<summary>Reporter Settings</summary>
|
|
79
|
+
|
|
80
|
+
<!--- @@inject: src/CSpellJUnitReporterSettings.ts --->
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
/**
|
|
84
|
+
* cspell-junit-reporter settings type definition
|
|
85
|
+
*/
|
|
86
|
+
export type CSpellJUnitReporterSettings = {
|
|
87
|
+
/**
|
|
88
|
+
* Path to the output JUnit XML file.
|
|
89
|
+
*
|
|
90
|
+
* Relative paths are relative to the current working directory.
|
|
91
|
+
*
|
|
92
|
+
* Special values:
|
|
93
|
+
* - `stdout` - write the XML to `stdout`.
|
|
94
|
+
* - `stderr` - write the XML to `stderr`.
|
|
95
|
+
*
|
|
96
|
+
* @default stdout
|
|
97
|
+
*/
|
|
98
|
+
outFile?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Name to use for the root `<testsuites>` element.
|
|
101
|
+
*
|
|
102
|
+
* @default cspell
|
|
103
|
+
*/
|
|
104
|
+
suiteName?: string;
|
|
105
|
+
};
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
<!--- @@inject-end: src/CSpellJUnitReporterSettings.ts --->
|
|
109
|
+
|
|
110
|
+
</details>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cspell-junit-reporter settings type definition
|
|
3
|
+
*/
|
|
4
|
+
export type CSpellJUnitReporterSettings = {
|
|
5
|
+
/**
|
|
6
|
+
* Path to the output JUnit XML file.
|
|
7
|
+
*
|
|
8
|
+
* Relative paths are relative to the current working directory.
|
|
9
|
+
*
|
|
10
|
+
* Special values:
|
|
11
|
+
* - `stdout` - write the XML to `stdout`.
|
|
12
|
+
* - `stderr` - write the XML to `stderr`.
|
|
13
|
+
*
|
|
14
|
+
* @default stdout
|
|
15
|
+
*/
|
|
16
|
+
outFile?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Name to use for the root `<testsuites>` element.
|
|
19
|
+
*
|
|
20
|
+
* @default cspell
|
|
21
|
+
*/
|
|
22
|
+
suiteName?: string;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=CSpellJUnitReporterSettings.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CSpellReporter, ReporterConfiguration } from '@cspell/cspell-types';
|
|
2
|
+
import type { CSpellJUnitReporterSettings } from './CSpellJUnitReporterSettings.js';
|
|
3
|
+
type ReporterConsole = Pick<Console, 'log' | 'warn' | 'error'>;
|
|
4
|
+
export interface CSpellJUnitReporterConfiguration extends ReporterConfiguration {
|
|
5
|
+
console?: ReporterConsole;
|
|
6
|
+
}
|
|
7
|
+
export declare function getReporter(settings: unknown | CSpellJUnitReporterSettings, cliOptions?: CSpellJUnitReporterConfiguration): Required<CSpellReporter>;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import { buildJUnitXml } from './utils/buildJUnitXml.js';
|
|
4
|
+
import { validateSettings } from './utils/validateSettings.js';
|
|
5
|
+
function mkdirp(p) {
|
|
6
|
+
return fs.mkdir(p, { recursive: true });
|
|
7
|
+
}
|
|
8
|
+
const STDOUT = 'stdout';
|
|
9
|
+
const STDERR = 'stderr';
|
|
10
|
+
export function getReporter(settings, cliOptions) {
|
|
11
|
+
const useSettings = normalizeSettings(settings);
|
|
12
|
+
const console = cliOptions?.console ?? globalThis.console;
|
|
13
|
+
const files = new Map();
|
|
14
|
+
const errors = [];
|
|
15
|
+
const fileOrder = [];
|
|
16
|
+
function getOrCreateFile(filename) {
|
|
17
|
+
const existing = files.get(filename);
|
|
18
|
+
if (existing)
|
|
19
|
+
return existing;
|
|
20
|
+
const created = { filename, issues: [] };
|
|
21
|
+
files.set(filename, created);
|
|
22
|
+
fileOrder.push(filename);
|
|
23
|
+
return created;
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
issue: (issue) => {
|
|
27
|
+
const filename = issue.uri || '(unknown)';
|
|
28
|
+
getOrCreateFile(filename).issues.push(issue);
|
|
29
|
+
},
|
|
30
|
+
info: () => undefined,
|
|
31
|
+
debug: () => undefined,
|
|
32
|
+
error: (message, error) => {
|
|
33
|
+
errors.push({ message, error });
|
|
34
|
+
},
|
|
35
|
+
progress: (item) => {
|
|
36
|
+
if (item.type !== 'ProgressFileComplete')
|
|
37
|
+
return;
|
|
38
|
+
const file = getOrCreateFile(item.filename);
|
|
39
|
+
file.elapsedTimeMs = item.elapsedTimeMs;
|
|
40
|
+
file.processed = item.processed;
|
|
41
|
+
file.skippedReason = item.skippedReason;
|
|
42
|
+
},
|
|
43
|
+
result: async () => {
|
|
44
|
+
const orderedFiles = fileOrder.map((filename) => files.get(filename)).filter((f) => !!f);
|
|
45
|
+
const xml = buildJUnitXml(orderedFiles, errors, { suiteName: useSettings.suiteName || 'cspell' });
|
|
46
|
+
const outFile = useSettings.outFile || STDOUT;
|
|
47
|
+
if (outFile === STDOUT) {
|
|
48
|
+
console.log(xml);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (outFile === STDERR) {
|
|
52
|
+
console.error(xml);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const outFilePath = path.join(cliOptions?.root ?? process.cwd(), outFile);
|
|
56
|
+
await mkdirp(path.dirname(outFilePath));
|
|
57
|
+
return fs.writeFile(outFilePath, xml);
|
|
58
|
+
},
|
|
59
|
+
features: undefined,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function normalizeSettings(settings) {
|
|
63
|
+
if (settings === undefined)
|
|
64
|
+
return { outFile: STDOUT, suiteName: 'cspell' };
|
|
65
|
+
validateSettings(settings);
|
|
66
|
+
return settings;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ErrorLike, Issue } from '@cspell/cspell-types';
|
|
2
|
+
/**
|
|
3
|
+
* A single file that was checked by cspell.
|
|
4
|
+
*
|
|
5
|
+
* Maps to one `<testsuite>` element. Each spelling {@link Issue} found in the
|
|
6
|
+
* file maps to one `<testcase>` containing a `<failure>`. A file with no
|
|
7
|
+
* issues is reported as a single passing `<testcase>` so the suite is never
|
|
8
|
+
* empty (this mirrors the convention used by other JUnit reporters, e.g. the
|
|
9
|
+
* ESLint JUnit formatter).
|
|
10
|
+
*/
|
|
11
|
+
export interface FileReport {
|
|
12
|
+
/** File name/uri as reported by cspell. */
|
|
13
|
+
filename: string;
|
|
14
|
+
/** Spelling issues found in the file. */
|
|
15
|
+
issues: Issue[];
|
|
16
|
+
/** Time, in milliseconds, spent processing the file (if known). */
|
|
17
|
+
elapsedTimeMs?: number | undefined;
|
|
18
|
+
/** `false` if the file was skipped (not processed). */
|
|
19
|
+
processed?: boolean | undefined;
|
|
20
|
+
/** Reason the file was skipped, if applicable. */
|
|
21
|
+
skippedReason?: string | undefined;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A processing error reported by cspell that is not tied to a specific
|
|
25
|
+
* spelling issue (e.g. a bad config file or an unreadable file).
|
|
26
|
+
*/
|
|
27
|
+
export interface ErrorReport {
|
|
28
|
+
message: string;
|
|
29
|
+
error: ErrorLike;
|
|
30
|
+
}
|
|
31
|
+
export interface JUnitXmlOptions {
|
|
32
|
+
/** Name for the root `<testsuites>` element. */
|
|
33
|
+
suiteName: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Builds a JUnit compatible XML document from the results of a cspell run.
|
|
37
|
+
*
|
|
38
|
+
* Mapping:
|
|
39
|
+
* - one `<testsuites>` root for the whole run
|
|
40
|
+
* - one `<testsuite>` per file checked
|
|
41
|
+
* - one `<testcase>` per spelling issue found in a file, containing a `<failure>`
|
|
42
|
+
* - a single passing `<testcase>` for files with no issues
|
|
43
|
+
* - processing errors (not tied to a spelling issue) are reported in a
|
|
44
|
+
* dedicated `cspell-errors` testsuite using `<error>` instead of `<failure>`
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildJUnitXml(files: FileReport[], errors: ErrorReport[], options: JUnitXmlOptions): string;
|
|
47
|
+
//# sourceMappingURL=buildJUnitXml.d.ts.map
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { IssueType } from '@cspell/cspell-types';
|
|
2
|
+
import { escapeXmlAttribute, escapeXmlText } from './escapeXml.js';
|
|
3
|
+
function formatTime(ms) {
|
|
4
|
+
return ((ms ?? 0) / 1000).toFixed(3);
|
|
5
|
+
}
|
|
6
|
+
function issueName(issue) {
|
|
7
|
+
const location = `${issue.row}:${issue.col}`;
|
|
8
|
+
return `${issue.text} (${location})`;
|
|
9
|
+
}
|
|
10
|
+
function issueMessage(issue) {
|
|
11
|
+
if (issue.message)
|
|
12
|
+
return issue.message;
|
|
13
|
+
return `Unknown word: "${issue.text}"`;
|
|
14
|
+
}
|
|
15
|
+
function buildTestCaseForIssue(filename, issue) {
|
|
16
|
+
const name = escapeXmlAttribute(issueName(issue));
|
|
17
|
+
const classname = escapeXmlAttribute(filename);
|
|
18
|
+
const failureType = escapeXmlAttribute(issue.issueType === IssueType.directive ? 'directive' : 'spelling');
|
|
19
|
+
const message = escapeXmlAttribute(issueMessage(issue));
|
|
20
|
+
const body = escapeXmlText(issueMessage(issue));
|
|
21
|
+
return (` <testcase name="${name}" classname="${classname}">\n` +
|
|
22
|
+
` <failure type="${failureType}" message="${message}">${body}</failure>\n` +
|
|
23
|
+
` </testcase>\n`);
|
|
24
|
+
}
|
|
25
|
+
function buildPassingTestCase(filename) {
|
|
26
|
+
const classname = escapeXmlAttribute(filename);
|
|
27
|
+
return ` <testcase name="no issues found" classname="${classname}"/>\n`;
|
|
28
|
+
}
|
|
29
|
+
function buildSkippedTestCase(filename, reason) {
|
|
30
|
+
const classname = escapeXmlAttribute(filename);
|
|
31
|
+
const message = reason ? ` message="${escapeXmlAttribute(reason)}"` : '';
|
|
32
|
+
return ` <testcase name="skipped" classname="${classname}">\n <skipped${message}/>\n </testcase>\n`;
|
|
33
|
+
}
|
|
34
|
+
function fileSuiteCounts(file) {
|
|
35
|
+
if (file.processed === false)
|
|
36
|
+
return { tests: 1, failures: 0 };
|
|
37
|
+
const issueCount = file.issues.length;
|
|
38
|
+
return { tests: Math.max(issueCount, 1), failures: issueCount };
|
|
39
|
+
}
|
|
40
|
+
function buildTestSuiteForFile(file) {
|
|
41
|
+
const name = escapeXmlAttribute(file.filename);
|
|
42
|
+
const time = formatTime(file.elapsedTimeMs);
|
|
43
|
+
let testCases;
|
|
44
|
+
if (file.processed === false) {
|
|
45
|
+
testCases = buildSkippedTestCase(file.filename, file.skippedReason);
|
|
46
|
+
}
|
|
47
|
+
else if (file.issues.length) {
|
|
48
|
+
testCases = file.issues.map((issue) => buildTestCaseForIssue(file.filename, issue)).join('');
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
testCases = buildPassingTestCase(file.filename);
|
|
52
|
+
}
|
|
53
|
+
const { tests, failures } = fileSuiteCounts(file);
|
|
54
|
+
return (` <testsuite name="${name}" tests="${tests}" failures="${failures}" errors="0" time="${time}">\n` +
|
|
55
|
+
testCases +
|
|
56
|
+
` </testsuite>\n`);
|
|
57
|
+
}
|
|
58
|
+
function buildTestSuiteForErrors(errors) {
|
|
59
|
+
const testCases = errors
|
|
60
|
+
.map(({ message, error }) => {
|
|
61
|
+
const name = escapeXmlAttribute(message);
|
|
62
|
+
const errorMessage = escapeXmlAttribute(error.message);
|
|
63
|
+
const body = escapeXmlText(error.message);
|
|
64
|
+
return (` <testcase name="${name}" classname="cspell">\n` +
|
|
65
|
+
` <error type="error" message="${errorMessage}">${body}</error>\n` +
|
|
66
|
+
` </testcase>\n`);
|
|
67
|
+
})
|
|
68
|
+
.join('');
|
|
69
|
+
return (` <testsuite name="cspell-errors" tests="${errors.length}" failures="0" errors="${errors.length}" time="0.000">\n` +
|
|
70
|
+
testCases +
|
|
71
|
+
` </testsuite>\n`);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Builds a JUnit compatible XML document from the results of a cspell run.
|
|
75
|
+
*
|
|
76
|
+
* Mapping:
|
|
77
|
+
* - one `<testsuites>` root for the whole run
|
|
78
|
+
* - one `<testsuite>` per file checked
|
|
79
|
+
* - one `<testcase>` per spelling issue found in a file, containing a `<failure>`
|
|
80
|
+
* - a single passing `<testcase>` for files with no issues
|
|
81
|
+
* - processing errors (not tied to a spelling issue) are reported in a
|
|
82
|
+
* dedicated `cspell-errors` testsuite using `<error>` instead of `<failure>`
|
|
83
|
+
*/
|
|
84
|
+
export function buildJUnitXml(files, errors, options) {
|
|
85
|
+
const suiteName = escapeXmlAttribute(options.suiteName);
|
|
86
|
+
const fileSuites = files.map(buildTestSuiteForFile);
|
|
87
|
+
const errorSuite = errors.length ? [buildTestSuiteForErrors(errors)] : [];
|
|
88
|
+
const suites = [...fileSuites, ...errorSuite];
|
|
89
|
+
const fileCounts = files.map(fileSuiteCounts);
|
|
90
|
+
const totalTests = fileCounts.reduce((sum, counts) => sum + counts.tests, errors.length);
|
|
91
|
+
const totalFailures = fileCounts.reduce((sum, counts) => sum + counts.failures, 0);
|
|
92
|
+
const totalErrors = errors.length;
|
|
93
|
+
const totalTime = files.reduce((sum, file) => sum + (file.elapsedTimeMs ?? 0), 0) / 1000;
|
|
94
|
+
return ('<?xml version="1.0" encoding="UTF-8"?>\n' +
|
|
95
|
+
`<testsuites name="${suiteName}" tests="${totalTests}" failures="${totalFailures}" errors="${totalErrors}" time="${totalTime.toFixed(3)}">\n` +
|
|
96
|
+
suites.join('') +
|
|
97
|
+
'</testsuites>\n');
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=buildJUnitXml.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escapes text so it is safe to embed inside an XML element's text content.
|
|
3
|
+
*/
|
|
4
|
+
export declare function escapeXmlText(value: string): string;
|
|
5
|
+
/**
|
|
6
|
+
* Escapes text so it is safe to embed inside a double-quoted XML attribute value.
|
|
7
|
+
*/
|
|
8
|
+
export declare function escapeXmlAttribute(value: string): string;
|
|
9
|
+
//# sourceMappingURL=escapeXml.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Control characters that should not appear literally in XML 1.0 output
|
|
2
|
+
// (tab \t, newline \n, and carriage return \r are allowed).
|
|
3
|
+
// eslint-disable-next-line no-control-regex
|
|
4
|
+
const invalidXmlChars = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F]/g;
|
|
5
|
+
/**
|
|
6
|
+
* Escapes text so it is safe to embed inside an XML element's text content.
|
|
7
|
+
*/
|
|
8
|
+
export function escapeXmlText(value) {
|
|
9
|
+
return value
|
|
10
|
+
.replaceAll(invalidXmlChars, '')
|
|
11
|
+
.replaceAll('&', '&')
|
|
12
|
+
.replaceAll('<', '<')
|
|
13
|
+
.replaceAll('>', '>');
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Escapes text so it is safe to embed inside a double-quoted XML attribute value.
|
|
17
|
+
*/
|
|
18
|
+
export function escapeXmlAttribute(value) {
|
|
19
|
+
return value
|
|
20
|
+
.replaceAll(invalidXmlChars, '')
|
|
21
|
+
.replaceAll('&', '&')
|
|
22
|
+
.replaceAll('<', '<')
|
|
23
|
+
.replaceAll('>', '>')
|
|
24
|
+
.replaceAll('"', '"')
|
|
25
|
+
.replaceAll("'", '''); // cspell:ignore apos
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=escapeXml.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CSpellJUnitReporterSettings } from '../CSpellJUnitReporterSettings.js';
|
|
2
|
+
/**
|
|
3
|
+
* Throws an error if passed cspell-junit-reporter settings are invalid
|
|
4
|
+
*/
|
|
5
|
+
export declare function validateSettings(settings: unknown): asserts settings is CSpellJUnitReporterSettings;
|
|
6
|
+
//# sourceMappingURL=validateSettings.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AssertionError } from 'node:assert';
|
|
2
|
+
/**
|
|
3
|
+
* Throws an error if passed cspell-junit-reporter settings are invalid
|
|
4
|
+
*/
|
|
5
|
+
export function validateSettings(settings) {
|
|
6
|
+
if (!settings || typeof settings !== 'object' || Array.isArray(settings)) {
|
|
7
|
+
throw new AssertionError({
|
|
8
|
+
message: 'cspell-junit-reporter settings must be an object',
|
|
9
|
+
actual: typeof settings,
|
|
10
|
+
expected: 'object',
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
const { outFile = 'stdout', suiteName = 'cspell' } = settings;
|
|
14
|
+
if (typeof outFile !== 'string') {
|
|
15
|
+
throw new AssertionError({
|
|
16
|
+
message: 'cspell-junit-reporter settings.outFile must be a string',
|
|
17
|
+
actual: typeof outFile,
|
|
18
|
+
expected: 'string',
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
if (typeof suiteName !== 'string') {
|
|
22
|
+
throw new AssertionError({
|
|
23
|
+
message: 'cspell-junit-reporter settings.suiteName must be a string',
|
|
24
|
+
actual: typeof suiteName,
|
|
25
|
+
expected: 'string',
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=validateSettings.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cspell/cspell-junit-reporter",
|
|
3
|
+
"version": "10.1.0",
|
|
4
|
+
"description": "JUnit XML reporter for CSpell",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cspell",
|
|
7
|
+
"spelling",
|
|
8
|
+
"reporter",
|
|
9
|
+
"junit",
|
|
10
|
+
"junit-xml",
|
|
11
|
+
"ci"
|
|
12
|
+
],
|
|
13
|
+
"author": "Jason Dent",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/streetsidesoftware/cspell/labels/cspell-junit-reporter"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/streetsidesoftware/cspell/tree/main/packages/cspell-junit-reporter#readme",
|
|
19
|
+
"type": "module",
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"types": "dist/esm/index.d.ts",
|
|
22
|
+
"module": "dist/esm/index.js",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"import": "./dist/esm/index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"!**/*.tsbuildInfo",
|
|
31
|
+
"!**/__mocks__",
|
|
32
|
+
"!**/test/**",
|
|
33
|
+
"!**/*.test.*",
|
|
34
|
+
"!**/*.spec.*",
|
|
35
|
+
"!**/*.map"
|
|
36
|
+
],
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public",
|
|
39
|
+
"provenance": true
|
|
40
|
+
},
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/streetsidesoftware/cspell.git",
|
|
44
|
+
"directory": "packages/cspell-junit-reporter"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"clean": "shx rm -rf dist temp coverage \"*.tsbuildInfo\"",
|
|
48
|
+
"build": "tsc -b . -f",
|
|
49
|
+
"build:esm": "tsc -p tsconfig.esm.json",
|
|
50
|
+
"clean-build": "pnpm run clean && pnpm run build",
|
|
51
|
+
"coverage": "vitest run --coverage",
|
|
52
|
+
"test:watch": "vitest",
|
|
53
|
+
"test": "vitest run && pnpm test:run",
|
|
54
|
+
"test:run": "node ../../bin.mjs . --config cSpell.example.mjs",
|
|
55
|
+
"watch": "tsc -b . -w -f"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@cspell/cspell-types": "10.1.0"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=22.18.0"
|
|
62
|
+
},
|
|
63
|
+
"gitHead": "401518c5e37c06fb406aedadb3fc7a1c27eca8e6"
|
|
64
|
+
}
|