I'm building a multi-page typescript webapp using open-WC as the base, which came with rollup as the default build tool. We have a handful of assets included in our application and I'm having trouble getting them into the actual dist folder without using @web/rollup-plugin-copy
- from basic googling, this seems to be something that plenty of rollup plugins support doing automatically, but I've bashed my head against this for a while without success.
The image reference is a pretty simple <img alt="image" src="assets/image.jpg" />
in an html template literal in a .ts file, which is used as a custom HTML element in a bunch of other pages
The component:
@customElement("this-component")
export class ThisComponent extends connect(store)(LitElement) {
[...]
render() {
[...]
return html`
[...]
<img alt="image" src="assets/image.jpg" />
[...]
`;
}
[...]
}
The usage:
<!DOCTYPE html>
<html lang="en">
[...]
<body>
<the-component></the-component>
<another-component></another-component>
<script type="module" src="./out-tsc/src/TheComponent.js"></script>
<script type="module" src="./out-tsc/src/AnotherComponent.js"></script>
</body>
</html>
This may already be wildly incredibly obvious but I'm not an experienced frontend dev so I imagine huge parts of this are not idiomatic. Any advice on how to get this working in a more "natural" way would be ideal.
Project structure:
root/
<various html files>
styles.css
favicon.ico
<various project root files>
rollup.config.mjs
web-dev-server.config.mjs
tsconfig.json
- assets
- fonts
- src
-- *.ts
npm scripts:
"start": "tsc --outDir ./out-tsc && concurrently -k -r \"tsc --outDir ./out-tsc --watch --preserveWatchOutput\" \"wds\""
"build": "rimraf dist && tsc && cp -r dist/out-tsc . && rollup -c rollup.config.mjs && npm run analyze -- --exclude dist",
"start:build": "web-dev-server --root-dir dist --app-index index.html --open",
tsconfig:
{
"compilerOptions": {
"target": "es2018",
"module": "esnext",
"moduleResolution": "node",
"noEmitOnError": true,
"lib": ["es2017", "dom"],
"strict": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"importHelpers": true,
"outDir": "dist/out-tsc",
"sourceMap": true,
"inlineSources": true,
"rootDir": "./",
"incremental": true
},
"include": ["**/*.ts", "src/declaration.d.ts", "src/assets", "src/fonts"]
}
Rollup config:
import { copy } from "@web/rollup-plugin-copy";
import path from "path";
import nodeResolve from "@rollup/plugin-node-resolve";
import replace from "@rollup/plugin-replace";
import babel from "@rollup/plugin-babel";
import typescript from "@rollup/plugin-typescript";
import { importMetaAssets } from "@web/rollup-plugin-import-meta-assets";
import summary from "rollup-plugin-summary";
import terser from "@rollup/plugin-terser";
import { generateSW } from "rollup-plugin-workbox";
import image from "@rollup/plugin-image";
import { rollupPluginHTML as html } from "@web/rollup-plugin-html";
import dotenv from "dotenv";
dotenv.config();
const nodeEnv = process.env.NODE_ENV || "production";
const appUrl = process.env.APP_URL;
if (!appUrl) {
throw new Error("APP_URL environment variable required to build");
}
export default {
input: "*.html",
output: {
entryFileNames: "[hash].js",
chunkFileNames: "[hash].js",
assetFileNames: "[hash][extname]",
format: "es",
dir: "dist",
sourcemap: true
},
preserveEntrySignatures: false,
plugins: [
image(),
/** Resolve bare module imports */
nodeResolve(),
typescript(),
/** Enable using HTML as rollup entrypoint */
html({
minify: true,
injectServiceWorker: true,
serviceWorkerPath: "dist/sw.js"
}),
/** Minify JS */
terser(),
/** Bundle assets references via import.meta.url */
importMetaAssets(),
/** Compile JS to a lower language target */
babel({
babelHelpers: "bundled",
presets: [
[
"@babel/preset-env",
{
targets: [
"last 3 Chrome major versions",
"last 3 Firefox major versions",
"last 3 Edge major versions",
"last 3 Safari major versions"
],
modules: false,
bugfixes: true
}
]
],
plugins: [
[
"babel-plugin-template-html-minifier",
{
modules: { lit: ["html", { name: "css", encapsulation: "style" }] },
failOnError: false,
strictCSS: true,
htmlMinifier: {
collapseWhitespace: true,
conservativeCollapse: true,
removeComments: true,
caseSensitive: true,
minifyCSS: true
}
}
]
]
}),
/** Create and inject a service worker */
generateSW({
globIgnores: ["polyfills/*.js", "nomodule-*.js"],
// where to output the generated sw
swDest: path.join("dist", "sw.js"),
// directory to match patterns against to be precached
globDirectory: path.join("dist"),
// cache any html js and css by default
globPatterns: ["**/*.{html,js,css,webmanifest}"],
skipWaiting: true,
clientsClaim: true,
runtimeCaching: [{ urlPattern: "polyfills/*.js", handler: "CacheFirst" }]
}),
replace({
"process.env.NODE_ENV": JSON.stringify(nodeEnv),
"process.env.APP_URL": JSON.stringify(appUrl),
preventAssignment: true
}),
copy({
patterns: ["assets/**/*", "fonts/**/*"]
}),
summary()
]
};