Skip to content

Commit

Permalink
Support multiple source directories (#1)
Browse files Browse the repository at this point in the history
* Remove unused functions

* Support multiple source directories

elm-package.json may specify multiple source directories for a single Elm project (e.g. application modules and library modules). Modules in one source directory may import modules in another source directory transparently.

With this change, we now find the elm-package.json file responsible for the requested module and use it to search all of the project’s source directories for each dependency.

* Ensure elm-package.json source-directories include the module

* Fix support for relative path on input
  • Loading branch information
sentience authored and stoeffel committed Mar 23, 2017
1 parent 3ed59e8 commit 873f939
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 26 deletions.
80 changes: 55 additions & 25 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ function getBaseDir(file) {

// Returns a Promise that returns a flat list of all the Elm files the given
// Elm file depends on, based on the modules it loads via `import`.
function findAllDependencies(file, knownDependencies, baseDir, knownFiles) {
function findAllDependencies(file, knownDependencies, sourceDirectories, knownFiles) {
if (!knownDependencies) {
knownDependencies = [];
}
Expand All @@ -55,21 +55,56 @@ function findAllDependencies(file, knownDependencies, baseDir, knownFiles) {
return knownDependencies;
}

if (baseDir) {
return findAllDependenciesHelp(file, knownDependencies, baseDir, knownFiles).then(function(thing){
if (sourceDirectories) {
return findAllDependenciesHelp(file, knownDependencies, sourceDirectories, knownFiles).then(function(thing){
return thing.knownDependencies;
});
} else {
return getBaseDir(file).then(function(newBaseDir) {
return findAllDependenciesHelp(file, knownDependencies, newBaseDir, knownFiles).then(function(thing){
return thing.knownDependencies;
return getBaseDir(file)
.then(getElmPackageSourceDirectories)
.then(function(newSourceDirs) {
return findAllDependenciesHelp(file, knownDependencies, newSourceDirs, knownFiles).then(function(thing){
return thing.knownDependencies;
});
});
})
}
}

// Given a source directory (containing top-level Elm modules), locate the
// elm-package.json file that includes it and get all its source directories.
function getElmPackageSourceDirectories(baseDir, currentDir) {
if (typeof currentDir === "undefined") {
currentDir = baseDir = path.resolve(baseDir);
}

var elmPackagePath = path.join(currentDir, 'elm-package.json');
if (fs.existsSync(elmPackagePath)) {
var sourceDirectories = getSourceDirectories(elmPackagePath);
if (_.includes(sourceDirectories, baseDir)) {
return sourceDirectories;
}
}

if (isRoot(currentDir)) {
return false;
}

return getElmPackageSourceDirectories(baseDir, path.dirname(currentDir));
}

function isRoot(dir) {
var parsedPath = path.parse(dir);
return parsedPath.root === parsedPath.dir;
}

function findAllDependenciesHelp(file, knownDependencies, baseDir, knownFiles) {
function getSourceDirectories(elmPackagePath) {
var elmPackage = JSON.parse(fs.readFileSync(elmPackagePath, 'utf8'));
return elmPackage['source-directories'].map(function(sourceDir) {
return path.resolve(path.dirname(elmPackagePath), sourceDir);
});
}

function findAllDependenciesHelp(file, knownDependencies, sourceDirectories, knownFiles) {
return new Promise(function(resolve, reject) {
// if we already know the file, return known deps since we won't learn anything
if (knownFiles.indexOf(file) !== -1){
Expand Down Expand Up @@ -113,7 +148,14 @@ function findAllDependenciesHelp(file, knownDependencies, baseDir, knownFiles) {
}

// e.g. ~/code/elm-css/src/Css/Declarations.elm
var result = path.join(baseDir, dependencyLogicalName + extension);
var result = null;
_.find(sourceDirectories, function(sourceDir) {
var absPath = path.join(sourceDir, dependencyLogicalName + extension);
if (fs.existsSync(absPath)) {
result = absPath;
return true;
}
});

return _.includes(knownDependencies, result) ? null : result;

Expand All @@ -125,24 +167,24 @@ function findAllDependenciesHelp(file, knownDependencies, baseDir, knownFiles) {
var newDependencies = knownDependencies.concat(validDependencies);
var recursePromises = _.compact(validDependencies.map(function(dependency) {
return path.extname(dependency) === ".elm" ?
findAllDependenciesHelp(dependency, newDependencies, baseDir, knownFiles) : null;
findAllDependenciesHelp(dependency, newDependencies, sourceDirectories, knownFiles) : null;
}));

Promise.all(recursePromises).then(function(extraDependencies) {
// keep track of files that weren't found in our src directory
var externalPackageFiles = [];
var packagesInError = [];

var justDeps = extraDependencies.map(function(thing){
// if we had an error, we flag the file as a bad thing
if (thing.error){
externalPackageFiles.push(thing.file)
packagesInError.push(thing.file)
return [];
}
return thing.knownDependencies;
});

var flat = _.uniq(_.flatten(knownDependencies.concat(justDeps))).filter(function(file){
return externalPackageFiles.indexOf(file) === -1;
return packagesInError.indexOf(file) === -1;
});

resolve({
Expand All @@ -154,19 +196,7 @@ function findAllDependenciesHelp(file, knownDependencies, baseDir, knownFiles) {
}).catch(reject);
});
}
function handleError(pathToMake, err) {
if (err.code === "ENOENT") {
console.error("Could not find Elm compiler \"" + pathToMake + "\". Is it installed?")
} else if (err.code === "EACCES") {
console.error("Elm compiler \"" + pathToMake + "\" did not have permission to run. Do you need to give it executable permissions?");
} else {
console.error("Error attempting to run Elm compiler \"" + pathToMake + "\":\n" + err);
}
}

function escapePath(pathStr) {
return pathStr.replace(/ /g, "\\ ");
}
module.exports = {
findAllDependencies: findAllDependencies
};
16 changes: 16 additions & 0 deletions test/dependencies.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,20 @@ describe("#findAllDependencies", function() {
);
});
});

it("works for a file with dependencies stored in another source directory", function () {
return findAllDependencies(prependFixturesDir("ParentWithOtherSrcDeps.elm")).then(function(results) {
expect(results).to.deep.equal(
[ "other-src/OtherChild.elm" ].map(prependFixturesDir)
);
});
});

it('ignores an elm-package.json file that does not list the module’s source directory', function() {
return findAllDependencies(prependFixturesDir('other-src/OtherParent.elm')).then(function(results) {
expect(results).to.deep.equal(
[ "Test/ChildA.elm" ].map(prependFixturesDir)
);
});
});
});
8 changes: 8 additions & 0 deletions test/fixtures/ParentWithOtherSrcDeps.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module ParentWithOtherSrcDeps exposing (..)

import OtherChild
import Html


main =
Html.text "Hello, World!"
3 changes: 2 additions & 1 deletion test/fixtures/elm-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"repository": "https://github.com/foo/bar.git",
"license": "BSD3",
"source-directories": [
"."
".",
"other-src"
],
"native-modules": true,
"exposed-modules": [],
Expand Down
7 changes: 7 additions & 0 deletions test/fixtures/other-src/OtherChild.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module OtherChild exposing (..)

import Html


main =
Html.text "I am child A"
8 changes: 8 additions & 0 deletions test/fixtures/other-src/OtherParent.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module OtherParent exposing (..)

import Test.ChildA
import Html


main =
Html.text "Hello, World!"
16 changes: 16 additions & 0 deletions test/fixtures/other-src/elm-package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"version": "1.0.0",
"summary": "helpful summary of your project, less than 80 characters",
"repository": "https://github.com/foo/bar.git",
"license": "BSD3",
"source-directories": [
"bad-directory-does-not-exist"
],
"native-modules": true,
"exposed-modules": [],
"dependencies": {
"elm-lang/core": "5.0.0 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"
}

0 comments on commit 873f939

Please sign in to comment.