I am looking for a "module definition" framework to run on the server when deploying my JavaScript App.
I am aware of require.js and other AMD frameworks. However, the necessity to implement define() and require() in my production code is non-satisfying for my purposes and since the App will be deployed in one file, I do not need the ability to inject scripts asynchronously.
Is there a build tool that can merge scripts without adding infrastructure code?
For clarification: The HTML page embedding the scripts is not relevant to my problem. The merging process should be done on script level.
main.js:
function a() {
import("b");
b();
}
b.js:
var b = function() {
alert("b!");
};
Should simply become something like:
function a() {
var b = function() {
alert("b!");
};
b();
}
So you want a tool to find and replace
<script src="SOURCE_URI" type="text/javascript"></script>
in your HTML file With
<script type="text/javascript">
CONTENT_OF_SOURCE_URI
</script>
Or maybe you want to collect all of the SOURCE_URI's and then combine them into one concatenated file and then run that file into a JavaScript compiler/optimizer/obfuscater like Closure and then output the inline script?
Most of the tools that exist will probably use require() and take a JS file as the input, so I suggest you write your own. A simple regex to parse the SRC= out of the script tags in your HTML will probably suffice.
edit https://developers.google.com/closure/compiler/docs/api-tutorial3
With the default compilation level of SIMPLE_OPTIMIZATIONS, the Closure Compiler makes JavaScript smaller by renaming local variables.
There are symbols other than local variables that can be shortened,
however, and there are ways to shrink code other than renaming
symbols. Compilation with ADVANCED_OPTIMIZATIONS exploits the full
range of code-shrinking possibilities.
Compare the outputs for SIMPLE_OPTIMIZATIONS and
ADVANCED_OPTIMIZATIONS for the following code:
function unusedFunction(note) {
alert(note['text']);
}
function displayNoteTitle(note) {
alert(note['title']);
}
var flowerNote = {};
flowerNote['title'] = "Flowers";
displayNoteTitle(flowerNote);
Compilation with SIMPLE_OPTIMIZATIONS shortens the code to this:
function unusedFunction(a){alert(a.text)}function displayNoteTitle(a){alert(a.title)}var flowerNote={};flowerNote.title="Flowers";displayNoteTitle(flowerNote);
Compilation with ADVANCED_OPTIMIZATIONS shortens the code even further
to this:
var a={};a.title="Flowers";alert(a.title);
Both of these scripts produce an alert reading "Flowers", but the
second script is much smaller.
Related
This is a legacy project so I'm not going to change everything to TypeScript, but I do want to slowly start combining it, so I want to call TypeScript code from normal JS, using RequireJS. I have a 1-login.js and realtimeConnection.ts (that's transformed into realtimeConnection.js):
<script type="text/javascript" data-main="/content/scripts/realtimeConnection.js" src="/content/require.min.js"></script>
...
<script type="text/javascript" src="/content/pages/1-login.js"></script>
This is 1-login.js:
(function ($) {
requirejs(["/content/scripts/realtimeConnection.js"], function (rtCon) {
debugger;
// The probelm: rtCon === undefined
});
...
...
}(jQuery));
realtimeConnection.ts:
export class RealtimeConnection {
}
Which in turn becomes realtimeConnection.js:
"use strict";
var RealtimeConnection = (function () {
function RealtimeConnection() {
}
return RealtimeConnection;
}());
exports.RealtimeConnection = RealtimeConnection;
So how do I expose my TS code to the JS code via RequireJS?
Ok found it: I needed to tell the TypeScript compiler to build it in a RequireJS/AMD style:
Since I'm using Visual Studio 2015, I placed a tsconfig.json file with the appropriate settings ("module": "amd" in particular) in the root content dir, as per this SO answer.
Now it works.
Now to whoever going to mark it as duplicate:
That allegedly duplicate question, which its answer I linked above, has the title "Visual Studio 2015 RC does not create sourcemap when saving Typescript file". The answer is the same, but the question is different.
I found my answer, hope it'll help others as well... BTNOMB.. just saying...
I write different files under JavaScript Module Patern like this:
file 1.js
var Module = (function (module) {
module.function = function () {
console.log(this.thirdFunction) // This is my issue. I cannot access the function from the third file because it is concatenated in a way that the function still not exist.
};
return module;
}(Module || {}));
some-folder/file 2.js
var Module = (function (module) {
module.somethingElse = function () {
};
return module;
}(Module || {}));
whatever/file 3.js
var Module = (function (module) {
module.thirdFunction = function () {
};
}(Module || {}));
I put all these files in different directories, names in a different time.
Then I am using concatenating tool to have one file and then I use it in my html file. But I am facing trouble that I cannot resolve.
To have all these working, I have to include them in a specific way and order to call functions from whatever I need and to re-order files when something is not yet defined/created in the returned final object. If I have 100 files and folders it will be a trouble for me again.
Do I understand this right: http://gruntjs.com/configuring-tasks#globbing-patterns
that I have manually to order files, folders and everything in my grunt tasks?
I do not want to use AMD tools, just plain JavaScript files and some approach to hack the order requirements. If there is no any easy idea for me to you, I would try the AMD tools like require.js but I am not sure if these kind of tools can help with this lame problem.
I would appreciate some grunt tool, some files/folders names conventions, anything that would not force me to install more and more tools.
Thank you in advance!
Another thing that bothers me is the following:
If I want to isolate code but I do not have to return object property in the final object, is it alright to do something like this:
file 4.js
var Module = (function (module) {
var someThing = Module.somethingElse() // from file 2.js
and then using someThing here for clicking, DOM rendering, etc, etc
}(Module || {}));
Is it stupid to stick to the same var Module conventions for files where I actually do not return anything? I just think of way how to avoid the object name and using this again
Indeed, AMD tools were created just for this kind of problem. However you can work around this to some extent with grunt. You could simply organize the files that need to go first into a folder and list them out in the order you want, and then have another folder containing all files who's order doesn't matter, which will include everything.
'js/main/First.js',
'js/main/Second.js',
'js/rest/*.js'
No matter what you choose for this project, you might want to look into Require.js or Browserify for future work.
When I am using ADVANCED_OPTIMIZATIONS in closure, I can add to the web.config attributes such:
<compilation debug="false">
and than when I will write in my code:
if (goog.DEBUG) { code }
on advanced mode I will NOT see this script inside the .js file.
I would like to do the same with my own properties - I've created a define.js file:
Define.js:
goog.scope(function() {
define.IS_SHOW_CODE = false;
}
and wrote code:
if (!define.IS_SHOW_CODE) { code }
and I still CAN find this if and its content inside the compiled .js file!
How to prevent the closure from compiling script in advanced mode?
If "goog" works, likely you are missing the declaration of "define".
It should look something like this:
var define = {}; // goog.provide('define') would also work here.
/** #define {boolean} */
define.IS_SHOW_CODE = true;
Ok i found how -
i MUST use the prefix goog.[xxx] in order to tell the compiler to remove the script inside.
using "define" instead didnt remove the script.
so i've changed the define.IS_SHOW_CODE to goog.IS_SHOW_CODE
I am using RequireJS and the r.js optimizer to build a production-ready app. I noticed that when passing a variable to a require() call, everything works as expected.
var someDependencies = ["dependency-1", "dependency-2", "dependency-3"];
require(someDependencies);
However, when running the optimizer the dependencies aren't read from the variable, and therefore aren't included in the concatenated app script. As I understand it, this is because r.js looks for string literals inside of require() calls. It does not process JavaScript, so a variable means nothing to it. This is even stated in the documentation for r.js.
The optimizer will only combine modules that are specified in arrays
of string literals that are passed to top-level require and define
calls, or the require('name') string literal calls in a simplified
CommonJS wrapping. So, it will not find modules that are loaded via a
variable name.
My question is: Is there any way to overcome this so that I can pass variables into require() calls but still include them in the r.js build?
Modify the source before passing it to optimizer programatically. In your build configuration specify:
onBuildRead: function (moduleName, path, contents) {
// modify contents here
return contents;
}
This is called before optimizer starts processing the source. You can then match dependencies and replace. Simple concept:
var content = 'var someDependencies = ["dependency-1", "dependency-2", "dependency-3"];\n' +
'require(someDependencies, function(){\n' +
' });';
var varName = /require\((\w+)/.exec(content)[1];
var regex = new RegExp(varName + '\\s*=\\s*(\\[.*\\])');
var dependencies = regex.exec(content)[1];
var modifiedContent = content.replace('require(' + varName, 'require(' + dependencies);
console.log(modifiedContent);
See FIDDLER demo.
I have a file with a JS object:
function Monitor() {
var self = this;
...
And I have a file that creates an instance of this and uses it.
self.monitor = new Monitor();
The files are included in a cshtml file in order:
<script type="text/javascript" src="#Url.Content("~/Scripts/Shared/Monitor.js")"> </script>
<script type="text/javascript" src="#Url.Content("~/Scripts/Views/NewMonitor_Index.js")"></script>
The problem is I get this error:
Warning 1 JS Hint: 'Monitor' is not defined.
How do I configure it so that it finds the monitor object?
I don't think if there is an automatic way. Although JSHint could detect other script tags, it is probably more difficult to get the actual path to the file.
Anyways, if I know that a certain symbol is definitely available in the context, I add a
/*global Monitor*/
at the beginning of the script.
If a symbol will be available in every script, I add it to my .jshintrc file in the directory, like
{
"predef": [
"Monitor"
]
}
But I don't know if/how this works on Windows.