diff --git a/package-lock.json b/package-lock.json index 8a8a74cb..88a772ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "devDependencies": { "@stylelint/postcss-css-in-js": "^0.37.2", "@stylelint/prettier-config": "^2.0.0", + "@types/eslint": "^7.28.1", "@types/fs-extra": "^9.0.13", "@types/jest": "^27.0.1", "@types/lodash": "^4.14.151", @@ -1137,6 +1138,22 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/eslint": { + "version": "7.28.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.1.tgz", + "integrity": "sha512-XhZKznR3i/W5dXqUhgU9fFdJekufbeBd5DALmkuXoeFcjbQcPk+2cL+WLHf6Q81HWAnM2vrslIHpGVyCAviRwg==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "dev": true + }, "node_modules/@types/fs-extra": { "version": "9.0.13", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", @@ -6958,7 +6975,7 @@ }, "node_modules/stylelint": { "version": "13.13.1", - "resolved": "git+https://git@github.com/stylelint/stylelint.git#585d28a64358a6b7c72d1bed4aaf849caa3bf181", + "resolved": "git+https://git@github.com/stylelint/stylelint.git#816b19a2245fbc170045b970dd0a6bb6fc684463", "dev": true, "license": "MIT", "dependencies": { @@ -8761,6 +8778,22 @@ "@babel/types": "^7.3.0" } }, + "@types/eslint": { + "version": "7.28.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.1.tgz", + "integrity": "sha512-XhZKznR3i/W5dXqUhgU9fFdJekufbeBd5DALmkuXoeFcjbQcPk+2cL+WLHf6Q81HWAnM2vrslIHpGVyCAviRwg==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "dev": true + }, "@types/fs-extra": { "version": "9.0.13", "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", @@ -13122,7 +13155,7 @@ "dev": true }, "stylelint": { - "version": "git+https://git@github.com/stylelint/stylelint.git#585d28a64358a6b7c72d1bed4aaf849caa3bf181", + "version": "git+https://git@github.com/stylelint/stylelint.git#816b19a2245fbc170045b970dd0a6bb6fc684463", "dev": true, "from": "stylelint@git+https://git@github.com/stylelint/stylelint#v14", "requires": { diff --git a/package.json b/package.json index e92951cf..7a69157c 100644 --- a/package.json +++ b/package.json @@ -165,6 +165,7 @@ "devDependencies": { "@stylelint/postcss-css-in-js": "^0.37.2", "@stylelint/prettier-config": "^2.0.0", + "@types/eslint": "^7.28.1", "@types/fs-extra": "^9.0.13", "@types/jest": "^27.0.1", "@types/lodash": "^4.14.151", @@ -200,7 +201,6 @@ "lint": "run-s \"lint:*\"", "lint:formatting": "prettier . --check", "lint:js": "eslint --report-unused-disable-directives .", - "lint:stylelint-types": "node scripts/stylelint-types.js", "lint:types": "tsc", "test": "npm run bundle && jest", "test:lib": "jest --projects test/lib", diff --git a/scripts/stylelint-types.js b/scripts/stylelint-types.js deleted file mode 100644 index 387dc2e3..00000000 --- a/scripts/stylelint-types.js +++ /dev/null @@ -1,105 +0,0 @@ -'use strict'; - -const https = require('https'); -const path = require('path'); - -const fs = require('fs-extra'); - -const typesDir = path.join(__dirname, '../types/stylelint'); -const typesPath = path.join(typesDir, 'index.d.ts'); -const typesVersionPath = path.join(typesDir, 'version'); - -const lockfile = require('../package-lock.json'); - -const { version } = lockfile?.dependencies?.stylelint; - -if (!version) { - console.error('Could not find stylelint version in package-lock.json'); - process.exit(1); -} - -const args = new Set(process.argv.slice(2)); - -/** - * Checks if the Stylelint types file is up to date. - * @returns {Promise} - */ -const checkTypesVersion = async () => { - try { - const downloadedVersion = (await fs.readFile(typesVersionPath, 'utf8'))?.trim(); - - if (!downloadedVersion) { - console.error('Could not read types version. Try running `npm run download-types`.'); - process.exit(1); - } - - if (downloadedVersion !== version) { - console.error( - `Types version mismatch: ${downloadedVersion} !== ${version}. Try running \`npm run download-types\`.`, - ); - process.exit(1); - } - - console.log('Stylelint types are up to date.'); - } catch (error) { - console.error(error); - process.exit(1); - } -}; - -/** - * Gets the GitHub tag/commit for the given version of Stylelint. - * @param {string} lockfileVersion The version of stylelint from the package-lock.json file. - * @returns {string} - */ -const getTypesTagOrCommitHash = (lockfileVersion) => { - if (lockfileVersion.startsWith('git+')) { - const match = lockfileVersion.match(/#(.+)$/); - - if (!match || !match[1]) { - throw new Error(`Could not find commit hash for ${lockfileVersion}`); - } - - return match[1]; - } - - return lockfileVersion; -}; - -/** - * Downloads the stylelint types file for the currently installed version of Stylelint. - * @returns {Promise} - */ -const downloadTypes = async () => { - const repoVersion = getTypesTagOrCommitHash(version); - const typesURL = `https://raw.githubusercontent.com/stylelint/stylelint/${repoVersion}/types/stylelint/index.d.ts`; - - await fs.ensureDir(typesDir); - - const typesFile = fs.createWriteStream(typesPath); - - const typesRequest = https.get(typesURL, (response) => { - if (response.statusCode !== 200) { - console.error(`Request failed with status code ${response.statusCode}`); - process.exit(1); - } - - response.pipe(typesFile); - }); - - typesRequest.on('error', (error) => { - console.error(error); - process.exit(1); - }); - - typesRequest.on('close', () => { - console.log(`Downloaded Stylelint types file to ${typesPath}`); - fs.writeFile(typesVersionPath, `${version}\n`); - }); -}; - -if (args.has('--download')) { - downloadTypes(); -} else { - checkTypesVersion(); -} diff --git a/src/index.js b/src/index.js index 76f98b2f..d27b1c03 100644 --- a/src/index.js +++ b/src/index.js @@ -8,10 +8,7 @@ const { const { workspace, commands: Commands, window: Window } = require('vscode'); /** - * @typedef {import('vscode').ExtensionContext} ExtensionContext - */ -/** - * @param {ExtensionContext} context + * @param {vscode.ExtensionContext} context */ exports.activate = ({ subscriptions }) => { const serverPath = require.resolve('./server.js'); diff --git a/src/server.js b/src/server.js index a34e6b55..ca539277 100644 --- a/src/server.js +++ b/src/server.js @@ -26,28 +26,13 @@ const { } = require('vscode-languageserver/node'); const { TextDocument } = require('vscode-languageserver-textdocument'); -/** - * @typedef { import('vscode-languageserver').DocumentUri } DocumentUri - * @typedef { import('vscode-languageserver').Diagnostic } Diagnostic - * @typedef { import('vscode-languageserver').CompletionItem } CompletionItem - * @typedef { import('vscode-languageserver').CompletionParams } CompletionParams - * @typedef { import('./stylelint-vscode').DisableReportRange } DisableReportRange - * @typedef { import('stylelint').StylelintConfig } StylelintConfiguration - * @typedef { import('stylelint').StylelintStandaloneOptions } BaseStylelintLinterOptions - * @typedef { Partial } StylelintLinterOptions - * @typedef { "npm" | "yarn" | "pnpm" } PackageManager - * @typedef { import('./stylelint-vscode').StylelintVSCodeOption } StylelintVSCodeOption - * @typedef { Error & { reasons: string[] } } InvalidOptionError - * @typedef { Error & { code: 78 } } ConfigurationError - */ - const CommandIds = { applyAutoFix: 'stylelint.applyAutoFix', }; const StylelintSourceFixAll = `${CodeActionKind.SourceFixAll}.stylelint`; -/** @type {StylelintConfiguration} */ +/** @type {stylelint.Config} */ let config; /** @type {string} */ let configFile; @@ -74,23 +59,22 @@ const connection = createConnection(ProposedFeatures.all); const documents = new TextDocuments(TextDocument); /** - * @type {Map} + * @type {Map} */ const documentDiagnostics = new Map(); /** - * @type {Map} + * @type {Map} */ const needlessDisableReports = new Map(); /** - * @type {Map} + * @type {Map} */ const invalidScopeDisableReports = new Map(); /** - * * @param {TextDocument} document - * @param {StylelintLinterOptions} baseOptions - * @returns {Promise} + * @param {Partial} baseOptions + * @returns {Promise>} */ async function buildStylelintOptions(document, baseOptions = {}) { const options = { ...baseOptions }; @@ -152,10 +136,10 @@ async function buildStylelintOptions(document, baseOptions = {}) { /** * @param {TextDocument} document - * @returns {Promise} + * @returns {Promise} */ async function buildStylelintVSCodeOptions(document) { - /** @type {StylelintVSCodeOption} */ + /** @type {StylelintVSCodeOptions} */ const options = { connection, packageManager }; if (stylelintPath) { @@ -190,7 +174,7 @@ function handleError(err) { return; } - // https://github.com/stylelint/stylelint/blob/10.0.1/lib/utils/configurationError.js#L10 + // https://github.com/stylelint/stylelint/blob/551dcb5/lib/utils/configurationError.js#L12 if (/** @type {ConfigurationError} */ (err)?.code === 78) { connection.window.showErrorMessage(`stylelint: ${err.message}`); @@ -238,12 +222,12 @@ async function validate(document) { /** * @param {TextDocument} document - * @param {import('vscode-languageserver').FormattingOptions?} formattingOptions Formatting options to use. + * @param {lsp.FormattingOptions?} formattingOptions Formatting options to use. * Overriden by stylelint configuration. * @returns {Promise} */ async function getFixes(document, formattingOptions = null) { - /** @type {Partial} */ + /** @type {Partial} */ const baseOptions = { fix: true }; // If formatting options were provided, translate them to their corresponding rules. @@ -331,7 +315,7 @@ let registerFormatterDynamically = false; /** * A promise that resolves to the disposable for the dynamically registered document formatter. - * @type {Promise | undefined} + * @type {Promise | undefined} */ let formatterRegistration; @@ -543,8 +527,8 @@ connection.onCodeAction(async (params) => { if (edits.length > 0) { results.push( CodeAction.create( - needlessDisable.range.unusedRule !== 'all' - ? `Remove unused stylelint comment directive for ${needlessDisable.range.unusedRule} rule` + needlessDisable.range.rule !== 'all' + ? `Remove unused stylelint comment directive for ${needlessDisable.range.rule} rule` : `Remove unused stylelint comment directive.`, { documentChanges: [TextDocumentEdit.create(textDocumentIdentifer, edits)] }, CodeActionKind.QuickFix, @@ -632,8 +616,8 @@ function replaceEdits(document, newText) { } /** - * @param {CompletionParams} params - * @returns {CompletionItem[]} + * @param {lsp.CompletionParams} params + * @returns {lsp.CompletionItem[]} */ function onCompletion(params) { const uri = params.textDocument.uri; @@ -687,7 +671,7 @@ function onCompletion(params) { nextLineRules.delete(''); nextLineRules.delete('CssSyntaxError'); - /** @type {CompletionItem[]} */ + /** @type {lsp.CompletionItem[]} */ const results = []; const disableKind = getStyleLintDisableKind(document, params.position); @@ -739,7 +723,7 @@ function onCompletion(params) { /** * @param { 'stylelint-disable-line' | 'stylelint-disable-next-line' } kind * @param {string} rule - * @returns {CompletionItem} + * @returns {lsp.CompletionItem} */ function createDisableLineCompletionItem(kind, rule = '') { return { @@ -759,7 +743,7 @@ function createDisableLineCompletionItem(kind, rule = '') { } /** - * @returns {CompletionItem} + * @returns {lsp.CompletionItem} */ function createDisableEnableCompletionItem() { return { @@ -807,7 +791,7 @@ function getStyleLintDisableKind(document, position) { /** * @param {TextDocument} document - * @param {DisableReportRange} range + * @param {stylelint.DisableReportRange} range * @returns {TextEdit[]} */ function createRemoveCommentDirectiveTextEdits(document, range) { @@ -913,7 +897,7 @@ function createRemoveCommentDirectiveTextEdits(document, range) { } /** - * @param {Diagnostic} diagnostic + * @param {lsp.Diagnostic} diagnostic * @returns {string} */ function computeKey(diagnostic) { @@ -931,16 +915,16 @@ function escapeRegExp(value) { } /** - * @param {DisableReportRange} range + * @param {stylelint.DisableReportRange} range * @param {string} text * @param {string} directive * @returns {string} */ function removeCommentDirective(range, text, directive) { - if (range.unusedRule !== 'all') { + if (range.rule !== 'all') { // `/* directive rulename */` let newText = text.replace( - new RegExp(`\\/\\*\\s*${directive}\\s+${escapeRegExp(range.unusedRule)}\\s*\\*\\/`), + new RegExp(`\\/\\*\\s*${directive}\\s+${escapeRegExp(range.rule)}\\s*\\*\\/`), removesReplacer, ); @@ -952,7 +936,7 @@ function removeCommentDirective(range, text, directive) { newText = text.replace( new RegExp( `(\\/\\*\\s*${directive}\\s+[\\s\\S]*)\\s*,\\s*${escapeRegExp( - range.unusedRule, + range.rule, )}([\\s\\S]*\\*\\/)`, ), removesReplacer, @@ -966,7 +950,7 @@ function removeCommentDirective(range, text, directive) { newText = text.replace( new RegExp( `(\\/\\*\\s*${directive}\\s+[\\s\\S]*)${escapeRegExp( - range.unusedRule, + range.rule, )}\\s*,\\s*([\\s\\S]*\\*\\/)`, ), removesReplacer, diff --git a/src/stylelint-vscode.js b/src/stylelint-vscode.js index e4230abb..be7f66b8 100644 --- a/src/stylelint-vscode.js +++ b/src/stylelint-vscode.js @@ -3,32 +3,12 @@ const path = require('path'); const pathIsInside = require('path-is-inside'); const { at, has, map, stubString } = require('lodash'); -const { Diagnostic, DiagnosticSeverity, Position, Range } = require('vscode-languageserver-types'); const { execSync } = require('child_process'); const { Files } = require('vscode-languageserver/node'); const { URI } = require('vscode-uri'); const stylelintWarningToVscodeDiagnostic = require('./warnings-to-diagnostics'); -/** - * @typedef { import('stylelint').StylelintPublicAPI } StylelintModule - * @typedef { import('vscode-languageserver-textdocument').TextDocument } TextDocument - * @typedef { import('vscode-languageserver').Connection } Connection - * @typedef { "npm" | "yarn" | "pnpm" } PackageManager - * @typedef { {connection?: Connection, packageManager?: PackageManager, stylelintPath?: string } } StylelintVSCodeOption - * @typedef {object} StylelintVSCodeResult - * @property {Diagnostic[]} diagnostics - * @property {string} [output] - * @property {({ diagnostic: Diagnostic, range: DisableReportRange })[]} [needlessDisables] - * @property {({ diagnostic: Diagnostic, range: DisableReportRange })[]} [invalidScopeDisables] - * @typedef { import('stylelint').StylelintStandaloneOptions } BaseStylelintLinterOptions - * @typedef { Partial } StylelintLinterOptions - * @typedef { {unusedRule:string,start:number,end:?number} } DisableReportRange - * @typedef { { source?: string, ranges: DisableReportRange[] } } StylelintDisableReportEntry - * @typedef { import('./warnings-to-diagnostics').RuleDocUrlProvider } RuleDocUrlProvider - * @typedef { (message: string, verbose?: string) => void } TracerFn - */ - class InvalidOptionError extends Error { /** * @param {string[]} reasons @@ -40,107 +20,49 @@ class InvalidOptionError extends Error { } /** - * @param {import('stylelint').StylelintStandaloneReturnValue} resultContainer - * @param {TextDocument} textDocument + * @param {stylelint.LinterResult} resultContainer * @param {RuleDocUrlProvider} ruleDocUrlProvider * @returns {StylelintVSCodeResult} */ -function processResults(resultContainer, textDocument, ruleDocUrlProvider) { +function processResults(resultContainer, ruleDocUrlProvider) { const { results } = resultContainer; - /** @type {StylelintDisableReportEntry[]} */ - // @ts-expect-error -- The stylelint type is old. - const needlessDisables = resultContainer.needlessDisables; - /** @type {StylelintDisableReportEntry[]} */ - // @ts-expect-error -- The stylelint type is old. - const invalidScopeDisables = resultContainer.invalidScopeDisables; - - // https://github.com/stylelint/stylelint/blob/12.0.1/lib/standalone.js#L128-L134 - if ( - results.length === 0 && - (!needlessDisables || needlessDisables.length === 0) && - (!invalidScopeDisables || invalidScopeDisables.length === 0) - ) { - return { - diagnostics: [], - }; + + if (results.length === 0) { + return { diagnostics: [] }; } const [{ invalidOptionWarnings, warnings, ignored }] = results; if (ignored) { - return { - diagnostics: [], - }; + return { diagnostics: [] }; } if (invalidOptionWarnings.length !== 0) { throw new InvalidOptionError(map(invalidOptionWarnings, 'text')); } - const diagnostics = []; - let needlessDisableResults; - let invalidScopeDisableResults; - - const needlessDisableSourceReport = needlessDisables && needlessDisables[0]; - - if (needlessDisableSourceReport) { - needlessDisableResults = []; - - for (const range of needlessDisableSourceReport.ranges) { - const diagnostic = stylelintDisableOptionsReportRangeToVscodeDiagnostic(range, textDocument); - - diagnostics.push(diagnostic); - needlessDisableResults.push({ - range, - diagnostic, - }); - } - } - - const invalidScopeDisableSourceReport = invalidScopeDisables && invalidScopeDisables[0]; - - if (invalidScopeDisableSourceReport) { - invalidScopeDisableResults = []; - - for (const range of invalidScopeDisableSourceReport.ranges) { - const diagnostic = stylelintDisableOptionsReportRangeToVscodeDiagnostic(range, textDocument); - - diagnostics.push(diagnostic); - invalidScopeDisableResults.push({ - range, - diagnostic, - }); - } - } - - diagnostics.push( - ...warnings.map((warning) => stylelintWarningToVscodeDiagnostic(warning, ruleDocUrlProvider)), + const diagnostics = warnings.map((warning) => + stylelintWarningToVscodeDiagnostic(warning, ruleDocUrlProvider), ); if (has(resultContainer, 'output') && resultContainer.output) { return { diagnostics, output: resultContainer.output, - ...(needlessDisableResults ? { needlessDisables: needlessDisableResults } : {}), - ...(invalidScopeDisableResults ? { invalidScopeDisables: invalidScopeDisableResults } : {}), }; } - return { - diagnostics, - ...(needlessDisableResults ? { needlessDisables: needlessDisableResults } : {}), - ...(invalidScopeDisableResults ? { invalidScopeDisables: invalidScopeDisableResults } : {}), - }; + return { diagnostics }; } /** - * @param {TextDocument} textDocument - * @param {StylelintLinterOptions} options - * @param {StylelintVSCodeOption} serverOptions + * @param {lsp.TextDocument} textDocument + * @param {stylelint.LinterOptions} options + * @param {StylelintVSCodeOptions} serverOptions * @returns {Promise} */ module.exports = async function stylelintVSCode(textDocument, options = {}, serverOptions = {}) { - /** @type {StylelintLinterOptions} */ + /** @type {stylelint.LinterOptions} */ const priorOptions = { code: textDocument.getText(), formatter: stubString, @@ -182,7 +104,6 @@ module.exports = async function stylelintVSCode(textDocument, options = {}, serv rules: {}, }, }), - textDocument, createRuleDocUrlProvider(stylelint), ); } @@ -190,12 +111,12 @@ module.exports = async function stylelintVSCode(textDocument, options = {}, serv throw err; } - return processResults(resultContainer, textDocument, createRuleDocUrlProvider(stylelint)); + return processResults(resultContainer, createRuleDocUrlProvider(stylelint)); }; /** - * @param {StylelintVSCodeOption & {textDocument: TextDocument} } options - * @returns {Promise} + * @param {StylelintVSCodeOptions & {textDocument: lsp.TextDocument} } options + * @returns {Promise} */ async function resolveStylelint({ connection, @@ -282,7 +203,7 @@ async function resolveStylelint({ } /** - * @param {StylelintModule} stylelint + * @param {stylelint.PublicApi} stylelint * @returns {RuleDocUrlProvider} */ function createRuleDocUrlProvider(stylelint) { @@ -296,8 +217,8 @@ function createRuleDocUrlProvider(stylelint) { } /** - * @param {TextDocument} document - * @param {Connection} [connection] + * @param {lsp.TextDocument} document + * @param {lsp.Connection} [connection] * @returns {Promise} */ async function getWorkspaceFolder(document, connection) { @@ -364,47 +285,3 @@ function globalPathGet(packageManager = 'npm', trace) { return undefined; } - -/** - * @param {DisableReportRange} range - * @param {TextDocument} textDocument - * @returns {Diagnostic} - */ -function stylelintDisableOptionsReportRangeToVscodeDiagnostic(range, textDocument) { - let message = `unused rule: ${range.unusedRule}, start line: ${range.start}`; - const startPosition = convertStartPosition(range); - const endPosition = convertEndPosition(range, textDocument); - - if (range.end !== undefined) { - message += `, end line: ${range.end}`; - } - - return Diagnostic.create( - Range.create(startPosition, endPosition), - message, - DiagnosticSeverity.Warning, - range.unusedRule, - 'stylelint', - ); -} - -/** - * @param {DisableReportRange} range - * @returns {Position} - */ -function convertStartPosition(range) { - return Position.create(range.start - 1, 0); -} - -/** - * @param {DisableReportRange} range - * @param {TextDocument} textDocument - * @returns {Position} - */ -function convertEndPosition(range, textDocument) { - if (range.end) { - return textDocument.positionAt(textDocument.offsetAt(Position.create(range.end, 0)) - 1); - } - - return textDocument.positionAt(textDocument.getText().length); -} diff --git a/src/warnings-to-diagnostics.js b/src/warnings-to-diagnostics.js index 7949533a..24344423 100644 --- a/src/warnings-to-diagnostics.js +++ b/src/warnings-to-diagnostics.js @@ -2,18 +2,11 @@ const { Diagnostic, DiagnosticSeverity, Position, Range } = require('vscode-languageserver-types'); -/** - * @typedef { import('stylelint').Severity } Severity - * @typedef { { line: number, column: number, rule: string, severity: Severity, text: string } } Warning - * @typedef { import('vscode-languageserver-types').URI } URI - * @typedef { (rule: string) => (URI | null | undefined) } RuleDocUrlProvider - */ - /** @type {RuleDocUrlProvider} */ const NOOP_RULE_DOC_URL_PROVIDER = () => undefined; /** - * @param {Warning} warning + * @param {stylelint.Warning} warning * @param {RuleDocUrlProvider} [ruleDocUrlProvider] * @returns {Diagnostic} */ @@ -24,7 +17,7 @@ module.exports = function stylelintWarningToVscodeDiagnostic( const position = Position.create(warning.line - 1, warning.column - 1); /** - * @type {URI | null | undefined} + * @type {lsp.URI | null | undefined} */ const ruleDocUrl = ruleDocUrlProvider(warning.rule); diff --git a/test/lib/stylelint-vscode/__snapshots__/test.js.snap b/test/lib/stylelint-vscode/__snapshots__/test.js.snap index 01982f55..2a2cde19 100644 --- a/test/lib/stylelint-vscode/__snapshots__/test.js.snap +++ b/test/lib/stylelint-vscode/__snapshots__/test.js.snap @@ -108,11 +108,11 @@ Array [ "message": "Unclosed bracket (CssSyntaxError)", "range": Object { "end": Object { - "character": 19, + "character": 12, "line": 0, }, "start": Object { - "character": 19, + "character": 12, "line": 0, }, }, diff --git a/test/lib/stylelint-vscode/test.js b/test/lib/stylelint-vscode/test.js index f0e67b0d..70b52176 100644 --- a/test/lib/stylelint-vscode/test.js +++ b/test/lib/stylelint-vscode/test.js @@ -6,11 +6,12 @@ const { pathToFileURL } = require('url'); const createTextDocument = require('vscode-languageserver').TextDocument.create; const stylelintVSCode = require('../../../src/stylelint-vscode'); -const createDocument = ( - /** @type {string | null} */ uri, - /** @type {string} */ languageId, - /** @type {string} */ contents, -) => +/** + * @param {string | null} uri + * @param {string} languageId + * @param {string} contents + */ +const createDocument = (uri, languageId, contents) => createTextDocument( uri ? pathToFileURL(resolve(__dirname, '..', uri)).toString() : 'Untitled:Untitled', languageId, @@ -36,7 +37,10 @@ describe('stylelintVSCode()', () => { test('should be resolved with an empty array when no errors and warnings are reported', async () => { expect.assertions(1); const result = await stylelintVSCode(createDocument(null, 'scss', ''), { - config: { rules: { indentation: [2] } }, + config: { + customSyntax: 'postcss-scss', + rules: { indentation: [2] }, + }, }); expect(result.diagnostics).toEqual([]); @@ -168,9 +172,7 @@ a { color: #000 } test('should check CSS syntax even if no configuration is provided', async () => { expect.assertions(1); - const result = await stylelintVSCode( - createDocument('unclosed.xml', 'xml', ''), - ); + const result = await stylelintVSCode(createDocument('unclosed.css', 'css', 'a{color:rgba(}')); expect(result.diagnostics).toMatchSnapshot(); }); @@ -289,6 +291,7 @@ describe('stylelintVSCode() with autofix', () => { const result = await stylelintVSCode( createDocument('should-be-ignored.js', 'javascript', '"a"'), { + customSyntax: '@stylelint/postcss-css-in-js', config: { rules: {}, ignoreFiles: '**/*-ignored.js', diff --git a/test/lib/warnings-to-diagnostics/test.js b/test/lib/warnings-to-diagnostics/test.js index 1596c171..f9f35f70 100644 --- a/test/lib/warnings-to-diagnostics/test.js +++ b/test/lib/warnings-to-diagnostics/test.js @@ -1,6 +1,6 @@ 'use strict'; -const { lint } = /** @type {import('stylelint').StylelintPublicAPI} */ (require('stylelint')); +const { lint } = require('stylelint'); const fn = require('../../../src/warnings-to-diagnostics'); diff --git a/test/workspace/lint/stylelint.config.js b/test/workspace/lint/stylelint.config.js index be41f8bb..6d8aa822 100644 --- a/test/workspace/lint/stylelint.config.js +++ b/test/workspace/lint/stylelint.config.js @@ -1,9 +1,6 @@ 'use strict'; -/** @typedef {import('stylelint').StylelintConfig} StylelintConfig */ - -// TODO: Workaround for bad typings upstream -/** @type {StylelintConfig & { overrides: StylelintConfig }} */ +/** @type {stylelint.Config} */ const config = { rules: { indentation: [4], diff --git a/test/workspace/rule-doc/test-plugin.js b/test/workspace/rule-doc/test-plugin.js index 829fefd1..71557af4 100644 --- a/test/workspace/rule-doc/test-plugin.js +++ b/test/workspace/rule-doc/test-plugin.js @@ -1,47 +1,31 @@ 'use strict'; // Abbreviated example -const stylelint = /** @type {import('stylelint').StylelintPublicAPI} */ (require('stylelint')); +const stylelint = require('stylelint'); const ruleName = 'plugin/foo-bar'; const messages = stylelint.utils.ruleMessages(ruleName, { expected: 'Bar', }); -/** - * @typedef {import('postcss').Root} PostCSSRoot - * @typedef {import('stylelint').PostcssResult} PostCSSResult - */ +module.exports = stylelint.createPlugin(ruleName, (/** @type {any} */ primaryOption) => { + return (postcssRoot, postcssResult) => { + const validOptions = stylelint.utils.validateOptions(postcssResult, ruleName, { + actual: primaryOption, + }); -module.exports = stylelint.createPlugin( - ruleName, - // TODO: Workaround for bad typings upstream - /** @type {import('stylelint').StylelintRule} */ ( - /** @type {unknown} */ ( - (/** @type {any} */ primaryOption) => { - return ( - /** @type {PostCSSRoot} */ postcssRoot, - /** @type {PostCSSResult} */ postcssResult, - ) => { - const validOptions = stylelint.utils.validateOptions(postcssResult, ruleName, { - actual: primaryOption, - }); + if (!validOptions) { + return; + } - if (!validOptions) { - return; - } - - stylelint.utils.report({ - ruleName, - result: postcssResult, - message: messages.expected, - node: postcssRoot, - index: 5, - }); - }; - } - ) - ), -); + stylelint.utils.report({ + ruleName, + result: postcssResult, + message: messages.expected, + node: postcssRoot, + index: 5, + }); + }; +}); module.exports.messages = messages; diff --git a/test/workspace/utils.js b/test/workspace/utils.js index c744a091..633daab2 100644 --- a/test/workspace/utils.js +++ b/test/workspace/utils.js @@ -3,48 +3,19 @@ const { languages } = require('vscode'); const { URI } = require('vscode-uri'); -/** - * @typedef {import('vscode').Uri} Uri - * @typedef {import('vscode').Range} Range - * @typedef {import('vscode').Position} Position - * @typedef {import('vscode').Diagnostic} VSCodeDiagnostic - * @typedef {import('vscode').DiagnosticRelatedInformation} VSCodeDiagnosticRelatedInformation - * @typedef {import('vscode-languageserver').Diagnostic} LSPDiagnostic - * @typedef {import('vscode-languageserver').DiagnosticSeverity} DiagnosticSeverity - * @typedef {import('vscode-languageserver').CodeDescription} CodeDescription - * @typedef {import('vscode-languageserver').DiagnosticRelatedInformation} LSPDiagnosticRelatedInformation - * @typedef {{code?: string | number, codeDescription?: CodeDescription}} CodePart - */ - -/** - * @template {Record} T - * @template {keyof T} K - * @typedef {Partial & Pick} OptionalExcept - */ - -/** - * @typedef {OptionalExcept} PartialPosition - * @typedef {{ start: PartialPosition, end?: PartialPosition }} PartialRange - * @typedef {{ location: { range: PartialRange, uri: string }, message: string }} PartialRelatedInformation - * @typedef {Omit & - * { range: PartialRange, relatedInformation?: PartialRelatedInformation[] } - * } PartialLSPDiagnostic - */ - /** * Converts a VS Code diagnostic to a partial LSP diagnostic. - * @template {Record} T - * @param {VSCodeDiagnostic} message - * @returns {PartialLSPDiagnostic} + * @param {vscode.Diagnostic} message + * @returns {tests.Diagnostic} */ function normalizeDiagnostic(message) { const { code, codeDescription } = normalizeCode(message); - /** @type {PartialLSPDiagnostic} */ + /** @type {tests.Diagnostic} */ const diagnostic = { message: message.message, range: normalizeRange(message.range), - severity: /** @type {DiagnosticSeverity} */ (message.severity + 1), + severity: /** @type {lsp.DiagnosticSeverity} */ (message.severity + 1), }; if (code !== undefined) { @@ -71,19 +42,19 @@ function normalizeDiagnostic(message) { } /** - * @param {Uri} uri - * @returns {VSCodeDiagnostic[]} + * @param {vscode.Uri} uri + * @returns {vscode.Diagnostic[]} */ function getStylelintDiagnostics(uri) { return languages.getDiagnostics(uri).filter((d) => d.source === 'stylelint'); } /** - * @param {PartialRange} range - * @returns {PartialRange} + * @param {tests.Range} range + * @returns {tests.Range} */ function normalizeRange(range) { - /** @type {PartialRange} */ + /** @type {tests.Range} */ const obj = { start: { line: range.start.line, @@ -102,8 +73,8 @@ function normalizeRange(range) { } /** - * @param {VSCodeDiagnosticRelatedInformation[]} relatedInformation - * @returns {PartialRelatedInformation[]} + * @param {vscode.DiagnosticRelatedInformation[]} relatedInformation + * @returns {tests.DiagnosticRelatedInformation[]} */ function normalizeRelatedInformation(relatedInformation) { return relatedInformation.map(({ location, message }) => ({ @@ -116,8 +87,8 @@ function normalizeRelatedInformation(relatedInformation) { } /** - * @param {VSCodeDiagnostic} message - * @returns {CodePart} + * @param {vscode.Diagnostic} message + * @returns {tests.CodePart} */ function normalizeCode(message) { return !message.code || typeof message.code === 'string' || typeof message.code === 'number' diff --git a/tsconfig.json b/tsconfig.json index 4d95203e..36005196 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,6 +14,6 @@ "resolveJsonModule": true, "typeRoots": ["./types", "./node_modules/@types"] }, - "include": ["src/**/*", "test/**/*", "scripts/**/*"], + "include": ["src/**/*", "test/**/*", "scripts/**/*", "types/*.d.ts"], "exclude": ["**/coverage"] } diff --git a/types/imports.d.ts b/types/imports.d.ts new file mode 100644 index 00000000..5d3ddb05 --- /dev/null +++ b/types/imports.d.ts @@ -0,0 +1,19 @@ +import * as postcss from 'postcss'; +export import postcss = postcss; + +import * as stylelint from 'stylelint'; +export import stylelint = stylelint; + +import * as vscode from 'vscode'; +export import vscode = vscode; + +import * as vscodeLanguageServer from 'vscode-languageserver'; +export import vscodeLanguageServer = vscodeLanguageServer; + +import * as vscodeLanguageServerTextDocument from 'vscode-languageserver-textdocument'; +export import vscodeLanguageServerTextDocument = vscodeLanguageServerTextDocument; + +import * as vscodeLanguageServerTypes from 'vscode-languageserver-types'; +export import vscodeLanguageServerTypes = vscodeLanguageServerTypes; + +export as namespace imports; diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 00000000..c5bed005 --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,81 @@ +// Make module namespaces available globally +import postcss = imports.postcss; +import stylelint = imports.stylelint; +import vscode = imports.vscode; +import vscodeLanguageServer = imports.vscodeLanguageServer; +import vscodeLanguageServerTextDocument = imports.vscodeLanguageServerTextDocument; +import vscodeLanguageServerTypes = imports.vscodeLanguageServerTypes; + +/** + * Language Server Protocol and VS Code language server types. + */ +declare namespace lsp { + export import CodeDescription = vscodeLanguageServer.CodeDescription; + export import CompletionItem = vscodeLanguageServer.CompletionItem; + export import CompletionParams = vscodeLanguageServer.CompletionParams; + export import Connection = vscodeLanguageServer.Connection; + export import Diagnostic = vscodeLanguageServer.Diagnostic; + export import DiagnosticRelatedInformation = vscodeLanguageServer.DiagnosticRelatedInformation; + export import DiagnosticSeverity = vscodeLanguageServer.DiagnosticSeverity; + export import Disposable = vscodeLanguageServer.Disposable; + export import DocumentUri = vscodeLanguageServer.DocumentUri; + export import FormattingOptions = vscodeLanguageServer.FormattingOptions; + export import TextDocument = vscodeLanguageServerTextDocument.TextDocument; + export import URI = vscodeLanguageServerTypes.URI; +} + +type OptionalExcept, K extends keyof T> = Pick & + Partial>; + +/** + * Types used in tests. + */ +declare namespace tests { + type CodePart = { + code?: lsp.Diagnostic['code']; + codeDescription?: lsp.CodeDescription; + }; + type Position = OptionalExcept; + type Range = { start: Position; end?: Position }; + type DiagnosticRelatedInformation = { + location: { range: Range; uri: string }; + message: string; + }; + type Diagnostic = Omit & { + range: Range; + relatedInformation?: DiagnosticRelatedInformation[]; + }; +} + +type PackageManager = 'npm' | 'yarn' | 'pnpm'; + +type InvalidOptionError = Error & { reasons: string[] }; + +// TODO: Create type upstream +/** + * A Stylelint configuration error. Taken from + * https://github.com/stylelint/stylelint/blob/551dcb5/lib/utils/configurationError.js + */ +type ConfigurationError = Error & { code: 78 }; + +type RuleDocUrlProvider = (rule: string) => lsp.URI | null | undefined; + +type TracerFn = (message: string, verbose?: string) => void; + +type DisableReport = { + diagnostic: lsp.Diagnostic; + range: stylelint.DisableReportRange; +}; + +type StylelintVSCodeOptions = { + connection?: lsp.Connection; + packageManager?: PackageManager; + stylelintPath?: string; +}; + +type StylelintVSCodeResult = { + diagnostics: lsp.Diagnostic[]; + output?: string; + needlessDisables?: DisableReport[]; + invalidScopeDisables?: DisableReport[]; +}; diff --git a/types/stylelint/index.d.ts b/types/stylelint/index.d.ts deleted file mode 100644 index 6522858f..00000000 --- a/types/stylelint/index.d.ts +++ /dev/null @@ -1,326 +0,0 @@ -declare module 'stylelint' { - import { Comment, Result, Message, Root, Syntax, WarningOptions, Warning } from 'postcss'; - import { GlobbyOptions } from 'globby'; - import { cosmiconfig } from 'cosmiconfig'; - - export type Severity = 'warning' | 'error'; - - export type StylelintConfigExtends = string | Array; - export type StylelintConfigPlugins = string | Array; - export type StylelintConfigProcessor = string | [string, Object]; - export type StylelintConfigProcessors = string | Array; - export type StylelintConfigIgnoreFiles = string | Array; - export type StylelintConfigRuleSettings = - | null - | undefined - | NonNullable - | [NonNullable] - | [NonNullable, O]; - export type StylelintConfigRules = { - [ruleName: string]: StylelintConfigRuleSettings; - }; - export type StylelintConfigOverride = Pick< - StylelintConfig, - | 'plugins' - | 'pluginFunctions' - | 'processors' - | 'processorFunctions' - | 'rules' - | 'defaultSeverity' - > & { - files: string | string[]; - }; - - export type DisableOptions = { - except?: Array; - severity?: Severity; - }; - export type DisableSettings = StylelintConfigRuleSettings; - - export type StylelintConfig = { - extends?: StylelintConfigExtends; - plugins?: StylelintConfigPlugins; - pluginFunctions?: { - [pluginName: string]: Function; - }; - processors?: StylelintConfigProcessors; - processorFunctions?: Array; - ignoreFiles?: StylelintConfigIgnoreFiles; - ignorePatterns?: string; - rules?: StylelintConfigRules; - codeProcessors?: Array; - resultProcessors?: Array; - quiet?: boolean; - defaultSeverity?: Severity; - ignoreDisables?: DisableSettings; - reportNeedlessDisables?: DisableSettings; - reportInvalidScopeDisables?: DisableSettings; - reportDescriptionlessDisables?: DisableSettings; - overrides?: StylelintConfigOverride[]; - customSyntax?: CustomSyntax; - }; - - // A meta-type that returns a union over all properties of `T` whose values - // have type `U`. - type PropertyNamesOfType = { - [K in keyof T]-?: T[K] extends U ? K : never; - }[keyof T]; - export type DisablePropertyName = PropertyNamesOfType; - - // This type has the same properties as `CosmiconfigResult` from `cosmiconfig`. - export type StylelintCosmiconfigResult = { - config: StylelintConfig; - filepath: string; - isEmpty?: boolean; - } | null; - - export type DisabledRange = { - comment: Comment; - start: number; - strictStart: boolean; - end?: number; - strictEnd?: boolean; - rules?: string[]; - description?: string; - }; - - export type DisabledRangeObject = { - [ruleName: string]: Array; - }; - - export type DisabledWarning = { line: number; rule: string }; - - export type StylelintPostcssResult = { - ruleSeverities: { [k: string]: Severity }; - customMessages: { [k: string]: any }; - quiet?: boolean; - disabledRanges: DisabledRangeObject; - disabledWarnings?: DisabledWarning[]; - ignored?: boolean; - stylelintError?: boolean; - disableWritingFix?: boolean; - config?: StylelintConfig; - ruleDisableFix?: boolean; - }; - - type EmptyResult = { - root: { - nodes?: undefined; - source: { - lang?: undefined; - input: { - file?: string; - }; - }; - }; - messages: Message[]; - opts: undefined; - }; - - export type StylelintWarningOptions = WarningOptions & { - stylelintType?: string; - severity?: Severity; - rule?: string; - }; - - export type PostcssResult = (Result | EmptyResult) & { - stylelint: StylelintPostcssResult; - warn(message: string, options?: StylelintWarningOptions): void; - }; - - export type Formatter = ( - results: Array, - returnValue?: StylelintStandaloneReturnValue, - ) => string; - - export type FormatterIdentifier = - | 'compact' - | 'json' - | 'string' - | 'tap' - | 'unix' - | 'verbose' - | Formatter; - - export type CustomSyntax = string | Syntax; - - export type StylelintOptions = { - config?: StylelintConfig; - configFile?: string; - configBasedir?: string; - ignoreDisables?: boolean; - ignorePath?: string; - reportInvalidScopeDisables?: boolean; - reportNeedlessDisables?: boolean; - reportDescriptionlessDisables?: boolean; - syntax?: string; - customSyntax?: CustomSyntax; - fix?: boolean; - }; - - export type StylelintPluginContext = { - fix?: boolean | undefined; - newline?: string | undefined; - }; - - export type StylelintRuleMessages = Record string)>; - - export type StylelintRule

= (( - primaryOption: P, - secondaryOptions: Record, - context: StylelintPluginContext, - ) => (root: Root, result: PostcssResult) => Promise | void) & { - ruleName: string; - messages: StylelintRuleMessages; - primaryOptionArray?: boolean; - }; - - export type GetPostcssOptions = { - code?: string; - codeFilename?: string; - filePath?: string; - codeProcessors?: Array; - syntax?: string; - customSyntax?: CustomSyntax; - }; - - export type GetLintSourceOptions = GetPostcssOptions & { existingPostcssResult?: Result }; - - export type StylelintInternalApi = { - _options: StylelintStandaloneOptions; - _extendExplorer: ReturnType; - _specifiedConfigCache: Map>; - _postcssResultCache: Map; - - _getPostcssResult: (options?: GetPostcssOptions) => Promise; - _lintSource: (options: GetLintSourceOptions) => Promise; - _createStylelintResult: ( - postcssResult: PostcssResult, - filePath?: string, - ) => Promise; - - getConfigForFile: ( - searchPath?: string, - filePath?: string, - ) => Promise; - isPathIgnored: (s?: string) => Promise; - }; - - export type StylelintStandaloneOptions = { - files?: string | Array; - globbyOptions?: GlobbyOptions; - cache?: boolean; - cacheLocation?: string; - code?: string; - codeFilename?: string; - config?: StylelintConfig; - configFile?: string; - configBasedir?: string; - ignoreDisables?: boolean; - ignorePath?: string; - ignorePattern?: string[]; - reportDescriptionlessDisables?: boolean; - reportNeedlessDisables?: boolean; - reportInvalidScopeDisables?: boolean; - maxWarnings?: number; - /** @deprecated Use `customSyntax` instead. */ - syntax?: string; - customSyntax?: CustomSyntax; - formatter?: FormatterIdentifier; - disableDefaultIgnores?: boolean; - fix?: boolean; - allowEmptyInput?: boolean; - quiet?: boolean; - }; - - export type StylelintCssSyntaxError = { - column: number; - file?: string; - input: { - column: number; - file?: string; - line: number; - source: string; - }; - line: number; - message: string; - name: string; - reason: string; - source: string; - }; - - export type StylelintWarning = { - line: number; - column: number; - rule: string; - severity: Severity; - text: string; - stylelintType?: string; - }; - - export type StylelintResult = { - source?: string; - deprecations: Array<{ - text: string; - reference: string; - }>; - invalidOptionWarnings: Array<{ - text: string; - }>; - parseErrors: Array; - errored?: boolean; - warnings: Array; - ignored?: boolean; - _postcssResult?: PostcssResult; - }; - - export type DisableReportRange = { - rule: string; - start: number; - end?: number; - }; - - export type RangeType = DisabledRange & { used?: boolean }; - - export type StylelintDisableReportEntry = { - source?: string; - ranges: Array; - }; - - export type StylelintStandaloneReturnValue = { - results: Array; - errored: boolean; - output: any; - maxWarningsExceeded?: { - maxWarnings: number; - foundWarnings: number; - }; - reportedDisables: StylelintDisableOptionsReport; - descriptionlessDisables?: StylelintDisableOptionsReport; - needlessDisables?: StylelintDisableOptionsReport; - invalidScopeDisables?: StylelintDisableOptionsReport; - }; - - export type StylelintPublicAPI = { - lint: Function; - rules: { [k: string]: StylelintRule }; - formatters: { [k: string]: Formatter }; - createPlugin: ( - ruleName: string, - rule: StylelintRule, - ) => { ruleName: string; rule: StylelintRule }; - createLinter: Function; - utils: { - report: Function; - ruleMessages: Function; - validateOptions: Function; - checkAgainstRule: Function; - }; - }; - - export type StylelintDisableOptionsReport = Array; - - export type PostcssPluginOptions = - | Omit - | StylelintConfig; -} diff --git a/types/stylelint/version b/types/stylelint/version deleted file mode 100644 index 0ea783da..00000000 --- a/types/stylelint/version +++ /dev/null @@ -1 +0,0 @@ -git+https://git@github.com/stylelint/stylelint.git#585d28a64358a6b7c72d1bed4aaf849caa3bf181