|
"use strict"; |
|
const read_compat_1 = require("./read_compat"); |
|
const buildOutputWrapper = require("broccoli-output-wrapper"); |
|
const FSMerger = require("fs-merger"); |
|
const BROCCOLI_FEATURES = Object.freeze({ |
|
persistentOutputFlag: true, |
|
sourceDirectories: true, |
|
needsCacheFlag: true, |
|
volatileFlag: true, |
|
trackInputChangesFlag: true, |
|
}); |
|
const PATHS = new WeakMap(); |
|
const FSFACADE = new WeakMap(); |
|
|
|
function isPossibleNode(node) { |
|
if (node === null) { |
|
return false; |
|
} |
|
else if (typeof node === 'string') { |
|
return true; |
|
} |
|
else if (typeof node === 'object' && typeof node.__broccoliGetInfo__ === 'function') { |
|
|
|
return true; |
|
} |
|
else if (typeof node === 'object' && typeof node.read === 'function') { |
|
|
|
return true; |
|
} |
|
else { |
|
return false; |
|
} |
|
} |
|
function _checkBuilderFeatures(builderFeatures) { |
|
if ((typeof builderFeatures !== 'object' && builderFeatures !== null) || |
|
!builderFeatures.persistentOutputFlag || |
|
!builderFeatures.sourceDirectories) { |
|
|
|
throw new Error('BroccoliPlugin: Minimum builderFeatures not met: { persistentOutputFlag: true, sourceDirectories: true }'); |
|
} |
|
} |
|
class Plugin { |
|
constructor(inputNodes, options = {}) { |
|
|
|
|
|
|
|
this._instantiationError = new Error(); |
|
if (options.name != null) { |
|
this._name = options.name; |
|
} |
|
else if (this.constructor && this.constructor.name != null) { |
|
this._name = this.constructor.name; |
|
} |
|
else { |
|
this._name = 'Plugin'; |
|
} |
|
this._annotation = options.annotation; |
|
const label = this._name + (this._annotation != null ? ' (' + this._annotation + ')' : ''); |
|
if (!Array.isArray(inputNodes)) |
|
throw new TypeError(label + ': Expected an array of input nodes (input trees), got ' + inputNodes); |
|
for (let i = 0; i < inputNodes.length; i++) { |
|
if (!isPossibleNode(inputNodes[i])) { |
|
throw new TypeError(label + ': Expected Broccoli node, got ' + inputNodes[i] + ' for inputNodes[' + i + ']'); |
|
} |
|
} |
|
this._baseConstructorCalled = true; |
|
this._inputNodes = inputNodes; |
|
this._persistentOutput = !!options.persistentOutput; |
|
this._needsCache = options.needsCache != null ? !!options.needsCache : true; |
|
this._volatile = !!options.volatile; |
|
this._trackInputChanges = !!options.trackInputChanges; |
|
this._checkOverrides(); |
|
|
|
this.__broccoliFeatures__ = BROCCOLI_FEATURES; |
|
} |
|
|
|
|
|
|
|
|
|
get inputPaths() { |
|
if (!PATHS.has(this)) { |
|
throw new Error('BroccoliPlugin: this.inputPaths is only accessible once the build has begun.'); |
|
} |
|
return PATHS.get(this).inputPaths; |
|
} |
|
|
|
|
|
|
|
|
|
get outputPath() { |
|
if (!PATHS.has(this)) { |
|
throw new Error('BroccoliPlugin: this.outputPath is only accessible once the build has begun.'); |
|
} |
|
return PATHS.get(this).outputPath; |
|
} |
|
get input() { |
|
if (!FSFACADE.has(this)) { |
|
throw new Error('BroccoliPlugin: this.input is only accessible once the build has begun.'); |
|
} |
|
return FSFACADE.get(this).input; |
|
} |
|
get output() { |
|
if (!FSFACADE.has(this)) { |
|
throw new Error('BroccoliPlugin: this.output is only accessible once the build has begun.'); |
|
} |
|
return FSFACADE.get(this).output; |
|
} |
|
_checkOverrides() { |
|
if (typeof this.rebuild === 'function') { |
|
throw new Error('For compatibility, plugins must not define a plugin.rebuild() function'); |
|
} |
|
if (this.read !== Plugin.prototype.read) { |
|
throw new Error('For compatibility, plugins must not define a plugin.read() function'); |
|
} |
|
if (this.cleanup !== Plugin.prototype.cleanup) { |
|
throw new Error('For compatibility, plugins must not define a plugin.cleanup() function'); |
|
} |
|
} |
|
|
|
__broccoliGetInfo__(builderFeatures = { persistentOutputFlag: true, sourceDirectories: true }) { |
|
_checkBuilderFeatures(builderFeatures); |
|
if (!this._baseConstructorCalled) { |
|
throw new Error('Plugin subclasses must call the superclass constructor: Plugin.call(this, inputNodes)'); |
|
} |
|
const instantiationError = this._instantiationError; |
|
const nodeInfo = { |
|
nodeType: 'transform', |
|
inputNodes: this._inputNodes, |
|
setup: this._setup.bind(this), |
|
getCallbackObject: this.getCallbackObject.bind(this), |
|
get instantiationStack() { |
|
|
|
const errorStack = '' + instantiationError.stack; |
|
return errorStack.replace(/[^\n]*\n/, ''); |
|
}, |
|
name: this._name, |
|
annotation: this._annotation, |
|
persistentOutput: this._persistentOutput, |
|
needsCache: this._needsCache, |
|
volatile: this._volatile, |
|
trackInputChanges: this._trackInputChanges, |
|
}; |
|
|
|
|
|
if (!builderFeatures.needsCacheFlag) { |
|
|
|
delete nodeInfo.needsCache; |
|
} |
|
if (!builderFeatures.volatileFlag) { |
|
|
|
delete nodeInfo.volatile; |
|
} |
|
if (!builderFeatures.trackInputChangesFlag) { |
|
|
|
delete nodeInfo.trackInputChanges; |
|
} |
|
return nodeInfo; |
|
} |
|
_setup(builderFeatures, options) { |
|
PATHS.set(this, { |
|
inputPaths: options.inputPaths, |
|
outputPath: options.outputPath, |
|
}); |
|
if (!builderFeatures.needsCacheFlag) { |
|
this.cachePath = this._needsCache ? options.cachePath : undefined; |
|
} |
|
else { |
|
this.cachePath = options.cachePath; |
|
} |
|
FSFACADE.set(this, { |
|
input: new FSMerger(this._inputNodes).fs, |
|
output: buildOutputWrapper(this), |
|
}); |
|
} |
|
toString() { |
|
return '[' + this._name + (this._annotation != null ? ': ' + this._annotation : '') + ']'; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getCallbackObject() { |
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
build() { |
|
throw new Error('Plugin subclasses must implement a .build() function'); |
|
} |
|
|
|
read(readTree) { |
|
if (this._readCompat == null) { |
|
try { |
|
this._initializeReadCompat(); |
|
} |
|
catch (err) { |
|
|
|
this._readCompat = false; |
|
|
|
this._readCompatError = err; |
|
} |
|
} |
|
if (this._readCompatError != null) |
|
throw this._readCompatError; |
|
if (this._readCompat) { |
|
return this._readCompat.read(readTree); |
|
} |
|
} |
|
async cleanup() { |
|
if (this._readCompat) |
|
return this._readCompat.cleanup(); |
|
} |
|
_initializeReadCompat() { |
|
this._readCompat = new read_compat_1.default(this); |
|
} |
|
} |
|
module.exports = Plugin; |
|
|