mirror of
https://github.com/github/codeql.git
synced 2026-03-02 13:53:42 +01:00
55 lines
2.4 KiB
XML
55 lines
2.4 KiB
XML
<!DOCTYPE qhelp PUBLIC
|
|
"-//Semmle//qhelp//EN"
|
|
"qhelp.dtd">
|
|
<qhelp>
|
|
<overview>
|
|
<p>
|
|
The ECMAScript module system allows modules to cyclically depend on each other.
|
|
However, modules must still be initialized one at a time.
|
|
Normally, a module is initialized after its imported modules, but in the case of
|
|
cyclic imports, this is not possible.
|
|
This means that some modules in a cycle must be initialized before all of its imported modules
|
|
are ready to be used.
|
|
</p>
|
|
<p>
|
|
When the top-level code of a library depends on a value obtained from a cyclic import,
|
|
its behavior thus depends on the initialization order.
|
|
This order is determined by which module is first imported from the main module.
|
|
Simply adding, removing, or reordering <tt>import</tt> statements can therefore cause
|
|
the cyclic modules to stop working.
|
|
</p>
|
|
<p>
|
|
Note that imports that are only used for type annotations in TypeScript files are removed at compile time.
|
|
Such imports can therefore safely be used in a cycle.
|
|
</p>
|
|
</overview>
|
|
<recommendation>
|
|
<p>Cyclic dependencies can be hard to break. There are several approaches that may help:</p>
|
|
<ul>
|
|
<li>Split up the module so that the shared code is separated from the code that depends on the imported value.</li>
|
|
<li>Avoid using the imported value at the top-level, for example, by lazily initializing the variables that depend on it.</li>
|
|
<li>Ensure there are no other importers of the module whose value is used at the top-level. This ensures that the imported module has been initialized.</li>
|
|
<li>Merge two of the modules into a single module.</li>
|
|
</ul>
|
|
</recommendation>
|
|
<example>
|
|
|
|
<p>
|
|
In the example below, <tt>services.js</tt> and <tt>audio.js</tt> both depend on each other.
|
|
As long as <tt>services.js</tt> is imported first, the code works as expected, but if <tt>audio.js</tt> is imported
|
|
first, the list of services will contain <tt>undefined</tt> instead of the <tt>AudioService</tt> class.
|
|
</p>
|
|
<sample src="examples/UnstableCyclicImport.js" />
|
|
|
|
<p>
|
|
One solution is to factor out the <tt>registerService</tt> function into another module which <tt>AudioService</tt> can safely depend on.
|
|
The <tt>services</tt> module can use a re-export to maintain its original interface:
|
|
</p>
|
|
<sample src="examples/UnstableCyclicImportGood.js" />
|
|
|
|
</example>
|
|
<references>
|
|
<li>Mozilla Developer Network: <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import">Import statement</a>.</li>
|
|
</references>
|
|
</qhelp>
|