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

Commit

Permalink
perf: decaffeinate the provider and defer dependencies
Browse files Browse the repository at this point in the history
Move from CoffeeScript to JavaScript so the code is actually readable,
and allowing things like `async`/`await`. Also moves the configuration
to a `configSchema` in the `package.json` file. Dependencies are
deferred till they are actually needed, with an opportunistic load
during an "idle" time.
  • Loading branch information
Arcanemagus committed Jan 3, 2018
1 parent 3e6355b commit 3f607fc
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 129 deletions.
122 changes: 0 additions & 122 deletions lib/init.coffee

This file was deleted.

180 changes: 180 additions & 0 deletions lib/init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
'use babel';

// eslint-disable-next-line import/no-extraneous-dependencies, import/extensions
import { CompositeDisposable } from 'atom';
import path from 'path';

// Dependencies
let bundledPugLint;
let objectAssign;
let configFile;
let helpers;
let resolve;

const pugLints = new Map();

const resolvePath = (name, basedir) =>
new Promise(((res, reject) =>
resolve(name, { basedir }, (err, modulePath) => {
if (err != null) {
reject(err);
}
return res(modulePath);
})
));

const getPugLint = async (baseDir) => {
if (pugLints.has(baseDir)) {
return pugLints.get(baseDir);
}

try {
const pugLintPath = await resolvePath('pug-lint', baseDir);
// eslint-disable-next-line import/no-dynamic-require
pugLints.set(baseDir, require(pugLintPath));
} catch (e) {
pugLints.set(baseDir, bundledPugLint);
}

return pugLints.get(baseDir);
};

const loadDeps = () => {
if (!bundledPugLint) {
bundledPugLint = require('pug-lint');
}
if (!objectAssign) {
objectAssign = require('object-assign');
}
if (!configFile) {
configFile = require('pug-lint/lib/config-file');
}
if (!helpers) {
helpers = require('atom-linter');
}
if (!resolve) {
resolve = require('resolve');
}
};

module.exports = {
activate() {
this.idleCallbacks = new Set();
let depsCallbackID;
const installLinterJSHintDeps = () => {
this.idleCallbacks.delete(depsCallbackID);
loadDeps();
};
depsCallbackID = window.requestIdleCallback(installLinterJSHintDeps);
this.idleCallbacks.add(depsCallbackID);

if (atom.config.get('linter-pug.executablePath')) {
atom.notifications.addWarning(
'Removing custom pug-lint path',
{
detail: 'linter-pug has moved to the Node.js API for pug-lint and ' +
"will now use a project's local instance where possible, falling " +
'back to a bundled version of pug-lint where none is found.',
},
);
atom.config.unset('linter-pug.executablePath');
}

this.subscriptions = new CompositeDisposable();
this.subscriptions.add(atom.config.observe(
'linter-pug.projectConfigFile',
(value) => { this.projectConfigFile = value; },
));
this.subscriptions.add(atom.config.observe(
'linter-pug.onlyRunWhenConfig',
(value) => { this.onlyRunWhenConfig = value; },
));
},

deactivate() {
this.idleCallbacks.forEach(callbackID => window.cancelIdleCallback(callbackID));
this.idleCallbacks.clear();
this.subscriptions.dispose();
},

getConfig(filePath) {
let config;
if (path.isAbsolute(this.projectConfigFile)) {
config = configFile.load(false, this.projectConfigFile);
} else {
config = configFile.load(false, path.join(path.dirname(filePath), this.projectConfigFile));
}
if (!config && this.onlyRunWhenConfig) {
return undefined;
}

const options = {};
const newConfig = objectAssign(options, config);

if (!newConfig.configPath && config && config.configPath) {
newConfig.configPath = config.configPath;
}
return newConfig;
},

provideLinter() {
return {
name: 'pug-lint',
grammarScopes: ['source.jade', 'source.pug'],
scope: 'file',
lintOnFly: true,

lint: async (textEditor) => {
if (!atom.workspace.isTextEditor(textEditor)) {
// Somehow, called with an invalid TextEditor instance
return null;
}

const filePath = textEditor.getPath();
if (!filePath) {
// File somehow has no path
return null;
}

const fileText = textEditor.getText();
if (!fileText) {
// Nothing in the file
return null;
}

// Load the dependencies if they aren't already
loadDeps();

const projectConfig = this.getConfig(filePath);
if (!projectConfig || !projectConfig.configPath) {
if (this.onlyRunWhenConfig) {
atom.notifications.addError('Pug-lint config not found');
return null;
}
}

let rules = [];
if (this.onlyRunWhenConfig || projectConfig) {
rules = projectConfig;
}

// Use Atom's project root folder
let projectDir = atom.project.relativizePath(filePath)[0];
if ((projectDir == null)) {
// Fall back to the file directory
projectDir = path.dirname(filePath);
}

const linter = new (await getPugLint(projectDir))();
linter.configure(rules);
const results = linter.checkString(fileText);
return results.map(res => ({
type: res.name,
filePath,
range: helpers.generateRange(textEditor, res.line - 1, res.column - 1),
text: res.msg,
}));
},
};
},
};
48 changes: 42 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,64 @@
},
"license": "MIT",
"engines": {
"atom": ">=1.0.0 <2.0.0"
"atom": ">=1.7.0 <2.0.0"
},
"readmeFilename": "README.md",
"bugs": {
"url": "https://github.com/AtomLinter/atom-linter-pug/issues"
},
"homepage": "https://github.com/AtomLinter/atom-linter-pug",
"configSchema": {
"projectConfigFile": {
"type": "string",
"default": "",
"description": "Relative path from project to config file"
},
"onlyRunWhenConfig": {
"default": false,
"title": "Run Pug-lint only if config is found",
"description": "Disable linter if there is no config file found for the linter.",
"type": "boolean"
}
},
"providedServices": {
"linter": {
"versions": {
"1.0.0": "provideLinter"
"2.0.0": "provideLinter"
}
}
},
"dependencies": {
"atom-linter": "^10.0.0",
"atom-package-deps": "^4.0.1",
"object-assign": "^4.1.0",
"pug-lint": "^2.1.9",
"resolve": "^1.5.0"
},
"package-deps": [
"linter"
]
"devDependencies": {
"eslint": "^4.14.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "^2.8.0",
"jasmine-fix": "^1.3.1"
},
"eslintConfig": {
"rules": {
"global-require": "off",
"import/no-unresolved": [
"error",
{
"ignore": [
"atom"
]
}
]
},
"extends": "airbnb-base",
"globals": {
"atom": true
},
"env": {
"node": true,
"browser": true
}
}
}
14 changes: 14 additions & 0 deletions spec/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
env: {
atomtest: true,
jasmine: true,
},
rules: {
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": true
}
]
}
};
2 changes: 1 addition & 1 deletion spec/linter-stylint-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ noConfigRule = path.join(__dirname, 'fixtures', 'noConfig', 'badRule.pug')
noConfigSyntax = path.join(__dirname, 'fixtures', 'noConfig', 'badSyntax.pug')

describe 'The pug-lint provider for Linter', ->
lint = require('../lib/init.coffee').provideLinter().lint
lint = require('../lib/init').provideLinter().lint

beforeEach ->
atom.workspace.destroyActivePaneItem()
Expand Down

0 comments on commit 3f607fc

Please sign in to comment.