vue reactivity transform $ref not defined - vue.js

According to this page in the docs, macros should be
globally available and do not need to be imported when Reactivity
Transform is enabled
I've explicitly opted-in the reactivity transforms in my vue config in accordance with the docs here:
// vue.config.js
config.module.rule('vue')
.use('vue-loader')
.tap((options) => {
return {
...options,
reactivityTransform: true
}
})
But I am getting the '$ref' is not defined from eslint. I think I need to enable it somwhere so that eslint understands it's a global macro, but I can't find anything about it in the docs.
What am I missing?

I managed to solve this by adding this to .eslintrc,js
globals: {
$ref: 'readonly',
$computed: 'readonly',
$shallowRef: 'readonly',
$customRef: 'readonly',
$toRef: 'readonly'
}
...and this to global.d.ts
/// <reference types="vue/macros-global" />

If you are not already using .eslintrc and don't want to create one, alternative solution is to add
"types": [... "vue/ref-macros"]
in tsconfig.json -> compilerOptions.
Another solution is to add /// <reference types="vue/macros-global" /> to the top of your src/env.d.ts file.

Related

Docusaurus getting mermaid up and running

[docusaurus] Newbie question: I am attempting to get mermaid up and running on my website, but am struggling to implement https://docusaurus.io/docs/markdown-features/diagrams. My docusaurus.config.js file is structured as follows:
const config = {
...
presets: [
...
],
themeConfig:
({
...
}),
}
module.exports = config;
Where should the block
markdown: {
mermaid: true,
},
themes: ['#docusaurus/theme-mermaid'].
be included in this structure?
I have attempted to include the stated block at all different points in the config.js file, but I either get a compile fail, or compile succeed, but no mermaid behaviour.
Thanks!
It is now working - I think the issue here was that the project I was attempting to use mermaid on was a 2.1.0 project. Once I updated it to 2.2.0 then things work as the documentation suggests.

Using Stencil components with Ionic Vue

In the Stencil docs section on framework integration with Vue it states the following:
In order to use the custom element library within the Vue app, the
application must be modified to define the custom elements and to
inform the Vue compiler which elements to ignore during compilation.
According to the same page this can be achieved by modifying the config of your Vue instance like this:
Vue.config.ignoredElements = [/test-\w*/];
This relates to Vue 2 however. With Vue 3 (which Ionic Vue uses) you have to use isCustomElement as stated here.
Regretably, I can’t for the life of me get Vue and Stencil to play nice. I have tried setting the config like this:
app.config.compilerOptions.isCustomElement = tag => /gc-\w*/.test(tag)
This causes Vue throw the following warning in the console:
[Vue warn]: The `compilerOptions` config option is only respected when using a build of Vue.js that includes the runtime compiler (aka "full build"). Since you are using the runtime-only build, `compilerOptions` must be passed to `#vue/compiler-dom` in the build setup instead.
- For vue-loader: pass it via vue-loader's `compilerOptions` loader option.
- For vue-cli: see https://cli.vuejs.org/guide/webpack.html#modifying-options-of-a-loader
- For vite: pass it via #vitejs/plugin-vue options. See https://github.com/vitejs/vite/tree/main/p
However, I have no idea how to implement any of the above suggestions using Ionic Vue. I have been messing around with chainWebpack in config.vue.js but without success so far.
Any help would be greatly appreciated.
I'm not an expert in Vue but here's how I did it:
Add the following to your ./vue.config.js (or create it if it doesn't exist):
/**
* #type {import('#vue/cli-service').ProjectOptions}
*/
module.exports = {
// ignore Stencil web components
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.tap(options => {
options.compilerOptions = {
...options.compilerOptions,
isCustomElement: tag => tag.startsWith('test-')
}
return options
})
},
}
This will instruct Vue to ignore the test-* components. Source: https://v3.vuejs.org/guide/web-components.html#skipping-component-resolution
Next, load the components in ./src/main.ts.
Import the Stencil project:
import { applyPolyfills, defineCustomElements } from 'test-components/loader';
Then replace createApp(App).use(router).mount('#app') with:
const app = createApp(App).use(router);
// Bind the custom elements to the window object
applyPolyfills().then(() => {
defineCustomElements();
});
app.mount('#app')
Source: https://stenciljs.com/docs/vue
Also, if anyone is using vite2+, just edit the vite.config.js accordingly:
import { fileURLToPath, URL } from 'url'
import { defineConfig } from 'vite'
import vue from '#vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue({
template: {
compilerOptions: {
isCustomElement: tag => tag.startsWith('test-') // ✅ Here
}
}
}) ],
resolve: {
alias: {
'#': fileURLToPath(new URL('./src', import.meta.url))
}
}
})

Vue warn Failed to resolve component: ion-icon

Following the usage at https://ionicons.com/usage, the ion-icon displays but I get this warning:
Failed to resolve component: ion-icon
My steps were:
I used #vue/cli#4.5.11 to create a new app (vue create projectname)
added <ion-icon name="heart"></ion-icon> to HelloWorld.vue
added <script type="module" src="https://unpkg.com/ionicons#5.0.0/dist/ionicons/ionicons.esm.js"></script> to public/index.html
I've tried app.config.isCustomElement = tag => tag.startsWith('ion') which created another warning saying the option is only respected when using the runtime compiler, but I was able to suppress it by adding a vue.config.js with module.exports = {runtimeCompiler: true}. No effect on the ion-icon warning. This might be linked to needing to use a custom vue-loader but is there an easy way to get rid of this warning?
The full warning from using app.config.isCustomElement provides a useful clue:
The isCustomElement config option is only respected when using the runtime compiler. If you are using the runtime-only build, isCustomElement must be passed to #vue/compiler-dom in the build setup instead- for example, via the compilerOptions option in vue-loader: https://vue-loader.vuejs.org/options.html#compileroptions.
You could modify vue-loader's compilerOptions in vue.config.js to configure isCustomElement:
// vue.config.js
module.exports = {
chainWebpack: config => {
config.module
.rule('vue')
.use('vue-loader')
.tap(options => {
options.compilerOptions = {
...options.compilerOptions,
isCustomElement: tag => tag.startsWith('ion-')
}
return options
})
}
}

VueJS build started throwing Error: Conflict: Multiple assets emit to the same filename

My app used to work fine until I updated VueJS this morning. Now when I build, it shows the following error:
Error: Conflict: Multiple assets emit to the same filename img/default-contractor-logo.0346290f.svg
There's only one file like this in the repo.
Here's my vue.config.js:
module.exports = {
baseUrl: '/my/',
outputDir: 'dist/my',
css: {
loaderOptions: {
sass: {
data: `
#import "#/scss/_variables.scss";
#import "#/scss/_mixins.scss";
#import "#/scss/_fonts.scss";
`
}
}
},
devServer: {
disableHostCheck: true
}
};
I tried webpack fixes recommended in similar cases, but non helped.
I had the same error when importing SVG files using dynamically generated path in the require statement:
const url = require("../assets/svg/#{value}");
<img src={{url}} />
In this case file-loader processes all SVG files and saves them to the output path. My file-loader options were:
{
loader: "file-loader",
options: { name: "[name].[ext]" }
}
The folders structure has duplicate file names, something like this:
assets
|__ one
|____ file.svg
|__ two
|____ file.svg
In this case file-loader saves both file.svg files to the same output file: build/assets/file.svg - hence the warning.
I managed to fix it by adding [path] to the name option:
{
loader: "file-loader",
options: { name: "[path][name].[ext]" }
}
The answer by #ischenkodv is definitely correct, but because of my inexperience with webpack, I needed a little more context to use the information to fix the problem.
For the benefit of anyone else in the same situation, I'm adding the following details which I hope will be useful.
This section of the Vue.js documentation was particularly helpul:
VueJS - Modifying Options of a Loader
For the TL;DR fix, here is the relevant chunk of my vue.config.js:
// vue.config.js
module.exports = {
// ---snip---
chainWebpack: config =>
{
config.module
.rule('svg')
.test(/\.svg$/)
.use('file-loader')
.tap(options =>
{
return { name: "[path][name].[ext]" };
});
}
// ---snip---
};
In my project it was the flag-icon-css NPM package that was causing the Multiple assets emit to the same filename conflict errors. The above update to the vue.config.js file resolved the problem for me.
I suspect that the regular expression in the test could be tightened up to target just the items in the flag-icon-css package rather than matching all SVG files, but I haven't bothered since it's not causing any adverse effects so far.

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