How can I use NodeJS modules in Meteor? - meteor

In a NodeJS application ,I have finished some modules,Now I want to use them in Meteor,What should I do?
For example,there is a file 'hello.js',content:
require('url');// In here,require other modules
function sayHi(name){
console.log("Hi "+ name);
}
exports.sayHi = sayHi;
How do I use 'say Hi' in meteor?
when I do this:
if (Meteor.isServer) {
Meteor.startup(function () {
var require = __meteor_bootstrap__.require;
var index = require('./hello');
hello.syaHi('Ec');})}
Errors is:
app/index.js:1
require();
^
ReferenceError: require is not defined
at app/index.js:1:1
at /home/huyinghuan/workspace/NodeJs/myMeteorJS/testrequire/.meteor/local/build/server/server.js:113:21
at Array.forEach (native)
at Function._.each._.forEach (/usr/lib/meteor/lib/node_modules/underscore/underscore.js:79:11)
at run (/home/huyinghuan/workspace/NodeJs/myMeteorJS/testrequire/.meteor/local/build/server/server.js:99:7)

I think, you have to install/copy your module into projectdir/.meteor/local/build/server/node_modules which is a link to /usr/local/meteor/lib/node_modules. I tried this with the node.js module tracer and it worked. You have to copy your files into this directory every time you updated your meteor installation.

Also, it looks like Npm.require() is the right way to require node modules now.

Update, I had to install my module into .meteor/local/build/programs/server/node_modules as well as use Npm.require.

Here's a package which makes it a lot easier to use NPM packages inside Meteor:
https://github.com/meteorhacks/npm
example usage:
if (Meteor.isClient) {
getGists = function getGists(user, callback) {
Meteor.call('getGists', user, callback);
}
}
if (Meteor.isServer) {
Meteor.methods({
'getGists': function getGists(user) {
var GithubApi = Meteor.npmRequire('github');
var github = new GithubApi({
version: "3.0.0"
});
var gists = Async.runSync(function(done) {
github.gists.getFromUser({user: 'arunoda'}, function(err, data) {
done(null, data);
});
});
return gists.result;
}
});
}

Related

Get which program is generated in meteor by babel

How can I know in babel plugin whether files currently transpiled by babel are transpiled for server or client/browser package?
Meteor recently implemented option caller available in babel 7. To use it and access information in plugin, one can access Babel.caller poperty like this:
let caller;
module.exports = function(Babel) {
Babel.caller(function(c) {
caller = { ...c };
});
return {
visitor: {
BlockStatement(){
console.log(caller); // logs e.g. {name: "meteor", arch: "web.browser.legacy"}
}
}
};
};

usage example for chokidar in Meteor to prevent Error: Meteor code must always run within a Fiber

The code below gives me the Error: 'Meteor code must always run within a Fiber. Try wrapping callbacks that you pass to non-Meteor libraries with Meteor.bindEnvironment.'
import chokidar from 'chokidar';
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
const Plates = new Mongo.Collection('plates');
var path = '~/Documents/dev/lpr/test';
var watcher = chokidar.watch(path, {
ignored: /(^|[\/\\])\../,
persistent: true
});
watcher.on('add', path => {
console.log(`DEBUG: File ${path} has been added`);
Plates.insert({
path,
})
});
The Meteor documentation (https://guide.meteor.com/using-npm-packages.html#wrap-async) suggests using Meteor.wrapAsync to solve this issue but I don't understand how to apply in this case?
e.g. below returns 'TypeError: watcherFiber.on is not a function'
var watcherFiber = Meteor.wrapAsync(chokidar.watch(path, {
ignored: /(^|[\/\\])\../,
persistent: true
}));
watcherFiber
.on('add', path => {
console.log(`DEBUG: File ${path} has been added`);
Plates.insert({
path,
})
});
The error text points you to the correct way to do that.
It should be like this:
watcher.on('add', Meteor.bindEnvironment(path => {
console.log(`DEBUG: File ${path} has been added`);
Plates.insert({
path,
});
}));

wrapAsync + method + session

i'm having issues with wrapAsync + method + sessions.
How do I implement the WrapAsync correctly?
I want, in a template to know if the user has at least one item created by him. And then define whether or not he can create another item.
Now i'm getting this error:
W20141013-15:04:43.237(-3)? (STDERR) Error: Can't wait without a fiber
But, I could not find Fiber at Documentation. And for implementing this, is it really necessary?
 
On the client side I want something like:
//pagina.js
Template.pagina.helpers{
userHasItem: return Session.get('userHasItem');
}
//pagina.js
Meteor.call('userHasItem', Meteor.userId(), function (error,result) {
Session.set('userHasItem', result);
});
//at server side:
if(Meteor.isServer){
Meteor.startup(function () {
var userHasItemAsync = function (userId) {
setTimeout(function () {
if (Items.findOne({'userId': userId})) {
return true;
} else {
return false;
}
}, 4000);
};
Meteor.methods({
userHasItem: function(userId) {
var userHasItemSync = Meteor.wrapAsync(userHasItemAsync),
result;
try {
userHasItemSync(userId);
console.log(result);
return result;
}catch (e) {
console.log('erreur', e.message);
throw new Meteor.Error(500, e);
}
},
}
});
}
Can't get your error to reproduce based on the existing code.
Still, userHasItemAsync is not available because you've defined it locally in the Meteor.startup function. But the error you should get in this case is userHasItemAsync is undefined.
Also the code you've entered here has multiple errors (i guess you typed it in not copy / pasted from your project): template instead of Template, Template it's defined outside of isClient (probably it's in a file available for the client) etc. Because of that it's hard to reproduce your exact case.
There is no need to call a server method to see if the item exists (assuming you have set up the proper publications/subscriptions), nor any need to call wrapAsync. In fact, what you want to achieve doesn't even require a session. All of the code can be ultimately distilled to this:
Template.pagina.helpers{
userHasItem: return Items.find({ userId: Meteor.userId() }).count() > 0;
}
The cursor returned by Items.find is reactive in itself, so there is no need for using a Session.

Meteor URL prefix

I have an issue with Meteor 0.6.6.2
When I deploy on the production. I have to following error :
/home/gt/webapps/meteor/bundle/programs/server/boot.js:185
}).run();
^
Error: a route URL prefix must begin with a slash
at _.extend.declare (packages/routepolicy/routepolicy.js:95)
at new StreamServer (packages/livedata/stream_server.js:23)
at new Server (packages/livedata/livedata_server.js:980)
at Package (packages/livedata/server_convenience.js:10)
at packages/livedata.js:3909:4
at packages/livedata.js:3920:3
at /home/gt/webapps/meteor/bundle/programs/server/boot.js:154:10
at Array.forEach (native)
at Function._.each._.forEach (/home/gt/webapps/meteor/bundle/programs/server/node_modules/underscore/underscore.js:79:11)
at /home/gt/webapps/meteor/bundle/programs/server/boot.js:81:5
My root_url is set to :
export ROOT_URL='http://sub.mydomain.com'
I had not problem with older version of Meteor.
I found the error. I debugged the paths in routepolicy.js ( in /bundle/programs/server/app
line 56 with console.info(urlPrefix) ) and found that my export ROOT_URL was not correct.
For some reason, my export command (export ROOT_URL='http://mydomain.com' was not successfull and it was still ROOT_URL='mydomain.com')
See :
Github issue : https://github.com/meteor/meteor/issues/1404
Here's what I'm doing to support namespaced routes (using Iron Router):
lib/namespace.js contains:
Router._mapOld = Router.map;
Router.map = function(namespace, cb) {
if (_.isFunction(namespace)) {
cb = namespace;
return Router._mapOld.call(this, cb);
}
namespace = namespace.replace(/\/+$/, '');
var that = this;
that._routeOld = that.route;
that.route = function(name, options) {
if (!_.isString(options.path)) {
throw new Error(
'Namespaced routes must have a path specified as a string.');
}
return that._routeOld.call(that, name, _.extend(options, {
path : namespace + options.path
}));
};
var ret = Router._mapOld.call(that, cb);
that.route = that._routeOld;
return ret;
};
Then you can do:
Router.map(function() {
// Add routes normally with no prefix
});
Router.map('/prefix', function() {
// All routes you add here will be prefixed with /prefix
});
Do you use middleware, or server side routes somewhere? If so, all path params for middleware must begin with a /, so change some/path to /some/path. It started to be important in one of recent versions.
ROOT_URL does not have to end with a /, by the way – yours is correct.

AMD version of Google Maps V3 for use with require.js?

Has anybody used Googlemaps V3 with something like require.js where it needs to be in AMD version? Is there one already done somewhere?
In require.js you can use the async plugin, then call it like such:
define([
'async!http://maps.google.com/maps/api/js?sensor=false'
], function(){
//Create your map.
});
You can also do it using jQuery.Deferred() and some global variables (not ideal, but I needed it so I could optimize my files using grunt rjs, which didn't work for async):
// gmapsDone.js
window._mapsLoaded = $.Deferred();
window.gmapsLoaded = function(data) {
delete window.gmapsLoaded;
_mapsLoaded.resolve();
};
define(["http://maps.google.com/maps/api/js?v=3&sensor=false&callback=gmapsLoaded"], function(gmaps) {
"use strict";
return window._mapsLoaded.done;
});
Then, to use it:
define(["gmapsDone"], function(gmapsDone) {
function load() {
// Do something
}
gmapsDone(load);
});
https://gist.github.com/taktran/5389668
Inspired by http://blog.pixelingene.com/2011/10/using-jquery-dot-deferred-and-requirejs-to-lazy-load-google-maps-api/
I recently helped a friend solve this issue with a take off on the $.Deferred approach mentioned above. This plays nice with the optimizer and doesn't cause multiple script loads.
The Module
var google_maps_loaded_def = null;
define(['jquery'],function($) {
if(!google_maps_loaded_def) {
google_maps_loaded_def = $.Deferred();
window.google_maps_loaded = function() {
google_maps_loaded_def.resolve(google.maps);
}
require(['http://maps.googleapis.com/maps/api/js?sensor=true&callback=google_maps_loaded'],function(){},function(err) {
google_maps_loaded_def.reject();
//throw err; // maybe freak out a little?
});
}
return google_maps_loaded_def.promise();
});
Available as a Gist:
https://gist.github.com/MattSurabian/7868115
Usage
To Use the above module and take advantage of the fact that the promise resolves with google.maps:
define([ 'app/lib/google-maps-loader' ], function(GoogleMapsLoader){
GoogleMapsLoader.done(function(GoogleMaps){
// your google maps code here!
var geocoder = new GoogleMaps.Geocoder();
}).fail(function(){
console.error("ERROR: Google maps library failed to load");
});
});
Alternatively, just reference the google.maps object normally
define([ 'app/lib/google-maps-loader' ], function(GoogleMapsLoader){
GoogleMapsLoader.done(function(){
// your google maps code here!
var geocoder = new google.maps.Geocoder();
}).fail(function(){
console.error("ERROR: Google maps library failed to load");
});
});
I wrote a short blog post about this method here, which may be of some use: RequireJS Projects and Asynchronously Loading the Google Maps API
I put together a Google Maps AMD loader plugin, which adds some functionality on top of the async! loader.
require.config({
googlemaps: {
params: {
key: 'abcd1234', // sets api key
libraries: 'geometry' // set google libraries
}
}
});
require(['googlemaps!'], function(gmaps) {
// google.maps available as gmaps
var map = new gmaps.Map('map-canvas');
});

Resources