Skip to content

Commit

Permalink
Make sure forbidden-imports rule checks files directly inside layers (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
daniilsapa authored Jul 25, 2024
1 parent 79f3552 commit 4ed8d22
Show file tree
Hide file tree
Showing 4 changed files with 274 additions and 73 deletions.
5 changes: 5 additions & 0 deletions .changeset/quick-eggs-exist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@feature-sliced/steiger-plugin': patch
---

Make sure forbidden-imports rule checks files directly inside layers
39 changes: 39 additions & 0 deletions packages/steiger-plugin-fsd/src/_lib/index-source-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ export function indexSourceFiles(root: Folder): Record<string, SourceFile> {
}

for (const [layerName, layer] of Object.entries(getLayers(root))) {
// Even though files that are directly inside a layer are not encouraged by the FSD and are forbidden in most cases
// (except for an index/root file for the app layer as an entry point to the application), users can still add them.
// So, we need to index all files directly inside a layer to find errors.
layer.children
.filter((child) => child.type === 'file')
.forEach((file) => walk(file, { layerName: layerName as LayerName, sliceName: null, segmentName: null }))

if (!isSliced(layer)) {
for (const [segmentName, segment] of Object.entries(getSegments(layer))) {
walk(segment, { layerName: layerName as LayerName, sliceName: null, segmentName })
Expand Down Expand Up @@ -70,6 +77,11 @@ if (import.meta.vitest) {
📄 EditorPage.tsx
📄 Editor.tsx
📄 index.ts
📂 app
📂 ui
📄 index.ts
📄 root.ts
📄 index.ts
`)

expect(indexSourceFiles(root)).toEqual({
Expand Down Expand Up @@ -163,6 +175,33 @@ if (import.meta.vitest) {
segmentName: 'ui',
sliceName: null,
},
[joinFromRoot('app', 'ui', 'index.ts')]: {
file: {
path: joinFromRoot('app', 'ui', 'index.ts'),
type: 'file',
},
layerName: 'app',
segmentName: 'ui',
sliceName: null,
},
[joinFromRoot('app', 'root.ts')]: {
file: {
path: joinFromRoot('app', 'root.ts'),
type: 'file',
},
layerName: 'app',
segmentName: 'root',
sliceName: null,
},
[joinFromRoot('app', 'index.ts')]: {
file: {
path: joinFromRoot('app', 'index.ts'),
type: 'file',
},
layerName: 'app',
segmentName: null,
sliceName: null,
},
})
})
}
89 changes: 86 additions & 3 deletions packages/steiger-plugin-fsd/src/_lib/prepare-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import type { FsdRoot } from '@feature-sliced/filesystem'
import type { Folder, File, Diagnostic } from '@steiger/types'
import { vi } from 'vitest'

/** Parse a multi-line indented string with emojis for files and folders into an FSD root. */
export function parseIntoFsdRoot(fsMarkup: string): FsdRoot {
/** Parse a multi-line indented string with emojis for files and folders into an FSD root.
* @param fsMarkup - a file system tree represented in markup using file and folder emojis
* @param mountTo - virtually make the passed markup a subtree of the mountTo folder
* */
export function parseIntoFsdRoot(fsMarkup: string, mountTo?: string): FsdRoot {
function parseFolder(lines: Array<string>, path: string): Folder {
const children: Array<Folder | File> = []

Expand Down Expand Up @@ -33,7 +36,7 @@ export function parseIntoFsdRoot(fsMarkup: string): FsdRoot {
.map((line, _i, lines) => line.slice(lines[0].search(/\S/)))
.filter(Boolean)

return parseFolder(lines, joinFromRoot())
return parseFolder(lines, mountTo ?? joinFromRoot())
}

export function compareMessages(a: Diagnostic, b: Diagnostic): number {
Expand Down Expand Up @@ -149,4 +152,84 @@ if (import.meta.vitest) {
],
})
})

test('it should return a nested root folder when the optional rootPath argument is passed', () => {
const markup = `
📂 entities
📂 users
📂 ui
📄 index.ts
📂 posts
📂 ui
📄 index.ts
📂 shared
📂 ui
📄 index.ts
📄 Button.tsx
`
const root = parseIntoFsdRoot(markup, joinFromRoot('src'))

expect(root).toEqual({
type: 'folder',
path: joinFromRoot('src'),
children: [
{
type: 'folder',
path: joinFromRoot('src', 'entities'),
children: [
{
type: 'folder',
path: joinFromRoot('src', 'entities', 'users'),
children: [
{
type: 'folder',
path: joinFromRoot('src', 'entities', 'users', 'ui'),
children: [],
},
{
type: 'file',
path: joinFromRoot('src', 'entities', 'users', 'index.ts'),
},
],
},
{
type: 'folder',
path: joinFromRoot('src', 'entities', 'posts'),
children: [
{
type: 'folder',
path: joinFromRoot('src', 'entities', 'posts', 'ui'),
children: [],
},
{
type: 'file',
path: joinFromRoot('src', 'entities', 'posts', 'index.ts'),
},
],
},
],
},
{
type: 'folder',
path: joinFromRoot('src', 'shared'),
children: [
{
type: 'folder',
path: joinFromRoot('src', 'shared', 'ui'),
children: [
{
type: 'file',
path: joinFromRoot('src', 'shared', 'ui', 'index.ts'),
},
{
type: 'file',
path: joinFromRoot('src', 'shared', 'ui', 'Button.tsx'),
},
],
},
],
},
],
})
})
}
Loading

0 comments on commit 4ed8d22

Please sign in to comment.