copyDirectory({
    from, to,
    ignore,
    specificFiles,
    symlink,
    npmDiscards,
    directoryFilter,
  }) {
    if (to.slice(-1) === files.pathSep) {
      to = to.slice(0, -1);
    }

    const absPathTo = files.pathJoin(this.buildPath, to);

    if (symlink) {
      if (specificFiles) {
        throw new Error("can't copy only specific paths with a single symlink");
      }

      if (this.usedAsFile[to]) {
        throw new Error("tried to copy a directory onto " + to +
                        " but it is is already a file");
      }
    }

    ignore = ignore || [];
    let specificPaths = null;
    if (specificFiles) {
      specificPaths = {};
      specificFiles.forEach(f => {
        while (f !== '.') {
          specificPaths[files.pathJoin(to, f)] = true;
          f = files.pathDirname(f);
        }
      });
    }

    const walk = (
      absFrom,
      relTo,
      _currentRealRootDir = absFrom
    ) => {
      if (symlink && ! (relTo in this.usedAsFile)) {
        this._ensureDirectory(files.pathDirname(relTo));
        const absTo = files.pathResolve(this.buildPath, relTo);
        symlinkWithOverwrite(absFrom, absTo);
        return;
      }

      this._ensureDirectory(relTo);

      optimisticReaddir(absFrom).forEach(item => {
        let thisAbsFrom = files.pathResolve(absFrom, item);
        const thisRelTo = files.pathJoin(relTo, item);

        if (specificPaths && !(thisRelTo in specificPaths)) {
          return;
        }

        // Returns files.realpath(thisAbsFrom), iff it is external to
        // _currentRealRootDir, using caching because this function might
        // be called more than once.
        let cachedExternalPath;
        const getExternalPath = () => {
          if (typeof cachedExternalPath !== "undefined") {
            return cachedExternalPath;
          }

          try {
            var real = files.realpath(
              thisAbsFrom,
              this._realpathCache
            );
          } catch (e) {
            if (e.code !== "ENOENT" &&
                e.code !== "ELOOP") {
              throw e;
            }
            return cachedExternalPath = false;
          }

          const isExternal =
            files.pathRelative(_currentRealRootDir, real).startsWith("..");

          // Now cachedExternalPath is either a string or false.
          return cachedExternalPath = isExternal && real;
        };

        let fileStatus = optimisticLStatOrNull(thisAbsFrom);

        if (! symlink &&
            fileStatus &&
            fileStatus.isSymbolicLink()) {
          // If copyDirectory is not allowed to create symbolic links to
          // external files, and this file is a symbolic link that points
          // to an external file, update fileStatus so that we copy this
          // file as a normal file rather than as a symbolic link.

          const externalPath = getExternalPath();
          if (externalPath) {
            // Copy from the real path rather than the link path.
            thisAbsFrom = externalPath;

            // Update fileStatus to match the actual file rather than the
            // symbolic link, thus forcing the file to be copied below.
            fileStatus = optimisticLStatOrNull(externalPath);

            if (fileStatus && fileStatus.isDirectory()) {
              // Update _currentRealRootDir so that we can judge
              // isExternal relative to this new root directory when
              // traversing nested directories.
              _currentRealRootDir = externalPath;
            }
          }
        }

        if (! fileStatus) {
          // If the file did not exist, skip it.
          return;
        }

        let itemForMatch = item;
        const isDirectory = fileStatus.isDirectory();
        if (isDirectory) {
          itemForMatch += '/';
        }

        // skip excluded files
        if (ignore.some(pattern => itemForMatch.match(pattern))) {
          return;
        }

        if (npmDiscards instanceof NpmDiscards &&
            npmDiscards.shouldDiscard(thisAbsFrom, isDirectory)) {
          return;
        }

        if (isDirectory) {
          if (typeof directoryFilter !== "function" ||
              directoryFilter(thisAbsFrom)) {
            walk(thisAbsFrom, thisRelTo, _currentRealRootDir);
          }

        } else if (fileStatus.isSymbolicLink()) {
          symlinkWithOverwrite(
            // Symbolic links pointing to relative external paths are less
            // portable than absolute links, so getExternalPath() is
            // preferred if it returns a path.
            getExternalPath() || files.readlink(thisAbsFrom),
            files.pathResolve(this.buildPath, thisRelTo)
          );

          // A symlink counts as a file, as far as "can you put something under
          // it" goes.
          this.usedAsFile[thisRelTo] = true;

        } else {
          // XXX can't really optimize this copying without reading
          // the file into memory to calculate the hash.
          files.writeFile(
            files.pathResolve(this.buildPath, thisRelTo),
            // The reason we call files.writeFile here instead of
            // files.copyFile is so that we can read the file using
            // optimisticReadFile instead of files.createReadStream.
            optimisticReadFile(thisAbsFrom),
            // Logic borrowed from files.copyFile: "Create the file as
            // readable and writable by everyone, and executable by everyone
            // if the original file is executably by owner. (This mode will be
            // modified by umask.) We don't copy the mode *directly* because
            // this function is used by 'meteor create' which is copying from
            // the read-only tools tree into a writable app."
            { mode: (fileStatus.mode & 0o100) ? 0o777 : 0o666 },
          );

          this.usedAsFile[thisRelTo] = true;
        }
      });
    };

    walk(files.realpath(from, this._realpathCache), to);
  }
// - symlink: true if the directory should be symlinked instead of copying
true if the directory should be symlinked instead of copying
true if the directory should be symlinked instead of copying
copyDirectory({
    from, to,
    ignore,
    specificFiles,
    symlink,
    npmDiscards,
    directoryFilter,
  }) {
    if (to.slice(-1) === files.pathSep) {
      to = to.slice(0, -1);
    }
    const absPathTo = files.pathJoin(this.buildPath, to);
    if (symlink) {
      if (specificFiles) {
        throw new Error("can't copy only specific paths with a single symlink");
      }
      if (this.usedAsFile[to]) {
        throw new Error("tried to copy a directory onto " + to +
                        " but it is is already a file");
      }
    }
    ignore = ignore || [];
    let specificPaths = null;
    if (specificFiles) {
      specificPaths = {};
      specificFiles.forEach(f => {
        while (f !== '.') {
          specificPaths[files.pathJoin(to, f)] = true;
          f = files.pathDirname(f);
        }
      });
    }
    const walk = (
      absFrom,
      relTo,
      _currentRealRootDir = absFrom
    ) => {
      if (symlink && ! (relTo in this.usedAsFile)) {
        this._ensureDirectory(files.pathDirname(relTo));
        const absTo = files.pathResolve(this.buildPath, relTo);
        symlinkWithOverwrite(absFrom, absTo);
        return;
      }
      this._ensureDirectory(relTo);
      optimisticReaddir(absFrom).forEach(item => {
        let thisAbsFrom = files.pathResolve(absFrom, item);
        const thisRelTo = files.pathJoin(relTo, item);
        if (specificPaths && !(thisRelTo in specificPaths)) {
          return;
        }
        let cachedExternalPath;
        const getExternalPath = () => {
          if (typeof cachedExternalPath !== "undefined") {
            return cachedExternalPath;
          }
          try {
            var real = files.realpath(
              thisAbsFrom,
              this._realpathCache
            );
          } catch (e) {
            if (e.code !== "ENOENT" &&
                e.code !== "ELOOP") {
              throw e;
            }
            return cachedExternalPath = false;
          }
          const isExternal =
            files.pathRelative(_currentRealRootDir, real).startsWith("..");
          return cachedExternalPath = isExternal && real;
        };
        let fileStatus = optimisticLStatOrNull(thisAbsFrom);
        if (! symlink &&
            fileStatus &&
            fileStatus.isSymbolicLink()) {
          const externalPath = getExternalPath();
          if (externalPath) {
            thisAbsFrom = externalPath;
            fileStatus = optimisticLStatOrNull(externalPath);
            if (fileStatus && fileStatus.isDirectory()) {
              _currentRealRootDir = externalPath;
            }
          }
        }
        if (! fileStatus) {
          return;
        }
        let itemForMatch = item;
        const isDirectory = fileStatus.isDirectory();
        if (isDirectory) {
          itemForMatch += '/';
        }
        if (ignore.some(pattern => itemForMatch.match(pattern))) {
          return;
        }
        if (npmDiscards instanceof NpmDiscards &&
            npmDiscards.shouldDiscard(thisAbsFrom, isDirectory)) {
          return;
        }
        if (isDirectory) {
          if (typeof directoryFilter !== "function" ||
              directoryFilter(thisAbsFrom)) {
            walk(thisAbsFrom, thisRelTo, _currentRealRootDir);
          }
        } else if (fileStatus.isSymbolicLink()) {
          symlinkWithOverwrite(
            getExternalPath() || files.readlink(thisAbsFrom),
            files.pathResolve(this.buildPath, thisRelTo)
          );
          this.usedAsFile[thisRelTo] = true;
        } else {
          files.writeFile(
            files.pathResolve(this.buildPath, thisRelTo),
            optimisticReadFile(thisAbsFrom),
            { mode: (fileStatus.mode & 0o100) ? 0o777 : 0o666 },
          );
          this.usedAsFile[thisRelTo] = true;
        }
      });
    };
    walk(files.realpath(from, this._realpathCache), to);
  }
copyDirectory ( { from , to , ignore , specificFiles , symlink , npmDiscards , directoryFilter , } ) { if ( to . slice ( - 1 ) === files . pathSep ) { to = to . slice ( 0 , - 1 ) ; } const absPathTo = files . pathJoin ( this . buildPath , to ) ; if ( symlink ) { if ( specificFiles ) { throw new Error ( "can't copy only specific paths with a single symlink" ) ; } if ( this . usedAsFile [ to ] ) { throw new Error ( "tried to copy a directory onto " + to + " but it is is already a file" ) ; } } ignore = ignore || [ ] ; let specificPaths = null ; if ( specificFiles ) { specificPaths = { } ; specificFiles . forEach ( f => { while ( f !== '.' ) { specificPaths [ files . pathJoin ( to , f ) ] = true ; f = files . pathDirname ( f ) ; } } ) ; } const walk = ( absFrom , relTo , _currentRealRootDir = absFrom ) => { if ( symlink && ! ( relTo in this . usedAsFile ) ) { this . _ensureDirectory ( files . pathDirname ( relTo ) ) ; const absTo = files . pathResolve ( this . buildPath , relTo ) ; symlinkWithOverwrite ( absFrom , absTo ) ; return ; } this . _ensureDirectory ( relTo ) ; optimisticReaddir ( absFrom ) . forEach ( item => { let thisAbsFrom = files . pathResolve ( absFrom , item ) ; const thisRelTo = files . pathJoin ( relTo , item ) ; if ( specificPaths && ! ( thisRelTo in specificPaths ) ) { return ; } let cachedExternalPath ; const getExternalPath = ( ) => { if ( typeof cachedExternalPath !== "undefined" ) { return cachedExternalPath ; } try { var real = files . realpath ( thisAbsFrom , this . _realpathCache ) ; } catch ( e ) { if ( e . code !== "ENOENT" && e . code !== "ELOOP" ) { throw e ; } return cachedExternalPath = false ; } const isExternal = files . pathRelative ( _currentRealRootDir , real ) . startsWith ( ".." ) ; return cachedExternalPath = isExternal && real ; } ; let fileStatus = optimisticLStatOrNull ( thisAbsFrom ) ; if ( ! symlink && fileStatus && fileStatus . isSymbolicLink ( ) ) { const externalPath = getExternalPath ( ) ; if ( externalPath ) { thisAbsFrom = externalPath ; fileStatus = optimisticLStatOrNull ( externalPath ) ; if ( fileStatus && fileStatus . isDirectory ( ) ) { _currentRealRootDir = externalPath ; } } } if ( ! fileStatus ) { return ; } let itemForMatch = item ; const isDirectory = fileStatus . isDirectory ( ) ; if ( isDirectory ) { itemForMatch += '/' ; } if ( ignore . some ( pattern => itemForMatch . match ( pattern ) ) ) { return ; } if ( npmDiscards instanceof NpmDiscards && npmDiscards . shouldDiscard ( thisAbsFrom , isDirectory ) ) { return ; } if ( isDirectory ) { if ( typeof directoryFilter !== "function" || directoryFilter ( thisAbsFrom ) ) { walk ( thisAbsFrom , thisRelTo , _currentRealRootDir ) ; } } else if ( fileStatus . isSymbolicLink ( ) ) { symlinkWithOverwrite ( getExternalPath ( ) || files . readlink ( thisAbsFrom ) , files . pathResolve ( this . buildPath , thisRelTo ) ) ; this . usedAsFile [ thisRelTo ] = true ; } else { files . writeFile ( files . pathResolve ( this . buildPath , thisRelTo ) , optimisticReadFile ( thisAbsFrom ) , { mode : ( fileStatus . mode & 0o100 ) ? 0o777 : 0o666 } , ) ; this . usedAsFile [ thisRelTo ] = true ; } } ) ; } ; walk ( files . realpath ( from , this . _realpathCache ) , to ) ; }
symlink: true if the directory should be symlinked instead of copying
symlink : true if the directory should be symlinked instead of copying
// Returns files.realpath(thisAbsFrom), iff it is external to // _currentRealRootDir, using caching because this function might // be called more than once. // Now cachedExternalPath is either a string or false. // If copyDirectory is not allowed to create symbolic links to // external files, and this file is a symbolic link that points // to an external file, update fileStatus so that we copy this // file as a normal file rather than as a symbolic link. // Copy from the real path rather than the link path. // Update fileStatus to match the actual file rather than the // symbolic link, thus forcing the file to be copied below. // Update _currentRealRootDir so that we can judge // isExternal relative to this new root directory when // traversing nested directories. // If the file did not exist, skip it. // skip excluded files // Symbolic links pointing to relative external paths are less // portable than absolute links, so getExternalPath() is // preferred if it returns a path. // A symlink counts as a file, as far as "can you put something under // it" goes. // XXX can't really optimize this copying without reading // the file into memory to calculate the hash. // The reason we call files.writeFile here instead of // files.copyFile is so that we can read the file using // optimisticReadFile instead of files.createReadStream. // Logic borrowed from files.copyFile: "Create the file as // readable and writable by everyone, and executable by everyone // if the original file is executably by owner. (This mode will be // modified by umask.) We don't copy the mode *directly* because // this function is used by 'meteor create' which is copying from // the read-only tools tree into a writable app."