From ac258adda3fda7380ed9f6cbd01d7e9bf2f99fa9 Mon Sep 17 00:00:00 2001 From: PR Replica Date: Sat, 1 Jan 2000 00:00:00 +0000 Subject: [PATCH] fix: walk and copy directories without a stat per file Source PR: https://github.com/sveltejs/kit/pull/16995 Source head: 7b73f95c60c191209839826d6f6d6556738a21d5 --- .changeset/quick-dirs-walk.md | 5 ++ packages/kit/src/utils/filesystem.js | 70 +++++++++++++++++----------- 2 files changed, 48 insertions(+), 27 deletions(-) create mode 100644 .changeset/quick-dirs-walk.md diff --git a/.changeset/quick-dirs-walk.md b/.changeset/quick-dirs-walk.md new file mode 100644 index 000000000000..371ef2901727 --- /dev/null +++ b/.changeset/quick-dirs-walk.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: walk and copy directories without a stat per file diff --git a/packages/kit/src/utils/filesystem.js b/packages/kit/src/utils/filesystem.js index 37b5838a9aa7..d564c4a0316e 100644 --- a/packages/kit/src/utils/filesystem.js +++ b/packages/kit/src/utils/filesystem.js @@ -16,46 +16,58 @@ export function copy(source, target, opts = {}) { /** @type {string[]} */ const files = []; - const prefix = posixify(target) + '/'; - const regex = opts.replace ? new RegExp(`\\b(${Object.keys(opts.replace).join('|')})\\b`, 'g') : null; + /** @type {string | undefined} */ + let created; + /** * @param {string} from * @param {string} to + * @param {string} file posix path of `to` relative to `target`, empty when copying a single file + * @param {boolean} is_directory */ - function go(from, to) { + function go(from, to, file, is_directory) { if (opts.filter && !opts.filter(path.basename(from))) return; - const stats = fs.statSync(from); - - if (stats.isDirectory()) { - fs.readdirSync(from).forEach((file) => { - go(path.join(from, file), path.join(to, file)); - }); - } else { - fs.mkdirSync(path.dirname(to), { recursive: true }); - - if (opts.replace) { - const data = fs.readFileSync(from, 'utf-8'); - fs.writeFileSync( - to, - data.replace( - /** @type {RegExp} */ (regex), - (_match, key) => /** @type {Record} */ (opts.replace)[key] - ) + if (is_directory) { + for (const entry of fs.readdirSync(from, { withFileTypes: true })) { + const child = path.join(from, entry.name); + go( + child, + path.join(to, entry.name), + file ? `${file}/${entry.name}` : entry.name, + entry.isSymbolicLink() ? fs.statSync(child).isDirectory() : entry.isDirectory() ); - } else { - fs.copyFileSync(from, to); } + return; + } - files.push(to === target ? posixify(path.basename(to)) : posixify(to).replace(prefix, '')); + const dir = path.dirname(to); + if (dir !== created) { + fs.mkdirSync(dir, { recursive: true }); + created = dir; } + + if (opts.replace) { + const data = fs.readFileSync(from, 'utf-8'); + fs.writeFileSync( + to, + data.replace( + /** @type {RegExp} */ (regex), + (_match, key) => /** @type {Record} */ (opts.replace)[key] + ) + ); + } else { + fs.copyFileSync(from, to); + } + + files.push(file || posixify(path.basename(to))); } - go(source, target); + go(source, target, '', fs.statSync(source).isDirectory()); return files; } @@ -67,9 +79,13 @@ export function copy(source, target, opts = {}) { * @returns {Generator} the posix paths of all found files, relative to `cwd` */ export function* walk(cwd, dir = '') { - for (const file of fs.readdirSync(path.join(cwd, dir))) { - const joined = dir ? `${dir}/${file}` : file; - if (fs.statSync(path.join(cwd, joined)).isDirectory()) { + for (const entry of fs.readdirSync(path.join(cwd, dir), { withFileTypes: true })) { + const joined = dir ? `${dir}/${entry.name}` : entry.name; + const is_directory = entry.isSymbolicLink() + ? fs.statSync(path.join(cwd, joined)).isDirectory() + : entry.isDirectory(); + + if (is_directory) { yield* walk(cwd, joined); } else { yield joined;