Related
Background
Need to create an external React component library using existing components from a Next.js site, using Tailwindcss#^2. This needs to be pulled in using npm, onto any site that requires the components.
Problem
I've managed to create the package using Webpack#^5 to compile. This is all fine, but the problem I face is that, the components don't work when reintroduced to the initial site. I have a feeling this is either the compiling is wrong or the fact that Next.js is server side rendered, or it could be both.
Attempted solution
I have tried to use Rollup as an alternate solution to compile the components, but I keep running into an issue with Postcss. A common issue I've noticed, where it's asking for v8 of PostCss (which it appears is present in node_modules), but it still complains. I did have a similar issue when using Webpack, but was eventually able to fix this. I've tried similar methods to fix for Rollup, but no luck.
package.json
{
"name": "#github_account/package_name",
"description": "Lorem ipsum",
"author": "github_account",
"version": "1.0.0",
"license": "ISC",
"repository": {
"type": "git",
"url": "repo_name_here"
},
"main": "./build/main.cjs.js",
"module": "./build/main.esm.js",
"browser": "./build/main.js",
"source": "./src/index.js",
"publishConfig": {
"registry": "https://npm.pkg.github.com/github_account"
},
"scripts": {
"build-rollup": "rollup -c",
"build": "webpack --mode production"
},
"dependencies": {
"#babel/runtime": "^7.17.8",
"autoprefixer": "^9.8.8",
"classnames": "^2.2.6",
"formik": "^2.2.6",
"iframe-resizer-react": "^1.1.0",
"luxon": "^2.0.2",
"prop-types": "^15.8.1",
"react": "^17.0.0",
"react-collapsible": "^2.8.4",
"react-device-detect": "^2.1.2",
"react-dom": "^17.0.0",
"react-google-recaptcha-v3": "^1.9.7",
"react-icons": "^3.10.0",
"react-markdown": "^6.0.3",
"react-modal": "^3.14.4",
"react-select": "^5.0.0",
"react-slick": "^0.28.1",
"react-table": "^7.7.0",
"sass": "^1.42.1",
"slick-carousel": "^1.8.1",
"yup": "^0.32.8"
},
"devDependencies": {
"#babel/core": "^7.18.6",
"#babel/plugin-syntax-jsx": "^7.18.6",
"#babel/plugin-transform-runtime": "^7.18.9",
"#babel/preset-env": "^7.18.6",
"#babel/preset-react": "^7.18.6",
"#rollup/plugin-babel": "^5.3.1",
"#rollup/plugin-commonjs": "^22.0.1",
"#rollup/plugin-node-resolve": "^13.3.0",
"#squoosh/lib": "^0.4.0",
"autoprefixer": "~9",
"babel-eslint": "^10.0.3",
"babel-loader": "^8.2.5",
"clean-webpack-plugin": "^4.0.0",
"cross-env": "^7.0.2",
"css-loader": "^6.7.1",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.7.0",
"eslint-config-standard": "^14.1.0",
"eslint-config-standard-react": "^9.2.0",
"eslint-plugin-import": "^2.18.2",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-prettier": "^3.1.1",
"eslint-plugin-promise": "^4.2.1",
"eslint-plugin-react": "^7.17.0",
"eslint-plugin-standard": "^4.0.1",
"image-minimizer-webpack-plugin": "^3.2.3",
"microbundle-crl": "^0.13.11",
"mini-css-extract-plugin": "^2.6.1",
"next": "^12.1.0",
"next-seo": "^4.28.1",
"npm-run-all": "^4.1.5",
"path": "^0.12.7",
"postcss": "8.4.6",
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-import": "^14.1.0",
"postcss-loader": "^4.3.0",
"postcss-nested": "^5.0.6",
"postcss-preset-env": "^6.7.1",
"prettier": "^2.3.1",
"react-scripts": "^3.4.1",
"rollup": "^2.77.0",
"rollup-plugin-filesize": "^9.1.2",
"rollup-plugin-image": "^1.0.2",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.2",
"rollup-plugin-replace": "^2.2.0",
"rollup-plugin-tailwindcss": "^1.0.0",
"sass-loader": "^12",
"tailwindcss": "^2.2.6",
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0",
"webpack-node-externals": "^3.0.0"
}
}
webpack.config.js
const path = require("path");
const webpack = require("webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const nodeExternals = require("webpack-node-externals");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const stylesHandler = MiniCssExtractPlugin.loader;
module.exports = {
devtool: "source-map",
context: __dirname,
entry: "./src/index.js",
externals: [nodeExternals({ importType: "umd" })],
output: {
filename: "[name].js",
path: path.resolve(__dirname, "./dist"),
libraryTarget: "umd",
library: {
name: "cruil",
type: "umd",
},
assetModuleFilename: (pathData) => {
const filepath = path.dirname(pathData.filename).split("/").slice(1).join("/");
return `${filepath}/[name].[hash][ext][query]`;
},
globalObject: "this",
},
experiments: {
outputModule: true,
},
optimization: {
runtimeChunk: "single",
minimizer: [
"...",
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.squooshMinify,
options: {
encodeOptions: {
pngquant: {
quality: 90,
},
},
},
},
}),
],
splitChunks: {
chunks: "all",
maxInitialRequests: Infinity,
minSize: 0,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name(module) {
// get the name. E.g. node_modules/packageName/not/this/part.js
// or node_modules/packageName
const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
// npm package names are URL-safe, but some servers don't like # symbols
return `npm.${packageName.replace("#", "")}`;
},
},
},
},
},
module: {
rules: [
{
test: /\.js$|jsx/,
include: path.resolve(__dirname, "./src"),
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env", "#babel/preset-react"],
},
},
},
{
test: /\.(s(a|c)ss)$/,
use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"],
include: path.resolve(__dirname, "./src"),
},
{
test: /\.css$/i,
use: [stylesHandler, "css-loader", "postcss-loader"],
},
{
test: /\.(jpe?g|png|gif|woff|woff2|eot|ttf|svg)(\?[a-z0-9=.]+)?$/,
include: path.resolve(__dirname, "./src/assets"),
type: "asset/resource",
},
],
},
plugins: [
new CleanWebpackPlugin(),
new MiniCssExtractPlugin(),
new webpack.DefinePlugin({
"process.env": {
// This has effect on the react lib size
NODE_ENV: JSON.stringify("production"),
},
}),
],
};
rollup.config.js
import babel from "#rollup/plugin-babel";
import resolve from "#rollup/plugin-node-resolve";
import commonjs from "#rollup/plugin-commonjs";
import postcss from "rollup-plugin-postcss";
import filesize from "rollup-plugin-filesize";
import autoprefixer from "autoprefixer";
import image from "rollup-plugin-image";
import external from "rollup-plugin-peer-deps-external";
import replace from "rollup-plugin-replace";
import tailwind from "rollup-plugin-tailwindcss";
import pkg from "./package.json";
const INPUT_FILE_PATH = "src/index.js";
const OUTPUT_NAME = "main";
const GLOBALS = {
react: "React",
"react-dom": "ReactDOM",
"prop-types": "PropTypes",
};
const getBabelOptions = ({ useESModules }) => ({
babelrc: false,
exclude: "node_modules/**",
extensions: [".js", ".ts", ".jsx", ".tsx"],
babelHelpers: "runtime",
presets: [["#babel/preset-env", { modules: false }], "#babel/preset-react"],
plugins: [["#babel/transform-runtime", { regenerator: false, useESModules }], "#babel/plugin-syntax-jsx"],
});
const PLUGINS = [
external({
includeDependencies: true,
}),
image(),
tailwind({
input: "./src/styles/index.css",
purge: false,
}),
postcss({
extract: true,
modules: true,
plugins: [autoprefixer],
}),
resolve({
browser: true,
resolveOnly: [/^(?!react$)/, /^(?!react-dom$)/, /^(?!prop-types)/],
preferBuiltins: false,
}),
commonjs({
include: /node_modules/,
}),
replace({ "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV) }),
babel(getBabelOptions({ useESModules: true })),
filesize(),
];
const EXTERNAL = ["react", "react-dom", "prop-types"];
// https://github.com/rollup/plugins/tree/master/packages/babel#babelhelpers
const CJS_AND_ES_EXTERNALS = EXTERNAL.concat(/#babel\/runtime/);
const OUTPUT_DATA = [
{
file: pkg.browser,
format: "umd",
},
{
file: pkg.main,
format: "cjs",
},
{
file: pkg.module,
format: "es",
},
];
const config = OUTPUT_DATA.map(({ file, format }) => ({
input: INPUT_FILE_PATH,
output: {
file,
format,
name: OUTPUT_NAME,
globals: GLOBALS,
},
external: ["cjs", "es"].includes(format) ? CJS_AND_ES_EXTERNALS : EXTERNAL,
plugins: PLUGINS,
}));
export default config;
postcss.config.js
module.exports = {
plugins: [
require("postcss-import"),
require("tailwindcss"),
require("postcss-flexbugs-fixes"),
require("postcss-nesting"),
require("postcss-custom-properties"),
require("autoprefixer"),
],
};
index.js
import "./styles/index.css";
import ExampleTestComponent from "./components/ExampleTestComponent";
...
export {
ExampleTestComponent,
...
}
ExampleTestComponent.js
import React from "react";
const ExampleTestComponent = (props) => {
return (
<div className="ExampleTestComponentClass">
--- This is a test ---
</div>
);
};
export default ExampleTestComponent;
index.css
#tailwind base;
#tailwind components;
#tailwind utilities;
Site Implementation
import ExampleTestComponent from "#github_account/package_name";
const HomePageTemplate = ({ heroItems, partners, newsItems, videos }) => {
const partnerItems = partners.map((partner) => ({
image: partner.logo,
url: partner.url,
}));
return (
<HomePageLayout heroItems={heroItems} partnerItems={partnerItems}>
<ExampleTestComponent />
<SuccessStoriesSlider videos={videos} />
<NewsBlock
newsItems={newsItems}
title="News"
subtitle="Latest news posts"
viewMoreBtnLabel="View Posts"
readMoreBtnLabel="Read More"
/>
</HomePageLayout>
);
};
export default HomePageTemplate;
UPDATE 1
Turns out a combination of using Yarn and adding the below
{
"resolutions": {
"postcss": "8"
}
}
to package.json, fixes the Postcss issue. So now I have the package created, I am attempting to import into the main site. However I'm now getting the error:
Error: Must use import to load ES Module: /node_modules/#github_account/package_name/node_modules/#babel/runtime/helpers/esm/defineProperty.js
require() of ES modules is not supported.
require() of /node_modules/#github_account/package_name/node_modules/#babel/runtime/helpers/esm/defineProperty.js from /node_modules/#github_account/package_name/build/main.cjs.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename defineProperty.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /node_modules/#github_account/package_name/node_modules/#babel/runtime/helpers/esm/package.json.
I am having difficulty with resolving webpack deployment issue.
In development mode everything seems fine, but as I deployed this project through vercel, I am only getting blank screen. When I checked the page, nothing but index.html existed. (no other js files)
These are my files below. I really want to know what on earth the problem is.
webpack.config.js
const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ReactRefreshWebpackPlugin = require('#pmmmwh/react-refresh-webpack-plugin');
const { ESBuildMinifyPlugin } = require('esbuild-loader');
const isDevelopment = process.env.NODE_ENV !== 'production';
module.exports = () => ({
mode: isDevelopment ? 'development' : 'production',
devtool: isDevelopment ? 'eval-cheap-module-source-map' : false,
cache: { type: isDevelopment ? 'memory' : 'filesystem' },
entry: './src/index.tsx',
target: 'web',
output: {
filename: 'js/[name]-[chunkhash].js',
assetModuleFilename: 'img/[hash][ext][query]',
pathinfo: false,
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
clean: true,
},
resolve: {
alias: {
icons: path.resolve(__dirname, 'public/assets/icons'),
images: path.resolve(__dirname, 'public/assets/images'),
components: path.resolve(__dirname, 'src/components'),
data: path.resolve(__dirname, 'src/data'),
docs: path.resolve(__dirname, 'src/docs'),
pages: path.resolve(__dirname, 'src/pages'),
styles: path.resolve(__dirname, 'src/styles'),
types: path.resolve(__dirname, 'src/types'),
utils: path.resolve(__dirname, 'src/utils'),
},
extensions: ['*', '.js', '.tsx', '.ts'],
modules: [__dirname, 'src', 'node_modules'],
fallback: {
process: require.resolve('process/browser'),
zlib: require.resolve('browserify-zlib'),
stream: require.resolve('stream-browserify'),
util: require.resolve('util'),
buffer: require.resolve('buffer'),
assert: require.resolve('assert'),
},
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'esbuild-loader',
options: {
loader: 'tsx',
target: 'esnext',
},
},
{
test: /\.(png|jpg|svg|gif)$/,
type: 'asset/resource',
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(ts|tsx)$/,
exclude: '/node_modules',
use: 'ts-loader',
},
{
test: /restructure(\/|\\)src(\/|\\)[\w]+.js/,
use: [
{
loader: 'imports-loader',
options: {
type: 'commonjs',
imports: 'multiple ../../buffer/ Buffer Buffer',
},
},
],
},
],
},
plugins: [
new HtmlWebpackPlugin({ template: './public/index.html' }),
new webpack.ProvidePlugin({
Buffer: ['buffer', 'Buffer'],
process: 'process/browser',
}),
new webpack.EnvironmentPlugin({
DEVELOPMENT: isDevelopment,
}),
isDevelopment && new webpack.HotModuleReplacementPlugin(),
isDevelopment && new ReactRefreshWebpackPlugin(),
],
optimization: {
minimize: !isDevelopment,
minimizer: [
new ESBuildMinifyPlugin({
target: 'esnext',
css: true,
}),
],
splitChunks: {
chunks: 'all',
},
},
externals: {
axios: 'axios',
},
devServer: {
port: 3000,
open: true,
hot: true,
historyApiFallback: true,
},
});
package.json
{
"name": "webpack",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"engines": {
"node": ">=16.15.0",
"yarn": ">=1.22.17"
},
"scripts": {
"build": "webpack",
"dev": "NODE_ENV=development & webpack serve --progress",
"build:dev": "NODE_ENV=development & yarn build",
"build:prod": "NODE_ENV=production & yarn build"
},
"keywords": [],
"devDependencies": {
"#pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
"#react-pdf/types": "^2.1.0",
"#types/react": "^18.0.12",
"#types/react-dom": "^18.0.5",
"#types/shortid": "^0.0.29",
"#types/styled-components": "^5.1.25",
"#typescript-eslint/eslint-plugin": "^5.27.1",
"#typescript-eslint/parser": "^5.27.1",
"css-loader": "^6.7.1",
"esbuild-loader": "^2.19.0",
"eslint": "^8.17.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.30.0",
"eslint-plugin-react-hooks": "^4.5.0",
"eslint-plugin-simple-import-sort": "^7.0.0",
"html-webpack-plugin": "^5.5.0",
"imports-loader": "^4.0.0",
"lodash": "^4.17.21",
"prettier": "^2.6.2",
"react-refresh": "^0.13.0",
"style-loader": "^3.3.1",
"ts-loader": "^9.3.0",
"typescript": "^4.7.3",
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.9.2"
},
"dependencies": {
"#react-pdf/renderer": "^2.2.0",
"assert": "^2.0.0",
"axios": "^0.27.2",
"browserify-zlib": "^0.2.0",
"buffer": "^6.0.3",
"node-env": "^0.1.6",
"process": "^0.11.10",
"react": "^18.1.0",
"react-daum-postcode": "^3.1.1",
"react-dom": "^18.1.0",
"react-router-dom": "^6.3.0",
"shortid": "^2.2.16",
"stream-browserify": "^3.0.0",
"styled-components": "^5.3.5",
"styled-reset": "^4.4.1",
"util": "^0.12.4"
}
}
index.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import GlobalFont from 'styles/globalFont';
import GlobalStyle from 'styles/globalStyle';
import App from './App';
const rootEl = document.getElementById('root');
if (!rootEl) throw new Error('Failed to find the root element');
const root = createRoot(rootEl);
root.render(
<React.StrictMode>
<GlobalFont />
<GlobalStyle />
<App />
</React.StrictMode>,
);
App.tsx
import Home from 'pages/Home';
import PdfViewer from 'pages/PdfViewer';
import Test from 'pages/Test';
import React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/docs" element={<PdfViewer />} />
<Route path="/test" element={<Test />} />
</Routes>
</BrowserRouter>
);
}
export default App;
Struggling to find a solution to this. I fear it's something to do with versions. However i am not excited about the prospect of downgrading, but downgrade what?
Any help would be appreciated. Please ask for any extra information.. Was not sure what else or what to show.
After running webpack;
TS2339: Property 'TabPane' does not exist on type '(...args: any[]) =>
any'.
Code main.tsx
import * as React from "react";
import { Tabs, Button, Icon } from 'antd';
const TabPane = Tabs.TabPane;
export class Main extends React.Component
{
render()
{
return (
<div className="main-container">
<div>
<Tabs>
<TabPane key="1" tab={<span><Icon type="android" />Tab1</span>}>
<div style={{ padding: '5px' }}>
Tab1
</div>
</TabPane>
<TabPane key="2" tab={<span><Icon type="android" />Tab2 </span>}>
</TabPane>
</Tabs>
</div>
</div>
);
}
}
Webpack.config.js
var createVendorChunk = require('webpack-create-vendor-chunk');
var webpack = require('webpack');
var path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const nodeExternals = require('webpack-node-externals');
module.exports = {
mode: 'development',
entry: {
ui: './src/index.tsx'
},
externals: {
"react": "React",
"react-dom": "ReactDOM",
},
devtool: 'source-map',
resolve: {
extensions: [".ts", ".tsx", ".js"]
},
output: {
path: path.resolve(__dirname, "chrome-extension/build/"),
publicPath: "build",
filename: "[name].bundle.js",
chunkFilename: '[name].bundle.js',
},
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
enforce: true,
chunks: 'all'
},
}
},
runtimeChunk: true
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
],
target: 'node', // in order to ignore built-in modules like path, fs, etc.
externals: [nodeExternals()], // in order to ignore all modules in node_modules folder
module: {
rules: [
{
enforce: "pre",
test: /\.js$/,
loader: "source-map-loader",
},
{
test: /\.tsx?$/,
loader: "ts-loader",
exclude: /node_modules/
},
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, "css-loader"]
},
{
test: /\.less$/,
use: [{
loader: "style-loader"
}, {
loader: "css-loader"
}, {
loader: "less-loader",
options: {
javascriptEnabled: true
}
}]
}
],
}
};
package.json
{
"name": "test",
"version": "0.0.0",
"description": "",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack",
"dev": "webpack --watch"
},
"author": "Elgan",
"license": "MIT",
"devDependencies": {
"#babel/core": "^7.0.1",
"#babel/preset-env": "^7.0.0",
"#babel/preset-react": "^7.0.0",
"awesome-typescript-loader": "^5.2.1",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-loader": "^8.0.2",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.11.1",
"css-loader": "^1.0.0",
"file-loader": "^2.0.0",
"less": "^3.8.1",
"less-loader": "^4.1.0",
"mini-css-extract-plugin": "^0.4.2",
"node-sass": "^4.9.3",
"sass-loader": "^7.1.0",
"source-map-loader": "^0.2.4",
"style-loader": "^0.23.0",
"typescript": "^3.0.3",
"webpack": "^4.19.0",
"webpack-cli": "^3.1.0",
"webpack-create-vendor-chunk": "^0.1.1",
"webpack-node-externals": "^1.7.2"
},
"dependencies": {
"#types/react": "^16.4.14",
"#types/react-dom": "^16.0.7",
"antd": "^3.9.2",
"babel": "^6.23.0",
"babel-plugin-import": "^1.9.1",
"babel-preset-env": "^1.7.0",
"classnames": "^2.2.5",
"install": "^0.12.1",
"lodash": "^4.17.11",
"npm": "^6.4.1",
"prop-types": "^15.6.2",
"react": "^16.5.1",
"react-dom": "^16.5.1",
"ts-loader": "^5.1.1"
}
}
Changing to awesome-typescript-loader seemed to help. Though not sure why, so elaboration would be useful.
I changed the TS loader from ts-loader to awesome-typescript-loader in the webpack.config.js. Then i changed the TSConfig module to commonjs. It compiled now.
webpack.config.js
{
test: /\.tsx?$/,
loader: "awesome-typescript-loader",
exclude: /node_modules/
},
tsconfig.ts
{
"compilerOptions": {
"outDir": "./dist/", // path to output directory
"sourceMap": true, // allow sourcemap support
"strictNullChecks": true, // enable strict null checks as a best practice
"module": "commonjs", // specify module code generation
"jsx": "react", // use typescript to transpile jsx to js
"target": "es5", // specify ECMAScript target version
"allowJs": true // allow a partial TypeScript and JavaScript codebase
},
"exclude": [
"node_modules"
],
}
I'm trying to get up and running my app using webpack-dev-middleware, webpack-hot-middle-ware, express and react. But when I startup my code, system doesn't give me [hash].hot-update.json file. When I change something in my code system rebuilds and give such file but with wrong hash in file name. Here is my code:
entry point for development server:
require('ignore-styles').default(undefined, (module, filename) => {
if ((['.png', '.jpg'].some(ext => filename.endsWith(ext)) )) {
module.exports = '/build/' + path.basename(filename);
}
});
require('babel-register')({
ignore: [ /(node_modules)/ ],
presets: ["env", "stage-0", "react"],
plugins: [
'dynamic-import-node',
'syntax-dynamic-import',
'react-loadable/babel'
]
});
const express = require('express');
const path = require('path');
const webpack = require('webpack');
const Loadable = require('react-loadable');
const webpackDevMiddleware = require('webpack-dev-middleware');
const serverRenderer = require('./server').default;
const config = require('../configs/webpack.common.js');
const compiler = webpack(config);
const app = express();
app.use(webpackDevMiddleware(compiler, { serverSideRender: true, publicPath: '/build/', }));
app.use(require("webpack-hot-middleware")(compiler));
app.use(serverRenderer());
Loadable.preloadAll().then(() => {
app.listen(3000, () => console.log('Development server is running on port 3000'));
}).catch(err => {
console.log(err);
});
My webpack.config.js file:
module.exports = [{
name: 'client',
mode: 'development',
target: 'web',
entry: ['webpack-hot-middleware/client', './app/index.js'],
output: {
path: path.join(__dirname, '../build'),
filename: 'client.js',
publicPath: '/build/',
hotUpdateChunkFilename: '[id].[hash].hot-update.js',
hotUpdateMainFilename: '[hash].hot-update.json',
},
devtool: 'source-map',
plugins: [
new ManifestPlugin({
writeToFileEmit: true,
}),
new WriteFilePlugin(),
new webpack.HotModuleReplacementPlugin(),
],
module: {
rules: [
{
test: /\.js?$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader']
},
{
test: /\.(png|jpg|gif|ico|jpeg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
}
}
]
}
],
},
},
{
name: 'server',
mode: 'development',
target: 'node',
entry: './server/server.js',
externals: [nodeExternals()],
output: {
path: path.join(__dirname, '../build'),
filename: 'server.js',
libraryTarget: 'commonjs2',
publicPath: '/build/',
},
plugins: [
new WriteFilePlugin(),
],
module: {
rules: [
{
test: /\.js?$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.scss$/,
use: [
{
loader: 'style-loader',
},
{
loader: 'css-loader'
},
{
loader: 'sass-loader'
}
]
},
{
test: /\.(png|jpg|gif|ico|jpeg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
emitFile: false
}
}
]
}
]
}
}];
My package.json file:
{
"name": "xxx",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node server/development.js",
"build": "rimraf build && NODE_ENV=production webpack --config configs/webpack.production.js --progress --profile --colors",
"prod": "node server/production.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.4",
"babel-plugin-dynamic-import-node": "^1.2.0",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-preset-env": "^1.6.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"babel-register": "^6.26.0",
"css-loader": "^0.28.11",
"express": "^4.16.3",
"file-loader": "^1.1.11",
"ignore-styles": "^5.0.1",
"mini-css-extract-plugin": "^0.4.0",
"node-sass": "^4.9.0",
"react-hot-loader": "^4.1.2",
"rimraf": "^2.6.2",
"sass-loader": "^7.0.1",
"serialize-javascript": "^1.5.0",
"style-loader": "^0.21.0",
"uglifyjs-webpack-plugin": "^1.2.5",
"webpack": "^4.6.0",
"webpack-cli": "^2.0.15",
"webpack-dev-middleware": "^3.1.3",
"webpack-hot-middleware": "^2.22.1",
"webpack-manifest-plugin": "^2.0.2",
"webpack-merge": "^4.1.2",
"webpack-node-externals": "^1.7.2",
"write-file-webpack-plugin": "^4.3.2"
},
"dependencies": {
"react": "^16.4.0",
"react-dom": "^16.4.0",
"react-helmet": "^5.2.0",
"react-loadable": "^5.3.1",
"react-redux": "^5.0.7",
"react-router-config": "^1.0.0-beta.4",
"react-router-dom": "^4.2.2",
"redux": "^4.0.0",
"redux-thunk": "^2.2.0"
}
}
and my server.js file:
import React from 'react'
import { renderToString } from 'react-dom/server';
import { StaticRouter } from 'react-router-dom';
import { renderRoutes } from 'react-router-config';
import { Provider } from 'react-redux'
import { Helmet } from 'react-helmet';
import Loadable from 'react-loadable';
import serialize from 'serialize-javascript';
import configureStore from '../app/store';
import routes from '../app/routes';
import template from './template';
const store = configureStore();
const normalizeAssets = (assets) => {
return Array.isArray(assets) ? assets : [assets]
};
const searchModuleFiles = (modules, assets) => {
return modules.map(module => {
let files = [`${module}.js`, `${module}.css`];
return files.map(file => ({ file: assets[file] }));
})
};
export default (stats = null) => (req, res) => {
const context = {};
const modules = [];
let devStats = [];
let mainScript = null;
const content = renderToString(
<Provider store={store}>
<StaticRouter context={context} location={req.url}>
<Loadable.Capture report={moduleName => modules.push(moduleName)}>
{renderRoutes(routes)}
</Loadable.Capture>
</StaticRouter>
</Provider>
);
if(!stats) {
let chunks = res.locals.webpackStats.toJson().children.filter(item => item.name === 'client')[0].assetsByChunkName;
devStats = normalizeAssets(modules.map(module => chunks[module])[0]).map(item => ({ file: `/build/${item}` }));
mainScript = { file: '/build/client.js' };
} else {
mainScript = { file: stats['main.js'] };
}
let bundles = stats ? searchModuleFiles(modules, stats)[0] : devStats;
let scripts = bundles.filter(bundle => bundle.file.endsWith('.js'));
let styles = bundles.filter(bundle => bundle.file.endsWith('.css'));
const helmet = Helmet.renderStatic();
const data = serialize(store.getState());
res.status(200).send(template({ content, data, mainScript, scripts, styles, helmet }));
}
Also any help about server side rendering wanted.
The line import * as Styles from './ButtonStyle.css'; is throwing the error [ts] Cannot find module './ButtonStyle.css'
My component:
require('./ButtonStyle.css');
import * as React from 'react';
import * as Styles from './ButtonStyle.css';
export interface IState{
type: string;
className: string;
caption: string;
}
export class Button extends React.Component<{}, {}>{
render(){
return(
<button
type="button"
className="btn btn-primary"
>Work In Progress</button>
);
}
}
package.json
{
"name": "src",
"private": true,
"version": "0.0.0",
"devDependencies": {
"#types/history": "4.6.0",
"#types/react": "15.0.35",
"#types/react-dom": "15.5.1",
"#types/react-hot-loader": "3.0.3",
"#types/react-router": "4.0.12",
"#types/react-router-dom": "4.0.5",
"#types/webpack-env": "1.13.0",
"aspnet-webpack": "^2.0.1",
"aspnet-webpack-react": "^3.0.0",
"awesome-typescript-loader": "3.2.1",
"bootstrap": "3.3.7",
"css-loader": "0.28.4",
"event-source-polyfill": "0.0.9",
"extract-text-webpack-plugin": "2.1.2",
"file-loader": "0.11.2",
"isomorphic-fetch": "2.2.1",
"jquery": "3.2.1",
"json-loader": "0.5.4",
"react": "15.6.1",
"react-dom": "15.6.1",
"react-hot-loader": "3.0.0-beta.7",
"react-router-dom": "4.1.1",
"style-loader": "0.18.2",
"typescript": "2.4.1",
"url-loader": "0.5.9",
"webpack": "2.5.1",
"webpack-hot-middleware": "2.18.2"
},
"dependencies": {
"#types/react-virtualized-select": "^3.0.2",
"react-virtualized-select": "^3.1.1"
}
}
webpack config:
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
const bundleOutputDir = './wwwroot/dist';
module.exports = (env) => {
const isDevBuild = !(env && env.prod);
return [{
stats: { modules: false },
entry: { 'main': './ClientApp/boot.tsx' },
resolve: { extensions: ['.js', '.jsx', '.ts', '.tsx'] },
output: {
path: path.join(__dirname, bundleOutputDir),
filename: '[name].js',
publicPath: 'dist/'
},
module: {
rules: [
{ test: /\.tsx?$/, include: /ClientApp/, use: 'awesome-typescript-loader?silent=true' },
{ test: /\.css$/, use: isDevBuild ? ['style-loader', 'css-loader'] : ExtractTextPlugin.extract({ use: 'css-loader?minimize' }) },
{ test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' }
]
},
plugins: [
new CheckerPlugin(),
new webpack.DllReferencePlugin({
context: __dirname,
manifest: require('./wwwroot/dist/vendor-manifest.json')
})
].concat(isDevBuild ? [
// Plugins that apply in development builds only
new webpack.SourceMapDevToolPlugin({
filename: '[file].map', // Remove this line if you prefer inline source maps
moduleFilenameTemplate: path.relative(bundleOutputDir, '[resourcePath]') // Point sourcemap entries to the original file locations on disk
})
] : [
// Plugins that apply in production builds only
new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin('site.css')
])
}];
};