Skip to content

Commit b2d953f

Browse files
Merge pull request #43 from studiometa/bugfix/syntax-error
[Bugfix] Handle syntax error
2 parents 6d2508a + 4d74ad9 commit b2d953f

24 files changed

+753
-199
lines changed

.github/workflows/tests.yml

+18-9
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,21 @@ jobs:
1111
unit:
1212
runs-on: macos-latest
1313
steps:
14-
- uses: actions/checkout@v4
15-
- uses: actions/setup-node@v4
16-
with:
17-
node-version: 20
18-
cache: npm
19-
- name: Install modules
20-
run: npm install
21-
- name: Run tests
22-
run: npm run test -- --coverage
14+
- uses: actions/checkout@v4
15+
- uses: actions/setup-node@v4
16+
with:
17+
node-version: 20
18+
cache: npm
19+
- name: Install modules
20+
run: npm install
21+
- name: Run tests
22+
run: npm run test -- --coverage --coverage-reporter lcov
23+
- name: Upload coverage to Codecov
24+
uses: codecov/codecov-action@v4
25+
with:
26+
files: ./coverage/lcov.info
27+
flags: unittests
28+
fail_ci_if_error: false
29+
verbose: true
30+
env:
31+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ node_modules/
44
/.data/
55
gl-prettier-codequality.json
66
/.eslintcache
7+
/coverage/

bin/cli.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ if (cmd) {
88
exec(cmd, async (error, stdout, stderr) => {
99
if (error) {
1010
console.log(error.message);
11-
await prettierFormatterGitLab(stderr || stdout);
11+
await prettierFormatterGitLab(stdout + stderr);
1212
process.exit(1);
1313
}
1414
});

package-lock.json

+23-23
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"CHANGELOG.md"
1818
],
1919
"scripts": {
20-
"test": "bun test test/",
20+
"test": "CI=true bun test test/",
2121
"lint": "eslint . --cache",
2222
"fix": "eslint . --cache --fix"
2323
},

src/formatters/diff.js

+19-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import chalk from 'chalk';
22
import { diffStringsUnified } from 'jest-diff';
3-
import getFileInfo from '../utils/get-file-info.js';
3+
import { getPrettierFileInfos } from '../utils/get-prettier-file-infos.js';
44

55
/**
66
* @typedef {import('../types.d.ts').FileInfo} FileInfo
@@ -9,21 +9,29 @@ import getFileInfo from '../utils/get-file-info.js';
99
/**
1010
* Format Prettier formatting errors in diff format.
1111
* @param {Partial<FileInfo>} fileInfo A file Prettier informations.
12+
* @returns {string}
1213
*/
13-
function formatDiff({ filename, input, output }) {
14-
console.log(`----------------------------------------------------
14+
function formatDiff({ filename, input, output, error } = {}) {
15+
let diff = `----------------------------------------------------
1516
${filename}
16-
----------------------------------------------------`);
17-
console.log(
18-
diffStringsUnified(input || '', output || '', {
17+
----------------------------------------------------
18+
`;
19+
20+
if (error) {
21+
diff += String(error);
22+
} else {
23+
diff += diffStringsUnified(input || '', output || '', {
1924
aColor: chalk.red,
2025
bColor: chalk.green,
2126
omitAnnotationLines: true,
2227
contextLines: 2,
2328
expand: false,
24-
}),
25-
);
26-
console.log('');
29+
});
30+
}
31+
32+
diff += '\n';
33+
34+
return diff;
2735
}
2836

2937
/**
@@ -32,6 +40,6 @@ ${filename}
3240
* @returns {Promise<void>}
3341
*/
3442
export async function diff(files) {
35-
const infos = await Promise.all(files.map(getFileInfo));
36-
infos.forEach(formatDiff);
43+
const infos = await Promise.all(files.map((file) => getPrettierFileInfos(file)));
44+
return infos.map((info) => formatDiff(info));
3745
}

src/formatters/gitlab.js

+20-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createHash } from 'node:crypto';
22
import { showInvisibles, generateDifferences } from 'prettier-linter-helpers';
3-
import getFileInfo from '../utils/get-file-info.js';
3+
import { getPrettierFileInfos } from '../utils/get-prettier-file-infos.js';
44

55
/**
66
* @typedef {import('../types.d.ts').FileInfo} FileInfo
@@ -23,10 +23,26 @@ function createFingerprint(...args) {
2323
/**
2424
* Format a file.
2525
* @param {Partial<FileInfo>} fileInfo
26-
* @returns {CodeQualityReport}
26+
* @returns {CodeQualityReport[]}
2727
*/
28-
function formatFile({ filename, input, output }) {
28+
function formatFile({ filename, input, output, error }) {
2929
const { INSERT, DELETE, REPLACE } = generateDifferences;
30+
31+
if (error) {
32+
return [
33+
{
34+
type: 'issue',
35+
check_name: 'prettier',
36+
description: String(error),
37+
severity: 'major',
38+
fingerprint: createFingerprint(filename, error.message),
39+
location: {
40+
path: filename,
41+
},
42+
},
43+
];
44+
}
45+
3046
const differences = generateDifferences(input, output);
3147

3248
return differences.map(({ offset, operation, deleteText = '', insertText = '' }) => {
@@ -74,6 +90,6 @@ function formatFile({ filename, input, output }) {
7490
* @returns {Promise<CodeQualityReport[]>}
7591
*/
7692
export async function gitlab(files) {
77-
const infos = await Promise.all(files.map(getFileInfo));
93+
const infos = await Promise.all(files.map((file) => getPrettierFileInfos(file)));
7894
return infos.reduce((acc, fileInfo) => [...acc, ...formatFile(fileInfo)], []);
7995
}

src/index.js

+3-18
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { join, resolve, dirname } from 'node:path';
55
import yaml from 'js-yaml';
66
import { diff } from './formatters/diff.js';
77
import { gitlab } from './formatters/gitlab.js';
8+
import { parse } from './utils/parse-prettier-results.js';
89

910
const {
1011
// Used as a fallback for local testing.
@@ -31,23 +32,6 @@ function getOutputPath() {
3132
return resolve(CI_PROJECT_DIR, location);
3233
}
3334

34-
/**
35-
* Parse the `prettier --list-different` output to format a GitLab Code Quality report JSON.
36-
* @param {string} results The output of a `prettier --list-different` failing command.
37-
* @returns {Array<object>}
38-
*/
39-
function parse(results) {
40-
return results
41-
.split('\n')
42-
.filter(
43-
(line) =>
44-
Boolean(line) &&
45-
!line.startsWith('Checking formatting...') &&
46-
!line.includes('Code style issues found'),
47-
)
48-
.map((line) => line.replace('[warn] ', ''));
49-
}
50-
5135
/**
5236
* Format Prettier results for GitLab Code Quality Reports.
5337
* @param {string} results
@@ -58,7 +42,8 @@ export async function prettierFormatterGitLab(results) {
5842
if (CI_JOB_NAME || PRETTIER_CODE_QUALITY_REPORT) {
5943
const files = parse(results);
6044

61-
const [data] = await Promise.all([gitlab(files), diff(files)]);
45+
const [data, diffs] = await Promise.all([gitlab(files), diff(files)]);
46+
diffs.forEach((diff) => console.log(diff));
6247
const outputPath = PRETTIER_CODE_QUALITY_REPORT || getOutputPath();
6348
const dir = dirname(outputPath);
6449
mkdirSync(dir, { recursive: true });

src/types.d.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
export interface FileInfo {
22
filename: string;
3-
input: string;
4-
output: string;
5-
isFormatted: boolean;
3+
input?: string;
4+
output?: string;
5+
isFormatted?: boolean;
6+
error?: Error;
67
}
78

89
export interface CodeQualityReport {

src/utils/get-file-info.js

-41
This file was deleted.

src/utils/get-prettier-file-infos.js

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { readFileSync } from 'node:fs';
2+
import { relative, dirname } from 'node:path';
3+
import {
4+
resolveConfig,
5+
resolveConfigFile,
6+
getFileInfo as prettierGetFileInfo,
7+
check,
8+
format,
9+
} from 'prettier';
10+
11+
/**
12+
* @typedef {import('../types.d.ts').FileInfo} FileInfo
13+
*/
14+
15+
/**
16+
* Get Prettier file informations.
17+
* @param {string} filePath The absolute path to the file.
18+
* @returns {Promise<FileInfo>} An object with Prettier informations.
19+
*/
20+
async function getFileInfos(filePath) {
21+
const input = readFileSync(filePath, 'utf8').toString();
22+
const [options, configPath] = await Promise.all([
23+
resolveConfig(filePath),
24+
resolveConfigFile(filePath),
25+
]);
26+
const filename = relative(dirname(configPath), filePath);
27+
const infos = await prettierGetFileInfo(filePath, options);
28+
29+
if (!options.parser) {
30+
options.parser = infos.inferredParser;
31+
}
32+
33+
try {
34+
const [isFormatted, output] = await Promise.all([
35+
check(input, options),
36+
format(input, options),
37+
]);
38+
39+
return {
40+
filename,
41+
input,
42+
output,
43+
isFormatted,
44+
};
45+
} catch (error) {
46+
return {
47+
filename,
48+
error,
49+
};
50+
}
51+
}
52+
53+
const cache = new Map();
54+
55+
/**
56+
* Get Prettier information for a given file.
57+
* @param {string} filePath The absolute path to the file.
58+
* @returns {Promise<FileInfo>}
59+
*/
60+
export async function getPrettierFileInfos(filePath) {
61+
if (!cache.has(filePath)) {
62+
const fileInfo = await getFileInfos(filePath);
63+
cache.set(filePath, fileInfo);
64+
}
65+
66+
return cache.get(filePath);
67+
}

0 commit comments

Comments
 (0)