I am working on a project where we want the user to be able to define custom colors. We are running the latest version of Meteor with, among others, the less package.
Right now all colors are variables located in a single theme.lessimport file which is included early in processing. All colors throughout the site (and many subsequent less files) are generated from these few variables.
The idea was to just generate a new userTheme.lessimport file for each user that, if present, could be imported just after the theme.lessimport file to override the variables with custom values. It all works beautifully and flawlessly if you physically add the file to the directory, but I can't seem to even think of a way to do it dynamically/programmatically.
I'm starting to wonder if this can even be done with less.
one of the big hang-ups is that so much of the css is derived from these variables—including CSS included with our own app's plugins/modules.
it appears that you can't import a remote file for inclusion in less pre-processing... so the file can't be generated on a remote server (this would be the ideal situation for our situation as user data will exist on an API server).
there doesn't seem to be any programmatic way to generate or otherwise inject any values into less—at least on Meteor—as I can't find any way to interact with the less through JS.
Aside from this inconvenience, less has been perfect for what we're doing, so I really want to make this work. Hoping someone out there has some wisdom or direction they can impart.
Take a look at how the bootstrap3-less package implements variables and mixins. Specifically the Advanced Usage section of their README.
"If you want to #import a file, give it the extension .import.less to prevent Meteor from processing it independently." So in your instance you'll name your theme file: theme.import.less
Of course you can do it. Just use the "fs" node module.
Here's a rather stupid example. There are lots of gotchas when you do it, but for a basic proof-of-concept, check this.
if (Meteor.isClient) {
Template.hello.greeting = function () {
return "Welcome to less_injector_meteor_test.";
};
Template.hello.events({
'click #button': function () {
var css = "body {background: " + $("#color").val() + ";}";
Meteor.call("writeToUserThemeFile", css);
}
});
}
if (Meteor.isServer) {
Meteor.methods({
"writeToUserThemeFile" :function(css) {
var fs = Npm.require("fs");
var path = "/Users/charnjitsingh/Desktop/less_injector_meteor_test";
fs.writeFile(path+"/user_theme.less", css, function(err) {
console.log("WRITING FILE");
if (err) {
console.log("ERROR WHEN WRITING", err);
}
});
}
});
}
Related
I'm using Webpack to bundle source code and assets for a game. I also use the CompressionPlugin() to make static gzip files available so that my web server can send the precompressed files when appropriate. Some of the game assets are large so I have a loading experience up front that shows a progress bar while the assets are fetched.
Unfortunately a problem arises on Chome during loading when receiving a gzip response for an XMLHttpRequest in that the onprogress total is always 0. There are some imperfect workarounds for this such as this solution but they're not entirely appropriate for my case.
Instead I'd like to inject the compressed & decompressed file sizes of specific bundled assets into the html or javascript so that they're immediately accessible to the loading javascript code. Injecting something as follows would be perfect:
<script>
const assetSizes = {
"game.25317abd3eb6cf0fb0f1.wasm": {uncompressed: 8192, compressed: 1024},
"game.25317abd3eb6cf0fb0f1.data": {uncompressed: 8192, compressed: 1024}
};
</script>
I'm somewhat new to webpack so I'm not entirely sure how to approach this. I've considered using the WebpackManifestPlugin and implementing a custom generate option function. This would allow me to control the output of the generated manifest.json but it's still not clear to me if this the right thing to do or how I'd go about subsequently injecting this files contents ahead of my own loading javascript code.
Perhaps there is a better approach that would be more appropriate?
Update: I've been trying to progress this further and it feels like a custom Webpack plugin might be the right direction to go. If I tap into the afterEmit compiler hook it seems I have access to the filesizes I need and I can construct an appropriate dictionary:
class InjectFileSizePlugin {
apply(compiler) {
compiler.hooks.afterEmit.tap(
"InjectFileSizePlugin",
(compilation) => {
const fileSizes = {};
for (const [assetPath, assetInfo] of compilation.assetsInfo) {
if (assetInfo.related) {
fileSizes[assetPath] = {
uncompressed: assetInfo.size,
compressed: -1
};
if (assetInfo.related.gzipped) {
const gzippedAssetInfo = compilation.assetsInfo.get(
assetInfo.related.gzipped
);
if (gzippedAssetInfo) {
fileSizes[assetPath].compressed =
gzippedAssetInfo.size;
}
}
}
}
console.log(fileSizes); // <-- output is as I'd like, how to inject it now?
}
);
}
}
What's not clear though is how I can now go about injecting this fileSizes data into the bundle as afterEmit is called very late in the compilation stage after the bundle javascript has been emitted. There is an additionalPass compiler hook but I currently can't figure out how it works.
changing exports.X in a function seems to not work...
I want to be able to load settings from a file & access them in Node.js. I have this currently, however, the clients connecting to my node application can edit what's in the settings file. Unfortunately as it stands the Node application has to be restarted for the changes to take effect. Is there a way I can reload the module.exports on the fly?
EDIT:
Settings file is literally a JSON string.
My settings module is 'required' in almost every single file, and there's a lot of files... So reloading it per-file basis is out of the question. I do, however, know precisely when someone makes a change to the settings.
If you are using require to load the settings and only referencing the settings from one module, then doing something along the lines of:
delete require.cache[require.resolve(filename)];
will work for you.
If, on the other hand, multiple modules will be referencing these settings, that approach can become a bit unwieldy and open you up to unforeseen bugs. For example, if any of the modules are holding on to a reference to the required settings file, they would each need to somehow learn that the settings had changed and update their references.
To alleviate (not completely solve) the caching issue, you build your settings interface so that users of it must access either the settings object via a function and/or require that properties are accessed via functions. Even with this model, someone may still decide to cache a setting causing an obscure failure later down the road.
Using the simplest approach of a single getter for the settings object would look something like this:
var settings = require('./settings.json');
// ... watch for changes and reload by invalidating node's cache
module.exports = function() { return settings; }
Usage:
var settings = require('./path/to/settings');
settings().foo;
There are several libraries that do settings. Depending on your needs, I'm partial to nconf.
I'd set up a file watcher here that checks for changes of a JSON file dynamically. It is not recommended practice to change a JS script once the app is running.
Something like:
var _ = require("lodash");
var fs = require("fs");
var result = {};
fs.watch('my-settings.json',function(event,filename){
fs.readFile(filename,function(err,data){
if(err){
// your error catching
}
_.extend(result,JSON.parse(data));
});
});
module.exports = result;
Now, this comes with lots of caveats, first that fs.watch is not always supported by all platforms.
http://nodejs.org/api/fs.html#fs_fs_watch_filename_options_listener
Second, that it's really awkward to change a property like this. The expectation is generally that exports of module not mutate. I'd instead recommend exposing a method whose result can change based on the state of the file, a getter for the resulting data.
Third, a file watcher can be expensive, memory-wise.
This is better code, IMHO:
var _ = require("lodash");
var fs = require("fs");
var filename = 'my-settings.json';
var lastModified;
var mySetting;
module.exports = {
getSettingAsync : function (callback) {
fs.stat(filename,function(err,stat){
if(stat.mtime == lastModified) {
callback(mySetting);
} else {
fs.readFile(filename,function(err,data){
if(err){
// your error catching
}
// this assumes that your data is always correct
mySetting = JSON.parse(data).mySetting;
callback(mySetting);
});
}
});
}
};
In this case, we both check for a JSON file, and expose this as an async method. You could just as easily change the code to use the sync versions if need be and return the value instead of invoking the callback. This version checks when the file was changed, which is cheaper than reading the whole file every time, reads the file if newer and saves you the need to use a potentially buggy file watcher.
By the way, I've not tested this code and it may contain errors as is, but the concept is sound.
But, perhaps the more salient question, why not just store that value in the database?
I have a interesting concept I was working on and looking over, through various stack questions on auto loading JavaScript. I dint want to use a third party tool, aside form jquery, so I thought I would role my own. The concept I have is:
var scripts = {
'name' : 'path/to/script_dir/' // Load all scripts in this file.
}
requireScripts(scripts); // Requires all scripts
// Call your classes, methods, objects and so on ....
The requireScript() function would work something like:
function requireScript(hash){
$.each(hash, function(key, value)){
$.ajax({
url: value,
dataType: "script",
async: false,
error: function () {
throw new Error("Could not load script " + script);
}
});
});
}
Note: The above is just a concept, I don't think it will work.
The above would let you load SPECIFIC scripts. so in essence your hash key value would be 'name' : 'path/to/specific/script'. The issue this posses is that your hash would get rather large ....
The other issue I ran into is what if I simplified this to "php pear naming standard" so, as the trend seems to be - we would create a class, and it would be named after its location:
var some_folder_name_class = function(){}
Would be translated by the autoloader as: some/folder/name/class.js and then loaded that way.
To wrap up and get to my point there are two ways of loading javascript file I am looking at, via rolling my own "require" method. One is loading a directory of javascript files via the hash idea implemented above. (the provided code sample of how this hash would be walked through would have to be changed and fixed....I dont think it works to even load a single file)
OR
to have you just do:
new some_class_name() and have a global function listen for the new word, go find the file your trying to call based on the name of the class and load it, this you never have to worry - as long as you follow "pear naming standards" in both class and folder structure your js file will be loaded.
Can either approach be done? or am I dreaming to big?
I see a lot of frameworks do a bunch of require('/path/to/script') and if I could role my own autoloader to just allow me to either load a directory of js files or even have it where it listens for new before a class instantiation then I could make my life SO MUCH easier.
Have you consider using requirejs and probably Lazy loading.
http://www.joezimjs.com/javascript/lazy-loading-javascript-with-requirejs/
Here is sample version:
You can download here.
The sample is based on this folder structure :
public
index.html
scripts
app.js
lib
** jquery-1.10.2.js
** require.js
3 . From Code:
html
`<!DOCTYPE html><html>
<head><title>Sample Test</title>`
<script src="scripts/lib/require.js"></script> <!-- downloaded from link provide above-->
<script src="scripts/app.js"></script></head>
`<body><h1>My Sample Project</h1><div id="someDiv"></div></body></html>`
application configuration app.js
requirejs.config({
baseUrl: 'scripts',
paths: {
app: 'app',
jquery: 'lib/jquery-1.10.2' //your libraries/modules definitions
}
});
// Start the main app logic. loading jquery module
require(['jquery'], function ($) {
$(document).on('ready',function(){
$('#someDiv').html('Hello World');
});
});
jQuery-only option
If you are looking for a jQuery-only solution, have a look at jQuery.getScript(). It would be a great candidate for handling the script loading portion of your problem. You could then write a very small wrapper around it to load all the scripts—something like you wrote above:
var loadScripts = function(scripts) {
$.each(scripts, function(name, path) {
jQuery.getScript("/root/path/" + path + ".js");
})
}
If you are interested in more information on this approach, read this article by David Walsh.
Other great libraries
I strongly recommend taking a look at the current batch of script-loading libraries. I think that you will pleasantly surprised by what is out there. Plus, they come with the benefit of great community support and documentation. RequireJS seems to be the front runner but David Walsh has great articles on curl.js and LABjs.
I've started using bundling and minification included with the MVC4 Beta. I'm running into a few issues with it:
For one thing, if I use the classic <script src="Folder/js" type="text/javascript"/> bundling, it seems like I have to rename my files to make sure they're bundled in the proper order.
Let's say I have three javascript files: "ants.js", "bugs.js", "insects.js"
ants.js depends on bugs.js
bugs.js depends on insects.js
Default bundling seems to bundle them in alphabetical order.
To get them to bundle properly, I have to rename them to: "0.insects.js", "1.bugs.js", "2.ants.js"
That's really hackish and there has to be a cleaner way.
The next problem I'm having is debugging. I like to step through the javascript in my testing browsers, is there a way to turn off just the minification while in DEBUG mode?
EDIT: To be clear, I know I can create bundles and register them from C#, it just seems really ugly to have to do it that way.
To temporarily get non-minified output use this
public class NonMinifyingJavascript : IBundleTransform
{
public void Process(BundleContext context, BundleResponse bundle)
{
if(bundle == null)
{
throw new ArgumentNullException("bundle");
}
context.HttpContext.Response.Cache.SetLastModifiedFromFileDependencies();
foreach(FileInfo file in bundle.Files)
{
HttpContext.Current.Response.AddFileDependency(file.FullName);
}
bundle.ContentType = "text/javascript";
//base.Process(context, bundle);
}
}
If you wanted it based totally on a config setting, I imagine you could create an IBundle transform that delegates to this one or JsMinify depending on your config setting
In order to control the ordering of the javascript files you need to use the BundleFileSetOrdering
var javascriptBundle = new Bundle("~/site/js", new NonMinifyingJavascript());
//controls ordering for javascript files, otherwise they are processed in order of AddFile calls
var bootstrapOrdering = new BundleFileSetOrdering("bootstrap");
//The popover plugin requires the tooltip plugin
bootstrapOrdering.Files.Add("bootstrap-tooltip.js");
bootstrapOrdering.Files.Add("bootstrap-popover.js");
BundleTable.Bundles.FileSetOrderList.Add(bootstrapOrdering);
javascriptBundle.AddDirectory("~/Scripts", "bootstrap-*.js");
I use the MVC default NoTransform instead of the NonMinifyingJavascript proposed by chrisortman. As far as I know it does the same.
But still not optimal. Ideally I want a script tag for each idividual script file when I want to debug. This makes debugging a lot easier with VS11, which I like to use (one debugger so I can debug js and c# in one debug session).
So I created this little helper:
#helper RenderScriptTags(string virtualPath)
{
if (Minify /* some appsetting */)
{
<script src="#System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl(virtualPath)"></script>
}
else
{
foreach (var file in System.Web.Optimization.BundleResolver.Current.GetBundleContents(virtualPath))
{
<script src="#Url.Content(file)"></script>
}
}
}
#RenderScriptTags("~/libraries")
I have a single page app, so I have this in my main cshtml file, but it can easily be generalized by moving this to an htmlhelper extension method.
Works nice!
This code takes also into account the BundleFileSetOrdering if you have set one!
Might also take a look at RequestReduce. It bundles your scripts and CSS without any coding or configuration by looking at how they are laid out on your page and bundling according to that. It also allows you to turn off bundling and minification via web.config or for individual requests via a querystring param: RRFilter=disabled.
I ran into this same problem yesterday and couldn't find a good solution with the new System.Web.Optimization namespace. There were some broken MSDN links, so the fact that everything is in beta means it may change, but I digress...
You could always load the scripts differently during development than in production. Easy to do with an AppSetting:
#if (System.Configuration.
ConfigurationManager.AppSettings["BundleResources"] != null)
{
#* load the css & js using bundles *#
}
else
{
#* load the css & js files individually*#
}
You can then enable / disable the optimization stuff by commenting out an appsetting in web.config:
<appSettings>
...
<!--<add key="BundleResources" value="uhuh" />-->
...
</appSettings>
I will explain my idea behind this:
I use python for google app engine + js + css
the main project will be stored under the src folder like this:
\src
\app <--- here goes all the python app for gae
\javascript <--- my non-packed javascript files
\static_files <--- static files for gae
now the javascript dir looks like this
\javascript
\frameworks <--- maybe jQuery && jQueryUI
\models <--- js files
\controllers <--- js files
\views <--- HTML files!
app.js <--- the main app for js
compile.py <--- this is the file I will talk more
About compile.py:
This file will have 2 methods one for the min and other for the development javascript file;
When is run will do:
Join all the files with "js" extension;
The app.js contains a variable named "views" and is an object, like a hash; Then the compiler copy the contents of each file with "html" extension located in the "/javascript/views/" dir using this rule;
example: if we have a view like this "/views/login.html" then the "views" js var will have a property named "login"; views['login'] = '...content...';
example2: "/views/admin/sexyBitcy.html" then view['admin.sexyBitcy'] = '...content...' or whatever exists in that html file..;
Then this big file will be saved into the "/src/static_files/core.js"; if is minified will be saved as "/src/static_files/core.min.js";
The javascript will use dependenccy injection, or sort of it. (:
I will explain how it will work then:
the index.html that is loaded when you come into the site loads the core.js and the jquery.js;
the core.js will create the layout of the page, as SEO is not important for the most of the pages;
the core.js uses the controllers-models-views to create the layout of course; the html for the layout is inside the var "views"; will be a heavy variable of course!
Some code:
mvcInjector = new MVCInjector;
mvcInjector.mapView(views['login'], 'login', LoginController);
parent = $('#jscontent');
jquery
view = mvcInjector.instanceView('login', parent); // <--- this will create the contents of the views['login'] in the parent node "parent = $('#jscontent');" then will instance the LoginController that will map the "SkinParts" (like in FLEX if you know); what does it mean map the "SkinParts"? - when the user will click on a button an handler for that action is defined in the controller; ex.:
// LoginController
this.init = function(){
// map skin parts
this.mapSkinPart('email', 'input[name]="email"');
this.mapSkinPart('submit', 'input[name]="submit"');
// link skin parts to handlers
this.getSkinPart('submit').click = this.login;
}
// handlers
this.login = function(event){
// connect to the db
// some problems here the get the value as the "this" keyword references to the this of the controller class, I will work it around soon
alert('open window button1' + this.getSkinPart('email').value());
}
If something is not clear just say something, I will be happy to explain;
So the question remains: is this scalable, manageable and fast enough for a big RIA application build with javascript+jquery and maybe with jqueryUI?
Thanks ;)
I like your idea quit a bit.
I would think about loading html pages by ajax, if they are big and there are many of them...
Have a look on angular project, I hope, it could help you a lot. It's a kind of JS framework, designed to work together with jQuery. Well suitable for test driven development.
It uses html as templates, you can simply create your own controllers, use dependency injector, etc... Feel free to ask any question on mailing list.
Then, I must recommend JsTestDriver - really cool test runner for JS (so you can easily run unit tests in many browsers, during development - let's say after save...)