How to implement a plugin that changes the source module only if necessary?

I have a plugin that extends the original module.
It should only modify the module when it is explicitly required.

Problem:
As soon as this is required once, the source module will be changed forever, also for cases when the plugin is not a dependency.
The order here does not matter, enough to require a plugin once.

Example:

define("main", [], function() { return {opt: "A"}; }); define("plugin", ["main"], function(obj) { obj.opt = "B"; }); require(["main", "plugin"], function(obj) { console.log(obj.opt); // should log B }); require(["main"], function(obj) { console.log(obj.opt); // should log A but logs B }); 

I guess you need to say something to always reload main from the source instead of using the cached version.
I have no idea how, however. Or maybe there is an even more elegant way?

Please enlighten me guys.

Fiddle: http://jsfiddle.net/r75e446f

UPDATE: Some may find it important to know that I need this for my unit test karma environment in order to test a module with and without a plugin.

UPDATE2: See below for my own solution.

+6
source share
5 answers

So, I learned how to achieve what I want and thought that I would share it here.

The answer is called the "context", which is an option in config requirejs.

http://requirejs.org/docs/api.html#multiversion

Here is how I implemented my solution:

 var reqOne = require.config({ context: 'one' }); var reqTwo = require.config({ context: 'two' }); reqOne(["main", "plugin"], function(obj) { console.log(obj.opt); // logs B }); reqTwo(["main"], function(obj) { console.log(obj.opt); // logs A }); 

Unfortunately, this does not work in the fiddle , because the second requirement will try to load the main module from the outside, and I cannot upload files to jsfiddle.

But the fact that he is trying to do this and is not using the main module already loaded into another requires proof that his method works.
If definitions are passed to single files, this works like a charm.

Thanks to everyone who burst in.

0
source

RequireJS modules are single point. If you load main once, twice, 10 times, you will always get the same module. And if you change its state, it will be changed for all modules that use it. You can specify RequireJS to define a module, but I do not recommend it, because it will just make your code unclear.

If I wanted to do what you are trying to do, I would develop my code something like this:

 <script> define("main", [], function() { function Main (opt) { this.opt = opt; } return Main; }); define("plugin1", [], function() { return { install: function (main) { main.opt += " plugin1"; } }; }); define("plugin2", [], function() { return { install: function (main) { main.opt += " plugin2"; } }; }); // Case 1: no plugins require(["main"], function(Main) { var main = new Main("A"); console.log(main.opt); }); // Case 2: only plugin1 require(["plugin1", "main"], function (plugin1, Main) { var main = new Main("A"); plugin1.install(main); console.log(main.opt); }); // Case 3: only plugin2 require(["plugin2", "main"], function (plugin2, Main) { var main = new Main("A"); plugin2.install(main); console.log(main.opt); }); // Case 4: plugin1 and plugin2 require(["plugin1", "plugin2", "main"], function (plugin1, plugin2, Main) { var main = new Main("A"); plugin1.install(main); plugin2.install(main); console.log(main.opt); }); 

In principle, make the main class common to all cases, which can be initialized during construction and which can be modified by plugins. Then each plugin can set itself to main . The above code is a minimal illustration of how this can be done. In a real project, the final solution should be developed taking into account the specific needs of the project.

+4
source

If you do not want the source module to be changed for all modules that use it, your plugin should not modify the original module. Instead, the plugin will return a modified copy of the source module.

 define("main", [], function() { return {opt: "A"}; }); define("plugin", ["main"], function(obj) { var decorator = {} for (var key in obj) { decorator[key] = obj[key];} decorator.opt = "B"; return decorator }); require(["main", "plugin"], function(obj, plugin) { console.log(plugin.opt); // should log B }); require(["main"], function(obj) { console.log(obj.opt); // should log A but logs B }); 

This will work without any complications if your source object is a simple structural object without any functions. If there are functions, or if your source object was created using the module template, then the copy has a strong possibility of subtle errors, depending on how the methods were defined.

EDIT 2015-01-13: The OP clarified his question that he would like his tests to be able to run both a modified and an unmodified source module without having to reload the page. In this case, I would recommend using require.undef to unload the main module and then reload it without reloading the entire page.

+3
source

I would like to talk about my understanding for discussion. When we are AMD, we define module . If the module (in this case main , a specific source) is able to accept the plugin and allow the plugin to change the properties, I think we can’t capture it or will it be an antivirus, a template?

how about expanding / cloning a specific source like

 define("plugin", ["main"], function(obj) { return $.extend(true, {}, obj, {opt: "B"}); }); 

http://jsfiddle.net/r75e446f/6/

here the plugin module always uses the main module as its dependency, when using the module we just use the plugin module directly and no longer need the main module? (by the way, I understand that this defeats the definition of "plug-in", but my direction is that we are dealing with module as something that should be designed)

- second approach -

if you really want to make this a plugin, how about a loader plugin ? as a text plugin, we always use `require ('text! abc.json'). So write a bootloader and register it in config requireJs, and then we can use it. (more details)

0
source

A more elegant solution would be to take an object-oriented approach, where:

  • modules return constructors, not instances, and
  • plugin may be a subclass of main .

This is the implementation ( fiddle ):

 console.clear(); define("Main", [], function() { return function() { this.opt = "A"; }; }); define("Plugin", ["Main"], function(Main) { return function() { Main.call(this); this.opt = "B"; }; }); require(["Plugin"], function(Plugin) { console.log((new Plugin()).opt); // should log B }); require(["Main"], function(Main) { console.log((new Main()).opt); // should log A }); 
-2
source

Source: https://habr.com/ru/post/979805/


All Articles