Can SystemJS plugins modify already transpiled files? - angularjs

While attempting to get Angular (1.x) working with systemjs I realized that there is not currently the capability (that I know of) to automatically insert $inject into angular components, which keeps the components working even when the arguments to the functions are mangled by the minifier. Manually creating the $inject annotations is tedious, error-prone and violates the DRY principal.
There is a mature npm module called ng-annotate which solves this problem and is used in many similar situations for bundling. As I've been exploring SystemJS, I see that there is a plugin system that includes the ability to translate source code, which is exactly what ng-annotate does.
From what I can see, though, SystemJS only gives you the ability to map a particular file extension to a single loader and all the examples of the plugins are to support a new file type. What I would like to do is post-process the output of SystemJS's transpilation process rather than add a new file type. It seems like SystemJS should be able to do this since it has a processing pipeline, but I can't quite figure out how to hook into it the right way. Right now I am using Browserify to achieve the same effect, but I have ended up with a rather complex set of build tasks and I would like to simplify it with SystemJS if possible.
Other strategies to be able to use ng-annotate in the loader pipeline with SystemJS would be appreciated too.

Eventually I figured out a way, but this feels really clunky. System.src itself uses a hook() function to do this, but it's not exported for use. I'd be grateful for any ways to improve this and I hope eventually a properly supported mechanism for chaining loader functionality becomes available:
var System = require('systemjs');
var systemTranslate = System.translate;
System.translate = function(load) {
return systemTranslate.call(this, load).then(function (result) {
if (result) {
var processedResult = result; // Do your processing here.
load.source = processedResult;
}
return load.source;
});
}
I haven't experimented much with this, since my particular use cases for System.js building is currently a dead-end (Typescript source-maps are still busted), but presumably you can also return a promise.
I'll leave this answer un-chosen for a while to see if anybody has some better advice.

Related

Server-side rendering of CSS modules

I am going to import styles with CSS Module and make it work with server-side rendering. I tried the following methods but each one has its own caveat. What is the best possible way to require('.style.scss') if any side effects?
Using the css-modules-require-hook:
Advantage: Easy to configure. You just need to call the hook at the beginning of server code. You don't need to modify components.
Caveat: It modifies the require.extensions global object, which is deprecated.
Using the isomorphic-style-loader:
Advantage: No more hooks to require.extensions.
Caveat: Wrapping components with HOCs that uses the React Context, which is an experimental API and likely to break in future releases of React.
Using the webpack-isomorphic-tools:
Advantage: No dependency on require.extensions or Context (AFAIK).
Caveat: Wrapping server inside webpack-isomorphic-tools instance. And can we please get rid of webpack-assets.json?
Bundling server with Webpack:
Advantage: No more hooks or injections.
Caveat: In development, it is very cumbersome to bundle everything whenever the code changes and even makes it harder to debug in a large bundled .js file. Not sure - you may need to pass a bundled .js to test runner.
Disclaimer:
The advantages and caveats below are just my two cents, and actually I love all the libraries, plugins and approaches they took to solve the problem and really appreciate their efforts.
I am not a native English speaker, please correct me if I misrepresent myself.
In the end, I decided to hook the require.extensions in development. Probably it is not the best way, showing warning messages on console, like checksum mismatch, but in development mode, I can ignore it.

Dynamically override angular service?

Use case:
I'm writing system tests using Geb/Selenium (so outside of angular).
I want to decorate $http to log all requests/responses at run time.
and here's the catch: without touching the source code.
Before you rush to answer "use $provide#decorator", for example,
http://blog.xebia.com/2014/08/08/extending-angularjs-services-with-the-decorate-method/
That solution for this use case means adding a test hook into production code... that's normally a bad thing I want to avoid if possible.
Update: Geb allows you to run Javascript in the browser window. So just for the heck of it I ran the tutorial code to decorate $http. Unfortunately, it didn't work because apparently you can't re-config the app after it's been loaded. But even if it did work, this brings up another interesting point---I need to override $http before any modules have had a chance to use it.
Since decorating $http service would be the cleanest way of doing this, you can avoid polluting production code by using something like ng-constants and gulp/grunt to only add decoration code for a 'test' environment.
See related Q/A here: How do I configure different environments in Angular.js?
If you are inclined on changing this at runtime(where runtime takes place in a test environment), you may need to go 'closer to the metal' and deal with XMLHttpRequests: Add a "hook" to all AJAX requests on a page

pitfalls of IIFEs with AngularJS

I have an application made with angularJS, whole application is composed of IIFEs(Immediately Invoked Function Expression). Every module, directive, controller is itself an IIFE and they are around 100s.
I want to know what is the performance pitfall when an app has so many IIFEs.
It is OK to use IIFEs with AngularJS?
How good is using libs like browserify and requirejs with AngularJS for managing the dependencies?
Can you please throw some light on this?
The question you need to ask first is whether you've got some internal parts within the IIFE that you don't want to expose to the global scope.
The whole point of creating a closure in this manner is to mimic encapsulation and avoid polluting the global scope.
The performance pitfall is not so easy to measure; I think the performance issue is negligible when you create the IIFE (don't forget you're just creating a function). One performance issue that I might think of is that when you reference a function variable form an inner function, you need to travel through the scope chain and get it from the closure object.
I would recommend you to look at patterns like module pattern, revealing module pattern etc. I personally use the revealing module pattern.
Browserify and requireJS are two libraries that implement two different specs; the commonJS and AMD respectively. These two specifications try to accommodate a functionality that is not supported by ES3 or ES5; That is a way of defining module and then loading them in specified locations.
If you want to define modules and load them synchronously in a similar manner to hose nodeJS works, you can use Browserify. In addition, Browserify allows you to use the same modules both for client-side and server-side (as long as you're using nodeJS).
On the other hand if you want to asynchronously load your modules you can go with AMD and requireJS, but you won't be able to reuse them on the back-end.
Finally, bare in mind that everything you've mentioned is not directly connected to angularJS; those are some good JavaScript practises to overcome some issues of the language itself. It can be well used whether you're working with angular or not.
I would recommend that you use either browserify or requireJS; it will benefit you in the long run. Just imagine having 100 JS files; you would need to out the manually into your html in the correct order based on the dependency graph. You can easily come across issues like race conditions where one file should have been inserted before another one.
As for the performance overhead of IIFEs, you shouldn't have any serious issues.
As another answer said, IIFEs are a good general practice, independent of AngularJS.
If you're concerned about the performance of IIFEs, go ahead and check out these performance tests on JSPerf (or write your own):
http://jsperf.com/iife-antecedents-in-js
http://jsperf.com/immediate-anonymous-function-invocation
While some ways of IIFE creation are clearly much slower than others, I wouldn't worry about the performance of IIFEs unless you're in a large loop.

Using Breeze with RequireJS, Angular is loaded

Using TypeScript, AMD, "requirejs", "breeze" but not "angular", I just upgraded from Breeze 1.4.0 to 1.4.6. Now, when "breeze" is loaded, it also tries to load "angular" which fails with...
Module name "angular" has not been loaded yet for context: _.
Use require([]) http://requirejs.org/docs/errors.html#notloaded
The problem seems to result from Breeze executing the following line.
var ng = core.requireLib("angular");
What have I done wrong so this code is executed anyway? Why does Breeze think that Angular has to be loaded?
When replacing the above line with the following, anything works fine.
var ng = undefined;
I'm having trouble reproducing this.
True, due to a peculiarity in the Breeze code, Breeze will execute the following line even when angular is not present
var ng = core.requireLib("angular");
... and that will fail with
Module name "angular" has not been loaded yet for context
because it can't find a library called angular.js (I assume you don't have it)
But that failure occurs inside another function (__requireLibCore) within a try/catch
function __requireLibCore(libName) {
var lib;
try {
if (this.window) {
...
if (window.require) {
lib = window.require(libName);
}
if (lib) return lib;
}
} catch(e) {
}
return lib;
Are you seeing some other code path in which this error is thrown?
UPDATE 11 December 2013
Thanks, #mgs, for your email explaining how you saw the exception. I summarize for those who read this later.
Breeze started looking for AngularJS in v.1.4.6. To be perfectly clear, Breeze does not need angular; it just wants to know if angular is available. Breeze doesn’t need knockout, jQuery or Q either. But under varying circumstances it may look for these libraries and use them if it finds them.
AFAIK there is no other way in requireJS to detect if a module has been loaded then to call require("angular"). Unfortunately, require("angular") throws an exception when it can't find that module.
Breeze itself does not fail because that call is caught in a try/catch. Many (most?) devs will never see this exception.
But #mgs has attached a requirejs.onError event handler ... which I hasten to add is a good practice. Of course his handler will see the "angular not found" exception even though Breeze caught it and moved on. He writes:
That “requirejs.onError” function is called, when Breeze tries to load Angular. In the real program this causes an entry into the log, and alarm bells are ringing. It was this entry that made me ask the question on StackOverflow.
What to do? I don't think Breeze can do anything unless someone knows a way to detect a loaded module without triggering the requirejs.onError event. I think it is up to the developer to compensate.
I recommend teaching your handler to ignore Breeze's attempt to find angular. Don't sound the "alarm bell" for this particular failure.
Alternatively, you could define a dummy module, as #mgs, did so that require finds an "angular" module. I'm not fond of this approach because it fools Breeze into believing that Angular is available and Breeze might act on that misunderstanding in the future. It's harmless today but who knows about tomorrow.
In any event, the mystery has been explained and suitable workarounds are now known. Thanks, #mgs, for the opportunity to explore this subject.
UPDATE 12 December 2013
We just released Breeze v.1.4.7 which includes the change you recommended ... using require.defined to check first if a module is loaded. Works like a charm.
I took the occasion to re-write the Todo-Require sample such that everything is loaded in a single require script line:
<!-- Require + main. All scripts retrieved async by requireJS -->
<script data-main="Scripts/app/main" src="Scripts/require.js"></script>
The documentation explains how it all works.
Thanks for your question and feedback.
you can force angularjs to be loaded by adding the following typescript line:
/// <amd-dependency path="angular"/>
alternatively you can make breeze depend on angular in your requirejs config.
Update In case you are not using angularjs you can safely exclude this file from your project : https://github.com/IdeaBlade/Breeze/blob/master/Breeze.Client/Scripts/IBlade/b00_breeze.ajax.angular.js

What is angular-loader.js for?

I saw a similar question on the Google groups and also here on Stackoverflow. Both times the question was not answered. The code in this file doesn't make it very clear about what exactly it does and how it is used. Also it's not clear from the Angular documentation.
Can someone explain how this is used. Also can this be used along with Require.js?
Angular loader allows your angular scripts to be loaded in any order.
As angular-seed project shows us, Angular loader does not have any specific api, you just put it at the top of your index file (so that it's executed first) and then proceed to load your application files anyway you prefer.
But, the most important thing for your use case is that you don't really need angular loader at all. RequireJS also allows your files to be loaded in any order, but it also provides you with many other features that angular loader just isn't made for.
So, yes, you may use it with RequireJS, but you don't need to, because it becomes redundant.
Angular modules solve the problem of removing global state from the application and provide a way of configuring the injector. As opposed to AMD or require.js modules, Angular modules don't try to solve the problem of script load ordering or lazy script fetching. These goals are orthogonal and both module systems can live side by side and fulfil their goals.
http://docs.angularjs.org/tutorial/step_07#anoteaboutdiinjectorandproviders
It allows for you asynchronously load files when bootstrapping your angular application. A good example is the angular-seed project that has an index-async.html file that does this.
index-async.html
This is useful for using other libraries that load in modules asynchronously.
See angular-async-loader:
https://github.com/subchen/angular-async-loader/
To async load following components:
List item
controller
services
filter
directive
value
constant
provider
decorator

Resources