Skip to content

Commit

Permalink
Introduce resolver
Browse files Browse the repository at this point in the history
The resolver module implements a path resolving algorithm that
leverages node's own require.

This has a couple benefits:
* more likely to continue working as expected when features are
introduced to npm/node
* reduction of code duplication (npm/node already implements the module
resolution we are interested in for this project)
* allow the use of NODE_PATH variable to control module directory

Additionally, `resolver` leverages `Package` and `Import` constructors
which implement some of the same logic previously found on the `find`
function in `lib/importer.js`. The benefit of creating these
constructors will obvious when unit tests are introduced for this
project.

`Package`: implements logic related to retrieving package data, such as
getting the package.json, and getting the sass 'main' file for a given
package.

`Import`: implements logic to make sense of a sass import.
  • Loading branch information
GeorgeTaveras1231 committed Nov 21, 2016
1 parent ec16eb2 commit 230e069
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 60 deletions.
63 changes: 4 additions & 59 deletions lib/importer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,16 @@ var findup = require('findup'),
path = require('path');

var local = require('./local');

function find(dir, file, callback) {
var name, isScoped = false;
if (file.split('/')[0][0] === '@') {
name = file.split('/').slice(0, 2).join('/');
isScoped = true;
} else {
name = file.split('/')[0];
}

var modulePath = './node_modules/' + name + '/package.json';

findup(dir, modulePath, function (err, moduleDir) {
if (err) { return callback(err); }

var root = path.dirname(path.resolve(moduleDir, modulePath));
var location;
// if import is just a module name
if (file === name) {
var json = require(path.resolve(moduleDir, modulePath));
// look for "sass" declaration in package.json
if (json.sass) {
location = json.sass;
// look for "style" declaration in package.json
} else if (json.style) {
location = json.style;
// look for a css/sass/scss file in the "main" declaration in package.json
} else if (/\.(sa|c|sc)ss$/.test(json.main)) {
location = json.main;
// otherwise assume ./styles.scss
} else {
location = './styles';
}
// if a full path is provided
} else {
if(isScoped) {
location = path.join('..', '..', file);
} else {
location = path.join('..', file);
}
}
callback(null, path.resolve(root, location));
});
}
var resolve = require('./resolver').Resolver();

function importer(url, file, done) {
local(url, file, function (err, isLocal) {
if (err || isLocal) {
done({
file: url
});
done({ file: url });
} else {
find(path.dirname(file), url, function (err, location) {
if (err) {
done({
file: url
});
} else {
done({
file: location
});
};
});
done({ file: resolve(url) });
}
})
});
}

module.exports = importer;
19 changes: 19 additions & 0 deletions lib/resolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var Package = require('./resolver/package');
var Import = require('./resolver/import');

exports.Resolver = function Resolver(requireFn) {
/* Facilitate testing by allowing requireFn to be specified */
requireFn = (requireFn || require) ;

return function resolve(sassImportPath) {
var _import = new Import(sassImportPath);
var _package = new Package(_import.packageName(), requireFn);

if (_import.isEntrypoint()) {
return _package.resolveEntrypoint();
} else {
return _package.safeResolve(_import.specifiedFilePath());
}
}
}

34 changes: 34 additions & 0 deletions lib/resolver/import.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var path = require('path');

module.exports = Import;

function Import(sassImportPath) {
this.sassImportPath = sassImportPath;
};

Import.prototype.isScoped = function isScoped() {
return this.sassImportPath[0] === '@';
};

Import.prototype.packageName = function packageName() {
if (this.isScoped()) {
return this.sassImportPath.split(path.sep, 2).join(path.sep);
} else {
return this.sassImportPath.split(path.sep, 1)[0];
}
};

Import.prototype.isEntrypoint = function isEntrypoint() {
var safePathSplitPattern = new RegExp(path.sep + '.');
var pathSegmentCount = this.sassImportPath.split(safePathSplitPattern).length;

if (this.isScoped()) {
return pathSegmentCount === 2;
} else {
return pathSegmentCount === 1;
}
};

Import.prototype.specifiedFilePath = function specifiedFileName() {
return this.sassImportPath.slice(this.packageName().length);
};
46 changes: 46 additions & 0 deletions lib/resolver/package.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
var path = require('path');

module.exports = Package;

function Package(name, requireFn) {
this.requireFn = requireFn;

this.path = path.join.bind(null, name);
};

Package.prototype.json = function packageJSON() {
return this.requireFn(this.path('package.json'));
};

Package.prototype.resolve = function resolve(path) {
return this.requireFn.resolve(this.path(path));
};

Package.prototype.safeResolve = function safeResolve(potentiallyNonExistentPath) {
return path.join(this.dir(), potentiallyNonExistentPath);
};

Package.prototype.dir = function dir() {
return path.dirname(this.resolve('package.json'));
};

Package.prototype.entrypoint = function entrypoint() {
var packageJson = this.json();

if (packageJson.sass) {
return packageJson.sass;
// look for "style" declaration in package.json
} else if (packageJson.style) {
return packageJson.style;
// look for a css/sass/scss file in the "main" declaration in package.json
} else if (/\.(sa|c|sc)ss$/.test(packageJson.main)) {
return packageJson.main;
// otherwise assume ./styles.scss
} else {
return 'styles';
}
};

Package.prototype.resolveEntrypoint = function resolveEntrypoint() {
return this.safeResolve(this.entrypoint());
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"main": "index.js",
"bin": "./bin/npm-sass",
"scripts": {
"test": "mocha test/index.js"
"test": "NODE_PATH=$NODE_PATH:\"$(git rev-parse --show-toplevel)/test/fixtures/node_modules\" mocha test/index.js"
},
"repository": {
"type": "git",
Expand Down

0 comments on commit 230e069

Please # to comment.