totaljs / framework

Node.js framework
http://www.totaljs.com
Other
4.36k stars 450 forks source link

@{import()} an entire directory of files? #638

Closed zerr0ne closed 6 years ago

zerr0ne commented 6 years ago

Hi, just started playing around with total.js and was wondering if there is any way of telling the framework to concatenate an entire directory with files inside, let's say I have lots of JS files in /js/ and don't want to manually type all the filenames out when I do @{import()} or @{js()}, so I tried @{import('*.js')} but that didn't work (its just trying to include a file called *.js. Could that be a useful addition tot he framework to allow for importing entire directories?

petersirka commented 6 years ago

Hi @zerr0ne, this won't work ... Why don't you use F.merge()? It supports directory merging.

zerr0ne commented 6 years ago

Hi, thanks for the fast reply, yeah, thought so from looking at the core code... how would I use F.merge() for the purpose outlined below (and then use it in a layout)? Cannot figure it out based on the docs @ https://docs.totaljs.com/latest/en.html#api~Framework~F.merge as the examples there just merge files (typed out filenames), I just like to pass the directory, e.g. F.merge('/js/') or if that is not possible, use a wildcard type of expression like F.merge('/js/*.js') or F.merge('*.js'), is that possible with F.merge()?

EDIT: Damn, overlooked that:

// Merging all files
F.merge('/merge.js', '/js/*.js');

can I use something like @{merge()} in a layout file? (couldn't find anything in that direction @ https://docs.totaljs.com/latest/en.html#api~FrameworkViews

EDIT2: guess I found how it would be done @ https://github.com/totaljs/examples/tree/master/static-file-merge

would be convenient to have a @{merge()} available in the view engine though =), AND/OR it could also be solved by allowing directory paths to be passed to @{import(), @{js()}, @{css() which would then implicitly trigger F.merge() from within Controller.prototype.$import and Controller.prototype.$css / Controller.prototype.$js respectively. It would also be useful if F.merge() had a way of telling it to ignore files, or would that work by prefixing them with a dot .?

awesome work btw, keep it up!

zerr0ne commented 6 years ago

Tried if I can come up with something, but got stuck... put this in index.js (of total.js core)

/**
 * Merge directory of files and append <script> or <link> TAG
 * @private
 * @return {String}
 */
Controller.prototype.$merge = function(url, files) {
    var self = this;
    var paths = [];

    for (var i = 1; i < arguments.length; i++)
        paths.push(arguments[i]);

    return this.$absolute(F.merge(url, paths));
};

then tried to call it in the layout.html with @{merge('scripts.js', '/js/*.js')} but got the following error:

500: Internal Server ErrorError: View "layout": merge('scripts.js', '/js/*.js') - Line: 352 - merge is not defined
    at Controller.$viewrender (/path/to/project/node_modules/total.js/index.js:12418:9)
    at Controller.view (/path/to/project/node_modules/total.js/index.js:12363:14)
    at Controller.$viewrender (/path/to/project/node_modules/total.js/index.js:12469:15)
    at Controller.view (/path/to/project/node_modules/total.js/index.js:12363:14)
    at Controller.view_index (/path/to/project/controllers/default.js:9:7)
    at IncomingMessage.PROTO.$total_execute2 (/path/to/project/node_modules/total.js/index.js:14082:32)
    at IncomingMessage.PROTO.$total_execute (/path/to/project/node_modules/total.js/index.js:14050:9)
    at IncomingMessage.PROTO.$total_prepare (/path/to/project/node_modules/total.js/index.js:14342:9)
    at IncomingMessage.PROTO.$total_end (/path/to/project/node_modules/total.js/index.js:13991:9)
    at Framework.F.$requestcontinue (/path/to/project/node_modules/total.js/index.js:7055:9)

also tried @{G.merge('scripts.js', '/js/*.js')} within layout.html with even less success...how do I register "new" methods of the controller for the View?

molda commented 6 years ago

Hi, i would just like to point out one important thing and that is the view engine and all it's methods are synchronous and F.merge is asynchronous. The main thing is you only need to merge those files once at the start of your app, you don't need to do it every time you render a view. The F.merge creates a new file in the tmp directory and serves it when requested. In a way it has nothig to do with the views.

Even if the view engine was asynchronous You would want to avoid async stuff during the view render as much as possible due to the performance. The faster you respond the faster you can handle the next request.

The other methods @{import(), @{js()}, @{css() are sync since they only write a string into the view template during the render.

zerr0ne commented 6 years ago

Alright, that makes sense, thanks for taking the time to answer!

I'll then go with the approach from the example, using the "definitions" approach!

zerr0ne commented 6 years ago

Is it possible that F.merge()

a) is not automatically reloading/recompiling/remerging the files when a new one is added (I have to restart the app to make it see new files) and b) is not minifying the entire script file, e.g. there are always the "MERGED" comment blocks visible (is there a way to turn that off as it exposes system paths?)

zerr0ne commented 6 years ago

Aaah, got it, it's about using the debug.js or release.js configs, on release it will not show these inline comments, nice, all good (besides the auto-reload question)...

zerr0ne commented 6 years ago

Any explanation why F.merge() does not seem to recognize new files? (sorry about the open/close spam)...

petersirka commented 6 years ago

a) the framework doesn't reloading/recompiling/remerging files in release mode b) of course because you are in debug mode

zerr0ne commented 6 years ago

It also does not recompile in debug mode, e.g. if I add an entire NEW file in the folder /js/, it will not "see" it until the server is restarted, changes on EXISTING files are recognized and will trigger a recompile...

petersirka commented 6 years ago

Yes, it's a bit problem to implement because F.merge() loads file list when is declared and Total.js watcher doesn't watch non server-side files. So you need to restart app if you add a new file.

zerr0ne commented 6 years ago

Ah OK, so because F.merge() creates a "virtual" file basically?

petersirka commented 6 years ago

Yes.

zerr0ne commented 6 years ago

Thanks again!