diff --git a/.changeset/poor-sites-buy.md b/.changeset/poor-sites-buy.md new file mode 100644 index 0000000..59591f7 --- /dev/null +++ b/.changeset/poor-sites-buy.md @@ -0,0 +1,5 @@ +--- +'vite-plugin-static-copy': patch +--- + +improve performance of internal `isSubdirectoryOrEqual` function diff --git a/src/utils.test.ts b/src/utils.test.ts index 7a647f7..f0d6e60 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -1,6 +1,43 @@ import { describe, expect, test } from 'vitest' -import { groupTargetsByDirectoryTree } from './utils' +import { isSubdirectoryOrEqual, groupTargetsByDirectoryTree } from './utils' import path from 'node:path' +import { fileURLToPath } from 'node:url' +import os from 'node:os' + +const _dirname = path.dirname(fileURLToPath(import.meta.url)) +const isWindows = os.platform() === 'win32' + +describe('isSubdirectoryOrEqual', () => { + const cases: readonly [a: string, b: string, expected: boolean][] = [ + ['./', '.', true], + ['./', './', true], + ['.', './', true], + ['./index.ts', './', true], + ['./foo/', './', true], + ['./foo/bar', './', true], + ['./foo/bar.js', './', true], + ['..', './', false], + ['../', './', false], + ['../test', './', false], + ['../test/', './', false], + ...(isWindows + ? ([ + ['C:/', 'C:/', true], + ['C:\\', 'C:/', true], + ['C:/', 'D:/', false], + ['C:\\', 'D:/', false] + ] satisfies readonly [string, string, boolean][]) + : []) + ] + + const resolve = (p: string) => path.resolve(_dirname, p) + + for (const [a, b, expected] of cases) { + test(`isSubdirectoryOrEqual(${a}, ${b})`, () => { + expect(isSubdirectoryOrEqual(resolve(a), resolve(b))).toBe(expected) + }) + } +}) describe('groupTargetsByDirectoryTree', () => { const defineCase = (input: string[], expected: string[][]) => ({ diff --git a/src/utils.ts b/src/utils.ts index 0d69c5e..aa5163f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -27,12 +27,14 @@ type ResolvedTarget = SimpleTarget & { resolvedSrc: string } -const isSubdirectoryOrEqual = (a: string, b: string) => { - const relative = path.relative(b, a) - return ( - !relative || - (!relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative)) - ) +/** + * Whether a is a subdirectory of b or equal to b + * + * @param a absolute path + * @param b absolute path + */ +export const isSubdirectoryOrEqual = (a: string, b: string) => { + return a.startsWith(b + path.sep) || a === b } export const groupTargetsByDirectoryTree = (