Skip to content

Commit

Permalink
Release 2.1.0 - No longer return our own error on file not found.
Browse files Browse the repository at this point in the history
  • Loading branch information
mpratt committed May 30, 2017
1 parent dd16b4d commit fb1d636
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 148 deletions.
8 changes: 8 additions & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
extends: airbnb-base
plugins:
- import
rules:
no-console: off
func-names: off
max-len: off
no-confusing-arrow: off
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Test Files
testfile.js
test-folder

# Logs
logs
*.log
Expand Down Expand Up @@ -33,4 +37,5 @@ node_modules
.node_repl_history

# Webstorm
.idea
.idea
*.iml
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Changelog

# 2017-5-30: 2.1.0

* No longer will intercept what should be a standard Webpack "File not found" error.
* This also resolves the issue where the plugin wouldn't recognize when a file was added.
* Hardened tests.
* Cleaned up code and added an 'engines' config to package.json

# 2017-3-31: 2.0.0

* Use the compiler filesystem, which helps when other plugins change the 'fs' object being used by the compiler.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,4 @@ Thanks & Credit
* [Lance Eastgate](https://github.com/NorwegianKiwi) who added some internationalization support
* [Jonathan Kim](https://github.com/jkimbo) and [Dan Abramov](https://github.com/gaearon) who investigated, fixed, and added some tests for a crashing bug.
* [Jason Quense](https://github.com/jquense) who switched it to properly use the webpack-provided fs object.
* [Cesare Soldini](https://github.com/caesarsol) who added a test
183 changes: 96 additions & 87 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,134 +24,143 @@
--------
*/

var path = require('path');
const path = require('path');

function CaseSensitivePathsPlugin(options) {
this.options = options || {};
this.reset();
this.options = options || {};
this.reset();
}

CaseSensitivePathsPlugin.prototype.reset = function () {
this.pathCache = {};
this.fsOperations = 0;
this.primed = false;
}
this.pathCache = {};
this.fsOperations = 0;
this.primed = false;
};

CaseSensitivePathsPlugin.prototype.getFilenamesInDir = function (dir, callback) {
var _that = this
var fs = this.compiler.inputFileSystem;
this.fsOperations += 1;

if (this.pathCache.hasOwnProperty(dir)) {
return callback(this.pathCache[dir]);
const that = this;
const fs = this.compiler.inputFileSystem;
this.fsOperations += 1;

if (Object.prototype.hasOwnProperty.call(this.pathCache, dir)) {
callback(this.pathCache[dir]);
return;
}
if (this.options.debug) {
console.log('[CaseSensitivePathsPlugin] Reading directory', dir);
}

fs.readdir(dir, (err, files) => {
if (err) {
if (that.options.debug) {
console.log('[CaseSensitivePathsPlugin] Failed to read directory', dir, err);
}
callback([]);
return;
}
if (this.options.debug) {
console.log('[CaseSensitivePathsPlugin] Reading directory', dir);
}

fs.readdir(dir, function(err, files) {
if (err) {
if (_that.options.debug) {
console.log('[CaseSensitivePathsPlugin] Failed to read directory', dir, err);
}

return callback([]);
}

callback(files.map(function(f) {
return f.normalize ? f.normalize('NFC') : f;
}));
})
callback(files.map(f => f.normalize ? f.normalize('NFC') : f));
});
};

// This function based on code found at http://stackoverflow.com/questions/27367261/check-if-file-exists-case-sensitive
// By Patrick McElhaney (No license indicated - Stack Overflow Answer)
// This version will return with the real name of any incorrectly-cased portion of the path, null otherwise.
CaseSensitivePathsPlugin.prototype.fileExistsWithCase = function (filepath, callback) {
// Split filepath into current filename (or directory name) and parent directory tree.
var _this = this;
var dir = path.dirname(filepath);
var filename = path.basename(filepath);
var parsedPath = path.parse(dir);
const that = this;
const dir = path.dirname(filepath);
const filename = path.basename(filepath);
const parsedPath = path.parse(dir);

// If we are at the root, or have found a path we already know is good, return.
if (parsedPath.dir === parsedPath.root || dir === '.' || _this.pathCache.hasOwnProperty(filepath)) {
return callback();
}
if (parsedPath.dir === parsedPath.root || dir === '.' || Object.prototype.hasOwnProperty.call(that.pathCache, filepath)) {
callback();
return;
}

// Check all filenames in the current dir against current filename to ensure one of them matches.
// Read from the cache if available, from FS if not.
_this.getFilenamesInDir(dir, function (filenames) {
that.getFilenamesInDir(dir, (filenames) => {
// If the exact match does not exist, attempt to find the correct filename.
if (filenames.indexOf(filename) === - 1) {
// Fallback value, just in case.
var correctFilename = '- File does not exist.';

for (var i = 0; i < filenames.length; i++) {
if (filenames[i].toLowerCase() === filename.toLowerCase()) {
correctFilename = '`' + filenames[i] + '`.';
break;
}
}
return callback(correctFilename);
if (filenames.indexOf(filename) === -1) {
// Fallback value which triggers us to abort.
let correctFilename = '!nonexistent';

for (let i = 0; i < filenames.length; i += 1) {
if (filenames[i].toLowerCase() === filename.toLowerCase()) {
correctFilename = `\`${filenames[i]}\`.`;
break;
}
}
callback(correctFilename);
return;
}

// If exact match exists, recurse through directory tree until root.
_this.fileExistsWithCase(dir, function (recurse) {
that.fileExistsWithCase(dir, (recurse) => {
// If found an error elsewhere, return that correct filename
// Don't bother caching - we're about to error out anyway.
if (!recurse) {
_this.pathCache[dir] = filenames;
}
if (!recurse) {
that.pathCache[dir] = filenames;
}

callback(recurse)
});
callback(recurse);
});
}
});
};

CaseSensitivePathsPlugin.prototype.primeCache = function (callback) {
if (this.primed) return callback();
if (this.primed) {
callback();
return;
}

var _this = this;
const that = this;
// Prime the cache with the current directory. We have to assume the current casing is correct,
// as in certain circumstances people can switch into an incorrectly-cased directory.
var currentPath = path.resolve();
_this.getFilenamesInDir(currentPath, function (files) {
_this.pathCache[currentPath] = files;
_this.primed = true;
callback()
});
}
const currentPath = path.resolve();
that.getFilenamesInDir(currentPath, (files) => {
that.pathCache[currentPath] = files;
that.primed = true;
callback();
});
};

CaseSensitivePathsPlugin.prototype.apply = function(compiler) {
var _this = this;
CaseSensitivePathsPlugin.prototype.apply = function (compiler) {
const that = this;

this.compiler = compiler;
this.compiler = compiler;

compiler.plugin('done', function() {
if (_this.options.debug) {
console.log('[CaseSensitivePathsPlugin] Total filesystem reads:', _this.fsOperations);
}
_this.reset();
});
compiler.plugin('done', () => {
if (that.options.debug) {
console.log('[CaseSensitivePathsPlugin] Total filesystem reads:', that.fsOperations);
}
that.reset();
});

compiler.plugin('normal-module-factory', function(nmf) {
nmf.plugin('after-resolve', function(data, done) {
_this.primeCache(function () {
compiler.plugin('normal-module-factory', (nmf) => {
nmf.plugin('after-resolve', (data, done) => {
that.primeCache(() => {
// Trim ? off, since some loaders add that to the resource they're attemping to load
var pathName = data.resource.split('?')[0];
pathName = pathName.normalize ? pathName.normalize('NFC') : pathName;

_this.fileExistsWithCase(pathName, function(realName) {
if (realName) {
done(new Error('[CaseSensitivePathsPlugin] `' + pathName + '` does not match the corresponding path on disk ' + realName));
} else {
done(null, data);
}
});
});
let pathName = data.resource.split('?')[0];
pathName = pathName.normalize ? pathName.normalize('NFC') : pathName;

that.fileExistsWithCase(pathName, (realName) => {
if (realName) {
if (realName === '!nonexistent') {
// If file does not exist, let Webpack show a more appropriate error.
done(null, data);
} else {
done(new Error(`[CaseSensitivePathsPlugin] \`${pathName}\` does not match the corresponding path on disk ${realName}`));
}
} else {
done(null, data);
}
});
});
});
});
};

module.exports = CaseSensitivePathsPlugin;
11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
{
"name": "case-sensitive-paths-webpack-plugin",
"version": "2.0.0",
"version": "2.1.0",
"description": "Enforces module path case sensitivity in Webpack",
"engines": { "node": ">4.0" },
"main": "index.js",
"scripts": {
"test": "mocha test/"
"test": "mocha test/",
"lint": "eslint index.js",
"lintfix": "eslint --fix index.js"
},
"repository": {
"type": "git",
Expand All @@ -27,6 +30,10 @@
},
"homepage": "https://github.com/Urthen/case-sensitive-paths-webpack-plugin#readme",
"devDependencies": {
"eslint": "^3.19.0",
"eslint-config-airbnb-base": "^11.2.0",
"eslint-plugin-import": "^2.3.0",
"fs-extra": "^2.1.2",
"mocha": "^3.0.1",
"webpack": "^1.13.1"
}
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/file-creation/entry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
var testModule = require('./testfile');
Loading

0 comments on commit fb1d636

Please sign in to comment.