@allakando/allakando-web-ui
Version:
Allakando's web component library
148 lines (144 loc) • 5.52 kB
JavaScript
import { createLogger, defineConfig } from "vite"
import pkg from "./package.json"
import fs from "fs"
import sass from "sass"
import tsconfigPaths from "vite-plugin-tsconfig-paths";
const DIST_FOLDER = "dist"
const EXPORT_NAME = "AllakandoWebUI"
const LIBRARY_NAME = pkg.name
const VERSION = pkg.version
const AUTHOR = pkg.author
const DESCRIPTION = pkg.description
const BANNER = `/** @preserve @license @cc_on
* ----------------------------------------------------------
* ${LIBRARY_NAME} version ${VERSION}
* ${DESCRIPTION}
* Copyright (c) ${new Date().getFullYear()} ${AUTHOR}
* All Rights Reserved. MIT License
* https://mit-license.org/
* ----------------------------------------------------------
*/
`
//This allows us to overwrite certain warnings and errors that are irrelevant and that will spam the console otherwise.
const ignoredErrorPatterns = [
//Vite seems to do its code analysis before the resolveId hook is fired, causing incorrect logs
"Default and named imports from CSS files are deprecated.",
//Vite seems to do its code analysis before the resolveId hook is fired, causing incorrect logs
"Do not know how to load path: html:"
]
const logger = createLogger()
const originalWarning = logger.warn
const originalError = logger.error
logger.warn = (message, options) => {
if (!ignoredErrorPatterns.find(pattern => message.includes(pattern))) {
originalWarning(message, options)
}
}
logger.error = (message, options) => {
if (!ignoredErrorPatterns.find(pattern => message.includes(pattern))) {
originalError(message, options)
}
}
export default defineConfig({
customLogger: logger,
build: {
target: "esnext",
outDir: "./dist",
emptyOutDir: false,
minify: "terser",
lib: {
entry: "./src/index.js",
formats: ["es", "umd"],
name: EXPORT_NAME,
fileName: format => {
return format === "umd" ? "js/bundle.js" : "js/esm-bundle.js"
}
}
},
plugins: [
tsconfigPaths(),
{
//We want html imports and scss imports to be loaded as strings
//To accomplish this in vanilla Vite we would need to pollute the import statements with ?inline for .scss and ?raw for .html
//Using a transform plugins works in Rollup but for some reason not in build mode in vite. The "code" argument is empty for some file extensions.
//This plugin is a bit of a hack but solves it globally and isolates the logic for these imports within the vite config file
name: "inline-html-and-scss-imports",
enforce: "pre",
async resolveId(id, importer, options) {
const isHTML = id.match(/ako-.*\.html$/)
const isSCSS = id.match(/ako-.*\.scss(\?used)?$/) || id.match(/Stylesheets[/\\]External.*\.scss(\?used)?$/) //Vite adds ?used to some scss resources before this hook runs
if ((isHTML || isSCSS) && !options.isEntry) {
const res = await this.resolve(id, importer, {
skipSelf: true,
...options
})
if (!res || res.external) return res
return `${res.id}${isSCSS ? "?inline" : "?raw"}`
}
}
},
{
//After the JavaScript build has completed we build all the css assets manually
name: "build-stylesheets",
writeBundle() {
fs.mkdirSync(`${DIST_FOLDER}/css`, { recursive: true })
fs.readdirSync("./src/Stylesheets/External/").map(fileName => {
const css = sass.compile(`./src/Stylesheets/External/${fileName}`, {
style: "compressed",
sourceMap: false
})
fs.writeFileSync(`${DIST_FOLDER}/css/${fileName.replace(/.scss$/, "")}.css`, `${BANNER}${css.css}`, "utf-8")
})
//This file is created due to some standard behaviour in Vite that cannot be turned off. We can safely remove it.
fs.existsSync(`${DIST_FOLDER}/style.css`) && fs.rmSync(`${DIST_FOLDER}/style.css`)
return null
}
},
{
//Vite does not put the banner specified in rollupConfig at the very top of the file, but instead inside the UMD module.
//This plugin adds the banner at the very top when writing the bundle to disk so as to guarantee its always in the right place.
name: "add-banner",
apply: "build",
async writeBundle(_, bundle) {
for (const fileName of Object.entries(bundle)) {
const file = fileName[0]
if (_.dir.endsWith("dist") && file.match(/(css|js)(\\|\/).*\.(css|js)$/i)) {
const fileURL = `${DIST_FOLDER}/${file}`
let data = fs.readFileSync(fileURL, { encoding: "utf8" })
data = `${BANNER} ${data}`
fs.writeFileSync(fileURL, data)
}
}
}
},
{
//The SCSS files are built outside of the scope of the input entrypoint file, so we need to manually tell the watcher to include them.
name: "rebuild-on-css-changes",
buildStart() {
fs.readdirSync("./src/Stylesheets/External/").forEach(fileName => {
this.addWatchFile(`./src/Stylesheets/External/${fileName}`)
})
fs.readdirSync("./src/Stylesheets/Internal/").forEach(fileName => {
this.addWatchFile(`./src/Stylesheets/Internal/${fileName}`)
})
this.addWatchFile("./src/index.html")
return null
}
},
{
//This allows us to serve index.html from the root URL in the vanilla server even though it is located in src.
//One can also manually go to /src/index.html in the browser
name: "redirect-to-index-html",
configureServer(server) {
server.middlewares.use((req, res, next) => {
if (req.url === "/") {
req.url = "/src/index.html"
} else if (req.url.startsWith("/Stylesheets") || req.url.startsWith("/Assets")) {
req.url = `/src${req.url}`
}
next()
})
}
}
]
})