text: // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
  
  // This module is the entry point for "compiler" isolate, ie. the one
  // that is created when Deno needs to compile TS/WASM to JS.
  //
  // It provides two functions that should be called by Rust:
  //  - `bootstrapCompilerRuntime`
  // This functions must be called when creating isolate
  // to properly setup runtime.
  //  - `tsCompilerOnMessage`
  // This function must be called when sending a request
  // to the compiler.
  
  // Removes the `__proto__` for security reasons.  This intentionally makes
  // Deno non compliant with ECMA-262 Annex B.2.2.1
  //
  delete Object.prototype.__proto__;
  
  ((window) => {
    const core = window.Deno.core;
    const { assert, log, notImplemented } = window.__bootstrap.util;
    const util = window.__bootstrap.util;
    const errorStack = window.__bootstrap.errorStack;
    const errors = window.__bootstrap.errors.errors;
  
    /**
     * @param {import("../dts/typescript").DiagnosticRelatedInformation} diagnostic
     */
    function fromRelatedInformation({
      start,
      length,
      file,
      messageText: msgText,
      ...ri
    }) {
      let messageText;
      let messageChain;
      if (typeof msgText === "object") {
        messageChain = msgText;
      } else {
        messageText = msgText;
      }
      if (start !== undefined && length !== undefined && file) {
        const startPos = file.getLineAndCharacterOfPosition(start);
        const sourceLine = file.getFullText().split("\n")[startPos.line];
        const fileName = file.fileName;
        return {
          start: startPos,
          end: file.getLineAndCharacterOfPosition(start + length),
          fileName,
          messageChain,
          messageText,
          sourceLine,
          ...ri,
        };
      } else {
        return {
          messageChain,
          messageText,
          ...ri,
        };
      }
    }
  
    /**
     * @param {import("../dts/typescript").Diagnostic[]} diagnostics 
     */
    function fromTypeScriptDiagnostic(diagnostics) {
      return diagnostics.map(({ relatedInformation: ri, source, ...diag }) => {
        const value = fromRelatedInformation(diag);
        value.relatedInformation = ri
          ? ri.map(fromRelatedInformation)
          : undefined;
        value.source = source;
        return value;
      });
    }
  
    function opNow() {
      const res = core.jsonOpSync("op_now");
      return res.seconds * 1e3 + res.subsecNanos / 1e6;
    }
  
    // We really don't want to depend on JSON dispatch during snapshotting, so
    // this op exchanges strings with Rust as raw byte arrays.
    function getAsset(name) {
      const opId = core.ops()["op_fetch_asset"];
      const sourceCodeBytes = core.dispatch(opId, core.encode(name));
      return core.decode(sourceCodeBytes);
    }
  
    // Constants used by `normalizeString` and `resolvePath`
    const CHAR_DOT = 46; /* . */
    const CHAR_FORWARD_SLASH = 47; /* / */
    // Using incremental compile APIs requires that all
    // paths must be either relative or absolute. Since
    // analysis in Rust operates on fully resolved URLs,
    // it makes sense to use the same scheme here.
    const ASSETS = "asset://";
    const OUT_DIR = "deno://";
    const CACHE = "cache:///";
    // This constant is passed to compiler settings when
    // doing incremental compiles. Contents of this
    // file are passed back to Rust and saved to $DENO_DIR.
    const TS_BUILD_INFO = "cache:///tsbuildinfo.json";
  
    const DEFAULT_COMPILE_OPTIONS = {
      allowJs: false,
      allowNonTsExtensions: true,
      checkJs: false,
      esModuleInterop: true,
      jsx: ts.JsxEmit.React,
      module: ts.ModuleKind.ESNext,
      outDir: OUT_DIR,
      sourceMap: true,
      strict: true,
      removeComments: true,
      target: ts.ScriptTarget.ESNext,
    };
  
    const CompilerHostTarget = {
      Main: "main",
      Runtime: "runtime",
      Worker: "worker",
    };
  
    // Warning! The values in this enum are duplicated in `cli/msg.rs`
    // Update carefully!
    const MediaType = {
      0: "JavaScript",
      1: "JSX",
      2: "TypeScript",
      3: "Dts",
      4: "TSX",
      5: "Json",
      6: "Wasm",
      7: "BuildInfo",
      8: "Unknown",
      JavaScript: 0,
      JSX: 1,
      TypeScript: 2,
      Dts: 3,
      TSX: 4,
      Json: 5,
      Wasm: 6,
      BuildInfo: 7,
      Unknown: 6,
    };
  
    function getExtension(fileName, mediaType) {
      switch (mediaType) {
        case MediaType.JavaScript:
          return ts.Extension.Js;
        case MediaType.JSX:
          return ts.Extension.Jsx;
        case MediaType.TypeScript:
          return fileName.endsWith(".d.ts") ? ts.Extension.Dts : ts.Extension.Ts;
        case MediaType.TSX:
          return ts.Extension.Tsx;
        case MediaType.Wasm:
          // Custom marker for Wasm type.
          return ts.Extension.Js;
        case MediaType.Unknown:
        default:
          throw TypeError(
            `Cannot resolve extension for "${fileName}" with mediaType "${
              MediaType[mediaType]
            }".`,
          );
      }
    }
  
    /** A global cache of module source files that have been loaded.
     * This cache will be rewritten to be populated on compiler startup
     * with files provided from Rust in request message.
     */
    const SOURCE_FILE_CACHE = new Map();
    /** A map of maps which cache resolved specifier for each import in a file.
     * This cache is used so `resolveModuleNames` ops is called as few times
     * as possible.
     *
     * First map's key is "referrer" URL ("file://a/b/c/mod.ts")
     * Second map's key is "raw" import specifier ("./foo.ts")
     * Second map's value is resolved import URL ("file:///a/b/c/foo.ts")
     */
    const RESOLVED_SPECIFIER_CACHE = new Map();
  
    function parseCompilerOptions(compilerOptions) {
      const { options, errors } = ts.convertCompilerOptionsFromJson(
        compilerOptions,
        "",
        "tsconfig.json",
      );
      return {
        options,
        diagnostics: errors.length ? errors : undefined,
      };
    }
  
    class SourceFile {
      constructor(json) {
        this.processed = false;
        Object.assign(this, json);
        this.extension = getExtension(this.url, this.mediaType);
      }
  
      static addToCache(json) {
        if (SOURCE_FILE_CACHE.has(json.url)) {
          throw new TypeError("SourceFile already exists");
        }
        const sf = new SourceFile(json);
        SOURCE_FILE_CACHE.set(sf.url, sf);
        return sf;
      }
  
      static getCached(url) {
        return SOURCE_FILE_CACHE.get(url);
      }
  
      static cacheResolvedUrl(resolvedUrl, rawModuleSpecifier, containingFile) {
        containingFile = containingFile || "";
        let innerCache = RESOLVED_SPECIFIER_CACHE.get(containingFile);
        if (!innerCache) {
          innerCache = new Map();
          RESOLVED_SPECIFIER_CACHE.set(containingFile, innerCache);
        }
        innerCache.set(rawModuleSpecifier, resolvedUrl);
      }
  
      static getResolvedUrl(moduleSpecifier, containingFile) {
        const containingCache = RESOLVED_SPECIFIER_CACHE.get(containingFile);
        if (containingCache) {
          return containingCache.get(moduleSpecifier);
        }
        return undefined;
      }
    }
  
    function getAssetInternal(filename) {
      const lastSegment = filename.split("/").pop();
      const url = ts.libMap.has(lastSegment)
        ? ts.libMap.get(lastSegment)
        : lastSegment;
      const sourceFile = SourceFile.getCached(url);
      if (sourceFile) {
        return sourceFile;
      }
      const name = url.includes(".") ? url : `${url}.d.ts`;
      const sourceCode = getAsset(name);
      return SourceFile.addToCache({
        url,
        filename: `${ASSETS}/${name}`,
        mediaType: MediaType.TypeScript,
        versionHash: "1",
        sourceCode,
      });
    }
  
    class Host {
      #options;
      #target;
      #writeFile;
      /* Deno specific APIs */
  
      constructor(
        options,
        target,
        writeFile,
      ) {
        this.#target = target;
        this.#writeFile = writeFile;
        this.#options = options;
      }
  
      get options() {
        return this.#options;
      }
  
      /* TypeScript CompilerHost APIs */
  
      fileExists(_fileName) {
        return notImplemented();
      }
  
      getCanonicalFileName(fileName) {
        return fileName;
      }
  
      getCompilationSettings() {
        log("compiler::host.getCompilationSettings()");
        return this.#options;
      }
  
      getCurrentDirectory() {
        return CACHE;
      }
  
      getDefaultLibFileName(_options) {
        log("compiler::host.getDefaultLibFileName()");
        switch (this.#target) {
          case CompilerHostTarget.Main:
          case CompilerHostTarget.Runtime:
            return `${ASSETS}/lib.deno.window.d.ts`;
          case CompilerHostTarget.Worker:
            return `${ASSETS}/lib.deno.worker.d.ts`;
        }
      }
  
      getNewLine() {
        return "\n";
      }
  
      getSourceFile(
        fileName,
        languageVersion,
        onError,
        shouldCreateNewSourceFile,
      ) {
        log("compiler::host.getSourceFile", fileName);
        try {
          assert(!shouldCreateNewSourceFile);
          const sourceFile = fileName.startsWith(ASSETS)
            ? getAssetInternal(fileName)
            : SourceFile.getCached(fileName);
          assert(sourceFile != null);
          if (!sourceFile.tsSourceFile) {
            assert(sourceFile.sourceCode != null);
            const tsSourceFileName = fileName.startsWith(ASSETS)
              ? sourceFile.filename
              : fileName;
  
            sourceFile.tsSourceFile = ts.createSourceFile(
              tsSourceFileName,
              sourceFile.sourceCode,
              languageVersion,
            );
            sourceFile.tsSourceFile.version = sourceFile.versionHash;
            delete sourceFile.sourceCode;
          }
          return sourceFile.tsSourceFile;
        } catch (e) {
          if (onError) {
            onError(String(e));
          } else {
            throw e;
          }
          return undefined;
        }
      }
  
      readFile(_fileName) {
        return notImplemented();
      }
  
      resolveModuleNames(moduleNames, containingFile) {
        log("compiler::host.resolveModuleNames", {
          moduleNames,
          containingFile,
        });
        const resolved = moduleNames.map((specifier) => {
          const maybeUrl = SourceFile.getResolvedUrl(specifier, containingFile);
  
          log("compiler::host.resolveModuleNames maybeUrl", {
            specifier,
            maybeUrl,
          });
  
          let sourceFile = undefined;
  
          if (specifier.startsWith(ASSETS)) {
            sourceFile = getAssetInternal(specifier);
          } else if (typeof maybeUrl !== "undefined") {
            sourceFile = SourceFile.getCached(maybeUrl);
          }
  
          if (!sourceFile) {
            return undefined;
          }
  
          return {
            resolvedFileName: sourceFile.url,
            isExternalLibraryImport: specifier.startsWith(ASSETS),
            extension: sourceFile.extension,
          };
        });
        log(resolved);
        return resolved;
      }
  
      useCaseSensitiveFileNames() {
        return true;
      }
  
      writeFile(fileName, data, _writeByteOrderMark, _onError, sourceFiles) {
        log("compiler::host.writeFile", fileName);
        this.#writeFile(fileName, data, sourceFiles);
      }
    }
  
    class IncrementalCompileHost extends Host {
      #buildInfo = "";
  
      constructor(
        options,
        target,
        writeFile,
        buildInfo,
      ) {
        super(options, target, writeFile);
        if (buildInfo) {
          this.#buildInfo = buildInfo;
        }
      }
  
      readFile(fileName) {
        if (fileName == TS_BUILD_INFO) {
          return this.#buildInfo;
        }
        throw new Error("unreachable");
      }
    }
  
    // NOTE: target doesn't really matter here,
    // this is in fact a mock host created just to
    // load all type definitions and snapshot them.
    let SNAPSHOT_HOST = new Host(
      DEFAULT_COMPILE_OPTIONS,
      CompilerHostTarget.Main,
      () => {},
    );
    const SNAPSHOT_COMPILER_OPTIONS = SNAPSHOT_HOST.getCompilationSettings();
  
    // This is a hacky way of adding our libs to the libs available in TypeScript()
    // as these are internal APIs of TypeScript which maintain valid libs
    ts.libs.push("deno.ns", "deno.window", "deno.worker", "deno.shared_globals");
    ts.libMap.set("deno.ns", "lib.deno.ns.d.ts");
    ts.libMap.set("deno.web", "lib.deno.web.d.ts");
    ts.libMap.set("deno.fetch", "lib.deno.fetch.d.ts");
    ts.libMap.set("deno.window", "lib.deno.window.d.ts");
    ts.libMap.set("deno.worker", "lib.deno.worker.d.ts");
    ts.libMap.set("deno.shared_globals", "lib.deno.shared_globals.d.ts");
    ts.libMap.set("deno.unstable", "lib.deno.unstable.d.ts");
  
    // this pre-populates the cache at snapshot time of our library files, so they
    // are available in the future when needed.
    SNAPSHOT_HOST.getSourceFile(
      `${ASSETS}/lib.deno.ns.d.ts`,
      ts.ScriptTarget.ESNext,
    );
    SNAPSHOT_HOST.getSourceFile(
      `${ASSETS}/lib.deno.web.d.ts`,
      ts.ScriptTarget.ESNext,
    );
    SNAPSHOT_HOST.getSourceFile(
      `${ASSETS}/lib.deno.fetch.d.ts`,
      ts.ScriptTarget.ESNext,
    );
    SNAPSHOT_HOST.getSourceFile(
      `${ASSETS}/lib.deno.window.d.ts`,
      ts.ScriptTarget.ESNext,
    );
    SNAPSHOT_HOST.getSourceFile(
      `${ASSETS}/lib.deno.worker.d.ts`,
      ts.ScriptTarget.ESNext,
    );
    SNAPSHOT_HOST.getSourceFile(
      `${ASSETS}/lib.deno.shared_globals.d.ts`,
      ts.ScriptTarget.ESNext,
    );
    SNAPSHOT_HOST.getSourceFile(
      `${ASSETS}/lib.deno.unstable.d.ts`,
      ts.ScriptTarget.ESNext,
    );
  
    // We never use this program; it's only created
    // during snapshotting to hydrate and populate
    // source file cache with lib declaration files.
    const _TS_SNAPSHOT_PROGRAM = ts.createProgram({
      rootNames: [`${ASSETS}/bootstrap.ts`],
      options: SNAPSHOT_COMPILER_OPTIONS,
      host: SNAPSHOT_HOST,
    });
  
    // Derference the snapshot host so it can be GCed
    SNAPSHOT_HOST = undefined;
  
    // This function is called only during snapshotting process
    const SYSTEM_LOADER = getAsset("system_loader.js");
    const SYSTEM_LOADER_ES5 = getAsset("system_loader_es5.js");
  
    function buildLocalSourceFileCache(sourceFileMap) {
      for (const entry of Object.values(sourceFileMap)) {
        assert(entry.sourceCode.length > 0);
        SourceFile.addToCache({
          url: entry.url,
          filename: entry.url,
          mediaType: entry.mediaType,
          sourceCode: entry.sourceCode,
          versionHash: entry.versionHash,
        });
  
        for (const importDesc of entry.imports) {
          let mappedUrl = importDesc.resolvedSpecifier;
          const importedFile = sourceFileMap[importDesc.resolvedSpecifier];
          assert(importedFile);
          const isJsOrJsx = importedFile.mediaType === MediaType.JavaScript ||
            importedFile.mediaType === MediaType.JSX;
          // If JS or JSX perform substitution for types if available
          if (isJsOrJsx) {
            // @deno-types has highest precedence, followed by
            // X-TypeScript-Types header
            if (importDesc.resolvedTypeDirective) {
              mappedUrl = importDesc.resolvedTypeDirective;
            } else if (importedFile.typeHeaders.length > 0) {
              const typeHeaders = importedFile.typeHeaders[0];
              mappedUrl = typeHeaders.resolvedSpecifier;
            } else if (importedFile.typesDirectives.length > 0) {
              const typeDirective = importedFile.typesDirectives[0];
              mappedUrl = typeDirective.resolvedSpecifier;
            }
          }
  
          mappedUrl = mappedUrl.replace("memory://", "");
          SourceFile.cacheResolvedUrl(mappedUrl, importDesc.specifier, entry.url);
        }
        for (const fileRef of entry.referencedFiles) {
          SourceFile.cacheResolvedUrl(
            fileRef.resolvedSpecifier.replace("memory://", ""),
            fileRef.specifier,
            entry.url,
          );
        }
        for (const fileRef of entry.libDirectives) {
          SourceFile.cacheResolvedUrl(
            fileRef.resolvedSpecifier.replace("memory://", ""),
            fileRef.specifier,
            entry.url,
          );
        }
      }
    }
  
    function buildSourceFileCache(sourceFileMap) {
      for (const entry of Object.values(sourceFileMap)) {
        SourceFile.addToCache({
          url: entry.url,
          filename: entry.url,
          mediaType: entry.mediaType,
          sourceCode: entry.sourceCode,
          versionHash: entry.versionHash,
        });
  
        for (const importDesc of entry.imports) {
          let mappedUrl = importDesc.resolvedSpecifier;
          const importedFile = sourceFileMap[importDesc.resolvedSpecifier];
          // IMPORTANT: due to HTTP redirects we might end up in situation
          // where URL points to a file with completely different URL.
          // In that case we take value of `redirect` field and cache
          // resolved specifier pointing to the value of the redirect.
          // It's not very elegant solution and should be rethinked.
          assert(importedFile);
          if (importedFile.redirect) {
            mappedUrl = importedFile.redirect;
          }
          const isJsOrJsx = importedFile.mediaType === MediaType.JavaScript ||
            importedFile.mediaType === MediaType.JSX;
          // If JS or JSX perform substitution for types if available
          if (isJsOrJsx) {
            // @deno-types has highest precedence, followed by
            // X-TypeScript-Types header
            if (importDesc.resolvedTypeDirective) {
              mappedUrl = importDesc.resolvedTypeDirective;
            } else if (importedFile.typeHeaders.length > 0) {
              const typeHeaders = importedFile.typeHeaders[0];
              mappedUrl = typeHeaders.resolvedSpecifier;
            } else if (importedFile.typesDirectives.length > 0) {
              const typeDirective = importedFile.typesDirectives[0];
              mappedUrl = typeDirective.resolvedSpecifier;
            }
          }
  
          SourceFile.cacheResolvedUrl(mappedUrl, importDesc.specifier, entry.url);
        }
        for (const fileRef of entry.referencedFiles) {
          SourceFile.cacheResolvedUrl(
            fileRef.resolvedSpecifier,
            fileRef.specifier,
            entry.url,
          );
        }
        for (const fileRef of entry.libDirectives) {
          SourceFile.cacheResolvedUrl(
            fileRef.resolvedSpecifier,
            fileRef.specifier,
            entry.url,
          );
        }
      }
    }
  
    // Warning! The values in this enum are duplicated in `cli/msg.rs`
    // Update carefully!
    const CompilerRequestType = {
      Compile: 0,
      Bundle: 1,
      RuntimeCompile: 2,
      RuntimeBundle: 3,
      RuntimeTranspile: 4,
    };
  
    function createBundleWriteFile(state) {
      return function writeFile(_fileName, data, sourceFiles) {
        assert(sourceFiles != null);
        assert(state.host);
        // we only support single root names for bundles
        assert(state.rootNames.length === 1);
        state.bundleOutput = buildBundle(
          state.rootNames[0],
          data,
          sourceFiles,
          state.host.options.target ?? ts.ScriptTarget.ESNext,
        );
      };
    }
  
    function createCompileWriteFile(state) {
      return function writeFile(fileName, data, sourceFiles) {
        const isBuildInfo = fileName === TS_BUILD_INFO;
  
        if (isBuildInfo) {
          assert(isBuildInfo);
          state.buildInfo = data;
          return;
        }
  
        assert(sourceFiles);
        assert(sourceFiles.length === 1);
        state.emitMap[fileName] = {
          filename: sourceFiles[0].fileName,
          contents: data,
        };
      };
    }
  
    function createRuntimeCompileWriteFile(state) {
      return function writeFile(fileName, data, sourceFiles) {
        assert(sourceFiles);
        assert(sourceFiles.length === 1);
        state.emitMap[fileName] = {
          filename: sourceFiles[0].fileName,
          contents: data,
        };
      };
    }
  
    const IGNORED_DIAGNOSTICS = [
      // TS2306: File 'file:///Users/rld/src/deno/cli/tests/subdir/amd_like.js' is
      // not a module.
      2306,
      // TS1375: 'await' expressions are only allowed at the top level of a file
      // when that file is a module, but this file has no imports or exports.
      // Consider adding an empty 'export {}' to make this file a module.
      1375,
      // TS1103: 'for-await-of' statement is only allowed within an async function
      // or async generator.
      1103,
      // TS2691: An import path cannot end with a '.ts' extension. Consider
      // importing 'bad-module' instead.
      2691,
      // TS5009: Cannot find the common subdirectory path for the input files.
      5009,
      // TS5055: Cannot write file
      // 'http://localhost:4545/cli/tests/subdir/mt_application_x_javascript.j4.js'
      // because it would overwrite input file.
      5055,
      // TypeScript is overly opinionated that only CommonJS modules kinds can
      // support JSON imports.  Allegedly this was fixed in
      // Microsoft/TypeScript#26825 but that doesn't seem to be working here,
      // so we will ignore complaints about this compiler setting.
      5070,
      // TS7016: Could not find a declaration file for module '...'. '...'
      // implicitly has an 'any' type.  This is due to `allowJs` being off by
      // default but importing of a JavaScript module.
      7016,
    ];
  
    const IGNORED_COMPILE_DIAGNOSTICS = [
      // TS1208: All files must be modules when the '--isolatedModules' flag is
      // provided.  We can ignore because we guarantuee that all files are
      // modules.
      1208,
    ];
  
    const stats = [];
    let statsStart = 0;
  
    function performanceStart() {
      stats.length = 0;
      // TODO(kitsonk) replace with performance.mark() when landed
      statsStart = opNow();
      ts.performance.enable();
    }
  
    function performanceProgram({ program, fileCount }) {
      if (program) {
        if ("getProgram" in program) {
          program = program.getProgram();
        }
        stats.push({ key: "Files", value: program.getSourceFiles().length });
        stats.push({ key: "Nodes", value: program.getNodeCount() });
        stats.push({ key: "Identifiers", value: program.getIdentifierCount() });
        stats.push({ key: "Symbols", value: program.getSymbolCount() });
        stats.push({ key: "Types", value: program.getTypeCount() });
        stats.push({
          key: "Instantiations",
          value: program.getInstantiationCount(),
        });
      } else if (fileCount != null) {
        stats.push({ key: "Files", value: fileCount });
      }
      const programTime = ts.performance.getDuration("Program");
      const bindTime = ts.performance.getDuration("Bind");
      const checkTime = ts.performance.getDuration("Check");
      const emitTime = ts.performance.getDuration("Emit");
      stats.push({ key: "Parse time", value: programTime });
      stats.push({ key: "Bind time", value: bindTime });
      stats.push({ key: "Check time", value: checkTime });
      stats.push({ key: "Emit time", value: emitTime });
      stats.push({
        key: "Total TS time",
        value: programTime + bindTime + checkTime + emitTime,
      });
    }
  
    function performanceEnd() {
      // TODO(kitsonk) replace with performance.measure() when landed
      const duration = opNow() - statsStart;
      stats.push({ key: "Compile time", value: duration });
      return stats;
    }
  
    function normalizeString(path) {
      let res = "";
      let lastSegmentLength = 0;
      let lastSlash = -1;
      let dots = 0;
      let code;
      for (let i = 0, len = path.length; i <= len; ++i) {
        if (i < len) code = path.charCodeAt(i);
        else if (code === CHAR_FORWARD_SLASH) break;
        else code = CHAR_FORWARD_SLASH;
  
        if (code === CHAR_FORWARD_SLASH) {
          if (lastSlash === i - 1 || dots === 1) {
            // NOOP
          } else if (lastSlash !== i - 1 && dots === 2) {
            if (
              res.length < 2 ||
              lastSegmentLength !== 2 ||
              res.charCodeAt(res.length - 1) !== CHAR_DOT ||
              res.charCodeAt(res.length - 2) !== CHAR_DOT
            ) {
              if (res.length > 2) {
                const lastSlashIndex = res.lastIndexOf("/");
                if (lastSlashIndex === -1) {
                  res = "";
                  lastSegmentLength = 0;
                } else {
                  res = res.slice(0, lastSlashIndex);
                  lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
                }
                lastSlash = i;
                dots = 0;
                continue;
              } else if (res.length === 2 || res.length === 1) {
                res = "";
                lastSegmentLength = 0;
                lastSlash = i;
                dots = 0;
                continue;
              }
            }
          } else {
            if (res.length > 0) res += "/" + path.slice(lastSlash + 1, i);
            else res = path.slice(lastSlash + 1, i);
            lastSegmentLength = i - lastSlash - 1;
          }
          lastSlash = i;
          dots = 0;
        } else if (code === CHAR_DOT && dots !== -1) {
          ++dots;
        } else {
          dots = -1;
        }
      }
      return res;
    }
  
    function commonPath(paths, sep = "/") {
      const [first = "", ...remaining] = paths;
      if (first === "" || remaining.length === 0) {
        return first.substring(0, first.lastIndexOf(sep) + 1);
      }
      const parts = first.split(sep);
  
      let endOfPrefix = parts.length;
      for (const path of remaining) {
        const compare = path.split(sep);
        for (let i = 0; i < endOfPrefix; i++) {
          if (compare[i] !== parts[i]) {
            endOfPrefix = i;
          }
        }
  
        if (endOfPrefix === 0) {
          return "";
        }
      }
      const prefix = parts.slice(0, endOfPrefix).join(sep);
      return prefix.endsWith(sep) ? prefix : `${prefix}${sep}`;
    }
  
    let rootExports;
  
    function normalizeUrl(rootName) {
      const match = /^(\S+:\/{2,3})(.+)$/.exec(rootName);
      if (match) {
        const [, protocol, path] = match;
        return `${protocol}${normalizeString(path)}`;
      } else {
        return rootName;
      }
    }
  
    function buildBundle(rootName, data, sourceFiles, target) {
      // when outputting to AMD and a single outfile, TypeScript makes up the module
      // specifiers which are used to define the modules, and doesn't expose them
      // publicly, so we have to try to replicate
      const sources = sourceFiles.map((sf) => sf.fileName);
      const sharedPath = commonPath(sources);
      rootName = normalizeUrl(rootName)
        .replace(sharedPath, "")
        .replace(/\.\w+$/i, "");
      // If one of the modules requires support for top-level-await, TypeScript will
      // emit the execute function as an async function.  When this is the case we
      // need to bubble up the TLA to the instantiation, otherwise we instantiate
      // synchronously.
      const hasTla = data.match(/execute:\sasync\sfunction\s/);
      let instantiate;
      if (rootExports && rootExports.length) {
        instantiate = hasTla
          ? `const __exp = await __instantiate("${rootName}", true);\n`
          : `const __exp = __instantiate("${rootName}", false);\n`;
        for (const rootExport of rootExports) {
          if (rootExport === "default") {
            instantiate += `export default __exp["${rootExport}"];\n`;
          } else {
            instantiate +=
              `export const ${rootExport} = __exp["${rootExport}"];\n`;
          }
        }
      } else {
        instantiate = hasTla
          ? `await __instantiate("${rootName}", true);\n`
          : `__instantiate("${rootName}", false);\n`;
      }
      const es5Bundle = target === ts.ScriptTarget.ES3 ||
        target === ts.ScriptTarget.ES5 ||
        target === ts.ScriptTarget.ES2015 ||
        target === ts.ScriptTarget.ES2016;
      return `${
        es5Bundle ? SYSTEM_LOADER_ES5 : SYSTEM_LOADER
      }\n${data}\n${instantiate}`;
    }
  
    function setRootExports(program, rootModule) {
      // get a reference to the type checker, this will let us find symbols from
      // the AST.
      const checker = program.getTypeChecker();
      // get a reference to the main source file for the bundle
      const mainSourceFile = program.getSourceFile(rootModule);
      assert(mainSourceFile);
      // retrieve the internal TypeScript symbol for this AST node
      const mainSymbol = checker.getSymbolAtLocation(mainSourceFile);
      if (!mainSymbol) {
        return;
      }
      rootExports = checker
        .getExportsOfModule(mainSymbol)
        // .getExportsOfModule includes type only symbols which are exported from
        // the module, so we need to try to filter those out.  While not critical
        // someone looking at the bundle would think there is runtime code behind
        // that when there isn't.  There appears to be no clean way of figuring that
        // out, so inspecting SymbolFlags that might be present that are type only
        .filter(
          (sym) =>
            sym.flags & ts.SymbolFlags.Class ||
            !(
              sym.flags & ts.SymbolFlags.Interface ||
              sym.flags & ts.SymbolFlags.TypeLiteral ||
              sym.flags & ts.SymbolFlags.Signature ||
              sym.flags & ts.SymbolFlags.TypeParameter ||
              sym.flags & ts.SymbolFlags.TypeAlias ||
              sym.flags & ts.SymbolFlags.Type ||
              sym.flags & ts.SymbolFlags.Namespace ||
              sym.flags & ts.SymbolFlags.InterfaceExcludes ||
              sym.flags & ts.SymbolFlags.TypeParameterExcludes ||
              sym.flags & ts.SymbolFlags.TypeAliasExcludes
            ),
        )
        .map((sym) => sym.getName());
    }
  
    function compile({
      buildInfo,
      compilerOptions,
      rootNames,
      target,
      sourceFileMap,
      type,
      performance,
    }) {
      if (performance) {
        performanceStart();
      }
      log(">>> compile start", { rootNames, type: CompilerRequestType[type] });
  
      // When a programme is emitted, TypeScript will call `writeFile` with
      // each file that needs to be emitted.  The Deno compiler host delegates
      // this, to make it easier to perform the right actions, which vary
      // based a lot on the request.
      const state = {
        rootNames,
        emitMap: {},
      };
  
      let diagnostics = [];
  
      const { options, diagnostics: diags } = parseCompilerOptions(
        compilerOptions,
      );
  
      diagnostics = diags.filter(
        ({ code }) => code != 5023 && !IGNORED_DIAGNOSTICS.includes(code),
      );
  
      // TODO(bartlomieju): this options is excluded by `ts.convertCompilerOptionsFromJson`
      // however stuff breaks if it's not passed (type_directives_js_main.js, compiler_js_error.ts)
      options.allowNonTsExtensions = true;
  
      const host = new IncrementalCompileHost(
        options,
        target,
        createCompileWriteFile(state),
        buildInfo,
      );
  
      buildSourceFileCache(sourceFileMap);
      // if there was a configuration and no diagnostics with it, we will continue
      // to generate the program and possibly emit it.
      if (diagnostics.length === 0) {
        const options = host.getCompilationSettings();
        const program = ts.createIncrementalProgram({
          rootNames,
          options,
          host,
        });
  
        // TODO(bartlomieju): check if this is ok
        diagnostics = [
          ...program.getConfigFileParsingDiagnostics(),
          ...program.getSyntacticDiagnostics(),
          ...program.getOptionsDiagnostics(),
          ...program.getGlobalDiagnostics(),
          ...program.getSemanticDiagnostics(),
        ];
        diagnostics = diagnostics.filter(
          ({ code }) =>
            !IGNORED_DIAGNOSTICS.includes(code) &&
            !IGNORED_COMPILE_DIAGNOSTICS.includes(code),
        );
  
        // We will only proceed with the emit if there are no diagnostics.
        if (diagnostics.length === 0) {
          const emitResult = program.emit();
          // If `checkJs` is off we still might be compiling entry point JavaScript file
          // (if it has `.ts` imports), but it won't be emitted. In that case we skip
          // assertion.
          if (options.checkJs) {
            assert(
              emitResult.emitSkipped === false,
              "Unexpected skip of the emit.",
            );
          }
          // emitResult.diagnostics is `readonly` in TS3.5+ and can't be assigned
          // without casting.
          diagnostics = emitResult.diagnostics;
        }
        performanceProgram({ program });
      }
  
      log("<<< compile end", { rootNames, type: CompilerRequestType[type] });
      const stats = performance ? performanceEnd() : undefined;
  
      return {
        emitMap: state.emitMap,
        buildInfo: state.buildInfo,
        diagnostics: fromTypeScriptDiagnostic(diagnostics),
        stats,
      };
    }
  
    function bundle({
      compilerOptions,
      rootNames,
      target,
      sourceFileMap,
      type,
      performance,
    }) {
      if (performance) {
        performanceStart();
      }
      log(">>> bundle start", {
        rootNames,
        type: CompilerRequestType[type],
      });
  
      // When a programme is emitted, TypeScript will call `writeFile` with
      // each file that needs to be emitted.  The Deno compiler host delegates
      // this, to make it easier to perform the right actions, which vary
      // based a lot on the request.
      const state = {
        rootNames,
        bundleOutput: undefined,
      };
  
      const { options, diagnostics: diags } = parseCompilerOptions(
        compilerOptions,
      );
  
      diagnostics = diags.filter(
        ({ code }) => code != 5023 && !IGNORED_DIAGNOSTICS.includes(code),
      );
  
      // TODO(bartlomieju): this options is excluded by `ts.convertCompilerOptionsFromJson`
      // however stuff breaks if it's not passed (type_directives_js_main.js)
      options.allowNonTsExtensions = true;
  
      const host = new Host(
        options,
        target,
        createBundleWriteFile(state),
      );
      state.host = host;
  
      buildSourceFileCache(sourceFileMap);
      // if there was a configuration and no diagnostics with it, we will continue
      // to generate the program and possibly emit it.
      if (diagnostics.length === 0) {
        const options = host.getCompilationSettings();
        const program = ts.createProgram({
          rootNames,
          options,
          host,
        });
  
        diagnostics = ts
          .getPreEmitDiagnostics(program)
          .filter(({ code }) => !IGNORED_DIAGNOSTICS.includes(code));
  
        // We will only proceed with the emit if there are no diagnostics.
        if (diagnostics.length === 0) {
          // we only support a single root module when bundling
          assert(rootNames.length === 1);
          setRootExports(program, rootNames[0]);
          const emitResult = program.emit();
          assert(
            emitResult.emitSkipped === false,
            "Unexpected skip of the emit.",
          );
          // emitResult.diagnostics is `readonly` in TS3.5+ and can't be assigned
          // without casting.
          diagnostics = emitResult.diagnostics;
        }
        if (performance) {
          performanceProgram({ program });
        }
      }
  
      let bundleOutput;
  
      if (diagnostics.length === 0) {
        assert(state.bundleOutput);
        bundleOutput = state.bundleOutput;
      }
  
      const stats = performance ? performanceEnd() : undefined;
  
      const result = {
        bundleOutput,
        diagnostics: fromTypeScriptDiagnostic(diagnostics),
        stats,
      };
  
      log("<<< bundle end", {
        rootNames,
        type: CompilerRequestType[type],
      });
  
      return result;
    }
  
    function runtimeCompile(request) {
      const { compilerOptions, rootNames, target, sourceFileMap } = request;
  
      log(">>> runtime compile start", {
        rootNames,
      });
  
      // if there are options, convert them into TypeScript compiler options,
      // and resolve any external file references
      const result = parseCompilerOptions(
        compilerOptions,
      );
      const options = result.options;
      // TODO(bartlomieju): this options is excluded by `ts.convertCompilerOptionsFromJson`
      // however stuff breaks if it's not passed (type_directives_js_main.js, compiler_js_error.ts)
      options.allowNonTsExtensions = true;
  
      buildLocalSourceFileCache(sourceFileMap);
  
      const state = {
        rootNames,
        emitMap: {},
      };
      const host = new Host(
        options,
        target,
        createRuntimeCompileWriteFile(state),
      );
      const program = ts.createProgram({
        rootNames,
        options: host.getCompilationSettings(),
        host,
      });
  
      const diagnostics = ts
        .getPreEmitDiagnostics(program)
        .filter(({ code }) =>
          !IGNORED_DIAGNOSTICS.includes(code) &&
          !IGNORED_COMPILE_DIAGNOSTICS.includes(code)
        );
  
      const emitResult = program.emit();
      assert(emitResult.emitSkipped === false, "Unexpected skip of the emit.");
  
      log("<<< runtime compile finish", {
        rootNames,
        emitMap: Object.keys(state.emitMap),
      });
  
      const maybeDiagnostics = diagnostics.length
        ? fromTypeScriptDiagnostic(diagnostics)
        : [];
  
      return {
        diagnostics: maybeDiagnostics,
        emitMap: state.emitMap,
      };
    }
  
    function runtimeBundle(request) {
      const { compilerOptions, rootNames, target, sourceFileMap } = request;
  
      log(">>> runtime bundle start", {
        rootNames,
      });
  
      // if there are options, convert them into TypeScript compiler options,
      // and resolve any external file references
      const result = parseCompilerOptions(
        compilerOptions,
      );
      const options = result.options;
      // TODO(bartlomieju): this options is excluded by `ts.convertCompilerOptionsFromJson`
      // however stuff breaks if it's not passed (type_directives_js_main.js, compiler_js_error.ts)
      options.allowNonTsExtensions = true;
  
      buildLocalSourceFileCache(sourceFileMap);
  
      const state = {
        rootNames,
        bundleOutput: undefined,
      };
  
      const host = new Host(
        options,
        target,
        createBundleWriteFile(state),
      );
      state.host = host;
  
      const program = ts.createProgram({
        rootNames,
        options: host.getCompilationSettings(),
        host,
      });
  
      setRootExports(program, rootNames[0]);
      const diagnostics = ts
        .getPreEmitDiagnostics(program)
        .filter(({ code }) => !IGNORED_DIAGNOSTICS.includes(code));
  
      const emitResult = program.emit();
  
      assert(emitResult.emitSkipped === false, "Unexpected skip of the emit.");
  
      log("<<< runtime bundle finish", {
        rootNames,
      });
  
      const maybeDiagnostics = diagnostics.length
        ? fromTypeScriptDiagnostic(diagnostics)
        : [];
  
      return {
        diagnostics: maybeDiagnostics,
        output: state.bundleOutput,
      };
    }
  
    function runtimeTranspile(request) {
      const result = {};
      const { sources, compilerOptions } = request;
  
      const parseResult = parseCompilerOptions(
        compilerOptions,
      );
      const options = parseResult.options;
      // TODO(bartlomieju): this options is excluded by `ts.convertCompilerOptionsFromJson`
      // however stuff breaks if it's not passed (type_directives_js_main.js, compiler_js_error.ts)
      options.allowNonTsExtensions = true;
  
      for (const [fileName, inputText] of Object.entries(sources)) {
        const { outputText: source, sourceMapText: map } = ts.transpileModule(
          inputText,
          {
            fileName,
            compilerOptions: options,
          },
        );
        result[fileName] = { source, map };
      }
      return Promise.resolve(result);
    }
  
    function opCompilerRespond(msg) {
      core.jsonOpSync("op_compiler_respond", msg);
    }
  
    async function tsCompilerOnMessage(msg) {
      const request = msg.data;
      switch (request.type) {
        case CompilerRequestType.Compile: {
          const result = compile(request);
          opCompilerRespond(result);
          break;
        }
        case CompilerRequestType.Bundle: {
          const result = bundle(request);
          opCompilerRespond(result);
          break;
        }
        case CompilerRequestType.RuntimeCompile: {
          const result = runtimeCompile(request);
          opCompilerRespond(result);
          break;
        }
        case CompilerRequestType.RuntimeBundle: {
          const result = runtimeBundle(request);
          opCompilerRespond(result);
          break;
        }
        case CompilerRequestType.RuntimeTranspile: {
          const result = await runtimeTranspile(request);
          opCompilerRespond(result);
          break;
        }
        default:
          throw new Error(
            `!!! unhandled CompilerRequestType: ${request.type} (${
              CompilerRequestType[request.type]
            })`,
          );
      }
    }
  
    function runtimeStart(source) {
      core.ops();
      // First we send an empty `Start` message to let the privileged side know we
      // are ready. The response should be a `StartRes` message containing the CLI
      // args and other info.
      const s = core.jsonOpSync("op_start");
      util.setLogDebug(s.debugFlag, source);
      errorStack.setPrepareStackTrace(Error);
      return s;
    }
  
    let hasBootstrapped = false;
  
    function bootstrapCompilerRuntime() {
      if (hasBootstrapped) {
        throw new Error("Worker runtime already bootstrapped");
      }
      hasBootstrapped = true;
      core.registerErrorClass("NotFound", errors.NotFound);
      core.registerErrorClass("PermissionDenied", errors.PermissionDenied);
      core.registerErrorClass("ConnectionRefused", errors.ConnectionRefused);
      core.registerErrorClass("ConnectionReset", errors.ConnectionReset);
      core.registerErrorClass("ConnectionAborted", errors.ConnectionAborted);
      core.registerErrorClass("NotConnected", errors.NotConnected);
      core.registerErrorClass("AddrInUse", errors.AddrInUse);
      core.registerErrorClass("AddrNotAvailable", errors.AddrNotAvailable);
      core.registerErrorClass("BrokenPipe", errors.BrokenPipe);
      core.registerErrorClass("AlreadyExists", errors.AlreadyExists);
      core.registerErrorClass("InvalidData", errors.InvalidData);
      core.registerErrorClass("TimedOut", errors.TimedOut);
      core.registerErrorClass("Interrupted", errors.Interrupted);
      core.registerErrorClass("WriteZero", errors.WriteZero);
      core.registerErrorClass("UnexpectedEof", errors.UnexpectedEof);
      core.registerErrorClass("BadResource", errors.BadResource);
      core.registerErrorClass("Http", errors.Http);
      core.registerErrorClass("URIError", URIError);
      core.registerErrorClass("TypeError", TypeError);
      core.registerErrorClass("Other", Error);
      core.registerErrorClass("Busy", errors.Busy);
      delete globalThis.__bootstrap;
      runtimeStart("TS");
    }
  
    globalThis.bootstrapCompilerRuntime = bootstrapCompilerRuntime;
    globalThis.tsCompilerOnMessage = tsCompilerOnMessage;
  })(this);
  