Using require.js to collect and inline npm-installed modules into front end JS files - node.js

I have a project and installed for example underscore and marked via npm install. When I launch node in my project root folder, I can require both with var __ = require("underscore"), similar for marked. This is how I use both libraries in my backend, if needed. Now I want to write a myscript.js which is delivered to the browser, it's referenced for example in my index.html file. In this script, I want to use both mentioned libraries as well.
For this to work, I think there are two methods:
inline the required files (I think I prefer this)
copy the required js files into a separate directory, and use require.js in the browser.
In neither case I want to point a program to where the libs are found, similar to node, which knows what to do by just saying require("lib").
My questions are:
Did I understand this correctly?
Is grunt-contrib-requirejs exactly the the thing I need to automate this process?
Can someone provide me with a snippet of code or point me to a blog post or something? I think I lost the overview, I found the documentation but many things are confusing.
Here is what I have so far:
Configuration for my Grunt task:
requirejs: {
compile: {
options: {
baseUrl: "./",
name: "src/main.js",
out: "build/result.js"
}
}
}
My main.js:
var _ = require("underscore")
function foo(values)
{
_.find(values, function(value){ return false; });
}
The result:
function foo(e){_.find(e,function(e){return!1})}var _=require("underscore");define("src/main.js",function(){});
... not exactly what I thought.

While this does not answer my original question, I found out that using node modules is not exactly the way to go for frontend stuff, I found bower. Anyway, this whole thing is very frustrating.
Bower allows me to install required packages, and then there are modules for grunt which allow the JS files either to be "plugged" into the HTML document within a special section, or to be concatenated into one file. Bower-ready modules should ship with a bower.json file. This reliefes me fron the work of manually collecting all needed js files from the node_modules folder like I was afraid of in the question.

Related

NodeJS import file with the same name as a folder

I have the following file structure: ([] are folders)
[MySQL]
ConnectionPool.ts
Connection.ts
MySQL.ts
In my code, I am using typescript to develop the app, which will be built to javascript for the production version. The development version is tested directly from the uncompiled typescript files, using babel with the following configurations:
{
"presets": [
["#babel/preset-env", {
"targets": {
"node": "current"
}
}],
"#babel/preset-typescript"
],
"plugins": [
"#babel/plugin-transform-runtime",
[
"module-resolver",
{
"alias": {
... list of some aliases
}
}
]
]
}
My problem is the following, I do most of my imports like this:
import ConnectionPool from 'MySQL/ConnectionPool`
which works for me, as when I run my dev code, the compiler correctly identifies the file extension to .ts, and it also correctly identifies the built versions file extension to .js.
But if I want to import my MySQL.ts file, I can't do it this way, as I will get the following error: Error: Cannot find module './'. If I specify in my import statement the file extension, everything works correctly, but then, when I build my code, there will be no more .ts files in there, so I will get errors there.
Strangely, on the frontend, where I use webpack, I get no complaints about importing files without extension whose name is the same as a folders name in the same directory. What solutions do I have for this issue, which does not revolve around renaming my files or folders in the structure?
Rename your file MySQL.ts to index.ts and move it into the folder MySQL
In the meantime, I resolved my problem by explicitly specifying .ts as the file extension, and when building the code, using the babel-plugin-transform-rename-import to replace the .ts extensions to .js extension.
I won't accept this as the correct answer, as this is too hacky for me, so if a better solution comes up, I am still open for it.
I don't believe what you want to do is possible, node will automatically include either the file or the directory but if both of them are called the same thing node will have no idea which one to import and it will always prefer one over the other.
Using import Example from 'example' will have node search either for any files called example.js or directories called example with an index.js and it will only load one of them.
The reason webpack does it correctly is because webpack will never bundle a "directory", it will take what you've specified as an import and try all of the resolves extensions until it finds one and then it will import that file, so you'll never have conflicting files with directories with webpack.
I thought maybe a solution is to use aliasing in your typescript config so that you can use import Example from '#example' and differentiate between your directories and files that way. Then you can also do import Example from 'example' if you just want to load the file itself. But even then I don't think that will even work because once it gets compiled to javascript you'll just have the same issue as before with conflicting paths.
That being said, while I understand being a little bit obsessed with naming conventions and small things, I really don't think you should be storing "mysql" outside of the "mysql" directory, for two reasons. The first reason is that it's part of that directory, that's what that directory is for, it contains the "mysql" stuff, so why would you want to store it outside. Secondly using a "index.ts" communicates something to other developers, when I open up a new project or directory I'm immediately looking for something called "index.ts" or similar, otherwise I have no idea where to even begin. Using "index.ts" is a good way to communicate to anyone that "this file right here is the one you're looking for, everything starts here". That being said, just call it index.ts and store it where it belongs.
You can try in your tsconfig.json to set moduleResolution to the classic strategy:
{
"compilerOptions": {
"moduleResolution": "classic"
}
}
Unfortunately, looking at the code, this seems to be impossible in your setup:
import is handled by module-resolver
module-resolver calls the NPM package resolve
resolve.sync checks for the no-extension case before testing extensions.
Given that most systems are going to follow Node module resolution, I think you're out of luck, without some manual aliases or moving files around.
FWIW, NodeJS says that extensions in import are mandatory.

Why is webpack spitting out 10,000 extra lines?

I'm using webpack via the default laravel setup after having run npm install, with the default config.
In webpack.mix.js, I have the following:
mix.js('resources/assets/js/init.js', 'public/js');
And resources/assets/js/init.js contains the following:
(function ($) {
$(function () {
$('.button-collapse').sideNav();
}); // end of document ready
})(jQuery);
Why, then, does webpack emit a whopping 10,000+ lines for this file?!:
Here's the output in a gist.
Did I completely misunderstand webpack, or is a laravel or webpack default messed up? I expect basically a copy of the JS, as npm run dev is not supposed to minify and it doesn't have any ES6 syntax in it... So what is this? The same thing works perfectly to compile scss to css if I add the appropriate line to the mix.
Short Answer
As best I can tell from what code you've given - yep, that's right.
Long Answer
What webpack does isn't just to compile your app to ES5. Rather, what is designed to do is package every dependency together in a single file so the end user downloads a single JS file.
In your original file, I assume at some point you define jQuery using require or some other method. (I'm not familiar with mix, but I assume at some point jQuery must be defined.) When you use require('jquery'), webpack transforms that into all of the jQuery source code - which is likely where almost all of the 10,000 lines of code are from.
We see your original code at the very end of the webpack bundle, starting at line 10,302:
/* WEBPACK VAR INJECTION */(function(jQuery) {(function ($) {
$(function () {
$('.button-collapse').sideNav();
}); // end of document ready
})(jQuery);
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1)))
As you can see, it's basically what you wrote, but now it defines jQuery using the previous 10,000 lines of code. This way, it's not referencing any external files.
Splitting your code
If you don't want your webpack bundle to have a bunch of jQuery at the top, you can split your code into vendor and app files. According to the laravel docs, that's done like this:
mix.js('resources/assets/js/init.js', 'public/js')
.extract(['jquery'])
Then, your bundle will output three files instead of one - one file containing the webpack manifest, one file containing all of the libraries (such as jQuery in this case), and one file containing the main app code (the five lines you wrote in resources/assets/js/init.js).

Gulp + RequireJS Remove Vendor Files

I'm having a problem using Gulp to compile a RequireJS project properly. What I need to do is have gulp create a single distribution file that only includes the file necessary to have the application run.
In our application we are following a modular approach breaking out major pieces of functionality into different repos. So while developing my piece I have RequireJS including angular and many other vendor libraries that are common to all of the projects in the application. However when I go to move my piece into the larger application I no longer need these files in the final output since those dependencies also exist in that application (and having those extra libraries also makes the final distribution file over 300K).
I've tried creating another main.js (called gulp-main.js) file that only includes the dependencies that I need but when I run the gulp process it fails. I don't get an error but it seems to be failing because I'm not including the required dependencies for the project to build successfully. Below is the config object that is being passed to the RequireJS optimize method.
var config = {
baseUrl: 'app/',
mainConfigFile: 'app/main.js',
out: 'dist/app/output.js',
name: 'main'
};
Any ideas on what I could do to either remove the unnecessary vendor files or even split them into a single vendor and a single non-vendor file would really be appreciated. I have already tried using the modules array option but that does not produce the results that I am after since it seems to create a single file for each item defined not a single compiled JS file with all scripts contained within.
Thanks in advance.
When you don't want some file in your final output. add " ! " in Your gulp task's src
example :
gulp.src(['./app/*.js', '!./node_modules/**']) // '!./vendor-libraries-dest to igonore'

Is it possible to compile nodejs project into single javascript file?

The difficulty I see is with all the require calls, and the dependency tree. Is there a way to iterate through a project, including dependencies where needed, and produce a single, fully contained javascript file?
I am hoping to convert some server side only libraries to client side apps.
Alternatively, is there another method to achieve this...
If there is a file lib/_third_party_main.js in the node source when you compile, it will run that on start. See src/node.js. You might be able to compile your sources with e.g. UglifyJS or Google Closure.
Edit: Also, you can require any modules you put in lib as if they were native modules. Example:
lib/_third_party_main.js
var foo = require('foo');
foo();
lib/foo.js
module.exports = function() {
console.log('O hai');
}
Compile and run, and it will print O hai.
Edit: You might be able to use Ender.js, Browserify or a similar browser packaging tool to build a single file.

Easy way to precompile Emberjs Handlebar templates with nodejs?

I'm enjoying emberjs a lot and would like to take the next step in a couple of my small, mobile apps and precompile my Ember/Handlebars templates as part of my build process.
I'd prefer to stay away from messing with Ruby and would like to use node.js as I'm more comfortable with using it.
I believe what I want to use is Ember.Handlebars.precompile, but unfortunately I'm unable to load the canonical ember.js file in a node environment. Example of a naive attempt from the node repl:
> var e = require('./ember');
ReferenceError: window is not defined
at /Users/jeremyosborne/git/projects/ldls/client/lib/emberjs/src/ember.js:5:1
at Object.<anonymous> (/Users/jeremyosborne/git/projects/ldls/client/lib/emberjs/src/ember.js:1596:2)
--- stack trace, you get the idea ---
I think I've already figured out how to set them up in my code so that they work correctly with my views, I just want to compile them in an environment outside of a browser DOM.
In lieu of getting the canonical ember.js to load in node, are there a specific set of files that I can pluck from the ember repo and use to compile my templates?
EDIT
I did a kluge fix that works great but gets an F for maintainability. I grabbed all the Handlebars code minus the reference to the window object. Then I followed with the Ember.Handlebars.Compiler code, replacing Ember.create with Object.create, exporting my Ember object, and viola things work seemingly great in node (as in it works and the functions produced are templates). But I don't consider this an answer to my own question due to the aforementioned maintainafail, so still open for answers.
EDIT 2
The above turns out to be a total fail. Perhaps there's something wrong in the procedure, but using Ember.Handlebars.precompile or Ember.Handlebars.compile doesn't work. The templates get made, but when I use the precompiled templates attached to Ember.TEMPLATES in my code, they do not work. I only seem to be able to get templates to work when they are explicitly passed in the modified script tags, as suggested on the emberjs.com site.
EDIT 3
I figured out what I was doing wrong. My answer is below.
I've written a grunt plugin called grunt-ember-handlebars that does exactly this. It pretty much mimics Garth's script, with one important difference:
It uses lib/headless-ember.js and lib/ember.js, which are maintained (at least for now) by ember.js to precompile default templates. If you don't want to use grunt, you can extract the relevant code from the precompile_handlebars helper in tasks/ember-handlebars.js.
Found a good enough solution to my problem that seems easy enough to maintain that I'll consider my problem solved.
Here's how I solved things:
Grab the minimal amount of code I need to precompile the ember templates.
Copy the contents of the ember handlebars into a file. This file is located at the emberjs repo: https://github.com/emberjs/ember.js/blob/master/packages/handlebars/lib/main.js
Within the code, find the one instance of window.Handlebars = Handlebars; and remove it.
Append the contents of the Ember template compiler and handlebar overrides. The file is located at the emberjs repo: https://github.com/emberjs/ember.js/blob/master/packages/ember-handlebars/lib/ext.js
Find all instances of Ember.create and change to Object.create.
Outline of the simple precompile procedure:
Load up the code with a var Ember = require('./my_ember_precompiler').Ember.
Get your templates as strings and compile them with var templateString = Ember.Handlebars.precompile(str).toString().
This will be different from app to app, but Ember seems to require registration of precompiled templates. After loading, for every created template, we need to register our templates. Basically wrap templateString in a call to Handlebars.template() and make sure this wrapped function is added to the Ember.TEMPLATES object.
The above is painless when it's automated in a script.
I've published a version of ember-precompiler with a similar interface to the handlebars command line utility. You can install it from NPM:
npm install -g ember-precompile
and then run it as:
ember-precompile template... [-f OUTPUT_FILE]
It does essentially what you describe (and what the gist versions do too): mock out the missing components needed to run Ember and Handlebars, compile the templates and add them to Ember.TEMPLATES.
Take a look at the npm package Ember-Runner
The following gist contains a nodejs build script that will precompile all .handlebars files in a given folder:
https://gist.github.com/1622236
I wrote an official precompiler npm module for anyone else who might be wanting to do this w/ a recent version of ember.js
https://npmjs.org/package/ember-template-compiler
It's simple to install and use (example below)
npm install ember-template-compiler
var compiler = require('ember-template-compiler');
var template = fs.readFileSync('foo.handlebars').toString();
var input = compiler.precompile(template).toString();
var output = "Ember.TEMPLATES['foo'] = Ember.Handlebars.template(" + input + ");";
Look at the code for the official ember-rails gem at https://github.com/emberjs/ember-rails
While it's not a node.js project, it does show you how to precompile the templates using the Rails 3.1+ asset pipeline, and it includes all the necessary Javascript code that you would need to do it in Node without having to hack together a solution that you'd have to maintain on your own.
More specifically, look at vendor/assets/javascripts/ember-precompiler.js and lib/ember-rails/hjs_template.rb
I'm far from an expert on Node (obviously, Rails is more my thing).. but I think those two files should point you in the right direction. Basically, you're going to want to concatenate ember-precompiler.js (which acts as a "shim" for lack of a better word) with ember.js and then call EmberRails.precompile to compile your templates.
window object can be mocked by jsdom
var jsdom = require("jsdom").jsdom;
global.document = jsdom("<html><head></head><body></body></html>");
global.window = document.createWindow();
global.$ = global.jQuery = window.$ = window.jQuery = require("jquery");
global.Handlebars = window.Handlebars = require('handlebars');
global.Application = window.Application = {};
require('ember.js');
and now you can run anything from Ember including Ember.Handlebars.compile

Resources