How to tell webpack to preprocess a yarn package with flow annotations? - reactjs

I am currently having a client side application which uses React, flow types and I package it using webpack 3.11 and everything is working great so far.
Just because the app is starting to get a little too big, I decided to move some React components to their own package by moving them to their own folder, setting up a package.json and then installing them by doing yarn add ./path/to/package --dev and after that webpack started complaining because it could not parse my flow annotations.
Error example
Module parse failed: Unexpected token (15:7)
You may need an appropriate loader to handle this file type.
| import "./BaseTree.css";
|
| export type Tree = {
| name: NodeName,
| parent: ?NodeName,
I've read about compiling my assets and using the stripped down javascript in my app but that's an extra building step I'd rather avoid since I don't plan on using this package anywhere else and my webpack setup already handles flow types just fine on it's own.
.babelrc
{
"presets": [
[
"env",
{
"targets": {
"node": "current",
"browsers": ["last 3 versions"]
}
}
],
"jest",
"react",
"flow"
],
"plugins": ["transform-class-properties"]
}
webpack.config.js (as generated by #symfony/webpack-encore)
{
"context": "/vhosts/app",
"entry": {
"main": "./assets/modules/main.js",
},
"output": {
"path": "/vhosts/app/public/build",
"filename": "[name].js",
"publicPath": "/build/",
"pathinfo": true
},
"module": {
"rules": [
{
"test": {},
"exclude": {},
"use": [
{
"loader": "babel-loader",
"options": {
"cacheDirectory": true
}
}
]
},
{
"test": {},
"use": [
{
"loader": "/vhosts/app/node_modules/extract-text-webpack-plugin/dist/loader.js",
"options": {
"omit": 1,
"remove": true
}
},
{
"loader": "style-loader?sourceMap"
},
{
"loader": "css-loader",
"options": {
"minimize": false,
"sourceMap": true,
"importLoaders": 1
}
},
{
"loader": "postcss-loader",
"options": {
"sourceMap": true
}
}
]
},
{
"test": {},
"loader": "file-loader",
"options": {
"name": "images/[name].[hash:8].[ext]",
"publicPath": "/build/"
}
},
{
"test": {},
"loader": "file-loader",
"options": {
"name": "fonts/[name].[hash:8].[ext]",
"publicPath": "/build/"
}
}
]
},
"plugins": [
{
"filename": "[name].css",
"id": 1,
"options": {
"allChunks": false
}
},
{
"entriesToDelete": []
},
{
"opts": {
"basePath": "build/",
"publicPath": "/build/",
"fileName": "manifest.json",
"stripSrc": null,
"transformExtensions": {},
"writeToFileEmit": true,
"cache": null
}
},
{
"options": {
"debug": true,
"options": {
"context": "/vhosts/app",
"output": {
"path": "/vhosts/app/public/build"
}
},
"test": {}
}
},
{
"options": {}
},
{
"definitions": {
"$": "jquery",
"jQuery": "jquery",
"window.jQuery": "jquery"
}
},
{
"paths": [
"**/*"
],
"options": {
"root": "/vhosts/app/public/build",
"verbose": false,
"allowExternal": false,
"dry": false
}
},
{
"definitions": {
"process.env": {
"NODE_ENV": "\"development\""
}
}
},
{
"compilationSuccessInfo": {
"messages": []
},
"shouldClearConsole": false,
"formatters": [
null,
null,
null,
null,
null,
null
],
"transformers": [
null,
null,
null,
null,
null,
null
]
},
{
"outputPath": "public/build",
"friendlyErrorsPlugin": {
"compilationSuccessInfo": {
"messages": []
},
"shouldClearConsole": false,
"formatters": [
null,
null,
null,
null,
null,
null
],
"transformers": [
null,
null,
null,
null,
null,
null
]
}
}
],
"devtool": "inline-source-map",
"performance": {
"hints": false
},
"stats": {
"hash": false,
"version": false,
"timings": false,
"assets": false,
"chunks": false,
"maxModules": 0,
"modules": false,
"reasons": false,
"children": false,
"source": false,
"errors": false,
"errorDetails": false,
"warnings": false,
"publicPath": false
},
"resolve": {
"extensions": [
".js",
".jsx",
".vue",
".ts",
".tsx"
],
"alias": {}
},
"externals": {}
}
TL;DR version
Have app written using flow types.
Also have own npm package written using flow types to be used just on that app.
Can I set up webpack so that it strips the types for both my app code and the package?

Related

Eslint and Babel parser throwing errors in React

Having some strange behaviour when trying to configure ESlint(global install) and babel:
Editor(VSCode) showing error on top most import:
Resolve error: TypeError: Cannot read properties of undefined (reading 'ENV')
at module.exports (.....\client\webpack.config.js:29:44)
at Object.exports.resolve (.....\client\node_modules\eslint-import-resolver-webpack\index.js:107:21)
while running npm run lint on the whole project throws:
.......\client\webpack.config.js
0:0 error Parsing error: No Babel config file detected for .......\client\webpack.config.js. Either disable config file checking with requireConfigFile: false, or configure Babel so that it can find the config files
for every file it's trying to parse.
eslintrc.json
{
"globals": {
"graphql": true,
"theme": true,
"classes": true
},
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:jsx-a11y/recommended",
"plugin:import/recommended"
],
"parser": "#babel/eslint-parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true,
"globalReturn": false
},
"ecmaVersion": "latest",
"sourceType": "module",
"babelOptions": {
"root": "./client/"
}
},
"plugins": [
"react",
"unused-imports",
"#babel",
"react-hooks",
"jsx-a11y",
"import"
],
"rules": {
"indent": [
"error",
4
],
"linebreak-style": [
"error",
"windows"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"never"
],
"react/jsx-uses-react": "error",
"react/jsx-uses-vars": "error",
"react/jsx-pascal-case": [
2,
{
"allowAllCaps": true
}
],
"jsx-quotes": [
"error",
"prefer-single"
],
"react/jsx-closing-bracket-location": [
1,
"tag-aligned"
],
"react/jsx-closing-tag-location": 1,
"no-multi-spaces": [
"error",
{
"ignoreEOLComments": true
}
],
"react/jsx-tag-spacing": 2,
"react/jsx-boolean-value": [
2,
"never"
],
"react/self-closing-comp": [
"error",
{
"component": true,
"html": true
}
],
"react/prop-types": "off",
"import/no-unresolved": [
"error",
{
"caseSensitive": false
}
],
"no-unused-vars": ["error", { "vars": "local", "args": "after-used" }], // or "#typescript-eslint/no-unused-vars": "off",
"unused-imports/no-unused-imports": "off",
"unused-imports/no-unused-vars": [
"warn",
{
"vars": "all",
"varsIgnorePattern": "^_",
"args": "after-used",
"argsIgnorePattern": "^_"
}
],
"react/no-unknown-property": [
"error",
{
"ignore": [
"css"
]
}
],
"import/order": ["error", {
"newlines-between": "always",
"groups": ["builtin", "external", "internal", ["sibling", "parent"], "index"],
"alphabetize": {
"order": "asc"
}
}],
"import/namespace": "error",
"import/no-duplicates": "error",
"import/no-self-import": "error",
"react-hooks/exhaustive-deps": "error",
"object-curly-spacing": ["error", "always"],
"template-curly-spacing": ["error", "always"],
"react/jsx-curly-spacing": ["error", {"when": "always"}]
},
"settings": {
"react": {
"createClass": "createReactClass", // Regex for Component Factory to use,
// default to "createReactClass"
"pragma": "React", // Pragma to use, default to "React"
"fragment": "Fragment", // Fragment to use (may be a property of <pragma>), default to "Fragment"
"version": "detect", // React version. "detect" automatically picks the version you have installed.
// You can also use `16.0`, `16.3`, etc, if you want to override the detected value.
// default to latest and warns if missing
// It will default to "detect" in the future
"flowVersion": "0.53" // Flow version
},
"propWrapperFunctions": [
// The names of any function used to wrap propTypes, e.g. `forbidExtraProps`. If this isn't set, any propTypes wrapped in a function will be skipped.
"forbidExtraProps",
{
"property": "freeze",
"object": "Object"
},
{
"property": "myFavoriteWrapper"
},
// for rules that check exact prop wrappers
{
"property": "forbidExtraProps",
"exact": true
}
],
"componentWrapperFunctions": [
// The name of any function used to wrap components, e.g. Mobx `observer` function. If this isn't set, components wrapped by these functions will be skipped.
"observer", // `property`
{
"property": "styled"
}, // `object` is optional
{
"property": "observer",
"object": "Mobx"
},
{
"property": "observer",
"object": "<pragma>"
} // sets `object` to whatever value `settings.react.pragma` is set to
],
"formComponents": [
// Components used as alternatives to <form> for forms, eg. <Form endpoint={ url } />
"CustomForm",
{
"name": "Form",
"formAttribute": "endpoint"
}
],
"linkComponents": [
// Components used as alternatives to <a> for linking, eg. <Link to={ url } />
"Hyperlink",
{
"name": "Link",
"linkAttribute": "to"
}
],
"import/resolver": {
"node": {
"paths": [
"client/src",
"src"
],
"extensions": [
".js",
".jsx",
".ts",
".tsx"
]
},
"webpack": {
"config": "webpack.config.js"
}
}
}
}
babel.config.json
{
"presets": [
"#babel/env",
"#babel/react",
"#emotion/babel-preset-css-prop"
],
"plugins": [
"syntax-dynamic-import",
"react-hot-loader/babel",
"#babel/plugin-transform-react-jsx-source"
]
}
webpack.config.js
const path = require('path')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const dotenv = require('dotenv')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpack = require('webpack')
const port = 3000
const host = '0.0.0.0'
module.exports = (env) => {
const currentPath = path.join(__dirname)
const envPath = currentPath + `/${ env.ENV }.env`
const fileEnv = dotenv.config({ path: envPath }).parsed
const envKeys = Object.keys(fileEnv).reduce((prev, next) => {
prev[`process.env.${ next }`] = JSON.stringify(fileEnv[next])
return prev
}, {})
const isProd = (env.ENV === 'prod')
return {
mode: isProd ? 'production': 'development',
entry: [
path.resolve('src', 'index.js')
],
output: {
path: path.resolve(__dirname, 'dist'),
filename: isProd ? '[name].[chunkhash].js' : '[name].[hash:8].js',
chunkFilename: isProd ? '[id].[chunkhash].js' : '[id].[hash:8].js',
sourceMapFilename: isProd ? '[name].[chunkhash].map' : '[name].[hash:8].map',
},
module: {
rules: [
{
test: /\.(jpe?g|png|svg)$/i,
type: 'asset',
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test:/\.css$/,
//include: path.resolve(__dirname, 'node_modules'),
use: ['style-loader', 'css-loader']
},
{
test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
exclude: /node_modules/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'content/fonts/'
}
}]
}
]
},
resolve: {
extensions: ['.js', '.jsx', 'json'],
modules: [path.join(__dirname, 'src'), 'node_modules'],
alias: {
react: path.join(__dirname, 'node_modules', 'react'),
services: path.join(__dirname, 'src/services/'),
utils: path.join(__dirname, 'src/utils/'),
Redux: path.join(__dirname, 'src/redux/'),
scss: path.join(__dirname, 'src/scss/'),
hooks: path.join(__dirname, 'src/hooks/'),
config: path.join(__dirname, 'src/config/'),
components: path.join(__dirname, 'src/components/'),
modules: path.join(__dirname, 'src/modules/'),
locales: path.join(__dirname, 'src/locales/'),
routes: path.join(__dirname, 'src/routes/')
},
preferRelative: true,
roots: [path.resolve(__dirname), 'content']
},
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from:'src/content/images',
to: 'content/images',
globOptions: {
ignore: ['**/index.html']
}
}
],
}),
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
inject: 'body',
//favicon: './content/favicon.ico'
}),
new webpack.DefinePlugin(envKeys)
],
devtool: 'eval-cheap-source-map',
devServer: {
hot: true,
host: host,
port: port,
historyApiFallback: true,
headers: { 'Access-Control-Allow-Origin': '*' },
static: {
directory: path.resolve(__dirname, 'content'),
publicPath: '/content'
}
}
}
}
Edit: I fixed the VSCode complaining with adding this to eslintrc's import-resolver rule:
"webpack": {
"config": "webpack.config.js",
"env":{
"ENV": "dev",
"production": false
}
}

Eslint CLI doesnt show prettier errors but VSCode does

I wanted to use eslint to integrate the airbnb styleguide. Also I want the prettier style rules.
Problem is eslint is giving me no errors when using it from the command line but VSCode does.
VSCode shows in the Problems Tab "eslint(prettier/prettier)" as error.
When i run eslint src/** i get zero errors.
Also pressing save in VSCode Formats my Code using prettier ( Plugin ) but most of the time it just changes back to the old formatting then.
So i want when i execute eslint src/** to apply prettier rules like my VSCode does.
My .eslintrc.json
{
"extends": [
"airbnb",
"airbnb/hooks",
"airbnb-typescript",
"prettier"
],
"plugins": [
"prettier",
"#typescript-eslint"
],
"env": {
"browser": true,
"es6": true,
"jest": true
},
"ignorePatterns": [
"*.css",
"*.png",
"*.json",
"*.svg"
],
"parser": "#typescript-eslint/parser",
"parserOptions": {
"sourceType": "module",
"project": "./tsconfig.json"
},
"rules": {
"react/jsx-filename-extension": [
1,
{
"extensions": [
".tsx"
]
}
],
"import/extensions": [
"error",
"never",
{
"json": "always",
"svg": "always",
"css": "always"
}
],
"max-len": [
"error",
{
"code": 999,
"ignoreStrings": true,
"ignoreComments": true,
"ignoreTemplateLiterals": true
}
],
"func-names": [
"error",
"as-needed",
{
"generators": "never"
}
],
"react/jsx-props-no-spreading": [
"off"
],
"no-param-reassign": [
"error",
{ "props": false }
],
"import/order": [
"error",
{
"groups": [
"index",
"sibling",
"parent",
"internal",
"external",
"builtin",
"object",
"type"
],
"alphabetize": {
"order": "asc",
"caseInsensitive": true
}
}
],
"react/function-component-definition": [
2,
{
"namedComponents": "function-declaration"
}
],
"no-tabs": ["error", { "allowIndentationTabs": false }],
"prettier/prettier": "error"
}
}
.prettierrc
{
"printWidth": 999,
"singleQuote": true,
"useTabs": false
}

webpack5 configuration don't works

this is kind of frustrating issue.
I've been trying to build my own "react-scripts" bundle, so my company no longer relies on it. This is something ordered from the heads, in order to reduce licenses and external dependencies.
I've tried the following webpack configurations:
Mine's (paths are hidden):
{
"mode": "development",
"devtool": "cheap-module-source-map",
"entry": {
"app": "./src/index.js"
},
"output": {
"filename": "[name].js",
"path": ".../static",
"publicPath": "/"
},
"optimization": {
"minimize": false
},
"resolve": {
"symlinks": false,
"extensions": [
".js",
".mjs",
".json"
],
"alias": {
"#": ".../src"
}
},
"module": {
"rules": [
{
"test": {},
"loader": "babel-loader",
"exclude": {},
"include": [
".../src",
".../node_modules/webpack-dev-server/client"
],
"type": "javascript/auto",
"options": {
"babelrc": false,
"presets": [
"#babel/preset-env",
"#babel/preset-react"
],
"plugins": [
[
"#babel/plugin-proposal-class-properties",
{
"loose": false
}
],
[
"#babel/plugin-proposal-private-methods",
{
"loose": false
}
],
"#babel/plugin-proposal-throw-expressions",
"#babel/plugin-transform-modules-commonjs",
"#babel/plugin-transform-runtime"
],
"env": {
"test": {
"plugins": [
"istanbul"
]
}
}
}
},
{
"test": {},
"exclude": {},
"use": [
{
"loader": "style-loader"
},
{
"loader": "css-loader",
"options": {
"sourceMap": true
}
},
{
"loader": "postcss-loader",
"options": {
"plugins": [
null,
null,
null
]
}
},
{
"loader": "less-loader",
"options": {
"sourceMap": true
}
}
]
},
{
"test": {},
"loader": "url-loader",
"options": {
"limit": 10000,
"name": "images/[name].[hash:7].[ext]"
}
},
{
"test": {},
"loader": "url-loader",
"options": {
"limit": 10000,
"name": "media/[name].[hash:7].[ext]"
}
},
{
"test": {},
"loader": "url-loader",
"options": {
"limit": 10000,
"name": "fonts/[name].[hash:7].[ext]"
}
}
]
},
"plugins": [
{
"key": "ESLintWebpackPlugin",
"options": {
"extensions": "js",
"emitError": true,
"emitWarning": true,
"failOnError": true
}
},
{
"definitions": {
"ENVIRONMENT": "\"development\""
}
},
{
"userOptions": {
"filename": "index.html",
"template": "index.html",
"inject": true,
"minify": false
},
"version": 5
},
{
"patterns": [
{
"from": ".../static",
"to": "",
"globOptions": {
"ignore": [
".*"
]
}
}
],
"options": {}
},
{
"options": {}
},
{}
],
"devServer": {
"host": "localhost",
"port": 8080,
"clientLogLevel": "warning",
"hot": true,
"contentBase": false,
"compress": true,
"historyApiFallback": {
"rewrites": [
{
"from": {},
"to": "/index.html"
}
]
},
"open": false,
"overlay": {
"warnings": false,
"errors": true
},
"publicPath": "/",
"proxy": {},
"quiet": true,
"watchOptions": {
"poll": false
}
}
}
Demo setup I've found through the Internet:
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const EslintWebpackPlugin = require('eslint-webpack-plugin');
const eslintConfig = require(path.resolve('...'));
eslintConfig.globals.React = true;
module.exports = {
entry: {
app : './src/index.js'
},
output: {
path: path.join(__dirname, '/dist'),
filename: '[name].js'
},
devServer: {
port: 8080,
watchContentBase: true
},
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules\/(?!(?:...))(\?.*)?$/gi,
include:[
path.resolve('src')
],
use: {
loader: 'babel-loader',
options : {
babelrc: false,
presets : [ '#babel/preset-env', '#babel/preset-react' ]
}
}
},
{
test: /\.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
}
]
},
plugins: [
new EslintWebpackPlugin({
context : path.resolve('src'),
overrideConfig: eslintConfig
}),
new HtmlWebpackPlugin({ template: path.resolve('index.html'), inject : true })
],
}
When I run npx webpack serve with my configuration, seems that content is not injected into index template so I get:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Cannot GET /</pre>
</body>
</html>
When I run the same but using demo configuration, it works as expected.
NOTE: After fixing some issues, bundling works, but running dev server still doesn't.

Solving linter error- 'shallow' is not defined no-undef

I am using JEST and Enzyme. In my eslint file I have added jest as true under env. But I get a lint error for shallow as I have included it globally. Error is- error 'shallow' is not defined no-undef
setupTests.js
//as we are accessing our application with a http://localhost prefix, we need to update our jest configuration
import { shallow, render, mount, configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
// React 16 Enzyme adapter
configure({ adapter: new Adapter() });
// Make Enzyme functions available in all test files without importing
global.shallow = shallow;
global.render = render;
global.mount = mount;
.eslintrc
{
parser: "babel-eslint",
"extends": ["airbnb"],
"env": {
"browser": true,
"jest": true
},
"rules": {
"max-len": [1, 200, 2, {ignoreComments: true}],
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
"no-underscore-dangle": [0, { "allow": [] }],
"jsx-a11y/label-has-associated-control": [
"error", {
"required": {
"every": [ "id" ]
}
}
],
"jsx-a11y/label-has-for": [
"error", {
"required": {
"every": [ "id" ]
}
}
]
}
}
app.test.js
import React from 'react';
import { LoginFormComponent } from '../../components';
describe('LoginForm', () => {
const loginform = shallow(<LoginFormComponent />);
it('renders correctly', () => {
expect(loginform).toMatchSnapshot();
});
});
package.json
"scripts": {
"dev": "webpack-dev-server --historyApiFallback true --port 8888 --content-base build/",
"test": "jest",
"lint": "eslint ./src",
"lintfix": "eslint ./src --fix"
},
"jest": {
"verbose": true,
"testURL": "http://localhost/",
"transform": {
"^.+\\.js$": "babel-jest"
},
"setupFiles": [
"./setupTests.js"
],
"snapshotSerializers": [
"enzyme-to-json/serializer"
]
},
The error comes in my app.test.js where I am trying to use shallow. Do I have to add something in my eslint config for enzyme the way I have made jest as true?
How about add global statement? eslint no-undef docs
/*global someFunction b:true*/
/*eslint no-undef: "error"*/
var a = someFunction();
b = 10;
or set global on .eslintrc (eslint global)
{
"globals": {
"shallow": true,
"render": true,
"mount": true
}
}
Updated .eslintrc
{
parser: "babel-eslint",
"extends": ["airbnb"],
"env": {
"browser": true,
"jest": true
},
"globals": {
"shallow": true
},
"rules": {
"max-len": [1, 200, 2, {ignoreComments: true}],
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
"no-underscore-dangle": [0, { "allow": [] }],
"jsx-a11y/label-has-associated-control": [
"error", {
"required": {
"every": [ "id" ]
}
}
],
"jsx-a11y/label-has-for": [
"error", {
"required": {
"every": [ "id" ]
}
}
]
}
}
Since globals are only used in test files the best practise is not to set them globally but only for the test files. That can be done by using overrides property with proper files glob:
overrides: [
{
files: "*.test.js",
globals: {
shallow: true,
render: true,
mount: true,
},
},
],
Full .eslintrc after addition in the snippet.
{
"parser": "babel-eslint",
"extends": ["airbnb"],
"env": {
"browser": true,
"jest": true
},
"rules": {
"max-len": [1, 200, 2, { "ignoreComments": true }],
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }],
"no-underscore-dangle": [0, { "allow": [] }],
"jsx-a11y/label-has-associated-control": [
"error",
{
"required": {
"every": ["id"]
}
}
],
"jsx-a11y/label-has-for": [
"error",
{
"required": {
"every": ["id"]
}
}
]
},
"overrides": [
{
"files": "*.test.js",
"globals": {
"shallow": true,
"render": true,
"mount": true
}
}
]
}

ExtJS 6 app.js does not updates after new production release

We are using Extjs 6 and we are using sencha cmd to build our application.
We are facing one issue. Every time we release production version of our application like 6.3 to 6.4, bundled app.js does not get updated and browser take that file from (from disk cache). So every time we have to tell our users that please clear your browser's cache after you got new release. That's annoying.
This is my app.json file.
"output": {
"base": "${workspace.build.dir}/${build.environment}/${app.name}",
"page": "index.html",
"manifest": "${build.id}.json",
"js": "${build.id}/.js",
"appCache": {
"enable": false,
"update": "full"
},
"resources": {
"path": "${build.id}/resources",
"shared": "resources"
}
},
"production": {
"output": {
"appCache": {
"enable": false,
"path": "cache.appcache"
}
},
......
"cache": {
"enable": false
}
...
Here are two options to solve your issue:
Customize app.js filename
{
"production": {
"output": {
"js": "${build.id}/app_${build.timestamp}.js"
},
"cache": {
"enable": true
},
"js": [
{
"path": "${build.id}/app.js",
"bundle": true,
"includeInBundle": false
}
],
"output": {
"base": "${workspace.build.dir}/${build.environment}/${app.name}",
"page": "index.html",
"manifest": "${build.id}.json",
"js": "${build.id}/app_${app.version}.js",
"appCache": {
"enable": false,
"update": "full"
},
"resources": {
"path": "${build.id}/resources",
"shared": "resources"
}
}
}
With this, you get every time you build your app an new file name for app.js.
Add static cache parameter
{
"production": {
"loader": {
"cache": "${build.timestamp}"
},
"cache": {
"enable": true
}
}
}
With this solution ExtJs will append a ?_dc=12345678 parameter to the app.js request. This parameter stays the same until your next build.
I have found solution:
"js": [
{
"path": "app.js",
"bundle": true,
"includeInBundle": false
}
],
.....
"output": {
"base": "${workspace.build.dir}/${build.environment}/${app.name}",
"page": "index.html",
"manifest": "${build.id}.json",
"js": "${build.id}/app_${app.version}.js",
"appCache": {
"enable": false,
"update": "full"
},
"resources": {
"path": "${build.id}/resources",
"shared": "resources"
}
},
....
This will not include app.js file in production build and create new app.js file with version appended at last to it like: app_6.4.js.

Resources