Skip to content

Commit 5af8bd1

Browse files
authored
fix: Fix option parsing and types (#258)
The option types wrongly declared `jsonSortOrder` as a required option, resulting in a type mismatch. If no `jsonSortOrder` was specified, it would be typed as `Record<..>` despite having the value `null`. This had no runtime functional impact but it was still wrong and confusing. The option parsing has been updated to better preserve type safety, and the types have been updated to differentiate pre-processed options, processed options, and options with defaults applied at runtime.
1 parent f835976 commit 5af8bd1

File tree

1 file changed

+58
-26
lines changed

1 file changed

+58
-26
lines changed

src/index.ts

+58-26
Original file line numberDiff line numberDiff line change
@@ -253,54 +253,68 @@ function sortAst(
253253
* JSON sorting options. See README for details.
254254
*/
255255
type SortJsonOptions = {
256-
jsonRecursiveSort: boolean;
257-
jsonSortOrder: Record<string, CategorySort | null>;
256+
jsonRecursiveSort?: boolean;
257+
jsonSortOrder?: Record<string, CategorySort | null>;
258258
};
259259

260+
/**
261+
* JSON sorting options after they have been processed by Prettier.
262+
*
263+
* Defaults set in the 'options' export have been applied by this point.
264+
*/
265+
type ProcessedSortJsonOptions = Omit<SortJsonOptions, 'jsonRecursiveSort'> &
266+
Required<Pick<SortJsonOptions, 'jsonRecursiveSort'>>;
267+
260268
/**
261269
* Parse JSON sort options from Prettier options.
262270
*
263271
* @param prettierOptions - Prettier options.
264272
* @returns JSON sort options.
265273
*/
266-
function parseOptions(prettierOptions: ParserOptions): SortJsonOptions {
267-
const { jsonRecursiveSort } = prettierOptions;
268-
274+
function parseOptions(
275+
prettierOptions: ParserOptions,
276+
): ProcessedSortJsonOptions {
269277
// Unreachable, validated before here by Prettier
270278
/* c8 ignore start */
271-
if (typeof jsonRecursiveSort !== 'boolean') {
279+
if (typeof prettierOptions.jsonRecursiveSort !== 'boolean') {
272280
throw new Error(
273281
`Invalid 'jsonRecursiveSort' option; expected boolean, got '${typeof prettierOptions.jsonRecursiveSort}'`,
274282
);
275283
}
276284
/* c8 ignore stop */
285+
const parsedJsonSortOptions: ProcessedSortJsonOptions = {
286+
jsonRecursiveSort: prettierOptions.jsonRecursiveSort,
287+
};
277288

278-
const rawJsonSortOrder = prettierOptions.jsonSortOrder ?? null;
279-
// Unreachable, validated before here by Prettier
280-
/* c8 ignore start */
281-
if (rawJsonSortOrder !== null && typeof rawJsonSortOrder !== 'string') {
282-
throw new Error(
283-
`Invalid 'jsonSortOrder' option; expected string, got '${typeof prettierOptions.rawJsonSortOrder}'`,
284-
);
285-
}
286-
/* c8 ignore stop */
289+
if ('jsonSortOrder' in prettierOptions) {
290+
const rawJsonSortOrder = prettierOptions.jsonSortOrder;
291+
// Unreachable, validated before here by Prettier
292+
/* c8 ignore start */
293+
if (typeof rawJsonSortOrder !== 'string') {
294+
throw new Error(
295+
`Invalid 'jsonSortOrder' option; expected string, got '${typeof prettierOptions.rawJsonSortOrder}'`,
296+
);
297+
}
298+
/* c8 ignore stop */
287299

288-
let jsonSortOrder = null;
289-
if (rawJsonSortOrder !== null) {
300+
let parsedJsonSortOrder;
290301
try {
291-
jsonSortOrder = JSON.parse(rawJsonSortOrder);
302+
parsedJsonSortOrder = JSON.parse(rawJsonSortOrder);
292303
} catch (error) {
293304
// @ts-expect-error Error cause property not yet supported by '@types/node' (see https://github.com/DefinitelyTyped/DefinitelyTyped/pull/61827)
294305
throw new Error(`Failed to parse sort order option as JSON`, {
295306
cause: error,
296307
});
297308
}
298309

299-
if (Array.isArray(jsonSortOrder) || typeof jsonSortOrder !== 'object') {
310+
if (
311+
Array.isArray(parsedJsonSortOrder) ||
312+
typeof parsedJsonSortOrder !== 'object'
313+
) {
300314
throw new Error(`Invalid custom sort order; must be an object`);
301315
}
302316

303-
for (const categorySort of Object.values(jsonSortOrder)) {
317+
for (const categorySort of Object.values(parsedJsonSortOrder)) {
304318
if (
305319
(categorySort !== null && typeof categorySort !== 'string') ||
306320
!allowedCategorySortValues.includes(categorySort)
@@ -312,9 +326,28 @@ function parseOptions(prettierOptions: ParserOptions): SortJsonOptions {
312326
);
313327
}
314328
}
329+
330+
parsedJsonSortOptions.jsonSortOrder = parsedJsonSortOrder;
315331
}
316332

317-
return { jsonRecursiveSort, jsonSortOrder };
333+
return parsedJsonSortOptions;
334+
}
335+
336+
/**
337+
* Apply default sort options.
338+
*
339+
* @param options - JSON sort options as configured.
340+
* @returns JSON sort options with defaults applied.
341+
*/
342+
function applyDefaultOptions(
343+
options: ProcessedSortJsonOptions,
344+
): Required<ProcessedSortJsonOptions> {
345+
const { jsonRecursiveSort, jsonSortOrder } = options;
346+
347+
return {
348+
jsonRecursiveSort, // Default already applied by Prettier
349+
jsonSortOrder: jsonSortOrder ?? {},
350+
};
318351
}
319352

320353
/**
@@ -395,7 +428,9 @@ function createParser(
395428
parser: JsonParser,
396429
): (text: string, options: ParserOptions) => Promise<any> {
397430
return async (text: string, prettierOptions: ParserOptions): Promise<any> => {
398-
const { jsonRecursiveSort, jsonSortOrder } = parseOptions(prettierOptions);
431+
const { jsonRecursiveSort, jsonSortOrder } = applyDefaultOptions(
432+
parseOptions(prettierOptions),
433+
);
399434

400435
const jsonRootAst = await babelParsers[parser].parse(text, prettierOptions);
401436

@@ -415,10 +450,7 @@ function createParser(
415450
return jsonRootAst;
416451
}
417452

418-
let sortCompareFunction: (a: string, b: string) => number = lexicalSort;
419-
if (jsonSortOrder) {
420-
sortCompareFunction = createSortCompareFunction(jsonSortOrder);
421-
}
453+
const sortCompareFunction = createSortCompareFunction(jsonSortOrder);
422454
const sortedAst = sortAst(ast, jsonRecursiveSort, sortCompareFunction);
423455

424456
return {

0 commit comments

Comments
 (0)