'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

function _extends() {
  _extends = Object.assign || function (target) {
    for (var i = 1; i < arguments.length; i++) {
      var source = arguments[i];

      for (var key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
          target[key] = source[key];
        }
      }
    }

    return target;
  };

  return _extends.apply(this, arguments);
}

var hasLocalStorage = false;

try {
  hasLocalStorage = typeof localStorage !== "undefined";
} catch (error) {}

var hasProcess = typeof process !== "undefined";
var shouldDebug = hasLocalStorage && /*#__PURE__*/localStorage.getItem("DEBUG") || hasProcess && process.env.DEBUG;
var debugLog = shouldDebug ? console.log : function (_message) {
  return "";
};
/**
 * Makes a virtual copy of the TypeScript environment. This is the main API you want to be using with
 * @typescript/vfs. A lot of the other exposed functions are used by this function to get set up.
 *
 * @param sys an object which conforms to the TS Sys (a shim over read/write access to the fs)
 * @param rootFiles a list of files which are considered inside the project
 * @param ts a copy pf the TypeScript module
 * @param compilerOptions the options for this compiler run
 * @param customTransformers custom transformers for this compiler run
 */

function createVirtualTypeScriptEnvironment(sys, rootFiles, ts, compilerOptions, customTransformers) {
  if (compilerOptions === void 0) {
    compilerOptions = {};
  }

  var mergedCompilerOpts = _extends({}, defaultCompilerOptions(ts), compilerOptions);

  var _createVirtualLanguag = createVirtualLanguageServiceHost(sys, rootFiles, mergedCompilerOpts, ts, customTransformers),
      languageServiceHost = _createVirtualLanguag.languageServiceHost,
      _updateFile = _createVirtualLanguag.updateFile;

  var languageService = ts.createLanguageService(languageServiceHost);
  var diagnostics = languageService.getCompilerOptionsDiagnostics();

  if (diagnostics.length) {
    var compilerHost = createVirtualCompilerHost(sys, compilerOptions, ts);
    throw new Error(ts.formatDiagnostics(diagnostics, compilerHost.compilerHost));
  }

  return {
    // @ts-ignore
    name: "vfs",
    sys: sys,
    languageService: languageService,
    getSourceFile: function getSourceFile(fileName) {
      var _languageService$getP;

      return (_languageService$getP = languageService.getProgram()) == null ? void 0 : _languageService$getP.getSourceFile(fileName);
    },
    createFile: function createFile(fileName, content) {
      _updateFile(ts.createSourceFile(fileName, content, mergedCompilerOpts.target, false));
    },
    updateFile: function updateFile(fileName, content, optPrevTextSpan) {
      var prevSourceFile = languageService.getProgram().getSourceFile(fileName);

      if (!prevSourceFile) {
        throw new Error("Did not find a source file for " + fileName);
      }

      var prevFullContents = prevSourceFile.text; // TODO: Validate if the default text span has a fencepost error?

      var prevTextSpan = optPrevTextSpan != null ? optPrevTextSpan : ts.createTextSpan(0, prevFullContents.length);
      var newText = prevFullContents.slice(0, prevTextSpan.start) + content + prevFullContents.slice(prevTextSpan.start + prevTextSpan.length);
      var newSourceFile = ts.updateSourceFile(prevSourceFile, newText, {
        span: prevTextSpan,
        newLength: content.length
      });

      _updateFile(newSourceFile);
    }
  };
}
/**
 * Grab the list of lib files for a particular target, will return a bit more than necessary (by including
 * the dom) but that's OK
 *
 * @param target The compiler settings target baseline
 * @param ts A copy of the TypeScript module
 */

var knownLibFilesForCompilerOptions = function knownLibFilesForCompilerOptions(compilerOptions, ts) {
  var target = compilerOptions.target || ts.ScriptTarget.ES5;
  var lib = compilerOptions.lib || [];
  var files = ["lib.d.ts", "lib.dom.d.ts", "lib.dom.iterable.d.ts", "lib.webworker.d.ts", "lib.webworker.importscripts.d.ts", "lib.scripthost.d.ts", "lib.es5.d.ts", "lib.es6.d.ts", "lib.es2015.collection.d.ts", "lib.es2015.core.d.ts", "lib.es2015.d.ts", "lib.es2015.generator.d.ts", "lib.es2015.iterable.d.ts", "lib.es2015.promise.d.ts", "lib.es2015.proxy.d.ts", "lib.es2015.reflect.d.ts", "lib.es2015.symbol.d.ts", "lib.es2015.symbol.wellknown.d.ts", "lib.es2016.array.include.d.ts", "lib.es2016.d.ts", "lib.es2016.full.d.ts", "lib.es2017.d.ts", "lib.es2017.full.d.ts", "lib.es2017.intl.d.ts", "lib.es2017.object.d.ts", "lib.es2017.sharedmemory.d.ts", "lib.es2017.string.d.ts", "lib.es2017.typedarrays.d.ts", "lib.es2018.asyncgenerator.d.ts", "lib.es2018.asynciterable.d.ts", "lib.es2018.d.ts", "lib.es2018.full.d.ts", "lib.es2018.intl.d.ts", "lib.es2018.promise.d.ts", "lib.es2018.regexp.d.ts", "lib.es2019.array.d.ts", "lib.es2019.d.ts", "lib.es2019.full.d.ts", "lib.es2019.object.d.ts", "lib.es2019.string.d.ts", "lib.es2019.symbol.d.ts", "lib.es2020.d.ts", "lib.es2020.full.d.ts", "lib.es2020.string.d.ts", "lib.es2020.symbol.wellknown.d.ts", "lib.es2020.bigint.d.ts", "lib.es2020.promise.d.ts", "lib.es2020.sharedmemory.d.ts", "lib.es2020.intl.d.ts", "lib.es2021.d.ts", "lib.es2021.full.d.ts", "lib.es2021.promise.d.ts", "lib.es2021.string.d.ts", "lib.es2021.weakref.d.ts", "lib.esnext.d.ts", "lib.esnext.full.d.ts", "lib.esnext.intl.d.ts", "lib.esnext.promise.d.ts", "lib.esnext.string.d.ts", "lib.esnext.weakref.d.ts"];
  var targetToCut = ts.ScriptTarget[target];
  var matches = files.filter(function (f) {
    return f.startsWith("lib." + targetToCut.toLowerCase());
  });
  var targetCutIndex = files.indexOf(matches.pop());

  var getMax = function getMax(array) {
    return array && array.length ? array.reduce(function (max, current) {
      return current > max ? current : max;
    }) : undefined;
  }; // Find the index for everything in


  var indexesForCutting = lib.map(function (lib) {
    var matches = files.filter(function (f) {
      return f.startsWith("lib." + lib.toLowerCase());
    });
    if (matches.length === 0) return 0;
    var cutIndex = files.indexOf(matches.pop());
    return cutIndex;
  });
  var libCutIndex = getMax(indexesForCutting) || 0;
  var finalCutIndex = Math.max(targetCutIndex, libCutIndex);
  return files.slice(0, finalCutIndex + 1);
};
/**
 * Sets up a Map with lib contents by grabbing the necessary files from
 * the local copy of typescript via the file system.
 */

var createDefaultMapFromNodeModules = function createDefaultMapFromNodeModules(compilerOptions, ts, tsLibDirectory) {
  var tsModule = ts || require("typescript");

  var path = requirePath();
  var fs = requireFS();

  var getLib = function getLib(name) {
    var lib = tsLibDirectory || path.dirname(require.resolve("typescript"));
    return fs.readFileSync(path.join(lib, name), "utf8");
  };

  var libs = knownLibFilesForCompilerOptions(compilerOptions, tsModule);
  var fsMap = new Map();
  libs.forEach(function (lib) {
    fsMap.set("/" + lib, getLib(lib));
  });
  return fsMap;
};
/**
 * Adds recursively files from the FS into the map based on the folder
 */

var addAllFilesFromFolder = function addAllFilesFromFolder(map, workingDir) {
  var path = requirePath();
  var fs = requireFS();

  var walk = function walk(dir) {
    var results = [];
    var list = fs.readdirSync(dir);
    list.forEach(function (file) {
      file = path.join(dir, file);
      var stat = fs.statSync(file);

      if (stat && stat.isDirectory()) {
        /* Recurse into a subdirectory */
        results = results.concat(walk(file));
      } else {
        /* Is a file */
        results.push(file);
      }
    });
    return results;
  };

  var allFiles = walk(workingDir);
  allFiles.forEach(function (lib) {
    var fsPath = "/node_modules/@types" + lib.replace(workingDir, "");
    var content = fs.readFileSync(lib, "utf8");
    var validExtensions = [".ts", ".tsx"];

    if (validExtensions.includes(path.extname(fsPath))) {
      map.set(fsPath, content);
    }
  });
};
/** Adds all files from node_modules/@types into the FS Map */

var addFilesForTypesIntoFolder = function addFilesForTypesIntoFolder(map) {
  return addAllFilesFromFolder(map, "node_modules/@types");
};
/**
 * Create a virtual FS Map with the lib files from a particular TypeScript
 * version based on the target, Always includes dom ATM.
 *
 * @param options The compiler target, which dictates the libs to set up
 * @param version the versions of TypeScript which are supported
 * @param cache should the values be stored in local storage
 * @param ts a copy of the typescript import
 * @param lzstring an optional copy of the lz-string import
 * @param fetcher an optional replacement for the global fetch function (tests mainly)
 * @param storer an optional replacement for the localStorage global (tests mainly)
 */

var createDefaultMapFromCDN = function createDefaultMapFromCDN(options, version, cache, ts, lzstring, fetcher, storer) {
  var fetchlike = fetcher || fetch;
  var fsMap = new Map();
  var files = knownLibFilesForCompilerOptions(options, ts);
  var prefix = "https://typescript.azureedge.net/cdn/" + version + "/typescript/lib/";

  function zip(str) {
    return lzstring ? lzstring.compressToUTF16(str) : str;
  }

  function unzip(str) {
    return lzstring ? lzstring.decompressFromUTF16(str) : str;
  } // Map the known libs to a node fetch promise, then return the contents


  function uncached() {
    return Promise.all(files.map(function (lib) {
      return fetchlike(prefix + lib).then(function (resp) {
        return resp.text();
      });
    })).then(function (contents) {
      contents.forEach(function (text, index) {
        return fsMap.set("/" + files[index], text);
      });
    });
  } // A localstorage and lzzip aware version of the lib files


  function cached() {
    var storelike = storer || localStorage;
    var keys = Object.keys(localStorage);
    keys.forEach(function (key) {
      // Remove anything which isn't from this version
      if (key.startsWith("ts-lib-") && !key.startsWith("ts-lib-" + version)) {
        storelike.removeItem(key);
      }
    });
    return Promise.all(files.map(function (lib) {
      var cacheKey = "ts-lib-" + version + "-" + lib;
      var content = storelike.getItem(cacheKey);

      if (!content) {
        // Make the API call and store the text concent in the cache
        return fetchlike(prefix + lib).then(function (resp) {
          return resp.text();
        }).then(function (t) {
          storelike.setItem(cacheKey, zip(t));
          return t;
        });
      } else {
        return Promise.resolve(unzip(content));
      }
    })).then(function (contents) {
      contents.forEach(function (text, index) {
        var name = "/" + files[index];
        fsMap.set(name, text);
      });
    });
  }

  var func = cache ? cached : uncached;
  return func().then(function () {
    return fsMap;
  });
};

function notImplemented(methodName) {
  throw new Error("Method '" + methodName + "' is not implemented.");
}

function audit(name, fn) {
  return function () {
    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    var res = fn.apply(void 0, args);
    var smallres = typeof res === "string" ? res.slice(0, 80) + "..." : res;
    debugLog.apply(void 0, ["> " + name].concat(args));
    debugLog("< " + smallres);
    return res;
  };
}
/** The default compiler options if TypeScript could ever change the compiler options */


var defaultCompilerOptions = function defaultCompilerOptions(ts) {
  return _extends({}, ts.getDefaultCompilerOptions(), {
    jsx: ts.JsxEmit.React,
    strict: true,
    esModuleInterop: true,
    module: ts.ModuleKind.ESNext,
    suppressOutputPathCheck: true,
    skipLibCheck: true,
    skipDefaultLibCheck: true,
    moduleResolution: ts.ModuleResolutionKind.NodeJs
  });
}; // "/DOM.d.ts" => "/lib.dom.d.ts"


var libize = function libize(path) {
  return path.replace("/", "/lib.").toLowerCase();
};
/**
 * Creates an in-memory System object which can be used in a TypeScript program, this
 * is what provides read/write aspects of the virtual fs
 */


function createSystem(files) {
  return {
    args: [],
    createDirectory: function createDirectory() {
      return notImplemented("createDirectory");
    },
    // TODO: could make a real file tree
    directoryExists: audit("directoryExists", function (directory) {
      return Array.from(files.keys()).some(function (path) {
        return path.startsWith(directory);
      });
    }),
    exit: function exit() {
      return notImplemented("exit");
    },
    fileExists: audit("fileExists", function (fileName) {
      return files.has(fileName) || files.has(libize(fileName));
    }),
    getCurrentDirectory: function getCurrentDirectory() {
      return "/";
    },
    getDirectories: function getDirectories() {
      return [];
    },
    getExecutingFilePath: function getExecutingFilePath() {
      return notImplemented("getExecutingFilePath");
    },
    readDirectory: audit("readDirectory", function (directory) {
      return directory === "/" ? Array.from(files.keys()) : [];
    }),
    readFile: audit("readFile", function (fileName) {
      return files.get(fileName) || files.get(libize(fileName));
    }),
    resolvePath: function resolvePath(path) {
      return path;
    },
    newLine: "\n",
    useCaseSensitiveFileNames: true,
    write: function write() {
      return notImplemented("write");
    },
    writeFile: function writeFile(fileName, contents) {
      files.set(fileName, contents);
    }
  };
}
/**
 * Creates a file-system backed System object which can be used in a TypeScript program, you provide
 * a set of virtual files which are prioritised over the FS versions, then a path to the root of your
 * project (basically the folder your node_modules lives)
 */

function createFSBackedSystem(files, _projectRoot, ts, tsLibDirectory) {
  // We need to make an isolated folder for the tsconfig, but also need to be able to resolve the
  // existing node_modules structures going back through the history
  var root = _projectRoot + "/vfs";
  var path = requirePath(); // The default System in TypeScript

  var nodeSys = ts.sys;
  var tsLib = tsLibDirectory != null ? tsLibDirectory : path.dirname(require.resolve("typescript"));
  return {
    // @ts-ignore
    name: "fs-vfs",
    root: root,
    args: [],
    createDirectory: function createDirectory() {
      return notImplemented("createDirectory");
    },
    // TODO: could make a real file tree
    directoryExists: audit("directoryExists", function (directory) {
      return Array.from(files.keys()).some(function (path) {
        return path.startsWith(directory);
      }) || nodeSys.directoryExists(directory);
    }),
    exit: nodeSys.exit,
    fileExists: audit("fileExists", function (fileName) {
      if (files.has(fileName)) return true; // Don't let other tsconfigs end up touching the vfs

      if (fileName.includes("tsconfig.json") || fileName.includes("tsconfig.json")) return false;

      if (fileName.startsWith("/lib")) {
        var tsLibName = tsLib + "/" + fileName.replace("/", "");
        return nodeSys.fileExists(tsLibName);
      }

      return nodeSys.fileExists(fileName);
    }),
    getCurrentDirectory: function getCurrentDirectory() {
      return root;
    },
    getDirectories: nodeSys.getDirectories,
    getExecutingFilePath: function getExecutingFilePath() {
      return notImplemented("getExecutingFilePath");
    },
    readDirectory: audit("readDirectory", function () {
      if ((arguments.length <= 0 ? undefined : arguments[0]) === "/") {
        return Array.from(files.keys());
      } else {
        return nodeSys.readDirectory.apply(nodeSys, arguments);
      }
    }),
    readFile: audit("readFile", function (fileName) {
      if (files.has(fileName)) return files.get(fileName);

      if (fileName.startsWith("/lib")) {
        var tsLibName = tsLib + "/" + fileName.replace("/", "");
        var result = nodeSys.readFile(tsLibName);

        if (!result) {
          var libs = nodeSys.readDirectory(tsLib);
          throw new Error("TSVFS: A request was made for " + tsLibName + " but there wasn't a file found in the file map. You likely have a mismatch in the compiler options for the CDN download vs the compiler program. Existing Libs: " + libs + ".");
        }

        return result;
      }

      return nodeSys.readFile(fileName);
    }),
    resolvePath: function resolvePath(path) {
      if (files.has(path)) return path;
      return nodeSys.resolvePath(path);
    },
    newLine: "\n",
    useCaseSensitiveFileNames: true,
    write: function write() {
      return notImplemented("write");
    },
    writeFile: function writeFile(fileName, contents) {
      files.set(fileName, contents);
    }
  };
}
/**
 * Creates an in-memory CompilerHost -which is essentially an extra wrapper to System
 * which works with TypeScript objects - returns both a compiler host, and a way to add new SourceFile
 * instances to the in-memory file system.
 */

function createVirtualCompilerHost(sys, compilerOptions, ts) {
  var sourceFiles = new Map();

  var save = function save(sourceFile) {
    sourceFiles.set(sourceFile.fileName, sourceFile);
    return sourceFile;
  };

  var vHost = {
    compilerHost: _extends({}, sys, {
      getCanonicalFileName: function getCanonicalFileName(fileName) {
        return fileName;
      },
      getDefaultLibFileName: function getDefaultLibFileName() {
        return "/" + ts.getDefaultLibFileName(compilerOptions);
      },
      // getDefaultLibLocation: () => '/',
      getDirectories: function getDirectories() {
        return [];
      },
      getNewLine: function getNewLine() {
        return sys.newLine;
      },
      getSourceFile: function getSourceFile(fileName) {
        return sourceFiles.get(fileName) || save(ts.createSourceFile(fileName, sys.readFile(fileName), compilerOptions.target || defaultCompilerOptions(ts).target, false));
      },
      useCaseSensitiveFileNames: function useCaseSensitiveFileNames() {
        return sys.useCaseSensitiveFileNames;
      }
    }),
    updateFile: function updateFile(sourceFile) {
      var alreadyExists = sourceFiles.has(sourceFile.fileName);
      sys.writeFile(sourceFile.fileName, sourceFile.text);
      sourceFiles.set(sourceFile.fileName, sourceFile);
      return alreadyExists;
    }
  };
  return vHost;
}
/**
 * Creates an object which can host a language service against the virtual file-system
 */

function createVirtualLanguageServiceHost(sys, rootFiles, compilerOptions, ts, customTransformers) {
  var fileNames = [].concat(rootFiles);

  var _createVirtualCompile = createVirtualCompilerHost(sys, compilerOptions, ts),
      compilerHost = _createVirtualCompile.compilerHost,
      _updateFile2 = _createVirtualCompile.updateFile;

  var fileVersions = new Map();
  var projectVersion = 0;

  var languageServiceHost = _extends({}, compilerHost, {
    getProjectVersion: function getProjectVersion() {
      return projectVersion.toString();
    },
    getCompilationSettings: function getCompilationSettings() {
      return compilerOptions;
    },
    getCustomTransformers: function getCustomTransformers() {
      return customTransformers;
    },
    // A couple weeks of 4.8 TypeScript nightlies had a bug where the Program's
    // list of files was just a reference to the array returned by this host method,
    // which means mutations by the host that ought to result in a new Program being
    // created were not detected, since the old list of files and the new list of files
    // were in fact a reference to the same underlying array. That was fixed in
    // https://github.com/microsoft/TypeScript/pull/49813, but since the twoslash runner
    // is used in bisecting for changes, it needs to guard against being busted in that
    // couple-week period, so we defensively make a slice here.
    getScriptFileNames: function getScriptFileNames() {
      return fileNames.slice();
    },
    getScriptSnapshot: function getScriptSnapshot(fileName) {
      var contents = sys.readFile(fileName);

      if (contents) {
        return ts.ScriptSnapshot.fromString(contents);
      }

      return;
    },
    getScriptVersion: function getScriptVersion(fileName) {
      return fileVersions.get(fileName) || "0";
    },
    writeFile: sys.writeFile
  });

  var lsHost = {
    languageServiceHost: languageServiceHost,
    updateFile: function updateFile(sourceFile) {
      projectVersion++;
      fileVersions.set(sourceFile.fileName, projectVersion.toString());

      if (!fileNames.includes(sourceFile.fileName)) {
        fileNames.push(sourceFile.fileName);
      }

      _updateFile2(sourceFile);
    }
  };
  return lsHost;
}

var requirePath = function requirePath() {
  return require(String.fromCharCode(112, 97, 116, 104));
};

var requireFS = function requireFS() {
  return require(String.fromCharCode(102, 115));
};

exports.addAllFilesFromFolder = addAllFilesFromFolder;
exports.addFilesForTypesIntoFolder = addFilesForTypesIntoFolder;
exports.createDefaultMapFromCDN = createDefaultMapFromCDN;
exports.createDefaultMapFromNodeModules = createDefaultMapFromNodeModules;
exports.createFSBackedSystem = createFSBackedSystem;
exports.createSystem = createSystem;
exports.createVirtualCompilerHost = createVirtualCompilerHost;
exports.createVirtualLanguageServiceHost = createVirtualLanguageServiceHost;
exports.createVirtualTypeScriptEnvironment = createVirtualTypeScriptEnvironment;
exports.knownLibFilesForCompilerOptions = knownLibFilesForCompilerOptions;
//# sourceMappingURL=vfs.cjs.development.js.map
