UNPKG

2.52 kBJavaScriptView Raw
1const debug = require('debug')('upward-js:IOAdapter');
2const containsPath = require('contains-path');
3const { resolve, dirname } = require('path');
4const { readFile: fsReadFile } = require('fs');
5const { promisify } = require('util');
6
7const readFile = promisify(fsReadFile);
8class IOAdapter {
9 static default(upwardPath) {
10 debug(`creating default IO from ${upwardPath}`);
11 const baseDir = dirname(upwardPath);
12 debug(`baseDir ${baseDir}`);
13 return new IOAdapter({
14 networkFetch: require('node-fetch'),
15 readFile: (filePath, enc) => {
16 // prevent path traversal above baseDir
17 const resolvedPath = resolve(baseDir, filePath);
18 if (!containsPath(resolvedPath, baseDir)) {
19 throw new Error(
20 `Cannot read ${resolvedPath} because it is outside ${baseDir}`
21 );
22 }
23
24 /**
25 * Replaces binary as null.
26 * For some reason nodejs interprets 'binary' as 'latin1'.
27 * See: https://stackoverflow.com/a/46441727
28 */
29 if (enc === 'binary') enc = null;
30
31 return readFile(resolvedPath, enc);
32 }
33 });
34 }
35 constructor(implementations) {
36 const missingImpls = ['readFile', 'networkFetch'].reduce(
37 (missing, method) =>
38 method in implementations
39 ? missing
40 : missing +
41 `Must provide an implementation of '${method}\n`,
42 ''
43 );
44 if (missingImpls) {
45 throw new Error(`Error creating IOAdapter:\n${missingImpls}`);
46 }
47 Object.assign(this, implementations);
48 }
49 /**
50 * Works like promisified Node `fs.readFile`. (Injected for testability.)
51 * Cannot traverse below working directory.
52 * @param {string} path Path of file to read.
53 * @param {string} [encoding] Character set, e.g. 'utf-8'.
54 * @return {Promise<string|Buffer>} Promise for file contents.
55 */
56 async readFile(filePath, encoding) {} //eslint-disable-line no-unused-vars
57
58 /**
59 * Works like `node-fetch`. (Injected for testability.)
60 * @param {string|URL} URL URL to fetch.
61 * @param {object} options Fetch options, see node-fetch docs.
62 * @return {Promise<Response>}
63 */
64 async networkFetch(url, options) {} //eslint-disable-line no-unused-vars
65}
66
67module.exports = IOAdapter;