Skip to content

Commit

Permalink
Merge pull request #12 from GeorgeTaveras1231/master
Browse files Browse the repository at this point in the history
Allow relative paths in scoped packages
  • Loading branch information
lennym committed Feb 1, 2017
2 parents eaa348a + 3084a15 commit fe7c03f
Show file tree
Hide file tree
Showing 17 changed files with 368 additions and 56 deletions.
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,12 @@ build/Release

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
/node_modules
# Ignore node_modules installed during tests
/test/test-cases/**/node_modules

# npm debug logs
npm-debug.log*

# Vim swap files
*.sw[pon]
63 changes: 9 additions & 54 deletions lib/importer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,21 @@ var findup = require('findup'),
path = require('path');

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

function find(dir, file, callback) {
var name;
if (file.split('/')[0][0] === '@') {
name = file.split('/').slice(0, 2).join('/');
} 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 {
location = path.join('../', file);
}
callback(null, path.resolve(root, location));
});
}
var resolve = require('./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
});
};
});
resolve(url, file)
.catch(function () { return url; })
.then(function (path) {
return { file: path };
})
.then(done);
}
})
});
}

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

module.exports = function resolve(importUrl, importOriginPath) {
var _import = new Import(importUrl);

return nearestPackageRoot(_import.packageName(), importOriginPath).then(function (packageRoot) {
var _package = new Package(packageRoot);

if (_import.isEntrypoint()) {
return _package.fullPathToEntrypoint();
} else {
return _package.root(_import.specifiedFilePath());
}
});
}
35 changes: 35 additions & 0 deletions lib/resolver/import.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
var path = require('path');
var nearestPackageRoot = require('./nearest-package-root');

module.exports = Import;

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

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

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

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

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

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

/**
* @param {string} importOriginPath Path of file that made @import reference
*/
module.exports = function nearestPackageRoot(packageName, importOriginPath) {
var pathToFind = path.join('node_modules', packageName, 'package.json');
var dirnameOfImportOrigin = path.dirname(importOriginPath);
var promise, handleFoundPath;

promise = new Promise(function (resolve, reject) {
handleFoundPath = function (err, nearestPackageParent) {
if(err) {
reject(err);
return;
}

var packageJSONLocation = path.join( nearestPackageParent, 'node_modules', packageName);

resolve(packageJSONLocation);
};
});

findup(dirnameOfImportOrigin, pathToFind, handleFoundPath);

return promise;
};
28 changes: 28 additions & 0 deletions lib/resolver/package.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
var path = require('path');
var nearestPackageRoot = require('./nearest-package-root');

module.exports = Package;

function Package(rootPath) {
this.root = path.join.bind(null, rootPath);
this.JSON = require(this.root('package.json'));
};

Package.prototype.fullPathToEntrypoint = function fullPathToEntrypoint() {
return this.root(this.entrypoint());
};

Package.prototype.entrypoint = function entrypoint() {
if (this.JSON.sass) {
return this.JSON.sass;
// look for "style" declaration in package.json
} else if (this.JSON.style) {
return this.JSON.style;
// look for a css/sass/scss file in the "main" declaration in package.json
} else if (/\.(sa|c|sc)ss$/.test(this.JSON.main)) {
return this.JSON.main;
// otherwise assume ./styles.scss
} else {
return 'styles';
}
};
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@
"description": "sass compilation with npm aware include paths",
"main": "index.js",
"bin": "./bin/npm-sass",
"config": {
"integration-test-files": "test/index.js",
"unit-test-files": "test/units/*.js"
},
"scripts": {
"test": "mocha test/index.js"
"test": "mocha $npm_package_config_unit_test_files $npm_package_config_integration_test_files",
"unit-test": "mocha $npm_package_config_unit_test_files",
"integration-test": "mocha $npm_package_config_integration_test_files"
},
"repository": {
"type": "git",
Expand All @@ -19,6 +25,7 @@
"homepage": "https://github.com/lennym/npm-sass",
"dependencies": {
"argh": "^0.1.4",
"bluebird": "^3.4.6",
"camelify": "0.0.2",
"findup": "^0.1.5",
"glob": "^6.0.1",
Expand Down
Empty file.
Empty file.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import "@lennym/npm-sass-test-sass/child-file";
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "npm-sass-test-npm-modules",
"version": "0.0.0",
"description": "sass compilation with npm aware include paths",
"dependencies": {
"@lennym/npm-sass-test-sass": "0.0.0"
}
}
46 changes: 46 additions & 0 deletions test/units/import-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
var Import = require('../../lib/resolver/import');
var assert = require('assert');

describe('Import', function () {
describe('#isScoped', function () {
it('returns true if package is scoped', function () {
var subject1 = new Import('@scoped/package');
var subject2 = new Import('non-scoped-package');

assert(subject1.isScoped());
assert(!subject2.isScoped());
});
});

describe('#packageName', function () {
it('returns the assumed package name', function () {
var subject1 = new Import('@scoped/package/nested-path');
var subject2 = new Import('non-scoped-package/nested-path');

assert.equal(subject1.packageName(), '@scoped/package');
assert.equal(subject2.packageName(), 'non-scoped-package');
});
});

describe('#isEntrypoint', function () {
it('returns true if the import is for the entrypoint', function () {
var subject1 = new Import('@scoped/package/nested-path');
var subject2 = new Import('@scoped/package');
var subject3 = new Import('non-scoped-package/nested-path');
var subject4 = new Import('non-scoped-package');

assert(!subject1.isEntrypoint());
assert(subject2.isEntrypoint());
assert(!subject3.isEntrypoint());
assert(subject4.isEntrypoint());
});
});

describe('#specifiedFilePath', function () {
it('returns the specified nested path', function () {
var subject = new Import('@scoped/package/nested-path1/nested-path2');

assert.equal(subject.specifiedFilePath(), '/nested-path1/nested-path2');
});
});
});
30 changes: 30 additions & 0 deletions test/units/nearest-package-root-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
var nearestPackageRoot = require('../../lib/resolver/nearest-package-root');
var assert = require('assert');
var path = require('path');

var fixturesPath = path.resolve.bind(null,
__dirname,
'..',
'fixtures',
'nearest-package-root-test'
)

describe('nearestPackageRoot', function () {
it('finds the nearest module folder based on the import origin', function (done) {
var sourcePath = fixturesPath('index.scss');
nearestPackageRoot('test-module', sourcePath).then(function (result) {
assert.equal(result, fixturesPath('node_modules', 'test-module'));
})
.then(done)
.catch(done);
});

it('works with nested dependencies', function (done) {
var sourcePath = fixturesPath('node_modules', 'test-module', 'index.scss');
nearestPackageRoot('nested-module', sourcePath).then(function (result) {
assert.equal(result, fixturesPath('node_modules', 'test-module', 'node_modules', 'nested-module'));
})
.then(done)
.catch(done);
});
});

0 comments on commit fe7c03f

Please sign in to comment.