From
Atmosphere
to
npm
Namir Sayed-Ahmad Baraza
@namirsab [github|twitter]
namirsab@gmail.com
I'm a software engineer at
We use meteor to develop our tool
What we are going to see...
Introduction
- What is atmosphere?
- What is npm?
- Meteor Roadmap
Migrating a Package
- From 1.2 to 1.3 with ES6 Modules
- From 1.3 to npm
Conclusions
- Pros
- Cons
Introduction
As we know, javascript development is crazy
It's changing every day
But
We
Use Meteor!
- Blaze for the Frontend / Templating System
- MongoDB as the Database
- Atmosphere as our package manager
- Meteor magics for the rest!
Now is different
-
Blaze / React / Angular[1.x|2.x]
-
MongoDB ( but Apollo in the Roadmap)
-
Atmosphere and npm as packages managers
-
Meteor for the rest ( but less than before)
What Is Atmosphere?
-
Package manager for Meteor
-
It is the best friend of Meteor developer
$> meteor add username:packagename
Pros:
Cons:
- Simple to use
- All for Meteor
- Simple to write and publish packages
- Outside of the general ecosystem
- Version managing is really strict
What Is npm?
-
Originally the Node Package Manager
-
Nowadays, is just the Javascript Package Manager
-
Just like pip for Python and gem for Ruby
$> npm install packagename [options]
Pros:
Cons:
- Very configurable
- +250 000 packages
- Snippet catalog
- Simple to write and to publish a package
- Used among all the community
- Allows private npm
- More complex to use than Atmosphere
- It's not only for Meteor
What is in the Roadmap?
"1.3 introduced npm install support along with ES2015 modules. With 1.4, we would like to transition the Meteor package ecosystem over entirely from Atmosphere to npm"
MDG in the Meteor Roadmap
Migrating a Package
From 1.2 to 1.3 with ES6 modules
const height = new ReactiveVar(window.outerHeight);
const width = new ReactiveVar(window.outerWidth);
const $window = $(window);
Meteor.startup(() => {
$window.resize(function() {
height.set(window.outerHeight);
width.set(window.outerWidth);
});
});
// Global Symbol necessary to export it
WindowState = {
isMaximized() {
const currentHeight = height.get();
const currentWidth = width.get();
const availHeight = screen.availHeight * 0.9;
const availWidth = screen.availWidth * 0.9;
return !(currentHeight < availHeight || currentWidth < availWidth);
},
height: () => height.get(),
width: () => width.get()
};
Original code
Original package.js
Package.describe({
name: 'qlp:qlp-window-state',
version: '0.0.1',
// Brief, one-line summary of the package.
summary: '',
// URL to the Git repository containing the source code for this package.
git: '',
// By default, Meteor will default to using README.md for documentation.
// To avoid submitting documentation, set this field to null.
documentation: 'README.md'
});
Package.onUse(function (api) {
api.versionsFrom('1.2.0.2');
api.use([
'ecmascript'
]);
// Packages for Client
api.use([
'jquery',
'reactive-var',
], 'client');
api.addFiles('client/WindowState.js', 'client');
// Export the Global Symbol we've defined in client/WindowState.js
api.export('WindowState');
});
import { ReactivVar} from 'meteor/reactive-var';
import { jQuery as $} from 'meteor/jquery'; // Note we import everything now
const height = new ReactiveVar(window.outerHeight);
const width = new ReactiveVar(window.outerWidth);
const $window = $(window);
Meteor.startup(() => {
$window.resize(function () {
height.set(window.outerHeight);
width.set(window.outerWidth);
});
});
// The symbol is not global anymore
const WindowState = {
isMaximized() {
const currentHeight = height.get();
const currentWidth = width.get();
const availHeight = screen.availHeight * 0.9;
const availWidth = screen.availWidth * 0.9;
return !(currentHeight < availHeight || currentWidth < availWidth);
},
height: () => height.get(),
width: () => width.get()
};
// We export it using ES6 exports
export {
WindowState
};
New Code
New package.js
Package.describe({
name: 'qlp:qlp-window-state',
version: '1.0.0',
// Brief, one-line summary of the package.
summary: '',
// URL to the Git repository containing the source code for this package.
git: '',
// By default, Meteor will default to using README.md for documentation.
// To avoid submitting documentation, set this field to null.
documentation: 'README.md'
});
Package.onUse(function (api) {
api.versionsFrom('1.3.2.4');
api.use([
'ecmascript'
]);
// Packages for Client
api.use([
'jquery',
'reactive-var',
], 'client');
// Now we export it with api.mainModule
api.mainModule('client/WindowState.js', 'client');
});
Just a few changes
-
Remove global object
-
Remove api.export
-
Use api.mainModule
Warning:
Right now our package is not compatible with 1.2 and below
Migrating a Package
From 1.3 with ES6 modules to npm package
Steps
-
Create /node_modules
-
Create package folder
-
Initialize npm package
$> mkdir node_modules;
$> cd node_modules;
$> mkdir qlp-window-state
$> cd qlp-window-state && npm init
- Now fill everything npm asks you
- Move your package code into the new folder
You should have something like this
my-app/
node_modules/
package-name/
package.json
myCode.js
And now we can install dependencies directly with npm
$> meteor npm install jquery --save
Back to our example: package.json
{
"name": "qlp-window-state",
"version": "1.0.0",
"description": "Reactive Window State",
"main": "client/WindowState.js",
"author": "quantilope",
"license": "MIT",
"dependencies": {
"jquery": "^2.2.4"
}
}
Take a look to the dependencies...
Don't you miss something?
reactive-var
First Problem with npm
Packages hosted only on Atmosphere cannot be linked as dependencies of an npm package
But we can do a trick...
function importFromMeteorPackage(packageName, symbolName) {
if(!Package) {
throw new Error(`This package is only working inside Meteor apps`);
}
else if (!Package['reactive-var']) {
throw new Error(`${symbolName} is required, add it with "meteor add ${packageName}"`);
}
else {
return Package[packageName][symbolName];
}
}
Let's take a look to the code
import $ from 'jquery'; //jquery now comes from npm
const ReactiveVar = importFromMeteorPackage('reactive-var', 'ReactiveVar');
const height = new ReactiveVar(window.outerHeight);
const width = new ReactiveVar(window.outerWidth);
const $window = $(window);
// No more Meteor.startup. We don't want more Meteor dependecies.
$(document).ready(function () {
$window.resize(function () {
height.set(window.outerHeight);
width.set(window.outerWidth);
});
});
const WindowState = {
isMaximized() {
const currentHeight = height.get();
const currentWidth = width.get();
const availHeight = screen.availHeight * 0.9;
const availWidth = screen.availWidth * 0.9;
return !(currentHeight < availHeight || currentWidth < availWidth);
},
height: () => height.get(),
width: () => width.get()
};
// npm module.exports instead of export from js
module.exports = {
WindowState
}
New Code
And then you try to run the app
But it doesn't work
Uncaught SyntaxError: Unexpected token import
Any ideas?
Transpile npm packages
In your package folder:
- npm install babel-cli babel-preset-es2015 --save-dev
- Create build script
- Change package entry point to point to the transpiled code
{
"name": "qlp-window-state",
"version": "1.0.0",
"description": "Reactive Window State",
"main": "./distribution/WindowState.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "babel client --presets babel-preset-es2015 --out-dir distribution"
},
"author": "quantilope",
"license": "MIT",
"dependencies": {
"jquery": "^2.2.4"
},
"devDependencies": {
"babel-cli": "^6.9.0",
"babel-preset-es2015": "^6.9.0"
}
}
New package.json
Now run npm run build
and everything works!
Conclusions
1.2 Packages
Pros
Cons
- Compatible with <= 1.3
- We are used to write them
- One day they will be deprecated
- Force us to write global symbols inside packages
1.3 Packages with Modules
Pros
Cons
- Use of ES6 modules
- Better syntax. No global symbols.
- It's more futureproof
- They don't work < 1.3
- Force us to write global symbols inside packages
- Still will be deprecated sooner than later
1.3 npm packages
Pros
Cons
- npm packages, with all npm features.
- Embrace the ecosystem. Learning to write npm packages makes us more valuable as developers.
- It's totally futureproof. Meteor wants to move everything to npm.
- They don't work < 1.3
- They are a little bit more complex to handle.
- You cannot depend on atmosphere packages.
- No automatic transpiling
Bibliography
Access to the code
- meteor-lte-1.2
- meteor-gte-1.3-modules
- meteor-gte-1.3-npm
Checkout this branches
FromAtmospheretonpm
By Namir Sayed-Ahmad Baraza
FromAtmospheretonpm
- 1,005