Skip to content

Commit

Permalink
re implement
Browse files Browse the repository at this point in the history
  • Loading branch information
baseballyama committed Jan 3, 2025
1 parent 4fbe97d commit cb70fea
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 61 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { TSESTree } from '@typescript-eslint/types';
import { createRule } from '../utils/index.js';
import { getSvelteContext } from '../utils/svelte-context.js';

export default createRule('no-export-load-in-svelte-module-in-kit-pages', {
meta: {
Expand All @@ -16,11 +15,14 @@ export default createRule('no-export-load-in-svelte-module-in-kit-pages', {
unexpected:
'disallow exporting load functions in `*.svelte` module in SvelteKit page components.'
},
type: 'problem'
type: 'problem',
conditions: [
{
svelteKitFileTypes: ['+page.svelte', '+error.svelte', '+layout.svelte']
}
]
},
create(context) {
const svelteContext = getSvelteContext(context);
if (svelteContext?.svelteKitFileType == null) return {};
let isModule = false;
return {
// <script context="module">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { AST } from 'svelte-eslint-parser';
import type { TSESTree } from '@typescript-eslint/types';
import { createRule } from '../utils/index.js';
import { getSvelteContext } from '../utils/svelte-context.js';
import type { RuleContext } from '../types.js';

const EXPECTED_PROP_NAMES = ['data', 'errors', 'form', 'snapshot'];
Expand Down Expand Up @@ -35,11 +34,14 @@ export default createRule('valid-prop-names-in-kit-pages', {
messages: {
unexpected: 'disallow props other than data or errors in SvelteKit page components.'
},
type: 'problem'
type: 'problem',
conditions: [
{
svelteKitFileTypes: ['+page.svelte', '+error.svelte', '+layout.svelte']
}
]
},
create(context) {
const svelteContext = getSvelteContext(context);
if (svelteContext?.svelteKitFileType == null) return {};
let isScript = false;
return {
// <script>
Expand Down
13 changes: 13 additions & 0 deletions packages/eslint-plugin-svelte/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type { Root as SelectorRoot, Node as SelectorNode } from 'postcss-selecto
import type { ASTNode, ASTNodeWithParent, ASTNodeListener } from './types-for-node.js';
import type * as TS from 'typescript';
import type { SourceLocation } from 'svelte-eslint-parser/lib/ast/common.js';
import type { SvelteContext } from './utils/svelte-context.js';

export type { ASTNode, ASTNodeWithParent, ASTNodeListener };
export interface RuleListener extends ASTNodeListener {
Expand Down Expand Up @@ -108,6 +109,18 @@ export interface PartialRuleMetaData {
deprecated?: boolean;
replacedBy?: string[] | { note: string };
type: 'problem' | 'suggestion' | 'layout';
/**
* Conditions to determine whether this rule should be applied.
* Multiple conditions can be specified as array, and the rule will be applied if any one of them matches (logical OR).
* If not specified, the rule will be applied to all files.
*/
conditions?: {
svelteVersion?: string; // e.g.: '>= 5.0.0'
fileTypes?: SvelteContext['fileType'][];
runes?: SvelteContext['runes'];
svelteKitVersion?: string; // e.g.: '>= 1.0.0'
svelteKitFileTypes?: NonNullable<SvelteContext['svelteKitFileType']>[];
}[];
}

export type RuleContext = {
Expand Down
77 changes: 75 additions & 2 deletions packages/eslint-plugin-svelte/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,69 @@
import type { RuleModule, PartialRuleModule } from '../types.js';
import type { RuleModule, PartialRuleModule, PartialRuleMetaData, RuleContext } from '../types.js';
import { getSvelteContext, type SvelteContext } from '../utils/svelte-context.js';
import semver from 'semver';

function satisfiesCondition(
condition: NonNullable<PartialRuleMetaData['conditions']>[number],
svelteContext: SvelteContext
): boolean {
if (
condition.svelteVersion != null &&
!semver.satisfies(svelteContext.svelteVersion, condition.svelteVersion)
) {
return false;
}

if (
condition.fileTypes != null &&
condition.fileTypes.length > 0 &&
!condition.fileTypes.includes(svelteContext.fileType)
) {
return false;
}

if (condition.runes != null && condition.runes !== svelteContext.runes) {
return false;
}

if (condition.svelteKitVersion != null) {
if (
svelteContext.svelteKitVersion == null ||
!semver.satisfies(svelteContext.svelteKitVersion, condition.svelteKitVersion)
) {
return false;
}
}

if (condition.svelteKitFileTypes != null && condition.svelteKitFileTypes.length > 0) {
if (
svelteContext.svelteKitFileType == null ||
!condition.svelteKitFileTypes.includes(svelteContext.svelteKitFileType)
) {
return false;
}
}

return true;
}

function shouldRun(
svelteContext: SvelteContext | null,
conditions: PartialRuleMetaData['conditions']
): boolean {
// If svelteContext is null, it means the rule might be executed based on the analysis result of a different parser.
// In this case, always execute the rule.
if (svelteContext == null || conditions == null) {
return true;
}

for (const condition of conditions) {
if (satisfiesCondition(condition, svelteContext)) {
return true;
}
}

return false;
}

/**
* Define the rule.
Expand All @@ -16,6 +81,14 @@ export function createRule(ruleName: string, rule: PartialRuleModule): RuleModul
ruleName
}
},
create: rule.create as never
create(context: RuleContext) {
const { conditions } = rule.meta;
const svelteContext = getSvelteContext(context);
if (!shouldRun(svelteContext, conditions)) {
return {};
}

return rule.create(context);
}
};
}
91 changes: 43 additions & 48 deletions packages/eslint-plugin-svelte/src/utils/svelte-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import { getFilename, getSourceCode } from './compat.js';

const isRunOnBrowser = !fs.readFileSync;

type FileType = '.svelte' | '.svelte.[js|ts]';
export type SvelteContext = {
svelteVersion: string;
fileType: '.svelte' | '.svelte.[js|ts]';
runes: boolean;
svelteKitVersion: string | null;
svelteKitFileType:
| '+page.svelte'
| '+page.js'
Expand All @@ -18,18 +21,9 @@ export type SvelteContext = {
| '+layout.server.js'
| '+server.js'
| null;
} & (
| {
version: 3 | 4;
}
| {
version: 5;
runes: boolean;
fileType: FileType;
}
);

function getFileType(filePath: string): FileType | null {
};

function getFileType(filePath: string): SvelteContext['fileType'] | null {
if (filePath.endsWith('.svelte')) {
return '.svelte';
}
Expand Down Expand Up @@ -79,16 +73,23 @@ function getSvelteKitFileTypeFromFilePath(filePath: string): SvelteContext['svel
}
}

function getSvelteKitFileType(context: RuleContext): SvelteContext['svelteKitFileType'] {
function getSvelteKitContext(
context: RuleContext
): Pick<SvelteContext, 'svelteKitFileType' | 'svelteKitVersion'> {
const filePath = getFilename(context);

// Hack: if it runs on browser, it regards as SvelteKit project.
if (isRunOnBrowser) {
return getSvelteKitFileTypeFromFilePath(filePath);
const svelteKitVersion = gteSvelteKitVersion(filePath);
if (svelteKitVersion == null) {
return {
svelteKitFileType: null,
svelteKitVersion: null
};
}

if (!hasSvelteKit(getFilename(context))) {
return null;
if (isRunOnBrowser) {
return {
svelteKitVersion,
// Judge by only file path if it runs on browser.
svelteKitFileType: getSvelteKitFileTypeFromFilePath(filePath)
};
}

const routes =
Expand All @@ -99,10 +100,16 @@ function getSvelteKitFileType(context: RuleContext): SvelteContext['svelteKitFil
const projectRootDir = getProjectRootDir(getFilename(context)) ?? '';

if (!filePath.startsWith(path.join(projectRootDir, routes))) {
return null;
return {
svelteKitVersion,
svelteKitFileType: null
};
}

return getSvelteKitFileTypeFromFilePath(filePath);
return {
svelteKitVersion,
svelteKitFileType: getSvelteKitFileTypeFromFilePath(filePath)
};
}

/**
Expand All @@ -113,21 +120,22 @@ function getSvelteKitFileType(context: RuleContext): SvelteContext['svelteKitFil
* @param filePath A file path.
* @returns
*/
function hasSvelteKit(filePath: string): boolean {
function gteSvelteKitVersion(filePath: string): string | null {
// Hack: if it runs on browser, it regards as SvelteKit project.
if (isRunOnBrowser) return true;
if (isRunOnBrowser) return '2.15.1';
try {
const packageJson = getPackageJson(filePath);
if (!packageJson) return false;
if (!packageJson) return null;
if (packageJson.name === 'eslint-plugin-svelte')
// Hack: CI removes `@sveltejs/kit` and it returns false and test failed.
// So always it returns true if it runs on the package.
return true;
return Boolean(
packageJson.dependencies?.['@sveltejs/kit'] ?? packageJson.devDependencies?.['@sveltejs/kit']
);
return '2.15.1';

const version =
packageJson.dependencies?.['@sveltejs/kit'] ?? packageJson.devDependencies?.['@sveltejs/kit'];
return typeof version === 'string' ? version : null;
} catch {
return false;
return null;
}
}

Expand Down Expand Up @@ -156,21 +164,7 @@ export function getSvelteContext(context: RuleContext): SvelteContext | null {
}

const filePath = getFilename(context);
const svelteKitFileType = getSvelteKitFileType(context);

if (compilerVersion.startsWith('3')) {
return {
version: 3,
svelteKitFileType
};
}

if (compilerVersion.startsWith('4')) {
return {
version: 4,
svelteKitFileType
};
}
const svelteKitContext = getSvelteKitContext(context);

const runes = svelteParseContext.runes === true;
const fileType = getFileType(filePath);
Expand All @@ -179,9 +173,10 @@ export function getSvelteContext(context: RuleContext): SvelteContext | null {
}

return {
version: 5,
svelteVersion: compilerVersion,
runes,
fileType,
svelteKitFileType
svelteKitVersion: svelteKitContext.svelteKitVersion,
svelteKitFileType: svelteKitContext.svelteKitFileType
};
}
4 changes: 1 addition & 3 deletions packages/eslint-plugin-svelte/tools/new-rule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ void (async ([ruleId, ...args]) => {
ruleFile,
`import { AST } from 'svelte-eslint-parser';
import { createRule } from '${getModulePath(ruleFile, utilsPath)}';
import { getSvelteContext } from '../utils/svelte.js';
export default createRule('${ruleId}', {
meta: {
Expand All @@ -66,10 +65,9 @@ export default createRule('${ruleId}', {
schema: [],
messages: {},
type: 'suggestion', // 'problem', or 'layout',
conditions: [], // Conditions for applying this rule. Leave empty to always execute.
},
create(context) {
// TODO: Please refer to the context regarding Svelte and SvelteKit, and return early if run the rule is unnecessary.
const svelteContext = getSvelteContext(context);
return {}
},
});
Expand Down

0 comments on commit cb70fea

Please sign in to comment.