Windi CSS HMR not working for Svelte + vite app - yarnpkg

I created a Svelte project with Vite and added windicss. I am using Yarn as build tool. I added WindiCSS to vite using https://windicss.org/integrations/vite.html#install. It works fine when I start the project using,
yarn dev
But HMR (Hot Module Reload) for Windi CSS does not work. But when I kill the server and restart it picks up the Windi CSS changes. Even the Devtool changes are working fine, only HMR is not working.
package.json file,
{
"name": "svelte-in",
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"devDependencies": {
"#sveltejs/vite-plugin-svelte": "^1.0.0-next.11",
"svelte": "^3.37.0",
"vite": "^2.6.4",
"vite-plugin-windicss": "^1.4.11",
"windicss": "^3.1.9"
}
}
vite.config.js file,
import { defineConfig } from 'vite'
import { svelte } from '#sveltejs/vite-plugin-svelte'
import WindiCSS from 'vite-plugin-windicss'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
svelte(),
WindiCSS()
]
})
And main.js is,
import App from './App.svelte'
import 'virtual:windi.css'
import 'virtual:windi-devtools' // To enable windi in dev tools
const app = new App({
target: document.getElementById('app')
})
export default app
Not sure if I am missing anything else.

I had to put WindiCSS() before svelte() for HMR to work.

Related

Default Laravel + Vite configuration throws WebSocket connection to failed:

So Laravel decided to innovate once again and fix what was not broken, so Mix is gone and now default asset bundling goes with Vite.
I'm following the absolute default in their documentation to a bunch of front-end bugs and finally only several remained:
I use Laragon with SSL.
I haven't configured anything additional and my vite.config.js looks like this:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '#vitejs/plugin-vue';
export default defineConfig({
plugins: [
laravel({
input: 'resources/js/app.js',
refresh: true,
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
});
When I run npm run dev and visit the Laragon domain I get the following in the console:
client.ts:78 WebSocket connection to 'wss://127.0.0.1:5173/' failed.
client.ts:48 [vite] failed to connect to websocket.
your current setup:
(browser) 127.0.0.1:5173/ <--[HTTP]--> 127.0.0.1:5173/ (server)
(browser) 127.0.0.1:5173/ <--[WebSocket (failing)]--> 127.0.0.1:5173/ (server)
Check out your Vite / network configuration and https://vitejs.dev/config/server-options.html#server-hmr .
I guess I need to configure my actual domain somewhere? I tried doing that in a server object in the config, but it didn't help those errors.
PS: Now in my vue files I need to import including the .vue extension e.g. import Button from '#/Components/Button.vue' is there any way I can ommit the .vue like it was with Laravel Mix?
I haven't use laragon before, but if you have a custom domain, eg, like
http://cutom-domain.test, you need to tell vite to use the certificate like so;
In your vite.config.js, add a server key with the following configuration
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '#vitejs/plugin-vue';
import fs from 'fs';
import { homedir } from 'os';
import { resolve } from 'path';
// Ignore the protocol on the host, ie do not put "http"
const host = 'cutom-domain.test';
const viteServerConfig = host => {
let keyPath = resolve(homedir(), `.config/valet/Certificates/${host}.key`)
let certificatePath = resolve(homedir(), `.config/valet/Certificates/${host}.crt`)
if (!fs.existsSync(keyPath)) {
return {}
}
if (!fs.existsSync(certificatePath)) {
return {}
}
return {
hmr: {host},
host,
https: {
key: fs.readFileSync(keyPath),
cert: fs.readFileSync(certificatePath),
},
}
}
export default defineConfig({
server: viteServerConfig(host),
plugins: [
laravel({
input: 'resources/js/app.js',
refresh: true,
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
});
Credit to this blogpost that explains more - Making Vite and Valet play nice together
I don't know if it's still relevant, but looking in the source code of laravel-vite-plugin I found a way to solve this problem in a very simple way, without even changing the vite.config.js file.
Put these two variables in the .env file and set them with full path to .key and .crt files on your system:
VITE_DEV_SERVER_KEY='C:/laragon/etc/ssl/laragon.key'
VITE_DEV_SERVER_CERT='C:/laragon/etc/ssl/laragon.crt'
Do not change anything on vite.config.js file. Here is my (fresh install of laravel + jetstream w/ inertia and --ssr):
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '#vitejs/plugin-vue';
export default defineConfig({
plugins: [
laravel({
input: 'resources/js/app.js',
ssr: 'resources/js/ssr.js',
refresh: true,
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
});
And that's it. Run npm run dev and Vite will "magically" start a development server with https on.
VITE v4.0.4 ready in 1248 ms
➜ Local: https://laravel.test:5173/
➜ Network: https://192.168.1.2:5173/
➜ press h to show help
LARAVEL v9.48.0 plugin v0.7.3
➜ APP_URL: https://laravel.test/
Even though the configuration present in the official documentation also works, this way is much simpler, and the host, key and cert variables are not defined in the file, but they are dynamic reflecting the dev environment.
Hope this helps someone.
Here is the source where I found this, and you can also inspect in node_modules\laravel-vite-plugin\dist\index.js of your project.
When I do npm run build instead of regular npm run dev, the problem is gone. I guess, build mechanism is different for prod, so there is no WSS related errors in console.
So, in other words, perform a production Vite build and deploy it (if you are testing on a remote project).

How to instantiate a vue 3 application within Laravel without using a parent App.vue?

I am working in a Laravel 8.x application and have had vue 2.6.12 installed for the longest time. I am working on upgrading to use Vue 3 with laravel and am using with Webpack. I have updated my package.json scripts and have the following installed for Vue 3 compatability:
{
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production"
},
"devDependencies": {
"bootstrap": "^4.5.2",
"jquery": "^3.4",
"laravel-mix": "^6.0.39",
"laravel-mix-polyfill": "^2.0.0",
"vue-loader": "^16.8.3"
},
"dependencies": {
"#vue/compiler-sfc": "^3.2.24",
"jquery-validation": "^1.17.0",
"jquery.nicescroll": "^3.7.6",
"jquery.scrollto": "^2.1.2",
"mdb-vue-ui-kit": "^1.7.0",
"sass": "^1.21.0",
"sass-loader": "^7.1.0",
"vue": "^3.2.24",
"vue-moment": "^4.1.0",
"vue-router": "^4.0.12",
"vue-template-compiler": "^2.6.14",
}
}
I never had a main App.vue file as this started as a Laravel app and Vue was brought in later. Instead I have created (with vue 2 and vue3 now) the initial vue object in my /resources/js/app.js file. This just keyed off of a <div id="app"> located in my parent blade.php file. But now with Vue 3 I am unsure how to do this without manually adding an App.vue file. Is this needed or is there something else I can do to configure vue 3 instantiation in my Laravel app?
Vue 2 setup (working)
const router = new VueRouter({
mode: 'history',
routes: routes
});
const app = new Vue({
el: '#app',
router
});
Vue 3 instantiation attempt
import Vue, {createApp } from 'vue';
import router from './router/routes.js';
const app = new Vue({});
createApp(app)
.use(router)
.use(require('vue-moment'))
.use(VueToast)
.mount('#app')
Main error I get when running npm run dev
WARNING in ./resources/js/app.js 50:14-17
export 'default' (imported as 'Vue') was not found in 'vue' (possible exports: BaseTransition, Comment, EffectScope, Fragment, KeepAlive, ReactiveEffect, Static, Suspense, Teleport, Text, Transition, TransitionGroup, VueElement, callWithAsyncErrorHandling, callWithErrorHandling, camelize, capitalize, cloneVNode, compatUtils, compile, computed, createApp, createBlock, createCommentVNode, createElementBlock, createElementVNode, createHydrationRenderer, createPropsRestProxy, createRenderer, createSSRApp, createSlots, createStaticVNode, createTextVNode, createVNode, customRef, defineAsyncComponent, defineComponent, defineCustomElement, defineEmits, defineExpose, defineProps, defineSSRCustomElement, devtools, effect, effectScope, getCurrentInstance, getCurrentScope, getTransitionRawChildren, guardReactiveProps, h, handleError, hydrate, initCustomFormatter, initDirectivesForSSR, inject, isMemoSame, isProxy, isReactive, isReadonly, isRef, isRuntimeOnly, isVNode, markRaw, mergeDefaults, mergeProps, nextTick, normalizeClass, normalizeProps, normalizeStyle, onActivated, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onRenderTracked, onRenderTriggered, onScopeDispose, onServerPrefetch, onUnmounted, onUpdated, openBlock, popScopeId, provide, proxyRefs, pushScopeId, queuePostFlushCb, reactive, readonly, ref, registerRuntimeCompiler, render, renderList, renderSlot, resolveComponent, resolveDirective, resolveDynamicComponent, resolveFilter, resolveTransitionHooks, setBlockTracking, setDevtoolsHook, setTransitionHooks, shallowReactive, shallowReadonly, shallowRef, ssrContextKey, ssrUtils, stop, toDisplayString, toHandlerKey, toHandlers, toRaw, toRef, toRefs, transformVNodeArgs, triggerRef, unref, useAttrs, useCssModule, useCssVars, useSSRContext, useSlots, useTransitionState, vModelCheckbox, vModelDynamic, vModelRadio, vModelSelect, vModelText, vShow, version, warn, watch, watchEffect, watchPostEffect, watchSyncEffect, withAsyncContext, withCtx, withDefaults, withDirectives, withKeys, withMemo, withModifiers, withScopeId)
You can register global components to the app you are creating.
import {createApp, defineComponent } from 'vue';
import router from './router/routes.js';
import MyComponent from '#/components/MyComponent.vue'
// Root vue component
const root = defineComponent({/* ... */})
//Create the app
const app = createApp(root)
//Configure the app
app.use(router)
.use(require('vue-moment'))
.use(VueToast)
//Attach global components to the app
app.component('my-component', MyComponent)
//Mount the app
app.mount('#app')

Custom Node server causes Nextjs to fail to load 'No `pages` directory found'

I'm trying to bootstrap a new Nextjs app, and for integrating with Auth0 I need my localhost to be running HTTPS. I followed the guide here (https://medium.com/responsetap-engineering/nextjs-https-for-a-local-dev-server-98bb441eabd7), and generated a local certificate which is in more trusted certificate store.
To use HTTPS for localhost, you apparently need to create a custom server (this seems an odd oversight on the Nextjs side), so here's my custom server:
const { createServer } = require('https');
const { parse } = require('url');
const next = require('next');
const fs = require('fs');
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev, dir: __dirname });
const handle = app.getRequestHandler();
const httpsOptions = {
key: fs.readFileSync('./certificates/ReactDevCertificate.key'),
cert: fs.readFileSync('./certificates/ReactDevCertificate.cer'),
};
app.prepare().then(() => {
createServer(httpsOptions, (req, res) => {
const parsedUrl = parse(req.url, true);
handle(req, res, parsedUrl);
}).listen(3000, (err) => {
if (err) throw err;
console.log('> Server started on https://localhost:3000');
});
});
Now, previous without the custom server, the app loads fine. But, with the custom server, it fails to load:
➜ yarn run dev
yarn run v1.22.15
$ next dev ./server.js
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
Error: > No `pages` directory found. Did you mean to run `next` in the parent (`../`) directory?
at Object.findPagesDir (C:\Clients\ING\Framework\samples\fictionist-ui\node_modules\next\dist\lib\find-pages-dir.js:31:15)
at new DevServer (C:\Clients\ING\Framework\samples\fictionist-ui\node_modules\next\dist\server\dev\next-dev-server.js:110:44)
at NextServer.createServer (C:\Clients\ING\Framework\samples\fictionist-ui\node_modules\next\dist\server\next.js:102:20)
at C:\Clients\ING\Framework\samples\fictionist-ui\node_modules\next\dist\server\next.js:117:42
at async NextServer.prepare (C:\Clients\ING\Framework\samples\fictionist-ui\node_modules\next\dist\server\next.js:92:24)
at async C:\Clients\ING\Framework\samples\fictionist-ui\node_modules\next\dist\cli\next-dev.js:126:9
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
It feels like somewhere the app has navigated to a child folder, as there is code in the function find-pages-dir.js (https://github.com/vercel/next.js/blob/canary/packages/next/lib/find-pages-dir.ts#L22) that looks specifically to the parent directory for the pages folder.
For reference, here is my package.json:
{
"name": "fictionist-ui",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev ./server.js",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"next": "12.0.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"semantic-ui-css": "^2.4.1",
"semantic-ui-react": "^2.0.4"
},
"devDependencies": {
"#types/node": "16.11.6",
"#types/react": "17.0.33",
"eslint": "7.32.0",
"eslint-config-next": "12.0.1",
"typescript": "4.4.4"
}
}
OS: Windows 11
NPM: 16.9.0
Yarn: 1.22.15
My mistake was that I didn't get the command right:
"scripts": {
"dev": "node server.js" // was "next dev ./server.js"
}

Adding Vuex-ORM to Laravel Nova tool

Can someone help me out with adding vuex-orm to a Laravel Nova Tool.
The base of a Laravel Nova tool has tool.js with the following content ('planning-tool' in name, and path may vary according to the name of your tool):
Nova.booting((Vue, router, store) => {
router.addRoutes([
{
name: 'planning-tool',
path: '/planning-tool',
component: require('./components/Tool'),
},
])
})
As you can see Laravel Nova already has a store present.
According to the Vuex-ORM docs (https://vuex-orm.org/guide/prologue/getting-started.html#register-models-to-vuex), I should get it started using:
import Vue from 'vue'
import Vuex from 'vuex'
import VuexORM from '#vuex-orm/core'
import User from '#/models/User'
Vue.use(Vuex)
// Create a new instance of Database.
const database = new VuexORM.Database()
// Register Models to Database.
database.register(User)
// Create Vuex Store and register database through Vuex ORM.
const store = new Vuex.Store({
plugins: [VuexORM.install(database)]
})
export default store
Since Laravel Nova already has a Vuex store I was trying to mix it up. But as soon as I add an import related to #vuex-orm/core I get the following error:
ERROR in ./node_modules/#vuex-orm/core/dist/vuex-orm.esm.js
Module parse failed: Unexpected token (1105:21)
You may need an appropriate loader to handle this file type.
| }
| if (typeof target === 'object' && target !== {}) {
| const cp = { ...target };
| Object.keys(cp).forEach((k) => (cp[k] = cloneDeep(cp[k])));
| return cp;
# ./resources/js/tool.js 1:0-37
# multi ./resources/js/tool.js ./resources/sass/tool.scss
My code (just so far) is:
import VuexORM from '#vuex-orm/core'
Nova.booting((Vue, router, store) => {
router.addRoutes([
{
name: 'planning-tool',
path: '/planning-tool',
component: require('./components/NewTool'),
},
])
// Create a new instance of Database.
const database = new VuexORM.Database()
// TODO ...
})
Does anybody have a suggestion? I know I can use normal Vuex store but vuex-orm adds so many nice features (which I'm used to) that I would take me a lot of time to work with normal objects as models by looping through them and loading additional relationships.
FURTHER AHEAD
To get ahead of myself, how should I register the vuex-orm database plugin since all I can find is creating a Vuex store with the (VuexORM) plugin directly passed along. I've read that a plugin is just function with the store as the only argument, so would something like this work? I've read that it does not always work when you use it like this.
const plugin = VuexORM.install(database)
plugin(store)
Any suggestions of what to try is welcome, I've been at it for quite a while now...
The problem was with webpack and/or Laravel mix 1. We solved it by adding babel dependencies and upgrading Laravel mix tot ^6.0.19.
Our package.json is:
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "mix",
"watch": "mix watch",
"watch-poll": "mix watch -- --watch-options-poll=1000",
"hot": "mix watch --hot",
"prod": "npm run production",
"production": "mix --production"
},
"devDependencies": {
"#babel/core": "^7.14.3",
"#babel/plugin-transform-runtime": "^7.14.3",
"#babel/preset-env": "^7.14.4",
"#vuex-orm/core": "^0.36.4",
"babel-loader": "^8.2.2",
"cross-env": "^5.0.0",
"laravel-mix": "^6.0.19",
"vue": "^2.6.14",
"vue-loader": "^15.9.7",
"vuex": "^3.6.2"
},
"dependencies": {
"#babel/runtime": "^7.14.0"
}
}
Our .babelrc is:
{
"presets": [
[
"#babel/preset-env",
{
"useBuiltIns": false
}
]
],
"plugins": [
"#babel/transform-runtime"
]
}
Our tool.js is:
import VuexORM from '#vuex-orm/core'
import ExampleModel from './models/ExampleModel'
import Tool from './components/Tool'
Nova.booting((Vue, router, store) => {
router.addRoutes([
{
name: 'planning-tool',
path: '/planning-tool',
component: Tool,
},
])
// Create a new instance of Database.
const database = new VuexORM.Database()
database.register(ExampleModel)
const plugin = VuexORM.install(database)
plugin(store)
})
I hope this helps someone in the future.

Rollup with #types/d3 using Visual Studio Code

My library project uses...
Typescript
Rollup
D3
#types/d3
The package.json file has these dependencies
"dependencies": {
"#types/d3": "^4.4.0" /* also tried moving this to devDeps */
},
"devDependencies": {
"cssnano": "^3.10.0",
"postcss-cssnext": "^2.9.0",
"postcss-nested": "^1.0.0",
"postcss-simple-vars": "^3.0.0",
"rollup": "^0.40.1",
"rollup-plugin-commonjs": "^7.0.0",
"rollup-plugin-livereload": "^0.4.0",
"rollup-plugin-node-resolve": "^2.0.0",
"rollup-plugin-postcss": "^0.2.0",
"rollup-plugin-serve": "^0.1.0",
"rollup-plugin-typescript": "^0.8.1",
"rollup-plugin-uglify": "^1.0.1",
"rollup-watch": "^3.1.0"
}
My rollup.config.js file has these plugins configured...
plugins: [
typescript(),
postcss({
extension: ['.css'],
plugins: [
simplevars(),
nested(),
cssnext({ warnForDuplicates: false }),
cssnano(),
],
}),
nodeResolve({
jsnext: true, //use jsnext if the node package supports it
main: true, //look for main file
browser: true, //if there is a browser version, use it
}),
commonjs(),
//uglify(),
serve({
contentBase: 'build',
port: '80',
}),
livereload(),
]
Rollup builds it fine. However, in the editor, VSCode displays an error on the d3 symbol. When I hover over it I see this error...
[ts] 'd3' refers to a UMD global, but the current file is a module. Consider adding an import instead.
I can add this import to make the error go away in the editor.
import d3 from '#types/d3';
However, then rollup fails with...
Could not resolve '#types/d3' from '...'
Is there a way to configure vscode to not show that error, so it doesn't drown out real errors?
Is there an alternative way to configure D3 type definitions in a rollup project?
You only have the TypeScript typings for d3 defined. You still need to specify d3 as dependency.
package.json
"dependencies": {
"#types/d3": "4.8.0",
"d3": "4.8.0"
}
You can now import d3:
import * as d3 from "d3";

Resources