mirror of
https://github.com/denoland/deno.git
synced 2024-12-26 09:10:40 -05:00
feat(fs): add copy/copySync (#278)
This commit is contained in:
parent
98257ef303
commit
ab5b0887cc
9 changed files with 837 additions and 0 deletions
13
fs/README.md
13
fs/README.md
|
@ -129,6 +129,19 @@ moveSync("./foo", "./existingFolder", { overwrite: true });
|
|||
// Will overwrite existingFolder
|
||||
```
|
||||
|
||||
### copy
|
||||
|
||||
copy a file or directory. Overwrites it if option provided
|
||||
|
||||
```ts
|
||||
import { copy, copySync } from "https://deno.land/std/fs/mod.ts";
|
||||
|
||||
copy("./foo", "./bar"); // returns a promise
|
||||
copySync("./foo", "./bar"); // void
|
||||
copySync("./foo", "./existingFolder", { overwrite: true });
|
||||
// Will overwrite existingFolder
|
||||
```
|
||||
|
||||
### readJson
|
||||
|
||||
Reads a JSON file and then parses it into an object
|
||||
|
|
260
fs/copy.ts
Normal file
260
fs/copy.ts
Normal file
|
@ -0,0 +1,260 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
import * as path from "./path/mod.ts";
|
||||
import { ensureDir, ensureDirSync } from "./ensure_dir.ts";
|
||||
import { isSubdir, getFileInfoType } from "./utils.ts";
|
||||
|
||||
export interface CopyOptions {
|
||||
/**
|
||||
* overwrite existing file or directory. Default is `false`
|
||||
*/
|
||||
overwrite?: boolean;
|
||||
/**
|
||||
* When `true`, will set last modification and access times to the ones of the original source files.
|
||||
* When `false`, timestamp behavior is OS-dependent.
|
||||
* Default is `false`.
|
||||
*/
|
||||
preserveTimestamps?: boolean;
|
||||
}
|
||||
|
||||
async function ensureValidCopy(
|
||||
src: string,
|
||||
dest: string,
|
||||
options: CopyOptions,
|
||||
isCopyFolder: boolean = false
|
||||
): Promise<Deno.FileInfo> {
|
||||
let destStat: Deno.FileInfo;
|
||||
|
||||
destStat = await Deno.lstat(dest).catch(
|
||||
(): Promise<null> => Promise.resolve(null)
|
||||
);
|
||||
|
||||
if (destStat) {
|
||||
if (isCopyFolder && !destStat.isDirectory()) {
|
||||
throw new Error(
|
||||
`Cannot overwrite non-directory '${dest}' with directory '${src}'.`
|
||||
);
|
||||
}
|
||||
if (!options.overwrite) {
|
||||
throw new Error(`'${dest}' already exists.`);
|
||||
}
|
||||
}
|
||||
|
||||
return destStat;
|
||||
}
|
||||
|
||||
function ensureValidCopySync(
|
||||
src: string,
|
||||
dest: string,
|
||||
options: CopyOptions,
|
||||
isCopyFolder: boolean = false
|
||||
): Deno.FileInfo {
|
||||
let destStat: Deno.FileInfo;
|
||||
|
||||
try {
|
||||
destStat = Deno.lstatSync(dest);
|
||||
} catch {
|
||||
// ignore error
|
||||
}
|
||||
|
||||
if (destStat) {
|
||||
if (isCopyFolder && !destStat.isDirectory()) {
|
||||
throw new Error(
|
||||
`Cannot overwrite non-directory '${dest}' with directory '${src}'.`
|
||||
);
|
||||
}
|
||||
if (!options.overwrite) {
|
||||
throw new Error(`'${dest}' already exists.`);
|
||||
}
|
||||
}
|
||||
|
||||
return destStat;
|
||||
}
|
||||
|
||||
/* copy file to dest */
|
||||
async function copyFile(
|
||||
src: string,
|
||||
dest: string,
|
||||
options: CopyOptions
|
||||
): Promise<void> {
|
||||
await ensureValidCopy(src, dest, options);
|
||||
await Deno.copyFile(src, dest);
|
||||
if (options.preserveTimestamps) {
|
||||
const statInfo = await Deno.stat(src);
|
||||
await Deno.utime(dest, statInfo.accessed, statInfo.modified);
|
||||
}
|
||||
}
|
||||
/* copy file to dest synchronously */
|
||||
function copyFileSync(src: string, dest: string, options: CopyOptions): void {
|
||||
ensureValidCopySync(src, dest, options);
|
||||
Deno.copyFileSync(src, dest);
|
||||
if (options.preserveTimestamps) {
|
||||
const statInfo = Deno.statSync(src);
|
||||
Deno.utimeSync(dest, statInfo.accessed, statInfo.modified);
|
||||
}
|
||||
}
|
||||
|
||||
/* copy symlink to dest */
|
||||
async function copySymLink(
|
||||
src: string,
|
||||
dest: string,
|
||||
options: CopyOptions
|
||||
): Promise<void> {
|
||||
await ensureValidCopy(src, dest, options);
|
||||
const originSrcFilePath = await Deno.readlink(src);
|
||||
const type = getFileInfoType(await Deno.lstat(src));
|
||||
await Deno.symlink(originSrcFilePath, dest, type);
|
||||
if (options.preserveTimestamps) {
|
||||
const statInfo = await Deno.lstat(src);
|
||||
await Deno.utime(dest, statInfo.accessed, statInfo.modified);
|
||||
}
|
||||
}
|
||||
|
||||
/* copy symlink to dest synchronously */
|
||||
function copySymlinkSync(
|
||||
src: string,
|
||||
dest: string,
|
||||
options: CopyOptions
|
||||
): void {
|
||||
ensureValidCopySync(src, dest, options);
|
||||
const originSrcFilePath = Deno.readlinkSync(src);
|
||||
const type = getFileInfoType(Deno.lstatSync(src));
|
||||
Deno.symlinkSync(originSrcFilePath, dest, type);
|
||||
if (options.preserveTimestamps) {
|
||||
const statInfo = Deno.lstatSync(src);
|
||||
Deno.utimeSync(dest, statInfo.accessed, statInfo.modified);
|
||||
}
|
||||
}
|
||||
|
||||
/* copy folder from src to dest. */
|
||||
async function copyDir(
|
||||
src: string,
|
||||
dest: string,
|
||||
options: CopyOptions
|
||||
): Promise<void> {
|
||||
const destStat = await ensureValidCopy(src, dest, options, true);
|
||||
|
||||
if (!destStat) {
|
||||
await ensureDir(dest);
|
||||
}
|
||||
|
||||
if (options.preserveTimestamps) {
|
||||
const srcStatInfo = await Deno.stat(src);
|
||||
await Deno.utime(dest, srcStatInfo.accessed, srcStatInfo.modified);
|
||||
}
|
||||
|
||||
const files = await Deno.readDir(src);
|
||||
|
||||
for (const file of files) {
|
||||
const srcPath = file.path as string;
|
||||
const destPath = path.join(dest, path.basename(srcPath as string));
|
||||
if (file.isDirectory()) {
|
||||
await copyDir(srcPath, destPath, options);
|
||||
} else if (file.isFile()) {
|
||||
await copyFile(srcPath, destPath, options);
|
||||
} else if (file.isSymlink()) {
|
||||
await copySymLink(srcPath, destPath, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* copy folder from src to dest synchronously */
|
||||
function copyDirSync(src: string, dest: string, options: CopyOptions): void {
|
||||
const destStat: Deno.FileInfo = ensureValidCopySync(src, dest, options, true);
|
||||
|
||||
if (!destStat) {
|
||||
ensureDirSync(dest);
|
||||
}
|
||||
|
||||
if (options.preserveTimestamps) {
|
||||
const srcStatInfo = Deno.statSync(src);
|
||||
Deno.utimeSync(dest, srcStatInfo.accessed, srcStatInfo.modified);
|
||||
}
|
||||
|
||||
const files = Deno.readDirSync(src);
|
||||
|
||||
for (const file of files) {
|
||||
const srcPath = file.path as string;
|
||||
const destPath = path.join(dest, path.basename(srcPath as string));
|
||||
if (file.isDirectory()) {
|
||||
copyDirSync(srcPath, destPath, options);
|
||||
} else if (file.isFile()) {
|
||||
copyFileSync(srcPath, destPath, options);
|
||||
} else if (file.isSymlink()) {
|
||||
copySymlinkSync(srcPath, destPath, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a file or directory. The directory can have contents. Like `cp -r`.
|
||||
* @param src the file/directory path.
|
||||
* Note that if `src` is a directory it will copy everything inside of this directory,
|
||||
* not the entire directory itself
|
||||
* @param dest the destination path. Note that if `src` is a file, `dest` cannot be a directory
|
||||
* @param options
|
||||
*/
|
||||
export async function copy(
|
||||
src: string,
|
||||
dest: string,
|
||||
options: CopyOptions = {}
|
||||
): Promise<void> {
|
||||
src = path.resolve(src);
|
||||
dest = path.resolve(dest);
|
||||
|
||||
if (src === dest) {
|
||||
throw new Error("Source and destination cannot be the same.");
|
||||
}
|
||||
|
||||
const srcStat = await Deno.lstat(src);
|
||||
|
||||
if (srcStat.isDirectory() && isSubdir(src, dest)) {
|
||||
throw new Error(
|
||||
`Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`
|
||||
);
|
||||
}
|
||||
|
||||
if (srcStat.isDirectory()) {
|
||||
await copyDir(src, dest, options);
|
||||
} else if (srcStat.isFile()) {
|
||||
await copyFile(src, dest, options);
|
||||
} else if (srcStat.isSymlink()) {
|
||||
await copySymLink(src, dest, options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a file or directory. The directory can have contents. Like `cp -r`.
|
||||
* @param src the file/directory path.
|
||||
* Note that if `src` is a directory it will copy everything inside of this directory,
|
||||
* not the entire directory itself
|
||||
* @param dest the destination path. Note that if `src` is a file, `dest` cannot be a directory
|
||||
* @param options
|
||||
*/
|
||||
export function copySync(
|
||||
src: string,
|
||||
dest: string,
|
||||
options: CopyOptions = {}
|
||||
): void {
|
||||
src = path.resolve(src);
|
||||
dest = path.resolve(dest);
|
||||
|
||||
if (src === dest) {
|
||||
throw new Error("Source and destination cannot be the same.");
|
||||
}
|
||||
|
||||
const srcStat = Deno.lstatSync(src);
|
||||
|
||||
if (srcStat.isDirectory() && isSubdir(src, dest)) {
|
||||
throw new Error(
|
||||
`Cannot copy '${src}' to a subdirectory of itself, '${dest}'.`
|
||||
);
|
||||
}
|
||||
|
||||
if (srcStat.isDirectory()) {
|
||||
copyDirSync(src, dest, options);
|
||||
} else if (srcStat.isFile()) {
|
||||
copyFileSync(src, dest, options);
|
||||
} else if (srcStat.isSymlink()) {
|
||||
copySymlinkSync(src, dest, options);
|
||||
}
|
||||
}
|
558
fs/copy_test.ts
Normal file
558
fs/copy_test.ts
Normal file
|
@ -0,0 +1,558 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
import { test } from "../testing/mod.ts";
|
||||
import {
|
||||
assertEquals,
|
||||
assertThrows,
|
||||
assertThrowsAsync,
|
||||
assert
|
||||
} from "../testing/asserts.ts";
|
||||
import { copy, copySync } from "./copy.ts";
|
||||
import { exists, existsSync } from "./exists.ts";
|
||||
import * as path from "./path/mod.ts";
|
||||
import { ensureDir, ensureDirSync } from "./ensure_dir.ts";
|
||||
import { ensureFile, ensureFileSync } from "./ensure_file.ts";
|
||||
import { ensureSymlink, ensureSymlinkSync } from "./ensure_symlink.ts";
|
||||
|
||||
const testdataDir = path.resolve("fs", "testdata");
|
||||
|
||||
// TODO(axetroy): Add test for Windows once symlink is implemented for Windows.
|
||||
const isWindows = Deno.platform.os === "win";
|
||||
|
||||
async function testCopy(
|
||||
name: string,
|
||||
cb: (tempDir: string) => Promise<void>
|
||||
): Promise<void> {
|
||||
test({
|
||||
name,
|
||||
async fn(): Promise<void> {
|
||||
const tempDir = await Deno.makeTempDir({
|
||||
prefix: "deno_std_copy_async_test_"
|
||||
});
|
||||
await cb(tempDir);
|
||||
await Deno.remove(tempDir, { recursive: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function testCopySync(name: string, cb: (tempDir: string) => void): void {
|
||||
test({
|
||||
name,
|
||||
fn: (): void => {
|
||||
const tempDir = Deno.makeTempDirSync({
|
||||
prefix: "deno_std_copy_sync_test_"
|
||||
});
|
||||
cb(tempDir);
|
||||
Deno.removeSync(tempDir, { recursive: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
testCopy(
|
||||
"[fs] copy file if it does no exist",
|
||||
async (tempDir: string): Promise<void> => {
|
||||
const srcFile = path.join(testdataDir, "copy_file_not_exists.txt");
|
||||
const destFile = path.join(tempDir, "copy_file_not_exists_1.txt");
|
||||
await assertThrowsAsync(
|
||||
async (): Promise<void> => {
|
||||
await copy(srcFile, destFile);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
testCopy(
|
||||
"[fs] copy if src and dest are the same paths",
|
||||
async (tempDir: string): Promise<void> => {
|
||||
const srcFile = path.join(tempDir, "copy_file_same.txt");
|
||||
const destFile = path.join(tempDir, "copy_file_same.txt");
|
||||
await assertThrowsAsync(
|
||||
async (): Promise<void> => {
|
||||
await copy(srcFile, destFile);
|
||||
},
|
||||
Error,
|
||||
"Source and destination cannot be the same."
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
testCopy(
|
||||
"[fs] copy file",
|
||||
async (tempDir: string): Promise<void> => {
|
||||
const srcFile = path.join(testdataDir, "copy_file.txt");
|
||||
const destFile = path.join(tempDir, "copy_file_copy.txt");
|
||||
|
||||
const srcContent = new TextDecoder().decode(await Deno.readFile(srcFile));
|
||||
|
||||
assertEquals(
|
||||
await exists(srcFile),
|
||||
true,
|
||||
`source should exist before copy`
|
||||
);
|
||||
assertEquals(
|
||||
await exists(destFile),
|
||||
false,
|
||||
"destination should not exist before copy"
|
||||
);
|
||||
|
||||
await copy(srcFile, destFile);
|
||||
|
||||
assertEquals(await exists(srcFile), true, "source should exist after copy");
|
||||
assertEquals(
|
||||
await exists(destFile),
|
||||
true,
|
||||
"destination should exist before copy"
|
||||
);
|
||||
|
||||
const destContent = new TextDecoder().decode(await Deno.readFile(destFile));
|
||||
|
||||
assertEquals(
|
||||
srcContent,
|
||||
destContent,
|
||||
"source and destination should have the same content"
|
||||
);
|
||||
|
||||
// Copy again and it should throw an error.
|
||||
await assertThrowsAsync(
|
||||
async (): Promise<void> => {
|
||||
await copy(srcFile, destFile);
|
||||
},
|
||||
Error,
|
||||
`'${destFile}' already exists.`
|
||||
);
|
||||
|
||||
// Modify destination file.
|
||||
await Deno.writeFile(destFile, new TextEncoder().encode("txt copy"));
|
||||
|
||||
assertEquals(
|
||||
new TextDecoder().decode(await Deno.readFile(destFile)),
|
||||
"txt copy"
|
||||
);
|
||||
|
||||
// Copy again with overwrite option.
|
||||
await copy(srcFile, destFile, { overwrite: true });
|
||||
|
||||
// Make sure the file has been overwritten.
|
||||
assertEquals(
|
||||
new TextDecoder().decode(await Deno.readFile(destFile)),
|
||||
"txt"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
testCopy(
|
||||
"[fs] copy with preserve timestamps",
|
||||
async (tempDir: string): Promise<void> => {
|
||||
const srcFile = path.join(testdataDir, "copy_file.txt");
|
||||
const destFile = path.join(tempDir, "copy_file_copy.txt");
|
||||
|
||||
const srcStatInfo = await Deno.stat(srcFile);
|
||||
|
||||
assert(typeof srcStatInfo.accessed === "number");
|
||||
assert(typeof srcStatInfo.modified === "number");
|
||||
|
||||
// Copy with overwrite and preserve timestamps options.
|
||||
await copy(srcFile, destFile, {
|
||||
overwrite: true,
|
||||
preserveTimestamps: true
|
||||
});
|
||||
|
||||
const destStatInfo = await Deno.stat(destFile);
|
||||
|
||||
assert(typeof destStatInfo.accessed === "number");
|
||||
assert(typeof destStatInfo.modified === "number");
|
||||
assertEquals(destStatInfo.accessed, srcStatInfo.accessed);
|
||||
assertEquals(destStatInfo.modified, srcStatInfo.modified);
|
||||
}
|
||||
);
|
||||
|
||||
testCopy(
|
||||
"[fs] copy directory to its subdirectory",
|
||||
async (tempDir: string): Promise<void> => {
|
||||
const srcDir = path.join(tempDir, "parent");
|
||||
const destDir = path.join(srcDir, "child");
|
||||
|
||||
await ensureDir(srcDir);
|
||||
|
||||
await assertThrowsAsync(
|
||||
async (): Promise<void> => {
|
||||
await copy(srcDir, destDir);
|
||||
},
|
||||
Error,
|
||||
`Cannot copy '${srcDir}' to a subdirectory of itself, '${destDir}'.`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
testCopy(
|
||||
"[fs] copy directory and destination exist and not a directory",
|
||||
async (tempDir: string): Promise<void> => {
|
||||
const srcDir = path.join(tempDir, "parent");
|
||||
const destDir = path.join(tempDir, "child.txt");
|
||||
|
||||
await ensureDir(srcDir);
|
||||
await ensureFile(destDir);
|
||||
|
||||
await assertThrowsAsync(
|
||||
async (): Promise<void> => {
|
||||
await copy(srcDir, destDir);
|
||||
},
|
||||
Error,
|
||||
`Cannot overwrite non-directory '${destDir}' with directory '${srcDir}'.`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
testCopy(
|
||||
"[fs] copy directory",
|
||||
async (tempDir: string): Promise<void> => {
|
||||
const srcDir = path.join(testdataDir, "copy_dir");
|
||||
const destDir = path.join(tempDir, "copy_dir");
|
||||
const srcFile = path.join(srcDir, "0.txt");
|
||||
const destFile = path.join(destDir, "0.txt");
|
||||
const srcNestFile = path.join(srcDir, "nest", "0.txt");
|
||||
const destNestFile = path.join(destDir, "nest", "0.txt");
|
||||
|
||||
await copy(srcDir, destDir);
|
||||
|
||||
assertEquals(await exists(destFile), true);
|
||||
assertEquals(await exists(destNestFile), true);
|
||||
|
||||
// After copy. The source and destination should have the same content.
|
||||
assertEquals(
|
||||
new TextDecoder().decode(await Deno.readFile(srcFile)),
|
||||
new TextDecoder().decode(await Deno.readFile(destFile))
|
||||
);
|
||||
assertEquals(
|
||||
new TextDecoder().decode(await Deno.readFile(srcNestFile)),
|
||||
new TextDecoder().decode(await Deno.readFile(destNestFile))
|
||||
);
|
||||
|
||||
// Copy again without overwrite option and it should throw an error.
|
||||
await assertThrowsAsync(
|
||||
async (): Promise<void> => {
|
||||
await copy(srcDir, destDir);
|
||||
},
|
||||
Error,
|
||||
`'${destDir}' already exists.`
|
||||
);
|
||||
|
||||
// Modify the file in the destination directory.
|
||||
await Deno.writeFile(destNestFile, new TextEncoder().encode("nest copy"));
|
||||
assertEquals(
|
||||
new TextDecoder().decode(await Deno.readFile(destNestFile)),
|
||||
"nest copy"
|
||||
);
|
||||
|
||||
// Copy again with overwrite option.
|
||||
await copy(srcDir, destDir, { overwrite: true });
|
||||
|
||||
// Make sure the file has been overwritten.
|
||||
assertEquals(
|
||||
new TextDecoder().decode(await Deno.readFile(destNestFile)),
|
||||
"nest"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
testCopy(
|
||||
"[fs] copy symlink file",
|
||||
async (tempDir: string): Promise<void> => {
|
||||
const dir = path.join(testdataDir, "copy_dir_link_file");
|
||||
const srcLink = path.join(dir, "0.txt");
|
||||
const destLink = path.join(tempDir, "0_copy.txt");
|
||||
|
||||
if (isWindows) {
|
||||
await assertThrowsAsync(
|
||||
// (): Promise<void> => copy(srcLink, destLink),
|
||||
(): Promise<void> => ensureSymlink(srcLink, destLink)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(
|
||||
(await Deno.lstat(srcLink)).isSymlink(),
|
||||
`'${srcLink}' should be symlink type`
|
||||
);
|
||||
|
||||
await copy(srcLink, destLink);
|
||||
|
||||
const statInfo = await Deno.lstat(destLink);
|
||||
|
||||
assert(statInfo.isSymlink(), `'${destLink}' should be symlink type`);
|
||||
}
|
||||
);
|
||||
|
||||
testCopy(
|
||||
"[fs] copy symlink directory",
|
||||
async (tempDir: string): Promise<void> => {
|
||||
const srcDir = path.join(testdataDir, "copy_dir");
|
||||
const srcLink = path.join(tempDir, "copy_dir_link");
|
||||
const destLink = path.join(tempDir, "copy_dir_link_copy");
|
||||
|
||||
if (isWindows) {
|
||||
await assertThrowsAsync(
|
||||
// (): Promise<void> => copy(srcLink, destLink),
|
||||
(): Promise<void> => ensureSymlink(srcLink, destLink)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
await ensureSymlink(srcDir, srcLink);
|
||||
|
||||
assert(
|
||||
(await Deno.lstat(srcLink)).isSymlink(),
|
||||
`'${srcLink}' should be symlink type`
|
||||
);
|
||||
|
||||
await copy(srcLink, destLink);
|
||||
|
||||
const statInfo = await Deno.lstat(destLink);
|
||||
|
||||
assert(statInfo.isSymlink());
|
||||
}
|
||||
);
|
||||
|
||||
testCopySync(
|
||||
"[fs] copy file synchronously if it does not exist",
|
||||
(tempDir: string): void => {
|
||||
const srcFile = path.join(testdataDir, "copy_file_not_exists_sync.txt");
|
||||
const destFile = path.join(tempDir, "copy_file_not_exists_1_sync.txt");
|
||||
assertThrows(
|
||||
(): void => {
|
||||
copySync(srcFile, destFile);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
testCopySync(
|
||||
"[fs] copy synchronously with preserve timestamps",
|
||||
(tempDir: string): void => {
|
||||
const srcFile = path.join(testdataDir, "copy_file.txt");
|
||||
const destFile = path.join(tempDir, "copy_file_copy.txt");
|
||||
|
||||
const srcStatInfo = Deno.statSync(srcFile);
|
||||
|
||||
assert(typeof srcStatInfo.accessed === "number");
|
||||
assert(typeof srcStatInfo.modified === "number");
|
||||
|
||||
// Copy with overwrite and preserve timestamps options.
|
||||
copySync(srcFile, destFile, {
|
||||
overwrite: true,
|
||||
preserveTimestamps: true
|
||||
});
|
||||
|
||||
const destStatInfo = Deno.statSync(destFile);
|
||||
|
||||
assert(typeof destStatInfo.accessed === "number");
|
||||
assert(typeof destStatInfo.modified === "number");
|
||||
assertEquals(destStatInfo.accessed, srcStatInfo.accessed);
|
||||
assertEquals(destStatInfo.modified, srcStatInfo.modified);
|
||||
}
|
||||
);
|
||||
|
||||
testCopySync(
|
||||
"[fs] copy synchronously if src and dest are the same paths",
|
||||
(): void => {
|
||||
const srcFile = path.join(testdataDir, "copy_file_same_sync.txt");
|
||||
assertThrows(
|
||||
(): void => {
|
||||
copySync(srcFile, srcFile);
|
||||
},
|
||||
Error,
|
||||
"Source and destination cannot be the same."
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
testCopySync(
|
||||
"[fs] copy file synchronously",
|
||||
(tempDir: string): void => {
|
||||
const srcFile = path.join(testdataDir, "copy_file.txt");
|
||||
const destFile = path.join(tempDir, "copy_file_copy_sync.txt");
|
||||
|
||||
const srcContent = new TextDecoder().decode(Deno.readFileSync(srcFile));
|
||||
|
||||
assertEquals(existsSync(srcFile), true);
|
||||
assertEquals(existsSync(destFile), false);
|
||||
|
||||
copySync(srcFile, destFile);
|
||||
|
||||
assertEquals(existsSync(srcFile), true);
|
||||
assertEquals(existsSync(destFile), true);
|
||||
|
||||
const destContent = new TextDecoder().decode(Deno.readFileSync(destFile));
|
||||
|
||||
assertEquals(srcContent, destContent);
|
||||
|
||||
// Copy again without overwrite option and it should throw an error.
|
||||
assertThrows(
|
||||
(): void => {
|
||||
copySync(srcFile, destFile);
|
||||
},
|
||||
Error,
|
||||
`'${destFile}' already exists.`
|
||||
);
|
||||
|
||||
// Modify destination file.
|
||||
Deno.writeFileSync(destFile, new TextEncoder().encode("txt copy"));
|
||||
|
||||
assertEquals(
|
||||
new TextDecoder().decode(Deno.readFileSync(destFile)),
|
||||
"txt copy"
|
||||
);
|
||||
|
||||
// Copy again with overwrite option.
|
||||
copySync(srcFile, destFile, { overwrite: true });
|
||||
|
||||
// Make sure the file has been overwritten.
|
||||
assertEquals(new TextDecoder().decode(Deno.readFileSync(destFile)), "txt");
|
||||
}
|
||||
);
|
||||
|
||||
testCopySync(
|
||||
"[fs] copy directory synchronously to its subdirectory",
|
||||
(tempDir: string): void => {
|
||||
const srcDir = path.join(tempDir, "parent");
|
||||
const destDir = path.join(srcDir, "child");
|
||||
|
||||
ensureDirSync(srcDir);
|
||||
|
||||
assertThrows(
|
||||
(): void => {
|
||||
copySync(srcDir, destDir);
|
||||
},
|
||||
Error,
|
||||
`Cannot copy '${srcDir}' to a subdirectory of itself, '${destDir}'.`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
testCopySync(
|
||||
"[fs] copy directory synchronously, and destination exist and not a directory",
|
||||
(tempDir: string): void => {
|
||||
const srcDir = path.join(tempDir, "parent_sync");
|
||||
const destDir = path.join(tempDir, "child.txt");
|
||||
|
||||
ensureDirSync(srcDir);
|
||||
ensureFileSync(destDir);
|
||||
|
||||
assertThrows(
|
||||
(): void => {
|
||||
copySync(srcDir, destDir);
|
||||
},
|
||||
Error,
|
||||
`Cannot overwrite non-directory '${destDir}' with directory '${srcDir}'.`
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
testCopySync(
|
||||
"[fs] copy directory synchronously",
|
||||
(tempDir: string): void => {
|
||||
const srcDir = path.join(testdataDir, "copy_dir");
|
||||
const destDir = path.join(tempDir, "copy_dir_copy_sync");
|
||||
const srcFile = path.join(srcDir, "0.txt");
|
||||
const destFile = path.join(destDir, "0.txt");
|
||||
const srcNestFile = path.join(srcDir, "nest", "0.txt");
|
||||
const destNestFile = path.join(destDir, "nest", "0.txt");
|
||||
|
||||
copySync(srcDir, destDir);
|
||||
|
||||
assertEquals(existsSync(destFile), true);
|
||||
assertEquals(existsSync(destNestFile), true);
|
||||
|
||||
// After copy. The source and destination should have the same content.
|
||||
assertEquals(
|
||||
new TextDecoder().decode(Deno.readFileSync(srcFile)),
|
||||
new TextDecoder().decode(Deno.readFileSync(destFile))
|
||||
);
|
||||
assertEquals(
|
||||
new TextDecoder().decode(Deno.readFileSync(srcNestFile)),
|
||||
new TextDecoder().decode(Deno.readFileSync(destNestFile))
|
||||
);
|
||||
|
||||
// Copy again without overwrite option and it should throw an error.
|
||||
assertThrows(
|
||||
(): void => {
|
||||
copySync(srcDir, destDir);
|
||||
},
|
||||
Error,
|
||||
`'${destDir}' already exists.`
|
||||
);
|
||||
|
||||
// Modify the file in the destination directory.
|
||||
Deno.writeFileSync(destNestFile, new TextEncoder().encode("nest copy"));
|
||||
assertEquals(
|
||||
new TextDecoder().decode(Deno.readFileSync(destNestFile)),
|
||||
"nest copy"
|
||||
);
|
||||
|
||||
// Copy again with overwrite option.
|
||||
copySync(srcDir, destDir, { overwrite: true });
|
||||
|
||||
// Make sure the file has been overwritten.
|
||||
assertEquals(
|
||||
new TextDecoder().decode(Deno.readFileSync(destNestFile)),
|
||||
"nest"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
testCopySync(
|
||||
"[fs] copy symlink file synchronously",
|
||||
(tempDir: string): void => {
|
||||
const dir = path.join(testdataDir, "copy_dir_link_file");
|
||||
const srcLink = path.join(dir, "0.txt");
|
||||
const destLink = path.join(tempDir, "0_copy.txt");
|
||||
|
||||
if (isWindows) {
|
||||
assertThrows(
|
||||
// (): void => copySync(srcLink, destLink),
|
||||
(): void => ensureSymlinkSync(srcLink, destLink)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(
|
||||
Deno.lstatSync(srcLink).isSymlink(),
|
||||
`'${srcLink}' should be symlink type`
|
||||
);
|
||||
|
||||
copySync(srcLink, destLink);
|
||||
|
||||
const statInfo = Deno.lstatSync(destLink);
|
||||
|
||||
assert(statInfo.isSymlink(), `'${destLink}' should be symlink type`);
|
||||
}
|
||||
);
|
||||
|
||||
testCopySync(
|
||||
"[fs] copy symlink directory synchronously",
|
||||
(tempDir: string): void => {
|
||||
const originDir = path.join(testdataDir, "copy_dir");
|
||||
const srcLink = path.join(tempDir, "copy_dir_link");
|
||||
const destLink = path.join(tempDir, "copy_dir_link_copy");
|
||||
|
||||
if (isWindows) {
|
||||
assertThrows(
|
||||
// (): void => copySync(srcLink, destLink),
|
||||
(): void => ensureSymlinkSync(srcLink, destLink)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
ensureSymlinkSync(originDir, srcLink);
|
||||
|
||||
assert(
|
||||
Deno.lstatSync(srcLink).isSymlink(),
|
||||
`'${srcLink}' should be symlink type`
|
||||
);
|
||||
|
||||
copySync(srcLink, destLink);
|
||||
|
||||
const statInfo = Deno.lstatSync(destLink);
|
||||
|
||||
assert(statInfo.isSymlink());
|
||||
}
|
||||
);
|
|
@ -8,6 +8,7 @@ export * from "./exists.ts";
|
|||
export * from "./glob.ts";
|
||||
export * from "./globrex.ts";
|
||||
export * from "./move.ts";
|
||||
export * from "./copy.ts";
|
||||
export * from "./read_file_str.ts";
|
||||
export * from "./write_file_str.ts";
|
||||
export * from "./read_json.ts";
|
||||
|
|
|
@ -11,6 +11,7 @@ import "./ensure_file_test.ts";
|
|||
import "./ensure_symlink_test.ts";
|
||||
import "./ensure_link_test.ts";
|
||||
import "./move_test.ts";
|
||||
import "./copy_test.ts";
|
||||
import "./read_json_test.ts";
|
||||
import "./write_json_test.ts";
|
||||
import "./read_file_str_test.ts";
|
||||
|
|
1
fs/testdata/copy_dir/0.txt
vendored
Normal file
1
fs/testdata/copy_dir/0.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
text
|
1
fs/testdata/copy_dir/nest/0.txt
vendored
Normal file
1
fs/testdata/copy_dir/nest/0.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
nest
|
1
fs/testdata/copy_dir_link_file/0.txt
vendored
Symbolic link
1
fs/testdata/copy_dir_link_file/0.txt
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
./fs/testdata/copy_dir/0.txt
|
1
fs/testdata/copy_file.txt
vendored
Normal file
1
fs/testdata/copy_file.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
txt
|
Loading…
Reference in a new issue