Skip to content

Instantly share code, notes, and snippets.

@webstrand
Created March 4, 2023 22:29
Show Gist options
  • Select an option

  • Save webstrand/de518199f8ff118e61e3c6dfa6f29beb to your computer and use it in GitHub Desktop.

Select an option

Save webstrand/de518199f8ff118e61e3c6dfa6f29beb to your computer and use it in GitHub Desktop.
const tsconfig = {
include: ["lib/**/*.ts", "lib/packages/**/*.ts", "lib/index.ts"],
exclude: ["src"],
files: ["lib/index.ts"],
typeAcquisition: {
enable: true,
disableFilenameBasedTypeAcquisition: true,
},
compilerOptions: {
allowUnreachableCode: true,
allowUnusedLabels: true,
exactOptionalPropertyTypes: true,
noImplicitAny: true,
noImplicitOverride: true,
noImplicitThis: false,
noPropertyAccessFromIndexSignature: false,
noUncheckedIndexedAccess: false,
noUnusedLocals: true,
noUnusedParameters: true,
strict: true,
allowUmdGlobalAccess: false,
baseUrl: "./lib",
module: "ESNext",
moduleResolution: "node",
paths: {
"@utils/*": ["utils/*"],
"@test-helpers/*": ["packages/__test__/*"],
},
resolveJsonModule: true,
types: ["node", "jest"],
typeRoots: ["lib/packages/__test__/jest-ext.d.ts", "node_modules/@types"],
declaration: false,
outDir: "dist/tsc",
preserveConstEnums: true,
stripInternal: true,
checkJs: true,
plugins: [
{
name: "typescript-eslint-language-service",
},
{
transform: "typescript-transform-paths",
},
{
transform: "typescript-transform-paths",
afterDeclarations: true,
},
],
allowSyntheticDefaultImports: false,
esModuleInterop: false,
forceConsistentCasingInFileNames: true,
preserveSymlinks: true,
lib: ["esnext", "dom"],
target: "esnext",
explainFiles: false,
extendedDiagnostics: true,
listEmittedFiles: true,
listFiles: true,
traceResolution: false,
composite: false,
incremental: false,
},
};
const sortorder = `
Top Level: compilerOptions, files, extends, include, exclude, references, watchOptions, typeAcquisition
Type Checking: allowUnreachableCode, allowUnusedLabels, alwaysStrict, exactOptionalPropertyTypes, noFallthroughCasesInSwitch, noImplicitAny, noImplicitOverride, noImplicitReturns, noImplicitThis, noPropertyAccessFromIndexSignature, noUncheckedIndexedAccess, noUnusedLocals, noUnusedParameters, strict, strictBindCallApply, strictFunctionTypes, strictNullChecks, strictPropertyInitialization, useUnknownInCatchVariables
Modules: allowUmdGlobalAccess, baseUrl, module, moduleResolution, moduleSuffixes, noResolve, paths, resolveJsonModule, rootDir, rootDirs, typeRoots, types
Emit: declaration, declarationDir, declarationMap, downlevelIteration, emitBOM, emitDeclarationOnly, importHelpers, importsNotUsedAsValues, inlineSourceMap, inlineSources, mapRoot, newLine, noEmit, noEmitHelpers, noEmitOnError, outDir, outFile, preserveConstEnums, preserveValueImports, removeComments, sourceMap, sourceRoot, stripInternal
JavaScript Support: allowJs, checkJs, maxNodeModuleJsDepth
Editor Support: disableSizeLimit, plugins
Interop Constraints: allowSyntheticDefaultImports, esModuleInterop, forceConsistentCasingInFileNames, isolatedModules, preserveSymlinks
Backwards Compatibility: charset, keyofStringsOnly, noImplicitUseStrict, noStrictGenericChecks, out, suppressExcessPropertyErrors, suppressImplicitAnyIndexErrors
Language, Environment: emitDecoratorMetadata, experimentalDecorators, jsx, jsxFactory, jsxFragmentFactory, jsxImportSource, lib, moduleDetection, noLib, reactNamespace, target, useDefineForClassFields
Compiler Diagnostics: diagnostics, explainFiles, extendedDiagnostics, generateCpuProfile, listEmittedFiles, listFiles, traceResolution
Projects: composite, disableReferencedProjectLoad, disableSolutionSearching, disableSourceOfProjectReferenceRedirect, incremental, tsBuildInfoFile
Output Formatting: noErrorTruncation, preserveWatchOutput, pretty
Completeness: skipDefaultLibCheck, skipLibCheck
Watch Options: assumeChangesOnlyAffectDirectDependencies
watchOptions: watchFile, watchDirectory, fallbackPolling, synchronousWatchDirectory, excludeDirectories, excludeFiles
typeAcquisition: enable, include, exclude, disableFilenameBasedTypeAcquisition
`
.split(/\s*\n\s*/)
.filter((x) => x)
.map((group) =>
(([label, opts]) => ({
group: label,
sort: opts.split(/[,\s]+/).filter((opt) => opt),
}))(group.split(/\s*:\s*/) as [string, string])
);
console.log(sortorder);
const defaults = {
// Top Level
compilerOptions: {
//
resolvePackageJsonExports: false,
resolvePackageJsonImports: false,
// Type Checking
exactOptionalPropertyTypes: false,
noFallthroughCasesInSwitch: false,
noImplicitOverride: false,
noImplicitReturns: false,
noUnusedLocals: false,
noUnusedParameters: false,
strict: false,
strictBindCallApply: false,
strictFunctionTypes: false,
strictNullChecks: false,
strictPropertyInitialization: false,
useUnknownInCatchVariables: false,
// Modules
allowUmdGlobalAccess: false,
moduleResolution: "classic",
noResolve: false,
resolveJsonModule: false,
// Emit
declaration: false,
declarationMap: false,
downlevelIteration: false,
emitBOM: false,
emitDeclarationOnly: false,
importHelpers: false,
importsNotUsedAsValues: "remove",
inlineSourceMap: false,
inlineSources: false,
noEmit: false,
noEmitHelpers: false,
noEmitOnError: false,
preserveConstEnums: false,
preserveValueImports: false,
removeComments: false,
sourceMap: false,
// JavaScript Support
allowJs: false,
checkJs: false,
maxNodeModuleJsDepth: 0,
// Editor Support
disableSizeLimit: false,
// Interop Constraints
esModuleInterop: false,
forceConsistentCasingInFileNames: false,
isolatedModules: false,
preserveSymlinks: false,
// Backwards Compatibility
keyofStringsOnly: false,
noImplicitUseStrict: false,
noStrictGenericChecks: false,
suppressExcessPropertyErrors: false,
suppressImplicitAnyIndexErrors: false,
// Language, Environment
jsxFactory: "React.createElement",
jsxFragmentFactory: "React.Fragment",
jsxImportSource: "react",
noLib: false,
reactNamespace: "React",
target: "ES3",
useDefineForClassFields: false,
// Compiler Diagnostics
extendedDiagnostics: false,
generateCpuProfile: "profile.cpuprofile",
listEmittedFiles: false,
listFiles: false,
traceResolution: false,
// Projects
composite: true,
tsBuildInfoFile: ".tsbuildinfo",
// Output Formatting
noErrorTruncation: false,
pretty: true,
// Completeness
skipDefaultLibCheck: false,
skipLibCheck: false,
// watchOptions
watchFile: "useFsEvents",
watchDirectory: "useFsEvents",
},
};
type Json = string | number | boolean | null | { [key: string]: Json } | Json[];
type SortableJson =
| string
| number
| boolean
| null
| { entries: readonly (readonly [string, SortableJson])[] }
| readonly SortableJson[];
type GroupedSortableJson =
| string
| number
| boolean
| null
| {
entries: readonly (readonly (readonly [
string,
GroupedSortableJson,
number,
number
])[])[];
}
| readonly GroupedSortableJson[];
function makeSortable(u: Json): SortableJson {
if (typeof u === "string") return u;
else if (typeof u === "number") return u;
else if (typeof u === "boolean") return u;
else if (typeof u === "object") {
if (u === null) return null;
if (u instanceof Array) return u.map((v) => makeSortable(v));
return {
entries: Array.from(Object.entries(u), ([key, v]) => [
key,
makeSortable(v),
]),
};
}
throw new Error(`unknown json ${u} ${typeof u}`);
}
function writeSortable(u: GroupedSortableJson, i = ""): string {
if (typeof u === "string") return JSON.stringify(u);
else if (typeof u === "number") return JSON.stringify(u);
else if (typeof u === "boolean") return JSON.stringify(u);
else if (typeof u === "object") {
if (u === null) return "null";
else if (u instanceof Array)
return `[\n${u
.map((v) => `${i}\t${writeSortable(v, i + "\t")}`)
.join(",\n")}\n${i}]`;
return `{\n${u.entries
.map(
(group) =>
`${i}\t// ${sortorder[group[0]?.[2] ?? 1e309]?.group ?? ""}\n` +
group
.map(
([k, v, g, s]) =>
`${i}\t${JSON.stringify(k)}: ${writeSortable(v, i + "\t")}`
)
.join(",\n")
)
.join(",\n\n")}\n${i}}`;
}
throw new Error(`unknown sortable-json ${u} ${typeof u}`);
}
function sortSortableJson(
u: SortableJson,
groups: readonly { group: string; sort: string[] }[]
): GroupedSortableJson {
if (typeof u === "string") return u;
else if (typeof u === "number") return u;
else if (typeof u === "boolean") return u;
else if (typeof u === "object") {
if (u === null) return "null";
else if (u instanceof Array)
return u.map((v) => sortSortableJson(v, groups));
// group the keys into the groups specified by the sortorder
const grouped = groupByPredicate(
u.entries.map(([k, v]) => {
let sortIndex = -1;
const groupIndex = groups.findIndex(
(group) => (sortIndex = group.sort.indexOf(k)) !== -1
);
return [k, v, groupIndex, sortIndex] as const;
}),
([, , group]) => group
).sort(([[, , a]], [[, , b]]) => a - b);
// sort the keys in each group by the specified sortorder
const sorted = grouped.map((group) =>
group
.sort(([ka, , , a], [kb, , , b]) => a - b || ka.localeCompare(kb))
.map(([k, v, g, s]) => [k, sortSortableJson(v, groups), g, s] as const)
);
return { entries: sorted };
}
throw new Error(`unknown sortable-json ${u} ${typeof u}`);
}
/**
* Split the array into an arbitrary number of non-empty groups. The group
* ordering is arbitrary.
*/
function groupByPredicate<T, G>(
values: readonly T[],
predicate: (v: T) => G
): ([T, ...T[]])[] {
const map = new Map<G, T[]>();
for (const v of values) {
const group = predicate(v);
let table = map.get(group);
if (!table) map.set(group, (table = []));
table.push(v);
}
return [...map.values()] as never;
}
console.log(writeSortable(sortSortableJson(makeSortable(tsconfig), sortorder)));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment