@feature-sliced/filesystem
Version:
A set of utilities for locating and working with FSD roots in the file system.
162 lines (157 loc) • 6.29 kB
text/typescript
import ts, { CompilerOptions } from 'typescript';
interface File {
type: "file";
path: string;
}
interface Folder {
type: "folder";
path: string;
children: Array<File | Folder>;
}
/**
* An FSD root is an isolated folder structure that adheres to the rules of FSD.
*
* There can be several FSD roots in a project, they can also be arbitrarily nested.
*/
type FsdRoot = Folder;
type LayerName = "shared" | "entities" | "features" | "widgets" | "pages" | "app";
declare const layerSequence: Array<LayerName>;
declare const unslicedLayers: string[];
declare const conventionalSegmentNames: string[];
declare const crossReferenceToken = "@x";
/**
* Extract layers from an FSD root.
*
* @returns A mapping of layer name to folder object.
*/
declare function getLayers(fsdRoot: Folder): Partial<Record<LayerName, Folder>>;
/**
* Extract slices from a **sliced** layer.
*
* A folder is detected as a slice when it has at least one folder/file with a name of a conventional segment (`ui`, `api`, `model`, `lib`, `config`).
* If your project contains slices that don't have those segments, you can provide additional segment names.
*
* @returns A mapping of slice name (potentially containing slashes) to folder object.
*/
declare function getSlices(slicedLayer: Folder, additionalSegmentNames?: Array<string>): Record<string, Folder>;
/**
* Extract segments from a slice or an **unsliced** layer.
*
* @returns A mapping of segment name to folder or file object.
*/
declare function getSegments(sliceOrUnslicedLayer: Folder): Record<string, Folder | File>;
/**
* Extract slices from all layers of an FSD root.
*
* A folder is detected as a slice when it has at least one folder/file with a name of a conventional segment (`ui`, `api`, `model`, `lib`, `config`).
* If your project contains slices that don't have those segments, you can provide additional segment names.
*
* @returns A mapping of slice name (potentially containing slashes) to folder object with added layer name.
*/
declare function getAllSlices(fsdRoot: Folder, additionalSegmentNames?: Array<string>): Record<string, Folder & {
layerName: string;
}>;
/**
* Extract segments from all slices and layers of an FSD root.
*
* @returns A flat array of segments along with their name and location in the FSD root (layer, slice).
*/
declare function getAllSegments(fsdRoot: Folder): Array<{
segment: Folder | File;
segmentName: string;
sliceName: string | null;
layerName: LayerName;
}>;
/**
* Determine if this layer is sliced.
*
* Only layers Shared and App are not sliced, the rest are.
*/
declare function isSliced(layerOrName: Folder | LayerName): boolean;
/**
* Get the index (public API) of a slice or segment.
*
* When a segment is a file, it is its own index.
* When a segment is a folder, it returns an array of index files within that folder.
* Multiple index files (e.g., `index.client.js`, `index.server.js`) are supported.
*/
declare function getIndexes(fileOrFolder: File | Folder): File[];
/** Determine if a given file or folder is an index file. */
declare function isIndex(fileOrFolder: File | Folder): boolean;
/**
* Check if a given file is a cross-import public API defined in the slice `inSlice` for the slice `forSlice` on a given layer.
*
* @example
* const file = { path: "./src/entities/user/@x/product.ts", type: "file" }
* isCrossImportPublicApi(file, { inSlice: "user", forSlice: "product", layerPath: "./src/entities" }) // true
*/
declare function isCrossImportPublicApi(file: File, { inSlice, forSlice, layerPath, }: {
inSlice: string;
forSlice: string;
layerPath: string;
}): boolean;
/**
* Determine if this folder is a slice.
*
* Slices are defined as folders that contain at least one segment.
* Additional segment names can be provided if some slice in project contains only unconventional segments.
*/
declare function isSlice(folder: Folder, additionalSegmentNames?: Array<string>): boolean;
/**
* Given a file name, an imported path, and a TSConfig object, produce a path to the imported file, relative to TypeScript's `baseUrl`.
*
* The resulting path uses OS-appropriate path separators.
*
* @example
* ```tsx
* // ./src/pages/home/ui/HomePage.tsx
* import { Button } from "~/shared/ui";
* ```
*
* ```json
* // ./tsconfig.json
* {
* "compilerOptions": {
* "moduleResolution": "Bundler",
* "baseUrl": ".",
* "paths": {
* "~/*": ["./src/*"],
* },
* },
* }
* ```
*
* ```tsx
* resolveImport(
* "~/shared/ui",
* "./src/pages/home/ui/HomePage.tsx",
* { moduleResolution: "Bundler", baseUrl: ".", paths: { "~/*": ["./src/*"] } },
* fs.existsSync
* );
* ```
* Expected output: `src/shared/ui/index.ts`
*/
declare function resolveImport(importedPath: string, importerPath: string, tsCompilerOptions: ImperfectCompilerOptions, fileExists: (path: string) => boolean, directoryExists?: (path: string) => boolean): string | null;
declare const imperfectKeys: {
module: typeof ts.ModuleKind;
moduleResolution: {
node: ts.ModuleResolutionKind;
Classic: ts.ModuleResolutionKind.Classic;
NodeJs: ts.ModuleResolutionKind.NodeJs;
Node10: ts.ModuleResolutionKind.NodeJs;
Node16: ts.ModuleResolutionKind.Node16;
NodeNext: ts.ModuleResolutionKind.NodeNext;
Bundler: ts.ModuleResolutionKind.Bundler;
};
moduleDetection: typeof ts.ModuleDetectionKind;
newLine: typeof ts.NewLineKind;
target: typeof ts.ScriptTarget;
};
interface ImperfectCompilerOptions extends Omit<CompilerOptions, keyof typeof imperfectKeys> {
module?: ts.ModuleKind | keyof typeof ts.ModuleKind;
moduleResolution?: ts.ModuleResolutionKind | keyof typeof ts.ModuleResolutionKind | "node";
moduleDetection?: ts.ModuleDetectionKind | keyof typeof ts.ModuleDetectionKind;
newLine?: ts.NewLineKind | keyof typeof ts.NewLineKind;
target?: ts.ScriptTarget | keyof typeof ts.ScriptTarget;
}
export { type File, type Folder, type FsdRoot, type LayerName, conventionalSegmentNames, crossReferenceToken, getAllSegments, getAllSlices, getIndexes, getLayers, getSegments, getSlices, isCrossImportPublicApi, isIndex, isSlice, isSliced, layerSequence, resolveImport, unslicedLayers };