<h2 dir="auto">Problem</h2>
<p dir="auto">Current module resolution logic is roughly based on Node module loading logic however not all aspects of Node specific module loading were implemented. Also this approach does not really play well with scenarios like RequireJS\ES6 style module loading where resolution of relative files names is performed deterministically using the <a href="http://requirejs.org/docs/api.html#config-baseUrl" rel="nofollow">base url</a> without needing the folder walk. Also current process does not allow user to specify extra locations for module resolution.</p>
<h2 dir="auto">Proposal</h2>
<p dir="auto">Instead of using one hybrid way to resolve modules, have two implementations, one for out-of-browser workflows (i.e Node) and one for in-browser versions (ES6). These implementations should closely mimic its runtime counterparts to avoid runtime failures when design time module resolution succeeded and vice versa.</p>
<h2 dir="auto">Node Resolution Algorithm</h2>
<p dir="auto">Resolution logic should use the following algorithm (originally taken from <a href="https://nodejs.org/api/modules.html#modules_all_together" rel="nofollow">Modules all toghether</a>):</p>
<p dir="auto"><em>require(X) from module at path Y</em></p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="
If exists ambient external module named X {
  return the ambient external module 
}
else if X begins with './' or '../' or it is rooted path {
  try LOAD_AS_FILE(Y + X, loadOnlyDts=false)
  try LOAD_AS_DIRECTORY(Y + X, loadOnlyDts=false)
}
else {
  LOAD_NODE_MODULES(X, dirname(Y))
}
THROW &quot;not found&quot;

function LOAD_AS_FILE(X, loadOnlyDts) {
  if loadOnlyDts then load X.d.ts 
  else { 
    if  X.ts is a file, load X.ts
    else if X.tsx is a file, load X.tsx
    else If X.d.ts is a file, load X.d.ts
  }
}

function LOAD_AS_DIRECTORY(X, loadOnlyDts) {
  If X/package.json is a file {
    Parse X/package.json, and look for &quot;typings&quot; field.
    if parsed json has field &quot;typings&quot;: 
    let M = X + (json &quot;typings&quot; field)
    LOAD_AS_FILE(M, loadOnlyDts).
  }
  LOAD_AS_FILE(X/index, loadOnlyDts)
}

function LOAD_NODE_MODULES(X, START) {
  let DIRS=NODE_MODULES_PATHS(START)
  for each DIR in DIRS {
    LOAD_AS_FILE(DIR/X, loadOnlyDts=true)
    LOAD_AS_DIRECTORY(DIR/X, loadOnlyDts=true)
  }
}

function NODE_MODULES_PATHS(START) {
  let PARTS = path split(START)
  let I = count of PARTS - 1
  let DIRS = []
  while I &gt;= 0 {
    if PARTS[I] = &quot;node_modules&quot; CONTINUE
    DIR = path join(PARTS[0 .. I] + &quot;node_modules&quot;)
    DIRS = DIRS + DIR
    let I = I - 1
  }
  return DIRS
}"><pre class="notranslate"><span class="pl-v">If</span> <span class="pl-s1">exists</span> <span class="pl-s1">ambient</span> <span class="pl-s1">external</span> <span class="pl-smi">module</span> <span class="pl-s1">named</span> <span class="pl-v">X</span> <span class="pl-kos">{</span>
  <span class="pl-k">return</span> <span class="pl-s1">the</span> <span class="pl-s1">ambient</span> <span class="pl-s1">external</span> <span class="pl-smi">module</span> 
<span class="pl-kos">}</span>
<span class="pl-s1">else</span> <span class="pl-s1">if</span> <span class="pl-v">X</span> <span class="pl-s1">begins</span> <span class="pl-s1">with</span> <span class="pl-s">'./'</span> <span class="pl-s1">or</span> <span class="pl-s">'../'</span> <span class="pl-s1">or</span> <span class="pl-s1">it</span> <span class="pl-s1">is</span> <span class="pl-s1">rooted</span> <span class="pl-s1">path</span> <span class="pl-kos">{</span>
  <span class="pl-k">try</span> <span class="pl-c1">LOAD_AS_FILE</span><span class="pl-kos">(</span><span class="pl-v">Y</span> <span class="pl-c1">+</span> <span class="pl-v">X</span><span class="pl-kos">,</span> <span class="pl-s1">loadOnlyDts</span><span class="pl-c1">=</span><span class="pl-c1">false</span><span class="pl-kos">)</span>
  <span class="pl-k">try</span> <span class="pl-c1">LOAD_AS_DIRECTORY</span><span class="pl-kos">(</span><span class="pl-v">Y</span> <span class="pl-c1">+</span> <span class="pl-v">X</span><span class="pl-kos">,</span> <span class="pl-s1">loadOnlyDts</span><span class="pl-c1">=</span><span class="pl-c1">false</span><span class="pl-kos">)</span>
<span class="pl-kos">}</span>
<span class="pl-s1">else</span><span class="pl-kos"></span> <span class="pl-kos">{</span>
  <span class="pl-c1">LOAD_NODE_MODULES</span><span class="pl-kos">(</span><span class="pl-v">X</span><span class="pl-kos">,</span> <span class="pl-en">dirname</span><span class="pl-kos">(</span><span class="pl-v">Y</span><span class="pl-kos">)</span><span class="pl-kos">)</span>
<span class="pl-kos">}</span>
<span class="pl-c1">THROW</span><span class="pl-kos"></span> <span class="pl-s">"not found"</span>

<span class="pl-k">function</span> <span class="pl-c1">LOAD_AS_FILE</span><span class="pl-kos">(</span><span class="pl-v">X</span><span class="pl-kos">,</span> <span class="pl-s1">loadOnlyDts</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-k">if</span> <span class="pl-s1">loadOnlyDts</span> <span class="pl-s1">then</span> <span class="pl-s1">load</span> <span class="pl-v">X</span><span class="pl-kos">.</span><span class="pl-c1">d</span><span class="pl-kos">.</span><span class="pl-c1">ts</span> 
  <span class="pl-s1">else</span> <span class="pl-kos">{</span> 
    <span class="pl-k">if</span>  <span class="pl-v">X</span><span class="pl-kos">.</span><span class="pl-c1">ts</span> <span class="pl-s1">is</span> <span class="pl-s1">a</span> <span class="pl-s1">file</span><span class="pl-kos">,</span> <span class="pl-s1">load</span> <span class="pl-v">X</span><span class="pl-kos">.</span><span class="pl-c1">ts</span>
    <span class="pl-s1">else</span> <span class="pl-s1">if</span> <span class="pl-v">X</span><span class="pl-kos">.</span><span class="pl-c1">tsx</span> <span class="pl-s1">is</span> <span class="pl-s1">a</span> <span class="pl-s1">file</span><span class="pl-kos">,</span> <span class="pl-s1">load</span> <span class="pl-v">X</span><span class="pl-kos">.</span><span class="pl-c1">tsx</span>
    <span class="pl-s1">else</span> <span class="pl-v">If</span> <span class="pl-v">X</span><span class="pl-kos">.</span><span class="pl-c1">d</span><span class="pl-kos">.</span><span class="pl-c1">ts</span> <span class="pl-s1">is</span> <span class="pl-s1">a</span> <span class="pl-s1">file</span><span class="pl-kos">,</span> <span class="pl-s1">load</span> <span class="pl-v">X</span><span class="pl-kos">.</span><span class="pl-c1">d</span><span class="pl-kos">.</span><span class="pl-c1">ts</span>
  <span class="pl-kos">}</span>
<span class="pl-kos">}</span>

<span class="pl-k">function</span> <span class="pl-c1">LOAD_AS_DIRECTORY</span><span class="pl-kos">(</span><span class="pl-v">X</span><span class="pl-kos">,</span> <span class="pl-s1">loadOnlyDts</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-v">If</span> <span class="pl-v">X</span><span class="pl-c1">/</span><span class="pl-s1">package</span><span class="pl-kos">.</span><span class="pl-c1">json</span> <span class="pl-s1">is</span> <span class="pl-s1">a</span> <span class="pl-s1">file</span> <span class="pl-kos">{</span>
    <span class="pl-v">Parse</span> <span class="pl-v">X</span><span class="pl-c1">/</span><span class="pl-s1">package</span><span class="pl-kos">.</span>json<span class="pl-kos">,</span> <span class="pl-s1">and</span> <span class="pl-s1">look</span> <span class="pl-k">for</span> <span class="pl-s">"typings"</span> <span class="pl-s1">field</span><span class="pl-kos">.</span>
    <span class="pl-k">if</span> <span class="pl-s1">parsed</span> <span class="pl-s1">json</span> <span class="pl-s1">has</span> <span class="pl-s1">field</span> <span class="pl-s">"typings"</span>: 
    <span class="pl-s1">let</span> <span class="pl-v">M</span> <span class="pl-c1">=</span> <span class="pl-v">X</span> <span class="pl-c1">+</span> <span class="pl-kos">(</span><span class="pl-s1">json</span> <span class="pl-s">"typings"</span> <span class="pl-s1">field</span><span class="pl-kos">)</span>
    <span class="pl-c1">LOAD_AS_FILE</span><span class="pl-kos">(</span><span class="pl-v">M</span><span class="pl-kos">,</span> <span class="pl-s1">loadOnlyDts</span><span class="pl-kos">)</span><span class="pl-kos">.</span>
  <span class="pl-kos">}</span>
  <span class="pl-en">LOAD_AS_FILE</span><span class="pl-kos">(</span><span class="pl-v">X</span><span class="pl-c1">/</span><span class="pl-s1">index</span><span class="pl-kos">,</span> <span class="pl-s1">loadOnlyDts</span><span class="pl-kos">)</span>
<span class="pl-kos">}</span>

<span class="pl-k">function</span> <span class="pl-c1">LOAD_NODE_MODULES</span><span class="pl-kos">(</span><span class="pl-v">X</span><span class="pl-kos">,</span> <span class="pl-c1">START</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-k">let</span> <span class="pl-c1">DIRS</span><span class="pl-c1">=</span><span class="pl-c1">NODE_MODULES_PATHS</span><span class="pl-kos">(</span><span class="pl-c1">START</span><span class="pl-kos">)</span>
  <span class="pl-k">for</span> <span class="pl-s1">each</span> <span class="pl-c1">DIR</span> <span class="pl-k">in</span> <span class="pl-c1">DIRS</span><span class="pl-kos"></span> <span class="pl-kos">{</span>
    <span class="pl-c1">LOAD_AS_FILE</span><span class="pl-kos">(</span><span class="pl-c1">DIR</span><span class="pl-c1">/</span><span class="pl-v">X</span><span class="pl-kos">,</span> <span class="pl-s1">loadOnlyDts</span><span class="pl-c1">=</span><span class="pl-c1">true</span><span class="pl-kos">)</span>
    <span class="pl-c1">LOAD_AS_DIRECTORY</span><span class="pl-kos">(</span><span class="pl-c1">DIR</span><span class="pl-c1">/</span><span class="pl-v">X</span><span class="pl-kos">,</span> <span class="pl-s1">loadOnlyDts</span><span class="pl-c1">=</span><span class="pl-c1">true</span><span class="pl-kos">)</span>
  <span class="pl-kos">}</span>
<span class="pl-kos">}</span>

<span class="pl-k">function</span> <span class="pl-c1">NODE_MODULES_PATHS</span><span class="pl-kos">(</span><span class="pl-c1">START</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
  <span class="pl-k">let</span> <span class="pl-c1">PARTS</span> <span class="pl-c1">=</span> <span class="pl-s1">path</span> <span class="pl-en">split</span><span class="pl-kos">(</span><span class="pl-c1">START</span><span class="pl-kos">)</span>
  <span class="pl-k">let</span> <span class="pl-v">I</span> <span class="pl-c1">=</span> <span class="pl-s1">count</span> <span class="pl-s1">of</span> <span class="pl-c1">PARTS</span> <span class="pl-c1">-</span> <span class="pl-c1">1</span>
  <span class="pl-k">let</span> <span class="pl-c1">DIRS</span> <span class="pl-c1">=</span> <span class="pl-kos">[</span><span class="pl-kos">]</span>
  <span class="pl-k">while</span> <span class="pl-v">I</span> <span class="pl-c1">&gt;=</span> <span class="pl-c1">0</span> <span class="pl-kos">{</span>
    <span class="pl-k">if</span> <span class="pl-c1">PARTS</span><span class="pl-kos">[</span><span class="pl-v">I</span><span class="pl-kos">]</span> <span class="pl-c1">=</span> <span class="pl-s">"node_modules"</span><span class="pl-kos"></span> <span class="pl-c1">CONTINUE</span>
    <span class="pl-c1">DIR</span> <span class="pl-c1">=</span> <span class="pl-s1">path</span> <span class="pl-en">join</span><span class="pl-kos">(</span><span class="pl-c1">PARTS</span><span class="pl-kos">[</span><span class="pl-c1">0</span> <span class="pl-kos">.</span><span class="pl-kos">.</span> <span class="pl-c1">I</span><span class="pl-kos">]</span> <span class="pl-c1">+</span> <span class="pl-s">"node_modules"</span><span class="pl-kos">)</span>
    <span class="pl-c1">DIRS</span> <span class="pl-c1">=</span> <span class="pl-c1">DIRS</span> <span class="pl-c1">+</span> <span class="pl-c1">DIR</span>
    <span class="pl-k">let</span> <span class="pl-v">I</span> <span class="pl-c1">=</span> <span class="pl-v">I</span> <span class="pl-c1">-</span> <span class="pl-c1">1</span>
  <span class="pl-kos">}</span>
  <span class="pl-k">return</span> <span class="pl-c1">DIRS</span>
<span class="pl-kos">}</span></pre></div>
<h2 dir="auto">RequireJS/ES6 module loader</h2>
<ul dir="auto">
<li>If module name starts with './' - then name is relative to the file that imports module or calls <code class="notranslate">require</code>.</li>
<li>If module name is a relative path (i.e. 'a/b/c') - it is resolved using the base folder.</li>
</ul>
<p dir="auto">Base folder can be either specified explicitly via command line option or can be inferred:</p>
<ul dir="auto">
<li>if compiler can uses 'tsconfig.json' to determine files and compilation options then location of 'tsconfig.json' is the base folder</li>
<li>otherwise base folder is common subpath for all explicitly provided files</li>
</ul>
<p dir="auto">Path mappings can be used to customize module resolution process. In 'package.json' these mappings can be represented as JSON object with a following structure:</p>
<div class="highlight highlight-source-json notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="  {
    &quot;*.ts&quot;:&quot;project/ts/*.ts&quot;,
    &quot;annotations&quot;: &quot;/common/core/annotations&quot;
  }"><pre class="notranslate">  {
    <span class="pl-ent">"*.ts"</span>:<span class="pl-s"><span class="pl-pds">"</span>project/ts/*.ts<span class="pl-pds">"</span></span>,
    <span class="pl-ent">"annotations"</span>: <span class="pl-s"><span class="pl-pds">"</span>/common/core/annotations<span class="pl-pds">"</span></span>
  }</pre></div>
<p dir="auto">Property name represents a pattern that might contain zero or one asterisk (which acts as a capture group). Property value represents a substitution that might contain zero or one asterisk - here it marks the location where captured content will be spliced. For example mapping above for a path 'assert.ts' will produce a string 'project/ts/assert.ts'. Effectively this logic is the same with the implementation of <code class="notranslate">locate</code> function in <a href="https://github.com/ModuleLoader/es6-module-loader/blob/master/src/system.js#L221">System.js</a>.</p>
<p dir="auto">With path mappings in mind module resolution can be described as:</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="for (var path in [relative_path, relative_path + '.ts', relative_path + &quot;d.ts&quot;]) {
    var mappedPath = apply_path_mapping(path);
    var candidatePath = isPathRooted(mappedPath) ? mappedPath : combine(baseFolder, mappedPath);
    if (fileExists(candidatePath)) {
        return candidatePath
    }
}
return undefined"><pre class="notranslate"><span class="pl-k">for</span> <span class="pl-kos">(</span><span class="pl-k">var</span> <span class="pl-s1">path</span> <span class="pl-k">in</span> <span class="pl-kos">[</span><span class="pl-s1">relative_path</span><span class="pl-kos">,</span> <span class="pl-s1">relative_path</span> <span class="pl-c1">+</span> <span class="pl-s">'.ts'</span><span class="pl-kos">,</span> <span class="pl-s1">relative_path</span> <span class="pl-c1">+</span> <span class="pl-s">"d.ts"</span><span class="pl-kos">]</span><span class="pl-kos">)</span> <span class="pl-kos">{</span>
    <span class="pl-k">var</span> <span class="pl-s1">mappedPath</span> <span class="pl-c1">=</span> <span class="pl-en">apply_path_mapping</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">var</span> <span class="pl-s1">candidatePath</span> <span class="pl-c1">=</span> <span class="pl-en">isPathRooted</span><span class="pl-kos">(</span><span class="pl-s1">mappedPath</span><span class="pl-kos">)</span> ? <span class="pl-s1">mappedPath</span> : <span class="pl-en">combine</span><span class="pl-kos">(</span><span class="pl-s1">baseFolder</span><span class="pl-kos">,</span> <span class="pl-s1">mappedPath</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">fileExists</span><span class="pl-kos">(</span><span class="pl-s1">candidatePath</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">candidatePath</span>
    <span class="pl-kos">}</span>
<span class="pl-kos">}</span>
<span class="pl-k">return</span> <span class="pl-c1">undefined</span></pre></div>
<p dir="auto">With path mappings it becomes trivial to resolve some module names to files located on network share or some location on the disk outside the project folder.</p>
<div class="highlight highlight-source-json notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="{
    &quot;*.ts&quot;: &quot;project/scripts/*.ts&quot;,
    &quot;shared/*&quot;: &quot;q:/shared/*.ts&quot;
}"><pre class="notranslate">{
    <span class="pl-ent">"*.ts"</span>: <span class="pl-s"><span class="pl-pds">"</span>project/scripts/*.ts<span class="pl-pds">"</span></span>,
    <span class="pl-ent">"shared/*"</span>: <span class="pl-s"><span class="pl-pds">"</span>q:/shared/*.ts<span class="pl-pds">"</span></span>
}</pre></div>
<p dir="auto">Using this mapping relative path 'shared/core' will be mapped to absolute path 'q:/shared/core.ts'.</p>
<p dir="auto">We can apply the same resolution rules for both modules and tripleslash references though for the latter onces its is not strictly necessary since they do not implact runtime in any way.</p>
<p dir="auto"><strong>TypeScript Version:</strong></p>
<p dir="auto">1.8.7 / nightly (1.9.0-dev.20160217)</p>
<p dir="auto"><strong>Code</strong></p>
<p dir="auto">Consider the following three files:</p>
<p dir="auto">a.ts</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="export class A { }"><pre class="notranslate"><span class="pl-k">export</span> <span class="pl-k">class</span> <span class="pl-smi">A</span> <span class="pl-kos">{</span> <span class="pl-kos">}</span></pre></div>
<p dir="auto">foo.ts</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import {A} from './a';

declare module './a' {
    interface A {
        foo(): number;
    }
}

A.prototype.foo = () =&gt; 42;"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-kos">{</span><span class="pl-smi">A</span><span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'./a'</span><span class="pl-kos">;</span>

<span class="pl-k">declare</span> module <span class="pl-s">'./a'</span> <span class="pl-kos">{</span>
    <span class="pl-k">interface</span> <span class="pl-smi">A</span> <span class="pl-kos">{</span>
        <span class="pl-c1">foo</span><span class="pl-kos">(</span><span class="pl-kos">)</span>: <span class="pl-smi">number</span><span class="pl-kos">;</span>
    <span class="pl-kos">}</span>
<span class="pl-kos">}</span>

<span class="pl-smi">A</span><span class="pl-kos">.</span><span class="pl-c1">prototype</span><span class="pl-kos">.</span><span class="pl-en">foo</span> <span class="pl-c1">=</span> <span class="pl-kos">(</span><span class="pl-kos">)</span> <span class="pl-c1">=&gt;</span> <span class="pl-c1">42</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">main.ts</p>
<div class="highlight highlight-source-ts notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import {A} from './a';
// import './foo';

var a = new A();
console.log(a.foo()); // Shouldn't the second import be necessary in order for this code to compile?"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-kos">{</span><span class="pl-smi">A</span><span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'./a'</span><span class="pl-kos">;</span>
<span class="pl-c">// import './foo';</span>

<span class="pl-k">var</span> <span class="pl-s1">a</span> <span class="pl-c1">=</span> <span class="pl-k">new</span> <span class="pl-smi">A</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s1">a</span><span class="pl-kos">.</span><span class="pl-en">foo</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-kos">)</span><span class="pl-kos">;</span> <span class="pl-c">// Shouldn't the second import be necessary in order for this code to compile?</span></pre></div>
<p dir="auto"><strong>Expected behavior:</strong><br>
The <code class="notranslate">foo</code> member should not be suggested in autocomplete, and main.ts should not compile. That should only happen if <code class="notranslate">foo</code> is imported (i.e. the second import is uncommented).</p>
<p dir="auto"><strong>Actual behavior:</strong><br>
The code compiles, but will fail at runtime with:</p>
<blockquote>
<p dir="auto">TypeError: a.foo is not a function</p>
</blockquote>
<p dir="auto"><strong>Additional info:</strong><br>
I think this was working correctly as of early Feb, so this might be a regression introduced by something else.</p>