Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion packages/core/src/webviews/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -397,8 +397,26 @@ export abstract class VueWebview {
protected setDidLoad(module: string) {
this.loadMetadata?.loadTimeout?.dispose()

/**
* The metadata may already be gone by the time the frontend reports success: the 10s
* loadTimeout clears it (assuming the load failed), and a second page within the same
* webview reporting readiness arrives after the first consumed it. Both are late but
* successful loads. Emitting without a duration is fine; throwing is not -- this runs
* inside the webview's setUiReady command, so a throw here surfaces to the user as
* "Error: Webview error" on the login view (a customer-reported failure in 2.5.0).
*/
if (this.loadMetadata === undefined) {
telemetry.toolkit_didLoadModule.emit({
passive: true,
module,
result: 'Succeeded',
reason: 'LoadReportedAfterMetadataCleared',
})
return
}

// Represents time from intent to open, to confirmation of a successful load
const duration = globals.clock.Date.now() - this.loadMetadata!.start
const duration = globals.clock.Date.now() - this.loadMetadata.start

telemetry.toolkit_didLoadModule.emit({
passive: true,
Expand Down
31 changes: 31 additions & 0 deletions packages/webpack.vue.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
const path = require('path')
const glob = require('glob')
const { VueLoaderPlugin } = require('vue-loader')
const { EsbuildPlugin } = require('esbuild-loader')
const baseConfigFactory = require('./webpack.base.config')
const { merge } = require('webpack-merge')
const currentDir = process.cwd()
Expand Down Expand Up @@ -71,6 +72,36 @@ module.exports = (env, argv) => {
plugins: [new VueLoaderPlugin()],
})

// Keep esbuild as a pure minifier for these bundles, rather than letting it also choose an output
// format.
//
// esbuild-loader v4 infers `format: 'iife'` whenever the compiler target is 'web' (which this
// config sets) and the minifier target is anything other than 'esnext'. The base config's
// minifier targets es2021, so that inference fires here. The resulting IIFE wrapper rewrites
// top-level `this` into an internal exports object, which silently breaks
// `output.libraryTarget: 'this'` above: the bundle's exports are copied onto a dead local object
// instead of onto `window`, so nothing is exposed to the classic <script> that loads it.
//
// The concrete symptom was the Amazon Q chat webview failing with
// "Uncaught ReferenceError: HybridChatAdapter is not defined" and rendering blank, because the
// inline bootstrap script in the chat webview HTML resolves that class off the global scope.
//
// Targeting 'esnext' here suppresses the format inference (esbuild then knows it needs no helper
// wrapping) without changing what the code is compiled down to -- the loader has already
// transpiled to es2021, and these bundles only ever run in the IDE's Chromium webview.
//
// Note this exact upgrade was reverted once before for the same class of breakage; see
// 91a54b0c1 "Revert deps(build): update esbuild-loader to 4.3.0" ("broke loading mynah-ui").
config.optimization = {
...config.optimization,
minimizer: [
new EsbuildPlugin({
target: 'esnext',
legalComments: 'eof',
}),
],
}

if (isDevelopment) {
// add development specific vue config settings
config = {
Expand Down
Loading