Skip to content

Commit

Permalink
Changed to load stylelint from local node modules (#26)
Browse files Browse the repository at this point in the history
Changed to load stylelint from local node modules
  • Loading branch information
ntwb committed Jan 5, 2020
2 parents ea5b179 + faf1ce0 commit 73c78e6
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 4,789 deletions.
3 changes: 2 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
/lib/**
/lib/find-pkg-dir
/lib/inspect-with-kind
5 changes: 4 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
node_modules
.coverage
.nyc_output
/lib/**
/lib/find-pkg-dir
/lib/inspect-with-kind
/lib/stylelint-warning-to-vscode-diagnostic
/lib/array-to-sentence
/.vscode-test/**

# Unignore config files like .prettierrc.js, because they're ignored by default
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ A [Visual Studio Code](https://code.visualstudio.com/) extension to lint [CSS](h

![screenshot](screenshot.png)

The extension uses the stylelint library installed in the opened workspace folder. If the workspace folder does not provide the stylelint, the extension looks for a global installed stylelint.
If not in the global installed stylelint, the extension uses the stylelint embedded in the extension. (However, using stylelint embedded in the extension is not recommended.)

## Installation

1. Execute `Extensions: Install Extensions` command from [Command Palette](https://code.visualstudio.com/docs/getstarted/userinterface#_command-palette).
Expand Down Expand Up @@ -77,6 +80,13 @@ Default: `null`

Set stylelint [`config`](https://github.com/stylelint/stylelint/blob/master/docs/user-guide/node-api.md#config) option. Note that when this option is enabled, stylelint doesn't load configuration files.

#### stylelint.packageManager

Type: `"npm" | "yarn" | "pnpm"`
Default: `"npm"`

Controls the package manager to be used to resolve the stylelint library. This has only an influence if the stylelint library is resolved globally. Valid values are `"npm"` or `"yarn"` or `"pnpm"`.

#### editor.codeActionsOnSave

This setting supports the entry `source.fixAll.stylelint`. If set to `true` all auto-fixable stylelint errors will be fixed on save.
Expand Down
174 changes: 136 additions & 38 deletions lib/stylelint-vscode/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
'use strict';

const path = require('path');
const pathIsInside = require('path-is-inside');
const { at, has, intersection, isPlainObject, map, stubString } = require('lodash');
const { execSync } = require('child_process');
const { Files, TextDocument } = require('vscode-languageserver');
const { lint } = require('stylelint');
const { URI } = require('vscode-uri');

const arrayToError = require('../array-to-error');
const arrayToSentence = require('../array-to-sentence');
Expand Down Expand Up @@ -70,19 +73,7 @@ function processResults(resultContainer) {
};
}

module.exports = async function stylelintVSCode(...args) {
const argLen = args.length;

if (argLen !== 1 && argLen !== 2) {
throw new RangeError(
`Expected 1 or 2 arguments (<TextDocument>[, <Object>]), but got ${
argLen === 0 ? 'no' : argLen
} arguments.`,
);
}

const [textDocument, options = {}] = args;

module.exports = async function stylelintVSCode(textDocument, options = {}, serverOptions = {}) {
if (!TextDocument.is(textDocument)) {
throw new TypeError(
`Expected a TextDocument https://code.visualstudio.com/docs/extensionAPI/vscode-api#TextDocument, but got ${inspectWithKind(
Expand All @@ -91,24 +82,22 @@ module.exports = async function stylelintVSCode(...args) {
);
}

if (argLen === 2) {
if (!isPlainObject(options)) {
throw new TypeError(
`Expected an object containing stylelint API options, but got ${inspectWithKind(options)}.`,
);
}
if (!isPlainObject(options)) {
throw new TypeError(
`Expected an object containing stylelint API options, but got ${inspectWithKind(options)}.`,
);
}

const providedUnsupportedOptions = intersection(Object.keys(options), UNSUPPORTED_OPTIONS);
const providedUnsupportedOptions = intersection(Object.keys(options), UNSUPPORTED_OPTIONS);

if (providedUnsupportedOptions.length !== 0) {
throw new TypeError(
`${arrayToSentence(
map(UNSUPPORTED_OPTIONS, quote),
)} options are not supported because they will be derived from a document and there is no need to set them manually, but ${arrayToSentence(
map(providedUnsupportedOptions, quote),
)} was provided.`,
);
}
if (providedUnsupportedOptions.length !== 0) {
throw new TypeError(
`${arrayToSentence(
map(UNSUPPORTED_OPTIONS, quote),
)} options are not supported because they will be derived from a document and there is no need to set them manually, but ${arrayToSentence(
map(providedUnsupportedOptions, quote),
)} was provided.`,
);
}

const priorOptions = {
Expand Down Expand Up @@ -139,21 +128,27 @@ module.exports = async function stylelintVSCode(...args) {
}

try {
resultContainer = await lint({ ...options, ...priorOptions });
resultContainer = await lint(
{ ...options, ...priorOptions },
{ ...serverOptions, textDocument },
);
} catch (err) {
if (
err.message.startsWith('No configuration provided for') ||
err.message.includes('No rules found within configuration')
) {
// Check only CSS syntax errors without applying any stylelint rules
return processResults(
await lint({
...options,
...priorOptions,
config: {
rules: {},
}
}),
await lint(
{
...options,
...priorOptions,
config: {
rules: {},
},
},
{ ...serverOptions, textDocument },
),
);
}

Expand All @@ -162,3 +157,106 @@ module.exports = async function stylelintVSCode(...args) {

return processResults(resultContainer);
};

async function lint(options, { connection, packageManager, textDocument }) {
function trace(message, verbose) {
connection.tracer.log(message, verbose);
}

let stylelint;

try {
const resolvedGlobalPackageManagerPath = globalPathGet(packageManager, trace);
const uri = URI.parse(textDocument.uri);

let cwd;

if (uri.scheme === 'file') {
const file = uri.fsPath;
const directory = path.dirname(file);

cwd = directory;
} else {
const workspaceFolder = await getWorkspaceFolder(textDocument, connection);

cwd = workspaceFolder;
}

const stylelintPath = await Files.resolve(
'stylelint',
resolvedGlobalPackageManagerPath,
cwd,
trace,
);

stylelint = require(stylelintPath);
} catch {
// ignore
}

if (!stylelint || typeof stylelint.lint !== 'function') {
// Use self module
stylelint = require('stylelint');
}

return await stylelint.lint(options);
}

async function getWorkspaceFolder(document, connection) {
const documentPath = URI.parse(document.uri).fsPath;

if (documentPath) {
const workspaceFolders = await connection.workspace.getWorkspaceFolders();

if (workspaceFolders) {
for (const { uri } of workspaceFolders) {
const workspacePath = URI.parse(uri).fsPath;

if (pathIsInside(documentPath, workspacePath)) {
return workspacePath;
}
}
}
}

return undefined;
}

const globalPaths = {
yarn: {
cache: undefined,
get(trace) {
return Files.resolveGlobalYarnPath(trace);
},
},
npm: {
cache: undefined,
get(trace) {
return Files.resolveGlobalNodePath(trace);
},
},
pnpm: {
cache: undefined,
get() {
const pnpmPath = execSync('pnpm root -g')
.toString()
.trim();

return pnpmPath;
},
},
};

function globalPathGet(packageManager, trace) {
const pm = globalPaths[packageManager];

if (pm) {
if (pm.cache === undefined) {
pm.cache = pm.get(trace);
}

return pm.cache;
}

return undefined;
}
Loading

0 comments on commit 73c78e6

Please sign in to comment.