UNPKG

feeles-ide

Version:

The hackable and serializable IDE to make learning material

580 lines (477 loc) 19.2 kB
'use strict'; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var fs = require('fs'); var path = require('path'); var mime = require('mime'); var unorm = require('unorm'); var _require = require('es6-promisify'), promisify = _require.promisify; var readFile = promisify(fs.readFile); var stat = promisify(fs.stat); var toPOSIX = function toPOSIX(str) { return path.sep !== '/' ? str.split(path.sep).join('/') : str; }; var MountFile = /*#__PURE__*/ function () { function MountFile(mountName, mountDir, timestamp) { (0, _classCallCheck2.default)(this, MountFile); // Copy props this.mountName = mountName; this.mountDir = mountDir; this.timestamp = timestamp; } (0, _createClass2.default)(MountFile, [{ key: "serialize", value: function () { var _serialize = (0, _asyncToGenerator2.default)( /*#__PURE__*/ _regenerator.default.mark(function _callee() { var mountName, absolutePath, text, replaced, composed; return _regenerator.default.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: // Convert unicode mountName = unorm.nfc(this.mountName); absolutePath = path.resolve(this.mountDir, mountName); _context.next = 4; return readFile(absolutePath, 'utf8'); case 4: text = _context.sent; replaced = replaceEnvironmentVars(text); composed = new Buffer(replaced).toString('base64'); return _context.abrupt("return", { name: toPOSIX(mountName), type: mime.getType(absolutePath), lastModified: Date.parse(this.timestamp), composed: composed, options: { isTrashed: false }, credits: [] }); case 8: case "end": return _context.stop(); } } }, _callee, this); })); function serialize() { return _serialize.apply(this, arguments); } return serialize; }() }, { key: "serialized", get: function get() { if (!this._serialized) { // Serialize this._serialized = this.serialize(); } return this._serialized; } }]); return MountFile; }(); module.exports = /*#__PURE__*/ function () { function FeelesWebpackPlugin(params) { (0, _classCallCheck2.default)(this, FeelesWebpackPlugin); params = Object.assign({ paths: ['mount'], output: 'index.json', ignore: /[]/, // eslint-disable-line debug: false }, params); this.output = params.output; this.ignore = params.ignore; this.debug = params.debug; this.cache = new Map(); // { [mountName]: [<MountFile>] } this.mountDirs = params.paths.map(function (s) { return path.resolve(s); }); this.priorityOrders = new Map(); // { [mountDir]: [order] } var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = this.mountDirs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var mountDir = _step.value; this.priorityOrders.set(mountDir, this.priorityOrders.size); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return != null) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } (0, _createClass2.default)(FeelesWebpackPlugin, [{ key: "apply", value: function apply(compiler) { var _this = this; // コンパイル開始 compiler.plugin('compilation', function (compilation, params) { var pushDirFiles = function pushDirFiles(dirPath) { var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = fs.readdirSync(dirPath)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var name = _step2.value; var targetPath = path.resolve(dirPath, name); var stats = fs.statSync(targetPath); if (stats.isFile() && !_this.ignore.test(targetPath)) { // 次の emit の compilation.fileDependencies に含める params.compilationDependencies.add(targetPath); } if (stats.isDirectory()) { pushDirFiles(targetPath); } } } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return != null) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } }; // すべての mountDir を再帰的に探索する var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = _this.mountDirs[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var mountDir = _step3.value; pushDirFiles(mountDir); } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return != null) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } }); compiler.plugin('emit', /*#__PURE__*/ function () { var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/ _regenerator.default.mark(function _callee2(compilation, callback) { var mountNameDir, _iteratorNormalCompletion4, _didIteratorError4, _iteratorError4, _iterator4, _step4, absolutePath, _iteratorNormalCompletion6, _didIteratorError6, _iteratorError6, _iterator6, _step6, mountDir, mountName, nextMountDir, entry, changed, _iteratorNormalCompletion5, _didIteratorError5, _iteratorError5, _iterator5, _step5, _step5$value, _mountName, _mountDir, _absolutePath, timestamp, p, add, files, json; return _regenerator.default.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: // compilation.fileDependencies をもとにプロジェクト全体をシリアライズ // { [mountName]: [mountDir] } mountNameDir = new Map(); _iteratorNormalCompletion4 = true; _didIteratorError4 = false; _iteratorError4 = undefined; _context2.prev = 4; _iterator4 = compilation.fileDependencies[Symbol.iterator](); case 6: if (_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done) { _context2.next = 32; break; } absolutePath = _step4.value; if (!_this.ignore.test(absolutePath)) { _context2.next = 10; break; } return _context2.abrupt("continue", 29); case 10: // ignore // mountName を切り出す _iteratorNormalCompletion6 = true; _didIteratorError6 = false; _iteratorError6 = undefined; _context2.prev = 13; for (_iterator6 = _this.mountDirs[Symbol.iterator](); !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { mountDir = _step6.value; if (absolutePath.startsWith(mountDir + path.sep)) { mountName = path.relative(mountDir, absolutePath); // すでにある候補もふくめて最も優先順位の高いパスを mountNameDir に set nextMountDir = _this.maxPriority(mountDir, mountNameDir.get(mountName)); mountNameDir.set(mountName, nextMountDir); } } _context2.next = 21; break; case 17: _context2.prev = 17; _context2.t0 = _context2["catch"](13); _didIteratorError6 = true; _iteratorError6 = _context2.t0; case 21: _context2.prev = 21; _context2.prev = 22; if (!_iteratorNormalCompletion6 && _iterator6.return != null) { _iterator6.return(); } case 24: _context2.prev = 24; if (!_didIteratorError6) { _context2.next = 27; break; } throw _iteratorError6; case 27: return _context2.finish(24); case 28: return _context2.finish(21); case 29: _iteratorNormalCompletion4 = true; _context2.next = 6; break; case 32: _context2.next = 38; break; case 34: _context2.prev = 34; _context2.t1 = _context2["catch"](4); _didIteratorError4 = true; _iteratorError4 = _context2.t1; case 38: _context2.prev = 38; _context2.prev = 39; if (!_iteratorNormalCompletion4 && _iterator4.return != null) { _iterator4.return(); } case 41: _context2.prev = 41; if (!_didIteratorError4) { _context2.next = 44; break; } throw _iteratorError4; case 44: return _context2.finish(41); case 45: return _context2.finish(38); case 46: entry = []; // プロジェクトのファイル全体 changed = false; // 変更があったかどうか _iteratorNormalCompletion5 = true; _didIteratorError5 = false; _iteratorError5 = undefined; _context2.prev = 51; _iterator5 = mountNameDir.entries()[Symbol.iterator](); case 53: if (_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done) { _context2.next = 81; break; } _step5$value = (0, _slicedToArray2.default)(_step5.value, 2), _mountName = _step5$value[0], _mountDir = _step5$value[1]; // webpack が提供する timestamp を取得 _absolutePath = path.resolve(_mountDir, _mountName); _context2.t2 = compilation.fileTimestamps[_absolutePath]; if (_context2.t2) { _context2.next = 63; break; } _context2.t3 = Date; _context2.next = 61; return stat(_absolutePath); case 61: _context2.t4 = _context2.sent.mtime; _context2.t2 = _context2.t3.parse.call(_context2.t3, _context2.t4); case 63: timestamp = _context2.t2; if (!_this.cache.has(_mountName)) { _context2.next = 73; break; } p = _this.cache.get(_mountName); if (!(_mountDir === p.mountDir && timestamp <= p.timestamp)) { _context2.next = 71; break; } // 前回のビルドと同じ. そのまま entry.push(p.serialized); return _context2.abrupt("continue", 78); case 71: // 場所かタイムスタンプが異なるのでキャッシュを削除 _this.cache.delete(_mountName); if (_this.debug) { console.log('📦 Feeles/mod:', _mountName, _mountDir); } case 73: // キャッシュとは異なるので新しく作成 add = new MountFile(_mountName, _mountDir, timestamp); entry.push(add.serialized); _this.cache.set(_mountName, add); // フラグを立てる changed = true; if (_this.debug) { console.log('📦 Feeles/add:', _mountName, _mountDir); } case 78: _iteratorNormalCompletion5 = true; _context2.next = 53; break; case 81: _context2.next = 87; break; case 83: _context2.prev = 83; _context2.t5 = _context2["catch"](51); _didIteratorError5 = true; _iteratorError5 = _context2.t5; case 87: _context2.prev = 87; _context2.prev = 88; if (!_iteratorNormalCompletion5 && _iterator5.return != null) { _iterator5.return(); } case 90: _context2.prev = 90; if (!_didIteratorError5) { _context2.next = 93; break; } throw _iteratorError5; case 93: return _context2.finish(90); case 94: return _context2.finish(87); case 95: if (!changed) { _context2.next = 102; break; } _context2.next = 98; return Promise.all(entry); case 98: files = _context2.sent; console.log("\uD83D\uDCE6 Feeles:".concat(entry.length, " files mounted\tin ").concat(_this.mountDirs.join())); json = JSON.stringify(files); compilation.assets[_this.output] = { source: function source() { return json; }, size: function size() { return json.length; } }; case 102: callback(); case 103: case "end": return _context2.stop(); } } }, _callee2, this, [[4, 34, 38, 46], [13, 17, 21, 29], [22,, 24, 28], [39,, 41, 45], [51, 83, 87, 95], [88,, 90, 94]]); })); return function (_x, _x2) { return _ref.apply(this, arguments); }; }()); compiler.plugin('after-emit', function (compilation, callback) { // 次の emit の compilation.fileDependencies に含める var _iteratorNormalCompletion7 = true; var _didIteratorError7 = false; var _iteratorError7 = undefined; try { for (var _iterator7 = _this.mountDirs[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) { var mountDir = _step7.value; compilation.contextDependencies.add(mountDir); } } catch (err) { _didIteratorError7 = true; _iteratorError7 = err; } finally { try { if (!_iteratorNormalCompletion7 && _iterator7.return != null) { _iterator7.return(); } } finally { if (_didIteratorError7) { throw _iteratorError7; } } } callback(); }); } }, { key: "maxPriority", value: function maxPriority(a, b) { if (!this.priorityOrders.has(a)) return b; if (!this.priorityOrders.has(b)) return a; return this.priorityOrders.get(a) < this.priorityOrders.get(b) ? a : b; } }]); return FeelesWebpackPlugin; }(); /** * Feeles で使える環境変数. これらの文字を "文字列としてリプレイス" する (import のロード先にも使える) * process.env.__FEELES_COMMON_REGISTER__: @hackforplay/common の register.js * process.env.__FEELES_COMMON_INDEX__: @hackforplay/common の index.js * process.env.__FEELES_NODE_ENV__: process.env.NODE_ENV */ var environmentVars = function (vars) { return function () { return vars = vars || Object.keys(process.env).filter(function (s) { return s.startsWith('__FEELES_'); }); }; }(); function replaceEnvironmentVars() { var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var _iteratorNormalCompletion8 = true; var _didIteratorError8 = false; var _iteratorError8 = undefined; try { for (var _iterator8 = environmentVars()[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) { var key = _step8.value; text = text.split(key).join(process.env[key]); } } catch (err) { _didIteratorError8 = true; _iteratorError8 = err; } finally { try { if (!_iteratorNormalCompletion8 && _iterator8.return != null) { _iterator8.return(); } } finally { if (_didIteratorError8) { throw _iteratorError8; } } } return text; }