qualityfaster
qualityfaster copied to clipboard
Meteor 1.5 problem with duplicated method and collection names
Hello, recently I've started a new project using Meteor 1.5. At the begging, I had a problem with dynamic import. I updated my wallaby config with help of #71 issue. It's pretty similar to my previous config running fine on 1.4.4. This time I get an error about duplicated methods and collection names. When I change collection name when Wallaby is already running the error is gone. I'll attach my config below. Thank you in advance for any help.
// How to run:
//
// 1. Start your Meteor app and wait until it has built.
// 2. Start Wallaby with this configuration.
// How to modify for your project:
//
// 1. Change relativeMeteorAppPath (line 16 and 69) to the relative location of your Meteor app.
// 2. Adjust the files array (line 37) to match all your Meteor app server files.
// 3. Adjust the tests array (line 44) to match all your server tests that should run with Wallaby.
var path = require('path')
var nodePath = require('child_process')
.execSync('meteor node -e "process.stdout.write(process.execPath)"', { encoding: 'utf8' })
var relativeMeteorAppPath = ''
module.exports = function (wallaby) {
process.env.NODE_PATH += path.delimiter +
path.join(
wallaby.localProjectDir,
relativeMeteorAppPath,
'.meteor/local/build/programs/server/node_modules'
)
process.env.NODE_PATH += path.delimiter +
path.join(
wallaby.localProjectDir,
relativeMeteorAppPath,
'node_modules'
)
// process.env.NODE_PATH += path.delimiter +
// path.join(wallaby.projectCacheDir, relativeMeteorAppPath, 'imports');
return {
files: [
{ pattern: relativeMeteorAppPath + '/tests/helpers.js', instrument: false },
{ pattern: relativeMeteorAppPath + '/imports/api/**/*.js', load: false },
{ pattern: relativeMeteorAppPath + '/imports/api/**/*-test.js', ignore: true },
{ pattern: relativeMeteorAppPath + '/.meteor/**/*', ignore: true },
{ pattern: relativeMeteorAppPath + '/utility/meteor/*.js', ignore: false }
],
tests: [
{ pattern: relativeMeteorAppPath + '/imports/api/**/*-test.js' }
],
compilers: {
'**/*.js': wallaby.compilers.babel({
presets: ['es2015', 'stage-1']
})
},
env: {
type: 'node',
runner: nodePath
},
testFramework: 'mocha',
workers: {
recycle: true,
initial: 1,
regular: 1
},
debug: true,
bootstrap: function (wallaby) {
var relativeMeteorAppPath = ''
wallaby.delayStart()
if (!process.env.ROOT_URL) {
process.env.ROOT_URL = 'http://localhost:3000/'
}
if (!process.env.MONGO_URL) {
process.env.MONGO_URL = 'mongodb://127.0.0.1:3001/wallaby-server'
}
process.on('unhandledRejection', function (reason, promise) {
var exception = reason.stack ? reason.stack.replace(/\(\/.*?\/instrumented\//g, '(') : reason
console.error('Unhandled promise rejection', exception)
})
var path = require('path')
var appPath = path.resolve(wallaby.localProjectDir, relativeMeteorAppPath)
var serverPath = path.resolve(appPath, '.meteor/local/build/programs/server')
var meteorModulesPath = path.resolve(appPath, '.meteor/local/dev_bundle/lib/node_modules')
var meteorServerModulesPath = path.resolve(appPath, '.meteor/local/dev_bundle/server-lib/node_modules')
process.argv.splice(2, 0, 'program.json')
try {
process.chdir(serverPath)
} catch (error) {
if (error.message.match(/^ENOENT/)) {
throw new Error('You need to run the Meteor app before you start Wallaby!')
} else {
throw error
}
}
var Fiber = require('fibers')
require('babel-polyfill')
require(path.join(meteorModulesPath, 'reify/lib/runtime'))
.enable(module.constructor.prototype)
// This should allow Fibers to work in across an await point in an async function but doesn't seem to work
require(path.join(meteorServerModulesPath, 'meteor-promise'))
.makeCompatible(Promise, Fiber)
var fs = require('fs')
var Future = require('fibers/future')
var _ = require('underscore')
var sourcemap_support = require('source-map-support')
// var bootUtils = require('./boot-utils.js');
var files = require(path.resolve(serverPath, './mini-files.js'))
var npmRequire = require(path.resolve(serverPath, './npm-require.js')).require
var Profile = require(path.resolve(serverPath, './profile.js')).Profile
// This code is duplicated in tools/main.js.
var MIN_NODE_VERSION = 'v0.10.41'
var hasOwn = Object.prototype.hasOwnProperty
if (require('semver').lt(process.version, MIN_NODE_VERSION)) {
process.stderr.write(
'Meteor requires Node ' + MIN_NODE_VERSION + ' or later.\n')
process.exit(1)
}
// read our control files
var serverJsonPath = path.resolve(process.argv[2])
var serverDir = path.dirname(serverJsonPath)
var serverJson = require(path.resolve(serverPath, './server-json.js'))
var configJson =
JSON.parse(fs.readFileSync(path.resolve(serverDir, 'config.json'), 'utf8'))
// Set up environment
__meteor_bootstrap__ = {
startupHooks: [],
serverDir: serverDir,
configJson: configJson }
__meteor_runtime_config__ = { meteorRelease: configJson.meteorRelease }
if (!process.env.APP_ID) {
process.env.APP_ID = configJson.appId
}
// Map from load path to its source map.
var parsedSourceMaps = {}
// Read all the source maps into memory once.
_.each(serverJson.load, function (fileInfo) {
if (fileInfo.sourceMap) {
var rawSourceMap = fs.readFileSync(
path.resolve(serverDir, fileInfo.sourceMap), 'utf8')
// Parse the source map only once, not each time it's needed. Also remove
// the anti-XSSI header if it's there.
var parsedSourceMap = JSON.parse(rawSourceMap.replace(/^\)\]\}'/, ''))
// source-map-support doesn't ever look at the sourcesContent field, so
// there's no point in keeping it in memory.
delete parsedSourceMap.sourcesContent
var url
if (fileInfo.sourceMapRoot) {
// Add the specified root to any root that may be in the file.
parsedSourceMap.sourceRoot = path.join(
fileInfo.sourceMapRoot, parsedSourceMap.sourceRoot || '')
}
parsedSourceMaps[path.resolve(__dirname, fileInfo.path)] = parsedSourceMap
}
})
var retrieveSourceMap = function (pathForSourceMap) {
if (_.has(parsedSourceMaps, pathForSourceMap)) { return { map: parsedSourceMaps[pathForSourceMap] } }
return null
}
var origWrapper = sourcemap_support.wrapCallSite
var wrapCallSite = function (frame) {
var frame = origWrapper(frame)
var wrapGetter = function (name) {
var origGetter = frame[name]
frame[name] = function (arg) {
// replace a custom location domain that we set for better UX in Chrome
// DevTools (separate domain group) in source maps.
var source = origGetter(arg)
if (! source) { return source }
return source.replace(/(^|\()meteor:\/\/..app\//, '$1')
}
}
wrapGetter('getScriptNameOrSourceURL')
wrapGetter('getEvalOrigin')
return frame
}
sourcemap_support.install({
// Use the source maps specified in program.json instead of parsing source
// code for them.
retrieveSourceMap: retrieveSourceMap,
// For now, don't fix the source line in uncaught exceptions, because we
// haven't fixed handleUncaughtExceptions in source-map-support to properly
// locate the source files.
handleUncaughtExceptions: false,
wrapCallSite: wrapCallSite
})
var specialArgPaths = {
'packages/modules-runtime.js': function () {
return {
npmRequire: npmRequire,
Profile: Profile
}
},
'packages/dynamic-import.js': function (file) {
var dynamicImportInfo = {}
Object.keys(configJson.clientPaths).map(function (key) {
var programJsonPath = path.resolve(configJson.clientPaths[key])
var programJson = require(programJsonPath)
dynamicImportInfo[key] = {
dynamicRoot: path.join(path.dirname(programJsonPath), 'dynamic')
}
})
dynamicImportInfo.server = {
dynamicRoot: path.join(serverDir, 'dynamic')
}
return { dynamicImportInfo: dynamicImportInfo }
}
}
const mocha = require('mocha')
const suite = (wallaby) ? wallaby.testFramework.suite : mocha.suite
function fiberize (fn) {
return function (done) {
var self = this
Fiber(function () {
try {
if (fn.length == 1) {
fn.call(self, done)
} else {
fn.call(self)
done()
}
} catch (e) {
process.nextTick(function () {
throw e
})
}
}).run()
}
}
suite.on('pre-require', (context) => {
['beforeEach', 'afterEach', 'after', 'before', 'it'].forEach((method) => {
const original = global[method]
context[method] = _.wrap(original, function (fn) {
const args = Array.prototype.slice.call(arguments, 1)
if (_.isFunction(_.last(args))) {
args.push(fiberize(args.pop()))
}
return fn.apply(this, args)
})
_.extend(context[method], _(original).pick('only', 'skip'))
})
})
Fiber(function () {
_.each(serverJson.load, function (fileInfo) {
var code = fs.readFileSync(path.resolve(serverDir, fileInfo.path))
var nonLocalNodeModulesPaths = []
function addNodeModulesPath (path) {
nonLocalNodeModulesPaths.push(
files.pathResolve(serverDir, path)
)
}
if (typeof fileInfo.node_modules === 'string') {
addNodeModulesPath(fileInfo.node_modules)
} else if (fileInfo.node_modules) {
_.each(fileInfo.node_modules, function (info, path) {
if (! info.local) {
addNodeModulesPath(path)
}
})
}
function statOrNull (path) {
try {
return fs.statSync(path)
} catch (e) {
return null
}
}
var Npm = {
/**
* @summary Require a package that was specified using
* `Npm.depends()`.
* @param {String} name The name of the package to require.
* @locus Server
* @memberOf Npm
*/
require: Profile(function getBucketName (name) {
return 'Npm.require(' + JSON.stringify(name) + ')'
}, function (name) {
if (nonLocalNodeModulesPaths.length === 0) {
return require(name)
}
var fullPath
nonLocalNodeModulesPaths.some(function (nodeModuleBase) {
var packageBase = files.convertToOSPath(files.pathResolve(
nodeModuleBase,
name.split('/', 1)[0]
))
if (statOrNull(packageBase)) {
return fullPath = files.convertToOSPath(
files.pathResolve(nodeModuleBase, name)
)
}
})
if (fullPath) {
return require(fullPath)
}
try {
return require(name)
} catch (e) {
// Try to guess the package name so we can print a nice
// error message
// fileInfo.path is a standard path, use files.pathSep
var filePathParts = fileInfo.path.split(files.pathSep)
var packageName = filePathParts[1].replace(/\.js$/, '')
// XXX better message
throw new Error(
"Can't find npm module '" + name +
"'. Did you forget to call 'Npm.depends' in package.js " +
"within the '" + packageName + "' package?")
}
})
}
var getAsset = function (assetPath, encoding, callback) {
var fut
if (! callback) {
fut = new Future()
callback = fut.resolver()
}
// This assumes that we've already loaded the meteor package, so meteor
// itself can't call Assets.get*. (We could change this function so that
// it doesn't call bindEnvironment if you don't pass a callback if we need
// to.)
var _callback = Package.meteor.Meteor.bindEnvironment(function (err, result) {
if (result && ! encoding)
// Sadly, this copies in Node 0.10.
{ result = new Uint8Array(result) }
callback(err, result)
}, function (e) {
console.log('Exception in callback of getAsset', e.stack)
})
// Convert a DOS-style path to Unix-style in case the application code was
// written on Windows.
assetPath = files.convertToStandardPath(assetPath)
// Unicode normalize the asset path to prevent string mismatches when
// using this string elsewhere.
assetPath = files.unicodeNormalizePath(assetPath)
if (!fileInfo.assets || !_.has(fileInfo.assets, assetPath)) {
_callback(new Error('Unknown asset: ' + assetPath))
} else {
var filePath = path.join(serverDir, fileInfo.assets[assetPath])
fs.readFile(files.convertToOSPath(filePath), encoding, _callback)
}
if (fut) { return fut.wait() }
}
var Assets = {
getText: function (assetPath, callback) {
return getAsset(assetPath, 'utf8', callback)
},
getBinary: function (assetPath, callback) {
return getAsset(assetPath, undefined, callback)
},
/**
* @summary Get the absolute path to the static server asset. Note that assets are read-only.
* @locus Server [Not in build plugins]
* @memberOf Assets
* @param {String} assetPath The path of the asset, relative to the application's `private` subdirectory.
*/
absoluteFilePath: function (assetPath) {
// Unicode normalize the asset path to prevent string mismatches when
// using this string elsewhere.
assetPath = files.unicodeNormalizePath(assetPath)
if (!fileInfo.assets || !_.has(fileInfo.assets, assetPath)) {
throw new Error('Unknown asset: ' + assetPath)
}
assetPath = files.convertToStandardPath(assetPath)
var filePath = path.join(serverDir, fileInfo.assets[assetPath])
return files.convertToOSPath(filePath)
}
}
var wrapParts = ['(function(Npm,Assets']
var specialArgs =
hasOwn.call(specialArgPaths, fileInfo.path) &&
specialArgPaths[fileInfo.path](fileInfo)
var specialKeys = Object.keys(specialArgs || {})
specialKeys.forEach(function (key) {
wrapParts.push(',' + key)
})
// \n is necessary in case final line is a //-comment
wrapParts.push('){', code, '\n})')
var wrapped = wrapParts.join('')
// It is safer to use the absolute path when source map is present as
// different tooling, such as node-inspector, can get confused on relative
// urls.
// fileInfo.path is a standard path, convert it to OS path to join with
// __dirname
var fileInfoOSPath = files.convertToOSPath(fileInfo.path)
var absoluteFilePath = path.resolve(__dirname, fileInfoOSPath)
var scriptPath =
parsedSourceMaps[absoluteFilePath] ? absoluteFilePath : fileInfoOSPath
// The final 'true' is an undocumented argument to runIn[Foo]Context that
// causes it to print out a descriptive error message on parse error. It's
// what require() uses to generate its errors.
var func = require('vm').runInThisContext(wrapped, scriptPath, true)
var args = [Npm, Assets]
specialKeys.forEach(function (key) {
args.push(specialArgs[key])
})
func.apply(global, args)
})
process.chdir(appPath)
wallaby.start()
}).run()
}
}
}