diff --git a/packages/core/src/lib/main/parse-project.ts b/packages/core/src/lib/main/parse-project.ts index 0d812de..c9f837c 100644 --- a/packages/core/src/lib/main/parse-project.ts +++ b/packages/core/src/lib/main/parse-project.ts @@ -2,13 +2,13 @@ import { FsPath } from '../file-info/fs-path'; import { FileInfo } from '../modules/file.info'; import { generateUnassignedFileInfo } from '../file-info/generate-unassigned-file-info'; import { getProjectDirsFromFileInfo } from '../modules/get-project-dirs-from-file-info'; -import { findModulePathsWithBarrel } from '../modules/find-module-paths-with-barrel'; import { createModules } from '../modules/create-modules'; import { fillFileInfoMap } from '../modules/fill-file-info-map'; import throwIfNull from '../util/throw-if-null'; import { TsData } from '../file-info/ts-data'; import { Module } from '../modules/module'; import { SheriffConfig } from '../config/sheriff-config'; +import { findModulePaths } from "../modules/find-module-paths"; export type ParsedResult = { fileInfo: FileInfo; @@ -38,8 +38,7 @@ export const parseProject = ( const getFileInfo = (path: FsPath) => throwIfNull(fileInfoMap.get(path), `cannot find FileInfo for ${path}`); - const modulePathsWithBarrel = findModulePathsWithBarrel(projectDirs, config.barrelFileName); - const modulePaths = modulePathsWithBarrel; + const modulePaths = findModulePaths(projectDirs, config.tagging, config.barrelFileName); const modules = createModules( unassignedFileInfo, modulePaths, diff --git a/packages/core/src/lib/modules/create-modules.ts b/packages/core/src/lib/modules/create-modules.ts index 8005027..76ba875 100644 --- a/packages/core/src/lib/modules/create-modules.ts +++ b/packages/core/src/lib/modules/create-modules.ts @@ -2,57 +2,49 @@ import { Module } from './module'; import { UnassignedFileInfo } from '../file-info/unassigned-file-info'; import traverseUnassignedFileInfo from '../file-info/traverse-unassigned-file-info'; import throwIfNull from '../util/throw-if-null'; -import { FsPath } from '../file-info/fs-path'; -import { formatModules } from './format-modules'; -import get from '../util/get'; -import { logger } from '../log'; +import { FsPath, toFsPath } from '../file-info/fs-path'; import { FileInfo } from './file.info'; +import { ModulePathMap } from './find-module-paths'; +import { entries, fromEntries, keys, values } from "../util/typed-object-functions"; -const log = logger('core.modules.create'); - -const findClosestModule = (path: string, moduleInfos: Module[]) => { - return throwIfNull( - moduleInfos - .filter((moduleInfo) => path.startsWith(moduleInfo.directory)) - .sort((p1, p2) => (p1.directory.length > p2.directory.length ? -1 : 1)) - .at(0), - `findClosestModule for ${path}`, - ); -}; - -export const createModules = ( - fileInfo: UnassignedFileInfo, - existingModules: Set, +export function createModules( + entryFileInfo: UnassignedFileInfo, + modulePathMap: ModulePathMap, rootDir: FsPath, fileInfoMap: Map, getFileInfo: (path: FsPath) => FileInfo, -): Module[] => { - const moduleInfos = Array.from(existingModules).map( - (module) => new Module(module, fileInfoMap, getFileInfo, false), +): Module[] { + const moduleMap = fromEntries( + entries(modulePathMap).map(([path, hasBarrel]) => [ + path, + new Module(toFsPath(path), fileInfoMap, getFileInfo, false, hasBarrel), + ]), ); - moduleInfos.push(new Module(rootDir, fileInfoMap, getFileInfo, true)); - const modulleInfoPerBarrelFile = new Map( - moduleInfos.map((moduleInfo) => [moduleInfo.path, moduleInfo]), - ); - const moduleInfoMap = new Map( - moduleInfos.map((moduleInfo) => [moduleInfo.directory, moduleInfo]), + // add root module + moduleMap[rootDir] = new Module( + rootDir, + fileInfoMap, + getFileInfo, + true, + false, ); - for (const element of traverseUnassignedFileInfo(fileInfo)) { - const fi = element.fileInfo; - if (isFileInfoAModule(fi, existingModules)) { - get(modulleInfoPerBarrelFile, fi.path).addFileInfo(fi); - } else { - findClosestModule(fi.path, moduleInfos).addFileInfo(fi); - } + const modulePaths = keys(moduleMap); + + for (const { fileInfo } of traverseUnassignedFileInfo(entryFileInfo)) { + const modulePath = findClosestModulePath(fileInfo.path, modulePaths); + moduleMap[modulePath].addFileInfo(fileInfo); } - const foundModules = Array.from(moduleInfoMap.values()); - log.info(formatModules(foundModules)); - return foundModules; -}; + return values(moduleMap); +} -const isFileInfoAModule = ( - { path }: UnassignedFileInfo, - existingModules: Set, -) => existingModules.has(path); +function findClosestModulePath(path: string, modulePaths: FsPath[]) { + return throwIfNull( + modulePaths + .filter((modulePath) => path.startsWith(modulePath)) + .sort((p1, p2) => (p1.length > p2.length ? -1 : 1)) + .at(0), + `findClosestModule for ${path}`, + ); +} diff --git a/packages/core/src/lib/modules/find-module-paths.ts b/packages/core/src/lib/modules/find-module-paths.ts index d0e28c1..d34b98d 100644 --- a/packages/core/src/lib/modules/find-module-paths.ts +++ b/packages/core/src/lib/modules/find-module-paths.ts @@ -3,7 +3,7 @@ import { findModulePathsWithoutBarrel } from "./internal/find-module-paths-witho import { TagConfig } from "../config/tag-config"; import { findModulePathsWithBarrel } from "./internal/find-module-paths-with-barrel"; -export type ModulePaths = Record +export type ModulePathMap = Record /** * Find module paths which can be defined via having a barrel file or the @@ -11,10 +11,10 @@ export type ModulePaths = Record * * If a module has a barrel file and an internal, it is of type barrel file. */ -export function findModulePaths(projectDirs: FsPath[], moduleConfig: TagConfig, barrelFileName: string): ModulePaths { +export function findModulePaths(projectDirs: FsPath[], moduleConfig: TagConfig, barrelFileName: string): ModulePathMap { const modulesWithoutBarrel = findModulePathsWithoutBarrel(projectDirs, moduleConfig); const modulesWithBarrel = findModulePathsWithBarrel(projectDirs, barrelFileName); - const modulePaths: ModulePaths = {}; + const modulePaths: ModulePathMap = {}; for (const path of modulesWithoutBarrel) { modulePaths[path] = false; diff --git a/packages/core/src/lib/modules/internal/find-module-paths-with-barrel.ts b/packages/core/src/lib/modules/internal/find-module-paths-with-barrel.ts index ac6514c..cee926f 100644 --- a/packages/core/src/lib/modules/internal/find-module-paths-with-barrel.ts +++ b/packages/core/src/lib/modules/internal/find-module-paths-with-barrel.ts @@ -1,17 +1,14 @@ -import { logger } from "../../log"; -import { FsPath } from "../../file-info/fs-path"; -import getFs from "../../fs/getFs"; - -const log = logger('core.modules.find-path'); - -export function findModulePathsWithBarrel(projectDirs: FsPath[], barrelFileName: string): Set { - const fs = getFs(); - let modules: FsPath[] = []; - - for (const projectDir of projectDirs) { - modules = modules.concat(fs.findFiles(projectDir, barrelFileName)); - } - - log.info(modules.join(', ')); - return new Set(modules); -}; +import { FsPath, toFsPath } from '../../file-info/fs-path'; +import getFs from '../../fs/getFs'; + +export function findModulePathsWithBarrel( + projectDirs: FsPath[], + barrelFileName: string, +): FsPath[] { + return projectDirs.flatMap((projectDir) => + getFs() + .findFiles(projectDir, barrelFileName) + .map((path) => path.slice(0, -(barrelFileName.length + 1))) + .map(toFsPath), + ); +} diff --git a/packages/core/src/lib/modules/module.ts b/packages/core/src/lib/modules/module.ts index 9b3bb59..6288fe4 100644 --- a/packages/core/src/lib/modules/module.ts +++ b/packages/core/src/lib/modules/module.ts @@ -1,24 +1,17 @@ -import {UnassignedFileInfo} from '../file-info/unassigned-file-info'; +import { UnassignedFileInfo } from '../file-info/unassigned-file-info'; import { FileInfo } from './file.info'; -import { FsPath, toFsPath } from '../file-info/fs-path'; +import { FsPath } from '../file-info/fs-path'; export class Module { - readonly directory: FsPath; fileInfos: FileInfo[] = []; + constructor( public readonly path: FsPath, private readonly fileInfoMap: Map, private readonly getFileInfo: (fsPath: FsPath) => FileInfo, - public readonly isRoot: boolean - ) { - if (path.endsWith('index.ts')) { - this.directory = toFsPath( - this.path.substring(0, this.path.length - '/index.ts'.length), - ); - } else { - this.directory = path; - } - } + public readonly isRoot: boolean, + public readonly hasBarrel: boolean, + ) {} addFileInfo(unassignedFileInfo: UnassignedFileInfo) { const fileInfo = new FileInfo(unassignedFileInfo, this, this.getFileInfo); diff --git a/packages/core/src/lib/modules/tests/create-module.spec.ts b/packages/core/src/lib/modules/tests/create-module.spec.ts index 3e31a46..341697b 100644 --- a/packages/core/src/lib/modules/tests/create-module.spec.ts +++ b/packages/core/src/lib/modules/tests/create-module.spec.ts @@ -1,245 +1,259 @@ -import {UnassignedFileInfo} from '../../file-info/unassigned-file-info'; +import { UnassignedFileInfo } from '../../file-info/unassigned-file-info'; import { createModules } from '../create-modules'; import findFileInfo from '../../test/find-file-info'; import { Module } from '../module'; -import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; +import { afterEach, beforeAll, describe, expect, it } from 'vitest'; import throwIfNull from '../../util/throw-if-null'; import getFs, { useVirtualFs } from '../../fs/getFs'; import { FsPath, toFsPath } from '../../file-info/fs-path'; import { FileInfo } from '../file.info'; -import {buildFileInfo} from "../../test/build-file-info"; +import { buildFileInfo } from '../../test/build-file-info'; +import { fromEntries } from '../../util/typed-object-functions'; interface TestParameter { - name: string; fileInfo: UnassignedFileInfo; - modulePaths: string[]; + barrelFiles: string[]; expectedModules: { path: string; fileInfoPaths: string[] }[]; } -const simple: () => TestParameter = () => ({ - name: 'simple', - fileInfo: buildFileInfo('/src/app/app.component.ts', [ - './customers/customer.component.ts', - ['./holidays/index.ts', ['./holiday.component.ts']], - ]), - modulePaths: ['/src/app/customers/index.ts', '/src/app/holidays/index.ts'], - expectedModules: [ - { - path: '/src/app/customers/index.ts', - fileInfoPaths: ['/src/app/customers/customer.component.ts'], - }, - { - path: '/src/app/holidays/index.ts', - fileInfoPaths: [ +describe('createModule', () => { + beforeAll(() => { + useVirtualFs(); + }); + + afterEach(() => { + getFs().reset(); + }); + it('should create module for simple configuration', () => { + assertModule(() => ({ + fileInfo: buildFileInfo('/src/app/app.component.ts', [ + './customers/customer.component.ts', + ['./holidays/index.ts', ['./holiday.component.ts']], + ]), + barrelFiles: [ + '/src/app/customers/index.ts', '/src/app/holidays/index.ts', - '/src/app/holidays/holiday.component.ts', ], - }, - { - path: '/', - fileInfoPaths: ['/src/app/app.component.ts'], - }, - ], -}); - -const multipleFilesPerModule: () => TestParameter = () => ({ - name: 'multiple files per module', - fileInfo: buildFileInfo('/src/app/app.component.ts', [ - './customers/customer.component.ts', - './customers/detail.component.ts', - './customers/customer.service.ts', - [ - './holidays/index.ts', - ['./holiday.component.ts', './detail.component.ts', './holiday.pipe.ts'], - ], - ]), - modulePaths: ['/src/app/customers/index.ts', '/src/app/holidays/index.ts'], - expectedModules: [ - { - path: '/src/app/customers/index.ts', - fileInfoPaths: [ - '/src/app/customers/customer.component.ts', - '/src/app/customers/detail.component.ts', - '/src/app/customers/customer.service.ts', + expectedModules: [ + { + path: '/src/app/customers', + fileInfoPaths: ['/src/app/customers/customer.component.ts'], + }, + { + path: '/src/app/holidays', + fileInfoPaths: [ + '/src/app/holidays/index.ts', + '/src/app/holidays/holiday.component.ts', + ], + }, + { + path: '/', + fileInfoPaths: ['/src/app/app.component.ts'], + }, ], - }, - { - path: '/src/app/holidays/index.ts', - fileInfoPaths: [ + })); + }); + + it('should create module for multiple files per module configuration', () => { + assertModule(() => ({ + fileInfo: buildFileInfo('/src/app/app.component.ts', [ + './customers/customer.component.ts', + './customers/detail.component.ts', + './customers/customer.service.ts', + [ + './holidays/index.ts', + [ + './holiday.component.ts', + './detail.component.ts', + './holiday.pipe.ts', + ], + ], + ]), + barrelFiles: [ + '/src/app/customers/index.ts', '/src/app/holidays/index.ts', - '/src/app/holidays/holiday.component.ts', - '/src/app/holidays/detail.component.ts', - '/src/app/holidays/holiday.pipe.ts', ], - }, - { path: '/', fileInfoPaths: ['/src/app/app.component.ts'] }, - ], -}); + expectedModules: [ + { + path: '/src/app/customers', + fileInfoPaths: [ + '/src/app/customers/customer.component.ts', + '/src/app/customers/detail.component.ts', + '/src/app/customers/customer.service.ts', + ], + }, + { + path: '/src/app/holidays', + fileInfoPaths: [ + '/src/app/holidays/index.ts', + '/src/app/holidays/holiday.component.ts', + '/src/app/holidays/detail.component.ts', + '/src/app/holidays/holiday.pipe.ts', + ], + }, + { path: '/', fileInfoPaths: ['/src/app/app.component.ts'] }, + ], + })); + }); -const noModules: () => TestParameter = () => ({ - name: 'no modules', - fileInfo: buildFileInfo('/src/app/app.component.ts', [ - './customers/customer.component.ts', - './holidays/holiday.component.ts', - ]), - modulePaths: [], - expectedModules: [ - { - path: '/', - fileInfoPaths: [ - '/src/app/app.component.ts', - '/src/app/customers/customer.component.ts', - '/src/app/holidays/holiday.component.ts', + it('should create module for a project without modules', () => { + assertModule(() => ({ + fileInfo: buildFileInfo('/src/app/app.component.ts', [ + './customers/customer.component.ts', + './holidays/holiday.component.ts', + ]), + barrelFiles: [], + expectedModules: [ + { + path: '/', + fileInfoPaths: [ + '/src/app/app.component.ts', + '/src/app/customers/customer.component.ts', + '/src/app/holidays/holiday.component.ts', + ], + }, ], - }, - ], -}); + })); + }); -const nestedModules: () => TestParameter = () => ({ - name: 'nested modules', - fileInfo: buildFileInfo('/src/app/main.ts', [ - [ - './app.component.ts', - [ + it('should create module for nested modules', () => { + assertModule(() => ({ + fileInfo: buildFileInfo('/src/app/main.ts', [ [ - './customers/customer.component.ts', + './app.component.ts', [ - './feature/feature.service.ts', - './data/customer.facade.ts', - './ui/ui.component.ts', + [ + './customers/customer.component.ts', + [ + './feature/feature.service.ts', + './data/customer.facade.ts', + './ui/ui.component.ts', + ], + ], ], ], + ]), + barrelFiles: [ + '/src/app/customers/index.ts', + '/src/app/customers/feature/index.ts', + '/src/app/customers/data/index.ts', + '/src/app/customers/ui/index.ts', ], - ], - ]), - modulePaths: [ - '/src/app/customers/index.ts', - '/src/app/customers/feature/index.ts', - '/src/app/customers/data/index.ts', - '/src/app/customers/ui/index.ts', - ], - expectedModules: [ - { - path: '/src/app/customers/index.ts', - fileInfoPaths: ['/src/app/customers/customer.component.ts'], - }, - { - path: '/src/app/customers/feature/index.ts', - fileInfoPaths: ['/src/app/customers/feature/feature.service.ts'], - }, - { - path: '/src/app/customers/data/index.ts', - fileInfoPaths: ['/src/app/customers/data/customer.facade.ts'], - }, - { - path: '/src/app/customers/ui/index.ts', - fileInfoPaths: ['/src/app/customers/ui/ui.component.ts'], - }, - { - path: '/', - fileInfoPaths: ['/src/app/main.ts', '/src/app/app.component.ts'], - }, - ], -}); + expectedModules: [ + { + path: '/src/app/customers', + fileInfoPaths: ['/src/app/customers/customer.component.ts'], + }, + { + path: '/src/app/customers/feature', + fileInfoPaths: ['/src/app/customers/feature/feature.service.ts'], + }, + { + path: '/src/app/customers/data', + fileInfoPaths: ['/src/app/customers/data/customer.facade.ts'], + }, + { + path: '/src/app/customers/ui', + fileInfoPaths: ['/src/app/customers/ui/ui.component.ts'], + }, + { + path: '/', + fileInfoPaths: ['/src/app/main.ts', '/src/app/app.component.ts'], + }, + ], + })); + }); -const multipleDirectories: () => TestParameter = () => ({ - name: 'multiple directories', - fileInfo: buildFileInfo('/src/app/main.ts', [ - [ - './app.component.ts', - [ + it('should create module for multiple directories', () => { + assertModule(() => ({ + fileInfo: buildFileInfo('/src/app/main.ts', [ [ - './customers/customer.component.ts', + './app.component.ts', [ - './feature/feature.service.ts', - './data/customer.facade.ts', - './ui/ui.component.ts', + [ + './customers/customer.component.ts', + [ + './feature/feature.service.ts', + './data/customer.facade.ts', + './ui/ui.component.ts', + ], + ], ], ], + ]), + barrelFiles: ['/src/app/customers/index.ts'], + expectedModules: [ + { + path: '/src/app/customers', + fileInfoPaths: [ + '/src/app/customers/customer.component.ts', + '/src/app/customers/feature/feature.service.ts', + '/src/app/customers/data/customer.facade.ts', + '/src/app/customers/ui/ui.component.ts', + ], + }, + { + path: '/', + fileInfoPaths: ['/src/app/main.ts', '/src/app/app.component.ts'], + }, ], - ], - ]), - modulePaths: ['/src/app/customers/index.ts'], - expectedModules: [ - { - path: '/src/app/customers/index.ts', - fileInfoPaths: [ - '/src/app/customers/customer.component.ts', - '/src/app/customers/feature/feature.service.ts', - '/src/app/customers/data/customer.facade.ts', - '/src/app/customers/ui/ui.component.ts', - ], - }, - { - path: '/', - fileInfoPaths: ['/src/app/main.ts', '/src/app/app.component.ts'], - }, - ], + })); + }); }); -describe('create module infos', () => { - beforeAll(() => { - useVirtualFs(); - }); +function assertModule(createTestParams: () => TestParameter) { + const testParams = createTestParams(); + const { fileInfo, barrelFiles } = testParams; + const fileInfoMap = new Map(); + const getFileInfo = (path: FsPath) => + throwIfNull( + fileInfoMap.get(path), + `cannot find AssignedFileInfo for ${path}`, + ); - beforeEach(() => { - getFs().reset(); + barrelFiles.forEach((modulePath) => { + getFs().writeFile(modulePath, ''); }); + const modules = createModules( + fileInfo, + fromEntries( + barrelFiles.map((path) => [ + toFsPath(path.replace('/index.ts', '')), + true, + ]), + ), + toFsPath('/'), + fileInfoMap, + getFileInfo, + ); - it.each([ - ['simple', simple], - ['multipleFilesPerModule', multipleFilesPerModule], - ['noModules', noModules], - ['nestedModules', nestedModules], - ['multipleDirectories', multipleDirectories], - ])( - 'should create a moduleInfos for configuration: %s', - (_, createTestParams) => { - const { fileInfo, modulePaths, expectedModules } = createTestParams(); - const fileInfoMap = new Map(); - const getFileInfo = (path: FsPath) => - throwIfNull( - fileInfoMap.get(path), - `cannot find AssignedFileInfo for ${path}`, - ); - - modulePaths.forEach((modulePath) => { - getFs().writeFile(modulePath, ''); - }); - const moduleInfos = createModules( - fileInfo, - new Set(modulePaths.map(toFsPath)), - toFsPath('/'), - fileInfoMap, - getFileInfo, - ); + const expectedModules = testParams.expectedModules.map((mi) => { + const fileInfos = mi.fileInfoPaths.map((fip) => + throwIfNull( + findFileInfo(fileInfo, fip), + `${fip} does not exist in passed FileInfo`, + ), + ); + const module = new Module( + toFsPath(mi.path), + fileInfoMap, + getFileInfo, + mi.path === '/', + mi.path !== '/', + ); + for (const fi of fileInfos) { + module.addFileInfo(fi); + } - expect(moduleInfos).toEqual( - expectedModules.map((mi) => { - const fileInfos = mi.fileInfoPaths.map((fip) => - throwIfNull( - findFileInfo(fileInfo, fip), - `${fip} does not exist in passed FileInfo`, - ), - ); - const moduleInfo = new Module( - toFsPath(mi.path), - fileInfoMap, - getFileInfo, - mi.path === '/' - ); - for (const fi of fileInfos) { - moduleInfo.addFileInfo(fi); - } + return module; + }); - return moduleInfo; - }), - ); - }, + for (const module of modules) { + const expectedModule = expectedModules.find((m) => m.path === module.path); + expect(expectedModule, `cannot find module ${module.path}`).toBeDefined(); + expect(module, `Module ${module.path} is wrong`).toEqual(expectedModule); + } + expect(modules.length, 'different size of modules').toEqual( + expectedModules.length, ); -}); - -it.todo('should check for directory path with implicit index.ts'); -it.todo( - 'should assign two imports with implicit and explict index.ts to the same module', -); +} diff --git a/packages/core/src/lib/modules/tests/find-module-paths-with-barrel.spec.ts b/packages/core/src/lib/modules/tests/find-module-paths-with-barrel.spec.ts index e5f9355..04211bb 100644 --- a/packages/core/src/lib/modules/tests/find-module-paths-with-barrel.spec.ts +++ b/packages/core/src/lib/modules/tests/find-module-paths-with-barrel.spec.ts @@ -5,7 +5,7 @@ import { findModulePathsWithBarrel } from "../internal/find-module-paths-with-ba import { toFsPath } from "../../file-info/fs-path"; describe('should find two modules', () => { - it('should find two modules and root', () => { + it('should find two modules', () => { createProject({ 'tsconfig.json': tsConfig(), 'sheriff.config.ts': '', @@ -23,9 +23,8 @@ describe('should find two modules', () => { const modulePaths = findModulePathsWithBarrel([toFsPath('/project')], 'index.ts'); expect(modulePaths).toEqual([ - '/project/integration/src/app/customers/index.ts', - '/project/integration/src/app/holidays/index.ts', - '/project/integration', + '/project/src/app/customers', + '/project/src/app/holidays', ]); }); }); diff --git a/packages/core/src/lib/modules/tests/find-module-paths-without-barrel.spec.ts b/packages/core/src/lib/modules/tests/find-module-paths-without-barrel.spec.ts index 68900df..cc9edcb 100644 --- a/packages/core/src/lib/modules/tests/find-module-paths-without-barrel.spec.ts +++ b/packages/core/src/lib/modules/tests/find-module-paths-without-barrel.spec.ts @@ -16,7 +16,7 @@ function assertModulePaths( expect(Array.from(actualModulePaths)).toEqual(modulePaths); } -describe('create module infos without barrel files', () => { +describe.todo('create module infos without barrel files', () => { it('should have no modules', () => { assertModulePaths( { diff --git a/packages/core/src/lib/modules/tests/get-project-dirs-from-file-info.spec.ts b/packages/core/src/lib/modules/tests/get-project-dirs-from-file-info.spec.ts index c00a7b3..841a531 100644 --- a/packages/core/src/lib/modules/tests/get-project-dirs-from-file-info.spec.ts +++ b/packages/core/src/lib/modules/tests/get-project-dirs-from-file-info.spec.ts @@ -1,12 +1,6 @@ import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import getFs, { useVirtualFs } from '../../fs/getFs'; -import UnassignedFileInfo, { - buildFileInfo, -} from '../../file-info/unassigned-file-info'; -import { getProjectDirsFromFileInfo } from '../get-project-dirs-from-file-info'; -import { toFsPath } from '../../file-info/fs-path'; -import getFs, { useVirtualFs } from '../../fs/getFs'; -import {UnassignedFileInfo} from '../../file-info/unassigned-file-info'; +import { UnassignedFileInfo } from '../../file-info/unassigned-file-info'; import { getProjectDirsFromFileInfo } from '../get-project-dirs-from-file-info'; import { toFsPath } from '../../file-info/fs-path'; import { buildFileInfo } from '../../test/build-file-info'; diff --git a/packages/core/src/lib/util/typed-object-functions.ts b/packages/core/src/lib/util/typed-object-functions.ts new file mode 100644 index 0000000..76665f9 --- /dev/null +++ b/packages/core/src/lib/util/typed-object-functions.ts @@ -0,0 +1,19 @@ +type PropertyKey = string | number | symbol; + +export function keys(value: Record): Key[] { + return Object.keys(value) as Key[]; +} + +export function entries(value: Record): [Key, Value][] { + return Object.entries(value) as [Key, Value][]; +} + +export function fromEntries( + entries: Iterable<[Key, Value]>, +): Record { + return Object.fromEntries(entries) as Record; +} + +export function values(value: Record): Value[] { + return Object.values(value) as Value[]; +}