Skip to content

Commit 6fd0ebb

Browse files
authored
Merge pull request #14 from andrewmolyuk/sfc-order
Sfc order
2 parents cf92dc3 + e373fcc commit 6fd0ebb

File tree

10 files changed

+347
-61
lines changed

10 files changed

+347
-61
lines changed

.releaserc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
},
1212
"writerOpts": {
1313
"commitsSort": ["subject", "scope"]
14-
}
14+
},
15+
"excludeMergeCommits": true
1516
}
1617
],
1718
"@semantic-release/npm",

README.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ This plugin provides rules to enforce modular architecture boundaries in Vue.js
107107

108108
> The list of rules is a work in progress. Implemented rules are linked below; unimplemented rules are listed as plain names.
109109
>
110-
> ![Progress](https://progress-bar.xyz/6/?scale=92&width=500&title=6%20of%2092%20rules%20completed)
110+
> ![Progress](https://progress-bar.xyz/8/?scale=92&width=500&title=8%20of%2092%20rules%20completed)
111111
112112
### File Organization Rules
113113

@@ -130,17 +130,17 @@ This plugin provides rules to enforce modular architecture boundaries in Vue.js
130130
| app-imports | `app/` folder can import from `shared/` and `features/` (exception: `app/router.ts` may import feature route files to compose the global router). |
131131
| cross-feature-via-shared | All cross-feature communication must go through the `shared/` layer. |
132132

133-
### Component Rules
134-
135-
| Rule | Description |
136-
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
137-
| sfc-required | All Vue components should be written as Single File Components (SFC) with `.vue` extension; when present on disk a `.vue` file must contain at least a `<template>` or `<script>` / `<script setup>` block. |
138-
| sfc-order | Enforce SFC block order: `<script>`, `<template>`, `<style>`; the rule only enforces ordering when script/template blocks are present. |
139-
| layout-components-location | Layout-specific components must be in `app/components/`. |
140-
| ui-components-location | Reusable UI components (design system) must be in `shared/ui/`. |
141-
| business-components-location | Business components used across features must be in `shared/components/`. |
142-
| feature-components-location | Feature-specific components must be in `features/{feature}/components/`. |
143-
| component-props-typed | Component props must be typed with TypeScript interfaces. |
133+
### Components Rules
134+
135+
| Rule | Description |
136+
| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
137+
| [sfc-required](./docs/rules/sfc-required.md) | All Vue components should be written as Single File Components (SFC) with `.vue` extension; when present on disk a `.vue` file must contain at least a `<template>` or `<script>` / `<script setup>` block. |
138+
| [sfc-order](./docs/rules/sfc-order.md) | Enforce SFC block order: `<script>`, `<template>`, `<style>`; the rule only enforces ordering when script/template blocks are present. |
139+
| layout-components-location | Layout-specific components must be in `app/components/`. |
140+
| ui-components-location | Reusable UI components (design system) must be in `shared/ui/`. |
141+
| business-components-location | Business components used across features must be in `shared/components/`. |
142+
| feature-components-location | Feature-specific components must be in `features/{feature}/components/`. |
143+
| component-props-typed | Component props must be typed with TypeScript interfaces. |
144144

145145
### Service Rules
146146

docs/rules/sfc-order.md

Lines changed: 91 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,136 @@
11
# vue-modular/sfc-order
22

3-
Require a conventional order for Single File Component (SFC) blocks when those blocks exist.
3+
Enforce a conventional order for Single File Component (SFC) blocks.
44

5-
This rule parses `.vue` files using `@vue/compiler-sfc` and validates the relative order of blocks that are present in the file. Missing blocks are allowed — only the relative order among present blocks is checked.
5+
This rule parses Vue component files using `@vue/compiler-sfc` and validates the relative order of SFC blocks (`<script>`, `<template>`, `<style>`). The rule only enforces order among blocks that are present — missing blocks are allowed.
66

7-
By default the recommended order is:
7+
Default order:
88

99
1. `<script>` / `<script setup>`
1010
2. `<template>`
1111
3. `<style>` (one or more)
1212

1313
## Rule Details
1414

15-
- The rule runs only for on-disk `.vue` files under the configured `src` segment (default: `src`).
16-
- Virtual inputs (filenames starting with `<...>`) and files matched by the `ignore` option are skipped.
17-
- The rule computes the actual block order from the SFC descriptor and validates that the sequence of present blocks respects the relative order defined by the `order` option (or the default order).
18-
- This rule intentionally does not report missing blocks — use `vue-modular/sfc-required` to enforce presence of `<template>`/`<script>`.
15+
- **Component detection**: Only runs on `.vue` files located in the configured components folder. Files outside component folders are ignored.
16+
- **SFC parsing**: Uses `@vue/compiler-sfc` to parse the Vue file and extract block positions.
17+
- **Order validation**: Checks that present blocks follow the configured relative order. Missing blocks are allowed.
18+
- **Early exit**: If a component has neither `<template>` nor `<script>` blocks, the rule skips validation (no meaningful content to order).
1919

2020
## Options
2121

2222
The rule accepts a single options object:
2323

24-
- `src` (string, default: `"src"`) — path segment used to identify the repository source area where the rule should run.
25-
- `ignore` (string[], default: `[]`) — minimatch-style patterns to ignore files or folders.
26-
- `order` (string[], default: `['script','template','style']`) — the desired relative block ordering. Allowed values are `"script"`, `"template"`, and `"style"`. Missing blocks are permitted.
24+
- `order` (string[], default: `["script", "template", "style"]`) — desired relative block ordering. Allowed values: `"script"`, `"template"`, `"style"`
25+
- `ignores` (string[], default: `[""]`) — minimatch-style patterns to exclude specific files
26+
27+
### Project Configuration
28+
29+
The rule uses project-wide settings from your ESLint configuration's `settings['vue-modular']`:
30+
31+
- `componentsFolderName` (string, default: `"components"`) — name of the folder that contains components
32+
- `rootPath` (string, default: `"src"`) — root path of the project
33+
- `rootAlias` (string, default: `"@"`) — alias for the root path
2734

2835
### Example configuration
2936

3037
```js
38+
// ESLint config
3139
{
32-
"vue-modular/sfc-order": ["error", { "src": "src", "ignore": [], "order": ["script", "template", "style"] }]
40+
"settings": {
41+
"vue-modular": {
42+
"componentsFolderName": "components",
43+
"rootPath": "src",
44+
"rootAlias": "@"
45+
}
46+
},
47+
"rules": {
48+
"vue-modular/sfc-order": ["error", {
49+
"order": ["script", "template", "style"],
50+
"ignores": []
51+
}]
52+
}
3353
}
3454
```
3555

3656
## Incorrect
3757

3858
```vue
59+
<!-- Wrong order: template before script -->
3960
<template>
40-
<div />
61+
<div>Hello</div>
4162
</template>
42-
<script>
43-
export default {}
63+
<script setup>
64+
const message = 'Hello'
65+
</script>
66+
```
67+
68+
```vue
69+
<!-- Wrong order: style before template -->
70+
<script setup>
71+
const message = 'Hello'
4472
</script>
73+
<style>
74+
.hello {
75+
color: blue;
76+
}
77+
</style>
78+
<template>
79+
<div>Hello</div>
80+
</template>
4581
```
4682

4783
## Correct
4884

4985
```vue
86+
<!-- Correct order: script, template, style -->
5087
<script setup>
51-
const a = 1
88+
const message = 'Hello'
5289
</script>
5390
<template>
54-
<div />
91+
<div>{{ message }}</div>
5592
</template>
5693
<style>
57-
/* css */
94+
.hello {
95+
color: blue;
96+
}
97+
</style>
98+
```
99+
100+
```vue
101+
<!-- Missing blocks are allowed -->
102+
<script setup>
103+
// Script-only component
104+
export function useUtils() {}
105+
</script>
106+
```
107+
108+
```vue
109+
<!-- Custom order example -->
110+
<style>
111+
/* Custom order: style first */
112+
.component {
113+
display: block;
114+
}
58115
</style>
116+
<script setup>
117+
const props = defineProps({})
118+
</script>
119+
<template>
120+
<div class="component">Content</div>
121+
</template>
59122
```
60123

61124
## Usage Notes
62125

63-
- The rule uses `@vue/compiler-sfc` to parse SFCs, so `<script setup>` and other modern SFC syntax are handled correctly.
64-
- The rule checks order only; it does not assert the presence of template/script blocks. Use `vue-modular/sfc-required` to require those blocks.
65-
- The `ignore` option accepts minimatch patterns — match feature folder names (for example `"**/legacy/**"`) to skip legacy areas.
126+
- The rule uses `@vue/compiler-sfc` to parse Vue files reliably, supporting `<script setup>` and other modern SFC syntax.
127+
- Only files identified as components (`.vue` files in the components folder) are checked.
128+
- Both `<script>` and `<script setup>` blocks are treated as `"script"` type for ordering purposes.
129+
- Multiple `<style>` blocks are allowed and treated as individual `"style"` blocks.
130+
- Files with only style blocks (no template or script) are skipped entirely.
131+
- Files can be excluded using the `ignores` option with minimatch patterns.
132+
133+
## When Not To Use
134+
135+
- Disable this rule if your project doesn't follow consistent SFC block ordering conventions.
136+
- Use the `ignores` option to exclude specific files or patterns rather than disabling the rule entirely.

docs/rules/sfc-required.md

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,67 @@
11
# vue-modular/sfc-required
22

3-
Require Single File Component (SFC) structure for Vue component files under a configured `src` segment.
3+
Require Vue Single File Component (SFC) files to contain at least a `<template>` or `<script>` block.
44

5-
This rule enforces that any Vue component file that lives under the configured `src` segment uses the `.vue` SFC format and, when the file is present on disk, contains at least one of the meaningful SFC blocks (`<template>` or `<script>` / `<script setup>`). Files that are virtual (like `"<input>"`) or explicitly ignored by the rule's `ignore` option are skipped.
5+
This rule enforces that Vue component files contain meaningful content by requiring at least one of the core SFC blocks (`<template>` or `<script>` / `<script setup>`). It only runs on files that are identified as Vue components (`.vue` files in the configured components folder).
66

77
## Rule Details
88

9-
The rule performs two related checks:
9+
The rule performs the following checks:
1010

11-
1. It only runs for files inside the configured `src` segment (default: `src`). Files outside that segment are ignored.
12-
2. For on-disk `.vue` files it parses the SFC using `@vue/compiler-sfc` and verifies the descriptor contains either a `<template>` block or a `<script>` / `<script setup>` block. If neither block exists the rule reports `missingSfcBlock` with the filename.
11+
1. **Component detection**: Only runs on `.vue` files located in the configured components folder (default: `components`). Files outside component folders are ignored.
12+
2. **SFC parsing**: Uses `@vue/compiler-sfc` to parse the Vue file and extract the SFC descriptor.
13+
3. **Block validation**: Verifies that the SFC contains either a `<template>` block or a `<script>`/`<script setup>` block. If neither exists, reports an error.
1314

14-
This behavior helps catch accidentally empty `.vue` files or files that only contain style blocks (for example UI kit style-only files that should not be placed inside feature code), while avoiding false positives for virtual or non-.vue files.
15+
This helps catch accidentally empty component files or files that only contain style blocks without meaningful component logic.
1516

1617
## Options
1718

1819
The rule accepts a single options object:
1920

20-
- `src` (string, default: `"src"`) — the path segment used to identify the codebase root under which the rule should run. The rule finds the configured segment in the linted file path and only enforces rules for files under that segment.
21-
- `ignore` (string[], default: `[]`) — minimatch-style patterns matched against the relative path (or parent segment) to skip files or features. Use this to exclude legacy folders or third-party directories.
21+
- `ignores` (string[], default: `[""]`) — minimatch-style patterns to exclude specific files from checking.
22+
23+
### Project Configuration
24+
25+
The rule uses project-wide settings from your ESLint configuration's `settings['vue-modular']`:
26+
27+
- `componentsFolderName` (string, default: `"components"`) — name of the folder that contains components
28+
- `rootPath` (string, default: `"src"`) — root path of the project
29+
- `rootAlias` (string, default: `"@"`) — alias for the root path
2230

2331
### Example configuration
2432

2533
```js
26-
// Recommended defaults: scan files under `src` and do not ignore anything
34+
// ESLint config
2735
{
28-
"vue-modular/sfc-required": ["error", { "src": "src", "ignore": [] }]
36+
"settings": {
37+
"vue-modular": {
38+
"componentsFolderName": "components",
39+
"rootPath": "src",
40+
"rootAlias": "@"
41+
}
42+
},
43+
"rules": {
44+
"vue-modular/sfc-required": ["error", { "ignores": [] }]
45+
}
2946
}
3047
```
3148

3249
## Incorrect
3350

34-
```text
35-
// File: src/features/payments/components/Orphan.vue
36-
// File contents: only style and no template or script blocks
51+
```vue
52+
<!-- File: src/components/EmptyComponent.vue -->
53+
<!-- Only style block, no template or script -->
3754
<style>
38-
/* only styles */
55+
.card {
56+
padding: 1rem;
57+
}
3958
</style>
4059
```
4160

4261
## Correct
4362

4463
```vue
45-
<!-- File: src/features/payments/components/Card.vue -->
64+
<!-- File: src/components/Card.vue -->
4665
<template>
4766
<div class="card">...</div>
4867
</template>
@@ -52,17 +71,30 @@ const props = defineProps({})
5271
</script>
5372
5473
<style>
55-
/* styles */
74+
.card {
75+
padding: 1rem;
76+
}
5677
</style>
5778
```
5879

80+
```vue
81+
<!-- File: src/components/Utility.vue -->
82+
<!-- Script-only component is also valid -->
83+
<script setup>
84+
export function useCardUtils() {
85+
// utility logic
86+
}
87+
</script>
88+
```
89+
5990
## Usage Notes
6091

61-
- The rule uses `@vue/compiler-sfc` to parse the `.vue` file reliably; this allows detection of `<script setup>` and other valid SFC syntax.
62-
- The rule runs only once per ESLint process using the plugin's `runOnce` helper to avoid duplicate scans and noise when linting many files.
63-
- If a `.vue` file is missing on disk (for example an IDE virtual document) the rule will skip the content check.
64-
- The `ignore` option accepts minimatch patterns; match the feature folder name (for example `"**/legacy/**"`) to skip legacy areas.
92+
- The rule uses `@vue/compiler-sfc` to parse Vue files reliably, supporting `<script setup>` and other modern SFC syntax.
93+
- Only files identified as components (`.vue` files in the components folder) are checked.
94+
- Files can be excluded using the `ignores` option with minimatch patterns.
95+
- The rule respects project-wide component folder configuration via ESLint settings.
6596

6697
## When Not To Use
6798

68-
- Do not enable this rule for folders that intentionally contain non-SFC Vue artifacts (for example a style-only UI-kit folder that you choose to keep outside of features). Instead, configure `ignore` to skip those paths.
99+
- Disable this rule if you intentionally create Vue files with only style blocks (e.g., style-only component libraries).
100+
- Use the `ignores` option to exclude specific files or patterns rather than disabling the rule entirely.

src/rules/file-component-naming.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ export const fileComponentNaming = createRule<VueModularRuleModule>({
4141
{
4242
type: 'object',
4343
properties: {
44-
src: { type: 'string' },
4544
ignores: { type: 'array', items: { type: 'string' } },
4645
},
4746
additionalProperties: false,

src/rules/file-ts-naming.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ export const fileTsNaming = createRule<VueModularRuleModule>({
3939
{
4040
type: 'object',
4141
properties: {
42-
src: { type: 'string' },
4342
ignores: { type: 'array', items: { type: 'string' } },
4443
},
4544
additionalProperties: false,

src/rules/folder-kebab-case.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ export const folderKebabCase = createRule<VueModularRuleModule>({
5050
{
5151
type: 'object',
5252
properties: {
53-
src: { type: 'string' },
5453
ignores: { type: 'array', items: { type: 'string' } },
5554
},
5655
additionalProperties: false,

0 commit comments

Comments
 (0)