Skip to content
This repository has been archived by the owner on Aug 7, 2023. It is now read-only.

Commit

Permalink
Merge pull request #196 from smhxx/use-global-tslint
Browse files Browse the repository at this point in the history
Implement feature request #189 (Option to use global tslint instance)
  • Loading branch information
Arcanemagus authored Sep 22, 2017
2 parents f3e9b96 + 9d910ab commit ee9af70
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 34 deletions.
10 changes: 10 additions & 0 deletions lib/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const idleCallbacks = new Set();
const config = {
rulesDirectory: null,
useLocalTslint: false,
useGlobalTslint: false,
globalNodePath: null,
};

// Worker still hasn't initialized, since the queued idle callbacks are
Expand Down Expand Up @@ -59,6 +61,14 @@ export default {
config.enableSemanticRules = enableSemanticRules;
workerHelper.changeConfig('enableSemanticRules', enableSemanticRules);
}),
atom.config.observe('linter-tslint.useGlobalTslint', (use) => {
config.useGlobalTslint = use;
workerHelper.changeConfig('useGlobalTslint', use);
}),
atom.config.observe('linter-tslint.globalNodePath', (globalNodePath) => {
config.globalNodePath = globalNodePath;
workerHelper.changeConfig('globalNodePath', globalNodePath);
}),
atom.config.observe('linter-tslint.ignoreTypings', (ignoreTypings) => {
this.ignoreTypings = ignoreTypings;
}),
Expand Down
85 changes: 66 additions & 19 deletions lib/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import escapeHTML from 'escape-html';
import fs from 'fs';
import path from 'path';
import { getRuleUri } from 'tslint-rule-documentation';
import ChildProcess from 'child_process';
import getPath from 'consistent-path';

process.title = 'linter-tslint worker';

Expand All @@ -15,10 +17,10 @@ const config = {
useLocalTslint: false,
};

let tslintDef;
let fallbackLinter;
let requireResolve;

async function stat(pathname) {
function stat(pathname) {
return new Promise((resolve, reject) => {
fs.stat(pathname, (err, stats) => {
if (err) {
Expand Down Expand Up @@ -58,14 +60,8 @@ function shim(Linter) {
return LinterShim;
}

function loadDefaultTSLint() {
if (!tslintDef) {
// eslint-disable-next-line import/no-dynamic-require
tslintDef = require(tslintModuleName).Linter;
}
}

async function getLocalLinter(basedir) {
function resolveAndCacheLinter(fileDir, moduleDir) {
const basedir = moduleDir || fileDir;
return new Promise((resolve) => {
if (!requireResolve) {
requireResolve = require('resolve');
Expand All @@ -83,11 +79,26 @@ async function getLocalLinter(basedir) {
// eslint-disable-next-line import/no-dynamic-require
linter = require('loophole').allowUnsafeNewFunction(() => require(linterPath).Linter);
}
tslintCache.set(fileDir, linter);
}
resolve(linter);
},
);
});
}

function getNodePrefixPath() {
return new Promise((resolve, reject) => {
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
ChildProcess.exec(
`${npmCommand} get prefix`,
{ env: Object.assign(Object.assign({}, process.env), { PATH: getPath() }) },
(err, stdout, stderr) => {
if (err || stderr) {
reject(err || new Error(stderr));
} else {
linter = tslintDef;
resolve(stdout.trim());
}
tslintCache.set(basedir, linter);
return resolve(linter);
},
);
});
Expand All @@ -99,15 +110,49 @@ async function getLinter(filePath) {
return tslintCache.get(basedir);
}

// Initialize the default instance if it hasn't already been initialized
loadDefaultTSLint();

if (config.useLocalTslint) {
return getLocalLinter(basedir);
const localLint = await resolveAndCacheLinter(basedir);
if (localLint) {
return localLint;
}
}

if (fallbackLinter) {
tslintCache.set(basedir, fallbackLinter);
return fallbackLinter;
}

if (config.useGlobalTslint) {
if (config.globalNodePath) {
const globalLint = await resolveAndCacheLinter(basedir, config.globalNodePath);
if (globalLint) {
fallbackLinter = globalLint;
return globalLint;
}
}

let prefix;
try {
prefix = await getNodePrefixPath();
} catch (err) {
// eslint-disable-next-line no-console
console.warn(`Attempted to load global tslint, but \`npm get prefix\`
failed. Falling back to the packaged version of tslint. You can specify
your prefix manually in the settings or linter-tslint config file.\n
The error message encountered was:\n\n${err.message}`);
}

if (prefix) {
const globalLint = await resolveAndCacheLinter(basedir, prefix);
fallbackLinter = globalLint;
return globalLint;
}
}

tslintCache.set(basedir, tslintDef);
return tslintDef;
// eslint-disable-next-line import/no-dynamic-require
fallbackLinter = require(tslintModuleName).Linter;
tslintCache.set(basedir, fallbackLinter);
return fallbackLinter;
}

async function getProgram(Linter, configurationPath) {
Expand Down Expand Up @@ -208,6 +253,8 @@ async function lint(content, filePath, options) {
export default async function (initialConfig) {
config.useLocalTslint = initialConfig.useLocalTslint;
config.enableSemanticRules = initialConfig.enableSemanticRules;
config.useGlobalTslint = initialConfig.useGlobalTslint;
config.globalNodePath = initialConfig.globalNodePath;

process.on('message', async (message) => {
if (message.messageType === 'config') {
Expand Down
50 changes: 35 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,56 @@
"atom": ">=1.14.0 <2.0.0"
},
"configSchema": {
"enableSemanticRules": {
"type": "boolean",
"title": "Enable semantic rules",
"description": "Allow passing a TypeScript program object to the linter. May negatively affect performance. See this page for details: https://palantir.github.io/tslint/usage/type-checking/",
"default": false,
"order": 1
},
"rulesDirectory": {
"type": "string",
"title": "Custom rules directory (absolute path)",
"default": ""
"default": "",
"order": 2
},
"useLocalTslint": {
"type": "boolean",
"title": "Try using the local tslint package (if exist)",
"default": true
},
"enableSemanticRules": {
"fixOnSave": {
"title": "Fix errors on save",
"description": "Have tslint attempt to fix some errors automatically when saving the file.",
"type": "boolean",
"title": "Enable semantic rules",
"description": "Allow passing a TypeScript program object to the linter. May negatively affect performance. See this page for details: https://palantir.github.io/tslint/usage/type-checking/",
"default": false
"default": false,
"order": 3
},
"ignoreTypings": {
"type": "boolean",
"title": "Ignore typings files (.d.ts)",
"default": false
"default": false,
"order": 4
},
"fixOnSave": {
"title": "Fix errors on save",
"description": "Have tslint attempt to fix some errors automatically when saving the file.",
"useLocalTslint": {
"type": "boolean",
"title": "Try to use the project's local tslint package, if it exists",
"default": true,
"order": 5
},
"useGlobalTslint": {
"type": "boolean",
"default": false
"title": "Use the global tslint install",
"description": "If enabled, the global tslint installation will be used as a fallback, instead of the version packaged with linter-tslint.",
"default": false,
"order": 6
},
"globalNodePath": {
"type": "string",
"title": "Global node installation path",
"description": "The location of your global npm install. (Will default to `npm get prefix`.)",
"default": "",
"order": 7
}
},
"dependencies": {
"atom-package-deps": "^4.3.1",
"consistent-path": "^2.0.1",
"crypto-random-string": "^1.0.0",
"escape-html": "^1.0.3",
"loophole": "^1.1.0",
Expand Down

0 comments on commit ee9af70

Please sign in to comment.