Package vue components that use class syntax - vuejs2

I have a hard time packaging our components as an npm package so we can reuse them in other projects.
I have the feeling I searched everywhere on the internet to no avail. I'm suspecting that using our components in the class syntax style makes most, if not even all, examples fail for me.
The final and most successful so far was the one from the Vue documentation
However with that one I get an error:
[!] (buble plugin) SyntaxError: Unexpected character '#'
The reason for that is obviously the class syntax with #Component immediately failing the build. Is there a way to make this work with class syntax?
My component's script part looks like this (nothing special on the css and template parts):
<script>
import { Vue, Component, Prop } from 'vue-property-decorator';
#Component
export default class Checkbox extends Vue {
#Prop({default: false}) checked;
};
</script>

I think that the problem is with installing vue-loader and vue-template-compiler together.
I'm Quoting Vue-loader Vue Docs
The plugin is required! It is responsible for cloning any other rules
you have defined and applying them to the corresponding language
blocks in .vue files. For example, if you have a rule matching
/\.js$/, it will be applied to <script> blocks in .vue files.
After you npm install them, you need to change your webpack.config.js file like this
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
module: {
rules: [
// ... other rules
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
// make sure to include the plugin!
new VueLoaderPlugin()
]
}

Related

Unable to load stencil components lib with Vue3 using Vite

I created a sample project to reproduce this issue: https://github.com/splanard/vue3-vite-web-components
I initialized a vue3 project using npm init vue#latest, as recommanded in the official documentation.
Then I installed Scale, a stencil-built web components library. (I have the exact same issue with the internal design system of my company, so I searched for public stencil-built libraries to reproduce the issue.)
I configured the following in main.ts:
import '#telekom/scale-components-neutral/dist/scale-components/scale-components.css';
import { applyPolyfills, defineCustomElements } from '#telekom/scale-components-neutral/loader';
const app = createApp(App);
app.config.compilerOptions.isCustomElement = (tag) => tag.startsWith('scale-')
applyPolyfills().then(() => {
defineCustomElements(window);
});
And the same isCustomElement function in vite.config.js:
export default defineConfig({
plugins: [vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('scale-')
}
}
})]
// ...
})
I inserted a simple button in my view (TestView.vue), then run npm run dev.
When opening my test page (/test) containing the web component, I have an error in my web browser's console:
failed to load module "http://localhost:3000/node_modules/.vite/deps/scale-button_14.entry.js?import" because of disallowed MIME type " "
As it's the case with both Scale and my company's design system, I'm pretty sure it's reproducible with any stencil-based components library.
Edit
It appears that node_modules/.vite is the directory where Vite's dependency pre-bundling feature caches things. And the script scale-button_14.entry.js the browser fails to load doesn't exist at all in node_modules/.vite/deps. So the issue might be linked to this "dependency pre-bundling" feature: somehow, could it not detect the components from the library loader?
Edit 2
I just found out there is an issue in Stencil repository mentioning that dynamic imports do not work with modern built tools like Vite. This issue has been closed 7 days ago (lucky me!), and version 2.16.0 of Stencil is supposed to fix this. We shall see.
For the time being, dropping the lazy loading and loading all the components at once through a plain old script tag in the HTML template seems to be an acceptable workaround.
<link rel="stylesheet" href="node_modules/#telekom/scale-components/dist/scale-components/scale-components.css">
<script type="module" src="node_modules/#telekom/scale-components/dist/scale-components/scale-components.esm.js"></script>
However, I can't get vite pre-bundling feature to ignore these imports. I configured optimizeDeps.exclude in vite.config.js but I still get massive warnings from vite when I run npm run dev:
export default defineConfig({
optimizeDeps: {
exclude: [
// I tried pretty much everything here: no way to force vite pre-bundling to ignore it...
'scale-components-neutral'
'#telekom/scale-components-neutral'
'#telekom/scale-components-neutral/**/*'
'#telekom/scale-components-neutral/**/*.js'
'node_modules/#telekom/scale-components-neutral/**/*.js'
],
},
// ...
});
This issue has been fixed by Stencil in version 2.16.
Upgrading Stencil to 2.16.1 in the components library dependency and rebuilding it with the experimentalImportInjection flag solved the problem.
Then, I can import it following the official documentation:
main.ts
import '#telekom/scale-components-neutral/dist/scale-components/scale-components.css';
import { applyPolyfills, defineCustomElements } from '#telekom/scale-components-neutral/loader';
const app = createApp(App);
applyPolyfills().then(() => {
defineCustomElements(window);
});
And configure the custom elements in vite config:
vite.config.js
export default defineConfig({
plugins: [vue({
template: {
compilerOptions: {
isCustomElement: (tag) => tag.startsWith('scale-')
}
}
})]
// ...
})
I did not configure main.ts
stencil.js version is 2.12.1,tsconfig.json add new config option in stencil:
{
"compilerOptions": {
...
"skipLibCheck": true,
...
}
}
add new config option in webpack.config.js :
vue 3 document
...
module: {
rules:[
...
{
test: /\.vue$/,
use: {
loader: "vue-loader",
options: {
compilerOptions: {
isCustomElement: tag => tag.includes("-")
}
}
}
}
...
]
}
...

Symfony: How to Import a Sass File into Every Vue Component

I am currently struggling with this. In my Symfony project I have a _variables.scss file, where I keep my global variables (e.g. colors).
This is included in my main scss file like this #import "variables"; - which works fine. Now I also use VueJs in my project and I would like to use my global variables inside Vue components. Now one way to achieve this is just importing the variables.scss itself:
//CustomButton.vue
<template>
...
</template>
<script>
export default {
name: "CustomButton"
}
</script>
<style lang="scss" scoped>
#import "../../scss/_variables.scss";
//able to use variables here
</style>
However with a growing project and different paths, this seems to be unnecessary work. What I'd like to achieve is to load the variables.scss into every Vue component automatically.
This is quite well explained here: [css-tricks.com - How to Import a Sass File into Every Vue Component in an App][1]
Sadly this does not work in my case (I think the vue.config.js is ignored completly) - the variables are still not usable inside the Vue component. I also tried to add the JavaScript into my main js file - where I load Vue - however this seems to break stuff (some module exception).
Is there any specific way to achieve this with symfony?
PS: I am using Symfony 5, Sass-Loader 9.0.1, Vue 2.6.12, Vue-Loader 15, Vue-Template-Compiler 2.6.12
[1]: https://css-tricks.com/how-to-import-a-sass-file-into-every-vue-component-in-an-app/
Solution 1:
//webpack.config.js
.enableSassLoader(options => {
options.additionalData = `
#import "./assets/scss/_variables.scss"; //path.resolve is not working in my case to import the absolute path
`
})
```
Webpack config:
module.exports = {
...
module: {
rules: [
{
test: /\.scss$/,
use: [
process.env.NODE_ENV !== 'production'
? 'vue-style-loader'
: MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'sass-loader',
options: {
data: `
#import "functions";
#import "variables";
#import "mixins";
`,
includePaths: [
path.resolve(__dirname, "../asset/scss/framework")
]
}
}
]
},
]
}
this config imports 3 files: "_functions.scss", "_variables.scss", "_mixins.scss" from folder"../asset/scss/framework" and no need to use #import "../../scss/_variables.scss"; every time in your vue components. Maybe, you will adapt this webpack config to your needs or will get some idea to resolve your issue.
This is what you need c:
Since you are using Encore, modify your webpack.config.js, add this code
var Encore = require('#symfony/webpack-encore');
Encore.configureLoaderRule('scss', (loaderRule) => {
loaderRule.oneOf.forEach((rule) => {
rule.use.push({
loader: 'sass-resources-loader',
options: {
resources: [
// Change this url to your _variables.scss path
path.resolve(__dirname, './assets/app/styles/_vars.scss'),
]
},
})
})
})
You also are going to have to install sass-resources-loader
npm install sass-resources-loader
Then compile your code again and your sass variables will be available everywhere
Solution to my question:
//webpack.config.js
.enableSassLoader(options => {
options.additionalData = `
#import "./assets/scss/_variables.scss"; //path.resolve is not working in my case to import the absolute path
`
})
this will import the file in every sass template + vue component

how to solve the error "Require statement not part of import statement" in using storybook?

I'm using storybook in vue.js.
I tried to import my component but it was failed, because Sass which imported in my component made error.
(component which is not importing Sass can imported and displayed.)
so, I check official description and created a .storybook/main.js file to add Sass support.
But i faced a unexpected error, "Require statement not part of import statement" in using storybook" at const path = require('path').
I couldn't find example of this error related storybook, so i'm confusing.
how to solve this error?
I just started using storybook, so i don't know which file info is needed to solve this.
if need other file info, I'll add it.
My main.js
const path = require('path');
// Export a function. Accept the base config as the only param.
module.exports = {
webpackFinal: async ( config ) => {
// Make whatever fine-grained changes you need
config.module.rules.push({
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'],
include: path.resolve(__dirname, '../'),
});
// Return the altered config
return config;
},
};
official description of using scss in storybook

Loading vuetify in a package that i use in a vuetify project

What is the correct way of loading vuetify into a package that i use in a vuetify project?
When serving projects it all seems to work fine but when i build the project i've got some issues with the css/sass
things i've tried:
With vuetify loader: the css is loaded twice so i can't overwrite sass variables
Without vuetify loader: the package doesn't have the vuetify css, so it looks horrible
Without vuetify loader with vuetify.min.css: the css is loaded twice so i can't overwrite sass variables, and the loaded css is all the css so it's huge
My package is called vuetify-resource, and this is the source code of the index.js (without the vuetify loader) At this point everything works on npm run serve But when i build the package doesn't have "access" to the vuetify css.
import Vue from 'vue';
import Vuetify from 'vuetify';
import VuetifyResourceComponent from './VuetifyResource.vue';
Vue.use(Vuetify);
const VuetifyResource = {
install(Vue, options) {
Vue.component('vuetify-resource', VuetifyResourceComponent);
},
};
export default VuetifyResource;
To solve my issue i had to do a couple of things.
Make peer dependencies of vuetify and vue
add vuetify to the webpack externals, so when someone uses the package, the package uses that projects vuetify
not longer import vue and vuetify in the index.js it's not needed, the project that uses the package imports that
import the specific components that you use in every .vue file
for example:
Vue.config.js
module.exports = {
configureWebpack: {
externals: {'vuetify/lib': 'vuetify/lib'},
},
};
index.js
import VuetifyResourceComponent from './VuetifyResource.vue';
const VuetifyResource = {
install(Vue, options) {
Vue.component('vuetify-resource', VuetifyResourceComponent);
},
};
export default VuetifyResource;
part of the component.vue
import { VDataTable } from 'vuetify/lib';
export default {
name: 'vuetify-resource',
components: {
VDataTable
},
Step 4 in Ricardo's answer is not needed if you use vuetify-loader, it will do the job for you.
And I would modify step 2 to also exclude Vuetify's styles/css from your bundle. If you don't exclude them you can run into styling issues when the Vuetify version differ between your library and your application.
Use a regular expression in vue.config.js like this: configureWebpack: { externals: /^vuetify\// }. That way, only your own styles are included in the library bundle.

Importing SCSS file in Vue SFC components without duplication with Webpack

When I try to access a scss file in all my Vue SFCs the styles are duplicated causing large css bundles and Dev Tools to crash when style debugging
I am using Webpack 4 and webpack-dev-server to build and run development services with hot reload. I did not create the project with Vue CLI.
I have quite a lot of SFCs (~50) and a sass file (index.scss) that contains global styles and variables. I need to be able to use the styles and variables in index.scss across my SFCs. My current approach is using the data option in my Webpack sass-loader.
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
},
{
test: /\.js$/,
loader: 'babel-loader',
},
{
test: /\.scss$/,
use: [
isDev ? { loader: 'vue-style-loader', options: { sourceMap: hasSM }} : {loader: MiniCssExtractPlugin.loader },
{
loader: 'css-loader',
options: {
sourceMap: hasSM
}
},
{
loader: 'sass-loader',
options: {
sourceMap: hasSM,
data: `#import "#/styles/index.scss";`
}
}
]
}
]
}
This is successful, however I am noticing my index.scss styles included in every component. Local development with devserver is almost impossible because the duplication across 50 components is so vast and devtools can't cope. When I do a build to extract the css then I can see the duplication and the app.css file is huge. I can use a minifying process for deployments but this is not suitable at all for local development.
I have tried other approaches such as removing the data option from sass-loader using import ./styles/index.scss in my main.js file instead, however this fails to build because it can't find the variables I use in my SFCs.
Please see my code snippet for roughly how I have my loaders set up. I feel as if there is a better way to do this, whether it's using different loaders/plugins and I am open to using different approaches
I managed to solve. Essentially when every Vue component was being processed then the sass-loader was importing my global sass file that included Variables, Mixins and most importantly my Styles. The import statement in my main.js doesn't work because the Variables and Mixins are not available by the time the component is being processed.
So I only need to import Variables and Mixins in my components and the Styles can be external and included in my main.js. Since Variable and Mixins are just being included when required (with #include statements), then there is no duplication since it's getting compile to CSS.
So I split my Styles and Variables and Mixins into separate variables
Styles => styles.scss
Variable and Mixins => variableMixins.scss
then import ./styles/styles.scss in my main.js
and my webpack sass-loader would be like
{
loader: 'sass-loader',
options: {
sourceMap: hasSM,
data: `#import "#/styles/variableMixins.scss";`
}
}
Been trying to solve this for too long.
This solution works but isn't perfect for me because I want to keep variables and global styles for specific things together.
_typography.scss, for example should contain general styles and variables relating to global line-height and vertical rhythm.
Then more granularly, a component such as _H1.scss would contain it's own sandboxed styles.
I don't want those general styles duplicated for every component.
The value of using CSS is sharing styles and being able to adjust and rebrand in a project globally. Components do the opposite of this by sandboxing everything.
I think CSS is currently at odds with componentized development and perhaps the answer may be to create a new approach where we use 'component ecosystems' to create relationships between otherwise dumb nodes.
A bit like how we use revealing module patterns in JS.
We ended up splitting up css into separate files (one per component), imporing them all into index.scss and importing index in App.vue`
import '#/scss/index.scss'
This eliminated lots of bloat / duplication and significantly simplified workflow: using sourcemaps + workspaces we can now edit scss directly in devtools and it won't break layout or multiply <style> tags or do any other quakery described all over Webpack's and vue-cli's github issues.
Surprisingly, even code-splitting for css still works, and dynamically loaded modules have their separate css files.
If you need scoping (which you'd need), just use id="component-name" on main node and wrap your scss in
#component-name {
...
}
It amazes me how unsupported modern css techniques by Devtools, Webpack etc. Every aspect is full of bugs and only simpliest scenarios sort of work out of the box (if work at all).