refactor(internal): Detect project based on filename instead of process.cwd

Detection of the Meteor project through process.cwd() fails when used in Atom Editor. Use filename i
This commit is contained in:
Dominik Ferber
2015-10-04 19:41:47 +02:00
parent f1371ef1d2
commit 650d61efd7
18 changed files with 253 additions and 208 deletions

View File

@@ -1,9 +1,4 @@
import getProjectRootPaths from './util/getProjectRootPaths'
import isMeteorProject from './util/isMeteorProject'
import getMeteorMeta from './util/getMeteorMeta'
const rootPaths = getProjectRootPaths(process.cwd(), isMeteorProject)
const getMeta = getMeteorMeta.bind(null, rootPaths)
import getMeta from './util/getMeta'
function unpack (rule) {
const packedRule = require(rule)

6
lib/util/getMeta.js Normal file
View File

@@ -0,0 +1,6 @@
import getMeteorMeta from './internal/getMeteorMeta'
var getRelativePath = require('./internal/getRelativePath')
export default function (filename) {
return getMeteorMeta(getRelativePath(filename))
}

View File

@@ -1,48 +0,0 @@
import path from 'path'
var walk = require('walkdir')
function findOneUpwards (currentDirectory, matcher, attempts = 0) {
// No folder with '.meteor/release' in it found
if (attempts > 50 || currentDirectory === path.sep) {
return false
}
if (matcher(currentDirectory)) {
return currentDirectory
}
return findOneUpwards(path.join(currentDirectory, '..'), matcher, attempts + 1)
}
function findAllDownwards (startPath, matcher) {
const matchedPaths = []
const options = {follow_symlinks: false, no_recurse: false, max_depth: 20}
walk.sync(
startPath,
options,
function (currentPath) {
if (matcher(currentPath)) {
matchedPaths.push(currentPath)
this.ignore(currentPath)
}
}
)
return matchedPaths
}
export default function getProjectRootPaths (currentDirectory, matcher) {
if (matcher(currentDirectory)) {
return [currentDirectory]
}
const upwards = findOneUpwards(currentDirectory, matcher)
if (upwards) {
return [upwards]
}
return findAllDownwards(currentDirectory, matcher)
}

View File

@@ -0,0 +1,15 @@
import path from 'path'
export default function findOneUpwards (currentDirectory, matcher) {
if (matcher(currentDirectory)) {
return currentDirectory
}
// No folder with '.meteor/release' in it found
if (currentDirectory === path.sep) {
return false
}
return findOneUpwards(path.join(currentDirectory, '..'), matcher)
}

View File

@@ -1,10 +1,8 @@
import path from 'path'
import invariant from 'invariant'
import find from 'lodash.find'
import ENVIRONMENT from './environment.js'
import folderNames from './folderNames.js'
import ENVIRONMENT from '../environment.js'
import folderNames from '../folderNames.js'
function matchFirst (dirs, list) {
function matchLeft (dirs, list) {
for (let i = 0; i < dirs.length; i++) {
if (list.indexOf(dirs[i]) !== -1) {
return dirs[i]
@@ -47,7 +45,7 @@ function determineEnvironment (pathInProjectList) {
// remove filename
const dirList = pathInProjectList.slice(0, -1)
const matchedEnvironment = matchFirst(dirList, specialFolders)
const matchedEnvironment = matchLeft(dirList, specialFolders)
switch (matchedEnvironment) {
case folderNames.CLIENT:
@@ -64,19 +62,6 @@ function determineEnvironment (pathInProjectList) {
}
function stripPathPrefix (parent, child) {
const normalizedParent = path.normalize(parent)
const normalizedChild = path.normalize(child)
invariant(
normalizedChild.substr(0, normalizedParent.length) === parent,
'Linted file is not in CWD'
)
// also strip the / at the end, which is not in normalizedParent
return normalizedChild.substr(normalizedParent.length + 1)
}
function isMobileConfig (pathInProject) {
return pathInProject === 'mobile-config.js'
}
@@ -93,41 +78,27 @@ function isPackageConfig (pathInProjectList) {
return pathInProjectList[0] === folderNames.PACKAGES && pathInProjectList[2] === 'package.js'
}
function getMeteorFileInfo (rootPath, filename) {
const pathInProject = stripPathPrefix(rootPath, filename)
const pathInProjectList = pathInProject.split(path.sep)
function getMeteorFileInfo (relativeFilename) {
const pathInProjectList = relativeFilename.split(path.sep)
const environment = determineEnvironment(pathInProjectList)
return {
path: pathInProject,
path: relativeFilename,
env: environment,
isCompatibilityFile: isCompatibilityFile(environment, pathInProjectList),
isInMeteorProject: true,
isMobileConfig: isMobileConfig(pathInProject),
isMobileConfig: isMobileConfig(relativeFilename),
isPackageConfig: isPackageConfig(pathInProjectList)
}
}
function hasFile (parent, filename) {
return filename.substr(0, parent.length) === parent
}
export default function getMeteorMeta (relativeFilename) {
export default function getMeteorMeta (rootPaths, filename) {
if (!Array.isArray(rootPaths)) {
throw new Error('rootPath must be an array')
}
const rootPath = find(rootPaths, function (currentPath) {
return hasFile(currentPath, filename)
})
if (!rootPath) {
if (!relativeFilename) {
// not in a Meteor Project
return {isInMeteorProject: false}
}
return getMeteorFileInfo(rootPath, filename)
return getMeteorFileInfo(relativeFilename)
}

View File

@@ -0,0 +1,6 @@
import stripPathPrefix from './stripPathPrefix'
var getRootPath = require('./getRootPath')
export default function (filename) {
return stripPathPrefix(getRootPath(filename), filename)
}

View File

@@ -0,0 +1,27 @@
import find from 'lodash.find'
import isMeteorProject from './isMeteorProject'
var findOneUpwards = require('./findOneUpwards')
function hasFile (parent, filename) {
return filename.substr(0, parent.length) === parent
}
// cache for root paths
const rootPaths = []
export default function (filename) {
// check whether the project root is already known or not
let rootPath = find(rootPaths, function (currentPath) {
return hasFile(currentPath, filename)
})
if (!rootPath) {
// project root is unkown. search for it
rootPath = findOneUpwards(filename, isMeteorProject)
rootPaths.push(rootPath)
}
return rootPath
}

View File

@@ -1,5 +1,5 @@
import path from 'path'
import pathExists from 'path-exists'
var pathExists = require('path-exists')
export default function isMeteorProject (currentDirectory) {
const meteorPath = path.join(currentDirectory, '.meteor', 'release')

View File

@@ -0,0 +1,15 @@
import path from 'path'
import invariant from 'invariant'
export default function stripPathPrefix (parent, child) {
const normalizedParent = path.normalize(parent)
const normalizedChild = path.normalize(child)
invariant(
normalizedChild.substr(0, normalizedParent.length) === parent,
'Linted file is not in parent'
)
// also strip the / at the end, which is not in normalizedParent
return normalizedChild.substr(normalizedParent.length + 1)
}

View File

@@ -30,8 +30,7 @@
"dependencies": {
"invariant": "2.1.1",
"lodash.find": "3.2.1",
"path-exists": "2.0.0",
"walkdir": "0.0.10"
"path-exists": "2.0.0"
},
"devDependencies": {
"babel": "5.8.23",

22
tests/lib/util/getMeta.js Normal file
View File

@@ -0,0 +1,22 @@
/* eslint-env mocha */
import assert from 'assert'
var rewire = require('rewire')
var getMeta = rewire('../../../dist/util/getMeta')
getMeta.__set__('getRelativePath', function (path) {
return path
})
describe('getMeta', function () {
it('returns information when a filename is set', function () {
var result = getMeta('client/index.js')
assert.equal(typeof result, 'object')
})
it('returns no information when a filename is set', function () {
var result = getMeta()
assert.equal(typeof result, 'object')
assert.equal(result.isInMeteorProject, false)
})
})

View File

@@ -1,64 +0,0 @@
/* eslint-env mocha */
var assert = require('assert')
var rewire = require('rewire')
var getProjectRootPaths = rewire('../../../dist/util/getProjectRootPaths.js')
var walkShouldFindRoot
getProjectRootPaths.__set__('walk', {
sync: function (start, options, cb) {
var context = {ignore: function () {}}
if (walkShouldFindRoot) {
cb.call(context, '/User/anon/a/b/meteor-project')
cb.call(context, '/User/anon/a/b/c/meteor-project')
} else {
cb.call(context, '/User/otherguy')
cb.call(context, '/User/otherguy/a')
cb.call(context, '/User/otherguy/b')
cb.call(context, '/User/otherguy/a/b')
}
}
})
function matcher (filename) {
return (
filename === '/User/anon/a/b/meteor-project' ||
filename === '/User/anon/a/b/c/meteor-project'
)
}
describe('getProjectRootPaths', function () {
beforeEach(function () {
walkShouldFindRoot = true
})
it(`returns an empty array when no project is found`, function () {
walkShouldFindRoot = false
var result = getProjectRootPaths('/User/otherguy/', matcher)
assert.ok(Array.isArray(result))
assert.equal(result.length, 0)
})
it(`returns the path when it's the same directory`, function () {
var result = getProjectRootPaths('/User/anon/a/b/meteor-project', matcher)
assert.equal(result.length, 1)
assert.equal(result[0], '/User/anon/a/b/meteor-project')
})
it(`returns a parent Meteor project`, function () {
var result = getProjectRootPaths('/User/anon/a/b/meteor-project/a/b/c', matcher)
assert.equal(result.length, 1)
assert.equal(result[0], '/User/anon/a/b/meteor-project')
})
it(`returns all child Meteor projects`, function () {
var result = getProjectRootPaths('/User/anon', matcher)
assert.equal(result.length, 2)
assert.equal(result[0], '/User/anon/a/b/meteor-project')
assert.equal(result[1], '/User/anon/a/b/c/meteor-project')
})
})

View File

@@ -0,0 +1,33 @@
/* eslint-env mocha */
// Tests in this file will fail on windows, because they always use forward-slashes,
// but the actual implementation uses slashes depending on the OS.
// To fix the tests, use path.sep to determine correct slashes.
import assert from 'assert'
function matcher (filename) {
return (
filename === '/User/anon/a/b/meteor-project' ||
filename === '/User/anon/a/b/c/meteor-project'
)
}
var findOneUpwards = require('../../../../dist/util/internal/findOneUpwards.js')
describe('findOneUpwards', function () {
it('returns false when no project is found', function () {
var result = findOneUpwards('/User/otherguy/', matcher)
assert.equal(result, false)
})
it('returns the path when it\'s the same directory', function () {
var result = findOneUpwards('/User/anon/a/b/meteor-project', matcher)
assert.equal(result, '/User/anon/a/b/meteor-project')
})
it('returns a parent Meteor project', function () {
var result = findOneUpwards('/User/anon/a/b/meteor-project/x/y/z', matcher)
assert.equal(result, '/User/anon/a/b/meteor-project')
})
})

View File

@@ -1,28 +1,17 @@
/* eslint-env mocha */
var assert = require('assert')
var path = require('path')
import assert from 'assert'
import path from 'path'
var ENVIRONMENT = require('../../../dist/util/environment.js')
var getMeteorMeta = require('../../../dist/util/getMeteorMeta.js')
import ENVIRONMENT from '../../../../dist/util/environment.js'
import getMeteorMeta from '../../../../dist/util/internal/getMeteorMeta.js'
const rootPath = path.join('User', 'anon', 'meteor-project')
const rootPaths = [rootPath]
describe('getMeteorMeta', function () {
it('does not accept anything that is not an array', function () {
assert.throws(getMeteorMeta.bind(null), Error)
assert.throws(getMeteorMeta.bind(null, {}), Error)
assert.throws(getMeteorMeta.bind(null, true), Error)
assert.throws(getMeteorMeta.bind(null, false), Error)
assert.throws(getMeteorMeta.bind(null, 2), Error)
})
describe('when not in Meteor project', function () {
describe('when no filename is given', function () {
it('returns default env', function () {
var result = getMeteorMeta([], 'file.js')
var result = getMeteorMeta()
assert.equal(typeof result, 'object')
assert.equal(result.isInMeteorProject, false)
assert.equal(Object.keys(result).length, 1)
@@ -31,8 +20,8 @@ describe('getMeteorMeta', function () {
describe('in public', function () {
it('detects the environment', function () {
var filename = path.join(rootPath, 'public', 'file.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('public', 'file.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(typeof result, 'object')
assert.equal(result.path, 'public/file.js')
assert.equal(result.env, ENVIRONMENT.PUBLIC)
@@ -43,8 +32,8 @@ describe('getMeteorMeta', function () {
describe('in private', function () {
it('detects the environment', function () {
var filename = path.join(rootPath, 'private', 'file.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('private', 'file.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(typeof result, 'object')
assert.equal(result.path, 'private/file.js')
assert.equal(result.env, ENVIRONMENT.PRIVATE)
@@ -55,8 +44,8 @@ describe('getMeteorMeta', function () {
describe('in package', function () {
it('detects the environment', function () {
var filename = path.join(rootPath, 'packages', 'awesome-pkg', 'file.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('packages', 'awesome-pkg', 'file.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(typeof result, 'object')
assert.equal(result.path, 'packages/awesome-pkg/file.js')
assert.equal(result.env, ENVIRONMENT.PACKAGE)
@@ -67,8 +56,8 @@ describe('getMeteorMeta', function () {
describe('on no special folder', function () {
it('has universal environment', function () {
var filename = path.join(rootPath, 'file.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('file.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(typeof result, 'object')
assert.equal(result.path, 'file.js')
assert.equal(result.env, ENVIRONMENT.UNIVERSAL)
@@ -80,8 +69,8 @@ describe('getMeteorMeta', function () {
describe('on client', function () {
it('returns file info', function () {
var filename = path.join(rootPath, 'client', 'lib', 'file.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('client', 'lib', 'file.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(typeof result, 'object')
assert.equal(result.path, 'client/lib/file.js')
assert.equal(result.env, ENVIRONMENT.CLIENT)
@@ -90,8 +79,8 @@ describe('getMeteorMeta', function () {
})
it('does not detect compatibility when directly in client-folder ', function () {
var filename = path.join(rootPath, 'client', 'file.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('client', 'file.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(typeof result, 'object')
assert.equal(result.path, 'client/file.js')
assert.equal(result.env, ENVIRONMENT.CLIENT)
@@ -100,8 +89,8 @@ describe('getMeteorMeta', function () {
})
it('detects compatibility mode', function () {
var filename = path.join(rootPath, 'client', 'compatibility', 'file.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('client', 'compatibility', 'file.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(typeof result, 'object')
assert.equal(result.path, 'client/compatibility/file.js')
assert.equal(result.env, ENVIRONMENT.CLIENT)
@@ -112,8 +101,8 @@ describe('getMeteorMeta', function () {
describe('on server', function () {
it('detects the environment', function () {
var filename = path.join(rootPath, 'server', 'file.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('server', 'file.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(typeof result, 'object')
assert.equal(result.path, 'server/file.js')
assert.equal(result.env, ENVIRONMENT.SERVER)
@@ -123,8 +112,8 @@ describe('getMeteorMeta', function () {
describe('that is nested', function () {
it('detects the environment', function () {
var filename = path.join(rootPath, 'lib', 'server', 'file.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('lib', 'server', 'file.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(typeof result, 'object')
assert.equal(result.path, 'lib/server/file.js')
assert.equal(result.env, ENVIRONMENT.SERVER)
@@ -135,8 +124,8 @@ describe('getMeteorMeta', function () {
})
describe('in tests', function () {
var filename = path.join(rootPath, 'tests', 'file.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('tests', 'file.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(typeof result, 'object')
assert.equal(result.path, 'tests/file.js')
assert.equal(result.env, ENVIRONMENT.TEST)
@@ -145,8 +134,8 @@ describe('getMeteorMeta', function () {
})
describe('in node_modules', function () {
var filename = path.join(rootPath, 'node_modules', 'my-module', 'file.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('node_modules', 'my-module', 'file.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(typeof result, 'object')
assert.equal(result.path, 'node_modules/my-module/file.js')
assert.equal(result.env, ENVIRONMENT.NODE_MODULE)
@@ -156,15 +145,15 @@ describe('getMeteorMeta', function () {
describe('mobile-config.js', function () {
it('is detected', function () {
var filename = path.join(rootPath, 'mobile-config.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('mobile-config.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(result.isMobileConfig, true)
})
it('is not detected', function () {
var filename = path.join(rootPath, 'sub', 'mobile-config.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('sub', 'mobile-config.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(result.isMobileConfig, false)
})
@@ -172,15 +161,15 @@ describe('getMeteorMeta', function () {
describe('package.js', function () {
it('is detected', function () {
var filename = path.join(rootPath, 'packages', 'my-module', 'package.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('packages', 'my-module', 'package.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(result.isPackageConfig, true)
})
it('is not detected', function () {
var filename = path.join(rootPath, 'packages', 'package.js')
var result = getMeteorMeta(rootPaths, filename)
var relativeFilename = path.join('packages', 'package.js')
var result = getMeteorMeta(relativeFilename)
assert.equal(result.isPackageConfig, false)
})

View File

@@ -0,0 +1,18 @@
/* eslint-env mocha */
import assert from 'assert'
var rewire = require('rewire')
var getRelativePath = rewire('../../../../dist/util/internal/getRelativePath.js')
getRelativePath.__set__('getRootPath', function (filename) {
if (filename === '/Users/anon/git/meteor-project/client/file.js') {
return '/Users/anon/git/meteor-project'
}
})
describe('getRelativePath', function () {
it('gets the correct relative path', function () {
var result = getRelativePath('/Users/anon/git/meteor-project/client/file.js')
assert.equal(result, 'client/file.js')
})
})

View File

@@ -0,0 +1,25 @@
/* eslint-env mocha */
import assert from 'assert'
var rewire = require('rewire')
var getRootPath = rewire('../../../../dist/util/internal/getRootPath.js')
getRootPath.__set__('findOneUpwards', function (filename) {
if (filename === '/Users/anon/git/meteor-project/file.js') {
return '/Users/anon/git/meteor-project'
}
})
describe('getRootPath', function () {
it('finds the root path the first time', function () {
var result = getRootPath('/Users/anon/git/meteor-project/file.js')
assert.equal(result, '/Users/anon/git/meteor-project')
})
it('finds the root path on subsequent calls', function () {
var result1 = getRootPath('/Users/anon/git/meteor-project/file.js')
var result2 = getRootPath('/Users/anon/git/meteor-project/file.js')
assert.equal(result1, '/Users/anon/git/meteor-project')
assert.equal(result2, '/Users/anon/git/meteor-project')
})
})

View File

@@ -0,0 +1,23 @@
/* eslint-env mocha */
import assert from 'assert'
var rewire = require('rewire')
var isMeteorProject = rewire('../../../../dist/util/internal/isMeteorProject')
isMeteorProject.__set__('pathExists', {
sync: function (path) {
return path === '/Users/anon/git/meteor-project/.meteor/release'
}
})
describe('isMeteorProject', function () {
it('detects a Meteor project', function () {
var result = isMeteorProject('/Users/anon/git/meteor-project')
assert.ok(result)
})
it('does not detect a non-Meteor project', function () {
var result = isMeteorProject('/Users/anon/git/non-meteor-project')
assert.equal(result, false)
})
})

View File

@@ -0,0 +1,13 @@
/* eslint-env mocha */
import assert from 'assert'
import stripPathPrefix from '../../../../dist/util/internal/stripPathPrefix.js'
describe('stripPathPrefix', function () {
it('strips path correctly', function () {
var parent = '/Users/anon/git/meteor-project'
var child = '/Users/anon/git/meteor-project/folder/file.js'
var result = stripPathPrefix(parent, child)
assert.equal(result, 'folder/file.js')
})
})