UNPKG

@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
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 };