<p dir="auto">Hello, I start to use TypeScript recently. It's super useful language for me :)<br>
By the way, I'm in a tutorial about <code class="notranslate">String Type</code> in here <a href="https://www.typescriptlang.org/docs/handbook/basic-types.html" rel="nofollow">https://www.typescriptlang.org/docs/handbook/basic-types.html</a></p>
<p dir="auto"><strong>TypeScript Version:</strong></p>
<p dir="auto">1.8.10</p>
<p dir="auto"><strong>Code</strong></p>
<h2 dir="auto">It <em>doesn't</em> work.</h2>
<p dir="auto"><em>hello.ts</em></p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="let name: string = &quot;bob&quot;;"><pre class="notranslate"><span class="pl-k">let</span> <span class="pl-s1">name</span>: <span class="pl-smi">string</span> <span class="pl-c1">=</span> <span class="pl-s">"bob"</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">Whenever I compile, the compiler will emit the following.</p>
<div class="highlight highlight-source-shell notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="$ tsc hello.ts
../../usr/local/lib/node_modules/typescript/lib/lib.d.ts(16757,13): error TS2451: Cannot redeclare block-scoped variable 'name'.
hello.ts(1,5): error TS2451: Cannot redeclare block-scoped variable 'name'."><pre class="notranslate">$ tsc hello.ts
../../usr/local/lib/node_modules/typescript/lib/lib.d.ts(16757,13): error TS2451: Cannot redeclare block-scoped variable <span class="pl-s"><span class="pl-pds">'</span>name<span class="pl-pds">'</span></span>.
hello.ts(1,5): error TS2451: Cannot redeclare block-scoped variable <span class="pl-s"><span class="pl-pds">'</span>name<span class="pl-pds">'</span></span>.</pre></div>
<h2 dir="auto">It works.</h2>
<p dir="auto"><em>hello.ts</em></p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="let aname: string = &quot;bob&quot;;"><pre class="notranslate"><span class="pl-k">let</span> <span class="pl-s1">aname</span>: <span class="pl-smi">string</span> <span class="pl-c1">=</span> <span class="pl-s">"bob"</span><span class="pl-kos">;</span></pre></div>
<h2 dir="auto">Expecting Behavior</h2>
<p dir="auto">I read a part of the <code class="notranslate">lib.d.ts</code> and it has <code class="notranslate">declare var name: string;</code>. So I may understand the compiler emits an error. However what concept cannot we use the variable <code class="notranslate">name</code> in TypeScript?<br>
And I think the tutorial which doesn't work is not good :(<br>
Or is there something is wrong with me? I hope to replay an answer :)</p>
<h1 dir="auto">Proposed module resolution strategy</h1>
<p dir="auto"><strong>UPDATE</strong>: proposal below is updated based on the results of the design meeting.<br>
Initial version can be found <a href="https://gist.github.com/vladima/725949fd9464e6a94771">here</a>.</p>
<p dir="auto">Primary differences:</p>
<ul dir="auto">
<li>instead of having <code class="notranslate">baseUrl</code> as a separate module resolution strategy we are introducing a set of properties<br>
that will allow to customize resoluton process in existing resolution strategies but base strategy still is used as a fallback.</li>
<li><code class="notranslate">rootDirs</code> are decoupled from the <code class="notranslate">baseUrl</code> and can be used without it.</li>
</ul>
<p dir="auto">Currently TypeScript supports two ways of resolving module names: <code class="notranslate">classic</code> (module name always resolves to a file, module are searched using a folder walk)<br>
and <code class="notranslate">node</code> (uses <a href="https://github.com/Microsoft/TypeScript/issues/2338" data-hovercard-type="issue" data-hovercard-url="/microsoft/TypeScript/issues/2338/hovercard">rules</a> similar to <a href="https://nodejs.org/api/modules.html#modules_all_together" rel="nofollow">node module loader</a>, was introduced in TypeScript 1.6).<br>
These approaches worked reasonably well but they were not able to model <em>baseUrl</em> based mechanics used by<br>
<a href="http://requirejs.org/docs/api.html#config-baseUrl" rel="nofollow">RequireJS</a> or <a href="https://github.com/systemjs/systemjs/blob/master/docs/overview.md#baseurl">SystemJS</a>.</p>
<p dir="auto">We could introduce third type of module resolution that will fill this gap but this will mean that once user has started to use this new type then support to<br>
discover typings embedded in node modules (and distributed via <code class="notranslate">npm</code>) is lost. Effectively user that wanted both to use <code class="notranslate">baseUrl</code> to refer to modules defined inside the project<br>
and rely on <code class="notranslate">npm</code> to obtain modules with typings will have to choose what part of the system will be broken.</p>
<p dir="auto">Instead of doing this we'll allow to declare a set of properties that will augment existing module resolution strategies. These properties are:<br>
<code class="notranslate">baseUrl</code>, <code class="notranslate">paths</code> and <code class="notranslate">rootDirs</code> (<code class="notranslate">paths</code> can only be used if <code class="notranslate">baseUrl</code> is set). If at least one of these properties is defined then compiler will try to<br>
use it to resolve module name and if it fail - will fallback to a default behavior for a current resolution strategy.</p>
<p dir="auto">Also choice of resolution strategy determines what does it mean to load a module from a given path. To be more concrete given some module name <code class="notranslate">/a/b/c</code>:</p>
<ul dir="auto">
<li><code class="notranslate">classic</code> resolver will check for the presense of files <code class="notranslate">/a/b/c.ts</code>, <code class="notranslate">/a/b/c.tsx</code> and <code class="notranslate">/a/b/c.d.ts</code>.</li>
<li><code class="notranslate">node</code> resolver will first try to load module as file by probing the same files as <code class="notranslate">classic</code> and then try to load module from directory<br>
(check <code class="notranslate">/a/b/c/index</code> with supported extension, then peek into <code class="notranslate">package.json</code> etc. More details can be found in <a href="https://github.com/Microsoft/TypeScript/issues/2338" data-hovercard-type="issue" data-hovercard-url="/microsoft/TypeScript/issues/2338/hovercard">this issue</a>)</li>
</ul>
<h1 dir="auto">Properties</h1>
<ul dir="auto">
<li><a href="#baseUrl">baseUrl</a></li>
<li><a href="#pathMappings">path mappings</a></li>
<li><a href="#rootDirs">rootDirs</a></li>
</ul>
<h2 dir="auto">BaseUrl <a id="user-content-baseurl"></a></h2>
<p dir="auto">All non-rooted paths are computed relative to <em>baseUrl</em>.<br>
Value of <em>baseUrl</em> is determined as either:</p>
<ul dir="auto">
<li>value of <em>baseUrl</em> command line argument (if given path is relative it is computed based on current directory)</li>
<li>value of <em>baseUrl</em> propery in 'tsconfig.json' (if given path is relative it is computed based on then location of 'tsconfig.json')</li>
</ul>
<h2 dir="auto">Path mappings <a id="user-content-pathmappings"></a></h2>
<p dir="auto">Sometimes modules are not directly located under <em>baseUrl</em>. It is possible to control how locations are computed in such cases<br>
using path mappings. Path mappings are specified using the following JSON structure:</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="{
    &quot;paths&quot;: {
        &quot;pattern-1&quot;: [&quot;list of substitutions&quot;],
        &quot;pattern-2&quot;: [&quot;list of substitutions&quot;],
        ...
        &quot;pattern-N&quot;: [&quot;list of substitutions&quot;]
    }
}"><pre class="notranslate"><span class="pl-kos">{</span>
    <span class="pl-s">"paths"</span>: <span class="pl-kos">{</span>
        <span class="pl-s">"pattern-1"</span>: <span class="pl-kos">[</span><span class="pl-s">"list of substitutions"</span><span class="pl-kos">]</span><span class="pl-kos">,</span>
        <span class="pl-s">"pattern-2"</span>: <span class="pl-kos">[</span><span class="pl-s">"list of substitutions"</span><span class="pl-kos">]</span><span class="pl-kos">,</span>
        ...
        <span class="pl-s">"pattern-N"</span>: <span class="pl-kos">[</span><span class="pl-s">"list of substitutions"</span><span class="pl-kos">]</span>
    <span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">Patterns and substitutions are strings that can have zero or one asteriks ('*').<br>
Interpretation of both patterns and substitutions will be described in <a href="#resolutionProcess">Resolution process</a> section.</p>
<h2 dir="auto">Resolution process <a id="user-content-resolutionprocess"></a></h2>
<p dir="auto">Non-relative module names are resolved slightly differently comparing<br>
to relative (start with "./" or "../") and rooted module names (start with "/", drive name or schema).</p>
<h3 dir="auto">Resolution of non-relative module names (mostly matches SystemJS)</h3>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="
// mimics path mappings in SystemJS
// NOTE: moduleExists checks if file with any supported extension exists on disk
function resolveNonRelativeModuleName(moduleName: string): string {
    // check if module name should be used as-is or it should be mapped to different value
    let longestMatchedPrefixLength = 0;
    let matchedPattern: string;
    let matchedWildcard: string;

    for (let pattern in config.paths) {
        assert(pattern.countOf('*') &lt;= 1);
        let indexOfWildcard = pattern.indexOf('*'); 
        if (indexOfWildcard !== -1) {
            // if pattern contains asterisk then asterisk acts as a capture group with a greedy matching
            // i.e. for the string 'abbb' pattern 'a*b' will get 'bb' as '*'

            // check if module name starts with prefix, ends with suffix and these two don't overlap
            let prefix = pattern.substr(0, indexOfWildcard);
            let suffix = pattern.substr(indexOfWildcard + 1);
            if (moduleName.length &gt;= prefix.length + suffix.length &amp;&amp; 
                moduleName.startsWith(prefix) &amp;&amp;
                moduleName.endsWith(suffix)) {

                // use length of matched prefix as betterness criteria
                if (longestMatchedPrefixLength &lt; prefix.length) {
                    // save length of the prefix
                    longestMatchedPrefixLength = prefix.length;
                    // save matched pattern
                    matchedPattern = pattern;
                    // save matched wildcard content 
                    matchedWildcard = moduleName.substr(prefix.length, moduleName.length - suffix.length);
                }
            }
        }
        else {
            // pattern does not contain asterisk - module name should exactly match pattern to succeed
            if (pattern === moduleName) {
                // save pattern
                matchedPattern = pattern;
                // drop saved wildcard match 
                matchedWildcard = undefined;
                // exact match is found - can exit early 
                break;
            }
        }
    }

    if (!matchedPattern) {
        // no pattern was matched so module name can be used as-is
        let path = combine(baseUrl, moduleName);
        return moduleExists(path) ? path : undefined;
    }

    // some pattern was matched - module name needs to be substituted
    let substitutions = config.paths[matchedPattern].asArray();
    for (let subst of substitutions) {
        assert(substs.countOf('*') &lt;= 1);
        // replace * in substitution with matched wildcard
        let path = matchedWildcard ? subst.replace(&quot;*&quot;, matchedWildcard) : subst;
        // if substituion is a relative path - combine it with baseUrl
        path = isRelative(path) ? combine(baseUrl, path) : path;
        if (moduleExists(path)) {
            return path;
        }
    }

    return undefined;   
}
"><pre class="notranslate"><span class="pl-c">// mimics path mappings in SystemJS</span>
<span class="pl-c">// NOTE: moduleExists checks if file with any supported extension exists on disk</span>
<span class="pl-k">function</span> <span class="pl-en">resolveNonRelativeModuleName</span><span class="pl-kos">(</span><span class="pl-s1">moduleName</span>: <span class="pl-s1">string</span><span class="pl-kos">)</span>: <span class="pl-s1">string</span> <span class="pl-kos">{</span>
    <span class="pl-c">// check if module name should be used as-is or it should be mapped to different value</span>
    <span class="pl-k">let</span> <span class="pl-s1">longestMatchedPrefixLength</span> <span class="pl-c1">=</span> <span class="pl-c1">0</span><span class="pl-kos">;</span>
    <span class="pl-k">let</span> <span class="pl-s1">matchedPattern</span>: <span class="pl-s1">string</span><span class="pl-kos">;</span>
    <span class="pl-k">let</span> <span class="pl-s1">matchedWildcard</span>: <span class="pl-s1">string</span><span class="pl-kos">;</span>

    <span class="pl-k">for</span> <span class="pl-kos">(</span><span class="pl-k">let</span> <span class="pl-s1">pattern</span> <span class="pl-k">in</span> <span class="pl-s1">config</span><span class="pl-kos">.</span><span class="pl-c1">paths</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
        <span class="pl-en">assert</span><span class="pl-kos">(</span><span class="pl-s1">pattern</span><span class="pl-kos">.</span><span class="pl-en">countOf</span><span class="pl-kos">(</span><span class="pl-s">'*'</span><span class="pl-kos">)</span> <span class="pl-c1">&lt;=</span> <span class="pl-c1">1</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">let</span> <span class="pl-s1">indexOfWildcard</span> <span class="pl-c1">=</span> <span class="pl-s1">pattern</span><span class="pl-kos">.</span><span class="pl-en">indexOf</span><span class="pl-kos">(</span><span class="pl-s">'*'</span><span class="pl-kos">)</span><span class="pl-kos">;</span> 
        <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">indexOfWildcard</span> <span class="pl-c1">!==</span> <span class="pl-c1">-</span><span class="pl-c1">1</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
            <span class="pl-c">// if pattern contains asterisk then asterisk acts as a capture group with a greedy matching</span>
            <span class="pl-c">// i.e. for the string 'abbb' pattern 'a*b' will get 'bb' as '*'</span>

            <span class="pl-c">// check if module name starts with prefix, ends with suffix and these two don't overlap</span>
            <span class="pl-k">let</span> <span class="pl-s1">prefix</span> <span class="pl-c1">=</span> <span class="pl-s1">pattern</span><span class="pl-kos">.</span><span class="pl-en">substr</span><span class="pl-kos">(</span><span class="pl-c1">0</span><span class="pl-kos">,</span> <span class="pl-s1">indexOfWildcard</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
            <span class="pl-k">let</span> <span class="pl-s1">suffix</span> <span class="pl-c1">=</span> <span class="pl-s1">pattern</span><span class="pl-kos">.</span><span class="pl-en">substr</span><span class="pl-kos">(</span><span class="pl-s1">indexOfWildcard</span> <span class="pl-c1">+</span> <span class="pl-c1">1</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
            <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">moduleName</span><span class="pl-kos">.</span><span class="pl-c1">length</span> <span class="pl-c1">&gt;=</span> <span class="pl-s1">prefix</span><span class="pl-kos">.</span><span class="pl-c1">length</span> <span class="pl-c1">+</span> <span class="pl-s1">suffix</span><span class="pl-kos">.</span><span class="pl-c1">length</span> <span class="pl-c1">&amp;&amp;</span> 
                <span class="pl-s1">moduleName</span><span class="pl-kos">.</span><span class="pl-en">startsWith</span><span class="pl-kos">(</span><span class="pl-s1">prefix</span><span class="pl-kos">)</span> <span class="pl-c1">&amp;&amp;</span>
                <span class="pl-s1">moduleName</span><span class="pl-kos">.</span><span class="pl-en">endsWith</span><span class="pl-kos">(</span><span class="pl-s1">suffix</span><span class="pl-kos">)</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>

                <span class="pl-c">// use length of matched prefix as betterness criteria</span>
                <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">longestMatchedPrefixLength</span> <span class="pl-c1">&lt;</span> <span class="pl-s1">prefix</span><span class="pl-kos">.</span><span class="pl-c1">length</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
                    <span class="pl-c">// save length of the prefix</span>
                    <span class="pl-s1">longestMatchedPrefixLength</span> <span class="pl-c1">=</span> <span class="pl-s1">prefix</span><span class="pl-kos">.</span><span class="pl-c1">length</span><span class="pl-kos">;</span>
                    <span class="pl-c">// save matched pattern</span>
                    <span class="pl-s1">matchedPattern</span> <span class="pl-c1">=</span> <span class="pl-s1">pattern</span><span class="pl-kos">;</span>
                    <span class="pl-c">// save matched wildcard content </span>
                    <span class="pl-s1">matchedWildcard</span> <span class="pl-c1">=</span> <span class="pl-s1">moduleName</span><span class="pl-kos">.</span><span class="pl-en">substr</span><span class="pl-kos">(</span><span class="pl-s1">prefix</span><span class="pl-kos">.</span><span class="pl-c1">length</span><span class="pl-kos">,</span> <span class="pl-s1">moduleName</span><span class="pl-kos">.</span><span class="pl-c1">length</span> <span class="pl-c1">-</span> <span class="pl-s1">suffix</span><span class="pl-kos">.</span><span class="pl-c1">length</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
                <span class="pl-kos">}</span>
            <span class="pl-kos">}</span>
        <span class="pl-kos">}</span>
        <span class="pl-k">else</span> <span class="pl-kos">{</span>
            <span class="pl-c">// pattern does not contain asterisk - module name should exactly match pattern to succeed</span>
            <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">pattern</span> <span class="pl-c1">===</span> <span class="pl-s1">moduleName</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
                <span class="pl-c">// save pattern</span>
                <span class="pl-s1">matchedPattern</span> <span class="pl-c1">=</span> <span class="pl-s1">pattern</span><span class="pl-kos">;</span>
                <span class="pl-c">// drop saved wildcard match </span>
                <span class="pl-s1">matchedWildcard</span> <span class="pl-c1">=</span> <span class="pl-c1">undefined</span><span class="pl-kos">;</span>
                <span class="pl-c">// exact match is found - can exit early </span>
                <span class="pl-k">break</span><span class="pl-kos">;</span>
            <span class="pl-kos">}</span>
        <span class="pl-kos">}</span>
    <span class="pl-kos">}</span>

    <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-c1">!</span><span class="pl-s1">matchedPattern</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
        <span class="pl-c">// no pattern was matched so module name can be used as-is</span>
        <span class="pl-k">let</span> <span class="pl-s1">path</span> <span class="pl-c1">=</span> <span class="pl-en">combine</span><span class="pl-kos">(</span><span class="pl-s1">baseUrl</span><span class="pl-kos">,</span> <span class="pl-s1">moduleName</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">return</span> <span class="pl-en">moduleExists</span><span class="pl-kos">(</span><span class="pl-s1">path</span><span class="pl-kos">)</span> ? <span class="pl-s1">path</span> : <span class="pl-c1">undefined</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span>

    <span class="pl-c">// some pattern was matched - module name needs to be substituted</span>
    <span class="pl-k">let</span> <span class="pl-s1">substitutions</span> <span class="pl-c1">=</span> <span class="pl-s1">config</span><span class="pl-kos">.</span><span class="pl-c1">paths</span><span class="pl-kos">[</span><span class="pl-s1">matchedPattern</span><span class="pl-kos">]</span><span class="pl-kos">.</span><span class="pl-en">asArray</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">for</span> <span class="pl-kos">(</span><span class="pl-k">let</span> <span class="pl-s1">subst</span> <span class="pl-k">of</span> <span class="pl-s1">substitutions</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
        <span class="pl-en">assert</span><span class="pl-kos">(</span><span class="pl-s1">substs</span><span class="pl-kos">.</span><span class="pl-en">countOf</span><span class="pl-kos">(</span><span class="pl-s">'*'</span><span class="pl-kos">)</span> <span class="pl-c1">&lt;=</span> <span class="pl-c1">1</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-c">// replace * in substitution with matched wildcard</span>
        <span class="pl-k">let</span> <span class="pl-s1">path</span> <span class="pl-c1">=</span> <span class="pl-s1">matchedWildcard</span> ? <span class="pl-s1">subst</span><span class="pl-kos">.</span><span class="pl-en">replace</span><span class="pl-kos">(</span><span class="pl-s">"*"</span><span class="pl-kos">,</span> <span class="pl-s1">matchedWildcard</span><span class="pl-kos">)</span> : <span class="pl-s1">subst</span><span class="pl-kos">;</span>
        <span class="pl-c">// if substituion is a relative path - combine it with baseUrl</span>
        <span class="pl-s1">path</span> <span class="pl-c1">=</span> <span class="pl-en">isRelative</span><span class="pl-kos">(</span><span class="pl-s1">path</span><span class="pl-kos">)</span> ? <span class="pl-en">combine</span><span class="pl-kos">(</span><span class="pl-s1">baseUrl</span><span class="pl-kos">,</span> <span class="pl-s1">path</span><span class="pl-kos">)</span> : <span class="pl-s1">path</span><span class="pl-kos">;</span>
        <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-en">moduleExists</span><span class="pl-kos">(</span><span class="pl-s1">path</span><span class="pl-kos">)</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
            <span class="pl-k">return</span> <span class="pl-s1">path</span><span class="pl-kos">;</span>
        <span class="pl-kos">}</span>
    <span class="pl-kos">}</span>

    <span class="pl-k">return</span> <span class="pl-c1">undefined</span><span class="pl-kos">;</span>   
<span class="pl-kos">}</span></pre></div>
<h3 dir="auto">Resolution of relative module names</h3>
<h4 dir="auto">Default resolution logic (matches SystemJS)</h4>
<p dir="auto">Relative module names are computed treating location of source file that contains the import as base folder.<br>
Path mappings are not applied.</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="function resolveRelativeModuleName(moduleName: string, containingFile: string): string {
    let path = combine(getDirectoryName(containingFile), moduleName);
    return moduleExists(path) ? path : undefined;
}"><pre class="notranslate"><span class="pl-k">function</span> <span class="pl-en">resolveRelativeModuleName</span><span class="pl-kos">(</span><span class="pl-s1">moduleName</span>: <span class="pl-s1">string</span><span class="pl-kos">,</span> <span class="pl-s1">containingFile</span>: <span class="pl-s1">string</span><span class="pl-kos">)</span>: <span class="pl-s1">string</span> <span class="pl-kos">{</span>
    <span class="pl-k">let</span> <span class="pl-s1">path</span> <span class="pl-c1">=</span> <span class="pl-en">combine</span><span class="pl-kos">(</span><span class="pl-en">getDirectoryName</span><span class="pl-kos">(</span><span class="pl-s1">containingFile</span><span class="pl-kos">)</span><span class="pl-kos">,</span> <span class="pl-s1">moduleName</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">return</span> <span class="pl-en">moduleExists</span><span class="pl-kos">(</span><span class="pl-s1">path</span><span class="pl-kos">)</span> ? <span class="pl-s1">path</span> : <span class="pl-c1">undefined</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
<h4 dir="auto">Using <code class="notranslate">rootDirs</code><a id="user-content-rootdirs"></a></h4>
<p dir="auto">'rootDirs' allows the project to be spreaded across multiple locations and resolve modules with relative names as if multiple project roots were merged together in one folder. For example project contains source files that are located in different directories on then file system (not under the same root) but user still still prefers to use relative module names because in runtime such names can be successfully resolved due to bundling.</p>
<p dir="auto">For example consider this project structure:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content=" shared
 └── projects
     └── project
         └── src
             ├── viewManager.ts (imports './views/view1')
             └── views
                 └── view2.ts (imports './view1')
 userFiles
 └── project
     └── src
         └── views
             └── view1.ts (imports './view2')"><pre class="notranslate"><code class="notranslate"> shared
 └── projects
     └── project
         └── src
             ├── viewManager.ts (imports './views/view1')
             └── views
                 └── view2.ts (imports './view1')
 userFiles
 └── project
     └── src
         └── views
             └── view1.ts (imports './view2')
</code></pre></div>
<p dir="auto">Logically files in <code class="notranslate">userFiles/project</code> and <code class="notranslate">shared/projects/project</code> belong to the same project and<br>
after build they indeed will be bundled together.</p>
<p dir="auto">In order to support this we'll add configuration property "rootDirs":</p>
<div class="highlight highlight-source-json notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="{
    &quot;rootDirs&quot;: [
        &quot;rootDir-1/&quot;,
        &quot;rootDir-2/&quot;,
        ...
        &quot;rootDir-n/&quot;
    ]
}"><pre class="notranslate">{
    <span class="pl-ent">"rootDirs"</span>: [
        <span class="pl-s"><span class="pl-pds">"</span>rootDir-1/<span class="pl-pds">"</span></span>,
        <span class="pl-s"><span class="pl-pds">"</span>rootDir-2/<span class="pl-pds">"</span></span>,
        <span class="pl-ii">...</span>
        <span class="pl-s"><span class="pl-pds">"</span>rootDir-n/<span class="pl-pds">"</span></span>
    ]
}</pre></div>
<p dir="auto">This property stores list of base folders, every folder name can be either absolute or relative.<br>
Elements in <code class="notranslate">rootDirs</code> that represent non-absolute paths will be converted to absolute using location of tsconfig.json as a base folder - this is the common approach for all paths defined in tsconfig.json</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="///Algorithm for resolving relative module name
function resolveRelativeModuleName(moduleName: string, containingFile: string): string {
    // convert relative module name to absolute using location of containing file
    // this step is exactly the same as when doing resolution without path mapping
    let path = combine(getDirectoryName(containingFile), moduleName);

    // convert absolute module name to non-relative
    // try to find element in 'rootDirs' that is the longest prefix for &quot;path' and return path.substr(prefix.length) as non-relative name
    let { matchingRootDir, nonRelativeName } = tryFindLongestPrefixAndReturnSuffix(rootDirs, path);
    if (!matchingRootDir) {
        // cannot extract non relative name
        return undefined;
    }
    // first try to load module from initial location
    if (moduleExists(path)) {
        return path;
    }
    // then try other entries in rootDirs
    for (const rootDir of rootDirs) {
        if (rootDir === matchingRootDir) {
            continue;
        }
        const candidate = combine(rootDir, nonRelativeName);
        if (moduleExists(candidate)) {
            return candidate;
        }
    }
    // failure case
    return undefined;
}"><pre class="notranslate"><span class="pl-c">///Algorithm for resolving relative module name</span>
<span class="pl-k">function</span> <span class="pl-en">resolveRelativeModuleName</span><span class="pl-kos">(</span><span class="pl-s1">moduleName</span>: <span class="pl-s1">string</span><span class="pl-kos">,</span> <span class="pl-s1">containingFile</span>: <span class="pl-s1">string</span><span class="pl-kos">)</span>: <span class="pl-s1">string</span> <span class="pl-kos">{</span>
    <span class="pl-c">// convert relative module name to absolute using location of containing file</span>
    <span class="pl-c">// this step is exactly the same as when doing resolution without path mapping</span>
    <span class="pl-k">let</span> <span class="pl-s1">path</span> <span class="pl-c1">=</span> <span class="pl-en">combine</span><span class="pl-kos">(</span><span class="pl-en">getDirectoryName</span><span class="pl-kos">(</span><span class="pl-s1">containingFile</span><span class="pl-kos">)</span><span class="pl-kos">,</span> <span class="pl-s1">moduleName</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

    <span class="pl-c">// convert absolute module name to non-relative</span>
    <span class="pl-c">// try to find element in 'rootDirs' that is the longest prefix for "path' and return path.substr(prefix.length) as non-relative name</span>
    <span class="pl-k">let</span> <span class="pl-kos">{</span> matchingRootDir<span class="pl-kos">,</span> nonRelativeName <span class="pl-kos">}</span> <span class="pl-c1">=</span> <span class="pl-en">tryFindLongestPrefixAndReturnSuffix</span><span class="pl-kos">(</span><span class="pl-s1">rootDirs</span><span class="pl-kos">,</span> <span class="pl-s1">path</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
    <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-c1">!</span><span class="pl-s1">matchingRootDir</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
        <span class="pl-c">// cannot extract non relative name</span>
        <span class="pl-k">return</span> <span class="pl-c1">undefined</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span>
    <span class="pl-c">// first try to load module from initial location</span>
    <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-en">moduleExists</span><span class="pl-kos">(</span><span class="pl-s1">path</span><span class="pl-kos">)</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
        <span class="pl-k">return</span> <span class="pl-s1">path</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span>
    <span class="pl-c">// then try other entries in rootDirs</span>
    <span class="pl-k">for</span> <span class="pl-kos">(</span><span class="pl-k">const</span> <span class="pl-s1">rootDir</span> <span class="pl-k">of</span> <span class="pl-s1">rootDirs</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
        <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-s1">rootDir</span> <span class="pl-c1">===</span> <span class="pl-s1">matchingRootDir</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
            <span class="pl-k">continue</span><span class="pl-kos">;</span>
        <span class="pl-kos">}</span>
        <span class="pl-k">const</span> <span class="pl-s1">candidate</span> <span class="pl-c1">=</span> <span class="pl-en">combine</span><span class="pl-kos">(</span><span class="pl-s1">rootDir</span><span class="pl-kos">,</span> <span class="pl-s1">nonRelativeName</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
        <span class="pl-k">if</span> <span class="pl-kos">(</span><span class="pl-en">moduleExists</span><span class="pl-kos">(</span><span class="pl-s1">candidate</span><span class="pl-kos">)</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
            <span class="pl-k">return</span> <span class="pl-s1">candidate</span><span class="pl-kos">;</span>
        <span class="pl-kos">}</span>
    <span class="pl-kos">}</span>
    <span class="pl-c">// failure case</span>
    <span class="pl-k">return</span> <span class="pl-c1">undefined</span><span class="pl-kos">;</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">Configuration for the example above:</p>
<div class="highlight highlight-source-json notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="{
    &quot;rootDirs&quot;: [
        &quot;userFiles/project/&quot;,
        &quot;/shared/projects/project/&quot;
    ]
}"><pre class="notranslate">{
    <span class="pl-ent">"rootDirs"</span>: [
        <span class="pl-s"><span class="pl-pds">"</span>userFiles/project/<span class="pl-pds">"</span></span>,
        <span class="pl-s"><span class="pl-pds">"</span>/shared/projects/project/<span class="pl-pds">"</span></span>
    ]
}</pre></div>
<h3 dir="auto">Example 1</h3>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="projectRoot
├── folder1
│   └── file1.ts (imports 'folder2/file2')
├── folder2
│   ├── file2.ts (imports './file3')
│   └── file3.ts
└── tsconfig.json
"><pre class="notranslate"><code class="notranslate">projectRoot
├── folder1
│   └── file1.ts (imports 'folder2/file2')
├── folder2
│   ├── file2.ts (imports './file3')
│   └── file3.ts
└── tsconfig.json

</code></pre></div>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// configuration in tsconfig.json
{
    &quot;baseUrl&quot;: &quot;.&quot;
}"><pre class="notranslate"><span class="pl-c">// configuration in tsconfig.json</span>
<span class="pl-kos">{</span>
    <span class="pl-s">"baseUrl"</span>: <span class="pl-s">"."</span>
<span class="pl-kos">}</span></pre></div>
<ul dir="auto">
<li>import 'folder2/file2'
<ol dir="auto">
<li><em>baseUrl</em> is specified in configuration and has value '.' -&gt; <em>baseUrl</em> is computed relative to<br>
the location of tsconfig.json -&gt; <code class="notranslate">projectRoot</code></li>
<li>path mappings are not available -&gt; path = moduleName</li>
<li>resolved module file name = combine(baseUrl, path) -&gt; <code class="notranslate">projectRoot/folder2/file2.ts</code></li>
</ol>
</li>
<li>import './file3'
<ol dir="auto">
<li>moduleName is relative and <em>rootDirs</em> are not specified in configuration - compute module name<br>
relative to the location of containing file: resolved module file name = <code class="notranslate">projectRoot/folder2/file3.ts</code></li>
</ol>
</li>
</ul>
<h3 dir="auto">Example 2</h3>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="projectRoot
├── folder1
│   ├── file1.ts (imports 'folder1/file2' and 'folder2/file3')
│   └── file2.ts
├── generated
│   ├── folder1
│   └── folder2
│       └── file3.ts
└── tsconfig.json
"><pre class="notranslate"><code class="notranslate">projectRoot
├── folder1
│   ├── file1.ts (imports 'folder1/file2' and 'folder2/file3')
│   └── file2.ts
├── generated
│   ├── folder1
│   └── folder2
│       └── file3.ts
└── tsconfig.json

</code></pre></div>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// configuration in tsconfig.json
{
    &quot;baseUrl&quot;: &quot;.&quot;,
    &quot;paths&quot;: {
    &quot;*&quot;: [
            &quot;*&quot;,
            &quot;generated/*&quot; 
        ]
    }
}"><pre class="notranslate"><span class="pl-c">// configuration in tsconfig.json</span>
<span class="pl-kos">{</span>
    <span class="pl-s">"baseUrl"</span>: <span class="pl-s">"."</span><span class="pl-kos">,</span>
    <span class="pl-s">"paths"</span>: <span class="pl-kos">{</span>
    <span class="pl-s">"*"</span>: <span class="pl-kos">[</span>
            <span class="pl-s">"*"</span><span class="pl-kos">,</span>
            <span class="pl-s">"generated/*"</span> 
        <span class="pl-kos">]</span>
    <span class="pl-kos">}</span>
<span class="pl-kos">}</span></pre></div>
<ul dir="auto">
<li>import 'folder1/file2'
<ol dir="auto">
<li><em>baseUrl</em> is specified in configuration and has value '.' -&gt; <em>baseUrl</em> is computed relative to<br>
the location of tsconfig.json -&gt; <code class="notranslate">projectRoot</code></li>
<li>configuration contains path mappings.</li>
<li>pattern '*' is matched and wildcard captures the whole module name</li>
<li>try first substitution in the list: '*' -&gt; <code class="notranslate">folder1/file2</code></li>
<li>result of substitution is relative name - combine it with <em>baseUrl</em> -&gt; <code class="notranslate">projectRoot/folder1/file2.ts</code>.<br>
This file exists.</li>
</ol>
</li>
<li>import 'folder2/file2'
<ol dir="auto">
<li><em>baseUrl</em> is specified in configuration and has value '.' -&gt; <em>baseUrl</em> is computed relative to<br>
the location of tsconfig.json and will be folder that contains tsconfig.json</li>
<li>configuration contains path mappings.</li>
<li>pattern '*' is matched and wildcard captures the whole module name</li>
<li>try first substitution in the list: '*' -&gt; <code class="notranslate">folder2/file3</code></li>
<li>result of substitution is relative name - combine it with <em>baseUrl</em> -&gt; <code class="notranslate">projectRoot/folder2/file3.ts</code>.<br>
File does not exists, move to the second substitution</li>
<li>second substitution 'generated/*' -&gt; <code class="notranslate">generated/folder2/file3</code></li>
<li>result of substitution is relative name - combine it with <em>baseUrl</em> -&gt; <code class="notranslate">projectRoot/generated/folder2/file3.ts</code>.<br>
File exists</li>
</ol>
</li>
</ul>
<h2 dir="auto">Example 3</h2>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="rootDir
├── folder1
│   └── file1.ts (imports './file2')
├── generated
│   ├── folder1
│   │   ├── file2.ts
│   │   └── file3.ts (imports '../folder1/file1')
│   └── folder2
└── tsconfig.json"><pre class="notranslate"><code class="notranslate">rootDir
├── folder1
│   └── file1.ts (imports './file2')
├── generated
│   ├── folder1
│   │   ├── file2.ts
│   │   └── file3.ts (imports '../folder1/file1')
│   └── folder2
└── tsconfig.json
</code></pre></div>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="// configuration in tsconfig.json
{
    &quot;rootDirs&quot;: [
        &quot;./&quot;,
        &quot;./generated/&quot; 
    ],
}"><pre class="notranslate"><span class="pl-c">// configuration in tsconfig.json</span>
<span class="pl-kos">{</span>
    <span class="pl-s">"rootDirs"</span>: <span class="pl-kos">[</span>
        <span class="pl-s">"./"</span><span class="pl-kos">,</span>
        <span class="pl-s">"./generated/"</span> 
    <span class="pl-kos">]</span><span class="pl-kos">,</span>
<span class="pl-kos">}</span></pre></div>
<p dir="auto">All non-rooted entries in <code class="notranslate">rootDirs</code> are expanded using location of tsconfig.json as base location so after expansion <code class="notranslate">rootDirs</code> will<br>
look like this:</p>
<div class="highlight highlight-source-json notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="    &quot;rootDirs&quot;: [
        &quot;rootDir/&quot;,
        &quot;rootDir/generated/&quot; 
    ],"><pre class="notranslate">    <span class="pl-ent">"rootDirs"</span>: [
        <span class="pl-s"><span class="pl-pds">"</span>rootDir/<span class="pl-pds">"</span></span>,
        <span class="pl-s"><span class="pl-pds">"</span>rootDir/generated/<span class="pl-pds">"</span></span> 
    ],</pre></div>
<ul dir="auto">
<li>import './file2'
<ol dir="auto">
<li>name is relative, first make it absolute using location of containing file as base location - <code class="notranslate">rootDir/folder1/file2</code></li>
<li>for this string find the longest prefix in <code class="notranslate">rootDirs</code> - <code class="notranslate">rootDir/</code> and for this prefix compute as suffix - <code class="notranslate">folder1/file2</code></li>
<li>since matching entry in <code class="notranslate">rootDirs</code> was found try to resolve module using <code class="notranslate">rootDir</code> - first check if <code class="notranslate">rootDir/folder1/file2</code><br>
can be resolved as module - such module does not exist</li>
<li>try remaining entries in <code class="notranslate">rootDirs</code> - check if module <code class="notranslate">rootDir/generated/folder1/file2</code> exists - yes.</li>
</ol>
</li>
<li>import '../folder1/file1'
<ol dir="auto">
<li>name is relative, first make it absolute using location of containing file as base location - <code class="notranslate">rootDir/generated/folder1/file1</code></li>
<li>for this string find the longest prefix in <code class="notranslate">rootDirs</code> - <code class="notranslate">rootDir/generated</code> and for this prefix compute as suffix - <code class="notranslate">folder1/file1</code></li>
<li>since matching entry in <code class="notranslate">rootDirs</code> was found try to resolve module using <code class="notranslate">rootDir</code> - first check if <code class="notranslate">rootDir/generated/folder1/file1</code><br>
can be resolved as module - such module does not exist</li>
<li>try remaining entries in <code class="notranslate">rootDirs</code> - check if module <code class="notranslate">rootDir/folder1/file1</code> exists - yes.</li>
</ol>
</li>
</ul>