gulp-emu
Version:
Gulp plugin for ecmarkup
279 lines • 12.9 kB
JavaScript
"use strict";
/*!
* Copyright 2021 Ron Buckton (rbuckton@chronicles.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
const async_countdown_1 = require("@esfx/async-countdown");
const async_queue_1 = require("@esfx/async-queue");
const fs = require("fs");
const path = require("path");
const stream_1 = require("stream");
const ecmarkupModule_1 = require("./ecmarkupModule");
const PluginError = require("plugin-error");
const Vinyl = require("vinyl");
function ecmarkup(opts) {
return new ecmarkup.EcmarkupTransform(opts);
}
(function (ecmarkup) {
class EcmarkupTransform extends stream_1.Transform {
constructor(opts = {}) {
var _a, _b, _c, _d, _e;
super({ objectMode: true });
this._queue = new async_queue_1.AsyncQueue();
this._countdown = new async_countdown_1.AsyncCountdownEvent(1);
this._cache = new Map();
this._ecmarkup = ecmarkupModule_1.getEcmarkupModule();
this._opts = {
js: (_b = (_a = pluck(opts, "js")) !== null && _a !== void 0 ? _a : pluck(opts, "jsOut")) !== null && _b !== void 0 ? _b : false,
css: (_d = (_c = pluck(opts, "css")) !== null && _c !== void 0 ? _c : pluck(opts, "cssOut")) !== null && _d !== void 0 ? _d : false,
biblio: (_e = pluck(opts, "biblio")) !== null && _e !== void 0 ? _e : false,
assetsDir: pluck(opts, "assetsDir"),
};
this._emuOpts = Object.assign({}, opts);
delete this._emuOpts.outfile;
delete this._emuOpts.cssOut;
delete this._emuOpts.jsOut;
delete this._emuOpts.assetsDir;
if (this._emuOpts.multipage) {
if (this._opts.js) {
throw new Error("Cannot use 'multipage' with 'js'");
}
if (this._opts.css) {
throw new Error("Cannot use 'multipage' with 'css'");
}
}
switch (this._ecmarkup.mode) {
case "v18":
// ecmarkup v18 removes `jsOut` & `cssOut` in favor of `assetsDir`
if (this._opts.js) {
throw new Error(`Cannot use 'js' or 'jsOut' option with ecmarkup@v18 or later. Specify 'assets: "external"' and use 'assetsDir' instead.`);
}
if (this._opts.css) {
throw new Error(`Cannot use 'css' or 'cssOut' option with ecmarkup@v18 or later. Specify 'assets: "external"' and use 'assetsDir' instead.`);
}
if (this._emuOpts.multipage) {
this._emuOpts.outfile = "";
}
break;
case "v7":
// ecmarkup v7 adds js and css outputs to `generatedFiles`
if (this._emuOpts.multipage) {
this._emuOpts.outfile = "";
}
if (typeof this._opts.js === "string") {
this._emuOpts.jsOut = this._opts.js;
}
else if (this._opts.js) {
this._emuOpts.jsOut = "ecmarkup.js";
}
else if (this._opts.assetsDir) {
this._emuOpts.jsOut = path.join(this._opts.assetsDir, "ecmarkup.js");
}
if (typeof this._opts.css === "string") {
this._emuOpts.cssOut = this._opts.css;
}
else if (this._opts.css) {
this._emuOpts.cssOut = "ecmarkup.css";
}
else if (this._opts.assetsDir) {
this._emuOpts.cssOut = path.join(this._opts.assetsDir, "ecmarkup.css");
}
break;
case "v3":
if (this._emuOpts.multipage) {
throw new Error("'multipage' requires ecmarkup >= v7.0.0");
}
break;
}
this._waitForWrite();
}
get ecmarkupVersion() {
return this._ecmarkup.version;
}
_write(file, enc, cb) {
if (file.isNull()) {
return cb();
}
if (file.isStream()) {
throw new PluginError("gulp-emu", "Stream not supported.");
}
// cache the file contents
this._cache.set(file.path, file.contents.toString("utf8"));
// put an entry into the queue for the transformation
this._enqueue(this._buildAsync(file));
cb();
}
_flush(cb) {
switch (this._ecmarkup.mode) {
case "v3": {
const files = [];
// add extra files
const css = this._opts.css;
if (css) {
const dest = typeof css === "string" ? css : "elements.css";
files.push({ src: [path.join(this._ecmarkup.path, "css/elements.css")], dest });
}
const js = this._opts.js;
if (js) {
if (typeof js === "string") {
files.push({
src: [
path.join(this._ecmarkup.path, "js/menu.js"),
path.join(this._ecmarkup.path, "js/findLocalReferences.js")
],
dest: js
});
}
else {
files.push({ src: [path.join(this._ecmarkup.path, "js/menu.js")], dest: "menu.js" });
files.push({ src: [path.join(this._ecmarkup.path, "js/findLocalReferences.js")], dest: "findLocalReferences.js" });
}
}
if (files.length) {
this._enqueue(this._readFilesAsync(files));
}
}
}
this._countdown.signal();
this._countdown.wait()
.then(() => cb())
.catch(e => this.emit("error", e));
}
_enqueue(promise) {
this._countdown.add();
this._queue.put(promise);
}
_finishWrite(files) {
for (const file of files) {
this.push(file);
}
this._countdown.signal();
this._waitForWrite();
}
_waitForWrite() {
this._queue.get().then(file => this._finishWrite(file), e => this.emit("error", e));
}
_buildAsync(file) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
const files = [];
const opts = Object.assign({}, this._emuOpts);
(_a = opts.log) !== null && _a !== void 0 ? _a : (opts.log = msg => {
console.log("ecmarkup:", msg);
});
(_b = opts.warn) !== null && _b !== void 0 ? _b : (opts.warn = err => {
console.warn(err.message);
});
const spec = yield this._ecmarkup.module.build(file.path, path => this._readFileAsync(path), this._emuOpts);
if (spec) {
if (this._opts.biblio) {
const dirname = path.dirname(file.path);
const extname = path.extname(file.path);
const basename = path.basename(file.path, extname);
const biblio = new Vinyl({
path: path.join(dirname, basename + ".biblio.json"),
base: file.base,
contents: Buffer.from(JSON.stringify(spec.exportBiblio(), undefined, " "), "utf8")
});
files.push(biblio);
}
switch (this._ecmarkup.mode) {
case "v18":
case "v7":
if (!isMultiPageSpec(spec))
throw new TypeError("Cannot read spec output.");
const dirname = path.dirname(file.path);
for (const [filename, contents] of spec.generatedFiles) {
if (filename === null) {
file.contents = Buffer.from(contents, "utf8");
files.push(file);
}
else {
const output = new Vinyl({
path: path.join(dirname, filename),
base: file.base,
contents: Buffer.from(contents, "utf8")
});
files.push(output);
}
}
break;
case "v3":
if (!isSinglePageSpec(spec))
throw new TypeError("Cannot read spec output.");
file.contents = Buffer.from(spec.toHTML(), "utf8");
files.push(file);
break;
}
}
return files;
});
}
_readFilesAsync(files) {
return __awaiter(this, void 0, void 0, function* () {
return yield Promise.all(files.map(file => this._mergeFilesAsync(file)));
});
}
_mergeFilesAsync(file) {
return __awaiter(this, void 0, void 0, function* () {
const srcContents = yield Promise.all(file.src.map(src => this._readFileAsync(src)));
const contents = srcContents.join("");
const base = path.dirname(file.src[0]);
return new Vinyl({
path: path.join(base, file.dest),
base,
contents: Buffer.from(contents, "utf8")
});
});
}
_readFileAsync(path) {
return __awaiter(this, void 0, void 0, function* () {
let contents = this._cache.get(path);
if (!contents) {
contents = yield readFile(path, "utf8");
this._cache.set(path, contents);
}
return contents;
});
}
}
ecmarkup.EcmarkupTransform = EcmarkupTransform;
})(ecmarkup || (ecmarkup = {}));
function readFile(file, encoding) {
return new Promise((resolve, reject) => {
fs.readFile(file, encoding, (err, data) => err ? reject(err) : resolve(data));
});
}
function pluck(obj, key) {
const value = obj[key];
delete obj[key];
return value;
}
function isSinglePageSpec(spec) {
return typeof spec.toHTML === "function";
}
function isMultiPageSpec(spec) {
return typeof spec.generatedFiles === "object";
}
module.exports = ecmarkup;
//# sourceMappingURL=index.js.map