ui
This commit is contained in:
+40
@@ -0,0 +1,40 @@
|
||||
let Task = require('./Task');
|
||||
let FileCollection = require('../FileCollection');
|
||||
const { FileGlob } = require('./FileGlob');
|
||||
const File = require('../File');
|
||||
|
||||
/**
|
||||
* @extends {Task<{ src: string|string[], output: File, babel: boolean, ignore?: string[] }>}
|
||||
*/
|
||||
class ConcatenateFilesTask extends Task {
|
||||
/**
|
||||
* Run the task.
|
||||
*/
|
||||
run() {
|
||||
return this.merge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the files into one.
|
||||
*/
|
||||
async merge() {
|
||||
const files = await FileGlob.expand(this.data.src, {
|
||||
ignore: this.data.ignore || []
|
||||
});
|
||||
|
||||
this.files = new FileCollection(files);
|
||||
|
||||
return this.files
|
||||
.merge(this.data.output, this.data.babel)
|
||||
.then(this.assets.push.bind(this.assets));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle when a relevant source file is changed.
|
||||
*/
|
||||
onChange() {
|
||||
return this.merge();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ConcatenateFilesTask;
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
let Task = require('./Task');
|
||||
let FileCollection = require('../FileCollection');
|
||||
let Log = require('../Log');
|
||||
const path = require('path');
|
||||
const File = require('../File');
|
||||
|
||||
/**
|
||||
* @extends {Task<{ from: string|string[], to: File }>}
|
||||
*/
|
||||
class CopyFilesTask extends Task {
|
||||
/**
|
||||
* Run the task.
|
||||
*/
|
||||
run() {
|
||||
let copy = this.data;
|
||||
|
||||
this.files = new FileCollection(copy.from);
|
||||
|
||||
this.files.copyTo(copy.to);
|
||||
|
||||
this.assets = this.files.assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle when a relevant source file is changed.
|
||||
*
|
||||
* @param {string} updatedFile
|
||||
*/
|
||||
onChange(updatedFile) {
|
||||
let destination = this.data.to;
|
||||
|
||||
// If we're copying a src directory recursively, we have to calculate
|
||||
// the correct destination path, based on the src directory tree.
|
||||
if (!Array.isArray(this.data.from) && new File(this.data.from).isDirectory()) {
|
||||
destination = destination.append(
|
||||
path.normalize(updatedFile).replace(path.normalize(this.data.from), '')
|
||||
);
|
||||
}
|
||||
|
||||
Log.feedback(`Copying ${updatedFile} to ${destination.path()}`);
|
||||
|
||||
this.files.copyTo(destination, new File(updatedFile));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CopyFilesTask;
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
let path = require('path');
|
||||
let glob = require('glob');
|
||||
let File = require('../File');
|
||||
let { promisify } = require('util');
|
||||
let { concat } = require('lodash');
|
||||
let globAsync = promisify(glob);
|
||||
|
||||
/** @internal */
|
||||
module.exports.FileGlob = class FileGlob {
|
||||
/**
|
||||
* Find all relevant files matching the given source path.
|
||||
*
|
||||
* @param {string|string[]} src
|
||||
* @param {{ ignore?: string[] }} options
|
||||
* @returns {Promise<string[]>}
|
||||
*/
|
||||
static async expand(src, { ignore = [] } = {}) {
|
||||
const paths = concat([], src);
|
||||
|
||||
const results = await Promise.all(
|
||||
paths.map(async srcPath => {
|
||||
const result = await this.find(srcPath);
|
||||
|
||||
if (!result.isDir && result.matches.length === 0) {
|
||||
return [srcPath];
|
||||
}
|
||||
|
||||
return result.matches;
|
||||
})
|
||||
);
|
||||
|
||||
const filepaths = results.flatMap(files => files);
|
||||
|
||||
return filepaths.filter(filepath => {
|
||||
return !ignore.includes(filepath);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @internal
|
||||
* @param {string} src
|
||||
* @returns {Promise<{isDir: boolean, matches: string[]}>}
|
||||
*/
|
||||
static async find(src) {
|
||||
const isDir = File.find(src).isDirectory();
|
||||
const pattern = isDir ? path.join(src, '**/*') : src;
|
||||
|
||||
const matches = await globAsync(pattern, { nodir: true });
|
||||
|
||||
return {
|
||||
isDir,
|
||||
matches
|
||||
};
|
||||
}
|
||||
};
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
let chokidar = require('chokidar');
|
||||
let File = require('../File');
|
||||
let FileCollection = require('../FileCollection');
|
||||
let NOOP = () => {};
|
||||
|
||||
/**
|
||||
* @template {object} TData
|
||||
*/
|
||||
class Task {
|
||||
/**
|
||||
* Create a new task instance.
|
||||
*
|
||||
* @param {TData} data
|
||||
*/
|
||||
constructor(data) {
|
||||
/** @type {TData} */
|
||||
this.data = data;
|
||||
|
||||
/** @type {File[]} */
|
||||
this.assets = [];
|
||||
|
||||
/** @type {FileCollection} */
|
||||
this.files = new FileCollection();
|
||||
|
||||
this.isBeingWatched = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch all relevant files for changes.
|
||||
*
|
||||
* @param {boolean} usePolling
|
||||
*/
|
||||
watch(usePolling = false) {
|
||||
if (this.isBeingWatched) return;
|
||||
|
||||
let files = this.files.get();
|
||||
let watcher = chokidar
|
||||
.watch(files, { usePolling, persistent: true })
|
||||
.on('change', this.onChange.bind(this));
|
||||
|
||||
// Workaround for issue with atomic writes.
|
||||
// See https://github.com/paulmillr/chokidar/issues/591
|
||||
if (!usePolling) {
|
||||
watcher.on('raw', event => {
|
||||
if (event === 'rename') {
|
||||
watcher.unwatch(files);
|
||||
watcher.add(files);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.isBeingWatched = true;
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
run() {
|
||||
throw new Error('Task.run is an abstract method. Please override it.');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} filepath
|
||||
*/
|
||||
onChange(filepath) {
|
||||
throw new Error('Task.onChange is an abstract method. Please override it.');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Task;
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
let Task = require('./Task');
|
||||
let File = require('../File');
|
||||
let FileCollection = require('../FileCollection');
|
||||
const { FileGlob } = require('./FileGlob');
|
||||
|
||||
/**
|
||||
* @extends {Task<{ files: string[] }>}
|
||||
*/
|
||||
class VersionFilesTask extends Task {
|
||||
/**
|
||||
* Run the task.
|
||||
*/
|
||||
async run() {
|
||||
const fileGroups = await Promise.all(
|
||||
this.data.files.map(async filepath => {
|
||||
const relativePath = new File(filepath).forceFromPublic().relativePath();
|
||||
|
||||
return FileGlob.expand(relativePath);
|
||||
})
|
||||
);
|
||||
|
||||
const files = fileGroups.flat();
|
||||
|
||||
this.files = new FileCollection(files);
|
||||
this.assets = files.map(filepath => {
|
||||
const file = new File(filepath);
|
||||
|
||||
this.mix.manifest.hash(file.pathFromPublic());
|
||||
|
||||
return file;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle when a relevant source file is changed.
|
||||
*
|
||||
* @param {string} updatedFile
|
||||
*/
|
||||
onChange(updatedFile) {
|
||||
this.mix.manifest.hash(new File(updatedFile).pathFromPublic()).refresh();
|
||||
}
|
||||
|
||||
get mix() {
|
||||
return global.Mix;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VersionFilesTask;
|
||||
Reference in New Issue
Block a user