diff --git a/README.md b/README.md index 9c338e6..b044d22 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ This repo includes the following presets and plugins: - [babel-preset-vite](./packages/babel-preset-vite) - [babel-plugin-transform-vite-meta-env](./packages/babel-plugin-transform-vite-meta-env) - [babel-plugin-transform-vite-meta-glob](./packages/babel-plugin-transform-vite-meta-glob) + - [babel-plugin-transform-vite-meta-hot](./packages/babel-plugin-transform-vite-meta-hot) For installation, usage and example, please refer to the documentation in the above packages. @@ -41,6 +42,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Mohit

💻
Rubén Moya

💻
Mitchel van Bever

🤔 +
Kelvin Zhang

💻⚠️ 📖 diff --git a/package.json b/package.json index 40dd7d9..542a080 100644 --- a/package.json +++ b/package.json @@ -48,10 +48,12 @@ "build": "run-p --aggregate-output build:**", "build:babel-plugin-transform-vite-meta-env": "npm run build --prefix packages/babel-plugin-transform-vite-meta-env", "build:babel-plugin-transform-vite-meta-glob": "npm run build --prefix packages/babel-plugin-transform-vite-meta-glob", + "build:babel-plugin-transform-vite-meta-hot": "npm run build --prefix packages/babel-plugin-transform-vite-meta-hot", "build:babel-preset-vite": "npm run build --prefix packages/babel-preset-vite", "test": "run-p --aggregate-output test:** --", "test:babel-plugin-transform-vite-meta-env": "npm run test --prefix packages/babel-plugin-transform-vite-meta-env -- --coverage", "test:babel-plugin-transform-vite-meta-glob": "npm run test --prefix packages/babel-plugin-transform-vite-meta-glob -- --coverage", + "test:babel-plugin-transform-vite-meta-hot": "npm run test --prefix packages/babel-plugin-transform-vite-meta-hot -- --coverage", "test:babel-preset-vite": "npm run test --prefix packages/babel-preset-vite -- --coverage", "typecheck": "kcd-scripts typecheck", "lint": "kcd-scripts lint", diff --git a/packages/babel-plugin-transform-vite-meta-hot/.npmrc b/packages/babel-plugin-transform-vite-meta-hot/.npmrc new file mode 100644 index 0000000..4fd15ed --- /dev/null +++ b/packages/babel-plugin-transform-vite-meta-hot/.npmrc @@ -0,0 +1,2 @@ +package-lock=false +legacy-peer-deps=true diff --git a/packages/babel-plugin-transform-vite-meta-hot/LICENSE.md b/packages/babel-plugin-transform-vite-meta-hot/LICENSE.md new file mode 100644 index 0000000..7f4e4f1 --- /dev/null +++ b/packages/babel-plugin-transform-vite-meta-hot/LICENSE.md @@ -0,0 +1,18 @@ +MIT License + +Copyright (c) [2021] [Michael Peyper] + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, +sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT +NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES +OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/babel-plugin-transform-vite-meta-hot/README.md b/packages/babel-plugin-transform-vite-meta-hot/README.md new file mode 100644 index 0000000..77a550f --- /dev/null +++ b/packages/babel-plugin-transform-vite-meta-hot/README.md @@ -0,0 +1,71 @@ +# babel-plugin-transform-vite-meta-hot + + +[![Build Status](https://img.shields.io/github/workflow/status/OpenSourceRaidGuild/babel-vite/validate?logo=github&style=flat-square)](https://github.com/OpenSourceRaidGuild/babel-vite/actions?query=workflow%3Avalidate) +[![codecov](https://img.shields.io/codecov/c/github/OpenSourceRaidGuild/babel-vite.svg?style=flat-square)](https://codecov.io/gh/OpenSourceRaidGuild/babel-vite) +[![version](https://img.shields.io/npm/v/babel-plugin-transform-vite-meta-hot.svg?style=flat-square)](https://www.npmjs.com/package/babel-plugin-transform-vite-meta-hot) +[![downloads](https://img.shields.io/npm/dm/babel-plugin-transform-vite-meta-hot.svg?style=flat-square)](http://www.npmtrends.com/babel-plugin-transform-vite-meta-hot) +[![MIT License](https://img.shields.io/npm/l/babel-plugin-transform-vite-meta-hot.svg?style=flat-square)](https://github.com/OpenSourceRaidGuild/babel-vite/blob/master/LICENSE.md) + +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) +[![Code of Conduct](https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square)](https://github.com/OpenSourceRaidGuild/babel-vite/blob/master/CODE_OF_CONDUCT.md) +[![Discord](https://img.shields.io/discord/808364903822917662.svg?color=7389D8&labelColor=6A7EC2&logo=discord&logoColor=ffffff&style=flat-square)](https://discord.gg/grS89HWeYh) + +[![Watch on GitHub](https://img.shields.io/github/watchers/OpenSourceRaidGuild/babel-vite.svg?style=social)](https://github.com/OpenSourceRaidGuild/babel-vite/watchers) +[![Star on GitHub](https://img.shields.io/github/stars/OpenSourceRaidGuild/babel-vite.svg?style=social)](https://github.com/OpenSourceRaidGuild/babel-vite/stargazers) +[![Tweet](https://img.shields.io/twitter/url/https/github.com/OpenSourceRaidGuild/babel-vite.svg?style=social)](https://twitter.com/intent/tweet?text=Check%20out%20babel-plugin-transform-vite-meta-hot%20by%20OpenSourceRaidGuild%20https%3A%2F%2Fgithub.com%2FOpenSourceRaidGuild%2Fbabel-vite%20%F0%9F%91%8D) + + +> Please note: this plugin is intended to provide an approximation of some of Vite specific +> transformations when running the code in non-Vite environment, for example, running tests with a +> NodeJS based test runner. +> +> **The functionality within these transformations should not be relied upon in production.** + +## Example + +**In** + +``` +if (import.meta.hot) { + import.meta.hot.accept(callback); +} +``` + +**Out** + +``` +if (module.hot) { + module.hot.accept(callback); +} +``` + +## Installation + +```sh +npm install --save-dev babel-plugin-transform-vite-meta-hot +``` + +## Usage + +### With a configuration file (Recommended) + +```json +{ + "plugins": ["babel-plugin-transform-vite-meta-hot"] +} +``` + +### Via CLI + +```sh +babel --plugins babel-plugin-transform-vite-meta-hot script.js +``` + +### Via Node API + +```javascript +require('@babel/core').transformSync('code', { + plugins: ['babel-plugin-transform-vite-meta-hot'] +}) +``` diff --git a/packages/babel-plugin-transform-vite-meta-hot/package.json b/packages/babel-plugin-transform-vite-meta-hot/package.json new file mode 100644 index 0000000..2cab9f9 --- /dev/null +++ b/packages/babel-plugin-transform-vite-meta-hot/package.json @@ -0,0 +1,44 @@ +{ + "name": "babel-plugin-transform-vite-meta-hot", + "version": "0.0.0-semantically-released", + "description": "babel plugin that emulates vite's import.meta.hot functionality", + "main": "lib/index.js", + "typings": "lib/index.d.ts", + "files": [ + "lib", + "src" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/OpenSourceRaidGuild/babel-vite.git" + }, + "keywords": [ + "babel", + "vite", + "plugin", + "import", + "meta", + "hot" + ], + "author": "Kelvin Zhang ", + "license": "MIT", + "bugs": { + "url": "https://github.com/OpenSourceRaidGuild/babel-vite/issues" + }, + "homepage": "https://github.com/OpenSourceRaidGuild/babel-vite#readme", + "scripts": { + "build": "kcd-scripts build --out-dir lib", + "test": "kcd-scripts test" + }, + "devDependencies": { + "@babel/core": "^7.13.8", + "@types/babel-plugin-tester": "^9.0.1", + "babel-plugin-tester": "^10.0.0", + "kcd-scripts": "^12.0.0", + "typescript": "^4.2.2" + }, + "dependencies": { + "@babel/runtime": "^7.13.9", + "@types/babel__core": "^7.1.12" + } +} diff --git a/packages/babel-plugin-transform-vite-meta-hot/src/__tests__/__snapshots__/index.ts.snap b/packages/babel-plugin-transform-vite-meta-hot/src/__tests__/__snapshots__/index.ts.snap new file mode 100644 index 0000000..cb43cfe --- /dev/null +++ b/packages/babel-plugin-transform-vite-meta-hot/src/__tests__/__snapshots__/index.ts.snap @@ -0,0 +1,91 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`vite-meta-hot not import.meta lookup: not import.meta lookup 1`] = ` + +const x = import.meta() + + ↓ ↓ ↓ ↓ ↓ ↓ + +const x = import.meta() + + +`; + +exports[`vite-meta-hot not replace import.meta.hot: not replace import.meta.hot 1`] = ` + +const x = module.hot + + ↓ ↓ ↓ ↓ ↓ ↓ + +const x = module.hot + + +`; + +exports[`vite-meta-hot not replace key access: not replace key access 1`] = ` + +const key = "env"; const x = import.meta[key] + + ↓ ↓ ↓ ↓ ↓ ↓ + +const key = 'env' +const x = import.meta[key] + + +`; + +exports[`vite-meta-hot not replace string access: not replace string access 1`] = ` + +const x = import.meta["env"] + + ↓ ↓ ↓ ↓ ↓ ↓ + +const x = import.meta['env'] + + +`; + +exports[`vite-meta-hot not replaceable: not replaceable 1`] = ` + +const x = import.meta.other + + ↓ ↓ ↓ ↓ ↓ ↓ + +const x = import.meta.other + + +`; + +exports[`vite-meta-hot replace import.meta.hot: replace import.meta.hot 1`] = ` + +const x = import.meta.hot + + ↓ ↓ ↓ ↓ ↓ ↓ + +const x = module.hot + + +`; + +exports[`vite-meta-hot replace key access: replace key access 1`] = ` + +const key = "hot"; const x = import.meta[key] + + ↓ ↓ ↓ ↓ ↓ ↓ + +const key = 'hot' +const x = module.hot + + +`; + +exports[`vite-meta-hot replace string access: replace string access 1`] = ` + +const x = import.meta["hot"] + + ↓ ↓ ↓ ↓ ↓ ↓ + +const x = module.hot + + +`; diff --git a/packages/babel-plugin-transform-vite-meta-hot/src/__tests__/index.ts b/packages/babel-plugin-transform-vite-meta-hot/src/__tests__/index.ts new file mode 100644 index 0000000..75163b1 --- /dev/null +++ b/packages/babel-plugin-transform-vite-meta-hot/src/__tests__/index.ts @@ -0,0 +1,18 @@ +import pluginTester from 'babel-plugin-tester' +import plugin from '..' + +pluginTester({ + plugin, + pluginName: 'vite-meta-hot', + snapshot: true, + tests: { + 'replace import.meta.hot': 'const x = import.meta.hot', + 'not replace import.meta.hot': 'const x = module.hot', + 'replace string access': 'const x = import.meta["hot"]', + 'not replace string access': 'const x = import.meta["env"]', + 'replace key access': 'const key = "hot"; const x = import.meta[key]', + 'not replace key access': 'const key = "env"; const x = import.meta[key]', + 'not replaceable': 'const x = import.meta.other', + 'not import.meta lookup': 'const x = import.meta()' + } +}) diff --git a/packages/babel-plugin-transform-vite-meta-hot/src/index.ts b/packages/babel-plugin-transform-vite-meta-hot/src/index.ts new file mode 100644 index 0000000..1853018 --- /dev/null +++ b/packages/babel-plugin-transform-vite-meta-hot/src/index.ts @@ -0,0 +1,52 @@ +import type babelCore from '@babel/core' + +export default function viteMetaHotBabelPlugin({ + template, + types: t +}: typeof babelCore): babelCore.PluginObj { + return { + name: 'vite-meta-hot', + visitor: { + MemberExpression(path) { + const isMetaProperty = t.isMetaProperty(path.node.object) + const isHotVar = t.isIdentifier(path.node.property) && path.node.property.name === 'hot' + + if (!isMetaProperty || !isHotVar) { + return + } + + path.replaceWith(template.expression.ast('module.hot')) + }, + StringLiteral(path) { + const isMetaProperty = + t.isMemberExpression(path.parentPath.node) && + t.isMetaProperty(path.parentPath.node.object) + const isHotVar = path.node.value === 'hot' + + if (!isMetaProperty || !isHotVar) { + return + } + + path.parentPath.replaceWith(template.expression.ast('module.hot')) + }, + Identifier(path) { + if ( + !t.isMemberExpression(path.parentPath.node) || + !t.isMetaProperty(path.parentPath.node.object) + ) { + return + } + + const key = path.node.name + /* @ts-expect-error outdated types */ + // eslint-disable-next-line + const keyValue = path.scope.getBinding(key)?.path.node.init?.value + if (keyValue !== 'hot') { + return + } + + path.parentPath.replaceWith(template.expression.ast('module.hot')) + } + } + } +} diff --git a/packages/babel-plugin-transform-vite-meta-hot/tsconfig.json b/packages/babel-plugin-transform-vite-meta-hot/tsconfig.json new file mode 100644 index 0000000..52d43ea --- /dev/null +++ b/packages/babel-plugin-transform-vite-meta-hot/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*"] +} diff --git a/packages/babel-preset-vite/README.md b/packages/babel-preset-vite/README.md index 36a4a4b..dfe4f8b 100644 --- a/packages/babel-preset-vite/README.md +++ b/packages/babel-preset-vite/README.md @@ -26,6 +26,7 @@ This preset includes the following plugins: - [babel-plugin-transform-vite-meta-env](../babel-plugin-transform-vite-meta-env) - [babel-plugin-transform-vite-meta-glob](../babel-plugin-transform-vite-meta-glob) +- [babel-plugin-transform-vite-meta-hot](../babel-plugin-transform-vite-meta-hot) ## Installation @@ -54,7 +55,8 @@ With options: "babel-preset-vite", { "env": false, // defaults to true - "glob": false // defaults to true + "glob": false, // defaults to true + "hot": false // defaults to true } ] ] @@ -92,5 +94,12 @@ Toggles whether or not to perform [`import.meta.glob` and `import.meta.globEager`](https://vitejs.dev/guide/features.html#glob-import) transformations. +### `hot` + +`boolean`, defaults to `true` + +Toggles whether or not to perform [`import.meta.hot`](https://vitejs.dev/guide/api-hmr.html) +transformations. + > You can read more about configuring preset options > [here](https://babeljs.io/docs/en/presets#preset-options) diff --git a/packages/babel-preset-vite/package.json b/packages/babel-preset-vite/package.json index ce08d67..9b7f28f 100644 --- a/packages/babel-preset-vite/package.json +++ b/packages/babel-preset-vite/package.json @@ -15,7 +15,10 @@ "keywords": [ "babel", "vite", - "preset" + "preset", + "env", + "glob", + "hot" ], "author": "Michael Peyper ", "license": "MIT", @@ -38,6 +41,7 @@ "@babel/runtime": "^7.13.9", "@types/babel__core": "^7.1.12", "babel-plugin-transform-vite-meta-env": "^0.0.0-semantically-released", - "babel-plugin-transform-vite-meta-glob": "^0.0.0-semantically-released" + "babel-plugin-transform-vite-meta-glob": "^0.0.0-semantically-released", + "babel-plugin-transform-vite-meta-hot": "^0.0.0-semantically-released" } } diff --git a/packages/babel-preset-vite/src/__tests__/__snapshots__/index.ts.snap b/packages/babel-preset-vite/src/__tests__/__snapshots__/index.ts.snap index e987bac..2960b10 100644 --- a/packages/babel-preset-vite/src/__tests__/__snapshots__/index.ts.snap +++ b/packages/babel-preset-vite/src/__tests__/__snapshots__/index.ts.snap @@ -8,6 +8,8 @@ export const modules = import.meta.glob('files/*.ts') export const eagerModules = import.meta.globEager('files/*.ts') +export const hot = import.meta.hot + ↓ ↓ ↓ ↓ ↓ ↓ export const envVar = process.env.VITE_VAR @@ -17,6 +19,7 @@ export const modules = { export const eagerModules = { 'files/file1.ts': require('files/file1.ts') } +export const hot = module.hot `; @@ -29,6 +32,8 @@ export const modules = import.meta.glob('files/*.ts') export const eagerModules = import.meta.globEager('files/*.ts') +export const hot = import.meta.hot + ↓ ↓ ↓ ↓ ↓ ↓ export const envVar = process.env.VITE_VAR @@ -38,6 +43,7 @@ export const modules = { export const eagerModules = { 'files/file1.ts': require('files/file1.ts') } +export const hot = module.hot `; @@ -50,11 +56,14 @@ export const modules = import.meta.glob('files/*.ts') export const eagerModules = import.meta.globEager('files/*.ts') +export const hot = import.meta.hot + ↓ ↓ ↓ ↓ ↓ ↓ export const envVar = process.env.VITE_VAR export const modules = import.meta.glob('files/*.ts') export const eagerModules = import.meta.globEager('files/*.ts') +export const hot = import.meta.hot `; @@ -67,6 +76,8 @@ export const modules = import.meta.glob('files/*.ts') export const eagerModules = import.meta.globEager('files/*.ts') +export const hot = import.meta.hot + ↓ ↓ ↓ ↓ ↓ ↓ export const envVar = import.meta.env.VITE_VAR @@ -76,6 +87,27 @@ export const modules = { export const eagerModules = { 'files/file1.ts': require('files/file1.ts') } +export const hot = import.meta.hot + + +`; + +exports[`vite hot-only: hot-only 1`] = ` + +export const envVar = import.meta.env.VITE_VAR + +export const modules = import.meta.glob('files/*.ts') + +export const eagerModules = import.meta.globEager('files/*.ts') + +export const hot = import.meta.hot + + ↓ ↓ ↓ ↓ ↓ ↓ + +export const envVar = import.meta.env.VITE_VAR +export const modules = import.meta.glob('files/*.ts') +export const eagerModules = import.meta.globEager('files/*.ts') +export const hot = module.hot `; diff --git a/packages/babel-preset-vite/src/__tests__/fixtures/input.ts b/packages/babel-preset-vite/src/__tests__/fixtures/input.ts index c65d5b8..d2d2a5f 100644 --- a/packages/babel-preset-vite/src/__tests__/fixtures/input.ts +++ b/packages/babel-preset-vite/src/__tests__/fixtures/input.ts @@ -3,3 +3,5 @@ export const envVar = import.meta.env.VITE_VAR export const modules = import.meta.glob('files/*.ts') export const eagerModules = import.meta.globEager('files/*.ts') + +export const hot = import.meta.hot diff --git a/packages/babel-preset-vite/src/__tests__/index.ts b/packages/babel-preset-vite/src/__tests__/index.ts index 04b81a4..43ce10e 100644 --- a/packages/babel-preset-vite/src/__tests__/index.ts +++ b/packages/babel-preset-vite/src/__tests__/index.ts @@ -15,8 +15,9 @@ pluginTester({ babelOptions: { filename: __filename }, tests: { defaults: fixture('input'), - all: fixture('input', { env: true, glob: true }), - 'env-only': fixture('input', { env: true, glob: false }), - 'glob-only': fixture('input', { env: false, glob: true }) + all: fixture('input', { env: true, glob: true, hot: true }), + 'env-only': fixture('input', { env: true, glob: false, hot: false }), + 'glob-only': fixture('input', { env: false, glob: true, hot: false }), + 'hot-only': fixture('input', { env: false, glob: false, hot: true }) } }) diff --git a/packages/babel-preset-vite/src/index.ts b/packages/babel-preset-vite/src/index.ts index 7937f9a..9f87e36 100644 --- a/packages/babel-preset-vite/src/index.ts +++ b/packages/babel-preset-vite/src/index.ts @@ -3,17 +3,19 @@ import type babelCore from '@babel/core' export interface VitePresetOptions { env?: boolean glob?: boolean + hot?: boolean } function vitePreset( _: typeof babelCore, opts: VitePresetOptions ): { plugins: babelCore.PluginItem[] } { - const { env = true, glob = true } = opts + const { env = true, glob = true, hot = true } = opts return { plugins: [ glob && require.resolve('babel-plugin-transform-vite-meta-glob'), - env && require.resolve('babel-plugin-transform-vite-meta-env') + env && require.resolve('babel-plugin-transform-vite-meta-env'), + hot && require.resolve('babel-plugin-transform-vite-meta-hot') ].filter(isEnabled) } }