mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Reduce the amount of shared code between CompilerPlugin's and LinterPlugin's
This commit is contained in:
@@ -1,17 +1,14 @@
|
||||
var archinfo = require('./archinfo.js');
|
||||
var colonConverter = require('./colon-converter.js');
|
||||
var files = require('./files.js');
|
||||
var linker = require('./linker.js');
|
||||
var compiler = require('./compiler.js');
|
||||
var _ = require('underscore');
|
||||
|
||||
exports.BuildPluginDefintion = function (options, factoryFunction) {
|
||||
var self = this;
|
||||
self.id = options.id;
|
||||
self.type = options.type;
|
||||
self.extensions = options.extensions.slice();
|
||||
self.archMatching = options.archMatching;
|
||||
self.isTemplate = !! options.isTemplate;
|
||||
self.buildPluginClass = options.buildPluginClass;
|
||||
self.factoryFunction = factoryFunction;
|
||||
};
|
||||
_.extend(exports.BuildPluginDefintion.prototype, {
|
||||
@@ -20,315 +17,11 @@ _.extend(exports.BuildPluginDefintion.prototype, {
|
||||
// XXX BBP proper error handling --- this is running user-supplied plugin
|
||||
// code
|
||||
var userPlugin = self.factoryFunction();
|
||||
return new BuildPlugin(self, userPlugin);
|
||||
return new self.buildPluginClass(self, userPlugin);
|
||||
},
|
||||
relevantForArch: function (arch) {
|
||||
var self = this;
|
||||
return ! self.archMatching || archinfo.matches(arch, self.archMatching);
|
||||
},
|
||||
getInputFileClass: function () {
|
||||
throw new Error("getInputFileClass should be implemented by a subclass of BuildPluginDefinition");
|
||||
}
|
||||
});
|
||||
|
||||
var BuildPlugin = function (pluginDefinition, userPlugin) {
|
||||
var self = this;
|
||||
// The actual object returned from the user-supplied factory.
|
||||
self.userPlugin = userPlugin;
|
||||
self.pluginDefinition = pluginDefinition;
|
||||
};
|
||||
_.extend(BuildPlugin.prototype, {
|
||||
// XXX BBP full docs
|
||||
run: function (resourceSlots) {
|
||||
var self = this;
|
||||
|
||||
var InputFile = self.pluginDefinition.getInputFileClass();
|
||||
var inputFiles = _.map(resourceSlots, function (resourceSlot) {
|
||||
return new InputFile(resourceSlot);
|
||||
});
|
||||
|
||||
// XXX BBP proper error handling --- this is running user-supplied plugin
|
||||
// code
|
||||
self.userPlugin.processFilesForTarget(inputFiles);
|
||||
}
|
||||
});
|
||||
|
||||
exports.BuildPluginProcessor = function (options) {
|
||||
var self = this;
|
||||
self.type = options.type;
|
||||
self.unibuilds = options.unibuilds;
|
||||
self.arch = options.arch;
|
||||
self.isopackCache = options.isopackCache;
|
||||
// id -> {buildPlugin, resourceSlots}
|
||||
self.buildPlugins = null;
|
||||
};
|
||||
_.extend(exports.BuildPluginProcessor.prototype, {
|
||||
// XXX BBP don't re-instantiate buildPlugins on every rebuild
|
||||
_loadPluginsAndInstantiatePlugins: function () {
|
||||
var self = this;
|
||||
self.buildPlugins = {};
|
||||
_.each(self.unibuilds, function (unibuild) {
|
||||
var isopack = unibuild.pkg;
|
||||
isopack.ensurePluginsInitialized();
|
||||
_.each(isopack.sourceProcessors[self.type], function (buildPlugin, id) {
|
||||
if (_.has(self.buildPlugins, id)) {
|
||||
throw Error("duplicate buildPlugin plugin ID! " + id);
|
||||
}
|
||||
self.buildPlugins[id] = {
|
||||
buildPlugin: buildPlugin.instantiatePlugin(),
|
||||
resourceSlots: []
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
runBuildPlugins: function () {
|
||||
var self = this;
|
||||
self._loadPluginsAndInstantiatePlugins();
|
||||
|
||||
var sourceBatches = _.map(self.unibuilds, function (unibuild) {
|
||||
return new PackageSourceBatch(unibuild, self);
|
||||
});
|
||||
|
||||
// Find out which files go with which buildPlugins.
|
||||
_.each(sourceBatches, function (sourceBatch) {
|
||||
_.each(sourceBatch.resourceSlots, function (resourceSlot) {
|
||||
var buildPlugin = resourceSlot.buildPlugin;
|
||||
// Skip non-sources.
|
||||
if (! buildPlugin)
|
||||
return;
|
||||
|
||||
if (! _.has(self.buildPlugins, buildPlugin.id)) {
|
||||
throw Error("uninstantiated buildPlugin plugin " + buildPlugin.id);
|
||||
}
|
||||
self.buildPlugins[buildPlugin.id].resourceSlots.push(resourceSlot);
|
||||
});
|
||||
});
|
||||
|
||||
// Now actually run the handlers.
|
||||
_.each(self.buildPlugins, function (buildPluginInfo, id) {
|
||||
var buildPlugin = buildPluginInfo.buildPlugin;
|
||||
var resourceSlots = buildPluginInfo.resourceSlots;
|
||||
// Don't run buildPlugins with no files.
|
||||
if (! resourceSlots.length)
|
||||
return;
|
||||
|
||||
buildPlugin.run(resourceSlots);
|
||||
});
|
||||
|
||||
return sourceBatches;
|
||||
}
|
||||
});
|
||||
|
||||
// XXX BBP doc
|
||||
var ResourceSlot = function (unibuildResourceInfo,
|
||||
buildPlugin,
|
||||
packageSourceBatch) {
|
||||
var self = this;
|
||||
self.inputResource = unibuildResourceInfo; // XXX BBP prototype?
|
||||
self.outputResources = [];
|
||||
self.buildPlugin = buildPlugin;
|
||||
self.packageSourceBatch = packageSourceBatch;
|
||||
|
||||
if (self.inputResource.type === "source") {
|
||||
if (! buildPlugin) {
|
||||
throw Error("no buildPlugin plugin for source? " +
|
||||
JSON.stringify(unibuildResourceInfo));
|
||||
}
|
||||
} else {
|
||||
if (buildPlugin) {
|
||||
throw Error("buildPlugin plugin for non-source? " +
|
||||
JSON.stringify(unibuildResourceInfo));
|
||||
}
|
||||
// Any resource that isn't handled by buildPlugin plugins just gets passed
|
||||
// through.
|
||||
self.outputResources.push(self.inputResource);
|
||||
}
|
||||
};
|
||||
_.extend(ResourceSlot.prototype, {
|
||||
// XXX BBP check args
|
||||
addStylesheet: function (options) {
|
||||
var self = this;
|
||||
if (! self.buildPlugin)
|
||||
throw Error("addStylesheet on non-source ResourceSlot?");
|
||||
|
||||
// XXX BBP this is wrong (eg totally broken for in app) and is in the wrong
|
||||
// place
|
||||
var unibuild = self.packageSourceBatch.unibuild;
|
||||
var serveRoot;
|
||||
if (unibuild.pkg.name) {
|
||||
serveRoot = files.pathJoin('/packages/', unibuild.pkg.name);
|
||||
} else {
|
||||
serveRoot = '/';
|
||||
}
|
||||
|
||||
// XXX BBP prototype?
|
||||
self.outputResources.push({
|
||||
type: "css",
|
||||
refreshable: true,
|
||||
data: new Buffer(files.convertToStandardLineEndings(options.data), 'utf8'),
|
||||
servePath: colonConverter.convert(
|
||||
files.pathJoin(
|
||||
serveRoot,
|
||||
// XXX BBP should we decide in our API that everything is / ?
|
||||
files.convertToStandardPath(options.path, true)))
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// XXX BBP ???
|
||||
var PackageSourceBatch = function (unibuild, processor) {
|
||||
var self = this;
|
||||
self.type = processor.type;
|
||||
self.unibuild = unibuild;
|
||||
self.processor = processor;
|
||||
var buildPluginsByExtension = self._getBuildPluginsByExtension();
|
||||
self.resourceSlots = _.map(unibuild.resources, function (resource) {
|
||||
var buildPlugin = null;
|
||||
if (resource.type === "source") {
|
||||
var basename = files.pathBasename(resource.path);
|
||||
var parts = basename.split('.');
|
||||
for (var i = 1; i < parts.length; i++) {
|
||||
var extension = parts.slice(i).join('.');
|
||||
if (_.has(buildPluginsByExtension, extension)) {
|
||||
buildPlugin = buildPluginsByExtension[extension];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! buildPlugin) {
|
||||
// XXX BBP better error handling
|
||||
throw Error("no plugin found for " + resource.path);
|
||||
}
|
||||
}
|
||||
return new ResourceSlot(resource, buildPlugin, self);
|
||||
});
|
||||
};
|
||||
_.extend(PackageSourceBatch.prototype, {
|
||||
_getBuildPluginsByExtension: function () {
|
||||
var self = this;
|
||||
var isopack = self.unibuild.pkg;
|
||||
// Packages always get plugins from themselves.
|
||||
var activePluginPackages = [isopack];
|
||||
|
||||
// We don't use plugins from weak dependencies, because the ability to build
|
||||
// a certain type of file shouldn't depend on whether or not some unrelated
|
||||
// package in the target has a dependency. And we skip unordered
|
||||
// dependencies, because it's not going to work to have circular build-time
|
||||
// dependencies.
|
||||
//
|
||||
// eachUsedUnibuild takes care of pulling in implied dependencies for us
|
||||
// (eg, templating from standard-app-packages).
|
||||
//
|
||||
// We pass archinfo.host here, not self.arch, because it may be more
|
||||
// specific, and because plugins always have to run on the host
|
||||
// architecture.
|
||||
compiler.eachUsedUnibuild({
|
||||
dependencies: self.unibuild.uses,
|
||||
arch: archinfo.host(),
|
||||
isopackCache: self.processor.isopackCache,
|
||||
skipUnordered: true
|
||||
}, function (otherUnibuild) {
|
||||
if (! _.isEmpty(otherUnibuild.pkg.plugins)) {
|
||||
activePluginPackages.push(otherUnibuild.pkg);
|
||||
}
|
||||
});
|
||||
|
||||
activePluginPackages = _.uniq(activePluginPackages);
|
||||
|
||||
var buildPluginsByExtension = {};
|
||||
_.each(activePluginPackages, function (otherPkg) {
|
||||
// self.type is "compiler" or "linter" or similar
|
||||
_.each(otherPkg.sourceProcessors[self.type], function (buildPlugin, id) {
|
||||
if (! buildPlugin.relevantForArch(self.processor.arch)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_.each(buildPlugin.extensions, function (ext) {
|
||||
if (_.has(buildPluginsByExtension, ext)) {
|
||||
// XXX BBP use buildmessage
|
||||
throw Error("duplicate extension " + JSON.stringify({
|
||||
package: isopack.name,
|
||||
ext: ext
|
||||
}));
|
||||
}
|
||||
buildPluginsByExtension[ext] = buildPlugin;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return buildPluginsByExtension;
|
||||
},
|
||||
|
||||
getResources: function () {
|
||||
var self = this;
|
||||
var resources = Array.prototype.concat.apply(
|
||||
[],
|
||||
_.pluck(self.resourceSlots, 'outputResources'));
|
||||
return resources.concat(self._getPrelinkedJsResources());
|
||||
},
|
||||
|
||||
// XXX BBP copied from Unibuild.getResources, which should get deleted
|
||||
// XXX BBP this should also support JS resources produced by buildPlugin plugins
|
||||
_getPrelinkedJsResources: function () {
|
||||
var self = this;
|
||||
var isopackCache = self.processor.isopackCache;
|
||||
var bundleArch = self.processor.arch;
|
||||
|
||||
if (! archinfo.matches(bundleArch, self.unibuild.arch))
|
||||
throw new Error(
|
||||
"unibuild of arch '" + self.unibuild.arch + "' does not support '" +
|
||||
bundleArch + "'?");
|
||||
|
||||
// Compute imports by merging the exports of all of the packages we
|
||||
// use. Note that in the case of conflicting symbols, later packages get
|
||||
// precedence.
|
||||
//
|
||||
// We don't get imports from unordered dependencies (since they may not be
|
||||
// defined yet) or from weak/debugOnly dependencies (because the meaning of
|
||||
// a name shouldn't be affected by the non-local decision of whether or not
|
||||
// an unrelated package in the target depends on something).
|
||||
var imports = {}; // map from symbol to supplying package name
|
||||
|
||||
var addImportsForUnibuild = function (depUnibuild) {
|
||||
_.each(depUnibuild.packageVariables, function (symbol) {
|
||||
// Slightly hacky implementation of test-only exports.
|
||||
if (symbol.export === true ||
|
||||
(symbol.export === "tests" && self.unibuild.pkg.isTest))
|
||||
imports[symbol.name] = depUnibuild.pkg.name;
|
||||
});
|
||||
};
|
||||
compiler.eachUsedUnibuild({
|
||||
dependencies: self.unibuild.uses,
|
||||
arch: bundleArch,
|
||||
isopackCache: isopackCache,
|
||||
skipUnordered: true,
|
||||
skipDebugOnly: true
|
||||
}, addImportsForUnibuild);
|
||||
|
||||
// Phase 2 link
|
||||
var isApp = ! self.unibuild.pkg.name;
|
||||
var files = linker.link({
|
||||
imports: imports,
|
||||
useGlobalNamespace: isApp,
|
||||
// XXX report an error if there is a package called global-imports
|
||||
importStubServePath: isApp && '/packages/global-imports.js',
|
||||
prelinkFiles: self.unibuild.prelinkFiles,
|
||||
packageVariables: self.unibuild.packageVariables,
|
||||
includeSourceMapInstructions: archinfo.matches(self.unibuild.arch, "web"),
|
||||
name: self.unibuild.pkg.name || null
|
||||
});
|
||||
|
||||
// Add each output as a resource
|
||||
var jsResources = _.map(files, function (file) {
|
||||
return {
|
||||
type: "js",
|
||||
data: new Buffer(file.source, 'utf8'), // XXX encoding
|
||||
servePath: file.servePath,
|
||||
sourceMap: file.sourceMap
|
||||
};
|
||||
});
|
||||
return jsResources;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -337,25 +30,24 @@ _.extend(PackageSourceBatch.prototype, {
|
||||
// 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)
|
||||
exports.InputFile = function (resourceSlot) {
|
||||
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._resourceSlot = resourceSlot;
|
||||
};
|
||||
_.extend(exports.InputFile.prototype, {
|
||||
// XXX BBP refine this API and document it
|
||||
getContentsAsBuffer: function () {
|
||||
var self = this;
|
||||
return self._resourceSlot.inputResource.data;
|
||||
throw new Error("Not Implemented");
|
||||
},
|
||||
// XXX is this null for app?
|
||||
getPackageName: function () {
|
||||
throw new Error("Not Implemented");
|
||||
},
|
||||
getPathInPackage: function () {
|
||||
throw new Error("Not Implemented");
|
||||
},
|
||||
|
||||
getContentsAsString: function () {
|
||||
var self = this;
|
||||
return self.getContentsAsBuffer().toString('utf8');
|
||||
},
|
||||
getPathInPackage: function () {
|
||||
var self = this;
|
||||
return self._resourceSlot.inputResource.path;
|
||||
},
|
||||
getBasename: function () {
|
||||
var self = this;
|
||||
return files.pathBasename(self.getPathInPackage());
|
||||
@@ -364,13 +56,15 @@ _.extend(exports.InputFile.prototype, {
|
||||
var self = this;
|
||||
return files.pathDirname(self.getPathInPackage());
|
||||
},
|
||||
// XXX is this null for app?
|
||||
getPackageName: function () {
|
||||
var self = this;
|
||||
return self._resourceSlot.packageSourceBatch.unibuild.pkg.name;
|
||||
},
|
||||
error: function (options) {
|
||||
var self = this;
|
||||
var relPath = self.getPathInPackage();
|
||||
buildmessage.error(options.message || ("error building " + relPath), {
|
||||
file: options.sourcePath || relPath,
|
||||
line: options.line ? options.line : undefined,
|
||||
column: options.column ? options.column : undefined,
|
||||
func: options.func ? options.func : undefined
|
||||
});
|
||||
// XXX BBP handle errors here
|
||||
}
|
||||
});
|
||||
|
||||
@@ -166,7 +166,7 @@ var Profile = require('./profile.js').Profile;
|
||||
var compiler = require('./compiler.js');
|
||||
var packageVersionParser = require('./package-version-parser.js');
|
||||
var colonConverter = require('./colon-converter.js');
|
||||
var buildPluginModule = require('./build-plugin.js');
|
||||
var compilerPluginModule = require('./compiler-plugin.js');
|
||||
|
||||
// files to ignore when bundling. node has no globs, so use regexps
|
||||
exports.ignoreFiles = [
|
||||
@@ -664,8 +664,7 @@ _.extend(Target.prototype, {
|
||||
|
||||
_runCompilerPlugins: function () {
|
||||
var self = this;
|
||||
var processor = new buildPluginModule.BuildPluginProcessor({
|
||||
type: "compiler",
|
||||
var processor = new compilerPluginModule.CompilerPluginProcessor({
|
||||
unibuilds: self.unibuilds,
|
||||
arch: self.arch,
|
||||
isopackCache: self.isopackCache
|
||||
|
||||
@@ -1,29 +1,122 @@
|
||||
var archinfo = require('./archinfo.js');
|
||||
var buildPluginModule = require('./build-plugin.js');
|
||||
var colonConverter = require('./colon-converter.js');
|
||||
var files = require('./files.js');
|
||||
var compiler = require('./compiler.js');
|
||||
var linker = require('./linker.js');
|
||||
var util = require('util');
|
||||
var _ = require('underscore');
|
||||
|
||||
exports.CompilerPluginDefinition = function (options, factory) {
|
||||
buildPluginModule.BuildPluginDefintion.call(this, _.extend({
|
||||
type: "compiler"
|
||||
}, options), factory);
|
||||
exports.CompilerPlugin = function (pluginDefinition, userPlugin) {
|
||||
var self = this;
|
||||
// The actual object returned from the user-supplied factory.
|
||||
self.userPlugin = userPlugin;
|
||||
self.pluginDefinition = pluginDefinition;
|
||||
};
|
||||
_.extend(exports.CompilerPlugin.prototype, {
|
||||
// XXX BBP full docs
|
||||
run: function (resourceSlots) {
|
||||
var self = this;
|
||||
|
||||
util.inherits(exports.CompilerPluginDefinition, buildPluginModule.BuildPluginDefintion);
|
||||
var inputFiles = _.map(resourceSlots, function (resourceSlot) {
|
||||
return new InputFile(resourceSlot);
|
||||
});
|
||||
|
||||
_.extend(exports.CompilerPluginDefinition.prototype, {
|
||||
getInputFileClass: function () {
|
||||
return InputFile;
|
||||
// XXX BBP proper error handling --- this is running user-supplied plugin
|
||||
// code
|
||||
self.userPlugin.processFilesForTarget(inputFiles);
|
||||
}
|
||||
});
|
||||
|
||||
var InputFile = function () {
|
||||
buildPluginModule.InputFile.apply(this, arguments);
|
||||
|
||||
exports.CompilerPluginProcessor = function (options) {
|
||||
var self = this;
|
||||
self.unibuilds = options.unibuilds;
|
||||
self.arch = options.arch;
|
||||
self.isopackCache = options.isopackCache;
|
||||
// id -> {buildPlugin, resourceSlots}
|
||||
self.buildPlugins = null;
|
||||
};
|
||||
_.extend(exports.CompilerPluginProcessor.prototype, {
|
||||
// XXX BBP don't re-instantiate buildPlugins on every rebuild
|
||||
_loadPluginsAndInstantiatePlugins: function () {
|
||||
var self = this;
|
||||
self.buildPlugins = {};
|
||||
_.each(self.unibuilds, function (unibuild) {
|
||||
var isopack = unibuild.pkg;
|
||||
isopack.ensurePluginsInitialized();
|
||||
_.each(isopack.sourceProcessors.compiler, function (buildPlugin, id) {
|
||||
if (_.has(self.buildPlugins, id)) {
|
||||
throw Error("duplicate buildPlugin plugin ID! " + id);
|
||||
}
|
||||
self.buildPlugins[id] = {
|
||||
buildPlugin: buildPlugin.instantiatePlugin(),
|
||||
resourceSlots: []
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
runBuildPlugins: function () {
|
||||
var self = this;
|
||||
self._loadPluginsAndInstantiatePlugins();
|
||||
|
||||
var sourceBatches = _.map(self.unibuilds, function (unibuild) {
|
||||
return new PackageSourceBatch(unibuild, self);
|
||||
});
|
||||
|
||||
// Find out which files go with which buildPlugins.
|
||||
_.each(sourceBatches, function (sourceBatch) {
|
||||
_.each(sourceBatch.resourceSlots, function (resourceSlot) {
|
||||
var buildPlugin = resourceSlot.buildPlugin;
|
||||
// Skip non-sources.
|
||||
if (! buildPlugin)
|
||||
return;
|
||||
|
||||
if (! _.has(self.buildPlugins, buildPlugin.id)) {
|
||||
throw Error("uninstantiated buildPlugin plugin " + buildPlugin.id);
|
||||
}
|
||||
self.buildPlugins[buildPlugin.id].resourceSlots.push(resourceSlot);
|
||||
});
|
||||
});
|
||||
|
||||
// Now actually run the handlers.
|
||||
_.each(self.buildPlugins, function (buildPluginInfo, id) {
|
||||
var buildPlugin = buildPluginInfo.buildPlugin;
|
||||
var resourceSlots = buildPluginInfo.resourceSlots;
|
||||
// Don't run buildPlugins with no files.
|
||||
if (! resourceSlots.length)
|
||||
return;
|
||||
|
||||
buildPlugin.run(resourceSlots);
|
||||
});
|
||||
|
||||
return sourceBatches;
|
||||
}
|
||||
});
|
||||
|
||||
var InputFile = function (resourceSlot) {
|
||||
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._resourceSlot = resourceSlot;
|
||||
};
|
||||
util.inherits(InputFile, buildPluginModule.InputFile);
|
||||
|
||||
_.extend(InputFile.prototype, {
|
||||
getContentsAsBuffer: function () {
|
||||
var self = this;
|
||||
return self._resourceSlot.inputResource.data;
|
||||
},
|
||||
// XXX is this null for app?
|
||||
getPackageName: function () {
|
||||
var self = this;
|
||||
return self._resourceSlot.packageSourceBatch.unibuild.pkg.name;
|
||||
},
|
||||
getPathInPackage: function () {
|
||||
var self = this;
|
||||
return self._resourceSlot.inputResource.path;
|
||||
},
|
||||
|
||||
// XXX BBP remove these, they are duplicated in build-plugin.js
|
||||
xxxContentsAsBuffer: function () {
|
||||
var self = this;
|
||||
@@ -53,3 +146,213 @@ _.extend(InputFile.prototype, {
|
||||
}
|
||||
});
|
||||
|
||||
// XXX BBP doc
|
||||
var ResourceSlot = function (unibuildResourceInfo,
|
||||
buildPlugin,
|
||||
packageSourceBatch) {
|
||||
var self = this;
|
||||
self.inputResource = unibuildResourceInfo; // XXX BBP prototype?
|
||||
self.outputResources = [];
|
||||
self.buildPlugin = buildPlugin;
|
||||
self.packageSourceBatch = packageSourceBatch;
|
||||
|
||||
if (self.inputResource.type === "source") {
|
||||
if (! buildPlugin) {
|
||||
throw Error("no buildPlugin plugin for source? " +
|
||||
JSON.stringify(unibuildResourceInfo));
|
||||
}
|
||||
} else {
|
||||
if (buildPlugin) {
|
||||
throw Error("buildPlugin plugin for non-source? " +
|
||||
JSON.stringify(unibuildResourceInfo));
|
||||
}
|
||||
// Any resource that isn't handled by buildPlugin plugins just gets passed
|
||||
// through.
|
||||
self.outputResources.push(self.inputResource);
|
||||
}
|
||||
};
|
||||
_.extend(ResourceSlot.prototype, {
|
||||
// XXX BBP check args
|
||||
addStylesheet: function (options) {
|
||||
var self = this;
|
||||
if (! self.buildPlugin)
|
||||
throw Error("addStylesheet on non-source ResourceSlot?");
|
||||
|
||||
// XXX BBP this is wrong (eg totally broken for in app) and is in the wrong
|
||||
// place
|
||||
var unibuild = self.packageSourceBatch.unibuild;
|
||||
var serveRoot;
|
||||
if (unibuild.pkg.name) {
|
||||
serveRoot = files.pathJoin('/packages/', unibuild.pkg.name);
|
||||
} else {
|
||||
serveRoot = '/';
|
||||
}
|
||||
|
||||
// XXX BBP prototype?
|
||||
self.outputResources.push({
|
||||
type: "css",
|
||||
refreshable: true,
|
||||
data: new Buffer(files.convertToStandardLineEndings(options.data), 'utf8'),
|
||||
servePath: colonConverter.convert(
|
||||
files.pathJoin(
|
||||
serveRoot,
|
||||
// XXX BBP should we decide in our API that everything is / ?
|
||||
files.convertToStandardPath(options.path, true)))
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// XXX BBP ???
|
||||
var PackageSourceBatch = function (unibuild, processor) {
|
||||
var self = this;
|
||||
self.unibuild = unibuild;
|
||||
self.processor = processor;
|
||||
var buildPluginsByExtension = self._getBuildPluginsByExtension();
|
||||
self.resourceSlots = _.map(unibuild.resources, function (resource) {
|
||||
var buildPlugin = null;
|
||||
if (resource.type === "source") {
|
||||
var basename = files.pathBasename(resource.path);
|
||||
var parts = basename.split('.');
|
||||
for (var i = 1; i < parts.length; i++) {
|
||||
var extension = parts.slice(i).join('.');
|
||||
if (_.has(buildPluginsByExtension, extension)) {
|
||||
buildPlugin = buildPluginsByExtension[extension];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (! buildPlugin) {
|
||||
// XXX BBP better error handling
|
||||
throw Error("no plugin found for " + resource.path);
|
||||
}
|
||||
}
|
||||
return new ResourceSlot(resource, buildPlugin, self);
|
||||
});
|
||||
};
|
||||
_.extend(PackageSourceBatch.prototype, {
|
||||
_getBuildPluginsByExtension: function () {
|
||||
var self = this;
|
||||
var isopack = self.unibuild.pkg;
|
||||
// Packages always get plugins from themselves.
|
||||
var activePluginPackages = [isopack];
|
||||
|
||||
// We don't use plugins from weak dependencies, because the ability to build
|
||||
// a certain type of file shouldn't depend on whether or not some unrelated
|
||||
// package in the target has a dependency. And we skip unordered
|
||||
// dependencies, because it's not going to work to have circular build-time
|
||||
// dependencies.
|
||||
//
|
||||
// eachUsedUnibuild takes care of pulling in implied dependencies for us
|
||||
// (eg, templating from standard-app-packages).
|
||||
//
|
||||
// We pass archinfo.host here, not self.arch, because it may be more
|
||||
// specific, and because plugins always have to run on the host
|
||||
// architecture.
|
||||
compiler.eachUsedUnibuild({
|
||||
dependencies: self.unibuild.uses,
|
||||
arch: archinfo.host(),
|
||||
isopackCache: self.processor.isopackCache,
|
||||
skipUnordered: true
|
||||
}, function (otherUnibuild) {
|
||||
if (! _.isEmpty(otherUnibuild.pkg.plugins)) {
|
||||
activePluginPackages.push(otherUnibuild.pkg);
|
||||
}
|
||||
});
|
||||
|
||||
activePluginPackages = _.uniq(activePluginPackages);
|
||||
|
||||
var buildPluginsByExtension = {};
|
||||
_.each(activePluginPackages, function (otherPkg) {
|
||||
// self.type is "compiler" or "linter" or similar
|
||||
_.each(otherPkg.sourceProcessors.compiler, function (buildPlugin, id) {
|
||||
if (! buildPlugin.relevantForArch(self.processor.arch)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_.each(buildPlugin.extensions, function (ext) {
|
||||
if (_.has(buildPluginsByExtension, ext)) {
|
||||
// XXX BBP use buildmessage
|
||||
throw Error("duplicate extension " + JSON.stringify({
|
||||
package: isopack.name,
|
||||
ext: ext
|
||||
}));
|
||||
}
|
||||
buildPluginsByExtension[ext] = buildPlugin;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return buildPluginsByExtension;
|
||||
},
|
||||
|
||||
getResources: function () {
|
||||
var self = this;
|
||||
var resources = Array.prototype.concat.apply(
|
||||
[],
|
||||
_.pluck(self.resourceSlots, 'outputResources'));
|
||||
return resources.concat(self._getPrelinkedJsResources());
|
||||
},
|
||||
|
||||
// XXX BBP copied from Unibuild.getResources, which should get deleted
|
||||
// XXX BBP this should also support JS resources produced by buildPlugin plugins
|
||||
_getPrelinkedJsResources: function () {
|
||||
var self = this;
|
||||
var isopackCache = self.processor.isopackCache;
|
||||
var bundleArch = self.processor.arch;
|
||||
|
||||
if (! archinfo.matches(bundleArch, self.unibuild.arch))
|
||||
throw new Error(
|
||||
"unibuild of arch '" + self.unibuild.arch + "' does not support '" +
|
||||
bundleArch + "'?");
|
||||
|
||||
// Compute imports by merging the exports of all of the packages we
|
||||
// use. Note that in the case of conflicting symbols, later packages get
|
||||
// precedence.
|
||||
//
|
||||
// We don't get imports from unordered dependencies (since they may not be
|
||||
// defined yet) or from weak/debugOnly dependencies (because the meaning of
|
||||
// a name shouldn't be affected by the non-local decision of whether or not
|
||||
// an unrelated package in the target depends on something).
|
||||
var imports = {}; // map from symbol to supplying package name
|
||||
|
||||
var addImportsForUnibuild = function (depUnibuild) {
|
||||
_.each(depUnibuild.packageVariables, function (symbol) {
|
||||
// Slightly hacky implementation of test-only exports.
|
||||
if (symbol.export === true ||
|
||||
(symbol.export === "tests" && self.unibuild.pkg.isTest))
|
||||
imports[symbol.name] = depUnibuild.pkg.name;
|
||||
});
|
||||
};
|
||||
compiler.eachUsedUnibuild({
|
||||
dependencies: self.unibuild.uses,
|
||||
arch: bundleArch,
|
||||
isopackCache: isopackCache,
|
||||
skipUnordered: true,
|
||||
skipDebugOnly: true
|
||||
}, addImportsForUnibuild);
|
||||
|
||||
// Phase 2 link
|
||||
var isApp = ! self.unibuild.pkg.name;
|
||||
var files = linker.link({
|
||||
imports: imports,
|
||||
useGlobalNamespace: isApp,
|
||||
// XXX report an error if there is a package called global-imports
|
||||
importStubServePath: isApp && '/packages/global-imports.js',
|
||||
prelinkFiles: self.unibuild.prelinkFiles,
|
||||
packageVariables: self.unibuild.packageVariables,
|
||||
includeSourceMapInstructions: archinfo.matches(self.unibuild.arch, "web"),
|
||||
name: self.unibuild.pkg.name || null
|
||||
});
|
||||
|
||||
// Add each output as a resource
|
||||
var jsResources = _.map(files, function (file) {
|
||||
return {
|
||||
type: "js",
|
||||
data: new Buffer(file.source, 'utf8'), // XXX encoding
|
||||
servePath: file.servePath,
|
||||
sourceMap: file.sourceMap
|
||||
};
|
||||
});
|
||||
return jsResources;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -231,6 +231,7 @@ var compileUnibuild = function (options) {
|
||||
// XXX BBP redoc
|
||||
var allHandlersWithPkgs = {};
|
||||
var compilerPluginsByExtension = {};
|
||||
var linterPluginsByExtension = {};
|
||||
var sourceExtensions = {}; // maps source extensions to isTemplate
|
||||
|
||||
sourceExtensions['js'] = false;
|
||||
@@ -307,6 +308,14 @@ var compileUnibuild = function (options) {
|
||||
sourceExtensions[ext] = compilerPlugin.isTemplate;
|
||||
});
|
||||
});
|
||||
|
||||
// Iterate over the linters
|
||||
_.each(otherPkg.sourceProcessors.linter, function (linterPlugin, id) {
|
||||
_.each(linterPlugin.extensions, function (ext) {
|
||||
linterPluginsByExtension[ext] = linterPluginsByExtension[ext] || [];
|
||||
linterPluginsByExtension[ext].push(linterPlugin);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// *** Determine source files
|
||||
@@ -362,11 +371,7 @@ var compileUnibuild = function (options) {
|
||||
return JSON.stringify(srcmap);
|
||||
};
|
||||
|
||||
_.each(sourceItems, function (source) {
|
||||
var relPath = source.relPath;
|
||||
var fileOptions = _.clone(source.fileOptions) || {};
|
||||
var absPath = files.pathResolve(inputSourceArch.pkg.sourceRoot, relPath);
|
||||
var filename = files.pathBasename(relPath);
|
||||
var wrappedSourceItems = _.map(sourceItems, function (source) {
|
||||
var fileWatchSet = new watch.WatchSet;
|
||||
// readAndWatchFileWithHash returns an object carrying a buffer with the
|
||||
// file-contents. The buffer contains the original data of the file (no EOL
|
||||
@@ -374,8 +379,35 @@ var compileUnibuild = function (options) {
|
||||
// We don't put this into the unibuild's watchSet immediately since we want
|
||||
// to avoid putting it there if it turns out not to be relevant to our
|
||||
// arch.
|
||||
var absPath = files.pathResolve(
|
||||
inputSourceArch.pkg.sourceRoot, source.relPath);
|
||||
var file = watch.readAndWatchFileWithHash(fileWatchSet, absPath);
|
||||
var contents = file.contents;
|
||||
|
||||
Console.nudge(true);
|
||||
|
||||
return {
|
||||
relPath: source.relPath,
|
||||
watchset: fileWatchSet,
|
||||
contents: file.contents,
|
||||
hash: file.hash,
|
||||
source: source,
|
||||
'package': isopk.name
|
||||
};
|
||||
});
|
||||
|
||||
_.each(linterPluginsByExtension, function (linters, ext) {
|
||||
// XXX would run linters here
|
||||
});
|
||||
|
||||
_.each(wrappedSourceItems, function (wrappedSource) {
|
||||
var source = wrappedSource.source;
|
||||
var relPath = source.relPath;
|
||||
var fileOptions = _.clone(source.fileOptions) || {};
|
||||
var absPath = files.pathResolve(inputSourceArch.pkg.sourceRoot, relPath);
|
||||
var filename = files.pathBasename(relPath);
|
||||
var contents = wrappedSource.contents;
|
||||
var hash = wrappedSource.hash;
|
||||
var fileWatchSet = wrappedSource.watchset;
|
||||
|
||||
Console.nudge(true);
|
||||
|
||||
@@ -398,7 +430,7 @@ var compileUnibuild = function (options) {
|
||||
|
||||
// Find the handler for source files with this extension.
|
||||
var handler = null;
|
||||
var isCompilerPluginSource = false;
|
||||
var isBuildPluginSource = false;
|
||||
if (! fileOptions.isAsset) {
|
||||
var parts = filename.split('.');
|
||||
// don't use iteration functions, so we can return/break
|
||||
@@ -412,7 +444,7 @@ var compileUnibuild = function (options) {
|
||||
// the server.)
|
||||
return;
|
||||
}
|
||||
isCompilerPluginSource = true;
|
||||
isBuildPluginSource = true;
|
||||
break;
|
||||
}
|
||||
if (_.has(allHandlersWithPkgs, extension)) {
|
||||
@@ -425,14 +457,14 @@ var compileUnibuild = function (options) {
|
||||
// OK, this is relevant to this arch, so watch it.
|
||||
watchSet.merge(fileWatchSet);
|
||||
|
||||
if (isCompilerPluginSource) {
|
||||
if (isBuildPluginSource) {
|
||||
// This is source used by a new-style compiler plugin; it will be fully
|
||||
// processed later in the bundler.
|
||||
resources.push({
|
||||
type: "source",
|
||||
data: contents,
|
||||
path: relPath,
|
||||
hash: file.hash
|
||||
hash: hash
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -443,7 +475,7 @@ var compileUnibuild = function (options) {
|
||||
//
|
||||
// XXX This is pretty confusing, especially if you've
|
||||
// accidentally forgotten a plugin -- revisit?
|
||||
addAsset(contents, relPath, file.hash);
|
||||
addAsset(contents, relPath, hash);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -596,7 +628,7 @@ var compileUnibuild = function (options) {
|
||||
_fullInputPath: files.convertToOSPath(absPath), // avoid, see above..
|
||||
|
||||
// Used for one optimization. Don't rely on this otherwise.
|
||||
_hash: file.hash,
|
||||
_hash: hash,
|
||||
|
||||
// XXX duplicates _pathForSourceMap() in linker
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,7 @@ var packageMapModule = require('./package-map.js');
|
||||
var colonConverter = require('./colon-converter.js');
|
||||
var compilerPluginModule = require('./compiler-plugin.js');
|
||||
var linterPluginModule = require('./linter-plugin.js');
|
||||
var BuildPluginDefintion = require('./build-plugin.js').BuildPluginDefintion;
|
||||
var Future = require('fibers/future');
|
||||
var Console = require('./console.js').Console;
|
||||
var Profile = require('./profile.js').Profile;
|
||||
@@ -659,11 +660,12 @@ _.extend(Isopack.prototype, {
|
||||
// We're finally done validating! Save the processor plugin, and mark
|
||||
// all its extensions as used.
|
||||
self.sourceProcessors[type][processorPluginId] =
|
||||
new additionalOptions.pluginDefinitionClass({
|
||||
new BuildPluginDefintion({
|
||||
id: processorPluginId,
|
||||
extensions: options.extensions,
|
||||
archMatching: options.archMatching,
|
||||
isTemplate: options.isTemplate
|
||||
isTemplate: options.isTemplate,
|
||||
buildPluginClass: additionalOptions.buildPluginClass
|
||||
}, factory);
|
||||
_.each(options.extensions, function (ext) {
|
||||
pluginSourceExtensions[ext] = true;
|
||||
@@ -675,7 +677,7 @@ _.extend(Isopack.prototype, {
|
||||
var self = this;
|
||||
self._registerSourceProcessor(options, factory, {
|
||||
type: "compiler",
|
||||
pluginDefinitionClass: compilerPluginModule.CompilerPluginDefinition,
|
||||
buildPluginClass: compilerPluginModule.CompilerPlugin,
|
||||
skipUniqExtCheck: false
|
||||
});
|
||||
},
|
||||
@@ -686,7 +688,7 @@ _.extend(Isopack.prototype, {
|
||||
isopack.sourceProcessors.linter = isopack.sourceProcessors.linter || {};
|
||||
self._registerSourceProcessor(options, factory, {
|
||||
type: "linter",
|
||||
pluginDefinitionClass: linterPluginModule.LinterPluginDefinition,
|
||||
buildPluginClass: linterPluginModule.LinterPlugin,
|
||||
// Several linters can handle the same extension
|
||||
skipUniqExtCheck: true
|
||||
});
|
||||
|
||||
@@ -1,30 +1,33 @@
|
||||
var CompilerPluginDefinition = require('./compiler-plugin.js').CompilerPluginDefinition;
|
||||
var buildPluginModule = require('./build-plugin.js');
|
||||
var util = require('util');
|
||||
var _ = require('underscore');
|
||||
|
||||
var LinterPluginDefinition = function () {
|
||||
CompilerPluginDefinition.apply(this, arguments);
|
||||
exports.LinterPlugin = function (pluginDefinition, userPlugin) {
|
||||
var self = this;
|
||||
self.userPlugin = userPlugin;
|
||||
self.pluginDefinition = pluginDefinition;
|
||||
};
|
||||
|
||||
util.inherits(LinterPluginDefinition, CompilerPluginDefinition);
|
||||
|
||||
exports.LinterPluginDefinition = LinterPluginDefinition;
|
||||
|
||||
_.extend(exports.LinterPluginDefinition.prototype, {
|
||||
getInputFileClass: function () {
|
||||
return LintingFile;
|
||||
}
|
||||
_.extend(exports.LinterPlugin.prototype, {
|
||||
run: function () {}
|
||||
});
|
||||
|
||||
|
||||
var LintingFile = function () {
|
||||
buildPluginModule.InputFile.apply(this, arguments);
|
||||
var LintingFile = function (source) {
|
||||
var self = this;
|
||||
self._source = source;
|
||||
};
|
||||
|
||||
util.inherits(LintingFile, buildPluginModule.InputFile);
|
||||
|
||||
_.extend(LintingFile.prototype, {
|
||||
getContentsAsBuffer: function () {
|
||||
return this._source.contents;
|
||||
},
|
||||
getPathInPackage: function () {
|
||||
return this._source.relPath;
|
||||
},
|
||||
getPackageName: function () {
|
||||
return this._source['package'];
|
||||
},
|
||||
getPackageImports: function () {
|
||||
// XXX
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user