Functions are not available in webpack bundle - javascript

I'm working on Arrow project and I'm in phase to create the Bundle file using Webpack.
I grouped my modules in a folder. and for each folder I have index.js where I export all modules.
Also, I have global index.js that imports all index.js like this :
import * as Has from "./scripts/array/has"
import * as Math from "./scripts/array/math"
import * as Arrow from "./scripts/array"
export { Has, Math, Arrow }
Now, I want to create my bundle from this global index.js .
My Webpack configuration looks like :
const path = require("path")
const { CleanWebpackPlugin } = require("clean-webpack-plugin")
module.exports = {
mode: "development",
entry: {
arrow: "./src/1.x.x/index",
},
plugins: [
new CleanWebpackPlugin({ cleanStaleWebpackAssets: false }),
],
devtool: 'inline-source-map',
devServer: {
contentBase: './build',
},
output: {
filename: "[name]-alpha.js",
path: path.resolve(__dirname, 'build'),
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
}
The Issue is when I try to import my functions from the build the functions do not appear in the Autocomplete and I received errors that these functions are undefined!
I have just ___esModule :
import { __esModule } from "./arrow-alpha"
I want to let developers use and import the functions like the example
import {omit} from "arrow" // arrow is the bundled file
const frameworks = ["react", "vue", "ember", "angular"];
const withoutAngular = omit(frameworks,"angular");
console.log(withoutAngular); // ["react", "vue", "ember"]
I blocked in this step for days and I could not figure out the issue.

It sounds like you are looking to export your code as library with webpack. In order to do so, you should export your library module as umd which can work for backend/client side so IDE can also understand what you export. Just add following configuration in your webpack.config.js:
output: {
libraryTarget: 'umd', // style for your library
library: 'yourLibName', // if you keen to have a name for library
// ...
},
NOTE: this only works if you pass to webpack a cjs style code, otherwise it will convert to harmony module which you can't export accordingly (such as esm module)
In order to make sure babel would convert your esm module to commonjs, you have to set modules to commonjs like this:
babel.config.js or .babelrc
[
"#babel/preset-env",
{
"modules": "commonjs",
}
]

Adding below Configuration. Will eject your final bundle to be accessed as Window.Arrow on adding script to index.html page
output: {
filename: "[name]-alpha.js",
path: path.resolve(__dirname, 'build'),
library: 'Arrow',
libraryTarget: 'var'
},
and your index.js page inside src folder to be as
module.exports = require('./scripts/array');

Related

Webpack static field

why doesn't webpack support static field?
When i try
export class Game {
#lasttime = 0;
#FRAME_DURATION = 1000 / 144;
I get an error
Module parse failed: Unexpected character '#' (2:4)
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.
What is the problem?
const path = require('path');
const HTMLPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
filename: 'js/main.js',
path: path.resolve(__dirname, 'dist'),
},
devtool: 'inline-source-map',
devServer: {
contentBase: './dist',
},
plugins: [
new HTMLPlugin({
template: './src/index.html'
})
],
};
https://webpack.js.org/guides/getting-started/#modules:
Note that webpack will not alter any code other than import and export statements. If you are using other ES2015 features, make sure to use a transpiler such as Babel or Bublé via webpack's loader system.
You need to configure babel-loader yourself, see https://webpack.js.org/loaders/babel-loader/#root

How to propperly build react modular library

I'm trying to create a react components library which is based on Typescript and SASS. The components library will be used in multiple other typescript projects, so type exports are needed as well. Ideally I want to mimic something like "Material-UI"/"React-Bootrap" libraries dist output solutions.
Example project structure:
|Tabs
+--Tabs.tsx
+--Tabs.scss
+--index.tsx
index.tsx
index.tsx
export { Tabs } from './Tabs/Tabs';
Tabs/index.tsx
import React from 'react';
import './Tabs.scss';
interface TabsProps {
...
}
export const Tabs: React.FC<TabsProps> = (props) => <div>...</div>
Tabs/index.tsx
export { Tabs } from './Tabs';
Expected built dist structure should mimic the src structure:
|Tabs
+--Tabs.js
+--Tabs.d.ts
+--index.js
+--index.d.ts
index.js
index.tsx
I tried analyzing open source projects and see how they are building the libraries, however I could not find libraries using the same approaches that I could reuse.
The solutions I've tried:
Webpack: While I could compile typescript and sass files the webpack would always emit only one file specified in the output section, which usually would be bundled and I would loose the ability to import single component from a specific component's module. I know I can specify multiple entry points, but the project will be having a lot of exports and manually specifying them is not an option...
Example config I tried:
const path = require('path');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
module.exports = {
entry: './src/index.tsx',
module: {
rules: [
// sass-loader is not used here yet, but should be once desired structure can be reached
{
test: /\.tsx?$/,
loader: 'babel-loader',
},
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{ test: /\.js$/, loader: "source-map-loader" }
]
},
// Enable sourcemaps for debugging webpack's output.
devtool: "source-map",
resolve: {
extensions: [".tsx", ".ts", ".js"],
plugins: [
new TsconfigPathsPlugin({ configFile: "./tsconfig.build.json" })
]
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
};
Rollup: Similar situation as webpack
Example config that I tried:
// rollup.config.js
import babel from 'rollup-plugin-babel';
import sass from 'rollup-plugin-sass';
import nodeResolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import react from 'react';
import reactDom from 'react-dom';
const babelOptions = {
exclude: /node_modules/,
// We are using #babel/plugin-transform-runtime
runtimeHelpers: true,
extensions: ['.js', '.ts', '.tsx'],
configFile: './babel.config.js',
};
const nodeOptions = {
extensions: ['.js', '.tsx', '.ts'],
};
const commonjsOptions = {
ignoreGlobal: true,
include: /node_modules/,
namedExports: {
react: Object.keys(react),
'react-dom': Object.keys(reactDom)
},
};
export default {
input: 'src/index.tsx',
output: {
name: '[name].js',
dir: 'dist',
format: 'umd',
sourcemap: true,
},
plugins: [
nodeResolve(nodeOptions),
sass(),
commonjs(commonjsOptions),
babel(babelOptions)
],
};
Babel: I managed to compile the typescript code however once I came close to transpiling SASS files I would end up with suggestions to use webpack for that...
TSC: I successfully could run the typescript compiler and it would compile all the files without problems and would maintain the same structure. However TSC does not support other transpiling options so after a lot of searches I would end up with suggestions to use webpack and "ts-loader" or "babel-loader"..
tsconfig:
{
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"lib": [ "es2015", "dom" ],
"outDir": "dist",
"baseUrl": ".",
"declaration": true,
"composite": true,
"module": "commonjs",
"target": "es5"
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"dist"
]
}
Desirced solution:
I should be able after compiling the library and installing it in another project be able to run the following:
import { Tabs } from 'my-lib/Tabs';
import { Tabs } from 'my-lib';
After a lot of playing around I managed to produce the wanted result with rollup. The only downside of the current configuration is that it does not support newly added files in the --watch mode. The magic setting is under the output.preserveModules
Config:
// rollup.config.js
import commonjs from '#rollup/plugin-commonjs';
import typescript from '#rollup/plugin-typescript';
import postcss from 'rollup-plugin-postcss';
import postcssUrl from 'postcss-url';
import resolve from "#rollup/plugin-node-resolve";
import peerDepsExternal from "rollup-plugin-peer-deps-external";
export default {
input: 'src/index.tsx',
output: {
dir: 'dist',
format: 'es',
preserveModules: true,
sourcemap: true,
},
plugins: [
resolve(),
peerDepsExternal(),
commonjs(),
typescript({
tsconfig: 'tsconfig.build.json'
}),
postcss({
minimize: true,
modules: {
generateScopedName: "[hash:base64:5]"
},
plugins: [
postcssUrl({
url: "inline"
})
]
}),
],
};
I hope this config can help others as well
You can checkout this repo. I made some changes for building a lib.
https://github.com/21paradox/react-webpack-typescript-starter
To use a library like below:
import Input from 'my-custom-ui/entry/Input';
import { Input } from 'my-custom-ui';
After doing a lot of searching, I ended up writing a plugin to manually generate the webpack entry code that webpack needed (for building a ui library).
The multiple entry + manualy generated entry file seems to be working for component seperate & no redudant code. This is also very helpful if you want to build a vue based libray.

Load GLB model with Webpack - Three.js

I'm trying to use Webpack for the first time and I have trouble to add my glb model. My model is ok, used many times and I put in public folder. I dont' understand console error, any help will be appreciate, thanks.
I'm using three.js r116 and Firefox. Safari tell me same error, can't found the model.
Here a part of my JS code :
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
const loader = new GLTFLoader();
loader.load('/assets/models/street_car.glb', (gltf) => {
scene.add(gltf.scene);
});
My webpack.config :
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
entry: './src/scripts/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'dist/main.js',
},
performance: {
hints: false
},
plugins: [
new CopyWebpackPlugin([{ from: '**/*', to: '' }], {
context: 'src',
writeToDisk: true,
}),
],
devServer: {
contentBase: path.resolve(__dirname, 'dist'),
port: 9000,
historyApiFallback: true
}
};
And finally console error :
I just find the problem, add this lines to webpack.config
module:
{
rules:
[
{
test: /\.(glb|gltf)$/,
use:
[
{
loader: 'file-loader',
options:
{
outputPath: 'assets/models/'
}
}
]
},
]
}
And don't need to add assets in public folder, they are in my src folder with scripts.
For Webpack v5, the previous loaders are now obsolete (like file-loader, raw-loader, etc), and replaced by the use of Asset Modules
The correct asset module for 3d model formats like gltf, obj, fbx, stl etc. is asset/resource
I was looking for something like this but for a Symfony Webpack Encore application configuration. #MlleBz your answer actually helped me a lot, thanks.
So, with no further ado, if you're looking to implement a .glb or .gltf loader in a Symfony / React / ThreeJS application, add this to your webpack.config.js :
// add loader for .glb files (use this with three.js)
.addLoader({
test: /\.(glb|gltf)$/,
loader: 'file-loader'
})

In Webpack v4 with multiple entries, my splitChunks is not optimized, if my two pages includes core-js, they will all get a copy

You can clone my minimum repo https://github.com/rockmandash/webpack-chunks-question
Or see the code below
page1.js
import 'core-js';
console.log('I am page1');
page2.js
import 'core-js';
import 'react';
import 'react-dom';
console.log('I am page2');
My webpack config:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const entriesFilePath = [
require.resolve('./src/page1.js'),
require.resolve('./src/page2.js'),
];
const mode = 'development';
const webpackConfig = entriesFilePath.map((entrieFilePath) => {
const fileName = path.basename(entrieFilePath, path.extname(entrieFilePath));
// fileName would be page1 and page2
return {
mode,
devtool: 'cheap-module-source-map',
entry: {
[fileName]: entrieFilePath,
},
output: {
filename: 'static/js/[name].js',
chunkFilename: 'static/js/[name].chunk.js',
},
optimization: {
splitChunks: {
chunks: 'all',
},
},
plugins: [
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin(
Object.assign(
{},
{
inject: true,
filename: `${fileName}.html`,
},
),
),
].filter(Boolean),
};
});
module.exports = webpackConfig; // I have to export an array, because in the future, I need to do something else.
The generated dist folder looks like this:
dist
/page1.html
/page2.html
/static
/js
/page1.js.map
/vendors~page2.chunk.js.map
/vendors~page1.chunk.js // this includes core-js !!!!
/page1.js
/page2.js
/vendors~page1.chunk.js.map
/vendors~page2.chunk.js // this includes core-js too !!!!
/page2.js.map
You see, the generated two chunks both includes core-js, how can I make my webpack config smart enough to automatically separate core-js or other common vendor files out of the box?
You do not need to import core-js anywhere. Create a .babelrc file.
{
"presets": [
[
"#babel/preset-env",
{
"debug": true,
"useBuiltIns": "usage",
"corejs": 3
}
]
]
}
Instal #babel/core, #babel/polyfill, #babel/preset-env, babel-loader
Add to webpack
const optimization = {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'vendor',
chunks: 'all',
}
}
}
};
module: {rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
}
]},
Take a look at my solution, there is a complete code. Use of core-js. He uses only part polyfil when it is needed.
https://github.com/tomik23/webpack-babel-corejs/blob/master/webpack.config.js#L17
And the second solution needed in your code is to use spliChunks
https://github.com/tomik23/photoBlog/blob/master/config/webpack.config.js#L31
P.S. If you import 'core-js' in this way; you download the whole core-js if you use my method then core-js chooses only what is needed and packages are smaller.

Webpack: vendor bundle not imported in main output

I'm struggling with what looks like a generic error from Webpack after trying to optimise my source code.
Assuming I have the following files in ./src:
├── main.js
├── moduleA.js
└── moduleB.js
main.js imports and uses ModuleA.
moduleA.js imports and uses ModuleB
ModuleA.js and ModuleB.js both import flatten-array from node_modules
My expectation is that if I try to optimise my bundle (see below) it will output two files:
1. index.js
2. vendors~main.index.js
Trying to execute the index.js output bundle results in:
/******/ modules[moduleId].call(module.exports, module,
module.exports, __webpack_require__);
^
TypeError: Cannot read property 'call' of undefined
Although the files are generated, index.js doesn't appear to import vendors~main.index.js. Yet it executes fine when removing the optimization (and vendors javascript)
Is this the correct assumption? How can I make it work like this?
While this is a bundle for Node, there are valid reasons that I'd like to export a vendors file.
Accompanying git repo to reproduce available here:
https://github.com/supasympa/webpack-vendors-issue
Files are:
main.js
const moduleA = require('./moduleA');
moduleA.log('log from main.js');
moduleA.js
const moduleB = require('./moduleB');
const flatten = require('array-flatten');
module.exports.log = function(msg){
moduleB.log('logging from moduleA.js');
console.log(`ModuleA logging: ${msg}`);
console.log(`flattened: ${flatten([[1,2,3],[4,5],[6,7]])}`)
};
moduleB.js
const flatten = require('array-flatten');
module.exports.log = function(msg){
console.log(`ModuleB logging: ${msg}`);
console.log(`flattened: ${flatten([[1,2,3],[4,5],[6,7]])}`)
};
webpack.config.js
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
module: {
rules: [{
include: [path.resolve(__dirname, 'src')],
loader: 'babel-loader',
options: {
plugins: ['syntax-dynamic-import'],
presets: [['env', {
'modules': 'commonjs'
}]]
},
test: /\.js$/
}]
},
entry: './src/main',
target: 'node',
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist')
},
mode: 'development',
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
priority: -10,
test: /[\\/]node_modules[\\/]/,
enforce: true
},
},
// concatenateModules: false,
chunks: 'all',
minChunks: 1,
minSize: 0,
name: true
}
},
plugins: [
new CleanWebpackPlugin(['dist']),
]
};
It turns out that this is not yet implemented outside of the browser, in Webpack.
https://github.com/webpack/webpack/issues/8330
https://github.com/webpack/webpack/issues/8161
https://github.com/webpack/webpack/issues/8156
Defining, chunks: 'all', you're explicitly taking all initial entries and async imports (by default it only on-demand async chunks) and specifying the bundler to create a new vendor chunk/file out of that.
So the behavior is as expected. The idea is to remove the common dependencies from entry files so they can be shared and the page has to load less vendors/common code overall.
One way to explicitly control what the entry files include is this pattern: https://github.com/webpack/webpack/tree/master/examples/two-explicit-vendor-chunks

Categories

Resources