Webpack: Get list of chunks from inside javascript - javascript

I wanted to access the list of JS chunk files from within entry point Javascript file. Is their a way I can inject it to JS as an array?
Description:
For example, I have following JS files:
node_modules
moment.js
jquery.js
public
index.html
menu.html
src
shared.js
utils.js
main.js (dependent on shared.js & uses moment)
index.js (dependent on shared.js & uses moment + jquery)
menu.js (dependent on utils.js & uses moment + jquery)
Now in webpack config I have 3 entry points say main, index, menu. For index & menu, I have related html files and I use HtmlWebPack plugin to inject the chunk files which works well.
But "main" entry point doesn't have any html files and it would be used for some other reason. Here for this entrypoint also it creates multiple dependent chunks say for example (0.js, main.chunk.js, runtime-main.chunk.js).
So for me, from "index.js" file, I wanted to get the list of all the chunks related to "main" entrypoint as array so that I can use those chunks dynamically when the code is getting executed in the browser.
Sample "index.js":
// Currently I am hardcoding this list, which makes me manually update this list whenever I add some more additional dependent files to it.
// I wanted this file list to be populated automatically while the bundling happens so that I need not maintain this list manually.
const filesList = ['/runtime-main.chunk.js','/0.js','main.chunk.js'];
filesList.forEach(file =>{
// I will do something with this file path
});
Mine is a very specific usecase where I definetely need the list of file names dynamically instead of somehow statically loading it. I know I can make use of "webpack-manifest-plugin" which generates the required mapping information as json file which I can use dynamically. But I am looking for a solution where it can somehow injected the list as array itself while bundling the code. I am definetely sure this is possible as I could achieve something similar elsewhere.
Question 2:
For the same usecase above, is their a way I can bundle all the dependency of "main" entrypoint alone as a single file which basically would be "main.chunk.js" so that i can just use that file without need for an array of files.
In advance, thank you for all your answers!!

Related

Webpack create JSON map file with dynamic modules

I have a project where I use turbolinks in conjunction with Webpack's dynamic imports.
What I have is that the initial javascript file is as small as possible and then for each page, I load the relevant javascript code:
document.addEventListener('turbolinks:load', () => {
const moduleName = document.body.dataset.route;
if (!moduleName) return;
const chunkName = moduleName.replace('.', '/');
import(`#pages/${chunkName}.js`)
.catch((e) => {console.error(e);});
});
This is a great approach as each page gets its own minimal JS file.
The only issue is that we wait until the page has fully loaded before we fetch the page's javascript which makes initial page loads feel slightly slow. Ideally, I would love to preload the assets for the page when the page loads.
I thought that maybe adding a link rel=preload would solve this issue but the thing is that I do not know which chunks I need to preload on each page. This is logic that only Weboack knows.
My webpack.config.js file looks like:
output: {
chunkFilename: 'js/chunks/[name].js?id=[chunkhash]',
},
So basically each chunk is put in the js/chunks directory and its name is 0.js, 1.js, 2.js etc.
I would love to maybe somehow generate an additional json file where webpack can build a map for me. It would basically look like this (chunk key: modules that are within it):
{
0: ['#pages/tips/index.js', '#pages/tips/show.js'],
1: ['#pages/destinations/index.js', '#pages/tips/show.js'],
}
Then, I would read the file on each page and dynamically create the link rel=preload. For example, say I render the tips/show page now, I would scan the file above for each key that contains the #pages/tips/show.js and render a link rel=preload for each file (0.js and 1.js file in this case).
I'm using Webpack Commons Chunk plugin to extract the same vendors and modules to their own chunk file.
Is doing such a thing is even possible?
Thanks!
Webpack has something that is called stats that contains a-lot of info regarding the compilation.
It also contains the separation of chunks.
You can checkout what react-loadable webpack plugin does in order to generate similar task.
Hope this helps.
EDIT
You will need to write a webpack plugin that hooks on emit phase and then you will have an access to the compilation object that has all the data.

How load several HTML files as "document" and then manipulate them with Javascript?

I want to create and manipulate several HTML files with JavaScript and NodeJS. The usual way to update HTML files is to include an update script at the top, but I want to create multiple HTML files with one JS file. I have a list with the necessary data to process. It contains the names for the HTML files and the data to put in.
My idea was to use a model.html file, which contains the data, that will be included in all of the files. This model file will be copied and renamed to the necessary file. Then I want to load it with a NodeJS module or any other way fit, so it will be recognized as "document" as if the script was included in the file. Then I want to manipulate it with plain JS. A for loop should iterate through the list and create the file, load it as "document", manipulate it and then go to the next file.
Is this possible?
If you want to just parse the HTML and get the document and operate on it you can check https://cheerio.js.org/
If you want to execute the Javascript also then you need to use Headless browsers like Headless Chrome

yii2 registering JS files to a View

I have A.php view file in /views/A/ folder.
And I have A.js js file in /views/A/ folder
Please help me register js file in view file.
As I understand I must write
$this->registerJsFile('path/to/file.js'); in view file.
But (Question A) I get method registerJsFile is not found in a class message from PHPStorm.
Also (Question B) what should I write in path considering both files are in the same folder /views/A/ ?
This is not elegant, but working if you need to have your js file registered after jquery (as seen in the Yii2 doc)
<?php $this->registerJsFile(Yii::$app->request->baseUrl.'/js/youFile.js',['depends' => [\yii\web\JqueryAsset::className()]]); ?>
If you register a js file by:
$this->registerJsFile("#web/js/all.js");
This will work but you will not be able to use jQuery. Because this file all.js is loaded before jQuery. To load after jQuery we make it depend it on 'yii\web\YiiAsset' or on \yii\web\JqueryAsset . So it will be loaded after jQuery.js. Example:
$this->registerJsFile("#web/js/all.js",[
'depends' => [
\yii\web\JqueryAsset::className()
]
]);
So What is difference between \yii\web\JqueryAsset and \yii\web\YiiAsset?
In jQueryAsset the js file will load after jQuery.js and in YiiAsset the js file will load after yii.js file.
If you want to create your own custom Asset Bundle:
<?php
namespace frontend\components;
use yii;
use yii\web\AssetBundle;
class CustomAssets extends AssetBundle
{
public $css = [
"path/to/css/file.css"
];
public $js = [
"path/to/js/file.js"
];
public $depends = [
];
}
Register your js file on given possion
$this->registerJsFile('path/to/file.js', ['position' => \yii\web\View::POS_END]);
The first argument is the actual JS code we want to insert into the page. The second argument determines where script should be inserted into the page. Possible values are:
View::POS_HEAD for head section.
View::POS_BEGIN for right after opening .
View::POS_END for right before closing .
View::POS_READY for executing code on document ready event.
This will register jQuery automatically.
View::POS_LOAD for executing code on document load event. This will register jQuery automatically.
The last argument is a unique script ID that is used to identify code block and replace existing one with the same ID instead of adding a new one. If you don't provide it, the JS code itself will be used as the ID.
An external script can be added like the following:
$this->registerJsFile('http://example.com/js/main.js', ['depends' => [\yii\web\JqueryAsset::className()]]);
The arguments for registerJsFile() are similar to those for registerCssFile(). In the above example, we register the main.js file with the dependency on JqueryAsset. This means the main.js file will be added AFTER jquery.js. Without this dependency specification, the relative order between main.js and jquery.js would be undefined.
is there any specific reason to include the file manually rather than creating an asset bundle?
In any case if you've read the documentation regarding assets, you would have noticed that there's a clear distinction about source, published and external assets.
The most important part of it being that source and published assets use different options to determine whether and how a file should be published.
In your case you've got a source asset which needs to be copied over to the assets directory.
The invocation of registerJsFile as hinted in the documentation, will expect a published asset.
Here you have specifically two options, which probably the first is more quick and coherent:
move the asset in the web/ folder, as in web/js/ or whatever you prefer and keep using registerJsFile()
add a new asset bundle for source assets, specifying the various options as detailed in the above linked page.
Hope this clears things out.
A: From the docs: http://www.yiiframework.com/doc-2.0/yii-web-view.html
Your code seem correct.
Do you register the js from the view file itself? not the controller?
The registerJsFile() method is from the view class.
Its highly possible that your IDE is not finding the method, have you tried it in a apache enviroment?
B: Use a alias

Best way to import JavaScript files into one file?

I have a background in coding in languages that have a concept of "classes". Now that I am coding JavaScript, I would like to code in a similar way so that each object oriented "class" I create is its own separate file.
see Accessing "Public" methods from "Private" methods in javascript class
see http://phrogz.net/JS/classes/OOPinJS.html
In other languages, I would create import statements at the top of the class file to ensure other custom classes that were used within a class file so that the other custom classes were compiled into the final binary.
Of course JavaScript is not a compiled language; however, I would still like to be able to be include some kind of "import" statement at the top of custom class files so I could ensure the imported JS "class" file was available for the user's browser to download.
It would be ideal if there were a 3rd party tool that combined all of my separate class files into one JS file so the browser only had to make one HTTP request for a single JS file instead of many calls for each indicidual JS "class". Does anyone know if such a tool exists where it would do the following:
allowed me to choose which JS files that I wanted to include in a single JS file
crawled thru the files I selected in step 1 and found all the "import" statements at the top of each custom "class" file. These "import" statements could simply be specially formatted comments in the code that the 3rd party recognizes as import statements.
The 3rd party would then create the single JS file with all of the files that were selected from step 1 and from all of the imported files that were found in step 2.
Some popular JavaScript frameworks seem to do just that. For example, jQueryUI allows you to customize the download of a single jQueryUI source file by allowing the user to check off which objects you want to use. If you uncheck an element that is needed for an item that you checked off, then the form tells you that there is a dependency you need to rectify before being able to proceed to download the file.
see http://jqueryui.com/download/
So is there a 3rd party tool that allows a developer to use some kind of "import" statement comment to ensure that many dependent JS files (and only the ones that the developer needs) to be combined into a single JS file?
RequireJS was built for exactly this purpose.
Have a look at Require.js. It lets you import various javascript files in a modularized fashion and add the required dependencies between them. Also at the end you can minify them all into one single JS file using r.js
A trivial batch file can do this for you:
#for %i in (classes/*.js) type %i >> build.js
This works best if your JS source files are all in one folder, and this example assumes that folder is named classes. It gets a bit more complicated if you have subfolders, but a similar principle can be applied.
Have a look at GruntJS, JQuery uses it for building. If you don't care for HTTP requests, you can use already mentioned RequireJS, which also has nice async methods to load files, which can improve perfomance in some situations.
Check out this class https://www.youtube.com/watch?v=KnQfGXrRoPM
This allows for importing on the fly within classes. also it allows
for importing all classes within an folder and all of its sub folders.
and its really simple because it is just a prototype function added to String.
just by adding the importer class you will call in classes like "com.project.Classfile.js".import();
or "com.project.*".import() to get all sub-classes.
fork on - https://github.com/jleelove/Utils

Grails: Javascript files in views folder

I'd like to split my views in Grails into 2 files a .gsp file and a .js file so that I get a cleaner Javascript separation from my views. So here's an example:
views/index.gsp
views/index.js
views/home/index.jsp
views/home/index.js
But when I simply add the index.js script reference like this:
<script src="index.js" type="text/javascript"></script>
all I get is a 404.
Does anyone knows how to deal with this?
A great benefit would be to have the ability to use view data inside the index.js file to produce the desired content.
Matthias.
Actually, it should be perfectly possible to serve a JS file (or any other file type) as a GSP from your grails-app/views/ directory. The only thing you have to do, is define a suitable URL mapping for those GSPs, e.g.:
"/javascript/home/index"(view:'/home/index.js')
With this URL mapping, you can put your JS code into grails-app/views/home/index.js.gsp (note the trailing .gsp) and you can use any grails tags in your JS source. To ensure that your JS is delivered with the correct content type, you may want to place
<%# page contentType="text/javascript"%>
at the beginning of your GSP.
Unfortunately, the createLink tag doesn't support link rewriting to views, but it should be easy to write your own tag to create those links.
Anyways, keep in mind that this won't have a very positive impact on your app's performance. It's usually better to have static JS files (and also serve them as static resources) while passing dynamic stuff as parameters to JS functions for example. This will also keep you from some headaches wrt. caching etc.
The idea is good, but Grails has this directory structure for a reason. The view folder is intended for a certain artifact type (views)..
You could clone your view folder structure under web-inf, but that gives you more work as I guess the idea behind this is to keep related files close together for convenience reasons.
Even though I'm not to excited about storing Javascript together with the view I loved Robert's idea of hooking into the build process by using build events to copy javascript sources into the right directory! If you decide to go down that road you might as well compress the sources while you're at it. ShrinkSafe is popular library.
I don't think you are allowed to access js inside views/
if you need to do that ... here is the trick
create your js and rename it with myjs.gsp (use "")
iniside _myjs.gsp type you js
... write down you js in here ...
inside you gsp (for example: index.gsp, view.gsp, etc)
type this tag to upload you js
Update 2:
Grails offer the possibility of hooking into the build lifecycle using custom events.
An event handler can be written which synchronises all JavaScript files under grails-app/views with the target folder of web-app/js.
Place the custom code in $PROJECT/scripts/Events.groovy. The PackagingEnd is a good target for the invocation, since it happens right after web.xml is generated.
eventPackagingEnd = { ->
// for each js file under grails-app/views move to web-app/js
}
Update
If you'd like the JavaScript files simply 'meshed' together, you can do that using symlinks, e.g.:
grails-app/views/view1/index.js -> webapp/js/view1/index.js
As far as I know, there is no way of forcing grails to directly serve content which is outside of web-app.
Alternatively, you can inline your JavaScript, but that can have performance implications.
JavaScript files belong under web-app/js.
Then you can reference them using <g:javascript src="index.js" />.

Categories