UNPKG

2.71 kBJavaScriptView Raw
1const path = require("path");
2const childProcess = require("child_process");
3
4const isBinaryPath = require("is-binary-path");
5const fse = require("fs-extra");
6
7const logger = require("./logger");
8
9const resources = new Map;
10const cache = new Map;
11
12const PATH_LIKE = new Set(["file", "text", "raw"]);
13
14const DEFAULT_READERS = [{
15 name: "file",
16 read: readFile,
17 getSign: getFileSign
18}, {
19 name: "text",
20 read: (...args) => readFile(...args, false),
21 getSign: getFileSign
22}, {
23 name: "raw",
24 read: (...args) => readFile(...args, true),
25 getSign: getFileSign
26}, {
27 name: "cmd",
28 read: readCmd,
29 getSign: getCmdSign
30}];
31
32DEFAULT_READERS.forEach(add);
33
34function getFileSign(source, target) {
35 return JSON.stringify(target);
36}
37
38function getCmdSign(source, target) {
39 return JSON.stringify([target, getDir(source)]);
40}
41
42function readFile(source, target, isBinary = isBinaryPath(target.args[0])) {
43 return fse.readFile(
44 path.resolve(getDir(source), target.args[0]),
45 isBinary ? null : "utf8"
46 );
47}
48
49function readCmd(source, target) {
50 return new Promise((resolve, reject) => {
51 const options = {
52 cwd: getDir(source),
53 stdio: [0, "pipe", 2],
54 shell: true
55 };
56 const output = [];
57 const cp = childProcess.spawn(target.args[0], options);
58 cp.on("error", reject);
59 cp.stdout.pipe(logger.getLogStream());
60 cp.stdout.on("data", chunk => output.push(chunk));
61 cp.on("close", exitCode => {
62 if (exitCode) {
63 reject(new Error(`Non-zero exit code: ${exitCode}`));
64 return;
65 }
66 let data = Buffer.concat(output);
67 if (target.args[1] !== "buffer") {
68 data = data.toString(target.args[1]);
69 }
70 resolve(data);
71 });
72 });
73}
74
75function getDir(source) {
76 return path.resolve(isPath(source) ? path.dirname(source.args[0]) : ".");
77}
78
79function add(reader) {
80 resources.set(reader.name, reader);
81}
82
83function read(source, target) {
84 const reader = resources.get(target.name);
85 const sign = reader.getSign && reader.getSign(source, target);
86 let pending;
87 if (sign && cache.has(sign)) {
88 pending = cache.get(sign);
89 } else {
90 pending = reader.read(source, target);
91 if (sign) {
92 cache.set(sign, pending);
93 }
94 }
95 if (!pending.then) {
96 pending = Promise.resolve(pending);
97 }
98 return pending;
99}
100
101function resolve(source, target) {
102 if (isPath(target)) {
103 target.args[0] = path.resolve(getDir(source), target.args[0]);
104 }
105}
106
107function isPath(source) {
108 return source && PATH_LIKE.has(source.name);
109}
110
111function has(name) {
112 return resources.has(name);
113}
114
115module.exports = {add, read, resolve, PATH_LIKE, has};