-
-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathconfig_prettier.ts
More file actions
154 lines (142 loc) · 6.35 KB
/
config_prettier.ts
File metadata and controls
154 lines (142 loc) · 6.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import fs from "node:fs";
import path from "node:path";
import JSONC from "tiny-jsonc";
import zeptomatch from "zeptomatch";
import Known from "./known.js";
import { fastJoinedPath, fastRelativeChildPath, getModulePath } from "./utils.js";
import { isObject, isString, isTruthy, isUndefined, memoize, noop, normalizePrettierOptions, omit, zipObjectUnless } from "./utils.js";
import type { PrettierConfig, PrettierConfigWithOverrides, PromiseMaybe } from "./types.js";
const Loaders = {
auto: (filePath: string): Promise<unknown> => {
const basename = path.basename(filePath);
const ext = path.extname(filePath).slice(1);
const loader = File2Loader[basename] || Ext2Loader[ext] || File2Loader["default"];
return loader(filePath);
},
js: async (filePath: string): Promise<unknown> => {
const module = await import(filePath);
return module.default || module.exports || module.prettier || module;
},
json: async (filePath: string): Promise<unknown> => {
const fileContent = fs.readFileSync(filePath, "utf8");
const config = JSON.parse(fileContent);
return config;
},
jsonc: async (filePath: string): Promise<unknown> => {
const fileContent = fs.readFileSync(filePath, "utf8");
const config = JSONC.parse(fileContent);
return config;
},
json5: async (filePath: string): Promise<unknown> => {
const fileContent = fs.readFileSync(filePath, "utf8");
const JSON5 = (await import("json5")).default;
const config = JSON5.parse(fileContent);
return config;
},
package: async (filePath: string): Promise<unknown> => {
const fileBuffer = fs.readFileSync(filePath);
if (!fileBuffer.includes("prettier")) return; //FIXME: Technically this breaks support for escaped chars, but why would anybody do that though?
const fileContent = fileBuffer.toString("utf8");
const pkg = JSON.parse(fileContent);
if (isObject(pkg) && "prettier" in pkg) {
const config = pkg.prettier;
if (isObject(config)) {
return config;
} else if (isString(config)) {
const modulePath = getModulePath(config, filePath);
return Loaders.auto(modulePath);
}
}
},
toml: async (filePath: string): Promise<unknown> => {
const fileContent = fs.readFileSync(filePath, "utf8");
const toml = await import("smol-toml");
return toml.parse(fileContent);
},
yaml: async (filePath: string): Promise<unknown> => {
const yaml = (await import("js-yaml")).default;
const fileContent = fs.readFileSync(filePath, "utf8");
return yaml.load(fileContent, {
schema: yaml.JSON_SCHEMA,
});
},
};
const File2Loader: Record<string, (filePath: string) => Promise<unknown>> = {
default: Loaders.yaml,
"package.json": Loaders.package,
".prettierrc": Loaders.yaml,
".prettierrc.yml": Loaders.yaml,
".prettierrc.yaml": Loaders.yaml,
".prettierrc.json": Loaders.json,
".prettierrc.jsonc": Loaders.jsonc,
".prettierrc.json5": Loaders.json5,
".prettierrc.toml": Loaders.toml,
".prettierrc.js": Loaders.js,
".prettierrc.cjs": Loaders.js,
".prettierrc.mjs": Loaders.js,
"prettier.config.js": Loaders.js,
"prettier.config.cjs": Loaders.js,
"prettier.config.mjs": Loaders.js,
};
const Ext2Loader: Record<string, (filePath: string) => Promise<unknown>> = {
default: Loaders.yaml,
yml: Loaders.yaml,
yaml: Loaders.yaml,
json: Loaders.json,
jsonc: Loaders.jsonc,
json5: Loaders.json5,
toml: Loaders.toml,
js: Loaders.js,
cjs: Loaders.js,
mjs: Loaders.js,
};
const getPrettierConfig = (folderPath: string, fileName: string, ignoreKnown?: boolean): PromiseMaybe<PrettierConfigWithOverrides | undefined> => {
const filePath = fastJoinedPath(folderPath, fileName);
if (!ignoreKnown && !Known.hasFilePath(filePath)) return;
const loader = File2Loader[fileName] || File2Loader["default"];
const normalize = (config: unknown) => (isObject(config) ? { ...config, ...normalizePrettierOptions(config, folderPath) } : undefined);
return loader(filePath).then(normalize).catch(noop);
};
const getPrettierConfigs = memoize(
async (folderPath: string, filesNames: string[], ignoreKnown: boolean = false): Promise<PrettierConfigWithOverrides[] | undefined> => {
const configsRaw = await Promise.all(filesNames.map((fileName) => getPrettierConfig(folderPath, fileName, ignoreKnown)));
const configs = configsRaw.filter(isTruthy);
if (!configs.length) return;
return configs;
},
);
const getPrettierConfigsMap = async (foldersPaths: string[], filesNames: string[]): Promise<Partial<Record<string, PrettierConfig[]>>> => {
const configs = await Promise.all(foldersPaths.map((folderPath) => getPrettierConfigs(folderPath, filesNames)));
const map = zipObjectUnless(foldersPaths, configs, isUndefined);
return map;
};
const getPrettierConfigsUp = memoize(async (folderPath: string, filesNames: string[], ignoreKnown: boolean = false): Promise<PrettierConfigWithOverrides[]> => {
const config = (await getPrettierConfigs(folderPath, filesNames, ignoreKnown))?.[0];
const folderPathUp = path.dirname(folderPath);
const configsUp = folderPath !== folderPathUp ? await getPrettierConfigsUp(folderPathUp, filesNames, ignoreKnown) : [];
const configs = config ? [...configsUp, config] : configsUp;
return configs;
});
const getPrettierConfigResolved = async (filePath: string, filesNames: string[], ignoreKnown?: boolean): Promise<PrettierConfig> => {
const folderPath = path.dirname(filePath);
const configs = await getPrettierConfigsUp(folderPath, filesNames, ignoreKnown);
let resolved: PrettierConfig = {};
for (let ci = 0, cl = configs.length; ci < cl; ci++) {
const config = configs[ci];
const formatOptions = omit(config, ["overrides"]);
resolved = ci ? { ...resolved, ...formatOptions } : formatOptions;
const overrides = config.overrides;
if (overrides) {
for (let oi = 0, ol = overrides.length; oi < ol; oi++) {
const override = overrides[oi];
const filePathRelative = fastRelativeChildPath(override.folder, filePath);
if (!filePathRelative) continue;
if (!zeptomatch(override.filesPositive, filePathRelative)) continue;
if (zeptomatch(override.filesNegative, filePathRelative)) continue;
resolved = { ...resolved, ...override.options };
}
}
}
return resolved;
};
export { Loaders, File2Loader, Ext2Loader, getPrettierConfig, getPrettierConfigsMap, getPrettierConfigsUp, getPrettierConfigResolved };