From e12a9730f40232f73ff8ee21902b0862381f25ba Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Tue, 9 Sep 2025 11:32:10 +0000 Subject: [PATCH 01/18] ci:: wip windows support --- .github/workflows/NodeCI.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/NodeCI.yml b/.github/workflows/NodeCI.yml index 4100ec4f6..ed2da6fe5 100644 --- a/.github/workflows/NodeCI.yml +++ b/.github/workflows/NodeCI.yml @@ -51,7 +51,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest] + os: [ubuntu-latest, windows-latest] eslint: [8, 9] node: [18.x, 20.x, 22.x, latest] steps: @@ -84,7 +84,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest] + os: [ubuntu-latest, windows-latest] eslint: [9] node: [18, 20, 22] steps: @@ -115,7 +115,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest] + os: [ubuntu-latest, windows-latest] node: [18] steps: - name: Checkout From c8674400c6e40c78098a9a97aaa5fd72f82f20d9 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Tue, 9 Sep 2025 11:35:05 +0000 Subject: [PATCH 02/18] Fix formatting in NodeCI workflow for OS matrix --- .github/workflows/NodeCI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/NodeCI.yml b/.github/workflows/NodeCI.yml index ed2da6fe5..2409d4686 100644 --- a/.github/workflows/NodeCI.yml +++ b/.github/workflows/NodeCI.yml @@ -51,7 +51,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest,windows-latest] eslint: [8, 9] node: [18.x, 20.x, 22.x, latest] steps: From 20e061aeae4fbd1fbc90c614d49fc5e02c8a4449 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Thu, 11 Sep 2025 12:38:35 +0200 Subject: [PATCH 03/18] ci: improve consistency across runners with bash shell --- .github/workflows/NodeCI.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/NodeCI.yml b/.github/workflows/NodeCI.yml index 2409d4686..ba532e6b1 100644 --- a/.github/workflows/NodeCI.yml +++ b/.github/workflows/NodeCI.yml @@ -9,6 +9,10 @@ on: env: project_root_path: ./packages/eslint-plugin-svelte +defaults: + run: + shell: bash + jobs: lint: runs-on: ubuntu-latest @@ -51,7 +55,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest,windows-latest] + os: [ubuntu-latest, windows-latest] eslint: [8, 9] node: [18.x, 20.x, 22.x, latest] steps: From 4ab9d19c65d52bf0b44245868058978995248e66 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Thu, 11 Sep 2025 19:31:58 +0200 Subject: [PATCH 04/18] fix(tools): migrate to use crossplatform fileURLToPath --- packages/eslint-plugin-svelte/tools/update-meta.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin-svelte/tools/update-meta.ts b/packages/eslint-plugin-svelte/tools/update-meta.ts index 761ba2d0e..c35ff039b 100644 --- a/packages/eslint-plugin-svelte/tools/update-meta.ts +++ b/packages/eslint-plugin-svelte/tools/update-meta.ts @@ -1,10 +1,10 @@ -import path from 'path'; +import { fileURLToPath } from 'url'; import { name, version } from '../package.json'; import { getNewVersion } from './lib/changesets-util.js'; import { writeAndFormat } from './lib/write.js'; -const __dirname = path.dirname(new URL(import.meta.url).pathname); -const META_PATH = path.join(__dirname, '../src/meta.ts'); +const fileURL = new URL('../src/meta.ts', import.meta.url); +const META_PATH = fileURLToPath(fileURL); void main(); From 795d700eeb0a4b035f752a22b335e710e0ef3315 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Thu, 11 Sep 2025 19:38:11 +0200 Subject: [PATCH 05/18] fix(tests): change utils dir get to fileURLToPath --- packages/eslint-plugin-svelte/tests/utils/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin-svelte/tests/utils/utils.ts b/packages/eslint-plugin-svelte/tests/utils/utils.ts index 0b503dd88..ab4e42c6a 100644 --- a/packages/eslint-plugin-svelte/tests/utils/utils.ts +++ b/packages/eslint-plugin-svelte/tests/utils/utils.ts @@ -12,8 +12,9 @@ import * as svelteParser from 'svelte-eslint-parser'; import * as typescriptParser from '@typescript-eslint/parser'; import Module from 'module'; import globals from 'globals'; +import { fileURLToPath } from 'url'; -const __dirname = path.dirname(new URL(import.meta.url).pathname); +const __dirname = path.dirname(fileURLToPath(new URL(import.meta.url))); const require = Module.createRequire(import.meta.url); /** From 413eb46d5086b56ce8c7738051015130e8e21498 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Fri, 12 Sep 2025 00:02:50 +0200 Subject: [PATCH 06/18] fix(tests): normalize ruleName for crossplatform compatibility --- packages/eslint-plugin-svelte/tests/utils/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin-svelte/tests/utils/utils.ts b/packages/eslint-plugin-svelte/tests/utils/utils.ts index ab4e42c6a..ca9d89d1b 100644 --- a/packages/eslint-plugin-svelte/tests/utils/utils.ts +++ b/packages/eslint-plugin-svelte/tests/utils/utils.ts @@ -305,7 +305,7 @@ function writeFixtures( } function getConfig(ruleName: string, inputFile: string) { - const filename = inputFile.slice(inputFile.indexOf(ruleName)); + const filename = inputFile.slice(inputFile.indexOf(path.normalize(ruleName))); const code = fs.readFileSync(inputFile, 'utf8'); let config; let configFile = [ From e7a904d37320c46b53855402c0763ae4a80c2955 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Fri, 12 Sep 2025 00:10:11 +0200 Subject: [PATCH 07/18] fix(tests): use fileURLToPath to get dir name --- .../original-tests/no-unnecessary-condition.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin-svelte/tests/src/rules/@typescript-eslint/original-tests/no-unnecessary-condition.ts b/packages/eslint-plugin-svelte/tests/src/rules/@typescript-eslint/original-tests/no-unnecessary-condition.ts index 39cf721c2..b6c06624c 100644 --- a/packages/eslint-plugin-svelte/tests/src/rules/@typescript-eslint/original-tests/no-unnecessary-condition.ts +++ b/packages/eslint-plugin-svelte/tests/src/rules/@typescript-eslint/original-tests/no-unnecessary-condition.ts @@ -2,13 +2,15 @@ // https://github.com/typescript-eslint/typescript-eslint/blob/78467fc1bde9bd2db1e08b3d19f151f4adaff8a9/packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts /* eslint func-style: off, eslint-plugin/consistent-output: off -- respect original */ import * as path from 'path'; +import { fileURLToPath } from 'url'; import { RuleTester } from '../../../../utils/eslint-compat.js'; import type * as eslint from 'eslint'; import * as typescriptParser from '@typescript-eslint/parser'; import rule from '../../../../../src/rules/@typescript-eslint/no-unnecessary-condition.js'; -const __dirname = path.dirname(new URL(import.meta.url).pathname); +const url = new URL(import.meta.url); +const __dirname = path.dirname(fileURLToPath(url)); function getFixturesRootDir(): string { return path.join(__dirname, 'fixtures'); From 4c6511570c3f916c357f96d070cc948c1259e900 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Fri, 12 Sep 2025 00:45:12 +0200 Subject: [PATCH 08/18] fix(rules): no-unused-props normalize TS declaraction fileName --- packages/eslint-plugin-svelte/src/rules/no-unused-props.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts b/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts index 718032c60..4a4cef8fa 100644 --- a/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts +++ b/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts @@ -4,6 +4,7 @@ import type { TSESTree } from '@typescript-eslint/types'; import type ts from 'typescript'; import { findVariable } from '../utils/ast-utils.js'; import { toRegExp } from '../utils/regexp.js'; +import { normalize } from 'path'; type PropertyPathArray = string[]; type DeclaredPropertyNames = Set<{ originalName: string; aliasName: string }>; @@ -122,7 +123,7 @@ export default createRule('no-unused-props', { const declarations = symbol.getDeclarations(); if (!declarations || declarations.length === 0) return false; - return declarations.every((decl) => decl.getSourceFile().fileName === fileName); + return declarations.every((decl) => normalize(decl.getSourceFile().fileName) === fileName); } /** From 209a606b872ddce8f25b6fca3c7cbfb9d023fda1 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Fri, 12 Sep 2025 00:57:48 +0200 Subject: [PATCH 09/18] fix(rules): valid-style-parse cross platform wip --- packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts | 5 +++-- .../valid-style-parse/invalid/invalid-css01-errors.yaml | 2 +- .../valid-style-parse/invalid/invalid-scss01-errors.yaml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts b/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts index f92b76cdb..b1371ea5a 100644 --- a/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts +++ b/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts @@ -1,4 +1,5 @@ import { createRule } from '../utils/index.js'; +import path from 'path'; export default createRule('valid-style-parse', { meta: { @@ -16,7 +17,7 @@ export default createRule('valid-style-parse', { if (!sourceCode.parserServices.isSvelte) { return {}; } - const cwd = `${context.cwd ?? process.cwd()}/`; + const cwd = `${context.cwd ?? process.cwd()}${path.sep}`; return { SvelteStyleElement(node) { @@ -24,7 +25,7 @@ export default createRule('valid-style-parse', { if (styleContext.status === 'parse-error') { context.report({ loc: node.loc, - message: `Error parsing style element. Error message: "${styleContext.error.message.replace(cwd, '')}"` + message: `Error parsing style element. Error message: "${path.posix.normalize(styleContext.error.message.replace(cwd, ''))}"` }); } if (styleContext.status === 'unknown-lang') { diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-errors.yaml index fade06900..ec7d480a4 100644 --- a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-errors.yaml +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-errors.yaml @@ -1,5 +1,5 @@ - message: 'Error parsing style element. Error message: - "tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-input.svelte:4:11: + "tests\fixtures\rules\valid-style-parse\invalid\invalid-css01-input.svelte:4:11: Unknown word .div-class/35"' line: 7 column: 1 diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-errors.yaml index 506757636..382364694 100644 --- a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-errors.yaml +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-errors.yaml @@ -1,5 +1,5 @@ - message: 'Error parsing style element. Error message: - "tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-input.svelte:4:11: + "tests\fixtures\rules\valid-style-parse\invalid\invalid-scss01-input.svelte:4:11: Unknown word .div-class/35"' line: 7 column: 1 From 93860ad3b940241eccecb01624e89cb7418f0823 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Fri, 12 Sep 2025 01:11:07 +0200 Subject: [PATCH 10/18] fix(rules): force valid-style-parse to use forward slash on windows --- packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts | 5 ++++- .../valid-style-parse/invalid/invalid-css01-errors.yaml | 2 +- .../valid-style-parse/invalid/invalid-scss01-errors.yaml | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts b/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts index b1371ea5a..25fef8cd1 100644 --- a/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts +++ b/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts @@ -23,9 +23,12 @@ export default createRule('valid-style-parse', { SvelteStyleElement(node) { const styleContext = sourceCode.parserServices.getStyleContext!(); if (styleContext.status === 'parse-error') { + let message = styleContext.error.message.replace(cwd, ''); + if (path.sep === '\\') message = message.replace(/\\/g, '/'); + context.report({ loc: node.loc, - message: `Error parsing style element. Error message: "${path.posix.normalize(styleContext.error.message.replace(cwd, ''))}"` + message: `Error parsing style element. Error message: "${message}"` }); } if (styleContext.status === 'unknown-lang') { diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-errors.yaml index ec7d480a4..fade06900 100644 --- a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-errors.yaml +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-errors.yaml @@ -1,5 +1,5 @@ - message: 'Error parsing style element. Error message: - "tests\fixtures\rules\valid-style-parse\invalid\invalid-css01-input.svelte:4:11: + "tests/fixtures/rules/valid-style-parse/invalid/invalid-css01-input.svelte:4:11: Unknown word .div-class/35"' line: 7 column: 1 diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-errors.yaml b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-errors.yaml index 382364694..506757636 100644 --- a/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-errors.yaml +++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-errors.yaml @@ -1,5 +1,5 @@ - message: 'Error parsing style element. Error message: - "tests\fixtures\rules\valid-style-parse\invalid\invalid-scss01-input.svelte:4:11: + "tests/fixtures/rules/valid-style-parse/invalid/invalid-scss01-input.svelte:4:11: Unknown word .div-class/35"' line: 7 column: 1 From b58e013981620ee89a6d1571ef93913a52a5ba12 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Fri, 12 Sep 2025 01:17:22 +0200 Subject: [PATCH 11/18] chore: force '\n' (lf) as end of line character --- .gitattributes | 1 + .vscode/settings.json | 3 ++- prettier.config.cjs | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..94f480de9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 04456da0f..91ca0cdc9 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,5 +10,6 @@ "typescript.tsdk": "node_modules/typescript/lib", "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" - } + }, + "files.eol": "\n" } diff --git a/prettier.config.cjs b/prettier.config.cjs index f4831cf13..e745cad5e 100644 --- a/prettier.config.cjs +++ b/prettier.config.cjs @@ -1,6 +1,7 @@ 'use strict'; module.exports = { + endOfLine: 'lf', useTabs: true, singleQuote: true, trailingComma: 'none', From 4463ddea6a96a0a40a2740f7156af00c9b08f184 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Fri, 12 Sep 2025 01:29:34 +0200 Subject: [PATCH 12/18] chore: add comments to explain changes --- .github/workflows/NodeCI.yml | 2 ++ packages/eslint-plugin-svelte/src/rules/no-unused-props.ts | 1 + packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts | 2 ++ packages/eslint-plugin-svelte/tests/utils/utils.ts | 1 + 4 files changed, 6 insertions(+) diff --git a/.github/workflows/NodeCI.yml b/.github/workflows/NodeCI.yml index ba532e6b1..2327bcd0c 100644 --- a/.github/workflows/NodeCI.yml +++ b/.github/workflows/NodeCI.yml @@ -11,6 +11,8 @@ env: defaults: run: + # Setting every runner to bash simplifies command calls; + # plus, every platform supports it shell: bash jobs: diff --git a/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts b/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts index 4a4cef8fa..41e198da4 100644 --- a/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts +++ b/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts @@ -123,6 +123,7 @@ export default createRule('no-unused-props', { const declarations = symbol.getDeclarations(); if (!declarations || declarations.length === 0) return false; + // TypeScript declaration file name is normalized to support Windows style paths return declarations.every((decl) => normalize(decl.getSourceFile().fileName) === fileName); } diff --git a/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts b/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts index 25fef8cd1..42c7c8179 100644 --- a/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts +++ b/packages/eslint-plugin-svelte/src/rules/valid-style-parse.ts @@ -23,6 +23,8 @@ export default createRule('valid-style-parse', { SvelteStyleElement(node) { const styleContext = sourceCode.parserServices.getStyleContext!(); if (styleContext.status === 'parse-error') { + // This will replace backslashes to forward slashes only + // if Node.js reports Windows style path separators let message = styleContext.error.message.replace(cwd, ''); if (path.sep === '\\') message = message.replace(/\\/g, '/'); diff --git a/packages/eslint-plugin-svelte/tests/utils/utils.ts b/packages/eslint-plugin-svelte/tests/utils/utils.ts index ca9d89d1b..340203843 100644 --- a/packages/eslint-plugin-svelte/tests/utils/utils.ts +++ b/packages/eslint-plugin-svelte/tests/utils/utils.ts @@ -305,6 +305,7 @@ function writeFixtures( } function getConfig(ruleName: string, inputFile: string) { + // ruleName is normalized to support Windows style paths const filename = inputFile.slice(inputFile.indexOf(path.normalize(ruleName))); const code = fs.readFileSync(inputFile, 'utf8'); let config; From 8cb54c67b2f36f202aef010499e0770bf3624e00 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Thu, 18 Sep 2025 18:19:45 +0200 Subject: [PATCH 13/18] fix(tools): migrate update scripts to cross platform URL's --- .../tools/lib/changesets-util.ts | 6 +++--- .../tools/lib/load-rules.ts | 15 +++++++-------- .../eslint-plugin-svelte/tools/lib/write.ts | 17 ++++++++++------- .../tools/update-docs-rules-index.ts | 7 ++----- .../eslint-plugin-svelte/tools/update-docs.ts | 13 +++++-------- .../eslint-plugin-svelte/tools/update-meta.ts | 6 ++---- .../tools/update-readme.ts | 19 +++++++++---------- .../tools/update-rule-types.ts | 5 +---- .../tools/update-rules.ts | 7 ++----- .../tools/update-rulesets.ts | 15 +++++++-------- .../tools/update-types-for-node.ts | 14 +++++--------- 11 files changed, 53 insertions(+), 71 deletions(-) diff --git a/packages/eslint-plugin-svelte/tools/lib/changesets-util.ts b/packages/eslint-plugin-svelte/tools/lib/changesets-util.ts index a714cde82..a376dfc1a 100644 --- a/packages/eslint-plugin-svelte/tools/lib/changesets-util.ts +++ b/packages/eslint-plugin-svelte/tools/lib/changesets-util.ts @@ -1,11 +1,11 @@ import getReleasePlan from '@changesets/get-release-plan'; -import path from 'path'; +import { fileURLToPath } from 'url'; -const __dirname = path.dirname(new URL(import.meta.url).pathname); +const cwdURL = new URL('../../../..', import.meta.url); /** Get new version string from changesets */ export async function getNewVersion(): Promise { - const releasePlan = await getReleasePlan(path.resolve(__dirname, '../../../..')); + const releasePlan = await getReleasePlan(fileURLToPath(cwdURL)); return releasePlan.releases.find(({ name }) => name === 'eslint-plugin-svelte')!.newVersion; } diff --git a/packages/eslint-plugin-svelte/tools/lib/load-rules.ts b/packages/eslint-plugin-svelte/tools/lib/load-rules.ts index b6ecdeff4..4a80333d6 100644 --- a/packages/eslint-plugin-svelte/tools/lib/load-rules.ts +++ b/packages/eslint-plugin-svelte/tools/lib/load-rules.ts @@ -1,18 +1,18 @@ import path from 'path'; import fs from 'fs'; import type { RuleModule } from '../../src/types.js'; +import util from 'util'; -const __dirname = path.dirname(new URL(import.meta.url).pathname); +const rulesLibRootURL = new URL('../../src/rules/', import.meta.url); /** * Get the all rules * @returns {Array} The all rules */ async function readRules() { - const rulesLibRoot = path.resolve(__dirname, '../../src/rules'); const rules: RuleModule[] = []; for (const name of iterateTsFiles()) { - const module = await import(path.join(rulesLibRoot, name)); + const module = await import(new URL(name, rulesLibRootURL).href); const rule: RuleModule = module && module.default; if (!rule || typeof rule.create !== 'function') { continue; @@ -27,8 +27,7 @@ export const rules = await readRules(); /** Iterate ts files */ function* iterateTsFiles() { - const rulesLibRoot = path.resolve(__dirname, '../../src/rules'); - const files = fs.readdirSync(rulesLibRoot); + const files = fs.readdirSync(rulesLibRootURL); while (files.length) { const file = files.shift()!; @@ -36,10 +35,10 @@ function* iterateTsFiles() { yield file; continue; } - const filePath = path.join(rulesLibRoot, file); - if (!fs.statSync(filePath).isDirectory()) { + const filePathURL = new URL(file, rulesLibRootURL); + if (!fs.statSync(filePathURL).isDirectory()) { continue; } - files.unshift(...fs.readdirSync(filePath).map((n) => path.join(file, n))); + files.unshift(...fs.readdirSync(filePathURL).map((n) => path.join(file, n))); } } diff --git a/packages/eslint-plugin-svelte/tools/lib/write.ts b/packages/eslint-plugin-svelte/tools/lib/write.ts index d35f8d0cb..853c409eb 100644 --- a/packages/eslint-plugin-svelte/tools/lib/write.ts +++ b/packages/eslint-plugin-svelte/tools/lib/write.ts @@ -1,28 +1,31 @@ import fs from 'fs'; import { ESLint } from 'eslint'; import prettier from 'prettier'; +import { fileURLToPath } from 'url'; /** * Write file and format it with ESLint */ -export function writeAndFormat(fileName: string, content: string): Promise { - fs.writeFileSync(fileName, content, 'utf8'); +export async function writeAndFormat(fileURL: URL, content: string): Promise { + fs.writeFileSync(fileURL, content, 'utf8'); + + const filePath = fileURLToPath(fileURL); return prettier - .resolveConfig(fileName) + .resolveConfig(fileURL) .then((prettierrc) => { if (!prettierrc) { return content; } - return prettier.format(content, { filepath: fileName, ...prettierrc }); + return prettier.format(content, { filepath: filePath, ...prettierrc }); }) .then((formatted) => { - fs.writeFileSync(fileName, formatted, 'utf8'); + fs.writeFileSync(fileURL, formatted, 'utf8'); const eslint = new ESLint({ fix: true }); - return eslint.lintText(formatted, { filePath: fileName }); + return eslint.lintText(formatted, { filePath }); }) .then(([result]) => { if (result.output) { - fs.writeFileSync(fileName, result.output, 'utf8'); + fs.writeFileSync(fileURL, result.output, 'utf8'); } }); } diff --git a/packages/eslint-plugin-svelte/tools/update-docs-rules-index.ts b/packages/eslint-plugin-svelte/tools/update-docs-rules-index.ts index ae38b30c0..19756c824 100644 --- a/packages/eslint-plugin-svelte/tools/update-docs-rules-index.ts +++ b/packages/eslint-plugin-svelte/tools/update-docs-rules-index.ts @@ -1,13 +1,10 @@ -import path from 'path'; import renderRulesTableContent from './render-rules.js'; import { writeAndFormat } from './lib/write.js'; -const __dirname = path.dirname(new URL(import.meta.url).pathname); - // ----------------------------------------------------------------------------- -const readmeFilePath = path.resolve(__dirname, '../../../docs/rules.md'); +const readmeFileURL = new URL('../../../docs/rules.md', import.meta.url); void writeAndFormat( - readmeFilePath, + readmeFileURL, `--- sidebarDepth: 0 --- diff --git a/packages/eslint-plugin-svelte/tools/update-docs.ts b/packages/eslint-plugin-svelte/tools/update-docs.ts index 1f0e52e3a..5f5ef311d 100644 --- a/packages/eslint-plugin-svelte/tools/update-docs.ts +++ b/packages/eslint-plugin-svelte/tools/update-docs.ts @@ -1,12 +1,9 @@ -import path from 'path'; import fs from 'fs'; import { rules } from '../src/utils/rules.js'; import type { RuleModule } from '../src/types.js'; import { getNewVersion } from './lib/changesets-util.js'; import { writeAndFormat } from './lib/write.js'; -const __dirname = path.dirname(new URL(import.meta.url).pathname); - function formatItems(items: string[]) { if (items.length <= 2) { return items.join(' and '); @@ -21,7 +18,7 @@ function yamlValue(val: unknown) { return val; } -const ROOT = path.resolve(__dirname, '../../../docs/rules'); +const ROOT_URL = new URL('../../../docs/rules/', import.meta.url); function pickSince(content: string): string | null | Promise { const fileIntro = /^---\n((?:.*\n)+)---\n*/.exec(content); @@ -46,7 +43,7 @@ function pickSince(content: string): string | null | Promise { class DocFile { private readonly rule: RuleModule; - private readonly filePath: string; + private readonly fileURL: URL; private content: string; @@ -54,8 +51,8 @@ class DocFile { public constructor(rule: RuleModule) { this.rule = rule; - this.filePath = path.join(ROOT, `${rule.meta.docs.ruleName}.md`); - this.content = fs.readFileSync(this.filePath, 'utf8'); + this.fileURL = new URL(`./${rule.meta.docs.ruleName}.md`, ROOT_URL); + this.content = fs.readFileSync(this.fileURL, 'utf8'); this.since = pickSince(this.content); } @@ -221,7 +218,7 @@ ${ public async write() { this.content = this.content.replace(/\r?\n/gu, '\n'); - await writeAndFormat(this.filePath, this.content); + await writeAndFormat(this.fileURL, this.content); } } diff --git a/packages/eslint-plugin-svelte/tools/update-meta.ts b/packages/eslint-plugin-svelte/tools/update-meta.ts index c35ff039b..3eaff026f 100644 --- a/packages/eslint-plugin-svelte/tools/update-meta.ts +++ b/packages/eslint-plugin-svelte/tools/update-meta.ts @@ -1,17 +1,15 @@ -import { fileURLToPath } from 'url'; import { name, version } from '../package.json'; import { getNewVersion } from './lib/changesets-util.js'; import { writeAndFormat } from './lib/write.js'; -const fileURL = new URL('../src/meta.ts', import.meta.url); -const META_PATH = fileURLToPath(fileURL); +const META_URL = new URL('../src/meta.ts', import.meta.url); void main(); /** main */ async function main() { await writeAndFormat( - META_PATH, + META_URL, `/* * IMPORTANT! * This file has been automatically generated, diff --git a/packages/eslint-plugin-svelte/tools/update-readme.ts b/packages/eslint-plugin-svelte/tools/update-readme.ts index 48e844061..f8b0ec816 100644 --- a/packages/eslint-plugin-svelte/tools/update-readme.ts +++ b/packages/eslint-plugin-svelte/tools/update-readme.ts @@ -1,27 +1,26 @@ -import path from 'path'; import fs from 'fs'; import renderRulesTableContent from './render-rules.js'; import { writeAndFormat } from './lib/write.js'; -const __dirname = path.dirname(new URL(import.meta.url).pathname); +const rootURL = new URL('../../../', import.meta.url); const insertText = `\n${renderRulesTableContent( (name) => `https://sveltejs.github.io/eslint-plugin-svelte/rules/${name}/` )}\n`; -const readmeFilePath = path.resolve(__dirname, '../../../README.md'); +const readmeFileURL = new URL('README.md', rootURL); const newReadme = fs - .readFileSync(readmeFilePath, 'utf8') + .readFileSync(readmeFileURL, 'utf8') .replace( /[\s\S]*/u, `${insertText.replace(/\$/g, '$$$$')}` ); -void writeAndFormat(readmeFilePath, newReadme); +void writeAndFormat(readmeFileURL, newReadme); -const docsReadmeFilePath = path.resolve(__dirname, '../../../docs/README.md'); +const docsReadmeFileURL = new URL('./docs/README.md', rootURL); void writeAndFormat( - docsReadmeFilePath, + docsReadmeFileURL, `--- title: "eslint-plugin-svelte" --- @@ -65,12 +64,12 @@ ${newReadme .replace(/\n{3,}/gu, '\n\n')}` ); -const docsUserGuideFilePath = path.resolve(__dirname, '../../../docs/user-guide.md'); +const docsUserGuideFileURL = new URL('./docs/user-guide.md', rootURL); -const docsUserGuide = fs.readFileSync(docsUserGuideFilePath, 'utf8'); +const docsUserGuide = fs.readFileSync(docsUserGuideFileURL, 'utf8'); void writeAndFormat( - docsUserGuideFilePath, + docsUserGuideFileURL, docsUserGuide .replace( /[\s\S]*/u, diff --git a/packages/eslint-plugin-svelte/tools/update-rule-types.ts b/packages/eslint-plugin-svelte/tools/update-rule-types.ts index c360a2eb0..164b42211 100644 --- a/packages/eslint-plugin-svelte/tools/update-rule-types.ts +++ b/packages/eslint-plugin-svelte/tools/update-rule-types.ts @@ -1,9 +1,6 @@ import fs from 'fs'; -import path from 'path'; import plugin from '../src/index.js'; -const __dirname = path.dirname(new URL(import.meta.url).pathname); - void main(); async function main() { @@ -13,7 +10,7 @@ async function main() { const ruleTypes = await pluginsToRulesDTS({ svelte: plugin }); void fs.writeFileSync( - path.join(__dirname, '../src/rule-types.ts'), + new URL('../src/rule-types.ts', import.meta.url), `// IMPORTANT! // This file has been automatically generated, // in order to update its content execute "pnpm run update" diff --git a/packages/eslint-plugin-svelte/tools/update-rules.ts b/packages/eslint-plugin-svelte/tools/update-rules.ts index 1aa9c7fbb..ce562c526 100644 --- a/packages/eslint-plugin-svelte/tools/update-rules.ts +++ b/packages/eslint-plugin-svelte/tools/update-rules.ts @@ -1,10 +1,7 @@ -import path from 'path'; // import eslint from "eslint" import { rules } from './lib/load-rules.js'; import { writeAndFormat } from './lib/write.js'; -const __dirname = path.dirname(new URL(import.meta.url).pathname); - /** * Convert text to camelCase */ @@ -41,7 +38,7 @@ export const rules = [ ] as RuleModule[] `; -const filePath = path.resolve(__dirname, '../src/utils/rules.ts'); +const fileURL = new URL('../src/utils/rules.ts', import.meta.url); // Update file. -void writeAndFormat(filePath, content); +void writeAndFormat(fileURL, content); diff --git a/packages/eslint-plugin-svelte/tools/update-rulesets.ts b/packages/eslint-plugin-svelte/tools/update-rulesets.ts index cdbcf4bfd..b37c62fbc 100644 --- a/packages/eslint-plugin-svelte/tools/update-rulesets.ts +++ b/packages/eslint-plugin-svelte/tools/update-rulesets.ts @@ -1,8 +1,7 @@ -import path from 'path'; import { rules } from './lib/load-rules.js'; import { writeAndFormat } from './lib/write.js'; -const __dirname = path.dirname(new URL(import.meta.url).pathname); +const flatConfigFolderURL = new URL('../src/configs/flat/', import.meta.url); // ------------------ // Flat Config @@ -79,10 +78,10 @@ const config: Linter.Config[] = [ export default config `; -const baseFilePath = path.resolve(__dirname, '../src/configs/flat/base.ts'); +const baseFileURL = new URL('base.ts', flatConfigFolderURL); // Update file. -void writeAndFormat(baseFilePath, baseContent); +void writeAndFormat(baseFileURL, baseContent); const recommendedContent = `/* * IMPORTANT! @@ -110,10 +109,10 @@ const config: Linter.Config[] = [ export default config `; -const recommendedFilePath = path.resolve(__dirname, '../src/configs/flat/recommended.ts'); +const recommendedFileURL = new URL('recommended.ts', flatConfigFolderURL); // Update file. -void writeAndFormat(recommendedFilePath, recommendedContent); +void writeAndFormat(recommendedFileURL, recommendedContent); const prettierContent = `/* * IMPORTANT! @@ -138,7 +137,7 @@ const config: Linter.Config[] = [ export default config `; -const prettierFilePath = path.resolve(__dirname, '../src/configs/flat/prettier.ts'); +const prettierFileURL = new URL('prettier.ts', flatConfigFolderURL); // Update file. -void writeAndFormat(prettierFilePath, prettierContent); +void writeAndFormat(prettierFileURL, prettierContent); diff --git a/packages/eslint-plugin-svelte/tools/update-types-for-node.ts b/packages/eslint-plugin-svelte/tools/update-types-for-node.ts index 85b1bf55c..aab8bd521 100644 --- a/packages/eslint-plugin-svelte/tools/update-types-for-node.ts +++ b/packages/eslint-plugin-svelte/tools/update-types-for-node.ts @@ -1,15 +1,11 @@ import { AST_NODE_TYPES } from '@typescript-eslint/types'; import { parseForESLint } from 'svelte-eslint-parser'; -import path from 'path'; import { writeAndFormat } from './lib/write.js'; -const __dirname = path.dirname(new URL(import.meta.url).pathname); +const sourceFolderURL = new URL('../src/', import.meta.url); -// import { fileURLToPath } from "url" -// const filename = fileURLToPath(import.meta.url) -const dirname = __dirname; // path.dirname(filename) -const typesForNodeFilename = path.join(dirname, '../src/types-for-node.ts'); -const estreeFilename = path.join(dirname, '../src/type-defs/estree.d.ts'); +const typesForNodeFileURL = new URL('types-for-node.ts', sourceFolderURL); +const estreeFileURL = new URL('type-defs/estree.d.ts', sourceFolderURL); const { visitorKeys } = parseForESLint(''); const esNextNodeNames = ['Decorator', 'ImportAttribute', 'StaticBlock']; @@ -119,5 +115,5 @@ for (const nodeType of svelteNodeNames.filter((k) => !esSvelteNodeNames.includes typesForNodeCode.push(`}`); estreeCode.push(`}`); -void writeAndFormat(typesForNodeFilename, typesForNodeCode.join('\n')); -void writeAndFormat(estreeFilename, estreeCode.join('\n')); +void writeAndFormat(typesForNodeFileURL, typesForNodeCode.join('\n')); +void writeAndFormat(estreeFileURL, estreeCode.join('\n')); From 9d9315990ec05d47881199ddaa2574c5cc4ba817 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Thu, 18 Sep 2025 20:35:23 +0200 Subject: [PATCH 14/18] fix(tools): new-rule.ts cross platform path handling --- .../eslint-plugin-svelte/tools/new-rule.ts | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/eslint-plugin-svelte/tools/new-rule.ts b/packages/eslint-plugin-svelte/tools/new-rule.ts index 650401474..7df4ce2a2 100644 --- a/packages/eslint-plugin-svelte/tools/new-rule.ts +++ b/packages/eslint-plugin-svelte/tools/new-rule.ts @@ -1,11 +1,8 @@ import path from 'node:path'; import fs from 'node:fs'; import cp from 'node:child_process'; -import url from 'node:url'; import { writeAndFormat } from './lib/write.js'; - -const __filename = url.fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); +import { fileURLToPath } from 'node:url'; const logger = console; @@ -21,39 +18,39 @@ void (async ([ruleId, ...args]) => { process.exitCode = 1; return; } - const utilsPath = path.resolve(__dirname, `../src/utils/index.ts`); - const testUtilsPath = path.resolve(__dirname, `../tests/utils/utils.ts`); + const utilsURL = new URL(`../src/utils/index.ts`, import.meta.url); + const testUtilsURL = new URL(`../tests/utils/utils.ts`, import.meta.url); - const ruleFile = path.resolve(__dirname, `../src/rules/${ruleId}.ts`); - const testFile = path.resolve(__dirname, `../tests/src/rules/${ruleId}.ts`); - const docFile = path.resolve(__dirname, `../../../docs/rules/${ruleId}.md`); - const fixturesRoot = path.resolve(__dirname, `../tests/fixtures/rules/${ruleId}/`); + const ruleFileURL = new URL(`../src/rules/${ruleId}.ts`, import.meta.url); + const testFileURL = new URL(`../tests/src/rules/${ruleId}.ts`, import.meta.url); + const docFileURL = new URL(`../../../docs/rules/${ruleId}.md`, import.meta.url); + const fixturesRootURL = new URL(`../tests/fixtures/rules/${ruleId}/`, import.meta.url); try { - fs.mkdirSync(path.dirname(ruleFile), { recursive: true }); + fs.mkdirSync(new URL('./', ruleFileURL), { recursive: true }); } catch { // ignore } try { - fs.mkdirSync(path.dirname(testFile), { recursive: true }); + fs.mkdirSync(new URL('./', testFileURL), { recursive: true }); } catch { // ignore } try { - fs.mkdirSync(path.dirname(docFile), { recursive: true }); + fs.mkdirSync(new URL('./', docFileURL), { recursive: true }); } catch { // ignore } try { - fs.mkdirSync(path.resolve(fixturesRoot, 'valid'), { recursive: true }); - fs.mkdirSync(path.resolve(fixturesRoot, 'invalid'), { recursive: true }); + fs.mkdirSync(new URL('./valid', fixturesRootURL), { recursive: true }); + fs.mkdirSync(new URL('./invalid', fixturesRootURL), { recursive: true }); } catch { // ignore } await writeAndFormat( - ruleFile, + ruleFileURL, `import { AST } from 'svelte-eslint-parser'; -import { createRule } from '${getModulePath(ruleFile, utilsPath)}'; +import { createRule } from '${getModulePath(ruleFileURL, utilsURL)}'; export default createRule('${ruleId}', { meta: { @@ -74,10 +71,10 @@ export default createRule('${ruleId}', { ` ); await writeAndFormat( - testFile, + testFileURL, `import { RuleTester } from '../../utils/eslint-compat.js'; -import rule from '${getModulePath(testFile, ruleFile)}'; -import { loadTestCases } from '${getModulePath(testFile, testUtilsPath)}'; +import rule from '${getModulePath(testFileURL, ruleFileURL)}'; +import { loadTestCases } from '${getModulePath(testFileURL, testUtilsURL)}'; const tester = new RuleTester({ languageOptions: { @@ -90,7 +87,7 @@ tester.run('${ruleId}', rule as any, loadTestCases('${ruleId}')); ` ); await writeAndFormat( - docFile, + docFileURL, `# (svelte/${ruleId}) > description @@ -139,17 +136,20 @@ This rule reports ???. try { // Use code -v to know if vscode is installed and do not print anything to the console cp.execSync('code -v', { stdio: 'ignore' }); - cp.execSync(`code "${ruleFile}"`); - cp.execSync(`code "${testFile}"`); - cp.execSync(`code "${docFile}"`); + cp.execSync(`code "${fileURLToPath(ruleFileURL)}"`); + cp.execSync(`code "${fileURLToPath(testFileURL)}"`); + cp.execSync(`code "${fileURLToPath(docFileURL)}"`); } catch { logger.error('Unable to find code command. Will not open files with VSCode.'); } })(process.argv.slice(2)); /** Get module path */ -function getModulePath(from: string, module: string): string { - return path.relative(path.dirname(from), module).replace(/.ts$/u, '.js'); +function getModulePath(from: URL, module: URL): string { + const fromDir = fileURLToPath(new URL('./', from)); + const modulePath = fileURLToPath(module); + + return path.relative(fromDir, modulePath).replace(/\\/g, '/').replace(/.ts$/u, '.js'); } /** Argument parsing */ From 7d45a98aa1bd40b9ff5217754013d9bb4cd8799a Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Thu, 18 Sep 2025 20:43:45 +0200 Subject: [PATCH 15/18] chore(tools): remove unused import --- packages/eslint-plugin-svelte/tools/lib/load-rules.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/eslint-plugin-svelte/tools/lib/load-rules.ts b/packages/eslint-plugin-svelte/tools/lib/load-rules.ts index 4a80333d6..15c4b1e0d 100644 --- a/packages/eslint-plugin-svelte/tools/lib/load-rules.ts +++ b/packages/eslint-plugin-svelte/tools/lib/load-rules.ts @@ -1,7 +1,6 @@ import path from 'path'; import fs from 'fs'; import type { RuleModule } from '../../src/types.js'; -import util from 'util'; const rulesLibRootURL = new URL('../../src/rules/', import.meta.url); From e2ebcd5a5fa14ca8f85a67bf7ab41259e32aa087 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Thu, 18 Sep 2025 20:49:59 +0200 Subject: [PATCH 16/18] chore: add changeset --- .changeset/icy-rocks-camp.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .changeset/icy-rocks-camp.md diff --git a/.changeset/icy-rocks-camp.md b/.changeset/icy-rocks-camp.md new file mode 100644 index 000000000..395051607 --- /dev/null +++ b/.changeset/icy-rocks-camp.md @@ -0,0 +1,8 @@ +--- +'eslint-plugin-svelte': patch +--- + +fix: properly support Windows in `no-unused-props` rule +fix: properly support Windows in `valid-style-parse` rule +fix: properly support Windows in `nno-unnecessary-condition` rule +fix: properly support Windows in update tools From f2eb5f0f1ad29ff7777e0f3812cab9d48ce48bc8 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Thu, 18 Sep 2025 20:51:23 +0200 Subject: [PATCH 17/18] chore: remove internal tools reference in changeset --- .changeset/icy-rocks-camp.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.changeset/icy-rocks-camp.md b/.changeset/icy-rocks-camp.md index 395051607..d2ad433f9 100644 --- a/.changeset/icy-rocks-camp.md +++ b/.changeset/icy-rocks-camp.md @@ -5,4 +5,3 @@ fix: properly support Windows in `no-unused-props` rule fix: properly support Windows in `valid-style-parse` rule fix: properly support Windows in `nno-unnecessary-condition` rule -fix: properly support Windows in update tools From 892547d290c6acdf1d0457add877f5562927b026 Mon Sep 17 00:00:00 2001 From: InkedCat <146424693+InkedCat@users.noreply.github.com> Date: Thu, 18 Sep 2025 20:54:19 +0200 Subject: [PATCH 18/18] chore: fix typo in changeset --- .changeset/icy-rocks-camp.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/icy-rocks-camp.md b/.changeset/icy-rocks-camp.md index d2ad433f9..dfce79bd5 100644 --- a/.changeset/icy-rocks-camp.md +++ b/.changeset/icy-rocks-camp.md @@ -4,4 +4,4 @@ fix: properly support Windows in `no-unused-props` rule fix: properly support Windows in `valid-style-parse` rule -fix: properly support Windows in `nno-unnecessary-condition` rule +fix: properly support Windows in `no-unnecessary-condition` rule