So I couldn't build my gatsby app because it has quite a lot of dependencies on window. Many components are built depending on the width of the browser window.
After the "gatsby build" command I got WebpackError: ReferenceError: window is not defined.
I found a solution on the internet to paste the following code into gatsby-node.js:
exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
if (stage === 'build-html' || stage === 'develop-html') {
actions.setWebpackConfig({
module: {
rules: [
{
test: /node_modules/,
use: loaders.null(),
},
],
},
});
}
};
but when rebuilding the app I get this error:
<w> [webpack.cache.PackFileCacheStrategy] Skipped not serializable cache item 'Compilation/modules|json|C:\Users\Damian\Documents\Ossolinsky\app\node_modules\null-loader\dist\cjs.js??ruleSet[1].rules[13].use!C:\Users\Damian\Documents\Ossolinsky\app\node_modules\gatsby\package.json': No serializer registered for JSONParseError
ERROR #98123 WEBPACK
Generating SSR bundle failed
Unexpected end of JSON input while parsing empty string
File: node_modules\gatsby\package.json
not finished Building HTML renderer - 1.257s
I found a solution on the internet to paste the following code into
gatsby-node.js
This is the recipe of the devil. With the following snippet:
{
test: /node_modules/,
use: loaders.null(),
},
You are adding a null loader to all node_modules folder and the idea here is to add only instances that need to be ignored in the server because they had a window dependency. Since rules is an array you can do:
exports.onCreateWebpackConfig = ({ stage, loaders, actions }) => {
if (stage === 'build-html' || stage === 'develop-html') {
actions.setWebpackConfig({
module: {
rules: [
{
test: /some_package_1/,
use: loaders.null(),
},
{
test: /some_package_2/,
use: loaders.null(),
},
{
test: /some_package_3/,
use: loaders.null(),
},
],
},
});
}
};
Keep in mind that /some_package_3/ is a regular expression (hence the test key and that's why is between slashes) that needs to match the folder name inside node_modules.
To know which string should be placed as a regular expression, normally if you import { some_package_1 } from 'some_package_1' means that some_package_1 is the folder inside node_modules that needs to be added.
There's a chance that some_package_1 is not using a window per se, and it's a dependency of the dependency: in that case, you need to find out which one is it and apply the same reasoning ignoring it.
Many components are built depending on the width of the browser
window.
If those are internal components, you should check first if there's a window available when the component is being rendered/parsed, as the docs suggests, with the following condition:
export default function MyComponent() {
if (typeof window !== "undefined") {
return <div>Component that can use window and will bypass SSR</div>
}
return <div>Component that can't use window</div>
}
Related
I have a ReactJS project where I wanted to use a Barcode-Scanner npm module with the name html5-qrcode, but I always get this error:
Failed to parse source map from 'C:\...\node_modules\html5-qrcode\third_party\index.js.map' file: Error: ENOENT: no such file or directory, open 'C:\...\node_modules\html5-qrcode\third_party\index.js.map'
And then there are errors like: (seperated for readability)
WARNING in ./node_modules/html5-qrcode/esm/camera/core-impl.js Module Warning (from ./node_modules/source-map-loader/dist/cjs.js):
Failed to parse source map from 'C:\...\node_modules\src\camera\core-impl.ts' file: Error: ENOENT: no such file or directory, open 'C:\...\node_modules\src\camera\core-impl.ts'
I thought it might be an TS error, because every file of the second error part has a .ts ending.
So I made a new ReactTS project with all components and co in it, but I still get the same error.
I thought it might be an TS error, because every file of the second error part has a .ts ending. So I made a new ReactTS project with all components and co in it, but I still get the same error.
It seems like the npm package has issues with its source maps and webpacks's source-map-loader module is not able to process them. This doesn't really affect the application itself but having all those warnings is annoying.
I came across two solutions: either force the source-map-loader to skip the culprit package or ignore source-map warnings all together.
To achieve either solution, you'll need to be able to override the webpack.config.js. How to override it really depends on the framework you use to run your React apps (I have mine setup using NX)
Solution 1: Ignore source-mapping warnings (Easiest)
Add ignoreWarnings: [/Failed to parse source map/] to your webpack configuration.
E.g.
const { merge } = require('webpack-merge');
module.exports = (config) => {
return merge(config, {
ignoreWarnings: [/Failed to parse source map/]
});
};
Your webpack.config.js will look a lot different than this.
The idea is to add (or override) ignoreWarnings with the pattern of the message it should ignore.
Solution 2: Skip source-map-loading for culprit package (Cleanest?)
const { merge } = require('webpack-merge');
module.exports = (config, context) => {
return merge(config, {
module: {
rules: [
{
enforce: 'pre',
test: /\.js$/,
use: [
{
loader: 'source-map-loader',
options: {
filterSourceMappingUrl: (url, resourcePath) => {
// #zxing has issues with its source maps
if (/#zxing/i.test(resourcePath)) {
return false;
}
return true;
}
}
}
]
}
]
}
});
};
The idea here is to override the source-map-loader rules and skip its execution if the current resource matches the regex. In my case, I want to skip any resource that contains #zxing.
I tried using hte exclude option but I had no luck with that and opted to use the filterSourceMappingUrl instead. Maybe it works for you though. Remember, the pathing has to be absolute so you might need to adapt the excluded pathings.
More details here
const { merge } = require('webpack-merge');
module.exports = (config, context) => {
return merge(config, {
module: {
rules: [
{
enforce: 'pre',
test: /\.js$/,
use: ['source-map-loader'],
exclude: ['/node_modules/#zxing']
}
]
}
});
};
Hope this helps.
Cheers.
add react-app-rewired to your project:
yarn add --dev react-app-rewired
or
npm install react-app-rewired --save-dev
modify your package.json with those lines:
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
create a file called config-overrides.js in project root (same folder as package.json) with this content:
module.exports = function override(config) {
return {
...config,
ignoreWarnings: [
{
module: /node_modules\/stylis-plugin-rtl/,
},
],
}
}
change "stylis-plugin-rtl" in config-overrides.js above to whichever name of library that has an invalid build and throwing errors.
source maps errors should no longer appear.
I am currently having trouble with my React TypeScript project.
I created my project with npx create-react-app my-app --template typescript.
I recently added tsyringe for dependency injection and was trying to implement it for an apiService. After following the readme(https://github.com/microsoft/tsyringe#injecting-primitive-values-named-injection) for adding primitive values I have hit a block. I already add experimentalDecorators and emitDecoratorMetadata to my tsconfig.json file with no success.
The error actual error I am encountering is:
./src/ts/utils/NetworkService.ts 9:14
Module parse failed: Unexpected character '#' (9:14)
File was processed with these loaders:
* ./node_modules/#pmmmwh/react-refresh-webpack-plugin/loader/index.js
* ./node_modules/babel-loader/lib/index.js
You may need an additional loader to handle the result of these loaders.
|
| let NetworkService = (_dec = singleton(), _dec(_class = (_temp = class NetworkService {
> constructor(#inject('SpecialString')
| value) {
| this.str = void 0;
I am fairly sure this problem is caused by Babel, however I created this with npm create react-app --template typescript and do not seem to have access to the Babel configuration.
NetworkService.ts
#singleton()
export default class NetworkService
{
private str: string;
constructor(#inject('SpecialString') value: string) {
this.str = value;
}
}
Invocation method
bob()
{
const inst = container.resolve(NetworkService);
}
Registering Class in index.ts
container.register('SpecialString', {useValue: 'https://myme.test'});
#registry([
{ token: NetworkService, useClass: NetworkService },
])
class RegisterService{}
React-Scripts manages many of the configs related to the project. For many cases, this is fine and actually a nice feature. However, because React-Scripts uses Babel for it's development environment and does not expose the config.
You have to run npm run eject to expose the configurations.
Please note, this is a one-way operation and can not be undone.
Personally, I prefer more control with my configuration.
After this you can edit the webpack.config.js in the newly created config folder.
Find the section related to the babel-loader in the dev-environment and add 'babel-plugin-transform-typescript-metadata' to the plugins array.
Expanding on Jordan Schnur's reply, here are some more pitfalls I encountered when adding TSyringe to my CRA app:
Use import type with #inject
If you get this error "TS1272: A type referenced in a decorated signature must be imported with 'import type' or a namespace import when 'isolatedModules' and 'emitDecoratorMetadata' are enabled." replace import with import type for the offending imports. You will encounter this when working with #inject
E.g. replace import { IConfig } from "iconfig" with import type { IConfig } from "iconfig"
Fixing Jest
Your Jest tests will also break with TSyringe, especially when using #inject. I got the error "Jest encountered an unexpected token" with details constructor(#((0, _tsyringe.inject)("")) ("#" marked as the offending token). I took the following steps to fix that in CRA:
Add the line import "reflect-metadata"; to the top of the file src/setupTests.ts
In config/jest/babelTransform.js replace line 18 and following:
From
module.exports = babelJest.createTransformer({
presets: [
[
require.resolve('babel-preset-react-app'),
{
runtime: hasJsxRuntime ? 'automatic' : 'classic',
},
],
],
babelrc: false,
configFile: false,
});
to:
module.exports = babelJest.createTransformer({
presets: [
[
require.resolve('babel-preset-react-app'),
{
runtime: hasJsxRuntime ? 'automatic' : 'classic',
},
],
],
plugins: [
require.resolve('babel-plugin-transform-typescript-metadata')
],
babelrc: false,
configFile: false,
});
Instead of eject, you may use a lib that "overrides" some of your params.
I used craco : https://www.npmjs.com/package/#craco/craco
I've created an simpler DI library that doesn't need decorators or polyfill. Works with CRA like a charm and has cool React bindings
iti
import { useContainer } from "./_containers/main-app"
function Profile() {
const [auth, authErr] = useContainer().auth
if (authErr) return <div>failed to load</div>
if (!auth) return <div>loading...</div>
return <div>hello {auth.profile.name}!</div>
}
This is the error:
"Build failed!
× ERROR ./media/fonts/handFont3.otf 1:4.
Module parse failed: Unexpected token (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.See https://webpack.js.org/concepts#loaders
(Source code omitted for this binary file)
# ./index.js 3:0-37
# ../node_modules/preact-cli/lib/lib/entry.js
# multi ../node_modules/preact-cli/lib/lib/entry webpack-dev-server/client webpack/hot/dev-server"
I'm trying to import pdf into preact component to make it build so I can get the link to the page with pdf file. Here is how:
import pdfFile from '../../media/images/pdfFile.pdf'
<a href={pdfFile} target="_blank"...
It didn't work, so I Googled this two solutions to add in wepback.config.js:
module.exports = {
module: {
rules: [
{
test: /\.(png|svg|jpg|gif|pdf)$/,
use: ['file-loader']
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: ['file-loader']
},
]
},
};
and
{
test: /\.(pdf)$/,
use: ['url-loader']
},
I do manually make webpack.config work by this string:
--config webpack.config.js
And file is working, but result is the same. I'm still getting the error. And the same with otf fonts.
Thank you.
I figured out the solution. You can't use webpack config with preact config together.
So I removed webpack config and changed preact one to this:
export default (config, env, helpers, options) => {
const rule = {
test: /\.(otf|pdf)$/,
loader: 'file-loader'
}
config.module.rules.push(rule);
}
I have a "common library" written in Typescript and I want to use it in a couple more projects. I don't want to create a npm package from it, I just want to import it as "uncompiled package" but I'm having trouble setting this up.
I tried adding ts-loader and excluding the module like this:
webpackConfig.module.rules.push({
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules\/((?!common-library).)*$/,
});
However, this results in Error: TypeScript emitted no output for /path/to/index,tsx.
I also tried using craco-babel-loader https://github.com/rjerue/craco-babel-loader, however when adding it to the list of plugins:
{
plugin: rewireBabelLoader,
options: {
includes: [resolveApp('node_modules/common-library')],
excludes: [/node_modules/],
}
},
I'm getting the following:
C:\path\to\app\node_modules\#craco\craco\lib\loaders.js:34
rules.some((rule, index) => {
^
TypeError: rules.some is not a function
I'm working on an Angular.js project written with TypeScript. We're trying to evaluate whether to upgrade to Angular 8 and we're stuck with how to use ng-annotate with angular-cli's webpack configuration.
I believe that this can be achieved either by using the #angular-builders/custom-webpack tool or by using ngx-build-plus tool but I had not succeeded with neither of them.
My current attempt includes a partial webpack file for ngx-build-plus with the following configuration :
module: {
rules: [
{
test: /\.ts$/,
loaders: ['ng-annotate-loader?ngAnnotate=ng-annotate-patched'],
},
{
test: /\.tpl\.html$/,
loader: 'ng-cache-loader?-url&module=templates&prefix=src:./**/'
}
]
},
};
Having this, when I run ng serve --extra-webpack-config webpack.partial.js -o I get the following error : NonErrorEmittedError: (Emitted value instead of an instance of Error) error: couldn't process source due to parse error,Unexpected token
The token to which it refers, is simply the type declaration for a method parameter. So I'm guessing that there is some conflict with the loader that angular-cli already uses for TypeScript files but I don't know how to resolve this.
Is there any input on how to solve this either using one of the two tools or something else?
So, the way to do this is by using webpack-merge and custom-webpack.
This is the configuration to run ng-annotate with Typescript files :
module.exports = (config, options) => {
const customConfig = {
module: {
rules: [
{
test: /\.ts$/,
loaders: ['ng-annotate-loader?ngAnnotate=ng-annotate-patched'],
},
{
test: /\.tpl\.html$/,
loader: 'ng-cache-loader?-url&module=templates&prefix=src:./**/'
}
]
}
};
return merge.strategy({
'module.rules': 'prepend'
})(config, customConfig)
};
The key part is the merge.strategy call which will make sure that the loaders of the custom configuration will be prepended to the ones that angular-cli already sets up.
After several hours of looking at alternatives, the solution that worked the best for me was using babel-plugin-angularjs-annotate by:
Creating a new project
Installing babel and babel-plugin-angularjs-annotate
Executing babel, which takes the files from src, adds the annotations, and puts the result in the folder output.
For others needing the complete solution, this is what I executed:
mkdir babel-project && cd babel-project
npm init -y
npm install -D babel-cli babel-plugin-angularjs-annotate
Create .babelrc and add this:
{
"presets": [],
"plugins": [ "angularjs-annotate" ]
}
Finally execute babel:
npx babel src -d build --extensions .ts
This takes files like:
import * as _ from "underscore";
import "#app2/hello/chao"
const greeting = () => "Hello World"
angular.module("MyMod")
.controller("MyCtrl", ($scope, HelloClient) => {
HelloClient.add($scope, greeting);
});
And turns them into:
import * as _ from "underscore";
import "#app2/hello/chao";
const greeting = () => "Hello World";
angular.module("MyMod").controller("MyCtrl", ["$scope", "HelloClient", ($scope, HelloClient) => {
HelloClient.add($scope, greeting);
}]);
Update
Using babel-plugin-angularjs-annotate messed a lot with my formatting, so I ended up copying&pasting just the relevant parts of the Babel output, which took some hours.