Skip to content

[ko] Fix typo - Module Resolution #78

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 15, 2021

Conversation

skyuecx0630
Copy link
Contributor

  • Removed unnecessary html tags.

@github-actions
Copy link
Contributor

github-actions bot commented Jun 7, 2021

Thanks for the PR!

This section of the codebase is owned by @bumkeyy, @yeonjuan, and @guyeol - if they write a comment saying "LGTM" then it will be merged.

@github-actions
Copy link
Contributor

github-actions bot commented Jun 7, 2021

Translation of Module Resolution.md

title: Module Resolution
layout: docs
permalink: /ko/docs/handbook/module-resolution.html
oneline: How TypeScript resolves modules in JavaScript

translatable: true

This section presupposes a basic knowledge of modules.
For more information, see moduleLet's see .

Module analysis is a process used by the compiler to find out what import refers to.
import { a } from "moduleA"Think of the same import statement;
aTo check all the use of, the compiler needs to know exactly what it refers to, and moduleA You need to examine the definition.

At this point, the compiler is "moduleAWhat is the form of?" I'm going to ask you a question.
It seems simple, moduleAis .ts/.tsx defined in the file, or if the code depends on .d.tscan be defined in .

First, the compiler will try to find the location of the file that represents the imported module.
To do so, the compiler follows one of two different strategies: classic or node.
These strategies are moduleATo find _Where is_tells you if you need to see .

If this method is not good and the module name is non-relative , ("moduleA( ) the compiler ambient module declarationwill try to find.
The non-relative import will be addressed next.

Finally, if the compiler is not able to interpret the module, an error log occurs.
In this case, the error error TS2307: Cannot find module 'moduleA'The same.

Relative vs. Non-Relative Module imports

Depending on the module reference relative or non-relative, the module import is interpreted differently.

Relative import is /, ./ or ../. start with one of the
Some examples include:

  • import Entry from "./components/Entry";
  • import { DefaultHeaders } from "../constants/http";
  • import "/mod";

All other imports Non-relative is considered .
Some examples include:

  • import * as $ from "jquery";
  • import { Component } from "@angular/core";

Relative import is interpreted relative to imported files and interpreted as ambient module declarations can't be.
For your own modules, you should use a relative import that ensures that they maintain a relative position at run time.

Non-relative import baseUrlor as a path mapping that will be addressed below.
ambient module declarationcan also be interpreted as .
When importing external dependencies, use non-relative paths.

Module Analysis Strategies

There are two possible module analysis strategies: nodeand classic.
--moduleResolution You can use flags to specify module analysis strategies.
If not specified, the default is --module AMD | System | ES2015In [classic][#클래식-classic]and the rest nodeIs.

Classic (Classic)

Used as the default analysis strategy for TypeScript.
Nowadays, this strategy is primarily provided for backward compatibility.

The relative import is interpreted relative to the file you import.
So the source file /root/src/folder/A.tsIn import { b } from "./moduleB"' looks up as follows:

  1. /root/src/folder/moduleB.ts
  2. /root/src/folder/moduleB.d.ts

However, in a non-relative module import, the compiler starts with the directory that has the imported file and goes back to the directory tree to find the location of the appropriate definition file.

For example:

Source file /root/src/folder/A.tsIn the import { b } from "moduleB"like moduleBthe non-relative import of "moduleB"To find the location of , find the following location:

  1. /root/src/folder/moduleB.ts
  2. /root/src/folder/moduleB.d.ts
  3. /root/src/moduleB.ts
  4. /root/src/moduleB.d.ts
  5. /root/moduleB.ts
  6. /root/moduleB.d.ts
  7. /moduleB.ts
  8. /moduleB.d.ts

Node (Node)

This analysis strategy Node.jsattempts to mimic the module analysis mechanism in .
The entire Node.js analysis algorithm Node.js Module Documentis summarized in .

How .js interprets modules (How Node.js resolves modules)

To understand what the TS compiler will follow, it is .js node and other modules.
Traditionally, node.js import require Call the function to do it.
Node.js behavior is requiredepends on whether the relative or non-relative path is given to .

The relative path is very simple.
For example var x = require("./moduleB");including an import statement called /root/src/moduleA.jsLet's think about the file located in .
Node.js interprets import in the following order:

  1. /root/src/moduleB.jsverify that a file named Exists.

  2. What if "main" Specifying a module package.jsonIf you include a file called, /root/src/moduleB Check the folder.
    In this example, if Node.js { "main": "lib/mainModule.js" }containing /root/src/moduleB/package.jsonWhen you find the file, Node.js /root/src/moduleB/lib/mainModule.jswill refer to .

  3. index.js If you include a file called, /root/src/moduleB Check it out.
    This file implicitly indicates that it is the "main" module of the folder.

For more information, see node.js article File Moduleand Folder moduleYou can read more at .

however Non-relative module nameThe interpretation of performs differently.
Node is node_modulesYou will find the module in a special folder called .
node_modules The folder may be at the same level as the current file, or it may be higher in the directory chain.
Node climbs the directory chain until it can find the module you want to load. node_modulesfind .

Follow the example above, /root/src/moduleA.jsinstead of using a non-relative path. var x = require("moduleB"); Let's think you have an import.
Node is used at each location until one matches moduleBattempts to interpret .

  1. /root/src/node_modules/moduleB.js
  2. /root/src/node_modules/moduleB/package.json ("main" If you specified an item)
  3. /root/src/node_modules/moduleB/index.js


  4. /root/node_modules/moduleB.js
  5. /root/node_modules/moduleB/package.json ("main" If you specified an item)
  6. /root/node_modules/moduleB/index.js


  7. /node_modules/moduleB.js
  8. /node_modules/moduleB/package.json ("main" If you specified an item)
  9. /node_modules/moduleB/index.js

Note .js jumped directories in (4) and (7).

For more information about the process, see the Node.js article node_modulesLoading modules fromYou can read about it in .

How TypeScript interprets modules (How TypeScript resolves modules)

TypeScript mimics Node's runtime analysis strategy to find the location of the module.js at compile-time.
To achieve this, TypeScript has a TypeScript source file extension (.ts, .tsx and .d.ts) on node's analysis logic.
TypeScript is a "main"Purpose of - The compiler uses it to find the "main" definition file to reference. to reflect the "types"Called package.jsonUse fields inside

For example /root/src/moduleA.tsIn the import { b } from "./moduleB" The same import statement "./moduleB"To find the location of , find the following location:

  1. /root/src/moduleB.ts
  2. /root/src/moduleB.tsx
  3. /root/src/moduleB.d.ts
  4. /root/src/moduleB/package.json ("types" If you specified an item)
  5. /root/src/moduleB/index.ts
  6. /root/src/moduleB/index.tsx
  7. /root/src/moduleB/index.d.ts

Node.js moduleB.js After you find the file, package.jsonLooking for, index.jslet's remind you that you found

Similarly, the non-relative import follows .js node, follows the analysis logic, firstly finds the file, and then finds the corresponding folder.
so /root/src/moduleA.ts Inside the source file import { b } from "moduleB"has the following lookups:

  1. /root/src/node_modules/moduleB.ts
  2. /root/src/node_modules/moduleB.tsx
  3. /root/src/node_modules/moduleB.d.ts
  4. /root/src/node_modules/moduleB/package.json ("types" if you have specified a property)
  5. /root/src/node_modules/@types/moduleB.d.ts
  6. /root/src/node_modules/moduleB/index.ts
  7. /root/src/node_modules/moduleB/index.tsx
  8. /root/src/node_modules/moduleB/index.d.ts


  9. /root/node_modules/moduleB.ts
  10. /root/node_modules/moduleB.tsx
  11. /root/node_modules/moduleB.d.ts
  12. /root/node_modules/moduleB/package.json ("types" If you specified an item)
  13. /root/node_modules/@types/moduleB.d.ts
  14. /root/node_modules/moduleB/index.ts
  15. /root/node_modules/moduleB/index.tsx
  16. /root/node_modules/moduleB/index.d.ts


  17. /node_modules/moduleB.ts
  18. /node_modules/moduleB.tsx
  19. /node_modules/moduleB.d.ts
  20. /node_modules/moduleB/package.json ("types" If you specified an item)
  21. /node_modules/@types/moduleB.d.ts
  22. /node_modules/moduleB/index.ts
  23. /node_modules/moduleB/index.tsx
  24. /node_modules/moduleB/index.d.ts

Don't be afraid because you can step-typeScript and still jump twice from the directory (9) and 17.

It'.js more complicated than Node" does.

Additional module analysis flags

In some areas, the project source layout does not match the output.
Typically, you create the final output from which a series of build steps are generated.
.tsThe file .jsand copy dependencies from one source location to one output location.
The end result is that the module at run time may have a different name than the source file that contains the definition.
Alternatively, the module path of the final output may not match the source file path corresponding to compile time.

The TypeScript compiler has additional flags.
The TypeScript compiler has a set of additional flags to inform the compiler of transformations that are expected to happen to the sources to generate the final output.
The TypeScript compiler provides the compiler with conversions that are expected to occur to the source to generate the final output. Let them know there is an additional set of flags for it.

The compiler also does not perform these conversions. Does note that it is;
Use this information to guide you through the process of interpreting module import as a definition file.

Default URL (Base URL)

baseUrlIs a common method for applications that use AMD module loaders, where modules are "deployed" as a single folder at run time.
The source of these modules may be in different directories, but the build scripts will all be made one.

baseUrlTo set up is to tell the compiler where to find the module.
Module imports with all non-relative names baseUrlrelative to .

_baseUrl_The value of is determined by one of the following:

  • baseUrl Command line argument value (if the given path is relative, it is calculated based on the current directory)
  • Inside 'tsconfig.json' baseUrl Property value (if the given path is relative, calculated based on the position of 'tsconfig.json')

Note that the relative module import is always relatively interpreted by the imported file, so it is not affected by setting baseUrl.

More articles on baseUrl RequireJSand SystemJS You can find it in the article.

Path mapping

Sometimes a module baseUrl It may not be located below.
For example "jquery" The module's import "node_modules/jquery/dist/jquery.slim.min.js"translated into .
The loader will use a mapping configuration to map the module name to the file at run time. NeedeJs Articlesand SystemJS documentsLook at .

The TypeScript compiler tsconfig.json Inside the file "paths" Supports declaration of mappings using properties.
jqueryfor "paths" Here's an example of how to specify a property.

{
  "compilerOptions": {
    "baseUrl": ".", // "paths"가 있는 경우 반드시 지정되어야함.
    "paths": {
      "jquery": ["node_modules/jquery/dist/jquery"] // 이 매핑은 "baseUrl"에 상대적임.
    }
  }
}

"paths"price "baseUrl"is interpreted relative to .
"baseUrl"a "."Values other than , yes tsconfig.jsonIf set to Directory, the mapping changes accordingly.
If you set it to "baseUrl": "./src" in the example above, jquery will say ".. /node_modules/jquery/dist/jquery"

"paths"can be used for sophisticated mapping, including multiple previous locations.
Think of a project configuration where only some modules are available in one location, and others are in another.
The build steps will be gathered in one place.
The layout of the project looks like this:

projectRoot
├── folder1
│   ├── file1.ts (imports 'folder1/file2' and 'folder2/file3')
│   └── file2.ts
├── generated
│   ├── folder1
│   └── folder2
│       └── file3.ts
└── tsconfig.json

tsconfig.jsonshould look like this:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "*": [
        "*",
        "generated/*"
      ]
    }
  }
}

This gives the compiler a pattern in two locations. "*" (i.e. all values) and tells you all the module imports that match

  1. "*": The same name means not changed, so <moduleName> => <baseUrl>/<moduleName>Mapped to
  2. "generated/*" The prefix "genericed" means the added module name, so <moduleName> => <baseUrl>/generated/<moduleName>mapped to

By following this logic, the compiler will try to interpret two imports:

import 'folder1/file2':

  1. Module '*' matches and wildcard captures full module name
  2. First alternate attempt in the list: '*' -> folder1/file2
  3. The result of the substitution is a non-relative name - _baseUrl_and combined with -> projectRoot/folder1/file2.ts
  4. The file exists. completion.

import 'folder2/file3':

  1. Module '*' matches and wildcard captures full module name
  2. First alternate attempt in the list: '*' -> folder2/file3
  3. The result of the substitution is a non-relative name - _baseUrl_and combined with -> projectRoot/folder2/file3.ts
  4. File does not exist, move to second substitute
  5. The second substitution is 'genericed/*' -> generated/folder2/file3
  6. The result of the substitution is a non-relative name - _baseUrl_and combined with -> projectRoot/generated/folder2/file3.ts
  7. The file exists. completion.

rootDirs Virtual Directories with rootDirs)

Sometimes, at compile time, project sources from multiple directories are combined to create a single output directory.
Multiple source directories are likely to generate "virtual" directories.

With 'rootDirirs', the compiler can configure this "virtual" directory. _roots_you can inform;
Therefore, the compiler can use relative module import within these "virtual" directories. hammer It can be interpreted as if you merged them together in one directory.

For example, consider the structure of this project:

 src
 └── views
     └── view1.ts (imports './template1')
     └── view2.ts

 generated
 └── templates
         └── views
             └── template1.ts (imports './view2')

src/views The files inside are user code for UI controls.
generated/templated The files inside are ui template binding code that is automatically-generated by the template generator as part of the build.
The build step is /src/viewand /generated/templates/viewsfrom the output to the same directory.
At run time, the view will expect the template to be next to it. "./template"You should use a relative name in import, such as .

To assign this relationship to the compiler, "rootDirs"use .
"rootDirs"content is expected to be merged at run time. roots Specify a list of .
So in the following example, tsconfig.json The file should look like this:

{
  "compilerOptions": {
    "rootDirs": [
      "src/views",
      "generated/templates/views"
    ]
  }
}

Compiler rootDirs Each time you view a relative module import in a subfolder, each rootDirsYou will want to find this import in the entry in .

rootDirsFlexibility is not limited to specifying a list of logically merged physical source directories. The array provided can probably contain any number of ad hoc, arbitrary directory names, regardless of whether they exist or not. This gives the compiler the ability to capture complex bundling and runtime features, such as conditional inclusion and project-specific loader plug-ins, in a secure way.

./#{locale}/messagesas part of a relative module path, such as #{locale}Consider an internationalization scenario where a build tool automatically generates a locale-only burn by interpoling a special path token, such as . In this hypothetical setting, enumerate the locales supported by the tool, and ./zh/messages, ./de/messages map, etc.

Each module assumes that it exports an array of strings. For example ./zh/messagesincludes:

export default [
    "您好吗",
    "很高兴认识你"
];

rootDirsto tell the compiler about this mapping so that even if the directory doesn't exist, it's safe to ./#{locale}/messagesto be able to interpret . For example, the following: tsconfig.jsonSee:

{
  "compilerOptions": {
    "rootDirs": [
      "src/zh",
      "src/de",
      "src/#{locale}"
    ]
  }
}

The compiler now has './#{locale}/messages'를 './zh/messages'design time support in a locale-specific way without compromising.

Tracking module resolve

As discussed earlier, the compiler can visit files outside the current folder when interpreting the module.
This can be difficult when diagnosing why a module is not interpreted or interpreted as a mis definition.
By enabling compiler module analysis tracking using '--traceResolution', you can gain insights into what happened during the module analysis process.

typescript Let's just because there is an example application that uses modules.
app.tsis import * as ts from "typescript" There is the same import.

│   tsconfig.json
├───node_modules
│   └───typescript
│       └───lib
│               typescript.d.ts
└───src
        app.ts

--traceResolutionCall the compiler with

tsc --traceResolution

The following output occurs:

======== Resolving module 'typescript' from 'src/app.ts'. ========
Module resolution kind is not specified, using 'NodeJs'.
Loading module 'typescript' from 'node_modules' folder.
File 'src/node_modules/typescript.ts' does not exist.
File 'src/node_modules/typescript.tsx' does not exist.
File 'src/node_modules/typescript.d.ts' does not exist.
File 'src/node_modules/typescript/package.json' does not exist.
File 'node_modules/typescript.ts' does not exist.
File 'node_modules/typescript.tsx' does not exist.
File 'node_modules/typescript.d.ts' does not exist.
Found 'package.json' at 'node_modules/typescript/package.json'.
'package.json' has 'types' field './lib/typescript.d.ts' that references 'node_modules/typescript/lib/typescript.d.ts'.
File 'node_modules/typescript/lib/typescript.d.ts' exist - use it as a module resolution result.
======== Module name 'typescript' was successfully resolved to 'node_modules/typescript/lib/typescript.d.ts'. ========

Precautions (Things to look out for)

  • The name and location of the import

    ======== 'src/app.ts' In 'typesciprt' Module analysis. ========

  • Compiler-followed strategies

    If no module analysis type is specified, 'NodeJs use.

  • Loading types from npm packages

    'package.json' refers to 'node_modules/typescript/lib/typescript.d.ts' 'types' The field './lib/typescript.d.ts' is found.

  • Final result

    ======== Module name 'typescript' is 'node_modules/typescript/lib/typescript.d.ts' Successfully interpreted Was. ========

--noResolve Using --noResolve)

In general, the compiler wants to interpret all module imports before starting the compilation process.
The file's importEach successful analysis of the file is added to the file set for later processing by the compiler.

--noResolve The compiler option instructs you not to "add" files to the compilation that you have not passed to the command line.
You still want to interpret the module in the file, but if the file is not specified, it is not included.

For example:

app.ts

import * as A from "moduleA" // 성공, 'moduleA'가 명령줄로 전달됨
import * as B from "moduleB" // Error TS2307: Cannot find module 'moduleB.
tsc app.ts moduleA.ts --noResolve

--noResolveusing the app.tsThe compilation of results is as follows:

  • because I passed it to the command line moduleAFind exactly what you're looking for.
  • Because I didn't pass moduleBfailed to find .

Common Questions (Common Questions)

Why is the compiler still selecting modules from the exclusion list? (Why does a module in the exclude list still get picked up by the compiler?)

tsconfig.jsonreplaces the folder with a "project."
"exclude" I "files" If you do not specify an entry, tsconfig.jsonAll files and all sub-directories in the folder containing are included in the compilation.
If you want to save some files, "exclude", and instead of let the compiler find it, you want to specify all the files. "files"Use .

tsconfig.jsonAutomatic inclusion of .
This is not the built-in module analysis discussed above.
If the compiler identifies the file as a module import target, it will be included in the compilation regardless of whether it was excluded from the previous step.

So in order to exclude files from compilation, except those files importI /// <reference path="..."" /> directived all You must exclude the file.

Generated by 🚫 dangerJS against 18d4f24

@yeonjuan
Copy link
Contributor

LGTM

@github-actions github-actions bot merged commit f22e1c8 into microsoft:main Jun 15, 2021
@github-actions
Copy link
Contributor

Merging because @yeonjuan is a code-owner of all the changes - thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants