Sapui5 app not uglified using grunt - gruntjs

I'm trying to uglify ( mangle + remove comments ) a SapUI5 app by using grunt together with the grunt-sapui5-bestpractice-build plugin.
According to this link, there should not be an uglify task by using this plugin so I've combine it together with the grunt-contrib-uglify plugin.
The problem that I have is; when performing the application build, it seems that my uglify task is being ignored because the grunt-sapui5-bestpractice-build has an implicit uglify task that overwrites the one I'm defining.
For a better understanding, this is the code:
package.json
{
"name": "grunt-build",
"version": "0.0.1",
"description": "Grunt build",
"private": true,
"devDependencies": {
"#sap/grunt-sapui5-bestpractice-build": "1.3.33",
"grunt-contrib-uglify": "3.3.0"
}
}
Gruntfile.js
module.exports = function(grunt) {
'use strict';
// Project configuration.
grunt.initConfig({
uglify: {
options: {
mangle: true,
compress: {
drop_console: true,
dead_code: false,
unused: false
}
},
files: {
expand: true,
cwd: "<%= ref.staging%>",
src: ["**/*.js", '!test/**', '!test_local.html'],
dest: "<%= ref.process%>"
}
}
});
grunt.loadNpmTasks('#sap/grunt-sapui5-bestpractice-build');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.registerTask('default', [
'lint',
'clean',
'build',
'uglify'
]);
};
The result is that the application is correctly built & minified, Component-preload.js is created ad used to load the app but the mangle is not done and the comments are still there.
Could you kindly provide any advice here? Do you have any way to input the grunt-sapui5-bestpractice-build with the mangle option in order to do it just using one plugin?
Thanks in advance.

This is more of an extended comment, and not a fully functional answer. I just ran out of space.
The advantage of having the Component-preload.js not mangled is that you can debug it normally, because it contains the spaces, comments etc. Debugging mangled code is more difficult.
You'll have to look at the things you need to do to accomplish this mangling. After '#sap/grunt-sapui5-bestpractice-build' is done, you have minified but not mangled Component.js, a minified but not mangled Comonponent-preload.js and a normal development version in Component-dbg.js, correct?
You might want to abandon the best practice package and go for something else entirely like this: https://www.npmjs.com/package/grunt-openui5. I can't see what happens in the steps in '#sap/grunt-sapui5-bestpractice-build', I can't even find that package anywhere.
Grunt is only executing a series of tasks in a row. I haven't tried the webIDE versions of this but here's the list of tasks I'm executing as part of an offline openui5 app:
'clean:build', // remove `build` folder
'clean:dist', // remove `dist` folder
'copy:build', // copy all my code to the `build` folder
'preload', // mangle all my code in the `build` folder and create a preload
'copy:dist:minified', // copy the mangled code to `dist`
'copy:dist:dbg', // copy and rename my original code as `-dbg.js` versions to `dist`
'clean:build' // remove the `build` folder
This was done with gulp, using packages gulp-openui5-preload and gulp-uglify and some others. Not sure if gulp works on the web ide, but I think you can recreate this in Grunt.
You can either use something like this to mangle the results of the default process and create a new preload file, or you can scrap the default and try to roll your own entirely.

Related

Grunt , Babel setup for Es6 with external helper

Hi there I have been forced to come here due to every resource out there on the topic is very poor and incomplete.
Not only on the babel site but every single post out there is not complete and informative enough.
I tried to reach out at the babel forum and no replies.
I am trying to convert my prototype libraries to Es6 and convert to the most leanest possible code. So no bloaty duplicated generated code and if possible no bloaty requirejs and whatever browserify generates.
I have tried to make a project with grunt and babel directly, configure the external-helpers plugin according to the babel documentation.
It fails to include the relevant helper code and fails to include the import module code altogether.
ie a babel config like
{
options: {
sourceMap: false,
presets: ['es2015'],
"plugins": ["external-helpers"]
},
dist: {
files: {
'build/<%= package.name %>.js': ['src/<%= package.name %>.js']
}
}
}
The main project file has an import like
import Button from './ui/buttons/Button';
The module code looks like this as if the export is placed underneath extra code is generated for that.
export default class ShareButton {}
produces an output like this
Object.defineProperty(exports, "__esModule", {
value: true
});
require('babel-core/external-helpers');
var _Button = require('./ui/buttons/Button');
var _Button2 = babelHelpers.interopRequireDefault(_Button);
No source of the module or the helper object is included.
I searched hard for how to deal with external-helpers and it suggests it has to be imported into a separate file ie something like this to generate only the helper functions needed
babel-external-helpers -l createClass > src/helpers.js
But any resource regards to this fails to go as far as to how to import that into the project.
If I use the transform-runtime plugin, it produces a massive polyfill that cannot be disabled so a bug and not so useful for what I need.
"plugins": [
["transform-runtime", { "polyfill": false, "regenerator": false }]
]
If I use browserify / babelify it makes a royal mess and duplicates code still.
A config like
{
options: {
"transform": [["babelify", {
"presets": ["es2015"],
"plugins": ["external-helpers"],
sourceMap: false
}]]
},
dist: {
files: {
'build/<%= package.name %>.js': ['src/<%= package.name %>.js']
}
}
}
Produces code like this still with the external helper missing and duplicated code not relevant to the helper. ie
Object.defineProperty(exports, "__esModule", {
value: true
});
Is within every module in the generated file.
If I export the classes like this at the bottom of every file
export default class {}
Duplicated code is generated like
var _class = function _class() {
babelHelpers.classCallCheck(this, _class);
};
exports.default = _class;
In terms of filesize that doesn't include bloaty wrapping code like
},{}],2:[function(require,module,exports){
It seems concatting all the prototype classes files together to bundle in one file is the winner still.
So trying to port the library but keep it similar and bundle it together into one file.
Hopefully this is concise enough and there is a simple solution.
FYI browsers do not understand tabs and 4 spaces. I had to edit this post in my editor to get the code blocks working ! It would be nice to have a markup like other places like "```" ?
Let me know thanks.
I'm using rollup with babel now. Rollup produces a clean output as umd mode. Browserify is really bloaty in itself.
There is just a problem with polyfills being converted. I have to concat external ones in like for WeakMap.
I had a problem trying to use the generated Iterator and finding a polyfill for that so I have to code loops a particular way it doesn't generate Iterators.
The polyfill generation in babel is still too bloaty and crazy. It's pretty terrible. So I concat in minified polyfills that are very small and it's used globally.
I was running into something very similar. Was tired of trying to do it the "right way" and ended up just creating https://www.npmjs.com/package/grunt-babel-helpers which simply manipulates the string output.

Multi-language app -> Angular-gettext: how to update po files?

I am building an angular app, with internationalization using angular-gettext.
I would like to know how to better automate the workflow when updating the translation files.
Currently it is the following:
- running "grunt" (generation of pot file + translation files)
- opening all "po" files in poedit
- in poedit, update po file with the new "pot" file
- update translations in poedit & save
- run grunt again
Is there a way to have something better?
Like, is it possible to apply the pot file to all "po" files using a grunt command?
Here is my gruntfile currently.
Thanks a lot
module.exports = function(grunt)
{
grunt.initConfig({
nggettext_extract: {
pot: {
files: {
'po/template.pot': ['www/app/**/*.html']
}
},
},
nggettext_compile: {
all: {
options: {
module: 'app'
},
files: {
'www/resources/translations.js': ['po/*.po']
}
},
}
});
grunt.loadNpmTasks('grunt-angular-gettext');
// Default task(s).
grunt.registerTask('default', ['nggettext_extract', 'nggettext_compile']);
}
poedit doesn't have a command line argument to execute the "Update from pot" file feature. So I don't think it will be possible with it.
Actually there is a way to do this!
We wrote grunt tasks to call command line gettext utilities. They are also available for windows here.
Basically you need to call msgmerge with the po file and the pot file to update it. See the online documentation for msgmerge to see what you may need. It's pretty slick once you get it going.

Grunt cssmin rebasing a relative URI?

I'm currently setting up grunt-usemin for our project but I'm running in a small issue with the cssmin task.
Our project depends on a few external libraries, some which bring a long some extra assets (like images or fonts). The problem is these libraries do not have the same folder structure.
This is an example of the different folder structures
lib
|--lib1
| |--style1.css
| +--image1.png
+--lib2
|--styles
| +--style2.css
+--images
+--image2.png
In the index.html all the stylesheets are referenced and put inside a build block. As such, when the usemin task executes, the stylesheets of the libraries are concatenated in one minified file and put inside the output folder. The corresponding assets (images) are copied over to this output folder as well and flatened in the img folder. The output folder structure looks like
out
|--allstyles.min.css
|--image1.png
+--image2.png
As you can guess, the concatenated stylesheets has (in this example) two different relative URIs:
image1.png
..\images\image2.png
This is causing an issue where some of the images cannot be found. I need a solution to rebase all relative URIs to the out folder. I tried using the target and root options of the cssmin task but to no avail. Could someone point me to a correct configuration of this task or to another Grunt task that could achieve what I'm looking for?
Thanks in advance!
I have a grunt file in C:\web\project and CSS files in C:\web\project\www\css. The following snippet is from my grunt file and it rebases URLs correctly for me.
var cssFiles = [
'www/css/layout/Header.css',
'www/css/layout/Footer.css',
'www/css/vendor/chosen/chosen.css'
// ...
];
cssmin: {
concat: {
options: {
keepBreaks: true, // whether to keep line breaks (default is false)
debug: true, // set to true to get minification statistics under 'stats' property (see test/custom-test.js for examples)
noAdvanced: true, // set to true to disable advanced optimizations - selector & property merging, reduction, etc.
//relativeTo: 'http://online-domain-tools.com/'
noRebase: false, // whether to skip URLs rebasing
root: 'www'
},
nonull: true,
src: cssFiles,
dest: 'www/temp/application.css'
},
minify: {
options: {},
nonull: true,
src: ['www/temp/application.css'],
dest: 'www/temp/application.min.css'
}
},
// ...
grunt.registerTask('default', ['cssmin:concat', 'cssmin:minify']);
Can you post your gruntfile to compare it?
Related reading: https://stackoverflow.com/a/21415649/99256
look in the cssmin documentation/options:
rebase: set to false to skip URL rebasing
That solves the issue.

Watching multiple "file arrays" in gruntjs

So in my config file, I'm statically defining the css files to watch, along with some html partials. (eventually I will minimatch with exclusions .. I'm just going with first pass right now)
Originally I was storing these in the grunt config object, but struggled to get the output I wanted, so I moved them out of the initConfig method and into the wrapping function:
Original pass:
grunt.initConfig({
cssFiles: [ ... list of files ... ],
htmlFiles: [ ... list of files ...],
watch: {
reload: {
files: ['<%= cssFiles.concat(htmlFiles).join(",") %>']
}
}
});
I tried several variations of this (with and without join), as an example.
Current "Working" version:
module.exports = function(grunt) {
var cssFiles = ['someFile.css',...'lastFile.css'],
htmlFiles = [ ... ];
grunt.initConfig({
watch: {
reload: {
files: cssFiles.concat(cshtmlFiles)
}
}
});
};
I feel like I should be able to do this without having to move my array's out of the grunt config (although I don't know WHY I feel they should stay there ... I guess I just haven't seen many Gruntfile's with code outside of initconfig)
I'm using a system that stores all the paths I need in a single config object, like so:
grunt.initConfig({
pathTo: {
distcss : './dist/css/master.css',
srcstyles : './lib/styles/**/*.scss',
vendor : './lib/vendor'
},
// tasks...
});
Then, I load those in via underscore templates like in your first example. If your project is structured in a good way then usually just having one minimatch pattern is enough. All my CSS ends up in lib/styles, and any misc. third party stuff is usually in lib/vendor as that is managed through Bower.
With the right directory structure and pattern you shouldn't need a large array of paths. A sample JavaScript project could look like this:
lib
└── src
├── app
└── tests
Then your minimatch pattern to lint your application and test code would just be lib/src/**/*.js, for example.
But what works for you works for you; if you've written a system that you're happy with, regardless of whether you've seen it elsewhere or not, there's no reason to change it. :-)

How to use grunt-contrib-livereload?

I'm trying to use grunt-contrib-livereload, but can't seem to figure it out. The readme seems to skip over everything that I need explained, and then ends with an example that doesn't work when I try it and doesn't seem directly related to the documentation. I have searched for a better explanation in a blog post or tutorial or whatever, but haven't been able to find one. Can someone please explain how to get started with this tool?
Here are the kinds of questions I have, based on the readme:
The documentation says the livereload task "must be passed the list of files that have changed" --- but how do I pass it this list of files? The example does not seem to illustrate this. Does regarde pass the list?
Is grunt-contrib-connect required? What does it do, and how do I use it? Do I need to learn connect before I try using livereload?
The readme mentions middleware that "must be the first one inserted" --- but inserted into what, before what else? And how is it inserted?
And I suppose I don't understand how I need to manipulate ports. "All the browsers listening on the livereload port will be reloaded" --- but how do I know which browser is listening to which port? Do I need to learn all about ports before I can try using livereload? (Any suggestion on how to best learn about that?)
Finally, in the example, there is a folderMount function that doesn't seem related to any of the documentation before. What is that, and do I need it?
I guess I'm asking if someone can please:
point me towards a tutorial that is much more effective than the current readme;
explain these unexplained parts of the readme, if those answers are what I need to understand the plugin;
or provide a functional example with some explanation of why it is functional.
Live reloading is now built into grunt-contrib-watch version 0.4.0. grunt-contrib-livereload and grunt-regarde will be deprecated soon.
Now just set the option livereload to true in your config and it will create a live reload server then reload after the tasks have run:
grunt.initConfig({
watch: {
all: {
options: { livereload: true },
files: ['lib/*.js'],
tasks: ['jshint'],
},
},
});
By default the live reload server will be started on port 35729. So to enable live reloading on your page just add <script src="http://localhost:35729/livereload.js"></script> to your page.
View more info on the docs: https://github.com/gruntjs/grunt-contrib-watch#live-reloading
Edit: Check versioning info. grunt-contrib-watch now has livereload support baked in.
What a doozy. I ran into issues with this one too so let me do what I can to explain (or at least get you up and running). Keep in mind, this is how I have it set up and it seems to work most of the time.
For starters, you'll want to make sure you've udpated your package.json with the right dependencies. I'm not sure that livereload works with the baked in "watch" task and I've been using grunt-regarde of late. My package.json usually looks like this:
"dependencies": {
"grunt": "~0.4.x",
"grunt-contrib-livereload": "0.1.2",
"grunt-contrib-connect": "0.2.0",
"grunt-regarde": "0.1.1"
},
Obvi you want grunt (duhhh), livereload, connect seems to help with mounting folders, and regarde is like grunt-watch, it just seems to work better (I forget why exactly).
You could make your package.json even better by specifying livereload in its own "devDependencies" object if you're so inclined. Now, run your good old fasioned npm install to get the goodies in your project.
Let's talk gruntfiles:
As you probably know, the gruntfile is what makes the magic happen. Somewhere towards the bottom of your gruntfile, you'll want to specify
grunt.loadNpmTasks('grunt-regarde');
grunt.loadNpmTasks('grunt-contrib-livereload');
grunt.loadNpmTasks('grunt-contrib-connect');
At the top of your gruntfile, we'll want to add some utils for livereload. Under /*global module:false*/, go ahead and add var lrSnippet = require('grunt-contrib-livereload/lib/utils').livereloadSnippet;.
After that, you don't really need to learn connect, you just gotta use it. Check my style:
var folderMount = function folderMount(connect, point) {
return connect.static(path.resolve(point));
};
This comes before module.exports = function(grunt) {
Now let's get into the meat of the gruntfile. Again, I forget what connect is doing but this is where the middleware magic comes into play. In your modules.exports, add:
connect: {
livereload: {
options: {
port: 9999,
middleware: function(connect, options) {
return [lrSnippet, folderMount(connect, '.')]
}
}
}
},
Now we want to have the files watched. I like to set up a few different tasks since I don't want my whole grunt process running every time I save a CSS file. Here's what I work with (again, add to module.exports):
regarde: {
txt: {
files: ['styles/*.css', 'index.html'],
tasks: ['livereload']
},
styles: {
files: ['sass/*.scss', 'sass/*/*.scss'],
tasks: ['compass']
},
templates: {
files: ['templates/*.jade'],
tasks: ['jade']
}
},
You can see that I only want livereload to fire when there have been changes to my compiled css (*.css) or to my compiled html. If I edit a SCSS file, I want to fire off just compass. If I edit a jade template, I want to only fire the jade to HTML compiler. I think you can see what's going on. You can toy with this, just be smart about it because you could get caught in an infinite loop.
Lastly, you need to fire off these processes. I like tying them all to my main grunt task because my gruntfile is just that sweet.
// Default task.
grunt.registerTask('default', ['livereload-start', 'connect', 'regarde']);
Now, when you fire up grunt in the CLI, you should (hopefully, maybe, cross your fingers) get something like this:
Running "connect:livereload" (connect) task
Starting connect web server on localhost:9999.
Browse to http://localhost:9999/yourpage.html and watch magic happen.
full gruntfile here. full package.json here.
Here is a solution based on Gulp instead of Grunt and the following Gulpfile.js to get livereload working:
var gulp = require('gulp');
var connect = require('connect');
var connectLivereload = require('connect-livereload');
var opn = require('opn');
var gulpLivereload = require('gulp-livereload');
var config = {
rootDir: __dirname,
servingPort: 8080,
// the files you want to watch for changes for live reload
filesToWatch: ['*.{html,css,js}', '!Gulpfile.js']
}
// The default task - called when you run `gulp` from CLI
gulp.task('default', ['watch', 'serve']);
gulp.task('watch', ['connect'], function () {
gulpLivereload.listen();
gulp.watch(config.filesToWatch, function(file) {
gulp.src(file.path)
.pipe(gulpLivereload());
});
});
gulp.task('serve', ['connect'], function () {
return opn('http://localhost:' + config.servingPort);
});
gulp.task('connect', function(){
return connect()
.use(connectLivereload())
.use(connect.static(config.rootDir))
.listen(config.servingPort);
});
I know this is a little old but can help someone.
In the Gruntfile.js add "options":
sass: {
files: 'scss/**/*.scss',
tasks: ['sass'],
options: {
livereload: true,
}
}
In the index add:
<script src="http://localhost:35729/livereload.js"></script>

Resources