From a1a5c06bb6ccdb67645f4a8fc4d349c7b11dd85c Mon Sep 17 00:00:00 2001 From: Aleksei Dmitriev Date: Wed, 6 Jan 2021 15:07:18 +0000 Subject: [PATCH] fix: Workspace path match (#33) * fix: Project path compare * chore: Create @garment/utils package, add isSubPath win32 test * chore: Change path import --- core/garment/package.json | 7 +-- core/garment/src/garment.ts | 3 +- core/utils/package.json | 10 ++++ core/utils/src/index.ts | 1 + .../src/isSubPath/__tests__/isSubPath.test.ts | 50 +++++++++++++++++++ core/utils/src/isSubPath/index.ts | 7 +++ core/workspace/package.json | 3 +- core/workspace/src/ProjectRegistry.ts | 5 +- garment.json | 4 ++ 9 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 core/utils/package.json create mode 100644 core/utils/src/index.ts create mode 100644 core/utils/src/isSubPath/__tests__/isSubPath.test.ts create mode 100644 core/utils/src/isSubPath/index.ts diff --git a/core/garment/package.json b/core/garment/package.json index 1419567..e2dfdba 100644 --- a/core/garment/package.json +++ b/core/garment/package.json @@ -9,17 +9,18 @@ "@garment/runner": "^0.14.0", "@garment/scheduler": "^0.14.0", "@garment/schema-validator": "^0.14.0", + "@garment/utils": "^0.0.14", "@garment/workspace": "^0.14.0", "@parcel/watcher": "^2.0.0-alpha.9", "fs-extra": "8.1.0", + "globby": "10.0.1", "is-valid-path": "^0.1.1", "matcher": "^2.1.0", "memfs": "^3.1.2", "multimatch": "^4.0.0", "normalize-path": "^3.0.0", "tempy": "0.3.0", - "unionfs": "^4.4.0", - "globby": "10.0.1" + "unionfs": "^4.4.0" }, "files": [ "lib", @@ -30,4 +31,4 @@ "@types/is-valid-path": "^0.1.0", "@types/tempy": "^0.2.0" } -} +} \ No newline at end of file diff --git a/core/garment/src/garment.ts b/core/garment/src/garment.ts index 755fb0e..68ded3e 100644 --- a/core/garment/src/garment.ts +++ b/core/garment/src/garment.ts @@ -39,6 +39,7 @@ import { FileCache } from './FileCache'; import { getProjectsByName } from './getProjectsByName'; import globby = require('globby'); import normalizePath = require('normalize-path'); +import { isSubPath } from '@garment/utils'; export type Cache = | { @@ -954,7 +955,7 @@ async function garmentFromWorkspace( if ( subscription.type === 'glob' && - normalizePath(event.path).startsWith(subscription.input.rootDir) + isSubPath(subscription.input.rootDir, normalizePath(event.path)) ) { const matched = multimatch( event.path, diff --git a/core/utils/package.json b/core/utils/package.json new file mode 100644 index 0000000..16bfaad --- /dev/null +++ b/core/utils/package.json @@ -0,0 +1,10 @@ +{ + "name": "@garment/utils", + "version": "0.0.14", + "main": "lib/index.js", + "license": "MIT", + "dependencies": {}, + "files": [ + "lib" + ] +} diff --git a/core/utils/src/index.ts b/core/utils/src/index.ts new file mode 100644 index 0000000..90be8f6 --- /dev/null +++ b/core/utils/src/index.ts @@ -0,0 +1 @@ +export * from './isSubPath'; diff --git a/core/utils/src/isSubPath/__tests__/isSubPath.test.ts b/core/utils/src/isSubPath/__tests__/isSubPath.test.ts new file mode 100644 index 0000000..26943bc --- /dev/null +++ b/core/utils/src/isSubPath/__tests__/isSubPath.test.ts @@ -0,0 +1,50 @@ +import { isSubPath } from '..'; +import * as Path from 'path'; + +describe('utils | isSubPath', () => { + it('should return true if path is a subpath', () => { + expect(isSubPath('/root', '/root/child')).toBeTruthy(); + }); + + it('should return false if path is not a subpath', () => { + expect(isSubPath('/root/foo', '/root/bar/baz')).toBeFalsy(); + }); + + it('should return false if path has a similar substring', () => { + expect(isSubPath('/root/foo', '/root/foo bar/baz')).toBeFalsy(); + }); + + it("should return true if path has special path .. symbols and it's a subpath", () => { + expect(isSubPath('/root/foo', '/root/../root/foo/bar')).toBeTruthy(); + }); + + it("should return true if path has special path . symbol and it's a subpath", () => { + expect(isSubPath('/root/foo', '/root/./foo/bar')).toBeTruthy(); + }); +}); + +describe('utils | isSubPath [win32]', () => { + let relativeSpy: jest.SpyInstance; + let isAbsoluteSpy: jest.SpyInstance; + + beforeEach(() => { + relativeSpy = jest + .spyOn(Path, 'relative') + .mockImplementationOnce(Path.win32.relative); + isAbsoluteSpy = jest + .spyOn(Path, 'isAbsolute') + .mockImplementationOnce(Path.win32.isAbsolute); + }); + + afterEach(() => { + relativeSpy.mockRestore(); + isAbsoluteSpy.mockRestore(); + }); + + it('should return true if path is a subpath', () => { + expect(isSubPath('C:\\Foo', 'C:\\Foo\\Bar')).toBeTruthy(); + }); + it('should return false if path is not a subpath', () => { + expect(isSubPath('C:\\Foo\\Bar', 'D:\\Foo\\Bar')).toBeFalsy(); + }); +}); diff --git a/core/utils/src/isSubPath/index.ts b/core/utils/src/isSubPath/index.ts new file mode 100644 index 0000000..ac34473 --- /dev/null +++ b/core/utils/src/isSubPath/index.ts @@ -0,0 +1,7 @@ +import * as Path from 'path'; + +export const isSubPath = (parent: string, child: string) => { + const relative = Path.relative(parent, child); + + return relative && !relative.startsWith('..') && !Path.isAbsolute(relative); +}; diff --git a/core/workspace/package.json b/core/workspace/package.json index b521681..0279660 100644 --- a/core/workspace/package.json +++ b/core/workspace/package.json @@ -9,6 +9,7 @@ ], "dependencies": { "@garment/schema-validator": "^0.14.0", + "@garment/utils": "^0.0.14", "dependency-graph": "^0.8.0", "fs-extra": "8.1.0", "mustache": "^3.0.1", @@ -21,4 +22,4 @@ "@types/normalize-path": "^3.0.0", "@types/object-hash": "^1.3.0" } -} +} \ No newline at end of file diff --git a/core/workspace/src/ProjectRegistry.ts b/core/workspace/src/ProjectRegistry.ts index 7ce3a0d..fd8abba 100644 --- a/core/workspace/src/ProjectRegistry.ts +++ b/core/workspace/src/ProjectRegistry.ts @@ -1,6 +1,7 @@ import { Project } from './Project'; import normalizePath = require('normalize-path'); +import { isSubPath } from '@garment/utils'; export class ProjectRegistry { [Symbol.iterator]() { @@ -32,13 +33,13 @@ export class ProjectRegistry { getByPaths(...paths: string[]) { paths = paths.map(path => normalizePath(path)); return this.list().filter(project => - paths.some(path => path.indexOf(project.fullPath) === 0) + paths.some(path => isSubPath(project.fullPath, path)) ); } getByPath(path: string) { path = normalizePath(path); - return this.list().find(project => path.indexOf(project.fullPath) === 0); + return this.list().find(project => isSubPath(project.fullPath, path)); } getByPathExact(path: string) { diff --git a/garment.json b/garment.json index 3059478..4edb889 100644 --- a/garment.json +++ b/garment.json @@ -268,6 +268,10 @@ "visualize-graph": { "path": "utils/visualize-graph", "extends": ["tspackage"] + }, + "utils": { + "path": "core/utils", + "extends": ["tspackage"] } }, "schematics": ["@garment/schematics", "@garment/schematics-typescript"],