I have an ember-cli based app which needs to be integrated into an existing java/JSP app. For this to happen I need to generate a JSP file with js/css fingerprinted URLs which are generated by ember-cli/broccoli-asset-rev.
This is working fine for a html file and I can set it use a JSP file by changing my Brocfile.js to include:
var app = new EmberApp({
outputPaths: {
app : {
html: 'index.jsp'
}
}
});
but this prevents ember serve working as it uses the index.jsp as the html file. Is it possible to have both generated?
After trying many things I have come up with two solutions, both have drawbacks. The first is to use make a new broccoli tree and merge it with he app tree then explicity run broccoli-asset-rev on the resulting tree. The downside of this is that the mustache does not get hydrated, this is useful for outputting config. This would look something like:
//Brocfile.js
var mergeTrees = require('broccoli-merge-trees');
var funnel = require('broccoli-funnel');
var assetRev = require('broccoli-asset-rev');
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
var jspTree;
var app = new EmberApp({
fingerprint: {
enabled: false
},
storeConfigInMeta: false
});
jspTree = funnel('app', {
files: ['index.jsp']
});
module.exports = assetRev(mergeTrees([appTree = app.toTree(), jspTree]), {
extensions: ['js', 'css'],
replaceExtensions: ['jsp', 'html']
});
The other solution is the override a private api method in ember-cli which builds the tree for the index. This solution does let the mustache get hydrated but relies on a private method. You can find details here and here
How about adding symbolic link?
ln -s index.jsp index.html
Depending on what build tool you're using in your project, I'd probably recommend something like the following:
Put some placeholder sections in your index.html.
Copy index.jsp to index.jsp.tmp.
Copy in code from index.jsp into your placeholder sections.
Move index.jsp.tmp back to index.jsp and clean up.
You might consider something like gulp-replace to do the work.
Related
I'm using a "core" ember addon in a boilerplate, with
npm link core-addon
This addon contains generic components, helpers, routes...
Is there a way to exclude some of these components in the boilerplate's ember-cli-build file?
I already tried the following in the ember-build-cli in my boilerplate project, which is probably wrong:
const EmberApp = require('ember-cli/lib/broccoli/ember-app');
const environment = EmberApp.env();
module.exports = function (defaults) {
let app = new EmberApp(defaults, {
funnel: {
enabled:true,
exclude:['core-addon/pods/components/pages/**/*']
},
});
return app.toTree();
};
Ember version: 3.5.0
Ember cli version 3.5.0 node version 8.11.3
Addons generally take the reverse approach of this: The addon manages what gets merged into the consuming app via configuration in the consuming app.
At the highest level, each addon has an entry point that is the index.js file sitting in the root directory of the addon. The addon provides certain configuration options that it reads from config/environment.js of the consuming app when installing.
I think a really good case study for you would be ember-bootstrap. Look at their configuration options and more specifically the blacklist option. They allow the consuming application to only install a subset of the bootstrap components. Furthermore, the project supports bootstrap 3 or bootstrap 4, but the consuming app isn't getting both! The work is done in index.js
Let's look just at how they blacklist (ie exclude) certain components from being added to the consuming app:
treeForApp(tree) {
tree = this.filterComponents(tree);
return this._super.treeForApp.call(this, tree);
},
filterComponents(tree) {
let whitelist = this.generateWhitelist(this.bootstrapOptions.whitelist);
let blacklist = this.bootstrapOptions.blacklist || [];
// exit early if no opts defined
if (whitelist.length === 0 && blacklist.length === 0) {
return tree;
}
return new Funnel(tree, {
exclude: [(name) => this.excludeComponent(name, whitelist, blacklist)]
});
}
where this.excludeComponent at it's core is a boolean returning filter function that returns true if the blacklist contains it in the blacklist case (there for excluding it). The treeForApp function returns the tree for all app files, ie what will be merged from the addon's app dir into the consuming app:
The consuming app's ember-cli-build would look something like this:
//your-bootstrap-app/ember-cli-build.js
module.exports = function(defaults) {
let app = new EmberApp(defaults, {
'ember-bootstrap': {
blacklist: ['bs-popover', 'bs-accordion']
}
});
return app.toTree();
};
and the result would be no bs-popover and no bs-accordion available in the consuming apps app tree. These options are obtained in the index.js file like so:
let options =Object.assign({}, defaultOptions, app.options['ember-bootstrap']);
this.bootstrapOptions = options;
Check this general guide to building addons and the more advanced api for more info.
Messing around with deploying, and I'm going to need to put the app outside the root url of the server. Based on this answer all I need to do is change the environment.js file to look like this.
module.exports = function(environment) {
var ENV = {
modulePrefix: 'ember-drupal',
environment: environment,
rootURL: '/',
locationType: 'auto'
};
if (environment === 'production') {
ENV.rootUrl = '/myApp/';
ENV.locationType = 'hash';
}
return ENV;
};
So when I run
ember build --environment=production
I expect it to set the rootUrl to be /myApp/, yet when I load up localhost/myApp/ it gives me 404 saying that it's still looking for /assets/ instead of /myApp/assets.
Two interesting notes.
If I change the default rootUrl to /myApp/, it works.
The source code has a meta tag called "ember-drupal/config/environment". The content of the meta tag is json of my environment variables.
The 'ember build' command spits out this:
{
"modulePrefix":"ember-drupal",
"environment":"development",
"rootURL":"/",
"locationType":"auto",
"exportApplicationGlobal":true
}
And the 'ember build --environment=production' spits out this:
{
"modulePrefix":"ember-drupal",
"environment":"production",
"rootURL":"/",
"locationType":"hash",
"rootUrl":"/myApp/",
"exportApplicationGlobal":false
}
So it's setting the locationType correctly, but setting the rootUrl twice.
A freaking capitalization error..... rootUrl vs rootURL. It took me all the way of typing this out to find it.
I have an addon which needs to copy a set of JS files from their bower directory to the Ember app's root of /dist (this is for scoping rules associated with service workers). I thought maybe I could use the treeForApp hook but while I'm getting no errors I'm also not getting the desired result.
The index.js is:
const Funnel = require('broccoli-funnel');
module.exports = {
name: 'ember-upup',
treeForApp: function(tree) {
tree = new Funnel(tree, { include:
[
'bower_components/upup/dist/upup.min.js',
'bower_components/upup/dist/upup.sw.min.js'
]});
return this._super.treeForApp.call(this, tree);
},
Note: I thought I might be able to solve this problem by simply copying the javascript files as part of index.js postBuild hook but while this DOES put the JS files into the dist folder's root it is not served by ember-cli's ember serve apparently if not pushed through one of it's build pipelines.
Stefan Penner has now pointed out the the dist directory is for developers to look at but serving is actually done within the tmp directory structure ... this explains why my "hack" didn't work.
It looks like my initial attempt wasn't entirely far off. To make it work you need to hook into the treeForPublic hook like so:
const path = require('path');
const Funnel = require('broccoli-funnel');
const mergeTrees = require('broccoli-merge-trees');
const JS_FILES = ['upup.min.js', 'upup.sw.min.js'];
module.exports = {
treeForPublic: function() {
const upupPath = path.join(this.app.bowerDirectory, 'upup/dist');
const publicTree = this._super.treeForPublic.apply(this, arguments);
const trees = [];
if (publicTree) {
trees.push(publicTree);
}
trees.push(new Funnel(upupPath, {
include: JS_FILES,
destDir: '/'
}));
return mergeTrees(trees);
}
}
Hope that helps.
In my app I'm adding EmberJS to a page where jQuery is already loaded. So I don't need the ember-cli to include jQuery.
The ember-cli build step has the addition of jQuery hardcoded, but you can override it via configuration. I'm not sure if this is documented, but you can check node_modules/ember-cli/lib/broccoli/ember-app.js
this.vendorFiles = merge(options.vendorFiles, {
'loader.js': this.bowerDirectory + '/loader/loader.js',
'jquery.js': this.bowerDirectory + '/jquery/dist/jquery.js',
'handlebars.js': {
development: this.bowerDirectory + '/handlebars/handlebars.js',
production: this.bowerDirectory + '/handlebars/handlebars.runtime.js'
} /* etc, etc, */
}
options is the hash that is passed to a new instance of EmberApp in your Brocfile.js
Instead of,
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
var app = new EmberApp({});
Pass the location of a stub file (use the vendor/ dir for this),
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
var app = new EmberApp({
vendorFiles : {
'jquery.js': 'vendor/stub.js'
}
});
This stub will take priority over the hard-coded jQuery path. Just make sure you load in jQuery before your ember app is loaded.
In an Ember-CLI project, if I add a directory containing webfonts and their CSS stylesheets to the public/assets directory, I can use them with something like #import 'assets/font/regular/stylesheet.css. This works fine.
Ideally though, I'd like to keep these assets out my git repository, and instead bower install them as client-side dependencies, but how can these assets be used in the Ember-CLI build?
The documentation mentions app.import(FILE) in Brocfile.js, which works for CSS stylesheets, but not for a WOFF font file:
$ ember build
version: 0.0.28
Build failed.
Error: Path or pattern "nicefont.woff" did not match any files
at Object.multiGlob (/(PATH)/node_modules/ember-cli/node_modules/broccoli-static-compiler/node_modules/broccoli-kitchen-sink-helpers/index.js:216:13)
at /(PATH)/demo/node_modules/ember-cli/node_modules/broccoli-static-compiler/index.js:25:27
at invokeCallback (/(PATH)/node_modules/ember-cli/node_modules/rsvp/dist/commonjs/rsvp/promise.js:228:21)
at publish (/(PATH)/node_modules/ember-cli/node_modules/rsvp/dist/commonjs/rsvp/promise.js:176:9)
at publishFulfillment (/(PATH)/node_modules/ember-cli/node_modules/rsvp/dist/commonjs/rsvp/promise.js:312:5)
at flush (/(PATH)/node_modules/ember-cli/node_modules/rsvp/dist/commonjs/rsvp/asap.js:41:9)
Also, I would like to specify a directory, which is app.import() refuses.
Is there an Ember-CLI / Brocolli way of doing this?
I thought I was stuck on this issue, but apparently a cup of tea and explicitly phrasing the question on StackOverflow pushed me in the right direction…
If you install a client-side dependency with bower, then in an Ember-CLI project these will end up in vendor/. To use (parts of) them without changing them, we can use Broccoli's slightly awkwardly named broccoli-static-compiler. First, install two build-time dependencies:
npm install --save-dev broccoli-static-compiler
npm install --save-dev broccoli-merge-trees
In Brocfile.js add at the top below the EmberApp import:
var mergeTrees = require('broccoli-merge-trees');
var pickFiles = require('broccoli-static-compiler');
And at the bottom of Brocfile.js:
// Remove this line:
// module.exports = app.toTree()
// Copy only the relevant files:
var fontOpenSans = pickFiles('vendor/font-opensans', {
srcDir: '/',
files: ['**/*.woff', '**/stylesheet.css'],
destDir: '/assets/fonts'
});
// Merge the app tree and our new font assets.
module.exports = mergeTrees([app.toTree(), fontOpenSans]);
Here our client-side dependency is a font-opensans, which refers to a local git repository containing a copy of the Open Sans webfont.
That is all! To use the web-font, link to assets/ from index.html:
<link rel="stylesheet" href="assets/fonts/opensans_regular/stylesheet.css">
This was tested with ember-cli 0.0.40 and a few earlier versions.
The supported answers are a bit out of date. At the time of this writing Ember CLI 0.2.2, there is support for directly copying/fingerprinting vendor folders you want in your assets directory.
// Brocfile.js
var app = new EmberApp();
...
var extraAssets = new Funnel('bower_components/a-lovely-webfont', {
srcDir: '/',
include: ['**/*.woff', '**/stylesheet.css'],
destDir: '/assets/fonts'
});
module.exports = app.toTree(extraAssets);
Documentation here
Similar to answer from JeroenHoek, in ember-cli, version 0.0.40, I ended up doing it right under the app.import before module.exports. I use the augmentation pattern to encapsulate what I'm trying to do so that when/if it's no longer necessary, or there is a more preferred way to do it, I can clean it up easily, and remove modules that aren't used anymore.
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
var app = new EmberApp();
// Use `app.import` to add additional libraries to the generated
// output files.
//
// ... [comments omitted]
app.import('vendor/moment/moment.js');
var tree = app.toTree();
tree = (function mergeFontAwesomeTree(tree) {
var mergeTrees = require('broccoli-merge-trees');
var pickFiles = require('broccoli-static-compiler');
var fontawesome = pickFiles('vendor/fontawesome/fonts', {
srcDir: '/',
destDir: '/fonts'
});
return mergeTrees([tree, fontawesome]);
})(tree);
module.exports = tree;