Prevent browser cache issue on Javascript files with RequireJS in SeedStack - javascript

using SeedStack 14.7 we are facing a cache issue when uploading a new version on servers: every user have to clear their cache to get the last version of files.
I tried to use "urlArgs": "version=2" in the requireConfig part of the fragment JSON file. It do the job by adding argument on every files and so we can use it when changing version, but it also affect the urls in the config of each modules !
As we are using this config to pass the REST base url to each module, it breaks all REST requests by adding the argument to the base url.
My fragment JSON file :
{
"id": "mac2-portail",
"modules": {
"gestionImage": {
"path": "{mac2-portail}/modules/gestionImage",
"autoload": true,
"config": {
"apiUrl": "muserver/rest"
}
}
},
"i18n": {...},
"routes": {...},
"requireConfig": {
"urlArgs": "version=2",
"shim": {...}
}
}
Any idea to solve the cache issue without breaking REST requests ?
EDIT : it is not a duplicate of Prevent RequireJS from Caching Required Scripts. Yes SeedStack uses RequireJS and this configuration solve the cache issue, but it also affect other modules defined in the fragment so I need to find another solution to prevent browser to cache files

The module configuration values, like apiUrl in your example, are not touched by RequireJS unless you call require.toUrl() on them explicitly. I think this is what is happening in your case. To avoid this problem, you should always do the concatenation first and only then call require.toUrl() on the full resulting URL.
So, instead of doing:
var fullUrl = require.toUrl(config.apiUrl) + '/my/resource';
Do this:
var fullUrl = require.toUrl(config.apiUrl + '/my/resource');
By the way, instead of setting the version directly in the RequireJS configuration, you can simply add the version of your application to the data-w20-app-version attribute on the <html> element of the master page:
<html data-w20-app data-w20-app-version="2.0.0">
This will provide the same behavior but will work correctly in the case of Angular templates in $templateCache. If your master page is automatically generated by the backend, this is done automatically. Check this page for the details.

Related

Page data from page-data.json for the failed page "/"

I get this error when I run gatsby build. I have not used "document" in my code. Can someone explain what this means?
ERROR
Page data from page-data.json for the failed page "/": {
"componentChunkName": "component---src-pages-index-js", "path": "/",
"result": {
"pageContext": {} }, "staticQueryHashes": [] }
failed Building static HTML for pages - 2.990s
ERROR #95312
"document" is not available during server side rendering.
The reason why this issue appears is because somewhere in your code you are using document global object and, because gatsby develop is rendered by the browser, where there are window and document global objects, it compiles, however, when you run gatsby build, the code is compiled in the Node server, where there's no window or document variables because they are not even defined yet, they are client-side variables parsed in the SSR (Server-Side Rendering).
This is an extreme reduction of what's happening, you can find a more detailed explanation in Debugging HTML Builds docs.
To fix/bypass this issue, you only need to add the following condition where you are using document object.
if(window !== "undefined"){
// your document or window manipulation
}
You can use both window or document in the condition, they are equivalent in terms of bypassing the server-side rendering.
If you are not using document in your project, the issue may still rise if some of your dependencies (third-party) are using it (i.e: canvas, maps, sliders that uses JavaScript calculations, etc). If that's your scenario, the way to bypass it is to ignore webpacks bundling by adding a null loader:
exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
if (stage === "build-html" || stage === "develop-html") {
actions.setWebpackConfig({
module: {
rules: [
{
test: /bad-module/,
use: loaders.null(),
},
],
},
})
}
}
Where /bad-module/ is a regular expression (test) (that's why is between slashes, /). In this case, you need to replace bad-module for the dependency folder name in node_modules.

Load app.js before rest of application

I'm trying to figure out how I can load app.js before allowing the user to get the actual application. What I'm attempting to do is load a user's configuration file before all of my class Ext.defines fire... the reason I want to do this is because the Ext.defines actually depend on values in the user's configuration. So for example, in an Ext.define, I could have the title property set to pull from this global user configuration var. And no, I don't want to have to go through and change all of these properties to use initComponent... that could take quite some time.
Instead, what I'd like to do is load the configuration, and then let the Ext.defines run, but I will need Ext JS and one of my defined classes to be loaded before the rest of the classes. Is this possible? I've been looking into Sencha Cmd settings, but I've been extremely unsuccessful with getting this to work. I was playing with the bootstrap.manifest.exclude: "loadOrder" property, which loads classic.json, and doesn't define my classes, but unfortunately, that also doesn't fully load Ext JS, so Ext.onReady can't be used... nor can I use my model to load the configuration.
I have a very high level example below (here's the Fiddle).
Ext.define('MyConfigurationModel', {
extend: 'Ext.data.Model',
singleton: true,
fields: [{
name: 'testValue',
type: 'string'
}],
proxy: {
type: 'ajax',
url: '/configuration',
reader: {
type: 'json'
}
}
});
// Pretend this would be the class we're requiring in our Main file
Ext.define('MyApp.view.child.ClassThatUsesConfiguration', {
extend: 'Ext.panel.Panel',
alias: 'widget.classThatUsesConfiguration',
/* We get an undefined value here because MyConfigurationModel hasn't
* actually loaded yet, so what I need is to wait until MyConfigurationModel
* has loaded, and then I can include this class, so the define runs and
* adds this to the prototype... and no, I don't want to put this in
* initComponent, as that would mean I would have to update a ton of classes
* just to accomplish this */
title: MyConfigurationModel.get('testValue')
});
Ext.define('MyApp.view.main.MainView', {
extend: 'Ext.Viewport',
alias: 'widget.appMain',
requires: [
'MyApp.view.child.ClassThatUsesConfiguration'
],
items: [{
xtype: 'classThatUsesConfiguration'
}]
});
Ext.define('MyApp.Application', {
extend: 'Ext.app.Application',
mainView: 'MyApp.view.main.MainView',
launch: function() {
console.log('launched');
}
});
/* In app.js... right now, this gets called after classic.json is downloaded and
* after our Ext.defines set up, but I basically want this to run first before
* all of my classes run their Ext.define */
Ext.onReady(function() {
MyConfigurationModel.load({
callback: onLoadConfigurationModel
})
});
function onLoadConfigurationModel(record, operation, successful) {
if (successful) {
Ext.application({
name: 'MyApp',
extend: 'MyApp.Application'
});
}
else {
// redirect to login page
}
}
I call this "splitting the build", because it removes the Ext.container.Viewport class's dependency tree from the Ext.app.Application class. All Ext JS applications have a viewport that is set as the main view. By moving all requires declarations of the core of the application to the viewport class, an application can load the viewport explicitly from the application class, and the production build can be configured to output two separate files, app.js and viewport.js. Then any number of operations can occur before the core of the application is loaded.
// The app.js file defines the application class and loads the viewport
// file.
Ext.define('MyApp.Application', {
extend: 'Ext.app.Application',
requires: [
// Ext JS
'Ext.Loader'
],
appProperty: 'application',
name: 'MyApp',
launch: function() {
// Perform additional operations before loading the viewport
// and its dependencies.
Ext.Ajax.request({
url: 'myapp/config',
method: 'GET',
success: this.myAppRequestSuccessCallback
});
},
myAppRequestSuccessCallback: function(options, success, response) {
// Save response of the request and load the viewport without
// declaring a dependency on it.
Ext.Loader.loadScript('classic/viewport.js');
}
});
-
// The clasic/viewport.js file requires the viewport class which in turn
// requires the rest of the application.
Ext.require('MyApp.container.Viewport', function() {
// The viewport requires all additional classes of the application.
MyApp.application.setMainView('MyApp.container.Viewport');
});
When building in production, the viewport and its dependencies will not be included in app.js, because it is not declared in the requires statement. Add the following to the application's build.xml file to compile the viewport and all of its dependencies into viewport.js. Conveniently, the development and production file structures remain the same.
<target name="-after-js">
<!-- The following is derived from the compile-js target in
.sencha/app/js-impl.xml. Compile the viewport and all of its
dependencies into viewport.js. Include in the framework
dependencies in the framework file. -->
<x-compile refid="${compiler.ref.id}">
<![CDATA[
union
-r
-class=${app.name}.container.Viewport
and
save
viewport
and
intersect
-set=viewport,allframework
and
include
-set=frameworkdeps
and
save
frameworkdeps
and
include
-tag=Ext.cmd.derive
and
concat
-remove-text-references=${build.remove.references}
-optimize-string-references=${build.optimize.string.references}
-remove-requirement-nodes=${build.remove.requirement.nodes}
${build.compression}
-out=${build.framework.file}
${build.concat.options}
and
restore
viewport
and
exclude
-set=frameworkdeps
and
exclude
-set=page
and
exclude
-tag=Ext.cmd.derive,derive
and
concat
-remove-text-references=${build.remove.references}
-optimize-string-references=${build.optimize.string.references}
-remove-requirement-nodes=${build.remove.requirement.nodes}
${build.compression}
-out=${build.out.base.path}/${build.id}/viewport.js
${build.concat.options}
]]>
</x-compile>
<!-- Concatenate the file that sets the main view. -->
<concat destfile="${build.out.base.path}/${build.id}/viewport.js" append="true">
<fileset file="classic/viewport.js" />
</concat>
</target>
<target name="-before-sass">
<!-- The viewport is not explicitly required by the application,
however, its SCSS dependencies need to be included. Unfortunately,
the property required to filter the output, sass.name.filter, is
declared as local and cannot be overridden. Use the development
configuration instead. -->
<property name="build.include.all.scss" value="true"/>
</target>
This particular implementation saves the framework dependencies in their own file, framework.js. This is configured as part of the output declaration in the app.json file.
"output": {
...
"framework": {
// Split the framework from the application.
"enable": true
}
}
https://docs.sencha.com/extjs/6.2.0/classic/Ext.app.Application.html#cfg-mainView
https://docs.sencha.com/extjs/6.2.0/classic/Ext.container.Viewport.html
https://docs.sencha.com/cmd/guides/advanced_cmd/cmd_build.html#advanced_cmd-_-cmd_build_-_introduction
As far as I know, this is not possible with Sencha Cmd, because while Sencha Cmd can load framework and application separately, it is not possible to tell the production microloader to wait with the second file until the code from the first file has done something (presumably loaded something from the server?).
So the only approach would be to get the options outside ExtJS, before loading ExtJS.
You would have to write your own javascript that loads the configuration into a global variable using a bare, synchronous XmlHttpRequest, and include that into the index.html before the ExtJS script. That way, the script is executed before ExtJS is loaded at all, and you have completely consistent behaviour across development, testing and production builds without modifying any framework file that may be overwritten during framework upgrades.
I guess this is what you are searching for.
So how I did it: In index.html, I added a custom script that fills some global variables:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<script type="text/javascript">
APIURI = '../api/', // <- also used in ExtJS.
var xhr = new XMLHttpRequest();
xhr.open('GET', APIURI+'GetOptions', false);
xhr.setRequestHeader('Accept','application/json');
xhr.send(null);
try {
var configdata = eval("(" + xhr.responseText + ")");
} catch(e) {
// snip: custom code for the cases where responseText was invalid JSON because of a shitty backend
}
if(configdata.options!=undefined) Settings = configdata.options;
else Settings = {};
if(configdata.translations!=undefined) Translations = configdata.translations;
else Translations = {};
Translations.get=function(str) {
if(typeof Translations[str]=="string") return Translations[str];
return "Translation string "+str+" missing.";
};
</script>
<link rel="icon" type="image/vnd.microsoft.icon" href="../favicon.ico">
<title>Application</title>
<script id="microloader" data-app="1a7a9de2-a3b2-2a57-b5af-df428680b72b" type="text/javascript" src="bootstrap.js"></script>
Then I could use in Ext.define() e.g. title: Translations.get('TEST') or hidden: Settings.HideSomeButton or url: APIURI + 'GetUserData'.
However, this has major drawbacks you should consider before proceeding.
After a short period of time, new feature requests emerged and settings I had considered fixed should change at runtime, and I realized that always reloading the application when a setting changes is not good user experience. A while later, I also found that Chrome has deprecated synchronous XmlHttpRequests, and that this approach delays application startup time.
So, the decision was made that in the long run, the only sane approach is to be able to react to changes of any configuration value at runtime, without a full reload of the application. That way, settings could be applied after loading the application, and the requirement could be dropped to wait for settings load before proceeding with the application.
For this, I had to completely work out everything needed for full localization support, so the user can switch between languages without reload of the application, and also any other setting can change at runtime and is automatically applied to the application.
Short-term, this is quite some work, which didn't really matter to me because I was scheduled to rework the whole application layout, but long-term, this will save quite some time and headache, especially when someone decides we should start polling for changes to the settings from the server, or that we should use an ExtJS form for login instead of good old Basic authentication (which was by then already asked for multiple times, but we couldn't deliver because of said shitty ExtJS app architecture).
We actually do use a Sencha CMD approach. As #Alexander mentioned, we also use a global variable for keeping the application's configuration. This approach also implies that the server returns the actual declaration of the global config variable.
If you dig into the app.json file, and find the js config key, you will see that in the description it says
List of all JavaScript assets in the right execution order.
So, we add the configuration's endpoint before the app.js asset
"js": [
{
"path": "data/config",
"remote": true
},
{
"path": "${framework.dir}/build/ext-all-debug.js"
},
{
"path": "app.js",
"bundle": true
}
]
also specifying remote: true.
// Specify as true if this file is remote and should not be copied
into the build folder
The "data/config" endpoint returns something like:
var CONFIG = {
user: {
id: 1,
name: 'User'
},
app: {
language: 'en'
}
}
And now we can have a reference to the CONFIG variable anywhere in our classes.

How to Alternatively set "*" to allowedModules to enable everything in jsreport script file?

I'm trying to create a report using jsreport STUDIO, but I got error like below
Error occured - Error during rendering report: Unsupported module in scripts: request. To enable require on particular module, you need to update the configuration as {"scripts": { "allowedModules": ["request"] } } ... Alternatively you can also set "" to allowedModules to enable everything
Stak - Error: Unsupported module in scripts: request. To enable require on particular module, you need to update the configuration as {"scripts": { "allowedModules": ["request"] } } ... Alternatively you can also set "" to allowedModules to enable everything
Can anyone tell me where I can find the configuration file to update the allowedModules ?
https://jsreport.net/learn/configuration
jsreport merges configuration from file, environment variables, command line arguments and also directly from the application code. The configuration file needs to be stored at the root of the application with the name prod.config.json. There should be already pre created for you the default one.
There should be dev|prod.config.json in your app root if you followed the common installation. You should edit it and add the required options. If you use jsreport inside your node.js application, you should pass this option through the options object in call require('jsreport')({scripts: { ... } })
I have changed the dev.config.json file. It works fine
"scripts": {
"allowedModules": ["request"],
"timeout": 60000
},

Prevent optimization of text! and json! plugins on requirejs optimization tool

I'm using the following architecture for my multipage requirejs based application.:
https://github.com/requirejs/example-multipage-shim
The repository explains how to optimize the application by running r.js, the command line tool used for this kind of task.
Everything should work fine but my project as some modules that have dependencies that perform HTTP GET request to fetch data from server (wich can be text or json)
This is because some of my jsons and templates used by some pages need to be server-side processed for localization.
Here is a basic example to show what i'm talking about:
define( function( require ){
var appLang = require('json!/pagelang/login'), //(a)
loginTemplate = require('text!/template/login'), //(b)
module = require('app/model/user'), //(c)
....
An HTTP get request is made to my server localhost/pagelang/login
which returns a json
{
"hash": "translated_value",
...
}
The same applied for template/template_namewhere an html with it's UI translated into the user language is returned from the server.
By running r.js it attempts to load those locations for an existant directory directory on the server, which obviously, don't exist.
Tracing dependencies for: app/main/login
Error: Error: Loader plugin did not call the load callback in the build:
json:
json!/pagelang/login: Error: ENOENT, no such file or directory '/pagelang/login'
Module loading did not complete for: app/main/login
So, i would like to prevent the command line tool from optimizing text! and json! modules. Is it possible?
I checked requirejs build settings but i didn't find the solution for my problem. Any help?
https://github.com/jrburke/r.js/blob/master/build/example.build.js
The json plugin uses if (( config.isBuild && (config.inlineJSON === false || name.indexOf(CACHE_BUST_QUERY_PARAM +'=') !== -1)) || (url.indexOf('empty:') === 0)) { when the optimiser runs so you have a couple of options.
Add the build config option inlineJSON: false,
Add !bust to the end of the json require. require('json!/pagelang/login!bust'), or
Add the path to the build config option paths: { "/pagelang/login": "empty:" }
The text plugin uses if (config.isBuild && !config.inlineText) { and if (url.indexOf('empty:') === 0) {
Set the build config option inlineText: false, or
Add the path to the build config option paths: { "/template/login": "empty:" }
=================================================================
Update: if you can't get the option inlineJSON to work, try using inlineText, which seems to cover JSON as well.
Reference: https://github.com/requirejs/r.js/blob/master/build/example.build.js

Minify Scripts/CSS in production mode with node.js

I have a web app that runs in node. All the (client) Javascript/CSS files are not minified at the moment to make it easier to debug.
When I am going into production, I would like to minify these scripts. It would be nice to have something like:
node app.js -production
How do I serve the minified version of my scripts without changing the script tags in my html files? There should be something like: if I am in production, use these 2 minified(combined) scripts, else use all my unminified scripts..
Is this possible? Maybe I am thinking too complicated?
You might be interested in Piler. It's a Node.js module that delivers all the JavaScript (and CSS) files you specify as usual when in debug mode, but concatenated and minified when in production mode.
As a special feature, you can force CSS updates via Socket.io in real-time to appear in your browser (called "CSS Live Updated" in Piler), which is quite awesome :-).
The trick is that inside your template you only have placeholders for the script and link elements, and Piler renders these elements at runtime - as single elements in debug mode, and as a dynamically generated single element in production mode.
This way you can forget about creating concatenated and minified versions of your assets manually or using a build tool, it's just there at runtime, but you always have the separated, full versions when developing and debugging.
you could use 2 separate locations for your static files
Here's some express code:
if (process.env.MODE === "production") {
app.use(express['static'](__dirname + '/min'));
} else {
app.use(express['static'](__dirname + '/normal'));
}
and start node with
MODE=production node app.js
Furthermore, if you don't want to duplicate all your files, you could take advantage of the fact that express static router stops at the first file, and do something like this instead:
if (process.env.MODE === "production") {
app.use(express['static'](__dirname + '/min')); // if minized version exists, serves it
}
app.use(express['static'](__dirname + '/normal')); // fallback to regular files
Using the same name for minimized or not is going to cause problem with browser caching, though.
I want to share my final solution with you guys.
I use JSHTML for Express (enter link description here)
In my main node file I use a special route:
app.get('/**:type(html)', function (req, res, next) {
var renderingUrl = req.url.substring(1, req.url.lastIndexOf("."));
//TODO: Find a better solution
try{
var assetUrl = req.url.substring(req.url.lastIndexOf("/") + 1, req.url.lastIndexOf("."));
var assets = config.getResourceBundle(assetUrl);
assets.production = config.getEnviroment() === "production";
res.locals(assets);
res.render(renderingUrl);
}catch(e){
res.redirect("/");
}
});
As you can see, I get my assets from config.getResourceBundle. This is a simply function:
exports.getResourceBundle = function(identifier){
switch(enviroment){
case "development":
return devConfig.getResourceBundle(identifier);
case "production":
return prodConfig.getResourceBundle(identifier);
default:
return devConfig.getResourceBundle(identifier);
}
}
And finally an example for an asset file collection is here:
exports.getResourceBundle = function (identifier) {
return resourceBundle[identifier];
};
resourceBundle = {
index:{
cssFiles:[
"resources/dev/css/login.css",
"resources/dev/css/logonDlg.css",
"resources/dev/css/footer.css"
],
jsFiles:[
"resources/dev/js/lib/jquery/jquery.183.js",
"resources/dev/js/utilities.js",
"resources/dev/js/lib/crypto.3.1.2.js"
]
},
register:{
cssFiles:[
"resources/dev/css/login.css",
"resources/dev/css/modalDialog.css",
"resources/dev/css/footer.css"
],
jsFiles:[
"resources/dev/js/lib/jquery/jquery.183.js",
"resources/dev/js/utilities.js",
"resources/dev/js/lib/crypto.3.1.2.js",
"resources/dev/js/lib/jquery.simplemodal.js",
"resources/dev/js/xfiles.register.js"
]
}
(...)
I have 2 folders. dev / prod. grunt will copy the minified files into prod/.. and deletes the files from dev/...
And if the NODE_ENV variable is set to production, I will ship the minified versions of my scripts/css.
I think this is the most elegant solution at the moment.
There are build tool plugins for you, may help you gracefully solve this problem:
For Gulp:
https://www.npmjs.org/package/gulp-useref/
For Grunt:
https://github.com/pajtai/grunt-useref
Another Node.js module which could be relevant is connect-cachify.
It doesn't seem to do the actual minification for you, but it does let you serve the minified version in production, or all the original scripts in development, without changing the templates (thanks to cachify_js and cachify_css).
Seems it's not as feature-rich as Piler, but probably a bit simpler, and should meet all the requirements mentioned in the question.

Categories

Resources