Very basic hacky CSS support works

This commit is contained in:
David Glasser
2015-04-10 11:59:39 -07:00
parent 6a090e7e34
commit 1db92d27b6
3 changed files with 215 additions and 28 deletions

View File

@@ -1,25 +1,132 @@
var archinfo = require('./archinfo.js');
var colonConverter = require('./colon-converter.js');
var compiler = require('./compiler.js');
var files = require('./files.js');
var _ = require('underscore');
exports.BatchBuildHandlerFactory = function (options, factory) {
// XXX BBP define Plugin.PHASE_LINKER etc
exports.DEFAULT_PHASE = 200;
exports.BatchBuildHandlerFactory = function (options, factoryFunction) {
var self = this;
self.id = options.id;
self.phase = options.phase;
self.extensions = options.extensions.slice();
self.archMatching = !! options.archMatching;
self.archMatching = options.archMatching;
self.isTemplate = !! options.isTemplate;
self.factoryFunction = factoryFunction;
};
_.extend(exports.BatchBuildHandlerFactory.prototype, {
createHandler: function () {
var self = this;
// XXX BBP proper error handling --- this is running user-supplied plugin
// code
var batchHandler = self.factoryFunction();
return new BatchBuildHandler(self, batchHandler);
}
});
var BatchBuildHandler = function (factory, batchHandler) {
var self = this;
// The actual object returned from the user-supplied factory.
self.batchHandler = batchHandler;
self.factory = factory;
};
_.extend(BatchBuildHandler.prototype, {
// XXX BBP full docs
// files is an array of {packageSourceBatch, fileIndex}
run: function (sourceSlots) {
var self = this;
exports.DEFAULT_PHASE = 200;
var inputFiles = _.map(sourceSlots, function (sourceSlot) {
// Register this file as being processed, so it gets replaced by its
// output at the end of the phase.
sourceSlot.activate();
return new InputFile(sourceSlot);
});
// XXX BBP proper error handling --- this is running user-supplied plugin
// code
self.batchHandler.processFilesForTarget(inputFiles);
}
});
// This is the object presented to the user's plugin code.
// XXX BBP actually design its API
// XXX BBP decide if the API always presents / to the code (it probably
// should because you're not supposed to do your own IO anyway)
var InputFile = function (sourceSlot) {
var self = this;
// We use underscored attributes here because this is user-visible code and we
// don't want users to be accessing anything that we don't document.
self._sourceSlot = sourceSlot;
};
_.extend(InputFile.prototype, {
// XXX BBP we should have a better API
xxxContentsAsBuffer: function () {
var self = this;
return self._sourceSlot.inputSource.data;
},
xxxPathInPackage: function () {
var self = this;
return self._sourceSlot.inputSource.path;
},
xxxBasename: function () {
var self = this;
return files.pathBasename(self.xxxPathInPackage());
},
xxxDirname: function () {
var self = this;
return files.pathDirname(self.xxxPathInPackage());
},
// XXX is this null for app?
xxxPackageName: function () {
var self = this;
return self._sourceSlot.packageSourceBatch.unibuild.pkg.name;
},
xxxOutput: function (sourceInfo) {
var self = this;
// XXX BBP validate input!!
self._sourceSlot.outputSource(sourceInfo);
}
});
// XXX BBBP doc
var SourceSlot = function (unibuildSourceInfo, packageSourceBatch) {
var self = this;
self.inputSource = unibuildSourceInfo; // XXX BBP prototype?
// When we activate this sourceSlot, this becomes a list. Then at the end of
// the phase, we replace this object with a new SourceSlot for each output
// source, if any. If this SourceSlot is not activated in a given phase, it
// remains to the next phase.
self.outputSources = null;
self.packageSourceBatch = packageSourceBatch;
};
_.extend(SourceSlot.prototype, {
activate: function () {
var self = this;
if (self.outputSources)
throw Error("activate twice?");
self.outputSources = [];
},
outputSource: function (source) {
var self = this;
if (! self.outputSources)
throw Error("outputSource on unactivated SourceSlot?");
// XXX BBP prototype?
self.outputSources.push(source);
}
});
// XXX BBP ???
var PackageSourceBatch = function (unibuild, processor) {
var self = this;
self.processor = processor;
// XXX BBP maybe give these objects a prototype
self.orderedSources = _.clone(unibuild.sources);
self.sourceSlots = _.map(unibuild.sources, function (source) {
return new SourceSlot(source, self);
});
// Remember the unibuild too, so that we can pull out legacy resources
// (js/css/etc that were placed into an Isopack by an
// registerSourceHandler-style build plugin) later.
@@ -67,6 +174,16 @@ _.extend(PackageSourceBatch.prototype, {
}
var phaseHandlers = activeHandlers[phase];
_.each(factories, function (factory) {
// Ignore factories which aren't for this arch (eg, ignore CSS
// handlers on the server).
// XXX BBP actually this is wrong, we should DROP these sources
// (otherwise they last until the end and cause errors)
// (maybe this should just be handled at getSources time)
if (factory.archMatching &&
! archinfo.matches(self.processor.arch, factory.archMatching)) {
return;
}
_.each(factory.extensions, function (ext) {
if (_.has(phaseHandlers, ext)) {
// XXX BBP use buildmessage
@@ -86,7 +203,7 @@ _.extend(PackageSourceBatch.prototype, {
},
// XXX BBP doc? rename?
eachFileInPhase: function (phase, f) {
eachSourceSlotWithPhaseFactoryId: function (phase, f) {
var self = this;
// No handlers in this phase! Done.
@@ -95,17 +212,71 @@ _.extend(PackageSourceBatch.prototype, {
}
// ext -> factoryId
var phaseHandlers = self.activeHandlers[phase];
_.each(self.orderedSources, function (source, fileIndex) {
var parts = files.pathBasename(source.path).split('.');
_.each(self.sourceSlots, function (sourceSlot) {
var parts = files.pathBasename(sourceSlot.inputSource.path).split('.');
for (var i = 1; i < parts.length; i++) {
var extension = parts.slice(i).join('.');
if (_.has(phaseHandlers, extension)) {
f(fileIndex, phaseHandlers[extension]);
f(sourceSlot, phaseHandlers[extension]);
break;
}
}
// No handlers at this phase for this file! That's OK.
});
},
replaceSourceSlotsWithOutput: function () {
var self = this;
var newSourceSlots = [];
_.each(self.sourceSlots, function (sourceSlot) {
// If this source file wasn't processed in this phase, keep it around
// until the next phase.
if (! sourceSlot.outputSources) {
newSourceSlots.push(sourceSlot);
return;
}
// Otherwise, replace it with SourceSlots for the sources that it output
// (if any).
_.each(sourceSlot.outputSources, function (outputSource) {
newSourceSlots.push(new SourceSlot(outputSource, self));
});
});
self.sourceSlots = newSourceSlots;
},
getResources: function () {
var self = this;
// Start with legacy resources.
var resources = self.unibuild.getResources(self.processor.arch, {
isopackCache: self.processor.isopackCache
});
// XXX BBP this is wrong (eg totally broken for in app)
var serveRoot;
if (self.unibuild.pkg.name) {
serveRoot = files.pathJoin('/packages/', self.unibuild.pkg.name);
} else {
serveRoot = '/';
}
// Now add resources from the batch plugins.
_.each(self.sourceSlots, function (sourceSlot) {
var source = sourceSlot.inputSource;
// XXX BBP make less hacky
if (! source.path.match(/\.css$/)) {
throw Error("we only know how to output CSS! " + source.path);
}
resources.push({
type: "css",
refreshable: true,
data: source.data,
servePath: colonConverter.convert(
files.pathJoin(
serveRoot,
// XXX BBP should we decide in our API that everything is / ?
files.convertToStandardPath(source.path, true)))
});
});
return resources;
}
});
@@ -114,6 +285,7 @@ _.extend(PackageSourceBatch.prototype, {
exports.BatchBuildProcessor = function (options) {
var self = this;
self.unibuilds = options.unibuilds;
self.arch = options.arch;
self.isopackCache = options.isopackCache;
// id -> { factory, handler, phase }
self.handlers = null;
@@ -130,12 +302,7 @@ _.extend(exports.BatchBuildProcessor.prototype, {
if (_.has(self.handlers, factory.id)) {
throw Error("duplicate handler factory ID! " + factory.id);
}
self.handlers[factory.id] = {
factory: factory,
phase: phase,
// XXX BBP better error if this throws?
handler: factory.factory()
};
self.handlers[factory.id] = factory.createHandler();
});
});
});
@@ -147,8 +314,8 @@ _.extend(exports.BatchBuildProcessor.prototype, {
throw Error("call _loadPluginsAndCreateHandlers first");
var phaseSet = {};
_.each(self.handlers, function (handlerInfo) {
phaseSet[handlerInfo.phase] = true;
_.each(self.handlers, function (handler) {
phaseSet[handler.factory.phase] = true;
});
return _.map(phaseSet, function (unused, phaseStr) {
return +phaseStr;
@@ -166,21 +333,37 @@ _.extend(exports.BatchBuildProcessor.prototype, {
var phases = self._getSortedPhases(self.unibuilds);
_.each(phases, function (phase) {
// id -> [{ packageSourceBatch, fileIndex }]
// For each phase, figure out which files go with which handlers.
// These should be disjoint sets (which don't necessarily include
// every file in the target).
// id -> [SourceSlot]
var handlersToRun = {};
_.each(sourceBatches, function (sourceBatch) {
sourceBatch.eachFileInPhase(phase, function (fileIndex, factoryId) {
sourceBatch.eachSourceSlotWithPhaseFactoryId(phase, function (sourceSlot, factoryId) {
if (! _.has(handlersToRun, factoryId)) {
handlersToRun[factoryId] = [];
}
handlersToRun[factoryId].push({
packageSourceBatch: sourceBatch,
fileIndex: fileIndex
});
handlersToRun[factoryId].push(sourceSlot);
});
});
console.log(phase, handlersToRun);
// Now actually run the handlers.
_.each(handlersToRun, function (sourceSlots, factoryId) {
var handler = self.handlers[factoryId];
if (! handler)
throw Error("handler not created?");
handler.run(sourceSlots);
});
// Now that we've run all the handlers, replace all source files with
// their output.
_.each(sourceBatches, function (sourceBatch) {
sourceBatch.replaceSourceSlotsWithOutput();
});
});
return sourceBatches;
}
});

View File

@@ -425,6 +425,8 @@ var Target = function (options) {
// All of the Unibuilds that are to go into this target, in the order
// that they are to be loaded.
self.unibuilds = [];
// XXX BBP ???
self.sourceBatches = null;
// JavaScript files. List of File. They will be loaded at startup in
// the order given.
@@ -664,9 +666,10 @@ _.extend(Target.prototype, {
var self = this;
var processor = new batchBuildPlugin.BatchBuildProcessor({
unibuilds: self.unibuilds,
arch: self.arch,
isopackCache: self.isopackCache
});
processor.runBatchHandlers();
self.sourceBatches = processor.runBatchHandlers();
},
// Process all of the sorted unibuilds (which includes running the JavaScript
@@ -678,7 +681,9 @@ _.extend(Target.prototype, {
var isOs = archinfo.matches(self.arch, "os");
// Copy their resources into the bundle in order
_.each(self.unibuilds, function (unibuild) {
_.each(self.sourceBatches, function (sourceBatch) {
var unibuild = sourceBatch.unibuild;
if (self.cordovaDependencies) {
_.each(unibuild.pkg.cordovaDependencies, function (version, name) {
self._addCordovaDependency(
@@ -693,9 +698,7 @@ _.extend(Target.prototype, {
var isApp = ! unibuild.pkg.name;
// Emit the resources
var resources = unibuild.getResources(self.arch, {
isopackCache: self.isopackCache
});
var resources = sourceBatch.getResources();
// First, find all the assets, so that we can associate them with each js
// resource (for os unibuilds).

View File

@@ -671,6 +671,7 @@ _.extend(Isopack.prototype, {
phaseHandlers.push(
new batchBuildPlugin.BatchBuildHandlerFactory({
id: handlerId,
phase: phase,
extensions: options.extensions,
archMatching: options.archMatching,
isTemplate: options.isTemplate