Vite how to get relative url in css? - javascript

This is my config in vite, I can't get build result in build mode. Relative paths for fonts and images.
As an example ../fonts/Roborts.ttf
In any case and with any rearrangement of files, I get ./fonts/Roborts.ttf
All required files like main.js and index.html are in src as are all images and scss
import { defineConfig, splitVendorChunkPlugin } from 'vite'
import { resolve } from 'path'
import path from 'path'
// plg
import handlebars from 'vite-plugin-handlebars'
import viteImagemin from 'vite-plugin-imagemin'
export default defineConfig({
root: path.resolve(__dirname, 'src'),
base: './',
plugins: [
handlebars({
partialDirectory: resolve(__dirname, 'src/partials'),
reloadOnPartialChange: true,
}),
viteImagemin({
gifsicle: {
optimizationLevel: 7,
interlaced: false,
},
optipng: {
optimizationLevel: 7,
},
mozjpeg: {
quality: 20,
},
pngquant: {
quality: [0.8, 0.9],
speed: 4,
},
svgo: {
plugins: [
{
name: 'removeViewBox',
},
{
name: 'removeEmptyAttrs',
active: false,
},
],
},
}),
splitVendorChunkPlugin(),
],
build: {
emptyOutDir: path.resolve(__dirname, 'dist'),
outDir: path.resolve(__dirname, 'dist'),
minify: false,
rollupOptions: {
output: {
chunkFileNames: 'js/[name].js',
assetFileNames: ({ name }) => {
if (/\.(gif|jpeg|jpg|png|svg)$/.test(name ?? '')) {
return 'images/[name][extname]'
}
if (/(ttf)/.test(name ?? '')) {
return 'fonts/[name][extname]'
}
if (/css/.test(name ?? '')) {
return 'css/main[extname]'
}
return '[name]-[hash][extname]'
},
},
input: {
index: path.resolve(__dirname, 'src/index.html'),
about: path.resolve(__dirname, 'src/about.html'),
},
},
},
server: {
port: 3000,
host: '0.0.0.0',
open: true,
hmr: true,
},
})
I tried to change the structure, returned to the old structure, moved the files outside the src to the initial state, but did not find an answer. I also consider the problem for about two days, but I did not understand why this is happening.

Related

How to decrease the directory hierarchy of the output of html files in Vite.js?

I have built an MPA project based on vite. And I put the templates in src/pages/. When I run dev or build, it will output something like /pages/list/index.html.But I don‘t need the router pages. How can I get the output like /pages/list/index.html?
The vite.config.ts is like below,
const root = path.resolve(__dirname, 'src');
const pageDir = path.resolve(root, 'pages')
const outDir = path.resolve(__dirname, 'dist');
export default defineConfig({
root,
plugins: [
react(),
copy({
targets: [{ src: 'src/lib/**/*', dest: 'dist/lib' }],
hook: 'writeBundle'
}),
],
build: {
outDir,
emptyOutDir: true,
rollupOptions: {
input: {
list: path.resolve(pageDir, 'list/index.html'),
filter: path.resolve(pageDir, 'filter/index.html'),
result: path.resolve(pageDir, 'result/index.html'),
search: path.resolve(pageDir, 'search/index.html'),
}
},
},
resolve: {
alias: [
{
find: /#\//,
replacement: path.join(__dirname, './src/')
}
]
},
css: {
"preprocessorOptions": {
"less": {
"javascriptEnabled": true
}
},
"modules": {
"localsConvention": "camelCase"
}
}
})

webpack-dev-server and WASM HMR

I'm trying to make a real time shooter game on the web using ThreeJS and Rapier. Rapier is a physics engine written in rust and can be used in JS using the WASM module. Now in development webpack-dev-server and HMR are really a god given tool, if it works. The RAPIER WASM module gets loaded but from the wrong port. My HTTP server runs on http://localhost:8080 but the devserver on -:9000. The fetch mechanism that webpack uses to get the WASM doesn't configure the port for the fetch. Is this a bug or misconfiguration on my end?
My webpack.config.js:
const dotenv = require('dotenv');
dotenv.config();
const path = require('path');
const zlib = require('zlib');
const CompressionPlugin = require('compression-webpack-plugin');
const ThreadsPlugin = require('threads-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const WebpackBar = require('webpackbar');
const { WebpackManifestPlugin: ManifestPlugin } = require('webpack-manifest-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const DEBUG = process.env.NODE_ENV !== 'production';
const FILENAME = DEBUG ? '[name]' : '[name].[contenthash]';
const CHUNK_FILENAME = DEBUG ? '[name]' : '[id].[contenthash]';
module.exports = {
mode: DEBUG ? 'development' : 'production',
entry: { bundle: path.join(__dirname, 'resources/js/bundle.js') },
output: {
path: path.join(__dirname, 'public'),
filename: FILENAME + '.js',
chunkFilename: CHUNK_FILENAME + '.js',
publicPath: '',
clean: true,
},
module: {
rules: [
{
test: '/\.m?js$/',
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['#babel/preset-env']
}
}
},
{ test: /\.(frag|vert)$/, use: 'raw-loader' },
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { importLoaders: 2 } },
'postcss-loader',
'sass-loader',
]
},
{ test: /\.(png|svg|jpg|jpeg|webm|gif)$/i, type: 'asset/resource' },
{ test: /\.(woff|woff2|eot|ttf|otf)$/i, type: 'asset/resource' },
{ test: /\.svelte$/, use: { loader: 'svelte-loader', options: {
emitCss: false,
hotReload: DEBUG,
compilerOptions: { dev: DEBUG },
hotOptions: {
preserveLocalState: false,
noReload: false, // prevent reload after fatal error
acceptAccesssors: true,
acceptNamedExports: true
}
} } },
{ test: /node_modules\/svelte\/.*\.mjs$/, resolve: { fullySpecified: false } }
]
},
plugins: [
new CopyPlugin({ patterns: [
// { from: './resources/images', to: 'images' },
{ from: './resources/textures', to: 'textures' },
{ from: './resources/scenes', to: 'scenes' },
] }),
new ManifestPlugin(),
new MiniCssExtractPlugin({
filename: FILENAME + '.css',
chunkFilename: CHUNK_FILENAME + '.css'
}),
// new ThreadsPlugin(),
new WebpackBar(),
].concat(DEBUG ? [] : [
new CompressionPlugin({
filename: '[path][base].gz',
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8,
}),
new CompressionPlugin({
filename: '[path][base].br',
algorithm: 'brotliCompress',
test: /\.(js|css|html|svg)$/,
compressionOptions: {
params: {
[zlib.constants.BROTLI_PARAM_QUALITY]: 11,
},
},
threshold: 10240,
minRatio: 0.8,
})
]),
optimization: {
minimize: true,
minimizer: [ new TerserPlugin(), new CssMinimizerPlugin() ],
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'all'
}
}
}
},
devServer: {
port: 9000,
static: path.join(__dirname, 'public'),
hot: true,
client: {
overlay: {
errors: true,
warnings: false,
},
progress: true,
reconnect: 10,
webSocketURL: {
hostname: 'localhost',
port: 9000,
}
},
compress: true
},
devtool: DEBUG ? 'inline-source-map' : false,
experiments: {
asyncWebAssembly: true
}
};
I've googled a fair bit for solutions but so far I haven't found anything that looks like my particular issue.

WARNING in Unknown plugin: imageminSvgo. Did you forget to install the plugin?

This is the Warning that I receive from Webpack, despite installing the imageminSvgo plugin.
I have used it within the Image Minimizer Plugin as imageminSvgo, but Webpack doesn't seem to detect it.
I would really appreciate some help in knowing how to use this plugin in my project in the best way.
Here are my webpack.config.js configs.
webpack.config.js
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
// const imageminSvgo = require('imagemin-svgo')
const mode = "production";
module.exports = {
devtool: false,
stats: "errors-warnings",
resolve: {
alias: {
CssFolder: path.resolve(__dirname, "src/stylesheets/"),
JsFolder: path.resolve(__dirname, "src/javascripts/"),
ImgFolder: path.resolve(__dirname, "src/images/"),
},
},
entry: { TOAA: "./src/index.js" },
mode: mode,
devServer: {
port: 8080,
// contentBase: "./build",
hot: true,
// overlay: true,
compress: true,
historyApiFallback: true,
},
output: {
filename: "[name]-[contenthash].bundle.js",
path: path.resolve(__dirname, "build"),
},
optimization: {
minimize: true,
minimizer: [new TerserPlugin()],
},
plugins: [
// imageminSvgo({
// plugins: [
// {
// name: "removeViewBox",
// active: false,
// },
// ],
// }),
new CompressionPlugin(),
new HtmlWebpackPlugin(
{ template: "./public/index.html" },
{ inject: true }
),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename:
mode === "production"
? "[name]-[contenthash].bundle.css"
: "[name].css",
}),
new ImageMinimizerPlugin({
minimizerOptions: {
// Lossless optimization with custom option
// Feel free to experiment with options for better result for you
plugins: [
["gifsicle", { interlaced: true }],
["jpegtran", { progressive: true }],
["optipng", { optimizationLevel: 5 }],
["imageminSvgo", { removeViewBox: true }],
// Svgo configuration here https://github.com/svg/svgo#configuration
[
"svgo",
{
plugins: [
{
name: "removeViewBox",
active: false,
},
{
name: "addAttributesToSVGElement",
params: {
attributes: [{ xmlns: "http://www.w3.org/2000/svg" }],
},
},
{
name: "preset-default",
params: {
overrides: {
// customize plugin options
convertShapeToPath: {
convertArcs: true,
},
// disable plugins
convertPathData: false,
},
},
},
],
},
],
],
},
}),
new NodePolyfillPlugin(),
],
module: {
rules: [
{
test: /\.css$/i,
use: [
{ loader: MiniCssExtractPlugin.loader, options: {} },
{ loader: "css-loader", options: { importLoaders: 1 } },
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
[
"autoprefixer",
{
overrideBrowserslist: ["last 3 versions", "ie >9"],
},
],
],
},
},
},
],
},
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"],
},
},
},
{
test: /\.(jpe?g|png|gif|svg|jpg)$/i,
type: "asset",
},
{
test: /\.html$/i,
loader: "html-loader",
},
],
},
resolve: {
extensions: [".js", ".jsx"],
},
};
Please let me know if additional info is required.
Try reinstall imagemin forcing the installation of the plugins. Use something like this: npm install -g imagemin-cli#3.0.0 --unsafe-perm=true --allow-root

core-js is large in bundle (using 62kB)

I'm reducing my js bundle size and stumbled upon core-js. It takes around 62kB which represents ~24% of the whole package.
I tried using #babel/preset-env, but wasn't able to shrink the size any further. Not sure if I'm using the "right" settings:
'#babel/preset-env',
{
targets: {
browsers: ['>1%'],
},
useBuiltIns: 'usage',
corejs: { version: 3, proposals: true },
},
The full webpack.config.js
const path = require('path');
const webpack = require('webpack');
const dotenv = require('dotenv');
const copyWebpackPlugin = require('copy-webpack-plugin');
const bundleOutputDir = './dist';
/* eslint-disable import/no-extraneous-dependencies */
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
// const CompressionPlugin = require('compression-webpack-plugin');
module.exports = (env) => {
// get project ID (environment)
const projectID = process.env.PROJECT_ID;
if (!projectID || projectID === undefined) {
throw new Error('Need env variable PROJECT_ID');
}
const isDevEnvironment = !(projectID === 'production-project');
const isDevBuild = !(env && env.prod);
const analyzeBundle = env && env.analyze;
// call dotenv and it will return an Object with a parsed key
const dotEnv = isDevEnvironment ? dotenv.config({ path: './.env.development' }).parsed : dotenv.config().parsed;
// reduce it to a nice object, the same as before
const envKeys = Object.keys(dotEnv).reduce((prev, next) => {
const updatedPrev = prev;
updatedPrev[`process.env.${next}`] = JSON.stringify(dotEnv[next]);
return updatedPrev;
}, {});
envKeys['process.env.PROJECT_ID'] = JSON.stringify(projectID);
// need to remove quotes from env
const publicURL = 'https:/mysite.com'
const plugins = [new webpack.DefinePlugin(envKeys), new ForkTsCheckerWebpackPlugin()];
if (isDevBuild) {
// eslint-disable-next-line new-cap
plugins.push(new webpack.SourceMapDevToolPlugin(), new copyWebpackPlugin([{ from: 'dev/' }]));
} else {
// Don't need to enable compressinon plugin as Firebase Hosting automatically zips the files for us
// plugins.push(new CompressionPlugin());
}
if (analyzeBundle) {
// eslint-disable-next-line global-require
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
plugins.push(new BundleAnalyzerPlugin());
}
const babelPlugins = [
// syntax sugar found in React components
'#babel/proposal-class-properties',
'#babel/proposal-object-rest-spread',
// transpile JSX/TSX to JS
[
'#babel/plugin-transform-react-jsx',
{
// we use Preact, which has `Preact.h` instead of `React.createElement`
pragma: 'h',
pragmaFrag: 'Fragment',
},
],
[
'transform-react-remove-prop-types',
{
removeImport: !isDevBuild,
},
],
];
if (!isDevBuild) {
babelPlugins.push(['transform-remove-console', { exclude: ['error', 'warn'] }]);
}
return [
{
entry: './src/index.ts',
output: {
filename: 'widget.js',
path: path.resolve(bundleOutputDir),
publicPath: isDevBuild ? '' : publicURL,
},
devServer: {
host: '0.0.0.0', // your ip address
port: 8080,
disableHostCheck: true,
contentBase: bundleOutputDir,
open: 'google chrome',
},
plugins,
optimization: {
minimize: !isDevBuild,
nodeEnv: 'production',
mangleWasmImports: true,
removeAvailableModules: true,
usedExports: true,
sideEffects: true,
providedExports: true,
concatenateModules: true,
},
mode: isDevBuild ? 'development' : 'production',
module: {
rules: [
// packs PNG's discovered in url() into bundle
{
test: /\.(jpe?g|png|webp)$/i,
use: [
{
loader: 'responsive-loader',
options: {
// eslint-disable-next-line global-require
adapter: require('responsive-loader/sharp'),
// sizes: [160, 320, 640, 960, 1280],
name: '[path][name]-[width].[ext]',
sourceMap: isDevBuild,
},
},
],
},
{ test: /\.svg/, use: ['#svgr/webpack'] },
{
test: /\.(css)$/i,
use: [
{
loader: 'style-loader',
options: {
injectType: 'singletonStyleTag',
},
},
{
// allows import CSS as modules
loader: 'css-loader',
options: {
modules: {
// css class names format
localIdentName: '[name]-[local]-[hash:base64:5]',
},
sourceMap: isDevBuild,
},
},
],
},
{
test: /\.(scss)$/i,
use: [
{
loader: 'style-loader',
options: { injectType: 'singletonStyleTag' },
},
{
// allows import CSS as modules
loader: 'css-loader',
options: {
modules: {
// css class names format
localIdentName: '[name]-[local]-[hash:base64:5]',
},
sourceMap: isDevBuild,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: isDevBuild,
},
},
],
},
// use babel-loader for TS and JS modeles,
// starting v7 Babel babel-loader can transpile TS into JS,
// so no need for ts-loader
// note, that in dev we still use tsc for type checking
{
test: /\.(js|ts|tsx|jsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
[
'#babel/preset-env',
{
targets: {
browsers: ['>1%'],
},
useBuiltIns: 'usage',
corejs: { version: 3, proposals: true },
},
],
[
// enable transpiling ts => js
'#babel/typescript',
// tell babel to compile JSX using into Preact
{ jsxPragma: 'h' },
],
],
plugins: babelPlugins,
},
},
],
},
],
},
resolve: {
extensions: ['*', '.js', '.ts', '.tsx'],
plugins: [new TsconfigPathsPlugin()],
alias: {
react: 'preact/compat',
'react-dom': 'preact/compat',
images: path.join(__dirname, 'images'),
sharedimages: path.resolve(__dirname, '../../packages/shared/src/resources'),
},
},
},
];
};
It looks like the targets property for #babel/preset-env is no longer used and instead the browserlist is recommended to include the list of supported browsers.
https://babeljs.io/docs/en/babel-preset-env#browserslist-integration

Webpack build does not work with crawlers

My build works perfectly fine in browsers (even legacy v40 < chrome). It does not work with any crawler I have tried so far. There's the same error which strangely only happens in crawlers (such as googlebot) which makes it incredibly difficult to pinpoint the issue.
I tried
Disabling production build to see unminified errors, but crawlers then refuse to load the js file as it is too big
Running the site in as many browsers as I can (except IE) it works in all of them.
Disabling preloading for crawlers (renders the app's html using headless chrome)
This is the error that googlebot sees:
You can test this yourself at https://wavedistrict.com
Webpack config:
const { resolve } = require("path")
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin")
const CleanWebpackPlugin = require("clean-webpack-plugin")
const CopyWebpackPlugin = require("copy-webpack-plugin")
const HtmlWebpackPlugin = require("html-webpack-plugin")
const HtmlWebpackInlineSourcePlugin = require("html-webpack-inline-source-plugin")
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin")
const WebpackPwaManifest = require("webpack-pwa-manifest")
const webpackMerge = require("webpack-merge")
const Visualizer = require("webpack-visualizer-plugin")
const isProduction = process.env.NODE_ENV === "production"
/**
* Variable for the project root.
* Change this when moving the configuration files
*/
const projectRoot = resolve(__dirname)
const sourceFolder = resolve(projectRoot, "src")
const tsFolder = resolve(sourceFolder, "ts")
const buildFolder = resolve(projectRoot, "build")
const publicFolder = resolve(projectRoot, "public")
const htmlTemplateFile = resolve(publicFolder, "index.html")
const tsconfigPath = resolve(projectRoot, "tsconfig.json")
const tslintPath = resolve(projectRoot, "tslint.json")
const tsLoader = {
loader: "ts-loader",
options: {
compilerOptions: {
module: "esnext",
target: "es5",
allowSyntheticDefaultImports: true,
},
transpileOnly: true,
configFile: tsconfigPath,
allowTsInNodeModules: true,
},
}
const babelLoader = {
loader: "babel-loader",
}
const workerRule = {
test: /\.worker\.ts$/,
use: {
loader: "worker-loader",
},
}
const babelRule = {
test: /\.(js|ts|tsx)$/,
use: [babelLoader],
}
const sassRule = {
test: /\.scss$/,
use: [
isProduction
? MiniCssExtractPlugin.loader
: {
loader: "style-loader",
options: {
singleton: true,
},
},
{ loader: "css-loader" },
{
loader: "sass-loader",
options: {
data: "#import './ts/modules/core/styles/_.scss';",
includePaths: [sourceFolder],
},
},
],
}
/** #type {import('webpack').Configuration} */
const baseConfig = {
context: projectRoot,
entry: [
"babel-polyfill",
"url-search-params-polyfill",
resolve(tsFolder, "init"),
],
output: {
filename: "js/[name].js",
path: buildFolder,
publicPath: "/",
},
module: {
rules: [workerRule, babelRule, sassRule],
},
resolve: {
modules: ["node_modules"],
extensions: [".js", ".ts", ".tsx", ".scss"],
alias: {
modules: resolve(tsFolder, "modules"),
common: resolve(tsFolder, "common"),
},
mainFields: ["jsnext:main", "module", "main"],
},
plugins: [
new CopyWebpackPlugin([
{
from: publicFolder,
ignore: [htmlTemplateFile],
},
]),
new CleanWebpackPlugin(buildFolder, { root: projectRoot, verbose: false }),
/**new ForkTsCheckerWebpackPlugin({
tslint: tslintPath,
tsconfig: tsconfigPath,
}),**/
],
stats: {
children: false,
entrypoints: false,
modules: false,
},
}
if (process.argv.includes("--stats")) {
if (baseConfig.plugins) {
baseConfig.plugins.push(new Visualizer())
}
}
const devConfig = webpackMerge(baseConfig, {
mode: "development",
plugins: [
new HtmlWebpackPlugin({
template: htmlTemplateFile,
chunksSortMode: "dependency",
}),
],
devtool: "inline-source-map",
devServer: {
hot: false,
historyApiFallback: true,
},
})
const prodConfig = webpackMerge(baseConfig, {
mode: "production",
optimization: {
minimize: true,
nodeEnv: "production",
},
plugins: [
new WebpackPwaManifest({
name: "WaveDistrict",
short_name: "WaveDistrict",
description: "",
background_color: "#091F35",
theme_color: "#00ba8c",
orientation: "any",
icons: [
{
src: resolve(publicFolder, "img/logo.svg"),
sizes: [48, 72, 96, 128, 144, 192, 256, 512],
destination: "icons",
},
{
src: resolve(publicFolder, "img/logo.png"),
sizes: [48, 72, 96, 128, 144, 192, 256, 512],
destination: "icons",
},
],
}),
new MiniCssExtractPlugin({
filename: "css/[name].css",
}),
new OptimizeCssAssetsWebpackPlugin(),
new HtmlWebpackPlugin({
template: htmlTemplateFile,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
inject: true,
}),
new HtmlWebpackInlineSourcePlugin(),
],
performance: {
maxAssetSize: 500000,
},
devtool: "source-map",
})
module.exports = isProduction ? prodConfig : devConfig
Babel config (needed to convert ES6 node_modules to ES5):
const babelEnv = {
targets: {
chrome: "41", // For googlebot
},
}
/** Keep track of all conflicting dependencies here */
const nonES5Deps = ["qs", "querystring", "query-string", "decko"]
module.exports = function(api) {
api.cache(true)
return {
exclude: [],
include: (path) => {
if (nonES5Deps.some((p) => path.match(p))) {
return true
}
if (path.match(/node_modules/)) return false
return true
},
presets: [
["#babel/preset-env", babelEnv],
"#babel/preset-react",
"#babel/preset-typescript",
],
plugins: [
"#babel/plugin-syntax-dynamic-import",
"#babel/plugin-transform-arrow-functions",
[
"#babel/plugin-proposal-decorators",
{
legacy: true,
},
],
[
"#babel/plugin-proposal-class-properties",
{
loose: true,
},
],
],
}
}
So what's going on here? How can I debug the issue when it only appears in crawlers?
I have discovered the issue with the help of a friend. It appears Googlebot (and other crawlers) does not support the AudioContext object, hence undefined is not a function.
Modifying my code to check for it and safely disabling functionality if it doesn't exist has solved the problem.

Categories