两个版本的插件

This commit is contained in:
onvia
2023-07-20 19:00:23 +08:00
parent 44bce05250
commit 68895155e5
2385 changed files with 1826008 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018-present, Yuxi (Evan) You
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.

View File

@@ -0,0 +1,121 @@
# @vue/reactivity-transform
> ⚠️ This is experimental and currently only provided for testing and feedback. It may break during patches or even be removed. Use at your own risk!
>
> Follow https://github.com/vuejs/rfcs/discussions/369 for details and updates.
## Basic Rules
- Ref-creating APIs have `$`-prefixed versions that create reactive variables instead. They also do not need to be explicitly imported. These include:
- `ref`
- `computed`
- `shallowRef`
- `customRef`
- `toRef`
- `$()` can be used to destructure an object into reactive variables, or turn existing refs into reactive variables
- `$$()` to "escape" the transform, which allows access to underlying refs
```js
import { watchEffect } from 'vue'
// bind ref as a variable
let count = $ref(0)
watchEffect(() => {
// no need for .value
console.log(count)
})
// assignments are reactive
count++
// get the actual ref
console.log($$(count)) // { value: 1 }
```
Macros can be optionally imported to make it more explicit:
```js
// not necessary, but also works
import { $, $ref } from 'vue/macros'
let count = $ref(0)
const { x, y } = $(useMouse())
```
### Global Types
To enable types for the macros globally, include the following in a `.d.ts` file:
```ts
/// <reference types="vue/macros-global" />
```
## API
This package is the lower-level transform that can be used standalone. Higher-level tooling (e.g. `@vitejs/plugin-vue` and `vue-loader`) will provide integration via options.
### `shouldTransform`
Can be used to do a cheap check to determine whether full transform should be performed.
```js
import { shouldTransform } from '@vue/reactivity-transform'
shouldTransform(`let a = ref(0)`) // false
shouldTransform(`let a = $ref(0)`) // true
```
### `transform`
```js
import { transform } from '@vue/reactivity-transform'
const src = `let a = $ref(0); a++`
const {
code, // import { ref as _ref } from 'vue'; let a = (ref(0)); a.value++"
map
} = transform(src, {
filename: 'foo.ts',
sourceMap: true,
// @babel/parser plugins to enable.
// 'typescript' and 'jsx' will be auto-inferred from filename if provided,
// so in most cases explicit parserPlugins are not necessary
parserPlugins: [
/* ... */
]
})
```
**Options**
```ts
interface RefTransformOptions {
filename?: string
sourceMap?: boolean // default: false
parserPlugins?: ParserPlugin[]
importHelpersFrom?: string // default: "vue"
}
```
### `transformAST`
Transform with an existing Babel AST + MagicString instance. This is used internally by `@vue/compiler-sfc` to avoid double parse/transform cost.
```js
import { transformAST } from '@vue/reactivity-transform'
import { parse } from '@babel/parser'
import MagicString from 'magic-string'
const src = `let a = $ref(0); a++`
const ast = parse(src, { sourceType: 'module' })
const s = new MagicString(src)
const {
rootRefs, // ['a']
importedHelpers // ['ref']
} = transformAST(ast, s)
console.log(s.toString()) // let a = _ref(0); a.value++
```

View File

@@ -0,0 +1,570 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var MagicString = require('magic-string');
var estreeWalker = require('estree-walker');
var compilerCore = require('@vue/compiler-core');
var parser = require('@babel/parser');
var shared = require('@vue/shared');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e['default'] : e; }
var MagicString__default = /*#__PURE__*/_interopDefaultLegacy(MagicString);
const CONVERT_SYMBOL = '$';
const ESCAPE_SYMBOL = '$$';
const IMPORT_SOURCE = 'vue/macros';
const shorthands = ['ref', 'computed', 'shallowRef', 'toRef', 'customRef'];
const transformCheckRE = /[^\w]\$(?:\$|ref|computed|shallowRef)?\s*(\(|\<)/;
function shouldTransform(src) {
return transformCheckRE.test(src);
}
function transform(src, { filename, sourceMap, parserPlugins, importHelpersFrom = 'vue' } = {}) {
const plugins = parserPlugins || [];
if (filename) {
if (/\.tsx?$/.test(filename)) {
plugins.push('typescript');
}
if (filename.endsWith('x')) {
plugins.push('jsx');
}
}
const ast = parser.parse(src, {
sourceType: 'module',
plugins
});
const s = new MagicString__default(src);
const res = transformAST(ast.program, s, 0);
// inject helper imports
if (res.importedHelpers.length) {
s.prepend(`import { ${res.importedHelpers
.map(h => `${h} as _${h}`)
.join(', ')} } from '${importHelpersFrom}'\n`);
}
return {
...res,
code: s.toString(),
map: sourceMap
? s.generateMap({
source: filename,
hires: true,
includeContent: true
})
: null
};
}
function transformAST(ast, s, offset = 0, knownRefs, knownProps) {
// TODO remove when out of experimental
warnExperimental();
const userImports = Object.create(null);
for (const node of ast.body) {
if (node.type !== 'ImportDeclaration')
continue;
walkImportDeclaration(node);
}
// macro import handling
let convertSymbol;
let escapeSymbol;
for (const { local, imported, source, specifier } of Object.values(userImports)) {
if (source === IMPORT_SOURCE) {
if (imported === ESCAPE_SYMBOL) {
escapeSymbol = local;
}
else if (imported === CONVERT_SYMBOL) {
convertSymbol = local;
}
else if (imported !== local) {
error(`macro imports for ref-creating methods do not support aliasing.`, specifier);
}
}
}
// default symbol
if (!convertSymbol && !userImports[CONVERT_SYMBOL]) {
convertSymbol = CONVERT_SYMBOL;
}
if (!escapeSymbol && !userImports[ESCAPE_SYMBOL]) {
escapeSymbol = ESCAPE_SYMBOL;
}
const importedHelpers = new Set();
const rootScope = {};
const scopeStack = [rootScope];
let currentScope = rootScope;
let escapeScope; // inside $$()
const excludedIds = new WeakSet();
const parentStack = [];
const propsLocalToPublicMap = Object.create(null);
if (knownRefs) {
for (const key of knownRefs) {
rootScope[key] = true;
}
}
if (knownProps) {
for (const key in knownProps) {
const { local } = knownProps[key];
rootScope[local] = 'prop';
propsLocalToPublicMap[local] = key;
}
}
function walkImportDeclaration(node) {
const source = node.source.value;
if (source === IMPORT_SOURCE) {
s.remove(node.start + offset, node.end + offset);
}
for (const specifier of node.specifiers) {
const local = specifier.local.name;
const imported = (specifier.type === 'ImportSpecifier' &&
specifier.imported.type === 'Identifier' &&
specifier.imported.name) ||
'default';
userImports[local] = {
source,
local,
imported,
specifier
};
}
}
function isRefCreationCall(callee) {
if (!convertSymbol || currentScope[convertSymbol] !== undefined) {
return false;
}
if (callee === convertSymbol) {
return convertSymbol;
}
if (callee[0] === '$' && shorthands.includes(callee.slice(1))) {
return callee;
}
return false;
}
function error(msg, node) {
const e = new Error(msg);
e.node = node;
throw e;
}
function helper(msg) {
importedHelpers.add(msg);
return `_${msg}`;
}
function registerBinding(id, isRef = false) {
excludedIds.add(id);
if (currentScope) {
currentScope[id.name] = isRef;
}
else {
error('registerBinding called without active scope, something is wrong.', id);
}
}
const registerRefBinding = (id) => registerBinding(id, true);
let tempVarCount = 0;
function genTempVar() {
return `__$temp_${++tempVarCount}`;
}
function snip(node) {
return s.original.slice(node.start + offset, node.end + offset);
}
function walkScope(node, isRoot = false) {
for (const stmt of node.body) {
if (stmt.type === 'VariableDeclaration') {
walkVariableDeclaration(stmt, isRoot);
}
else if (stmt.type === 'FunctionDeclaration' ||
stmt.type === 'ClassDeclaration') {
if (stmt.declare || !stmt.id)
continue;
registerBinding(stmt.id);
}
else if ((stmt.type === 'ForOfStatement' || stmt.type === 'ForInStatement') &&
stmt.left.type === 'VariableDeclaration') {
walkVariableDeclaration(stmt.left);
}
else if (stmt.type === 'ExportNamedDeclaration' &&
stmt.declaration &&
stmt.declaration.type === 'VariableDeclaration') {
walkVariableDeclaration(stmt.declaration, isRoot);
}
else if (stmt.type === 'LabeledStatement' &&
stmt.body.type === 'VariableDeclaration') {
walkVariableDeclaration(stmt.body, isRoot);
}
}
}
function walkVariableDeclaration(stmt, isRoot = false) {
if (stmt.declare) {
return;
}
for (const decl of stmt.declarations) {
let refCall;
const isCall = decl.init &&
decl.init.type === 'CallExpression' &&
decl.init.callee.type === 'Identifier';
if (isCall &&
(refCall = isRefCreationCall(decl.init.callee.name))) {
processRefDeclaration(refCall, decl.id, decl.init);
}
else {
const isProps = isRoot && isCall && decl.init.callee.name === 'defineProps';
for (const id of compilerCore.extractIdentifiers(decl.id)) {
if (isProps) {
// for defineProps destructure, only exclude them since they
// are already passed in as knownProps
excludedIds.add(id);
}
else {
registerBinding(id);
}
}
}
}
}
function processRefDeclaration(method, id, call) {
excludedIds.add(call.callee);
if (method === convertSymbol) {
// $
// remove macro
s.remove(call.callee.start + offset, call.callee.end + offset);
if (id.type === 'Identifier') {
// single variable
registerRefBinding(id);
}
else if (id.type === 'ObjectPattern') {
processRefObjectPattern(id, call);
}
else if (id.type === 'ArrayPattern') {
processRefArrayPattern(id, call);
}
}
else {
// shorthands
if (id.type === 'Identifier') {
registerRefBinding(id);
// replace call
s.overwrite(call.start + offset, call.start + method.length + offset, helper(method.slice(1)));
}
else {
error(`${method}() cannot be used with destructure patterns.`, call);
}
}
}
function processRefObjectPattern(pattern, call, tempVar, path = []) {
if (!tempVar) {
tempVar = genTempVar();
// const { x } = $(useFoo()) --> const __$temp_1 = useFoo()
s.overwrite(pattern.start + offset, pattern.end + offset, tempVar);
}
let nameId;
for (const p of pattern.properties) {
let key;
let defaultValue;
if (p.type === 'ObjectProperty') {
if (p.key.start === p.value.start) {
// shorthand { foo }
nameId = p.key;
if (p.value.type === 'Identifier') {
// avoid shorthand value identifier from being processed
excludedIds.add(p.value);
}
else if (p.value.type === 'AssignmentPattern' &&
p.value.left.type === 'Identifier') {
// { foo = 1 }
excludedIds.add(p.value.left);
defaultValue = p.value.right;
}
}
else {
key = p.computed ? p.key : p.key.name;
if (p.value.type === 'Identifier') {
// { foo: bar }
nameId = p.value;
}
else if (p.value.type === 'ObjectPattern') {
processRefObjectPattern(p.value, call, tempVar, [...path, key]);
}
else if (p.value.type === 'ArrayPattern') {
processRefArrayPattern(p.value, call, tempVar, [...path, key]);
}
else if (p.value.type === 'AssignmentPattern') {
if (p.value.left.type === 'Identifier') {
// { foo: bar = 1 }
nameId = p.value.left;
defaultValue = p.value.right;
}
else if (p.value.left.type === 'ObjectPattern') {
processRefObjectPattern(p.value.left, call, tempVar, [
...path,
[key, p.value.right]
]);
}
else if (p.value.left.type === 'ArrayPattern') {
processRefArrayPattern(p.value.left, call, tempVar, [
...path,
[key, p.value.right]
]);
}
else ;
}
}
}
else {
// rest element { ...foo }
error(`reactivity destructure does not support rest elements.`, p);
}
if (nameId) {
registerRefBinding(nameId);
// inject toRef() after original replaced pattern
const source = pathToString(tempVar, path);
const keyStr = shared.isString(key)
? `'${key}'`
: key
? snip(key)
: `'${nameId.name}'`;
const defaultStr = defaultValue ? `, ${snip(defaultValue)}` : ``;
s.appendLeft(call.end + offset, `,\n ${nameId.name} = ${helper('toRef')}(${source}, ${keyStr}${defaultStr})`);
}
}
if (nameId) {
s.appendLeft(call.end + offset, ';');
}
}
function processRefArrayPattern(pattern, call, tempVar, path = []) {
if (!tempVar) {
// const [x] = $(useFoo()) --> const __$temp_1 = useFoo()
tempVar = genTempVar();
s.overwrite(pattern.start + offset, pattern.end + offset, tempVar);
}
let nameId;
for (let i = 0; i < pattern.elements.length; i++) {
const e = pattern.elements[i];
if (!e)
continue;
let defaultValue;
if (e.type === 'Identifier') {
// [a] --> [__a]
nameId = e;
}
else if (e.type === 'AssignmentPattern') {
// [a = 1]
nameId = e.left;
defaultValue = e.right;
}
else if (e.type === 'RestElement') {
// [...a]
error(`reactivity destructure does not support rest elements.`, e);
}
else if (e.type === 'ObjectPattern') {
processRefObjectPattern(e, call, tempVar, [...path, i]);
}
else if (e.type === 'ArrayPattern') {
processRefArrayPattern(e, call, tempVar, [...path, i]);
}
if (nameId) {
registerRefBinding(nameId);
// inject toRef() after original replaced pattern
const source = pathToString(tempVar, path);
const defaultStr = defaultValue ? `, ${snip(defaultValue)}` : ``;
s.appendLeft(call.end + offset, `,\n ${nameId.name} = ${helper('toRef')}(${source}, ${i}${defaultStr})`);
}
}
if (nameId) {
s.appendLeft(call.end + offset, ';');
}
}
function pathToString(source, path) {
if (path.length) {
for (const seg of path) {
if (shared.isArray(seg)) {
source = `(${source}${segToString(seg[0])} || ${snip(seg[1])})`;
}
else {
source += segToString(seg);
}
}
}
return source;
}
function segToString(seg) {
if (typeof seg === 'number') {
return `[${seg}]`;
}
else if (typeof seg === 'string') {
return `.${seg}`;
}
else {
return snip(seg);
}
}
function rewriteId(scope, id, parent, parentStack) {
if (shared.hasOwn(scope, id.name)) {
const bindingType = scope[id.name];
if (bindingType) {
const isProp = bindingType === 'prop';
if (compilerCore.isStaticProperty(parent) && parent.shorthand) {
// let binding used in a property shorthand
// skip for destructure patterns
if (!parent.inPattern ||
compilerCore.isInDestructureAssignment(parent, parentStack)) {
if (isProp) {
if (escapeScope) {
// prop binding in $$()
// { prop } -> { prop: __props_prop }
registerEscapedPropBinding(id);
s.appendLeft(id.end + offset, `: __props_${propsLocalToPublicMap[id.name]}`);
}
else {
// { prop } -> { prop: __props.prop }
s.appendLeft(id.end + offset, `: ${shared.genPropsAccessExp(propsLocalToPublicMap[id.name])}`);
}
}
else {
// { foo } -> { foo: foo.value }
s.appendLeft(id.end + offset, `: ${id.name}.value`);
}
}
}
else {
if (isProp) {
if (escapeScope) {
// x --> __props_x
registerEscapedPropBinding(id);
s.overwrite(id.start + offset, id.end + offset, `__props_${propsLocalToPublicMap[id.name]}`);
}
else {
// x --> __props.x
s.overwrite(id.start + offset, id.end + offset, shared.genPropsAccessExp(propsLocalToPublicMap[id.name]));
}
}
else {
// x --> x.value
s.appendLeft(id.end + offset, '.value');
}
}
}
return true;
}
return false;
}
const propBindingRefs = {};
function registerEscapedPropBinding(id) {
if (!propBindingRefs.hasOwnProperty(id.name)) {
propBindingRefs[id.name] = true;
const publicKey = propsLocalToPublicMap[id.name];
s.prependRight(offset, `const __props_${publicKey} = ${helper(`toRef`)}(__props, '${publicKey}');\n`);
}
}
// check root scope first
walkScope(ast, true);
estreeWalker.walk(ast, {
enter(node, parent) {
parent && parentStack.push(parent);
// function scopes
if (compilerCore.isFunctionType(node)) {
scopeStack.push((currentScope = {}));
compilerCore.walkFunctionParams(node, registerBinding);
if (node.body.type === 'BlockStatement') {
walkScope(node.body);
}
return;
}
// catch param
if (node.type === 'CatchClause') {
scopeStack.push((currentScope = {}));
if (node.param && node.param.type === 'Identifier') {
registerBinding(node.param);
}
walkScope(node.body);
return;
}
// non-function block scopes
if (node.type === 'BlockStatement' && !compilerCore.isFunctionType(parent)) {
scopeStack.push((currentScope = {}));
walkScope(node);
return;
}
// skip type nodes
if (parent &&
parent.type.startsWith('TS') &&
parent.type !== 'TSAsExpression' &&
parent.type !== 'TSNonNullExpression' &&
parent.type !== 'TSTypeAssertion') {
return this.skip();
}
if (node.type === 'Identifier' &&
// if inside $$(), skip unless this is a destructured prop binding
!(escapeScope && rootScope[node.name] !== 'prop') &&
compilerCore.isReferencedIdentifier(node, parent, parentStack) &&
!excludedIds.has(node)) {
// walk up the scope chain to check if id should be appended .value
let i = scopeStack.length;
while (i--) {
if (rewriteId(scopeStack[i], node, parent, parentStack)) {
return;
}
}
}
if (node.type === 'CallExpression' && node.callee.type === 'Identifier') {
const callee = node.callee.name;
const refCall = isRefCreationCall(callee);
if (refCall && (!parent || parent.type !== 'VariableDeclarator')) {
return error(`${refCall} can only be used as the initializer of ` +
`a variable declaration.`, node);
}
if (escapeSymbol &&
currentScope[escapeSymbol] === undefined &&
callee === escapeSymbol) {
s.remove(node.callee.start + offset, node.callee.end + offset);
escapeScope = node;
}
// TODO remove when out of experimental
if (callee === '$raw') {
error(`$raw() has been replaced by $$(). ` +
`See ${RFC_LINK} for latest updates.`, node);
}
if (callee === '$fromRef') {
error(`$fromRef() has been replaced by $(). ` +
`See ${RFC_LINK} for latest updates.`, node);
}
}
},
leave(node, parent) {
parent && parentStack.pop();
if ((node.type === 'BlockStatement' && !compilerCore.isFunctionType(parent)) ||
compilerCore.isFunctionType(node)) {
scopeStack.pop();
currentScope = scopeStack[scopeStack.length - 1] || null;
}
if (node === escapeScope) {
escapeScope = undefined;
}
}
});
return {
rootRefs: Object.keys(rootScope).filter(key => rootScope[key] === true),
importedHelpers: [...importedHelpers]
};
}
const RFC_LINK = `https://github.com/vuejs/rfcs/discussions/369`;
const hasWarned = {};
function warnExperimental() {
// eslint-disable-next-line
if (typeof window !== 'undefined') {
return;
}
warnOnce(`Reactivity transform is an experimental feature.\n` +
`Experimental features may change behavior between patch versions.\n` +
`It is recommended to pin your vue dependencies to exact versions to avoid breakage.\n` +
`You can follow the proposal's status at ${RFC_LINK}.`);
}
function warnOnce(msg) {
const isNodeProd = typeof process !== 'undefined' && process.env.NODE_ENV === 'production';
if (!isNodeProd && !false && !hasWarned[msg]) {
hasWarned[msg] = true;
warn(msg);
}
}
function warn(msg) {
console.warn(`\x1b[1m\x1b[33m[@vue/reactivity-transform]\x1b[0m\x1b[33m ${msg}\x1b[0m\n`);
}
exports.shouldTransform = shouldTransform;
exports.transform = transform;
exports.transformAST = transformAST;

View File

@@ -0,0 +1,43 @@
import { ImportDefaultSpecifier } from '@babel/types';
import { ImportNamespaceSpecifier } from '@babel/types';
import { ImportSpecifier } from '@babel/types';
import MagicString from 'magic-string';
import { ParserPlugin } from '@babel/parser';
import { Program } from '@babel/types';
import { SourceMap } from 'magic-string';
export declare interface ImportBinding {
local: string;
imported: string;
source: string;
specifier: ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier;
}
export declare interface RefTransformOptions {
filename?: string;
sourceMap?: boolean;
parserPlugins?: ParserPlugin[];
importHelpersFrom?: string;
}
export declare interface RefTransformResults {
code: string;
map: SourceMap | null;
rootRefs: string[];
importedHelpers: string[];
}
export declare function shouldTransform(src: string): boolean;
export declare function transform(src: string, { filename, sourceMap, parserPlugins, importHelpersFrom }?: RefTransformOptions): RefTransformResults;
export declare function transformAST(ast: Program, s: MagicString, offset?: number, knownRefs?: string[], knownProps?: Record<string, // public prop key
{
local: string;
default?: any;
}>): {
rootRefs: string[];
importedHelpers: string[];
};
export { }

View File

@@ -0,0 +1,41 @@
{
"name": "@vue/reactivity-transform",
"version": "3.2.45",
"description": "@vue/reactivity-transform",
"main": "dist/reactivity-transform.cjs.js",
"files": [
"dist"
],
"buildOptions": {
"formats": [
"cjs"
],
"prod": false
},
"types": "dist/reactivity-transform.d.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/vuejs/core.git",
"directory": "packages/reactivity-transform"
},
"keywords": [
"vue"
],
"author": "Evan You",
"license": "MIT",
"bugs": {
"url": "https://github.com/vuejs/core/issues"
},
"homepage": "https://github.com/vuejs/core/tree/dev/packages/reactivity-transform#readme",
"dependencies": {
"@babel/parser": "^7.16.4",
"@vue/compiler-core": "3.2.45",
"@vue/shared": "3.2.45",
"estree-walker": "^2.0.2",
"magic-string": "^0.25.7"
},
"devDependencies": {
"@babel/core": "^7.16.0",
"@babel/types": "^7.16.0"
}
}