-
-
Notifications
You must be signed in to change notification settings - Fork 111
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a18c557
commit ce5b8f8
Showing
11 changed files
with
474 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
{ | ||
"name": "@vue-macros/v-scope", | ||
"version": "0.3.4", | ||
"packageManager": "pnpm@7.31.0", | ||
"description": "v-scope feature from Vue Macros.", | ||
"keywords": [ | ||
"vue-macros", | ||
"macros", | ||
"vue", | ||
"sfc", | ||
"setup", | ||
"script-setup", | ||
"v-scope", | ||
"unplugin" | ||
], | ||
"license": "MIT", | ||
"homepage": "https://github.com/sxzz/unplugin-vue-macros#readme", | ||
"bugs": { | ||
"url": "https://github.com/sxzz/unplugin-vue-macros/issues" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/sxzz/unplugin-vue-macros.git", | ||
"directory": "packages/named-template" | ||
}, | ||
"author": "三咲智子 <sxzz@sxzz.moe>", | ||
"files": [ | ||
"dist" | ||
], | ||
"main": "dist/index.js", | ||
"module": "dist/index.mjs", | ||
"types": "dist/index.d.ts", | ||
"exports": { | ||
".": { | ||
"dev": "./src/index.ts", | ||
"types": "./dist/index.d.ts", | ||
"require": "./dist/index.js", | ||
"import": "./dist/index.mjs" | ||
}, | ||
"./api": { | ||
"dev": "./src/api.ts", | ||
"types": "./dist/api.d.ts", | ||
"require": "./dist/api.js", | ||
"import": "./dist/api.mjs" | ||
}, | ||
"./esbuild": { | ||
"dev": "./src/esbuild.ts", | ||
"types": "./dist/esbuild.d.ts", | ||
"require": "./dist/esbuild.js", | ||
"import": "./dist/esbuild.mjs" | ||
}, | ||
"./rollup": { | ||
"dev": "./src/rollup.ts", | ||
"types": "./dist/rollup.d.ts", | ||
"require": "./dist/rollup.js", | ||
"import": "./dist/rollup.mjs" | ||
}, | ||
"./vite": { | ||
"dev": "./src/vite.ts", | ||
"types": "./dist/vite.d.ts", | ||
"require": "./dist/vite.js", | ||
"import": "./dist/vite.mjs" | ||
}, | ||
"./webpack": { | ||
"dev": "./src/webpack.ts", | ||
"types": "./dist/webpack.d.ts", | ||
"require": "./dist/webpack.js", | ||
"import": "./dist/webpack.mjs" | ||
}, | ||
"./*": [ | ||
"./*", | ||
"./*.d.ts" | ||
] | ||
}, | ||
"typesVersions": { | ||
"<=4.9": { | ||
"*": [ | ||
"./dist/*", | ||
"./*" | ||
] | ||
} | ||
}, | ||
"scripts": { | ||
"build": "tsup && tsx ../../scripts/postbuild.mts", | ||
"dev": "DEV=true tsup" | ||
}, | ||
"dependencies": { | ||
"@vue-macros/common": "workspace:~", | ||
"@vue/compiler-dom": "^3.3.0-alpha.8", | ||
"unplugin": "^1.3.1" | ||
}, | ||
"devDependencies": { | ||
"vue": "^3.3.0-alpha.8" | ||
}, | ||
"engines": { | ||
"node": ">=14.19.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export const SCOPE_DIRECTIVE = '_directive_scope' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import { | ||
MagicString, | ||
babelParse, | ||
getLang, | ||
getTransformResult, | ||
} from '@vue-macros/common' | ||
import { | ||
type ArrayExpression, | ||
type ArrowFunctionExpression, | ||
type BlockStatement, | ||
type CallExpression, | ||
type Identifier, | ||
type ImportDeclaration, | ||
type ObjectExpression, | ||
type ObjectMethod, | ||
type ObjectProperty, | ||
type ReturnStatement, | ||
type SequenceExpression, | ||
type VariableDeclaration, | ||
} from '@babel/types' | ||
import { SCOPE_DIRECTIVE } from './constant' | ||
|
||
export function preTransform(code: string, id: string) { | ||
const s = new MagicString(code) | ||
|
||
return getTransformResult(s, id) | ||
} | ||
|
||
export function postTransform(code: string, id: string) { | ||
if (!code.includes(SCOPE_DIRECTIVE)) { | ||
return | ||
} | ||
|
||
const lang = getLang(id) | ||
const program = babelParse(code, lang) | ||
const s = new MagicString(code) | ||
const importStatement = program.body[0] as ImportDeclaration | ||
|
||
const sfcMainStmts = program.body[2] as VariableDeclaration | ||
|
||
const valueStmt = sfcMainStmts.declarations[0].init as ObjectExpression | ||
|
||
const setupStmt = valueStmt.properties[1] | ||
|
||
const retStmt = (setupStmt as ObjectMethod).body.body[1] as ReturnStatement | ||
const arguStmt = retStmt.argument as ArrowFunctionExpression | ||
|
||
const innerVar = (arguStmt.body as BlockStatement) | ||
.body[0] as VariableDeclaration | ||
const innerRet = (arguStmt.body as BlockStatement).body[1] as ReturnStatement | ||
|
||
const safeSpecifier = ['toDisplayString', 'createTextVNode'] | ||
const specifiersFilter = importStatement.specifiers.filter((item) => { | ||
return ( | ||
item.type === 'ImportSpecifier' && | ||
item.imported.type === 'Identifier' && | ||
!safeSpecifier.includes(item.imported.name) | ||
) | ||
}) | ||
const importStart = specifiersFilter[0].start | ||
const importEnd = specifiersFilter[specifiersFilter.length - 1].end | ||
s.remove(importStart!, importEnd!) | ||
|
||
s.prependRight(importStart!, 'createElementVNode as _createElementVNode') | ||
|
||
// overwrite render function | ||
const direStart = innerVar.start! | ||
const direEnd = innerVar.end! | ||
s.remove(direStart, direEnd) | ||
parseReturn(innerRet.argument as CallExpression, s) | ||
|
||
return getTransformResult(s, id) | ||
} | ||
|
||
function parseReturn(argStmt: CallExpression, s: MagicString) { | ||
if (detectReturnWithDir(argStmt)) { | ||
const sequence = argStmt.arguments[0] as SequenceExpression | ||
const targetSeq = sequence.expressions[1] as CallExpression | ||
|
||
const targetSeqStart = targetSeq.start | ||
const targetSeqEnd = targetSeq.end | ||
const arrayExp = argStmt.arguments[1] as ArrayExpression | ||
const arrStr = (arrayExp.elements[0] as ArrayExpression) | ||
.elements[1] as ObjectExpression | ||
const key = (arrStr.properties[0] as ObjectProperty).key as Identifier | ||
const argStart = arrStr.start | ||
const argEnd = arrStr.end | ||
const nestedExp = targetSeq.arguments[2] | ||
if (nestedExp.type === 'ArrayExpression' && nestedScope(nestedExp)) { | ||
parseReturn(nestedExp.elements[0] as CallExpression, s) | ||
} | ||
const argStr = s.slice(argStart!, argEnd!) | ||
const ctxArg = `_ctx.${key.name}` | ||
|
||
const targetSeqStr = s.slice(targetSeqStart!, targetSeqEnd!) | ||
const changedTarget = targetSeqStr | ||
.replace(ctxArg, key.name) | ||
.replace('_createElementBlock', '_createElementVNode') | ||
const changedStr = `${argStr | ||
.replace(':', '=') | ||
.replace('{', '(') | ||
.replace('}', ')')}=>` | ||
|
||
const changedAllStr = `(${changedStr} ${changedTarget})()` | ||
|
||
s.remove(argStmt.start!, argStmt.end!) | ||
s.prependRight(argStmt.start!, changedAllStr) | ||
} | ||
} | ||
|
||
function nestedScope(exp: ArrayExpression) { | ||
const ele = exp.elements[0] | ||
return ele && ele.type === 'CallExpression' && detectReturnWithDir(ele) | ||
} | ||
|
||
function matchedScopeKey(code: string, keyword: RegExp): RegExpMatchArray[] { | ||
return [...code.matchAll(keyword)] | ||
} | ||
|
||
function detectReturnWithDir(exp: CallExpression) { | ||
const isDir = | ||
exp.callee.type === 'Identifier' && exp.callee.name === '_withDirectives' | ||
const scopeArg = exp.arguments[1] as ArrayExpression | ||
return isDir && iscorrectScopeArg(scopeArg) | ||
} | ||
|
||
function iscorrectScopeArg(arg: ArrayExpression) { | ||
const argElements = arg.elements | ||
const innerArgElements = (argElements[0] as ArrayExpression).elements | ||
const isscopeId = | ||
innerArgElements[0]!.type === 'Identifier' && | ||
innerArgElements[0]!.name === '_directive_scope' | ||
const isScopeArg = innerArgElements[1]?.type === 'ObjectExpression' | ||
|
||
return argElements.length === 1 && isscopeId && isScopeArg | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { createUnplugin } from 'unplugin' | ||
import { type BaseOptions, type MarkRequired } from '@vue-macros/common' | ||
import { | ||
REGEX_VUE_SFC, | ||
createFilter, | ||
detectVueVersion, | ||
} from '@vue-macros/common' | ||
import { postTransform, preTransform } from './core' | ||
|
||
export type Options = BaseOptions | ||
|
||
export type OptionsResolved = MarkRequired<Options, 'include' | 'version'> | ||
|
||
function resolveOption(options: Options): OptionsResolved { | ||
const version = options.version || detectVueVersion() | ||
return { | ||
include: [REGEX_VUE_SFC], | ||
...options, | ||
version, | ||
} | ||
} | ||
|
||
const name = 'unplugin-vue-v-scope' | ||
|
||
export const PrePlugin = createUnplugin<Options | undefined, false>( | ||
(userOptions = {}) => { | ||
const options = resolveOption(userOptions) | ||
const filter = createFilter(options) | ||
|
||
return { | ||
name: `${name}-pre`, | ||
enforce: 'pre', | ||
|
||
transformInclude(id) { | ||
return filter(id) | ||
}, | ||
|
||
transform(code, id) { | ||
return preTransform(code, id) | ||
}, | ||
} | ||
} | ||
) | ||
|
||
export const PostPlugin = createUnplugin<Options | undefined, false>( | ||
(userOptions = {}) => { | ||
const options = resolveOption(userOptions) | ||
const filter = createFilter(options) | ||
|
||
function transformInclude(id: string) { | ||
return filter(id) | ||
} | ||
|
||
return { | ||
name: `${name}-post`, | ||
enforce: 'post', | ||
|
||
transformInclude, | ||
transform(code, id) { | ||
return postTransform(code, id) | ||
}, | ||
|
||
rollup: { | ||
transform: { | ||
order: 'post', | ||
handler(code, id) { | ||
if (!transformInclude(id)) return | ||
return postTransform(code, id) | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
) | ||
|
||
const plugin = createUnplugin<Options | undefined, true>( | ||
(userOptions = {}, meta) => { | ||
return [PrePlugin.raw(userOptions, meta), PostPlugin.raw(userOptions, meta)] | ||
} | ||
) | ||
|
||
export default plugin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import unplugin from '.' | ||
|
||
export default unplugin.rollup |
Oops, something went wrong.