This commit is contained in:
2023-08-11 10:45:20 +08:00
commit 161ca982f3
31850 changed files with 2706500 additions and 0 deletions

View File

@ -0,0 +1,64 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"projectFolder": ".",
"mainEntryPointFilePath": "../../temp/packages/compiler-sfc/src/index.d.ts",
"compiler": {
"tsconfigFilePath": "../../api-extractor.tsconfig.json"
},
"dtsRollup": {
"enabled": true,
"untrimmedFilePath": "",
"publicTrimmedFilePath": "./dist/compiler-sfc.d.ts"
},
"apiReport": {
"enabled": false
},
"docModel": {
"enabled": false
},
"tsdocMetadata": {
"enabled": false
},
"messages": {
"compilerMessageReporting": {
"default": {
"logLevel": "warning"
}
},
"extractorMessageReporting": {
"default": {
"logLevel": "warning",
"addToApiReportFile": true
},
"ae-missing-release-tag": {
"logLevel": "none"
},
"ae-internal-missing-underscore": {
"logLevel": "none"
},
"ae-forgotten-export": {
"logLevel": "none"
}
},
"tsdocMessageReporting": {
"default": {
"logLevel": "warning"
},
"tsdoc-undefined-tag": {
"logLevel": "none"
}
}
}
}

View File

@ -0,0 +1,454 @@
import { LazyResult } from 'postcss';
import { ParserPlugin } from '@babel/parser';
declare interface AssetURLOptions {
[name: string]: string | string[];
}
declare type ASTAttr = {
name: string;
value: any;
dynamic?: boolean;
start?: number;
end?: number;
};
declare type ASTDirective = {
name: string;
rawName: string;
value: string;
arg: string | null;
isDynamicArg: boolean;
modifiers: ASTModifiers | null;
start?: number;
end?: number;
};
declare type ASTElement = {
type: 1;
tag: string;
attrsList: Array<ASTAttr>;
attrsMap: {
[key: string]: any;
};
rawAttrsMap: {
[key: string]: ASTAttr;
};
parent: ASTElement | void;
children: Array<ASTNode>;
start?: number;
end?: number;
processed?: true;
static?: boolean;
staticRoot?: boolean;
staticInFor?: boolean;
staticProcessed?: boolean;
hasBindings?: boolean;
text?: string;
attrs?: Array<ASTAttr>;
dynamicAttrs?: Array<ASTAttr>;
props?: Array<ASTAttr>;
plain?: boolean;
pre?: true;
ns?: string;
component?: string;
inlineTemplate?: true;
transitionMode?: string | null;
slotName?: string | null;
slotTarget?: string | null;
slotTargetDynamic?: boolean;
slotScope?: string | null;
scopedSlots?: {
[name: string]: ASTElement;
};
ref?: string;
refInFor?: boolean;
if?: string;
ifProcessed?: boolean;
elseif?: string;
else?: true;
ifConditions?: ASTIfConditions;
for?: string;
forProcessed?: boolean;
key?: string;
alias?: string;
iterator1?: string;
iterator2?: string;
staticClass?: string;
classBinding?: string;
staticStyle?: string;
styleBinding?: string;
events?: ASTElementHandlers;
nativeEvents?: ASTElementHandlers;
transition?: string | true;
transitionOnAppear?: boolean;
model?: {
value: string;
callback: string;
expression: string;
};
directives?: Array<ASTDirective>;
forbidden?: true;
once?: true;
onceProcessed?: boolean;
wrapData?: (code: string) => string;
wrapListeners?: (code: string) => string;
ssrOptimizability?: number;
};
declare type ASTElementHandler = {
value: string;
params?: Array<any>;
modifiers: ASTModifiers | null;
dynamic?: boolean;
start?: number;
end?: number;
};
declare type ASTElementHandlers = {
[key: string]: ASTElementHandler | Array<ASTElementHandler>;
};
declare type ASTExpression = {
type: 2;
expression: string;
text: string;
tokens: Array<string | Object>;
static?: boolean;
ssrOptimizability?: number;
start?: number;
end?: number;
};
declare type ASTIfCondition = {
exp: string | null;
block: ASTElement;
};
declare type ASTIfConditions = Array<ASTIfCondition>;
declare type ASTModifiers = {
[key: string]: boolean;
};
declare type ASTNode = ASTElement | ASTText | ASTExpression;
declare type ASTText = {
type: 3;
text: string;
static?: boolean;
isComment?: boolean;
ssrOptimizability?: number;
start?: number;
end?: number;
};
declare type BindingMetadata = {
[key: string]: BindingTypes | undefined;
} & {
__isScriptSetup?: boolean;
};
declare const enum BindingTypes {
/**
* returned from data()
*/
DATA = "data",
/**
* declared as a prop
*/
PROPS = "props",
/**
* a local alias of a `<script setup>` destructured prop.
* the original is stored in __propsAliases of the bindingMetadata object.
*/
PROPS_ALIASED = "props-aliased",
/**
* a let binding (may or may not be a ref)
*/
SETUP_LET = "setup-let",
/**
* a const binding that can never be a ref.
* these bindings don't need `unref()` calls when processed in inlined
* template expressions.
*/
SETUP_CONST = "setup-const",
/**
* a const binding that does not need `unref()`, but may be mutated.
*/
SETUP_REACTIVE_CONST = "setup-reactive-const",
/**
* a const binding that may be a ref.
*/
SETUP_MAYBE_REF = "setup-maybe-ref",
/**
* bindings that are guaranteed to be refs
*/
SETUP_REF = "setup-ref",
/**
* declared by other options, e.g. computed, inject
*/
OPTIONS = "options"
}
declare type CompiledResult = {
ast: ASTElement | null;
render: string;
staticRenderFns: Array<string>;
stringRenderFns?: Array<string>;
errors?: Array<string | WarningMessage>;
tips?: Array<string | WarningMessage>;
};
export declare type CompilerOptions = {
warn?: Function;
modules?: Array<ModuleOptions>;
directives?: {
[key: string]: Function;
};
staticKeys?: string;
isUnaryTag?: (tag: string) => boolean | undefined;
canBeLeftOpenTag?: (tag: string) => boolean | undefined;
isReservedTag?: (tag: string) => boolean | undefined;
preserveWhitespace?: boolean;
whitespace?: 'preserve' | 'condense';
optimize?: boolean;
mustUseProp?: (tag: string, type: string | null, name: string) => boolean;
isPreTag?: (attr: string) => boolean | null;
getTagNamespace?: (tag: string) => string | undefined;
expectHTML?: boolean;
isFromDOM?: boolean;
shouldDecodeTags?: boolean;
shouldDecodeNewlines?: boolean;
shouldDecodeNewlinesForHref?: boolean;
outputSourceRange?: boolean;
shouldKeepComment?: boolean;
delimiters?: [string, string];
comments?: boolean;
scopeId?: string;
bindings?: BindingMetadata;
};
/**
* Compile `<script setup>`
* It requires the whole SFC descriptor because we need to handle and merge
* normal `<script>` + `<script setup>` if both are present.
*/
export declare function compileScript(sfc: SFCDescriptor, options?: SFCScriptCompileOptions): SFCScriptBlock;
export declare function compileStyle(options: SFCStyleCompileOptions): SFCStyleCompileResults;
export declare function compileStyleAsync(options: SFCStyleCompileOptions): Promise<SFCStyleCompileResults>;
export declare function compileTemplate(options: SFCTemplateCompileOptions): SFCTemplateCompileResults;
export declare function generateCodeFrame(source: string, start?: number, end?: number): string;
declare interface ImportBinding {
isType: boolean;
imported: string;
source: string;
isFromSetup: boolean;
isUsedInTemplate: boolean;
}
declare type ModuleOptions = {
preTransformNode?: (el: ASTElement) => ASTElement | null | void;
transformNode?: (el: ASTElement) => ASTElement | null | void;
postTransformNode?: (el: ASTElement) => void;
genData?: (el: ASTElement) => string;
transformCode?: (el: ASTElement, code: string) => string;
staticKeys?: Array<string>;
};
export declare function parse(options: SFCParseOptions): SFCDescriptor;
/**
* Parse a single-file component (*.vue) file into an SFC Descriptor Object.
*/
export declare function parseComponent(source: string, options?: VueTemplateCompilerParseOptions): SFCDescriptor;
declare interface RawSourceMap extends StartOfSourceMap {
version: string;
sources: string[];
names: string[];
sourcesContent?: string[];
mappings: string;
}
/**
* Utility for rewriting `export default` in a script block into a variable
* declaration so that we can inject things into it
*/
export declare function rewriteDefault(input: string, as: string, parserPlugins?: ParserPlugin[]): string;
export declare interface SFCBlock extends SFCCustomBlock {
lang?: string;
scoped?: boolean;
module?: string | boolean;
}
export declare interface SFCCustomBlock {
type: string;
content: string;
attrs: {
[key: string]: string | true;
};
start: number;
end: number;
src?: string;
map?: RawSourceMap;
}
export declare interface SFCDescriptor {
source: string;
filename: string;
template: SFCBlock | null;
script: SFCScriptBlock | null;
scriptSetup: SFCScriptBlock | null;
styles: SFCBlock[];
customBlocks: SFCCustomBlock[];
cssVars: string[];
errors: (string | WarningMessage)[];
/**
* compare with an existing descriptor to determine whether HMR should perform
* a reload vs. re-render.
*
* Note: this comparison assumes the prev/next script are already identical,
* and only checks the special case where `<script setup lang="ts">` unused
* import pruning result changes due to template changes.
*/
shouldForceReload: (prevImports: Record<string, ImportBinding>) => boolean;
}
export declare interface SFCParseOptions {
source: string;
filename?: string;
compiler?: TemplateCompiler;
compilerParseOptions?: VueTemplateCompilerParseOptions;
sourceRoot?: string;
sourceMap?: boolean;
/**
* @deprecated use `sourceMap` instead.
*/
needMap?: boolean;
}
export declare interface SFCScriptBlock extends SFCBlock {
type: 'script';
setup?: string | boolean;
bindings?: BindingMetadata;
imports?: Record<string, ImportBinding>;
/**
* import('\@babel/types').Statement
*/
scriptAst?: any[];
/**
* import('\@babel/types').Statement
*/
scriptSetupAst?: any[];
}
export declare interface SFCScriptCompileOptions {
/**
* Scope ID for prefixing injected CSS variables.
* This must be consistent with the `id` passed to `compileStyle`.
*/
id: string;
/**
* Production mode. Used to determine whether to generate hashed CSS variables
*/
isProd?: boolean;
/**
* Enable/disable source map. Defaults to true.
*/
sourceMap?: boolean;
/**
* https://babeljs.io/docs/en/babel-parser#plugins
*/
babelParserPlugins?: ParserPlugin[];
}
export declare interface SFCStyleCompileOptions {
source: string;
filename: string;
id: string;
map?: any;
scoped?: boolean;
trim?: boolean;
preprocessLang?: string;
preprocessOptions?: any;
postcssOptions?: any;
postcssPlugins?: any[];
isProd?: boolean;
}
export declare interface SFCStyleCompileResults {
code: string;
map: any | void;
rawResult: LazyResult | void;
errors: string[];
}
export declare interface SFCTemplateCompileOptions {
source: string;
filename: string;
compiler?: TemplateCompiler;
compilerOptions?: CompilerOptions;
transformAssetUrls?: AssetURLOptions | boolean;
transformAssetUrlsOptions?: TransformAssetUrlsOptions;
preprocessLang?: string;
preprocessOptions?: any;
transpileOptions?: any;
isProduction?: boolean;
isFunctional?: boolean;
optimizeSSR?: boolean;
prettify?: boolean;
isTS?: boolean;
bindings?: BindingMetadata;
}
export declare interface SFCTemplateCompileResults {
ast: Object | undefined;
code: string;
source: string;
tips: (string | WarningMessage)[];
errors: (string | WarningMessage)[];
}
declare interface StartOfSourceMap {
file?: string;
sourceRoot?: string;
}
export declare interface TemplateCompiler {
parseComponent(source: string, options?: any): SFCDescriptor;
compile(template: string, options: CompilerOptions): CompiledResult;
ssrCompile(template: string, options: CompilerOptions): CompiledResult;
}
declare interface TransformAssetUrlsOptions {
/**
* If base is provided, instead of transforming relative asset urls into
* imports, they will be directly rewritten to absolute urls.
*/
base?: string;
/**
* If true, also processes absolute urls.
*/
includeAbsolute?: boolean;
}
declare interface VueTemplateCompilerParseOptions {
pad?: 'line' | 'space' | boolean;
deindent?: boolean;
outputSourceRange?: boolean;
}
export declare type WarningMessage = {
msg: string;
start?: number;
end?: number;
};
export { }

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,17 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -z "$NODE_PATH" ]; then
export NODE_PATH="/Users/evan/Vue/vue/node_modules/.pnpm/node_modules"
else
export NODE_PATH="$NODE_PATH:/Users/evan/Vue/vue/node_modules/.pnpm/node_modules"
fi
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/less@4.1.3/node_modules/less/bin/lessc" "$@"
else
exec node "$basedir/../../../../node_modules/.pnpm/less@4.1.3/node_modules/less/bin/lessc" "$@"
fi

View File

@ -0,0 +1,17 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -z "$NODE_PATH" ]; then
export NODE_PATH="/Users/evan/Vue/vue/node_modules/.pnpm/node_modules"
else
export NODE_PATH="$NODE_PATH:/Users/evan/Vue/vue/node_modules/.pnpm/node_modules"
fi
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/@babel+parser@7.19.4/node_modules/@babel/parser/bin/babel-parser.js" "$@"
else
exec node "$basedir/../../../../node_modules/.pnpm/@babel+parser@7.19.4/node_modules/@babel/parser/bin/babel-parser.js" "$@"
fi

View File

@ -0,0 +1,17 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -z "$NODE_PATH" ]; then
export NODE_PATH="/Users/evan/Vue/vue/node_modules/.pnpm/node_modules"
else
export NODE_PATH="$NODE_PATH:/Users/evan/Vue/vue/node_modules/.pnpm/node_modules"
fi
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/sass@1.55.0/node_modules/sass/sass.js" "$@"
else
exec node "$basedir/../../../../node_modules/.pnpm/sass@1.55.0/node_modules/sass/sass.js" "$@"
fi

View File

@ -0,0 +1,17 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -z "$NODE_PATH" ]; then
export NODE_PATH="/Users/evan/Vue/vue/node_modules/.pnpm/node_modules"
else
export NODE_PATH="$NODE_PATH:/Users/evan/Vue/vue/node_modules/.pnpm/node_modules"
fi
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/stylus@0.58.1/node_modules/stylus/bin/stylus" "$@"
else
exec node "$basedir/../../../../node_modules/.pnpm/stylus@0.58.1/node_modules/stylus/bin/stylus" "$@"
fi

34
node_modules/vue/packages/compiler-sfc/package.json generated vendored Normal file
View File

@ -0,0 +1,34 @@
{
"name": "@vue/compiler-sfc",
"version": "2.7.14",
"description": "compiler-sfc for Vue 2",
"main": "dist/compiler-sfc.js",
"types": "dist/compiler-sfc.d.ts",
"files": [
"dist"
],
"dependencies": {
"@babel/parser": "^7.18.4",
"postcss": "^8.4.14",
"source-map": "^0.6.1"
},
"devDependencies": {
"@babel/types": "^7.19.4",
"@types/estree": "^0.0.48",
"@types/hash-sum": "^1.0.0",
"@types/lru-cache": "^5.1.1",
"@vue/consolidate": "^0.17.3",
"de-indent": "^1.0.2",
"estree-walker": "^2.0.2",
"hash-sum": "^2.0.0",
"less": "^4.1.3",
"lru-cache": "^5.1.1",
"magic-string": "^0.25.9",
"merge-source-map": "^1.1.0",
"postcss-modules": "^4.3.1",
"postcss-selector-parser": "^6.0.10",
"pug": "^3.0.2",
"sass": "^1.52.3",
"stylus": "^0.58.1"
}
}

View File

@ -0,0 +1,423 @@
// https://github.com/vuejs/core/blob/main/packages/compiler-core/src/babelUtils.ts
// should only use types from @babel/types
// do not import runtime methods
import type {
Identifier,
Node,
Function,
ObjectProperty,
BlockStatement,
Program
} from '@babel/types'
import { walk } from 'estree-walker'
export function walkIdentifiers(
root: Node,
onIdentifier: (
node: Identifier,
parent: Node,
parentStack: Node[],
isReference: boolean,
isLocal: boolean
) => void,
onNode?: (node: Node) => void
) {
const includeAll = false
const parentStack: Node[] = []
const knownIds: Record<string, number> = Object.create(null)
const rootExp =
root.type === 'Program' &&
root.body[0].type === 'ExpressionStatement' &&
root.body[0].expression
;(walk as any)(root, {
enter(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
parent && parentStack.push(parent)
if (
parent &&
parent.type.startsWith('TS') &&
parent.type !== 'TSAsExpression' &&
parent.type !== 'TSNonNullExpression' &&
parent.type !== 'TSTypeAssertion'
) {
return this.skip()
}
if (onNode) onNode(node)
if (node.type === 'Identifier') {
const isLocal = !!knownIds[node.name]
const isRefed = isReferencedIdentifier(node, parent!, parentStack)
if (includeAll || (isRefed && !isLocal)) {
onIdentifier(node, parent!, parentStack, isRefed, isLocal)
}
} else if (
node.type === 'ObjectProperty' &&
parent!.type === 'ObjectPattern'
) {
// mark property in destructure pattern
;(node as any).inPattern = true
} else if (isFunctionType(node)) {
// walk function expressions and add its arguments to known identifiers
// so that we don't prefix them
walkFunctionParams(node, id => markScopeIdentifier(node, id, knownIds))
} else if (node.type === 'BlockStatement') {
// #3445 record block-level local variables
walkBlockDeclarations(node, id =>
markScopeIdentifier(node, id, knownIds)
)
}
},
leave(node: Node & { scopeIds?: Set<string> }, parent: Node | undefined) {
parent && parentStack.pop()
if (node !== rootExp && node.scopeIds) {
for (const id of node.scopeIds) {
knownIds[id]--
if (knownIds[id] === 0) {
delete knownIds[id]
}
}
}
}
})
}
export function isReferencedIdentifier(
id: Identifier,
parent: Node | null,
parentStack: Node[]
) {
if (!parent) {
return true
}
// is a special keyword but parsed as identifier
if (id.name === 'arguments') {
return false
}
if (isReferenced(id, parent)) {
return true
}
// babel's isReferenced check returns false for ids being assigned to, so we
// need to cover those cases here
switch (parent.type) {
case 'AssignmentExpression':
case 'AssignmentPattern':
return true
case 'ObjectPattern':
case 'ArrayPattern':
return isInDestructureAssignment(parent, parentStack)
}
return false
}
export function isInDestructureAssignment(
parent: Node,
parentStack: Node[]
): boolean {
if (
parent &&
(parent.type === 'ObjectProperty' || parent.type === 'ArrayPattern')
) {
let i = parentStack.length
while (i--) {
const p = parentStack[i]
if (p.type === 'AssignmentExpression') {
return true
} else if (p.type !== 'ObjectProperty' && !p.type.endsWith('Pattern')) {
break
}
}
}
return false
}
export function walkFunctionParams(
node: Function,
onIdent: (id: Identifier) => void
) {
for (const p of node.params) {
for (const id of extractIdentifiers(p)) {
onIdent(id)
}
}
}
export function walkBlockDeclarations(
block: BlockStatement | Program,
onIdent: (node: Identifier) => void
) {
for (const stmt of block.body) {
if (stmt.type === 'VariableDeclaration') {
if (stmt.declare) continue
for (const decl of stmt.declarations) {
for (const id of extractIdentifiers(decl.id)) {
onIdent(id)
}
}
} else if (
stmt.type === 'FunctionDeclaration' ||
stmt.type === 'ClassDeclaration'
) {
if (stmt.declare || !stmt.id) continue
onIdent(stmt.id)
}
}
}
export function extractIdentifiers(
param: Node,
nodes: Identifier[] = []
): Identifier[] {
switch (param.type) {
case 'Identifier':
nodes.push(param)
break
case 'MemberExpression':
let object: any = param
while (object.type === 'MemberExpression') {
object = object.object
}
nodes.push(object)
break
case 'ObjectPattern':
for (const prop of param.properties) {
if (prop.type === 'RestElement') {
extractIdentifiers(prop.argument, nodes)
} else {
extractIdentifiers(prop.value, nodes)
}
}
break
case 'ArrayPattern':
param.elements.forEach(element => {
if (element) extractIdentifiers(element, nodes)
})
break
case 'RestElement':
extractIdentifiers(param.argument, nodes)
break
case 'AssignmentPattern':
extractIdentifiers(param.left, nodes)
break
}
return nodes
}
function markScopeIdentifier(
node: Node & { scopeIds?: Set<string> },
child: Identifier,
knownIds: Record<string, number>
) {
const { name } = child
if (node.scopeIds && node.scopeIds.has(name)) {
return
}
if (name in knownIds) {
knownIds[name]++
} else {
knownIds[name] = 1
}
;(node.scopeIds || (node.scopeIds = new Set())).add(name)
}
export const isFunctionType = (node: Node): node is Function => {
return /Function(?:Expression|Declaration)$|Method$/.test(node.type)
}
export const isStaticProperty = (node: Node): node is ObjectProperty =>
node &&
(node.type === 'ObjectProperty' || node.type === 'ObjectMethod') &&
!node.computed
export const isStaticPropertyKey = (node: Node, parent: Node) =>
isStaticProperty(parent) && parent.key === node
/**
* Copied from https://github.com/babel/babel/blob/main/packages/babel-types/src/validators/isReferenced.ts
* To avoid runtime dependency on @babel/types (which includes process references)
* This file should not change very often in babel but we may need to keep it
* up-to-date from time to time.
*
* https://github.com/babel/babel/blob/main/LICENSE
*
*/
function isReferenced(node: Node, parent: Node, grandparent?: Node): boolean {
switch (parent.type) {
// yes: PARENT[NODE]
// yes: NODE.child
// no: parent.NODE
case 'MemberExpression':
case 'OptionalMemberExpression':
if (parent.property === node) {
return !!parent.computed
}
return parent.object === node
case 'JSXMemberExpression':
return parent.object === node
// no: let NODE = init;
// yes: let id = NODE;
case 'VariableDeclarator':
return parent.init === node
// yes: () => NODE
// no: (NODE) => {}
case 'ArrowFunctionExpression':
return parent.body === node
// no: class { #NODE; }
// no: class { get #NODE() {} }
// no: class { #NODE() {} }
// no: class { fn() { return this.#NODE; } }
case 'PrivateName':
return false
// no: class { NODE() {} }
// yes: class { [NODE]() {} }
// no: class { foo(NODE) {} }
case 'ClassMethod':
case 'ClassPrivateMethod':
case 'ObjectMethod':
if (parent.key === node) {
return !!parent.computed
}
return false
// yes: { [NODE]: "" }
// no: { NODE: "" }
// depends: { NODE }
// depends: { key: NODE }
case 'ObjectProperty':
if (parent.key === node) {
return !!parent.computed
}
// parent.value === node
return !grandparent || grandparent.type !== 'ObjectPattern'
// no: class { NODE = value; }
// yes: class { [NODE] = value; }
// yes: class { key = NODE; }
case 'ClassProperty':
if (parent.key === node) {
return !!parent.computed
}
return true
case 'ClassPrivateProperty':
return parent.key !== node
// no: class NODE {}
// yes: class Foo extends NODE {}
case 'ClassDeclaration':
case 'ClassExpression':
return parent.superClass === node
// yes: left = NODE;
// no: NODE = right;
case 'AssignmentExpression':
return parent.right === node
// no: [NODE = foo] = [];
// yes: [foo = NODE] = [];
case 'AssignmentPattern':
return parent.right === node
// no: NODE: for (;;) {}
case 'LabeledStatement':
return false
// no: try {} catch (NODE) {}
case 'CatchClause':
return false
// no: function foo(...NODE) {}
case 'RestElement':
return false
case 'BreakStatement':
case 'ContinueStatement':
return false
// no: function NODE() {}
// no: function foo(NODE) {}
case 'FunctionDeclaration':
case 'FunctionExpression':
return false
// no: export NODE from "foo";
// no: export * as NODE from "foo";
case 'ExportNamespaceSpecifier':
case 'ExportDefaultSpecifier':
return false
// no: export { foo as NODE };
// yes: export { NODE as foo };
// no: export { NODE as foo } from "foo";
case 'ExportSpecifier':
// @ts-expect-error
if (grandparent?.source) {
return false
}
return parent.local === node
// no: import NODE from "foo";
// no: import * as NODE from "foo";
// no: import { NODE as foo } from "foo";
// no: import { foo as NODE } from "foo";
// no: import NODE from "bar";
case 'ImportDefaultSpecifier':
case 'ImportNamespaceSpecifier':
case 'ImportSpecifier':
return false
// no: import "foo" assert { NODE: "json" }
case 'ImportAttribute':
return false
// no: <div NODE="foo" />
case 'JSXAttribute':
return false
// no: [NODE] = [];
// no: ({ NODE }) = [];
case 'ObjectPattern':
case 'ArrayPattern':
return false
// no: new.NODE
// no: NODE.target
case 'MetaProperty':
return false
// yes: type X = { someProperty: NODE }
// no: type X = { NODE: OtherType }
case 'ObjectTypeProperty':
return parent.key !== node
// yes: enum X { Foo = NODE }
// no: enum X { NODE }
case 'TSEnumMember':
return parent.id !== node
// yes: { [NODE]: value }
// no: { NODE: value }
case 'TSPropertySignature':
if (parent.key === node) {
return !!parent.computed
}
return true
}
return true
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,147 @@
const postcss = require('postcss')
import { ProcessOptions, LazyResult } from 'postcss'
import trimPlugin from './stylePlugins/trim'
import scopedPlugin from './stylePlugins/scoped'
import {
processors,
StylePreprocessor,
StylePreprocessorResults
} from './stylePreprocessors'
import { cssVarsPlugin } from './cssVars'
export interface SFCStyleCompileOptions {
source: string
filename: string
id: string
map?: any
scoped?: boolean
trim?: boolean
preprocessLang?: string
preprocessOptions?: any
postcssOptions?: any
postcssPlugins?: any[]
isProd?: boolean
}
export interface SFCAsyncStyleCompileOptions extends SFCStyleCompileOptions {
isAsync?: boolean
}
export interface SFCStyleCompileResults {
code: string
map: any | void
rawResult: LazyResult | void
errors: string[]
}
export function compileStyle(
options: SFCStyleCompileOptions
): SFCStyleCompileResults {
return doCompileStyle({ ...options, isAsync: false })
}
export function compileStyleAsync(
options: SFCStyleCompileOptions
): Promise<SFCStyleCompileResults> {
return Promise.resolve(doCompileStyle({ ...options, isAsync: true }))
}
export function doCompileStyle(
options: SFCAsyncStyleCompileOptions
): SFCStyleCompileResults {
const {
filename,
id,
scoped = true,
trim = true,
isProd = false,
preprocessLang,
postcssOptions,
postcssPlugins
} = options
const preprocessor = preprocessLang && processors[preprocessLang]
const preProcessedSource = preprocessor && preprocess(options, preprocessor)
const map = preProcessedSource ? preProcessedSource.map : options.map
const source = preProcessedSource ? preProcessedSource.code : options.source
const plugins = (postcssPlugins || []).slice()
plugins.unshift(cssVarsPlugin({ id: id.replace(/^data-v-/, ''), isProd }))
if (trim) {
plugins.push(trimPlugin())
}
if (scoped) {
plugins.push(scopedPlugin(id))
}
const postCSSOptions: ProcessOptions = {
...postcssOptions,
to: filename,
from: filename
}
if (map) {
postCSSOptions.map = {
inline: false,
annotation: false,
prev: map
}
}
let result, code, outMap
const errors: any[] = []
if (preProcessedSource && preProcessedSource.errors.length) {
errors.push(...preProcessedSource.errors)
}
try {
result = postcss(plugins).process(source, postCSSOptions)
// In async mode, return a promise.
if (options.isAsync) {
return result
.then(
(result: LazyResult): SFCStyleCompileResults => ({
code: result.css || '',
map: result.map && result.map.toJSON(),
errors,
rawResult: result
})
)
.catch(
(error: Error): SFCStyleCompileResults => ({
code: '',
map: undefined,
errors: [...errors, error.message],
rawResult: undefined
})
)
}
// force synchronous transform (we know we only have sync plugins)
code = result.css
outMap = result.map
} catch (e) {
errors.push(e)
}
return {
code: code || ``,
map: outMap && outMap.toJSON(),
errors,
rawResult: result
}
}
function preprocess(
options: SFCStyleCompileOptions,
preprocessor: StylePreprocessor
): StylePreprocessorResults {
return preprocessor(
options.source,
options.map,
Object.assign(
{
filename: options.filename
},
options.preprocessOptions
)
)
}

View File

@ -0,0 +1,205 @@
import { BindingMetadata, TemplateCompiler } from './types'
import assetUrlsModule, {
AssetURLOptions,
TransformAssetUrlsOptions
} from './templateCompilerModules/assetUrl'
import srcsetModule from './templateCompilerModules/srcset'
import consolidate from '@vue/consolidate'
import * as _compiler from 'web/entry-compiler'
import { prefixIdentifiers } from './prefixIdentifiers'
import { CompilerOptions, WarningMessage } from 'types/compiler'
export interface SFCTemplateCompileOptions {
source: string
filename: string
compiler?: TemplateCompiler
compilerOptions?: CompilerOptions
transformAssetUrls?: AssetURLOptions | boolean
transformAssetUrlsOptions?: TransformAssetUrlsOptions
preprocessLang?: string
preprocessOptions?: any
transpileOptions?: any
isProduction?: boolean
isFunctional?: boolean
optimizeSSR?: boolean
prettify?: boolean
isTS?: boolean
bindings?: BindingMetadata
}
export interface SFCTemplateCompileResults {
ast: Object | undefined
code: string
source: string
tips: (string | WarningMessage)[]
errors: (string | WarningMessage)[]
}
export function compileTemplate(
options: SFCTemplateCompileOptions
): SFCTemplateCompileResults {
const { preprocessLang } = options
const preprocessor = preprocessLang && consolidate[preprocessLang]
if (preprocessor) {
return actuallyCompile(
Object.assign({}, options, {
source: preprocess(options, preprocessor)
})
)
} else if (preprocessLang) {
return {
ast: {},
code: `var render = function () {}\n` + `var staticRenderFns = []\n`,
source: options.source,
tips: [
`Component ${options.filename} uses lang ${preprocessLang} for template. Please install the language preprocessor.`
],
errors: [
`Component ${options.filename} uses lang ${preprocessLang} for template, however it is not installed.`
]
}
} else {
return actuallyCompile(options)
}
}
function preprocess(
options: SFCTemplateCompileOptions,
preprocessor: any
): string {
const { source, filename, preprocessOptions } = options
const finalPreprocessOptions = Object.assign(
{
filename
},
preprocessOptions
)
// Consolidate exposes a callback based API, but the callback is in fact
// called synchronously for most templating engines. In our case, we have to
// expose a synchronous API so that it is usable in Jest transforms (which
// have to be sync because they are applied via Node.js require hooks)
let res: any, err
preprocessor.render(
source,
finalPreprocessOptions,
(_err: Error | null, _res: string) => {
if (_err) err = _err
res = _res
}
)
if (err) throw err
return res
}
function actuallyCompile(
options: SFCTemplateCompileOptions
): SFCTemplateCompileResults {
const {
source,
compiler = _compiler,
compilerOptions = {},
transpileOptions = {},
transformAssetUrls,
transformAssetUrlsOptions,
isProduction = process.env.NODE_ENV === 'production',
isFunctional = false,
optimizeSSR = false,
prettify = true,
isTS = false,
bindings
} = options
const compile =
optimizeSSR && compiler.ssrCompile ? compiler.ssrCompile : compiler.compile
let finalCompilerOptions = compilerOptions
if (transformAssetUrls) {
const builtInModules = [
transformAssetUrls === true
? assetUrlsModule(undefined, transformAssetUrlsOptions)
: assetUrlsModule(transformAssetUrls, transformAssetUrlsOptions),
srcsetModule(transformAssetUrlsOptions)
]
finalCompilerOptions = Object.assign({}, compilerOptions, {
modules: [...builtInModules, ...(compilerOptions.modules || [])],
filename: options.filename
})
}
finalCompilerOptions.bindings = bindings
const { ast, render, staticRenderFns, tips, errors } = compile(
source,
finalCompilerOptions
)
if (errors && errors.length) {
return {
ast,
code: `var render = function () {}\n` + `var staticRenderFns = []\n`,
source,
tips,
errors
}
} else {
// stripping `with` usage
let code =
`var __render__ = ${prefixIdentifiers(
`function render(${isFunctional ? `_c,_vm` : ``}){${render}\n}`,
isFunctional,
isTS,
transpileOptions,
bindings
)}\n` +
`var __staticRenderFns__ = [${staticRenderFns.map(code =>
prefixIdentifiers(
`function (${isFunctional ? `_c,_vm` : ``}){${code}\n}`,
isFunctional,
isTS,
transpileOptions,
bindings
)
)}]` +
`\n`
// #23 we use __render__ to avoid `render` not being prefixed by the
// transpiler when stripping with, but revert it back to `render` to
// maintain backwards compat
code = code.replace(/\s__(render|staticRenderFns)__\s/g, ' $1 ')
if (!isProduction) {
// mark with stripped (this enables Vue to use correct runtime proxy
// detection)
code += `render._withStripped = true`
if (prettify) {
try {
code = require('prettier').format(code, {
semi: false,
parser: 'babel'
})
} catch (e: any) {
if (e.code === 'MODULE_NOT_FOUND') {
tips.push(
'The `prettify` option is on, but the dependency `prettier` is not found.\n' +
'Please either turn off `prettify` or manually install `prettier`.'
)
}
tips.push(
`Failed to prettify component ${options.filename} template source after compilation.`
)
}
}
}
return {
ast,
code,
source,
tips,
errors
}
}
}

179
node_modules/vue/packages/compiler-sfc/src/cssVars.ts generated vendored Normal file
View File

@ -0,0 +1,179 @@
import { BindingMetadata } from './types'
import { SFCDescriptor } from './parseComponent'
import { PluginCreator } from 'postcss'
import hash from 'hash-sum'
import { prefixIdentifiers } from './prefixIdentifiers'
export const CSS_VARS_HELPER = `useCssVars`
export function genCssVarsFromList(
vars: string[],
id: string,
isProd: boolean,
isSSR = false
): string {
return `{\n ${vars
.map(
key => `"${isSSR ? `--` : ``}${genVarName(id, key, isProd)}": (${key})`
)
.join(',\n ')}\n}`
}
function genVarName(id: string, raw: string, isProd: boolean): string {
if (isProd) {
return hash(id + raw)
} else {
return `${id}-${raw.replace(/([^\w-])/g, '_')}`
}
}
function normalizeExpression(exp: string) {
exp = exp.trim()
if (
(exp[0] === `'` && exp[exp.length - 1] === `'`) ||
(exp[0] === `"` && exp[exp.length - 1] === `"`)
) {
return exp.slice(1, -1)
}
return exp
}
const vBindRE = /v-bind\s*\(/g
export function parseCssVars(sfc: SFCDescriptor): string[] {
const vars: string[] = []
sfc.styles.forEach(style => {
let match
// ignore v-bind() in comments /* ... */
const content = style.content.replace(/\/\*([\s\S]*?)\*\//g, '')
while ((match = vBindRE.exec(content))) {
const start = match.index + match[0].length
const end = lexBinding(content, start)
if (end !== null) {
const variable = normalizeExpression(content.slice(start, end))
if (!vars.includes(variable)) {
vars.push(variable)
}
}
}
})
return vars
}
const enum LexerState {
inParens,
inSingleQuoteString,
inDoubleQuoteString
}
function lexBinding(content: string, start: number): number | null {
let state: LexerState = LexerState.inParens
let parenDepth = 0
for (let i = start; i < content.length; i++) {
const char = content.charAt(i)
switch (state) {
case LexerState.inParens:
if (char === `'`) {
state = LexerState.inSingleQuoteString
} else if (char === `"`) {
state = LexerState.inDoubleQuoteString
} else if (char === `(`) {
parenDepth++
} else if (char === `)`) {
if (parenDepth > 0) {
parenDepth--
} else {
return i
}
}
break
case LexerState.inSingleQuoteString:
if (char === `'`) {
state = LexerState.inParens
}
break
case LexerState.inDoubleQuoteString:
if (char === `"`) {
state = LexerState.inParens
}
break
}
}
return null
}
// for compileStyle
export interface CssVarsPluginOptions {
id: string
isProd: boolean
}
export const cssVarsPlugin: PluginCreator<CssVarsPluginOptions> = opts => {
const { id, isProd } = opts!
return {
postcssPlugin: 'vue-sfc-vars',
Declaration(decl) {
// rewrite CSS variables
const value = decl.value
if (vBindRE.test(value)) {
vBindRE.lastIndex = 0
let transformed = ''
let lastIndex = 0
let match
while ((match = vBindRE.exec(value))) {
const start = match.index + match[0].length
const end = lexBinding(value, start)
if (end !== null) {
const variable = normalizeExpression(value.slice(start, end))
transformed +=
value.slice(lastIndex, match.index) +
`var(--${genVarName(id, variable, isProd)})`
lastIndex = end + 1
}
}
decl.value = transformed + value.slice(lastIndex)
}
}
}
}
cssVarsPlugin.postcss = true
export function genCssVarsCode(
vars: string[],
bindings: BindingMetadata,
id: string,
isProd: boolean
) {
const varsExp = genCssVarsFromList(vars, id, isProd)
return `_${CSS_VARS_HELPER}((_vm, _setup) => ${prefixIdentifiers(
`(${varsExp})`,
false,
false,
undefined,
bindings
)})`
}
// <script setup> already gets the calls injected as part of the transform
// this is only for single normal <script>
export function genNormalScriptCssVarsCode(
cssVars: string[],
bindings: BindingMetadata,
id: string,
isProd: boolean
): string {
return (
`\nimport { ${CSS_VARS_HELPER} as _${CSS_VARS_HELPER} } from 'vue'\n` +
`const __injectCSSVars__ = () => {\n${genCssVarsCode(
cssVars,
bindings,
id,
isProd
)}}\n` +
`const __setup__ = __default__.setup\n` +
`__default__.setup = __setup__\n` +
` ? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }\n` +
` : __injectCSSVars__\n`
)
}

31
node_modules/vue/packages/compiler-sfc/src/index.ts generated vendored Normal file
View File

@ -0,0 +1,31 @@
// API
export { parse } from './parse'
export { compileTemplate } from './compileTemplate'
export { compileStyle, compileStyleAsync } from './compileStyle'
export { compileScript } from './compileScript'
export { generateCodeFrame } from 'compiler/codeframe'
export { rewriteDefault } from './rewriteDefault'
// For backwards compat only. Some existing tools like
// fork-ts-checker-webpack-plugin relies on its presence for differentiating
// between Vue 2 and Vue 3.
// ref #12719
// ref https://github.com/TypeStrong/fork-ts-checker-webpack-plugin/issues/765
export { parseComponent } from './parseComponent'
// types
export { SFCParseOptions } from './parse'
export { CompilerOptions, WarningMessage } from 'types/compiler'
export { TemplateCompiler } from './types'
export {
SFCBlock,
SFCCustomBlock,
SFCScriptBlock,
SFCDescriptor
} from './parseComponent'
export {
SFCTemplateCompileOptions,
SFCTemplateCompileResults
} from './compileTemplate'
export { SFCStyleCompileOptions, SFCStyleCompileResults } from './compileStyle'
export { SFCScriptCompileOptions } from './compileScript'

129
node_modules/vue/packages/compiler-sfc/src/parse.ts generated vendored Normal file
View File

@ -0,0 +1,129 @@
import { SourceMapGenerator } from 'source-map'
import { RawSourceMap, TemplateCompiler } from './types'
import {
parseComponent,
VueTemplateCompilerParseOptions,
SFCDescriptor,
DEFAULT_FILENAME
} from './parseComponent'
import hash from 'hash-sum'
import LRU from 'lru-cache'
import { hmrShouldReload } from './compileScript'
import { parseCssVars } from './cssVars'
const cache = new LRU<string, SFCDescriptor>(100)
const splitRE = /\r?\n/g
const emptyRE = /^(?:\/\/)?\s*$/
export interface SFCParseOptions {
source: string
filename?: string
compiler?: TemplateCompiler
compilerParseOptions?: VueTemplateCompilerParseOptions
sourceRoot?: string
sourceMap?: boolean
/**
* @deprecated use `sourceMap` instead.
*/
needMap?: boolean
}
export function parse(options: SFCParseOptions): SFCDescriptor {
const {
source,
filename = DEFAULT_FILENAME,
compiler,
compilerParseOptions = { pad: false } as VueTemplateCompilerParseOptions,
sourceRoot = '',
needMap = true,
sourceMap = needMap
} = options
const cacheKey = hash(
filename + source + JSON.stringify(compilerParseOptions)
)
let output = cache.get(cacheKey)
if (output) {
return output
}
if (compiler) {
// user-provided compiler
output = compiler.parseComponent(source, compilerParseOptions)
} else {
// use built-in compiler
output = parseComponent(source, compilerParseOptions)
}
output.filename = filename
// parse CSS vars
output.cssVars = parseCssVars(output)
output.shouldForceReload = prevImports =>
hmrShouldReload(prevImports, output!)
if (sourceMap) {
if (output.script && !output.script.src) {
output.script.map = generateSourceMap(
filename,
source,
output.script.content,
sourceRoot,
compilerParseOptions.pad
)
}
if (output.styles) {
output.styles.forEach(style => {
if (!style.src) {
style.map = generateSourceMap(
filename,
source,
style.content,
sourceRoot,
compilerParseOptions.pad
)
}
})
}
}
cache.set(cacheKey, output)
return output
}
function generateSourceMap(
filename: string,
source: string,
generated: string,
sourceRoot: string,
pad?: 'line' | 'space' | boolean
): RawSourceMap {
const map = new SourceMapGenerator({
file: filename.replace(/\\/g, '/'),
sourceRoot: sourceRoot.replace(/\\/g, '/')
})
let offset = 0
if (!pad) {
offset = source.split(generated).shift()!.split(splitRE).length - 1
}
map.setSourceContent(filename, source)
generated.split(splitRE).forEach((line, index) => {
if (!emptyRE.test(line)) {
map.addMapping({
source: filename,
original: {
line: index + 1 + offset,
column: 0
},
generated: {
line: index + 1,
column: 0
}
})
}
})
return JSON.parse(map.toString())
}

View File

@ -0,0 +1,220 @@
import deindent from 'de-indent'
import { parseHTML } from 'compiler/parser/html-parser'
import { makeMap } from 'shared/util'
import { ASTAttr, WarningMessage } from 'types/compiler'
import { BindingMetadata, RawSourceMap } from './types'
import type { ImportBinding } from './compileScript'
export const DEFAULT_FILENAME = 'anonymous.vue'
const splitRE = /\r?\n/g
const replaceRE = /./g
const isSpecialTag = makeMap('script,style,template', true)
export interface SFCCustomBlock {
type: string
content: string
attrs: { [key: string]: string | true }
start: number
end: number
src?: string
map?: RawSourceMap
}
export interface SFCBlock extends SFCCustomBlock {
lang?: string
scoped?: boolean
module?: string | boolean
}
export interface SFCScriptBlock extends SFCBlock {
type: 'script'
setup?: string | boolean
bindings?: BindingMetadata
imports?: Record<string, ImportBinding>
/**
* import('\@babel/types').Statement
*/
scriptAst?: any[]
/**
* import('\@babel/types').Statement
*/
scriptSetupAst?: any[]
}
export interface SFCDescriptor {
source: string
filename: string
template: SFCBlock | null
script: SFCScriptBlock | null
scriptSetup: SFCScriptBlock | null
styles: SFCBlock[]
customBlocks: SFCCustomBlock[]
cssVars: string[]
errors: (string | WarningMessage)[]
/**
* compare with an existing descriptor to determine whether HMR should perform
* a reload vs. re-render.
*
* Note: this comparison assumes the prev/next script are already identical,
* and only checks the special case where `<script setup lang="ts">` unused
* import pruning result changes due to template changes.
*/
shouldForceReload: (prevImports: Record<string, ImportBinding>) => boolean
}
export interface VueTemplateCompilerParseOptions {
pad?: 'line' | 'space' | boolean
deindent?: boolean
outputSourceRange?: boolean
}
/**
* Parse a single-file component (*.vue) file into an SFC Descriptor Object.
*/
export function parseComponent(
source: string,
options: VueTemplateCompilerParseOptions = {}
): SFCDescriptor {
const sfc: SFCDescriptor = {
source,
filename: DEFAULT_FILENAME,
template: null,
script: null,
scriptSetup: null, // TODO
styles: [],
customBlocks: [],
cssVars: [],
errors: [],
shouldForceReload: null as any // attached in parse() by compiler-sfc
}
let depth = 0
let currentBlock: SFCBlock | null = null
let warn: any = msg => {
sfc.errors.push(msg)
}
if (__DEV__ && options.outputSourceRange) {
warn = (msg, range) => {
const data: WarningMessage = { msg }
if (range.start != null) {
data.start = range.start
}
if (range.end != null) {
data.end = range.end
}
sfc.errors.push(data)
}
}
function start(
tag: string,
attrs: ASTAttr[],
unary: boolean,
start: number,
end: number
) {
if (depth === 0) {
currentBlock = {
type: tag,
content: '',
start: end,
end: 0, // will be set on tag close
attrs: attrs.reduce((cumulated, { name, value }) => {
cumulated[name] = value || true
return cumulated
}, {})
}
if (typeof currentBlock.attrs.src === 'string') {
currentBlock.src = currentBlock.attrs.src
}
if (isSpecialTag(tag)) {
checkAttrs(currentBlock, attrs)
if (tag === 'script') {
const block = currentBlock as SFCScriptBlock
if (block.attrs.setup) {
block.setup = currentBlock.attrs.setup
sfc.scriptSetup = block
} else {
sfc.script = block
}
} else if (tag === 'style') {
sfc.styles.push(currentBlock)
} else {
sfc[tag] = currentBlock
}
} else {
// custom blocks
sfc.customBlocks.push(currentBlock)
}
}
if (!unary) {
depth++
}
}
function checkAttrs(block: SFCBlock, attrs: ASTAttr[]) {
for (let i = 0; i < attrs.length; i++) {
const attr = attrs[i]
if (attr.name === 'lang') {
block.lang = attr.value
}
if (attr.name === 'scoped') {
block.scoped = true
}
if (attr.name === 'module') {
block.module = attr.value || true
}
}
}
function end(tag: string, start: number) {
if (depth === 1 && currentBlock) {
currentBlock.end = start
let text = source.slice(currentBlock.start, currentBlock.end)
if (
options.deindent === true ||
// by default, deindent unless it's script with default lang or (j/t)sx?
(options.deindent !== false &&
!(
currentBlock.type === 'script' &&
(!currentBlock.lang || /^(j|t)sx?$/.test(currentBlock.lang))
))
) {
text = deindent(text)
}
// pad content so that linters and pre-processors can output correct
// line numbers in errors and warnings
if (currentBlock.type !== 'template' && options.pad) {
text = padContent(currentBlock, options.pad) + text
}
currentBlock.content = text
currentBlock = null
}
depth--
}
function padContent(block: SFCBlock, pad: true | 'line' | 'space') {
if (pad === 'space') {
return source.slice(0, block.start).replace(replaceRE, ' ')
} else {
const offset = source.slice(0, block.start).split(splitRE).length
const padChar = block.type === 'script' && !block.lang ? '//\n' : '\n'
return Array(offset).join(padChar)
}
}
parseHTML(source, {
warn,
start,
end,
outputSourceRange: options.outputSourceRange
})
return sfc
}

View File

@ -0,0 +1,82 @@
import MagicString from 'magic-string'
import { parseExpression, ParserOptions, ParserPlugin } from '@babel/parser'
import { makeMap } from 'shared/util'
import { isStaticProperty, walkIdentifiers } from './babelUtils'
import { BindingMetadata } from './types'
const doNotPrefix = makeMap(
'Infinity,undefined,NaN,isFinite,isNaN,' +
'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' +
'require,' + // for webpack
'arguments,' + // parsed as identifier but is a special keyword...
'_c' // cached to save property access
)
/**
* The input is expected to be a valid expression.
*/
export function prefixIdentifiers(
source: string,
isFunctional = false,
isTS = false,
babelOptions: ParserOptions = {},
bindings?: BindingMetadata
) {
const s = new MagicString(source)
const plugins: ParserPlugin[] = [
...(isTS ? (['typescript'] as const) : []),
...(babelOptions?.plugins || [])
]
const ast = parseExpression(source, {
...babelOptions,
plugins
})
const isScriptSetup = bindings && bindings.__isScriptSetup !== false
walkIdentifiers(
ast,
(ident, parent) => {
const { name } = ident
if (doNotPrefix(name)) {
return
}
let prefix = `_vm.`
if (isScriptSetup) {
const type = bindings[name]
if (type && type.startsWith('setup')) {
prefix = `_setup.`
}
}
if (isStaticProperty(parent) && parent.shorthand) {
// property shorthand like { foo }, we need to add the key since
// we rewrite the value
// { foo } -> { foo: _vm.foo }
s.appendLeft(ident.end!, `: ${prefix}${name}`)
} else {
s.prependRight(ident.start!, prefix)
}
},
node => {
if (node.type === 'WithStatement') {
s.remove(node.start!, node.body.start! + 1)
s.remove(node.end! - 1, node.end!)
if (!isFunctional) {
s.prependRight(
node.start!,
`var _vm=this,_c=_vm._self._c${
isScriptSetup ? `,_setup=_vm._self._setupProxy;` : `;`
}`
)
}
}
}
)
return s.toString()
}

View File

@ -0,0 +1,115 @@
import { parse, ParserPlugin } from '@babel/parser'
import MagicString from 'magic-string'
const defaultExportRE = /((?:^|\n|;)\s*)export(\s*)default/
const namedDefaultExportRE = /((?:^|\n|;)\s*)export(.+)(?:as)?(\s*)default/s
const exportDefaultClassRE =
/((?:^|\n|;)\s*)export\s+default\s+class\s+([\w$]+)/
/**
* Utility for rewriting `export default` in a script block into a variable
* declaration so that we can inject things into it
*/
export function rewriteDefault(
input: string,
as: string,
parserPlugins?: ParserPlugin[]
): string {
if (!hasDefaultExport(input)) {
return input + `\nconst ${as} = {}`
}
let replaced: string | undefined
const classMatch = input.match(exportDefaultClassRE)
if (classMatch) {
replaced =
input.replace(exportDefaultClassRE, '$1class $2') +
`\nconst ${as} = ${classMatch[2]}`
} else {
replaced = input.replace(defaultExportRE, `$1const ${as} =`)
}
if (!hasDefaultExport(replaced)) {
return replaced
}
// if the script somehow still contains `default export`, it probably has
// multi-line comments or template strings. fallback to a full parse.
const s = new MagicString(input)
const ast = parse(input, {
sourceType: 'module',
plugins: parserPlugins
}).program.body
ast.forEach(node => {
if (node.type === 'ExportDefaultDeclaration') {
if (node.declaration.type === 'ClassDeclaration') {
s.overwrite(node.start!, node.declaration.id.start!, `class `)
s.append(`\nconst ${as} = ${node.declaration.id.name}`)
} else {
s.overwrite(node.start!, node.declaration.start!, `const ${as} = `)
}
}
if (node.type === 'ExportNamedDeclaration') {
for (const specifier of node.specifiers) {
if (
specifier.type === 'ExportSpecifier' &&
specifier.exported.type === 'Identifier' &&
specifier.exported.name === 'default'
) {
if (node.source) {
if (specifier.local.name === 'default') {
const end = specifierEnd(input, specifier.local.end!, node.end)
s.prepend(
`import { default as __VUE_DEFAULT__ } from '${node.source.value}'\n`
)
s.overwrite(specifier.start!, end, ``)
s.append(`\nconst ${as} = __VUE_DEFAULT__`)
continue
} else {
const end = specifierEnd(input, specifier.exported.end!, node.end)
s.prepend(
`import { ${input.slice(
specifier.local.start!,
specifier.local.end!
)} } from '${node.source.value}'\n`
)
s.overwrite(specifier.start!, end, ``)
s.append(`\nconst ${as} = ${specifier.local.name}`)
continue
}
}
const end = specifierEnd(input, specifier.end!, node.end)
s.overwrite(specifier.start!, end, ``)
s.append(`\nconst ${as} = ${specifier.local.name}`)
}
}
}
})
return s.toString()
}
export function hasDefaultExport(input: string): boolean {
return defaultExportRE.test(input) || namedDefaultExportRE.test(input)
}
function specifierEnd(
input: string,
end: number,
nodeEnd: number | undefined | null
) {
// export { default , foo } ...
let hasCommas = false
let oldEnd = end
while (end < nodeEnd!) {
if (/\s/.test(input.charAt(end))) {
end++
} else if (input.charAt(end) === ',') {
end++
hasCommas = true
break
} else if (input.charAt(end) === '}') {
break
}
}
return hasCommas ? end : oldEnd
}

View File

@ -0,0 +1,203 @@
import { PluginCreator, Rule, AtRule } from 'postcss'
import selectorParser from 'postcss-selector-parser'
const animationNameRE = /^(-\w+-)?animation-name$/
const animationRE = /^(-\w+-)?animation$/
const scopedPlugin: PluginCreator<string> = (id = '') => {
const keyframes = Object.create(null)
const shortId = id.replace(/^data-v-/, '')
return {
postcssPlugin: 'vue-sfc-scoped',
Rule(rule) {
processRule(id, rule)
},
AtRule(node) {
if (
/-?keyframes$/.test(node.name) &&
!node.params.endsWith(`-${shortId}`)
) {
// register keyframes
keyframes[node.params] = node.params = node.params + '-' + shortId
}
},
OnceExit(root) {
if (Object.keys(keyframes).length) {
// If keyframes are found in this <style>, find and rewrite animation names
// in declarations.
// Caveat: this only works for keyframes and animation rules in the same
// <style> element.
// individual animation-name declaration
root.walkDecls(decl => {
if (animationNameRE.test(decl.prop)) {
decl.value = decl.value
.split(',')
.map(v => keyframes[v.trim()] || v.trim())
.join(',')
}
// shorthand
if (animationRE.test(decl.prop)) {
decl.value = decl.value
.split(',')
.map(v => {
const vals = v.trim().split(/\s+/)
const i = vals.findIndex(val => keyframes[val])
if (i !== -1) {
vals.splice(i, 1, keyframes[vals[i]])
return vals.join(' ')
} else {
return v
}
})
.join(',')
}
})
}
}
}
}
const processedRules = new WeakSet<Rule>()
function processRule(id: string, rule: Rule) {
if (
processedRules.has(rule) ||
(rule.parent &&
rule.parent.type === 'atrule' &&
/-?keyframes$/.test((rule.parent as AtRule).name))
) {
return
}
processedRules.add(rule)
rule.selector = selectorParser(selectorRoot => {
selectorRoot.each(selector => {
rewriteSelector(id, selector, selectorRoot)
})
}).processSync(rule.selector)
}
function rewriteSelector(
id: string,
selector: selectorParser.Selector,
selectorRoot: selectorParser.Root
) {
let node: selectorParser.Node | null = null
let shouldInject = true
// find the last child node to insert attribute selector
selector.each(n => {
// DEPRECATED ">>>" and "/deep/" combinator
if (
n.type === 'combinator' &&
(n.value === '>>>' || n.value === '/deep/')
) {
n.value = ' '
n.spaces.before = n.spaces.after = ''
// warn(
// `the >>> and /deep/ combinators have been deprecated. ` +
// `Use :deep() instead.`
// )
return false
}
if (n.type === 'pseudo') {
const { value } = n
// deep: inject [id] attribute at the node before the ::v-deep
// combinator.
if (value === ':deep' || value === '::v-deep') {
if (n.nodes.length) {
// .foo ::v-deep(.bar) -> .foo[xxxxxxx] .bar
// replace the current node with ::v-deep's inner selector
let last: selectorParser.Selector['nodes'][0] = n
n.nodes[0].each(ss => {
selector.insertAfter(last, ss)
last = ss
})
// insert a space combinator before if it doesn't already have one
const prev = selector.at(selector.index(n) - 1)
if (!prev || !isSpaceCombinator(prev)) {
selector.insertAfter(
n,
selectorParser.combinator({
value: ' '
})
)
}
selector.removeChild(n)
} else {
// DEPRECATED usage in v3
// .foo ::v-deep .bar -> .foo[xxxxxxx] .bar
// warn(
// `::v-deep usage as a combinator has ` +
// `been deprecated. Use :deep(<inner-selector>) instead.`
// )
const prev = selector.at(selector.index(n) - 1)
if (prev && isSpaceCombinator(prev)) {
selector.removeChild(prev)
}
selector.removeChild(n)
}
return false
}
// !!! Vue 2 does not have :slotted support
// ::v-slotted(.foo) -> .foo[xxxxxxx-s]
// if (value === ':slotted' || value === '::v-slotted') {
// rewriteSelector(id, n.nodes[0], selectorRoot, true /* slotted */)
// let last: selectorParser.Selector['nodes'][0] = n
// n.nodes[0].each(ss => {
// selector.insertAfter(last, ss)
// last = ss
// })
// // selector.insertAfter(n, n.nodes[0])
// selector.removeChild(n)
// // since slotted attribute already scopes the selector there's no
// // need for the non-slot attribute.
// shouldInject = false
// return false
// }
// global: replace with inner selector and do not inject [id].
// ::v-global(.foo) -> .foo
if (value === ':global' || value === '::v-global') {
selectorRoot.insertAfter(selector, n.nodes[0])
selectorRoot.removeChild(selector)
return false
}
}
if (n.type !== 'pseudo' && n.type !== 'combinator') {
node = n
}
})
if (node) {
;(node as selectorParser.Node).spaces.after = ''
} else {
// For deep selectors & standalone pseudo selectors,
// the attribute selectors are prepended rather than appended.
// So all leading spaces must be eliminated to avoid problems.
selector.first.spaces.before = ''
}
if (shouldInject) {
selector.insertAfter(
// If node is null it means we need to inject [id] at the start
// insertAfter can handle `null` here
node as any,
selectorParser.attribute({
attribute: id,
value: id,
raws: {},
quoteMark: `"`
})
)
}
}
function isSpaceCombinator(node: selectorParser.Node) {
return node.type === 'combinator' && /^\s+$/.test(node.value)
}
scopedPlugin.postcss = true
export default scopedPlugin

View File

@ -0,0 +1,18 @@
import { PluginCreator } from 'postcss'
const trimPlugin: PluginCreator<{}> = () => {
return {
postcssPlugin: 'vue-sfc-trim',
Once(root) {
root.walk(({ type, raws }) => {
if (type === 'rule' || type === 'atrule') {
if (raws.before) raws.before = '\n'
if ('after' in raws && raws.after) raws.after = '\n'
}
})
}
}
}
trimPlugin.postcss = true
export default trimPlugin

View File

@ -0,0 +1,135 @@
import merge from 'merge-source-map'
import { RawSourceMap } from 'source-map'
import { isFunction } from 'shared/util'
export type StylePreprocessor = (
source: string,
map: RawSourceMap | undefined,
options: {
[key: string]: any
additionalData?: string | ((source: string, filename: string) => string)
filename: string
}
) => StylePreprocessorResults
export interface StylePreprocessorResults {
code: string
map?: object
errors: Error[]
dependencies: string[]
}
// .scss/.sass processor
const scss: StylePreprocessor = (source, map, options) => {
const nodeSass = require('sass')
const finalOptions = {
...options,
data: getSource(source, options.filename, options.additionalData),
file: options.filename,
outFile: options.filename,
sourceMap: !!map
}
try {
const result = nodeSass.renderSync(finalOptions)
const dependencies = result.stats.includedFiles
if (map) {
return {
code: result.css.toString(),
map: merge(map, JSON.parse(result.map.toString())),
errors: [],
dependencies
}
}
return { code: result.css.toString(), errors: [], dependencies }
} catch (e: any) {
return { code: '', errors: [e], dependencies: [] }
}
}
const sass: StylePreprocessor = (source, map, options) =>
scss(source, map, {
...options,
indentedSyntax: true
})
// .less
const less: StylePreprocessor = (source, map, options) => {
const nodeLess = require('less')
let result: any
let error: Error | null = null
nodeLess.render(
getSource(source, options.filename, options.additionalData),
{ ...options, syncImport: true },
(err: Error | null, output: any) => {
error = err
result = output
}
)
if (error) return { code: '', errors: [error], dependencies: [] }
const dependencies = result.imports
if (map) {
return {
code: result.css.toString(),
map: merge(map, result.map),
errors: [],
dependencies: dependencies
}
}
return {
code: result.css.toString(),
errors: [],
dependencies: dependencies
}
}
// .styl
const styl: StylePreprocessor = (source, map, options) => {
const nodeStylus = require('stylus')
try {
const ref = nodeStylus(source)
Object.keys(options).forEach(key => ref.set(key, options[key]))
if (map) ref.set('sourcemap', { inline: false, comment: false })
const result = ref.render()
const dependencies = ref.deps()
if (map) {
return {
code: result,
map: merge(map, ref.sourcemap),
errors: [],
dependencies
}
}
return { code: result, errors: [], dependencies }
} catch (e: any) {
return { code: '', errors: [e], dependencies: [] }
}
}
function getSource(
source: string,
filename: string,
additionalData?: string | ((source: string, filename: string) => string)
) {
if (!additionalData) return source
if (isFunction(additionalData)) {
return additionalData(source, filename)
}
return additionalData + source
}
export type PreprocessLang = 'less' | 'sass' | 'scss' | 'styl' | 'stylus'
export const processors: Record<PreprocessLang, StylePreprocessor> = {
less,
sass,
scss,
styl,
stylus: styl
}

View File

@ -0,0 +1,84 @@
// vue compiler module for transforming `<tag>:<attribute>` to `require`
import { urlToRequire } from './utils'
import { ASTNode, ASTAttr } from 'types/compiler'
export interface AssetURLOptions {
[name: string]: string | string[]
}
export interface TransformAssetUrlsOptions {
/**
* If base is provided, instead of transforming relative asset urls into
* imports, they will be directly rewritten to absolute urls.
*/
base?: string
/**
* If true, also processes absolute urls.
*/
includeAbsolute?: boolean
}
const defaultOptions: AssetURLOptions = {
audio: 'src',
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: ['xlink:href', 'href'],
use: ['xlink:href', 'href']
}
export default (
userOptions?: AssetURLOptions,
transformAssetUrlsOption?: TransformAssetUrlsOptions
) => {
const options = userOptions
? Object.assign({}, defaultOptions, userOptions)
: defaultOptions
return {
postTransformNode: (node: ASTNode) => {
transform(node, options, transformAssetUrlsOption)
}
}
}
function transform(
node: ASTNode,
options: AssetURLOptions,
transformAssetUrlsOption?: TransformAssetUrlsOptions
) {
if (node.type !== 1 || !node.attrs) return
for (const tag in options) {
if (tag === '*' || node.tag === tag) {
const attributes = options[tag]
if (typeof attributes === 'string') {
node.attrs!.some(attr =>
rewrite(attr, attributes, transformAssetUrlsOption)
)
} else if (Array.isArray(attributes)) {
attributes.forEach(item =>
node.attrs!.some(attr =>
rewrite(attr, item, transformAssetUrlsOption)
)
)
}
}
}
}
function rewrite(
attr: ASTAttr,
name: string,
transformAssetUrlsOption?: TransformAssetUrlsOptions
) {
if (attr.name === name) {
const value = attr.value
// only transform static URLs
if (value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') {
attr.value = urlToRequire(value.slice(1, -1), transformAssetUrlsOption)
return true
}
}
return false
}

View File

@ -0,0 +1,76 @@
// vue compiler module for transforming `img:srcset` to a number of `require`s
import { urlToRequire } from './utils'
import { TransformAssetUrlsOptions } from './assetUrl'
import { ASTNode } from 'types/compiler'
interface ImageCandidate {
require: string
descriptor: string
}
export default (transformAssetUrlsOptions?: TransformAssetUrlsOptions) => ({
postTransformNode: (node: ASTNode) => {
transform(node, transformAssetUrlsOptions)
}
})
// http://w3c.github.io/html/semantics-embedded-content.html#ref-for-image-candidate-string-5
const escapedSpaceCharacters = /( |\\t|\\n|\\f|\\r)+/g
function transform(
node: ASTNode,
transformAssetUrlsOptions?: TransformAssetUrlsOptions
) {
if (node.type !== 1 || !node.attrs) {
return
}
if (node.tag === 'img' || node.tag === 'source') {
node.attrs.forEach(attr => {
if (attr.name === 'srcset') {
// same logic as in transform-require.js
const value = attr.value
const isStatic =
value.charAt(0) === '"' && value.charAt(value.length - 1) === '"'
if (!isStatic) {
return
}
const imageCandidates: ImageCandidate[] = value
.slice(1, -1)
.split(',')
.map(s => {
// The attribute value arrives here with all whitespace, except
// normal spaces, represented by escape sequences
const [url, descriptor] = s
.replace(escapedSpaceCharacters, ' ')
.trim()
.split(' ', 2)
return {
require: urlToRequire(url, transformAssetUrlsOptions),
descriptor
}
})
// "require(url1)"
// "require(url1) 1x"
// "require(url1), require(url2)"
// "require(url1), require(url2) 2x"
// "require(url1) 1x, require(url2)"
// "require(url1) 1x, require(url2) 2x"
const code = imageCandidates
.map(
({ require, descriptor }) =>
`${require} + "${descriptor ? ' ' + descriptor : ''}, " + `
)
.join('')
.slice(0, -6)
.concat('"')
.replace(/ \+ ""$/, '')
attr.value = code
}
})
}
}

View File

@ -0,0 +1,86 @@
import { TransformAssetUrlsOptions } from './assetUrl'
import { UrlWithStringQuery, parse as uriParse } from 'url'
import path from 'path'
export function urlToRequire(
url: string,
transformAssetUrlsOption: TransformAssetUrlsOptions = {}
): string {
const returnValue = `"${url}"`
// same logic as in transform-require.js
const firstChar = url.charAt(0)
if (firstChar === '~') {
const secondChar = url.charAt(1)
url = url.slice(secondChar === '/' ? 2 : 1)
}
if (isExternalUrl(url) || isDataUrl(url) || firstChar === '#') {
return returnValue
}
const uriParts = parseUriParts(url)
if (transformAssetUrlsOption.base) {
// explicit base - directly rewrite the url into absolute url
// does not apply to absolute urls or urls that start with `@`
// since they are aliases
if (firstChar === '.' || firstChar === '~') {
// Allow for full hostnames provided in options.base
const base = parseUriParts(transformAssetUrlsOption.base)
const protocol = base.protocol || ''
const host = base.host ? protocol + '//' + base.host : ''
const basePath = base.path || '/'
// when packaged in the browser, path will be using the posix-
// only version provided by rollup-plugin-node-builtins.
return `"${host}${(path.posix || path).join(
basePath,
uriParts.path + (uriParts.hash || '')
)}"`
}
}
if (
transformAssetUrlsOption.includeAbsolute ||
firstChar === '.' ||
firstChar === '~' ||
firstChar === '@'
) {
if (!uriParts.hash) {
return `require("${url}")`
} else {
// support uri fragment case by excluding it from
// the require and instead appending it as string;
// assuming that the path part is sufficient according to
// the above caseing(t.i. no protocol-auth-host parts expected)
return `require("${uriParts.path}") + "${uriParts.hash}"`
}
}
return returnValue
}
/**
* vuejs/component-compiler-utils#22 Support uri fragment in transformed require
* @param urlString an url as a string
*/
function parseUriParts(urlString: string): UrlWithStringQuery {
// initialize return value
const returnValue: UrlWithStringQuery = uriParse('')
if (urlString) {
// A TypeError is thrown if urlString is not a string
// @see https://nodejs.org/api/url.html#url_url_parse_urlstring_parsequerystring_slashesdenotehost
if ('string' === typeof urlString) {
// check is an uri
return uriParse(urlString, false, true) // take apart the uri
}
}
return returnValue
}
const externalRE = /^(https?:)?\/\//
function isExternalUrl(url: string): boolean {
return externalRE.test(url)
}
const dataUrlRE = /^\s*data:/i
function isDataUrl(url: string): boolean {
return dataUrlRE.test(url)
}

69
node_modules/vue/packages/compiler-sfc/src/types.ts generated vendored Normal file
View File

@ -0,0 +1,69 @@
import { CompilerOptions, CompiledResult } from 'types/compiler'
import { SFCDescriptor } from './parseComponent'
export interface StartOfSourceMap {
file?: string
sourceRoot?: string
}
export interface RawSourceMap extends StartOfSourceMap {
version: string
sources: string[]
names: string[]
sourcesContent?: string[]
mappings: string
}
export interface TemplateCompiler {
parseComponent(source: string, options?: any): SFCDescriptor
compile(template: string, options: CompilerOptions): CompiledResult
ssrCompile(template: string, options: CompilerOptions): CompiledResult
}
export const enum BindingTypes {
/**
* returned from data()
*/
DATA = 'data',
/**
* declared as a prop
*/
PROPS = 'props',
/**
* a local alias of a `<script setup>` destructured prop.
* the original is stored in __propsAliases of the bindingMetadata object.
*/
PROPS_ALIASED = 'props-aliased',
/**
* a let binding (may or may not be a ref)
*/
SETUP_LET = 'setup-let',
/**
* a const binding that can never be a ref.
* these bindings don't need `unref()` calls when processed in inlined
* template expressions.
*/
SETUP_CONST = 'setup-const',
/**
* a const binding that does not need `unref()`, but may be mutated.
*/
SETUP_REACTIVE_CONST = 'setup-reactive-const',
/**
* a const binding that may be a ref.
*/
SETUP_MAYBE_REF = 'setup-maybe-ref',
/**
* bindings that are guaranteed to be refs
*/
SETUP_REF = 'setup-ref',
/**
* declared by other options, e.g. computed, inject
*/
OPTIONS = 'options'
}
export type BindingMetadata = {
[key: string]: BindingTypes | undefined
} & {
__isScriptSetup?: boolean
}

16
node_modules/vue/packages/compiler-sfc/src/warn.ts generated vendored Normal file
View File

@ -0,0 +1,16 @@
const hasWarned: Record<string, boolean> = {}
export function warnOnce(msg: string) {
const isNodeProd =
typeof process !== 'undefined' && process.env.NODE_ENV === 'production'
if (!isNodeProd && !hasWarned[msg]) {
hasWarned[msg] = true
warn(msg)
}
}
export function warn(msg: string) {
console.warn(
`\x1b[1m\x1b[33m[@vue/compiler-sfc]\x1b[0m\x1b[33m ${msg}\x1b[0m\n`
)
}

View File

@ -0,0 +1,925 @@
// Vitest Snapshot v1
exports[`SFC analyze <script> bindings > auto name inference > basic 1`] = `
"export default {
__name: 'FooBar',
setup(__props) {
const a = 1
return { a }
}
}"
`;
exports[`SFC analyze <script> bindings > auto name inference > do not overwrite manual name (call) 1`] = `
"import { defineComponent } from 'vue'
const __default__ = defineComponent({
name: 'Baz'
})
export default /*#__PURE__*/Object.assign(__default__, {
setup(__props) {
const a = 1
return { a }
}
})"
`;
exports[`SFC analyze <script> bindings > auto name inference > do not overwrite manual name (object) 1`] = `
"const __default__ = {
name: 'Baz'
}
export default /*#__PURE__*/Object.assign(__default__, {
setup(__props) {
const a = 1
return { a }
}
})"
`;
exports[`SFC compile <script setup> > <script> after <script setup> the script content not end with \`\\n\` 1`] = `
"const n = 1
import { x } from './x'
export default {
setup(__props) {
return { n, x }
}
}"
`;
exports[`SFC compile <script setup> > <script> and <script setup> co-usage > script first 1`] = `
"import { x } from './x'
export const n = 1
const __default__ = {}
export default /*#__PURE__*/Object.assign(__default__, {
setup(__props) {
x()
return { n, x }
}
})"
`;
exports[`SFC compile <script setup> > <script> and <script setup> co-usage > script setup first 1`] = `
"export const n = 1
const __default__ = {}
import { x } from './x'
export default /*#__PURE__*/Object.assign(__default__, {
setup(__props) {
x()
return { n, x }
}
})"
`;
exports[`SFC compile <script setup> > <script> and <script setup> co-usage > script setup first, lang="ts", script block content export default 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
const __default__ = {
name: \\"test\\"
}
import { x } from './x'
export default /*#__PURE__*/_defineComponent({
...__default__,
setup(__props) {
x()
return { x }
}
})"
`;
exports[`SFC compile <script setup> > <script> and <script setup> co-usage > script setup first, named default export 1`] = `
"export const n = 1
const def = {}
const __default__ = def
import { x } from './x'
export default /*#__PURE__*/Object.assign(__default__, {
setup(__props) {
x()
return { n, def, x }
}
})"
`;
exports[`SFC compile <script setup> > <script> and <script setup> co-usage > spaces in ExportDefaultDeclaration node > with many spaces and newline 1`] = `
"import { x } from './x'
export const n = 1
const __default__ = {
some:'option'
}
export default /*#__PURE__*/Object.assign(__default__, {
setup(__props) {
x()
return { n, x }
}
})"
`;
exports[`SFC compile <script setup> > <script> and <script setup> co-usage > spaces in ExportDefaultDeclaration node > with minimal spaces 1`] = `
"import { x } from './x'
export const n = 1
const __default__ = {
some:'option'
}
export default /*#__PURE__*/Object.assign(__default__, {
setup(__props) {
x()
return { n, x }
}
})"
`;
exports[`SFC compile <script setup> > binding analysis for destructure 1`] = `
"export default {
setup(__props) {
const { foo, b: bar, ['x' + 'y']: baz, x: { y, zz: { z }}} = {}
return { foo, bar, baz, y, z }
}
}"
`;
exports[`SFC compile <script setup> > defineEmits() 1`] = `
"export default {
emits: ['foo', 'bar'],
setup(__props, { emit: myEmit }) {
return { myEmit }
}
}"
`;
exports[`SFC compile <script setup> > defineExpose() 1`] = `
"export default {
setup(__props, { expose }) {
expose({ foo: 123 })
return { }
}
}"
`;
exports[`SFC compile <script setup> > defineProps w/ external definition 1`] = `
"import { propsModel } from './props'
export default {
props: propsModel,
setup(__props) {
const props = __props
return { props, propsModel }
}
}"
`;
exports[`SFC compile <script setup> > defineProps w/ leading code 1`] = `
"import { x } from './x'
export default {
props: {},
setup(__props) {
const props = __props
return { props, x }
}
}"
`;
exports[`SFC compile <script setup> > defineProps() 1`] = `
"export default {
props: {
foo: String
},
setup(__props) {
const props = __props
const bar = 1
return { props, bar }
}
}"
`;
exports[`SFC compile <script setup> > defineProps/defineEmits in multi-variable declaration (full removal) 1`] = `
"export default {
props: ['item'],
emits: ['a'],
setup(__props, { emit }) {
const props = __props
return { props, emit }
}
}"
`;
exports[`SFC compile <script setup> > defineProps/defineEmits in multi-variable declaration 1`] = `
"export default {
props: ['item'],
emits: ['a'],
setup(__props, { emit }) {
const props = __props
const a = 1;
return { props, a, emit }
}
}"
`;
exports[`SFC compile <script setup> > dev mode import usage check > TS annotations 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { Foo, Baz, Qux, Fred } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
const a = 1
function b() {}
return { a, b, Baz }
}
})"
`;
exports[`SFC compile <script setup> > dev mode import usage check > attribute expressions 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { bar, baz } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
const cond = true
return { cond, bar, baz }
}
})"
`;
exports[`SFC compile <script setup> > dev mode import usage check > components 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { FooBar, FooBaz, FooQux, foo } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
const fooBar: FooBar = 1
return { fooBar, FooBaz, FooQux, foo }
}
})"
`;
exports[`SFC compile <script setup> > dev mode import usage check > directive 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { vMyDir } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { vMyDir }
}
})"
`;
exports[`SFC compile <script setup> > dev mode import usage check > js template string interpolations 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { VAR, VAR2, VAR3 } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { VAR, VAR3 }
}
})"
`;
exports[`SFC compile <script setup> > dev mode import usage check > last tag 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { FooBaz, Last } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { FooBaz, Last }
}
})"
`;
exports[`SFC compile <script setup> > dev mode import usage check > vue interpolations 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { x, y, z, x$y } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { x, z, x$y }
}
})"
`;
exports[`SFC compile <script setup> > errors > should allow defineProps/Emit() referencing imported binding 1`] = `
"import { bar } from './bar'
export default {
props: {
foo: {
default: () => bar
}
},
emits: {
foo: () => bar > 1
},
setup(__props) {
return { bar }
}
}"
`;
exports[`SFC compile <script setup> > errors > should allow defineProps/Emit() referencing scope var 1`] = `
"export default {
props: {
foo: {
default: bar => bar + 1
}
},
emits: {
foo: bar => bar > 1
},
setup(__props) {
const bar = 1
return { bar }
}
}"
`;
exports[`SFC compile <script setup> > imports > import dedupe between <script> and <script setup> 1`] = `
"import { x } from './x'
export default {
setup(__props) {
x()
return { x }
}
}"
`;
exports[`SFC compile <script setup> > imports > should allow defineProps/Emit at the start of imports 1`] = `
"import { ref } from 'vue'
export default {
props: ['foo'],
emits: ['bar'],
setup(__props) {
const r = ref(0)
return { r, ref }
}
}"
`;
exports[`SFC compile <script setup> > imports > should extract comment for import or type declarations 1`] = `
"import a from 'a' // comment
import b from 'b'
export default {
setup(__props) {
return { a, b }
}
}"
`;
exports[`SFC compile <script setup> > imports > should hoist and expose imports 1`] = `
"import { ref } from 'vue'
import 'foo/css'
export default {
setup(__props) {
return { ref }
}
}"
`;
exports[`SFC compile <script setup> > should expose top level declarations 1`] = `
"import { xx } from './x'
let aa = 1
const bb = 2
function cc() {}
class dd {}
import { x } from './x'
export default {
setup(__props) {
let a = 1
const b = 2
function c() {}
class d {}
return { aa, bb, cc, dd, a, b, c, d, xx, x }
}
}"
`;
exports[`SFC compile <script setup> > with TypeScript > const Enum 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
const enum Foo { A = 123 }
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { Foo }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (exported interface) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export interface Emits { (e: 'foo' | 'bar'): void }
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\"],
setup(__props, { emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (exported type alias) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export type Emits = { (e: 'foo' | 'bar'): void }
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\"],
setup(__props, { emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (interface) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
interface Emits { (e: 'foo' | 'bar'): void }
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\"],
setup(__props, { emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (referenced exported function type) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export type Emits = (e: 'foo' | 'bar') => void
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\"],
setup(__props, { emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (referenced function type) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
type Emits = (e: 'foo' | 'bar') => void
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\"],
setup(__props, { emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (type alias) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
type Emits = { (e: 'foo' | 'bar'): void }
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\"],
setup(__props, { emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (type literal w/ call signatures) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\", \\"baz\\"],
setup(__props, { emit }: { emit: ({(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\"],
setup(__props, { emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineProps w/ exported interface 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export interface Props { x?: number }
export default /*#__PURE__*/_defineComponent({
props: {
x: { type: Number, required: false }
},
setup(__props: any) {
return { }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineProps w/ exported interface in normal script 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export interface Props { x?: number }
export default /*#__PURE__*/_defineComponent({
props: {
x: { type: Number, required: false }
},
setup(__props: any) {
return { }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineProps w/ exported type alias 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export type Props = { x?: number }
export default /*#__PURE__*/_defineComponent({
props: {
x: { type: Number, required: false }
},
setup(__props: any) {
return { }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineProps w/ interface 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
interface Props { x?: number }
export default /*#__PURE__*/_defineComponent({
props: {
x: { type: Number, required: false }
},
setup(__props: any) {
return { }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineProps w/ type 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
interface Test {}
type Alias = number[]
export default /*#__PURE__*/_defineComponent({
props: {
string: { type: String, required: true },
number: { type: Number, required: true },
boolean: { type: Boolean, required: true },
object: { type: Object, required: true },
objectLiteral: { type: Object, required: true },
fn: { type: Function, required: true },
functionRef: { type: Function, required: true },
objectRef: { type: Object, required: true },
dateTime: { type: Date, required: true },
array: { type: Array, required: true },
arrayRef: { type: Array, required: true },
tuple: { type: Array, required: true },
set: { type: Set, required: true },
literal: { type: String, required: true },
optional: { type: null, required: false },
recordRef: { type: Object, required: true },
interface: { type: Object, required: true },
alias: { type: Array, required: true },
method: { type: Function, required: true },
symbol: { type: Symbol, required: true },
union: { type: [String, Number], required: true },
literalUnion: { type: String, required: true },
literalUnionNumber: { type: Number, required: true },
literalUnionMixed: { type: [String, Number, Boolean], required: true },
intersection: { type: Object, required: true },
foo: { type: [Function, null], required: true }
},
setup(__props: any) {
return { }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineProps w/ type alias 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
type Props = { x?: number }
export default /*#__PURE__*/_defineComponent({
props: {
x: { type: Number, required: false }
},
setup(__props: any) {
return { }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineProps/Emit w/ runtime options 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export default /*#__PURE__*/_defineComponent({
props: { foo: String },
emits: ['a', 'b'],
setup(__props, { emit }) {
const props = __props
return { props, emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > hoist type declarations 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export interface Foo {}
type Bar = {}
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > import type 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import type { Foo } from './main.ts'
import { type Bar, Baz } from './main.ts'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { Baz }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > runtime Enum 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
enum Foo { A = 123 }
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { Foo }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > runtime Enum in normal script 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
enum Foo { A = 123 }
export enum D { D = \\"D\\" }
const enum C { C = \\"C\\" }
enum B { B = \\"B\\" }
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { D, C, B, Foo }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > withDefaults (dynamic) 1`] = `
"import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from 'vue'
import { defaults } from './foo'
export default /*#__PURE__*/_defineComponent({
props: _mergeDefaults({
foo: { type: String, required: false },
bar: { type: Number, required: false },
baz: { type: Boolean, required: true }
}, { ...defaults }),
setup(__props: any) {
const props = __props as {
foo?: string
bar?: number
baz: boolean
}
return { props, defaults }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > withDefaults (static) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export default /*#__PURE__*/_defineComponent({
props: {
foo: { type: String, required: false, default: 'hi' },
bar: { type: Number, required: false },
baz: { type: Boolean, required: true },
qux: { type: Function, required: false, default() { return 1 } }
},
setup(__props: any) {
const props = __props as { foo: string, bar?: number, baz: boolean, qux(): number }
return { props }
}
})"
`;

View File

@ -0,0 +1,189 @@
// Vitest Snapshot v1
exports[`CSS vars injection > codegen > <script> w/ default export 1`] = `
"const __default__ = { setup() {} }
import { useCssVars as _useCssVars } from 'vue'
const __injectCSSVars__ = () => {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-color\\": (_vm.color)
}))}
const __setup__ = __default__.setup
__default__.setup = __setup__
? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
: __injectCSSVars__
export default __default__"
`;
exports[`CSS vars injection > codegen > <script> w/ default export in strings/comments 1`] = `
"
// export default {}
const __default__ = {}
import { useCssVars as _useCssVars } from 'vue'
const __injectCSSVars__ = () => {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-color\\": (_vm.color)
}))}
const __setup__ = __default__.setup
__default__.setup = __setup__
? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
: __injectCSSVars__
export default __default__"
`;
exports[`CSS vars injection > codegen > <script> w/ no default export 1`] = `
"const a = 1
const __default__ = {}
import { useCssVars as _useCssVars } from 'vue'
const __injectCSSVars__ = () => {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-color\\": (_vm.color)
}))}
const __setup__ = __default__.setup
__default__.setup = __setup__
? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
: __injectCSSVars__
export default __default__"
`;
exports[`CSS vars injection > codegen > should ignore comments 1`] = `
"import { useCssVars as _useCssVars } from 'vue'
export default {
setup(__props) {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-width\\": (_setup.width)
}))
const color = 'red';const width = 100
return { color, width }
}
}"
`;
exports[`CSS vars injection > codegen > should work with w/ complex expression 1`] = `
"import { useCssVars as _useCssVars } from 'vue'
export default {
setup(__props) {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-foo\\": (_setup.foo),
\\"xxxxxxxx-foo____px_\\": (_setup.foo + 'px'),
\\"xxxxxxxx-_a___b____2____px_\\": ((_setup.a + _setup.b) / 2 + 'px'),
\\"xxxxxxxx-__a___b______2___a_\\": (((_setup.a + _setup.b)) / (2 * _setup.a))
}))
let a = 100
let b = 200
let foo = 300
return { a, b, foo }
}
}"
`;
exports[`CSS vars injection > codegen > w/ <script setup> 1`] = `
"import { useCssVars as _useCssVars } from 'vue'
export default {
setup(__props) {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-color\\": (_setup.color)
}))
const color = 'red'
return { color }
}
}"
`;
exports[`CSS vars injection > codegen > w/ <script setup> using the same var multiple times 1`] = `
"import { useCssVars as _useCssVars } from 'vue'
export default {
setup(__props) {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-color\\": (_setup.color)
}))
const color = 'red'
return { color }
}
}"
`;
exports[`CSS vars injection > generating correct code for nested paths 1`] = `
"const a = 1
const __default__ = {}
import { useCssVars as _useCssVars } from 'vue'
const __injectCSSVars__ = () => {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-color\\": (_vm.color),
\\"xxxxxxxx-font_size\\": (_vm.font.size)
}))}
const __setup__ = __default__.setup
__default__.setup = __setup__
? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
: __injectCSSVars__
export default __default__"
`;
exports[`CSS vars injection > w/ <script setup> binding analysis 1`] = `
"import { useCssVars as _useCssVars } from 'vue'
import { ref } from 'vue'
export default {
props: {
foo: String
},
setup(__props) {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-color\\": (_setup.color),
\\"xxxxxxxx-size\\": (_setup.size),
\\"xxxxxxxx-foo\\": (_vm.foo)
}))
const color = 'red'
const size = ref('10px')
return { color, size, ref }
}
}"
`;
exports[`CSS vars injection > w/ normal <script> binding analysis 1`] = `
"
const __default__ = {
setup() {
return {
size: ref('100px')
}
}
}
import { useCssVars as _useCssVars } from 'vue'
const __injectCSSVars__ = () => {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-size\\": (_vm.size)
}))}
const __setup__ = __default__.setup
__default__.setup = __setup__
? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
: __injectCSSVars__
export default __default__"
`;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,203 @@
import { parse } from '../src/parse'
import { compileStyle, compileStyleAsync } from '../src/compileStyle'
test('preprocess less', () => {
const style = parse({
source:
'<style lang="less">\n' +
'@red: rgb(255, 0, 0);\n' +
'.color { color: @red; }\n' +
'</style>\n',
filename: 'example.vue',
sourceMap: true
}).styles[0]
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: style.content,
map: style.map,
scoped: false,
preprocessLang: style.lang
})
expect(result.errors.length).toBe(0)
expect(result.code).toEqual(expect.stringContaining('color: #ff0000;'))
expect(result.map).toBeTruthy()
})
test('preprocess scss', () => {
const style = parse({
source:
'<style lang="scss">\n' +
'$red: red;\n' +
'.color { color: $red; }\n' +
'</style>\n',
filename: 'example.vue',
sourceMap: true
}).styles[0]
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: style.content,
map: style.map,
scoped: false,
preprocessLang: style.lang
})
expect(result.errors.length).toBe(0)
expect(result.code).toMatch('color: red;')
expect(result.map).toBeTruthy()
})
test('preprocess sass', () => {
const style = parse({
source:
'<style lang="sass">\n' +
'$red: red\n' +
'.color\n' +
' color: $red\n' +
'</style>\n',
filename: 'example.vue',
sourceMap: true
}).styles[0]
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: style.content,
map: style.map,
scoped: false,
preprocessLang: style.lang
})
expect(result.errors.length).toBe(0)
expect(result.code).toMatch('color: red;')
expect(result.map).toBeTruthy()
})
test('preprocess stylus', () => {
const style = parse({
source:
'<style lang="styl">\n' +
'red-color = rgb(255, 0, 0);\n' +
'.color\n' +
' color: red-color\n' +
'</style>\n',
filename: 'example.vue',
sourceMap: true
}).styles[0]
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: style.content,
map: style.map,
scoped: false,
preprocessLang: style.lang
})
expect(result.errors.length).toBe(0)
expect(result.code).toEqual(expect.stringContaining('color: #f00;'))
expect(result.map).toBeTruthy()
})
test('custom postcss plugin', () => {
const spy = vi.fn()
compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: '.foo { color: red }',
scoped: false,
postcssPlugins: [require('postcss').plugin('test-plugin', () => spy)()]
})
expect(spy).toHaveBeenCalled()
})
test('custom postcss options', () => {
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: '.foo { color: red }',
scoped: false,
postcssOptions: { random: 'foo' }
})
expect((result.rawResult as any).opts.random).toBe('foo')
})
test('async postcss plugin in sync mode', () => {
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: '.foo { color: red }',
scoped: false,
postcssPlugins: [
require('postcss').plugin(
'test-plugin',
() => async (result: any) => result
)
]
})
expect(result.errors).toHaveLength(1)
})
test('async postcss plugin', async () => {
const promise = compileStyleAsync({
id: 'v-scope-xxx',
filename: 'example.vue',
source: '.foo { color: red }',
scoped: false,
postcssPlugins: [
require('postcss').plugin(
'test-plugin',
() => async (result: any) => result
)
]
})
expect(promise instanceof Promise).toBe(true)
const result = await promise
expect(result.errors).toHaveLength(0)
expect(result.code).toEqual(expect.stringContaining('color: red'))
})
test('media query', () => {
const result = compileStyle({
id: 'v-scope-xxx',
scoped: true,
filename: 'example.vue',
source: `
@media print {
.foo {
color: #000;
}
}`
})
expect(result.errors).toHaveLength(0)
expect(result.code).toContain(
'@media print {\n.foo[v-scope-xxx] {\n color: #000;\n}\n}'
)
})
test('supports query', () => {
const result = compileStyle({
id: 'v-scope-xxx',
scoped: true,
filename: 'example.vue',
source: `
@supports ( color: #000 ) {
.foo {
color: #000;
}
}`
})
expect(result.errors).toHaveLength(0)
expect(result.code).toContain(
'@supports ( color: #000 ) {\n.foo[v-scope-xxx] {\n color: #000;\n}\n}'
)
})

View File

@ -0,0 +1,258 @@
import { parse } from '../src/parse'
import { SFCBlock } from '../src/parseComponent'
import { compileTemplate } from '../src/compileTemplate'
import Vue from 'vue'
function mockRender(code: string, mocks: Record<string, any> = {}) {
const fn = new Function(
`require`,
`${code}; return { render, staticRenderFns }`
)
const vm = new Vue(
Object.assign(
{},
fn((id: string) => mocks[id])
)
)
vm.$mount()
return (vm as any)._vnode
}
test('should work', () => {
const source = `<div><p>{{ render }}</p></div>`
const result = compileTemplate({
filename: 'example.vue',
source
})
expect(result.errors.length).toBe(0)
expect(result.source).toBe(source)
// should expose render fns
expect(result.code).toMatch(`var render = function`)
expect(result.code).toMatch(`var staticRenderFns = []`)
// should mark with stripped
expect(result.code).toMatch(`render._withStripped = true`)
// should prefix bindings
expect(result.code).toMatch(`_vm.render`)
expect(result.ast).not.toBeUndefined()
})
test('preprocess pug', () => {
const template = parse({
source:
'<template lang="pug">\n' +
'body\n' +
' h1 Pug Examples\n' +
' div.container\n' +
' p Cool Pug example!\n' +
'</template>\n',
filename: 'example.vue',
sourceMap: true
}).template as SFCBlock
const result = compileTemplate({
filename: 'example.vue',
source: template.content,
preprocessLang: template.lang
})
expect(result.errors.length).toBe(0)
})
/**
* vuejs/component-compiler-utils#22 Support uri fragment in transformed require
*/
test('supports uri fragment in transformed require', () => {
const source = '<svg>\
<use href="~@svg/file.svg#fragment"></use>\
</svg>' //
const result = compileTemplate({
filename: 'svgparticle.html',
source: source,
transformAssetUrls: {
use: 'href'
}
})
expect(result.errors.length).toBe(0)
expect(result.code).toMatch(
/href: require\("@svg\/file.svg"\) \+ "#fragment"/
)
})
/**
* vuejs/component-compiler-utils#22 Support uri fragment in transformed require
*/
test('when too short uri then empty require', () => {
const source = '<svg>\
<use href="~"></use>\
</svg>' //
const result = compileTemplate({
filename: 'svgparticle.html',
source: source,
transformAssetUrls: {
use: 'href'
}
})
expect(result.errors.length).toBe(0)
expect(result.code).toMatch(/href: require\(""\)/)
})
test('warn missing preprocessor', () => {
const template = parse({
source: '<template lang="unknownLang">\n' + '</template>\n',
filename: 'example.vue',
sourceMap: true
}).template as SFCBlock
const result = compileTemplate({
filename: 'example.vue',
source: template.content,
preprocessLang: template.lang
})
expect(result.errors.length).toBe(1)
})
test('transform assetUrls', () => {
const source = `
<div>
<img src="./logo.png">
<img src="~fixtures/logo.png">
<img src="~/fixtures/logo.png">
</div>
`
const result = compileTemplate({
filename: 'example.vue',
source,
transformAssetUrls: true
})
expect(result.errors.length).toBe(0)
const vnode = mockRender(result.code, {
'./logo.png': 'a',
'fixtures/logo.png': 'b'
})
expect(vnode.children[0].data.attrs.src).toBe('a')
expect(vnode.children[2].data.attrs.src).toBe('b')
expect(vnode.children[4].data.attrs.src).toBe('b')
})
test('transform srcset', () => {
const source = `
<div>
<img src="./logo.png">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<image xlink:href="./logo.png" />
</svg>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<use xlink:href="./logo.png"/>
</svg>
</svg>
<img src="./logo.png" srcset="./logo.png">
<img src="./logo.png" srcset="./logo.png 2x">
<img src="./logo.png" srcset="./logo.png, ./logo.png 2x">
<img src="./logo.png" srcset="./logo.png 2x, ./logo.png">
<img src="./logo.png" srcset="./logo.png 2x, ./logo.png 3x">
<img src="./logo.png" srcset="./logo.png, ./logo.png 2x, ./logo.png 3x">
<img
src="./logo.png"
srcset="
./logo.png 2x,
./logo.png 3x
">
</div>
`
const result = compileTemplate({
filename: 'example.vue',
source,
transformAssetUrls: true
})
expect(result.errors.length).toBe(0)
const vnode = mockRender(result.code, {
'./logo.png': 'test-url'
})
// img tag
expect(vnode.children[0].data.attrs.src).toBe('test-url')
// image tag (SVG)
expect(vnode.children[2].children[0].data.attrs['xlink:href']).toBe(
'test-url'
)
// use tag (SVG)
expect(vnode.children[4].children[0].data.attrs['xlink:href']).toBe(
'test-url'
)
// image tag with srcset
expect(vnode.children[6].data.attrs.srcset).toBe('test-url')
expect(vnode.children[8].data.attrs.srcset).toBe('test-url 2x')
// image tag with multiline srcset
expect(vnode.children[10].data.attrs.srcset).toBe('test-url, test-url 2x')
expect(vnode.children[12].data.attrs.srcset).toBe('test-url 2x, test-url')
expect(vnode.children[14].data.attrs.srcset).toBe('test-url 2x, test-url 3x')
expect(vnode.children[16].data.attrs.srcset).toBe(
'test-url, test-url 2x, test-url 3x'
)
expect(vnode.children[18].data.attrs.srcset).toBe('test-url 2x, test-url 3x')
})
test('transform assetUrls and srcset with base option', () => {
const source = `
<div>
<img src="./logo.png">
<img src="~fixtures/logo.png">
<img src="~/fixtures/logo.png">
<img src="./logo.png" srcset="./logo.png 2x, ./logo.png 3x">
<img src="@/fixtures/logo.png">
</div>
`
const result = compileTemplate({
filename: 'example.vue',
source,
transformAssetUrls: true,
transformAssetUrlsOptions: { base: '/base/' }
})
expect(result.errors.length).toBe(0)
const vnode = mockRender(result.code, {
'@/fixtures/logo.png': 'aliased'
})
expect(vnode.children[0].data.attrs.src).toBe('/base/logo.png')
expect(vnode.children[2].data.attrs.src).toBe('/base/fixtures/logo.png')
expect(vnode.children[4].data.attrs.src).toBe('/base/fixtures/logo.png')
expect(vnode.children[6].data.attrs.srcset).toBe(
'/base/logo.png 2x, /base/logo.png 3x'
)
expect(vnode.children[8].data.attrs.src).toBe('aliased')
})
test('transform with includeAbsolute', () => {
const source = `
<div>
<img src="./logo.png">
<img src="/logo.png">
<img src="https://foo.com/logo.png">
</div>
`
const result = compileTemplate({
filename: 'example.vue',
source,
transformAssetUrls: true,
transformAssetUrlsOptions: { includeAbsolute: true }
})
expect(result.errors.length).toBe(0)
const vnode = mockRender(result.code, {
'./logo.png': 'relative',
'/logo.png': 'absolute'
})
expect(vnode.children[0].data.attrs.src).toBe('relative')
expect(vnode.children[2].data.attrs.src).toBe('absolute')
expect(vnode.children[4].data.attrs.src).toBe('https://foo.com/logo.png')
})

View File

@ -0,0 +1,247 @@
import { compileStyle, parse } from '../src'
import { mockId, compile, assertCode } from './util'
describe('CSS vars injection', () => {
test('generating correct code for nested paths', () => {
const { content } = compile(
`<script>const a = 1</script>\n` +
`<style>div{
color: v-bind(color);
font-size: v-bind('font.size');
}</style>`
)
expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
"${mockId}-color": (_vm.color),
"${mockId}-font_size": (_vm.font.size)
})`)
assertCode(content)
})
test('w/ normal <script> binding analysis', () => {
const { content } = compile(
`<script>
export default {
setup() {
return {
size: ref('100px')
}
}
}
</script>\n` +
`<style>
div {
font-size: v-bind(size);
}
</style>`
)
expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
"${mockId}-size": (_vm.size)
})`)
expect(content).toMatch(`import { useCssVars as _useCssVars } from 'vue'`)
assertCode(content)
})
test('w/ <script setup> binding analysis', () => {
const { content } = compile(
`<script setup>
import { defineProps, ref } from 'vue'
const color = 'red'
const size = ref('10px')
defineProps({
foo: String
})
</script>\n` +
`<style>
div {
color: v-bind(color);
font-size: v-bind(size);
border: v-bind(foo);
}
</style>`
)
// should handle:
// 1. local const bindings
// 2. local potential ref bindings
// 3. props bindings (analyzed)
expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
"${mockId}-color": (_setup.color),
"${mockId}-size": (_setup.size),
"${mockId}-foo": (_vm.foo)
})`)
expect(content).toMatch(`import { useCssVars as _useCssVars } from 'vue'`)
assertCode(content)
})
test('should rewrite CSS vars in compileStyle', () => {
const { code } = compileStyle({
source: `.foo {
color: v-bind(color);
font-size: v-bind('font.size');
}`,
filename: 'test.css',
id: 'data-v-test'
})
expect(code).toMatchInlineSnapshot(`
".foo[data-v-test] {
color: var(--test-color);
font-size: var(--test-font_size);
}"
`)
})
test('prod mode', () => {
const { content } = compile(
`<script>const a = 1</script>\n` +
`<style>div{
color: v-bind(color);
font-size: v-bind('font.size');
}</style>`,
{ isProd: true }
)
expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
"4003f1a6": (_vm.color),
"41b6490a": (_vm.font.size)
}))}`)
const { code } = compileStyle({
source: `.foo {
color: v-bind(color);
font-size: v-bind('font.size');
}`,
filename: 'test.css',
id: mockId,
isProd: true
})
expect(code).toMatchInlineSnapshot(`
".foo[xxxxxxxx] {
color: var(--4003f1a6);
font-size: var(--41b6490a);
}"
`)
})
describe('codegen', () => {
test('<script> w/ no default export', () => {
assertCode(
compile(
`<script>const a = 1</script>\n` +
`<style>div{ color: v-bind(color); }</style>`
).content
)
})
test('<script> w/ default export', () => {
assertCode(
compile(
`<script>export default { setup() {} }</script>\n` +
`<style>div{ color: v-bind(color); }</style>`
).content
)
})
test('<script> w/ default export in strings/comments', () => {
assertCode(
compile(
`<script>
// export default {}
export default {}
</script>\n` + `<style>div{ color: v-bind(color); }</style>`
).content
)
})
test('w/ <script setup>', () => {
assertCode(
compile(
`<script setup>const color = 'red'</script>\n` +
`<style>div{ color: v-bind(color); }</style>`
).content
)
})
//#4185
test('should ignore comments', () => {
const { content } = compile(
`<script setup>const color = 'red';const width = 100</script>\n` +
`<style>
/* comment **/
div{ /* color: v-bind(color); */ width:20; }
div{ width: v-bind(width); }
/* comment */
</style>`
)
expect(content).not.toMatch(`"${mockId}-color": (_setup.color)`)
expect(content).toMatch(`"${mockId}-width": (_setup.width)`)
assertCode(content)
})
test('w/ <script setup> using the same var multiple times', () => {
const { content } = compile(
`<script setup>
const color = 'red'
</script>\n` +
`<style>
div {
color: v-bind(color);
}
p {
color: v-bind(color);
}
</style>`
)
// color should only be injected once, even if it is twice in style
expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
"${mockId}-color": (_setup.color)
})`)
assertCode(content)
})
test('should work with w/ complex expression', () => {
const { content } = compile(
`<script setup>
let a = 100
let b = 200
let foo = 300
</script>\n` +
`<style>
p{
width: calc(v-bind(foo) - 3px);
height: calc(v-bind('foo') - 3px);
top: calc(v-bind(foo + 'px') - 3px);
}
div {
color: v-bind((a + b) / 2 + 'px' );
}
div {
color: v-bind ((a + b) / 2 + 'px' );
}
p {
color: v-bind(((a + b)) / (2 * a));
}
</style>`
)
expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
"${mockId}-foo": (_setup.foo),
"${mockId}-foo____px_": (_setup.foo + 'px'),
"${mockId}-_a___b____2____px_": ((_setup.a + _setup.b) / 2 + 'px'),
"${mockId}-__a___b______2___a_": (((_setup.a + _setup.b)) / (2 * _setup.a))
})`)
assertCode(content)
})
// #6022
test('should be able to parse incomplete expressions', () => {
const { cssVars } = parse({
source: `<script setup>let xxx = 1</script>
<style scoped>
label {
font-weight: v-bind("count.toString(");
font-weight: v-bind(xxx);
}
</style>`
})
expect(cssVars).toMatchObject([`count.toString(`, `xxx`])
})
})
})

View File

@ -0,0 +1,269 @@
import { WarningMessage } from 'types/compiler'
import { parseComponent } from '../src/parseComponent'
describe('Single File Component parser', () => {
it('should parse', () => {
const res = parseComponent(
`
<template>
<div>hi</div>
</template>
<style src="./test.css"></style>
<style lang="stylus" scoped>
h1
color red
h2
color green
</style>
<style module>
h1 { font-weight: bold }
</style>
<style bool-attr val-attr="test"></style>
<script>
export default {}
</script>
<div>
<style>nested should be ignored</style>
</div>
`
)
expect(res.template!.content.trim()).toBe('<div>hi</div>')
expect(res.styles.length).toBe(4)
expect(res.styles[0].src).toBe('./test.css')
expect(res.styles[1].lang).toBe('stylus')
expect(res.styles[1].scoped).toBe(true)
expect(res.styles[1].content.trim()).toBe(
'h1\n color red\nh2\n color green'
)
expect(res.styles[2].module).toBe(true)
expect(res.styles[3].attrs['bool-attr']).toBe(true)
expect(res.styles[3].attrs['val-attr']).toBe('test')
expect(res.script!.content.trim()).toBe('export default {}')
})
it('should parse template with closed input', () => {
const res = parseComponent(`
<template>
<input type="text"/>
</template>
`)
expect(res.template!.content.trim()).toBe('<input type="text"/>')
})
it('should handle nested template', () => {
const res = parseComponent(`
<template>
<div><template v-if="ok">hi</template></div>
</template>
`)
expect(res.template!.content.trim()).toBe(
'<div><template v-if="ok">hi</template></div>'
)
})
it('deindent content', () => {
const content = `
<template>
<div></div>
</template>
<script>
export default {}
</script>
<style>
h1 { color: red }
</style>
`
const deindentDefault = parseComponent(content.trim(), {
pad: false
})
const deindentEnabled = parseComponent(content.trim(), {
pad: false,
deindent: true
})
const deindentDisabled = parseComponent(content.trim(), {
pad: false,
deindent: false
})
expect(deindentDefault.template!.content).toBe('\n<div></div>\n')
expect(deindentDefault.script!.content).toBe(
'\n export default {}\n '
)
expect(deindentDefault.styles[0].content).toBe('\nh1 { color: red }\n')
expect(deindentEnabled.template!.content).toBe('\n<div></div>\n')
expect(deindentEnabled.script!.content).toBe('\nexport default {}\n')
expect(deindentEnabled.styles[0].content).toBe('\nh1 { color: red }\n')
expect(deindentDisabled.template!.content).toBe(
'\n <div></div>\n '
)
expect(deindentDisabled.script!.content).toBe(
'\n export default {}\n '
)
expect(deindentDisabled.styles[0].content).toBe(
'\n h1 { color: red }\n '
)
})
it('pad content', () => {
const content = `
<template>
<div></div>
</template>
<script>
export default {}
</script>
<style>
h1 { color: red }
</style>
`
const padDefault = parseComponent(content.trim(), {
pad: true,
deindent: true
})
const padLine = parseComponent(content.trim(), {
pad: 'line',
deindent: true
})
const padSpace = parseComponent(content.trim(), {
pad: 'space',
deindent: true
})
expect(padDefault.script!.content).toBe(
Array(3 + 1).join('//\n') + '\nexport default {}\n'
)
expect(padDefault.styles[0].content).toBe(
Array(6 + 1).join('\n') + '\nh1 { color: red }\n'
)
expect(padLine.script!.content).toBe(
Array(3 + 1).join('//\n') + '\nexport default {}\n'
)
expect(padLine.styles[0].content).toBe(
Array(6 + 1).join('\n') + '\nh1 { color: red }\n'
)
expect(padSpace.script!.content).toBe(
`<template>
<div></div>
</template>
<script>`.replace(/./g, ' ') + '\nexport default {}\n'
)
expect(padSpace.styles[0].content).toBe(
`<template>
<div></div>
</template>
<script>
export default {}
</script>
<style>`.replace(/./g, ' ') + '\nh1 { color: red }\n'
)
})
it('should handle template blocks with lang as special text', () => {
const res = parseComponent(
`
<template lang="pug">
div
h1(v-if='1 < 2') hello
</template>
`,
{ deindent: true }
)
expect(res.template!.content.trim()).toBe(`div\n h1(v-if='1 < 2') hello`)
})
it('should handle component contains "<" only', () => {
const res = parseComponent(`
<template>
<span><</span>
</template>
`)
expect(res.template!.content.trim()).toBe(`<span><</span>`)
})
it('should handle custom blocks without parsing them', () => {
const res = parseComponent(
`
<template>
<div></div>
</template>
<example name="simple">
<my-button ref="button">Hello</my-button>
</example>
<example name="with props">
<my-button color="red">Hello</my-button>
</example>
<test name="simple" foo="bar">
export default function simple (vm) {
describe('Hello', () => {
it('should display Hello', () => {
this.vm.$refs.button.$el.innerText.should.equal('Hello')
}))
}))
}
</test>
<custom src="./x.json"></custom>
`
)
expect(res.customBlocks.length).toBe(4)
const simpleExample = res.customBlocks[0]
expect(simpleExample.type).toBe('example')
expect(simpleExample.content.trim()).toBe(
'<my-button ref="button">Hello</my-button>'
)
expect(simpleExample.attrs.name).toBe('simple')
const withProps = res.customBlocks[1]
expect(withProps.type).toBe('example')
expect(withProps.content.trim()).toBe(
'<my-button color="red">Hello</my-button>'
)
expect(withProps.attrs.name).toBe('with props')
const simpleTest = res.customBlocks[2]
expect(simpleTest.type).toBe('test')
expect(simpleTest.content.trim())
.toBe(`export default function simple (vm) {
describe('Hello', () => {
it('should display Hello', () => {
this.vm.$refs.button.$el.innerText.should.equal('Hello')
}))
}))
}`)
expect(simpleTest.attrs.name).toBe('simple')
expect(simpleTest.attrs.foo).toBe('bar')
const customWithSrc = res.customBlocks[3]
expect(customWithSrc.src).toBe('./x.json')
})
// Regression #4289
it('accepts nested template tag', () => {
const raw = `<div>
<template v-if="true === true">
<section class="section">
<div class="container">
Should be shown
</div>
</section>
</template>
<template v-else>
<p>Should not be shown</p>
</template>
</div>`
const res = parseComponent(`<template>${raw}</template>`)
expect(res.template!.content.trim()).toBe(raw)
})
it('should not hang on trailing text', () => {
const res = parseComponent(`<template>hi</`)
expect(res.template!.content).toBe('hi')
})
it('should collect errors with source range', () => {
const res = parseComponent(`<template>hi</`, { outputSourceRange: true })
expect(res.errors.length).toBe(1)
expect((res.errors[0] as WarningMessage).start).toBe(0)
})
})

View File

@ -0,0 +1,97 @@
import { prefixIdentifiers } from '../src/prefixIdentifiers'
import { compile } from 'web/entry-compiler'
import { format } from 'prettier'
import { BindingTypes } from '../src/types'
const toFn = (source: string) => `function render(){${source}\n}`
it('should work', () => {
const { render } = compile(`<div id="app">
<div :style="{ color }">{{ foo }}</div>
<p v-for="i in list">{{ i }}</p>
<foo inline-template>
<div>{{ bar }}</div>
</foo>
</div>`)
const result = format(prefixIdentifiers(toFn(render)), {
semi: false,
parser: 'babel'
})
expect(result).not.toMatch(`_vm._c`)
expect(result).toMatch(`_vm.foo`)
expect(result).toMatch(`_vm.list`)
expect(result).toMatch(`{ color: _vm.color }`)
expect(result).not.toMatch(`_vm.i`)
expect(result).not.toMatch(`with (this)`)
expect(result).toMatchInlineSnapshot(`
"function render() {
var _vm = this,
_c = _vm._self._c
return _c(
\\"div\\",
{ attrs: { id: \\"app\\" } },
[
_c(\\"div\\", { style: { color: _vm.color } }, [_vm._v(_vm._s(_vm.foo))]),
_vm._v(\\" \\"),
_vm._l(_vm.list, function (i) {
return _c(\\"p\\", [_vm._v(_vm._s(i))])
}),
_vm._v(\\" \\"),
_c(\\"foo\\", {
inlineTemplate: {
render: function () {
var _vm = this,
_c = _vm._self._c
return _c(\\"div\\", [_vm._v(_vm._s(_vm.bar))])
},
staticRenderFns: [],
},
}),
],
2
)
}
"
`)
})
it('setup bindings', () => {
const { render } = compile(`<div @click="count++">{{ count }}</div>`)
const result = format(
prefixIdentifiers(toFn(render), false, false, undefined, {
count: BindingTypes.SETUP_REF
}),
{
semi: false,
parser: 'babel'
}
)
expect(result).toMatch(`_setup = _vm._self._setupProxy`)
expect(result).toMatch(`_setup.count++`)
expect(result).toMatch(`_vm._s(_setup.count)`)
expect(result).toMatchInlineSnapshot(`
"function render() {
var _vm = this,
_c = _vm._self._c,
_setup = _vm._self._setupProxy
return _c(
\\"div\\",
{
on: {
click: function (\$event) {
_setup.count++
},
},
},
[_vm._v(_vm._s(_setup.count))]
)
}
"
`)
})

View File

@ -0,0 +1,245 @@
import { rewriteDefault } from '../src'
describe('compiler sfc: rewriteDefault', () => {
test('without export default', () => {
expect(rewriteDefault(`export a = {}`, 'script')).toMatchInlineSnapshot(`
"export a = {}
const script = {}"
`)
})
test('rewrite export default', () => {
expect(
rewriteDefault(`export default {}`, 'script')
).toMatchInlineSnapshot(`"const script = {}"`)
})
test('rewrite export named default', () => {
expect(
rewriteDefault(
`const a = 1 \n export { a as b, a as default, a as c}`,
'script'
)
).toMatchInlineSnapshot(`
"const a = 1
export { a as b, a as c}
const script = a"
`)
expect(
rewriteDefault(
`const a = 1 \n export { a as b, a as default , a as c}`,
'script'
)
).toMatchInlineSnapshot(`
"const a = 1
export { a as b, a as c}
const script = a"
`)
})
test('w/ comments', async () => {
expect(rewriteDefault(`// export default\nexport default {}`, 'script'))
.toMatchInlineSnapshot(`
"// export default
const script = {}"
`)
})
test('export named default multiline', () => {
expect(
rewriteDefault(`let App = {}\n export {\nApp as default\n}`, '_sfc_main')
).toMatchInlineSnapshot(`
"let App = {}
export {
}
const _sfc_main = App"
`)
})
test('export named default multiline /w comments', () => {
expect(
rewriteDefault(
`const a = 1 \n export {\n a as b,\n a as default,\n a as c}\n` +
`// export { myFunction as default }`,
'script'
)
).toMatchInlineSnapshot(`
"const a = 1
export {
a as b,
a as c}
// export { myFunction as default }
const script = a"
`)
expect(
rewriteDefault(
`const a = 1 \n export {\n a as b,\n a as default ,\n a as c}\n` +
`// export { myFunction as default }`,
'script'
)
).toMatchInlineSnapshot(`
"const a = 1
export {
a as b,
a as c}
// export { myFunction as default }
const script = a"
`)
})
test(`export { default } from '...'`, async () => {
expect(
rewriteDefault(`export { default, foo } from './index.js'`, 'script')
).toMatchInlineSnapshot(`
"import { default as __VUE_DEFAULT__ } from './index.js'
export { foo } from './index.js'
const script = __VUE_DEFAULT__"
`)
expect(
rewriteDefault(`export { default , foo } from './index.js'`, 'script')
).toMatchInlineSnapshot(`
"import { default as __VUE_DEFAULT__ } from './index.js'
export { foo } from './index.js'
const script = __VUE_DEFAULT__"
`)
expect(
rewriteDefault(`export { foo, default } from './index.js'`, 'script')
).toMatchInlineSnapshot(`
"import { default as __VUE_DEFAULT__ } from './index.js'
export { foo, } from './index.js'
const script = __VUE_DEFAULT__"
`)
expect(
rewriteDefault(
`export { foo as default, bar } from './index.js'`,
'script'
)
).toMatchInlineSnapshot(`
"import { foo } from './index.js'
export { bar } from './index.js'
const script = foo"
`)
expect(
rewriteDefault(
`export { foo as default , bar } from './index.js'`,
'script'
)
).toMatchInlineSnapshot(`
"import { foo } from './index.js'
export { bar } from './index.js'
const script = foo"
`)
expect(
rewriteDefault(
`export { bar, foo as default } from './index.js'`,
'script'
)
).toMatchInlineSnapshot(`
"import { foo } from './index.js'
export { bar, } from './index.js'
const script = foo"
`)
})
test('export default class', async () => {
expect(rewriteDefault(`export default class Foo {}`, 'script'))
.toMatchInlineSnapshot(`
"class Foo {}
const script = Foo"
`)
})
test('export default class w/ comments', async () => {
expect(
rewriteDefault(`// export default\nexport default class Foo {}`, 'script')
).toMatchInlineSnapshot(`
"// export default
class Foo {}
const script = Foo"
`)
})
test('export default class w/ comments 2', async () => {
expect(
rewriteDefault(
`export default {}\n` + `// export default class Foo {}`,
'script'
)
).toMatchInlineSnapshot(`
"const script = {}
// export default class Foo {}"
`)
})
test('export default class w/ comments 3', async () => {
expect(
rewriteDefault(
`/*\nexport default class Foo {}*/\n` + `export default class Bar {}`,
'script'
)
).toMatchInlineSnapshot(`
"/*
export default class Foo {}*/
class Bar {}
const script = Bar"
`)
})
test('@Component\nexport default class', async () => {
expect(rewriteDefault(`@Component\nexport default class Foo {}`, 'script'))
.toMatchInlineSnapshot(`
"@Component
class Foo {}
const script = Foo"
`)
})
test('@Component\nexport default class w/ comments', async () => {
expect(
rewriteDefault(`// export default\n@Component\nexport default class Foo {}`, 'script')
).toMatchInlineSnapshot(`
"// export default
@Component
class Foo {}
const script = Foo"
`)
})
test('@Component\nexport default class w/ comments 2', async () => {
expect(
rewriteDefault(
`export default {}\n` + `// @Component\n// export default class Foo {}`,
'script'
)
).toMatchInlineSnapshot(`
"const script = {}
// @Component
// export default class Foo {}"
`)
})
test('@Component\nexport default class w/ comments 3', async () => {
expect(
rewriteDefault(
`/*\n@Component\nexport default class Foo {}*/\n` + `export default class Bar {}`,
'script'
)
).toMatchInlineSnapshot(`
"/*
@Component
export default class Foo {}*/
class Bar {}
const script = Bar"
`)
})
})

View File

@ -0,0 +1,137 @@
import { compileStyle } from '../src/compileStyle'
// vue-loader/#1370
test('spaces after selector', () => {
const { code } = compileStyle({
source: `.foo , .bar { color: red; }`,
filename: 'test.css',
id: 'test'
})
expect(code).toMatch(`.foo[test], .bar[test] { color: red;`)
})
test('leading deep selector', () => {
const { code } = compileStyle({
source: `>>> .foo { color: red; }`,
filename: 'test.css',
id: 'test'
})
expect(code).toMatch(`[test] .foo { color: red;`)
})
test('scoped css', () => {
const { code: style } = compileStyle({
id: 'v-scope-xxx',
scoped: true,
filename: 'example.vue',
source: `
.test {
color: yellow;
}
.test:after {
content: 'bye!';
}
h1 {
color: green;
}
.anim {
animation: color 5s infinite, other 5s;
}
.anim-2 {
animation-name: color;
animation-duration: 5s;
}
.anim-3 {
animation: 5s color infinite, 5s other;
}
.anim-multiple {
animation: color 5s infinite, opacity 2s;
}
.anim-multiple-2 {
animation-name: color, opacity;
animation-duration: 5s, 2s;
}
@keyframes color {
from { color: red; }
to { color: green; }
}
@-webkit-keyframes color {
from { color: red; }
to { color: green; }
}
@keyframes opacity {
from { opacity: 0; }
to { opacity: 1; }
}
@-webkit-keyframes opacity {
from { opacity: 0; }
to { opacity: 1; }
}
.foo p >>> .bar {
color: red;
}
.foo div /deep/ .bar {
color: red;
}
.foo span ::v-deep .bar {
color: red;
}
`
})
expect(style).toContain(`.test[v-scope-xxx] {\n color: yellow;\n}`)
expect(style).toContain(`.test[v-scope-xxx]:after {\n content: \'bye!\';\n}`)
expect(style).toContain(`h1[v-scope-xxx] {\n color: green;\n}`)
// scoped keyframes
expect(style).toContain(
`.anim[v-scope-xxx] {\n animation: color-v-scope-xxx 5s infinite, other 5s;`
)
expect(style).toContain(
`.anim-2[v-scope-xxx] {\n animation-name: color-v-scope-xxx`
)
expect(style).toContain(
`.anim-3[v-scope-xxx] {\n animation: 5s color-v-scope-xxx infinite, 5s other;`
)
expect(style).toContain(`@keyframes color-v-scope-xxx {`)
expect(style).toContain(`@-webkit-keyframes color-v-scope-xxx {`)
expect(style).toContain(
`.anim-multiple[v-scope-xxx] {\n animation: color-v-scope-xxx 5s infinite,opacity-v-scope-xxx 2s;`
)
expect(style).toContain(
`.anim-multiple-2[v-scope-xxx] {\n animation-name: color-v-scope-xxx,opacity-v-scope-xxx;`
)
expect(style).toContain(`@keyframes opacity-v-scope-xxx {`)
expect(style).toContain(`@-webkit-keyframes opacity-v-scope-xxx {`)
// >>> combinator
expect(style).toContain(`.foo p[v-scope-xxx] .bar {\n color: red;\n}`)
// /deep/ alias for >>>
expect(style).toContain(`.foo div[v-scope-xxx] .bar {\n color: red;\n}`)
// ::-v-deep alias for >>>
expect(style).toContain(`.foo span[v-scope-xxx] .bar {\n color: red;\n}`)
})
test('pseudo element', () => {
const { code } = compileStyle({
source: '::selection { display: none; }',
filename: 'test.css',
id: 'test'
})
expect(code).toContain('[test]::selection {')
})
test('spaces before pseudo element', () => {
const { code } = compileStyle({
source: '.abc, ::selection { color: red; }',
filename: 'test.css',
id: 'test'
})
expect(code).toContain('.abc[test],')
expect(code).toContain('[test]::selection {')
})

View File

@ -0,0 +1,7 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"types": ["node", "vitest/globals"]
},
"include": ["../src", "."]
}

35
node_modules/vue/packages/compiler-sfc/test/util.ts generated vendored Normal file
View File

@ -0,0 +1,35 @@
import {
parse,
compileScript,
type SFCParseOptions,
type SFCScriptCompileOptions
} from '../src'
import { parse as babelParse } from '@babel/parser'
export const mockId = 'xxxxxxxx'
export function compile(
source: string,
options?: Partial<SFCScriptCompileOptions>,
parseOptions?: Partial<SFCParseOptions>
) {
const sfc = parse({
...parseOptions,
source
})
return compileScript(sfc, { id: mockId, ...options })
}
export function assertCode(code: string) {
// parse the generated code to make sure it is valid
try {
babelParse(code, {
sourceType: 'module',
plugins: ['typescript']
})
} catch (e: any) {
console.log(code)
throw e
}
expect(code).toMatchSnapshot()
}