Using appropriate transpilers for React Native Web Project - javascript

I am trying to create a React Native Web project.
I have built several React Native apps before, but have never tried to put one on the web.
My biggest problem has been incompatibility between native libraries when launching the web - not an unexpected problem.
Anyway, my goal is to be able to load native libraries when on a native platform and having alternative libraries doing the same thing when on the web.
For example, I am getting the current error:
./node_modules/react-native-calendars/src/expandableCalendar/asCalendarConsumer.js
Module parse failed: Unexpected token (11:8)
You may need an appropriate loader to handle this file type.
| render() {
| return (
| <CalendarContext.Consumer>
| {(context) => (
| <WrappedComponent
How would I fix this? This library is theoretically compatible with React Native Web, and yet I get the above error.
Would this loader be in Babel? Metro? Webpack?
I have a babel.config.js that looks like this:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
resolve: {
alias: {
'react-native$': 'react-native-web'
}
},
rules: [
{
test: /\.(js|jsx|mjs)$/,
include: [
paths.src,
// In order to use react-native targetted libraries on web,
// we have to use babel to compile them from ES6 to ES5.
// This would still not allow us to use libraries that have RN
// dependencies that are not polyfilled by react-native-web.
path.resolve(paths.nodeModules, 'react-native-vector-icons'),
],
loader: 'babel-loader',
options: {
compact: true,
presets: ['react-native'],
},
}
]
};
I have a metro that looks like this:
const { getDefaultConfig } = require("metro-config");
module.exports = (async () => {
const {
resolver: { sourceExts }
} = await getDefaultConfig();
return {
transformer: {
babelTransformerPath: require.resolve("react-native-css-transformer")
},
resolver: {
sourceExts: [...sourceExts, "css"]
}
};
})();
And here is my webpack:
// webpack.config.js
module.exports = {
plugins: ["#babel/plugin-syntax-dynamic-import"],
resolve: {
alias: {
'react-native$': 'react-native-web'
},
},
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
//exclude: /node_modules/,
options: {
presets: ['es2015', 'stage-0', 'react', 'babel-preset-env', 'babel-preset-stage-0'],
plugins: ["#babel/plugin-syntax-dynamic-import"],
}
},
{
test: /\.ttf$/,
loader: "url-loader", // or directly file-loader
include: path.resolve(__dirname, "node_modules/react-native-vector-icons"),
},
]
}
I'm really quite lost on how to setup a Webpack, or how I am supposed to be using these files to get rid of the above error.
Where do I add the loader the error is asking about?
Sorry if this is a confusing question - this part of RN is completely new to me

Actually, I faced an issue like it but not the web, our project needs to have a unique logic but different UIs for Android and iOS, so we decide to decoupled the UIs with different files by .android.js and .ios.js files, its name is Platform-specific extensions and also config it on the .babelrc file:
{
"presets": ["module:metro-react-native-babel-preset", "module:react-native-dotenv"],
"plugins": [
"lodash",
["module-resolver", {
"extensions": [".android.js", ".ios.js", ".js"], // here
"cwd": "babelrc",
"root": ["./app"]
}]
],
"env":{
"production":{
"plugins": ["transform-remove-console"]
}
}
}
So for decoupling the UI for each stack be like below:
CheckoutPage.android.js
CheckoutPage.ios.js
For importing the component we use this way:
import Checkout from '[pathToComponent]/CheckoutPage';
~~~
<CheckoutPage ...
Solution:
Now my suggestion is using another file extension, the web.js and put it in the babel configuration:
"extensions": [".android.js", ".ios.js", ".web.js", ".js"],
Furthermore, add the web.js extension to the Webpack configuration for loading in the web build and use ignore-loader to ignore .ios.js and .android.js files on the web build:
// webpack configuration file
module.exports = {
module: {
rules: [
{
test: /\.(js|web.js)$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
},
},
{
test: /\.(android.js|ios.js)$/,
exclude: /node_modules/,
use: {
loader: "ignore-loader",
},
},
],
},
~~~
resolve: {
extensions: ['.web.js', '.js'],
},
};
For a better explanation, I create a project on CodeSandBox, and you can see there I just call import Home from './Home'; but the Home.web.js component is rendered.
By using this trick you can use platform-specific extensions even on the web build or develop.

Related

React.js integration with ASP MVC with create-react-app

I am new to React world, and I'm trying to integrate it in a new project that uses ASP MVC .net.
I want to use React.js with the create react app, not interested in the React.net integration.
I have seen a couple examples that don't use the CRA command, instead they configure the build set up themselves (webpack, babel, etc), I was trying that approach, but I'm worried that if the project grows I will lose track of updates, etc.
In that example, you need to add whatever the output of the webpack bundled file is into your index.cshtml.
<div id="root"></div>
#section scripts {
<script src="~/Scripts/React/dist/bundle.js"></script>
}
But when I use the CRA command I don't have access to that file during development, only when I build for production.
I'm a little lost here, what is the best way of achieving what I need with CRA without ejecting?
I really appreciate any help :)
I don't think it is possible to do what you want (and I wanted too) with CRA and I believe the complexity you will end up after ejecting is too high to be manageable.
My starting point: a big ASP MVC application running an Angular.js front-end within a single MVC Controller/View (the default index page).
My goal: stop growing the Angular.js app and develop new functionality with React whenever possible, i.e. when it is independent of existing UI; let's call it new modules. I still need to keep everything within the same MVC app because it provides authentication and authorization among other things.
The solution: a custom (with respect to CRA) webpack build toolchain whose starting point is the youtube example you provided. Thanks to this other tutorial, I have been able to add hot reload and after a few hours of trial and error I added loaders for css, images and fonts. The bundled result is for sure less optimal than the outcome of CRA, but it coexists with the old Angular.js so I believe it is good enough.
Here is some code.
webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const WebpackNotifierPlugin = require('webpack-notifier');
const BrowserSyncPlugin = require('browser-sync-webpack-plugin');
const webpack = require('webpack');
const PUBLIC_PATH = 'Scripts/react/dist';
module.exports = (env, arg) => {
const isDevelopment = arg.mode === 'development';
const fileLoaderName = file => {
if (isDevelopment)
return '[folder]/[name].[ext]';
return '[folder]/[name].[ext]?[contenthash]';
};
return {
entry: './app.js',
watch: isDevelopment,
devtool: isDevelopment ? 'source-map' : undefined,
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
'babel-loader',
{
loader: 'eslint-loader',
options: {
fix: true
}
}
],
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
use: [
{
loader: 'file-loader',
options: {
name: fileLoaderName,
publicPath: PUBLIC_PATH,
postTransformPublicPath: p => `__webpack_public_path__ + ${p}`
}
}
]
},
{
test: /\.(woff|woff2|ttf|otf|eot)$/i,
use: [
{
loader: 'file-loader',
options: {
name: fileLoaderName,
publicPath: PUBLIC_PATH,
postTransformPublicPath: p => `__webpack_public_path__ + ${p}`
}
}
]
}
],
},
plugins: [
new webpack.ProgressPlugin(),
new WebpackNotifierPlugin(),
new BrowserSyncPlugin(),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({ filename: "bundle.css" })
],
resolve: {
extensions: ['*', '.js', '.jsx']
},
output: {
path: __dirname + '/dist',
publicPath: '/',
filename: 'bundle.js'
},
}
};
.babelrc
{
"presets": [
"#babel/preset-env",
"#babel/preset-react"
],
"plugins": [
"#babel/plugin-transform-runtime"
]
}
.eslintrc
{
"extends": [
"plugin:react-app/recommended",
"prettier"
],
"plugins": [
"prettier"
],
"rules": {
"prettier/prettier": ["error"],
"quotes": [
"error",
"single",
{ "allowTemplateLiterals": true }
]
}
}
.prettierrc
{
"singleQuote": true
}
package.json
...
"scripts": {
"start": "webpack --mode development",
"build": "webpack --mode production",
...
},
There are still a few useful things that are missing and I plan to add in the future, like css modules and other css optimizations, but I think it's not going to be to difficult.

configure Laravel mix mix.webpackConfig({}) with entry points and outputs

I'm developing an application that use php 5.6 and laravel 5.4. I'm using laravel mix for build my assets. I need to know how to use mix.webpackConfig({}) method to use another webpack configurations like use babel-loader, riot-tag-loader etc. Is there any way to use this method to do that with entry point and output files? For an example, I need to do following thing inside my mix.webpackConfig({}).
module.exports = {
entry: {
admin: ['./resources/assets/admin/js/app.js'],
'manuals/parent/child/js': ['./resources/views/manuals/parent/child/js/app.js']
},
output: {
filename: '[name]/app.js',
path: path.resolve(__dirname + '/public')
},
module: {
rules: [
{
test: /\.tag$/,
exclude: /node_modules/,
loader: 'riot-tag-loader',
query: {
type: 'es6',
hot: true
}
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]
}
};
Is that possible? Is that so, please let me know how to do that. Thanks
I've hardly found the laravel-mix mix.webpackConfig({}) successfully initiated, and even working samples are rear. I do not know what framework you are trying to manage but this sample works; this is a config for less-loader, hope you can tune it to suit your purpose.
const path = require('path');
mix.webpackConfig({
module: {
rules: [
{
test: /\.less$/,
loader: "style-loader!css-loader!less-loader",
exclude: [
path.resolve(__dirname, "node-modules"),
path.resolve(__dirname, "resources/assets/less"),
],
},
]}
})
Mix is a configuration layer on top of Webpack, so to run your Mix tasks you only need to execute one of the NPM scripts that is included with the default Laravel package.json file: more details in official site
https://laravel.com/docs/5.7/mix

How can I pull out certain items from rxjs?

I'm writing a lib that has rxjs as a dependency. It only uses Subject - Is it possible for me to extract that one feature and include it within my lib, removing the need for rxjs as a dependency?
No. check inner dependencies Subject rely on (https://github.com/ReactiveX/rxjs/blob/master/src/internal/Subject.ts#L1-L8). It is pretty much requiring most of primitives in rx.
Put aside of availability, if you're depends on rxjs, what reason you'd like to not to specify it as dependency?
Looks like using Webpack's tree shaking feature did it for me.
https://webpack.js.org/guides/tree-shaking/
My Webpack configuration:
const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
entry: ['./src/index.js'],
output: {
filename: './dist/dist.bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader" ,
query: {
presets: ['env'],
plugins: ["transform-object-rest-spread"]
}
}
]
},
plugins: [
new UglifyJSPlugin()
]
}
And specified the location of the import like:
import { Subject } from "rxjs/subject"
Rather than
import { Subject } from "rxjs"
Bundle size went from 213kb to 14kb

Typescript: How to have some imports in the global scope?

Context:
I work on a project where the senior programmer decided to reduce the boilerplate code in newly created typescript files. Two examples of this boilerplate code would be importing the React library or the function that fetches and processes our localized strings.
Question:
Is it possible to have imports always available in files placed in certain folders without having to write the import tags every time?
What I've tried:
I've searched and read on the subject and found those links that talk about defining variables to use in the global space:
global.d.ts, global-modifying-module.d.ts, A typescript issue that seems to get it working
However, I was still unable to get it to work. Here is what I've tried:
At the root of the folder where I want React to be always available, I created a global.d.ts file which contains:
import * as R from "react";
declare global{
const React: typeof R;
}
With this file, the resource "React" is supposed to always be available to other files in subsequent folders. My IDE (Webstorm) recognizes that the import is there and allows me to manipulate the variable React without complaining. However, when I try to run the app, I get this error:
ReferenceError: React is not defined
I don't understand what is wrong with the code! Here is an example of the file I'm trying to render:
export default class World extends React.Component<{}, any> {
public render() {
return (<div>Hello world</div>);
}
}
From this stackoverflow question, I was under the impression that the problem could be webpack related. For the sake of completeness, here is the webpack config file we're currently using:
const webpack = require('webpack');
const path = require('path');
const BUILD_DIR = path.resolve(__dirname, './../bundles');
const WEBPACK_ENTRYFILE = path.resolve(__dirname, './../srcReact/ReactWrapper.tsx');
// `CheckerPlugin` is optional. Use it if you want async error reporting.
// We need this plugin to detect a `--watch` mode. It may be removed later
// after https://github.com/webpack/webpack/issues/3460 will be resolved.
const { CheckerPlugin } = require('awesome-typescript-loader');
const config = {
entry: [WEBPACK_ENTRYFILE],
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx', '.less']
},
output: {
path: BUILD_DIR,
filename: 'bundle.js'
},
plugins: [
new CheckerPlugin()
],
devtool: 'source-map', // Source maps support ('inline-source-map' also works)
module: {
loaders: [
{
loader: 'url-loader',
exclude: [
/\.html$/,
/\.(js|jsx)$/,
/\.(ts|tsx)$/,
/\.css$/,
/\.less$/,
/\.ttf/,
/\.woff/,
/\.woff2/,
/\.json$/,
/\.svg$/
],
query: {
limit: 10000,
name: 'static/media/[name].[hash:8].[ext]'
}
},
{
loader: 'url-loader',
test: /\.(ttf|woff|woff2)$/
},
{
loader: "style-loader!css-loader!less-loader",
test: /\.less$/
},
{
loader: "style-loader!css-loader",
test: /\.css$/
},
{
loader: "svg-loader",
test: /\.svg$/
},
{
loader: "json-loader",
test: /\.json$/
},
{
loader: "awesome-typescript-loader",
test: /\.(ts|tsx)$/
}
]
}
};
module.exports = config;
I am certain I am missing something. Can anyone help me?
Surely already open followed a tutorial like this
To do this creates a vendor file where you import these types of "global".
./src/vendors.ts;
import "react";
Add this file a to first place at entry parameter:
entry: { 'vendors': './src/vendors.ts', 'main': './src/main.ts' }
And add CommonChunkPlugins:
plugins: [ new CommonsChunkPlugin({
name: 'vendors'
}),
Like this in AngularClass with polyfills.

How can I include a multi part library with webpack?

I created a multi part library similar to the example from webpack/webpack/.../multi-part-library. In my apps I want to be able to import parts of my library like this:
ìmport Button from 'myLib/atoms/button';
// or
import { Button } from 'myLib/atoms';
My webpack configuration for the apps looks like this and I get an error (Cannot resolve module 'myLib/atoms' or Cannot resolve module 'myLib/atoms/button'):
module.exports = {
'entry': {
'app': './client.js',
},
'output': {
'filename': 'bundle.js',
},
'externals': {
'react': true,
'react-dom': true,
'myLib': true,
},
'module': {
'loaders': [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel',
},
]
},
};
The webpack configuration for the library looks like this:
const files = glob.sync('**/*.js', {
cwd: path.join(__dirname, 'atomic-design'),
absolute: true,
});
let entries = {};
files.forEach((file) => {
const name = path.basename(path.dirname(file));
const newName = `atoms/${name}`;
entries[newName] = file;
});
module.exports = {
'entry': entries,
'output': {
'path': path.join(__dirname, 'lib'),
'filename': 'myLib.[name].js',
'library': ['myLib', '[name]'],
'libraryTarget': 'umd',
'umdNamedDefine': 'myLib',
},
'externals': {
'react': true,
'react-dom': true,
},
'module': {
'loaders': [
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel'
},
]
}
};
The files are structured like this:
- app
- client.js
- webpack.config.js
- myLib
- webpack.config.js
- atomic-design
- button
- index.js
- text-field
- index.js
So far I could only find tutorials for creating libraries with webpack, where they only use small examples of libraries.
Thanks for your help in advance!
Best regards,
JBrieske
I believe you need to add the paths (and adjust the from 'path' accordingly) for the modules you're trying to import to resolve.modulesDirectories.
Relevant documentation: https://webpack.github.io/docs/configuration.html#resolve-modulesdirectories.
Bear in mind that this will change in Webpack2, which is feature complete and is just short of documentation for a release.

Categories