mirror of
https://github.com/renovatebot/renovate.git
synced 2025-04-17 13:52:40 +00:00
fix(util): parse jsonc as jsonc (#35177)
This commit is contained in:
parent
c8baf9a270
commit
c1581761cb
3 changed files with 62 additions and 14 deletions
|
@ -27,6 +27,14 @@ const onlyJson5parsableString = `
|
||||||
"isMarried": false,
|
"isMarried": false,
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
const validJsoncString = `
|
||||||
|
{
|
||||||
|
// This is a comment
|
||||||
|
"name": "John Doe",
|
||||||
|
"age": 30,
|
||||||
|
"city": "New York"
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
describe('util/common', () => {
|
describe('util/common', () => {
|
||||||
beforeEach(() => hostRules.clear());
|
beforeEach(() => hostRules.clear());
|
||||||
|
@ -106,7 +114,7 @@ describe('util/common', () => {
|
||||||
expect(() => parseJson(invalidJsonString, 'renovate.json')).toThrow();
|
expect(() => parseJson(invalidJsonString, 'renovate.json')).toThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('catches and warns if content parsing faield with JSON.parse but not with JSON5.parse', () => {
|
it('catches and warns if content parsing failed with JSON.parse but not with JSON5.parse', () => {
|
||||||
expect(parseJson(onlyJson5parsableString, 'renovate.json')).toEqual({
|
expect(parseJson(onlyJson5parsableString, 'renovate.json')).toEqual({
|
||||||
name: 'Bob',
|
name: 'Bob',
|
||||||
age: 35,
|
age: 35,
|
||||||
|
@ -118,5 +126,29 @@ describe('util/common', () => {
|
||||||
'File contents are invalid JSON but parse using JSON5. Support for this will be removed in a future release so please change to a support .json5 file name or ensure correct JSON syntax.',
|
'File contents are invalid JSON but parse using JSON5. Support for this will be removed in a future release so please change to a support .json5 file name or ensure correct JSON syntax.',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not warn if filename ends with .jsonc', () => {
|
||||||
|
parseJson(validJsoncString, 'renovate.jsonc');
|
||||||
|
expect(logger.logger.warn).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not warn if filename ends with .json5', () => {
|
||||||
|
parseJson(onlyJson5parsableString, 'renovate.json5');
|
||||||
|
expect(logger.logger.warn).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parseJsonc', () => {
|
||||||
|
it('returns parsed jsonc', () => {
|
||||||
|
expect(parseJson(validJsoncString, 'renovate.jsonc')).toEqual({
|
||||||
|
name: 'John Doe',
|
||||||
|
age: 30,
|
||||||
|
city: 'New York',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for invalid jsonc', () => {
|
||||||
|
expect(() => parseJson(invalidJsonString, 'renovate.jsonc')).toThrow();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import JSON5 from 'json5';
|
import JSON5 from 'json5';
|
||||||
|
import * as JSONC from 'jsonc-parser';
|
||||||
|
import type { JsonValue } from 'type-fest';
|
||||||
import {
|
import {
|
||||||
BITBUCKET_API_USING_HOST_TYPES,
|
BITBUCKET_API_USING_HOST_TYPES,
|
||||||
BITBUCKET_SERVER_API_USING_HOST_TYPES,
|
BITBUCKET_SERVER_API_USING_HOST_TYPES,
|
||||||
|
@ -80,21 +82,27 @@ export function noLeadingAtSymbol(input: string): string {
|
||||||
return input.startsWith('@') ? input.slice(1) : input;
|
return input.startsWith('@') ? input.slice(1) : input;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseJson(content: string | null, filename: string): unknown {
|
export function parseJson(content: string | null, filename: string): JsonValue {
|
||||||
if (!content) {
|
if (!content) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return filename.endsWith('.json5')
|
if (filename.endsWith('.jsonc')) {
|
||||||
? JSON5.parse(content)
|
return parseJsonc(content);
|
||||||
: parseJsonWithFallback(content, filename);
|
}
|
||||||
|
|
||||||
|
if (filename.endsWith('.json5')) {
|
||||||
|
return JSON5.parse(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseJsonWithFallback(content, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseJsonWithFallback(
|
export function parseJsonWithFallback(
|
||||||
content: string,
|
content: string,
|
||||||
context: string,
|
context: string,
|
||||||
): unknown {
|
): JsonValue {
|
||||||
let parsedJson: unknown;
|
let parsedJson: JsonValue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
parsedJson = JSON.parse(content);
|
parsedJson = JSON.parse(content);
|
||||||
|
@ -108,3 +116,12 @@ export function parseJsonWithFallback(
|
||||||
|
|
||||||
return parsedJson;
|
return parsedJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parseJsonc(content: string): JsonValue {
|
||||||
|
const errors: JSONC.ParseError[] = [];
|
||||||
|
const value = JSONC.parse(content, errors, { allowTrailingComma: true });
|
||||||
|
if (errors.length === 0) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
throw new Error('Invalid JSONC');
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import JSON5 from 'json5';
|
import JSON5 from 'json5';
|
||||||
import * as JSONC from 'jsonc-parser';
|
|
||||||
import { DateTime } from 'luxon';
|
import { DateTime } from 'luxon';
|
||||||
import type { JsonArray, JsonValue } from 'type-fest';
|
import type { JsonArray, JsonValue } from 'type-fest';
|
||||||
import {
|
import {
|
||||||
|
@ -11,6 +10,7 @@ import {
|
||||||
} from 'zod';
|
} from 'zod';
|
||||||
import { logger } from '../logger';
|
import { logger } from '../logger';
|
||||||
import type { PackageDependency } from '../modules/manager/types';
|
import type { PackageDependency } from '../modules/manager/types';
|
||||||
|
import { parseJsonc } from './common';
|
||||||
import { parse as parseToml } from './toml';
|
import { parse as parseToml } from './toml';
|
||||||
import type { YamlOptions } from './yaml';
|
import type { YamlOptions } from './yaml';
|
||||||
import { parseSingleYaml, parseYaml } from './yaml';
|
import { parseSingleYaml, parseYaml } from './yaml';
|
||||||
|
@ -226,13 +226,12 @@ export const Json5 = z.string().transform((str, ctx): JsonValue => {
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Jsonc = z.string().transform((str, ctx): JsonValue => {
|
export const Jsonc = z.string().transform((str, ctx): JsonValue => {
|
||||||
const errors: JSONC.ParseError[] = [];
|
try {
|
||||||
const value = JSONC.parse(str, errors, { allowTrailingComma: true });
|
return parseJsonc(str);
|
||||||
if (errors.length === 0) {
|
} catch {
|
||||||
return value;
|
ctx.addIssue({ code: 'custom', message: 'Invalid JSONC' });
|
||||||
|
return z.NEVER;
|
||||||
}
|
}
|
||||||
ctx.addIssue({ code: 'custom', message: 'Invalid JSONC' });
|
|
||||||
return z.NEVER;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const UtcDate = z
|
export const UtcDate = z
|
||||||
|
|
Loading…
Add table
Reference in a new issue