Storing Enviornment Variables using Service Workers - vue.js

The problem: I can't have my service worker access my .env variables
The only solution I found was storing the variables into a generated js files, I am using Vue PWA for this.
Vue Config
module.exports = {
pwa: {
name: 'Fintask App',
themeColor: '#3aa9ff',
msTileColor: '#3aa9ff',
appleMobileWebAppCapable: 'yes',
appleMobileWebAppStatusBarStyle: 'black',
// configure the workbox plugin
workboxPluginMode: 'InjectManifest',
workboxOptions: {
// swSrc is required in InjectManifest mode.
swSrc: 'src/service-worker.js',
// ...other Workbox options...
}
}
};
My service worker config
importScripts('swenv.js'); // this generates an error: Uncaught ReferenceError: Cannot access 'process' before initialization
workbox.setConfig({
debug: false,
});
workbox.precaching.precacheAndRoute([]);
workbox.routing.registerRoute(
new RegExp(`${process.env.VUE_APP_API_ROOT_URL}/organization/(.*)`),
workbox.strategies.networkFirst({
cacheName: 'organization',
}),
);
my swEnvBuild that should generate my swenv.js
//swEnvBuild.js - script that is separate from webpack
require('dotenv').config();
const fs = require('fs');
fs.writeFileSync("public/swenv.js",
`const process = {
env: {
VUE_APP_API_ROOT_URL: process.env.VUE_APP_API_ROOT_URL
}
}`);
my swenv.js
const process = {
env: {
VUE_APP_API_ROOT_URL: process.env.VUE_APP_API_ROOT_URL
}
}
This is still not working for me, I did find different solutions based on this method, I just wish there was a better way
My output
swenv.js:3 Uncaught ReferenceError: Cannot access 'process' before initialization
at swenv.js:3
at service-worker.js:3
Error during service worker registration: TypeError: Failed to register a ServiceWorker for scope ('http://localhost/') with script ('http://localhost/service-worker.js'): ServiceWorker script evaluation failed
Don't know why I get this error, this result was generated in my apache server

Related

Cypress 12 Component Tests Wont Load

I am trying to use Cypress 12 to run compnent tests in a Vue.js 2 app. Below is my cypress.config.ts file:
import { defineConfig } from "cypress";
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
baseUrl: "http://localhost:9090/.......",
defaultCommandTimeout: 60000,
},
component: {
devServer(cypressConfig: CypressConfiguration) {
// return devServer instance or a promise that resolves to
// a dev server here
return {
port: 9090,
close: () => {},
};
},
},
});
I setup a custom devServer in vue.config.js (otherwise Cypress starts uses its own localhost):
module.exports = {
devServer: {
port: 9090,
proxy: 'http://localhost:8080'
}
}
However, the tests wont load
When I run e2e tests, all is fine: tests appears, calls localhost:9090. However, if I want to run only component tests, it just gets stuck trying to load the tests.
It is not a DevTools problem as I have looked into that. All other configuration settings are standard.

Module parse failed: Unexpected token in Storybook when working with pdfjs-dist

I am working with a package that uses the pdfjs-dist package, and when trying to load the component that uses it in my Storybook, I get the following error
ERROR in ./node_modules/pdfjs-dist/build/pdf.js 2267:39
Module parse failed: Unexpected token (2267:39)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
|
| async getXfa() {
> return this._transport._htmlForXfa?.children[this._pageIndex] || null;
| }
|
My guess, it is about handling XFA files, which are PDF files.
This is my main.js file in .storybook
const path = require('path');
module.exports = {
stories: ['../components/**/*.stories.js', '../components/**/*.stories.mdx'],
addons: [
'#storybook/addon-links',
'#storybook/addon-essentials',
'storybook-dark-mode',
'storybook-addon-next-router',
],
webpackFinal: async (config, { isServer }) => {
config.resolve.modules = [path.resolve(__dirname, '..'), 'node_modules'];
config.resolve.alias = {
...config.resolve.alias,
'#': path.resolve(__dirname, '../components'),
store: path.resolve(__dirname, '../utils/stores'),
dummy: path.resolve(__dirname, '../utils/dummy'),
};
if (!isServer) {
config.node = {
fs: 'empty',
};
}
return config;
},
};
pdfjs-dist: https://github.com/mozilla/pdf.js
react-pdf-viewer: https://github.com/react-pdf-viewer/react-pdf-viewer
The component works swimmingly in my development server, the issue is only in Storybook. Because of that issue, it is unable to even start the storybook server. If I remove the component that uses the package, storybook loads.
The error tells me to use proper webpack configs, but I just cannot figure that one out. This is what I tried, and it didn't work. (in webpackFInal: async () => { ... )
config.module.entry['pdf.worker'] = 'pdfjs-dist/build/pdf.worker.entry';
and
config.module.rules.push({
test: /pdf\.worker\.js$/,
type: 'asset/inline',
generator: {
dataUrl: (content) => content.toString(),
},
});
Found them here: https://github.com/mozilla/pdf.js/issues/14172

Setup of vue.config.js file to imitate production setup (connect two apps)

I run an R Shiny app on port 3000 which serves my vue.js App like this:
library(shiny)
server <- function(input, output, session) {
histogramData <- reactive({
mtcars
})
observe({
session$sendCustomMessage("histogramData", histogramData())
})
}
ui <- function() {
htmlTemplate("dist/index.html")
}
# Serve the bundle at js/main.js
if (dir.exists("dist/js")) {
addResourcePath("js", "dist/js")
}
# Serve the bundle at js/main.js
if (dir.exists("dist/css")) {
addResourcePath("css", "dist/css")
}
# Serve the bundle at js/main.js
if (dir.exists("dist/img")) {
addResourcePath("img", "dist/img")
}
shinyApp(ui, server)
For development, I would change it like this:
ui <- function() {
htmlTemplate("public/index.html")
}
However, I can not always run the build process just to connect the apps, I want to use the dev server to connect the apps and send data back and forth.
I have setup a vue.config.js with the following configuration to create a connection between the two apps.
const path = require('path')
module.exports = {
publicPath: ".",
devServer: {
port: 4000,
contentBase: path.resolve(__dirname, 'public'),
proxy: {
'/': {
target: 'http://localhost:3000'
},
'/websocket': {
target: 'ws://localhost:3000',
ws: true
}
}
},
transpileDependencies: [
"vuetify"
]
}
This was taken from a github repository, I am acutally quite clueless how to archieve this connection. My idea was to connect go on localhost:4000 and receive the data from localhost:3000, but nothing gets passed:
TypeError: Cannot read property 'addCustomMessageHandler' of undefined at VueComponent.mounted (HelloWorld.vue?140d:42)
This is based on the following method in my vue component (which works perfectly after the build process):
mounted: function () {
window.Shiny.addCustomMessageHandler('histogramData', histogramData =>
this.data.histogramData = histogramData
)
Can anyone tell me what´s wrong and help me to setup the connection correctly?

Nuxt Ava End-to-End Testing Store Configuration

Given the example official Nuxt end-to-end test example using Ava:
import test from 'ava'
import { Nuxt, Builder } from 'nuxt'
import { resolve } from 'path'
// We keep a reference to Nuxt so we can close
// the server at the end of the test
let nuxt = null
// Init Nuxt.js and start listening on localhost:4000
test.before('Init Nuxt.js', async t => {
const rootDir = resolve(__dirname, '..')
let config = {}
try { config = require(resolve(rootDir, 'nuxt.config.js')) } catch (e) {}
config.rootDir = rootDir // project folder
config.dev = false // production build
config.mode = 'universal' // Isomorphic application
nuxt = new Nuxt(config)
await new Builder(nuxt).build()
nuxt.listen(4000, 'localhost')
})
// Example of testing only generated html
test('Route / exits and render HTML', async t => {
let context = {}
const { html } = await nuxt.renderRoute('/', context)
t.true(html.includes('<h1 class="red">Hello world!</h1>'))
})
// Close the Nuxt server
test.after('Closing server', t => {
nuxt.close()
})
How can you use Nuxt or Builder to configure/access the applications Vuex store? The example Vuex store would look like:
import Vuex from "vuex";
const createStore = () => {
return new Vuex.Store({
state: () => ({
todo: null
}),
mutations: {
receiveTodo(state, todo) {
state.todo = todo;
}
},
actions: {
async nuxtServerInit({ commit }, { app }) {
console.log(app);
const todo = await app.$axios.$get(
"https://jsonplaceholder.typicode.com/todos/1"
);
commit("receiveTodo", todo);
}
}
});
};
export default createStore;
Currently trying to run the provided Ava test, leads to an error attempting to access #nuxtjs/axios method $get:
TypeError {
message: 'Cannot read property \'$get\' of undefined',
}
I'd be able to mock $get and even $axios available on app in Vuex store method nuxtServerInit, I just need to understand how to access app in the test configuration.
Thank you for any help you can provide.
Just encountered this and after digging so many tutorial, I pieced together a solution.
You have essentially import your vuex store into Nuxt when using it programmatically. This is done by:
Importing Nuxt's config file
Adding to the config to turn off everything else but enable store
Load the Nuxt instance and continue your tests
Here's a working code (assuming your ava and dependencies are set up)
// For more info on why this works, check this aweomse guide by this post in getting this working
// https://medium.com/#brandonaaskov/how-to-test-nuxt-stores-with-jest-9a5d55d54b28
import test from 'ava'
import jsdom from 'jsdom'
import { Nuxt, Builder } from 'nuxt'
import nuxtConfig from '../nuxt.config' // your nuxt.config
// these boolean switches turn off the build for all but the store
const resetConfig = {
loading: false,
loadingIndicator: false,
fetch: {
client: false,
server: false
},
features: {
store: true,
layouts: false,
meta: false,
middleware: false,
transitions: false,
deprecations: false,
validate: false,
asyncData: false,
fetch: false,
clientOnline: false,
clientPrefetch: false,
clientUseUrl: false,
componentAliases: false,
componentClientOnly: false
},
build: {
indicator: false,
terser: false
}
}
// We keep a reference to Nuxt so we can close
// the server at the end of the test
let nuxt = null
// Init Nuxt.js and start listening on localhost:5000 BEFORE running your tests. We are combining our config file with our resetConfig using Object.assign into an empty object {}
test.before('Init Nuxt.js', async (t) => {
t.timeout(600000)
const config = Object.assign({}, nuxtConfig, resetConfig, {
srcDir: nuxtConfig.srcDir, // don't worry if its not in your nuxt.config file. it has a default
ignore: ['**/components/**/*', '**/layouts/**/*', '**/pages/**/*']
})
nuxt = new Nuxt(config)
await new Builder(nuxt).build()
nuxt.listen(5000, 'localhost')
})
// Then run our tests using the nuxt we defined initially
test.serial('Route / exists and renders correct HTML', async (t) => {
t.timeout(600000) // Sometimes nuxt's response is slow. We increase the timeont to give it time to render
const context = {}
const { html } = await nuxt.renderRoute('/', context)
t.true(html.includes('preload'))
// t.true(true)
})
test.serial('Route / exits and renders title', async (t) => {
t.timeout(600000)
const { html } = await nuxt.renderRoute('/', {})
const { JSDOM } = jsdom // this was the only way i could get JSDOM to work. normal import threw a functione error
const { document } = (new JSDOM(html)).window
t.true(document.title !== null && document.title !== undefined) // simple test to check if site has a title
})
Doing this should work. HOWEVER, You may still get some errors
✖ Timed out while running tests. If you get this you're mostly out of luck. I thought the problem was with Ava given that it didn't give a descriptive error (and removing any Nuxt method seemed to fix it), but so far even with the above snippet sometimes it works and sometimes it doesn't.
My best guess at this time is that there is a delay on Nuxt's side using either renderRouter or renderAndGetWindow that ava doesn't wait for, but on trying any of these methods ava almost immediately "times out" despite the t.timeout being explicitly set for each test. So far my research has lead me to checking the timeout for renderAndGetWindow (if it exists, but the docs doesn't indicate such).
That's all i've got.

Vue Cli 3 and Firebase service worker registration

I've used Vue-cli 3 to create a Vue app and I've been trying to incorporate FCM into it. However, I've been working on it for two days and I still cannot get it working.
First, here's my
importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase- app.js');
importScripts('https://www.gstatic.com/firebasejs/4.8.1/firebase-messaging.js');
var config = {
messagingSenderId: "69625964474"
};
firebase.initializeApp(config);
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function (payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload)
// Customize notification here
const notificationTitle = 'Background Message Title';
const notificationOptions = {
body: 'Background Message body.',
icon: '/firebase-logo.png'
}
return self.registration.showNotification(notificationTitle, notificationOptions)
});
```
One solution that sorta works is I moved this file into the public folder and register it in App.vue using
const registration = await navigator.serviceWorker.register(`${process.env.BASE_URL}firebase-messaging-sw.js`)
messaging.useServiceWorker(registration)
However, then I'll be having two service workers (the other one from Vue itself).
I tried to modify vue.config.js instead trying to work with Workbox by adding the following config:
module.exports = {
pwa: {
name: 'My App',
themeColor: '#4DBA87',
msTileColor: '#000000',
appleMobileWebAppCapable: 'yes',
appleMobileWebAppStatusBarStyle: 'black',
// configure the workbox plugin
workboxPluginMode: 'InjectManifest',
workboxOptions: {
// swSrc is required in InjectManifest mode.
swSrc: 'public/firebase-messaging-sw.js'
// ...other Workbox options...
}
}
}
And then register it again in App.vue:
const registration = await navigator.serviceWorker.register(`${process.env.BASE_URL}service-worker.js`)
messaging.useServiceWorker(registration)
Then I got the following error instead:
If you are confused by the files I mentioned or how the directory of my project looks like, what I did was simply creating a PWA using vue-cli 3. And I left most of the structure untouched.
And I set up firebase in main.js:
import firebase from '#firebase/app'
Vue.config.productionTip = false
const config = {
apiKey: process.env.VUE_APP_FIREBASE_API_KEY,
authDomain: process.env.VUE_APP_AUTH_DOMAIN,
databaseURL: process.env.VUE_APP_DATABASE_URL,
projectId: process.env.VUE_APP_PROJECT_ID,
storageBucket: process.env.VUE_APP_STORAGE_BUCKET,
messagingSenderId: process.env.VUE_APP_MESSAGING_SENDER_ID
}
firebase.initializeApp(config)
Then in App.vue:
import firebase from '#firebase/app'
import '#firebase/messaging'
const messaging = firebase.messaging()
messaging.usePublicVapidKey('PUBLIC_KEY')
The service worker is by default disabled in development mode, so running it in development will cause an HTML error page to be returned, this is the reason you are getting text/html error
You can find detailed explanation here LINK