diff --git a/fileComposition.schema.json b/fileComposition.schema.json index 0325c07..221c9ec 100644 --- a/fileComposition.schema.json +++ b/fileComposition.schema.json @@ -233,6 +233,10 @@ "type": "object", "additionalProperties": false, "properties": { + "projectRoot": { + "type": "string", + "default": "." + }, "filesRules": { "type": "array", "default": [], diff --git a/independentModules.schema.json b/independentModules.schema.json index 7c015f3..b7a25ec 100644 --- a/independentModules.schema.json +++ b/independentModules.schema.json @@ -85,6 +85,10 @@ "type": "boolean", "default": true }, + "tsconfigPath": { + "type": "string", + "default": "./tsconfig.json" + }, "pathAliases": { "type": "object", "default": {}, diff --git a/package.json b/package.json index 4680920..bf921ef 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "author": "Igor Kowalski (Igorkowalski94)", "name": "eslint-plugin-project-structure", - "version": "3.9.2", + "version": "3.10.0", "license": "MIT", "description": "Powerful ESLint plugin with rules to help you achieve a scalable, consistent, and well-structured project. Create your own framework! Define your folder structure, file composition, advanced naming conventions, and create independent modules. Take your project to the next level and save time by automating the review of key principles of a healthy project! react folder structure react file structure react project structure react conventions architecture react next.js angular node solid vue svelte", "keywords": [ diff --git a/src/helpers/cleanUpErrorFromCache.test.ts b/src/helpers/cleanUpErrorFromCache.test.ts index 2948f96..4a2bbd8 100644 --- a/src/helpers/cleanUpErrorFromCache.test.ts +++ b/src/helpers/cleanUpErrorFromCache.test.ts @@ -23,7 +23,7 @@ describe("cleanUpErrorFromCache", () => { expect( cleanUpErrorFromCache({ - cwd: "cwd", + projectRoot: "projectRoot", filename: "", }), ).toEqual(undefined); @@ -37,7 +37,7 @@ describe("cleanUpErrorFromCache", () => { (unlinkSync as jest.Mock).mockImplementation(unlinkSyncMock); cleanUpErrorFromCache({ - cwd: "cwd", + projectRoot: "projectRoot", filename: "filename1", }); @@ -57,12 +57,12 @@ describe("cleanUpErrorFromCache", () => { ); cleanUpErrorFromCache({ - cwd: "cwd", + projectRoot: "projectRoot", filename: "filename1", }); expect(createProjectStructureCacheFileMock).toHaveBeenCalledWith({ - cwd: "cwd", + projectRoot: "projectRoot", projectStructureCache: [ { errorMessage: "error2", filename: "filename2" }, ], diff --git a/src/helpers/cleanUpErrorFromCache.ts b/src/helpers/cleanUpErrorFromCache.ts index 5657974..6342e07 100644 --- a/src/helpers/cleanUpErrorFromCache.ts +++ b/src/helpers/cleanUpErrorFromCache.ts @@ -8,14 +8,14 @@ import { readProjectStructureCacheFile } from "helpers/readProjectStructureCache interface CleanUpErrorFromCacheProps { filename: string; - cwd: string; + projectRoot: string; } export const cleanUpErrorFromCache = ({ - cwd, + projectRoot, filename, }: CleanUpErrorFromCacheProps): void => { - const projectStructureCache = readProjectStructureCacheFile(cwd); + const projectStructureCache = readProjectStructureCacheFile(projectRoot); if (!projectStructureCache) return; @@ -24,10 +24,12 @@ export const cleanUpErrorFromCache = ({ ); if (!projectStructureCacheClean.length) - return unlinkSync(path.join(cwd, PROJECT_STRUCTURE_CACHE_FILE_NAME)); + return unlinkSync( + path.join(projectRoot, PROJECT_STRUCTURE_CACHE_FILE_NAME), + ); createProjectStructureCacheFile({ - cwd, + projectRoot, projectStructureCache: projectStructureCacheClean, }); }; diff --git a/src/helpers/createProjectStructureCacheFile.ts b/src/helpers/createProjectStructureCacheFile.ts index d464e0b..20b3e65 100644 --- a/src/helpers/createProjectStructureCacheFile.ts +++ b/src/helpers/createProjectStructureCacheFile.ts @@ -6,14 +6,14 @@ import { ProjectStructureCache } from "types"; interface CreateProjectStructureCacheFileProps { projectStructureCache: ProjectStructureCache; - cwd: string; + projectRoot: string; } export const createProjectStructureCacheFile = ({ - cwd, + projectRoot, projectStructureCache, }: CreateProjectStructureCacheFileProps): void => { - const filePath = path.join(cwd, PROJECT_STRUCTURE_CACHE_FILE_NAME); + const filePath = path.join(projectRoot, PROJECT_STRUCTURE_CACHE_FILE_NAME); const jsonData = JSON.stringify(projectStructureCache, null, 2); writeFileSync(filePath, jsonData, "utf8"); diff --git a/src/helpers/getProjectRoot.test.ts b/src/helpers/getProjectRoot.test.ts new file mode 100644 index 0000000..878aea2 --- /dev/null +++ b/src/helpers/getProjectRoot.test.ts @@ -0,0 +1,22 @@ +import path from "path"; + +import { getProjectRoot } from "helpers/getProjectRoot"; + +describe("getProjectRoot", () => { + it("should return rootPath when !projectRootConfig", () => { + jest + .spyOn(path, "dirname") + .mockReturnValue(path.resolve("c:/users/project/node_modules/test.ts")); + + expect(getProjectRoot()).toEqual(path.resolve("c:/users/project")); + }); + + it("should return rootPath with projectRootConfig", () => { + jest + .spyOn(path, "dirname") + .mockReturnValue(path.resolve("c:/users/project/node_modules/test.ts")); + expect(getProjectRoot("packages/package-name")).toEqual( + path.resolve("c:/users/project/packages/package-name"), + ); + }); +}); diff --git a/src/helpers/getProjectRoot.ts b/src/helpers/getProjectRoot.ts new file mode 100644 index 0000000..77b6670 --- /dev/null +++ b/src/helpers/getProjectRoot.ts @@ -0,0 +1,12 @@ +import path, { dirname, sep } from "path"; + +export const getProjectRoot = (projectRootConfig?: string): string => { + const dirnameSplit = dirname(__filename).split(sep); + + const indexOfNodeModules = dirnameSplit.indexOf("node_modules"); + const rootPath = dirnameSplit.slice(0, indexOfNodeModules).join(sep); + + if (!projectRootConfig) return rootPath; + + return path.resolve(rootPath, projectRootConfig); +}; diff --git a/src/helpers/handleCache.test.ts b/src/helpers/handleCache.test.ts index b0c8bd9..a81b4db 100644 --- a/src/helpers/handleCache.test.ts +++ b/src/helpers/handleCache.test.ts @@ -25,12 +25,12 @@ describe("handleCache", () => { ); handleCache({ - cwd: "cwd", + projectRoot: "projectRoot", errorCache: { errorMessage: "error", filename: "" }, }); expect(createProjectStructureCacheFileMock).toHaveBeenCalledWith({ - cwd: "cwd", + projectRoot: "projectRoot", projectStructureCache: [{ errorMessage: "error", filename: "" }], }); }); @@ -54,12 +54,12 @@ describe("handleCache", () => { ); handleCache({ - cwd: "cwd", + projectRoot: "projectRoot", errorCache: { errorMessage: "error1", filename: "" }, }); expect(createProjectStructureCacheFileMock).not.toHaveBeenCalledWith({ - cwd: "cwd", + projectRoot: "projectRoot", projectStructureCache: [ { errorMessage: "error1", filename: "1" }, { errorMessage: "error2", filename: "2" }, @@ -86,12 +86,12 @@ describe("handleCache", () => { ); handleCache({ - cwd: "cwd", + projectRoot: "projectRoot", errorCache: { errorMessage: "error3", filename: "3" }, }); expect(createProjectStructureCacheFileMock).not.toHaveBeenCalledWith({ - cwd: "cwd", + projectRoot: "projectRoot", projectStructureCache: [ { errorMessage: "error1", filename: "1" }, { errorMessage: "error2", filename: "2" }, diff --git a/src/helpers/handleCache.ts b/src/helpers/handleCache.ts index 28b7c1a..675b2c2 100644 --- a/src/helpers/handleCache.ts +++ b/src/helpers/handleCache.ts @@ -7,15 +7,18 @@ import { readProjectStructureCacheFile } from "helpers/readProjectStructureCache interface HandleCacheProps { errorCache: ErrorCache; - cwd: string; + projectRoot: string; } -export const handleCache = ({ cwd, errorCache }: HandleCacheProps): void => { - const projectStructureCache = readProjectStructureCacheFile(cwd); +export const handleCache = ({ + projectRoot, + errorCache, +}: HandleCacheProps): void => { + const projectStructureCache = readProjectStructureCacheFile(projectRoot); if (!projectStructureCache) return createProjectStructureCacheFile({ - cwd, + projectRoot, projectStructureCache: [errorCache], }); @@ -30,7 +33,7 @@ export const handleCache = ({ cwd, errorCache }: HandleCacheProps): void => { if (isErrorInCache) return; createProjectStructureCacheFile({ - cwd, + projectRoot, projectStructureCache: [errorCache, ...projectStructureCacheClean], }); }; diff --git a/src/helpers/isErrorInCache.test.ts b/src/helpers/isErrorInCache.test.ts index d2edb22..58d9051 100644 --- a/src/helpers/isErrorInCache.test.ts +++ b/src/helpers/isErrorInCache.test.ts @@ -15,7 +15,7 @@ describe("isErrorInCache", () => { expect( isErrorInCache({ - cwd: "cwd", + projectRoot: "projectRoot", errorCache: { errorMessage: "error", filename: "" }, }), ).toEqual(false); @@ -28,7 +28,7 @@ describe("isErrorInCache", () => { expect( isErrorInCache({ - cwd: "cwd", + projectRoot: "projectRoot", errorCache: { errorMessage: "error", filename: "filename1" }, }), ).toEqual(true); @@ -41,7 +41,7 @@ describe("isErrorInCache", () => { expect( isErrorInCache({ - cwd: "cwd", + projectRoot: "projectRoot", errorCache: { errorMessage: "error", filename: "filename1" }, }), ).toEqual(false); diff --git a/src/helpers/isErrorInCache.ts b/src/helpers/isErrorInCache.ts index c66ef4b..3b087c0 100644 --- a/src/helpers/isErrorInCache.ts +++ b/src/helpers/isErrorInCache.ts @@ -5,19 +5,19 @@ import { readProjectStructureCacheFile } from "helpers/readProjectStructureCache interface IsErrorInCacheProps { errorCache: ErrorCache; - cwd: string; + projectRoot: string; } export const isErrorInCache = ({ - cwd, + projectRoot, errorCache, }: IsErrorInCacheProps): boolean => { handleCache({ - cwd, + projectRoot, errorCache, }); - const projectStructureCache = readProjectStructureCacheFile(cwd); + const projectStructureCache = readProjectStructureCacheFile(projectRoot); const cacheData = projectStructureCache?.find( (cache) => cache.errorMessage === errorCache.errorMessage, diff --git a/src/helpers/readConfigFile/helpers/getConfigPath.test.ts b/src/helpers/readConfigFile/helpers/getConfigPath.test.ts index 939c15d..c464d1f 100644 --- a/src/helpers/readConfigFile/helpers/getConfigPath.test.ts +++ b/src/helpers/readConfigFile/helpers/getConfigPath.test.ts @@ -2,17 +2,22 @@ import path from "path"; import { getMissingConfigFileError } from "errors/getMissingConfigFileError"; +import { getProjectRoot } from "helpers/getProjectRoot"; import { getConfigPath } from "helpers/readConfigFile/helpers/getConfigPath"; +jest.mock("helpers/getProjectRoot", () => ({ getProjectRoot: jest.fn() })); + describe("getConfigPath", () => { afterEach(() => { jest.restoreAllMocks(); }); it("should throw getMissingConfigFileError when config path is missing", () => { - expect(() => - getConfigPath({ cwd: "src", key: "ruleKey", settings: {} }), - ).toThrow(getMissingConfigFileError("ruleKey")); + (getProjectRoot as jest.Mock).mockReturnValue("src"); + + expect(() => getConfigPath({ key: "ruleKey", settings: {} })).toThrow( + getMissingConfigFileError("ruleKey"), + ); }); it("should return config path when settings contain config path - relative", () => { @@ -20,9 +25,10 @@ describe("getConfigPath", () => { .spyOn(path, "resolve") .mockImplementation(() => "C:\\relative\\src\\config.json"); + (getProjectRoot as jest.Mock).mockReturnValue("C:/relative/src"); + expect( getConfigPath({ - cwd: "C:/relative/src", key: "ruleKey", settings: { ruleKey: "config.json" }, }), @@ -34,9 +40,10 @@ describe("getConfigPath", () => { .spyOn(path, "resolve") .mockImplementation(() => "D:\\relative\\src\\config.json"); + (getProjectRoot as jest.Mock).mockReturnValue("C:/absolute/src"); + expect( getConfigPath({ - cwd: "C:/absolute/src", key: "ruleKey", settings: { ruleKey: "D:\\relative\\src\\config.json", diff --git a/src/helpers/readConfigFile/helpers/getConfigPath.ts b/src/helpers/readConfigFile/helpers/getConfigPath.ts index d6f501c..b564fd2 100644 --- a/src/helpers/readConfigFile/helpers/getConfigPath.ts +++ b/src/helpers/readConfigFile/helpers/getConfigPath.ts @@ -4,14 +4,14 @@ import { SharedConfigurationSettings } from "@typescript-eslint/utils/dist/ts-es import { getMissingConfigFileError } from "errors/getMissingConfigFileError"; +import { getProjectRoot } from "helpers/getProjectRoot"; + interface GetConfigPathProps { - cwd: string; settings: SharedConfigurationSettings; key: string; } export const getConfigPath = ({ - cwd, settings, key, }: GetConfigPathProps): string => { @@ -19,5 +19,5 @@ export const getConfigPath = ({ if (!configPath) throw getMissingConfigFileError(key); - return path.resolve(cwd, configPath as string); + return path.resolve(getProjectRoot(), configPath as string); }; diff --git a/src/helpers/readConfigFile/readConfigFile.test.ts b/src/helpers/readConfigFile/readConfigFile.test.ts index 843f28e..527899f 100644 --- a/src/helpers/readConfigFile/readConfigFile.test.ts +++ b/src/helpers/readConfigFile/readConfigFile.test.ts @@ -31,7 +31,6 @@ describe("readConfigFile", () => { it("should return config from options", () => { expect( readConfigFile({ - cwd: "", key: "", options: [{ name: "options" }], settings: {}, @@ -49,7 +48,6 @@ describe("readConfigFile", () => { expect( readConfigFile({ - cwd: "", key: "", options: undefined, settings: {}, @@ -65,7 +63,6 @@ describe("readConfigFile", () => { expect( readConfigFile({ - cwd: "", key: "", options: undefined, settings: {}, @@ -81,7 +78,6 @@ describe("readConfigFile", () => { expect( readConfigFile({ - cwd: "", key: "", options: undefined, settings: {}, @@ -96,7 +92,6 @@ describe("readConfigFile", () => { expect(() => readConfigFile({ - cwd: "", key: "", options: undefined, settings: {}, diff --git a/src/helpers/readConfigFile/readConfigFile.ts b/src/helpers/readConfigFile/readConfigFile.ts index 4fd64f8..10ce12e 100644 --- a/src/helpers/readConfigFile/readConfigFile.ts +++ b/src/helpers/readConfigFile/readConfigFile.ts @@ -10,13 +10,11 @@ import { getConfigPath } from "helpers/readConfigFile/helpers/getConfigPath"; interface ReadConfigFileProps { key: string; - cwd: string; settings: SharedConfigurationSettings; options: T | undefined; } export const readConfigFile = ({ - cwd, key, settings, options, @@ -24,7 +22,6 @@ export const readConfigFile = ({ if (options) return options; const configPath = getConfigPath({ - cwd, key, settings, }); diff --git a/src/helpers/readProjectStructureCacheFile.test.ts b/src/helpers/readProjectStructureCacheFile.test.ts index 1b68e1b..916d099 100644 --- a/src/helpers/readProjectStructureCacheFile.test.ts +++ b/src/helpers/readProjectStructureCacheFile.test.ts @@ -10,7 +10,7 @@ describe("readProjectStructureCacheFile", () => { it("should return config", () => { (readFileSync as jest.Mock).mockReturnValue('{"name":"json"}'); - expect(readProjectStructureCacheFile("cwd")).toEqual({ + expect(readProjectStructureCacheFile("projectRoot")).toEqual({ name: "json", }); }); @@ -18,6 +18,6 @@ describe("readProjectStructureCacheFile", () => { it("should return undefined", () => { (readFileSync as jest.Mock).mockReturnValue(undefined); - expect(readProjectStructureCacheFile("cwd")).toEqual(undefined); + expect(readProjectStructureCacheFile("projectRoot")).toEqual(undefined); }); }); diff --git a/src/helpers/readProjectStructureCacheFile.ts b/src/helpers/readProjectStructureCacheFile.ts index 93c13b9..8a9ae0b 100644 --- a/src/helpers/readProjectStructureCacheFile.ts +++ b/src/helpers/readProjectStructureCacheFile.ts @@ -5,11 +5,14 @@ import { PROJECT_STRUCTURE_CACHE_FILE_NAME } from "consts"; import { ProjectStructureCache } from "types"; export const readProjectStructureCacheFile = ( - cwd: string, + projectRoot: string, ): ProjectStructureCache | undefined => { try { return JSON.parse( - readFileSync(path.join(cwd, PROJECT_STRUCTURE_CACHE_FILE_NAME), "utf-8"), + readFileSync( + path.join(projectRoot, PROJECT_STRUCTURE_CACHE_FILE_NAME), + "utf-8", + ), ) as ProjectStructureCache | undefined; } catch (_e) { return; diff --git a/src/rules/fileComposition/fileComposition.types.ts b/src/rules/fileComposition/fileComposition.types.ts index 7370a31..7cf1b3f 100644 --- a/src/rules/fileComposition/fileComposition.types.ts +++ b/src/rules/fileComposition/fileComposition.types.ts @@ -84,6 +84,7 @@ export interface FileRules { } export interface FileCompositionConfig { + projectRoot?: string; regexParameters?: RegexParameters; filesRules: FileRules[]; } diff --git a/src/rules/fileComposition/helpers/getFileCompositionConfig/getFileCompositionConfig.consts.ts b/src/rules/fileComposition/helpers/getFileCompositionConfig/getFileCompositionConfig.consts.ts index 173455a..4066c0f 100644 --- a/src/rules/fileComposition/helpers/getFileCompositionConfig/getFileCompositionConfig.consts.ts +++ b/src/rules/fileComposition/helpers/getFileCompositionConfig/getFileCompositionConfig.consts.ts @@ -1,6 +1,7 @@ import { JSONSchema4 } from "@typescript-eslint/utils/dist/json-schema"; export const FILE_COMPOSITION_SCHEMA: JSONSchema4 = { + $schema: "http://json-schema.org/draft-07/schema#", definitions: { SelectorType: { type: "string", @@ -234,6 +235,10 @@ export const FILE_COMPOSITION_SCHEMA: JSONSchema4 = { type: "object", additionalProperties: false, properties: { + projectRoot: { + type: "string", + default: ".", + }, filesRules: { type: "array", default: [], diff --git a/src/rules/fileComposition/helpers/getFileCompositionConfig/getFileCompositionConfig.ts b/src/rules/fileComposition/helpers/getFileCompositionConfig/getFileCompositionConfig.ts index 3a7316e..71c70cb 100644 --- a/src/rules/fileComposition/helpers/getFileCompositionConfig/getFileCompositionConfig.ts +++ b/src/rules/fileComposition/helpers/getFileCompositionConfig/getFileCompositionConfig.ts @@ -1,5 +1,6 @@ import path from "path"; +import { getProjectRoot } from "helpers/getProjectRoot"; import { isCorrectPattern } from "helpers/isCorrectPattern"; import { readConfigFile } from "helpers/readConfigFile/readConfigFile"; import { validateConfig } from "helpers/validateConfig"; @@ -17,13 +18,11 @@ interface GetFileCompositionConfigReturn { } export const getFileCompositionConfig = ({ - cwd, filename, settings, options, }: Context): GetFileCompositionConfigReturn => { const config = readConfigFile({ - cwd, key: "project-structure/file-composition-config-path", settings, options: options[0], @@ -31,7 +30,10 @@ export const getFileCompositionConfig = ({ validateConfig({ config, schema: FILE_COMPOSITION_SCHEMA }); - const filenamePath = path.relative(cwd, filename); + const filenamePath = path.relative( + getProjectRoot(config.projectRoot), + filename, + ); const fileConfig = config.filesRules.find(({ filePattern }) => isCorrectPattern({ input: filenamePath, pattern: filePattern }), ); diff --git a/src/rules/fileComposition/helpers/validateFile/validateFile.test.ts b/src/rules/fileComposition/helpers/validateFile/validateFile.test.ts index b3b86f3..da41a8f 100644 --- a/src/rules/fileComposition/helpers/validateFile/validateFile.test.ts +++ b/src/rules/fileComposition/helpers/validateFile/validateFile.test.ts @@ -29,13 +29,16 @@ jest.mock( }), ); +jest.mock("helpers/getProjectRoot", () => ({ + getProjectRoot: jest.fn().mockReturnValue("C:/somePath"), +})); + describe("validateFile", () => { test("Should return undefined if !fileConfig", () => { expect( validateFile({ context: { settings: {}, - cwd: "C:/somePath", filename: "C:/somePath/src/features/Feature1/Feature1.tsx", options: [], report: () => undefined, @@ -64,7 +67,6 @@ describe("validateFile", () => { validateFile({ context: { settings: {}, - cwd: "C:/somePath", filename: "C:/somePath/src/features/Feature1/Feature1.ts", report: reportMock, } as unknown as Context, @@ -93,7 +95,6 @@ describe("validateFile", () => { validateFile({ context: { settings: {}, - cwd: "C:/somePath", filename: "C:/somePath/src/features/Feature1/Feature1.ts", report: reportMock, } as unknown as Context, @@ -148,7 +149,6 @@ describe("validateFile", () => { context: { report: reportMock, settings: {}, - cwd: "C:/somePath", filename: "C:/somePath/src/features/Feature1/Feature1.ts", }, expressionName: undefined, @@ -180,7 +180,6 @@ describe("validateFile", () => { validateFile({ context: { settings: {}, - cwd: "C:/somePath", filename: "C:/somePath/src/features/Feature1/Feature1.ts", report: reportMock, } as unknown as Context, @@ -234,7 +233,6 @@ describe("validateFile", () => { context: { report: reportMock, settings: {}, - cwd: "C:/somePath", filename: "C:/somePath/src/features/Feature1/Feature1.ts", }, expressionName: undefined, @@ -266,7 +264,6 @@ describe("validateFile", () => { validateFile({ context: { settings: {}, - cwd: "C:/somePath", filename: "C:/somePath/src/features/Feature1/Feature1.ts", report: reportMock, } as unknown as Context, @@ -320,7 +317,6 @@ describe("validateFile", () => { context: { report: reportMock, settings: {}, - cwd: "C:/somePath", filename: "C:/somePath/src/features/Feature1/Feature1.ts", }, expressionName: undefined, diff --git a/src/rules/fileComposition/helpers/validateFile/validateFile.ts b/src/rules/fileComposition/helpers/validateFile/validateFile.ts index f68ba2b..332d9c9 100644 --- a/src/rules/fileComposition/helpers/validateFile/validateFile.ts +++ b/src/rules/fileComposition/helpers/validateFile/validateFile.ts @@ -1,5 +1,7 @@ import path from "path"; +import { getProjectRoot } from "helpers/getProjectRoot"; + import { Context, FileCompositionConfig, @@ -29,7 +31,7 @@ export const validateFile = ({ name, expressionName, context, - context: { cwd, filename }, + context: { filename }, node, nodeType, fileConfig, @@ -47,7 +49,10 @@ export const validateFile = ({ const nestedSelectorsRules = rules.filter(({ scope }) => isCorrectScope({ expect: "nestedSelectors", scope }), ); - const filenamePath = path.relative(cwd, filename); + const filenamePath = path.relative( + getProjectRoot(config.projectRoot), + filename, + ); const regexParameters = config.regexParameters; const selectorType = SELECTORS[nodeType]; diff --git a/src/rules/folderStructure/helpers/handleProgram.test.ts b/src/rules/folderStructure/helpers/handleProgram.test.ts index 8ede346..a72ac04 100644 --- a/src/rules/folderStructure/helpers/handleProgram.test.ts +++ b/src/rules/folderStructure/helpers/handleProgram.test.ts @@ -4,6 +4,7 @@ import { PROJECT_STRUCTURE_CACHE_FILE_NAME } from "consts"; import { FinalError } from "errors/FinalError"; +import { getProjectRoot } from "helpers/getProjectRoot"; import { isErrorInCache } from "helpers/isErrorInCache"; import { readConfigFile } from "helpers/readConfigFile/readConfigFile"; @@ -24,6 +25,10 @@ jest.mock("helpers/readConfigFile/readConfigFile", () => ({ readConfigFile: jest.fn(), })); +jest.mock("helpers/getProjectRoot", () => ({ + getProjectRoot: jest.fn(), +})); + jest.mock("helpers/isErrorInCache", () => ({ isErrorInCache: jest.fn(), })); @@ -38,12 +43,15 @@ describe("validateImport", () => { throw new FinalError("error"); }); + (getProjectRoot as jest.Mock).mockReturnValue( + path.resolve("C:/Users/eslint-plugin-project-structure"), + ); + handleProgram({ context: { report: reportMock, settings: {}, options: [], - cwd: path.resolve("C:/Users/eslint-plugin-project-structure"), filename: path.resolve( "C:/Users/eslint-plugin-project-structure/file.ts", ), @@ -66,12 +74,15 @@ describe("validateImport", () => { (isErrorInCache as jest.Mock).mockReturnValue(true); + (getProjectRoot as jest.Mock).mockReturnValue( + path.resolve("C:/Users/eslint-plugin-project-structure"), + ); + handleProgram({ context: { report: reportMock, settings: {}, options: [], - cwd: path.resolve("C:/Users/eslint-plugin-project-structure"), filename: path.resolve( "C:/Users/eslint-plugin-project-structure/file.ts", ), @@ -93,12 +104,15 @@ describe("validateImport", () => { validateFolderStructureMock, ); + (getProjectRoot as jest.Mock).mockReturnValue( + path.resolve("C:/Users/eslint-plugin-project-structure/src"), + ); + handleProgram({ context: { report: reportMock, settings: {}, options: [], - cwd: path.resolve("C:/Users/eslint-plugin-project-structure/src"), filename: path.resolve( "C:/Users/eslint-plugin-project-structure/file.ts", ), @@ -120,12 +134,15 @@ describe("validateImport", () => { validateFolderStructureMock, ); + (getProjectRoot as jest.Mock).mockReturnValue( + path.resolve("C:/Users/eslint-plugin-project-structure"), + ); + handleProgram({ context: { report: reportMock, settings: {}, options: [], - cwd: path.resolve("C:/Users/eslint-plugin-project-structure"), filename: path.resolve( `C:/Users/eslint-plugin-project-structure/${PROJECT_STRUCTURE_CACHE_FILE_NAME}`, ), @@ -146,13 +163,16 @@ describe("validateImport", () => { throw new Error("random error"); }); + (getProjectRoot as jest.Mock).mockReturnValue( + path.resolve("C:/Users/eslint-plugin-project-structure"), + ); + expect(() => handleProgram({ context: { report: reportMock, settings: {}, options: [], - cwd: path.resolve("C:/Users/eslint-plugin-project-structure"), filename: path.resolve( "C:/Users/eslint-plugin-project-structure/file.ts", ), diff --git a/src/rules/folderStructure/helpers/handleProgram.ts b/src/rules/folderStructure/helpers/handleProgram.ts index 83c2f0e..e6effc7 100644 --- a/src/rules/folderStructure/helpers/handleProgram.ts +++ b/src/rules/folderStructure/helpers/handleProgram.ts @@ -6,6 +6,7 @@ import { PROJECT_STRUCTURE_CACHE_FILE_NAME } from "consts"; import { finalErrorGuard } from "errors/finalErrorGuard"; import { cleanUpErrorFromCache } from "helpers/cleanUpErrorFromCache"; +import { getProjectRoot } from "helpers/getProjectRoot"; import { isErrorInCache } from "helpers/isErrorInCache"; import { readConfigFile } from "helpers/readConfigFile/readConfigFile"; @@ -21,17 +22,16 @@ export interface HandleProgramProps { } export const handleProgram = ({ - context: { cwd, settings, filename, options, report }, + context: { settings, filename, options, report }, node, }: HandleProgramProps): void => { const config = readConfigFile({ - cwd, key: "project-structure/folder-structure-config-path", settings, options: options[0], }); - const projectRoot = path.resolve(cwd, config.projectRoot ?? "."); + const projectRoot = getProjectRoot(config.projectRoot); const structureRoot = path.resolve(projectRoot, config.structureRoot ?? "."); if ( @@ -41,14 +41,19 @@ export const handleProgram = ({ return; try { - validateFolderStructure({ filename, cwd: structureRoot, config }); - cleanUpErrorFromCache({ cwd: projectRoot, filename }); + validateFolderStructure({ + filename, + structureRoot, + projectRoot, + config, + }); + cleanUpErrorFromCache({ projectRoot, filename }); } catch (error) { if (!finalErrorGuard(error)) throw error; if ( isErrorInCache({ - cwd: projectRoot, + projectRoot, errorCache: { filename, errorMessage: error.message, diff --git a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/checkNodeExistence.test.ts b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/checkNodeExistence.test.ts index bd647d9..2ae27f3 100644 --- a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/checkNodeExistence.test.ts +++ b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/checkNodeExistence.test.ts @@ -35,11 +35,13 @@ describe("checkNodeExistence", () => { expect(() => checkNodeExistence({ - cwd: "...", + structureRoot: "...", enforceExistence: ["{nodeName}.stories.tsx", "test.ts"], nodeName: "Feature1.tsx", nodePath: "src/features/Feature1/Feature1.tsx", nodeType: "File", + projectRoot: "...", + structureRootConfig: "...", }), ).not.toThrow(); }); @@ -63,11 +65,13 @@ describe("checkNodeExistence", () => { expect(() => checkNodeExistence({ - cwd: "...", + structureRoot: "...", enforceExistence: ["{nodeName2}.{PascalCase}.stories.tsx", "test.ts"], nodeName: "Feature1.tsx", nodePath: "src/features/Feature1/Feature1.tsx", nodeType: "File", + projectRoot: "...", + structureRootConfig: "...", }), ).toThrow( getInvalidReferenceError({ @@ -89,11 +93,13 @@ describe("checkNodeExistence", () => { expect(() => checkNodeExistence({ - cwd: "...", + structureRoot: "...", enforceExistence: ["test.ts"], nodeName: "Feature1", nodePath: "src/features/Feature1", nodeType: "Folder", + projectRoot: "...", + structureRootConfig: "...", }), ).not.toThrow(); }); @@ -109,11 +115,13 @@ describe("checkNodeExistence", () => { expect(() => checkNodeExistence({ - cwd: "...", + structureRoot: "...", enforceExistence: "test.ts", nodeName: "Feature1", nodePath: "src/features/Feature1", nodeType: "Folder", + projectRoot: "...", + structureRootConfig: "...", }), ).not.toThrow(); }); @@ -121,11 +129,13 @@ describe("checkNodeExistence", () => { it("should throw when node do not exist folder", () => { expect(() => checkNodeExistence({ - cwd: "...", + structureRoot: "...", enforceExistence: ["test.ts", "tests/test.ts"], nodeName: "features", nodePath: "src/features", nodeType: "Folder", + projectRoot: "...", + structureRootConfig: "...", }), ).toThrow( getNodeExistenceError({ @@ -134,7 +144,7 @@ describe("checkNodeExistence", () => { "./src/features/tests/test.ts", ], nodeName: "features", - nodePath: "src/features", + nodePath: ".../src/features", nodeType: "Folder", }), ); @@ -143,11 +153,13 @@ describe("checkNodeExistence", () => { it("should throw when node do not exist folder file", () => { expect(() => checkNodeExistence({ - cwd: "...", + structureRoot: "...", enforceExistence: ["test.ts", "tests/test.ts"], nodeName: "Feature1.tsx", nodePath: "src/features/Feature1/Feature1.tsx", nodeType: "File", + projectRoot: "...", + structureRootConfig: "...", }), ).toThrow( getNodeExistenceError({ @@ -156,7 +168,7 @@ describe("checkNodeExistence", () => { "./src/features/Feature1/tests/test.ts", ], nodeName: "Feature1.tsx", - nodePath: "src/features/Feature1/Feature1.tsx", + nodePath: ".../src/features/Feature1/Feature1.tsx", nodeType: "File", }), ); diff --git a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/checkNodeExistence.ts b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/checkNodeExistence.ts index b13380a..3a28f39 100644 --- a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/checkNodeExistence.ts +++ b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/checkNodeExistence.ts @@ -6,21 +6,26 @@ import { transformStringToCase } from "helpers/transformStringToCase"; import { getNodeExistenceError } from "rules/folderStructure/errors/getNodeExistenceError"; import { NodeType } from "rules/folderStructure/folderStructure.types"; +import { getNodePathWithStructureRoot } from "rules/folderStructure/helpers/validateFolderStructure/helpers/getNodePathWithStructureRoot"; interface CheckNodeExistenceProps { - cwd: string; + structureRoot: string; nodeName: string; enforceExistence: string[] | string; nodeType: NodeType; nodePath: string; + structureRootConfig?: string; + projectRoot: string; } export const checkNodeExistence = ({ enforceExistence, nodeName, nodeType, - cwd, + structureRoot, nodePath, + structureRootConfig, + projectRoot, }: CheckNodeExistenceProps): void => { const nodeDirname = path.dirname(nodePath); const currentNodeName = @@ -62,7 +67,7 @@ export const checkNodeExistence = ({ }); const enforcedNodeFullPath = path.join( - cwd, + structureRoot, currentDirname, enforcedNodeNameWithoutRef, ); @@ -71,9 +76,7 @@ export const checkNodeExistence = ({ return ( "./" + - path - .join(currentDirname, enforcedNodeNameWithoutRef) - .replaceAll(sep, "/") + path.relative(projectRoot, enforcedNodeFullPath).replaceAll(sep, "/") ); }) .filter((v): v is string => v !== undefined); @@ -84,6 +87,9 @@ export const checkNodeExistence = ({ enforcedNodeNames, nodeName, nodeType, - nodePath, + nodePath: getNodePathWithStructureRoot({ + nodePath, + structureRoot: structureRootConfig, + }), }); }; diff --git a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/getNodePath.ts b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/getNodePath.ts index b7b0990..6d23bd0 100644 --- a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/getNodePath.ts +++ b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/getNodePath.ts @@ -1,12 +1,12 @@ interface GetNodePathProps { - filenameWithoutCwd: string; + filenameWithoutProjectRoot: string; nodeName: string; pathname: string; } export const getNodePath = ({ - filenameWithoutCwd, + filenameWithoutProjectRoot, nodeName, pathname, }: GetNodePathProps): string => - filenameWithoutCwd.replace(pathname, "") + nodeName; + filenameWithoutProjectRoot.replace(pathname, "") + nodeName; diff --git a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/getNodePathWithStructureRoot.ts b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/getNodePathWithStructureRoot.ts new file mode 100644 index 0000000..c98e6c3 --- /dev/null +++ b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/getNodePathWithStructureRoot.ts @@ -0,0 +1,12 @@ +import path, { sep } from "path"; + +interface GetNodePathWithStructureRootProps { + structureRoot?: string; + nodePath: string; +} + +export const getNodePathWithStructureRoot = ({ + nodePath, + structureRoot, +}: GetNodePathWithStructureRootProps): string => + path.join(structureRoot ?? "", nodePath).replaceAll(sep, "/"); diff --git a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/getPathname.ts b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/getPathname.ts index 81c4210..6f46a82 100644 --- a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/getPathname.ts +++ b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/getPathname.ts @@ -1,9 +1,9 @@ import path, { sep } from "path"; interface GetPathnameProps { - cwd: string; + root: string; filename: string; } -export const getPathname = ({ cwd, filename }: GetPathnameProps): string => - path.relative(cwd, filename).replaceAll(sep, "/"); +export const getPathname = ({ root, filename }: GetPathnameProps): string => + path.relative(root, filename).replaceAll(sep, "/"); diff --git a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validateLongPath.test.ts b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validateLongPath.test.ts index 7c30a6d..ac67fc3 100644 --- a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validateLongPath.test.ts +++ b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validateLongPath.test.ts @@ -11,7 +11,7 @@ describe("validateFolderStructure", () => { validateLongPath({ filename: "", longPathsInfo: false, - cwd: "C:/Users/eslint-plugin-project-structure", + projectRoot: "C:/Users/eslint-plugin-project-structure", }), ).toEqual(undefined); }); @@ -21,7 +21,7 @@ describe("validateFolderStructure", () => { validateLongPath({ filename: "C:/Users/eslint-plugin-project-structure/features/Feature.tsx", - cwd: "C:/Users/eslint-plugin-project-structure", + projectRoot: "C:/Users/eslint-plugin-project-structure", }), ).toEqual(undefined); }); @@ -33,7 +33,7 @@ describe("validateFolderStructure", () => { validateLongPath({ filename: `C:/Users/eslint-plugin-project-structure/${new Array(240).fill("0").join("")}`, - cwd: "C:/Users/eslint-plugin-project-structure", + projectRoot: "C:/Users/eslint-plugin-project-structure", }); expect(getLongPathErrorMock).toHaveBeenCalledWith({ @@ -50,7 +50,7 @@ describe("validateFolderStructure", () => { validateLongPath({ longPathsInfo: { mode: "warn", root: "../../", maxLength: 3 }, - cwd: "C:/hello/Users/eslint-plugin-project-structure", + projectRoot: "C:/hello/Users/eslint-plugin-project-structure", filename: "C:/hello/Users/eslint-plugin-project-structure/src/features/Feature1.tsx", }); @@ -69,7 +69,7 @@ describe("validateFolderStructure", () => { validateLongPath({ longPathsInfo: { mode: "warn", countFromSystemRoot: true, maxLength: 3 }, - cwd: "C:/hello/Users/eslint-plugin-project-structure", + projectRoot: "C:/hello/Users/eslint-plugin-project-structure", filename: "C:/hello/Users/eslint-plugin-project-structure/src/features/Feature1.tsx", }); @@ -87,7 +87,7 @@ describe("validateFolderStructure", () => { longPathsInfo: { mode: "error", maxLength: 3 }, filename: "C:/Users/eslint-plugin-project-structure/features/Feature.tsx", - cwd: "C:/Users/eslint-plugin-project-structure", + projectRoot: "C:/Users/eslint-plugin-project-structure", }), ).toThrow(); }); diff --git a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validateLongPath.ts b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validateLongPath.ts index f2ee17c..d03f84e 100644 --- a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validateLongPath.ts +++ b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validateLongPath.ts @@ -8,13 +8,13 @@ import { getPathname } from "rules/folderStructure/helpers/validateFolderStructu interface ValidateLongPathProps { filename: string; - cwd: string; + projectRoot: string; longPathsInfo?: LongPathsInfo | false; } export const validateLongPath = ({ filename, - cwd, + projectRoot, longPathsInfo, }: ValidateLongPathProps): void => { if (longPathsInfo === false) return; @@ -22,7 +22,7 @@ export const validateLongPath = ({ const currentPath = longPathsInfo?.countFromSystemRoot ? filename : getPathname({ - cwd: path.resolve(cwd, longPathsInfo?.root ?? ".."), + root: path.resolve(projectRoot, longPathsInfo?.root ?? ".."), filename, }); diff --git a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validatePath/validatePath.test.ts b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validatePath/validatePath.test.ts index b80f238..d00bdd9 100644 --- a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validatePath/validatePath.test.ts +++ b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validatePath/validatePath.test.ts @@ -15,8 +15,8 @@ describe("validatePath", () => { expect( validatePath({ pathname: "src/componentName/componentName.tsx", - filenameWithoutCwd: "src/componentName/componentName.tsx", - cwd: "projectName", + filenameWithoutProjectRoot: "src/componentName/componentName.tsx", + structureRoot: "projectName", folderName: "projectName", rule: { name: "*", @@ -25,6 +25,7 @@ describe("validatePath", () => { config: { structure: [], }, + projectRoot: "...", }), ).toEqual(undefined); }); @@ -33,8 +34,8 @@ describe("validatePath", () => { expect(() => validatePath({ pathname: "src/componentName/componentName.tsx", - filenameWithoutCwd: "src/componentName/componentName.tsx", - cwd: "projectName", + filenameWithoutProjectRoot: "src/componentName/componentName.tsx", + structureRoot: "projectName", folderName: "projectName", rule: { name: "*", @@ -61,6 +62,7 @@ describe("validatePath", () => { }, ], }, + projectRoot: "...", }), ).toThrow( getNodeTypeError({ @@ -76,8 +78,8 @@ describe("validatePath", () => { expect(() => validatePath({ pathname: "src/componentName.tsx", - filenameWithoutCwd: "src/componentName.tsx", - cwd: "projectName", + filenameWithoutProjectRoot: "src/componentName.tsx", + structureRoot: "projectName", folderName: "projectName", rule: { name: "*", @@ -104,6 +106,7 @@ describe("validatePath", () => { }, ], }, + projectRoot: "...", }), ).toThrow( getNameError({ @@ -124,8 +127,8 @@ describe("validatePath", () => { validatePath({ pathname: "src/ComponentName.tsx", - filenameWithoutCwd: "src/ComponentName.tsx", - cwd: "projectName", + filenameWithoutProjectRoot: "src/ComponentName.tsx", + structureRoot: "projectName", folderName: "projectName", rule: { name: "*", @@ -154,6 +157,7 @@ describe("validatePath", () => { }, ], }, + projectRoot: "...", }); expect(checkNodeExistence).toHaveBeenCalled(); diff --git a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validatePath/validatePath.ts b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validatePath/validatePath.ts index 20f1258..fbdbf78 100644 --- a/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validatePath/validatePath.ts +++ b/src/rules/folderStructure/helpers/validateFolderStructure/helpers/validatePath/validatePath.ts @@ -6,6 +6,7 @@ import { } from "rules/folderStructure/folderStructure.types"; import { checkNodeExistence } from "rules/folderStructure/helpers/validateFolderStructure/helpers/checkNodeExistence"; import { getNodePath } from "rules/folderStructure/helpers/validateFolderStructure/helpers/getNodePath"; +import { getNodePathWithStructureRoot } from "rules/folderStructure/helpers/validateFolderStructure/helpers/getNodePathWithStructureRoot"; import { getRule } from "rules/folderStructure/helpers/validateFolderStructure/helpers/getRule"; import { getChildren } from "rules/folderStructure/helpers/validateFolderStructure/helpers/validatePath/helpers/getChildren/getChildren"; import { getNextPathname } from "rules/folderStructure/helpers/validateFolderStructure/helpers/validatePath/helpers/getNextPathname"; @@ -16,20 +17,22 @@ import { getNodeType } from "rules/folderStructure/helpers/validateFolderStructu interface ValidatePathProps { pathname: string; - filenameWithoutCwd: string; + filenameWithoutProjectRoot: string; folderName: string; rule: Rule; config: FolderStructureConfig; - cwd: string; + structureRoot: string; + projectRoot: string; } export const validatePath = ({ pathname, - filenameWithoutCwd, + filenameWithoutProjectRoot, folderName, rule, config, - cwd, + structureRoot, + projectRoot, }: ValidatePathProps): void => { const { rules, regexParameters } = config; @@ -50,7 +53,11 @@ export const validatePath = ({ regexParameters, }); - const nodePath = getNodePath({ filenameWithoutCwd, nodeName, pathname }); + const nodePath = getNodePath({ + filenameWithoutProjectRoot, + nodeName, + pathname, + }); if (!nodeRule) { const allowedNames = getNodeAllowedNames({ @@ -59,10 +66,25 @@ export const validatePath = ({ folderName, }); + const nodePathWithStructureRoot = getNodePathWithStructureRoot({ + nodePath, + structureRoot: config.structureRoot, + }); + if (!allowedNames.length) - throw getNodeTypeError({ nodePath, nodeType, nodeName, folderName }); + throw getNodeTypeError({ + nodePath: nodePathWithStructureRoot, + nodeType, + nodeName, + folderName, + }); - throw getNameError({ allowedNames, nodeName, nodePath, nodeType }); + throw getNameError({ + allowedNames, + nodeName, + nodePath: nodePathWithStructureRoot, + nodeType, + }); } const { children, enforceExistence, name } = getRule({ @@ -74,9 +96,11 @@ export const validatePath = ({ checkNodeExistence({ enforceExistence, nodeName, - cwd, + structureRoot, nodePath, nodeType, + structureRootConfig: config.structureRoot, + projectRoot, }); if (children) { @@ -84,11 +108,12 @@ export const validatePath = ({ validatePath({ pathname: nextPathname, - filenameWithoutCwd, + filenameWithoutProjectRoot, folderName: nodeName, rule: { name, enforceExistence, children }, config, - cwd, + structureRoot, + projectRoot, }); } }; diff --git a/src/rules/folderStructure/helpers/validateFolderStructure/validateFolderStructure.test.ts b/src/rules/folderStructure/helpers/validateFolderStructure/validateFolderStructure.test.ts index 8c224d7..53b6e1f 100644 --- a/src/rules/folderStructure/helpers/validateFolderStructure/validateFolderStructure.test.ts +++ b/src/rules/folderStructure/helpers/validateFolderStructure/validateFolderStructure.test.ts @@ -1,4 +1,4 @@ -import path, { sep } from "path"; +import path from "path"; import { validateConfig } from "helpers/validateConfig"; @@ -38,7 +38,8 @@ describe("validateFolderStructure", () => { expect( validateFolderStructure({ - cwd: path.join("C:", "rootFolderName"), + structureRoot: path.join("C:", "rootFolderName"), + projectRoot: path.join("C:", "rootFolderName"), config: { structure: [{ name: "camelCase.tsx" }] }, filename: path.join( "C:", @@ -61,7 +62,8 @@ describe("validateFolderStructure", () => { (validateConfig as jest.Mock).mockImplementation(); validateFolderStructure({ - cwd: path.join("C:", "rootFolderName"), + structureRoot: path.join("C:", "rootFolderName"), + projectRoot: path.join("C:", "rootFolderName"), config: { structure: { enforceExistence: ["./src/test.ts"], @@ -81,8 +83,9 @@ describe("validateFolderStructure", () => { enforceExistence: ["./src/test.ts"], nodeName: "rootFolderName", nodeType: "Folder", - cwd: path.join("C:", "rootFolderName"), - nodePath: path.join("C:", "rootFolderName").replaceAll(sep, "/"), + structureRoot: path.join("C:", "rootFolderName"), + projectRoot: path.join("C:", "rootFolderName"), + nodePath: "", }); }); @@ -94,7 +97,8 @@ describe("validateFolderStructure", () => { (validateConfig as jest.Mock).mockImplementation(); validateFolderStructure({ - cwd: path.join("C:", "rootFolderName"), + structureRoot: path.join("C:", "rootFolderName"), + projectRoot: path.join("C:", "rootFolderName"), config: { structure: { name: "name", children: [{ name: "camelCase.tsx" }] }, }, @@ -109,8 +113,9 @@ describe("validateFolderStructure", () => { expect(validatePathMock).toHaveBeenCalledWith({ pathname: "src/features/ComponentName.tsx", - filenameWithoutCwd: "src/features/ComponentName.tsx", - cwd: path.join("C:", "rootFolderName"), + filenameWithoutProjectRoot: "src/features/ComponentName.tsx", + structureRoot: path.join("C:", "rootFolderName"), + projectRoot: path.join("C:", "rootFolderName"), folderName: "rootFolderName", rule: { name: "rootFolderName", children: [{ name: "camelCase.tsx" }] }, config: { diff --git a/src/rules/folderStructure/helpers/validateFolderStructure/validateFolderStructure.ts b/src/rules/folderStructure/helpers/validateFolderStructure/validateFolderStructure.ts index 6c02f76..150c88d 100644 --- a/src/rules/folderStructure/helpers/validateFolderStructure/validateFolderStructure.ts +++ b/src/rules/folderStructure/helpers/validateFolderStructure/validateFolderStructure.ts @@ -1,4 +1,4 @@ -import path, { sep } from "path"; +import path from "path"; import { validateConfig } from "helpers/validateConfig"; @@ -14,13 +14,15 @@ import { FOLDER_STRUCTURE_SCHEMA } from "rules/folderStructure/helpers/validateF interface ValidateFolderStructureProps { filename: string; - cwd: string; + structureRoot: string; + projectRoot: string; config: FolderStructureConfig; } export const validateFolderStructure = ({ filename, - cwd, + structureRoot, + projectRoot, config, }: ValidateFolderStructureProps): void => { const { structure, ignorePatterns, longPathsInfo, rules } = config; @@ -28,37 +30,40 @@ export const validateFolderStructure = ({ validateConfig({ config, schema: FOLDER_STRUCTURE_SCHEMA }); const rulesWithFolderRecursion = extractFolderRecursionFromRules(rules); - const rootFolderName = path.basename(cwd); + const rootFolderName = path.basename(structureRoot); const rootRule = getRootRule({ structure, rootFolderName, rules: rulesWithFolderRecursion, }); const pathname = getPathname({ - cwd, + root: structureRoot, filename, }); if (isIgnoredPathname({ pathname, ignorePatterns })) return; - validateLongPath({ filename, cwd, longPathsInfo }); + validateLongPath({ filename, projectRoot, longPathsInfo }); if (rootRule.enforceExistence) { checkNodeExistence({ enforceExistence: rootRule.enforceExistence, nodeName: rootFolderName, nodeType: "Folder", - cwd, - nodePath: cwd.replaceAll(sep, "/"), + structureRoot, + nodePath: "", + structureRootConfig: config.structureRoot, + projectRoot, }); } validatePath({ pathname, - filenameWithoutCwd: pathname, - cwd, + filenameWithoutProjectRoot: pathname, + structureRoot, folderName: rootFolderName, rule: rootRule, config: { ...config, rules: rulesWithFolderRecursion }, + projectRoot, }); }; diff --git a/src/rules/independentModules/errors/getImportPathNotExistsError.ts b/src/rules/independentModules/errors/getImportPathNotExistsError.ts new file mode 100644 index 0000000..ceea643 --- /dev/null +++ b/src/rules/independentModules/errors/getImportPathNotExistsError.ts @@ -0,0 +1,6 @@ +import { FinalError } from "errors/FinalError"; + +export const getImportPathNotExistsError = (): FinalError => + new FinalError( + `🔥 This import does not exist. If the import includes a path alias, make sure that you have added the alias to the configuration. 🔥`, + ); diff --git a/src/rules/independentModules/helpers/getIndependentModulesConfig/getIndependentModulesConfig.consts.ts b/src/rules/independentModules/helpers/getIndependentModulesConfig/getIndependentModulesConfig.consts.ts index 1c0c8c3..0209c91 100644 --- a/src/rules/independentModules/helpers/getIndependentModulesConfig/getIndependentModulesConfig.consts.ts +++ b/src/rules/independentModules/helpers/getIndependentModulesConfig/getIndependentModulesConfig.consts.ts @@ -87,6 +87,10 @@ export const INDEPENDENT_MODULES_SCHEMA: JSONSchema4 = { type: "boolean", default: true, }, + tsconfigPath: { + type: "string", + default: "./tsconfig.json", + }, pathAliases: { type: "object", default: {}, diff --git a/src/rules/independentModules/helpers/getIndependentModulesConfig/getIndependentModulesConfig.ts b/src/rules/independentModules/helpers/getIndependentModulesConfig/getIndependentModulesConfig.ts index d7c7dfb..58baaca 100644 --- a/src/rules/independentModules/helpers/getIndependentModulesConfig/getIndependentModulesConfig.ts +++ b/src/rules/independentModules/helpers/getIndependentModulesConfig/getIndependentModulesConfig.ts @@ -9,12 +9,10 @@ import { } from "rules/independentModules/independentModules.types"; export const getIndependentModulesConfig = ({ - cwd, options, settings, }: Context): IndependentModulesConfig => { const config = readConfigFile({ - cwd, key: "project-structure/independent-modules-config-path", settings, options: options[0], @@ -22,7 +20,7 @@ export const getIndependentModulesConfig = ({ validateConfig({ config, schema: INDEPENDENT_MODULES_SCHEMA }); - const pathAliases = getPathAliases({ cwd, config }); + const pathAliases = getPathAliases({ config }); return { ...config, pathAliases }; }; diff --git a/src/rules/independentModules/helpers/getIndependentModulesConfig/helpers/getPathAliases.test.ts b/src/rules/independentModules/helpers/getIndependentModulesConfig/helpers/getPathAliases.test.ts index a525e48..0fb98e5 100644 --- a/src/rules/independentModules/helpers/getIndependentModulesConfig/helpers/getPathAliases.test.ts +++ b/src/rules/independentModules/helpers/getIndependentModulesConfig/helpers/getPathAliases.test.ts @@ -15,6 +15,10 @@ jest.mock("fs", () => ({ readFileSync: jest.fn(), })); +jest.mock("helpers/getProjectRoot", () => ({ + getProjectRoot: jest.fn().mockReturnValue("projectRoot"), +})); + describe("getPathAliases", () => { test.each<{ config: Partial; @@ -81,7 +85,6 @@ describe("getPathAliases", () => { expect( getPathAliases({ config: config as IndependentModulesConfig, - cwd: "cwd", }), ).toEqual(expected); }); @@ -94,7 +97,6 @@ describe("getPathAliases", () => { expect( getPathAliases({ config: { modules: [] }, - cwd: "cwd", }), ).toEqual(undefined); }); diff --git a/src/rules/independentModules/helpers/getIndependentModulesConfig/helpers/getPathAliases.ts b/src/rules/independentModules/helpers/getIndependentModulesConfig/helpers/getPathAliases.ts index 8149117..792366e 100644 --- a/src/rules/independentModules/helpers/getIndependentModulesConfig/helpers/getPathAliases.ts +++ b/src/rules/independentModules/helpers/getIndependentModulesConfig/helpers/getPathAliases.ts @@ -3,6 +3,8 @@ import path from "path"; import { parse } from "comment-json"; +import { getProjectRoot } from "helpers/getProjectRoot"; + import { DEFAULT_BASE_URL } from "rules/independentModules/independentModules.consts"; import { IndependentModulesConfig, @@ -11,19 +13,21 @@ import { } from "rules/independentModules/independentModules.types"; interface GetPathAliasesProps { - cwd: string; config: IndependentModulesConfig; } export const getPathAliases = ({ - cwd, config, }: GetPathAliasesProps): PathAliases | undefined => { const { pathAliases } = config; if (pathAliases) return pathAliases; - const tsconfigPath = config.tsconfigPath ?? path.join(cwd, "tsconfig.json"); + const projectRoot = getProjectRoot(); + + const tsconfigPath = config.tsconfigPath + ? path.resolve(projectRoot, config.tsconfigPath) + : path.join(projectRoot, "tsconfig.json"); let tsconfig: TsConfigJson | undefined; diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/addExtensionToImportPath/addExtensionToImportPath.test.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/addExtensionToImportPath/addExtensionToImportPath.test.ts index 607ba44..7290714 100644 --- a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/addExtensionToImportPath/addExtensionToImportPath.test.ts +++ b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/addExtensionToImportPath/addExtensionToImportPath.test.ts @@ -118,9 +118,9 @@ describe("addExtensionToImportPath", () => { expect( addExtensionToImportPath({ importPath, - cwdWithRoot: "C:\\Users\\user\\Desktop\\repo\\src\\", + projectRootWithBaseUrl: "C:\\Users\\user\\Desktop\\repo\\src\\", extensions, - cwd: "C:\\Users\\user\\Desktop\\repo", + projectRoot: "C:\\Users\\user\\Desktop\\repo", }), ).toEqual(expected); diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/addExtensionToImportPath/addExtensionToImportPath.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/addExtensionToImportPath/addExtensionToImportPath.ts index 83749f6..39a4702 100644 --- a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/addExtensionToImportPath/addExtensionToImportPath.ts +++ b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/addExtensionToImportPath/addExtensionToImportPath.ts @@ -6,16 +6,16 @@ import { IndependentModulesConfig } from "rules/independentModules/independentMo interface AddExtensionToImportPathProps { importPath: string; - cwdWithRoot: string; extensions: IndependentModulesConfig["extensions"]; - cwd: string; + projectRoot: string; + projectRootWithBaseUrl: string; } export const addExtensionToImportPath = ({ importPath, - cwdWithRoot, extensions = [], - cwd, + projectRoot, + projectRootWithBaseUrl, }: AddExtensionToImportPathProps): string => { const allExtensions = [...FILE_EXTENSIONS, ...extensions]; @@ -36,7 +36,11 @@ export const addExtensionToImportPath = ({ fullImportPathExternalTypesNodeIndex, fullImportPathExternalNode, fullImportPathExternalNodeIndex, - } = getFullImportPathVariants({ importPath, cwdWithRoot, cwd }); + } = getFullImportPathVariants({ + importPath, + projectRoot, + projectRootWithBaseUrl, + }); const fullImportPathsWithIndex = [ fullImportPathIndex, diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/checkImportPath.test.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/checkImportPath.test.ts index 98c3c4d..742b9fc 100644 --- a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/checkImportPath.test.ts +++ b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/checkImportPath.test.ts @@ -1,9 +1,11 @@ import { getExternalImportError } from "rules/independentModules/errors/getExternalImportError"; import { getImportError } from "rules/independentModules/errors/getImportError"; +import { getImportPathNotExistsError } from "rules/independentModules/errors/getImportPathNotExistsError"; import { checkImportPath } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/checkImportPath"; import { extractReferencesFromPatterns } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/extractReferencesFromPatterns/extractReferencesFromPatterns"; import { findModuleConfig } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/findModuleConfig"; import { isExternalImport } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/isExternalImport"; +import { isImportPathExists } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/isImportPathExists"; import { validateImportPath } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/validateImportPath"; jest.mock( @@ -27,6 +29,13 @@ jest.mock( }), ); +jest.mock( + "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/isImportPathExists", + () => ({ + isImportPathExists: jest.fn(), + }), +); + jest.mock( "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/validateImportPath", () => ({ @@ -45,7 +54,7 @@ describe("checkImportPath", () => { checkImportPath({ config: { modules: [] }, - cwd: "", + projectRoot: "", filename: "", importPath: "", }); @@ -64,7 +73,7 @@ describe("checkImportPath", () => { expect(() => checkImportPath({ config: { modules: [] }, - cwd: "", + projectRoot: "", filename: "", importPath: "react", }), @@ -90,7 +99,7 @@ describe("checkImportPath", () => { expect(() => checkImportPath({ config: { modules: [] }, - cwd: "", + projectRoot: "", filename: "", importPath: "react", }), @@ -116,7 +125,7 @@ describe("checkImportPath", () => { expect(() => checkImportPath({ config: { modules: [] }, - cwd: "", + projectRoot: "", filename: "", importPath: "react", }), @@ -131,6 +140,26 @@ describe("checkImportPath", () => { ); }); + test("Should throw when !importPathExists", () => { + (findModuleConfig as jest.Mock).mockReturnValue({ + name: "module", + errorMessage: "error", + }); + (extractReferencesFromPatterns as jest.Mock).mockReturnValue([]); + (isExternalImport as jest.Mock).mockReturnValue(false); + (validateImportPath as jest.Mock).mockReturnValue(true); + (isImportPathExists as jest.Mock).mockReturnValue(false); + + expect(() => + checkImportPath({ + config: { modules: [] }, + projectRoot: "", + filename: "", + importPath: "", + }), + ).toThrow(getImportPathNotExistsError()); + }); + test("Should not throw when isValidImportPath", () => { (findModuleConfig as jest.Mock).mockReturnValue({ name: "module", @@ -139,11 +168,12 @@ describe("checkImportPath", () => { (extractReferencesFromPatterns as jest.Mock).mockReturnValue([]); (isExternalImport as jest.Mock).mockReturnValue(false); (validateImportPath as jest.Mock).mockReturnValue(true); + (isImportPathExists as jest.Mock).mockReturnValue(true); expect(() => checkImportPath({ config: { modules: [] }, - cwd: "", + projectRoot: "", filename: "", importPath: "", }), @@ -165,11 +195,12 @@ describe("checkImportPath", () => { (extractReferencesFromPatterns as jest.Mock).mockReturnValue([]); (isExternalImport as jest.Mock).mockReturnValue(false); (validateImportPath as jest.Mock).mockReturnValue(false); + (isImportPathExists as jest.Mock).mockReturnValue(true); expect(() => checkImportPath({ config: { modules: [] }, - cwd: "", + projectRoot: "", filename: "", importPath: "", }), diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/checkImportPath.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/checkImportPath.ts index d697859..7f5f24f 100644 --- a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/checkImportPath.ts +++ b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/checkImportPath.ts @@ -2,10 +2,12 @@ import micromatch from "micromatch"; import { getExternalImportError } from "rules/independentModules/errors/getExternalImportError"; import { getImportError } from "rules/independentModules/errors/getImportError"; +import { getImportPathNotExistsError } from "rules/independentModules/errors/getImportPathNotExistsError"; import { extractReferencesFromPatterns } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/extractReferencesFromPatterns/extractReferencesFromPatterns"; import { findModuleConfig } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/findModuleConfig"; import { getReusableImportPatternsWithoutRef } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/getReusableImportPatternsWithoutRef"; import { isExternalImport } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/isExternalImport"; +import { isImportPathExists } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/isImportPathExists"; import { validateImportPath } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/validateImportPath"; import { IndependentModulesConfig } from "rules/independentModules/independentModules.types"; @@ -13,14 +15,14 @@ interface CheckImportPathProps { importPath: string; filename: string; config: IndependentModulesConfig; - cwd: string; + projectRoot: string; } export const checkImportPath = ({ importPath, filename, - config: { reusableImportPatterns, modules, debugMode }, - cwd, + config: { reusableImportPatterns, modules, debugMode, pathAliases }, + projectRoot, }: CheckImportPathProps): void => { const moduleConfig = findModuleConfig(filename, modules); @@ -42,7 +44,7 @@ export const checkImportPath = ({ reusableImportPatterns: reusableImportPatternsExtracted, }); - const isExternal = isExternalImport(importPath, cwd); + const isExternal = isExternalImport(importPath, projectRoot); if (isExternal) { const isValidExternalImportPattern = allowImportsFromExtracted.some((p) => @@ -61,6 +63,14 @@ export const checkImportPath = ({ }); } + const importPathExists = isImportPathExists({ + importPath, + projectRoot, + baseUrl: pathAliases?.baseUrl ?? ".", + }); + + if (!importPathExists) throw getImportPathNotExistsError(); + const isValidImportPath = validateImportPath({ allowImportsFrom: allowImportsFromExtracted, importPath, diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/isExternalImport.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/isExternalImport.ts index 79112e2..4f8f553 100644 --- a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/isExternalImport.ts +++ b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/isExternalImport.ts @@ -2,13 +2,20 @@ import fs from "fs"; import { getFullImportPathVariants } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getFullImportPathVariants"; -export const isExternalImport = (importPath: string, cwd: string): boolean => { +export const isExternalImport = ( + importPath: string, + projectRoot: string, +): boolean => { const { fullImportPathExternal, fullImportPathExternalTypes, fullImportPathExternalTypesNode, fullImportPathExternalNode, - } = getFullImportPathVariants({ importPath, cwd, cwdWithRoot: "" }); + } = getFullImportPathVariants({ + importPath, + projectRoot, + projectRootWithBaseUrl: "", + }); return ( fs.existsSync(fullImportPathExternal) || diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/isImportPathExists.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/isImportPathExists.ts new file mode 100644 index 0000000..1c0441f --- /dev/null +++ b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/helpers/isImportPathExists.ts @@ -0,0 +1,15 @@ +import fs from "fs"; +import path from "path"; + +interface IsImportPathExistsProps { + projectRoot: string; + importPath: string; + baseUrl: string; +} + +export const isImportPathExists = ({ + importPath, + projectRoot, + baseUrl, +}: IsImportPathExistsProps): boolean => + fs.existsSync(path.join(projectRoot, baseUrl, importPath)); diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/convertImportPathToNonRelative.test.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/convertImportPathToNonRelative.test.ts index ec0b649..150e607 100644 --- a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/convertImportPathToNonRelative.test.ts +++ b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/convertImportPathToNonRelative.test.ts @@ -11,7 +11,7 @@ describe("convertImportPathToNonRelative", () => { resolve: "C:\\Users\\user\\Desktop\\repo\\src\\features\\Feature1\\Feature1.tsx", importPath: "./Feature1.tsx", - cwdWithRoot: "C:\\Users\\user\\Desktop\\repo\\src\\", + projectRootWithBaseUrl: "C:\\Users\\user\\Desktop\\repo\\src", expected: "features/Feature1/Feature1.tsx", }, { @@ -20,7 +20,7 @@ describe("convertImportPathToNonRelative", () => { dirname: "C:\\Users\\user\\Desktop\\repo\\src\\features\\Feature1", resolve: "C:\\Users\\user\\Desktop\\repo\\src\\features\\Feature2.tsx", importPath: "../Feature2.tsx", - cwdWithRoot: "C:\\Users\\user\\Desktop\\repo\\src\\", + projectRootWithBaseUrl: "C:\\Users\\user\\Desktop\\repo\\src", expected: "features/Feature2.tsx", }, { @@ -29,7 +29,7 @@ describe("convertImportPathToNonRelative", () => { dirname: "C:\\Users\\user\\Desktop\\repo\\src\\features\\Feature1", resolve: "C:\\Users\\user\\Desktop\\repo\\src\\index.tsx", importPath: "../../index.tsx", - cwdWithRoot: "C:\\Users\\user\\Desktop\\repo\\src\\", + projectRootWithBaseUrl: "C:\\Users\\user\\Desktop\\repo\\src", expected: "index.tsx", }, { @@ -39,12 +39,19 @@ describe("convertImportPathToNonRelative", () => { resolve: "C:\\Users\\user\\Desktop\\repo\\src\\features\\Feature1\\Feature1.tsx", importPath: "features/Feature1/Feature1.tsx", - cwdWithRoot: "C:\\Users\\user\\Desktop\\repo\\src\\", + projectRootWithBaseUrl: "C:\\Users\\user\\Desktop\\repo\\src", expected: "features/Feature1/Feature1.tsx", }, ])( "Should return correct value for %s", - ({ filename, importPath, cwdWithRoot, dirname, resolve, expected }) => { + ({ + filename, + importPath, + projectRootWithBaseUrl, + dirname, + resolve, + expected, + }) => { jest.spyOn(path, "dirname").mockImplementation(() => dirname); jest.spyOn(path, "resolve").mockImplementation(() => resolve); @@ -52,7 +59,7 @@ describe("convertImportPathToNonRelative", () => { convertImportPathToNonRelative({ filename, importPath, - cwdWithRoot, + projectRootWithBaseUrl, }), ).toEqual(expected); diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/convertImportPathToNonRelative.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/convertImportPathToNonRelative.ts index 001da94..85358aa 100644 --- a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/convertImportPathToNonRelative.ts +++ b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/convertImportPathToNonRelative.ts @@ -1,15 +1,15 @@ import path from "path"; -import { removeCwdWithRootAndUnifySep } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/removeCwdWithRootAndUnifySep"; +import { removeProjectRootFromPath } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/removeProjectRootFromPath"; interface ConvertImportPathToNonRelativeProps { importPath: string; filename: string; - cwdWithRoot: string; + projectRootWithBaseUrl: string; } export const convertImportPathToNonRelative = ({ - cwdWithRoot, + projectRootWithBaseUrl, filename, importPath, }: ConvertImportPathToNonRelativeProps): string => { @@ -19,5 +19,5 @@ export const convertImportPathToNonRelative = ({ const fullImportPath = path.resolve(dirname, path.join(importPath)); - return removeCwdWithRootAndUnifySep(fullImportPath, cwdWithRoot); + return removeProjectRootFromPath(fullImportPath, projectRootWithBaseUrl); }; diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getCwdWithBaseUrl.test.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getCwdWithBaseUrl.test.ts deleted file mode 100644 index 25cddc6..0000000 --- a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getCwdWithBaseUrl.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import path, { sep } from "path"; - -import { getCwdWithBaseUrl } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getCwdWithBaseUrl"; - -describe("getCwdWithBaseUrl", () => { - test.each([ - { - cwd: path.join("C:", "Users", "user", "Desktop", "repo"), - baseUrl: "test", - expected: - path.join("C:", "Users", "user", "Desktop", "repo", "test") + sep, - }, - { - cwd: path.join("C:", "Users", "user", "Desktop", "repo"), - baseUrl: undefined, - expected: path.join("C:", "Users", "user", "Desktop", "repo") + sep, - }, - ])("Should return correct value for %s", ({ cwd, baseUrl, expected }) => { - expect(getCwdWithBaseUrl({ cwd, baseUrl })).toEqual(expected); - }); -}); diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getCwdWithBaseUrl.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getCwdWithBaseUrl.ts deleted file mode 100644 index f9021ae..0000000 --- a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getCwdWithBaseUrl.ts +++ /dev/null @@ -1,15 +0,0 @@ -import path, { sep } from "path"; - -interface GetCwdWithBaseUrlProps { - cwd: string; - baseUrl?: string; -} - -export const getCwdWithBaseUrl = ({ - cwd, - baseUrl, -}: GetCwdWithBaseUrlProps): string => { - if (!baseUrl) return cwd + sep; - - return path.join(cwd, baseUrl) + sep; -}; diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getFullImportPathVariants.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getFullImportPathVariants.ts index 1d20abb..d1cb503 100644 --- a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getFullImportPathVariants.ts +++ b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getFullImportPathVariants.ts @@ -2,8 +2,8 @@ import path from "path"; interface GetFullImportPathVariantsProps { importPath: string; - cwdWithRoot: string; - cwd: string; + projectRoot: string; + projectRootWithBaseUrl: string; } interface GetFullImportPathVariantsReturn { @@ -25,20 +25,24 @@ interface GetFullImportPathVariantsReturn { export const getFullImportPathVariants = ({ importPath, - cwdWithRoot, - cwd, + projectRoot, + projectRootWithBaseUrl, }: GetFullImportPathVariantsProps): GetFullImportPathVariantsReturn => { - const fullImportPath = path.join(cwdWithRoot, importPath); + const fullImportPath = path.join(projectRootWithBaseUrl, importPath); const fullImportPathIndex = path.join(fullImportPath, "index"); - const fullImportPathExternal = path.join(cwd, "node_modules", importPath); + const fullImportPathExternal = path.join( + projectRoot, + "node_modules", + importPath, + ); const fullImportPathExternalIndex = path.join( fullImportPathExternal, "index", ); const fullImportPathExternalTypes = path.join( - cwd, + projectRoot, "node_modules", "@types", importPath, @@ -49,7 +53,7 @@ export const getFullImportPathVariants = ({ ); const fullImportPathExternalNode = path.join( - cwd, + projectRoot, "node_modules", "node", importPath, @@ -60,7 +64,7 @@ export const getFullImportPathVariants = ({ ); const fullImportPathExternalTypesNode = path.join( - cwd, + projectRoot, "node_modules", "@types", "node", diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getImportPaths.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getImportPaths.ts index 4a466b5..4d3d7ca 100644 --- a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getImportPaths.ts +++ b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getImportPaths.ts @@ -13,9 +13,12 @@ export const getImportPaths = ({ if (!paths || !pathsKays.length) return [importPath]; - return pathsKays + const imports = pathsKays .map((key) => { const keyCleared = key.replace("*", ""); + + if (!importPath.includes(keyCleared)) return; + const importPaths = paths[key]; return importPaths @@ -26,5 +29,10 @@ export const getImportPaths = ({ importPath.replace(keyCleared, importPathReplace.replace("*", "")), ); }) - .flat(); + .flat() + .filter((v): v is string => !!v); + + if (!imports.length) return [importPath]; + + return imports; }; diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getProjectRootWithBaseUrl.test.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getProjectRootWithBaseUrl.test.ts new file mode 100644 index 0000000..f08fc3f --- /dev/null +++ b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getProjectRootWithBaseUrl.test.ts @@ -0,0 +1,26 @@ +import path, { sep } from "path"; + +import { getProjectRootWithBaseUrl } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getProjectRootWithBaseUrl"; + +describe("getProjectRootWithBaseUrl", () => { + test.each([ + { + projectRoot: path.join("C:", "Users", "user", "Desktop", "repo"), + baseUrl: "test", + expected: + path.join("C:", "Users", "user", "Desktop", "repo", "test") + sep, + }, + { + projectRoot: path.join("C:", "Users", "user", "Desktop", "repo"), + baseUrl: undefined, + expected: path.join("C:", "Users", "user", "Desktop", "repo") + sep, + }, + ])( + "Should return correct value for %s", + ({ projectRoot, baseUrl, expected }) => { + expect(getProjectRootWithBaseUrl({ projectRoot, baseUrl })).toEqual( + expected, + ); + }, + ); +}); diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getProjectRootWithBaseUrl.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getProjectRootWithBaseUrl.ts new file mode 100644 index 0000000..a10e9d6 --- /dev/null +++ b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getProjectRootWithBaseUrl.ts @@ -0,0 +1,15 @@ +import path, { sep } from "path"; + +interface GetProjectRootWithBaseUrlProps { + projectRoot: string; + baseUrl?: string; +} + +export const getProjectRootWithBaseUrl = ({ + projectRoot, + baseUrl, +}: GetProjectRootWithBaseUrlProps): string => { + if (!baseUrl) return projectRoot + sep; + + return path.join(projectRoot, baseUrl) + sep; +}; diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/removeCwdWithRootAndUnifySep.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/removeCwdWithRootAndUnifySep.ts deleted file mode 100644 index 4f44af9..0000000 --- a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/removeCwdWithRootAndUnifySep.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const removeCwdWithRootAndUnifySep = ( - path: string, - cwdWithRoot: string, -): string => path.replace(cwdWithRoot, "").replace(/\\/g, "/"); diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/removeProjectRootFromPath.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/removeProjectRootFromPath.ts new file mode 100644 index 0000000..1fb3542 --- /dev/null +++ b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/removeProjectRootFromPath.ts @@ -0,0 +1,6 @@ +import { sep } from "path"; + +export const removeProjectRootFromPath = ( + path: string, + projectRootWithBaseUrl: string, +): string => path.replace(projectRootWithBaseUrl + sep, "").replace(/\\/g, "/"); diff --git a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/validateAll.ts b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/validateAll.ts index 127a9bd..6dc8c8a 100644 --- a/src/rules/independentModules/helpers/validateImport/helpers/validateAll/validateAll.ts +++ b/src/rules/independentModules/helpers/validateImport/helpers/validateAll/validateAll.ts @@ -1,59 +1,57 @@ +import { getProjectRoot } from "helpers/getProjectRoot"; + import { addExtensionToImportPath } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/addExtensionToImportPath/addExtensionToImportPath"; import { checkImportPath } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/checkImportPath/checkImportPath"; import { convertImportPathToNonRelative } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/convertImportPathToNonRelative"; -import { getCwdWithBaseUrl } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getCwdWithBaseUrl"; import { getImportPaths } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/getImportPaths"; -import { removeCwdWithRootAndUnifySep } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/removeCwdWithRootAndUnifySep"; +import { removeProjectRootFromPath } from "rules/independentModules/helpers/validateImport/helpers/validateAll/helpers/removeProjectRootFromPath"; import { IndependentModulesConfig } from "rules/independentModules/independentModules.types"; interface ValidateAllProps { filename: string; importPath: string; - cwd: string; config: IndependentModulesConfig; } export const validateAll = ({ filename, importPath, - cwd, config, }: ValidateAllProps): void => { const { extensions, pathAliases } = config; - const cwdWithRoot = getCwdWithBaseUrl({ - cwd, - baseUrl: pathAliases?.baseUrl, - }); + const projectRoot = getProjectRoot(); + const projectRootWithBaseUrl = getProjectRoot(pathAliases?.baseUrl); + const importPaths = getImportPaths({ importPath, paths: pathAliases?.paths, }); - const filenameWithoutCwdWithRoot = removeCwdWithRootAndUnifySep( + const filenameWithoutProjectRootWithBaseUrl = removeProjectRootFromPath( filename, - cwdWithRoot, + projectRootWithBaseUrl, ); importPaths.forEach((currentImportPath) => { const importPathNonRelative = convertImportPathToNonRelative({ importPath: currentImportPath, filename, - cwdWithRoot, + projectRootWithBaseUrl, }); const importPathWithExtension = addExtensionToImportPath({ importPath: importPathNonRelative, - cwdWithRoot, + projectRootWithBaseUrl, extensions, - cwd, + projectRoot, }); checkImportPath({ importPath: importPathWithExtension, - filename: filenameWithoutCwdWithRoot, + filename: filenameWithoutProjectRootWithBaseUrl, config, - cwd, + projectRoot, }); }); }; diff --git a/src/rules/independentModules/helpers/validateImport/validateImport.test.ts b/src/rules/independentModules/helpers/validateImport/validateImport.test.ts index e1c11c9..f8e605c 100644 --- a/src/rules/independentModules/helpers/validateImport/validateImport.test.ts +++ b/src/rules/independentModules/helpers/validateImport/validateImport.test.ts @@ -22,7 +22,7 @@ describe("validateImport", () => { }); validateImport({ - context: { report: reportMock, settings: {}, cwd: "", options: [] }, + context: { report: reportMock, settings: {}, options: [] }, importPath: "", node: {}, config: {}, @@ -40,7 +40,7 @@ describe("validateImport", () => { expect(() => validateImport({ - context: { report: reportMock, settings: {}, cwd: "", options: [] }, + context: { report: reportMock, settings: {}, options: [] }, importPath: "", node: {}, config: {}, diff --git a/src/rules/independentModules/helpers/validateImport/validateImport.ts b/src/rules/independentModules/helpers/validateImport/validateImport.ts index 40be7fe..1c17c49 100644 --- a/src/rules/independentModules/helpers/validateImport/validateImport.ts +++ b/src/rules/independentModules/helpers/validateImport/validateImport.ts @@ -22,7 +22,7 @@ export interface ValidateImportProps { export const validateImport = ({ importPath, - context: { cwd, filename, report }, + context: { filename, report }, node, config, }: ValidateImportProps): void => { @@ -30,7 +30,6 @@ export const validateImport = ({ validateAll({ filename, importPath, - cwd, config, }); } catch (error) {