Related
I'm using Vite. I installed the npm plugin called 'vite-plugin-zip-file' in order to archive the dist folder. How can I run a separate from 'build' script in package.json specifically for this task?
vite.config.js below
import { defineConfig } from 'vite';
import { resolve } from 'path';
import { viteZip } from 'vite-plugin-zip-file';
export default defineConfig({
build: {
outDir: 'build',
},
plugins: [
viteZip({
folderPath: resolve(__dirname, 'build'),
outPath: resolve(__dirname),
zipName: 'Test.zip',
}),
],
});
package.json 'scripts' (I don't know what to write in 'zip' here)
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
"zip": ?
},
I tried adding the environmental variable, but I don't know exactly how to do it. So, there's probably another better way.
I followed the vite documentation for using library mode and I am able to produce a working component library.
I created the project with the vue-ts preset and in my component I have defined props with their types, and used some interfaces. But when I build the library, there are no types included.
How do I add types for the final build, either inferred from components automatically or manually with definition files?
More information
Here is some more information on my files:
tsconfig.json
{
"name": "#mneelansh/test-lib",
"private": false,
"version": "0.0.2",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview"
},
"emitDeclarationOnly": true, // testing
"declaration": true, // testing
"main": "./dist/lib.umd.js",
"module": "./dist/lib.es.js",
"types": "./dist/main.d.ts",
"exports": {
".": {
"import": "./dist/lib.es.js",
"require": "./dist/lib.umd.js"
},
"./dist/style.css": "./dist/style.css"
},
"files": [
"dist"
],
"dependencies": {
"#types/node": "^17.0.25",
"vue": "^3.2.25"
},
"devDependencies": {
"#vitejs/plugin-vue": "^2.3.1",
"typescript": "^4.5.4",
"vite": "^2.9.5",
"vue-tsc": "^0.34.7"
}
}
I added the emitDeclarationOnly and declaration properties but that didn't help.
My vite.config.ts:
import { defineConfig } from "vite";
import vue from "#vitejs/plugin-vue";
const path = require("path");
// https://vitejs.dev/config/
export default defineConfig({
build: {
lib: {
entry: path.resolve(__dirname, "src/index.ts"),
name: "Button",
fileName: (format) => `lib.${format}.js`,
},
rollupOptions: {
external: ["vue"],
output: {
globals: {
vue: "Vue",
},
},
},
},
plugins: [vue()],
});
You can use vite-plugin-dts
import dts from "vite-plugin-dts";
export default defineConfig({
plugins: [
dts({
insertTypesEntry: true,
}),
],
Usually with vite and typescript project you need add type checking before build, because vite doesn't do it by himself. Here I'm also using vite-plugin-dts as in post from Julien Kode, and for type checking rollup-plugin-typescript2.
Finally my vite.config.js:
import { defineConfig } from 'vite';
import Vue from '#vitejs/plugin-vue2';
import dts from 'vite-plugin-dts';
import rollupTs from 'rollup-plugin-typescript2';
export default defineConfig({
plugins: [
Vue(),
dts({ insertTypesEntry: true }),
// only for type checking
{
...rollupTs({
check: true,
tsconfig: './tsconfig.json',
tsconfigOverride: {
noEmits: true,
},
}),
// run before build
enforce: 'pre',
},
],
build: {
sourcemap: true,
lib: {
entry: './src/index.ts',
fileName: 'index',
},
rollupOptions: {
// make sure to externalize deps that shouldn't be bundled
// into your library
external: [
'vue',
'vue-class-component',
'vue-property-decorator',
'vuex',
'vuex-class',
],
output: {
// Provide global variables to use in the UMD build
// for externalized deps
globals: {
vue: 'Vue',
},
},
},
},
});
You could write your own Vite plugin to leverage tsc at the buildEnd step to accomplish this. As other answers have suggested, you can use the flag emitDeclarationOnly.
See this simple example:
import { type Plugin } from 'vite';
import { exec } from 'child_process';
const dts: Plugin = {
name: 'dts-generator',
buildEnd: (error?: Error) => {
if (!error) {
return new Promise((res, rej) => {
exec('tsc --emitDeclarationOnly', (err) => (err ? rej(err) : res()));
});
}
},
};
Then add to your plugins field of your vite config
Personally I think a nicer way to do it is with vue-tsc:
vue-tsc --declaration --emitDeclarationOnly
See https://stackoverflow.com/a/70343443/398287
I created an app within a monorepo (Lerna) using yarn workspace.
The architecture of the app is as follow:
my-monorepo
├── node_modules
├── packages
│ ├── package1(shared components)
│ ├── package2(other package consuming the shared components)
│ │ └── ./jest.config.js
├── package.json
The problem
the problem is jest is throwing the following error when trying to use package1 in package2 in any test, and I haven't found a way to fix it.
● Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest ca
nnot parse, e.g. it's not plain JavaScript.
By default, if Jest sees a Babel config, it will use that to transfor
m your files, ignoring "node_modules".
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io
/docs/en/ecmascript-modules for how to enable it.
• To have some of your "node_modules" files transformed, you can spe
cify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option i
n your config.
• If you simply want to mock your non-JS modules (e.g. binary assets
) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the
docs:
https://jestjs.io/docs/en/configuration.html
Details:
C:\Users\my-user\Desktop\my-monorepo\node_module
s\antd\es\date-picker\generatePicker\index.js:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__fi
lename,global,jest){import _extends from "#babel/runtime/helpers/esm/exte
nds";
^^^^^^
According the error, I'm trying to import a file which Jest cannot parse, so the problem comes from the package1, so the first thing that comes to my mind is: maybe I'm doing something wrong in rollup in package1 and the final bundle comes in some format that jest doesn't understand...
Jest config
Jest config located in package2, where i want to consume package1:
// jest.config.js in package2
const config = {
roots: ['src'],
setupFilesAfterEnv: ['./jest.setup.ts'],
moduleFileExtensions: ['ts', 'tsx', 'js'],
testPathIgnorePatterns: ['node_modules/'],
transform: {
'^.+\\.tsx?$': 'ts-jest',
},
testMatch: ['**/*.test.(ts|tsx)'],
moduleNameMapper: {
// Mocks out all these file formats when tests are run.
'\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'identity-obj-proxy',
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
},
};
export default config;
Rollup config
This is the rollup configuration in package1:
// rollup.config.js
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import typescript from 'rollup-plugin-typescript2';
import postcss from 'rollup-plugin-postcss';
import copy from 'rollup-plugin-copy';
import json from '#rollup/plugin-json';
import svgr from '#svgr/rollup';
import { babel } from '#rollup/plugin-babel';
import { visualizer } from 'rollup-plugin-visualizer';
import pkg from './package.json';
export default {
input: 'src/index.tsx',
output: [
{
file: pkg.main,
format: 'cjs',
exports: 'named',
sourcemap: true,
},
{
file: pkg.module,
format: 'esm',
exports: 'named',
sourcemap: true,
},
],
plugins: [
peerDepsExternal({
includeDependencies: true,
}),
json(),
svgr(),
resolve({ extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'] }),
commonjs({
include: /\**node_modules\**/,
}),
// UPDATE 3: Add babel
babel({
babelHelpers: 'bundled',
}),
typescript({
useTsconfigDeclarationDir: true,
exclude: ['*.d.ts', '**/*.d.ts', '**/*.test.tsx'],
rollupCommonJSResolveHack: true,
clean: true,
}),
postcss({
extensions: ['.css', '.less'],
use: {
less: { javascriptEnabled: true, modifyVars: {} },
},
}),
visualizer({ filename: 'stats-visualizer.html', gzipSize: true }),
copy({
targets: [
{
src: 'src/styles/themes/dark-variables.less',
dest: 'dist',
rename: 'theme/dark.less',
},
{
src: 'src/styles/themes/light-variables.less',
dest: 'dist',
rename: 'theme/light.less',
},
{
src: 'src/assets/icons',
dest: 'dist',
rename: 'assets/icons',
},
],
}),
],
};
UPDATE 1:
I've tried to use transform rules in jest.config.js as mentioned by Matt Carlota to transpile antdbut this doesn't work:
// jest.config.js
const config = {
// ... other jest settings
transform: {
'^.+\\.tsx?$': 'ts-jest',
'node_modules/antd/.+\\.(j|t)sx?$': 'ts-jest',
},
// I've tried with `antd/es` pattern too and doesn't work
transformIgnorePatterns: ['node_modules/(?!antd)'],
};
UPDATE 2:
Change manually antd/es by antd/lib in package1 resolve the problem temporarily, but there is one problem and that is that we are a large group of people working and it could be put as a convention to use only antd/lib but I feel it would be error prone.
every time someone forgets to use antd/lib and uses antd/en all tests break with the original error.
UPDATE 3:
Add babel config file and plugin in rollup configuration...
// babel.config.js
module.exports = {
plugins: [['import', { libraryName: 'antd', libraryDirectory: 'lib' }, 'antd']],
};
I've been having the same issue for the longest time and finally found a way.
The missing piece in my case was adding moduleNameMapper. A guide mentioned that here.
The solution doesn't require babel-jest.
jest.config.js
const path = require('path');
const { lstatSync, readdirSync } = require('fs');
// get listing of packages in the mono repo
const basePath = path.resolve(__dirname, '..', '..', 'packages');
const packages = readdirSync(basePath).filter((name) => {
return lstatSync(path.join(basePath, name)).isDirectory();
});
module.exports = {
preset: 'ts-jest',
verbose: true,
moduleFileExtensions: ['js', 'json', 'jsx', 'node', 'ts', 'tsx'],
moduleDirectories: ['node_modules', 'src'],
moduleNameMapper: {
...packages.reduce(
(acc, name) => ({
...acc,
[`#xyz/${name}(.*)$`]: `<rootDir>/../../packages/./${name}/src/$1`,
}),
{}
),
},
rootDir: './',
testRegex: '.spec.ts$',
transform: {
'^.+\\.(t)s$': 'ts-jest',
},
testEnvironment: 'node',
setupFilesAfterEnv: ['./jest.setup.ts'],
testMatch: null,
globals: {
'ts-jest': {
tsconfig: 'tsconfig.jest.json',
},
},
roots: ['<rootDir>'],
transformIgnorePatterns: [],
collectCoverage: false,
collectCoverageFrom: ['src/**/*.{js{,x},ts{,x}}', '!src/server/index.ts'],
};
tsconfig.json
{
"compilerOptions": {
"module": "esnext",
"target": "ES2019",
"lib": ["ES2019", "DOM"],
"noEmit": true,
"types": ["node", "jest"],
"rootDir": "./"
"paths": {
"#xyz/*": ["packages/*/src"]
},
},
"include": ["test/**/*.ts", "src/**/*.ts", "**/*.spec.ts"],
"references": [
{ "path": "../../packages/package1" },
]
}
package.json
...
scripts: {
"test": "NODE_ENV=production tsc --build ./tsconfig.jest.json && jest --env=node test --watch",
}
I use Vue 3 on Vite.js with Eslint + Airbnb config. Airbnb config has a rule eslint(import/no-unresolved), which is good, but Eslint doesn't know how to resolve alias path.
I want to use aliases for paths — example:
import TableComponent from '#/components/table/TableComponent.vue'˙
Environment is in plain JavaScript.
I managed to set up my vite.config.js so that the app can resolve paths like this:
import path from 'path';
import { defineConfig } from 'vite';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
resolve: {
alias: [{
find: "#", replacement: path.resolve(__dirname, 'src')
},],
},
});
Vue app works like that and resolves the import path correctly, but Eslint keeps reporting the error: Unable to resolve path to module eslint(import/no-unresolved)
How and where can I tell Eslint how to resolve aliases?
I have tried:
install eslint-plugin-import eslint-import-resolver-alias --save-dev
// .eslintrc.js
// ...
extends: [
'eslint:recommended',
'plugin:import/recommended',
'airbnb-base',
'plugin:vue/vue3-strongly-recommended',
],
settings: {
'import/resolver': {
alias: {
map: [
['#', 'src'],
],
},
},
},
But that doesn't work.
EDIT:
Solved the issue, see the accepted answer if you're using plain JavaScript like I do.
If you're using TypeScript, see if Seyd's answer can help you.
this solves the issue in my TypeScript project.
npm install eslint-import-resolver-typescript
After eslint-import-resolver-typescript installation
{
// other configuration are omitted for brevity
settings: {
"import/resolver": {
typescript: {} // this loads <rootdir>/tsconfig.json to eslint
},
},
}
should be added to .eslintrc.js.
my tsconfig.json (remove unwanted settings)
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"strict": false,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"esModuleInterop": true,
"lib": ["esnext", "dom"],
"types": ["vite/client", "node"],
"baseUrl": ".",
"paths": {
"#/*": ["src/*"]
},
"allowJs": true
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": ["node_modules"]
}
Check the discussion here:
In case someone runs into this problem, this works in my case*:
settings: {
'import/resolver': {
alias: {
map: [
['#', './src'],
],
},
},
},
*In my case, Vue's root is 'src' directory, while Eslint's is one level higher, so it needs the './src' path.
Huge thanks to #https://github.com/aladdin-add for the answer through the github question!
I had the same problem, and even I fixed the src path it still hat the issue. It did not work until I added extensions:
"settings": {
"import/resolver": {
"alias": {
"map": [
["#", "./src"]
],
"extensions": [".js",".jsx"] <--- HERE
}
}
},
1. Use & export aliases in vite.config.js
// vite.config.js
import { resolve } from 'path';
import { defineConfig } from 'vite';
export const aliases = {
'#': resolve(__dirname, './src'),
'#u': resolve(__dirname, './src/utils'),
};
export default () => defineConfig({
// ...
resolve: {
alias: aliases,
},
})
2. Create .eslintrc with ES Modules support. (Thanks to Morgan's answer)
2.1 npm i esm -D
2.2 Create sibling to .eslintrc.js — .eslintrc.esm.js.
2.3 Put your your ESLint config into .eslintrc.esm.js.
// .eslintrc.esm.js
export default {
root: true,
extends: ['#vue/airbnb', 'plugin:vue/recommended'],
// ...
}
2.4 Inside .eslintrc.js include this code:
const _require = require('esm')(module)
module.exports = _require('./.eslintrc.esm.js').default
3. Import, map & use aliases from vite.config.js in .eslintrc.esm.js
3.1 npm i eslint-import-resolver-alias -D
3.2 Inside .eslintrc.esm.js include following code:
// .eslintrc.esm.js
import { aliases } from './vite.config';
const mappedAliases = Object.entries(aliases).map((entry) => entry); // [[alias, path], [alias, path], ...]
export default {
// ...
settings: {
'import/resolver': {
alias: {
map: mappedAliases,
},
},
},
}
I can't for the life of me get this to work, I've tried everything above. Is there something else wrong with my file?
// .eslintrc.cjs
/* eslint-env node */
require("#rushstack/eslint-patch/modern-module-resolution");
module.exports = {
root: true,
extends: [
"airbnb",
"plugin:vue/vue3-essential",
"eslint:recommended",
"#vue/eslint-config-prettier",
],
parserOptions: {
ecmaVersion: "latest",
},
// Using the accepted answer
settings: {
"import/resolver": {
alias: {
map: [["#", "./src"]],
},
},
},
};
** UPDATE **
The only way I was able to get this to work was using this: eslint-config-airbnb link here, the readme was particularly helpful.
npm add --dev #vue/eslint-config-airbnb #rushstack/eslint-patch
My .eslintrc.js is now:
/* eslint-env node */
require("#rushstack/eslint-patch/modern-module-resolution");
const path = require("node:path");
const createAliasSetting = require("#vue/eslint-config-airbnb/createAliasSetting");
module.exports = {
root: true,
extends: [
"plugin:vue/vue3-essential",
"#vue/eslint-config-airbnb", // <-- added
"eslint:recommended",
"#vue/eslint-config-prettier",
],
parserOptions: {
ecmaVersion: "latest",
},
rules: {
"import/no-unresolved": "error",
},
settings: {
...createAliasSetting({
"#": `${path.resolve(__dirname, "./src")}`,
}),
},
};
Huzzah!
In the README.md of eslint-plugin-import it is said:
Currently Node and webpack resolution have been implemented, but the resolvers are just npm packages, so third party packages are supported (and encouraged!).
Here you can see the list of the third party resolvers.
One that worked for me was this: eslint-import-resolver-custom-alias, and this is how I used it:
settings:
import/resolver:
eslint-import-resolver-custom-alias:
alias:
'#': './app/javascript'
extensions:
- '.js'
- '.vue'
For me the # had been set as an alias for app/javascript inside vite.config.ts, you should change this based on your project configurations.
in .eslintrc.js add
settings: {
'import/resolver': {
alias: {
map: [['#', './src/']],
extensions: ['.js', '.vue'],
},
},
},
I've tried several guides and many configurations, but can't get my rollup, postcss, and svelte bundle process to work quite right.
Right now the svelte plugin is extracting the css from my .svelte files and emitting it to the posctcss plugin, but it's doing it one file at a time instead of the entire bundle. This makes it so some functions in the purgecss and nanocss postcss plugins don't completely work because they need the entire bundle to do things like remove duplicate/redundant/unused css rules.
// rollup.config.js
import svelte from 'rollup-plugin-svelte'
import resolve from 'rollup-plugin-node-resolve'
import commonjs from 'rollup-plugin-commonjs'
import livereload from 'rollup-plugin-livereload'
import { terser } from 'rollup-plugin-terser'
import rollup_start_dev from './rollup_start_dev'
import builtins from 'rollup-plugin-node-builtins'
import postcss from 'rollup-plugin-postcss'
const production = !process.env.ROLLUP_WATCH
export default {
input: 'src/main.js',
output: {
sourcemap: true,
format: 'iife',
name: 'app',
file: 'public/bundle.js',
},
plugins: [
svelte({
dev: !production,
emitCss: true,
}),
postcss({
extract: true,
sourceMap: true,
}),
builtins(),
resolve({
browser: true,
dedupe: importee =>
importee === 'svelte' || importee.startsWith('svelte/'),
}),
commonjs(),
!production && rollup_start_dev,
!production && livereload('public'),
production && terser(),
],
watch: {
clearScreen: false,
},
}
// postcss.config.js
const production = !process.env.ROLLUP_WATCH
const purgecss = require('#fullhuman/postcss-purgecss')
module.exports = {
plugins: [
require('postcss-import')(),
require('tailwindcss'),
require('autoprefixer'),
production &&
purgecss({
content: ['./src/**/*.svelte', './src/**/*.html', './public/**/*.html'],
css: ['./src/**/*.css'],
whitelistPatterns: [/svelte-/],
defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || [],
}),
production &&
require('cssnano')({
preset: 'default',
}),
],
}
How can I have rollup pass the entire bundle.css to postcss instead of one "file" at a time?
I had the same problem, preprocess goes file by file, so I had to actually include all my mixins and vars in every file, which is absolutely not a good solution.
So for me the first solution was to remove postcss from sveltePreprocess, not emit the css file and to use postcss on the css bundle, that you get in the css function from svelte.
You can then or (1) use postcss directly in the css function of svelte, and then emit the resulting css file in your dist directory, or (2) you can emit this file in a CSS directory, and have postcss-cli watch this directory and bundle everything
Solution 1
// rollup.config.js
// rollup.config.js
import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import postcss from 'postcss';
import postcssConfig from './postcss.config.js';
const postcssPlugins = postcssConfig({});
const postcssProcessor = postcss(postcssPlugins);
export default {
input: 'src/main.js',
output: {
file: 'public/bundle.js',
format: 'iife',
},
plugins: [
svelte({
emitCss: false,
css: async (css) => {
const result = await postcssProcessor.process(css.code);
css.code = result.css;
css.write('public/bundle.css');
},
}),
resolve(),
],
};
and my postcss.config.js which returns a function that return an array of plugins:
export default (options) => {
const plugins = [
require('postcss-preset-env')()
];
if (options.isProd) {
plugins.push(require('cssnano')({ autoprefixer: false }));
}
return plugins;
};
Solution 2
// rollup.config.js
import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
export default {
input: 'src/main.js',
output: {
file: 'public/bundle.js',
format: 'iife',
},
plugins: [
svelte({
emitCss: false,
css: async (css) => {
css.write('css/svelte-bundle.css');
},
}),
resolve(),
],
};
// package.json
{
//...
"scripts": {
"dev": "npm-run-all --parallel js:watch css:watch",
"js:watch": "rollup -c -w",
"css:watch": "postcss css/app.css --dir dist/ --watch",
},
}
/* css/app.css */
#import 'vars.css';
#import 'mixins.css';
/* all other code ... */
/* and svelte-bundle, which will trigger a bundling with postcss everytime it is emitted */
#import 'svelte-bundle.css';
Conclusion
All in all, I don't like this methods, for exemple because I can't use nesting, as svelte throws an error if the css is not valid.
I would prefer being able to use rollup-plugin-postcss after rollup-plugin-svelte, with emitCss set to false and the possibility to use rollup's this.emitFile in svelte css function, because since once the bundled file is emitted, we should be able to process it.
It seems there are some issues talking about using emitfile, let's hope it will happen sooner than later https://github.com/sveltejs/rollup-plugin-svelte/issues/71
Can't say for sure, but when i compare your setup with mine the most striking difference is that i have:
css: css => {
css.write('public/build/bundle.css');
}
in the svelte options additionally.
My whole svelte option looks like this:
svelte({
preprocess: sveltePreprocess({ postcss: true }),
dev: !production,
css: css => {
css.write('public/build/bundle.css');
}
})
Note, i'm using sveltePreprocess which would make your postcss superfluous, but i don't think that is causing your issue.