diff --git a/tools/build-plugin.js b/tools/build-plugin.js index ff140a68b0..4106e5b067 100644 --- a/tools/build-plugin.js +++ b/tools/build-plugin.js @@ -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 } }); diff --git a/tools/bundler.js b/tools/bundler.js index 7b673f42a0..29f7e2ee39 100644 --- a/tools/bundler.js +++ b/tools/bundler.js @@ -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 diff --git a/tools/compiler-plugin.js b/tools/compiler-plugin.js index f0183b5fd8..aa4a8d1d3b 100644 --- a/tools/compiler-plugin.js +++ b/tools/compiler-plugin.js @@ -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; + } +}); + diff --git a/tools/compiler.js b/tools/compiler.js index 25469c800c..d794a93b96 100644 --- a/tools/compiler.js +++ b/tools/compiler.js @@ -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 /** diff --git a/tools/isopack.js b/tools/isopack.js index bbf9733675..d277041ea3 100644 --- a/tools/isopack.js +++ b/tools/isopack.js @@ -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 }); diff --git a/tools/linter-plugin.js b/tools/linter-plugin.js index d00d07ddad..64d145ce18 100644 --- a/tools/linter-plugin.js +++ b/tools/linter-plugin.js @@ -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 }