This post will be devoted to the issue of building Angularjs applications. I will consider possible solutions and explain why I finally decided to write a few of my own plugins.
So, how is it generally accepted to solve a build problem lately? Grunt / Gulp plugins, require.js, browserify are the most popular options.
But there is a nuance. When you are dealing with an Angular application, you are faced with the need to declare dependencies between modules in order to build them properly. Let me explain by example.
')
If you have a simple application that consists of several files:
app.js controllers.js services.js directives.js filters.js
That is no problem. You can manually register the order of their connection in the same Grunt / Gulp.
But, let's say, if you want to implement an architecture in which each individual service, controller, directive, etc., are in separate files, that is:
app.js controllers/ FirstCtrl.js SecondCtrl.js services/ FirstSrv.js SecondSrv.js
Then you will face a number of difficulties. Mainly, the difficulties will be how to stitch all the files so that while Angular works without errors. So what can you do?
The main problem is that when declaring a controller, service, directive, etc., the module to which they belong must first be specified.
angular.module('App').controller('FirstCtrl',function($scope){...});
In this case, in order to create the FirstCtrl controller, it is necessary that the file in which the App module be declared is first included:
angular.module('App',[]);
In general, in a good way, for reasons of convenience, the test components of the module should be assembled into separate modules:
angular.module('App',['App.controllers','App.services','App.directives','App.filters']);
And this means that if you still want to store everything separately, then you need to create intermediate files with the declaration of these modules:
app.js controllers/ module.js FirstCtrl.js SecondCtrl.js services/ module.js FirstSrv.js SecondSrv.js
where the module.js files contain respectively:
angular.module('App.controllers',[]);
and
angular.module('App.services',[]);
And then, after all, there may be more and independent modules with their structure.
Thus, to solve the problem with a bare concat
will not work (
from the comments ). But this can be done using the Require.js and Browserify libraries.
An example of how such an application, written with the application Require.js,
here . On the one hand, an obvious advantage of the approach is the flexibility that it gives in the design. However, inevitably, the code is littered with AMD wrappers and multiple require ().
And here browserify with its CommonJs looks a bit more advantageous. But CommonJs is the one and CommonJs, that in the case of the AMD browser libraries you will have difficulty in setting up (
discussion of the approach in the comments ). But again, no one cancels the manual declaration of dependencies using require.
In all this, I was most confused by the fact that Angular initially has its own modular structure, which declares dependencies, but it can in no way affect the order of assembly.
Keeping this thought in my head, I wrote my own NgBuild solution. The idea was pretty straightforward. Make a layer that will analyze the syntax tree and include the necessary files, indicated at the place of the module dependencies.
angular.module('App','/controllers.js','/services.js')
after processing you get the following
angular.module('App.controllers',[]);
In principle, the remaining examples and features are described on github, so I will not dwell on this. Moreover, the disadvantage of this approach is obvious - direct intervention in the native syntax Angular.js is necessary
And, in addition to this, the question arises involuntarily, if we know the syntax of Angular.js, know how modules and their dependencies are described, why not just scan the code and, based on its analysis, not arrange the files in the right order.
Actually, these thoughts became the driver for writing another
NgConcat solution. The essence of which has come down to the fact that you just need to specify where your application is located, and the plugin will do the rest of the work for you.
At the same time inside the project, you can decompose the modules and components, as you like, and there is no extra blotches in the code.
Sample task for Gulp:
gulp.task('concat',function(){ gulp.src('/**/*.js') .pipe(concat('app.js')) .pipe(gulp.dest('./build/')); });
and for Grunt:
grunt.initConfig({ concat: { default_options: { files: { 'build/app.js': 'test/src/complex/**/*.js' } } } });
So far there is not enough sourcemap support, there is a great desire to bring together the possibility of adding annotations and building templates into one library. What I will work on in the near future.
As a result, I will again run through the possible options:
simple concat - difficult to implement,
Require.js is a stable library, but additional code is required,
Browserify is slightly smaller than Require.js, but there is the problem of integrating AMD modules,
NgBuild is essentially the same Require / Browserify, but the library has not yet been overgrown with users, therefore, pitfalls and bugs are possible,
NgConcat is simpler and more convenient than all the others, but so far the same is young and can break, so for now that I would not recommend adopting a large project, or take, but have a fallback and in case of anything to complain about Github, the author promises Do not throw your own and mercilessly punish any bugs.
PS When writing libraries, I also had to implement a module for working with the
Astra syntax tree. The API of which I tried to make more convenient than that of
Astral , plus added the possibility of asynchronous work. In my opinion, api should be understandable, but if someone suddenly needs and questions arise, please contact us personally.
That's all.