UNPKG

3.84 kBJavaScriptView Raw
1/**
2 * Copyright 2017 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17const path = require('path');
18const fs = require('fs');
19
20const readFileAsync = promisify(fs.readFile);
21const readdirAsync = promisify(fs.readdir);
22const writeFileAsync = promisify(fs.writeFile);
23
24const PROJECT_DIR = path.join(__dirname, '..', '..');
25
26class Source {
27 /**
28 * @param {string} filePath
29 * @param {string} text
30 */
31 constructor(filePath, text) {
32 this._filePath = filePath;
33 this._projectPath = path.relative(PROJECT_DIR, filePath);
34 this._name = path.basename(filePath);
35 this._text = text;
36 this._hasUpdatedText = false;
37 }
38
39 /**
40 * @return {string}
41 */
42 filePath() {
43 return this._filePath;
44 }
45
46 /**
47 * @return {string}
48 */
49 projectPath() {
50 return this._projectPath;
51 }
52
53 /**
54 * @return {string}
55 */
56 name() {
57 return this._name;
58 }
59
60 /**
61 * @param {string} text
62 * @return {boolean}
63 */
64 setText(text) {
65 if (text === this._text)
66 return false;
67 this._hasUpdatedText = true;
68 this._text = text;
69 return true;
70 }
71
72 /**
73 * @return {string}
74 */
75 text() {
76 return this._text;
77 }
78
79 /**
80 * @return {boolean}
81 */
82 hasUpdatedText() {
83 return this._hasUpdatedText;
84 }
85}
86
87class SourceFactory {
88 constructor() {
89 this._sources = new Map();
90 }
91
92 /**
93 * @return {!Array<!Source>}
94 */
95 sources() {
96 return Array.from(this._sources.values());
97 }
98
99 /**
100 * @return {!Promise<boolean>}
101 */
102 async saveChangedSources() {
103 const changedSources = Array.from(this._sources.values()).filter(source => source.hasUpdatedText());
104 if (!changedSources.length)
105 return false;
106 await Promise.all(changedSources.map(source => writeFileAsync(source.filePath(), source.text())));
107 return true;
108 }
109
110 /**
111 * @param {string} filePath
112 * @return {!Promise<Source>}
113 */
114 async readFile(filePath) {
115 filePath = path.resolve(filePath);
116 let source = this._sources.get(filePath);
117 if (!source) {
118 const text = await readFileAsync(filePath, {encoding: 'utf8'});
119 source = new Source(filePath, text);
120 this._sources.set(filePath, source);
121 }
122 return source;
123 }
124
125 /**
126 * @param {string} dirPath
127 * @param {string=} extension
128 * @return {!Promise<!Array<!Source>>}
129 */
130 async readdir(dirPath, extension = '') {
131 const fileNames = await readdirAsync(dirPath);
132 const filePaths = fileNames.filter(fileName => fileName.endsWith(extension)).map(fileName => path.join(dirPath, fileName));
133 return Promise.all(filePaths.map(filePath => this.readFile(filePath)));
134 }
135
136 /**
137 * @param {string} filePath
138 * @param {string} text
139 * @return {!Source}
140 */
141 createForTest(filePath, text) {
142 return new Source(filePath, text);
143 }
144}
145
146/**
147 * @param {function(?)} nodeFunction
148 * @return {function(?):!Promise<?>}
149 */
150function promisify(nodeFunction) {
151 /**
152 * @param {!Array<?>} options
153 * @return {!Promise<?>}
154 */
155 return function(...options) {
156 return new Promise(function(fulfill, reject) {
157 options.push(callback);
158 nodeFunction.call(null, ...options);
159 function callback(err, result) {
160 if (err)
161 reject(err);
162 else
163 fulfill(result);
164 }
165 });
166 };
167}
168
169module.exports = SourceFactory;