How to tell Vite to only build a specific component in library mode? - vue.js

I have a Vue project that is able to load other Vue components bundled as .mjs files. I want to develop all those pluggable components inside a repository but instead of distributing all pluggable components in a single build I want to tell Vite which component to build. If that works I don't need to think about dealing with a Monorepo, Multirepo or something else.
After creating the library project I thought about organizing each plugin into a separate folder in the src directory
.
└── src
├── textWithBlueBackground
| ├── index.ts ( importing "TextWithBlueBackground" and exporting as "Renderer" )
| └── TextWithBlueBackground.vue
└── textWithRedBackground
├── index.ts ( importing "TextWithRedBackground" and exporting as "Renderer" )
└── TextWithRedBackground.vue
The problem is that I need to switch to library mode but I don't know what to pass in there
build: {
lib: {
entry: resolve(__dirname, "./src/index.ts"), // this is wrong
name: "Renderer",
fileName: "renderer",
},
rollupOptions: {
external: ["vue"],
output: {
globals: {
vue: "Vue",
},
},
},
},
After fixing that... Is it possible to tell Vite ( via CLI flag ) to only build a specific sub directory? E.g.
vite build ./src/{folderName}
If that's not possible I could create an index.ts in src
import { Renderer as TextWithBlueBackground } from "./textWithBlueBackground";
import { Renderer as TextWithRedBackground } from "./textWithRedBackground";
export { TextWithBlueBackground, TextWithRedBackground }
but how can I tell Vite to only build a specific component then?
The generated .mjs file should only contain the desired component, preferably as "Renderer" but I think the component name should be fine too.

Related

How to import index.vue files without specifying the file name using vue 3 and vite?

I started working recently on a Vue 3 application which runs with vite and I am trying to restructure the directories so that I can group components and related sub-components in folders.
I am currently using /path/to/MyComponent/index.vue to import the higher-hierarchy component, and I would like to write the import statement without specifying the file name, so that I could do something like this:
import MyComponent from `#/path/to/MyComponent`
where the files structure looks like the following:
path
│
└───to
│
└───MyComponent
│ index.vue
│ SubComponent.vue
│ ...
I tried to play with the resolve.alias property in the vite.config.ts file, but I wasn't successful. Anyone managed to achieve this?
This is one of the attempts:
export default defineConfig({
...
resolve: {
alias: [
{
find: "#",
replacement: fileURLToPath(new URL("./src", import.meta.url)),
},
{
find: /(^(?!.*[.](ts|js|tsx|jsx|vue|)$))/,
replacement: "$1/index.vue",
},
],
},
...
After trying various things I found a solution in line with my original post involvin modifying the vite.config.ts file resolve.alias property.
In addition I had to modify the tsconfig.json file to make sure that typescript compiler is also able to resolve the path to the index.vue file.
This is how I achieved importing a index.vue component only referencing the parent folder name:
1. Vite configuration
// vite.config.ts
export default defineConfig({
resolve: {
alias: [
{
find: /#\/components\/((?!.*[.](ts|js|tsx|jsx|vue)$).*$)/,
replacement: fileURLToPath(
new URL("./src/components/$1/index.vue", import.meta.url)
),
},
{
find: "#",
replacement: fileURLToPath(new URL("./src", import.meta.url)),
},
// ...
The above configuration code will tell vite to check if the import statement contains with #/components and does NOT end with any of the following extensions: ts|js|tsx|jsx|vue. E.g. #/components/MyComponent.
When these criteria are met the find path will be replaced with the path to the index.vue file within the src/components folder. E.g. #/components/MyComponent/index.vue.
The only limitations of the above solution is that it targets a specific folder (in this case the components folder). However we can add more alias objects to match any other folder where we want to implement this import pattern.
2. Typescript configuration
If using typescript ESLint will throws two errors: Missing file extension and Unable to resolve path to module. This because the typescript compiler is agnostic of the above vite configuration.
For this reason I also had to modify the tsconfig.json file as following:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"#/components/*": ["./src/components/*", "./src/components/*/index.vue"],
"#/*": ["./src/*"]
}
}
The above code will leverage Typescript module resolution path mapping to map everything that matches #/components/* to ./src/components/*, where * represents the path within the ./src/components. This path is relative to where the tsconfig.json file reside (as defined by the baseUrl parameter).
If a component is not found, Typescript compiler will look inside ./src/components/*/index.vue.

pnpm, workspace dependency and also supporting publishing?

I am new to pnpm workspaces and am trying to resolve the following issue.
My demo project:
root
packages
common-ui
main-lib
common-ui is a Vite based package containing some Vue components that can be reused by other packages, in my example it's being used by main-lib.
"dependencies": {
"ui-common": "workspace:*"
},
common-ui is referencing an index.ts inside its package.json
"main": "index.ts",
index.ts is exporting my Vue components:
...
export { default as Heading } from './components/Heading/Heading.vue';
...
Now I am able to import those components inside main-lib:
import { Heading } from 'common-ui'
This all works fine but I would also like to be able to publish my library to the npm registry. As common-ui is using the Vite, it's possible to build in library mode: https://vitejs.dev/guide/build.html#library-mode. My package inside common-ui will need to change to:
{
"name": "common-ui",
"files": ["dist"],
"main": "./dist/common-ui.umd.js",
"module": "./dist/common-ui.es.js",
"exports": {
".": {
"import": "./dist/common-ui.es.js",
"require": "./dist/common-ui.umd.js"
}
}
}
main is not referencing index.ts anymore but a dist folder that only gets updated when the vite command is ran. Is there a way for me to support both publishing/versioning and referencing the actual source code from inside main-lib?
I've taken a quick look at Rush.js but I am not sure if provides a solution and I want to be sure before I continue on that path.

Need help in setting up a frontend monorepo for vue and nuxt using yarn workspaces and lerna

I'm trying to setup a monorepo for a frontend project using yarn workspaces and lerna.
There will be a shared component library for all the app packages in it.
The project structure looks something like this:
root
├── lerna.json
├── package.json
└── packages
├── ui-library
│ ├── src/main.js
│ ├── dist/library.common.js
│ └── package.json
└── app
When I import the ui-library in my app it gives me all sorts of lint errors.
packages/ui-library
is a regular vue project created by #vue/cli.
vue create ui-library
exposes the auto generated component src/App.vue from src/main.js
export { default as App } from './App'
I've added a script to build the project as a library
vue-cli-service build --target lib --name library src/main.js
The build script generates dist/library.common.js along with other stuff.
In the ui-library/package.json,
{
"name": "#<org>/ui-library",
"version": "0.0.1-alpha",
"private": true,
"main": "./dist/library.common.js",
"scripts": {
"build": "vue-cli-service build --target lib --name library src/main.js",
},
.
.
.
}
packages/app
It's a nuxt project created by npx create-nuxt-app app.
I've installed the local ui-library package like this
yarn workspace app add #<org>/ui-library#0.0.1-alpha
It finishes successfully.
I haven't done anything here other than importing the App component in pages/index.vue like this
<template>
<app />
</template>
<script>
import { App } from '#<org>/ui-library'
export default {
components: {
App
}
}
</script>
But the app fails to run because of lint errors.
$ yarn workspace app dev
.
.
app: path\to\packages\ui-library\dist\library.common.js
app: 88:10 error Unexpected newline between function and ( of function call
no-unexpected-multiline
app: 97:42 error '_unused_webpack_default_export' is assigned a value but never used
no-unused-vars
app: 102:17 error 'module' is defined but never used
no-unused-vars
.
.
app: 97:42 error '_unused_webpack_default_export' is assigned a value but never used
no-unused-vars
app: 102:17 error 'module' is defined but never used
no-unused-vars'HelloWorldvue_type_style_index_0_id_b9167eee_scoped_true_lang_css_' is assigned a value but never used no-unused-vars
app: 449:5 error 'Appvue_type_style_index_0_lang_css_' is assigned a value but never used no-unused-vars
app: ✖ 17 problems (17 errors, 0 warnings)
app: 1 error and 0 warnings potentially fixable with the `--fix` option.
What's the way of ignoring these lint errors or is my approach incorrect?
Any help will be much appreciated. Thanks in advance.
Now instead of shared components library, the application is divided into modules (read DDD). Each module is responsibility of a developer or a team and whatever needs to be shared is done using module federation.

Go to definition not working on my project (vue & sass file) [visual-studio-code]

I am disappointed on two points by developing a Nuxt project on vscode.
On vscode my jsconfig.js is the default one :
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"~/*": ["./*"],
"#/*": ["./*"],
"~~/*": ["./*"],
"##/*": ["./*"]
}
},
"exclude": ["node_modules", ".nuxt", "dist"]
}
It's working on vue file for autocompletion to import some components for example (with ctrl+space)
But impossible to go to definition next with cmd+click. I do not understand why and this is really annoying.
I can't post image (need 10 reputation), but here is my import on vue file (with no definition found for ...)
import PldFooter from '#/components/Footer';
Other point, I use sass files on assests folder. Compilation working well but I cannot access by cmd+click to the file from node_modules. Here is an example of import :
#import "~bulma/sass/base/helpers.sass";
==> No definition found for helpers.sass
Thank you for your help,
Ben.
Have you opened multiple folders (projects) in a window?
I'm not sure whether your issue the same as me. I got an issue "Go to Definition not working" in Visual Studio Code when I opened multiples folders (projects) and I resolved.
I have used the plugin Vetur to support the .vue file.
There are 2 ways which work well:
Open only one project in a window
You can open multiple projects in a window but the project you want to "Go to Definition" works well which must be the first project in the folder tree in the EXPLORER tab.
It seems the plugin Vetur picks up the first project in multiple projects to be the root folder.
My file tsconfig.json
{
...
"compilerOptions": {
"baseUrl": "./",
"paths": {
"#/*": ["src/*"],
}
},
"exclude": ["node_modules", "dist"]
...
}
Reference:
https://github.com/vuejs/vetur/issues/423#issuecomment-405415204
I apologize if my answer which cannot help you.
According to the Vetur setup guide:
If you are using Webpack's alias or TypeScript's path mapping to resolve components, you need to update Vetur's tsconfig.json or jsconfig.json
For example:
└── src
├── components
│ ├── a.vue
│ └── b.vue
├── containers
│ └── index.vue
├── index.js
└── jsconfig.json
jsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"components/*": [
"src/components/*"
]
}
}
}
index.vue
import a from 'components/a.vue'
import b from 'components/b.vue'
It solved the problem in my case.

How can I build my React Native Storybook to web?

I am running React Native Storybook which runs Storybook in the Native emulator.
In addition to the how React Native Storybook works currently, I would also like to build an instance of it for web as a reference companion to our app.
I am using "#storybook/react-native": "5.3.14". My stories are located at ./storybook.
Install react-native-web, #storybook/react and babel-plugin-react-native-web from npm in your project root.
Add a new configuration directory for Storybook, say ./.storybook-website. Inside this directory, add main.js. This creation would otherwise be done by the Storybook installation wizard.
my-app
├── .storybook-website
│   └── main.js
└── // .... rest of your app
Add the following content to main.js:
module.exports = {
stories: ['../storybook/stories/index.js'],
webpackFinal: async (config) => {
config.resolve.alias = {
...(config.resolve.alias || {}),
// Transform all direct `react-native` imports to `react-native-web`
'react-native$': 'react-native-web',
// make sure we're rendering output using **web** Storybook not react-native
'#storybook/react-native': '#storybook/react',
// plugin-level react-native-web extensions
'react-native-svg': 'react-native-svg/lib/commonjs/ReactNativeSVG.web',
// ...
};
// mutate babel-loader
config.module.rules[0].use[0].options.plugins.push(['react-native-web', { commonjs: true }]);
// console.dir(config, { depth: null });
return config;
},
};
Update the stories path in main.js to the location of your existing root story.
Finally add run scripts to your package.json:
"storybook:web": "start-storybook -p 6006 --config-dir ./.storybook-website",
"storybook-build:web": "build-storybook --config-dir ./.storybook-website --output-dir dist-storybook-website --quiet"
Presto! Run using yarn storybook:web. This will run storybook dev server, opening a browser showing what you usually would see in the device emulator.