actual build process, linting, formatting, tweaks
This commit is contained in:
parent
abfcc14b52
commit
678f7226a1
8 changed files with 39 additions and 304 deletions
7
.eslintrc.yaml
Normal file
7
.eslintrc.yaml
Normal file
|
@ -0,0 +1,7 @@
|
|||
env:
|
||||
es6: true
|
||||
node: true
|
||||
extends: 'eslint:recommended'
|
||||
parserOptions:
|
||||
ecmaVersion: 2017
|
||||
sourceType: module
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/dist/
|
||||
/node_modules/
|
288
index.js
288
index.js
|
@ -1,288 +0,0 @@
|
|||
'use strict';
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
let events = require("events");
|
||||
let fs = require("fs");
|
||||
let readFile$1 = fs.readFile;
|
||||
let stat$1 = fs.stat;
|
||||
function readFile$$1(...args) { return new Promise((res, rej) => readFile$1(...args, (err, val) => err ? rej(err) : res(val))) }
|
||||
function stat$$1(...args) { return new Promise((res, rej) => stat$1(...args, (err, val) => err ? rej(err) : res(val))) }
|
||||
let path = require("path");
|
||||
let relative = path.relative;
|
||||
class File {
|
||||
constructor(path) {
|
||||
this.path = path;
|
||||
this.stat = null;
|
||||
this._bytes = null;
|
||||
this._text = null;
|
||||
}
|
||||
get ext() {
|
||||
if (this.path && this.path.match) {
|
||||
let match = this.path.match(/\.[^./\\]+$/);
|
||||
if (match) {
|
||||
return match[0]
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
set ext(ext) {
|
||||
let oldExt = this.ext;
|
||||
if (oldExt) {
|
||||
this.path = this.path.slice(0, -oldExt.length) + ext;
|
||||
} else {
|
||||
this.path += ext;
|
||||
}
|
||||
}
|
||||
get bytes() {
|
||||
if (this._bytes == null && this._text != null) {
|
||||
this._bytes = Buffer.from(this._text);
|
||||
}
|
||||
return this._bytes
|
||||
}
|
||||
set bytes(bytes) {
|
||||
this._bytes = bytes;
|
||||
this._text = null;
|
||||
}
|
||||
get text() {
|
||||
if (this._text == null && this._bytes != null) {
|
||||
this._text = this._bytes.toString();
|
||||
}
|
||||
return this._text
|
||||
}
|
||||
set text(text) {
|
||||
this._text = text;
|
||||
this._bytes = null;
|
||||
}
|
||||
}
|
||||
let TRANSFORM = Symbol();
|
||||
let IF = Symbol();
|
||||
let ELSE = Symbol();
|
||||
let END = Symbol();
|
||||
class Defiler extends events {
|
||||
constructor() {
|
||||
super();
|
||||
this._origFiles = new Map();
|
||||
this._files = new Map();
|
||||
this._ready = null;
|
||||
this._gazes = [];
|
||||
this._gazePromises = [];
|
||||
this._transforms = [];
|
||||
this._filePromises = new Map();
|
||||
this._customGenerators = new Map();
|
||||
this._dependencies = new Map();
|
||||
}
|
||||
// read-only getters
|
||||
get ready() {
|
||||
return this._ready
|
||||
}
|
||||
get origFiles() {
|
||||
return this._origFiles
|
||||
}
|
||||
get files() {
|
||||
return this._files
|
||||
}
|
||||
get origPaths() {
|
||||
return [ ...(this._filePromises || this._origFiles).keys() ].sort()
|
||||
}
|
||||
// pre-exec (configuration) methods
|
||||
addGaze(gaze, rootPath, read = true) {
|
||||
this._checkBeforeExec('addGaze');
|
||||
this._gazes.push({ gaze, rootPath, read });
|
||||
this._gazePromises.push(new Promise(resolve => gaze.on('ready', resolve)));
|
||||
return this
|
||||
}
|
||||
addTransform(transform) {
|
||||
this._checkBeforeExec('addTransform');
|
||||
this._transforms.push({ type: TRANSFORM, transform });
|
||||
return this
|
||||
}
|
||||
if(condition) {
|
||||
this._checkBeforeExec('if');
|
||||
this._transforms.push({ type: IF, condition });
|
||||
return this
|
||||
}
|
||||
else() {
|
||||
this._checkBeforeExec('else');
|
||||
this._transforms.push({ type: ELSE });
|
||||
return this
|
||||
}
|
||||
end() {
|
||||
this._checkBeforeExec('end');
|
||||
this._transforms.push({ type: END });
|
||||
return this
|
||||
}
|
||||
addGeneratedFile(path, generator) {
|
||||
this._checkBeforeExec('addGeneratedFile');
|
||||
this._customGenerators.set(path, generator);
|
||||
return this
|
||||
}
|
||||
// exec
|
||||
exec() {
|
||||
this._checkBeforeExec('exec');
|
||||
this._ready = new Promise(async resolve => {
|
||||
await Promise.all(this._gazePromises);
|
||||
this._gazePromises = null;
|
||||
let promises = [];
|
||||
for (let { gaze, rootPath, read } of this._gazes) {
|
||||
let watched = gaze.watched();
|
||||
for (let dir in watched) {
|
||||
for (let absolutePath of watched[dir]) {
|
||||
let promise = this._processPhysicalFile(absolutePath, rootPath, read);
|
||||
promises.push(promise);
|
||||
this._filePromises.set(Defiler._relativePath(rootPath, absolutePath), promise);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let path of this._customGenerators.keys()) {
|
||||
let promise = this._handleGeneratedFile(path);
|
||||
promises.push(promise);
|
||||
this._filePromises.set(path, promise);
|
||||
}
|
||||
await Promise.all(promises);
|
||||
for (let { gaze, rootPath, read } of this._gazes) {
|
||||
gaze.on('all', (event, absolutePath) => {
|
||||
if (event === 'deleted') {
|
||||
let path = Defiler._relativePath(rootPath, absolutePath);
|
||||
this._origFiles.delete(path);
|
||||
this._files.delete(path);
|
||||
this.emit('deleted', path);
|
||||
} else {
|
||||
this._processPhysicalFile(absolutePath, rootPath, read);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.on('file', file => {
|
||||
let origins = new Set();
|
||||
for (let [ origin, deps ] of this._dependencies.entries()) {
|
||||
if (deps.has(file.path)) {
|
||||
origins.add(origin);
|
||||
this._dependencies.delete(origin);
|
||||
}
|
||||
}
|
||||
for (let originPath of origins) {
|
||||
this.refile(originPath);
|
||||
}
|
||||
});
|
||||
this._filePromises = null;
|
||||
resolve();
|
||||
});
|
||||
return this
|
||||
}
|
||||
// post-exec methods
|
||||
async use(path, origin) {
|
||||
this._checkAfterExec('use');
|
||||
if (Array.isArray(path)) {
|
||||
return Promise.all(path.map(path => this.use(path, origin)))
|
||||
}
|
||||
if (origin) {
|
||||
if (this._dependencies.has(origin)) {
|
||||
this._dependencies.get(origin).add(path);
|
||||
} else {
|
||||
this._dependencies.set(origin, new Set([ path ]));
|
||||
}
|
||||
}
|
||||
if (this._filePromises) {
|
||||
await this._filePromises.get(path);
|
||||
}
|
||||
return this._files.get(path)
|
||||
}
|
||||
async refile(path) {
|
||||
this._checkAfterExec('refile');
|
||||
if (this._customGenerators.has(path)) {
|
||||
await this._handleGeneratedFile(path);
|
||||
} else if (this._origFiles.has(path)) {
|
||||
await this._processFile(this._origFiles.get(path));
|
||||
}
|
||||
}
|
||||
async addFile(file) {
|
||||
this._checkAfterExec('addFile');
|
||||
let { path } = file;
|
||||
await this._transformFile(file);
|
||||
this._files.set(path, file);
|
||||
this.emit('file', file);
|
||||
}
|
||||
close() {
|
||||
this._checkAfterExec('close');
|
||||
for (let { gaze } of this._gazes) {
|
||||
gaze.close();
|
||||
}
|
||||
}
|
||||
// private methods
|
||||
_checkBeforeExec(methodName) {
|
||||
if (this._ready) {
|
||||
throw new Error(`Cannot call ${methodName} after calling exec`)
|
||||
}
|
||||
}
|
||||
_checkAfterExec(methodName) {
|
||||
if (!this._ready) {
|
||||
throw new Error(`Cannot call ${methodName} before calling exec`)
|
||||
}
|
||||
}
|
||||
async _processPhysicalFile(absolutePath, rootPath, read) {
|
||||
let fileStat = await stat$$1(absolutePath);
|
||||
if (!fileStat.isFile()) {
|
||||
return
|
||||
}
|
||||
let path = Defiler._relativePath(rootPath, absolutePath);
|
||||
let origFile = new File(path);
|
||||
origFile.stat = fileStat;
|
||||
if (read) {
|
||||
origFile.bytes = await readFile$$1(absolutePath);
|
||||
}
|
||||
this._origFiles.set(path, origFile);
|
||||
this.emit('origFile', origFile);
|
||||
await this._processFile(origFile);
|
||||
}
|
||||
async _processFile(origFile) {
|
||||
let file = new File(origFile.path);
|
||||
file.stat = origFile.stat;
|
||||
file.bytes = origFile.bytes;
|
||||
await this._transformFile(file);
|
||||
this._files.set(origFile.path, file);
|
||||
this.emit('file', file);
|
||||
}
|
||||
async _transformFile(file) {
|
||||
let depth = 0;
|
||||
let skipDepth = null;
|
||||
try {
|
||||
for (let { type, transform, condition } of this._transforms) {
|
||||
if (type === TRANSFORM) {
|
||||
if (skipDepth === null) {
|
||||
await transform.call(this, file);
|
||||
}
|
||||
} else if (type === IF) {
|
||||
if (skipDepth === null && !condition.call(this, file)) {
|
||||
skipDepth = depth;
|
||||
}
|
||||
depth++;
|
||||
} else if (type === ELSE) {
|
||||
if (skipDepth === null) {
|
||||
skipDepth = depth - 1;
|
||||
} else if (skipDepth === depth - 1) {
|
||||
skipDepth = null;
|
||||
}
|
||||
} else if (type === END) {
|
||||
depth--;
|
||||
if (skipDepth === depth) {
|
||||
skipDepth = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
this.emit('error', file, err);
|
||||
}
|
||||
}
|
||||
async _handleGeneratedFile(path) {
|
||||
try {
|
||||
let file = new File(path);
|
||||
await this._customGenerators.get(path).call(this, file);
|
||||
await this.addFile(file);
|
||||
} catch (err) {
|
||||
this.emit('error', path, err);
|
||||
}
|
||||
}
|
||||
static _relativePath(rootPath, absolutePath) {
|
||||
return relative(rootPath, absolutePath).replace(/\\/g, '/')
|
||||
}
|
||||
}
|
||||
exports.Defiler = Defiler;
|
||||
exports.File = File;
|
14
package.json
14
package.json
|
@ -2,9 +2,19 @@
|
|||
"name": "@conduitry/defiler",
|
||||
"version": "0.0.1",
|
||||
"description": "A small, strange building block",
|
||||
"main": "index.js",
|
||||
"main": "./dist/index.cjs.js",
|
||||
"module": "./src/index.es.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"build": "rollup -c",
|
||||
"eslint": "eslint",
|
||||
"prettier": "prettier",
|
||||
"lint": "eslint src",
|
||||
"format": "prettier --write --print-width 160 --use-tabs --no-semi --single-quote --trailing-comma es5 \"src/**/*.js\""
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^3.19.0",
|
||||
"prettier": "^1.2.2",
|
||||
"rollup": "^0.41.6"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
6
rollup.config.js
Normal file
6
rollup.config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
export default {
|
||||
entry: 'src/index.js',
|
||||
external: ['events', 'fs', 'path'],
|
||||
sourceMap: true,
|
||||
targets: [{ dest: 'dist/index.cjs.js', format: 'cjs' }, { dest: 'dist/index.es.js', format: 'es' }],
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import EventEmitter from 'events'
|
||||
import { readFile, stat } from 'fs.promise'
|
||||
import { readFile, stat } from 'fs'
|
||||
import { relative } from 'path'
|
||||
|
||||
import File from './File.js'
|
||||
|
||||
let TRANSFORM = Symbol()
|
||||
|
@ -9,7 +10,6 @@ let ELSE = Symbol()
|
|||
let END = Symbol()
|
||||
|
||||
export default class Defiler extends EventEmitter {
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
|
||||
|
@ -41,7 +41,7 @@ export default class Defiler extends EventEmitter {
|
|||
}
|
||||
|
||||
get origPaths() {
|
||||
return [ ...(this._filePromises || this._origFiles).keys() ].sort()
|
||||
return [...(this._filePromises || this._origFiles).keys()].sort()
|
||||
}
|
||||
|
||||
// pre-exec (configuration) methods
|
||||
|
@ -49,7 +49,7 @@ export default class Defiler extends EventEmitter {
|
|||
addGaze(gaze, rootPath, read = true) {
|
||||
this._checkBeforeExec('addGaze')
|
||||
this._gazes.push({ gaze, rootPath, read })
|
||||
this._gazePromises.push(new Promise(resolve => gaze.on('ready', resolve)))
|
||||
this._gazePromises.push(new Promise(res => gaze.on('ready', res)))
|
||||
return this
|
||||
}
|
||||
|
||||
|
@ -87,8 +87,7 @@ export default class Defiler extends EventEmitter {
|
|||
|
||||
exec() {
|
||||
this._checkBeforeExec('exec')
|
||||
this._ready = new Promise(async resolve => {
|
||||
|
||||
this._ready = new Promise(async res => {
|
||||
await Promise.all(this._gazePromises)
|
||||
this._gazePromises = null
|
||||
|
||||
|
@ -128,7 +127,7 @@ export default class Defiler extends EventEmitter {
|
|||
|
||||
this.on('file', file => {
|
||||
let origins = new Set()
|
||||
for (let [ origin, deps ] of this._dependencies.entries()) {
|
||||
for (let [origin, deps] of this._dependencies.entries()) {
|
||||
if (deps.has(file.path)) {
|
||||
origins.add(origin)
|
||||
this._dependencies.delete(origin)
|
||||
|
@ -140,7 +139,7 @@ export default class Defiler extends EventEmitter {
|
|||
})
|
||||
|
||||
this._filePromises = null
|
||||
resolve()
|
||||
res()
|
||||
})
|
||||
|
||||
return this
|
||||
|
@ -157,7 +156,7 @@ export default class Defiler extends EventEmitter {
|
|||
if (this._dependencies.has(origin)) {
|
||||
this._dependencies.get(origin).add(path)
|
||||
} else {
|
||||
this._dependencies.set(origin, new Set([ path ]))
|
||||
this._dependencies.set(origin, new Set([path]))
|
||||
}
|
||||
}
|
||||
if (this._filePromises) {
|
||||
|
@ -205,7 +204,7 @@ export default class Defiler extends EventEmitter {
|
|||
}
|
||||
|
||||
async _processPhysicalFile(absolutePath, rootPath, read) {
|
||||
let fileStat = await stat(absolutePath)
|
||||
let fileStat = await new Promise((res, rej) => stat(absolutePath, (err, data) => (err ? rej(err) : res(data))))
|
||||
if (!fileStat.isFile()) {
|
||||
return
|
||||
}
|
||||
|
@ -213,7 +212,7 @@ export default class Defiler extends EventEmitter {
|
|||
let origFile = new File(path)
|
||||
origFile.stat = fileStat
|
||||
if (read) {
|
||||
origFile.bytes = await readFile(absolutePath)
|
||||
origFile.bytes = await new Promise((res, rej) => readFile(absolutePath, (err, data) => (err ? rej(err) : res(data))))
|
||||
}
|
||||
this._origFiles.set(path, origFile)
|
||||
this.emit('origFile', origFile)
|
||||
|
@ -274,5 +273,4 @@ export default class Defiler extends EventEmitter {
|
|||
static _relativePath(rootPath, absolutePath) {
|
||||
return relative(rootPath, absolutePath).replace(/\\/g, '/')
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
export default class File {
|
||||
|
||||
constructor(path) {
|
||||
this.path = path
|
||||
this.stat = null
|
||||
|
@ -49,5 +48,4 @@ export default class File {
|
|||
this._text = text
|
||||
this._bytes = null
|
||||
}
|
||||
|
||||
}
|
||||
|
|
2
src/index.js
Normal file
2
src/index.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
export { default as File } from './File.js'
|
||||
export { default as Defiler } from './Defiler.js'
|
Loading…
Add table
Reference in a new issue