Skip to content

Commit 88c500f

Browse files
committed
chore: add reformatting script
1 parent b326da7 commit 88c500f

File tree

3 files changed

+338
-1
lines changed

3 files changed

+338
-1
lines changed

.prettierignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
meteor/.meteor
2+
**/dist/**
3+
4+
**/node_modules/**
5+

packages/job-worker/tsconfig.build.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
},
1212
"resolveJsonModule": true,
1313
"types": ["node"],
14-
"skipLibCheck": true
14+
"skipLibCheck": true,
15+
"esModuleInterop": true
1516
}
1617
}

scripts/reformat.mjs

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
import path from "path";
2+
import fs from "fs/promises";
3+
import { glob } from "glob";
4+
import cp from "child_process";
5+
6+
/*
7+
8+
Instructions for applying reformatting:
9+
10+
1. Cherry-pick the commit "chore: update dependencies and reformatting script"
11+
2. Run `yarn postinstall`
12+
3. Apply reformatting automatically:
13+
a. Run `node scripts/reformat.mjs --write`
14+
b. Run `node scripts/reformat.mjs --write` again :)
15+
c. commit the result
16+
"chore: reformat code using reformat.mjs"
17+
4. cherry-pick the commits:
18+
* "chore: apply various fixes after reformatting"
19+
20+
*/
21+
22+
const writeChanges = Boolean(process.argv.find((arg) => arg === "--write"));
23+
24+
if (!writeChanges) console.log('Dry run, use "--write" to apply changes');
25+
26+
let exitCode = 0;
27+
let maxCount = 99999;
28+
29+
async function main() {
30+
let anyChanged = false;
31+
for (let i = 0; i < 3; i++) {
32+
// First, prettier reformat all files:
33+
try {
34+
console.log("Prettier formatting ---------------------------------");
35+
await prettierReformatFiles();
36+
} catch (e) {
37+
console.error(e);
38+
exitCode = 1;
39+
}
40+
41+
try {
42+
console.log("Custom formatting -----------------------------------");
43+
anyChanged = await customReformatFiles();
44+
} catch (e) {
45+
console.error(e);
46+
exitCode = 1;
47+
}
48+
49+
if (!anyChanged) {
50+
break;
51+
} else {
52+
console.log("Running again to apply reformatting onto previous run");
53+
}
54+
}
55+
56+
if (anyChanged) {
57+
console.error(
58+
`ERROR! Something went wrong, reformatting went into a loop!`
59+
);
60+
exitCode = 1;
61+
} else {
62+
console.log("Done!");
63+
}
64+
}
65+
66+
// ----------------------------------------------------------------------------
67+
async function prettierReformatFiles() {
68+
// await runCmd(`npx prettier --check "./**/*.{ts,tsx,json,md}"`);
69+
await runCmd(
70+
`npx prettier ${
71+
writeChanges ? "--write" : "--check"
72+
} "./**/*.{ts,tsx,js,jsx,json,css,scss,md,html}"`
73+
);
74+
}
75+
async function customReformatFiles() {
76+
// include a
77+
const files = await glob(["**/*.ts", "**/*.tsx"], {
78+
ignore: ["**/node_modules/**", "**/dist/**"],
79+
});
80+
81+
console.log(`Found ${files.length} files...`);
82+
83+
let modified = [];
84+
85+
for (const filename of files) {
86+
maxCount--;
87+
if (maxCount < 0) break;
88+
89+
// if (!filename.includes("App.tsx")) continue;
90+
91+
console.log(filename);
92+
93+
const filePath = path.resolve(filename);
94+
95+
const fileContentOrg = await fs.readFile(filePath, "utf-8");
96+
let fileContent = fileContentOrg;
97+
// console.log("fileContent", fileContent);
98+
99+
const maybeReplaceCb = async (match) => {
100+
const matchStr = match[0];
101+
102+
// console.log("matchStr", matchStr);
103+
104+
if (
105+
// Already fixed:
106+
matchStr.includes(".js'") ||
107+
matchStr.includes(".jsx'") ||
108+
matchStr.includes(".ts'") ||
109+
matchStr.includes(".tsx'") ||
110+
matchStr.includes(".json'") ||
111+
matchStr.includes(".scss'")
112+
)
113+
return undefined;
114+
115+
if (
116+
// Must be a relative file name:
117+
!matchStr.includes("'./") &&
118+
!matchStr.includes("'..") &&
119+
!matchStr.includes("'.'")
120+
)
121+
return undefined;
122+
123+
let replaceFile = undefined;
124+
125+
let orgTarget = path.resolve(path.dirname(filePath), match[1]);
126+
127+
try {
128+
if (
129+
(await fsExists(orgTarget)) &&
130+
(await isDirectory(orgTarget)) &&
131+
!(await fsExists(orgTarget + ".ts")) // in case there is a lib.ts and a lib directory
132+
) {
133+
// is a directory
134+
135+
let isAlreadyAFile = false;
136+
// now check if it also is a file
137+
const tsFilePaths = [`${orgTarget}.ts`, `${orgTarget}.tsx`];
138+
for (const tsFilePath of tsFilePaths) {
139+
// console.log("tsFilePath", tsFilePath);
140+
if (await fsExists(tsFilePath)) {
141+
isAlreadyAFile = true;
142+
}
143+
}
144+
145+
if (!isAlreadyAFile) {
146+
// now check if there is an index file in it
147+
const indexFiles = [
148+
path.join(orgTarget, "index.ts"),
149+
path.join(orgTarget, "index.tsx"),
150+
];
151+
for (const indexFile of indexFiles) {
152+
// console.log("EXISTS?", indexFile);
153+
if (await fsExists(indexFile)) {
154+
// console.log("EXISTS", indexFile);
155+
156+
replaceFile = match[1] + "/index.js";
157+
break;
158+
}
159+
}
160+
}
161+
}
162+
} catch (e) {
163+
console.log("orgTarget", filePath, match[1], orgTarget);
164+
throw e;
165+
}
166+
167+
if (replaceFile === undefined) {
168+
replaceFile = match[1] + ".js";
169+
}
170+
171+
return matchStr.replace(match[1], replaceFile);
172+
173+
// return ` from '${replaceFile}'`;
174+
};
175+
if (!filename.startsWith("meteor")) {
176+
// Meteor doesn't support file extensions in imports
177+
178+
// Add file extensions to imports:
179+
fileContent = await customReplaceAll(
180+
fileContent,
181+
/ from '(.*?)'/g,
182+
maybeReplaceCb
183+
);
184+
fileContent = await customReplaceAll(
185+
fileContent,
186+
/import '(.*?)'/g,
187+
maybeReplaceCb
188+
);
189+
}
190+
191+
// myFunction && myFunction() -> myFunction?.()
192+
fileContent = await customReplaceAll(
193+
fileContent,
194+
/([^ \n\t]+) && ([^ \n\t]+)\(/g,
195+
(match) => {
196+
if (match[1] !== match[2]) return undefined;
197+
198+
return `${match[1]}?.(`;
199+
}
200+
);
201+
202+
// Custom fixes:
203+
fileContent = fileContent
204+
205+
// import deepmerge from 'deepmerge'
206+
//
207+
.replaceAll(
208+
/import (\w+) = require\('([\w-]+)'\)/g, // `import _ = require('underscore')`,
209+
`import $1 from '$2'` // `import _ from 'underscore'`
210+
)
211+
.replaceAll(
212+
`import deepmerge = require('deepmerge')`,
213+
`import deepmerge from 'deepmerge'`
214+
)
215+
.replaceAll(
216+
/const (\w+) = require\('([\w-]+)'\)\n/g, //`const clone = require('fast-clone')`,
217+
`import $1 from '$2'\n` // `import clone from 'fast-clone'`
218+
)
219+
.replaceAll(
220+
`import * as deepExtend from 'deep-extend'`,
221+
`import deepExtend from 'deep-extend'`
222+
)
223+
.replaceAll(
224+
`import * as deepmerge from 'deepmerge'`,
225+
`import deepmerge from 'deepmerge'`
226+
)
227+
.replaceAll(
228+
`import * as EventEmitter from 'events'`,
229+
`import { EventEmitter } from 'events'`
230+
)
231+
.replaceAll(
232+
`import objectPath from 'object-path'`,
233+
`import * as objectPath from 'object-path'`
234+
)
235+
.replaceAll(
236+
`import * as _ from 'underscore'`,
237+
`import _ from 'underscore'`
238+
)
239+
240+
.replaceAll(
241+
`// eslint-disable-next-line no-process-exit`,
242+
`// eslint-disable-next-line n/no-process-exit`
243+
)
244+
.replaceAll(
245+
`// eslint-disable-next-line @typescript-eslint/ban-types`,
246+
`// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type`
247+
)
248+
.replaceAll(
249+
`// eslint-disable-next-line @typescript-eslint/no-empty-interface`,
250+
`// eslint-disable-next-line @typescript-eslint/no-empty-object-type`
251+
);
252+
253+
if (fileContentOrg !== fileContent) {
254+
modified.push(filename);
255+
if (writeChanges) await fs.writeFile(filePath, fileContent, "utf-8");
256+
else console.log(`Needs fixing: ${filename}`);
257+
}
258+
}
259+
if (writeChanges) {
260+
console.log(`Modified ${modified.length} files`);
261+
} else {
262+
if (modified.length > 0) {
263+
throw new Error(`${modified.length} files need fixing`);
264+
} else {
265+
console.log(
266+
`${modified.length} files need fixing (checked ${files.length} files)`
267+
);
268+
}
269+
}
270+
271+
return modified.length > 0;
272+
}
273+
async function runCmd(cmd) {
274+
await new Promise((resolve, reject) => {
275+
const child = cp.exec(cmd, (err, stdout, stderr) => {
276+
if (err) {
277+
// console.error("stderr", stderr);
278+
reject(err);
279+
} else {
280+
resolve(stdout);
281+
}
282+
});
283+
284+
child.stdout.pipe(process.stdout);
285+
child.stderr.pipe(process.stderr);
286+
});
287+
}
288+
async function customReplaceAll(str, regexp, cb) {
289+
const matches = str.matchAll(regexp);
290+
291+
for (const match of matches) {
292+
const replaceWith = await cb(match);
293+
if (replaceWith !== undefined) {
294+
const matchStr = match[0];
295+
296+
const newStr = str.replace(matchStr, replaceWith);
297+
298+
if (newStr !== str) {
299+
str = newStr;
300+
301+
if (!writeChanges) {
302+
console.log(`- ${matchStr}\n+ ${replaceWith}\n`);
303+
}
304+
}
305+
}
306+
}
307+
return str;
308+
}
309+
310+
const cache = new Map();
311+
async function isDirectory(filePath) {
312+
if (cache.has(filePath)) return cache.get(filePath);
313+
314+
const stats = await fs.stat(filePath);
315+
const isDir = stats.isDirectory();
316+
317+
cache.set(filePath, isDir);
318+
return isDir;
319+
}
320+
async function fsExists(filePath) {
321+
try {
322+
await fs.access(filePath);
323+
return true;
324+
} catch (e) {
325+
return false;
326+
}
327+
}
328+
329+
main()
330+
.catch(console.error)
331+
.then(() => process.exit(exitCode));

0 commit comments

Comments
 (0)