{"action":"created","issue":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/2338","labels_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/2338/labels{/name}","comments_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/2338/comments","events_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/2338/events","html_url":"https://github.com/Microsoft/TypeScript/issues/2338","id":60991541,"number":2338,"title":"External module resolution logic","user":{"login":"vladima","id":8052307,"avatar_url":"https://avatars.githubusercontent.com/u/8052307?v=3","gravatar_id":"","url":"https://api.github.com/users/vladima","html_url":"https://github.com/vladima","followers_url":"https://api.github.com/users/vladima/followers","following_url":"https://api.github.com/users/vladima/following{/other_user}","gists_url":"https://api.github.com/users/vladima/gists{/gist_id}","starred_url":"https://api.github.com/users/vladima/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vladima/subscriptions","organizations_url":"https://api.github.com/users/vladima/orgs","repos_url":"https://api.github.com/users/vladima/repos","events_url":"https://api.github.com/users/vladima/events{/privacy}","received_events_url":"https://api.github.com/users/vladima/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/Microsoft/TypeScript/labels/Committed","name":"Committed","color":"009800"},{"url":"https://api.github.com/repos/Microsoft/TypeScript/labels/Suggestion","name":"Suggestion","color":"006b75"}],"state":"open","locked":false,"assignee":{"login":"vladima","id":8052307,"avatar_url":"https://avatars.githubusercontent.com/u/8052307?v=3","gravatar_id":"","url":"https://api.github.com/users/vladima","html_url":"https://github.com/vladima","followers_url":"https://api.github.com/users/vladima/followers","following_url":"https://api.github.com/users/vladima/following{/other_user}","gists_url":"https://api.github.com/users/vladima/gists{/gist_id}","starred_url":"https://api.github.com/users/vladima/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vladima/subscriptions","organizations_url":"https://api.github.com/users/vladima/orgs","repos_url":"https://api.github.com/users/vladima/repos","events_url":"https://api.github.com/users/vladima/events{/privacy}","received_events_url":"https://api.github.com/users/vladima/received_events","type":"User","site_admin":false},"milestone":null,"comments":91,"created_at":"2015-03-13T05:55:24Z","updated_at":"2015-09-03T18:47:34Z","closed_at":null,"body":"**Problem**\r\n\r\nCurrent 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 [base url](http://requirejs.org/docs/api.html#config-baseUrl) without needing the folder walk. Also current process does not allow user to specify extra locations for module resolution.\r\n\r\n**Proposal**\r\n\r\nInstead 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.\r\n\r\n**Node**\r\n\r\nResolution logic should use the following algorithm (originally taken from [Modules all toghether](https://nodejs.org/api/modules.html#modules_all_together)):\r\n\r\n*require(X) from module at path Y*\r\n\r\n```javascript\r\n\r\nIf exists ambient external module named X {\r\n  return the ambient external module \r\n}\r\nelse if X begins with './' or '../' or it is rooted path {\r\n  try LOAD_AS_FILE(Y + X, loadOnlyDts=false)\r\n  try LOAD_AS_DIRECTORY(Y + X, loadOnlyDts=false)\r\n}\r\nelse {\r\n  LOAD_NODE_MODULES(X, dirname(Y))\r\n}\r\nTHROW \"not found\"\r\n\r\nfunction LOAD_AS_FILE(X, loadOnlyDts) {\r\n  if loadOnlyDts then load X.d.ts \r\n  else { \r\n    if  X.ts is a file, load X.ts\r\n    else if X.tsx is a file, load X.tsx\r\n    else If X.d.ts is a file, load X.d.ts\r\n  }\r\n}\r\n\r\nfunction LOAD_AS_DIRECTORY(X, loadOnlyDts) {\r\n  If X/package.json is a file {\r\n    Parse X/package.json, and look for \"typings\" field.\r\n    if parsed json has field \"typings\": \r\n    let M = X + (json \"typings\" field)\r\n    LOAD_AS_FILE(M, loadOnlyDts).\r\n  }\r\n  LOAD_AS_FILE(X/index, loadOnlyDts)\r\n}\r\n\r\nfunction LOAD_NODE_MODULES(X, START) {\r\n  let DIRS=NODE_MODULES_PATHS(START)\r\n  for each DIR in DIRS {\r\n    LOAD_AS_FILE(DIR/X, loadOnlyDts=true)\r\n    LOAD_AS_DIRECTORY(DIR/X, loadOnlyDts=true)\r\n  }\r\n}\r\n\r\nfunction NODE_MODULES_PATHS(START) {\r\n  let PARTS = path split(START)\r\n  let I = count of PARTS - 1\r\n  let DIRS = []\r\n  while I >= 0 {\r\n    if PARTS[I] = \"node_modules\" CONTINUE\r\n    DIR = path join(PARTS[0 .. I] + \"node_modules\")\r\n    DIRS = DIRS + DIR\r\n    let I = I - 1\r\n  }\r\n  return DIRS\r\n}\r\n```\r\n\r\n**RequireJS/ES6 module loader**\r\n- If module name starts with './' - then name is relative to the file that imports module or calls `require`.\r\n- If module name is a relative path (i.e. 'a/b/c') - it is resolved using the base folder.\r\n\r\nBase folder can be either specified explicitly via command line option or can be inferred:\r\n- if compiler can uses 'tsconfig.json' to determine files and compilation options then location of 'tsconfig.json' is the base folder\r\n- otherwise base folder is common subpath for all explicitly provided files\r\n\r\nPath mappings can be used to customize module resolution process. In 'package.json' these mappings can be represented as JSON object with a following structure:\r\n\r\n```json\r\n  {\r\n    \"*.ts\":\"project/ts/*.ts\",\r\n    \"annotations\": \"/common/core/annotations\"\r\n  }\r\n```\r\nProperty 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 `locate` function in [System.js](https://github.com/ModuleLoader/es6-module-loader/blob/master/src/system.js#L221).\r\n\r\nWith path mappings in mind module resolution can be described as:\r\n```typescript\r\nfor (var path in [relative_path, relative_path + '.ts', relative_path + \"d.ts\"]) {\r\n    var mappedPath = apply_path_mapping(path);\r\n    var candidatePath = isPathRooted(mappedPath) ? mappedPath : combine(baseFolder, mappedPath);\r\n    if (fileExists(candidatePath)) {\r\n        return candidatePath\r\n    }\r\n}\r\nreturn undefined\r\n```\r\nWith 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.  \r\n\r\n```json\r\n{\r\n    \"*.ts\": \"project/scripts/*.ts\",\r\n    \"shared/*\": \"q:/shared/*.ts\"\r\n}\r\n```\r\n\r\nUsing this mapping relative path 'shared/core' will be mapped to absolute path 'q:/shared/core.ts'. \r\n\r\nWe 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. "},"comment":{"url":"https://api.github.com/repos/Microsoft/TypeScript/issues/comments/137539939","html_url":"https://github.com/Microsoft/TypeScript/issues/2338#issuecomment-137539939","issue_url":"https://api.github.com/repos/Microsoft/TypeScript/issues/2338","id":137539939,"user":{"login":"domenic","id":617481,"avatar_url":"https://avatars.githubusercontent.com/u/617481?v=3","gravatar_id":"","url":"https://api.github.com/users/domenic","html_url":"https://github.com/domenic","followers_url":"https://api.github.com/users/domenic/followers","following_url":"https://api.github.com/users/domenic/following{/other_user}","gists_url":"https://api.github.com/users/domenic/gists{/gist_id}","starred_url":"https://api.github.com/users/domenic/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/domenic/subscriptions","organizations_url":"https://api.github.com/users/domenic/orgs","repos_url":"https://api.github.com/users/domenic/repos","events_url":"https://api.github.com/users/domenic/events{/privacy}","received_events_url":"https://api.github.com/users/domenic/received_events","type":"User","site_admin":false},"created_at":"2015-09-03T18:47:34Z","updated_at":"2015-09-03T18:47:34Z","body":"Cool. As long as it is not marketed or enumerated as ES6 module resolution in any way, but instead CommonJS or AMD or SystemJS, we should be fine."}}