How to configure Svelte Kit to request assests from a CDN in production? - assets

I deployed my Svelte Kit app on K8s and noticed that things like Favicons weren't loading. After some investigation, it turns out that Nginx Ingress Controller is not designed to serve those files:
The Ingress Controller is not designed to serve static content. If you'd like to serve static content, consider having a separate Service for it.
See #257 (comment) — Github issue comment, 2018.
thus, splitting my statics assets from the rest of my Svelte Kit app is needed... but I'm a bit lost on how to correctly do that: After building my app I can see the static folder there, then I should just upload that to a CDN, but what about Svelte itself? Is changing %svelte.assets% good enough to signal the location of static assets in Svelte Kit?
This is my current Svelte Kit configuration:
/* jshint esversion: 11 */
// Imports
import preprocess from 'svelte-preprocess';
import { resolve, dirname } from 'path';
import { fileURLToPath } from 'url';
import svg from '#poppanator/sveltekit-svg';
// Adapters
import adapter from '#sveltejs/adapter-node';
// Custom require function as replacement for the require from the commonJS in ES Module
// Custom __dirname as replacement for the __dirname from the commonJS in ES Module
const __dirname = dirname(fileURLToPath(import.meta.url)); // jshint ignore:line
const options = JSON.stringify(process.env.OPTIONS || '{}');
/** #type {import('#sveltejs/kit').Config} */
const config = {
preprocess: [
preprocess({
postcss: true,
preserve: ['ld+json', 'module'],
typescript: true,
}),
],
kit: {
adapter: adapter({ ...options, out: './.svelte-kit/nodejs/build/' }),
prerender: {
crawl: true,
enabled: true,
onError: 'fail',
entries: ['*'],
},
vite: () => ({
resolve: {
alias: {
$stores: resolve(__dirname, './src/stores'),
$components: resolve(__dirname, './src/lib/shared/components'),
$ui: resolve(__dirname, './src/lib/shared/ui'),
$layouts: resolve(__dirname, './src/lib/layouts'),
$shared: resolve(__dirname, './src/lib/shared'),
$models: resolve(__dirname, './src/lib/models'),
$data: resolve(__dirname, './src/lib/data'),
$core: resolve(__dirname, './src/lib/core'),
$utils: resolve(__dirname, './src/lib/utils'),
$environment: resolve(__dirname, './src/environments'),
},
},
envPrefix: ['VITE_', 'SVELTEKIT_STARTER_'],
plugins: [svg({ type: 'src', svgoOptions: svg.OptimizeOptions })],
}),
},
};
export default config;

Related

Changes in yaml files displays only after restart metro server

I am using babel-plugin-content-transformer library to use yaml files. I need to use them instead of json in react-i18next localisation library. It works fine but there is one issue: when i change some translation in .yml file the changes displays only after I restart metro server. Reload also doesn't works, I see no changes after I pressed 'r' to reload server. To see changes I need to stop and after that run server again.
My babel.config.js is
module.exports = function (api) {
const presets = [['module:metro-react-native-babel-preset']]
const plugins = [
['module-resolver', {
root: ['./'],
extensions: ['.ios.js', '.android.js', '.js', '.ts', '.tsx', '.json'],
alias: {
'#bluecentury': './src',
}
}],
['module:react-native-dotenv', {
'moduleName': '#vemasys/env',
}],
['react-native-reanimated/plugin'],
['content-transformer', {
transformers: [{
file: /\.ya?ml$/,
format: 'yaml'
}]
}]
]
api.cache(false)
return {
presets,
plugins
}
}
my i18n.ts is
import i18n from 'i18next'
import {initReactI18next} from 'react-i18next'
import translationEN from './enyaml.yml'
// import translationEN from './en.json'
const resources = {
en: {
translation: translationEN,
},
}
i18n.use(initReactI18next).init({
resources,
lng: 'en',
fallbackLng: 'en',
interpolation: {
escapeValue: false,
},
})
When I'm using en.json everything works fine.

Production config Webpack build for a Rust, WebAssembly app to apache2 server

I have a Rust, Web assembly wasm app I need to deploy to an apache server. When upload the build, it doesn't run the software, it just shows a list of the files.
The build files look like this:
build
-0.bootstrap.js
-bootstap.js
-gfdgjkljlkjjiojohio.module.wasm
my webpack config looks like
const path = require('path');
module.exports = {
entry: "./bootstrap.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bootstrap.js",
},
mode: "development",
devServer: {
//host: "0.0.0.0",
port: 8080,
}
};
Where boostrap.js imports the main index.js file
// A dependency graph that contains any wasm must all be imported
// asynchronously. This `bootstrap.js` file does the single async import, so
// that no one else needs to worry about it again.
import("./index.js")
.catch(e => console.error("Error importing `index.js`:", e));
But when I deploy to my apache server, to my domain, I don't get the software running.
Why isn't it running?
I had to use HtmlWebpackPlugin and change my webpack config to this
module.exports = {
entry: './bootstrap.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
inject: true,
template: path.resolve(__dirname, 'index.html'),
}),
]
}

How to enable dotenv variables for a file inside the /public folder in a Vue project?

I have very little experience with configuring Webpack, and I'am a bit overwhelmed by this issue.
I've been working on a Vue2 project built on top of this boilerplate. The project has a folder called public which contains the entry point file index.html. Inside that index.html file I can normally access .env environment variables (e.g. process.env.VUE_APP_PAGE_TITLE).
I've included an HTML fragment inside the public folder, navbar.html, because I want it to be available for other applications via https://example.com/public/navbar.html. However, I cannot seem to get my environment variables working inside ./public/navbar.html even though they work just fine in ./public/index.html. I assume this is a problem with my webpack config.
I know I can edit my Webpack config by editing a file in my project root called vue.config.js. This file contains a configureWebpack object, but I have no idea how to make it enable environment variables inside ./public/navbar.html. Any help would be appreciated.
EDIT:
Here's my vue.config.js:
const HtmlWebpackPlugin = require('html-webpack-plugin');
function resolveClientEnv() {
const env = {};
Object.keys(process.env).forEach((key) => {
env[key] = process.env[key];
});
env.BASE_URL = '/';
return env;
}
module.exports = {
configureWebpack: {
plugins: [
new HtmlWebpackPlugin({
// This is the generated file from the build, which ends up in public/navbar.html
filename: 'navbar.html',
// This is the source file you edit.
template: 'public/navbar.html',
templateParameters: (compilation, assets, pluginOptions) => {
let stats;
return Object.assign({
// make stats lazy as it is expensive
get webpack() {
return stats || (stats = compilation.getStats().toJson());
},
compilation,
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options: pluginOptions,
},
}, resolveClientEnv());
},
}),
],
},
};
This is what my custom HTMLWebpackPlugin adds to the configuration according to vue inspect:
{
options: {
template: 'public/navbar.html',
templateContent: false,
templateParameters: function () { /* omitted long function */ },
filename: 'navbar.html',
hash: false,
inject: true,
compile: true,
favicon: false,
minify: 'auto',
cache: true,
showErrors: true,
chunks: 'all',
excludeChunks: [],
chunksSortMode: 'auto',
meta: {},
base: false,
title: 'Webpack App',
xhtml: false
},
childCompilerHash: undefined,
childCompilationOutputName: undefined,
assetJson: undefined,
hash: undefined,
version: 4
}
Use this standard plugin to generate navbar.html. https://github.com/jantimon/html-webpack-plugin.
If you read the docs, the templateParameters option is what you pass env variables to. Those variables will be available in navbar.html.
This is the same plugin that vue-cli uses for index.html. If you run the vue inspect command, you can see what options they provide to the plugin. You'll need to read the source code for resolveClientEnv() to see how it works.
Example:
/* config.plugin('html-portal') */
new HtmlWebpackPlugin(
{
templateParameters: (compilation, assets, pluginOptions) => {
// enhance html-webpack-plugin's built in template params
let stats
return Object.assign({
// make stats lazy as it is expensive
get webpack () {
return stats || (stats = compilation.getStats().toJson())
},
compilation: compilation,
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options: pluginOptions
}
}, resolveClientEnv(options, true /* raw */))
},
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true,
collapseBooleanAttributes: true,
removeScriptTypeAttributes: true
},
chunks: [
'chunk-vendors',
'chunk-common',
'portal'
],
template: 'C:\\Users\\Eric\\workspace\\arc-core\\portal\\client\\templates\\portal.html',
filename: 'portal.html',
title: 'Arc Portal'
}
),
That's a bit much, a minimal example would be:
new HtmlWebpackPlugin({
// This is the generated file from the build, which ends up in public/navbar.html
filename: 'navbar.html',
// This is the source file you edit.
template: 'templates/navbar.html',
templateParameters: {
MY_VAR: 'myVar'
}
}),

Webpack using wrong node_modules folder

I have a vue client project that uses a vue library project (the vue library project is also using some 3rd party packages like vue-material).
They are linked via the client project's Package.json like this "lib": "file:../lib" and I am importing components in the client project using import Comp from "lib/src/components/Comp";
The problem is that when I build the client project using Webpack, the files in my library use lib/node_modules/vue instead of node_modules/vue which causes double vue instancing.
Anyone has any idea why when I am using webpack build from the client folder, it looks for vue package in my library folder? and is there a way to get around that?
My webpack.config
"use strict";
const path = require("path");
const utils = require("./utils");
const config = require("../config");
const vueLoaderConfig = require("./vue-loader.conf");
function resolve(dir) {
return path.join(__dirname, "..", dir);
}
module.exports = {
entry: {
app: ["babel-polyfill", "./src/main.js"]
},
output: {
path: config.build.assetsRoot,
filename: "[name].js",
publicPath: process.env.NODE_ENV === "production" ? config.build.assetsPublicPath : config.dev.assetsPublicPath
},
resolve: {
extensions: [".js", ".vue", ".json"],
alias: {
vue$: "vue/dist/vue.esm.js",
"#": resolve("src"),
src: resolve("src"),
assets: resolve("src/assets"),
components: resolve("src/components"),
utilities: resolve("src/utilities"),
directives: resolve("src/directives"),
plugins: resolve("src/plugins"),
data: resolve("src/data"),
"vuex-store": resolve("src/store"),
"lib": resolve("node_modules/lib")
}
},
module: {
rules: [
{
test: /\.(js|vue)$/,
loader: "eslint-loader",
enforce: "pre",
include: [resolve("src")],
options: {
formatter: require("eslint-friendly-formatter")
}
},
{
test: /\.vue$/,
loader: "vue-loader",
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: "babel-loader",
include: [resolve("src"), resolve("../lib/src")]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: "url-loader",
options: {
limit: 10000,
name: utils.assetsPath("img/[name].[hash:7].[ext]")
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: "url-loader",
options: {
limit: 10000,
name: utils.assetsPath("media/[name].[hash:7].[ext]")
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: "base64-font-loader",
options: {
limit: 10000,
name: utils.assetsPath("fonts/[name].[hash:7].[ext]")
}
},
{
test: /\.ico$/,
loader: "file-loader?name=[name].[ext]"
}
]
}
};
My client's main entry
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
// Core Imports
import Vue from 'vue'
import App from './App'
// Plugins
import { ComponentsPlugin } from "lib/src/components";
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
As I ran out of solutions to this problem I decided to debug webpack's compiler.
It seems webpack uses a library called 'enhanced-resolve' (We actually send parameters to that library in our webpack's resolve:{} section). And using the resolve.alias property we can redirect any require to any folder we want.
So to redirect any require("vue") to my own vue.esm.js, all I need is to add an entry like this (I originally had that line but it was pointing to a relative path rather than an absolute path):
resolve: {
alias: {
vue$: resolve("node_modules/vue/dist/vue.esm.js"),
}
}
Note the $ at the end of the library name, It signals enhanced-resolve that the package only has 1 module so any require whos name is "vue" or a sub-directory of "vue" should be parsed using the alias.
Enhanced-Resolve - ResolverFactory.js
if(/\$$/.test(alias)) {
onlyModule = true;
....
}
Enhanced-Resolve - AliasPlugin.js
// InnerRequest is the path (vue/dist/vue.esm.js)
// name is the alias name without the $ (vue)
if(innerRequest === name || (!onlyModule && startsWith(innerRequest, name + "/"))) {
continue resolving....
}

Cannot add locales files from aurelia-i18n to dist folder for production

I have been using aurelia Framework and the aurelia-i18n plugin for my translations. Everything works fine in local dev but when I build my app (npm run build) the locales folders are not included.
Here is my main.js plugin setup:
.plugin(PLATFORM.moduleName('aurelia-i18n'), (instance) => {
let aliases = ['t', 'i18n'];
TCustomAttribute.configureAliases(aliases);
instance.i18next.use(Backend);
return instance.setup({
fallbackLng: 'en',
whitelist: ['en', 'es'],
preload: ['en', 'es'],
ns: 'translation',
attributes: aliases,
lng: 'en',
debug: true,
backend: {
loadPath: 'src/locales/{{lng}}/{{ns}}.json',
}
});
})
In webpack module rules I have this:
{
test: /\.json$/,
loader: 'json', // I have also tried json-loader
include: ['/locales/en', '/locales/es'],
},
Now, if I include the lang file in the webpack module dependencies the files do appear in the dist folder but are not accessible by the aurelia-i18n plugin as it is looking in the locales folder to find the files.
new ModuleDependenciesPlugin(
{
"aurelia-i18n": [
{ name: "locales/en/translation.json", chunk: "lang-en" },
{ name: "locales/es/translation.json", chunk: "lang-es" }
]
})
Any ideas?
Thank you in advance!
For production build we just copy the locales folder to dist.
This is how the webpack.config.js looks like
new CopyWebpackPlugin([
{
from: path.join(staticDir, 'locales'),
to: path.join(outDir, 'locales')
}
])
In the root of the project we have a folder called static that contains the locales folder with the translations.
And this is the function we use in main.js to configure i18n.
export function configureI18N(aurelia: Aurelia): void {
aurelia.use.plugin(PLATFORM.moduleName('aurelia-i18n'), (instance?: any) => {
// register backend plugin
instance.i18next.use(Backend);
const aliases = ['localization-key', 't', 'i18n'];
// add aliases for 't' attribute
TCustomAttribute.configureAliases(aliases);
TParamsCustomAttribute.configureAliases(['localization-key-value-params']);
// adapt options to your needs (see http://i18next.com/docs/options/)
// make sure to return the promise of the setup method, in order to guarantee proper loading
return instance.setup({
backend: { // <-- configure backend settings
loadPath: '../locales/{{lng}}.{{ns}}.json' // <-- XHR settings for where to get the files from
},
lng: 'en',
attributes: aliases,
fallbackLng: 'en',
lowerCaseLng: true
});
});
}