The goal is to create or generate some files in the user's app directory, so that he can make some uses of it. For instance, provide LESS
files that uses end-user's LESS variables (eg. the user's custom bootstrap theme).
This gist is just explaining the method Marco Pfeiffer has found to make its (awesome) highly configurable LESS boostrap package.
In order to create some file in the user's app workspace, you need to create a build plugin with the registerBuildPlugin
API. Then you need to get it called at least once during build. This is done by registering a sourceHandler
on some file extension. Your build plugin we get called once per file matching the extension. For now only one build plugin can register as a handler for a given extension, so be creative on the extension :).
Once you get called, you can use the path of the file(s) whose compilation triggered your build plugin as a base to make up a path and create your files, with node's fs
and path
APIs.
If you plan to generate your file from scratch in the build plugin, you can stop reading here.
Otherwise, if you want to read some static files to help you creating the files, you can use Meteor's Assets
API. This API allows to retrieve the content of an asset (text or binary).
An asset is a file that has been added with addFile
and a special option {isAsset:true}
.
Although it is not stated in the documentation, not only apps can use the Asset
API.
Packages can too, but they can only access the assets they have exported themselves.
As the Asset
API is restrictive on what assets you can read, you will not be able to access the assets of your package from the build plugin. But you can use a package in a build plugin. So the workaround is to have two dependent packages:
foo
contains the build plugin, and maybe the rest of your package's code.foo-assets
contains only the assets, and export functions to pipe the assets' contentfoo
's build plugin usesfoo-assets
and use the function to get the file content.
// pipe_foo_assets.js
PipeFooAssets = function(file) {
return Assets.getText(file);
}
// foo.import.less
foo{
background-color: @navbar-inverse-bg; // may be customized by package users
}
// package.js
Package.on_use(function (api) {
api.addFiles("foo.import.less", 'server', {isAsset:true});
api.addFiles('pipe_foo_assets.js', 'server');
api.export(['PipeFooAssets']);
});
// foo_build_plugin.js
var fs = Npm.require('fs');
var path = Npm.require('path');
// Get called once per file matching *.foo.json
// {archMatching: 'web'} tell meteor that we will only create files that
// are useful to the client, so no need to restart the server upon change.
Plugin.registerSourceHandler('foo.json', {archMatching: 'web'}, handler);
var handler = function (compileStep, isLiterate){
// copy foo.import.less in the same directory as *.foo.json at each build
// meaning that user won't be able to edit foo.import.less
var destPath = path.join(path.dirname(compileStep._fullInputPath), 'foo.import.less');
fs.writeFileSync(destPath, PipeFooAssets('foo.import.less'));
}
// package.js
Package._transitional_registerBuildPlugin({
name: 'foo_build_plugin',
use:['foo-assets'],
sources: ['foo_build_plugin.js'],
npmDependencies: {}
});