motion
Version:
motion - moving development forward
384 lines (282 loc) • 14.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports.scripts = scripts;
exports.afterBuild = afterBuild;
var _helpers = require('./lib/helpers');
var _streams = require('./lib/streams');
var _fns = require('../lib/fns');
var _unicodeToChar = require('../lib/unicodeToChar');
var _unicodeToChar2 = _interopRequireDefault(_unicodeToChar);
var _index = require('./index');
var _findRequires = require('../lib/findRequires');
var _babel = require('./babel');
var _babel2 = _interopRequireDefault(_babel);
var _bridge = require('../bridge');
var _bridge2 = _interopRequireDefault(_bridge);
var _cache = require('../cache');
var _cache2 = _interopRequireDefault(_cache);
var _builder = require('../builder');
var _builder2 = _interopRequireDefault(_builder);
var _bundler = require('../bundler');
var _bundler2 = _interopRequireDefault(_bundler);
var _scanner = require('./scanner');
var _scanner2 = _interopRequireDefault(_scanner);
var _opts = require('../opts');
var _opts2 = _interopRequireDefault(_opts);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { return step("next", value); }, function (err) { return step("throw", err); }); } } return step("next"); }); }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }
const serializeCache = _fns._.debounce(_cache2.default.serialize, 600);
const hasFinished = () => hasBuilt() && (0, _opts2.default)('hasRunInitialInstall');
const hasBuilt = () => (0, _opts2.default)('hasRunInitialBuild');
const getAllImports = (src, imports) => [].concat((0, _findRequires.findBabelRuntimeRequires)(src), imports);
const scanNow = () => (0, _opts2.default)('build') || (0, _opts2.default)('watch') || !(0, _opts2.default)('hasRunInitialBuild');
function scripts(_ref) {
let buildDone = function () {
var ref = _asyncToGenerator(function* (file) {
if (file.finishingFirstBuild) {
_opts2.default.set('hasRunInitialBuild', true);
_fns.log.gulp('buildDone!!'.green.bold);
waitingForFirstBuild.forEach(function (res) {
return res();
});
}
});
return function buildDone(_x2) {
return ref.apply(this, arguments);
};
}();
var _ref$inFiles = _ref.inFiles;
let inFiles = _ref$inFiles === undefined ? [] : _ref$inFiles;
let userStream = _ref.userStream;
let State = {
curFile: null,
lastError: null,
lastSaved: {},
previouslyInstalled: {},
loaded: 0,
total: inFiles.length,
outsideSources: {}
};
const scripts = userStream || _helpers.gulp.src(_helpers.SCRIPTS_GLOB).pipe(_helpers.$.if((0, _opts2.default)('watching'), _helpers.$.watch(_helpers.SCRIPTS_GLOB, { readDelay: 1 }))).pipe(_helpers.$.if(file => file.event == 'unlink', _helpers.$.ignore.exclude(true)));
const superStream = new _streams.SuperStream();
return ((0, _opts2.default)('watching') ? _helpers.$.merge(scripts, (0, _streams.dirAddStream)((0, _opts2.default)('appDir')), superStream.getStream()) : scripts).pipe(_helpers.$.if(buildCheck, _helpers.$.ignore.exclude(true))).pipe(_helpers.$.fn(reset)).pipe(_helpers.$.plumber(catchError)).pipe(_helpers.$.fn(setLastFile)).pipe((0, _scanner2.default)('pre')).pipe(_helpers.$.sourcemaps.init()).pipe(_babel2.default.file()).pipe(_helpers.$.fn(processDependencies)).pipe(_helpers.$.fn(sendOutsideChanged)) // right after motion
.pipe(_helpers.$.if(!userStream, _helpers.$.rename({ extname: '.js' }))).pipe(_helpers.$.if(file => file.babel.isExported, _helpers.$.multipipe(_helpers.$.fn(removeNewlyInternal), _helpers.$.fn(markFileSuccess), // before writing to preserve path
_helpers.gulp.dest((0, _opts2.default)('deps').internalDir), _helpers.$.if(hasBuilt, _helpers.$.fn(_bundler2.default.writeInternals.bind(null, { force: true }))), _helpers.$.fn(buildDone), _helpers.$.ignore.exclude(true)))).pipe(_helpers.$.sourcemaps.write('.')).pipe(_helpers.$.fn(markFileSuccess)).pipe(_helpers.$.if(checkWriteable, _helpers.gulp.dest((0, _opts2.default)('outDir')))).pipe(_helpers.$.fn(afterWrite))
// temporary bugfix because gulp doesnt work well with watch (pending gulp 4)
.pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn()).pipe(_helpers.$.fn());
function markDone(file) {
State.loaded++;
const done = State.loaded == State.total;
_fns.log.gulp('markDone', State.total, '/', State.loaded, done);
// check if done, only run once
if (done && !(0, _opts2.default)('hasRunInitialBuild')) {
_opts2.default.set('finishingFirstBuild', true);
file.finishingFirstBuild = true;
}
}
// only do on first run
function buildCheck(file) {
file.relativePath = _fns.path.relative((0, _opts2.default)('appDir'), file.path);
// BUGFIX gulp sends deleted files through here, this filters them
if (!file.contents) return true;
// already done with first build
if (hasBuilt()) return false;
// hide behind cached flag for now
if (!(0, _opts2.default)('cached')) {
markDone(file);
return false;
}
const prevFile = _cache2.default.getPrevious(file.path);
if (!prevFile) return false;
let outMTime, srcMTime;
// read srcfile
try {
srcMTime = _fns.fs.statSync(file.path).mtime;
} catch (e) {
return false;
}
// read outfile
try {
const outFile = prevFile.babel.isExported ? _fns.path.join((0, _opts2.default)('deps').dir, 'internal', file.relativePath) : _fns.path.join((0, _opts2.default)('outDir'), file.relativePath);
outMTime = _fns.fs.statSync(outFile).mtime;
} catch (e) {
_fns.log.gulp('buildCheck', 'out file removed');
markDone(file);
return false;
}
// final check
const goodBuild = +outMTime > +srcMTime;
const goodCache = prevFile.added > srcMTime;
if (!goodBuild || !goodCache) return false;
return finish(true);
function finish() {
let restored = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0];
markDone(file);
if (restored) {
_cache2.default.restorePrevious(file.path);
_helpers.out.goodScript(file);
afterWrite(file);
}
return restored;
}
}
function reset(file) {
_cache2.default.add(file.path);
_fns.emitter.emit('script:start', file);
State.lastError = false;
State.curFile = file;
file.startTime = Date.now();
file.message = { startTime: file.startTime };
}
function catchError(error) {
_fns.log.gulp('catchError', error);
State.lastError = true;
_helpers.out.badFile(State.curFile);
error.timestamp = Date.now();
error.stack = (0, _unicodeToChar2.default)(error.stack);
(0, _fns.logError)(error, State.curFile);
_index.event.run('error', State.curFile, error);
_cache2.default.addError(error.fileName || '', error);
if (error.fileName) error.file = _fns.path.relative((0, _opts2.default)('appDir'), error.fileName);
_bridge2.default.broadcast('compile:error', { error }, 'error');
markDone(State.curFile);
buildDone(State.curFile);
}
function setLastFile(file) {
if (!(0, _opts2.default)('watching')) return;
let name = file.path.replace((0, _opts2.default)('appDir'), '');
if (name.charAt(0) != '/') name = '/' + name;
_fns.log.gulp(name);
// add to message
file.message = _extends({}, file.message, {
name: name,
path: file.path,
compiledAt: file.startTime
});
State.curFile = file;
}
function willInstall(path, imports) {
const result = !!_fns._.xor(imports, State.previouslyInstalled[path]).length;
State.previouslyInstalled[path] = [].concat(_toConsumableArray(imports));
return result;
}
// sets isInternal and willInstall
// for handling npm and bundling related things
function processDependencies(file) {
_cache2.default.setFileInternal(file.path, file.babel.isExported);
const scan = () => {
_cache2.default.setFileImports(file.path, file.babel.imports);
_bundler2.default.scanFile(file);
};
if ((0, _opts2.default)('watching')) {
(0, _fns.debounce)('removeOldImports', 3000, _bundler2.default.uninstall);
// check will install
file.willInstall = willInstall(file.path, file.babel.imports);
if (file.willInstall) superStream.avoidSending(file.path);
}
// run scan
if (file.babel.isExported || scanNow()) scan();else (0, _fns.debounce)(`install:${ file.path }`, 2000, scan);
}
// detects if a file has changed not inside views for hot reloads correctness
function sendOutsideChanged(file) {
let src = file.contents.toString();
let meta = _cache2.default.getFileMeta(file.path);
if (!meta) return;
let changed = true;
const viewLocs = Object.keys(meta).map(view => meta[view].location);
if (viewLocs.length) {
// slice out all code not in views
const outerSlice = (ls, start, end) => ls.slice(0, start).concat(ls.slice(end));
const outside = viewLocs.reduce((src, loc) => outerSlice(src, loc[0][0], loc[1][0] + 1), src.split("\n")).join('');
const prevOutside = State.outsideSources[file.path];
changed = prevOutside !== outside;
State.outsideSources[file.path] = outside; // update
}
if ((0, _opts2.default)('hasRunInitialBuild')) _bridge2.default.broadcast('file:outsideChange', { name: _cache2.default.relative(file.path), changed });
}
function checkWriteable(file) {
if (userStream || State.lastError) return false;
if (!(0, _opts2.default)('watching')) return true;
const isNew = !State.lastSaved[file.path] || file.startTime > State.lastSaved[file.path];
if (isNew) {
State.lastSaved[file.path] = file.startTime;
return true;
}
_fns.log.gulp('not writeable');
return false;
}
function afterWrite(file) {
if ((0, _helpers.isSourceMap)(file.path)) return;
buildDone(file);
// avoid during initial build
if (!hasFinished()) return;
if (file.babel.isExported) return;
// run stuff after each change on build --watch
doBuild();
if (State.lastError) {
_fns.log.gulp('State.lastError', State.lastError);
return; // avoid if error
}
const finish = () => {
_fns.emitter.emit('script:end', { path: file.relativePath });
_bridge2.default.broadcast('script:add', file.message);
};
// dont broadcast script if installing/bundling
const isInstalling = _bundler2.default.isInstalling();
if (isInstalling) {
_fns.log.gulp('isInstalling', true);
return;
}
if (file.willInstall) {
_fns.log.gulp('willInstall', true);
_cache2.default.setFileInstalling(file.path, true);
return;
}
finish();
}
function doBuild() {
if ((0, _opts2.default)('watch') && hasBuilt()) {
_builder2.default.build();
return true;
}
}
function markFileSuccess(file) {
if ((0, _helpers.isSourceMap)(file.path)) return;
_helpers.out.goodScript(file);
_fns.log.gulp('DOWN', 'success'.green, 'internal?', file.babel.isExported);
if (file.babel.isExported) return;
// update cache error / state
_cache2.default.update(file.path);
// write cache state to disk
if ((0, _opts2.default)('hasRunInitialBuild')) serializeCache();
// message browser of compile success
_bridge2.default.broadcast('compile:success', file.message, 'error');
// check if other errors left still in queue
const error = _cache2.default.getLastError();
if (!error) return;
_fns.log.gulp('cache last error', error);
_bridge2.default.broadcast('compile:error', { error }, 'error');
}
// ok so we start a file
// its built into .motion/out
// we then add an export
// now we need to remove it from .motion/out
function removeNewlyInternal(file) {
// resolve path from .motion/.internal/deps/internals/xyz.js back to xyz.js
// then resolve path to .motion/.internal/out/xyz.js
const outPath = (0, _fns.p)((0, _opts2.default)('outDir'), file.relativePath);
// log.gulp('remove newly internal', outPath)
(0, _fns.rm)(outPath);
}
}
let waitingForFirstBuild = [];
function afterBuild() {
return new Promise((res, rej) => {
if (hasFinished()) return res();else waitingForFirstBuild.push(res);
});
}
//# sourceMappingURL=scripts.js.map