After Custom transformer, typescript still uses reference to old import - typescript-compiler-api

I'm using a CustomTransformer to update imports from:
import { global_spacer_form_element } from '#patternfly/react-tokens';
export const disabledLabelClassNameEx = global_spacer_form_element.var;
to
import global_spacer_form_element from '#patternfly/react-tokens/dist/js/global_spacer_form_element';
export const disabledLabelClassNameEx = global_spacer_form_element.var;
However, when using with ts-loader I get the following output (directly from ts-loader):
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.disabledLabelClassNameEx = void 0;
const global_spacer_form_element_1 = __importDefault(require("#patternfly/react-tokens/dist/js/global_spacer_form_element"));
exports.disabledLabelClassNameEx = react_tokens_1.global_spacer_form_element.var;
//# sourceMappingURL=Recipient2.js.map
Instead of using global_spacer_form_element directly, it is using react_tokens_1.global_spacer_form_element.
I suppose there is something missing in the transformer that the typescript compiler is using to build that react_tokens_1 variable.
The transformer is doing the following in its visitor (I'm simplifying the transformer code for the sake of showing the path it takes, full code can be see here):
const visitor: ts.Visitor = (node) => {
if (ts.isSourceFile(node)) {
return ts.visitEachChild(node, visitor, context)
}
if (!ts.isImportDeclaration(node) /* or if the lib name is not '#patternfly/react-tokens' */) {
return node
}
// for simplicity assume we take all NamedImports and the only found is...
const elements = ['global_spacer_form_element']
const importPath = '#patternfly/react-tokens/dist/js/global_spacer_form_element'
return elements.map((e) => {
return ts.factory.createImportDeclaration(
undefined,
undefined,
ts.factory.createImportClause(
false,
ts.factory.createIdentifier(e),
undefined,
),
ts.factory.createStringLiteral(importPath),
)
})
}
My tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"allowJs": true,
"checkJs": false,
"jsx": "react",
"outDir": "./build",
"removeComments": true,
"pretty": true,
"skipLibCheck": true,
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true,
"noImplicitAny": false,
"sourceMap": true,
"resolveJsonModule" : true
},
"include": [
"./src/**/*"
],
"exclude": [
"./node_modules/*",
"**/*.js"
]
}
and finally the ts-loader config:
{
test: /src\/.*\.tsx?$/,
loader: 'ts-loader',
exclude: /(node_modules)/i,
options: {
getCustomTransformers: () => ({
before: [
tsImportPluginFactory({
libraryName: '#patternfly/react-tokens',
libraryDirectory: 'dist/js',
camel2DashComponentName: false
})
]
})
}
Any idea of what else I need to update or what I could check to ensure this transformer works as I am expecting?
edit: Reference to old import is gone, but I didn't notice before that the new import also gets transformed: e.g. from foobar to foobar_1.

The TypeScript compiler has four main phases—parsing, binding, type checking, and emitting. Binding is where the relationships between identifiers are resolved, but transformation happens during the "emitting" phase. So by the time you're transforming it's too late and the compiler has already figured out what identifiers it's going to transform.
One way to do what you want to do, is to traverse all the nodes in the file, find the identifiers that match one of the ones in your import, then recreate those identifiers by returning context.factory.createIdentifier(node.escapedText) in the visitor for that node. That will make the compiler leave the node as-is when emitting.
The trouble though may be figuring out which identifiers in a file reference the named import identifier. Generally I don't recommend using the type checker in transforms because it can lead to unexpected results when there are multiple transformations happening on a file, but you might be able to get away with first checking if the identifier's escapeText matches, then checking if typeChecker.getSymbolAtLocation(node)?.declarations[0] equals the named export identifier found in the original import declaration. Alternatively, I think you would have to implement your own scope analysis.

Related

How to connect google analytics to Nuxt3 app?

I have a problem. I try to connect my Nuxt3 app with Google Analytics.
right now I do it by adding to nuxt.config.ts following code
export default defineNuxtConfig({
buildModules: [
'#nuxtjs/google-analytics'
],
googleAnalytics: {
id: process.env.GOOGLE_ANALYTICS_ID
},
})
but unfortunately I get following error when I try to build my app
ERROR Error compiling template: { 17:53:04
ssr: false,
src: 'C:\\Users\\szczu\\Elektryk\\node_modules\\#nuxtjs\\google-analytics\\lib\\plugin.js',
fileName: 'google-analytics.js',
options: {
dev: true,
debug: {
sendHitTask: true
},
id: undefined
},
filename: 'google-analytics.js',
dst: 'C:/Users/szczu/Elektryk/.nuxt/google-analytics.js'
}
ERROR serialize is not defined 17:53:04
at eval (eval at <anonymous> (node_modules\lodash.template\index.js:1550:12), <anonymous>:7:1)
at compileTemplate (/C:/Users/szczu/Elektryk/node_modules/#nuxt/kit/dist/index.mjs:493:45)
at async /C:/Users/szczu/Elektryk/node_modules/nuxt3/dist/chunks/index.mjs:1296:22
at async Promise.all (index 11)
at async generateApp (/C:/Users/szczu/Elektryk/node_modules/nuxt3/dist/chunks/index.mjs:1295:3)
at async _applyPromised (/C:/Users/szczu/Elektryk/node_modules/perfect-debounce/dist/index.mjs:54:10)
Does anyone have an idea how to fix it?
Try the vue-vtag-next package as a plugin
yarn add --dev vue-gtag-next
Create a plugin file plugins/vue-gtag.client.js
import VueGtag from 'vue-gtag-next'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(VueGtag, {
property: {
id: 'GA_MEASUREMENT_ID'
}
})
})
Late reply, but i would like to add for any future viewers.
The above solution only worked for me when the $router was passed. Please find below sample code.
Please also note:
The package being used, 'vue-gtag' instead of 'vue-gtag-next'.
You have to pass config object instead of property for the 'vue-gtag' package
import VueGtag from 'vue-gtag'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(VueGtag, {
config: {
id: 'GA_MEASUREMENT_ID',
},
}, nuxtApp.$router)
})
found this solution https://github.com/nuxt/framework/discussions/5702
.. And also you may use nuxt.config to provide app.head.script with children attribute on the app level:
import { defineNuxtConfig } from "nuxt";
export default defineNuxtConfig({
app: {
head: {
script: [{ children: 'console.log("test3");' }],
},
},
});
import VueGtag from 'vue-gtag-next'
export default defineNuxtPlugin(async (nuxtApp) => {
const { data: { value: {google_id, google_sv, yandex_id, privacy_policy} } } = await useMyApi("/api/main/site-metriks/");
nuxtApp.vueApp.use(VueGtag, {
property: {
id: google_id
}
})
})
For Nuxt 3:
Install vue-gtm: npm i #gtm-support/vue-gtm
Create file in /plugins/vue-gtm.client.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(createGtm({
id: 'GTM-ID',
defer: false, // Script can be set to `defer` to speed up page load at the cost of less accurate results (in case visitor leaves before script is loaded, which is unlikely but possible). Defaults to false, so the script is loaded `async` by default
compatibility: false, // Will add `async` and `defer` to the script tag to not block requests for old browsers that do not support `async`
nonce: '2726c7f26c', // Will add `nonce` to the script tag
enabled: true, // defaults to true. Plugin can be disabled by setting this to false for Ex: enabled: !!GDPR_Cookie (optional)
debug: true, // Whether or not display console logs debugs (optional)
loadScript: true, // Whether or not to load the GTM Script (Helpful if you are including GTM manually, but need the dataLayer functionality in your components) (optional)
vueRouter: useRouter(), // Pass the router instance to automatically sync with router (optional)
//ignoredViews: ['homepage'], // Don't trigger events for specified router names (optional)
trackOnNextTick: false, // Whether or not call trackView in Vue.nextTick
}))
})
Nuxt would automatically pick up this plugin and you're done.

Configure eslint to read common types in src/types/types.d.ts vue3

My eslint .eslintrc.js, now properly in the src folder, is the following:
module.exports = {
env: {
browser: true,
commonjs: true,
es2021: true
},
extends: [
'plugin:vue/vue3-recommended',
'standard',
'prettier'
],
parserOptions: {
ecmaVersion: 2020,
parser: '#typescript-eslint/parser',
'ecmaFeatures': {
'jsx': true
}
},
plugins: [
'vue',
'#typescript-eslint'
],
rules: {
'import/no-unresolved': 'error'
},
settings: {
'import/parsers': {
'#typescript-eslint/parser': ['.ts', '.tsx']
},
'import/resolver': {
'typescript': {
'alwaysTryTypes': true,
}
}
}
}
I'm attempting to use eslint-import-resolver-typescript, but the documentation is a bit opaque.
I currently get errors on lines where a externally defined type is used (StepData in this example):
setup() {
const data = inject("StepData") as StepData;
return {
data,
};
},
The answer was the following. In types.d.ts (or other file if you want to have different collections of your custom types):
export interface MyType {
positioner: DOMRect;
content: DOMRect;
arrow: DOMRect;
window: DOMRect;
}
export interface SomeOtherType {
.. and so on
Then in the .vue files, import the types I need for the component:
import type { MyType, SomeOtherType } from "../types/types";
Before I was not using the export keyword and the types just worked without being imported. They have to be imported like this if you use export. It's kind of amazing how you are just expected to know this, the documentation for Typescript or Vue is sorely lacking in examples.

How to use jest.fn() on an individually imported function

I'm having trouble mocking the call of an individually imported function to my tests. The test is a simple function that I put within my Redux actions to be able to set a variable based on a condition.
Here's the function in Body.duck.js:
export const getCurrentOrPrevSelection = isExecutedFromPagination => (dispatch, getState) => {
const {
editor: { selection },
body: { queryRequest },
} = getState();
if (isExecutedFromPagination && queryRequest.breadcrumb) {
const {
query: { branch, includeSplits, primaryFa, split, isInitial },
} = queryRequest.breadcrumb;
return {
branch,
includeSplits,
primaryFa,
split,
isInitial,
};
}
return selection;
};
And here's the test file:
import reudcer, { ...other exported functions, getCurrentOrPrevSelection } from '../Body.duck';
it ('should use selection in breadcrumb state when fetching new data from pagination action', () => {
let isExecutedFromPagination = false;
const bodyState = {
...initialState.body,
queryRequest: {
...initialState.body.queryRequest,
breadcrumb: {
...initialState.body.breadcrumb,
query: {
name: 'Full Book Performance',
branch: null,
includeSplits: true,
primaryFa: 'AXFO',
split: null,
isInitial: true,
},
},
},
};
const selection = {
branch: null,
includeSplits: true,
primaryFa: 'AXFO',
split: null,
isInitial: true,
};
expect(getCurrentOrPrevSelection(isExecutedFromPagination)(jest.fn(), () => ({
body: { ...bodyState },
editor: { faidSelection },
}))).toHaveReturnedWith({
branch: null,
includeSplits: true,
primaryFa: 'AXFO',
split: null,
isInitial: true,
});
});
If I don't include any sort of mock reference to getCurrentOrPrevSelection, I get this error below, but it returns the correct value as expected:
expect(jest.fn())[.not].toHaveReturnedWith()
jest.fn() value must be a mock function or spy.
Received:
object: {"branch": null, "includeSplits": true, "isInitial": true, "primaryFa": "AXFO", "split": null}
If I do something like getCurrentOrPrevFaidSelection = jest.fn();, I get an error saying getCurrentOrPrevFaidSelection is read-only
What can I do differently here?
You want to test this function. So you don't need to mock that.
Just call function and verify result with expect().toEqual or expect().toMatchObject.
expect(getCurrentOrPrevSelection(isExecutedFromPagination)(.....)).toMatchObject({
branch: null,
...
});
Also passing jest.fn() directly as argument does not really make sense: you cannot either verify it has been called or provide mock return.
const dispatchMock = jest.fn();
expect(getCurrentOrPrevSelection(isExecutedFromPagination)(dispatchMock, ....);
expect(dispatchMock).toHaveBeenCalledWith(...)
Once it's just not expected to be called as it is in your sample you better explicitly provide noop function () => {} instead of jest.fn(). This way you make it's explicit so nobody will be confused if it's expected there is no assertions against this function or not.
Offtop: actually this is not really good way to test redux action creators. See you actually test implementation details. What if you migrate from redux-thunk to redux-saga or redux-loop? Or split single action into 2 for better flexibility? By now it would mean you have to rewrite all your tests.
What if instead of testing action creator in isolation you connect action to real(not mocked) store? You could dispatch action(after mocking calls to external API) and validate store's state.

How to stub or ignore meteor/session in jest env?

Jestjs gives me this error when I am testing a react component:
import {Session} from 'meteor/session' ". The error is "Cannot find module 'meteor/session' "
myTestFile
import PlanSetup from "../../../ui/pages/planSetup/planSetup";
let PlanSetupWrapper;
const PlansetupProps={
name: "string",
budget: "string",
lang: { english: "en",
French : "fr"
}
};
describe('<PlanSetup />', () => {
PlanSetupWrapper = mount(<PlanSetup {...PlansetupProps}/>);
it('All child components renders correctly', () => {
expect(PlanSetupWrapper).toMatchSnapshot();
});
});
**jest.config.js**
module.exports = {
moduleNameMapper: {
"^meteor/(.*)": "<rootDir>/imports/tests/mocks/meteor.js"
},
transform: {
"^.+\\.(js|jsx)?$": "babel-jest",
".+\\.(css|styl|less|sass|scss)$": "/home/megha/Megha/TVStack/dan-tvstack-ui/node_modules/jest-css-modules-transform",
},
moduleFileExtensions: [
'js',
'jsx'
],
modulePaths: [
"<rootDir>/node_modules/"
],
globals: {
"window": true
},
unmockedModulePathPatterns: [
'/^imports\\/.*\\.jsx?$/'
],
setupFiles: [
"<rootDir>/setupTests.js"
]
};
**<rootDir>/imports/tests/mocks/meteor.js**
exports._session = {
__: function(value) { return value }
};
Welcome to Stack Overflow #MeghaRawat. There are a couple of things to consider here.
1) Keeping your components pure
2) Mocking up Meteor services
What this means is that any React components should be as pure as possible, and not reference Meteor - the containers should do that, and pass props to the components.
Jest doesn't know about Meteor, so any Meteor features will need to be stubbed, to prevent the the kind of problems you are encountering.
I may be able to find some code to help you if you need it.

CSS not loaded in production with React and Webpack

So while everything is fine in development, when I deploy to production, I have a lot of missing css. It seems that it is mostly where css was an import in a React component. I've done a lot of searching here and still at a loss. Is there a problem in the config? I have no errors on network tab in Chrome developer tools when looking at the app.
Here is my webpack.config.prod.js
// #remove-on-eject-begin
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
// #remove-on-eject-end
'use strict';
const autoprefixer = require('autoprefixer');
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const paths = require('./paths');
const getClientEnvironment = require('./env');
// Webpack uses `publicPath` to determine where the app is being served from.
// It requires a trailing slash, or the file assets will get an incorrect path.
const publicPath = paths.servedPath;
// Some apps do not use client-side routing with pushState.
// For these, "homepage" can be set to "." to enable relative asset paths.
const shouldUseRelativeAssetPaths = publicPath === './';
// `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
const publicUrl = publicPath.slice(0, -1);
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);
console.log('env: ', env);
console.log('publicPath: ', publicPath);
console.log('publirUrl: ', publicUrl);
// Assert this just to be safe.
// Development builds of React are slow and not intended for production.
if (env.stringified['process.env'].NODE_ENV !== '"production"') {
throw new Error('Production builds must have NODE_ENV=production.');
}
// Note: defined here because it will be used more than once.
const cssFilename = 'static/css/[name].[contenthash:8].css';
// ExtractTextPlugin expects the build output to be flat.
// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27)
// However, our output is structured with css, js and media folders.
// To have this structure working with relative paths, we have to use custom options.
const extractTextPluginOptions = shouldUseRelativeAssetPaths
? // Making sure that the publicPath goes back to to build folder.
{ publicPath: Array(cssFilename.split('/').length).join('../') }
: {};
// This is the production configuration.
// It compiles slowly and is focused on producing a fast and minimal bundle.
// The development configuration is different and lives in a separate file.
module.exports = {
// Don't attempt to continue if there are any errors.
bail: true,
// We generate sourcemaps in production. This is slow but gives good results.
// You can exclude the *.map files from the build during deployment.
devtool: 'source-map',
// In production, we only want to load the polyfills and the app code.
entry: [require.resolve('./polyfills'), paths.appIndexJs],
output: {
// The build folder.
path: paths.appBuild,
// Generated JS file names (with nested folders).
// There will be one main bundle, and one file per asynchronous chunk.
// We don't currently advertise code splitting but Webpack supports it.
filename: 'static/js/[name].[chunkhash:8].js',
chunkFilename: 'static/js/[name].[chunkhash:8].chunk.js',
// We inferred the "public path" (such as / or /my-project) from homepage.
publicPath: publicPath,
// Point sourcemap entries to original disk location
devtoolModuleFilenameTemplate: info =>
path.relative(paths.appSrc, info.absoluteResourcePath),
},
resolve: {
// This allows you to set a fallback for where Webpack should look for modules.
// We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebookincubator/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat(
// It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
// These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see:
// https://github.com/facebookincubator/create-react-app/issues/290
extensions: ['.js', '.json', '.jsx'],
alias: {
// #remove-on-eject-begin
// Resolve Babel runtime relative to react-scripts.
// It usually still works on npm 3 without this but it would be
// unfortunate to rely on, as react-scripts could be symlinked,
// and thus babel-runtime might not be resolvable from the source.
'babel-runtime': path.dirname(
require.resolve('babel-runtime/package.json')
),
// #remove-on-eject-end
// Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web',
},
plugins: [
// Prevents users from importing files from outside of src/ (or node_modules/).
// This often causes confusion because we only process files within src/ with babel.
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc),
],
},
module: {
strictExportPresence: true,
rules: [
// TODO: Disable require.ensure as it's not a standard language feature.
// We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176.
// { parser: { requireEnsure: false } },
// First, run the linter.
// It's important to do this before Babel processes the JS.
{
test: /\.(js|jsx)$/,
enforce: 'pre',
use: [
{
options: {
formatter: eslintFormatter,
// #remove-on-eject-begin
// TODO: consider separate config for production,
// e.g. to enable no-console and no-debugger only in production.
baseConfig: {
extends: [require.resolve('eslint-config-react-app')],
},
ignore: false,
useEslintrc: false,
// #remove-on-eject-end
},
loader: require.resolve('eslint-loader'),
},
],
include: paths.appSrc,
},
// ** ADDING/UPDATING LOADERS **
// The "file" loader handles all assets unless explicitly excluded.
// The `exclude` list *must* be updated with every change to loader extensions.
// When adding a new loader, you must add its `test`
// as a new entry in the `exclude` list in the "file" loader.
// "file" loader makes sure those assets end up in the `build` folder.
// When you `import` an asset, you get its filename.
{
exclude: [
/\.html$/,
/\.(js|jsx)$/,
/\.css$/,
/\.json$/,
/\.bmp$/,
/\.gif$/,
/\.jpe?g$/,
/\.png$/,
],
loader: require.resolve('file-loader'),
options: {
name: 'static/media/[name].[hash:8].[ext]',
},
},
// "url" loader works just like "file" loader but it also embeds
// assets smaller than specified size as data URLs to avoid requests.
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]',
},
},
// Process JS with Babel.
{
test: /\.(js|jsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
// #remove-on-eject-begin
options: {
babelrc: false,
presets: [require.resolve('babel-preset-react-app')],
},
// #remove-on-eject-end
},
// The notation here is somewhat confusing.
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader normally turns CSS into JS modules injecting <style>,
// but unlike in development configuration, we do something different.
// `ExtractTextPlugin` first applies the "postcss" and "css" loaders
// (second argument), then grabs the result CSS and puts it into a
// separate file in our build process. This way we actually ship
// a single CSS file in production instead of JS code injecting <style>
// tags. If you use code splitting, however, any async bundles will still
// use the "style" loader inside the async code so CSS from them won't be
// in the main CSS file.
{
test: /\.css$/,
loader: ExtractTextPlugin.extract(
Object.assign(
{
fallback: require.resolve('style-loader'),
use: [
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
sourceMap: true,
},
},
{
loader: require.resolve('postcss-loader'),
options: {
ident: 'postcss', // https://webpack.js.org/guides/migrating/#complex-options
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
],
},
extractTextPluginOptions
)
),
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
// ** STOP ** Are you adding a new loader?
// Remember to add the new extension(s) to the "file" loader exclusion list.
],
},
plugins: [
// Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In production, it will be an empty string unless you specify "homepage"
// in `package.json`, in which case it will be the pathname of that URL.
new InterpolateHtmlPlugin(env.raw),
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV was set to production here.
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env.stringified),
// Minify the code.
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
// Disabled because of an issue with Uglify breaking seemingly valid code:
// https://github.com/facebookincubator/create-react-app/issues/2376
// Pending further investigation:
// https://github.com/mishoo/UglifyJS2/issues/2011
comparisons: false,
},
output: {
comments: false,
},
sourceMap: true,
}),
// Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
new ExtractTextPlugin({
filename: cssFilename,
}),
// Generate a manifest file which contains a mapping of all asset filenames
// to their corresponding output file so that tools can pick it up without
// having to parse `index.html`.
new ManifestPlugin({
fileName: 'asset-manifest.json',
}),
// Generate a service worker script that will precache, and keep up to date,
// the HTML & assets that are part of the Webpack build.
new SWPrecacheWebpackPlugin({
// By default, a cache-busting query parameter is appended to requests
// used to populate the caches, to ensure the responses are fresh.
// If a URL is already hashed by Webpack, then there is no concern
// about it being stale, and the cache-busting can be skipped.
dontCacheBustUrlsMatching: /\.\w{8}\./,
filename: 'service-worker.js',
logger(message) {
if (message.indexOf('Total precache size is') === 0) {
// This message occurs for every build and is a bit too noisy.
return;
}
console.log(message);
},
minify: true,
// For unknown URLs, fallback to the index page
navigateFallback: publicUrl + '/index.html',
// Ignores URLs starting from /__ (useful for Firebase):
// https://github.com/facebookincubator/create-react-app/issues/2237#issuecomment-302693219
navigateFallbackWhitelist: [/^(?!\/__).*/],
// Don't precache sourcemaps (they're large) and build asset manifest:
staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
// Work around Windows path issue in SWPrecacheWebpackPlugin:
// https://github.com/facebookincubator/create-react-app/issues/2235
stripPrefix: paths.appBuild.replace(/\\/g, '/') + '/',
}),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how Webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
// Some libraries import Node modules but don't use them in the browser.
// Tell Webpack to provide empty mocks for them so importing them works.
node: {
fs: 'empty',
net: 'empty',
tls: 'empty',
},
};
In production, your CSS will be extracted to a separate file. Because off that, in production, you must include it in your HTML file

Resources