Dynamically load require.js modules under mixed base urls - javascript

We have an app using require.js, that at the same time allows extensions, and these extensions' js gets served from a different path. So in our config we have:
var require = {
baseUrl : '/app/js/modules/'
}
But the extension is served from /extension/foo-extension/js/modules/.
Also, extensions are dynamic to the point that they inject some html into the page that uses a data-module="foo/bar" which we pick up on to load that module.
Ideally we'd be able to pass / set a context for require.js that scoped the following module loads to be within baseUrl /extension/foo-extension/js/modules/. As far as I can tell we would need to do require('/extension/foo-extension/js/modules/foo/bar') to load foo/bar from foo-extension.
Here is some pseudo code to imagine where we need to handle setting the path / context:
define(['some-dep'], function(SomeDep) {
$.get('somepage', function(html) {
var extension = html.data('extension'); // "foo/bar"
var extensionBase = html.data('extensionBase'); // extension/foo-extension/js/modules/
// This is where we need to readjust require to use the base path for any foo module
});
});
Is there another solution to this part from going the absolute path route?

requirejs.config() can be called later, at any time, and the loader will merge the configs together. So it should be enough to wait until you know what you want to use for the paths config for 'extension', then set it via another requirejs.config() call, then do the loading of the extension with that path.

It sounds like you'd want to use the "paths" configuration option. Example:
var require = {
baseUrl : '/app/js/modules',
paths : {
'extension' : '/extension/foo-extension/js/modules'
}
};
Then you could simply call require('extension/foo/bar') to load /app/js/modules/extension/foo-extension/js/modules/foo/bar.js

Related

How to import/load entire file to include in my bundle?

How to include an entire file into my bundle main.js?
ES6 can import/export functions and classes. But what if i want to include the whole content from another file into my bundle main.js? how to do it?
I came across the query on Stackoverflow: Managing jQuery plugin dependency in webpack.
I'm not sure about this question though. Those options given there seem to target injecting implicit globals, configuring this, disabling AMD, to include large dists. I don't think this is what i want.
Let's say i have two files in src directory
1- rough.js
const rgh = "qwerty"
2- index.js
import './rough.js' //something like this
console.log (rgh)
Now what i expect in bundle.js is
const rgh = "query";
console.log(rgh)
I just want all the content inside one of my file to get all transported to index.js for webpack to bundle them
Those options given there seem to target injecting implicit globals,
configuring this, disabling AMD, to include large dists. I don't think
this is what i want.
To understand this you need to understand what webpack is doing for you. Web pack takes a series of Javascript files (and more importantly their contents) and parses these into one file. That's what it does from a file point of view, but if you ignore the file and think about what it does from a code point of view, it takes each one of the imported objects and makes them available to other objects depending upon the rules you define in your code (using import and export). You can think of this from a closure point of view something like this:
if you have some code like:
import a from 'a.js';
export default b(){
console.log(a.test());
}
This will be turned into something like, in one js file:
var a = (function() {
var testStr = "test";
function test(){
return testStr;
}
return {test:test};
})();
var b = (function(a) {
console.log(a.test());
})(a);
So you can see that the file isn't really important. What's important is the scope. b can use a because it is injected into it's scope (In this instance as a IIFE).
In the above example a and b are in the global scope but testStr isn't.
So when your talking about "importing my file", you need to forget about that and think about what objects in that file you want to import how. Any variables "in that file" declared directly var a = ....; are in the global scope. So it sounds like what you want to do is import the objects in that file into the global scope.
you just need to import that file in main.js
like this way

How to import a module from the static using dynamic import of es6?

I'm trying to add dynamic import into my code to have a better performance on the client-side. So I have a webpack config where is bundling js files. On SFCC the bundled files are in the static folder where the path to that files is something like this: /en/v1569517927607/js/app.js)
I have a function where I'm using dynamic import of es6 to call a module when the user clicks on a button. The problem is that when we call for that module, the browser doesn't find it because the path is wrong.
/en/lazyLoad.js net::ERR_ABORTED 404 (Not Found)
This is normal because the file is on /en/v1569517927607/js/lazyLoad.js.
There is a way to get it from the right path? Here is my code.
window.onload = () => {
const lazyAlertBtn = document.querySelector("#lazyLoad");
lazyAlertBtn.addEventListener("click", () => {
import(/* webpackChunkName: "lazyLoad" */ '../modules/lazyLoad').then(module => {
module.lazyLoad();
});
});
};
I had the same problem and solved it using the Merchant Tools > SEO > Dynamic Mapping module in Business Manager.
There you can use a rule like the following to redirect the request to the static folder:
**/*.bundle.js i s,,,,,/js/{0}.bundle.js
All my chunk files are named with the <module>.bundle pattern.
Here you can find more info :
https://documentation.b2c.commercecloud.salesforce.com/DOC1/topic/com.demandware.dochelp/content/b2c_commerce/topics/search_engine_optimization/b2c_dynamic_mappings.html
Hope this helps.
I believe you'll likely need to do some path.resolve() magic in either your import statement or your webpack.config.js file as is shown in the accepted answer to this question: Set correct path to lazy-load component using Webpack - ES6
We did it in a different way. That required two steps
From within the template file add a script tag that creates a global variable for the static path. Something like
// inside .isml template
<script>
// help webpack know about the path of js scripts -> used for lazy loading
window.__staticPath__ = "${URLUtils.httpsStatic('/')}";
</script>
Then you need to instruct webpack to know where to find chunks by changing __webpack_public_path__ at runtime
// somewhere in your main .js file
// eslint-disable-next-line
__webpack_public_path__ = window.__staticPath__ + 'js/';
Optional step:
You might also want to remove code version from your __staticPath__ using replace (at least we had to do that)
__webpack_public_path__ = window.__staticPath__.replace('{YOUR_CODE_VERSION_GOES_HERE}', '') + 'js/';

What's considered a good way to share "global" variables inside a node module?

How can I easily refer to other directories in my module without explicitly relative paths (../../../lib..)?
I'm writing a node module and there are some global things I want to reuse in my module.
Most basically - I want to set the root path of the module as a 'global' so I can easily call other sources without using a lot of relative paths ../../ and things like that. It can cause messy code and it's easy to mistake or miss it if the project structure changes.
So I've seen a lot of options in that post and some other libraries for dealing with those kind of things (such as modules that give the root path - app-module-path, rootpath, rfr etc.) but they all refer to a base project/application and not to a module that's being used by others.
Setting a global is a bad idea, and I understood that an environment variable is also not such a good idea.
Is there a good practice for that thing? Maybe there's something I haven't found or heard of.
Here's an example of what I'm trying to avoid and what I'm looking for:
// avoid things like that:
// ./lib/something/forthat/doit.js
var config = require('../../../config/project/type1/config.js');
// ./config/project/type1/config.js
module.exports = {
msg: 'hi'
};
// find somethings like that:
// when the root path/require can be found in every location of the module
// and is relative to my app and not the app using my module.
// ./lib/something/forthat/doit.js
var config = require(rootPath + 'config/project/type1/config.js');
// OR
var config = rootRequire('config/project/type1/config.js');
// OR anything else
// ./config/project/type1/config.js
module.exports = {
msg: 'hi'
};
For getting the current directory path you can use the global variable __dirname any where in your nodejs project. Example : console.log(__dirname) write this into any file in your project and console would print the current directory path as string.
Or, You can use express-session module like;
var express = require('express');
var session = require('express-session');
var app = express();
app.use(session({myVar: 'abc'}));
app.get('/',function(req,res){
var sess = req.session;
console.log(sess);
});
First of all..... relative paths doesn't create confusion at all. rather you can call it messy (if you want) and it is considered to be the best practice. But I can understand you too. That you don't like very messy codes. So for this situation you should serve that part/peice of code as static content. Hence easily you can access it with giving a very login tailing path. But remember this way is an alternate to relative path serving.

How can I access configuration of Require.js

I try to get the baseUrl configuration of Require.js inside a module, but I can't find where it is stored.
define([], function() {
// Here I'd like to access the `baseUrl` require.js is using
var baseUrl = requirejs.config().baseUrl;
});
In my case, the baseUrl is set up by Require.js using the data-main attribute of the script file.
I know I can request module to access the config attributes (e.g. define(['module'])), but I can't find how to access the higher level of configuration option.
You can also reach the config into:
requirejs.s.contexts._.config
to inspect the config object directly.
https://groups.google.com/forum/#!topic/requirejs/Hf-qNmM0ceI
In RequireJS 2.1.5, you can get the base URL just like epascarello says, except you'll need to pass the empty string.
var baseURL = require.toUrl('');
Do you want to use toUrl?
define({
load: function (name, parentRequire, load, config) {
var fullUrl = parentRequire.toUrl("foo/bar.css");
}
});
edit:
Starting in require.js 2.1.3, calling toURL return the path without extension. As so, to get the baseUrl:
var baseURL = require.toUrl();

How can I do my routing without having one long file of all the routes in nodejs?

Using this link as a reference https://github.com/visionmedia/express/tree/master/examples/route-separation to what "could be done"
I AM NOT USING EXPRESS. I AM USING THEM AS AN EXAMPLE.
I want to do something like this but "simpler" ...
How can I get away from declaring all my routes in one long, complex list all in one file? Can I define them by passing a router into my modules, and then including all the code in one directory ... ok, I'll suffer having one long document that only does "require" includes, like an index.js, for this one ~ at least that one my build scripts can rebuild for me, but preferably not in my primary file for every single route that I may add.
So for instance, they use this code:
// General
app.get('/', site.index);
// User
app.all('/users', user.list);
app.all('/user/:id/:op?', user.load);
app.get('/user/:id', user.view);
app.get('/user/:id/view', user.view);
app.get('/user/:id/edit', user.edit);
app.put('/user/:id/edit', user.update);
// Posts
app.get('/posts', post.list);
I want to avoid making a list like that in my app.js. I want instead to have each file know what the routes are for that file.
Here's what I'm wanting to do: (please don't critique the code, I'm making it very simple so I make sure that I am illustrating my code the way I want to do it)
//app.js
var router = require('./myRouter.js')
var includes = require('./routes/*.js').register(router)
// do some stuff here with an https server and start the server here
and
//./routes/user.js
var myRouter;
exports.register(router){
myRouter = router;
}
router.addRoute(/* here I do the magic associated with this route */)
Can I do it just that simply? What am I missing here?
I haven't written this code because I'm just ever so certain that I'm going about this the wrong way.
And if I am going to have to use something like an index.js in the /routes/ folder, can I use that same concept that I demonstrated I would like to use in my code of .register(router) appended so I can pass that information down recursively? Would that work?
I use an index.js file for this and use require("routes") which is a folder.
// app.js
route = require("./routes"),
...
route(app);
// routes/index.js
var index = require("./index-route"),
about = require("./about-route"),
posts = require("./posts-route");
module.exports = function(app) {
index(app);
about(app);
posts(app);
};
This works because if you require a folder it will load index.js by default.
If you have a lot of routes you might want to load them based on convention
var routes = [];
// read all files
fs.readdir("./", function(files) {
files.forEach(function(val) {
// require all non-index.js files.
if (val !== "index.js") {
routes.push(require(val));
}
});
});
module.exports = function(app) {
// for each route you required call it with app.
routes.forEach(val.bind(null, app));
}
This would load all .js files that are not "index.js", so any file in your /routes/ folder would be loaded and run when you route them.
Your solution looks vaguely like you wish to use the Visitor Patern, in which case I suggest you make ./roots/ require-able (see this question) and in index.js you include all the files you wish (as local's) and export a register module which calls the register module on each of the required files.
Or you could copy the code from the above answer directly into your main file.

Categories

Resources