Skip to content

Commit e48d463

Browse files
committed
feat(linter): add vue/no-export-in-script-setup rule
1 parent 3788785 commit e48d463

File tree

4 files changed

+290
-0
lines changed

4 files changed

+290
-0
lines changed

crates/oxc_linter/src/generated/rule_runner_impls.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2911,6 +2911,10 @@ impl RuleRunner for crate::rules::vue::max_props::MaxProps {
29112911
const NODE_TYPES: Option<&AstTypesBitset> = None;
29122912
}
29132913

2914+
impl RuleRunner for crate::rules::vue::no_export_in_script_setup::NoExportInScriptSetup {
2915+
const NODE_TYPES: Option<&AstTypesBitset> = None;
2916+
}
2917+
29142918
impl RuleRunner for crate::rules::vue::no_multiple_slot_args::NoMultipleSlotArgs {
29152919
const NODE_TYPES: Option<&AstTypesBitset> =
29162920
Some(&AstTypesBitset::from_types(&[AstType::CallExpression]));

crates/oxc_linter/src/rules.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,7 @@ pub(crate) mod vue {
644644
pub mod define_props_declaration;
645645
pub mod define_props_destructuring;
646646
pub mod max_props;
647+
pub mod no_export_in_script_setup;
647648
pub mod no_multiple_slot_args;
648649
pub mod no_required_prop_with_default;
649650
pub mod prefer_import_from_vue;
@@ -1247,6 +1248,7 @@ oxc_macros::declare_all_lint_rules! {
12471248
vue::define_emits_declaration,
12481249
vue::define_props_declaration,
12491250
vue::max_props,
1251+
vue::no_export_in_script_setup,
12501252
vue::no_multiple_slot_args,
12511253
vue::no_required_prop_with_default,
12521254
vue::prefer_import_from_vue,
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
use oxc_diagnostics::OxcDiagnostic;
2+
use oxc_macros::declare_oxc_lint;
3+
use oxc_span::Span;
4+
5+
use crate::{
6+
context::{ContextHost, LintContext},
7+
frameworks::FrameworkOptions,
8+
rule::Rule,
9+
};
10+
11+
fn no_export_in_script_setup_diagnostic(span: Span) -> OxcDiagnostic {
12+
OxcDiagnostic::warn("<script setup>` cannot contain ES module exports.").with_label(span)
13+
}
14+
15+
#[derive(Debug, Default, Clone)]
16+
pub struct NoExportInScriptSetup;
17+
18+
declare_oxc_lint!(
19+
/// ### What it does
20+
///
21+
/// Disallow `export` in `<script setup>`
22+
///
23+
/// ### Why is this bad?
24+
///
25+
/// The previous version of `<script setup>` RFC used `export` to define variables used in templates,
26+
/// but the new `<script setup>` RFC has been updated to define without using `export`.
27+
/// See [Vue RFCs - 0040-script-setup](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0040-script-setup.md) for more details.
28+
///
29+
/// ### Examples
30+
///
31+
/// Examples of **incorrect** code for this rule:
32+
/// ```vue
33+
/// <script setup>
34+
/// export let msg = 'Hello!'
35+
/// </script>
36+
/// ```
37+
///
38+
/// Examples of **correct** code for this rule:
39+
/// ```vue
40+
/// <script setup>
41+
/// let msg = 'Hello!'
42+
/// </script>
43+
/// ```
44+
NoExportInScriptSetup,
45+
vue,
46+
correctness,
47+
);
48+
49+
impl Rule for NoExportInScriptSetup {
50+
fn run_once(&self, ctx: &LintContext) {
51+
let modules = ctx.module_record();
52+
53+
for entry in &modules.local_export_entries {
54+
if entry.is_type {
55+
continue;
56+
}
57+
58+
ctx.diagnostic(no_export_in_script_setup_diagnostic(entry.span));
59+
}
60+
61+
for entry in &modules.indirect_export_entries {
62+
if entry.is_type {
63+
continue;
64+
}
65+
66+
ctx.diagnostic(no_export_in_script_setup_diagnostic(entry.span));
67+
}
68+
69+
for entry in &modules.star_export_entries {
70+
if entry.is_type {
71+
continue;
72+
}
73+
ctx.diagnostic(no_export_in_script_setup_diagnostic(entry.span));
74+
}
75+
76+
if let Some(span) = modules.export_default {
77+
ctx.diagnostic(no_export_in_script_setup_diagnostic(span));
78+
}
79+
}
80+
81+
fn should_run(&self, ctx: &ContextHost) -> bool {
82+
ctx.frameworks_options() == FrameworkOptions::VueSetup
83+
}
84+
}
85+
86+
#[test]
87+
fn test() {
88+
use crate::tester::Tester;
89+
use std::path::PathBuf;
90+
91+
let pass = vec![
92+
(
93+
"
94+
<script>
95+
export * from 'foo'
96+
export default {}
97+
export class A {}
98+
</script>
99+
",
100+
None,
101+
None,
102+
Some(PathBuf::from("test.vue")),
103+
),
104+
(
105+
"
106+
<script>
107+
export * from 'foo'
108+
export default {}
109+
export class A {}
110+
</script>
111+
<script setup>
112+
let foo;
113+
</script>
114+
",
115+
None,
116+
None,
117+
Some(PathBuf::from("test.vue")),
118+
),
119+
];
120+
121+
let fail = vec![
122+
(
123+
"
124+
<script setup>
125+
export * from 'foo'
126+
export default {}
127+
export class A {}
128+
export const test = '123'
129+
export function foo() {}
130+
const a = 1
131+
export { a }
132+
export { fao } from 'bar'
133+
</script>
134+
",
135+
None,
136+
None,
137+
Some(PathBuf::from("test.vue")),
138+
),
139+
(
140+
"
141+
<script>
142+
let foo;
143+
</script>
144+
<script setup>
145+
export * from 'foo'
146+
export default {}
147+
export class A {}
148+
</script>
149+
",
150+
None,
151+
None,
152+
Some(PathBuf::from("test.vue")),
153+
),
154+
(
155+
r#"
156+
<script setup lang="ts">
157+
export const Foo = {}
158+
export enum Bar {}
159+
export { }
160+
</script>
161+
"#,
162+
None,
163+
None,
164+
Some(PathBuf::from("test.vue")),
165+
), // { "parser": require("vue-eslint-parser"), "parserOptions": { "parser": require.resolve("@typescript-eslint/parser") } }
166+
];
167+
168+
Tester::new(NoExportInScriptSetup::NAME, NoExportInScriptSetup::PLUGIN, pass, fail)
169+
.test_and_snapshot();
170+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
---
2+
source: crates/oxc_linter/src/tester.rs
3+
---
4+
eslint-plugin-vue(no-export-in-script-setup): <script setup>` cannot contain ES module exports.
5+
╭─[no_export_in_script_setup.tsx:4:25]
6+
3 │ export * from 'foo'
7+
4 │ export default {}
8+
· ──
9+
5 │ export class A {}
10+
╰────
11+
12+
⚠ eslint-plugin-vue(no-export-in-script-setup): <script setup>` cannot contain ES module exports.
13+
╭─[no_export_in_script_setup.tsx:5:17]
14+
4 │ export default {}
15+
5 │ export class A {}
16+
· ──────────
17+
6 │ export const test = '123'
18+
╰────
19+
20+
⚠ eslint-plugin-vue(no-export-in-script-setup): <script setup>` cannot contain ES module exports.
21+
╭─[no_export_in_script_setup.tsx:6:17]
22+
5 │ export class A {}
23+
6 │ export const test = '123'
24+
· ──────────────────
25+
7 │ export function foo() {}
26+
╰────
27+
28+
⚠ eslint-plugin-vue(no-export-in-script-setup): <script setup>` cannot contain ES module exports.
29+
╭─[no_export_in_script_setup.tsx:7:17]
30+
6 │ export const test = '123'
31+
7 │ export function foo() {}
32+
· ─────────────────
33+
8 │ const a = 1
34+
╰────
35+
36+
⚠ eslint-plugin-vue(no-export-in-script-setup): <script setup>` cannot contain ES module exports.
37+
╭─[no_export_in_script_setup.tsx:9:19]
38+
8 │ const a = 1
39+
9 │ export { a }
40+
· ─
41+
10 │ export { fao } from 'bar'
42+
╰────
43+
44+
⚠ eslint-plugin-vue(no-export-in-script-setup): <script setup>` cannot contain ES module exports.
45+
╭─[no_export_in_script_setup.tsx:10:19]
46+
9 │ export { a }
47+
10 │ export { fao } from 'bar'
48+
· ───
49+
11 │ </script>
50+
╰────
51+
52+
⚠ eslint-plugin-vue(no-export-in-script-setup): <script setup>` cannot contain ES module exports.
53+
╭─[no_export_in_script_setup.tsx:3:10]
54+
2 │ <script setup>
55+
3 │ export * from 'foo'
56+
· ───────────────────
57+
4 │ export default {}
58+
╰────
59+
60+
⚠ eslint-plugin-vue(no-export-in-script-setup): <script setup>` cannot contain ES module exports.
61+
╭─[no_export_in_script_setup.tsx:4:17]
62+
3 │ export * from 'foo'
63+
4 │ export default {}
64+
· ───────
65+
5 │ export class A {}
66+
╰────
67+
68+
⚠ eslint-plugin-vue(no-export-in-script-setup): <script setup>` cannot contain ES module exports.
69+
╭─[no_export_in_script_setup.tsx:7:25]
70+
6 │ export * from 'foo'
71+
7 │ export default {}
72+
· ──
73+
8 │ export class A {}
74+
╰────
75+
76+
⚠ eslint-plugin-vue(no-export-in-script-setup): <script setup>` cannot contain ES module exports.
77+
╭─[no_export_in_script_setup.tsx:8:17]
78+
7 │ export default {}
79+
8 │ export class A {}
80+
· ──────────
81+
9 │ </script>
82+
╰────
83+
84+
⚠ eslint-plugin-vue(no-export-in-script-setup): <script setup>` cannot contain ES module exports.
85+
╭─[no_export_in_script_setup.tsx:6:10]
86+
5 │ <script setup>
87+
6 │ export * from 'foo'
88+
· ───────────────────
89+
7 │ export default {}
90+
╰────
91+
92+
⚠ eslint-plugin-vue(no-export-in-script-setup): <script setup>` cannot contain ES module exports.
93+
╭─[no_export_in_script_setup.tsx:7:17]
94+
6 │ export * from 'foo'
95+
7 │ export default {}
96+
· ───────
97+
8 │ export class A {}
98+
╰────
99+
100+
⚠ eslint-plugin-vue(no-export-in-script-setup): <script setup>` cannot contain ES module exports.
101+
╭─[no_export_in_script_setup.tsx:3:17]
102+
2 │ <script setup lang="ts">
103+
3 │ export const Foo = {}
104+
· ──────────────
105+
4 │ export enum Bar {}
106+
╰────
107+
108+
⚠ eslint-plugin-vue(no-export-in-script-setup): <script setup>` cannot contain ES module exports.
109+
╭─[no_export_in_script_setup.tsx:4:17]
110+
3 │ export const Foo = {}
111+
4 │ export enum Bar {}
112+
· ───────────
113+
5 │ export { }
114+
╰────

0 commit comments

Comments
 (0)