Grunt task to optionally include an AMD module for different environment - requirejs

I'm developing a web app using Require.js for AMD and amplify.request to abstract away my AJAX calls. The other advantage to amplify.request is that I've defined an alternative module containing mocked versions of my requests that I can use for testing purposes. Currently, I'm switching between the two versions of my request module by simply commenting/un-commenting the module reference in my main.js file.
What I'd love to do is use Grunt to create different builds of my app depending on which module I wanted included. I could also use it to do things like turn my debug mode on or off. I'm picturing something similar to usemin, only for references inside JavaScript, not HTML.
Anyone know of a plugin that does this, or have a suggestion about how I could do it with Grunt?

On our current project we have a few different environments. For each of them, we can specify different configuration settings for the requirejs build.
To distinguish between these different environments, I've used a parameter target.
You can simply pass this to grunt by appending it to your call like
grunt --target=debug
And you can access this parameter in the Gruntfile, by using grunt.option, like
var target = (grunt.option('target') || 'debug').toLowerCase();
The line above will default to debug. You could then make use of the paths configuration setting of requirejs to point the build to the correct module. Example code below.
requirejs: {
compile: {
options: {
paths: {
"your/path/to/amplify/request": target === "debug" ? "path/to/mock" : "path/to/real",
}
}
}
}

Related

Detect whether an npm package can run on browser, node or both

I'm building a NextJs application which uses a set of abstractions to enable code from both react (Component logic etc.) and node (API logic) to utilize same types and classes. The motive here is to make the development process seem seamless across client side and server side.
For example. a call on User.create method of the User class will behave differently based on the runtime environment (ie. browser or node) - on the browser, it will call an api with a POST request and on server it will persist data to a database.
So far this pattern worked just fine, and I like how it all turned out in terms of the code structure. However, for this to work, I have to import modules responsible for working on the server and browser to a single module (which in this case is the User class) this leads to a critical error when trying to resolve dependencies in each module.
For example I'm using firebase-admin in the server to persist data to the Firebase Firestore. But it uses fs which cannot be resolved when the same code runs on the browser.
As a work around I set the resolve alias of firebase-admin to false inside the webpack configuration (given it runs on browser) see code below.
/** next.config.js **/
webpack: (config, { isServer }) => {
if (!isServer) {
// set alias of node specific modules to false
// eg: service dependencies
config.resolve.alias = {
...config.resolve.alias,
'firebase-admin': false,
}
} else {
// set alias of browser only modules to false.
config.resolve.alias = {
...config.resolve.alias,
}
}
While this does the trick, it won't be much long until the process gets really tedious to include all such dependencies within resolve aliases.
So, my approach to this is to write a script that runs prior to npm run dev (or manually) that will read all dependencies in package.json and SOMEHOW identify packages that will not run on a specific runtime environment and add them to the webpack config. In order to this, there should be a way to identify this nature of each dependency which I don't think is something that comes right out of the box from npm or the package itself.
Any suggestion on how this can be achieved is really appreciated. Thanks`

Setting release configuration for SPA / webpack

I'm developing a Single Page Application and using Webpack for bundling up the modules.
One of my source files (I'm using TypeScript) has settings and configuration used in the application e.g.
// app-settings.ts
export class AppSettings {
public static ApiUrlPrefix: string = "//localhost/myapi/";
}
Which I then use in my code like:
//some-class.ts
import {AppSettings} from "./app-settings"
export class SomeClass {
contructor() {
var something = AppSettings.ApiUrlPrefix;
}
}
When release time comes, I'm going to want the settings to match the live environment.
What's a good way with either gulp, npm or webpack configs to update the settings file? I've seen the HtmlWebpackPlugin which can take a template and plug in some options, so I guess I'm looking for something similar.
You can also use the webpack.DefinePlugin. The Define plugin allows you to pass "Free Global" or "macro-like" variables into your project that you can use. To use webpack.DefinePlugin simply require() webpack into your project.
Here's the documentation and an example on how to use it.

Browserifying react with addons to a standalone component, usable by plugins

I am experementing a bit with react and browserify and have these wishes:
I want to bundle all code written by me into a single file
I want to bundle all 3rd party dependencies (react, react-router, lodash etc) into separate files, one for each lib, to maximize caching possibilities
I have managed to do the things described above but I ran into this specific situation:
In some places of my code I want to use react with addons and as such require it like this: var React = require('react/addons). I don't do this in all parts of my code and it is not done in 3rd party dependencies such as react-router. This seems to create a conflict. Either the browserified bundle will only be available through var React = require('react/addons) which breaks 3rd party dependencies, or I will have to bundle react both with or without addons which menas that react is bundled and downloaded twice.
I tried to use aliasify and make react an alias for react/addons but I couldn't make it work. Should this be possible?
Another acceptable solution would be to bundle just the addons in a separate bundle and through that make both react and react/addons available through calls to require. Is any of this possible?
Addition
As a comment to the first comment by BrandonTilley, this is not just applicable to React and addons. Lodash also comes with a number of different distributions and I would like to be able to choose the version to use in my webapp in this case as well.
Notice that what you want to achieve is documented here: Browserify partitionning
I'm packaging my app in 2 parts: appLibs.js and app.js.
I've done this for caching too but I choose to put all the code that does not change often in a single bundle instead of splitting it like you want, but you can use the same trick.
Here's the code that might interest you:
var libs = [
"react",
"react/addons", // See why: https://github.com/substack/node-browserify/issues/1161
... other libs
];
gulp.task('browserify-libs', function () {
var b = browserify(...);
libs.forEach(function(lib) {
b.require(lib);
});
return b.bundle().......
});
gulp.task('browserify',['browserify-libs'],function () {
var b = browserify(...);
libs.forEach(function(lib) {
b.external(lib);
});
return b.bundle().......
});
This way, React is only bundled once in appLibs.js and can be required inside app.js using both react and react/addons
If you really want to bundle your libs in separate files, bundle then with b.require("myLib"), but in this case be sure to check that the libraries do not have direct dependencies. If a lib to bundle has a dependency in React, this means that lib will be packaged in the bundle, potentially leading to multiple bundles having React inside them (and making weird failures at runtime). The library should rather use peerDependencies so that b.require does not try to package these dependencies
Sounds like the perfect use case for factor-bundle.
From the browserify handbook:
factor-bundle splits browserify output into multiple bundle targets based on entry-point. For each entry-point, an entry-specific output file is built. Files that are needed by two or more of the entry files get factored out into a common bundle.
Thanks for all suggestions but the solution I have chosen is a "shim" if that is the correct term. Looks like this:
Browserify react/addons into it's own file
Create my own file (called shim) only containing this: module.exports = require('react/addons');
Browserify my shim and use the expose option, exposing it as react
Now, either if react or react/addons is required I get react/addons

Meteor.js: How do you require or link one javascript file in another on the client and the server?

1) In node on the backend to link one javascript file to another we use the require statement and module.exports.
This allows us to create modules of code and link them together.
How do the same thing in Meteor?
2) On the front end, in Meteor is I want to access a code from another front end javascript file, I have to use globals. Is there a better way to do this, so I can require one javascript file in another file? I think something like browserify does this but I am not sure how to integrate this with Meteor.
Basically if on the client I have one file
browserifyTest.coffee
test = () ->
alert 'Hello'
I want to be able to access this test function in another file
test.coffee
Template.profileEdit.rendered = ->
$ ->
setPaddingIfMenuOpen()
test()
How can I do this in Meteor without using globals?
Meteor wraps all the code in a module (function(){...your code...})() for every file we create. If you want to export something out of your js file (module), make it a global. i.e don't use var with the variable name you want to export and it'll be accessible in all files which get included after this module. Keep in mind the order in which meteor includes js files http://docs.meteor.com/#structuringyourapp
I don't think you can do this without using globals. Meteor wraps code in js files in SEF (self executing function) expressions, and exports api is available for packages only. What problem do you exactly have with globals? I've worked with fairly large Meteor projects and while using a global object to keep my global helpers namespaces, I never had any issues with this approach of accessing functions/data from one file in other files.
You can use a local package, which is just like a normal Meteor package but used only in your app.
If the package proves to be useful in other apps, you may even publish it on atmosphere.
I suggest you read the WIP section "Writing Packages" of the Meteor docs, but expect breaking changes in coming weeks as Meteor 0.9 will include the final Package API, which is going to be slightly different.
http://docs.meteor.com/#writingpackages
Basically, you need to create a package directory (my-package) and put it under /packages.
Then you need a package description file which needs to be named package.js at the root of your package.
/packages/my-package/package.js
Package.describe({
summary:"Provides test"
});
Package.on_use(function(api){
api.use(["underscore","jquery"],"client");
api.add_files("client/lib/test.js","client");
// api.export is what you've been looking for all along !
api.export("Test","client");
});
Usually I try to mimic the Meteor application structure in my package so that's why I'd put test.js under my-package/client/lib/test.js : it's a utility function residing in the client.
/packages/my-package/client/lib/test.js
Test={
test:function(){
alert("Hello !");
}
};
Another package convention is to declare a package-global object containing everything public and then exporting this single object so the app can access it.
The variables you export NEED to be package-global so don't forget to remove the var keyword when declaring them : package scope is just like regular meteor app scope.
Last but not least, don't forget to meteor add your package :
meteor add my-package
And you will be able to use Test.test in the client without polluting the global namespace.
EDIT due to second question posted in the comments.
Suppose now you want to use NPM modules in your package.
I'll use momentjs as an example because it's simple yet interesting enough.
First you need to call Npm.depends in package.js, we'll depend on the latest version of momentjs :
/packages/my-moment-package/package.js
Package.describe({
summary:"Yet another moment packaged for Meteor"
});
Npm.depends({
"moment":"2.7.0"
});
Package.on_use(function(api){
api.add_files("server/lib/moment.js");
api.export("moment","server");
});
Then you can use Npm.require in your server side code just like this :
/packages/my-moment-package/server/moment.js
moment=Npm.require("moment");
A real moment package would also export moment in the client by loading the client side version of momentjs.
You can use the atmosphere npm package http://atmospherejs.com/package/npm which lets you use directly NPM packages in your server code without the need of wrapping them in a Meteor package first.
Of course if a specific NPM package has been converted to Meteor and is well supported on atmosphere you should use it.

is require.js the right tool (for the job) for "normal" web sites"

I have started building a web site and have used Require.js for selectively loading scripts which I require to implement functionality. What I am experiencing is:
the "main" script has not finished executing (or even downloading) before some of my code uses "require" to load dependencies. What this means is that the require.js config has not run and does not know the locations of my scripts.
because the require.js config has not run by the time my code needs to use it, the "shim" mechanism has not been initialised and cannot be used.
The Common Errors page along with a lot of the issues I seem to be reading about online while trying to solve my own problems seem to suggest that this is not the right tool for the job.
This seems to be useful for single page applications or node.js applications, but not traditional sites where other scripts could be running before require.js has been initialised.
If require.js is not the right tool for the job, is there a right tool for this job? If so then what is?
Are you loading the require.js script asynchronously (with an async='async')? You want requirejs to load synchronously. Once it's loaded, it will load further scripts, like your main.js file, asynchronously. They may all load out of order, but the code will actually get executed in the right order (respecting the declared dependencies).
So in your page template, you would have this:
<script src="/Scripts/require.js" type="text/javascript" data-main="main.js"></script>
That will load RequireJS, and once it's loaded it starts loading your main.js asynchronously. Typically main.js does not define any modules, it just makes use of modules defined in other files. These dependencies are listed in the require() call:
require(["moduleA", "moduleB"], function(A, B){
// Do something with A and B
A.someFunction();
B.someOtherFunction();
});
The files moduleA.js and moduleB.js must wrap their contents inside a define(). In moduleA.js (which depends on module C):
define(["moduleC"], function () {
// Build up an A
var A = ....;
return A;
});
I wonder now if you're wrapping your modules in a define call. Not doing that could explain the out-of-order execution you're experiencing.
RequireJS is a perfectly valid tool on a traditional site, not just on a single-page site.

Resources