host.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. /**
  4. * @license
  5. * Copyright Google Inc. All Rights Reserved.
  6. *
  7. * Use of this source code is governed by an MIT-style license that can be
  8. * found in the LICENSE file at https://angular.io/license
  9. */
  10. const fs = require("fs");
  11. const rxjs_1 = require("rxjs");
  12. const operators_1 = require("rxjs/operators");
  13. const src_1 = require("../src");
  14. // This will only be initialized if the watch() method is called.
  15. // Otherwise chokidar appears only in type positions, and shouldn't be referenced
  16. // in the JavaScript output.
  17. let FSWatcher;
  18. function loadFSWatcher() {
  19. if (!FSWatcher) {
  20. try {
  21. // tslint:disable-next-line:no-implicit-dependencies
  22. FSWatcher = require('chokidar').FSWatcher;
  23. }
  24. catch (e) {
  25. if (e.code !== 'MODULE_NOT_FOUND') {
  26. throw new Error('As of angular-devkit version 8.0, the "chokidar" package ' +
  27. 'must be installed in order to use watch() features.');
  28. }
  29. throw e;
  30. }
  31. }
  32. }
  33. function _callFs(fn, ...args) {
  34. return new rxjs_1.Observable(obs => {
  35. fn(...args, (err, result) => {
  36. if (err) {
  37. obs.error(err);
  38. }
  39. else {
  40. obs.next(result);
  41. obs.complete();
  42. }
  43. });
  44. });
  45. }
  46. /**
  47. * An implementation of the Virtual FS using Node as the background. There are two versions; one
  48. * synchronous and one asynchronous.
  49. */
  50. class NodeJsAsyncHost {
  51. get capabilities() {
  52. return { synchronous: false };
  53. }
  54. write(path, content) {
  55. return new rxjs_1.Observable(obs => {
  56. // Create folders if necessary.
  57. const _createDir = (path) => {
  58. if (fs.existsSync(src_1.getSystemPath(path))) {
  59. return;
  60. }
  61. if (src_1.dirname(path) === path) {
  62. throw new Error();
  63. }
  64. _createDir(src_1.dirname(path));
  65. fs.mkdirSync(src_1.getSystemPath(path));
  66. };
  67. _createDir(src_1.dirname(path));
  68. _callFs(fs.writeFile, src_1.getSystemPath(path), new Uint8Array(content)).subscribe(obs);
  69. });
  70. }
  71. read(path) {
  72. return _callFs(fs.readFile, src_1.getSystemPath(path)).pipe(operators_1.map(buffer => new Uint8Array(buffer).buffer));
  73. }
  74. delete(path) {
  75. return this.isDirectory(path).pipe(operators_1.mergeMap(isDirectory => {
  76. if (isDirectory) {
  77. const allFiles = [];
  78. const allDirs = [];
  79. const _recurseList = (path) => {
  80. for (const fragment of fs.readdirSync(src_1.getSystemPath(path))) {
  81. if (fs.statSync(src_1.getSystemPath(src_1.join(path, fragment))).isDirectory()) {
  82. _recurseList(src_1.join(path, fragment));
  83. allDirs.push(src_1.join(path, fragment));
  84. }
  85. else {
  86. allFiles.push(src_1.join(path, fragment));
  87. }
  88. }
  89. };
  90. _recurseList(path);
  91. return rxjs_1.concat(rxjs_1.from(allFiles).pipe(operators_1.mergeMap(p => _callFs(fs.unlink, src_1.getSystemPath(p))), operators_1.ignoreElements()), rxjs_1.from(allDirs).pipe(operators_1.concatMap(p => _callFs(fs.rmdir, src_1.getSystemPath(p)))));
  92. }
  93. else {
  94. return _callFs(fs.unlink, src_1.getSystemPath(path));
  95. }
  96. }), operators_1.map(() => undefined));
  97. }
  98. rename(from, to) {
  99. return _callFs(fs.rename, src_1.getSystemPath(from), src_1.getSystemPath(to));
  100. }
  101. list(path) {
  102. return _callFs(fs.readdir, src_1.getSystemPath(path)).pipe(operators_1.map(names => names.map(name => src_1.fragment(name))));
  103. }
  104. exists(path) {
  105. // Exists is a special case because it cannot error.
  106. return new rxjs_1.Observable(obs => {
  107. fs.exists(path, exists => {
  108. obs.next(exists);
  109. obs.complete();
  110. });
  111. });
  112. }
  113. isDirectory(path) {
  114. return _callFs(fs.stat, src_1.getSystemPath(path)).pipe(operators_1.map(stat => stat.isDirectory()));
  115. }
  116. isFile(path) {
  117. return _callFs(fs.stat, src_1.getSystemPath(path)).pipe(operators_1.map(stat => stat.isFile()));
  118. }
  119. // Some hosts may not support stat.
  120. stat(path) {
  121. return _callFs(fs.stat, src_1.getSystemPath(path));
  122. }
  123. // Some hosts may not support watching.
  124. watch(path, _options) {
  125. return new rxjs_1.Observable(obs => {
  126. loadFSWatcher();
  127. const watcher = new FSWatcher({ persistent: true }).add(src_1.getSystemPath(path));
  128. watcher
  129. .on('change', path => {
  130. obs.next({
  131. path: src_1.normalize(path),
  132. time: new Date(),
  133. type: 0 /* Changed */,
  134. });
  135. })
  136. .on('add', path => {
  137. obs.next({
  138. path: src_1.normalize(path),
  139. time: new Date(),
  140. type: 1 /* Created */,
  141. });
  142. })
  143. .on('unlink', path => {
  144. obs.next({
  145. path: src_1.normalize(path),
  146. time: new Date(),
  147. type: 2 /* Deleted */,
  148. });
  149. });
  150. return () => watcher.close();
  151. }).pipe(operators_1.publish(), operators_1.refCount());
  152. }
  153. }
  154. exports.NodeJsAsyncHost = NodeJsAsyncHost;
  155. /**
  156. * An implementation of the Virtual FS using Node as the backend, synchronously.
  157. */
  158. class NodeJsSyncHost {
  159. get capabilities() {
  160. return { synchronous: true };
  161. }
  162. write(path, content) {
  163. return new rxjs_1.Observable(obs => {
  164. // TODO: remove this try+catch when issue https://github.com/ReactiveX/rxjs/issues/3740 is
  165. // fixed.
  166. try {
  167. // Create folders if necessary.
  168. const _createDir = (path) => {
  169. if (fs.existsSync(src_1.getSystemPath(path))) {
  170. return;
  171. }
  172. _createDir(src_1.dirname(path));
  173. fs.mkdirSync(src_1.getSystemPath(path));
  174. };
  175. _createDir(src_1.dirname(path));
  176. fs.writeFileSync(src_1.getSystemPath(path), new Uint8Array(content));
  177. obs.next();
  178. obs.complete();
  179. }
  180. catch (err) {
  181. obs.error(err);
  182. }
  183. });
  184. }
  185. read(path) {
  186. return new rxjs_1.Observable(obs => {
  187. // TODO: remove this try+catch when issue https://github.com/ReactiveX/rxjs/issues/3740 is
  188. // fixed.
  189. try {
  190. const buffer = fs.readFileSync(src_1.getSystemPath(path));
  191. obs.next(new Uint8Array(buffer).buffer);
  192. obs.complete();
  193. }
  194. catch (err) {
  195. obs.error(err);
  196. }
  197. });
  198. }
  199. delete(path) {
  200. return this.isDirectory(path).pipe(operators_1.concatMap(isDir => {
  201. // TODO: remove this try+catch when issue https://github.com/ReactiveX/rxjs/issues/3740 is
  202. // fixed.
  203. if (isDir) {
  204. const dirPaths = fs.readdirSync(src_1.getSystemPath(path));
  205. const rmDirComplete = new rxjs_1.Observable((obs) => {
  206. try {
  207. fs.rmdirSync(src_1.getSystemPath(path));
  208. obs.complete();
  209. }
  210. catch (e) {
  211. obs.error(e);
  212. }
  213. });
  214. return rxjs_1.concat(...dirPaths.map(name => this.delete(src_1.join(path, name))), rmDirComplete);
  215. }
  216. else {
  217. try {
  218. fs.unlinkSync(src_1.getSystemPath(path));
  219. }
  220. catch (err) {
  221. return rxjs_1.throwError(err);
  222. }
  223. return rxjs_1.of(undefined);
  224. }
  225. }));
  226. }
  227. rename(from, to) {
  228. return new rxjs_1.Observable(obs => {
  229. // TODO: remove this try+catch when issue https://github.com/ReactiveX/rxjs/issues/3740 is
  230. // fixed.
  231. try {
  232. fs.renameSync(src_1.getSystemPath(from), src_1.getSystemPath(to));
  233. obs.next();
  234. obs.complete();
  235. }
  236. catch (err) {
  237. obs.error(err);
  238. }
  239. });
  240. }
  241. list(path) {
  242. return new rxjs_1.Observable(obs => {
  243. // TODO: remove this try+catch when issue https://github.com/ReactiveX/rxjs/issues/3740 is
  244. // fixed.
  245. try {
  246. const names = fs.readdirSync(src_1.getSystemPath(path));
  247. obs.next(names.map(name => src_1.fragment(name)));
  248. obs.complete();
  249. }
  250. catch (err) {
  251. obs.error(err);
  252. }
  253. });
  254. }
  255. exists(path) {
  256. return new rxjs_1.Observable(obs => {
  257. // TODO: remove this try+catch when issue https://github.com/ReactiveX/rxjs/issues/3740 is
  258. // fixed.
  259. try {
  260. obs.next(fs.existsSync(src_1.getSystemPath(path)));
  261. obs.complete();
  262. }
  263. catch (err) {
  264. obs.error(err);
  265. }
  266. });
  267. }
  268. isDirectory(path) {
  269. // tslint:disable-next-line:no-non-null-assertion
  270. return this.stat(path).pipe(operators_1.map(stat => stat.isDirectory()));
  271. }
  272. isFile(path) {
  273. // tslint:disable-next-line:no-non-null-assertion
  274. return this.stat(path).pipe(operators_1.map(stat => stat.isFile()));
  275. }
  276. // Some hosts may not support stat.
  277. stat(path) {
  278. return new rxjs_1.Observable(obs => {
  279. // TODO: remove this try+catch when issue https://github.com/ReactiveX/rxjs/issues/3740 is
  280. // fixed.
  281. try {
  282. obs.next(fs.statSync(src_1.getSystemPath(path)));
  283. obs.complete();
  284. }
  285. catch (err) {
  286. obs.error(err);
  287. }
  288. });
  289. }
  290. // Some hosts may not support watching.
  291. watch(path, _options) {
  292. return new rxjs_1.Observable(obs => {
  293. const opts = { persistent: false };
  294. loadFSWatcher();
  295. const watcher = new FSWatcher(opts).add(src_1.getSystemPath(path));
  296. watcher
  297. .on('change', path => {
  298. obs.next({
  299. path: src_1.normalize(path),
  300. time: new Date(),
  301. type: 0 /* Changed */,
  302. });
  303. })
  304. .on('add', path => {
  305. obs.next({
  306. path: src_1.normalize(path),
  307. time: new Date(),
  308. type: 1 /* Created */,
  309. });
  310. })
  311. .on('unlink', path => {
  312. obs.next({
  313. path: src_1.normalize(path),
  314. time: new Date(),
  315. type: 2 /* Deleted */,
  316. });
  317. });
  318. return () => watcher.close();
  319. }).pipe(operators_1.publish(), operators_1.refCount());
  320. }
  321. }
  322. exports.NodeJsSyncHost = NodeJsSyncHost;