1656759600
A vite plugin that converts the code from require syntax to import
"require syntax" is supported when develop with Webpack cause it transformed it internally.
but when serve with Vite error "require is not defined" will show up.
This plugin amis to support require when serve with vite.
yarn add -D vite-plugin-require-transform
or
npm i vite-plugin-require-transform --save-dev
// vite.config.(t|j)s
import { defineConfig } from 'vite';
/**
* @param match
* Regular expression in string or Regexp type,
* or a match predicate (this: vite transform context, code: string, id: file name string) => void
* @returns transformed code
*/
import requireTransform from 'vite-plugin-require-transform';
export default defineConfig({
plugins: [
// passing string type Regular expression
requireTransform({}),
],
});
// check the vite-plugin-require-transform params'type
export type VitePluginRequireTransformParamsType = {
//filter files that should enter the plugin
fileRegex?: RegExp = /.ts$|.tsx$/ ,
//prefix that would plugin into the requireSpecifier
importPrefix? = '_vite_plugin_require_transform_': string,
//to deal with the requireSpecifier
importPathHandler?: Function
}
you can also check the test directory to see the cases.
const case1 = require("case1");
console.log("case1", case1)
will be transformed into
import _vite_plugin_require_transform_case1 from "case1";
const case1 = _vite_plugin_require_transform_case1;
console.log("case1", case1);
const case2A = location.host == 'test' ? null : require("case2");
if(location.host == 'test1' ){
case2A.start();
}
case2A.stop();
will be transformed into
import { start as _vite_plugin_require_transform_case2start, stop as _vite_plugin_require_transform_case2stop } from "case2";
const _vite_plugin_require_transform_case2 = {
start: _vite_plugin_require_transform_case2start,
stop: _vite_plugin_require_transform_case2stop
};
const case2A = location.host == 'test' ? null : _vite_plugin_require_transform_case2;
if (location.host == 'test1') {
case2A.start();
}
case2A.stop();
const case2B = {
test:require('test2B').Something
}
will be transformed into
import { Something as _vite_plugin_require_transform_test2BSomething } from "test2B";
const case2B = {
test: _vite_plugin_require_transform_test2BSomething
};
const case2c =require('test2C')
case2c.forEach((item)=>{
console.log('item',item)
})
will be transformed into
import _vite_plugin_require_transform_test2C from "test2C";
const case2c = _vite_plugin_require_transform_test2C;
case2c.forEach(item => {
console.log('item', item);
});
when exist a case as same fileName,different extensions,by default it would be error cause the plugin only capture the path without extension.
//same path,different extension
const testCaseA = require("caseA.extA?aaa");
const testCaseB = require("caseA.extB?bbb");
console.log("caseA", testCaseA)
console.log("caseB", testCaseB)
so we need to make a importPathHandler to deal with the situtation
//check out __test__/index
glob("__test__/case3/*.ts", {
ignore: "**/*transformed_result.ts"
}, async (err, files) => {
for (const file of files) {
const fileContent = readFileSync(file, 'utf-8');
const transformedContent = await vitePluginRequireTransform(
{
importPathHandler: (requirePath: string) => {
return requirePath.replace('.', '_').replace('?', "_");
}
}
).transform(fileContent, file);
writeFileSync(file.replace('.ts', '_transformed_result.ts'), transformedContent.code);
}
})
will be transformed into
import _vite_plugin_require_transform_caseA_extB_bbb from "caseA.extB?bbb";
import _vite_plugin_require_transform_caseA_extA_aaa from "caseA.extA?aaa";
//same path,different extension
const testCaseA = _vite_plugin_require_transform_caseA_extA_aaa;
const testCaseB = _vite_plugin_require_transform_caseA_extB_bbb;
console.log("caseA", testCaseA);
console.log("caseB", testCaseB);
Author: WarrenJones
Source code: https://github.com/WarrenJones/vite-plugin-require-transform
License:
#vite #typescript
1656759600
Inspect the intermediate state of Vite plugins. Useful for debugging and authoring plugins.
npm i -D vite-plugin-inspect
Since
vite-plugin-inspect@v0.5.0
, Vite v2.9 or above is required.
Add plugin to your vite.config.ts
:
// vite.config.ts
import Inspect from 'vite-plugin-inspect'
export default {
plugins: [
Inspect(), // only applies in dev mode
],
}
Then visit localhost:3000/__inspect/ to inspect the modules.
Author: antfu
Source code: https://github.com/antfu/vite-plugin-inspect
License: MIT license
#vite #vue #typescript
1656752400
Vuelix is a Vue 3 + Vite starter template to scaffold new projects really fast and with a great developer experience.
Install Dependencies
npm install
Generate API client
npm run gen-api
NOTE: This command requires a java
jvm
to be installed, if you want to avoid asking all developers to install it check OpenAPI Client Generator for more info.
Start the development server
npm run dev
To build the app, run
npm run build
And to preview it, after building the app run
npm run serve
The version 3 of Vue with its powerful Composition API is available in this project.
The new <script setup>
SFCs syntax is also available and recommended.
Vite is the lightning Fast Next Generation Frontend Tooling that highly improves the development experience along with all the community-created plugins.
NOTE: The initial state of this project was generated using Vite oficial scaffolding:
npm init vite@latest
See:
TypeScript and SCSS languages are supported and strongly recommended.
See:
Routes for vue-router
will be auto-generated from Vue files in the src/pages
using the file structure.
See:
Vue components in the src/layouts
dir are used as layouts. By default, default.vue
will be used unless an alternative is specified in the route meta.
You can specify the layout in the page's SFCs like this:
<route lang="yaml">
meta:
layout: home
</route>
See:
@/
is aliased to the ./src/
folder.
For example, instead of having
import HelloWorld from '../../../components/HelloWorld.vue'
you can use
import HelloWorld from '@/components/HelloWorld.vue'
Use icons from any icon set, one syntax for all icons: Material Design Icons, Bootstrap Icons, Font Awesome, etc. All icons libraries are available powered by iconify and unplugin-icons. And don't worry, only the icons you use would be included in the final bundle, keeping the production build lightweight.
The usage is simple, if you want for instance a Material Design Icon (mdi) with name "thumb-up", then just place this inside your template:
<i-mdi-thumb-up />
Just by placing it, the unplugin-icons/resolver
would look for the corresponding icon and in case the related iconify icon set is not installed, it would automatically install it using npm
, e.g. @iconify-json/mdi
.
The convention to use icons is as follows:
{prefix}-{collection}-{icon}
Where the prefix
is "i", the collection
is the collection ID from https://icon-sets.iconify.design/, and finally the icon
is the icon name.
See:
Route changes are animated. By default, the fade
transition will be used unless an alternative is specified in the route meta.
The fade
and slide-fade
transitions are available. You can specify the transition in the page's SFCs like this:
<route lang="yaml">
meta:
transition: slide-fade
</route>
NOTE: Transitions are not triggered between routes of the same type, therefore changing the parameters of the active route won't cause a route transition. This could be changed by using the
route.fullPath
instead ofroute.name
as the key in RouterViewTransition.vue. More info: https://stackoverflow.com/a/70042452/4873750.
Route transitions can be deactivated by changing the provided
enable-route-transitions
value in main.ts.
See:
This project comes with the recommended Eslint configuration for Vue 3 plus integration with Prettier. Prettier helps formatting code while Eslint helps catching bugs in development.
When opening the project in VSCode, it will ask the developers to install Eslint and Prettier, because that way the VSCode settings.json will work and therefore both Prettier and Eslint fix will be executed when saving a file.
Aditionally, commands to lint, check and autoformat code are available in the scripts of package.json
See:
Manually creating an API client is hard to maintain and time demanding, but thanks to OpenAPI and its generators we can now generate the entire API client from an OpenAPI Spec
.
To do so just place your spec in spec/schema.yml
, then run:
npm run gen-api
Which would generate the API client in Typescript and place the generated code in src/api-client
.
NOTE: This command requires
java
to be installed, because the OpenAPI generator is built with it, if you want to avoid asking all developers to install ajvm
and run this command by themselves, just run it once you change the OpenAPI spec, and commit the generated code, for that you need to remove the/src/api-client
line from the.gitignore
. The reason we exclude the generated client by default if because it can always be generated from the spec (spec/schema.yml
), and because the spec file is actually versioned, then the code reviewing is improved by checking only spec changes and not the generated code that nobody wrotes.
INFO: If you have a Mac with an M1 Chip, this page have the correct
OpenJDK
installers for you: https://www.azul.com/downloads/?os=macos&architecture=arm-64-bit
To use the generated APIs just initialize them and make it available for the rest of the application. The following is an example using Swagger Demo PetStore API:
// "api/index.ts"
import { PetApi } from '@/api-client'
export const petApi = new PetApi()
You can also configure the APIs parameters like basePath
and provide your own axios
instance with interceptors configured like this:
// "api/index.ts"
import { PetApi } from '@/api-client'
import { Configuration } from '@/api-client/configuration'
import axiosInstance from './axios'
// See Vite env vars: https://vitejs.dev/guide/env-and-mode.html
const config = new Configuration({ basePath: import.meta.env.BASE_URL })
export const petApi = new PetApi(config, undefined, axiosInstance)
Then in your Vue Components:
<!-- "pages/home.vue" -->
<script setup lang="ts">
import { petApi } from '@/api'
import { Pet, PetStatusEnum } from '@/api-client'
import { ref } from 'vue'
const pets = ref<Pet[]>()
const loading = ref(false)
async function testOpenAPI() {
loading.value = true
const { data } = await petApi.findPetsByStatus({ status: [PetStatusEnum.Available] })
pets.value = data.slice(0, 10)
loading.value = false
}
</script>
See:
The auth system consist on three main parts:
The plugin is installed in Vue's globalProperties
with the name $auth
, it includes an isAuthenticated
property, an user
object, an accessToken
plus the login
and logout
functions. It can be used in templates like this:
<span v-if="$auth.isAuthenticated">
Authenticated as <b>{{ $auth.user.email }}</b>
<button @click="$auth.logout">Logout</button>
</span>
<span v-else>Not Authenticated</span>
The auth
instance is created using the composition API, therefore we can alternatively retrieve it outside of components with the useAuth
function:
import { useAuth } from './useAuth'
const auth = useAuth()
if (auth.isAuthenticated) {
console.log(auth.userFullName)
}
<script setup lang="ts">
import { useAuth } from './useAuth'
import { watchEffect } from 'vue'
const auth = useAuth()
watchEffect(() => {
console.log(auth.isAuthenticated)
})
</script>
Aditionally, the auth plugin can be inspected in the Vue's Devtools panel when having the extension in the browser. The plugin's values are displayed when inspecting any component.
The navigation guards protects pages from non-authenticated users and redirect them to the login page, by default all pages but the login
page are protected.
In order to make a page available for non-authenticated users, a route meta boolean called public
needs to be configured in the page. E.g:
<!-- pages/index.html -->
<route lang="yaml">
meta:
public: true
</route>
The navigation guards can be disabled by changing the autoConfigureNavigationGuards
when configuring the auth system:
// main.ts
import { createApp } from 'vue'
import { createAuth } from './auth'
import App from './App.vue'
import router from './router'
const auth = createAuth({
router,
loginRouteName: 'login',
autoConfigureNavigationGuards: false,
})
const app = createApp(App)
app.use(router)
app.use(auth)
The axios interceptors helps appending auth information to requests and responses of APIs.
The main interceptor adds the Authorization
header with a value of Bearer the-token-value
to all authenticated requests.
This can be configured and disabled in the createAuth
options:
// api/axios.ts
import axios from 'axios'
const axiosInstance = axios.create()
export default axiosInstance
// main.ts
import { createApp } from 'vue'
import { createAuth } from './auth'
import App from './App.vue'
import router from './router'
import axiosInstance from './api/axios'
const auth = createAuth({
router,
axios: {
instance: axiosInstance,
autoAddAuthorizationHeader: true, // default: false
authorizationHeaderPrefix: 'Token', // default: 'Bearer'
},
})
const app = createApp(App)
app.use(router)
app.use(auth)
See:
The vue-i18n
package is used as the internationalization system.
All translation files located in the locales
dir are loaded automatically with the corresponding language code obtained from the file name, e.g. locales/es.json
-> lang code: es
.
How to use it?
Put the texts in the original language inside the function of vue-i18n, for example:
<!-- Single or double quote, and template literals -->
<p>{{ $t('Hello World') }} {{ $t("Hello, how are you?") }} {{ $t(`Hey. I'm watching you!`) }}</p>
<!-- *Note: to be able to use it in tags or when we send text to a component, we must use the single quote format
and bind it to the attribute. -->
<MyComponent :text="$t('example text')" />
<b-form-input v-model="name" type="text" :placeholder="$t('Name')"></b-form-input>
// In TS:
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
t('This is an example')
</script>
You may have noticed that we don't use translations keys like: greetings.hello
, the reason is that defining keys is a troublesome task, and keys doesn't always show what we want to display, take this translation file for example:
// es.json
{
"greetings": {
"hello": "Hola, ¿cómo estás?."
}
}
And the corresponding translation usage:
// Component.vue
t('greetings.hello')
By just looking at the translation key, we won't know what the original text was, now look a this example:
// es.json
{
"Hello, how are you?": "Hola, ¿cómo estás?."
}
// Component.vue
$t('Hello, how are you?')
Better right?, we can directly see the original text, and it's much simpler to translate, we also won't need to define keys because the original text is the key!.
Browser language detection
The default language would match the language of the browser, in case the language is not supported by the application, the fallback language en
would be activated.
Vue i18n extract
Manually extracting the texts from vue or js,ts files is a complex task, we are often lazy to do so or we forget to add them, therefore we lose the sync between the translations files and the source code, that's why we use vue-i18n-extract
, a handy tool that runs static analysis of the source code files and extracts the translation texts from the source code and add them to the translations files like es.json
, en.json
, de.json
, etc. It no only adds the missing keys but also with a command we can remove the no longer used translations.
To extract the keys/original text into the translations files, run:
npm run vue-i18n-extract
This executes the command located in package.json
, which will search for the keys in the vue files given, compare it with the files inside the lang folder and if it finds new words, it will add them.
This script uses the vue-i18n-extract.config.js file for its configuration. This file is located in the root of the project.
Adding a new language:
To add a new language, for instance the German language, just create its file inside the locales
folder using its language code, example: ./locales/de.json
. Then run npm run vue-i18n-extract
to populate the translation keys into that file.
IMPORTANT: When creating the file, make it a valid JSON file, then at least it must has
{}
, otherwise the extraction would fail.
Example:
// locales/es.json
{
}
The file would be loaded automatically by vite
, a vite restart may be needed.
Removing unused translations
In case you want to remove the keys that are in the translation files but are not being used in the vue files, you can run:
npm run vue-i18n-extract-remove
See:
In Heroku create the app, then configure the following buildpacks in the same order:
Config the Heroku remote:
heroku login
heroku git:remote -a <app_name>
Finally, push the changes:
git push heroku main
Author: helixsoftco
Source code: https://github.com/helixsoftco/vuelix
License:
1656752400
Integrate Vite and Electron
npm i vite-plugin-electron -D
Example 👉 vite-plugin-electron-quick-start
vite.config.ts
import electron from 'vite-plugin-electron'
export default {
plugins: [
electron({
main: {
entry: 'electron/main.ts',
},
}),
],
}
electron(config: Configuration)
import type { LibraryOptions, UserConfig } from 'vite'
import type { InputOption } from 'rollup'
import type { VitePluginElectronRenderer } from 'vite-plugin-electron-renderer'
export interface CommonConfiguration {
vite?: UserConfig
/**
* Explicitly include/exclude some CJS modules
* `modules` includes `dependencies` of package.json, Node.js's `builtinModules` and `electron`
*/
resolve?: (modules: string[]) => typeof modules | undefined
}
export interface Configuration {
main: CommonConfiguration & {
/**
* Shortcut of `build.lib.entry`
*/
entry: LibraryOptions['entry']
}
preload?: CommonConfiguration & {
/**
* Shortcut of `build.rollupOptions.input`
*/
input: InputOption
}
/**
* Support use Node.js API in Electron-Renderer
* @see https://github.com/electron-vite/vite-plugin-electron-renderer
*/
renderer?: Parameters<VitePluginElectronRenderer>[0]
}
The plugin is just the encapsulation of the built-in scripts of electron-vite-boilerplate/scripts
Let's use the vanilla-ts template created based on create vite
as an example
+ ├─┬ electron
+ │ └── main.ts
├─┬ src
│ ├── main.ts
│ ├── style.css
│ └── vite-env.d.ts
├── .gitignore
├── favicon.svg
├── index.html
├── package.json
├── tsconfig.json
+ └── vite.config.ts
🚨 By default, the files in electron
folder will be built into the dist/electron
Electron-Main
In general, Vite may not correctly build Node.js packages, especially C/C++ native modules, but Vite can load them as external packages. So, put your Node.js package in dependencies
. Unless you know how to properly build them with Vite.
By default, vite-plugin-electron
treats packages in dependencies
as external
modules. If you don't want this, you can control this behavior with options.resolve()
.
通常的,Vite 可能不能正确的构建 Node.js 的包,尤其是 C/C++ 原生模块,但是 Vite 可以将它们以外部包的形式加载。所以,请将 Node.js 包放到 dependencies
中。除非你知道如何用 Vite 正确的构建它们。
默认情况下,vite-plugin-electron
会将 dependencies
中的包视为 external
模块。如果你不希望这样,你可以通过 options.resolve()
来控制改行为。
Electron-Renderer
You can see 👉 dependencies vs devDependencies
Author: electron-vite
Source code: https://github.com/electron-vite/vite-plugin-electron
License:
1656752400
The bundle tools for Vue I18n
Package | Bundler | Version (click for changelogs) |
---|---|---|
@intlify/unplugin-vue-i18n | vite / webpack | |
@intlify/vite-plugin-vue-i18n | vite | |
@intlify/vue-i18n-loader | webpack | |
@intlify/rollup-plugin-vue-i18n | rollup |
See Contributing Guide.
Author: intlify
Source Code: https://github.com/intlify/bundle-tools
License: MIT license
#typescript #javascript #vite #vue
1656752400
🍣 A Rollup plugin which imports files as data-URIs or ES Modules.
This plugin requires an LTS Node version (v8.0.0+) and Rollup v1.20.0+.
Using npm:
npm install @rollup/plugin-url --save-dev
Create a rollup.config.js
configuration file and import the plugin:
import url from '@rollup/plugin-url';
export default {
input: 'src/index.js',
output: {
dir: 'output',
format: 'cjs'
},
plugins: [url()]
};
Then call rollup
either via the CLI or the API.
With an accompanying file src/index.js
, the local image.svg
file would now be importable as seen below:
// src/index.js
import svg from './image.svg';
console.log(`svg contents: ${svg}`);
exclude
Type: String
| Array[...String]
Default: null
A minimatch pattern, or array of patterns, which specifies the files in the build the plugin should ignore. By default no files are ignored.
include
Type: String
| Array[...String]
Default: ['**/*.svg', '**/*.png', '**/*.jp(e)?g', '**/*.gif', '**/*.webp']
A minimatch pattern, or array of patterns, which specifies the files in the build the plugin should operate on. By default .svg, .png, .jpg, .jpeg, .gif and .webp files are targeted.
limit
Type: Number
Default: 14336
(14kb)
The file size limit for inline files. If a file exceeds this limit, it will be copied to the destination folder and the hashed filename will be provided instead. If limit
is set to 0
all files will be copied.
publicPath
Type: String
Default: (empty string)
A string which will be added in front of filenames when they are not inlined but are copied.
emitFiles
Type: Boolean
Default: true
If false
, will prevent files being emitted by this plugin. This is useful for when you are using Rollup to emit both a client-side and server-side bundle.
fileName
Type: String
Default: '[hash][extname]'
If emitFiles
is true
, this option can be used to rename the emitted files. It accepts the following string replacements:
[hash]
- The hash value of the file's contents[name]
- The name of the imported file (without its file extension)[extname]
- The extension of the imported file (including the leading .
)[dirname]
- The parent directory name of the imported file (including trailing /
)Type: String
Default: (empty string)
When using the [dirname]
replacement in fileName
, use this directory as the source directory from which to create the file path rather than the parent directory of the imported file. For example:
src/path/to/file.js
import png from './image.png';
rollup.config.js
url({
fileName: '[dirname][hash][extname]',
sourceDir: path.join(__dirname, 'src')
});
Emitted File: path/to/image.png
destDir
Type: String
Default: (empty string)
The destination dir to copy assets, usually used to rebase the assets according to HTML files.
Author:
Source Code: https://github.com/rollup/plugins/
License:
#vite #typescript #javascript #Rollup
1656748800
HTML template for vite app, like html-webpack-plugin for webpack.
It works perfectly together with vite-plugin-mpa.
<title></title>
.yarn add -D vite-plugin-html-template
// vite.config.ts
import htmlTemplate from 'vite-plugin-html-template'
// @see https://vitejs.dev/config/
export default defineConfig({
plugins: [
// ...other plugins
htmlTemplate(/* options */),
],
})
// for SPA, there is nothing to do, just use `public/index.html` as template
// for MPA, customise the template path (default is `public/index.html`) and page title:
{
// where is the pages' root directory?
pagesDir: 'src/pages',
// define pages like it is done in vue-cli
pages: {
index: {
template: './public/index.html',
title: 'Homepage',
},
subpage: {
template: './src/pages/subpage/index.html',
title: 'Subpage',
},
},
// expose to template
data: {
title: 'Homepage',
},
}
Author: IndexXuan
Source code: https://github.com/IndexXuan/vite-plugin-html-template
License:
1656748800
Supports:
📂 - Templates and layouts 🔗
📃 - Variables for each entry point (HTML) and global scope
🎠 - Custom filters 🔗 and extensions 🔗
Yarn
yarn add vite-plugin-nunjucks -D
or npm
npm i vite-plugin-nunjucks --save-dev
Use plugin in your Vite config (vite.config.ts
)
import nunjucks from 'vite-plugin-nunjucks'
export default {
plugins: [
nunjucks(),
]
}
Input (src/index.html
):
{% extends "src/html/layout.html" %}
{% include "src/html/hello.html" %}
{% block content %}
{% if username %}
Username: {{ username }}
{% else %}
Variable <code>username</code> is missing
{% endif %}
{% endblock %}
Template (src/html/layout.html
):
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
Template (src/html/hello.html
):
<h1>Hello world!</h1>
Vite config (vite.config.ts
)
import nunjucks from 'vite-plugin-nunjucks'
export default {
plugins: [
nunjucks({ variables: { 'index.html': { username: 'John' }}} ),
]
}
Output (dist/index.html
)
<!DOCTYPE html>
<html lang="en">
<head></head>
<body>
<h1>Hello world!</h1>
Username: John
</body>
</html>
Since v0.1.4 you can pass custom filters and extensions to the environment.
Config example:
import nunjucks from 'vite-plugin-nunjucks'
export default {
plugins: [
nunjucks({
nunjucksEnvironment: {
filters: {someFilter: someFilter},
extensions: {someExtension: SomeExtension}
}
}),
]
}
Filter should look like this (for more info check the Nunjucks documentation)
const someFilter = (val) => {
// ... some logic
return 'My modified filter content';
}
and extension like this:
const SomeExtension = {
tags: ['something'],
parse: function(parser, nodes, lexer) {
const [tag] = this.tags
const tok = parser.nextToken()
const args = parser.parseSignature(null, true)
parser.advanceAfterBlockEnd(tok.value)
const body = parser.parseUntilBlocks(tag, `end${tag}`)
parser.advanceAfterBlockEnd()
return new nodes.CallExtension(this, 'run', args, [body])
},
run (args) {
return 'My modified extension content'
}
}
then you can use it in the template:
{{ 'some text' | someFilter }}
{% something %}
Some content
{% endsomething %}
and the result should be:
My modified filter content
My modified extension content
If you need asynchronous filter you can pass nunjucksFilter
instead of nunjucksFilterCallback
:
import nunjucks from 'vite-plugin-nunjucks'
export default {
plugins: [
nunjucks({
nunjucksEnvironment: {
filters: {someFilter: {
async: true,
filter: someFilter
}},
}
}),
]
}
You can use your own environment that you configure entirely
import nunjucks from 'vite-plugin-nunjucks'
const env = new nunjucks.Environment(/* someOptions */)
env.addFilter('someFilter', someFilter);
env.addExtension('someExtension', SomeExtension);
export default {
plugins: [
nunjucks({nunjucksEnvironment: env}),
]
}
Parameter | Type | Default | Description |
---|---|---|---|
templatesDir | string | ./src/html | Absolute path where are HTML templates located. Example: path.resolve(process.cwd(), 'src', 'myTemplates') |
variables | Record<string, object> | {} | Variables for each entry point. Example { 'index.html': {username:'John'} } |
nunjucksConfigure | nunjucks.ConfigureOptions | {noCache:true} | Configure options for Nunjucks |
nunjucksEnvironment | nunjucksEnvironmentOptions OR nunjucks.Environment | {noCache:true} | Configure Nunjucks environment or pass your own env |
Author: Jax-p
Source code: https://github.com/Jax-p/vite-plugin-nunjucks
License: MIT license
1656748800
unplugin-auto-import
Auto import APIs on-demand for Vite, Webpack, Rollup and esbuild. With TypeScript support. Powered by unplugin.
without
import { computed, ref } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
with
const count = ref(0)
const doubled = computed(() => count.value * 2)
without
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return <div>{ count }</div>
}
with
export function Counter() {
const [count, setCount] = useState(0)
return <div>{ count }</div>
}
npm i -D unplugin-auto-import
Vite
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite'
export default defineConfig({
plugins: [
AutoImport({ /* options */ }),
],
})
Example: playground/
Rollup
// rollup.config.js
import AutoImport from 'unplugin-auto-import/rollup'
export default {
plugins: [
AutoImport({ /* options */ }),
// other plugins
],
}
Webpack
// webpack.config.js
module.exports = {
/* ... */
plugins: [
require('unplugin-auto-import/webpack')({ /* options */ }),
],
}
Nuxt
You don't need this plugin for Nuxt, it's already builtin.
Vue CLI
// vue.config.js
module.exports = {
configureWebpack: {
plugins: [
require('unplugin-auto-import/webpack')({ /* options */ }),
],
},
}
Quasar
// quasar.conf.js
const AutoImportPlugin = require('unplugin-auto-import/webpack')
module.exports = {
build: {
chainWebpack(chain) {
chain.plugin('unplugin-auto-import').use(
AutoImportPlugin({ /* options */ }),
)
},
},
}
esbuild
// esbuild.config.js
import { build } from 'esbuild'
build({
/* ... */
plugins: [
require('unplugin-auto-import/esbuild')({
/* options */
}),
],
})
AutoImport({
// targets to transform
include: [
/\.[tj]sx?$/, // .ts, .tsx, .js, .jsx
/\.vue$/, /\.vue\?vue/, // .vue
/\.md$/, // .md
],
// global imports to register
imports: [
// presets
'vue',
'vue-router',
// custom
{
'@vueuse/core': [
// named imports
'useMouse', // import { useMouse } from '@vueuse/core',
// alias
['useFetch', 'useMyFetch'], // import { useFetch as useMyFetch } from '@vueuse/core',
],
'axios': [
// default imports
['default', 'axios'], // import { default as axios } from 'axios',
],
'[package-name]': [
'[import-names]',
// alias
['[from]', '[alias]'],
],
},
],
// Auto import for all module exports under directories
dirs: [
// './hooks',
// './composables'
// ...
],
// Filepath to generate corresponding .d.ts file.
// Defaults to './auto-imports.d.ts' when `typescript` is installed locally.
// Set `false` to disable.
dts: './auto-imports.d.ts',
// Auto import inside Vue template
// see https://github.com/unjs/unimport/pull/15 and https://github.com/unjs/unimport/pull/72
vueTemplate: false,
// Custom resolvers, compatible with `unplugin-vue-components`
// see https://github.com/antfu/unplugin-auto-import/pull/23/
resolvers: [
/* ... */
],
// Generate corresponding .eslintrc-auto-import.json file.
// eslint globals Docs - https://eslint.org/docs/user-guide/configuring/language-options#specifying-globals
eslintrc: {
enabled: false, // Default `false`
filepath: './.eslintrc-auto-import.json', // Default `./.eslintrc-auto-import.json`
globalsPropValue: true, // Default `true`, (true | false | 'readonly' | 'readable' | 'writable' | 'writeable')
},
})
Refer to the type definitions for more options.
See src/presets.
In order to properly hint types for auto-imported APIs
|
|
💡 When using TypeScript, we recommend to disable
no-undef
rule directly as TypeScript already check for them and you don't need to worry about this.
If you have encountered ESLint error of no-undef
:
|
|
|
|
unimport
From v0.8.0, unplugin-auto-import
uses unimport
underneath. unimport
is designed to be a lower level tool (it also powered Nuxt's auto import). You can think unplugin-auto-import
is a wrapper of it that provides more user-friendly config APIs and capability like resolvers. Development of new features will mostly happend in unimport
from now.
vue-global-api
You can think of this plugin as a successor to vue-global-api
, but offering much more flexibility and bindings with libraries other than Vue (e.g. React).
Pros
Cons
vue-global-api
is pure runtime) - but hey, we have supported quite a few of them already!Author: antfu
Source code: https://github.com/antfu/unplugin-auto-import
License: MIT license
#vite #typescript
1656743535
🍣 A Rollup which allows importing and bundling WebAssembly modules.
WebAssembly Modules are imported asynchronous as base64 strings. Small modules can be imported synchronously.
This plugin requires an LTS Node version (v8.0.0+) and Rollup v1.20.0+.
Using npm:
npm install @rollup/plugin-wasm --save-dev
Create a rollup.config.js
configuration file and import the plugin:
import { wasm } from '@rollup/plugin-wasm';
export default {
input: 'src/index.js',
output: {
dir: 'output',
format: 'cjs'
},
plugins: [wasm()]
};
Then call rollup
either via the CLI or the API.
sync
Type: Array[...String]
Default: null
Specifies an array of strings that each represent a WebAssembly file to load synchronously. See Synchronous Modules for a functional example.
maxFileSize
Type: Number
Default: 14336
(14kb)
The maximum file size for inline files. If a file exceeds this limit, it will be copied to the destination folder and loaded from a separate file at runtime. If maxFileSize
is set to 0
all files will be copied.
Files specified in sync
to load synchronously are always inlined, regardless of size.
publicPath
Type: String
Default: (empty string)
A string which will be added in front of filenames when they are not inlined but are copied.
targetEnv
Type: "auto" | "browser" | "node"
Default: "auto"
Configures what code is emitted to instantiate the Wasm (both inline and separate):
"auto"
will determine the environment at runtime and invoke the correct methods accordingly"auto-inline"
always inlines the Wasm and will decode it according to the environment"browser"
omits emitting code that requires node.js builtin modules that may play havoc on downstream bundlers"node"
omits emitting code that requires fetch
Given the following simple C file:
int main() {
return 42;
}
Compile the file using emscripten
, or the online WasmFiddle tool. Then import and instantiate the resulting file:
import sample from './sample.wasm';
sample({ ...imports }).then(({ instance }) => {
console.log(instance.exports.main());
});
The WebAssembly is inlined as a base64 encoded string. At runtime the string is decoded and a module is returned.
Note: The base64 string that represents the WebAssembly within the bundle will be ~33% larger than the original file.
Small modules (< 4KB) can be compiled synchronously by specifying them in the configuration.
wasm({
sync: ['web/sample.wasm', 'web/foobar.wasm']
});
This means that the exports can be accessed immediately.
import sample from './sample.wasm';
const instance = sample({ ...imports });
console.log(instance.exports.main());
Author:
Source Code: https://github.com/rollup/plugins/
License:
#vite #typescript #javascript #Rollup #WebAssembly
1656741600
This plugin helps us configure additional html
The plugin is based on vite transformIndexHtml hooks.
If we want to distinguish the environment and introduce resources in index.html, we can use this plugin. stand by, favicon url, metas config, link tag config, style tag config, headScripts config,body script config.
node version: >=12.0.0
vite version: >=2.0.0
yarn add vite-plugin-html-config -D
// vite.config.js
import htmlPlugin from 'vite-plugin-html-config';
const htmlPluginOpt = {
favicon: './logo.svg',
headScripts: [
`var msg = 'head script'
console.log(msg);`,
{
async: true,
src: 'https://abc.com/b.js',
type: 'module'
},
{ content: `console.log('hello')`, charset: 'utf-8' }
],
scripts: [
`var msg = 'body script'
console.log(msg);`,
{
async: true,
src: 'https://abc.com/b.js',
type: 'module'
}
],
metas: [
{
name: 'keywords',
content: 'vite html meta keywords'
},
{
name: 'description',
content: 'vite html meta description'
},
{
bar: 'custom meta'
}
],
links: [
{
rel: 'stylesheet',
href: './style.css'
},
{
rel: 'modulepreload',
href: 'https://cn.vitejs.dev/assets/guide_api-plugin.md.6884005a.lean.js'
}
],
style: `body { color: red; };*{ margin: 0px }`
}
module.exports = {
plugins: [htmlPlugin(htmlPluginOpt)]
}
We can inject different scripts through different environments
such as:script in head,script in body and more.
in config file
// vite.config.js
const headScripts = []
// from app env
const APP_ENV = 'pro'
const BAIDU_KEY = APP_ENV==='pro'?'123123':'xxxxxx'
if (APP_ENV === 'pro') {
headScripts.push(
{
src: 'https://xxxxxxx/mito.js',
apikey: '123123123123123',
crossorigin: 'anonymous',
},
{
src: 'https://bbbbb.js',
},
)
}
const htmlPluginOpt = {
headScripts,
metas: [
{
name: 'keywords',
content: 'vite html meta keywords',
},
{
name: 'description',
content: 'vite html meta description',
},
],
links: [
{
rel: 'stylesheet',
href: './style.css',
},
{
rel: 'modulepreload',
href: 'https://www.google.com/xxx.js',
},
],
scripts:[
`var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?${BAIDU_KEY}";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();`
],
style: 'body { color: red; };*{ margin: 0px }',
}
module.exports = {
plugins: [htmlPlugin(htmlPluginOpt)]
}
build index.html
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite React</title>
<meta name="keywords" content="vite html meta keywords">
<meta name="description" content="vite html meta description">
<link rel="stylesheet" href="./style.css">
<link rel="modulepreload" href="https://www.google.com/xxx.js">
<style> body { color: red; };*{ margin: 0px }</style>
<script src="https://xxxxxxx/mito.js" apikey="123123123123123" crossorigin="anonymous" customTag=""></script>
<script src="https://bbbbb.js"></script>
</head>
<body>
<div id="root"></div>
<script>var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?123123";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();</script>
</body>
</html>
other example
https://github.com/saschazar21/jpeg-butcher/blob/main/vite.config.ts#L30
Author: ahwgs
Source code: https://github.com/ahwgs/vite-plugin-html-config
License: MIT license
1656741600
Static-site generation for Vue 3 on Vite.
ℹ️ Vite 2 is supported from
v0.2.x
, Vite 1's support is discontinued.
This library requires Node.js version >= 14
npm i -D vite-ssg vue-router @vueuse/head
// package.json
{
"scripts": {
"dev": "vite",
- "build": "vite build"
+ "build": "vite-ssg build"
}
}
// src/main.ts
import { ViteSSG } from 'vite-ssg'
import App from './App.vue'
// `export const createApp` is required instead of the original `createApp(App).mount('#app')`
export const createApp = ViteSSG(
// the root component
App,
// vue-router options
{ routes },
// function to have custom setups
({ app, router, routes, isClient, initialState }) => {
// install plugins etc.
},
)
To have SSG for the index page only (without vue-router
), import from vite-ssg/single-page
instead, and you only need to install npm i -D vite-ssg @vueuse/head
.
// src/main.ts
import { ViteSSG } from 'vite-ssg/single-page'
import App from './App.vue'
// `export const createApp` is required instead of the original `createApp(App).mount('#app')`
export const createApp = ViteSSG(App)
<ClientOnly/>
Component ClientOnly
is registered globally along with the app creation.
<client-only>
<your-client-side-components />
</client-only>
From v0.4.0
, we ship @vueuse/head
to manage the document head out-of-box. You can directly use it in your pages/components, for example:
<template>
<button @click="count++">Click</button>
</template>
<script setup>
import { useHead } from '@vueuse/head'
useHead({
title: 'Website Title',
meta: [
{
name: `description`,
content: `Website description`,
},
],
})
</script>
That's all, no configuration is needed. Vite SSG will handle the server-side rendering and merging automatically.
Refer to @vueuse/head
's docs for more usage about useHead
.
Vite SSG has built-in support for generating Critical CSS inlined in the HTML via the critters
package. Install it via:
npm i -D critters
Critical CSS generation will be enabled automatically for you.
The initial state comprises data that is serialized to your server-side generated HTML that is hydrated in the browser when accessed. This data can be data fetched from a CDN, an API, etc, and is typically needed as soon as the application starts or is accessed for the first time.
The main advantage of setting the application's initial state is that the statically generated pages do not need to fetch the data again as the data is fetched during build time and serialized into the page's HTML.
The initial state is a plain JavaScript object that can be set during SSR, i.e., when statically generating the pages, like this:
// src/main.ts
// ...
export const createApp = ViteSSG(
App,
{ routes },
({ app, router, routes, isClient, initialState }) => {
// ...
if (import.meta.env.SSR) {
// Set initial state during server side
initialState.data = { cats: 2, dogs: 3 }
}
else {
// Restore or read the initial state on the client side in the browser
console.log(initialState.data) // => { cats: 2, dogs: 3 }
}
// ...
},
)
Typically, you will use this with an application store, such as Vuex or Pinia. For examples, see below:
When using Pinia
Following [Pinia's guide](https://pinia.esm.dev/ssr), you will to adapt your `main.{ts,js}` file to look like this:
// main.ts
import { ViteSSG } from 'vite-ssg'
import { createPinia } from 'pinia'
import routes from 'virtual:generated-pages'
// use any store you configured that you need data from on start-up
import { useRootStore } from './store/root'
import App from './App.vue'
export const createApp = ViteSSG(
App,
{ routes },
({ app, router, initialState }) => {
const pinia = createPinia()
app.use(pinia)
if (import.meta.env.SSR)
initialState.pinia = pinia.state.value
else
pinia.state.value = initialState.pinia || {}
router.beforeEach((to, from, next) => {
const store = useRootStore(pinia)
if (!store.ready)
// perform the (user-implemented) store action to fill the store's state
store.initialize()
next()
})
},
)
When using Vuex
// main.ts
import { ViteSSG } from 'vite-ssg'
import routes from 'virtual:generated-pages'
import { createStore } from 'vuex'
import App from './App.vue'
// Normally, you should definitely put this in a separate file
// in order to be able to use it everywhere
const store = createStore({
// ...
})
export const createApp = ViteSSG(
App,
{ routes },
({ app, router, initialState }) => {
app.use(store)
if (import.meta.env.SSR)
initialState.store = store.state
else
store.replaceState(initialState.store)
router.beforeEach((to, from, next) => {
// perform the (user-implemented) store action to fill the store's state
if (!store.getters.ready)
store.dispatch('initialize')
next()
})
},
)
For the example of how to use a store with an initial state in a single page app, see the single page example.
Per default, the state is deserialized and serialized by using JSON.stringify
and JSON.parse
. If this approach works for you, you should definitely stick to it as it yields far better performance.
You may use the option transformState
in the ViteSSGClientOptions
as displayed below. A valid approach besides JSON.stringify
and JSON.parse
is @nuxt/devalue
(which is used by Nuxt.js):
import devalue from '@nuxt/devalue'
import { ViteSSG } from 'vite-ssg'
// ...
import App from './App.vue'
export const createApp = ViteSSG(
App,
{ routes },
({ app, router, initialState }) => {
// ...
},
{
transformState(state) {
return import.meta.env.SSR ? devalue(state) : state
},
},
)
A minor remark when using @nuxt/devalue
: In case, you are getting an error because of a require
within the package @nuxt/devalue
, you have to add the following piece of config to your Vite config:
// vite.config.ts
// ...
export default defineConfig({
resolve: {
alias: {
'@nuxt/devalue': '@nuxt/devalue/dist/devalue.js',
},
},
// ...
})
Some applications may make use of Vue features that cause components to render asynchronously (e.g. suspense
). When these features are used in ways that can influence initialState
, the onSSRAppRendered
may be used in order to ensure that all async operations have finished as part of the initial application render:
const { app, router, initialState, isClient, onSSRAppRendered } = ctx
const pinia = createPinia()
app.use(pinia)
if (isClient) {
pinia.state.value = (initialState.pinia) || {}
}
else {
onSSRAppRendered(() => {
initialState.pinia = pinia.state.value
})
}
You can pass options to Vite SSG in the ssgOptions
field of your vite.config.js
// vite.config.js
export default {
plugins: [],
ssgOptions: {
script: 'async',
},
}
See src/types.ts. for more options available.
You can use the includedRoutes
hook to exclude/include route paths to render, or even provide some complete custom ones.
// vite.config.js
export default {
plugins: [],
ssgOptions: {
includedRoutes(paths, routes) {
// exclude all the route paths that contains 'foo'
return paths.filter(i => !i.includes('foo'))
},
},
}
// vite.config.js
export default {
plugins: [],
ssgOptions: {
includedRoutes(paths, routes) {
// use original route records
return routes.flatMap((route) => {
return route.name === 'Blog'
? myBlogSlugs.map(slug => `/blog/${slug}`)
: route.path
})
},
},
}
Alternatively, you may export the includedRoutes
hook from your server entry file. This will be necessary if fetching your routes requires the use of environment variables managed by Vite.
// main.ts
import { ViteSSG } from 'vite-ssg'
import App from './App.vue'
export const createApp = ViteSSG(
App,
{ routes },
({ app, router, initialState }) => {
// ...
},
)
export async function includedRoutes(paths, routes) {
// Sensitive key is managed by Vite - this would not be available inside
// vite.config.js as it runs before the environment has been populated.
const apiClient = new MyApiClient(import.meta.env.MY_API_KEY)
return Promise.all(
routes.flatMap(async (route) => {
return route.name === 'Blog'
? (await apiClient.fetchBlogSlugs()).map(slug => `/blog/${slug}`)
: route.path
}),
)
}
Cons:
See Vitesse
Please refer to https://github.com/antfu/contribute
Author: antfu
Source Code: https://github.com/antfu/vite-ssg
License: MIT license
#vite #vue #typescript #javascript
1656738000
Vite starter template to scaffold a new Mithril with TypeScript project.
This is an unopinionated template; aside from Mithril, TypeScript and Vite, the rest of your project's tools are entirely up to you.
Pull the template files with degit and install dependencies.
npx degit ArthurClemens/mithril-ts-vite-starter my-project
cd my-project
npm install
npm run dev
- Starts the development server at port 3000npm run build
- Builds the applicationnpm run preview
- Serves the build files locally at port 5000Uncomment the esbuild
configuration in vite.config.js
.
Example App.tsx
:
import m from "mithril";
import "./App.css";
export const App = () => {
// Local state ...
return {
view: () => {
return (
<>
<h1>My Mithril App</h1>
</>
);
},
};
};
Author: ArthurClemens
Source code: https://github.com/ArthurClemens/mithril-ts-vite-starter
License:
1656738000
Share node_modules in monorepos. Best friend for pnpm's module isolation and module singletons sharing.
Use it as simple as:
// vite.config.ts
import { defineConfig } from 'vite';
import sharedModulesPlugin from 'vite-plugin-shared-modules'
import tsconfigPaths from 'rollup-plugin-tsconfig-paths';
export default defineConfig({
plugins: [
sharedModulesPlugin({
packageName: '@monorepo/shared',
}),
// necessary for resolving modules in `node_modules`
tsconfigPaths({
// specify the project's tsconfig.json, which configured paths mapping.
tsConfigPath: join(__dirname, '../../tsconfig.json')
}),
]
});
then you can import singletons by this way:
import foo from '@monorepo/shared/foo'
import bar from '@monorepo/shared/bar'
is equivalent to
import foo from '@monorepo/shared/node_modules/foo'
import bar from '@monorepo/shared/node_modules/bar'
moreover for getting type-safe, add tsconfig paths mapping:
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@monorepo/shared/*": ["./packages/shared/node_modules/*", "./packages/shared/node_modules/@types/*"]
}
}
}
the example above we assume the package @monorepo/shared
is located under ./packages/shared
.
The plugin options signatures:
export type SharedModulesPluginOption = {
packageName: string,
subpath?: string,
nodeModules?: string,
sourceMap?: boolean,
}
The default options:
export const defaultSharedModules = {
subpath: '',
nodeModules: 'node_modules',
sourceMap: true,
}
Author: zheeeng
Source code: https://github.com/zheeeng/vite-plugin-shared-modules
License:
#vite #typescript #javascript
1656738000
A Plugins for convert variables from css preprocessor file.
English | 中文
$> npm install vite-plugin-vars-modifier -D
#or
$> yarn add vite-plugin-vars-modifier -D
Add following options in vite.config.js
import modifier from 'vite-plugin-vars-modifier';
export default {
plugins: [
modifier({
paths: ['path/to/file'],
type: 'less',
}),
],
};
export interface ModifierOptions {
/**
* paths for content to pass to parser.
*
* support glob format
*
* @see https://github.com/mrmlnc/fast-glob#readme
*/
paths: string | string[];
/**
* Whether to remove the prefix
*
* @default true
*/
strip: boolean;
/**
* Typeof parser of modifier
*/
type: 'less' | 'scss';
}
Author: fanhaoyuan
Source code: https://github.com/fanhaoyuan/vite-plugin-vars-modifier
License: MIT license
#vite #typescript