How exactly do you use mapState? - vue.js

I'm trying to use mapState in Vuex as follows:
mapState({
locations: state => state.locations,
types: state => state.models.types,
models: state => state.models.models,
vendors: state => state.models.vendors,
statuses: state => state.statuses,
})
I get an 'Unexpected Token' compile error, with Browserify pointing at the first fat arrow. If I try and use the spread operator:
...mapState({
The compile error happens at the ...

That is an object property spread. Pretty much nothing supports it natively at the moment, and the es2015 babel preset doesn't include it.
Install it with npm i -D babel-plugin-transform-object-rest-spread
Add it to your .babelrc. It should look something like this:
{
"presets": ["es2015"],
"plugins": ["transform-object-rest-spread"]
}

It seems you dont have babel installed. Babel is a transpiler for JavaScript which turns ES6 into code that runs in our browser today.
You will be needing babelify in your setup of Browserify. you can go through any tutorial on how to setup babel with browerify, there are many available.

Related

How to prerender a Vue3 application?

I try without success to apply a prerendering (or a SSG) to my Vue3 application to make it more SEO friendly.
I found the vue-cli-plugin-prerender-spa, and when I try it with the command line: vue add prerender-spa I have the error:
ERROR TypeError: Cannot read properties of undefined (reading 'endsWith')
After that I tried prerender-spa-plugin but I have an error when I make a npm run build:
[prerender-spa-plugin] Unable to prerender all routes!
ERROR Error: Build failed with errors.
Error: Build failed with errors.
at /Users/myusername/Workspace/myproject/node_modules/#vue/cli-service/lib/commands/build/index.js:207:23
at /Users/myusername/Workspace/myproject/node_modules/webpack/lib/webpack.js:148:8
at /Users/myusername/Workspace/myproject/node_modules/webpack/lib/HookWebpackError.js:68:3
What do you think about this? Do you have any idea?
Nuxt3 is a really powerful meta-framework with a lot of features and huge ecosystem. Meanwhile, it's in RC2 right now so not 100% stable (may still work perfectly fine).
If your project is aiming for something simpler, I'd recommend using Vitesse. It may be a bit more stable and it's probably powerful enough (check what's coming with it to help you decide).
Some solutions like Prerender also exist but it's paid and not as good as some real SSG (/SSR). Also, it's more of a freemium.
I struggled with the same error output until I found the prerender-spa-plugin-next. Then I notice the latest version of prerender-spa-plugin was published 4 years ago and prerender-spa-plugin-next is continually updating. It seems like that prerender-spa-plugin-next is a new version of prerender-spa-plugin with the same functions. So I use prerender-spa-plugin-next instead of prerender-spa-plugin then everything works fine!
Here is my step:
install the package
npm i -D prerender-spa-plugin-next
modify vue.config.js like
const plugins = [];
if (process.env.NODE_ENV === 'production') {
const { join } = require('path');
const PrerenderPlugin = require('prerender-spa-plugin-next');
plugins.unshift(
new PrerenderPlugin({
staticDir: join(__dirname, 'dist'),
routes: ['/'], //the page route you want to prerender
})
);
}
module.exports = {
transpileDependencies: true,
configureWebpack(config) {
config.plugins = [...config.plugins, ...plugins];
},
};
build
npm run build
Then check the index.html under the dist folder you can see the page is prerendered.
Further usage refers to the homepage of prerender-spa-plugin-next
Found and fix about the scss files to import.
In nuxt.config.ts use :
vite: {
css: {
preprocessorOptions: {
scss: {
additionalData: `
#import "#/assets/scss/_variables.scss";
#import "#/assets/scss/my-style.scss";
`
}
},
},
}
Now my 2 mains issue are : how to install vuetify properly, currently syles and components seems working but the JS not, for example, accordions don't expands on click.
And second topic is to have a i18n module, it seems that vue-i18N no longer works.
Thanks.

Vue.js 3 extension breaks while using "vue-cli-service build" due to unsafe-eval

I am developing a chrome extension using vue 3, vue-router and vuex based on Kocal's project which uses vue-cli under the hood. I used whenever possible Single File Components with extensive use of vue bindings.
Everything works perfect on development mode but I recently tried to build the application for production and I encountered this error with partial rendering:
chunk-vendors.f6de00a6.js:11 EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'".
After a few days of digging, my understanding is that either webpack or vue compiling system is messing with CSP by referring/injecting code through eval scripts. As I am fairly new to this, it's hard for me to read to distinguish what I can do.
I tried different approaches:
defining $vue alias to a runtime only build in vue.config.js (supposedly removing unsafe eval by having code compiled before runtime but ending with a new error: Uncaught TypeError: Object(...) is not a function for o=Object(n["withScopeId"])("data-v-21ae70c6");)
using render() function at root
adding "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", to manifest.json
switching a component to render() to see if I have better chance with the partial rendering, but ending up with nothing being displayed although having console.log from render being executed.
Adding a config to chainWebpack splitting manifest and inline manifest on vue.config
What puzzles me is that I can't shake off the unsafe-eval, with at best a partial display, at worst a blank page. Bindings seem to be shaken off regardless and using a router-link to change page will give a blank page.
Edit: After digging through compiled code from webpack and setting minimize opt to false, it seems the error comes from a vendor: vue-i18n
The eval is likely coming from Webpack, due to an issue with global scoping.
see link for more detail https://mathiasbynens.be/notes/globalthis
Could you try adding this configuration to vue.config.js
module.exports = {
configureWebpack: {
node: {
global: false
},
plugins: [
new webpack.DefinePlugin({
global: "window"
})
]
}
};
tl;dr: Check your dependencies/packages, including those you wouldn't think they use unsafe-eval.
After digging into webpack internals and components building for vue3, here are the takeaways:
using Single File Components and default vue-cli config is ok as it will indeed just need vue runtime, so no unsolicited unsafe-eval
webpack config as below works:
module.exports = {
configureWebpack: {
plugins: [
new webpack.DefinePlugin({
global: "window" // Placeholder for global used in any node_modules
})
]
},
...
};
// Note that this plugin definition would break if you are using "unit-mocha" module for vue-cli
In the end, the issue was a dependency I was using for i18n vue-i18n#next, after removing it and switching to chrome's i18n way, it's now working.

Eslint error in postcss.config at vue-cli

I have started a new vue-cli project with
vue create -n tailwind-demo
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, TS, PWA, Vuex, Linter
? Use class-style component syntax? Yes
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? Yes
? Pick a linter / formatter config: Prettier
? Pick additional lint features: Lint on save, Lint and fix on commit
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
I have also add the following postcss.config.js:
module.exports = {
plugins: [
require("postcss-preset-env")({ stage: 0 }),
require("tailwindcss")(),
require("autoprefixer")()
]
};
When i run the yarn lint command it got the follow errors
error: Require statement not part of import statement (#typescript-eslint/no-var-requires) at postcss.config.js:3:5:
error: Require statement not part of import statement (#typescript-eslint/no-var-requires) at postcss.config.js:4:5:
error: Require statement not part of import statement (#typescript-eslint/no-var-requires) at postcss.config.js:5:5:
How can i import the plugins or how should i configure the eslint in Vue CLI for the postcss.config.js?
I have tried something like
import tailwindCss from "tailwindcss";
But i got a SyntaxError: Unexpected identifier while building.
It seems to work with the following postcss.config.js
module.exports = {
plugins: {
"postcss-preset-env": {
stage: 0
},
tailwindcss: {}
}
};

How do you remove console.log from a build using the JS Quasar Framework?

I am trying the Quasar Framework (for those not familiar, it's based on Vue) and it's going well. However I've tried running a build (npm run build) and get repeated:
error Unexpected console statement no-console
... so the build fails because it sees console.log(...) and is not happy. My options:
don't use console.log in development. But it's handy.
comment out the eslint rule that presumably enforces that, so letting console.log into production. But that's not ideal for performance/security.
have the build automatically remove any console.log. That's what I'm after.
But how?
I took a look at the build https://quasar.dev/quasar-cli/cli-documentation/build-commands and it mentions using webpack internally and UglifyJS too. Given that, I found this answer for removing console.log in a general Vue/webpack project: https://github.com/vuejs-templates/webpack-simple/issues/21
... but if that's how, where does that go within Quasar since there is no webpack config file? I imagine in the quasar.conf.js file (since I see an 'extendWebpack' line in there - sounds promising). Or is there a better way to do it? How do other people remove console.log in production when using Quasar? Or handle logging without it?
Thanks!
https://quasar.dev/quasar-cli/quasar-conf-js#Property%3A-build
quasar.conf.js:
module.exports = function (ctx) {
return {
...
build: {
...
uglifyOptions: {
compress: { drop_console: true }
}
},
}
}
The above will result in configuring terser plugin with the following:
terserOptions: {
compress: {
...
drop_console: true
},
(https://github.com/terser/terser#compress-options)
(you can see the generated config with quasar inspect -c build -p optimization.minimizer)
You still also need to remove the eslint rule to avoid build errors, see https://github.com/quasarframework/quasar/issues/5529
Note:
If you want instead to configure webpack directly use:
quasar.conf.js:
module.exports = function (ctx) {
return {
...
build: {
...
chainWebpack (chain) {
chain.optimization.minimizer('js').tap(args => {
args[0].terserOptions.compress.drop_console = true
return args
})
}
},
}
}
It will do the same as above.
See https://quasar.dev/quasar-cli/cli-documentation/handling-webpack
and https://github.com/neutrinojs/webpack-chain#config-optimization-minimizers-modify-arguments
https://github.com/quasarframework/quasar/blob/dev/app/lib/webpack/create-chain.js#L315
1 Edit package.json in Vue's project what had created it before.
2 Then find "rules": {}.
3 Change to this "rules":{"no-console":0}.
4 if you Vue server in on, off it and run it again. Then the issue will be done.
As an alternative I can suggest using something like loglevel instead of console.log. It's quite handy and allows you to control the output.

Unable to use Aurelia plugin

I'm trying to move one of my custom elements into a plug-in so that I can re-use it across projects.
I had a look at the skeleton plugin and noticed that it has a src/index.js that returns a config with all custom elements defined as globalResources.
So I tried the same thing and I basically have:
src/index.js
export function configure (config) {
config.globalResources([
'./google-map',
'./google-map-location-picker',
'./google-map-autocomplete'
]);
}
And then I have each one of my custom elements next to index.js, for example:
google-map.js
import {inject, bindable, bindingMode, inlineView} from 'aurelia-framework';
#inlineView(`
<template>
<div class="google-map"></div>
</template>
`)
#inject(Element)
export class GoogleMapCustomElement {
// All the Custom Element code here
}
I've also set up a basic npm script that runs babel on the code and sticks it in dist/:
"main": "dist/index.js",
"babel": {
"sourceMap": true,
"moduleIds": false,
"comments": false,
"compact": false,
"code": true,
"presets": [ "es2015-loose", "stage-1"],
"plugins": [
"syntax-flow",
"transform-decorators-legacy",
"transform-flow-strip-types"
]
},
"scripts": {
"build": "babel src -d dist"
},
Tbh I'm not entirely sure this is all correct but I took some of it from the skeleton plugin and it seems to run fine.
Anyway, the problem I'm having is that after I install the plugin (npm install --save-dev powerbuoy/AureliaGoogleMaps), add it to my aurelia.json in build.bundles[vendor-bundle.js].dependencies and tell aurelia to use it in main.js (.use.plugin('aurelia-google-maps')) I get:
GET http://localhost:9000/node_modules/aurelia-google-maps/dist/index/google-map.js (404)
So my question is, where does it get the dist/index/ part from?? I'm configuring my globalResources in index.js but nowhere does it say that I have an index folder.
What am I doing wrong?
Bonus question: What is the bare minimum required to transpile my ES6 plug-in code so that others can use it? Does my babel configuration look correct?
What about referencing your plugin within aurelia.json, like this:
{
"name": "aurelia-google-maps",
"path": "../node_modules/aurelia-google-maps/dist",
"main": "index"
}
I have absolutely no idea why, but in order to solve this problem I actually had to move my custom elements inside an index/ folder.
So now I have this:
- index.js
- index/
- custom-element-one.js
- custom-element-two.js
And my index.js still looks like this:
export function configure (config) {
config.globalResources([
'./custom-element-one',
'./custom-element-two'
]);
}
Where it gets index/ from I guess I will never know, but this works at least.
I did need the babel plug-in Marton mentioned too, but that alone did not solve the mystery of the made up path.
Edit: To elaborate a bit further, if I name my main entry point something other than index.js the folder too needs that name. For example, if I were to rename index.js main.js I would need to put my globalResources inside a folder called main/.
Update:
Edit: thanks for clarifying why you don't want to use the whole skeleton-plugin package.
Focusing on your original question: aurelia-cli uses RequireJS (AMD format) to load dependencies. Probably, your current output has a different format.
Add transform-es2015-modules-amd to babel.plugins to ensure AMD-style output, so it will be compatible with RequireJS and therefore with aurelia-cli.
"babel": {
"sourceMap": true,
"moduleIds": false,
"comments": false,
"compact": false,
"code": true,
"presets": [ "es2015-loose", "stage-1"],
"plugins": [
"syntax-flow",
"transform-decorators-legacy",
"transform-flow-strip-types",
"transform-es2015-modules-amd"
]
}
Original:
There are several blog post about plugin creation, I started with this: http://patrickwalters.net/making-out-first-plugin/ .
Of course, there have been many changes since then, but it's a useful piece of information and most of it still applies.
I'd recommend using plugin-skeleton as project structure. It provides you with a working set of gulp, babel, multiple output formats out-of-the-box.
With this approach, your plugin's availability wouldn't be limited to JSPM or CLI only but everyone would have the possibility to install it regardless of their build systems.
Migration is fairly easy in your case:
Download skeleton-plugin
Copy your classes + index.js into src/
npm install
...wait for it...
gulp build
check dist/ folder
most of your pain should now be gone :)
Here are some details based on my observations/experience.
1. Main index.js/plugin-name.js:
In general, a main/entry point is required, where the plugin's configure() method is placed. It serves as a starting point when using it within an Aurelia application. This file could have any name, usually it's index.js or plugin-name.js (e.g. aurelia-google-maps.js) to make it clear for other developers what should be included for bundling. Set that same entry point in package.json as well.
In addition to globalResources, you can implement a callback function to allow configuration overrides. That can be called in the application, which will use the plugin. Example solution
Plugin's index.js
export * from './some-element';
export function configure(config, callback) {
// default apiKey
let pluginConfig = Container.instance.get(CustomConfigClass);
pluginConfig.apiKey = '01010101';
// here comes an override
if (callback) {
callback(pluginConfig);
}
...
config.globalResources(
'./some-element'
);
}
Your app's main.js
export function configure(aurelia) {
aurelia.use
.standardConfiguration()
.developmentLogging()
.plugin('aurelia-google-maps', (pluginConfig) => {
// custom apiKey
pluginConfig.apiKey = '12345678';
});
aurelia.start().then(a => a.setRoot());
}
2. HTML and CSS resources:
If you have html only custom elements, you can make them available using globalResources.
Custom css styling is going to require a bit of additional configuration in bundling configuration (see below).
3. Using the plugin with aurelia-cli: Documentation
One of the first new features you'll see soon is a command to help you with 3rd party module configuration. The command will inspect a previously npm-installed package, and make a configuration recommendation to you, automating the process if you desire.
While we are looking forward to that above moment, let's edit aurelia.json:
Configure plugin dependencies. If there are any external libraries (e.g. Bootstrap), then those should be included before your plugin.
Include your plugin:
...
{
"name": "plugin-name",
"path": "../node_modules/plugin-name/dist/amd",
"main": "plugin-name",
"resources": ["**/*.html", "**/*.css"] // if there is any
},
...
Now, your plugin is ready to include it in main.js as showed in Section 1..
I hope you didn't get sick of reading the word 'plugin' so many (21!) times. :D