I'm building javascript library (more something like widget) which will have some UI within. I'm adding HTML elements to DOM by javascript. To add this HTML I have following code:
async insertWidgetMarkup() {
try {
const response = await fetch('src/html/form.html')
this.rootElement.innerHTML = await response.text()
} catch (e) {
console.error('Error gathering form HTML.', e)
}
}
I build entire thing with rollup
// rollup.config.js
import commonjs from '#rollup/plugin-commonjs';
export default {
input: 'main.js',
output: {
dir: 'dist',
format: 'cjs',
name: 'search_widget.js'
},
plugins: [commonjs()]
};
// package.json
"scripts": {
"build": "rollup --config --watch",
My issue is that in bundled file I have await fetch('src/html/form.html'); therefore it won't work in other applications. Can I somehow tell rollup to resolve this so it will have HTML in bundled file? Or if no - what other options I have, what is typical approach for that?
Instead of fetching, you can import the file directly with the rollup-plugin-html.
Setup rollup config to use the plugin like this
import commonjs from '#rollup/plugin-commonjs';
import html from 'rollup-plugin-html';
export default {
input: 'main.js',
output: {
format: 'umd',
name: 'search_widget',
file: 'dist/search_widget.js'
},
plugins: [
commonjs(),
html({
include: '**/*.html'
})
]
};
Then in your source file, use import like this
import html from 'src/html/form.html'
insertWidgetMarkup() {
try {
this.rootElement.innerHTML = html
} catch (e) {
console.error('Error gathering form HTML.', e)
}
}
Rollup will bundle the html files now.
Related
I am trying to bundle my Node.js server-side code (Koa app) into a single file which is what Webpack produces when I use the target as node. This is what ncc - node.js compiler collection also achieves.
I am now using Vite for my next project. I am able to bundle the code by using the SSR bundling feature provided by Vite. But I am not able to bundle third-party dependencies into this single file excluding core/built-in node.js modules. This is my Vite build script:
import path from 'path';
import { build } from 'vite';
import builtinModules from 'builtin-modules';
async function main() {
const result = await build({
mode: 'production',
appType: 'custom',
root: path.join(process.cwd(), 'backend'),
ssr: {
format: 'esm',
target: 'node',
// By default Vite bundles everything except the one we pass via `external` array.
external: builtinModules
},
build: {
manifest: false,
rollupOptions: {
input: 'backend/main.mjs',
output: {
assetFileNames: 'bundle.js'
}
},
outDir: '../dist/backend',
ssr: true,
emptyOutDir: true
},
plugins: [],
});
}
main();
In the above code, builtinModules is simply the string array of all the core node.js modules:
// builtinModules
[
'assert', 'async_hooks',
'buffer', 'child_process',
'cluster', 'console',
'constants', 'crypto',
'dgram', 'diagnostics_channel',
'dns', 'domain',
'events', 'fs',
'http', 'http2',
'https', 'inspector',
'module', 'net',
'os', 'path',
'perf_hooks', 'process',
'punycode', 'querystring',
'readline', 'repl',
'stream', 'string_decoder',
'timers', 'tls',
'trace_events', 'tty',
'url', 'util',
'v8', 'vm',
'worker_threads', 'zlib'
]
For my original source code:
// main.mjs
import path from 'path';
import Koa from 'koa';
async function main() {
console.log('Source folder', path.join(__dirname, 'src'));
const app = new Koa();
app.listen(3000);
}
main();
The produced output for the above Vite build configuration is:
// bundle.js
import path from "path";
import Koa from "koa";
async function main() {
console.log("Source folder", path.join(__dirname, "src"));
const app = new Koa();
app.listen(3e3);
}
main();
Ideally, I expected that it would bundle the koa package in the final output leaving behind only native path module. But that's not the case.
So, how do I enable bundling of my entire backend/Node.js source code compile into single file leaving only core node.js modules as external as Node.js is the runtime for my code? The vite server options also provide the noexternal option but setting it to the true works only for webworker like runtime where none of the built-in node modules are available.
Latest vite versions are using preloading you should tweak some rollup options to disable code splitting
import { defineConfig } from 'vite' // 2.8.0
import react from '#vitejs/plugin-react' // 1.0.7
export default defineConfig ({
plugins: [react()],
build: {
rollupOptions: {
output: {
manualChunks: {}
},
},
},
})
As per the documentation, I am not using worker-loader, I am trying to use the native way suggested by the webpack-5 documentation.
Below is the usage of the worker script in the main thread.
const worker = new window.Worker(
new URL("../workers/listOperation.worker.js", import.meta.url),
{
type: "module",
},
);
worker.postMessage({ list: hugeList, params: reqData });
worker.onerror = err => console.error(err);
worker.onmessage = e => {
const { list } = e.data;
// Usage of `list` from the response
worker.terminate();
};
return worker;
It works fine if there are no imports used in the script. But when I import any node modules (e.g. loadash/get) or any other function/constants from local, it does not work as the output webWorker bundle file doesn't transpile and bundle the imported code. It keeps the "import" statement as it is.
Below is the worker script (listOperation.worker.js)
import get from "lodash/get";
import { ANY_CONSTANT } from "../constants"; // This is some local constant
addEventListener("message", e => {
const { list, params } = e.data;
// Here I have some usage of `get` method from `lodash/get` and ANY_CONSTANT
self.postMessage({
list: list,
});
});
Webpack outputs the bundle file like below which won't be usable by the browser, if I put the /\.worker.js$/ pattern in the the exclude of babel-loader rule.
import get from "lodash/get";import{ANY_CONSTANT}from"../constants";addEventListener("message",(e=>{const{list:s,params:t,.......
And even if I don't put the /\.worker.js$/ pattern in the the exclude of babel-loader rule, the output bundle still doesn't include the implementation of get from lodash/get or the value of the constant. It just outputs it in cjs using require.
Also, I made use of asset module so that I can put the output file inside a directory, not directly in the root of the dist folder. Configuration changes in my webpack.config looks like this.
module.exports = {
entry: {...},
module: {
rules: [
{
test: /\.js$/,
exclude: [/(node_modules)/, /\.worker.js$/],
use: {
loader: "babel-loader", // This uses the config defined in babel.config.js
},
},
{
test: /\.worker.js$/,
exclude: /(node_modules)/,
type: "asset/resource",
generator: {
filename: "js/workers/[hash][ext][query]",
},
},
],
},
}
I'm trying to use a package: quoters
this is app.svelte file. the same code works fine for react and other places in JS.
<script>
import Quote from "quoters";
let quote = "";
let category = "QUOTE";
function handleCategory() {
quote = new Quote(category).get();
}
</script>
when I am trying to run the code. i get the following error: "Uncaught ReferenceError: exports is not defined". I checked the line it is referencing to.
it contains definition for exports inside the quoters package.
Object.defineProperty(exports, "__esModule", { value: true });
I've tried everything like babel and commonjs. maybe i am missing something. please can anyone tell me what is the issue and also fix for this issue.
my rollup config file for reference:
import svelte from "rollup-plugin-svelte";
import commonjs from "#rollup/plugin-commonjs";
import resolve from "#rollup/plugin-node-resolve";
import livereload from "rollup-plugin-livereload";
import { terser } from "rollup-plugin-terser";
import css from "rollup-plugin-css-only";
import copy from "rollup-plugin-copy";
import json from "#rollup/plugin-json";
import builtins from "rollup-plugin-node-builtins";
import globals from "rollup-plugin-node-globals";
import replace from "#rollup/plugin-replace";
import babel from "#rollup/plugin-babel";
import nodePolyfills from "rollup-plugin-node-polyfills";
const production = !process.env.ROLLUP_WATCH;
function serve() {
let server;
function toExit() {
if (server) server.kill(0);
}
return {
writeBundle() {
if (server) return;
server = require("child_process").spawn(
"npm",
["run", "start", "--", "--dev"],
{
stdio: ["ignore", "inherit", "inherit"],
shell: true,
}
);
process.on("SIGTERM", toExit);
process.on("exit", toExit);
},
};
}
export default {
input: "src/main.js",
output: {
sourcemap: true,
format: "iife",
name: "app",
file: "public/build/bundle.js",
exports: "auto",
},
moduleContext: {
"node_modules/quoters/dist/parseData.js": "window",
},
plugins: [
svelte({
compilerOptions: {
// enable run-time checks when not in production
dev: !production,
},
}),
nodePolyfills(),
json(),
copy({
targets: [
{
src: "node_modules/bootstrap/dist/**/*",
dest: "public/vendor/bootstrap",
},
],
}),
// we'll extract any component CSS out into
// a separate file - better for performance
css({ output: "bundle.css" }),
// If you have external dependencies installed from
// npm, you'll most likely need these plugins. In
// some cases you'll need additional configuration -
// consult the documentation for details:
// https://github.com/rollup/plugins/tree/master/packages/commonjs
babel({
exclude: "node_modules/**", // only transpile our source code
}),
resolve({
dedupe: ["svelte"],
browser: true,
preferBuiltins: false,
}),
commonjs(),
// In dev mode, call `npm run start` once
// the bundle has been generated
!production && serve(),
// Watch the `public` directory and refresh the
// browser on changes when not in production
!production && livereload("public"),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser(),
],
watch: {
clearScreen: false,
},
};
I've tried several guides and many configurations, but can't get my rollup, postcss, and svelte bundle process to work quite right.
Right now the svelte plugin is extracting the css from my .svelte files and emitting it to the posctcss plugin, but it's doing it one file at a time instead of the entire bundle. This makes it so some functions in the purgecss and nanocss postcss plugins don't completely work because they need the entire bundle to do things like remove duplicate/redundant/unused css rules.
// rollup.config.js
import svelte from 'rollup-plugin-svelte'
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
import livereload from 'rollup-plugin-livereload'
import { terser } from 'rollup-plugin-terser'
import rollup_start_dev from './rollup_start_dev'
import builtins from 'rollup-plugin-node-builtins'
import postcss from 'rollup-plugin-postcss'
const production = !process.env.ROLLUP_WATCH
export default {
input: 'src/main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/bundle.js',
},
plugins: [
svelte({
dev: !production,
emitCss: true,
}),
postcss({
extract: true,
sourceMap: true,
}),
builtins(),
resolve({
browser: true,
dedupe: importee =>
importee === 'svelte' || importee.startsWith('svelte/'),
}),
commonjs(),
!production && rollup_start_dev,
!production && livereload('public'),
production && terser(),
],
watch: {
clearScreen: false,
},
}
// postcss.config.js
const production = !process.env.ROLLUP_WATCH
const purgecss = require('#fullhuman/postcss-purgecss')
module.exports = {
plugins: [
require('postcss-import')(),
require('tailwindcss'),
require('autoprefixer'),
production &&
purgecss({
content: ['./src/**/*.svelte', './src/**/*.html', './public/**/*.html'],
css: ['./src/**/*.css'],
whitelistPatterns: [/svelte-/],
defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || [],
}),
production &&
require('cssnano')({
preset: 'default',
}),
],
}
How can I have rollup pass the entire bundle.css to postcss instead of one "file" at a time?
I had the same problem, preprocess goes file by file, so I had to actually include all my mixins and vars in every file, which is absolutely not a good solution.
So for me the first solution was to remove postcss from sveltePreprocess, not emit the css file and to use postcss on the css bundle, that you get in the css function from svelte.
You can then or (1) use postcss directly in the css function of svelte, and then emit the resulting css file in your dist directory, or (2) you can emit this file in a CSS directory, and have postcss-cli watch this directory and bundle everything
Solution 1
// rollup.config.js
// rollup.config.js
import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import postcss from 'postcss';
import postcssConfig from './postcss.config.js';
const postcssPlugins = postcssConfig({});
const postcssProcessor = postcss(postcssPlugins);
export default {
input: 'src/main.js',
output: {
file: 'public/bundle.js',
format: 'iife',
},
plugins: [
svelte({
emitCss: false,
css: async (css) => {
const result = await postcssProcessor.process(css.code);
css.code = result.css;
css.write('public/bundle.css');
},
}),
resolve(),
],
};
and my postcss.config.js which returns a function that return an array of plugins:
export default (options) => {
const plugins = [
require('postcss-preset-env')()
];
if (options.isProd) {
plugins.push(require('cssnano')({ autoprefixer: false }));
}
return plugins;
};
Solution 2
// rollup.config.js
import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
export default {
input: 'src/main.js',
output: {
file: 'public/bundle.js',
format: 'iife',
},
plugins: [
svelte({
emitCss: false,
css: async (css) => {
css.write('css/svelte-bundle.css');
},
}),
resolve(),
],
};
// package.json
{
//...
"scripts": {
"dev": "npm-run-all --parallel js:watch css:watch",
"js:watch": "rollup -c -w",
"css:watch": "postcss css/app.css --dir dist/ --watch",
},
}
/* css/app.css */
#import 'vars.css';
#import 'mixins.css';
/* all other code ... */
/* and svelte-bundle, which will trigger a bundling with postcss everytime it is emitted */
#import 'svelte-bundle.css';
Conclusion
All in all, I don't like this methods, for exemple because I can't use nesting, as svelte throws an error if the css is not valid.
I would prefer being able to use rollup-plugin-postcss after rollup-plugin-svelte, with emitCss set to false and the possibility to use rollup's this.emitFile in svelte css function, because since once the bundled file is emitted, we should be able to process it.
It seems there are some issues talking about using emitfile, let's hope it will happen sooner than later https://github.com/sveltejs/rollup-plugin-svelte/issues/71
Can't say for sure, but when i compare your setup with mine the most striking difference is that i have:
css: css => {
css.write('public/build/bundle.css');
}
in the svelte options additionally.
My whole svelte option looks like this:
svelte({
preprocess: sveltePreprocess({ postcss: true }),
dev: !production,
css: css => {
css.write('public/build/bundle.css');
}
})
Note, i'm using sveltePreprocess which would make your postcss superfluous, but i don't think that is causing your issue.
I use webpack4, Extracting css using extract-text-webpack-plugin.
// ...
plugins: [
...htmls,
new ExtractTextPlugin('style/[name].[contenthash].css')
]
// index.css
// home.css
// about.css
It's all okay, Correct output.
Until I use optimization.splitChunks to extract the react and react-dom frameworks.
optimization: {
splitChunks: {
cacheGroups: {
commons: {
name: 'react',
chunks: 'all'
}
}
}
}
Now he can only output a css file named react, And includes all the components in the style.
I don't know why this is, how can I solve it?