How to build webpack on heroku? - heroku

What is the best way to trigger Webpack build after deploying to Heroku?
Push already bundled version in not the most beautiful solution.

What kind of application is this? If you are using a package.json, you could run webpack in the postinstall step using npm scripts.

I have solved this issue by placing devDependencies in normal dependencies, and I changed the postinstall script to:
node_modules/.bin/webpack

You can set postinstall in your package.json to the following NODE_ENV=production webpack -p
Then set start to node
But you will need to make sure to config your webpack for production either by setting it within your webpack.config.js or webpack.config.js(production) as a production config.
I set everything within my webpack.config.js as follows..
const path = require('path');
const webpack = require('webpack');
const debug = process.env.NODE_ENV !== "production";
module.exports = {
entry: [
'./src/index.js'
],
output: {
path: path.resolve(__dirname, 'src'),
filename: 'bundle.js'
},
devtool: debug ? "inline-sourcemap" : null,
module: {
loader: [{
exclude: /node_modules/,
loader: 'babel',
query: {
presets: ['angular']
}
},
{
test: /\.css$/,
loader: "style-loader!css-loader"
}]
},
devServer: {
historyApiFallback: true,
contentBase: 'src'
},
plugins: debug ? [] : [
new webpack.optimize.DedupePlugin(),
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin({
mangle: {except: ['$', 'exports', 'require', 'app']},
compress: {warnings: false},
sourceMap: false
})
]
}
so basically, once the command runs npm run postinstall bundle will be generated in the directory as per webpack.config.js (output). But remember to include in your package.json with the commands NODE_ENV=production webpack -p before running 'npm start'. See example below..
{
"name": "",
"version": "1.0.0",
"description": "",
"main": "./src/bundle.js",
"engines": {
"node": "6.4.0"
},
"scripts": {
"start": "node ./src/server.js",
"postinstall": "NODE_ENV=production webpack -p"
},
"author": "",
"license": "ISC",
"dependencies": ...

Related

Getting error : Support for the experimental syntax 'jsx' isn't currently enabled . When trying to add animation in react-native-web project

In my project, I'm using the reanimate library to add animations, but I'm receiving an error
https://stackoverflow.com/questions/63005011/support-for-the-experimental-syntax-jsx-isnt-currently-enabled
I follow all the steps mention in the link but none of them work for me
My package.json file
{
"name": "animation",
"version": "0.1.0",
"private": true,
"dependencies": {
"#react-spring/native": "^9.6.1",
"#react-spring/web": "^9.6.1",
"#testing-library/jest-dom": "^5.16.5",
"#testing-library/react": "^13.4.0",
"#testing-library/user-event": "^13.5.0",
"babel-polyfill": "^6.26.0",
"lodash": "^4.17.21",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-native-reanimated": "^2.14.4",
"react-native-web": "^0.18.10",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"#babel/plugin-syntax-jsx": "^7.18.6",
"#babel/preset-env": "^7.20.2",
"#babel/preset-react": "^7.18.6",
"babel-loader": "^9.1.2",
"url-loader": "^4.1.1",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.11.1"
}
}
webpack.config.js
// web/webpack.config.js
const path = require("path");
const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const appDirectory = path.resolve(__dirname, "../");
// This is needed for webpack to compile JavaScript.
// Many OSS React Native packages are not compiled to ES5 before being
// published. If you depend on uncompiled packages they may cause webpack build
// errors. To fix this webpack can be configured to compile to the necessary
// `node_module`.
const babelLoaderConfiguration = {
test: /\.js$/,
// Add every directory that needs to be compiled by Babel during the build.
include: [
path.resolve(appDirectory, "index.web.js"),
path.resolve(appDirectory, "src"),
path.resolve(appDirectory, "node_modules/react-native-uncompiled"),
],
use: {
loader: "babel-loader",
options: {
cacheDirectory: true,
// The 'metro-react-native-babel-preset' preset is recommended to match React Native's packager
presets: [
"#babel/preset-react",
"module:metro-react-native-babel-preset",
],
// Re-write paths to import only the modules needed by the app
plugins: [
"#babel/plugin-syntax-jsx",
"react-native-web",
"react-native-reanimated/plugin",
],
},
},
};
// This is needed for webpack to import static images in JavaScript files.
const imageLoaderConfiguration = {
test: /\.(gif|jpe?g|png|svg)$/,
use: {
loader: "url-loader",
options: {
name: "[name].[ext]",
esModule: false,
},
},
};
module.exports = {
entry: [
// load any web API polyfills
path.resolve(appDirectory, "polyfills-web.js"),
// your web-specific entry file
path.resolve(appDirectory, "index.web.js"),
],
// configures where the build ends up
output: {
filename: "bundle.web.js",
path: path.resolve(appDirectory, "dist"),
},
// ...the rest of your config
plugins: [
new HtmlWebpackPlugin({
filename: "index.html",
template: "./index.html",
}),
new webpack.EnvironmentPlugin({ JEST_WORKER_ID: null }),
new webpack.DefinePlugin({ process: { env: {} } }),
],
module: {
rules: [babelLoaderConfiguration, imageLoaderConfiguration],
},
resolve: {
// This will only alias the exact import "react-native"
alias: {
"react-native$": "react-native-web",
},
// If you're working on a multi-platform React Native app, web-specific
// module implementations should be written in files using the extension
// `.web.js`.
extensions: [".web.js", ".js"],
},
};

npm run watch/hot only successful on the first run

Background:
I added TypeScript support to my existing project, so I added ts-loader and typescript. I think, I configured everything right and it is working fine in dev and prod mode.
I would like to update gradually, keeping all the JavaScript code in place and using TypeScript for everything new or where there is a need for refactoring. So it may be important to note that TableValue.vue is an old js component.
Problem:
Edit: It also occurs with npm run watch
When I run npm run hot in package.json: "scripts": { ..., "hot": "mix watch --hot", ...} it only works on the first try. As soon as I change any file and trigger a recompile, I get:
√ Mix: Compiled successfully in 19.15s
webpack compiled successfully
// Here the recompile is triggered
i Compiling Mix
√ Mix: Compiled with some errors in 509.01ms
ERROR in C:\fakepath\resources\js\components\test\component.vue.ts
24:23-41
[tsl] ERROR in C:\fakepath\resources\js\components\test\component.vue.ts(24,24)
TS2307: Cannot find module './TableValue.vue' or its corresponding type declarations.
webpack compiled with 1 error
I suspect that this error comes from ts-loader, but why is everything working on the first try?
I could just ignore this error, but then hot module replacement is unusable, because I have to manually trigger a new build process every time anyway.
Has someone got such an setup working?
What can I do to solve this error?
Infos:
I'm working with:
Laravel 8.58
Laravel Mix 6.0.25
Vue 2.6.14
ts-loader 9.2.5
typescript 4.4.2
Here the script tag from the test component:
<script lang="ts">
import Vue, { PropType } from 'vue';
import TableValue from "./TableValue.vue";
import Model from "#/js/types/model.js";
export default Vue.extend({
name: "TestComponent",
components: {
TableValue
},
props: {
'model': {
type: Object as PropType<Model>,
required: true
}
},
data() {
return {};
},
});
</script>
Project Structure:
app/
bootstrap/
config/
database/
node_modules/
public/
resources/
js/
components/
store/
types/
views/
app.js
bootstrap.js
routes.js
shims-vue.d.ts
lang/
sass/
views/
routes/
storage/
tests/
vendor/
composer.json
composer.lock
tsconfig.json
package-lock.json
package.json
phpunit.xml
vs.code-workspace
webpack.mix.js
webpack.mix.js:
const mix = require('laravel-mix');
const ResolveTypeScriptPlugin = require("resolve-typescript-plugin").default;
mix.webpackConfig({
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
options: { appendTsSuffixTo: [/\.vue$/] },
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.js', '.ts', '.vue'],
alias: {
'#': __dirname + '/resources'
},
fullySpecified: false,
plugins: [new ResolveTypeScriptPlugin()]
},
devtool: 'source-map'
}).sourceMaps();
mix.ts('resources/js/app.js', 'public/js')
.sass('resources/sass/app.sass', 'public/css').sourceMaps()
.vue();
mix.extract();
tsconfig.json:
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"noImplicitAny": false,
"importHelpers": true,
"moduleResolution": "node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"allowJs": true,
"checkJs": false,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"#/*": [
"resources/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"files": [
"resources/js/shims-vue.d.ts"
],
"include": [
"resources/js/**/*.ts",
"resources/js/**/*.vue",
],
"exclude": [
"node_modules",
".vscode",
"app",
"bootstrap",
"config",
"database",
"public",
"routes",
"storage",
"tests",
"vendor"
]
}
Update:
When I remove shims-vue.d.ts, I get the error immediately.
declare module "*.vue" {
import Vue from "vue";
export default Vue;
}
It looks like this file is only read/applyed once and not after? Not sure.
It looks like ts-loader doesn't support HMR yet.
https://github.com/TypeStrong/ts-loader#hot-module-replacement
I installed fork-ts-checker-webpack-plugin and updated webpack.mix.js to:
const mix = require('laravel-mix');
const path = require('path');
const ResolveTypeScriptPlugin = require("resolve-typescript-plugin").default;
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
mix.webpackConfig({
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
options: {
appendTsSuffixTo: [/\.vue$/],
transpileOnly: true
},
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.js', '.ts', '.tsx', '.vue'],
alias: {
'#': path.resolve(__dirname + '/resources'),
'#store': path.resolve(__dirname + '/resources/js/store'),
'#components': path.resolve(__dirname + '/resources/js/components')
},
fullySpecified: false,
plugins: [new ResolveTypeScriptPlugin()]
},
plugins: [new ForkTsCheckerWebpackPlugin()],
devtool: 'source-map'
}).sourceMaps();
mix.ts('resources/js/app.js', 'public/js')
.sass('resources/sass/app.sass', 'public/css').sourceMaps()
.vue();
mix.extract();
Now everything is working fine but I'm still not sure why watch was also affected and where exactly the problem was.
I'm not using Typescript, but the same thing was happening to me, when i ran npm run watch/hot where only successful on the first change of the code, then, you can not see the changes until you run npm run watch/hot or npm run dev again. The strange thing was that everything was compiling successfully on every change I made.
I manage to debug it with git, and found out that I was importing a component with a wrong name but did not get an error on the console.
My component name was WhosApplying.vue
I got:
import WhosApplying from "#/whosApplying.vue"
And change it for:
import WhosApplying from "#/WhosApplying.vue";
That mistake in the w instead of W make me lose hours. 😅
I ran into this using laravel-mix after adding typescript to an Inertia / Vue 3 project.
Using Volar (Not Vuter)
To fix it I changed my webpack.config.js file from:
const path = require('path');
module.exports = {
resolve: {
alias: {
'#': path.resolve('resources/js'),
},
},
};
to:
const path = require('path');
module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
loader: "ts-loader",
options: {
appendTsSuffixTo: [/\.vue$/],
transpileOnly: true
},
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.js', '.ts', '.tsx', '.vue'],
alias: {
'#': path.resolve(__dirname + '/resources/js'),
},
},
};

Async await doesn't get compiled in Grunt

I am trying to compile my ES6+ code to vanilla js using Grunt task runner.
I have purposely chosen Grunt over webpack and gulp because I just wanted to minify my js files.
I have successfully compiled my ES6 code to vanilla after running the code got an error saying generatorRuntime is not defined. After analysing the issue I could that my async and await code is giving the issue after it gets converted to vanilla js.
I have my code snippet of my gruntfile.js and package.json.
babel: {
options: {
sourceMap: true
},
dist: {
files: [{
"expand": true,
"cwd": "./htdocs/js/src",
"src": ["**/*.js"],
"dest": "./htdocs/js/compiled/",
"ext": ".js"
}]
}
},
//uglify will minify all the js files in js/src folder.
uglify: {
all_src : {
options : {
sourceMap : true
},
files: [{
expand: true,
flatten: true,
cwd:'./htdocs/js/compiled',
src: '**/*.js',
dest: './htdocs/js/dist',
ext: '.min.js'
}]
}
}
grunt.loadNpmTasks('grunt-babel');
grunt.loadNpmTasks('grunt-contrib-uglify');
Package.json
"devDependencies": {
"babel-core": "^6.26.3",
"babel-preset-latest": "^6.24.1",
"grunt": "^1.1.0",
"grunt-babel": "^7.0.0",
"grunt-cli": "^1.3.2",
"grunt-contrib-uglify": "^4.0.1"
},
"babel": {
"presets": [
"latest"
]
}
That's probably because the polyfills aren't getting shipped in your bundle. In your babel.options object inside Gruntfile, you can set
presets: [['#babel/preset-env', { useBuiltIns: 'usage', corejs: 3 }]]
and don't forget to include corejs as dependency in your project.
npm install core-js --save

How to insert image files in Electron + React.js + Webpack desktop app package for Mac OS

I am building a desktop application for Mac OS with Electron + React + Webpack mode.
When I build a Electron desktop app as production version, I would like to insert some image files in the setup file(Test.app and Test.dmg) and use the image as following:
let Logo = process.resourcesPath + '/logo.png';
class Loading extends Component {
render() {
return (
<div className={styles.container}>
<img className={styles.logo} src={Logo} />
...
</div>
);
}
}
But this app can't get the image because the logo.png file didn't be include in Test.app/Content/Resources/ directory when I run 'npm run package' command and build a Test.app for Mac OS.
Here, the issue is as following:
GET file:///Applications/Test.app/Contents/Resources/logo.png
net::ERR_FILE_NOT_FOUND
I would like to insert this image file in my Test.app package for Mac OS.
Please help me how to insert some image files into Test.app package for a Mac OS desktop application.
webpack.config.base.js
import path from 'path';
import webpack from 'webpack';
import { dependencies as externals } from './app/package.json';
export default {
externals: Object.keys(externals || {}),
module: {
rules: [{
test: /\.jsx?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
cacheDirectory: true
}
}
}]
},
output: {
path: path.join(__dirname, 'app'),
filename: 'bundle.js',
// https://github.com/webpack/webpack/issues/1114
libraryTarget: 'commonjs2'
},
resolve: {
extensions: ['.js', '.jsx', '.json'],
modules: [
path.join(__dirname, 'app'),
'node_modules',
],
},
plugins: [
new webpack.NamedModulesPlugin(),
],
};
webpack.config.production.js
import path from 'path';
import webpack from 'webpack';
import validate from 'webpack-validator';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import merge from 'webpack-merge';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import BabiliPlugin from 'babili-webpack-plugin';
import baseConfig from './webpack.config.base';
export default validate(merge(baseConfig, {
devtool: 'cheap-module-source-map',
entry: './app/index',
output: {
publicPath: '../dist/'
},
module: {
loaders: [
// Extract all .global.css to style.css as is
{
test: /\.global\.css$/,
loader: ExtractTextPlugin.extract(
'style-loader',
'css-loader'
)
},
// Pipe other styles through css modules and append to style.css
{
test: /^((?!\.global).)*\.css$/,
loader: ExtractTextPlugin.extract(
'style-loader',
'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
)
},
// Fonts
{ test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?name=fonts/[hash].[ext]&limit=50000&mimetype=application/font-woff' },
{ test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: 'url-loader?name=fonts/[hash].[ext]&limit=50000&mimetype=application/font-woff' },
{ test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader?name=fonts/[hash].[ext]' },
{ test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader?name=fonts/[hash].[ext]' },
{ test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: 'file-loader?name=fonts/[hash].[ext]' },
// Images
{
test: /\.(?:ico|gif|png|jpg|jpeg|webp)$/,
loader: 'url-loader'
}
]
},
plugins: [
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production')
}),
new BabiliPlugin({
// Disable deadcode until https://github.com/babel/babili/issues/385 fixed
deadcode: false,
}),
new webpack.optimize.UglifyJsPlugin({
compressor: {
screw_ie8: true,
warnings: false
}
}),
new ExtractTextPlugin('style.css', { allChunks: true }),
new HtmlWebpackPlugin({
filename: 'app.html',
template: 'app/app.html',
inject: false
})
],
target: 'electron-renderer'
}));
webpack.config.electron.js
/**
* Build config for electron 'Main Process' file
*/
import webpack from 'webpack';
import validate from 'webpack-validator';
import merge from 'webpack-merge';
import BabiliPlugin from 'babili-webpack-plugin';
import baseConfig from './webpack.config.base';
export default validate(merge(baseConfig, {
devtool: 'source-map',
entry: ['babel-polyfill', './main.development'],
// 'main.js' in root
output: {
path: __dirname,
filename: './main.js'
},
plugins: [
deadcode: false,
}),
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
})
],
target: 'electron-main',
node: {
__dirname: false,
__filename: false
}
}));
scripts section of package.json
"scripts": {
"test": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 node --trace-warnings ./test/runTests.js",
"test-all": "npm run lint && npm run flow && npm run test && npm run build && npm run test-e2e",
"test-watch": "npm test -- --watch",
"test-e2e": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 node --trace-warnings ./test/runTests.js e2e",
"lint": "eslint --cache --format=node_modules/eslint-formatter-pretty .",
"lint-fix": "npm run lint -- --fix",
"lint-styles": "stylelint app/*.css app/components/*.css --syntax scss",
"hot-updates-server": "cross-env NODE_ENV=development node --trace-warnings -r babel-register ./node_modules/webpack-dev-server/bin/webpack-dev-server --config webpack.config.renderer.dev.js",
"build": "concurrently \"npm run copy-images\" \"npm run build-main\" \"npm run build-renderer\"",
"build-dll": "cross-env NODE_ENV=development node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.renderer.dev.dll.js --progress --profile --colors",
"build-main": "cross-env NODE_ENV=production node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.main.prod.js --progress --profile --colors",
"build-renderer": "cross-env NODE_ENV=production node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.renderer.prod.js --progress --profile --colors",
"start": "cross-env NODE_ENV=production electron ./app/",
"prestart": "npm run build",
"flow": "flow",
"flow-typed": "rm -rf flow-typed && flow-typed install --overwrite || true",
"start-hot-renderer": "cross-env HOT=1 NODE_ENV=development electron -r babel-register -r babel-polyfill ./app/main.development",
"postinstall": "concurrently \"npm run flow-typed\" \"npm run build-dll\" \"install-app-deps\" \"node node_modules/fbjs-scripts/node/check-dev-engines.js package.json\"",
"dev": "cross-env START_HOT=1 npm run hot-updates-server",
"package": "npm run build && build --publish never",
"package-win": "npm run build && build --win --x64",
"package-linux": "npm run build && build --linux",
"package-all": "npm run build && build -mwl",
"cleanup": "mop -v",
"copy-images": "cp -Rf app/shared/images/ app/dist/images/"
},
"browserslist": "electron 1.4",
"build": {
"productName": "Test",
"appId": "org.Test",
"files": [
"dist/",
"node_modules/",
"app.html",
"main.js",
"main.js.map",
"package.json"
],
"dmg": {
"contents": [
{
"x": 130,
"y": 220
},
{
"x": 410,
"y": 220,
"type": "link",
"path": "/Applications"
}
]
},
"win": {
"target": [
"nsis"
]
},
"linux": {
"target": [
"deb",
"AppImage"
]
},
"directories": {
"buildResources": "resources",
"output": "release"
}
},
after electron app installed, it put all the source code and resource files into a .asar package. so to insert any image, u have to use a directory outside of app.
Generally this directory can be a Document folder.
const { remote } = require('electron');
let tempFolder =path.join( remote.app.getPath("documents"), 'AppFiles');
use the tempFolder directory to save image.

Webpack sass to css specify a specific folder?

I've been looking over the docs and checking other people's questions but I can't find the simple answer to how to compile all my sass down to a simple css file and specify the directory I want the resulting css file to output to.
For quick context:
I have a public directory with a stylesheets directory and a build directory in it. webpack compiles the app into build, and I'd like to have the sass compile style.css into the stylesheets directory.
Here's a screenshot of my public directory:
public dir img
I'd like to be able to do something like this in my webpack.config.js (only showing pertinent code for brevity):
const ExtractTextPlugin = require('extract-text-webpack-plugin');
...
// To be called in plugins:
const cssOutput = new ExtractTextPlugin('./public/stylesheets/style.css');
inside module loaders:
{
test: /\.scss$/,
loader: ExtractTextPlugin.extract('style-loader', 'css-loader!sass-loader'),
},
In plugins:
plugins: [
.
.
.
cssOutput,
],
I'd like to be able to access the output file with this line in my index.html file located in the public directory:
<link rel="stylesheet" href="/stylesheets/style.css" />
I'm currently doing this using gulp and it works fine, I'm just trying to transition everything into webpack. Any help would be greatly appreciated!
Turns out you can just set the output file like this:
const cssOutput = new ExtractTextPlugin('../stylesheets/style.css', { allChunks: true });
I made the noob mistake of forgetting to add:
require('_scss/style.scss');
in my index.jsx file.
For anyone who runs into this issue, I still had trouble with fonts and images, so inside module loaders in the webpack.config.js I had to add:
{
test: /\.(eot|woff|woff2|ttf|svg|png|jpe?g|gif)(\?\S*)?$/,
loader: 'file',
},
and since this output everything into my build directory, I just changed the css to output everything in the build directory as well to prevent path errors. I changed it to this:
const cssOutput = new ExtractTextPlugin('style.css', { allChunks: true });
Hopefully this helps someone else who runs into this type of issue!
Work for Me::
webpack.config.js
module.exports = {
entry: ['./src/app.ts', './src/sass/style.scss'],
module:{
rules: [
{
test: /\.ts$/,
use: 'ts-loader',
include: [path.resolve(__dirname, 'src', 'src/class')]
},
{
test: /\.scss$/,
use: [
{
loader: 'file-loader',
options: {
name: '/css/[name].css'
}
},
{
loader: 'extract-loader'
},
{
loader: 'css-loader?-url'
},
{
loader: 'postcss-loader'
},
{
loader: 'sass-loader'
}
],
include: [path.resolve(__dirname, 'src/sass')]
}
]
},
output: {
filename: 'js/app.js',
path: path.resolve(__dirname, 'public')
}
}
postcss.config.js
module.exports = {
plugins: {
'autoprefixer': {}
}
}
package.json
{
"name": "tsscript",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"nov": "webpack",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"autoprefixer": "^9.8.5",
"css": "^3.0.0",
"css-loader": "^3.6.0",
"extract-loader": "^5.1.0",
"postcss-loader": "^3.0.0",
"sass": "^1.26.10",
"sass-loader": "^9.0.2",
"style-loader": "^1.2.1",
"ts-loader": "^8.0.0",
"typescript": "^3.9.6",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12"
}
}
Directory Snap:
Have you take a look at https://www.npmjs.com/package/extract-text-webpack-plugin?
you'll probably need it.
Recent solution, you can use mini-css-extract-plugin, css-loader, and sass-loader. mini-css-extract-plugin can create a CSS file per JS file which contains CSS.
Install dependencies:
npm i -D mini-css-extract-plugin css-loader sass-loader
For example, your main JS file that includes a style is ./src/index.js:
...
import './src/style.scss';
...
Configure webpack.config.js:
...
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
...
entry: {
main: './src/index.js',
},
plugins: [
new MiniCssExtractPlugin(),
],
...
module: {
rules: [
...
{
test: /\.s[ac]ss$/i,
use: [
MiniCssExtractPlugin.loader,
"css-loader",
"sass-loader",
],
},
...
]
},
resolve: {
modules: ["node_modules", path.resolve(__dirname, "src")],
extensions: ['.js', '.scss', ...]
}
}
Then, you can check your output directory, there will be main.css. The plugin set default name to [name].css (entry name).

Resources