Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(fs): add copy/copySync #278

Merged
merged 30 commits into from
May 16, 2019
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
449a219
feat: add copy/copySync for fs modules
axetroy Mar 14, 2019
2aa835c
fix: eslint error
axetroy Mar 14, 2019
57af76b
use unimplemented() instead of throw by it self
axetroy Mar 16, 2019
a5231ca
fix: import without ext
axetroy Mar 17, 2019
76bdda3
Merge branch 'master' of https://github.com/denoland/deno_std into copy
axetroy Mar 17, 2019
a06d75e
refactor
axetroy Mar 17, 2019
67acf47
WIP: try to add copyLink
axetroy Mar 18, 2019
5a0b8ad
Merge branch 'master' into copy
axetroy May 13, 2019
b99d5a3
Merge branch 'copy' of https://github.com/axetroy/deno_std into copy
axetroy May 13, 2019
f79991b
fix eslint error
axetroy May 13, 2019
7c9aefc
rename function name
axetroy May 14, 2019
f670a8a
refactor add add preserve timestamps support
axetroy May 15, 2019
6c72aba
Merge branch 'master' into copy
axetroy May 15, 2019
4f6dcb4
fix eslint
axetroy May 15, 2019
fd5aebe
fix eslint
axetroy May 15, 2019
d626976
export copy() for mod.ts
axetroy May 15, 2019
37b6f4b
completed test case
axetroy May 15, 2019
98827b5
fix test in windows
axetroy May 15, 2019
347d755
update test case
axetroy May 15, 2019
ac690d6
try fix in windows
axetroy May 15, 2019
fff9a2d
try fix windows
axetroy May 15, 2019
7d4b1b8
update fs readme
axetroy May 15, 2019
86f723c
format readme
axetroy May 15, 2019
be2737d
update test case. do not modify the origin test folder
axetroy May 16, 2019
26c46f1
wrap 80 col for comments
axetroy May 16, 2019
41e05c0
fix grammar error
axetroy May 16, 2019
184aba4
refactor test case.
axetroy May 16, 2019
ca3c417
update test case
axetroy May 16, 2019
4b71be5
improve error message
axetroy May 16, 2019
94034cb
fix eslint
axetroy May 16, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions fs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
256 changes: 256 additions & 0 deletions fs/copy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
// 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 must not 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
axetroy marked this conversation as resolved.
Show resolved Hide resolved
* @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 must not 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);
}
}
Loading