Skip to content

Commit

Permalink
🐛 Fix computing relative URIs (#1177)
Browse files Browse the repository at this point in the history
  • Loading branch information
SPGoding authored May 23, 2024
1 parent 0c5e505 commit a8431ea
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 3 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/service/CacheService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ export class CacheService {
}

for (const [uri, checksum] of Object.entries(this.checksums.files)) {
if (unchangedRoots.some((root) => uri.startsWith(root))) {
if (unchangedRoots.some((root) => fileUtil.isSubUriOf(uri, root))) {
ans.unchangedFiles.push(uri)
continue
}
Expand Down
45 changes: 43 additions & 2 deletions packages/core/src/service/fileUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,46 @@ export type RootUriString = `${string}/`
export type FileExtension = `.${string}`

export namespace fileUtil {
/**
* Get the relative URI of `target` based from `base`.
*
* @returns The relative URI, or `undefined` if `target` is not under `base`.
*/
export function getRelativeUriFromBase(
target: string,
base: string,
): string | undefined {
const baseUri = new Uri(base)
const targetUri = new Uri(target)

if (baseUri.origin !== targetUri.origin) {
// Different scheme, hostname, and/or port
return undefined
}

const baseComponents = baseUri.pathname.split('/')
.filter((v) => !!v)
const targetComponents = targetUri.pathname.split('/')
.filter((v) => !!v)

if (
baseComponents.length > targetComponents.length ||
baseComponents.some((bc, i) =>
decodeURIComponent(bc) !== decodeURIComponent(targetComponents[i])
)
) {
return undefined
}

return targetComponents.slice(baseComponents.length).map(
encodeURIComponent,
).join('/')
}

export function isSubUriOf(uri: string, base: string): boolean {
return getRelativeUriFromBase(uri, base) !== undefined
}

/**
* @param rootUris The root URIs. Each URI in this array must end with a slash (`/`).
* @param uri The target file URI.
Expand All @@ -25,8 +65,9 @@ export namespace fileUtil {
rootUris: readonly RootUriString[],
): Generator<string, undefined, unknown> {
for (const root of rootUris) {
if (uri.startsWith(root)) {
yield decodeURIComponent(uri.slice(root.length))
const rel = getRelativeUriFromBase(uri, root)
if (rel !== undefined) {
yield rel
}
}
return undefined
Expand Down
50 changes: 50 additions & 0 deletions packages/core/test/service/fileUtil.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,56 @@ import { describe, it } from 'mocha'
import { fileUtil } from '../../lib/index.js'

describe('fileUtil', () => {
describe('getRelativeUriFromBase()', () => {
const bases: string[] = [
'file:///c%3A/Users/admin/',
'file:///c:/Users/admin/',
]
const suites: { uri: string; expected: string | undefined }[] = [
{ uri: 'file:///c%3A/Users/admin/', expected: '' },
{ uri: 'file:///c%3A/Users/admin/foo.mcdoc', expected: 'foo.mcdoc' },
{
uri: 'file:///c%3A/Users/admin/foo/bar.mcdoc',
expected: 'foo/bar.mcdoc',
},
{ uri: 'file:///c:/Users/admin/', expected: '' },
{ uri: 'file:///c:/Users/admin/foo.mcdoc', expected: 'foo.mcdoc' },
{
// Should treat multiple slashes in a row in pathname as a single slash.
uri: 'file:///c://///Users///admin//foo.mcdoc',
expected: 'foo.mcdoc',
},
{
uri: 'file:///c:/Users/admin/foo/bar.mcdoc',
expected: 'foo/bar.mcdoc',
},
{ uri: 'file:///qux.mcdoc', expected: undefined },
]
for (const { uri, expected } of suites) {
for (const base of bases) {
it(`Should return '${expected}' for '${uri}' when base is '${base}'`, () => {
assert.strictEqual(
fileUtil.getRelativeUriFromBase(uri, base),
expected,
)
})
}
}
})
describe('isSubUriOf()', () => {
const root: string = 'file:///c%3A/Users/admin/'
const suites: { uri: string; expected: boolean }[] = [
{ uri: 'file:///c%3A/Users/admin/', expected: true },
{ uri: 'file:///c%3A/Users/admin/foo.mcdoc', expected: true },
{ uri: 'file:///c:/Users/admin/foo.mcdoc', expected: true },
{ uri: 'file:///qux.mcdoc', expected: false },
]
for (const { uri, expected } of suites) {
it(`Should return '${expected}' for '${uri}'`, () => {
assert.strictEqual(fileUtil.isSubUriOf(uri, root), expected)
})
}
})
describe('getRel()', () => {
const rootUris: `${string}/`[] = [
'file:///root1/subdir/',
Expand Down

0 comments on commit a8431ea

Please sign in to comment.