I'm trying to write a chrome extension. I've found a way to create multiple
pages and a background script, but the background script contains a hash and is
placed into the dist/assets folder. I would like to output just 'dist/background.js'. Alternatively (and maybe better) I would like to have my manifest updated to contain the actual script name with the hash.
Here's my vite.config.ts
import { defineConfig, BuildOptions } from 'vite'
import vue from '#vitejs/plugin-vue'
const { resolve } = require('path')
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
build: {
// lib: {
// entry: resolve(__dirname, 'background.ts'),
// name: 'background',
// fileName: format => `background.{format}.js`
// },
rollupOptions: {
// output: {
// format: 'cjs'
// },
input: {
main: resolve(__dirname, 'index.html'),
popup: resolve(__dirname, 'popup/index.html'),
options: resolve(__dirname, 'options/index.html'),
background: resolve(__dirname, 'background.ts'),
},
// output: {
// format: 'cjs'
// }
}
}
})
I've tried a few things and some are shown here commented out, but I couldn't get anything to work how I wanted. Finally I was able to get it to work if I create a separate vite.config.background.ts file:
import { defineConfig } from 'vite'
const { resolve } = require('path')
// https://vitejs.dev/config/
export default defineConfig({
build: {
emptyOutDir: false,
rollupOptions: {
input: resolve(__dirname, 'background.ts'),
output: {
format: "esm",
file: "dist/background.js",
dir: null,
}
}
}
})
And edit my build script to build it after the other config:
"build": "vue-tsc --noEmit && vite build && vite build --config vite.config.background.ts",
But is there any way to use just one file, or to generate the manifest and use the hashed
file name? Right now it is a static json file.
This is what I ended up doing which isn't optimal. It's just hard to believe that you can specify multiple inputs which produce multiple outputs, but not define output settings for each... Structure:
+-- dist (output directory)
+-- options (options page)
+-- popup (popup page)
+-- public (contents copied directly to dist
| +-- background.js (generated background script from '/background.ts')
| +-- manifest.json (extension manifest)
+-- scripts (scripts used in build)
| +-- watch.mjs (script for 'yarn watch')
+-- src (main page)
+-- background.ts
So manifest.json is in the public directory which is copied to the root of dist. And the background script build puts the output background.js in that folder too for the main build to copy as part of the 'main' page.
scripts/watch.mjs
import { createServer, build } from 'vite'
/**
* #type {(server: import('vite').ViteDevServer) => Promise<import('rollup').RollupWatcher>}
*/
function watchBackground(server) {
return build({
configFile: 'vite.config.background.ts',
mode: 'development',
build: {
watch: {},
},
})
}
/**
* #type {(server: import('vite').ViteDevServer) => Promise<import('rollup').RollupWatcher>}
*/
function watchMain(server) {
return build({
configFile: 'vite.config.ts',
mode: 'development',
build: {
watch: {},
},
})
}
// bootstrap
const server = await createServer({ configFile: 'vite.config.ts' })
await server.listen()
await watchBackground(server) // outputs to public/background.js, so run first
await watchMain(server)
vite.config.background.ts
import { defineConfig } from 'vite'
const { resolve } = require('path')
// https://vitejs.dev/config/
export default defineConfig({
build: {
emptyOutDir: false,
rollupOptions: {
input: resolve(__dirname, 'background.ts'),
output: {
format: "esm",
file: "public/background.js",
dir: null,
}
}
}
})
vite.config.ts
import { defineConfig, BuildOptions } from 'vite'
import vue from '#vitejs/plugin-vue'
const { resolve } = require('path')
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
build: {
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
popup: resolve(__dirname, 'popup/index.html'),
options: resolve(__dirname, 'options/index.html'),
},
}
}
})
Running multiple rollup builds with one vite build command is not supported but you can generate a manifest file directly from vite. To generate it simply set build.manifest to true (but I am not sure if this will contain everything that you need for a manifest file for a web plugin though).
Related
I use Inertia, Vue3, and Vite as bundling assets. When I set up and run with SSR in nodejs the error always appears Unknown file extension ".vue". I've been googling for days but haven't solved it.
This is my ssr.js file which I run with nodemon
import { createSSRApp, h } from 'vue'
import { renderToString } from '#vue/server-renderer'
import { createInertiaApp } from '#inertiajs/inertia-vue3'
import createServer from '#inertiajs/server'
createServer.default((page) => createInertiaApp({
page,
render: renderToString,
resolve: async (name) => {
return (await import(`./Pages/${name}.vue`)).default;
},
setup({ app, props, plugin }) {
return createSSRApp({
render: () => h(app, props),
}).use(plugin)
},
}))
vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
// import react from '#vitejs/plugin-react';
import vue from '#vitejs/plugin-vue';
export default defineConfig({
plugins: [
laravel({
input: 'resources/js/app.js',
ssr: 'resources/js/ssr.mjs',
}),
// react(),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
ssr: {
noExternal: ['#inertiajs/server'],
},
resolve: {
alias: {
'#': '/resources/js'
}
}
});
when I run it with nodemon ssr.js an error like this will appear
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".vue" for C:\Users\USER\Documents\biviplant\biviplant\resources\js\Pages\Test.vue
I'm trying to include #mycompany/package1, and #mycompany/package2 to be compiled along with the rest of my code using babel-node. Since package1 and package2 are in ES6. (Also note I'm not using Webpack)
In my jest config I added the below option into my jest config which works fine. When testing the code will compile the packages correctly
"transformIgnorePatterns": [
"/node_modules/(?!(#mycompany)/).*/"
],
But when trying to run babel-node I get errors.
In my babel.config.js
module.exports = {
presets: [
'#babel/preset-flow',
[
'#babel/preset-env',
{
targets: {
node: 8
}
}
]
],
plugins: ['#babel/plugin-proposal-class-properties']
};
I tried adding the below code to my babel.config.js but it still complains about ES6 errors within my node_modules/#mycompany/package1
I tried to include the viz package but then babel wouldn't compile my src files
include: [path.resolve(__dirname, 'node_modules/#mycompany/package1')]
include: ['/node_modules/((#mycompany)/).*/']
I tried to exclude everything but #mycompany packages but I still get transpile errors in my package1
exclude: [/node_modules\/(?!(#mycompany)\/).*/],
I tried playing with ignore but those don't seem like they are the right options based on reading the docs
I found out that we can do this with webpack to help bundle the packages with the rest of your code.
This is my webpack file for NodeJS.
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const webpack = require('webpack');
const spawn = require('child_process').spawn;
const nodeEnv = process.env.NODE_ENV;
const isProduction = nodeEnv === 'production';
const compiler = webpack({
entry: ['#babel/polyfill', './src/server.js'],
output: {
path: path.resolve(__dirname, 'lib'),
filename: 'server.bundle.js',
libraryTarget: 'commonjs2'
},
externals: [
nodeExternals({
whitelist: [/#mycompany\/.*/]
})
],
plugins: plugins,
target: 'node',
mode: 'development',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules\/(?!(#mycompany)\/).*/,
use: {
loader: 'babel-loader',
options: {
configFile: './babel.config.js'
}
}
}
]
}
});
if (isProduction) {
compiler.run((err, stats) => {
if (err) {
console.error(err);
return;
}
console.log(
stats.toString({
colors: true
})
);
});
} else {
let serverControl;
compiler.watch(
{
aggregateTimeout: 300,
poll: 1000
},
(err, stats) => {
if (serverControl) {
serverControl.kill();
}
if (err) {
console.error(err);
return;
}
console.log(
stats.toString({
colors: true
})
);
// change app.js to the relative path to the bundle created by webpack, if necessary
serverControl = spawn('node', [
path.resolve(__dirname, 'lib/server.bundle.js')
]);
serverControl.stdout.on('data', data => console.log(data.toString()));
serverControl.stderr.on('data', data => console.error(data.toString()));
}
);
}
Note the most important part is
Adding webpack-node-externals. Since this is a node.js server we don't need to bundle the node_modules.
Make sure you whitelist your package that you need to be compiled/bundled and also make sure you have your packages included to be compiled in your babel-loader
nodeExternal tells webpack know not to bundle ANY node_modules.
whitelist is saying that we should bundle the packages we listed
externals: [
nodeExternals({
whitelist: [/#mycompany\/.*/]
})
]
This line means to exclude all node_modules EXCEPT #mycompany/* packages
exclude: /node_modules\/(?!(#mycompany)\/).*/,
I'm having trouble getting my configuration right for this. I have a NextJS setup with next-css and I'm trying to add react-svg-loader to the configuration:
next.config.js:
const withCSS = require("#zeit/next-css");
module.exports = withCSS({
cssModules: true,
cssLoaderOptions: {
importLoaders: 1,
localIdentName: "[local]__[hash:base64:4]"
},
webpack(config, options) {
const { dev, isServer } = options;
config.module.rules.push({
test: /\.svg$/,
use: [
{
loader: "react-svg-loader",
options: {
jsx: true // true outputs JSX tags
}
}
]
});
return config;
}
});
The svgs will still fail to load:
{ Error: (client) ./svgs/pencil.svg 10:9 Module parse failed:
Unexpected token (10:9) You may need an appropriate loader to handle
this file type.
Looks like my config above doesn't work but I can't quite figure out why.
I currently have 2 separate webpack builds for server rendered vs client rendered code. Is there an easy way to change the build output based on server/client build?
For example something like this:
// Have some code like this
if(is_client){
console.log('x.y.z')
} else {
server.log('x.y.z')
}
// Webpack outputs:
// replaced code in client.js
console.log('x.y.z')
// replaced code in server.js
server.log('x.y.z')
Have you tried anything like this?
// webpack.config.js
module.exports = () => ['web', 'node'].map(target => {
const config = {
target,
context: path.resolve('__dirname', 'src'),
entry: {
[target]: ['./application.js'],
},
output: {
path: path.resolve(__dirname, 'dist', target),
filename: '[name].js'
},
modules: { rules: ... },
plugins: [
new webpack.DefinePlugin({
IS_NODE: JSON.stringify(target === 'node'),
IS_WEB: JSON.stringify(target === 'web'),
}),
],
};
return config;
});
// later in your code
import logger from 'logger';
if (IS_NODE) {
logger.log('this is node js');
}
if (IS_WEB) {
console.log('this is web');
}
how the compilation works?
// client.bundle.js
import logger from 'logger';
// DefinePlugin creates a constant expression which causes the code below to be unreachable
if (false) {
logger.log('this is node js');
}
if (true) {
console.log('this is web');
}
Finally you will produce your build in production mode, so webpack will include a plugin called UglifyJS, this has a feature called dead code removal (aka tree shaking), so it will delete any unused/unreachable code.
and the final result will look like:
// node.bundle.js
import logger from 'logger';
console.log('this is node js');
//web.bundle.js
console.log('this is node js');
I am trying to put a bunch of libs on the DLL following guides like react-boilerplate and this one.
When I build and run, the DLL files are given as not defined.
I'm probably missing something I did a separated webpack to build the dll:
import webpack from 'webpack'
const library = '[name]'
export default {
entry: {
'lokka': ['lokka', 'lokka-transport-http', 'socket.io-client']
/** Other libs **/
},
output: {
filename: '[name].dll.js',
path: 'build/',
library: library
},
plugins: [
new webpack.DllPlugin({
path: 'build/[name]-manifest.json',
name: library
})
]
}
And added the references to the manifest.json
import webpack from 'webpack'
const desiredLibs = [
'lokka'
]
const plugins = desiredLibs.map((lib) => {
return new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require(`../build/${lib}-manifest.json`)
})
})
export const dllReference = () => {
return { plugins }
}
export default dllReference
Was there anything else I should do?
On my case, it is complaining that lokka is not found when the code is run.
Turns out I (obviously) need to include the generated DLL on my scripts src AND copy it in the case of dev, since hot reloading would only serve the entry of it and it's dependencies, so for the dllReference and copy part it became:
import webpack from 'webpack'
import CopyWebpackPlugin from 'copy-webpack-plugin'
import path from 'path'
const desiredLibs = ['lokka', 'react', 'moment']
const copies = []
const plugins = desiredLibs.map((lib) => {
copies.push({
from: path.join(__dirname, `../compileResources/${lib}.dll.js`),
to: `dll`
})
return new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require(`../compileResources/${lib}-manifest.json`)
})
})
plugins.push(
new CopyWebpackPlugin(copies)
)
/**
* Adds the dll references and copies the file
*/
export const dllReference = () => {
return { plugins }
}
export default dllReference
And then since I copied the dll's using the copy plugin I needed to add the scripts on the html. Really obvious on hindsight