@pika/pack
Version:
package building, reimagined.
152 lines (151 loc) • 6.01 kB
JavaScript
//
// // This module serves as a wrapper for file operations that are inconsistant across node and OS versions.
// import fs from 'fs';
// import {promisify} from './promise.js';
// import {constants} from './fs.js';
// export type CopyFileAction = {
// src: string,
// dest: string,
// atime: Date,
// mtime: Date,
// mode: number,
// };
// let disableTimestampCorrection: boolean; // OS dependent. will be detected on first file copy.
// const readFileBuffer = promisify(fs.readFile);
// const close: (fd: number) => Promise<void> = promisify(fs.close);
// const lstat: (path: string) => Promise<fs.Stats> = promisify(fs.lstat);
// const open: (path: string, flags: string | number, mode: number) => Promise<number> = promisify(fs.open);
// const futimes: (fd: number, atime: number, mtime: number) => Promise<void> = promisify(fs.futimes);
// const write: (
// fd: number,
// buffer: Buffer,
// offset?: number,
// length?: number,
// position?: number,
// ) => Promise<void> = promisify(fs.write);
// /**
// * Unlinks the destination to force a recreation. This is needed on case-insensitive file systems
// * to force the correct naming when the filename has changed only in character-casing. (Jest -> jest).
// */
// export const copyFile = async function(data: CopyFileAction, cleanup: () => any): Promise<void> {
// // $FlowFixMe: Flow doesn't currently support COPYFILE_FICLONE
// const ficloneFlag = (constants as any).COPYFILE_FICLONE || 0;
// try {
// await unlink(data.dest);
// await copyFilePoly(data.src, data.dest, ficloneFlag, data);
// } finally {
// if (cleanup) {
// cleanup();
// }
// }
// };
// // Node 8.5.0 introduced `fs.copyFile` which is much faster, so use that when available.
// // Otherwise we fall back to reading and writing files as buffers.
// const copyFilePoly: (src: string, dest: string, flags: number, data: CopyFileAction) => Promise<void> = (
// src,
// dest,
// flags,
// data,
// ) => {
// if (fs.copyFile) {
// return new Promise((resolve, reject) =>
// fs.copyFile(src, dest, flags, err => {
// if (err) {
// reject(err);
// } else {
// fixTimes(undefined, dest, data).then(() => resolve()).catch(ex => reject(ex));
// }
// }),
// );
// } else {
// return copyWithBuffer(src, dest, flags, data);
// }
// };
// const copyWithBuffer: (src: string, dest: string, flags: number, data: CopyFileAction) => Promise<void> = async (
// src,
// dest,
// flags,
// data,
// ) => {
// // Use open -> write -> futimes -> close sequence to avoid opening the file twice:
// // one with writeFile and one with utimes
// const fd = await open(dest, 'w', data.mode);
// try {
// const buffer = await readFileBuffer(src);
// await write(fd, buffer, 0, buffer.length);
// await fixTimes(fd, dest, data);
// } finally {
// await close(fd);
// }
// };
// // We want to preserve file timestamps when copying a file, since pika uses them to decide if a file has
// // changed compared to the cache.
// // There are some OS specific cases here:
// // * On linux, fs.copyFile does not preserve timestamps, but does on OSX and Win.
// // * On windows, you must open a file with write permissions to call `fs.futimes`.
// // * On OSX you can open with read permissions and still call `fs.futimes`.
// async function fixTimes(fd: number | undefined, dest: string, data: CopyFileAction): Promise<void> {
// const doOpen = fd === undefined;
// let openfd: number = fd ? fd : -1;
// if (disableTimestampCorrection === undefined) {
// // if timestamps match already, no correction is needed.
// // the need to correct timestamps varies based on OS and node versions.
// const destStat = await lstat(dest);
// disableTimestampCorrection = fileDatesEqual(destStat.mtime, data.mtime);
// }
// if (disableTimestampCorrection) {
// return;
// }
// if (doOpen) {
// try {
// openfd = await open(dest, 'a', data.mode);
// } catch (er) {
// // file is likely read-only
// try {
// openfd = await open(dest, 'r', data.mode);
// } catch (err) {
// // We can't even open this file for reading.
// return;
// }
// }
// }
// try {
// if (openfd) {
// await futimes(openfd, data.atime, data.mtime);
// }
// } catch (er) {
// // If `futimes` throws an exception, we probably have a case of a read-only file on Windows.
// // In this case we can just return. The incorrect timestamp will just cause that file to be recopied
// // on subsequent installs, which will effect pika performance but not break anything.
// } finally {
// if (doOpen && openfd) {
// await close(openfd);
// }
// }
// }
// // Compare file timestamps.
// // Some versions of Node on windows zero the milliseconds when utime is used.
// export const fileDatesEqual = (a: Date, b: Date) => {
// const aTime = a.getTime();
// const bTime = b.getTime();
// if (process.platform !== 'win32') {
// return aTime === bTime;
// }
// // See https://github.com/nodejs/node/pull/12607
// // Submillisecond times from stat and utimes are truncated on Windows,
// // causing a file with mtime 8.0079998 and 8.0081144 to become 8.007 and 8.008
// // and making it impossible to update these files to their correct timestamps.
// if (Math.abs(aTime - bTime) <= 1) {
// return true;
// }
// const aTimeSec = Math.floor(aTime / 1000);
// const bTimeSec = Math.floor(bTime / 1000);
// // See https://github.com/nodejs/node/issues/2069
// // Some versions of Node on windows zero the milliseconds when utime is used
// // So if any of the time has a milliseconds part of zero we suspect that the
// // bug is present and compare only seconds.
// if (aTime - aTimeSec * 1000 === 0 || bTime - bTimeSec * 1000 === 0) {
// return aTimeSec === bTimeSec;
// }
// return aTime === bTime;
// };