Skip to content
This repository has been archived by the owner on Sep 28, 2020. It is now read-only.

Port babel loader fs cache as the default caching engine #159

Merged
merged 4 commits into from
Mar 23, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 73 additions & 97 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,93 +1,40 @@
var eslint = require("eslint")
var assign = require("object-assign")
var loaderUtils = require("loader-utils")
var crypto = require("crypto")
var fs = require("fs")
var findCacheDir = require("find-cache-dir")
var objectHash = require("object-hash")
var os = require("os")
var pkg = require("./package.json")
var createCache = require("loader-fs-cache")
var cache = createCache("eslint-loader")

var engines = {}
var rules = {}
var cache = null
var cachePath = null

/**
* linter
* printLinterOutput
*
* @param {String|Buffer} input JavaScript string
* @param {Object} eslint.executeOnText return value
* @param {Object} config eslint configuration
* @param {Object} webpack webpack instance
* @return {void}
*/
function lint(input, config, webpack) {
var resourcePath = webpack.resourcePath
var cwd = process.cwd()

// remove cwd from resource path in case webpack has been started from project
// root, to allow having relative paths in .eslintignore
if (resourcePath.indexOf(cwd) === 0) {
resourcePath = resourcePath.substr(cwd.length + 1)
}

// get engine
var configHash = objectHash(config)
var engine = engines[configHash]
var rulesHash = rules[configHash]

var res
// If cache is enable and the data are the same as in the cache, just
// use them
if (config.cache) {
// just get rules hash once per engine for performance reasons
if (!rulesHash) {
rulesHash = objectHash(engine.getConfigForFile(resourcePath))
rules[configHash] = rulesHash
}
var inputMD5 = crypto.createHash("md5").update(input).digest("hex")
if (
cache[resourcePath] &&
cache[resourcePath].hash === inputMD5 &&
cache[resourcePath].rules === rulesHash
) {
res = cache[resourcePath].res
}
}

// Re-lint the text if the cache off or miss
if (!res) {
res = engine.executeOnText(input, resourcePath, true)

// Save new results in the cache
if (config.cache) {
cache[resourcePath] = {
hash: inputMD5,
rules: rulesHash,
res: res,
}
fs.writeFileSync(cachePath, JSON.stringify(cache))
}
}

// executeOnText ensure we will have res.results[0] only

function printLinterOutput(res, config, webpack) {
// skip ignored file warning
if (!(
res.warningCount === 1 &&
res.results[0].messages[0] &&
res.results[0].messages[0].message &&
res.results[0].messages[0].message.indexOf("ignore") > 1
)) {
if (
!(res.warningCount === 1 &&
res.results[0].messages[0] &&
res.results[0].messages[0].message &&
res.results[0].messages[0].message.indexOf("ignore") > 1)
) {
// quiet filter done now
// eslint allow rules to be specified in the input between comments
// so we can found warnings defined in the input itself
if (res.warningCount && config.quiet) {
res.warningCount = 0
res.results[0].warningCount = 0
res.results[0].messages = res.results[0].messages
.filter(function(message) {
return message.severity !== 1
})
res.results[0].messages = res.results[0].messages.filter(function(
message
) {
return message.severity !== 1
})
}

// if enabled, use eslint auto-fixing where possible
Expand Down Expand Up @@ -128,19 +75,21 @@ function lint(input, config, webpack) {
if (emitter) {
emitter(messages)
if (config.failOnError && res.errorCount) {
throw new Error("Module failed because of a eslint error.\n"
+ messages)
throw new Error(
"Module failed because of a eslint error.\n" + messages
)
}
else if (config.failOnWarning && res.warningCount) {
throw new Error("Module failed because of a eslint warning.\n"
+ messages)
throw new Error(
"Module failed because of a eslint warning.\n" + messages
)
}
}
else {
throw new Error(
"Your module system doesn't support emitWarning. " +
"Update available? \n" +
messages
"Update available? \n" +
messages
)
}
}
Expand All @@ -155,45 +104,72 @@ function lint(input, config, webpack) {
* @return {void}
*/
module.exports = function(input, map) {
var webpack = this
var config = assign(
// loader defaults
{
formatter: require("eslint/lib/formatters/stylish"),
cacheIdentifier: JSON.stringify({
"eslint-loader": pkg.version,
eslint: eslint.version,
}),
},
// user defaults
this.options.eslint || {},
// loader query string
loaderUtils.getOptions(this)
)
this.cacheable()

var cacheDirectory = config.cacheDirectory
var cacheIdentifier = config.cacheIdentifier

delete config.cacheDirectory
delete config.cacheIdentifier

// Create the engine only once per config
var configHash = objectHash(config)
if (!engines[configHash]) {
engines[configHash] = new eslint.CLIEngine(config)
}

// Read the cached information only once and if enable
if (cache === null) {
if (config.cache) {
var thunk = findCacheDir({
name: "eslint-loader",
thunk: true,
create: true,
})
cachePath = thunk("data.json") || os.tmpdir() + "/data.json"
try {
cache = require(cachePath)
}
catch (e) {
cache = {}
this.cacheable()

var resourcePath = webpack.resourcePath
var cwd = process.cwd()

// remove cwd from resource path in case webpack has been started from project
// root, to allow having relative paths in .eslintignore
if (resourcePath.indexOf(cwd) === 0) {
resourcePath = resourcePath.substr(cwd.length + 1)
}

var engine = engines[configHash]
// return early if cached
if (config.cache) {
var callback = this.async()
return cache(
{
directory: cacheDirectory,
identifier: cacheIdentifier,
options: config,
source: input,
transform: function() {
return lint(engine, input, resourcePath)
},
},
function(err, res) {
if (err) {
return callback(err)
}
printLinterOutput(res || {}, config, webpack)
return callback(null, input, map)
}
}
else {
cache = false
}
)
}
printLinterOutput(lint(engine, input, resourcePath), config, this)
this.callback(input, map)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@viankakrisna i think this is your problem - first argument to this.callback is an error so should be set to null like the other callback a couple of line above it.

}

lint(input, config, this)
this.callback(null, input, map)
function lint(engine, input, resourcePath) {
return engine.executeOnText(input, resourcePath, true)
}
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
},
"dependencies": {
"find-cache-dir": "^0.1.1",
"loader-fs-cache": "^1.0.0",
"loader-utils": "^1.0.2",
"object-assign": "^4.0.1",
"object-hash": "^1.1.4"
"object-hash": "^1.1.4",
"rimraf": "^2.6.1"
},
"devDependencies": {
"ava": "^0.17.0",
Expand Down
Loading