splitting packages.js wip

This commit is contained in:
ekatek
2014-03-13 15:09:59 -07:00
parent 5d41a15a34
commit 79b4a6babc
2 changed files with 407 additions and 298 deletions

View File

@@ -1,280 +1,291 @@
// Process all source files through the appropriate handlers and run the
// prelink phase on any resulting JavaScript. Also add all provided source
// files to the package dependencies. Sets fields such as dependencies,
// exports, prelinkFiles, packageVariables, and resources.
var path = require('path');
var _ = require('underscore');
var watch = require('./watch.js');
var buildmessage = require('./buildmessage.js');
var archinfo = require(path.join(__dirname, 'archinfo.js'));
var linker = require('./linker.js');
var UnipackageSlice = require('./unipackage-class.js').UnipackageSlice;
var Unipackage = require('./unipackage-class.js').Unipackage;
// Process all source files through the appropriate handlers and run the prelink
// phase on any resulting JavaScript. Return a compiled UnipackageSlice object.
//
// inputSlice is a SourceSlice containing the source information for the
// final compiled slice.
//
// packageLoader is the PackageLoader to use to validate that the
// slice's dependencies actually exist (for cleaner error
// messages).
compileSlice = function (packageLoader) {
var self = this;
var isApp = ! self.pkg.name;
if (self.isBuilt)
throw new Error("slice built twice?");
//
var compileSlice = function (unipackage, inputSlice, packageLoader) {
var isApp = ! inputSlice.pkg.name;
var resources = [];
var js = [];
var sources = [];
// XXX: Provide a clone method on watchset.
var watchSet = watch.WatchSet.fromJSON(inputSlice.watchSet.toJSON());
// Preemptively check to make sure that each of the packages we
// Preemptively check to make sure that each of the packages we
// reference actually exist. If we find a package that doesn't
// exist, emit an error and remove it from the package list. That
// way we get one error about it instead of a new error at each
// stage in the build process in which we try to retrieve the
// package.
_.each(['uses', 'implies'], function (field) {
var scrubbed = [];
_.each(self[field], function (u) {
var pkg = packageLoader.getPackage(u.package,
{ throwOnError: false });
if (! pkg) {
buildmessage.error("no such package: '" + u.package + "'");
// recover by omitting this package from the field
} else
scrubbed.push(u);
});
self[field] = scrubbed;
});
var checkDependency = function (dependency) {
var pkg = packageLoader.getPackage(dependency.package,
{ throwOnError: false });
if (! pkg) {
buildmessage.error("no such package: '" + dependency.package + "'");
// recover by omitting this package from the field
return false;
}
return true;
};
var uses = _.filter(inputSlice.uses, checkDependency);
var implies = _.filter(inputSlice.implies, checkDependency);
var addAsset = function (contents, relPath, hash) {
// XXX hack
if (!self.pkg.name)
relPath = relPath.replace(/^(private|public)\//, '');
// XXX hack
if (! inputSlice.pkg.name)
relPath = relPath.replace(/^(private|public)\//, '');
resources.push({
type: "asset",
data: contents,
path: relPath,
servePath: path.join(self.pkg.serveRoot, relPath),
hash: hash
});
resources.push({
type: "asset",
data: contents,
path: relPath,
servePath: path.join(self.pkg.serveRoot, relPath),
hash: hash
});
self.pkg.sources.push(relPath);
sources.push(relPath);
};
_.each(self.getSourcesFunc(), function (source) {
var relPath = source.relPath;
var fileOptions = _.clone(source.fileOptions) || {};
var absPath = path.resolve(self.pkg.sourceRoot, relPath);
var filename = path.basename(relPath);
var handler = !fileOptions.isAsset &&
self._getSourceHandler(filename, packageLoader);
var file = watch.readAndWatchFileWithHash(self.watchSet, absPath);
var contents = file.contents;
_.each(inputSlice.getSourcesFunc(), function (source) {
var relPath = source.relPath;
var fileOptions = _.clone(source.fileOptions) || {};
var absPath = path.resolve(inputSlice.pkg.sourceRoot, relPath);
var filename = path.basename(relPath);
// XXX: _getSourceHandler
var handler = ! fileOptions.isAsset &&
self._getSourceHandler(filename, packageLoader);
var file = watch.readAndWatchFileWithHash(watchSet, absPath);
var contents = file.contents;
self.pkg.sources.push(relPath);
sources.push(relPath);
if (contents === null) {
buildmessage.error("File not found: " + source.relPath);
// recover by ignoring
return;
if (contents === null) {
buildmessage.error("File not found: " + source.relPath);
// recover by ignoring
return;
}
if (! handler) {
// If we don't have an extension handler, serve this file as a
// static resource on the client, or ignore it on the server.
//
// XXX This is pretty confusing, especially if you've
// accidentally forgotten a plugin -- revisit?
addAsset(contents, relPath, file.hash);
return;
}
// This object is called a #CompileStep and it's the interface
// to plugins that define new source file handlers (eg,
// Coffeescript).
//
// Fields on CompileStep:
//
// - arch: the architecture for which we are building
// - inputSize: total number of bytes in the input file
// - inputPath: the filename and (relative) path of the input
// file, eg, "foo.js". We don't provide a way to get the full
// path because you're not supposed to read the file directly
// off of disk. Instead you should call read(). That way we
// can ensure that the version of the file that you use is
// exactly the one that is recorded in the dependency
// information.
// - pathForSourceMap: If this file is to be included in a source map,
// this is the name you should use for it in the map.
// - rootOutputPath: on browser targets, for resources such as
// stylesheet and static assets, this is the root URL that
// will get prepended to the paths you pick for your output
// files so that you get your own namespace, for example
// '/packages/foo'. null on non-browser targets
// - fileOptions: any options passed to "api.add_files"; for
// use by the plugin. The built-in "js" plugin uses the "bare"
// option for files that shouldn't be wrapped in a closure.
// - declaredExports: An array of symbols exported by this slice, or null
// if it may not export any symbols (eg, test slices). This is used by
// CoffeeScript to ensure that it doesn't close over those symbols, eg.
// - read(n): read from the input file. If n is given it should
// be an integer, and you will receive the next n bytes of the
// file as a Buffer. If n is omitted you get the rest of the
// file.
// - appendDocument({ section: "head", data: "my markup" })
// Browser targets only. Add markup to the "head" or "body"
// section of the document.
// - addStylesheet({ path: "my/stylesheet.css", data: "my css",
// sourceMap: "stringified json sourcemap"})
// Browser targets only. Add a stylesheet to the
// document. 'path' is a requested URL for the stylesheet that
// may or may not ultimately be honored. (Meteor will add
// appropriate tags to cause the stylesheet to be loaded. It
// will be subject to any stylesheet processing stages in
// effect, such as minification.)
// - addJavaScript({ path: "my/program.js", data: "my code",
// sourcePath: "src/my/program.js",
// bare: true })
// Add JavaScript code, which will be namespaced into this
// package's environment (eg, it will see only the exports of
// this package's imports), and which will be subject to
// minification and so forth. Again, 'path' is merely a hint
// that may or may not be honored. 'sourcePath' is the path
// that will be used in any error messages generated (eg,
// "foo.js:4:1: syntax error"). It must be present and should
// be relative to the project root. Typically 'inputPath' will
// do handsomely. "bare" means to not wrap the file in
// a closure, so that its vars are shared with other files
// in the module.
// - addAsset({ path: "my/image.png", data: Buffer })
// Add a file to serve as-is over HTTP (browser targets) or
// to include as-is in the bundle (os targets).
// This time `data` is a Buffer rather than a string. For
// browser targets, it will be served at the exact path you
// request (concatenated with rootOutputPath). For server
// targets, the file can be retrieved by passing path to
// Assets.getText or Assets.getBinary.
// - error({ message: "There's a problem in your source file",
// sourcePath: "src/my/program.ext", line: 12,
// column: 20, func: "doStuff" })
// Flag an error -- at a particular location in a source
// file, if you like (you can even indicate a function name
// to show in the error, like in stack traces). sourcePath,
// line, column, and func are all optional.
//
// XXX for now, these handlers must only generate portable code
// (code that isn't dependent on the arch, other than 'browser'
// vs 'os') -- they can look at the arch that is provided
// but they can't rely on the running on that particular arch
// (in the end, an arch-specific slice will be emitted only if
// there are native node modules). Obviously this should
// change. A first step would be a setOutputArch() function
// analogous to what we do with native node modules, but maybe
// what we want is the ability to ask the plugin ahead of time
// how specific it would like to force builds to be.
//
// XXX we handle encodings in a rather cavalier way and I
// suspect we effectively end up assuming utf8. We can do better
// than that!
//
// XXX addAsset probably wants to be able to set MIME type and
// also control any manifest field we deem relevant (if any)
//
// XXX Some handlers process languages that have the concept of
// include files. These are problematic because we need to
// somehow instrument them to get the names and hashs of all of
// the files that they read for dependency tracking purposes. We
// don't have an API for that yet, so for now we provide a
// workaround, which is that _fullInputPath contains the full
// absolute path to the input files, which allows such a plugin
// to set up its include search path. It's then on its own for
// registering dependencies (for now..)
//
// XXX in the future we should give plugins an easy and clean
// way to return errors (that could go in an overall list of
// errors experienced across all files)
var readOffset = 0;
var compileStep = {
inputSize: contents.length,
inputPath: relPath,
_fullInputPath: absPath, // avoid, see above..
// XXX duplicates _pathForSourceMap() in linker
pathForSourceMap: (
inputSlice.pkg.name
? inputSlice.pkg.name + "/" + relPath
: path.basename(relPath)),
// null if this is an app. intended to be used for the sources
// dictionary for source maps.
packageName: inputSlice.pkg.name,
rootOutputPath: inputSlice.pkg.serveRoot,
arch: inputSlice.arch, // XXX: what is the story with arch?
archMatches: function (pattern) {
return archinfo.matches(inputSlice.arch, pattern);
},
fileOptions: fileOptions,
declaredExports: _.pluck(inputSlice.declaredExports, 'name'),
read: function (n) {
if (n === undefined || readOffset + n > contents.length)
n = contents.length - readOffset;
var ret = contents.slice(readOffset, readOffset + n);
readOffset += n;
return ret;
},
appendDocument: function (options) {
if (! archinfo.matches(inputSlice.arch, "browser"))
throw new Error("Document sections can only be emitted to " +
"browser targets");
if (options.section !== "head" && options.section !== "body")
throw new Error("'section' must be 'head' or 'body'");
if (typeof options.data !== "string")
throw new Error("'data' option to appendDocument must be a string");
resources.push({
type: options.section,
data: new Buffer(options.data, 'utf8')
});
},
addStylesheet: function (options) {
if (! archinfo.matches(inputSlice.arch, "browser"))
throw new Error("Stylesheets can only be emitted to " +
"browser targets");
if (typeof options.data !== "string")
throw new Error("'data' option to addStylesheet must be a string");
resources.push({
type: "css",
data: new Buffer(options.data, 'utf8'),
servePath: path.join(inputSlice.pkg.serveRoot, options.path),
sourceMap: options.sourceMap
});
},
addJavaScript: function (options) {
if (typeof options.data !== "string")
throw new Error("'data' option to addJavaScript must be a string");
if (typeof options.sourcePath !== "string")
throw new Error("'sourcePath' option must be supplied to addJavaScript. Consider passing inputPath.");
if (options.bare && ! archinfo.matches(inputSlice.arch, "browser"))
throw new Error("'bare' option may only be used for browser targets");
js.push({
source: options.data,
sourcePath: options.sourcePath,
servePath: path.join(inputSlice.pkg.serveRoot, options.path),
bare: !! options.bare,
sourceMap: options.sourceMap
});
},
addAsset: function (options) {
if (! (options.data instanceof Buffer))
throw new Error("'data' option to addAsset must be a Buffer");
addAsset(options.data, options.path);
},
error: function (options) {
buildmessage.error(options.message || ("error building " + relPath), {
file: options.sourcePath,
line: options.line ? options.line : undefined,
column: options.column ? options.column : undefined,
func: options.func ? options.func : undefined
});
}
};
if (! handler) {
// If we don't have an extension handler, serve this file as a
// static resource on the client, or ignore it on the server.
//
// XXX This is pretty confusing, especially if you've
// accidentally forgotten a plugin -- revisit?
addAsset(contents, relPath, file.hash);
return;
}
try {
(buildmessage.markBoundary(handler))(compileStep);
} catch (e) {
e.message = e.message + " (compiling " + relPath + ")";
buildmessage.exception(e);
// This object is called a #CompileStep and it's the interface
// to plugins that define new source file handlers (eg,
// Coffeescript).
//
// Fields on CompileStep:
//
// - arch: the architecture for which we are building
// - inputSize: total number of bytes in the input file
// - inputPath: the filename and (relative) path of the input
// file, eg, "foo.js". We don't provide a way to get the full
// path because you're not supposed to read the file directly
// off of disk. Instead you should call read(). That way we
// can ensure that the version of the file that you use is
// exactly the one that is recorded in the dependency
// information.
// - pathForSourceMap: If this file is to be included in a source map,
// this is the name you should use for it in the map.
// - rootOutputPath: on browser targets, for resources such as
// stylesheet and static assets, this is the root URL that
// will get prepended to the paths you pick for your output
// files so that you get your own namespace, for example
// '/packages/foo'. null on non-browser targets
// - fileOptions: any options passed to "api.add_files"; for
// use by the plugin. The built-in "js" plugin uses the "bare"
// option for files that shouldn't be wrapped in a closure.
// - declaredExports: An array of symbols exported by this slice, or null
// if it may not export any symbols (eg, test slices). This is used by
// CoffeeScript to ensure that it doesn't close over those symbols, eg.
// - read(n): read from the input file. If n is given it should
// be an integer, and you will receive the next n bytes of the
// file as a Buffer. If n is omitted you get the rest of the
// file.
// - appendDocument({ section: "head", data: "my markup" })
// Browser targets only. Add markup to the "head" or "body"
// section of the document.
// - addStylesheet({ path: "my/stylesheet.css", data: "my css",
// sourceMap: "stringified json sourcemap"})
// Browser targets only. Add a stylesheet to the
// document. 'path' is a requested URL for the stylesheet that
// may or may not ultimately be honored. (Meteor will add
// appropriate tags to cause the stylesheet to be loaded. It
// will be subject to any stylesheet processing stages in
// effect, such as minification.)
// - addJavaScript({ path: "my/program.js", data: "my code",
// sourcePath: "src/my/program.js",
// bare: true })
// Add JavaScript code, which will be namespaced into this
// package's environment (eg, it will see only the exports of
// this package's imports), and which will be subject to
// minification and so forth. Again, 'path' is merely a hint
// that may or may not be honored. 'sourcePath' is the path
// that will be used in any error messages generated (eg,
// "foo.js:4:1: syntax error"). It must be present and should
// be relative to the project root. Typically 'inputPath' will
// do handsomely. "bare" means to not wrap the file in
// a closure, so that its vars are shared with other files
// in the module.
// - addAsset({ path: "my/image.png", data: Buffer })
// Add a file to serve as-is over HTTP (browser targets) or
// to include as-is in the bundle (os targets).
// This time `data` is a Buffer rather than a string. For
// browser targets, it will be served at the exact path you
// request (concatenated with rootOutputPath). For server
// targets, the file can be retrieved by passing path to
// Assets.getText or Assets.getBinary.
// - error({ message: "There's a problem in your source file",
// sourcePath: "src/my/program.ext", line: 12,
// column: 20, func: "doStuff" })
// Flag an error -- at a particular location in a source
// file, if you like (you can even indicate a function name
// to show in the error, like in stack traces). sourcePath,
// line, column, and func are all optional.
//
// XXX for now, these handlers must only generate portable code
// (code that isn't dependent on the arch, other than 'browser'
// vs 'os') -- they can look at the arch that is provided
// but they can't rely on the running on that particular arch
// (in the end, an arch-specific slice will be emitted only if
// there are native node modules). Obviously this should
// change. A first step would be a setOutputArch() function
// analogous to what we do with native node modules, but maybe
// what we want is the ability to ask the plugin ahead of time
// how specific it would like to force builds to be.
//
// XXX we handle encodings in a rather cavalier way and I
// suspect we effectively end up assuming utf8. We can do better
// than that!
//
// XXX addAsset probably wants to be able to set MIME type and
// also control any manifest field we deem relevant (if any)
//
// XXX Some handlers process languages that have the concept of
// include files. These are problematic because we need to
// somehow instrument them to get the names and hashs of all of
// the files that they read for dependency tracking purposes. We
// don't have an API for that yet, so for now we provide a
// workaround, which is that _fullInputPath contains the full
// absolute path to the input files, which allows such a plugin
// to set up its include search path. It's then on its own for
// registering dependencies (for now..)
//
// XXX in the future we should give plugins an easy and clean
// way to return errors (that could go in an overall list of
// errors experienced across all files)
var readOffset = 0;
var compileStep = {
inputSize: contents.length,
inputPath: relPath,
_fullInputPath: absPath, // avoid, see above..
// XXX duplicates _pathForSourceMap() in linker
pathForSourceMap: (
self.pkg.name
? self.pkg.name + "/" + relPath
: path.basename(relPath)),
// null if this is an app. intended to be used for the sources
// dictionary for source maps.
packageName: self.pkg.name,
rootOutputPath: self.pkg.serveRoot,
arch: self.arch,
archMatches: function (pattern) {
return archinfo.matches(self.arch, pattern);
},
fileOptions: fileOptions,
declaredExports: _.pluck(self.declaredExports, 'name'),
read: function (n) {
if (n === undefined || readOffset + n > contents.length)
n = contents.length - readOffset;
var ret = contents.slice(readOffset, readOffset + n);
readOffset += n;
return ret;
},
appendDocument: function (options) {
if (! archinfo.matches(self.arch, "browser"))
throw new Error("Document sections can only be emitted to " +
"browser targets");
if (options.section !== "head" && options.section !== "body")
throw new Error("'section' must be 'head' or 'body'");
if (typeof options.data !== "string")
throw new Error("'data' option to appendDocument must be a string");
resources.push({
type: options.section,
data: new Buffer(options.data, 'utf8')
});
},
addStylesheet: function (options) {
if (! archinfo.matches(self.arch, "browser"))
throw new Error("Stylesheets can only be emitted to " +
"browser targets");
if (typeof options.data !== "string")
throw new Error("'data' option to addStylesheet must be a string");
resources.push({
type: "css",
data: new Buffer(options.data, 'utf8'),
servePath: path.join(self.pkg.serveRoot, options.path),
sourceMap: options.sourceMap
});
},
addJavaScript: function (options) {
if (typeof options.data !== "string")
throw new Error("'data' option to addJavaScript must be a string");
if (typeof options.sourcePath !== "string")
throw new Error("'sourcePath' option must be supplied to addJavaScript. Consider passing inputPath.");
if (options.bare && ! archinfo.matches(self.arch, "browser"))
throw new Error("'bare' option may only be used for browser targets");
js.push({
source: options.data,
sourcePath: options.sourcePath,
servePath: path.join(self.pkg.serveRoot, options.path),
bare: !!options.bare,
sourceMap: options.sourceMap
});
},
addAsset: function (options) {
if (! (options.data instanceof Buffer))
throw new Error("'data' option to addAsset must be a Buffer");
addAsset(options.data, options.path);
},
error: function (options) {
buildmessage.error(options.message || ("error building " + relPath), {
file: options.sourcePath,
line: options.line ? options.line : undefined,
column: options.column ? options.column : undefined,
func: options.func ? options.func : undefined
});
}
};
try {
(buildmessage.markBoundary(handler))(compileStep);
} catch (e) {
e.message = e.message + " (compiling " + relPath + ")";
buildmessage.exception(e);
// Recover by ignoring this source file (as best we can -- the
// handler might already have emitted resources)
}
// Recover by ignoring this source file (as best we can -- the
// handler might already have emitted resources)
}
});
// Phase 1 link
@@ -283,23 +294,26 @@ compileSlice = function (packageLoader) {
// js-analyze package, in which case never mind. (The js-analyze package's
// default slice is not allowed to depend on anything!)
var jsAnalyze = null;
if (! _.isEmpty(js) && self.pkg.name !== "js-analyze") {
jsAnalyze = unipackage.load({
packages: ["js-analyze"]
})["js-analyze"].JSAnalyze;
if (! _.isEmpty(js) && inputSlice.pkg.name !== "js-analyze") {
jsAnalyze = unipackage.load({
packages: ["js-analyze"]
})["js-analyze"].JSAnalyze;
}
var results = linker.prelink({
inputFiles: js,
useGlobalNamespace: isApp,
combinedServePath: isApp ? null :
"/packages/" + self.pkg.name +
(self.sliceName === "main" ? "" : (":" + self.sliceName)) + ".js",
name: self.pkg.name || null,
declaredExports: _.pluck(self.declaredExports, 'name'),
jsAnalyze: jsAnalyze
inputFiles: js,
useGlobalNamespace: isApp,
combinedServePath: isApp ? null :
"/packages/" + inputSlice.pkg.name +
(inputSlice.sliceName === "main" ? "" : (":" + inputSlice.sliceName)) + ".js",
name: inputSlice.pkg.name || null,
declaredExports: _.pluck(inputSlice.declaredExports, 'name'),
jsAnalyze: jsAnalyze
});
// XXX: record build time dependencies
/*
// Add dependencies on the source code to any plugins that we could have
// used. We need to depend even on plugins that we didn't use, because if
// they were changed they might become relevant to us. This means that we
@@ -308,38 +322,133 @@ compileSlice = function (packageLoader) {
// plugin program itself uses), as well as the package.js file from every
// package we directly use (since changing the package.js may add or remove
// a plugin).
//
// XXX: activePluginPackages is going to move here?
_.each(self._activePluginPackages(packageLoader), function (otherPkg) {
self.watchSet.merge(otherPkg.pluginWatchSet);
watchSet.merge(otherPkg.pluginWatchSet);
// XXX this assumes this is not overwriting something different
self.pkg.pluginProviderPackageDirs[otherPkg.name] =
otherPkg.packageDirectoryForBuildInfo;
pluginProviderPackageDirs[otherPkg.name] =
otherPkg.packageDirectoryForBuildInfo;
});
*/
self.prelinkFiles = results.files;
self.packageVariables = [];
var packageVariables = [];
var packageVariableNames = {};
_.each(self.declaredExports, function (symbol) {
if (_.has(packageVariableNames, symbol.name))
return;
self.packageVariables.push({
name: symbol.name,
export: symbol.testOnly? "tests" : true
});
packageVariableNames[symbol.name] = true;
if (_.has(packageVariableNames, symbol.name))
return;
packageVariables.push({
name: symbol.name,
export: symbol.testOnly? "tests" : true
});
packageVariableNames[symbol.name] = true;
});
_.each(results.assignedVariables, function (name) {
if (_.has(packageVariableNames, name))
return;
self.packageVariables.push({
name: name
});
packageVariableNames[name] = true;
if (_.has(packageVariableNames, name))
return;
packageVariables.push({
name: name
});
packageVariableNames[name] = true;
});
// Forget about the *declared* exports; what matters is packageVariables
// now.
self.declaredExports = null;
self.resources = resources;
self.isBuilt = true;
var retSlice = new UnipackageSlice(unipackage, {
sliceName: inputSlice.sliceName,
arch: inputSlice.arch, //XXX: arch?
uses: uses,
implies: implies,
watchSet: watchSet,
nodeModulesPath: inputSlice.nodeModulesPath,
prelinkFiles: results.files,
noExports: inputSlice.noExports,
packageVariables: packageVariables,
resources: resources
});
return {
slice: retSlice,
sources: sources
};
};
// Build a PackageSource into a Unipackage by running its source files through
// the appropriate compiler plugins. Once build has completed, any errors
// detected in the package will have been emitted to buildmessage.
compile = function (sourcePackage) {
var sources = [];
var pluginWatchSet = new watch.WatchSet();
var plugins = {};
// Build plugins
_.each(sourcePackage.pluginInfo, function (info) {
buildmessage.enterJob({
title: "building plugin `" + info.name +
"` in package `" + sourcePackage.name + "`",
rootPath: sourcePackage.sourceRoot
}, function () {
var buildResult = bundler.buildJsImage({
name: info.name,
// XXX XXX How do we determine the versions to use for a
// plugin? These are bundle-time versions, not build-time
// versions, so it's like building an app. The main question
// here seems to be, what is the equivalent of a
// .meteor/versions file for a plugin? Does it get its own
// versions file in some hidden directory in the package? Is
// there a way to run 'meteor update' on it or do you have
// to do that stuff by hand?
//
// (obviously null is just a placeholder value until we
// figure this out)
packageLoader: null,
use: info.use,
sourceRoot: sourcePackage.sourceRoot,
sources: info.sources,
npmDependencies: info.npmDependencies,
// Plugins have their own npm dependencies separate from the
// rest of the package, so they need their own separate npm
// shrinkwrap and cache state.
npmDir: path.resolve(path.join(sourcePackage.sourceRoot, '.npm',
'plugin', info.name))
});
// Add the plugin's sources to our list.
_.each(info.sources, function (source) {
sources.push(source);
});
// Add this plugin's dependencies to our "plugin dependency" WatchSet.
pluginWatchSet.merge(buildResult.watchSet);
// XXX: Record build-time dependencies
/*// Remember the versions of all of the build-time dependencies
// that were used.
_.extend(self.pluginProviderPackageDirs,
buildResult.pluginProviderPackageDirs);*/
// Register the built plugin's code.
if (!_.has(plugins, info.name))
plugins[info.name] = {};
plugins[info.name][buildResult.image.arch] = buildResult.image;
});
});
// XXX: what to do this should probably be passed in.
//var packageLoader = self._makeBuildTimePackageLoader();
// XXX: Now we just need a unipackage!
var unipackage = new Unipackage();
// Build slices. Might use our plugins, so needs to happen
// second.
_.each(sourcePackage.slices, function (slice) {
var result = compileSlice(unipackage, slice, packageLoader);
sources.push.apply(sources, result.sources);
unipackage.addSlice(result.slice);
});
return {
sources: _.uniq(self.sources),
unipackage: unipackage
};
};

View File

@@ -9,12 +9,12 @@
// - uses
// - implies
// - noExports
// - declaredExports
// - watchSet
// - nodeModulesPath
// - prelinkFiles
// - packageVariables
// - resources
//
// Do not include the source files in watchSet. They will be
// added at compile time when the sources are actually read.
var UnipackageSlice = function (unipackage, options) {
var self = this;
options = options || {};
@@ -26,8 +26,7 @@ var UnipackageSlice = function (unipackage, options) {
self.id = pkg.id + "." + options.name + "@" + self.arch;
self.uses = options.uses;
self.implies = options.implies || [];
self.noExports = options.noExports || false;
self.declaredExports = options.declaredExports || null;
self.noExports = options.noExports;
self.watchSet = options.watchSet || new watch.WatchSet();
self.nodeModulesPath = options.nodeModulesPath;
@@ -44,8 +43,8 @@ var UnipackageSlice = function (unipackage, options) {
// Both of these are saved into slices on disk, and are inputs into the final
// link phase, which inserts the final JavaScript resources into
// 'resources'.
self.prelinkFiles = null;
self.packageVariables = null;
self.prelinkFiles = options.prelinkFiles;
self.packageVariables = options.packageVariables;
// All of the data provided for eventual inclusion in the bundle,
// other than JavaScript that still needs to be fed through the
@@ -65,7 +64,8 @@ var UnipackageSlice = function (unipackage, options) {
// honored for CSS but ignored if we are concatenating.
//
// sourceMap: Allowed only for "js". If present, a string.
self.resources = null;
self.resources = options.resources;
};
_.extend(UnipackageSlice.prototype, {