UNPKG

7.5 kBJavaScriptView Raw
1/*
2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://www.apache.org/licenses/LICENSE-2.0
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14
15'use strict';
16
17const fs = require('fs');
18const fsPath = require('path');
19const Logger = require('@accordproject/concerto-util').Logger;
20const promisify = require('util').promisify;
21const readdir = fs.readdir ? promisify(fs.readdir) : undefined;
22const stat = fs.stat ? promisify(fs.stat) : undefined;
23
24const ENCODING = 'utf8';
25
26/**
27 * A utility class to load files from zip or directories
28 * @class
29 * @private
30 * @abstract
31 */
32class FileLoader {
33 /**
34 * Loads a required file from the zip, displaying an error if missing
35 * @internal
36 * @param {*} zip the JSZip instance
37 * @param {string} path the file path within the zip
38 * @param {boolean} json if true the file is converted to a JS Object using JSON.parse
39 * @param {boolean} required whether the file is required
40 * @return {Promise<string>} a promise to the contents of the zip file or null if it does not exist and
41 * required is false
42 */
43 static async loadZipFileContents(zip, path, json=false, required=false) {
44 Logger.debug('loadZipFileContents', 'Loading ' + path);
45 let zipFile = zip.file(path);
46 if(!zipFile && required) {
47 throw new Error(`Failed to find ${path} in archive file.`);
48 }
49
50 if(zipFile) {
51 const content = await zipFile.async('string');
52
53 if(json && content) {
54 return JSON.parse(content);
55 }
56 else {
57 return FileLoader.normalizeNLs(content);
58 }
59 }
60
61 return null;
62 }
63
64 /**
65 * Loads the contents of all files in the zip that match a regex
66 * @internal
67 * @param {*} zip the JSZip instance
68 * @param {RegExp} regex the regex to use to match files
69 * @return {Promise<object[]>} a promise to an array of objects with the name and contents of the zip files
70 */
71 static async loadZipFilesContents(zip, regex) {
72 const results = [];
73 let matchedFiles = zip.file(regex);
74
75 // do not use forEach, because we need to call an async function!
76 for(let n=0; n < matchedFiles.length; n++) {
77 const file = matchedFiles[n];
78 const result = {name: file.name};
79 result.contents = await FileLoader.loadZipFileContents(zip, file.name, false, true);
80 results.push(result);
81 }
82
83 return results;
84 }
85
86 /**
87 * Loads a required buffer of a file from the zip, displaying an error if missing
88 * @internal
89 * @param {*} zip the JSZip instance
90 * @param {string} path the file path within the zip
91 * @param {boolean} required whether the file is required
92 * @return {Promise<Buffer>} a promise to the Buffer of the zip file or null if it does not exist and
93 * required is false
94 */
95 static async loadZipFileBuffer(zip, path, required=false) {
96 Logger.debug('loadZipFileBuffer', 'Loading ' + path);
97 let zipFile = zip.file(path);
98 if(!zipFile && required) {
99 throw new Error(`Failed to find ${path} in archive file.`);
100 }
101
102 else if(zipFile) {
103 return zipFile.async('nodebuffer');
104 }
105
106 return null;
107 }
108
109 /**
110 * Loads a required file from a directory, displaying an error if missing
111 * @internal
112 * @param {*} path the root path
113 * @param {string} fileName the relative file name
114 * @param {boolean} json if true the file is converted to a JS Object using JSON.parse
115 * @param {boolean} required whether the file is required
116 * @return {Promise<string>} a promise to the contents of the file or null if it does not exist and
117 * required is false
118 */
119 static async loadFileContents(path, fileName, json=false, required=false) {
120
121 Logger.debug('loadFileContents', 'Loading ' + fileName);
122 const filePath = fsPath.resolve(path, fileName);
123
124 if (fs.existsSync(filePath)) {
125 const contents = fs.readFileSync(filePath, ENCODING);
126 if(json && contents) {
127 return JSON.parse(contents);
128 }
129 else {
130 return FileLoader.normalizeNLs(contents);
131 }
132 }
133 else {
134 if(required) {
135 throw new Error(`Failed to find ${fileName} in directory.`);
136 }
137 }
138
139 return null;
140 }
141
142 /**
143 * Loads a file as buffer from a directory, displaying an error if missing
144 * @internal
145 * @param {*} path the root path
146 * @param {string} fileName the relative file name
147 * @param {boolean} required whether the file is required
148 * @return {Promise<Buffer>} a promise to the buffer of the file or null if
149 * it does not exist and required is false
150 */
151 static async loadFileBuffer(path, fileName, required=false) {
152
153 Logger.debug('loadFileBuffer', 'Loading ' + fileName);
154 const filePath = fsPath.resolve(path, fileName);
155
156 if (fs.existsSync(filePath)) {
157 return fs.readFileSync(filePath);
158 }
159 else {
160 if(required) {
161 throw new Error(`Failed to find ${fileName} in directory`);
162 }
163 }
164 return null;
165 }
166
167 /**
168 * Loads the contents of all files under a path that match a regex
169 * Note that any directories called node_modules are ignored.
170 * @internal
171 * @param {*} path the file path
172 * @param {RegExp} regex the regex to match files
173 * @return {Promise<object[]>} a promise to an array of objects with the name and contents of the files
174 */
175 static async loadFilesContents(path, regex) {
176
177 Logger.debug('loadFilesContents', 'Loading ' + path);
178 const subdirs = await readdir(path);
179 const result = await Promise.all(subdirs.map(async (subdir) => {
180 const res = fsPath.resolve(path, subdir);
181
182 if((await stat(res)).isDirectory()) {
183 if( /.*node_modules$/.test(res) === false) {
184 return FileLoader.loadFilesContents(res, regex);
185 }
186 else {
187 return null;
188 }
189 }
190 else {
191 if(regex.test(res)) {
192 return {
193 name: res,
194 contents: await FileLoader.loadFileContents(path, res, false, true)
195 };
196 }
197 else {
198 return null;
199 }
200 }
201 }));
202 return result.reduce((a, f) => a.concat(f), []).filter((f) => f !== null);
203 }
204
205 /**
206 * Prepare the text for parsing (normalizes new lines, etc)
207 * @param {string} input - the text for the clause
208 * @return {string} - the normalized text for the clause
209 */
210 static normalizeNLs(input) {
211 // we replace all \r and \n with \n
212 let text = input.replace(/\r/gm,'');
213 return text;
214 }
215
216}
217
218module.exports = FileLoader;
\No newline at end of file