<h1 dir="auto">Bug report</h1>
<p dir="auto"><strong>What is the current behavior?</strong></p>
<p dir="auto">When importing a file that exports a simple object, attempting to use that import with optional chaining results in the operator being stripped.</p>
<p dir="auto">A full reproduction can be found on the repo here: <a href="https://github.com/Cherry/webpack-optional-chaining-imports">https://github.com/Cherry/webpack-optional-chaining-imports</a>, but here's a quick example:</p>
<ul dir="auto">
<li>An <code class="notranslate">imported.js</code> and an <code class="notranslate">index.js</code>. <code class="notranslate">imported.js</code> looks like</li>
</ul>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="export const foo = {
	bar: {
		baz: {},
	},
};"><pre class="notranslate"><span class="pl-k">export</span> <span class="pl-k">const</span> <span class="pl-s1">foo</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span>
	<span class="pl-c1">bar</span>: <span class="pl-kos">{</span>
		<span class="pl-c1">baz</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-kos">}</span><span class="pl-kos">;</span></pre></div>
<ul dir="auto">
<li><code class="notranslate">index.js</code> looks like this:</li>
</ul>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import {foo} from './imported.js';

console.log(foo?.bar?.baz?.thing?.unknown);

const bar = {
    foo2: {
        baz2: {}
    }
};

console.log(bar?.foo2?.baz2?.thing?.unknown);"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-kos">{</span><span class="pl-s1">foo</span><span class="pl-kos">}</span> <span class="pl-k">from</span> <span class="pl-s">'./imported.js'</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">foo</span><span class="pl-kos">?.</span><span class="pl-c1">bar</span><span class="pl-kos">?.</span><span class="pl-c1">baz</span><span class="pl-kos">?.</span><span class="pl-c1">thing</span><span class="pl-kos">?.</span><span class="pl-c1">unknown</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

<span class="pl-k">const</span> <span class="pl-s1">bar</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span>
    <span class="pl-c1">foo2</span>: <span class="pl-kos">{</span>
        <span class="pl-c1">baz2</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-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s1">bar</span><span class="pl-kos">?.</span><span class="pl-c1">foo2</span><span class="pl-kos">?.</span><span class="pl-c1">baz2</span><span class="pl-kos">?.</span><span class="pl-c1">thing</span><span class="pl-kos">?.</span><span class="pl-c1">unknown</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">The resulting output is then as follows (removing the webpack bootstrap):</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _imported_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./imported.js */ &quot;./src/imported.js&quot;);


console.log(_imported_js__WEBPACK_IMPORTED_MODULE_0__.foo.bar.baz.thing.unknown);

const bar = {
    foo2: {
        baz2: {}
    }
};

console.log(bar?.foo2?.baz2?.thing?.unknown);"><pre class="notranslate"><span class="pl-s1">__webpack_require__</span><span class="pl-kos">.</span><span class="pl-en">r</span><span class="pl-kos">(</span><span class="pl-s1">__webpack_exports__</span><span class="pl-kos">)</span><span class="pl-kos">;</span>
<span class="pl-c">/* harmony import */</span> <span class="pl-k">var</span> <span class="pl-s1">_imported_js__WEBPACK_IMPORTED_MODULE_0__</span> <span class="pl-c1">=</span> <span class="pl-en">__webpack_require__</span><span class="pl-kos">(</span><span class="pl-c">/*! ./imported.js */</span> <span class="pl-s">"./src/imported.js"</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">_imported_js__WEBPACK_IMPORTED_MODULE_0__</span><span class="pl-kos">.</span><span class="pl-c1">foo</span><span class="pl-kos">.</span><span class="pl-c1">bar</span><span class="pl-kos">.</span><span class="pl-c1">baz</span><span class="pl-kos">.</span><span class="pl-c1">thing</span><span class="pl-kos">.</span><span class="pl-c1">unknown</span><span class="pl-kos">)</span><span class="pl-kos">;</span>

<span class="pl-k">const</span> <span class="pl-s1">bar</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span>
    <span class="pl-c1">foo2</span>: <span class="pl-kos">{</span>
        <span class="pl-c1">baz2</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-smi">console</span><span class="pl-kos">.</span><span class="pl-en">log</span><span class="pl-kos">(</span><span class="pl-s1">bar</span><span class="pl-kos">?.</span><span class="pl-c1">foo2</span><span class="pl-kos">?.</span><span class="pl-c1">baz2</span><span class="pl-kos">?.</span><span class="pl-c1">thing</span><span class="pl-kos">?.</span><span class="pl-c1">unknown</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">As you can see, the optional chaining for the initial import from <code class="notranslate">imported.js</code> is entirely stripped, which causes a <code class="notranslate">TypeError</code> when run. The optional chaining on the inline object remains intact.</p>
<p dir="auto"><strong>What is the expected behavior?</strong></p>
<p dir="auto">The expected behaviour would be for the <code class="notranslate">console.log(_imported_js__WEBPACK_IMPORTED_MODULE_0__.foo.bar.baz.thing.unknown);</code> line to instead read <code class="notranslate">console.log(_imported_js__WEBPACK_IMPORTED_MODULE_0__.foo?.bar?.baz?.thing?.unknown);</code></p>
<p dir="auto"><strong>Other relevant information:</strong><br>
webpack version:  5.44.0<br>
Node.js version: 14.7.3<br>
Operating System: Windows 10 21H1</p>
<h1 dir="auto">Bug report</h1>




<p dir="auto"><strong>What is the current behavior?</strong></p>
<p dir="auto">Webpack incorrectly compiles <code class="notranslate">x?.value</code> to <code class="notranslate">(something).value</code> when <code class="notranslate">x</code> is something that's been imported.</p>
<p dir="auto"><strong>If the current behavior is a bug, please provide the steps to reproduce.</strong></p>
<p dir="auto">webpack.config.js:</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="module.exports = {
    entry: &quot;./index.js&quot;,
    target: &quot;web&quot;,
};"><pre class="notranslate"><span class="pl-smi">module</span><span class="pl-kos">.</span><span class="pl-c1">exports</span> <span class="pl-c1">=</span> <span class="pl-kos">{</span>
    <span class="pl-c1">entry</span>: <span class="pl-s">"./index.js"</span><span class="pl-kos">,</span>
    <span class="pl-c1">target</span>: <span class="pl-s">"web"</span><span class="pl-kos">,</span>
<span class="pl-kos">}</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">index.js:</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import x from './x.js';
console.log(`optional value is: ${x?.value}`);"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-s1">x</span> <span class="pl-k">from</span> <span class="pl-s">'./x.js'</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-s">`optional value is: <span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">x</span><span class="pl-kos">?.</span><span class="pl-c1">value</span><span class="pl-kos">}</span></span>`</span><span class="pl-kos">)</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">x.js:</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="export default undefined;"><pre class="notranslate"><span class="pl-k">export</span> <span class="pl-k">default</span> <span class="pl-c1">undefined</span><span class="pl-kos">;</span></pre></div>
<p dir="auto">Produces dist/main.js:</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="(()=&gt;{&quot;use strict&quot;;console.log(`optional value is: ${(void 0).value}`)})();"><pre class="notranslate"><span class="pl-kos">(</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-c1">=&gt;</span><span class="pl-kos">{</span><span class="pl-s">"use strict"</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-s">`optional value is: <span class="pl-s1"><span class="pl-kos">${</span><span class="pl-kos">(</span><span class="pl-k">void</span> <span class="pl-c1">0</span><span class="pl-kos">)</span><span class="pl-kos">.</span><span class="pl-c1">value</span><span class="pl-kos">}</span></span>`</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-kos">;</span></pre></div>
<p dir="auto">which leads to <code class="notranslate">TypeError: Cannot read property 'value' of undefined</code> at runtime.</p>






<p dir="auto"><strong>What is the expected behavior?</strong></p>
<p dir="auto">The <code class="notranslate">?</code> should be retained or converted to a ternary operator.</p>
<p dir="auto">When <code class="notranslate">import x from './x.js';</code> is removed and replaced with <code class="notranslate">const x = (() =&gt; void console)();</code> (something complicated enough that webpack doesn't completely constant-fold it), the output is:</p>
<div class="highlight highlight-source-js notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="(()=&gt;{const o=void console;console.log(`optional value is: ${o?.value}`)})();"><pre class="notranslate"><span class="pl-kos">(</span><span class="pl-kos">(</span><span class="pl-kos">)</span><span class="pl-c1">=&gt;</span><span class="pl-kos">{</span><span class="pl-k">const</span> <span class="pl-s1">o</span><span class="pl-c1">=</span><span class="pl-k">void</span> <span class="pl-smi">console</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-s">`optional value is: <span class="pl-s1"><span class="pl-kos">${</span><span class="pl-s1">o</span><span class="pl-kos">?.</span><span class="pl-c1">value</span><span class="pl-kos">}</span></span>`</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-kos">;</span></pre></div>
<p dir="auto">which is as expected (the <code class="notranslate">?</code> is retained).</p>


<p dir="auto"><strong>Other relevant information:</strong><br>
webpack version: latest, 5.27.2<br>
Node.js version: v15.9.0<br>
Operating System: macOS 11.2.2</p>