Concatenating raw javascript to a webpack bundle - javascript

I'm currently working on a build process that uses webpack. One of the dependencies that I need to include in my bundle is an old third-party library that doesn't follow any module patterns. I wan't to just include it in it's raw form, but it's coded in such a way that the usual techniques don't work.
The offending library is coded a bit like this:
function OldLibrary() {
console.log('What are modules?');
};
var foo = window.OldLibrary;
// Proceeds to use foo without a care in the world
As you can see from the code above, the library assumes that it will always be in the global context, and so it doesn't bother to explicitly set window.OldLibrary.
At first I tried using script-loader, but this wraps the script inside function(module, exports) {}, which means that OldLibrary now belongs to the scope of that function instead of window's and this causes an exception. I also tried using imports loader like imports?this=>window, but this only sets this to window, so it doesn't have effect on functions that are supposed to be global.
Finally, I tried out raw-loader, but this just adds the script as a string, so it's not executed. Is there a way to tell Webpack to just concatenate the script as is without wrapping it inside another function?
Edit
It turns out that script-loader does actually handle this library as expected, I just wasn't matching it properly because it was the only file that didn't have a .min.js extension. I'm still interested in finding a way to concatenate scripts without wrapping them in functions, if possible.

Related

script tag text/babel variable scope

Firstly, I understand text/babel is not for use in production, but I found it quite useful for development as when I make a change to my .jsx file django's dev webserver will reload without me having to do anything (i.e. compile the JSX to JS after every change).
I am not in control of the build environment (e.g. django) as this is a small plugin for a larger system that I am not developing.
The problem is this:
<script type="text/babel" src="{% static "myapp/js/main.jsx" %}"></script>
<script>
$(function() {
console.log(mything);
}
</script>
Where mything is in main.jsx, something as simple as:
var mything = "hello";
If main.jsx is javascript (and the type of the script tags is changed accordingly) then this will work just fine. As text/babel though, it will not work because mything is not in scope.
Uncaught ReferenceError: mything is not defined
This makes sense to me as I wouldn't expect script tags of different types to share a scope, but I'm wondering if there is some clever way around this to aid development?
I previously had all the code in a single text/babel block, but as it grows, it would be nice to separate it out into several JSX files.
Without diving too deeply into the Babel source (looking at https://github.com/babel/babel/blob/master/packages/babel/src/api/browser.js), I'm going to guess that it reads your JSX source, performs transformation on the source, and then evals the source in some way to execute it. The scope is not shared because babel prepends 'use strict'; to the transformed code (standard in ES6).
If you really need to expose a variable, you can attach it to window (ie use window.mything in your JSX instead of just mything). Ideally, you should make use of modules as you split your code up into multiple files. You can make use of a build step to transform your code through Babel and use browserify/webpack to manage dependencies.

How to create and implement a custom JavaScript library

I am new to javaScript and am unsure how to go about creating a new js library and referencing this in another js file.
If I have a standalone file Utilities.js
var Utilities=
{
show: function(input)
{
alert(input);
}
};
Am I missing something as to how a library should be defined standalone?
My second question is how to use that is sub-sequent js files. What I did so far is:
<script type="text/javascript" src="resources/Utilities.js"></script>
In my index.html. Is this enough to reference it as:
Utilities.show("Hello");
In any other java script file?
I tested it in this fashion and got and error "Utilities is not defined"
NOTE: This is just an example and not my full and practical purpose.
Yes, including that Javascript file with that global variable declared is enough to call your methods this way Utilities.show("Hello"); from another Javascript file loaded after Utilities.js or inside a <script></script> section of your html.
But you can actually improve it a little, following the module pattern and exposing only the functions you really need to the global scope (you'll likely write some functions that the users of your library should not call directly, this allows you to do it in a clean way):
var Utilities=Utilities||(function () {
//Accessible only here
var privateArray=[];
//Cannot be called from outside this function
var privateFunction=function(){
}
//Return only what must be publicly accessible, in this
//case only the show() method
return {
show: function(input){
privateFunction();
alert(input);
}
}
})();
That (function () { /* Code */})();, defining a new scope for your code will also avoid name clashes with other global javascript object/functions.
It is OK to use object literals, but you can define libraries using other patterns (module, revealing module, constructors, etc).
I recommend these links to understand primitives, scopes, closures, object literals, etc.
http://bonsaiden.github.io/JavaScript-Garden/
http://jsbooks.revolunet.com/
To call the method inside index.html you need to add a tag.
<script>
Utilities.show("Hello");
</script>
But this approach it's not recommended. Instead of it, you can create a new JS file to run your library code.
<script type="text/javascript" src="resources/Utilities.js"></script>
<script type="text/javascript" src="resources/main.js"></script>
In main.js
Utilities.show("Hello");
Hope it helps.
Given the fact that you gave, within yout question, zero context of what you're trying to achieve, the best answer to your original question is that it depends.
If you just need a bunch of files and you're done (like in your example, Utilities.js and a few more) then you're ok with the way you're heading to.
But of course, you'll allways want to scale your front end and thus you should adhere to some architectural pattern. So, if you're building a client side (browser-side) application, then you should really implement your libraries using the module pattern, and begin your project from a good project example / scaffold.
On the other hand, if you're rendering the html on server (e.g. you're using PHP to render the final html file that will be sent to browser) and you just need some thin functionality in the browser, the way you begun can be okay if you're careful. Also, you can still implement the module pattern here too, although I strongly suggest that you should make use of namespacing to have a clear separation of concerns.
In browser based javascript you can't just call functions from different files yet. In Es6 there are ways. But not yet. Which mean just because you have some variable or function etc then you cant reference it automatically in another file.
Unless both files are loaded into one html and are loaded in order.
Alternatively you could run task runner like grunt and 'merge' them upon each build.
Javascript doesnt have special concept of library, in es6 it's a little different, everything is an object.
What you are doing is just creating an object. and yes it will work.

How can I access constants in the lib/constants.js file in Meteor?

I followed the documentation to put the constants in the lib/constants.js file.
Question:
How to access these constants in my client side html and js files?
Variables in Meteor are file-scoped.
Normally a var myVar would go in the global Node context, however in Meteor it stays enclosed in the file (which makes it really useful to write more transparent code). What happens is that Meteor will wrap all files in an IIFE, scoping the variables in that function and thus effectively in the file.
To define a global variable, simply remove the var/let/const keyword and Meteor will take care to export it. You have to create functions through the same mechanism (myFunc = function myFunc() {} or myFunc = () => {}). This export will either be client-side if the code is in the client directory, or server-side if it is in the server directory, or both if it is in some other not-so-special directories.
Don't forget to follow these rules:
HTML template files are always loaded before everything else
Files beginning with main. are loaded last
Files inside any lib/ directory are loaded next
Files with deeper paths are loaded next
Files are then loaded in alphabetical order of the entire path
Now you may run into an issue server-side if you try to access this global variable immediately, but Meteor hasn't yet instantiated it because it hasn't run over the file defining the variable. So you have to fight with files and folder names, or maybe try to trick Meteor.startup() (good luck with that). This means less readable, fragile location-dependant code. One of your colleague moves a file and your application breaks.
Or maybe you just don't want to have to go back to the documentation each time you add a file to run a five-step process to know where to place this file and how to name it.
There is two solutions to this problem as of Meteor 1.3:
1. ES6 modules
Meteor 1.3 (currently in beta) allows you to use modules in your application by using the modules package (meteor add modules or api.use('modules')).
Modules go a long way, here is a simple example taken directly from the link above:
File: a.js (loaded first with traditional load order rules):
import {bThing} from './b.js';
// bThing is now usable here
File: b.js (loaded second with traditional load order rules):
export const bThing = 'my constant';
Meteor 1.3 will take care of loading the b.js file before a.js since it's been explicitly told so.
2. Packages
The last option to declare global variables is to create a package.
meteor create --package global_constants
Each variable declared without the var keyword is exported to the whole package. It means that you can create your variables in their own files, finely grain the load order with api.addFiles, control if they should go to the client, the server, or both. It also allows you to api.use these variables in other packages.
This means clear, reusable code. Do you want to add a constant? Either do it in one of the already created file or create one and api.addFiles it.
You can read more about package management in the doc.
Here's a quote from "Structuring your application":
This [using packages] is the ultimate in code separation, modularity, and reusability. If you put the code for each feature in a separate package, the code for one feature won't be able to access the code for the other feature except through exports, making every dependency explicit. This also allows for the easiest independent testing of features. You can also publish the packages and use them in multiple apps with meteor add.
It's amazing to combine the two approaches with Meteor 1.3. Modules are way easier and lighter to write than packages since using them is one export line and as many imports as needed rather than the whole package creation procedure, but not as dumb-error-proof (forgot to write the import line at top of file) as packages.
A good bet would be to use modules first, then switch to a package as soon as they're tiring to write or if an error happened because of it (miswritten the import, ...).
Just make sure to avoid relying on traditional load order if you're doing anything bigger than a POC.
You will need to make them global variables in order for other files to see them.
JavaScript
/lib/constants.js
THE_ANSWER = 42; // note the lack of var
/client/some-other-file.js
console.log(THE_ANSWER);
CoffeeScript
/lib/constants.coffee
#THE_ANSWER = 42
/client/some-other-file.coffee
console.log THE_ANSWER

Understanding meteorjs scope by adding the autotags library?

I'm trying to experiment with [this library](https://code.google.com/p/autotags/ in meteor).
First, when I tried to install all the individual javascript files to client/compatibility folder, I get an error message when calling AUTOTAGS from the constants js file:
AUTOTAGS = { ... } // autotags-js-core.js
AUTOTAGS.TAG_CONSTANTS = [ 'news','research','favourite' ]; // autotags-js-constants.js
It kept saying that AUTOTAGS was undefined. I deduced from reading about global scope that any external library with VAR that you want to have global scope, put it under compatibility.
If that's correct, then why isnt the scope global when putting all individual files under lib/external? AUTOTAGS = {..} without var means its available to entire application right?
Instead, I got the scope to work by combining all the javascript files in one single js file under lib/external. I thought I understood, but it gets worse.
Within a single js file - any function that begins with a closure is local to that FILE, and not global scope, whilst any function assigned to variable name makes it a global variable?
function(something() { ... } ) // closure, local
generateTags = function() { ... } // global scope?
var generateTags = function() { .... } // is this local or global?
If the var generateTags function is local, then putting it under client/compatibility will make it global? Lastly! - I get the vague notion that I should define global variables under lib/environment.js from here - https://github.com/oortcloud/unofficial-meteor-faq#where-should-i-put-my-files, is that true?
There are actually two questions here. One is about how the scope works in javascript, the other about integrating the autotags library into a meteor project. Since the answer for the first one should be relatively easy to find somewhere else, I'll only try to answer the second question.
The reason you are getting this "undefined" error is not a scope problem but it is somehow related to the order in which the files are loaded into your meteor app. Namely, autotags-js-constants.js comes before autotag-js-core.js because constants precedes core in the alphabetic ordering. In consequence, the AUTOTAGS variable is not defined at the point when the autotags-js-constants.js file is being parsed.
One simple way to overcome this issue is to rename your files, so as to enforce the right loading order. Another way is to use a tool that will enable you to define dependencies between files. If you are interested please take a look at require project, which is basically a lightweight and meteor friendly implementation of the core requirejs features.
However, probably the best solution is to create a custom smart package. It would allow you to explicitly define the order in which the files should be loaded. Just take a look on some existing smart packages to get the idea on how this should be implemented. I would also recommend using meteorite to manage your custom smart packages.

Replacing Handlebarjs's escaper

I'm working on a setup that runs a bunch of shell commands on a remote system when triggered. Instead of having a bunch of concatenated strings, for maintainability I decided to use a templater.
Handlebarjs has so far proven to be amazing, but by default it html encodes output. I've learned you can add { noEscape: true } to the compile command, or wrap the variable in {{{ }}}, but what I'd like to do is for certain templates, replace the default escaper entirely. Is this doable?
Right now my templates look like:
mkdir \"{{shellEscape directory}}\" with noEscape turned on, but I'd rather default that behaviour.
Failing that, I'd like to know if there is a Util function that translates the variable provided to a string (You can provide handlebar a function, but a helper doesn't evaluate it first)

Categories