-
-
Notifications
You must be signed in to change notification settings - Fork 63
Expand file tree
/
Copy path_common.ts
More file actions
159 lines (145 loc) · 4.39 KB
/
_common.ts
File metadata and controls
159 lines (145 loc) · 4.39 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
155
156
157
158
159
import { constants, promises as fsp } from "fs";
import * as JSON5 from "json5";
import { resolve } from "path";
import { parse } from "pg-connection-string";
import { Settings } from "../settings";
export const DEFAULT_GMRC_PATH = `${process.cwd()}/.gmrc`;
export const DEFAULT_GMRCJS_PATH = `${DEFAULT_GMRC_PATH}.js`;
/**
* Represents the option flags that are valid for all commands (see
* src/cli.ts).
*/
export interface CommonArgv {
/**
* Optional path to the gmrc file.
*/
config?: string;
}
export async function exists(path: string): Promise<boolean> {
try {
await fsp.access(path, constants.F_OK /* visible to us */);
return true;
} catch (e) {
return false;
}
}
export async function getSettingsFromJSON(path: string): Promise<Settings> {
let data;
try {
data = await fsp.readFile(path, "utf8");
} catch (e) {
throw new Error(`Failed to read '${path}': ${e.message}`);
}
try {
return JSON5.parse(data);
} catch (e) {
throw new Error(`Failed to parse '${path}': ${e.message}`);
}
}
/**
* Options passed to the getSettings function.
*/
interface Options {
/**
* Optional path to the gmrc config path to use; if not provided we'll fall
* back to `./.gmrc` and `./.gmrc.js`.
*
* This must be the full path, including extension. If the extension is `.js`
* then we'll use `require` to import it, otherwise we'll read it as JSON5.
*/
configFile?: string;
}
/**
* Gets the raw settings from the relevant .gmrc file. Does *not* validate the
* settings - the result of this call should not be trusted. Pass the result of
* this function to `parseSettings` to get validated settings.
*/
export async function getSettings(options: Options = {}): Promise<Settings> {
const { configFile } = options;
const tryImport = async (
path: string,
mode: "cjs" | "mjs" | "js",
): Promise<Settings> => {
// If the file is e.g. `foo.js` then Node `require('foo.js')` would look in
// `node_modules`; we don't want this - instead force it to be a relative
// path.
const relativePath = resolve(process.cwd(), path);
try {
if (mode === "cjs") {
return require(relativePath);
} else if (mode === "mjs") {
return (await import(relativePath)).default;
} else {
// try and require(CJS); but on ESM error, retry with import
try {
return require(relativePath);
} catch (e) {
if (
e.code === "ERR_REQUIRE_ESM" ||
(e.constructor.name === "SyntaxError" &&
e.message.includes(" token 'export'"))
) {
return tryImport(relativePath, "mjs");
} else {
throw e;
}
}
}
} catch (e) {
throw new Error(
`Failed to import '${relativePath}'; error:\n ${e.stack.replace(
/\n/g,
"\n ",
)}`,
);
}
};
if (configFile != null) {
if (!(await exists(configFile))) {
throw new Error(`Failed to import '${configFile}': file not found`);
}
if (configFile.endsWith(".js")) {
return tryImport(configFile, "js");
} else if (configFile.endsWith(".mjs")) {
return tryImport(configFile, "mjs");
} else if (configFile.endsWith(".cjs")) {
return tryImport(configFile, "cjs");
} else {
return await getSettingsFromJSON(configFile);
}
} else if (await exists(DEFAULT_GMRC_PATH)) {
return await getSettingsFromJSON(DEFAULT_GMRC_PATH);
} else if (await exists(DEFAULT_GMRCJS_PATH)) {
return tryImport(DEFAULT_GMRCJS_PATH, "js");
} else {
throw new Error(
"No .gmrc file found; please run `graphile-migrate init` first.",
);
}
}
export function readStdin(): Promise<string> {
return new Promise((resolve, reject) => {
let data = "";
process.stdin.setEncoding("utf8");
process.stdin.on("error", reject);
process.stdin.on("readable", () => {
let chunk;
// Use a loop to make sure we read all available data.
while ((chunk = process.stdin.read()) !== null) {
data += chunk;
}
});
process.stdin.on("end", () => {
resolve(data);
});
});
}
export function getDatabaseName(connectionString: string): string {
const databaseName = parse(connectionString).database;
if (!databaseName) {
throw new Error(
"Could not determine database name from connection string.",
);
}
return databaseName;
}