Skip to content

Commit

Permalink
feat: add svelte/no-svelte-internal rule (#749)
Browse files Browse the repository at this point in the history
`svelte/internal` is deprecated in Svelte 5 and will be removed in
Svelte 6 according to our current plan.
The Svelte compiler only compiles `.svelte` files and `.svelte.[jt]s`
files, so the compiler can not check normal `.[jt]s` files. Therefore,
the ESLint plugin checks this.
  • Loading branch information
baseballyama committed May 9, 2024
1 parent bb245f1 commit da4d535
Show file tree
Hide file tree
Showing 23 changed files with 193 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilly-bats-allow.md
@@ -0,0 +1,5 @@
---
"eslint-plugin-svelte": minor
---

feat: add `svelte/no-svelte-internal` rule
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -385,6 +385,7 @@ These rules relate to better ways of doing things to help you avoid problems:
| [svelte/no-inline-styles](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-inline-styles/) | disallow attributes and directives that produce inline styles | |
| [svelte/no-reactive-functions](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-reactive-functions/) | it's not necessary to define functions in reactive statements | :bulb: |
| [svelte/no-reactive-literals](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-reactive-literals/) | don't assign literal values in reactive statements | :bulb: |
| [svelte/no-svelte-internal](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-svelte-internal/) | svelte/internal will be removed in Svelte 6. | |
| [svelte/no-unused-class-name](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-class-name/) | disallow the use of a class in the template without a corresponding style | |
| [svelte/no-unused-svelte-ignore](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/) | disallow unused svelte-ignore comments | :star: |
| [svelte/no-useless-mustaches](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-useless-mustaches/) | disallow unnecessary mustache interpolations | :wrench: |
Expand Down
1 change: 1 addition & 0 deletions docs/rules.md
Expand Up @@ -58,6 +58,7 @@ These rules relate to better ways of doing things to help you avoid problems:
| [svelte/no-inline-styles](./rules/no-inline-styles.md) | disallow attributes and directives that produce inline styles | |
| [svelte/no-reactive-functions](./rules/no-reactive-functions.md) | it's not necessary to define functions in reactive statements | :bulb: |
| [svelte/no-reactive-literals](./rules/no-reactive-literals.md) | don't assign literal values in reactive statements | :bulb: |
| [svelte/no-svelte-internal](./rules/no-svelte-internal.md) | svelte/internal will be removed in Svelte 6. | |
| [svelte/no-unused-class-name](./rules/no-unused-class-name.md) | disallow the use of a class in the template without a corresponding style | |
| [svelte/no-unused-svelte-ignore](./rules/no-unused-svelte-ignore.md) | disallow unused svelte-ignore comments | :star: |
| [svelte/no-useless-mustaches](./rules/no-useless-mustaches.md) | disallow unnecessary mustache interpolations | :wrench: |
Expand Down
55 changes: 55 additions & 0 deletions docs/rules/no-svelte-internal.md
@@ -0,0 +1,55 @@
---
pageClass: 'rule-details'
sidebarDepth: 0
title: 'svelte/no-svelte-internal'
description: 'svelte/internal will be removed in Svelte 6.'
---

# svelte/no-svelte-internal

> svelte/internal will be removed in Svelte 6.
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>

## :book: Rule Details

This rule reports the use of the deprecated API `svelte/internal` and `svelte/internal/xxx`. `svelte/internal` is deprecated in Svelte 5. And it will be deleted in Svelte 6. These APIs can change in breaking ways at any time without notice.

<ESLintCodeBlock>

<!--eslint-skip-->

```svelte
<script>
/* eslint svelte/no-svelte-internal: "error" */
// ✓ GOOD
import { mount } from 'svelte';
// ✗ BAD
import { get_current_component } from 'svelte/internal';
import { inspect } from 'svelte/internal/client';
import('svelte/internal');
import('svelte/internal/disclose-version');
export * from 'svelte/internal';
export { listen } from 'svelte/internal';
export * from 'svelte/internal/server';
</script>
```

</ESLintCodeBlock>

## :wrench: Options

Nothing.

## :books: Further Reading

<!--TODO: update here when relevant statements are added in Svelte 5 documentation -->

Nothing.

## :mag: Implementation

- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/src/rules/no-svelte-internal.ts)
- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/tests/src/rules/no-svelte-internal.ts)
5 changes: 5 additions & 0 deletions src/rule-types.ts
Expand Up @@ -209,6 +209,11 @@ export interface RuleOptions {
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-store-async/
*/
'svelte/no-store-async'?: Linter.RuleEntry<[]>
/**
* svelte/internal will be removed in Svelte 6.
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-svelte-internal/
*/
'svelte/no-svelte-internal'?: Linter.RuleEntry<[]>
/**
* disallow `target="_blank"` attribute without `rel="noopener noreferrer"`
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-target-blank/
Expand Down
59 changes: 59 additions & 0 deletions src/rules/no-svelte-internal.ts
@@ -0,0 +1,59 @@
import { createRule } from '../utils';
import type { TSESTree } from '@typescript-eslint/types';

export default createRule('no-svelte-internal', {
meta: {
docs: {
description: 'svelte/internal will be removed in Svelte 6.',
category: 'Best Practices',
// TODO Switch to recommended in the major version.

Check warning on line 9 in src/rules/no-svelte-internal.ts

View workflow job for this annotation

GitHub Actions / lint

Unexpected 'todo' comment: 'TODO Switch to recommended in the major...'
// recommended: true,
recommended: false
},
schema: [],
messages: {
unexpected: 'Using svelte/internal is prohibited. This will be removed in Svelte 6.'
},
type: 'problem'
},
create(context) {
function report(node: TSESTree.Node) {
context.report({
node,
messageId: 'unexpected'
});
}

function isSvelteInternal(value: string) {
return value === 'svelte/internal' || value.startsWith('svelte/internal/');
}

return {
ImportDeclaration(node) {
if (node.source && isSvelteInternal(node.source.value)) {
report(node);
}
},
ImportExpression(node) {
if (
node.source &&
node.source.type === 'Literal' &&
typeof node.source.value === 'string' &&
isSvelteInternal(node.source.value)
) {
report(node);
}
},
ExportNamedDeclaration(node) {
if (node.source && isSvelteInternal(node.source.value)) {
report(node);
}
},
ExportAllDeclaration(node) {
if (node.source && isSvelteInternal(node.source.value)) {
report(node);
}
}
};
}
});
2 changes: 2 additions & 0 deletions src/utils/rules.ts
Expand Up @@ -41,6 +41,7 @@ import noRestrictedHtmlElements from '../rules/no-restricted-html-elements';
import noShorthandStylePropertyOverrides from '../rules/no-shorthand-style-property-overrides';
import noSpacesAroundEqualSignsInAttribute from '../rules/no-spaces-around-equal-signs-in-attribute';
import noStoreAsync from '../rules/no-store-async';
import noSvelteInternal from '../rules/no-svelte-internal';
import noTargetBlank from '../rules/no-target-blank';
import noTrailingSpaces from '../rules/no-trailing-spaces';
import noUnknownStyleDirectiveProperty from '../rules/no-unknown-style-directive-property';
Expand Down Expand Up @@ -105,6 +106,7 @@ export const rules = [
noShorthandStylePropertyOverrides,
noSpacesAroundEqualSignsInAttribute,
noStoreAsync,
noSvelteInternal,
noTargetBlank,
noTrailingSpaces,
noUnknownStyleDirectiveProperty,
Expand Down
@@ -0,0 +1,4 @@
- message: Using svelte/internal is prohibited. This will be removed in Svelte 6.
line: 3
column: 2
suggestions: null
@@ -0,0 +1,4 @@
<script>
// eslint-disable-next-line camelcase -- this is a test
import { get_current_component } from 'svelte/internal';
</script>
@@ -0,0 +1,4 @@
- message: Using svelte/internal is prohibited. This will be removed in Svelte 6.
line: 2
column: 2
suggestions: null
@@ -0,0 +1,3 @@
<script>
import { inspect } from 'svelte/internal/client';
</script>
@@ -0,0 +1,4 @@
- message: Using svelte/internal is prohibited. This will be removed in Svelte 6.
line: 2
column: 2
suggestions: null
@@ -0,0 +1,3 @@
<script>
import * as svelteInternal from 'svelte/internal';
</script>
@@ -0,0 +1,4 @@
- message: Using svelte/internal is prohibited. This will be removed in Svelte 6.
line: 2
column: 2
suggestions: null
@@ -0,0 +1,3 @@
<script>
export * from 'svelte/internal';
</script>
@@ -0,0 +1,4 @@
- message: Using svelte/internal is prohibited. This will be removed in Svelte 6.
line: 2
column: 2
suggestions: null
@@ -0,0 +1,3 @@
<script>
export * from 'svelte/internal/client';
</script>
@@ -0,0 +1,4 @@
- message: Using svelte/internal is prohibited. This will be removed in Svelte 6.
line: 2
column: 2
suggestions: null
@@ -0,0 +1,3 @@
<script>
export { inspect } from 'svelte/internal/client';
</script>
@@ -0,0 +1,4 @@
- message: Using svelte/internal is prohibited. This will be removed in Svelte 6.
line: 2
column: 2
suggestions: null
@@ -0,0 +1,3 @@
<script>
import('svelte/internal');
</script>
@@ -0,0 +1,3 @@
<script>
import { mount } from 'svelte';
</script>
12 changes: 12 additions & 0 deletions tests/src/rules/no-svelte-internal.ts
@@ -0,0 +1,12 @@
import { RuleTester } from '../../utils/eslint-compat';
import rule from '../../../src/rules/no-svelte-internal';
import { loadTestCases } from '../../utils/utils';

const tester = new RuleTester({
languageOptions: {
ecmaVersion: 2020,
sourceType: 'module'
}
});

tester.run('no-svelte-internal', rule as any, loadTestCases('no-svelte-internal'));

0 comments on commit da4d535

Please sign in to comment.