invalidate App cache Vite + vue3 + amplify - vue.js

I've made a vue app using vite and Amplify to deploy and host the application. Everything is great except that once amplify deploys a new version of the app, I have to manually clear the browser's cache to see the changes I have made.
So far I've tried a few things:
changing the version of the app in the package.json file -> didn't work
adding hash to files in the dist
And so far nothing has changed. When amplify builds and deploys the app, i still have to manually clear the browser cache in order to see the changes.
Another way to make it so that the browser fetches the files / clear its cache when I deploy a new version of the app would be really useful.
import { defineConfig } from 'vite'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
import Vue from '#vitejs/plugin-vue'
import Pages from 'vite-plugin-pages'
import generateSitemap from 'vite-plugin-pages-sitemap'
import PurgeIcons from 'vite-plugin-purge-icons'
import Icons from 'unplugin-icons/vite'
import IconsResolver from 'unplugin-icons/resolver'
import ViteRadar from 'vite-plugin-radar'
import Components from 'unplugin-vue-components/vite'
import ViteFonts from 'vite-plugin-fonts'
import vueI18n from '#intlify/vite-plugin-vue-i18n'
import { VitePWA } from 'vite-plugin-pwa'
import AutoImport from 'unplugin-auto-import/vite'
const SITEMAP_HOST = process.env.SITEMAP_HOST || 'http://localhost:3000/'
//const hash = Math.floor(Math.random() * 90000) + 10000
export default defineConfig({
// Project root directory (where index.html is located).
root: process.cwd(),
// Base public path when served in development or production.
base: '/',
// Directory to serve as plain static assets.
publicDir: 'public',
// Adjust console output verbosity.
logLevel: 'info',
// development server configuration
server: {
// Vite 3 now defaults to 5173, but you can override it with the port option.
port: 3000,
},
// Will be passed to #rollup/plugin-alias as its entries option.
resolve: {
alias: [
{
find: '/#src/',
replacement: `/src/`,
},
{
find: '/#/',
replacement: `/`,
},
{
find: './runtimeConfig',
replacement: './runtimeConfig.browser',
},
],
},
optimizeDeps: {
exclude: ['#vueuse/core', '#vueuse/components'],
include: [
'h3',
'plyr',
'vue-scrollto',
'vue-marquee-text-component',
'cobe',
'vue3-carousel',
'vue3-popper',
'vue-my-photos',
'vue3-markdown-it',
'vue-accessible-color-picker',
'prismjs',
'vue-prism-component',
'#iconify/iconify',
'nprogress',
'#vueform/slider',
],
},
build: {
chunkSizeWarningLimit: Infinity,
/**
* Uncomment this section to build the demo with missing images
* Don't forget to remove this section when you replaced assets with yours
*/
// rollupOptions: {
// output: {
// entryFileNames: `[name]` + hash + `.js`,
// chunkFileNames: `[name]` + hash + `.js`,
// assetFileNames: `[name]` + hash + `.[ext]`,
// },
// },
},
plugins: [
// https://github.com/vitejs/vite/tree/main/packages/plugin-vue
Vue({
include: [/\.vue$/],
}),
/**
* vite-plugin-vue-i18n plugin does i18n resources pre-compilation / optimizations
*
* #see https://github.com/intlify/bundle-tools/tree/main/packages/vite-plugin-vue-i18n
*/
vueI18n({
compositionOnly: true,
include: resolve(
// #ts-ignore
dirname(fileURLToPath(import.meta.url)),
'./src/locales/**'
),
}),
/**
* vite-plugin-pages plugin generate routes based on file system
*
* #see https://github.com/hannoeru/vite-plugin-pages
*/
Pages({
pagesDir: [
{
dir: 'src/pages',
baseRoute: '',
},
],
onRoutesGenerated: (routes) =>
generateSitemap({
routes,
hostname: SITEMAP_HOST,
}),
}),
/**
* unplugin-auto-import allow to automaticaly import modules/components
*
* #see https://github.com/antfu/unplugin-auto-import
*/
AutoImport({
dts: true,
imports: ['vue', 'vue-router', '#vueuse/core', 'pinia'],
}),
/**
* unplugin-vue-components plugin is responsible of autoloading components
* documentation and md file are loaded for elements and components sections
*
* #see https://github.com/antfu/unplugin-vue-components
*/
Components({
directoryAsNamespace: false,
dirs: ['src/components', 'src/layouts'],
extensions: ['vue'],
dts: true,
include: [/\.vue$/, /\.vue\?vue/],
resolvers: [IconsResolver()],
}),
/**
* vite-plugin-fonts plugin inject webfonts from differents providers
*
* #see https://github.com/stafyniaksacha/vite-plugin-fonts
*/
ViteFonts({
google: {
families: [
/*{
name: 'Poppins',
styles: 'ital,wght#0,300;0,400;1,500',
},*/
{
name: 'Roboto',
styles: 'ital,wght#0,100;0,300;0,400;0,500;0,700;1,300;1,400',
},
{
name: 'Montserrat',
styles: 'ital,wght#0,100;0,300;0,400;0,500;0,600;0,700;1,300;1,400',
},
],
},
}),
/**
* unplugin-icons plugin injects icons from differents providers
*
* #see https://github.com/antfu/unplugin-icons
*/
Icons({ compiler: 'vue3' }),
/**
* vite-plugin-purge-icons plugin is responsible of autoloading icones from multiples providers
*
* #see https://icones.netlify.app/
* #see https://github.com/antfu/purge-icons/tree/main/packages/vite-plugin-purge-icons
*/
PurgeIcons(),
/**
* vite-plugin-radar plugin inject snippets from analytics providers
*
* #see https://github.com/stafyniaksacha/vite-plugin-radar
*/
ViteRadar({
enableDev: true,
gtm: {
id: 'HIDDEN',
},
}),
/**
* vite-plugin-pwa generate manifest.json and register services worker to enable PWA
*
* #see https://github.com/antfu/vite-plugin-pwa
*/
VitePWA({
base: '/',
includeAssets: [
'android-chrome-192x192.png',
'android-chrome-512x512.png',
'apple-touch-icon.png',
'favicon-16x16.png',
'favicon-32x32.png',
'favicon.ico',
'robots.txt',
'mstile-150x150.png',
],
manifest: {
name: 'APPLICATION NAME HIDDEN',
short_name: 'HIDDEN',
start_url: '/?utm_source=pwa',
display: 'standalone',
theme_color: '#ffffff',
background_color: '#ffffff',
icons: [
{
src: 'android-chrome-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: 'android-chrome-512x512.png',
sizes: '512x512',
type: 'image/png',
},
{
src: 'android-chrome-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any maskable',
},
],
},
}),
],
})

Related

How to use tailwindcss in a custom Nuxt3 module?

I am building a custom Nuxt3 module and want to use tailwindcss to style
my components.
However, I am having trouble setting up tailwindcss for my module.
I tried to set it up, like I would with a normal css file:
In the 'src/' folder I have the follwing components:
'runtime/css/tailwind.css':
#import "tailwindcss/base";
#import "tailwindcss/components";
#import "tailwindcss/utilities";
'runtime/tailwind.config.js':
import defaultTheme from ("tailwindcss/defaultTheme")
module.exports = {
content: {
files: [
"./components/**/*.{vue,js}",
"./layouts/**/*.vue",
"./pages/**/*.vue",
"./plugins/**/*.{js,ts}",
"./modules/**/*.{js,ts,vue}"
],
},
theme: {
extend: {
fontFamily: {
sans: ['"Inter var"', ...defaultTheme.fontFamily.sans],
},
},
},
variants: {
extend: {},
}
};
'module.ts':
import { resolve } from 'path'
import { fileURLToPath } from 'url'
import { defineNuxtModule, addPlugin, addComponent } from '#nuxt/kit'
export interface ModuleOptions {
css: boolean
}
export default defineNuxtModule<ModuleOptions>({
meta: {
name: '#nuxt-module/polkadotjs-wallet',
configKey: 'polkadotjs-wallet'
},
defaults: {
css: true,
},
setup (options, nuxt) {
const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url))
nuxt.options.build.transpile.push(runtimeDir)
// add the plugin
addPlugin(resolve(runtimeDir, 'plugin'))
// add components
const componentsDir = resolve(runtimeDir, "components")
addComponent({
name: "Hello",
filePath: resolve(componentsDir, "Hello.vue")
})
if(options.css) {
nuxt.options.css.push(resolve(runtimeDir, "css/tailwind.css"))
}
}
})
While this approach works to use normal css styling, I cannot make tailwind work like that.
Running it like this does not give me an error, but it also does not enable me to use tailwind.
I think I find a way, but I'm just discovering Nuxt 3.
Maybe my answer won't be perfect, but as far as I read the documentation and the #nuxtjs/tailwindcss code, that's all I found to work.
move your runtime/css/tailwind.css to runtime/tailwind.css
I'm not sure this file is really useful, as there is a default one provided by #nuxtjs/tailwindcss (see in your node_modules/#nuxtjs/tailwindcss/dist/runtime/tailwind.css)
update your tailwind.config.js for content property. It's an array of string for me. Actually, your paths are relatives. But in your app, these paths will take the components app and not the one of your module. You need to give absolute paths.
import defaultTheme from ("tailwindcss/defaultTheme")
import { fileURLToPath } from 'node:url'
const srcDir = fileURLToPath(new URL('../', import.meta.url))
/** #type {import('tailwindcss').Config} */
export default {
content: [
srcDir + '/**/*.{js,ts,vue}', // or separate in folders ?
],
theme: {
extend: {
fontFamily: {
sans: ['"Inter var"', ...defaultTheme.fontFamily.sans],
},
},
},
variants: {
extend: {},
}
};
last part, but the most important, you need to update your module.ts. I would write yours like this :
async setup (options, nuxt) {
const runtimeDir = fileURLToPath(new URL('../src/runtime', import.meta.url))
/**
* Here, you use the installModule to specify that
* your module USE the #nuxtjs/tailwindcss module.
* I think this is the way to add the tailwind module
* to your playground, or the app that will use your module
*/
await installModule('#nuxtjs/tailwindcss', {
/**
* Here, you specify where your config is.
* By default, the module have a configPath relative
* to the current path, ie the playground !
* (or the app using your module)
*/
configPath: resolve(runtimeDir, 'tailwind.config'),
})
// add components
const componentsDir = resolve(runtimeDir, "components")
addComponent({
name: "Hello",
filePath: resolve(componentsDir, "Hello.vue")
})
/**
* for these lines, I don't know if they are still useful
* please check them before keeping them :-)
*/
const runtimeDir = fileURLToPath(new URL('./runtime', import.meta.url))
nuxt.options.build.transpile.push(runtimeDir)
// add the plugin
addPlugin(resolve(runtimeDir, 'plugin'))
if(options.css) {
nuxt.options.css.push(resolve(runtimeDir, "css/tailwind.css"))
}
}
Does this help you ?
References :
installModule for Nuxt3 Modules
default configPath for #nuxtjs/tailwindcss

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

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;

ESM library generated with rollup-plugin-postcss throws Cannot find module '../node_modules/style-inject/dist/style-inject.es.js'

We are maintaining an internal library which is exporting ESM modules using Rollup. We have just recently switched to using CSS modules, which we have set with rollup-plugin-postcss. We want to inject these styles into the head rather than have an external file.
Our built bundle generates the ESM file with:
import styleInject from '../node_modules/style-inject/dist/style-inject.es.js';
Our consuming library then fails with
Uncaught Error: Cannot find module '../node_modules/style-inject/dist/style-inject.es.js'
I would expect the ESM export to import styleInject from 'style-inject' and style-inject to be included in the package-lock.json as a dependency. What is the correct way of using CSS Modules and injecting into the head for the consumer of a library?
rollup.config.js
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import babel from '#rollup/plugin-babel';
import json from '#rollup/plugin-json';
import postcss from 'rollup-plugin-postcss';
import pkg from './package.json';
import fg from 'fast-glob';
import path from 'path';
export default [
{
input: 'src/index.js',
external: external(),
output: [
{
name: '#my/packageName',
file: pkg.module,
format: 'es',
sourcemap: true,
},
],
plugins: [
{
name: 'watch-external',
async buildStart() {
const files = await fg(['src/index.d.ts', 'playground/**/*']);
for (let file of files) {
this.addWatchFile(path.resolve(file));
}
},
},
json(),
postcss({
modules: true,
}),
babel({
exclude: /node_modules/,
babelHelpers: 'runtime',
babelrc: false,
presets: [
[
'#babel/preset-env',
{
modules: false,
useBuiltIns: 'entry',
corejs: 3,
targets: {
ie: 11,
},
},
],
'#babel/preset-react',
],
plugins: [
'#babel/plugin-transform-runtime',
'#babel/plugin-proposal-class-properties',
'#babel/plugin-proposal-export-namespace-from',
],
}),
commonjs(),
],
},
];
function external() {
const { dependencies = {}, peerDependencies = {} } = pkg;
const externals = [
...Object.keys(dependencies),
...Object.keys(peerDependencies),
];
return id =>
// match 'lodash' and 'lodash/fp/isEqual' for example
externals.some(dep => id === dep || id.startsWith(`${dep}/`));
}
There is a bug in the library where the import is relative rather than being the module name.
There is an open pr, but the library looks like it is no longer being maintained. There are two recommended solutions in the comments:
String replace the built output
Add a custom rollup plugin to do this
This is not a bug at all. It works properly because stylesInject is just a function from 'styles-inject' package, so we can allow it to be bundled. But it is not bundled because most likely you have this setting in your rollup.config.js:
external: [
/node_modules/
]
So, replace it with a code bellow and it will work:
external: (id) => {
if (/style-inject/.test(id)) return false;
if (/node_modules/.test(id)) return true;
return false;
},
also, you can write a regexp for it instead
You need to do 2 things:
Add style-inject dependency in package.json
"dependencies": {
"style-inject": "^0.3.0"
},
Add following plugin to rollup.config.js
const plugins = [
...
{
/**
* - https://github.com/egoist/rollup-plugin-postcss/issues/381#issuecomment-880771065
* - https://lightrun.com/answers/egoist-rollup-plugin-postcss-esm-library-generated-with-rollup-plugin-postcss-throws-cannot-find-module-node_modulesstyle-in
*/
name: 'Custom Rollup Plugin`',
generateBundle: (options, bundle) => {
Object.entries(bundle).forEach((entry) => {
// early return if the file we're currently looking at doesn't need to be acted upon by this plugin
if (!entry[0].match(/.*(.scss.js)$/)) {
return;
}
// this line only runs for .scss.js files, which were generated by the postcss plugin.
// depending on the use-case, the relative path to style-inject might need to change
bundle[entry[0]].code = entry[1].code.replace(
/\.\.?\/[^\n"?:*<>|]+\/style-inject\/dist\/style-inject.es.js/g,
'style-inject',
);
});
},
}
];
References:
https://github.com/egoist/rollup-plugin-postcss/issues/381#issuecomment-880771065
https://lightrun.com/answers/egoist-rollup-plugin-postcss-esm-library-generated-with-rollup-plugin-postcss-throws-cannot-find-module-node_modulesstyle-in

Run nuxt on https locally – problem with nuxt.config.js

I am trying to run nuxt locally with https to test some geolocation stuff.
(https://nuxtjs.org/, https://nuxtjs.org/api/nuxt)
I was following this tutorial:
https://www.mattshull.com/blog/https-on-localhost
And then I found this:
https://github.com/nuxt/nuxt.js/issues/146
Both links seem to describe pretty nicely how to run nuxt with server.js programmatically.
The thing is that in my nuxt.config.js I seem to have some problems.
I get the following error when runnung yarn dev:
/Users/USER/Documents/github/mynuxtrepo/nuxt.config.js:2
import { module } from 'npmmodule'
> SyntaxError: Unexpected token {
In my nuxt config I import a custom helper to generate localized routes. Not really important what it does but obviously it can't handle the import syntax.
I assume that the node version does not understand.
So how can I get it to run? Do I have to require everything instead of importing?
Or is my assumption wrong and the cause lies somewhere totally different?
Thank you for your help
Cheers.
------
Edit 1:
My nuxt config looks like this:
// eslint-disable-next-line prettier/prettier
import { generateLocalizedRoutes, generateRoutesFromData } from 'vuecid-helpers'
import config from './config'
// TODO: Add your post types
const postTypes = [{ type: 'pages' }, { type: 'posts', paginated: true }]
// TODO: Add your site title
const siteTitle = 'Title'
const shortTitle = 'short title'
const siteDescription = 'Page demonstrated with a wonderful example'
const themeColor = '#ffffff'
// TODO: Replace favicon source file in /static/icon.png (512px x 512px)
// eslint-disable-next-line prettier/prettier
const iconSizes = [32, 57, 60, 72, 76, 144, 120, 144, 152, 167, 180, 192, 512]
module.exports = {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: 'Loading…',
htmlAttrs: {
lang: config.env.DEFAULTLANG,
dir: 'ltr' // define directionality of text globally
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
// TODO: Check this info
{ name: 'author', content: 'Author' },
{ name: 'theme-color', content: themeColor },
// TODO: Add or remove google-site-verification
{
name: 'google-site-verification',
content: '...W1GdU'
}
],
link: []
},
/*
** env: lets you create environment variables that will be shared for the client and server-side.
*/
env: config.env,
/*
** Customize the progress-bar color
** TODO: Set your desired loading bar color
*/
loading: { color: '#0000ff' },
/*
** CSS
*/
css: ['#/assets/css/main.scss'],
/*
** Plugins
*/
plugins: [
{ src: '~/plugins/global.js' },
{ src: '~/plugins/throwNuxtError.js' },
{ src: '~/plugins/axios' },
{ src: '~/plugins/whatinput.js', ssr: false },
{ src: '~/plugins/i18n.js', injectAs: 'i18n' },
{ src: '~/plugins/vuex-router-sync' },
{ src: '~/plugins/vue-bows' },
{ src: '~/plugins/vue-breakpoint-component', ssr: false }
],
/*
** Modules
*/
modules: [
'#nuxtjs/axios',
'#nuxtjs/sitemap',
[
'#nuxtjs/pwa',
{
icon: {
sizes: iconSizes
},
// Override certain meta tags
meta: {
viewport: 'width=device-width, initial-scale=1',
favicon: true // Generates only apple-touch-icon
},
manifest: {
name: siteTitle,
lang: config.env.DEFAULTLANG,
dir: 'ltr',
short_name: shortTitle,
theme_color: themeColor,
start_url: '/',
display: 'standalone',
background_color: '#fff',
description: siteDescription
}
}
]
],
/*
** Workbox config
*/
workbox: {
config: {
debug: false,
cacheId: siteTitle
}
},
/*
** Axios config
*/
axios: {
baseURL: '/'
},
/*
** Generate
*/
generate: {
subFolders: true,
routes: [
...generateRoutesFromData({
langs: config.env.LANGS,
postTypes: postTypes,
dataPath: '../../../../../static/data',
bundle: 'basic',
homeSlug: config.env.HOMESLUG,
errorPrefix: config.env.ERROR_PREFIX
})
]
},
/*
** Build configuration
*/
build: {
extend(config, { isDev, isClient }) {
/*
** Run ESLINT on save
*/
if (isDev && isClient) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
})
}
}
},
/*
** Router
*/
router: {
linkActiveClass: 'is-active',
linkExactActiveClass: 'is-active-exact',
middleware: ['i18n'],
extendRoutes(routes) {
// extends basic routes (based on your files/folders in pages directory) with i18n locales (from our config.js)
const newRoutes = generateLocalizedRoutes({
baseRoutes: routes,
defaultLang: config.env.DEFAULTLANG,
langs: config.env.LANGS,
routesAliases: config.routesAliases
})
// Clear array
routes.splice(0, routes.length)
// Push newly created routes
routes.push(...newRoutes)
}
},
/*
** Sitemap Configuration
*/
sitemap: {
path: '/sitemap.xml',
hostname: config.env.FRONTENDURLPRODUCTION,
cacheTime: 1000 * 60 * 15,
generate: true,
routes: [
...generateRoutesFromData({
langs: config.env.LANGS,
postTypes: postTypes,
dataPath: '../../../../../static/data',
bundle: 'basic',
homeSlug: config.env.HOMESLUG,
errorPrefix: config.env.ERROR_PREFIX
})
]
}
}
You can see that the generateLocalizedRoutes and the generateRoutesFromData methods are used to generate localized routes and is also taking post json files to generate routes from data (:slug).
--------- Edit 2:
I got it to run eventually.
I had to require all parts within the nuxt.config.js instead of importing them. I also resolved issues with the certificates. So I thought it was all cool 🚀.
BUT!!! 🚧:
Then I found out that I had my config file used within my post template.
So I thought I would also require the file within my template:
Like const config = require('~/config').
But then I would get this error:
[nuxt] Error while initializing app TypeError: ""exports" is read-only"
After some research, I found that is probably a thing when using common.js require and module.exports together with ES6 import/export within my project. (Probably linked to: https://github.com/FranckFreiburger/vue-pdf/issues/1).
So how could I still use my config.js when running nuxt programmatically (with require) and then also within my app?
I am glad to hear any ideas on this...
Cheers
Well, just to close this:
My problem resulted from running nuxt as a node app, which does not understand ES6 import statements, which appeared in my nuxt config.
So I had to rewrite things to work with commons.js (require).
This works for now.
(I also tried to run babel-node when starting the server.js, but had no success. Does not mean this did not work, I just wasn't keen on trying harder).
Thanks for the comments.
cheers

Why are my js files being loaded as `index.html` instead of the actual js files created by webpack?

I'm creating a simple build from webpack, using typescript, jade, and stylus. When the final index.html file is spit out, however, it seems to think the js files are just the index.html file and not the actual js files bundled up by webpack and dynamically inserted at the bottom of the html body.
My project directory structure looks like this:
- dist (compiled/transpiled files)
- server
- dependencies
- index.js
- app.js
- app.[hash].js
- polyfills.[hash].js
- node_modules
- src
- server
- dependencies
- index.ts
- app.ts
- client (ng2 ts files)
- index.jade
This is my webpack build:
'use strict';
const webpack = require('webpack');
const path = require('path');
const HTMLWebpackPlugin = require('html-webpack-plugin');
const WebpackShellPlugin = require('webpack-shell-plugin');
const rootDir = __dirname;
/**
* Resolve paths so that we don't have to use relative paths when importing dependencies.
* Very helpful when scaling an application and changing the location of a file that my require another file
* in the same directory as the one it used to be in
*/
const pathResolves = [path.resolve(rootDir, 'src'), path.resolve(rootDir, 'node_modules')];
console.log('path', path.resolve(rootDir, 'src/server'));
module.exports = {
entry: {
'app': path.resolve(rootDir, 'src/client/main.ts'),
'polyfills': [
'core-js/es6',
'core-js/es7/reflect',
'zone.js/dist/zone'
]
},
output: {
path: path.resolve(rootDir, 'dist'),
filename: '[name].[hash].js'
},
module: {
rules: [
{
test: /\.component.ts$/,
use: [
{
loader: 'angular2-template-loader'
},
{
loader: 'ts-loader',
options: {
configFileName: path.resolve(rootDir, 'tsconfig.client.json')
}
}],
include: [path.resolve(rootDir, 'src/client')]
},
{
test: /\.ts$/,
use: [
{
loader: 'ts-loader',
options: {
configFileName: path.resolve(rootDir, 'tsconfig.client.json')
}
}
],
exclude: /\.component.ts$/
},
{
test: /\.jade$/,
use: ['pug-ng-html-loader']
},
{
test: /\.styl$/,
use: [
{ loader: 'raw-loader' },
{ loader: 'stylus-loader' }
]
}
]
},
resolve: {
extensions: ['.js', '.ts', '.jade', '.styl'],
modules: pathResolves
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
name: 'polyfills'
}),
new HTMLWebpackPlugin({
template: path.resolve(rootDir, 'dist/index.html')
}),
/**
* Define any environment variables for client
*/
new webpack.DefinePlugin({
APP_ENV: JSON.stringify(process.env.APP_ENVIRONMENT || 'development')
}),
/**
* This plugin is required because webpack 2.0 has some issues compiling angular 2.
* The angular CLI team implemented this quick regexp fix to get around compilation errors
*/
new webpack.ContextReplacementPlugin(
/angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
'./'
)
]
};
And finally, this is the src/server/app.ts file that serves up index.html:
import * as express from 'express';
import * as fs from 'fs';
import * as morgan from 'morgan';
import {
Config
}
from './dependencies/config';
export
function app(Container) {
const app = express();
const config: Config = Container.get(Config);
if (config.log.dev) {
app.use(morgan('combined'));
}
app.get('/', (req: express.Request, res: express.Response) => {
const indexPath: string = `dist/index.html`;
const encodeType: string = `utf-8`;
const html = fs.readFile(indexPath, encodeType, (err: Error, result: string) => {
if (err) {
return res.status(500).json(err);
}
return res.send(result);
});
});
return app;
}
The browser console shows the following 404 error messages (they're red in the browser console) when i go to localhost:3000:
GET http://localhost:3000/polyfills.9dcbd04127bb957ccf5e.js
GET http://localhost:3000/app.9dcbd04127bb957ccf5e.js
I know it's supposed to be getting the js files from dist/[file].[hash].js, but can't seem to make it work with webpack. Also, I should note that I set NODE_PATH to ./ in my gulp nodemon config. Any ideas why this isn't working?
Figured it out on my own. Forgot to add app.use(express.static('dist')) middleware to the app.ts file.