SymlinkPlugin.js 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const forEachBail = require("./forEachBail");
  7. const getPaths = require("./getPaths");
  8. const { getType, PathType } = require("./util/path");
  9. /** @typedef {import("./Resolver")} Resolver */
  10. /** @typedef {import("./Resolver").ResolveStepHook} ResolveStepHook */
  11. module.exports = class SymlinkPlugin {
  12. /**
  13. * @param {string | ResolveStepHook} source source
  14. * @param {string | ResolveStepHook} target target
  15. */
  16. constructor(source, target) {
  17. this.source = source;
  18. this.target = target;
  19. }
  20. /**
  21. * @param {Resolver} resolver the resolver
  22. * @returns {void}
  23. */
  24. apply(resolver) {
  25. const target = resolver.ensureHook(this.target);
  26. const fs = resolver.fileSystem;
  27. resolver
  28. .getHook(this.source)
  29. .tapAsync("SymlinkPlugin", (request, resolveContext, callback) => {
  30. if (request.ignoreSymlinks) return callback();
  31. const pathsResult = getPaths(request.path);
  32. const pathSegments = pathsResult.segments;
  33. const paths = pathsResult.paths;
  34. let containsSymlink = false;
  35. let idx = -1;
  36. forEachBail(
  37. paths,
  38. (path, callback) => {
  39. idx++;
  40. if (resolveContext.fileDependencies)
  41. resolveContext.fileDependencies.add(path);
  42. fs.readlink(path, (err, result) => {
  43. if (!err && result) {
  44. pathSegments[idx] = result;
  45. containsSymlink = true;
  46. // Shortcut when absolute symlink found
  47. const resultType = getType(result.toString());
  48. if (
  49. resultType === PathType.AbsoluteWin ||
  50. resultType === PathType.AbsolutePosix
  51. ) {
  52. return callback(null, idx);
  53. }
  54. }
  55. callback();
  56. });
  57. },
  58. (err, idx) => {
  59. if (!containsSymlink) return callback();
  60. const resultSegments =
  61. typeof idx === "number"
  62. ? pathSegments.slice(0, idx + 1)
  63. : pathSegments.slice();
  64. const result = resultSegments.reduceRight((a, b) => {
  65. return resolver.join(a, b);
  66. });
  67. const obj = {
  68. ...request,
  69. path: result
  70. };
  71. resolver.doResolve(
  72. target,
  73. obj,
  74. "resolved symlink to " + result,
  75. resolveContext,
  76. callback
  77. );
  78. }
  79. );
  80. });
  81. }
  82. };