Skip to main content

2 posts tagged with "extjs"

View All Tags

· 3 min read

Fix: Missing favicon.ico in Sencha ExtJS production builds

Fixing missing favicon.ico in Ext JS applications.

The original issue is tracked here: https://github.com/conjoon/conjoon/issues/24

The issue

I’m not quite sure when and why it broke, but it looks like production builds of Sencha Ext JS applications do not contain any favicon.ico originally existing in the development build (anymore).

While everything seems to be okay with development builds (that’s easy, they refer to the development’s root folder in most cases and do not copy and move files around like the production build does), deploying to production will show the default icon coming with your vendor’s browser for any Sencha ExtJS application, at least when your environment is using the following package versions:

    webpack v4.39.3n/a
ext-webpack-plugin v7.6.0
Ext JS v7.6.0.41
Sencha Cmd v7.6.0.87
``

A call to

```bash
$ cross-env webpack
--env.profile=desktop
--env.environment=production
--env.treeshake=yes
--env.cmdopts=--uses

will produce the production build, but the production build will not contain the favicon.ico. Here’s proof (… sort of):

The fix

I did not invest too much investigating the reason why this file is omitted. Instead, I added the copy-webpack-plugin to the project and made sure the the favicon is copied over when running npm run build.

If you’re reading this post, you most likely stumbled across the same issue. Here’s how to fix it.

  1. Add copy-webpack-plugin to your project

I’m still sporting webpack v4.39.3 so I had to fall back to an older version of the plugin. I’m using copy-webpack-plugin@6.4.1 in this case:

    $ npm i -D copy-webpack-plugin@6.4.1
  1. Add a few more modules to your project’s webpack.config.js

Add the following two lines to the head of the file:

    // ....
const CopyWebpackPlugin = require("copy-webpack-plugin");
const fs = require("fs");
//...

Why fs? I couldn’t find an easy way to access the application’s name at this point of the build step through the variables available, so I’m using fs to parse the project's app.json. The value of its name-property is then used for computing the destination folder for the copy-operation.

  1. Add the copy-webpack-plugin to the list of plugins in the script

You’ll easily find the location that has to be edited when looking for the @sencha/ext-webpack-plugin-configuration:

    const plugins = [
new ExtWebpackPlugin({
// ...
}),
new CopyWebpackPlugin({
patterns: [{
from: path.resolve(__dirname, './favicon.ico'),
to: path.join(
__dirname,
"build",
environment,
JSON.parse(
fs.readFileSync(
path.resolve(__dirname, './app.json')
)
).name
)
}]
})
]

Subsequent builds will now contain the favicon.ico.

· 3 min read

Fix: Ext JS Simlets and omitting the status code

I recently stumbled upon a mean error that was hard to track down. Luckily, the issue was caused by a simple carelessness regarding fully configured response-objects of Simlets used in the dev-environment of the conjoon-project and did not require any large refactoring.

The original issue is tracked here: https://github.com/conjoon/extjs-app-webmail/issues/196

The issue

The problem was related to batch-operations in Ext JS: When any operation of the batch fails, the pauseonexception-setting is considered and further processing of any remaining operation is halted. The user then has the option to retry the last failed operation, continuing with the remaining operations if this was finally successful. However, retrying some of the failed operations gave the following error:

The operation that failed — and which is now about to be retried — was already destroyed by Ext JS internal garbage-collector.

The fix

It took some time to find out that the operation was destroyed and that some of the properties required for re-running it were already de-referenced. This took me to the internals of response-handling of XMLHttpRequests of Ext JS and finally to the code where the HTTP status of a request was inspected. Take note that I’m mainly developing with Simlets to avoid costly network roundtrips to any backend.

It is mandatory for the Simlets to properly treat mocked backend-errors by also providing the appropriate status-code.

It was assumed that simply setting the success-property in any of the delete/put/...-methods of a simlet treating the request would later on be parsed internally; setting the status-code of the response automatically to anything other than 200. That is not the case. It is mandatory for the Simlets to properly treat mocked backend-errors by also providing the appropriate status-code.

The flaw was too often overseen since the status-field was not set, as the following code examples show (examples represent code in any of the delete/post/put/...-methods of a Json-Simlet; see Ext.ux.ajax.Simlet):

The following leaves the ret.status-property undefined:

    //....
const ret = {};
ret.responseText = Ext.JSON.encode({
success: false
});
return ret;

… while the following re-uses the predefined value of 200 for the status, even if success=false should indicate that the request could not be processed as expected:

    const 
me = this,
ret = {};

Ext.Array.forEach(me.responseProps, function (prop) {
if (prop in me) {
ret[prop] = me[prop];
}
});

ret.responseText = Ext.JSON.encode({
success: false
});

return ret;

In both cases, Ext.data.request.Ajax would parse the response and its status in the onComplete-method:

    result = Ext.data.request.Ajax.parseStatus(xhr.status, xhr);

An undefined status would cause the response to be treated as not successful (example 1), while anything other than undefined would be parsed and interpreted according to its HTTP-status representative (successful: 200; failure: 404, 500 etc.).

ExtJS would assume that a successful operation is not needed anymore; it is automatically destroyed then. This was unexpected behavior in this case, since the code marked the operation as a failure, but not via the status-code, which was required.