In this article we will: With the elimination of side effects, it becomes possible to use multiple versions of the same package within the same application. Ideally, you should be able to use components made with Unfortunately, there are some packages where using multiple versions is impossible. These are stateful packages. For example, can-view-callbacks is a stateful package used to register custom elements and attributes in CanJS. Its code looks similar to the following: A stateful module contains its own state ( Imagine wanting to use two versions of But if CanJS has the following stateful modules: There are a few ways to mitigate the problems with stateful modules: One option, is to avoid stateful modules altogether and make the user create the state and pass it around to other functionality that need it. For example, we could eliminate First, make all components export their constructor function: Then, every template must import their components: This isn’t a viable solution for many other packages because it would create too large a burden on developers with little concrete stability gains. Fortunately, there's other things we can do to help. Stateful packages should expose the state with the most minimal and simple API possible. You can always create other packages that interface with the stateful API. For example, we could just directly export the Other modules could add more user friendly APIs around this shared state. We use can-namespace to prevent loading duplicate packages in a sneaky way. The can-namespace package simply exports an empty object like: We should never have to release a new version of If we make changes to the stateful modules, we can at least ensure the user knows if they are getting multiple. Stateful code stinks, but minimizing the scope of its impact has helped CanJS progress much faster in the past year than anytime before, without having to make breaking changes. In the next section we will see how a tiny bit of well-defined statefulness with can-symbol allows CanJS to tightly integrate with other libraries.
can-component@4.X along side components made with can-component@3.X. This means you wouldn’t have to rewrite working code to use a new major release!
// can-view-callbacks@3
var tags = {};
module.exports ={
tag: function(tag, callback){
if(tag){
tags[tag] = callback;
} else{
return tags[tag];
}
}
});tags in can-view-callbacks case) and allows outside code to mutate that state. Let’s see an example of how multiple versions of a stateful package could be so much trouble.can-component in an application. old-thing.js uses can-component@3.X:// old-thing.js
var Component = require("can-component@3");
var view = require("./old-thing.stache");
Component.extend({
tag: "old-thing",
ViewModel: {},
view: view
});new-thing.js uses can-component@4.X:// new-thing.js
import {register} from "can-component@4";
import view from "./new-thing.curly";
import define from "can-define";
@define
class NewThing { }
Component.register("new-thing", NewThing, view);can-component@3.X MUST use can-view-callbacks@3.X and can-component@4.X MUST use can-view-callbacks@4.X, there will be two custom element registries and make it impossible to use both types of components in the same template. Stateful packages must be treated with care!CanJS’s stateful packages
Module
Purpose
can-cid
Uniquely labels objects.
can-observation
Registers reading an observable value.
can-view-callbacks
Registers custom elements and attributes.
can-namespace
Registers `can` namespace, prevents duplicate stateful packages.
can-symbol
Register integration behaviors with CanJS
Stateful solutions
1. Shift the statefulness to the developer.
can-view-callbacks as follows:// my-component.js
module.exports = Component.extend({ ... });<!-- app.stache -->
<can-import from="./my-component" as="MyComponent"/>
<MyComponent/>2. Minimize the statefulness and harden APIs.
tags data in can-view-callbacks like:// can-view-callbacks
module.exports = {};3. Let people know when they’ve loaded two versions of the same package.
// can-namespace@1.0.0
module.exports = {};can-namespace, but every stateful package imports it and makes sure there’s only one of itself as follows:// can-cid@1.0.0
var namespace = require("can-namespace");
if (namespace.cid) {
throw new Error("You can't have two versions of can-cid!”);
} else {
module.exports = namespace.cid = cid;
}Conclusions