First commit

This commit is contained in:
2026-03-27 10:14:29 +03:00
commit ad29150770
10404 changed files with 962562 additions and 0 deletions

21
node_modules/vue/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013-present, Yuxi (Evan) You
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

118
node_modules/vue/README.md generated vendored Normal file
View File

@@ -0,0 +1,118 @@
<p align="center"><a href="https://vuejs.org" target="_blank" rel="noopener noreferrer"><img width="100" src="https://vuejs.org/images/logo.png" alt="Vue logo"></a></p>
<p align="center">
<a href="https://circleci.com/gh/vuejs/vue/tree/dev"><img src="https://img.shields.io/circleci/project/github/vuejs/vue/dev.svg?sanitize=true" alt="Build Status"></a>
<a href="https://codecov.io/github/vuejs/vue?branch=dev"><img src="https://img.shields.io/codecov/c/github/vuejs/vue/dev.svg?sanitize=true" alt="Coverage Status"></a>
<a href="https://npmcharts.com/compare/vue?minimal=true"><img src="https://img.shields.io/npm/dm/vue.svg?sanitize=true" alt="Downloads"></a>
<a href="https://www.npmjs.com/package/vue"><img src="https://img.shields.io/npm/v/vue.svg?sanitize=true" alt="Version"></a>
<a href="https://www.npmjs.com/package/vue"><img src="https://img.shields.io/npm/l/vue.svg?sanitize=true" alt="License"></a>
<a href="https://chat.vuejs.org/"><img src="https://img.shields.io/badge/chat-on%20discord-7289da.svg?sanitize=true" alt="Chat"></a>
</p>
## This repo is for Vue 2
You are looking at the repository for Vue 2. The repo for Vue 3 is [vuejs/core](https://github.com/vuejs/core).
## Sponsors
Vue.js is an MIT-licensed open source project with its ongoing development made possible entirely by the support of these awesome [backers](https://github.com/vuejs/core/blob/main/BACKERS.md). If you'd like to join them, please consider [ sponsor Vue's development](https://vuejs.org/sponsor/).
<p align="center">
<h3 align="center">Special Sponsor</h3>
</p>
<p align="center">
<a target="_blank" href="https://github.com/appwrite/appwrite">
<img alt="special sponsor appwrite" src="https://sponsors.vuejs.org/images/appwrite.svg" width="300">
</a>
</p>
<p align="center">
<a target="_blank" href="https://vuejs.org/sponsor/">
<img alt="sponsors" src="https://sponsors.vuejs.org/sponsors.svg?v3">
</a>
</p>
---
## Introduction
Vue (pronounced `/vjuː/`, like view) is a **progressive framework** for building user interfaces. It is designed from the ground up to be incrementally adoptable, and can easily scale between a library and a framework depending on different use cases. It consists of an approachable core library that focuses on the view layer only, and an ecosystem of supporting libraries that helps you tackle complexity in large Single-Page Applications.
#### Browser Compatibility
Vue.js supports all browsers that are [ES5-compliant](https://kangax.github.io/compat-table/es5/) (IE8 and below are not supported).
## Ecosystem
| Project | Status | Description |
| --------------------- | ------------------------------------------------------------ | ------------------------------------------------------- |
| [vue-router] | [![vue-router-status]][vue-router-package] | Single-page application routing |
| [vuex] | [![vuex-status]][vuex-package] | Large-scale state management |
| [vue-cli] | [![vue-cli-status]][vue-cli-package] | Project scaffolding |
| [vue-loader] | [![vue-loader-status]][vue-loader-package] | Single File Component (`*.vue` file) loader for webpack |
| [vue-server-renderer] | [![vue-server-renderer-status]][vue-server-renderer-package] | Server-side rendering support |
| [vue-class-component] | [![vue-class-component-status]][vue-class-component-package] | TypeScript decorator for a class-based API |
| [vue-rx] | [![vue-rx-status]][vue-rx-package] | RxJS integration |
| [vue-devtools] | [![vue-devtools-status]][vue-devtools-package] | Browser DevTools extension |
[vue-router]: https://github.com/vuejs/vue-router
[vuex]: https://github.com/vuejs/vuex
[vue-cli]: https://github.com/vuejs/vue-cli
[vue-loader]: https://github.com/vuejs/vue-loader
[vue-server-renderer]: https://github.com/vuejs/vue/tree/dev/packages/vue-server-renderer
[vue-class-component]: https://github.com/vuejs/vue-class-component
[vue-rx]: https://github.com/vuejs/vue-rx
[vue-devtools]: https://github.com/vuejs/vue-devtools
[vue-router-status]: https://img.shields.io/npm/v/vue-router.svg
[vuex-status]: https://img.shields.io/npm/v/vuex.svg
[vue-cli-status]: https://img.shields.io/npm/v/@vue/cli.svg
[vue-loader-status]: https://img.shields.io/npm/v/vue-loader.svg
[vue-server-renderer-status]: https://img.shields.io/npm/v/vue-server-renderer.svg
[vue-class-component-status]: https://img.shields.io/npm/v/vue-class-component.svg
[vue-rx-status]: https://img.shields.io/npm/v/vue-rx.svg
[vue-devtools-status]: https://img.shields.io/chrome-web-store/v/nhdogjmejiglipccpnnnanhbledajbpd.svg
[vue-router-package]: https://npmjs.com/package/vue-router
[vuex-package]: https://npmjs.com/package/vuex
[vue-cli-package]: https://npmjs.com/package/@vue/cli
[vue-loader-package]: https://npmjs.com/package/vue-loader
[vue-server-renderer-package]: https://npmjs.com/package/vue-server-renderer
[vue-class-component-package]: https://npmjs.com/package/vue-class-component
[vue-rx-package]: https://npmjs.com/package/vue-rx
[vue-devtools-package]: https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd
## Documentation
To check out [live examples](https://v2.vuejs.org/v2/examples/) and docs, visit [vuejs.org](https://v2.vuejs.org).
## Questions
For questions and support please use [the official forum](https://forum.vuejs.org) or [community chat](https://chat.vuejs.org/). The issue list of this repo is **exclusively** for bug reports and feature requests.
## Issues
Please make sure to read the [Issue Reporting Checklist](https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#issue-reporting-guidelines) before opening an issue. Issues not conforming to the guidelines may be closed immediately.
## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/vuejs/vue/releases).
## Stay In Touch
- [Twitter](https://twitter.com/vuejs)
- [Blog](https://medium.com/the-vue-point)
- [Job Board](https://vuejobs.com/?ref=vuejs)
## Contribution
Please make sure to read the [Contributing Guide](https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md) before making a pull request. If you have a Vue-related project/component/tool, add it with a pull request to [this curated list](https://github.com/vuejs/awesome-vue)!
Thank you to all the people who already contributed to Vue!
<a href="https://github.com/vuejs/vue/graphs/contributors"><img src="https://opencollective.com/vuejs/contributors.svg?width=890" /></a>
## License
[MIT](https://opensource.org/licenses/MIT)
Copyright (c) 2013-present, Yuxi (Evan) You

1
node_modules/vue/compiler-sfc/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1 @@
export * from '@vue/compiler-sfc'

1
node_modules/vue/compiler-sfc/index.js generated vendored Normal file
View File

@@ -0,0 +1 @@
module.exports = require('@vue/compiler-sfc')

1
node_modules/vue/compiler-sfc/index.mjs generated vendored Normal file
View File

@@ -0,0 +1 @@
export * from '@vue/compiler-sfc'

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

@@ -0,0 +1,5 @@
{
"main": "index.js",
"module": "index.mjs",
"types": "index.d.ts"
}

11818
node_modules/vue/dist/vue.common.dev.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

5
node_modules/vue/dist/vue.common.js generated vendored Normal file
View File

@@ -0,0 +1,5 @@
if (process.env.NODE_ENV === 'production') {
module.exports = require('./vue.common.prod.js')
} else {
module.exports = require('./vue.common.dev.js')
}

11
node_modules/vue/dist/vue.common.prod.js generated vendored Normal file

File diff suppressed because one or more lines are too long

11731
node_modules/vue/dist/vue.esm.browser.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

11
node_modules/vue/dist/vue.esm.browser.min.js generated vendored Normal file

File diff suppressed because one or more lines are too long

11918
node_modules/vue/dist/vue.esm.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

11932
node_modules/vue/dist/vue.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

11
node_modules/vue/dist/vue.min.js generated vendored Normal file

File diff suppressed because one or more lines are too long

8736
node_modules/vue/dist/vue.runtime.common.dev.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

5
node_modules/vue/dist/vue.runtime.common.js generated vendored Normal file
View File

@@ -0,0 +1,5 @@
if (process.env.NODE_ENV === 'production') {
module.exports = require('./vue.runtime.common.prod.js')
} else {
module.exports = require('./vue.runtime.common.dev.js')
}

11
node_modules/vue/dist/vue.runtime.common.prod.js generated vendored Normal file

File diff suppressed because one or more lines are too long

8825
node_modules/vue/dist/vue.runtime.esm.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

8826
node_modules/vue/dist/vue.runtime.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

11
node_modules/vue/dist/vue.runtime.min.js generated vendored Normal file

File diff suppressed because one or more lines are too long

76
node_modules/vue/dist/vue.runtime.mjs generated vendored Normal file
View File

@@ -0,0 +1,76 @@
import Vue from './vue.runtime.common.js'
export default Vue
// this should be kept in sync with src/v3/index.ts
export const {
version,
// refs
ref,
shallowRef,
isRef,
toRef,
toRefs,
unref,
proxyRefs,
customRef,
triggerRef,
computed,
// reactive
reactive,
isReactive,
isReadonly,
isShallow,
isProxy,
shallowReactive,
markRaw,
toRaw,
readonly,
shallowReadonly,
// watch
watch,
watchEffect,
watchPostEffect,
watchSyncEffect,
// effectScope
effectScope,
onScopeDispose,
getCurrentScope,
// provide / inject
provide,
inject,
// lifecycle
onBeforeMount,
onMounted,
onBeforeUpdate,
onUpdated,
onBeforeUnmount,
onUnmounted,
onErrorCaptured,
onActivated,
onDeactivated,
onServerPrefetch,
onRenderTracked,
onRenderTriggered,
// v2 only
set,
del,
// v3 compat
h,
getCurrentInstance,
useSlots,
useAttrs,
mergeDefaults,
nextTick,
useCssModule,
useCssVars,
defineComponent,
defineAsyncComponent
} = Vue

136
node_modules/vue/package.json generated vendored Normal file
View File

@@ -0,0 +1,136 @@
{
"name": "vue",
"version": "2.7.16",
"packageManager": "pnpm@8.9.2",
"description": "Reactive, component-oriented view layer for modern web interfaces.",
"main": "dist/vue.runtime.common.js",
"module": "dist/vue.runtime.esm.js",
"unpkg": "dist/vue.js",
"jsdelivr": "dist/vue.js",
"typings": "types/index.d.ts",
"files": [
"src",
"dist/*.js",
"dist/*.mjs",
"types/*.d.ts",
"compiler-sfc",
"packages/compiler-sfc"
],
"exports": {
".": {
"types": "./types/index.d.ts",
"import": {
"node": "./dist/vue.runtime.mjs",
"default": "./dist/vue.runtime.esm.js"
},
"require": "./dist/vue.runtime.common.js"
},
"./compiler-sfc": {
"types": "./compiler-sfc/index.d.ts",
"import": "./compiler-sfc/index.mjs",
"require": "./compiler-sfc/index.js"
},
"./dist/*": "./dist/*",
"./types/*": [
"./types/*.d.ts",
"./types/*"
],
"./package.json": "./package.json"
},
"sideEffects": false,
"gitHooks": {
"pre-commit": "lint-staged",
"commit-msg": "node scripts/verify-commit-msg.js"
},
"lint-staged": {
"*.js": [
"prettier --write"
],
"*.ts": [
"prettier --parser=typescript --write"
]
},
"repository": {
"type": "git",
"url": "git+https://github.com/vuejs/vue.git"
},
"keywords": [
"vue"
],
"author": "Evan You",
"license": "MIT",
"bugs": {
"url": "https://github.com/vuejs/vue/issues"
},
"homepage": "https://github.com/vuejs/vue#readme",
"dependencies": {
"csstype": "^3.1.0",
"@vue/compiler-sfc": "2.7.16"
},
"devDependencies": {
"@babel/parser": "^7.23.5",
"@microsoft/api-extractor": "^7.25.0",
"@rollup/plugin-alias": "^3.1.9",
"@rollup/plugin-commonjs": "^22.0.0",
"@rollup/plugin-node-resolve": "^13.3.0",
"@rollup/plugin-replace": "^4.0.0",
"@types/he": "^1.1.2",
"@types/node": "^20.10.3",
"chalk": "^4.1.2",
"conventional-changelog-cli": "^2.2.2",
"cross-spawn": "^7.0.3",
"enquirer": "^2.3.6",
"esbuild": "^0.19.8",
"execa": "^4.1.0",
"he": "^1.2.0",
"jasmine-core": "^4.2.0",
"jsdom": "^19.0.0",
"karma": "^6.3.20",
"karma-chrome-launcher": "^3.1.1",
"karma-cli": "^2.0.0",
"karma-esbuild": "^2.2.5",
"karma-jasmine": "^5.0.1",
"lint-staged": "^12.5.0",
"lodash": "^4.17.21",
"marked": "^4.0.16",
"minimist": "^1.2.6",
"postcss": "^8.4.14",
"prettier": "^2.6.2",
"puppeteer": "^14.3.0",
"rimraf": "^3.0.2",
"rollup": "^2.79.1",
"rollup-plugin-typescript2": "^0.32.0",
"semver": "^7.3.7",
"shelljs": "^0.8.5",
"terser": "^5.14.0",
"todomvc-app-css": "^2.4.2",
"ts-node": "^10.8.1",
"tslib": "^2.4.0",
"typescript": "^4.8.4",
"vitest": "^1.0.4",
"yorkie": "^2.0.0"
},
"scripts": {
"dev": "rollup -w -c scripts/config.js --environment TARGET:full-dev",
"dev:cjs": "rollup -w -c scripts/config.js --environment TARGET:runtime-cjs-dev",
"dev:esm": "rollup -w -c scripts/config.js --environment TARGET:runtime-esm",
"dev:ssr": "rollup -w -c scripts/config.js --environment TARGET:server-renderer",
"dev:compiler": "rollup -w -c scripts/config.js --environment TARGET:compiler ",
"build": "node scripts/build.js",
"build:ssr": "npm run build -- runtime-cjs,server-renderer",
"build:types": "rimraf temp && tsc --declaration --emitDeclarationOnly --outDir temp && api-extractor run && api-extractor run -c packages/compiler-sfc/api-extractor.json",
"test": "npm run ts-check && npm run test:types && npm run test:unit && npm run test:e2e && npm run test:ssr && npm run test:sfc",
"test:unit": "vitest run test/unit",
"test:ssr": "npm run build:ssr && vitest run server-renderer",
"test:sfc": "vitest run compiler-sfc",
"test:e2e": "npm run build -- full-prod,server-renderer-basic && vitest run test/e2e",
"test:transition": "karma start test/transition/karma.conf.js",
"test:types": "npm run build:types && tsc -p ./types/tsconfig.json",
"format": "prettier --write --parser typescript \"(src|test|packages|types)/**/*.ts\"",
"ts-check": "tsc -p tsconfig.json --noEmit",
"ts-check:test": "tsc -p test/tsconfig.json --noEmit",
"bench:ssr": "npm run build:ssr && node benchmarks/ssr/renderToString.js && node benchmarks/ssr/renderToStream.js",
"release": "node scripts/release.js",
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
}
}

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/less@4.2.0/node_modules/less/bin/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/less@4.2.0/node_modules/less/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/less@4.2.0/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/node_modules"
else
export NODE_PATH="/Users/evan/Vue/vue/node_modules/.pnpm/less@4.2.0/node_modules/less/bin/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/less@4.2.0/node_modules/less/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/less@4.2.0/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/node_modules:$NODE_PATH"
fi
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../less/bin/lessc" "$@"
else
exec node "$basedir/../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/@babel+parser@7.23.5/node_modules/@babel/parser/bin/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/@babel+parser@7.23.5/node_modules/@babel/parser/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/@babel+parser@7.23.5/node_modules/@babel/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/@babel+parser@7.23.5/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/node_modules"
else
export NODE_PATH="/Users/evan/Vue/vue/node_modules/.pnpm/@babel+parser@7.23.5/node_modules/@babel/parser/bin/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/@babel+parser@7.23.5/node_modules/@babel/parser/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/@babel+parser@7.23.5/node_modules/@babel/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/@babel+parser@7.23.5/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/node_modules:$NODE_PATH"
fi
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../@babel/parser/bin/babel-parser.js" "$@"
else
exec node "$basedir/../@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/prettier@2.8.8/node_modules/prettier/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/prettier@2.8.8/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/node_modules"
else
export NODE_PATH="/Users/evan/Vue/vue/node_modules/.pnpm/prettier@2.8.8/node_modules/prettier/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/prettier@2.8.8/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/node_modules:$NODE_PATH"
fi
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../prettier/bin-prettier.js" "$@"
else
exec node "$basedir/../prettier/bin-prettier.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/sass@1.69.5/node_modules/sass/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/sass@1.69.5/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/node_modules"
else
export NODE_PATH="/Users/evan/Vue/vue/node_modules/.pnpm/sass@1.69.5/node_modules/sass/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/sass@1.69.5/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/node_modules:$NODE_PATH"
fi
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../sass/sass.js" "$@"
else
exec node "$basedir/../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/stylus@0.58.1/node_modules/stylus/bin/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/stylus@0.58.1/node_modules/stylus/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/stylus@0.58.1/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/node_modules"
else
export NODE_PATH="/Users/evan/Vue/vue/node_modules/.pnpm/stylus@0.58.1/node_modules/stylus/bin/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/stylus@0.58.1/node_modules/stylus/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/stylus@0.58.1/node_modules:/Users/evan/Vue/vue/node_modules/.pnpm/node_modules:$NODE_PATH"
fi
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../stylus/bin/stylus" "$@"
else
exec node "$basedir/../stylus/bin/stylus" "$@"
fi

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

@@ -0,0 +1,37 @@
{
"name": "@vue/compiler-sfc",
"version": "2.7.16",
"description": "compiler-sfc for Vue 2",
"main": "dist/compiler-sfc.js",
"types": "dist/compiler-sfc.d.ts",
"files": [
"dist"
],
"dependencies": {
"@babel/parser": "^7.23.5",
"postcss": "^8.4.14",
"source-map": "^0.6.1"
},
"devDependencies": {
"@babel/types": "^7.23.5",
"@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"
},
"optionalDependencies": {
"prettier": "^1.18.2 || ^2.0.0"
}
}

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,121 @@
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' && node.declaration.id) {
let start: number =
node.declaration.decorators && node.declaration.decorators.length > 0
? node.declaration.decorators[
node.declaration.decorators.length - 1
].end!
: node.start!
s.overwrite(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,971 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
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> > defineProps/defineEmits in multi-variable declaration fix #6757 1`] = `
"export default {
props: ['item'],
emits: ['a'],
setup(__props, { emit }) {
const props = __props;
const a = 1;
return { a, props, 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 > imported ref as template ref 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { aref } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { aref }
}
})"
`;
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 ts type) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
interface Emits { (e: 'foo'): void }
export default /*#__PURE__*/_defineComponent({
emits: ['foo'],
setup(__props, { emit }) {
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, https://vitest.dev/guide/snapshot.html
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,311 @@
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"
`)
})
// #13060
test('@Component\nexport default class w/ comments 4', async () => {
expect(
rewriteDefault(
`@Component
export default class App extends Vue {
/* default <- This word means my component is not built correctly */
@Prop({ type: String, required: true })
protected someString: string;
}`,
'script'
)
).toMatchInlineSnapshot(`
"@Component
class App extends Vue {
/* default <- This word means my component is not built correctly */
@Prop({ type: String, required: true })
protected someString: string;
}
const script = App"
`)
})
// #12892
test('@Component\nexport default class w/ comments 5', async () => {
expect(
rewriteDefault(
`@Component({})
export default class HelloWorld extends Vue {
test = "";
mounted() {
console.log("mounted!");
this.test = "Hallo Welt!";
}
exportieren(): void {
// do nothing
}
defaultWert(): void {
// do nothing
}
}`,
'script',
['typescript', 'decorators-legacy']
)
).toMatchInlineSnapshot(`
"@Component({}) class HelloWorld extends Vue {
test = "";
mounted() {
console.log("mounted!");
this.test = "Hallo Welt!";
}
exportieren(): void {
// do nothing
}
defaultWert(): void {
// do nothing
}
}
const script = HelloWorld"
`)
})
})

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()
}

52
node_modules/vue/src/compiler/codeframe.ts generated vendored Normal file
View File

@@ -0,0 +1,52 @@
const range = 2
export function generateCodeFrame(
source: string,
start: number = 0,
end: number = source.length
): string {
const lines = source.split(/\r?\n/)
let count = 0
const res: string[] = []
for (let i = 0; i < lines.length; i++) {
count += lines[i].length + 1
if (count >= start) {
for (let j = i - range; j <= i + range || end > count; j++) {
if (j < 0 || j >= lines.length) continue
res.push(
`${j + 1}${repeat(` `, 3 - String(j + 1).length)}| ${lines[j]}`
)
const lineLength = lines[j].length
if (j === i) {
// push underline
const pad = start - (count - lineLength) + 1
const length = end > count ? lineLength - pad : end - start
res.push(` | ` + repeat(` `, pad) + repeat(`^`, length))
} else if (j > i) {
if (end > count) {
const length = Math.min(end - count, lineLength)
res.push(` | ` + repeat(`^`, length))
}
count += lineLength + 1
}
}
break
}
}
return res.join('\n')
}
function repeat(str: string, n: number) {
let result = ''
if (n > 0) {
// eslint-disable-next-line no-constant-condition
while (true) {
// eslint-disable-line
if (n & 1) result += str
n >>>= 1
if (n <= 0) break
str += str
}
}
return result
}

170
node_modules/vue/src/compiler/codegen/events.ts generated vendored Normal file
View File

@@ -0,0 +1,170 @@
import { ASTElementHandler, ASTElementHandlers } from 'types/compiler'
const fnExpRE = /^([\w$_]+|\([^)]*?\))\s*=>|^function(?:\s+[\w$]+)?\s*\(/
const fnInvokeRE = /\([^)]*?\);*$/
const simplePathRE =
/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/
// KeyboardEvent.keyCode aliases
const keyCodes: { [key: string]: number | Array<number> } = {
esc: 27,
tab: 9,
enter: 13,
space: 32,
up: 38,
left: 37,
right: 39,
down: 40,
delete: [8, 46]
}
// KeyboardEvent.key aliases
const keyNames: { [key: string]: string | Array<string> } = {
// #7880: IE11 and Edge use `Esc` for Escape key name.
esc: ['Esc', 'Escape'],
tab: 'Tab',
enter: 'Enter',
// #9112: IE11 uses `Spacebar` for Space key name.
space: [' ', 'Spacebar'],
// #7806: IE11 uses key names without `Arrow` prefix for arrow keys.
up: ['Up', 'ArrowUp'],
left: ['Left', 'ArrowLeft'],
right: ['Right', 'ArrowRight'],
down: ['Down', 'ArrowDown'],
// #9112: IE11 uses `Del` for Delete key name.
delete: ['Backspace', 'Delete', 'Del']
}
// #4868: modifiers that prevent the execution of the listener
// need to explicitly return null so that we can determine whether to remove
// the listener for .once
const genGuard = condition => `if(${condition})return null;`
const modifierCode: { [key: string]: string } = {
stop: '$event.stopPropagation();',
prevent: '$event.preventDefault();',
self: genGuard(`$event.target !== $event.currentTarget`),
ctrl: genGuard(`!$event.ctrlKey`),
shift: genGuard(`!$event.shiftKey`),
alt: genGuard(`!$event.altKey`),
meta: genGuard(`!$event.metaKey`),
left: genGuard(`'button' in $event && $event.button !== 0`),
middle: genGuard(`'button' in $event && $event.button !== 1`),
right: genGuard(`'button' in $event && $event.button !== 2`)
}
export function genHandlers(
events: ASTElementHandlers,
isNative: boolean
): string {
const prefix = isNative ? 'nativeOn:' : 'on:'
let staticHandlers = ``
let dynamicHandlers = ``
for (const name in events) {
const handlerCode = genHandler(events[name])
//@ts-expect-error
if (events[name] && events[name].dynamic) {
dynamicHandlers += `${name},${handlerCode},`
} else {
staticHandlers += `"${name}":${handlerCode},`
}
}
staticHandlers = `{${staticHandlers.slice(0, -1)}}`
if (dynamicHandlers) {
return prefix + `_d(${staticHandlers},[${dynamicHandlers.slice(0, -1)}])`
} else {
return prefix + staticHandlers
}
}
function genHandler(
handler: ASTElementHandler | Array<ASTElementHandler>
): string {
if (!handler) {
return 'function(){}'
}
if (Array.isArray(handler)) {
return `[${handler.map(handler => genHandler(handler)).join(',')}]`
}
const isMethodPath = simplePathRE.test(handler.value)
const isFunctionExpression = fnExpRE.test(handler.value)
const isFunctionInvocation = simplePathRE.test(
handler.value.replace(fnInvokeRE, '')
)
if (!handler.modifiers) {
if (isMethodPath || isFunctionExpression) {
return handler.value
}
return `function($event){${
isFunctionInvocation ? `return ${handler.value}` : handler.value
}}` // inline statement
} else {
let code = ''
let genModifierCode = ''
const keys: string[] = []
for (const key in handler.modifiers) {
if (modifierCode[key]) {
genModifierCode += modifierCode[key]
// left/right
if (keyCodes[key]) {
keys.push(key)
}
} else if (key === 'exact') {
const modifiers = handler.modifiers
genModifierCode += genGuard(
['ctrl', 'shift', 'alt', 'meta']
.filter(keyModifier => !modifiers[keyModifier])
.map(keyModifier => `$event.${keyModifier}Key`)
.join('||')
)
} else {
keys.push(key)
}
}
if (keys.length) {
code += genKeyFilter(keys)
}
// Make sure modifiers like prevent and stop get executed after key filtering
if (genModifierCode) {
code += genModifierCode
}
const handlerCode = isMethodPath
? `return ${handler.value}.apply(null, arguments)`
: isFunctionExpression
? `return (${handler.value}).apply(null, arguments)`
: isFunctionInvocation
? `return ${handler.value}`
: handler.value
return `function($event){${code}${handlerCode}}`
}
}
function genKeyFilter(keys: Array<string>): string {
return (
// make sure the key filters only apply to KeyboardEvents
// #9441: can't use 'keyCode' in $event because Chrome autofill fires fake
// key events that do not have keyCode property...
`if(!$event.type.indexOf('key')&&` +
`${keys.map(genFilterCode).join('&&')})return null;`
)
}
function genFilterCode(key: string): string {
const keyVal = parseInt(key, 10)
if (keyVal) {
return `$event.keyCode!==${keyVal}`
}
const keyCode = keyCodes[key]
const keyName = keyNames[key]
return (
`_k($event.keyCode,` +
`${JSON.stringify(key)},` +
`${JSON.stringify(keyCode)},` +
`$event.key,` +
`${JSON.stringify(keyName)}` +
`)`
)
}

668
node_modules/vue/src/compiler/codegen/index.ts generated vendored Normal file
View File

@@ -0,0 +1,668 @@
import { genHandlers } from './events'
import baseDirectives from '../directives/index'
import { camelize, no, extend, capitalize } from 'shared/util'
import { baseWarn, pluckModuleFunction } from '../helpers'
import { emptySlotScopeToken } from '../parser/index'
import {
ASTAttr,
ASTDirective,
ASTElement,
ASTExpression,
ASTIfConditions,
ASTNode,
ASTText,
CompilerOptions
} from 'types/compiler'
import { BindingMetadata, BindingTypes } from 'sfc/types'
type TransformFunction = (el: ASTElement, code: string) => string
type DataGenFunction = (el: ASTElement) => string
type DirectiveFunction = (
el: ASTElement,
dir: ASTDirective,
warn: Function
) => boolean
export class CodegenState {
options: CompilerOptions
warn: Function
transforms: Array<TransformFunction>
dataGenFns: Array<DataGenFunction>
directives: { [key: string]: DirectiveFunction }
maybeComponent: (el: ASTElement) => boolean
onceId: number
staticRenderFns: Array<string>
pre: boolean
constructor(options: CompilerOptions) {
this.options = options
this.warn = options.warn || baseWarn
this.transforms = pluckModuleFunction(options.modules, 'transformCode')
this.dataGenFns = pluckModuleFunction(options.modules, 'genData')
this.directives = extend(extend({}, baseDirectives), options.directives)
const isReservedTag = options.isReservedTag || no
this.maybeComponent = (el: ASTElement) =>
!!el.component || !isReservedTag(el.tag)
this.onceId = 0
this.staticRenderFns = []
this.pre = false
}
}
export type CodegenResult = {
render: string
staticRenderFns: Array<string>
}
export function generate(
ast: ASTElement | void,
options: CompilerOptions
): CodegenResult {
const state = new CodegenState(options)
// fix #11483, Root level <script> tags should not be rendered.
const code = ast
? ast.tag === 'script'
? 'null'
: genElement(ast, state)
: '_c("div")'
return {
render: `with(this){return ${code}}`,
staticRenderFns: state.staticRenderFns
}
}
export function genElement(el: ASTElement, state: CodegenState): string {
if (el.parent) {
el.pre = el.pre || el.parent.pre
}
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
return genOnce(el, state)
} else if (el.for && !el.forProcessed) {
return genFor(el, state)
} else if (el.if && !el.ifProcessed) {
return genIf(el, state)
} else if (el.tag === 'template' && !el.slotTarget && !state.pre) {
return genChildren(el, state) || 'void 0'
} else if (el.tag === 'slot') {
return genSlot(el, state)
} else {
// component or element
let code
if (el.component) {
code = genComponent(el.component, el, state)
} else {
let data
const maybeComponent = state.maybeComponent(el)
if (!el.plain || (el.pre && maybeComponent)) {
data = genData(el, state)
}
let tag: string | undefined
// check if this is a component in <script setup>
const bindings = state.options.bindings
if (maybeComponent && bindings && bindings.__isScriptSetup !== false) {
tag = checkBindingType(bindings, el.tag)
}
if (!tag) tag = `'${el.tag}'`
const children = el.inlineTemplate ? null : genChildren(el, state, true)
code = `_c(${tag}${
data ? `,${data}` : '' // data
}${
children ? `,${children}` : '' // children
})`
}
// module transforms
for (let i = 0; i < state.transforms.length; i++) {
code = state.transforms[i](el, code)
}
return code
}
}
function checkBindingType(bindings: BindingMetadata, key: string) {
const camelName = camelize(key)
const PascalName = capitalize(camelName)
const checkType = (type) => {
if (bindings[key] === type) {
return key
}
if (bindings[camelName] === type) {
return camelName
}
if (bindings[PascalName] === type) {
return PascalName
}
}
const fromConst =
checkType(BindingTypes.SETUP_CONST) ||
checkType(BindingTypes.SETUP_REACTIVE_CONST)
if (fromConst) {
return fromConst
}
const fromMaybeRef =
checkType(BindingTypes.SETUP_LET) ||
checkType(BindingTypes.SETUP_REF) ||
checkType(BindingTypes.SETUP_MAYBE_REF)
if (fromMaybeRef) {
return fromMaybeRef
}
}
// hoist static sub-trees out
function genStatic(el: ASTElement, state: CodegenState): string {
el.staticProcessed = true
// Some elements (templates) need to behave differently inside of a v-pre
// node. All pre nodes are static roots, so we can use this as a location to
// wrap a state change and reset it upon exiting the pre node.
const originalPreState = state.pre
if (el.pre) {
state.pre = el.pre
}
state.staticRenderFns.push(`with(this){return ${genElement(el, state)}}`)
state.pre = originalPreState
return `_m(${state.staticRenderFns.length - 1}${
el.staticInFor ? ',true' : ''
})`
}
// v-once
function genOnce(el: ASTElement, state: CodegenState): string {
el.onceProcessed = true
if (el.if && !el.ifProcessed) {
return genIf(el, state)
} else if (el.staticInFor) {
let key = ''
let parent = el.parent
while (parent) {
if (parent.for) {
key = parent.key!
break
}
parent = parent.parent
}
if (!key) {
__DEV__ &&
state.warn(
`v-once can only be used inside v-for that is keyed. `,
el.rawAttrsMap['v-once']
)
return genElement(el, state)
}
return `_o(${genElement(el, state)},${state.onceId++},${key})`
} else {
return genStatic(el, state)
}
}
export function genIf(
el: any,
state: CodegenState,
altGen?: Function,
altEmpty?: string
): string {
el.ifProcessed = true // avoid recursion
return genIfConditions(el.ifConditions.slice(), state, altGen, altEmpty)
}
function genIfConditions(
conditions: ASTIfConditions,
state: CodegenState,
altGen?: Function,
altEmpty?: string
): string {
if (!conditions.length) {
return altEmpty || '_e()'
}
const condition = conditions.shift()!
if (condition.exp) {
return `(${condition.exp})?${genTernaryExp(
condition.block
)}:${genIfConditions(conditions, state, altGen, altEmpty)}`
} else {
return `${genTernaryExp(condition.block)}`
}
// v-if with v-once should generate code like (a)?_m(0):_m(1)
function genTernaryExp(el) {
return altGen
? altGen(el, state)
: el.once
? genOnce(el, state)
: genElement(el, state)
}
}
export function genFor(
el: any,
state: CodegenState,
altGen?: Function,
altHelper?: string
): string {
const exp = el.for
const alias = el.alias
const iterator1 = el.iterator1 ? `,${el.iterator1}` : ''
const iterator2 = el.iterator2 ? `,${el.iterator2}` : ''
if (
__DEV__ &&
state.maybeComponent(el) &&
el.tag !== 'slot' &&
el.tag !== 'template' &&
!el.key
) {
state.warn(
`<${el.tag} v-for="${alias} in ${exp}">: component lists rendered with ` +
`v-for should have explicit keys. ` +
`See https://v2.vuejs.org/v2/guide/list.html#key for more info.`,
el.rawAttrsMap['v-for'],
true /* tip */
)
}
el.forProcessed = true // avoid recursion
return (
`${altHelper || '_l'}((${exp}),` +
`function(${alias}${iterator1}${iterator2}){` +
`return ${(altGen || genElement)(el, state)}` +
'})'
)
}
export function genData(el: ASTElement, state: CodegenState): string {
let data = '{'
// directives first.
// directives may mutate the el's other properties before they are generated.
const dirs = genDirectives(el, state)
if (dirs) data += dirs + ','
// key
if (el.key) {
data += `key:${el.key},`
}
// ref
if (el.ref) {
data += `ref:${el.ref},`
}
if (el.refInFor) {
data += `refInFor:true,`
}
// pre
if (el.pre) {
data += `pre:true,`
}
// record original tag name for components using "is" attribute
if (el.component) {
data += `tag:"${el.tag}",`
}
// module data generation functions
for (let i = 0; i < state.dataGenFns.length; i++) {
data += state.dataGenFns[i](el)
}
// attributes
if (el.attrs) {
data += `attrs:${genProps(el.attrs)},`
}
// DOM props
if (el.props) {
data += `domProps:${genProps(el.props)},`
}
// event handlers
if (el.events) {
data += `${genHandlers(el.events, false)},`
}
if (el.nativeEvents) {
data += `${genHandlers(el.nativeEvents, true)},`
}
// slot target
// only for non-scoped slots
if (el.slotTarget && !el.slotScope) {
data += `slot:${el.slotTarget},`
}
// scoped slots
if (el.scopedSlots) {
data += `${genScopedSlots(el, el.scopedSlots, state)},`
}
// component v-model
if (el.model) {
data += `model:{value:${el.model.value},callback:${el.model.callback},expression:${el.model.expression}},`
}
// inline-template
if (el.inlineTemplate) {
const inlineTemplate = genInlineTemplate(el, state)
if (inlineTemplate) {
data += `${inlineTemplate},`
}
}
data = data.replace(/,$/, '') + '}'
// v-bind dynamic argument wrap
// v-bind with dynamic arguments must be applied using the same v-bind object
// merge helper so that class/style/mustUseProp attrs are handled correctly.
if (el.dynamicAttrs) {
data = `_b(${data},"${el.tag}",${genProps(el.dynamicAttrs)})`
}
// v-bind data wrap
if (el.wrapData) {
data = el.wrapData(data)
}
// v-on data wrap
if (el.wrapListeners) {
data = el.wrapListeners(data)
}
return data
}
function genDirectives(el: ASTElement, state: CodegenState): string | void {
const dirs = el.directives
if (!dirs) return
let res = 'directives:['
let hasRuntime = false
let i, l, dir, needRuntime
for (i = 0, l = dirs.length; i < l; i++) {
dir = dirs[i]
needRuntime = true
const gen: DirectiveFunction = state.directives[dir.name]
if (gen) {
// compile-time directive that manipulates AST.
// returns true if it also needs a runtime counterpart.
needRuntime = !!gen(el, dir, state.warn)
}
if (needRuntime) {
hasRuntime = true
res += `{name:"${dir.name}",rawName:"${dir.rawName}"${
dir.value
? `,value:(${dir.value}),expression:${JSON.stringify(dir.value)}`
: ''
}${dir.arg ? `,arg:${dir.isDynamicArg ? dir.arg : `"${dir.arg}"`}` : ''}${
dir.modifiers ? `,modifiers:${JSON.stringify(dir.modifiers)}` : ''
}},`
}
}
if (hasRuntime) {
return res.slice(0, -1) + ']'
}
}
function genInlineTemplate(
el: ASTElement,
state: CodegenState
): string | undefined {
const ast = el.children[0]
if (__DEV__ && (el.children.length !== 1 || ast.type !== 1)) {
state.warn(
'Inline-template components must have exactly one child element.',
{ start: el.start }
)
}
if (ast && ast.type === 1) {
const inlineRenderFns = generate(ast, state.options)
return `inlineTemplate:{render:function(){${
inlineRenderFns.render
}},staticRenderFns:[${inlineRenderFns.staticRenderFns
.map(code => `function(){${code}}`)
.join(',')}]}`
}
}
function genScopedSlots(
el: ASTElement,
slots: { [key: string]: ASTElement },
state: CodegenState
): string {
// by default scoped slots are considered "stable", this allows child
// components with only scoped slots to skip forced updates from parent.
// but in some cases we have to bail-out of this optimization
// for example if the slot contains dynamic names, has v-if or v-for on them...
let needsForceUpdate =
el.for ||
Object.keys(slots).some(key => {
const slot = slots[key]
return (
slot.slotTargetDynamic || slot.if || slot.for || containsSlotChild(slot) // is passing down slot from parent which may be dynamic
)
})
// #9534: if a component with scoped slots is inside a conditional branch,
// it's possible for the same component to be reused but with different
// compiled slot content. To avoid that, we generate a unique key based on
// the generated code of all the slot contents.
let needsKey = !!el.if
// OR when it is inside another scoped slot or v-for (the reactivity may be
// disconnected due to the intermediate scope variable)
// #9438, #9506
// TODO: this can be further optimized by properly analyzing in-scope bindings
// and skip force updating ones that do not actually use scope variables.
if (!needsForceUpdate) {
let parent = el.parent
while (parent) {
if (
(parent.slotScope && parent.slotScope !== emptySlotScopeToken) ||
parent.for
) {
needsForceUpdate = true
break
}
if (parent.if) {
needsKey = true
}
parent = parent.parent
}
}
const generatedSlots = Object.keys(slots)
.map(key => genScopedSlot(slots[key], state))
.join(',')
return `scopedSlots:_u([${generatedSlots}]${
needsForceUpdate ? `,null,true` : ``
}${
!needsForceUpdate && needsKey ? `,null,false,${hash(generatedSlots)}` : ``
})`
}
function hash(str) {
let hash = 5381
let i = str.length
while (i) {
hash = (hash * 33) ^ str.charCodeAt(--i)
}
return hash >>> 0
}
function containsSlotChild(el: ASTNode): boolean {
if (el.type === 1) {
if (el.tag === 'slot') {
return true
}
return el.children.some(containsSlotChild)
}
return false
}
function genScopedSlot(el: ASTElement, state: CodegenState): string {
const isLegacySyntax = el.attrsMap['slot-scope']
if (el.if && !el.ifProcessed && !isLegacySyntax) {
return genIf(el, state, genScopedSlot, `null`)
}
if (el.for && !el.forProcessed) {
return genFor(el, state, genScopedSlot)
}
const slotScope =
el.slotScope === emptySlotScopeToken ? `` : String(el.slotScope)
const fn =
`function(${slotScope}){` +
`return ${
el.tag === 'template'
? el.if && isLegacySyntax
? `(${el.if})?${genChildren(el, state) || 'undefined'}:undefined`
: genChildren(el, state) || 'undefined'
: genElement(el, state)
}}`
// reverse proxy v-slot without scope on this.$slots
const reverseProxy = slotScope ? `` : `,proxy:true`
return `{key:${el.slotTarget || `"default"`},fn:${fn}${reverseProxy}}`
}
export function genChildren(
el: ASTElement,
state: CodegenState,
checkSkip?: boolean,
altGenElement?: Function,
altGenNode?: Function
): string | void {
const children = el.children
if (children.length) {
const el: any = children[0]
// optimize single v-for
if (
children.length === 1 &&
el.for &&
el.tag !== 'template' &&
el.tag !== 'slot'
) {
const normalizationType = checkSkip
? state.maybeComponent(el)
? `,1`
: `,0`
: ``
return `${(altGenElement || genElement)(el, state)}${normalizationType}`
}
const normalizationType = checkSkip
? getNormalizationType(children, state.maybeComponent)
: 0
const gen = altGenNode || genNode
return `[${children.map(c => gen(c, state)).join(',')}]${
normalizationType ? `,${normalizationType}` : ''
}`
}
}
// determine the normalization needed for the children array.
// 0: no normalization needed
// 1: simple normalization needed (possible 1-level deep nested array)
// 2: full normalization needed
function getNormalizationType(
children: Array<ASTNode>,
maybeComponent: (el: ASTElement) => boolean
): number {
let res = 0
for (let i = 0; i < children.length; i++) {
const el: ASTNode = children[i]
if (el.type !== 1) {
continue
}
if (
needsNormalization(el) ||
(el.ifConditions &&
el.ifConditions.some(c => needsNormalization(c.block)))
) {
res = 2
break
}
if (
maybeComponent(el) ||
(el.ifConditions && el.ifConditions.some(c => maybeComponent(c.block)))
) {
res = 1
}
}
return res
}
function needsNormalization(el: ASTElement): boolean {
return el.for !== undefined || el.tag === 'template' || el.tag === 'slot'
}
function genNode(node: ASTNode, state: CodegenState): string {
if (node.type === 1) {
return genElement(node, state)
} else if (node.type === 3 && node.isComment) {
return genComment(node)
} else {
return genText(node)
}
}
export function genText(text: ASTText | ASTExpression): string {
return `_v(${
text.type === 2
? text.expression // no need for () because already wrapped in _s()
: transformSpecialNewlines(JSON.stringify(text.text))
})`
}
export function genComment(comment: ASTText): string {
return `_e(${JSON.stringify(comment.text)})`
}
function genSlot(el: ASTElement, state: CodegenState): string {
const slotName = el.slotName || '"default"'
const children = genChildren(el, state)
let res = `_t(${slotName}${children ? `,function(){return ${children}}` : ''}`
const attrs =
el.attrs || el.dynamicAttrs
? genProps(
(el.attrs || []).concat(el.dynamicAttrs || []).map(attr => ({
// slot props are camelized
name: camelize(attr.name),
value: attr.value,
dynamic: attr.dynamic
}))
)
: null
const bind = el.attrsMap['v-bind']
if ((attrs || bind) && !children) {
res += `,null`
}
if (attrs) {
res += `,${attrs}`
}
if (bind) {
res += `${attrs ? '' : ',null'},${bind}`
}
return res + ')'
}
// componentName is el.component, take it as argument to shun flow's pessimistic refinement
function genComponent(
componentName: string,
el: ASTElement,
state: CodegenState
): string {
const children = el.inlineTemplate ? null : genChildren(el, state, true)
return `_c(${componentName},${genData(el, state)}${
children ? `,${children}` : ''
})`
}
function genProps(props: Array<ASTAttr>): string {
let staticProps = ``
let dynamicProps = ``
for (let i = 0; i < props.length; i++) {
const prop = props[i]
const value = transformSpecialNewlines(prop.value)
if (prop.dynamic) {
dynamicProps += `${prop.name},${value},`
} else {
staticProps += `"${prop.name}":${value},`
}
}
staticProps = `{${staticProps.slice(0, -1)}}`
if (dynamicProps) {
return `_d(${staticProps},[${dynamicProps.slice(0, -1)}])`
} else {
return staticProps
}
}
// #3895, #4268
function transformSpecialNewlines(text: string): string {
return text.replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029')
}

83
node_modules/vue/src/compiler/create-compiler.ts generated vendored Normal file
View File

@@ -0,0 +1,83 @@
import { extend } from 'shared/util'
import { CompilerOptions, CompiledResult, WarningMessage } from 'types/compiler'
import { detectErrors } from './error-detector'
import { createCompileToFunctionFn } from './to-function'
export function createCompilerCreator(baseCompile: Function): Function {
return function createCompiler(baseOptions: CompilerOptions) {
function compile(
template: string,
options?: CompilerOptions
): CompiledResult {
const finalOptions = Object.create(baseOptions)
const errors: WarningMessage[] = []
const tips: WarningMessage[] = []
let warn = (
msg: WarningMessage,
range: { start: number; end: number },
tip: string
) => {
;(tip ? tips : errors).push(msg)
}
if (options) {
if (__DEV__ && options.outputSourceRange) {
// $flow-disable-line
const leadingSpaceLength = template.match(/^\s*/)![0].length
warn = (
msg: WarningMessage | string,
range: { start: number; end: number },
tip: string
) => {
const data: WarningMessage = typeof msg === 'string' ? { msg } : msg
if (range) {
if (range.start != null) {
data.start = range.start + leadingSpaceLength
}
if (range.end != null) {
data.end = range.end + leadingSpaceLength
}
}
;(tip ? tips : errors).push(data)
}
}
// merge custom modules
if (options.modules) {
finalOptions.modules = (baseOptions.modules || []).concat(
options.modules
)
}
// merge custom directives
if (options.directives) {
finalOptions.directives = extend(
Object.create(baseOptions.directives || null),
options.directives
)
}
// copy other options
for (const key in options) {
if (key !== 'modules' && key !== 'directives') {
finalOptions[key] = options[key as keyof CompilerOptions]
}
}
}
finalOptions.warn = warn
const compiled = baseCompile(template.trim(), finalOptions)
if (__DEV__) {
detectErrors(compiled.ast, warn)
}
compiled.errors = errors
compiled.tips = tips
return compiled
}
return {
compile,
compileToFunctions: createCompileToFunctionFn(compile)
}
}
}

9
node_modules/vue/src/compiler/directives/bind.ts generated vendored Normal file
View File

@@ -0,0 +1,9 @@
import { ASTDirective, ASTElement } from 'types/compiler'
export default function bind(el: ASTElement, dir: ASTDirective) {
el.wrapData = (code: string) => {
return `_b(${code},'${el.tag}',${dir.value},${
dir.modifiers && dir.modifiers.prop ? 'true' : 'false'
}${dir.modifiers && dir.modifiers.sync ? ',true' : ''})`
}
}

9
node_modules/vue/src/compiler/directives/index.ts generated vendored Normal file
View File

@@ -0,0 +1,9 @@
import on from './on'
import bind from './bind'
import { noop } from 'shared/util'
export default {
on,
bind,
cloak: noop
}

145
node_modules/vue/src/compiler/directives/model.ts generated vendored Normal file
View File

@@ -0,0 +1,145 @@
import { ASTElement, ASTModifiers } from 'types/compiler'
/**
* Cross-platform code generation for component v-model
*/
export function genComponentModel(
el: ASTElement,
value: string,
modifiers: ASTModifiers | null
): void {
const { number, trim } = modifiers || {}
const baseValueExpression = '$$v'
let valueExpression = baseValueExpression
if (trim) {
valueExpression =
`(typeof ${baseValueExpression} === 'string'` +
`? ${baseValueExpression}.trim()` +
`: ${baseValueExpression})`
}
if (number) {
valueExpression = `_n(${valueExpression})`
}
const assignment = genAssignmentCode(value, valueExpression)
el.model = {
value: `(${value})`,
expression: JSON.stringify(value),
callback: `function (${baseValueExpression}) {${assignment}}`
}
}
/**
* Cross-platform codegen helper for generating v-model value assignment code.
*/
export function genAssignmentCode(value: string, assignment: string): string {
const res = parseModel(value)
if (res.key === null) {
return `${value}=${assignment}`
} else {
return `$set(${res.exp}, ${res.key}, ${assignment})`
}
}
/**
* Parse a v-model expression into a base path and a final key segment.
* Handles both dot-path and possible square brackets.
*
* Possible cases:
*
* - test
* - test[key]
* - test[test1[key]]
* - test["a"][key]
* - xxx.test[a[a].test1[key]]
* - test.xxx.a["asa"][test1[key]]
*
*/
let len, str, chr, index, expressionPos, expressionEndPos
type ModelParseResult = {
exp: string
key: string | null
}
export function parseModel(val: string): ModelParseResult {
// Fix https://github.com/vuejs/vue/pull/7730
// allow v-model="obj.val " (trailing whitespace)
val = val.trim()
len = val.length
if (val.indexOf('[') < 0 || val.lastIndexOf(']') < len - 1) {
index = val.lastIndexOf('.')
if (index > -1) {
return {
exp: val.slice(0, index),
key: '"' + val.slice(index + 1) + '"'
}
} else {
return {
exp: val,
key: null
}
}
}
str = val
index = expressionPos = expressionEndPos = 0
while (!eof()) {
chr = next()
/* istanbul ignore if */
if (isStringStart(chr)) {
parseString(chr)
} else if (chr === 0x5b) {
parseBracket(chr)
}
}
return {
exp: val.slice(0, expressionPos),
key: val.slice(expressionPos + 1, expressionEndPos)
}
}
function next(): number {
return str.charCodeAt(++index)
}
function eof(): boolean {
return index >= len
}
function isStringStart(chr: number): boolean {
return chr === 0x22 || chr === 0x27
}
function parseBracket(chr: number): void {
let inBracket = 1
expressionPos = index
while (!eof()) {
chr = next()
if (isStringStart(chr)) {
parseString(chr)
continue
}
if (chr === 0x5b) inBracket++
if (chr === 0x5d) inBracket--
if (inBracket === 0) {
expressionEndPos = index
break
}
}
}
function parseString(chr: number): void {
const stringQuote = chr
while (!eof()) {
chr = next()
if (chr === stringQuote) {
break
}
}
}

9
node_modules/vue/src/compiler/directives/on.ts generated vendored Normal file
View File

@@ -0,0 +1,9 @@
import { warn } from 'core/util/index'
import { ASTDirective, ASTElement } from 'types/compiler'
export default function on(el: ASTElement, dir: ASTDirective) {
if (__DEV__ && dir.modifiers) {
warn(`v-on without argument does not support modifiers.`)
}
el.wrapListeners = (code: string) => `_g(${code},${dir.value})`
}

158
node_modules/vue/src/compiler/error-detector.ts generated vendored Normal file
View File

@@ -0,0 +1,158 @@
import { ASTElement, ASTNode } from 'types/compiler'
import { dirRE, onRE } from './parser/index'
type Range = { start?: number; end?: number }
// these keywords should not appear inside expressions, but operators like
// typeof, instanceof and in are allowed
const prohibitedKeywordRE = new RegExp(
'\\b' +
(
'do,if,for,let,new,try,var,case,else,with,await,break,catch,class,const,' +
'super,throw,while,yield,delete,export,import,return,switch,default,' +
'extends,finally,continue,debugger,function,arguments'
)
.split(',')
.join('\\b|\\b') +
'\\b'
)
// these unary operators should not be used as property/method names
const unaryOperatorsRE = new RegExp(
'\\b' +
'delete,typeof,void'.split(',').join('\\s*\\([^\\)]*\\)|\\b') +
'\\s*\\([^\\)]*\\)'
)
// strip strings in expressions
const stripStringRE =
/'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\]|\\.)*`|`(?:[^`\\]|\\.)*`/g
// detect problematic expressions in a template
export function detectErrors(ast: ASTNode | undefined, warn: Function) {
if (ast) {
checkNode(ast, warn)
}
}
function checkNode(node: ASTNode, warn: Function) {
if (node.type === 1) {
for (const name in node.attrsMap) {
if (dirRE.test(name)) {
const value = node.attrsMap[name]
if (value) {
const range = node.rawAttrsMap[name]
if (name === 'v-for') {
checkFor(node, `v-for="${value}"`, warn, range)
} else if (name === 'v-slot' || name[0] === '#') {
checkFunctionParameterExpression(
value,
`${name}="${value}"`,
warn,
range
)
} else if (onRE.test(name)) {
checkEvent(value, `${name}="${value}"`, warn, range)
} else {
checkExpression(value, `${name}="${value}"`, warn, range)
}
}
}
}
if (node.children) {
for (let i = 0; i < node.children.length; i++) {
checkNode(node.children[i], warn)
}
}
} else if (node.type === 2) {
checkExpression(node.expression, node.text, warn, node)
}
}
function checkEvent(exp: string, text: string, warn: Function, range?: Range) {
const stripped = exp.replace(stripStringRE, '')
const keywordMatch: any = stripped.match(unaryOperatorsRE)
if (keywordMatch && stripped.charAt(keywordMatch.index - 1) !== '$') {
warn(
`avoid using JavaScript unary operator as property name: ` +
`"${keywordMatch[0]}" in expression ${text.trim()}`,
range
)
}
checkExpression(exp, text, warn, range)
}
function checkFor(
node: ASTElement,
text: string,
warn: Function,
range?: Range
) {
checkExpression(node.for || '', text, warn, range)
checkIdentifier(node.alias, 'v-for alias', text, warn, range)
checkIdentifier(node.iterator1, 'v-for iterator', text, warn, range)
checkIdentifier(node.iterator2, 'v-for iterator', text, warn, range)
}
function checkIdentifier(
ident: string | null | undefined,
type: string,
text: string,
warn: Function,
range?: Range
) {
if (typeof ident === 'string') {
try {
new Function(`var ${ident}=_`)
} catch (e: any) {
warn(`invalid ${type} "${ident}" in expression: ${text.trim()}`, range)
}
}
}
function checkExpression(
exp: string,
text: string,
warn: Function,
range?: Range
) {
try {
new Function(`return ${exp}`)
} catch (e: any) {
const keywordMatch = exp
.replace(stripStringRE, '')
.match(prohibitedKeywordRE)
if (keywordMatch) {
warn(
`avoid using JavaScript keyword as property name: ` +
`"${keywordMatch[0]}"\n Raw expression: ${text.trim()}`,
range
)
} else {
warn(
`invalid expression: ${e.message} in\n\n` +
` ${exp}\n\n` +
` Raw expression: ${text.trim()}\n`,
range
)
}
}
}
function checkFunctionParameterExpression(
exp: string,
text: string,
warn: Function,
range?: Range
) {
try {
new Function(exp, '')
} catch (e: any) {
warn(
`invalid function parameter expression: ${e.message} in\n\n` +
` ${exp}\n\n` +
` Raw expression: ${text.trim()}\n`,
range
)
}
}

243
node_modules/vue/src/compiler/helpers.ts generated vendored Normal file
View File

@@ -0,0 +1,243 @@
import { emptyObject } from 'shared/util'
import { ASTElement, ASTModifiers } from 'types/compiler'
import { parseFilters } from './parser/filter-parser'
type Range = { start?: number; end?: number }
/* eslint-disable no-unused-vars */
export function baseWarn(msg: string, range?: Range) {
console.error(`[Vue compiler]: ${msg}`)
}
/* eslint-enable no-unused-vars */
export function pluckModuleFunction<T, K extends keyof T>(
modules: Array<T> | undefined,
key: K
): Array<Exclude<T[K], undefined>> {
return modules ? (modules.map(m => m[key]).filter(_ => _) as any) : []
}
export function addProp(
el: ASTElement,
name: string,
value: string,
range?: Range,
dynamic?: boolean
) {
;(el.props || (el.props = [])).push(
rangeSetItem({ name, value, dynamic }, range)
)
el.plain = false
}
export function addAttr(
el: ASTElement,
name: string,
value: any,
range?: Range,
dynamic?: boolean
) {
const attrs = dynamic
? el.dynamicAttrs || (el.dynamicAttrs = [])
: el.attrs || (el.attrs = [])
attrs.push(rangeSetItem({ name, value, dynamic }, range))
el.plain = false
}
// add a raw attr (use this in preTransforms)
export function addRawAttr(
el: ASTElement,
name: string,
value: any,
range?: Range
) {
el.attrsMap[name] = value
el.attrsList.push(rangeSetItem({ name, value }, range))
}
export function addDirective(
el: ASTElement,
name: string,
rawName: string,
value: string,
arg?: string,
isDynamicArg?: boolean,
modifiers?: ASTModifiers,
range?: Range
) {
;(el.directives || (el.directives = [])).push(
rangeSetItem(
{
name,
rawName,
value,
arg,
isDynamicArg,
modifiers
},
range
)
)
el.plain = false
}
function prependModifierMarker(
symbol: string,
name: string,
dynamic?: boolean
): string {
return dynamic ? `_p(${name},"${symbol}")` : symbol + name // mark the event as captured
}
export function addHandler(
el: ASTElement,
name: string,
value: string,
modifiers?: ASTModifiers | null,
important?: boolean,
warn?: Function,
range?: Range,
dynamic?: boolean
) {
modifiers = modifiers || emptyObject
// warn prevent and passive modifier
/* istanbul ignore if */
if (__DEV__ && warn && modifiers.prevent && modifiers.passive) {
warn(
"passive and prevent can't be used together. " +
"Passive handler can't prevent default event.",
range
)
}
// normalize click.right and click.middle since they don't actually fire
// this is technically browser-specific, but at least for now browsers are
// the only target envs that have right/middle clicks.
if (modifiers.right) {
if (dynamic) {
name = `(${name})==='click'?'contextmenu':(${name})`
} else if (name === 'click') {
name = 'contextmenu'
delete modifiers.right
}
} else if (modifiers.middle) {
if (dynamic) {
name = `(${name})==='click'?'mouseup':(${name})`
} else if (name === 'click') {
name = 'mouseup'
}
}
// check capture modifier
if (modifiers.capture) {
delete modifiers.capture
name = prependModifierMarker('!', name, dynamic)
}
if (modifiers.once) {
delete modifiers.once
name = prependModifierMarker('~', name, dynamic)
}
/* istanbul ignore if */
if (modifiers.passive) {
delete modifiers.passive
name = prependModifierMarker('&', name, dynamic)
}
let events
if (modifiers.native) {
delete modifiers.native
events = el.nativeEvents || (el.nativeEvents = {})
} else {
events = el.events || (el.events = {})
}
const newHandler: any = rangeSetItem({ value: value.trim(), dynamic }, range)
if (modifiers !== emptyObject) {
newHandler.modifiers = modifiers
}
const handlers = events[name]
/* istanbul ignore if */
if (Array.isArray(handlers)) {
important ? handlers.unshift(newHandler) : handlers.push(newHandler)
} else if (handlers) {
events[name] = important ? [newHandler, handlers] : [handlers, newHandler]
} else {
events[name] = newHandler
}
el.plain = false
}
export function getRawBindingAttr(el: ASTElement, name: string) {
return (
el.rawAttrsMap[':' + name] ||
el.rawAttrsMap['v-bind:' + name] ||
el.rawAttrsMap[name]
)
}
export function getBindingAttr(
el: ASTElement,
name: string,
getStatic?: boolean
): string | undefined {
const dynamicValue =
getAndRemoveAttr(el, ':' + name) || getAndRemoveAttr(el, 'v-bind:' + name)
if (dynamicValue != null) {
return parseFilters(dynamicValue)
} else if (getStatic !== false) {
const staticValue = getAndRemoveAttr(el, name)
if (staticValue != null) {
return JSON.stringify(staticValue)
}
}
}
// note: this only removes the attr from the Array (attrsList) so that it
// doesn't get processed by processAttrs.
// By default it does NOT remove it from the map (attrsMap) because the map is
// needed during codegen.
export function getAndRemoveAttr(
el: ASTElement,
name: string,
removeFromMap?: boolean
): string | undefined {
let val
if ((val = el.attrsMap[name]) != null) {
const list = el.attrsList
for (let i = 0, l = list.length; i < l; i++) {
if (list[i].name === name) {
list.splice(i, 1)
break
}
}
}
if (removeFromMap) {
delete el.attrsMap[name]
}
return val
}
export function getAndRemoveAttrByRegex(el: ASTElement, name: RegExp) {
const list = el.attrsList
for (let i = 0, l = list.length; i < l; i++) {
const attr = list[i]
if (name.test(attr.name)) {
list.splice(i, 1)
return attr
}
}
}
function rangeSetItem(item: any, range?: { start?: number; end?: number }) {
if (range) {
if (range.start != null) {
item.start = range.start
}
if (range.end != null) {
item.end = range.end
}
}
return item
}

24
node_modules/vue/src/compiler/index.ts generated vendored Normal file
View File

@@ -0,0 +1,24 @@
import { parse } from './parser/index'
import { optimize } from './optimizer'
import { generate } from './codegen/index'
import { createCompilerCreator } from './create-compiler'
import { CompilerOptions, CompiledResult } from 'types/compiler'
// `createCompilerCreator` allows creating compilers that use alternative
// parser/optimizer/codegen, e.g the SSR optimizing compiler.
// Here we just export a default compiler using the default parts.
export const createCompiler = createCompilerCreator(function baseCompile(
template: string,
options: CompilerOptions
): CompiledResult {
const ast = parse(template.trim(), options)
if (options.optimize !== false) {
optimize(ast, options)
}
const code = generate(ast, options)
return {
ast,
render: code.render,
staticRenderFns: code.staticRenderFns
}
})

135
node_modules/vue/src/compiler/optimizer.ts generated vendored Normal file
View File

@@ -0,0 +1,135 @@
import { makeMap, isBuiltInTag, cached, no } from 'shared/util'
import { ASTElement, CompilerOptions, ASTNode } from 'types/compiler'
let isStaticKey
let isPlatformReservedTag
const genStaticKeysCached = cached(genStaticKeys)
/**
* Goal of the optimizer: walk the generated template AST tree
* and detect sub-trees that are purely static, i.e. parts of
* the DOM that never needs to change.
*
* Once we detect these sub-trees, we can:
*
* 1. Hoist them into constants, so that we no longer need to
* create fresh nodes for them on each re-render;
* 2. Completely skip them in the patching process.
*/
export function optimize(
root: ASTElement | null | undefined,
options: CompilerOptions
) {
if (!root) return
isStaticKey = genStaticKeysCached(options.staticKeys || '')
isPlatformReservedTag = options.isReservedTag || no
// first pass: mark all non-static nodes.
markStatic(root)
// second pass: mark static roots.
markStaticRoots(root, false)
}
function genStaticKeys(keys: string): Function {
return makeMap(
'type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap' +
(keys ? ',' + keys : '')
)
}
function markStatic(node: ASTNode) {
node.static = isStatic(node)
if (node.type === 1) {
// do not make component slot content static. this avoids
// 1. components not able to mutate slot nodes
// 2. static slot content fails for hot-reloading
if (
!isPlatformReservedTag(node.tag) &&
node.tag !== 'slot' &&
node.attrsMap['inline-template'] == null
) {
return
}
for (let i = 0, l = node.children.length; i < l; i++) {
const child = node.children[i]
markStatic(child)
if (!child.static) {
node.static = false
}
}
if (node.ifConditions) {
for (let i = 1, l = node.ifConditions.length; i < l; i++) {
const block = node.ifConditions[i].block
markStatic(block)
if (!block.static) {
node.static = false
}
}
}
}
}
function markStaticRoots(node: ASTNode, isInFor: boolean) {
if (node.type === 1) {
if (node.static || node.once) {
node.staticInFor = isInFor
}
// For a node to qualify as a static root, it should have children that
// are not just static text. Otherwise the cost of hoisting out will
// outweigh the benefits and it's better off to just always render it fresh.
if (
node.static &&
node.children.length &&
!(node.children.length === 1 && node.children[0].type === 3)
) {
node.staticRoot = true
return
} else {
node.staticRoot = false
}
if (node.children) {
for (let i = 0, l = node.children.length; i < l; i++) {
markStaticRoots(node.children[i], isInFor || !!node.for)
}
}
if (node.ifConditions) {
for (let i = 1, l = node.ifConditions.length; i < l; i++) {
markStaticRoots(node.ifConditions[i].block, isInFor)
}
}
}
}
function isStatic(node: ASTNode): boolean {
if (node.type === 2) {
// expression
return false
}
if (node.type === 3) {
// text
return true
}
return !!(
node.pre ||
(!node.hasBindings && // no dynamic bindings
!node.if &&
!node.for && // not v-if or v-for or v-else
!isBuiltInTag(node.tag) && // not a built-in
isPlatformReservedTag(node.tag) && // not a component
!isDirectChildOfTemplateFor(node) &&
Object.keys(node).every(isStaticKey))
)
}
function isDirectChildOfTemplateFor(node: ASTElement): boolean {
while (node.parent) {
node = node.parent
if (node.tag !== 'template') {
return false
}
if (node.for) {
return true
}
}
return false
}

View File

@@ -0,0 +1,9 @@
let decoder
export default {
decode(html: string): string {
decoder = decoder || document.createElement('div')
decoder.innerHTML = html
return decoder.textContent
}
}

116
node_modules/vue/src/compiler/parser/filter-parser.ts generated vendored Normal file
View File

@@ -0,0 +1,116 @@
const validDivisionCharRE = /[\w).+\-_$\]]/
export function parseFilters(exp: string): string {
let inSingle = false
let inDouble = false
let inTemplateString = false
let inRegex = false
let curly = 0
let square = 0
let paren = 0
let lastFilterIndex = 0
let c, prev, i, expression, filters
for (i = 0; i < exp.length; i++) {
prev = c
c = exp.charCodeAt(i)
if (inSingle) {
if (c === 0x27 && prev !== 0x5c) inSingle = false
} else if (inDouble) {
if (c === 0x22 && prev !== 0x5c) inDouble = false
} else if (inTemplateString) {
if (c === 0x60 && prev !== 0x5c) inTemplateString = false
} else if (inRegex) {
if (c === 0x2f && prev !== 0x5c) inRegex = false
} else if (
c === 0x7c && // pipe
exp.charCodeAt(i + 1) !== 0x7c &&
exp.charCodeAt(i - 1) !== 0x7c &&
!curly &&
!square &&
!paren
) {
if (expression === undefined) {
// first filter, end of expression
lastFilterIndex = i + 1
expression = exp.slice(0, i).trim()
} else {
pushFilter()
}
} else {
switch (c) {
case 0x22:
inDouble = true
break // "
case 0x27:
inSingle = true
break // '
case 0x60:
inTemplateString = true
break // `
case 0x28:
paren++
break // (
case 0x29:
paren--
break // )
case 0x5b:
square++
break // [
case 0x5d:
square--
break // ]
case 0x7b:
curly++
break // {
case 0x7d:
curly--
break // }
}
if (c === 0x2f) {
// /
let j = i - 1
let p
// find first non-whitespace prev char
for (; j >= 0; j--) {
p = exp.charAt(j)
if (p !== ' ') break
}
if (!p || !validDivisionCharRE.test(p)) {
inRegex = true
}
}
}
}
if (expression === undefined) {
expression = exp.slice(0, i).trim()
} else if (lastFilterIndex !== 0) {
pushFilter()
}
function pushFilter() {
;(filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim())
lastFilterIndex = i + 1
}
if (filters) {
for (i = 0; i < filters.length; i++) {
expression = wrapFilter(expression, filters[i])
}
}
return expression
}
function wrapFilter(exp: string, filter: string): string {
const i = filter.indexOf('(')
if (i < 0) {
// _f: resolveFilter
return `_f("${filter}")(${exp})`
} else {
const name = filter.slice(0, i)
const args = filter.slice(i + 1)
return `_f("${name}")(${exp}${args !== ')' ? ',' + args : args}`
}
}

341
node_modules/vue/src/compiler/parser/html-parser.ts generated vendored Normal file
View File

@@ -0,0 +1,341 @@
/**
* Not type-checking this file because it's mostly vendor code.
*/
/*!
* HTML Parser By John Resig (ejohn.org)
* Modified by Juriy "kangax" Zaytsev
* Original code by Erik Arvidsson (MPL-1.1 OR Apache-2.0 OR GPL-2.0-or-later)
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
*/
import { makeMap, no } from 'shared/util'
import { isNonPhrasingTag } from 'web/compiler/util'
import { unicodeRegExp } from 'core/util/lang'
import { ASTAttr, CompilerOptions } from 'types/compiler'
// Regular Expressions for parsing tags and attributes
const attribute =
/^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
const dynamicArgAttribute =
/^\s*((?:v-[\w-]+:|@|:|#)\[[^=]+?\][^\s"'<>\/=]*)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z${unicodeRegExp.source}]*`
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
const startTagOpen = new RegExp(`^<${qnameCapture}`)
const startTagClose = /^\s*(\/?)>/
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
const doctype = /^<!DOCTYPE [^>]+>/i
// #7298: escape - to avoid being passed as HTML comment when inlined in page
const comment = /^<!\--/
const conditionalComment = /^<!\[/
// Special Elements (can contain anything)
export const isPlainTextElement = makeMap('script,style,textarea', true)
const reCache = {}
const decodingMap = {
'&lt;': '<',
'&gt;': '>',
'&quot;': '"',
'&amp;': '&',
'&#10;': '\n',
'&#9;': '\t',
'&#39;': "'"
}
const encodedAttr = /&(?:lt|gt|quot|amp|#39);/g
const encodedAttrWithNewLines = /&(?:lt|gt|quot|amp|#39|#10|#9);/g
// #5992
const isIgnoreNewlineTag = makeMap('pre,textarea', true)
const shouldIgnoreFirstNewline = (tag, html) =>
tag && isIgnoreNewlineTag(tag) && html[0] === '\n'
function decodeAttr(value, shouldDecodeNewlines) {
const re = shouldDecodeNewlines ? encodedAttrWithNewLines : encodedAttr
return value.replace(re, match => decodingMap[match])
}
export interface HTMLParserOptions extends CompilerOptions {
start?: (
tag: string,
attrs: ASTAttr[],
unary: boolean,
start: number,
end: number
) => void
end?: (tag: string, start: number, end: number) => void
chars?: (text: string, start?: number, end?: number) => void
comment?: (content: string, start: number, end: number) => void
}
export function parseHTML(html, options: HTMLParserOptions) {
const stack: any[] = []
const expectHTML = options.expectHTML
const isUnaryTag = options.isUnaryTag || no
const canBeLeftOpenTag = options.canBeLeftOpenTag || no
let index = 0
let last, lastTag
while (html) {
last = html
// Make sure we're not in a plaintext content element like script/style
if (!lastTag || !isPlainTextElement(lastTag)) {
let textEnd = html.indexOf('<')
if (textEnd === 0) {
// Comment:
if (comment.test(html)) {
const commentEnd = html.indexOf('-->')
if (commentEnd >= 0) {
if (options.shouldKeepComment && options.comment) {
options.comment(
html.substring(4, commentEnd),
index,
index + commentEnd + 3
)
}
advance(commentEnd + 3)
continue
}
}
// https://en.wikipedia.org/wiki/Conditional_comment#Downlevel-revealed_conditional_comment
if (conditionalComment.test(html)) {
const conditionalEnd = html.indexOf(']>')
if (conditionalEnd >= 0) {
advance(conditionalEnd + 2)
continue
}
}
// Doctype:
const doctypeMatch = html.match(doctype)
if (doctypeMatch) {
advance(doctypeMatch[0].length)
continue
}
// End tag:
const endTagMatch = html.match(endTag)
if (endTagMatch) {
const curIndex = index
advance(endTagMatch[0].length)
parseEndTag(endTagMatch[1], curIndex, index)
continue
}
// Start tag:
const startTagMatch = parseStartTag()
if (startTagMatch) {
handleStartTag(startTagMatch)
if (shouldIgnoreFirstNewline(startTagMatch.tagName, html)) {
advance(1)
}
continue
}
}
let text, rest, next
if (textEnd >= 0) {
rest = html.slice(textEnd)
while (
!endTag.test(rest) &&
!startTagOpen.test(rest) &&
!comment.test(rest) &&
!conditionalComment.test(rest)
) {
// < in plain text, be forgiving and treat it as text
next = rest.indexOf('<', 1)
if (next < 0) break
textEnd += next
rest = html.slice(textEnd)
}
text = html.substring(0, textEnd)
}
if (textEnd < 0) {
text = html
}
if (text) {
advance(text.length)
}
if (options.chars && text) {
options.chars(text, index - text.length, index)
}
} else {
let endTagLength = 0
const stackedTag = lastTag.toLowerCase()
const reStackedTag =
reCache[stackedTag] ||
(reCache[stackedTag] = new RegExp(
'([\\s\\S]*?)(</' + stackedTag + '[^>]*>)',
'i'
))
const rest = html.replace(reStackedTag, function (all, text, endTag) {
endTagLength = endTag.length
if (!isPlainTextElement(stackedTag) && stackedTag !== 'noscript') {
text = text
.replace(/<!\--([\s\S]*?)-->/g, '$1') // #7298
.replace(/<!\[CDATA\[([\s\S]*?)]]>/g, '$1')
}
if (shouldIgnoreFirstNewline(stackedTag, text)) {
text = text.slice(1)
}
if (options.chars) {
options.chars(text)
}
return ''
})
index += html.length - rest.length
html = rest
parseEndTag(stackedTag, index - endTagLength, index)
}
if (html === last) {
options.chars && options.chars(html)
if (__DEV__ && !stack.length && options.warn) {
options.warn(`Mal-formatted tag at end of template: "${html}"`, {
start: index + html.length
})
}
break
}
}
// Clean up any remaining tags
parseEndTag()
function advance(n) {
index += n
html = html.substring(n)
}
function parseStartTag() {
const start = html.match(startTagOpen)
if (start) {
const match: any = {
tagName: start[1],
attrs: [],
start: index
}
advance(start[0].length)
let end, attr
while (
!(end = html.match(startTagClose)) &&
(attr = html.match(dynamicArgAttribute) || html.match(attribute))
) {
attr.start = index
advance(attr[0].length)
attr.end = index
match.attrs.push(attr)
}
if (end) {
match.unarySlash = end[1]
advance(end[0].length)
match.end = index
return match
}
}
}
function handleStartTag(match) {
const tagName = match.tagName
const unarySlash = match.unarySlash
if (expectHTML) {
if (lastTag === 'p' && isNonPhrasingTag(tagName)) {
parseEndTag(lastTag)
}
if (canBeLeftOpenTag(tagName) && lastTag === tagName) {
parseEndTag(tagName)
}
}
const unary = isUnaryTag(tagName) || !!unarySlash
const l = match.attrs.length
const attrs: ASTAttr[] = new Array(l)
for (let i = 0; i < l; i++) {
const args = match.attrs[i]
const value = args[3] || args[4] || args[5] || ''
const shouldDecodeNewlines =
tagName === 'a' && args[1] === 'href'
? options.shouldDecodeNewlinesForHref
: options.shouldDecodeNewlines
attrs[i] = {
name: args[1],
value: decodeAttr(value, shouldDecodeNewlines)
}
if (__DEV__ && options.outputSourceRange) {
attrs[i].start = args.start + args[0].match(/^\s*/).length
attrs[i].end = args.end
}
}
if (!unary) {
stack.push({
tag: tagName,
lowerCasedTag: tagName.toLowerCase(),
attrs: attrs,
start: match.start,
end: match.end
})
lastTag = tagName
}
if (options.start) {
options.start(tagName, attrs, unary, match.start, match.end)
}
}
function parseEndTag(tagName?: any, start?: any, end?: any) {
let pos, lowerCasedTagName
if (start == null) start = index
if (end == null) end = index
// Find the closest opened tag of the same type
if (tagName) {
lowerCasedTagName = tagName.toLowerCase()
for (pos = stack.length - 1; pos >= 0; pos--) {
if (stack[pos].lowerCasedTag === lowerCasedTagName) {
break
}
}
} else {
// If no tag name is provided, clean shop
pos = 0
}
if (pos >= 0) {
// Close all the open elements, up the stack
for (let i = stack.length - 1; i >= pos; i--) {
if (__DEV__ && (i > pos || !tagName) && options.warn) {
options.warn(`tag <${stack[i].tag}> has no matching end tag.`, {
start: stack[i].start,
end: stack[i].end
})
}
if (options.end) {
options.end(stack[i].tag, start, end)
}
}
// Remove the open elements from the stack
stack.length = pos
lastTag = pos && stack[pos - 1].tag
} else if (lowerCasedTagName === 'br') {
if (options.start) {
options.start(tagName, [], true, start, end)
}
} else if (lowerCasedTagName === 'p') {
if (options.start) {
options.start(tagName, [], false, start, end)
}
if (options.end) {
options.end(tagName, start, end)
}
}
}
}

999
node_modules/vue/src/compiler/parser/index.ts generated vendored Normal file
View File

@@ -0,0 +1,999 @@
import he from 'he'
import { parseHTML } from './html-parser'
import { parseText } from './text-parser'
import { parseFilters } from './filter-parser'
import { genAssignmentCode } from '../directives/model'
import { extend, cached, no, camelize, hyphenate } from 'shared/util'
import { isIE, isEdge, isServerRendering } from 'core/util/env'
import {
addProp,
addAttr,
baseWarn,
addHandler,
addDirective,
getBindingAttr,
getAndRemoveAttr,
getRawBindingAttr,
pluckModuleFunction,
getAndRemoveAttrByRegex
} from '../helpers'
import {
ASTAttr,
ASTElement,
ASTIfCondition,
ASTNode,
ASTText,
CompilerOptions
} from 'types/compiler'
export const onRE = /^@|^v-on:/
export const dirRE = process.env.VBIND_PROP_SHORTHAND
? /^v-|^@|^:|^\.|^#/
: /^v-|^@|^:|^#/
export const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/
export const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
const stripParensRE = /^\(|\)$/g
const dynamicArgRE = /^\[.*\]$/
const argRE = /:(.*)$/
export const bindRE = /^:|^\.|^v-bind:/
const propBindRE = /^\./
const modifierRE = /\.[^.\]]+(?=[^\]]*$)/g
export const slotRE = /^v-slot(:|$)|^#/
const lineBreakRE = /[\r\n]/
const whitespaceRE = /[ \f\t\r\n]+/g
const invalidAttributeRE = /[\s"'<>\/=]/
const decodeHTMLCached = cached(he.decode)
export const emptySlotScopeToken = `_empty_`
// configurable state
export let warn: any
let delimiters
let transforms
let preTransforms
let postTransforms
let platformIsPreTag
let platformMustUseProp
let platformGetTagNamespace
let maybeComponent
export function createASTElement(
tag: string,
attrs: Array<ASTAttr>,
parent: ASTElement | void
): ASTElement {
return {
type: 1,
tag,
attrsList: attrs,
attrsMap: makeAttrsMap(attrs),
rawAttrsMap: {},
parent,
children: []
}
}
/**
* Convert HTML string to AST.
*/
export function parse(template: string, options: CompilerOptions): ASTElement {
warn = options.warn || baseWarn
platformIsPreTag = options.isPreTag || no
platformMustUseProp = options.mustUseProp || no
platformGetTagNamespace = options.getTagNamespace || no
const isReservedTag = options.isReservedTag || no
maybeComponent = (el: ASTElement) =>
!!(
el.component ||
el.attrsMap[':is'] ||
el.attrsMap['v-bind:is'] ||
!(el.attrsMap.is ? isReservedTag(el.attrsMap.is) : isReservedTag(el.tag))
)
transforms = pluckModuleFunction(options.modules, 'transformNode')
preTransforms = pluckModuleFunction(options.modules, 'preTransformNode')
postTransforms = pluckModuleFunction(options.modules, 'postTransformNode')
delimiters = options.delimiters
const stack: any[] = []
const preserveWhitespace = options.preserveWhitespace !== false
const whitespaceOption = options.whitespace
let root
let currentParent
let inVPre = false
let inPre = false
let warned = false
function warnOnce(msg, range) {
if (!warned) {
warned = true
warn(msg, range)
}
}
function closeElement(element) {
trimEndingWhitespace(element)
if (!inVPre && !element.processed) {
element = processElement(element, options)
}
// tree management
if (!stack.length && element !== root) {
// allow root elements with v-if, v-else-if and v-else
if (root.if && (element.elseif || element.else)) {
if (__DEV__) {
checkRootConstraints(element)
}
addIfCondition(root, {
exp: element.elseif,
block: element
})
} else if (__DEV__) {
warnOnce(
`Component template should contain exactly one root element. ` +
`If you are using v-if on multiple elements, ` +
`use v-else-if to chain them instead.`,
{ start: element.start }
)
}
}
if (currentParent && !element.forbidden) {
if (element.elseif || element.else) {
processIfConditions(element, currentParent)
} else {
if (element.slotScope) {
// scoped slot
// keep it in the children list so that v-else(-if) conditions can
// find it as the prev node.
const name = element.slotTarget || '"default"'
;(currentParent.scopedSlots || (currentParent.scopedSlots = {}))[
name
] = element
}
currentParent.children.push(element)
element.parent = currentParent
}
}
// final children cleanup
// filter out scoped slots
element.children = element.children.filter(c => !c.slotScope)
// remove trailing whitespace node again
trimEndingWhitespace(element)
// check pre state
if (element.pre) {
inVPre = false
}
if (platformIsPreTag(element.tag)) {
inPre = false
}
// apply post-transforms
for (let i = 0; i < postTransforms.length; i++) {
postTransforms[i](element, options)
}
}
function trimEndingWhitespace(el) {
// remove trailing whitespace node
if (!inPre) {
let lastNode
while (
(lastNode = el.children[el.children.length - 1]) &&
lastNode.type === 3 &&
lastNode.text === ' '
) {
el.children.pop()
}
}
}
function checkRootConstraints(el) {
if (el.tag === 'slot' || el.tag === 'template') {
warnOnce(
`Cannot use <${el.tag}> as component root element because it may ` +
'contain multiple nodes.',
{ start: el.start }
)
}
if (el.attrsMap.hasOwnProperty('v-for')) {
warnOnce(
'Cannot use v-for on stateful component root element because ' +
'it renders multiple elements.',
el.rawAttrsMap['v-for']
)
}
}
parseHTML(template, {
warn,
expectHTML: options.expectHTML,
isUnaryTag: options.isUnaryTag,
canBeLeftOpenTag: options.canBeLeftOpenTag,
shouldDecodeNewlines: options.shouldDecodeNewlines,
shouldDecodeNewlinesForHref: options.shouldDecodeNewlinesForHref,
shouldKeepComment: options.comments,
outputSourceRange: options.outputSourceRange,
start(tag, attrs, unary, start, end) {
// check namespace.
// inherit parent ns if there is one
const ns =
(currentParent && currentParent.ns) || platformGetTagNamespace(tag)
// handle IE svg bug
/* istanbul ignore if */
if (isIE && ns === 'svg') {
attrs = guardIESVGBug(attrs)
}
let element: ASTElement = createASTElement(tag, attrs, currentParent)
if (ns) {
element.ns = ns
}
if (__DEV__) {
if (options.outputSourceRange) {
element.start = start
element.end = end
element.rawAttrsMap = element.attrsList.reduce((cumulated, attr) => {
cumulated[attr.name] = attr
return cumulated
}, {})
}
attrs.forEach(attr => {
if (invalidAttributeRE.test(attr.name)) {
warn(
`Invalid dynamic argument expression: attribute names cannot contain ` +
`spaces, quotes, <, >, / or =.`,
options.outputSourceRange
? {
start: attr.start! + attr.name.indexOf(`[`),
end: attr.start! + attr.name.length
}
: undefined
)
}
})
}
if (isForbiddenTag(element) && !isServerRendering()) {
element.forbidden = true
__DEV__ &&
warn(
'Templates should only be responsible for mapping the state to the ' +
'UI. Avoid placing tags with side-effects in your templates, such as ' +
`<${tag}>` +
', as they will not be parsed.',
{ start: element.start }
)
}
// apply pre-transforms
for (let i = 0; i < preTransforms.length; i++) {
element = preTransforms[i](element, options) || element
}
if (!inVPre) {
processPre(element)
if (element.pre) {
inVPre = true
}
}
if (platformIsPreTag(element.tag)) {
inPre = true
}
if (inVPre) {
processRawAttrs(element)
} else if (!element.processed) {
// structural directives
processFor(element)
processIf(element)
processOnce(element)
}
if (!root) {
root = element
if (__DEV__) {
checkRootConstraints(root)
}
}
if (!unary) {
currentParent = element
stack.push(element)
} else {
closeElement(element)
}
},
end(tag, start, end) {
const element = stack[stack.length - 1]
// pop stack
stack.length -= 1
currentParent = stack[stack.length - 1]
if (__DEV__ && options.outputSourceRange) {
element.end = end
}
closeElement(element)
},
chars(text: string, start?: number, end?: number) {
if (!currentParent) {
if (__DEV__) {
if (text === template) {
warnOnce(
'Component template requires a root element, rather than just text.',
{ start }
)
} else if ((text = text.trim())) {
warnOnce(`text "${text}" outside root element will be ignored.`, {
start
})
}
}
return
}
// IE textarea placeholder bug
/* istanbul ignore if */
if (
isIE &&
currentParent.tag === 'textarea' &&
currentParent.attrsMap.placeholder === text
) {
return
}
const children = currentParent.children
if (inPre || text.trim()) {
text = isTextTag(currentParent)
? text
: (decodeHTMLCached(text) as string)
} else if (!children.length) {
// remove the whitespace-only node right after an opening tag
text = ''
} else if (whitespaceOption) {
if (whitespaceOption === 'condense') {
// in condense mode, remove the whitespace node if it contains
// line break, otherwise condense to a single space
text = lineBreakRE.test(text) ? '' : ' '
} else {
text = ' '
}
} else {
text = preserveWhitespace ? ' ' : ''
}
if (text) {
if (!inPre && whitespaceOption === 'condense') {
// condense consecutive whitespaces into single space
text = text.replace(whitespaceRE, ' ')
}
let res
let child: ASTNode | undefined
if (!inVPre && text !== ' ' && (res = parseText(text, delimiters))) {
child = {
type: 2,
expression: res.expression,
tokens: res.tokens,
text
}
} else if (
text !== ' ' ||
!children.length ||
children[children.length - 1].text !== ' '
) {
child = {
type: 3,
text
}
}
if (child) {
if (__DEV__ && options.outputSourceRange) {
child.start = start
child.end = end
}
children.push(child)
}
}
},
comment(text: string, start, end) {
// adding anything as a sibling to the root node is forbidden
// comments should still be allowed, but ignored
if (currentParent) {
const child: ASTText = {
type: 3,
text,
isComment: true
}
if (__DEV__ && options.outputSourceRange) {
child.start = start
child.end = end
}
currentParent.children.push(child)
}
}
})
return root
}
function processPre(el) {
if (getAndRemoveAttr(el, 'v-pre') != null) {
el.pre = true
}
}
function processRawAttrs(el) {
const list = el.attrsList
const len = list.length
if (len) {
const attrs: Array<ASTAttr> = (el.attrs = new Array(len))
for (let i = 0; i < len; i++) {
attrs[i] = {
name: list[i].name,
value: JSON.stringify(list[i].value)
}
if (list[i].start != null) {
attrs[i].start = list[i].start
attrs[i].end = list[i].end
}
}
} else if (!el.pre) {
// non root node in pre blocks with no attributes
el.plain = true
}
}
export function processElement(element: ASTElement, options: CompilerOptions) {
processKey(element)
// determine whether this is a plain element after
// removing structural attributes
element.plain =
!element.key && !element.scopedSlots && !element.attrsList.length
processRef(element)
processSlotContent(element)
processSlotOutlet(element)
processComponent(element)
for (let i = 0; i < transforms.length; i++) {
element = transforms[i](element, options) || element
}
processAttrs(element)
return element
}
function processKey(el) {
const exp = getBindingAttr(el, 'key')
if (exp) {
if (__DEV__) {
if (el.tag === 'template') {
warn(
`<template> cannot be keyed. Place the key on real elements instead.`,
getRawBindingAttr(el, 'key')
)
}
if (el.for) {
const iterator = el.iterator2 || el.iterator1
const parent = el.parent
if (
iterator &&
iterator === exp &&
parent &&
parent.tag === 'transition-group'
) {
warn(
`Do not use v-for index as key on <transition-group> children, ` +
`this is the same as not using keys.`,
getRawBindingAttr(el, 'key'),
true /* tip */
)
}
}
}
el.key = exp
}
}
function processRef(el) {
const ref = getBindingAttr(el, 'ref')
if (ref) {
el.ref = ref
el.refInFor = checkInFor(el)
}
}
export function processFor(el: ASTElement) {
let exp
if ((exp = getAndRemoveAttr(el, 'v-for'))) {
const res = parseFor(exp)
if (res) {
extend(el, res)
} else if (__DEV__) {
warn(`Invalid v-for expression: ${exp}`, el.rawAttrsMap['v-for'])
}
}
}
type ForParseResult = {
for: string
alias: string
iterator1?: string
iterator2?: string
}
export function parseFor(exp: string): ForParseResult | undefined {
const inMatch = exp.match(forAliasRE)
if (!inMatch) return
const res: any = {}
res.for = inMatch[2].trim()
const alias = inMatch[1].trim().replace(stripParensRE, '')
const iteratorMatch = alias.match(forIteratorRE)
if (iteratorMatch) {
res.alias = alias.replace(forIteratorRE, '').trim()
res.iterator1 = iteratorMatch[1].trim()
if (iteratorMatch[2]) {
res.iterator2 = iteratorMatch[2].trim()
}
} else {
res.alias = alias
}
return res
}
function processIf(el) {
const exp = getAndRemoveAttr(el, 'v-if')
if (exp) {
el.if = exp
addIfCondition(el, {
exp: exp,
block: el
})
} else {
if (getAndRemoveAttr(el, 'v-else') != null) {
el.else = true
}
const elseif = getAndRemoveAttr(el, 'v-else-if')
if (elseif) {
el.elseif = elseif
}
}
}
function processIfConditions(el, parent) {
const prev = findPrevElement(parent.children)
if (prev && prev.if) {
addIfCondition(prev, {
exp: el.elseif,
block: el
})
} else if (__DEV__) {
warn(
`v-${el.elseif ? 'else-if="' + el.elseif + '"' : 'else'} ` +
`used on element <${el.tag}> without corresponding v-if.`,
el.rawAttrsMap[el.elseif ? 'v-else-if' : 'v-else']
)
}
}
function findPrevElement(children: Array<any>): ASTElement | void {
let i = children.length
while (i--) {
if (children[i].type === 1) {
return children[i]
} else {
if (__DEV__ && children[i].text !== ' ') {
warn(
`text "${children[i].text.trim()}" between v-if and v-else(-if) ` +
`will be ignored.`,
children[i]
)
}
children.pop()
}
}
}
export function addIfCondition(el: ASTElement, condition: ASTIfCondition) {
if (!el.ifConditions) {
el.ifConditions = []
}
el.ifConditions.push(condition)
}
function processOnce(el) {
const once = getAndRemoveAttr(el, 'v-once')
if (once != null) {
el.once = true
}
}
// handle content being passed to a component as slot,
// e.g. <template slot="xxx">, <div slot-scope="xxx">
function processSlotContent(el) {
let slotScope
if (el.tag === 'template') {
slotScope = getAndRemoveAttr(el, 'scope')
/* istanbul ignore if */
if (__DEV__ && slotScope) {
warn(
`the "scope" attribute for scoped slots have been deprecated and ` +
`replaced by "slot-scope" since 2.5. The new "slot-scope" attribute ` +
`can also be used on plain elements in addition to <template> to ` +
`denote scoped slots.`,
el.rawAttrsMap['scope'],
true
)
}
el.slotScope = slotScope || getAndRemoveAttr(el, 'slot-scope')
} else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) {
/* istanbul ignore if */
if (__DEV__ && el.attrsMap['v-for']) {
warn(
`Ambiguous combined usage of slot-scope and v-for on <${el.tag}> ` +
`(v-for takes higher priority). Use a wrapper <template> for the ` +
`scoped slot to make it clearer.`,
el.rawAttrsMap['slot-scope'],
true
)
}
el.slotScope = slotScope
}
// slot="xxx"
const slotTarget = getBindingAttr(el, 'slot')
if (slotTarget) {
el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget
el.slotTargetDynamic = !!(
el.attrsMap[':slot'] || el.attrsMap['v-bind:slot']
)
// preserve slot as an attribute for native shadow DOM compat
// only for non-scoped slots.
if (el.tag !== 'template' && !el.slotScope) {
addAttr(el, 'slot', slotTarget, getRawBindingAttr(el, 'slot'))
}
}
// 2.6 v-slot syntax
if (process.env.NEW_SLOT_SYNTAX) {
if (el.tag === 'template') {
// v-slot on <template>
const slotBinding = getAndRemoveAttrByRegex(el, slotRE)
if (slotBinding) {
if (__DEV__) {
if (el.slotTarget || el.slotScope) {
warn(`Unexpected mixed usage of different slot syntaxes.`, el)
}
if (el.parent && !maybeComponent(el.parent)) {
warn(
`<template v-slot> can only appear at the root level inside ` +
`the receiving component`,
el
)
}
}
const { name, dynamic } = getSlotName(slotBinding)
el.slotTarget = name
el.slotTargetDynamic = dynamic
el.slotScope = slotBinding.value || emptySlotScopeToken // force it into a scoped slot for perf
}
} else {
// v-slot on component, denotes default slot
const slotBinding = getAndRemoveAttrByRegex(el, slotRE)
if (slotBinding) {
if (__DEV__) {
if (!maybeComponent(el)) {
warn(
`v-slot can only be used on components or <template>.`,
slotBinding
)
}
if (el.slotScope || el.slotTarget) {
warn(`Unexpected mixed usage of different slot syntaxes.`, el)
}
if (el.scopedSlots) {
warn(
`To avoid scope ambiguity, the default slot should also use ` +
`<template> syntax when there are other named slots.`,
slotBinding
)
}
}
// add the component's children to its default slot
const slots = el.scopedSlots || (el.scopedSlots = {})
const { name, dynamic } = getSlotName(slotBinding)
const slotContainer = (slots[name] = createASTElement(
'template',
[],
el
))
slotContainer.slotTarget = name
slotContainer.slotTargetDynamic = dynamic
slotContainer.children = el.children.filter((c: any) => {
if (!c.slotScope) {
c.parent = slotContainer
return true
}
})
slotContainer.slotScope = slotBinding.value || emptySlotScopeToken
// remove children as they are returned from scopedSlots now
el.children = []
// mark el non-plain so data gets generated
el.plain = false
}
}
}
}
function getSlotName(binding) {
let name = binding.name.replace(slotRE, '')
if (!name) {
if (binding.name[0] !== '#') {
name = 'default'
} else if (__DEV__) {
warn(`v-slot shorthand syntax requires a slot name.`, binding)
}
}
return dynamicArgRE.test(name)
? // dynamic [name]
{ name: name.slice(1, -1), dynamic: true }
: // static name
{ name: `"${name}"`, dynamic: false }
}
// handle <slot/> outlets
function processSlotOutlet(el) {
if (el.tag === 'slot') {
el.slotName = getBindingAttr(el, 'name')
if (__DEV__ && el.key) {
warn(
`\`key\` does not work on <slot> because slots are abstract outlets ` +
`and can possibly expand into multiple elements. ` +
`Use the key on a wrapping element instead.`,
getRawBindingAttr(el, 'key')
)
}
}
}
function processComponent(el) {
let binding
if ((binding = getBindingAttr(el, 'is'))) {
el.component = binding
}
if (getAndRemoveAttr(el, 'inline-template') != null) {
el.inlineTemplate = true
}
}
function processAttrs(el) {
const list = el.attrsList
let i, l, name, rawName, value, modifiers, syncGen, isDynamic
for (i = 0, l = list.length; i < l; i++) {
name = rawName = list[i].name
value = list[i].value
if (dirRE.test(name)) {
// mark element as dynamic
el.hasBindings = true
// modifiers
modifiers = parseModifiers(name.replace(dirRE, ''))
// support .foo shorthand syntax for the .prop modifier
if (process.env.VBIND_PROP_SHORTHAND && propBindRE.test(name)) {
;(modifiers || (modifiers = {})).prop = true
name = `.` + name.slice(1).replace(modifierRE, '')
} else if (modifiers) {
name = name.replace(modifierRE, '')
}
if (bindRE.test(name)) {
// v-bind
name = name.replace(bindRE, '')
value = parseFilters(value)
isDynamic = dynamicArgRE.test(name)
if (isDynamic) {
name = name.slice(1, -1)
}
if (__DEV__ && value.trim().length === 0) {
warn(
`The value for a v-bind expression cannot be empty. Found in "v-bind:${name}"`
)
}
if (modifiers) {
if (modifiers.prop && !isDynamic) {
name = camelize(name)
if (name === 'innerHtml') name = 'innerHTML'
}
if (modifiers.camel && !isDynamic) {
name = camelize(name)
}
if (modifiers.sync) {
syncGen = genAssignmentCode(value, `$event`)
if (!isDynamic) {
addHandler(
el,
`update:${camelize(name)}`,
syncGen,
null,
false,
warn,
list[i]
)
if (hyphenate(name) !== camelize(name)) {
addHandler(
el,
`update:${hyphenate(name)}`,
syncGen,
null,
false,
warn,
list[i]
)
}
} else {
// handler w/ dynamic event name
addHandler(
el,
`"update:"+(${name})`,
syncGen,
null,
false,
warn,
list[i],
true // dynamic
)
}
}
}
if (
(modifiers && modifiers.prop) ||
(!el.component && platformMustUseProp(el.tag, el.attrsMap.type, name))
) {
addProp(el, name, value, list[i], isDynamic)
} else {
addAttr(el, name, value, list[i], isDynamic)
}
} else if (onRE.test(name)) {
// v-on
name = name.replace(onRE, '')
isDynamic = dynamicArgRE.test(name)
if (isDynamic) {
name = name.slice(1, -1)
}
addHandler(el, name, value, modifiers, false, warn, list[i], isDynamic)
} else {
// normal directives
name = name.replace(dirRE, '')
// parse arg
const argMatch = name.match(argRE)
let arg = argMatch && argMatch[1]
isDynamic = false
if (arg) {
name = name.slice(0, -(arg.length + 1))
if (dynamicArgRE.test(arg)) {
arg = arg.slice(1, -1)
isDynamic = true
}
}
addDirective(
el,
name,
rawName,
value,
arg,
isDynamic,
modifiers,
list[i]
)
if (__DEV__ && name === 'model') {
checkForAliasModel(el, value)
}
}
} else {
// literal attribute
if (__DEV__) {
const res = parseText(value, delimiters)
if (res) {
warn(
`${name}="${value}": ` +
'Interpolation inside attributes has been removed. ' +
'Use v-bind or the colon shorthand instead. For example, ' +
'instead of <div id="{{ val }}">, use <div :id="val">.',
list[i]
)
}
}
addAttr(el, name, JSON.stringify(value), list[i])
// #6887 firefox doesn't update muted state if set via attribute
// even immediately after element creation
if (
!el.component &&
name === 'muted' &&
platformMustUseProp(el.tag, el.attrsMap.type, name)
) {
addProp(el, name, 'true', list[i])
}
}
}
}
function checkInFor(el: ASTElement): boolean {
let parent: ASTElement | void = el
while (parent) {
if (parent.for !== undefined) {
return true
}
parent = parent.parent
}
return false
}
function parseModifiers(name: string): Object | void {
const match = name.match(modifierRE)
if (match) {
const ret = {}
match.forEach(m => {
ret[m.slice(1)] = true
})
return ret
}
}
function makeAttrsMap(attrs: Array<Record<string, any>>): Record<string, any> {
const map = {}
for (let i = 0, l = attrs.length; i < l; i++) {
if (__DEV__ && map[attrs[i].name] && !isIE && !isEdge) {
warn('duplicate attribute: ' + attrs[i].name, attrs[i])
}
map[attrs[i].name] = attrs[i].value
}
return map
}
// for script (e.g. type="x/template") or style, do not decode content
function isTextTag(el): boolean {
return el.tag === 'script' || el.tag === 'style'
}
function isForbiddenTag(el): boolean {
return (
el.tag === 'style' ||
(el.tag === 'script' &&
(!el.attrsMap.type || el.attrsMap.type === 'text/javascript'))
)
}
const ieNSBug = /^xmlns:NS\d+/
const ieNSPrefix = /^NS\d+:/
/* istanbul ignore next */
function guardIESVGBug(attrs) {
const res: any[] = []
for (let i = 0; i < attrs.length; i++) {
const attr = attrs[i]
if (!ieNSBug.test(attr.name)) {
attr.name = attr.name.replace(ieNSPrefix, '')
res.push(attr)
}
}
return res
}
function checkForAliasModel(el, value) {
let _el = el
while (_el) {
if (_el.for && _el.alias === value) {
warn(
`<${el.tag} v-model="${value}">: ` +
`You are binding v-model directly to a v-for iteration alias. ` +
`This will not be able to modify the v-for source array because ` +
`writing to the alias is like modifying a function local variable. ` +
`Consider using an array of objects and use v-model on an object property instead.`,
el.rawAttrsMap['v-model']
)
}
_el = _el.parent
}
}

52
node_modules/vue/src/compiler/parser/text-parser.ts generated vendored Normal file
View File

@@ -0,0 +1,52 @@
import { cached } from 'shared/util'
import { parseFilters } from './filter-parser'
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g
const regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g
const buildRegex = cached(delimiters => {
const open = delimiters[0].replace(regexEscapeRE, '\\$&')
const close = delimiters[1].replace(regexEscapeRE, '\\$&')
return new RegExp(open + '((?:.|\\n)+?)' + close, 'g')
})
type TextParseResult = {
expression: string
tokens: Array<string | { '@binding': string }>
}
export function parseText(
text: string,
delimiters?: [string, string]
): TextParseResult | void {
//@ts-expect-error
const tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE
if (!tagRE.test(text)) {
return
}
const tokens: string[] = []
const rawTokens: any[] = []
let lastIndex = (tagRE.lastIndex = 0)
let match, index, tokenValue
while ((match = tagRE.exec(text))) {
index = match.index
// push text token
if (index > lastIndex) {
rawTokens.push((tokenValue = text.slice(lastIndex, index)))
tokens.push(JSON.stringify(tokenValue))
}
// tag token
const exp = parseFilters(match[1].trim())
tokens.push(`_s(${exp})`)
rawTokens.push({ '@binding': exp })
lastIndex = index + match[0].length
}
if (lastIndex < text.length) {
rawTokens.push((tokenValue = text.slice(lastIndex)))
tokens.push(JSON.stringify(tokenValue))
}
return {
expression: tokens.join('+'),
tokens: rawTokens
}
}

119
node_modules/vue/src/compiler/to-function.ts generated vendored Normal file
View File

@@ -0,0 +1,119 @@
import { noop, extend } from 'shared/util'
import { warn as baseWarn, tip } from 'core/util/debug'
import { generateCodeFrame } from './codeframe'
import type { Component } from 'types/component'
import { CompilerOptions } from 'types/compiler'
type CompiledFunctionResult = {
render: Function
staticRenderFns: Array<Function>
}
function createFunction(code, errors) {
try {
return new Function(code)
} catch (err: any) {
errors.push({ err, code })
return noop
}
}
export function createCompileToFunctionFn(compile: Function): Function {
const cache = Object.create(null)
return function compileToFunctions(
template: string,
options?: CompilerOptions,
vm?: Component
): CompiledFunctionResult {
options = extend({}, options)
const warn = options.warn || baseWarn
delete options.warn
/* istanbul ignore if */
if (__DEV__) {
// detect possible CSP restriction
try {
new Function('return 1')
} catch (e: any) {
if (e.toString().match(/unsafe-eval|CSP/)) {
warn(
'It seems you are using the standalone build of Vue.js in an ' +
'environment with Content Security Policy that prohibits unsafe-eval. ' +
'The template compiler cannot work in this environment. Consider ' +
'relaxing the policy to allow unsafe-eval or pre-compiling your ' +
'templates into render functions.'
)
}
}
}
// check cache
const key = options.delimiters
? String(options.delimiters) + template
: template
if (cache[key]) {
return cache[key]
}
// compile
const compiled = compile(template, options)
// check compilation errors/tips
if (__DEV__) {
if (compiled.errors && compiled.errors.length) {
if (options.outputSourceRange) {
compiled.errors.forEach(e => {
warn(
`Error compiling template:\n\n${e.msg}\n\n` +
generateCodeFrame(template, e.start, e.end),
vm
)
})
} else {
warn(
`Error compiling template:\n\n${template}\n\n` +
compiled.errors.map(e => `- ${e}`).join('\n') +
'\n',
vm
)
}
}
if (compiled.tips && compiled.tips.length) {
if (options.outputSourceRange) {
compiled.tips.forEach(e => tip(e.msg, vm))
} else {
compiled.tips.forEach(msg => tip(msg, vm))
}
}
}
// turn code into functions
const res: any = {}
const fnGenErrors: any[] = []
res.render = createFunction(compiled.render, fnGenErrors)
res.staticRenderFns = compiled.staticRenderFns.map(code => {
return createFunction(code, fnGenErrors)
})
// check function generation errors.
// this should only happen if there is a bug in the compiler itself.
// mostly for codegen development use
/* istanbul ignore if */
if (__DEV__) {
if ((!compiled.errors || !compiled.errors.length) && fnGenErrors.length) {
warn(
`Failed to generate render function:\n\n` +
fnGenErrors
.map(
({ err, code }) => `${(err as any).toString()} in\n\n${code}\n`
)
.join('\n'),
vm
)
}
}
return (cache[key] = res)
}
}

5
node_modules/vue/src/core/components/index.ts generated vendored Normal file
View File

@@ -0,0 +1,5 @@
import KeepAlive from './keep-alive'
export default {
KeepAlive
}

171
node_modules/vue/src/core/components/keep-alive.ts generated vendored Normal file
View File

@@ -0,0 +1,171 @@
import { isRegExp, isArray, remove } from 'shared/util'
import { getFirstComponentChild } from 'core/vdom/helpers/index'
import type VNode from 'core/vdom/vnode'
import type { VNodeComponentOptions } from 'types/vnode'
import type { Component } from 'types/component'
import { getComponentName } from '../vdom/create-component'
type CacheEntry = {
name?: string
tag?: string
componentInstance?: Component
}
type CacheEntryMap = Record<string, CacheEntry | null>
function _getComponentName(opts?: VNodeComponentOptions): string | null {
return opts && (getComponentName(opts.Ctor.options as any) || opts.tag)
}
function matches(
pattern: string | RegExp | Array<string>,
name: string
): boolean {
if (isArray(pattern)) {
return pattern.indexOf(name) > -1
} else if (typeof pattern === 'string') {
return pattern.split(',').indexOf(name) > -1
} else if (isRegExp(pattern)) {
return pattern.test(name)
}
/* istanbul ignore next */
return false
}
function pruneCache(
keepAliveInstance: {
cache: CacheEntryMap
keys: string[]
_vnode: VNode
$vnode: VNode
},
filter: Function
) {
const { cache, keys, _vnode, $vnode } = keepAliveInstance
for (const key in cache) {
const entry = cache[key]
if (entry) {
const name = entry.name
if (name && !filter(name)) {
pruneCacheEntry(cache, key, keys, _vnode)
}
}
}
$vnode.componentOptions!.children = undefined
}
function pruneCacheEntry(
cache: CacheEntryMap,
key: string,
keys: Array<string>,
current?: VNode
) {
const entry = cache[key]
if (entry && (!current || entry.tag !== current.tag)) {
// @ts-expect-error can be undefined
entry.componentInstance.$destroy()
}
cache[key] = null
remove(keys, key)
}
const patternTypes: Array<Function> = [String, RegExp, Array]
// TODO defineComponent
export default {
name: 'keep-alive',
abstract: true,
props: {
include: patternTypes,
exclude: patternTypes,
max: [String, Number]
},
methods: {
cacheVNode() {
const { cache, keys, vnodeToCache, keyToCache } = this
if (vnodeToCache) {
const { tag, componentInstance, componentOptions } = vnodeToCache
cache[keyToCache] = {
name: _getComponentName(componentOptions),
tag,
componentInstance
}
keys.push(keyToCache)
// prune oldest entry
if (this.max && keys.length > parseInt(this.max)) {
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
this.vnodeToCache = null
}
}
},
created() {
this.cache = Object.create(null)
this.keys = []
},
destroyed() {
for (const key in this.cache) {
pruneCacheEntry(this.cache, key, this.keys)
}
},
mounted() {
this.cacheVNode()
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
},
updated() {
this.cacheVNode()
},
render() {
const slot = this.$slots.default
const vnode = getFirstComponentChild(slot)
const componentOptions = vnode && vnode.componentOptions
if (componentOptions) {
// check pattern
const name = _getComponentName(componentOptions)
const { include, exclude } = this
if (
// not included
(include && (!name || !matches(include, name))) ||
// excluded
(exclude && name && matches(exclude, name))
) {
return vnode
}
const { cache, keys } = this
const key =
vnode.key == null
? // same constructor may get registered as different local components
// so cid alone is not enough (#3269)
componentOptions.Ctor.cid +
(componentOptions.tag ? `::${componentOptions.tag}` : '')
: vnode.key
if (cache[key]) {
vnode.componentInstance = cache[key].componentInstance
// make current key freshest
remove(keys, key)
keys.push(key)
} else {
// delay setting the cache until update
this.vnodeToCache = vnode
this.keyToCache = key
}
// @ts-expect-error can vnode.data can be undefined
vnode.data.keepAlive = true
}
return vnode || (slot && slot[0])
}
}

128
node_modules/vue/src/core/config.ts generated vendored Normal file
View File

@@ -0,0 +1,128 @@
import { no, noop, identity } from 'shared/util'
import { LIFECYCLE_HOOKS } from 'shared/constants'
import type { Component } from 'types/component'
/**
* @internal
*/
export interface Config {
// user
optionMergeStrategies: { [key: string]: Function }
silent: boolean
productionTip: boolean
performance: boolean
devtools: boolean
errorHandler?: (err: Error, vm: Component | null, info: string) => void
warnHandler?: (msg: string, vm: Component | null, trace: string) => void
ignoredElements: Array<string | RegExp>
keyCodes: { [key: string]: number | Array<number> }
// platform
isReservedTag: (x: string) => boolean | undefined
isReservedAttr: (x: string) => true | undefined
parsePlatformTagName: (x: string) => string
isUnknownElement: (x: string) => boolean
getTagNamespace: (x: string) => string | undefined
mustUseProp: (tag: string, type?: string | null, name?: string) => boolean
// private
async: boolean
// legacy
_lifecycleHooks: Array<string>
}
export default {
/**
* Option merge strategies (used in core/util/options)
*/
// $flow-disable-line
optionMergeStrategies: Object.create(null),
/**
* Whether to suppress warnings.
*/
silent: false,
/**
* Show production mode tip message on boot?
*/
productionTip: __DEV__,
/**
* Whether to enable devtools
*/
devtools: __DEV__,
/**
* Whether to record perf
*/
performance: false,
/**
* Error handler for watcher errors
*/
errorHandler: null,
/**
* Warn handler for watcher warns
*/
warnHandler: null,
/**
* Ignore certain custom elements
*/
ignoredElements: [],
/**
* Custom user key aliases for v-on
*/
// $flow-disable-line
keyCodes: Object.create(null),
/**
* Check if a tag is reserved so that it cannot be registered as a
* component. This is platform-dependent and may be overwritten.
*/
isReservedTag: no,
/**
* Check if an attribute is reserved so that it cannot be used as a component
* prop. This is platform-dependent and may be overwritten.
*/
isReservedAttr: no,
/**
* Check if a tag is an unknown element.
* Platform-dependent.
*/
isUnknownElement: no,
/**
* Get the namespace of an element
*/
getTagNamespace: noop,
/**
* Parse the real tag name for the specific platform.
*/
parsePlatformTagName: identity,
/**
* Check if an attribute must be bound using property, e.g. value
* Platform-dependent.
*/
mustUseProp: no,
/**
* Perform updates asynchronously. Intended to be used by Vue Test Utils
* This will significantly reduce performance if set to false.
*/
async: true,
/**
* Exposed for legacy reasons
*/
_lifecycleHooks: LIFECYCLE_HOOKS
} as unknown as Config

35
node_modules/vue/src/core/global-api/assets.ts generated vendored Normal file
View File

@@ -0,0 +1,35 @@
import { ASSET_TYPES } from 'shared/constants'
import type { GlobalAPI } from 'types/global-api'
import { isFunction, isPlainObject, validateComponentName } from '../util/index'
export function initAssetRegisters(Vue: GlobalAPI) {
/**
* Create asset registration methods.
*/
ASSET_TYPES.forEach(type => {
// @ts-expect-error function is not exact same type
Vue[type] = function (
id: string,
definition?: Function | Object
): Function | Object | void {
if (!definition) {
return this.options[type + 's'][id]
} else {
/* istanbul ignore if */
if (__DEV__ && type === 'component') {
validateComponentName(id)
}
if (type === 'component' && isPlainObject(definition)) {
// @ts-expect-error
definition.name = definition.name || id
definition = this.options._base.extend(definition)
}
if (type === 'directive' && isFunction(definition)) {
definition = { bind: definition, update: definition }
}
this.options[type + 's'][id] = definition
return definition
}
}
})
}

94
node_modules/vue/src/core/global-api/extend.ts generated vendored Normal file
View File

@@ -0,0 +1,94 @@
import { ASSET_TYPES } from 'shared/constants'
import type { Component } from 'types/component'
import type { GlobalAPI } from 'types/global-api'
import { defineComputed, proxy } from '../instance/state'
import { extend, mergeOptions, validateComponentName } from '../util/index'
import { getComponentName } from '../vdom/create-component'
export function initExtend(Vue: GlobalAPI) {
/**
* Each instance constructor, including Vue, has a unique
* cid. This enables us to create wrapped "child
* constructors" for prototypal inheritance and cache them.
*/
Vue.cid = 0
let cid = 1
/**
* Class inheritance
*/
Vue.extend = function (extendOptions: any): typeof Component {
extendOptions = extendOptions || {}
const Super = this
const SuperId = Super.cid
const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
if (cachedCtors[SuperId]) {
return cachedCtors[SuperId]
}
const name =
getComponentName(extendOptions) || getComponentName(Super.options)
if (__DEV__ && name) {
validateComponentName(name)
}
const Sub = function VueComponent(this: any, options: any) {
this._init(options)
} as unknown as typeof Component
Sub.prototype = Object.create(Super.prototype)
Sub.prototype.constructor = Sub
Sub.cid = cid++
Sub.options = mergeOptions(Super.options, extendOptions)
Sub['super'] = Super
// For props and computed properties, we define the proxy getters on
// the Vue instances at extension time, on the extended prototype. This
// avoids Object.defineProperty calls for each instance created.
if (Sub.options.props) {
initProps(Sub)
}
if (Sub.options.computed) {
initComputed(Sub)
}
// allow further extension/mixin/plugin usage
Sub.extend = Super.extend
Sub.mixin = Super.mixin
Sub.use = Super.use
// create asset registers, so extended classes
// can have their private assets too.
ASSET_TYPES.forEach(function (type) {
Sub[type] = Super[type]
})
// enable recursive self-lookup
if (name) {
Sub.options.components[name] = Sub
}
// keep a reference to the super options at extension time.
// later at instantiation we can check if Super's options have
// been updated.
Sub.superOptions = Super.options
Sub.extendOptions = extendOptions
Sub.sealedOptions = extend({}, Sub.options)
// cache constructor
cachedCtors[SuperId] = Sub
return Sub
}
}
function initProps(Comp: typeof Component) {
const props = Comp.options.props
for (const key in props) {
proxy(Comp.prototype, `_props`, key)
}
}
function initComputed(Comp: typeof Component) {
const computed = Comp.options.computed
for (const key in computed) {
defineComputed(Comp.prototype, key, computed[key])
}
}

68
node_modules/vue/src/core/global-api/index.ts generated vendored Normal file
View File

@@ -0,0 +1,68 @@
import config from '../config'
import { initUse } from './use'
import { initMixin } from './mixin'
import { initExtend } from './extend'
import { initAssetRegisters } from './assets'
import { set, del } from '../observer/index'
import { ASSET_TYPES } from 'shared/constants'
import builtInComponents from '../components/index'
import { observe } from 'core/observer/index'
import {
warn,
extend,
nextTick,
mergeOptions,
defineReactive
} from '../util/index'
import type { GlobalAPI } from 'types/global-api'
export function initGlobalAPI(Vue: GlobalAPI) {
// config
const configDef: Record<string, any> = {}
configDef.get = () => config
if (__DEV__) {
configDef.set = () => {
warn(
'Do not replace the Vue.config object, set individual fields instead.'
)
}
}
Object.defineProperty(Vue, 'config', configDef)
// exposed util methods.
// NOTE: these are not considered part of the public API - avoid relying on
// them unless you are aware of the risk.
Vue.util = {
warn,
extend,
mergeOptions,
defineReactive
}
Vue.set = set
Vue.delete = del
Vue.nextTick = nextTick
// 2.6 explicit observable API
Vue.observable = <T>(obj: T): T => {
observe(obj)
return obj
}
Vue.options = Object.create(null)
ASSET_TYPES.forEach(type => {
Vue.options[type + 's'] = Object.create(null)
})
// this is used to identify the "base" constructor to extend all plain-object
// components with in Weex's multi-instance scenarios.
Vue.options._base = Vue
extend(Vue.options.components, builtInComponents)
initUse(Vue)
initMixin(Vue)
initExtend(Vue)
initAssetRegisters(Vue)
}

9
node_modules/vue/src/core/global-api/mixin.ts generated vendored Normal file
View File

@@ -0,0 +1,9 @@
import type { GlobalAPI } from 'types/global-api'
import { mergeOptions } from '../util/index'
export function initMixin(Vue: GlobalAPI) {
Vue.mixin = function (mixin: Object) {
this.options = mergeOptions(this.options, mixin)
return this
}
}

23
node_modules/vue/src/core/global-api/use.ts generated vendored Normal file
View File

@@ -0,0 +1,23 @@
import type { GlobalAPI } from 'types/global-api'
import { toArray, isFunction } from '../util/index'
export function initUse(Vue: GlobalAPI) {
Vue.use = function (plugin: Function | any) {
const installedPlugins =
this._installedPlugins || (this._installedPlugins = [])
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// additional parameters
const args = toArray(arguments, 1)
args.unshift(this)
if (isFunction(plugin.install)) {
plugin.install.apply(plugin, args)
} else if (isFunction(plugin)) {
plugin.apply(null, args)
}
installedPlugins.push(plugin)
return this
}
}

27
node_modules/vue/src/core/index.ts generated vendored Normal file
View File

@@ -0,0 +1,27 @@
import Vue from './instance/index'
import { initGlobalAPI } from './global-api/index'
import { isServerRendering } from 'core/util/env'
import { FunctionalRenderContext } from 'core/vdom/create-functional-component'
import { version } from 'v3'
initGlobalAPI(Vue)
Object.defineProperty(Vue.prototype, '$isServer', {
get: isServerRendering
})
Object.defineProperty(Vue.prototype, '$ssrContext', {
get() {
/* istanbul ignore next */
return this.$vnode && this.$vnode.ssrContext
}
})
// expose FunctionalRenderContext for ssr runtime helper installation
Object.defineProperty(Vue, 'FunctionalRenderContext', {
value: FunctionalRenderContext
})
Vue.version = version
export default Vue

160
node_modules/vue/src/core/instance/events.ts generated vendored Normal file
View File

@@ -0,0 +1,160 @@
import type { Component } from 'types/component'
import {
tip,
toArray,
isArray,
hyphenate,
formatComponentName,
invokeWithErrorHandling
} from '../util/index'
import { updateListeners } from '../vdom/helpers/index'
export function initEvents(vm: Component) {
vm._events = Object.create(null)
vm._hasHookEvent = false
// init parent attached events
const listeners = vm.$options._parentListeners
if (listeners) {
updateComponentListeners(vm, listeners)
}
}
let target: any
function add(event, fn) {
target.$on(event, fn)
}
function remove(event, fn) {
target.$off(event, fn)
}
function createOnceHandler(event, fn) {
const _target = target
return function onceHandler() {
const res = fn.apply(null, arguments)
if (res !== null) {
_target.$off(event, onceHandler)
}
}
}
export function updateComponentListeners(
vm: Component,
listeners: Object,
oldListeners?: Object | null
) {
target = vm
updateListeners(
listeners,
oldListeners || {},
add,
remove,
createOnceHandler,
vm
)
target = undefined
}
export function eventsMixin(Vue: typeof Component) {
const hookRE = /^hook:/
Vue.prototype.$on = function (
event: string | Array<string>,
fn: Function
): Component {
const vm: Component = this
if (isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$on(event[i], fn)
}
} else {
;(vm._events[event] || (vm._events[event] = [])).push(fn)
// optimize hook:event cost by using a boolean flag marked at registration
// instead of a hash lookup
if (hookRE.test(event)) {
vm._hasHookEvent = true
}
}
return vm
}
Vue.prototype.$once = function (event: string, fn: Function): Component {
const vm: Component = this
function on() {
vm.$off(event, on)
fn.apply(vm, arguments)
}
on.fn = fn
vm.$on(event, on)
return vm
}
Vue.prototype.$off = function (
event?: string | Array<string>,
fn?: Function
): Component {
const vm: Component = this
// all
if (!arguments.length) {
vm._events = Object.create(null)
return vm
}
// array of events
if (isArray(event)) {
for (let i = 0, l = event.length; i < l; i++) {
vm.$off(event[i], fn)
}
return vm
}
// specific event
const cbs = vm._events[event!]
if (!cbs) {
return vm
}
if (!fn) {
vm._events[event!] = null
return vm
}
// specific handler
let cb
let i = cbs.length
while (i--) {
cb = cbs[i]
if (cb === fn || cb.fn === fn) {
cbs.splice(i, 1)
break
}
}
return vm
}
Vue.prototype.$emit = function (event: string): Component {
const vm: Component = this
if (__DEV__) {
const lowerCaseEvent = event.toLowerCase()
if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {
tip(
`Event "${lowerCaseEvent}" is emitted in component ` +
`${formatComponentName(
vm
)} but the handler is registered for "${event}". ` +
`Note that HTML attributes are case-insensitive and you cannot use ` +
`v-on to listen to camelCase events when using in-DOM templates. ` +
`You should probably use "${hyphenate(
event
)}" instead of "${event}".`
)
}
}
let cbs = vm._events[event]
if (cbs) {
cbs = cbs.length > 1 ? toArray(cbs) : cbs
const args = toArray(arguments, 1)
const info = `event handler for "${event}"`
for (let i = 0, l = cbs.length; i < l; i++) {
invokeWithErrorHandling(cbs[i], vm, args, vm, info)
}
}
return vm
}
}

27
node_modules/vue/src/core/instance/index.ts generated vendored Normal file
View File

@@ -0,0 +1,27 @@
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'
import type { GlobalAPI } from 'types/global-api'
function Vue(options) {
if (__DEV__ && !(this instanceof Vue)) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
//@ts-expect-error Vue has function type
initMixin(Vue)
//@ts-expect-error Vue has function type
stateMixin(Vue)
//@ts-expect-error Vue has function type
eventsMixin(Vue)
//@ts-expect-error Vue has function type
lifecycleMixin(Vue)
//@ts-expect-error Vue has function type
renderMixin(Vue)
export default Vue as unknown as GlobalAPI

143
node_modules/vue/src/core/instance/init.ts generated vendored Normal file
View File

@@ -0,0 +1,143 @@
import config from '../config'
import { initProxy } from './proxy'
import { initState } from './state'
import { initRender } from './render'
import { initEvents } from './events'
import { mark, measure } from '../util/perf'
import { initLifecycle, callHook } from './lifecycle'
import { initProvide, initInjections } from './inject'
import { extend, mergeOptions, formatComponentName } from '../util/index'
import type { Component } from 'types/component'
import type { InternalComponentOptions } from 'types/options'
import { EffectScope } from 'v3/reactivity/effectScope'
let uid = 0
export function initMixin(Vue: typeof Component) {
Vue.prototype._init = function (options?: Record<string, any>) {
const vm: Component = this
// a uid
vm._uid = uid++
let startTag, endTag
/* istanbul ignore if */
if (__DEV__ && config.performance && mark) {
startTag = `vue-perf-start:${vm._uid}`
endTag = `vue-perf-end:${vm._uid}`
mark(startTag)
}
// a flag to mark this as a Vue instance without having to do instanceof
// check
vm._isVue = true
// avoid instances from being observed
vm.__v_skip = true
// effect scope
vm._scope = new EffectScope(true /* detached */)
// #13134 edge case where a child component is manually created during the
// render of a parent component
vm._scope.parent = undefined
vm._scope._vm = true
// merge options
if (options && options._isComponent) {
// optimize internal component instantiation
// since dynamic options merging is pretty slow, and none of the
// internal component options needs special treatment.
initInternalComponent(vm, options as any)
} else {
vm.$options = mergeOptions(
resolveConstructorOptions(vm.constructor as any),
options || {},
vm
)
}
/* istanbul ignore else */
if (__DEV__) {
initProxy(vm)
} else {
vm._renderProxy = vm
}
// expose real self
vm._self = vm
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate', undefined, false /* setContext */)
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
/* istanbul ignore if */
if (__DEV__ && config.performance && mark) {
vm._name = formatComponentName(vm, false)
mark(endTag)
measure(`vue ${vm._name} init`, startTag, endTag)
}
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
export function initInternalComponent(
vm: Component,
options: InternalComponentOptions
) {
const opts = (vm.$options = Object.create((vm.constructor as any).options))
// doing this because it's faster than dynamic enumeration.
const parentVnode = options._parentVnode
opts.parent = options.parent
opts._parentVnode = parentVnode
const vnodeComponentOptions = parentVnode.componentOptions!
opts.propsData = vnodeComponentOptions.propsData
opts._parentListeners = vnodeComponentOptions.listeners
opts._renderChildren = vnodeComponentOptions.children
opts._componentTag = vnodeComponentOptions.tag
if (options.render) {
opts.render = options.render
opts.staticRenderFns = options.staticRenderFns
}
}
export function resolveConstructorOptions(Ctor: typeof Component) {
let options = Ctor.options
if (Ctor.super) {
const superOptions = resolveConstructorOptions(Ctor.super)
const cachedSuperOptions = Ctor.superOptions
if (superOptions !== cachedSuperOptions) {
// super option changed,
// need to resolve new options.
Ctor.superOptions = superOptions
// check if there are any late-modified/attached options (#4976)
const modifiedOptions = resolveModifiedOptions(Ctor)
// update base extend options
if (modifiedOptions) {
extend(Ctor.extendOptions, modifiedOptions)
}
options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)
if (options.name) {
options.components[options.name] = Ctor
}
}
}
return options
}
function resolveModifiedOptions(
Ctor: typeof Component
): Record<string, any> | null {
let modified
const latest = Ctor.options
const sealed = Ctor.sealedOptions
for (const key in latest) {
if (latest[key] !== sealed[key]) {
if (!modified) modified = {}
modified[key] = latest[key]
}
}
return modified
}

80
node_modules/vue/src/core/instance/inject.ts generated vendored Normal file
View File

@@ -0,0 +1,80 @@
import { warn, hasSymbol, isFunction, isObject } from '../util/index'
import { defineReactive, toggleObserving } from '../observer/index'
import type { Component } from 'types/component'
import { resolveProvided } from 'v3/apiInject'
export function initProvide(vm: Component) {
const provideOption = vm.$options.provide
if (provideOption) {
const provided = isFunction(provideOption)
? provideOption.call(vm)
: provideOption
if (!isObject(provided)) {
return
}
const source = resolveProvided(vm)
// IE9 doesn't support Object.getOwnPropertyDescriptors so we have to
// iterate the keys ourselves.
const keys = hasSymbol ? Reflect.ownKeys(provided) : Object.keys(provided)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
Object.defineProperty(
source,
key,
Object.getOwnPropertyDescriptor(provided, key)!
)
}
}
}
export function initInjections(vm: Component) {
const result = resolveInject(vm.$options.inject, vm)
if (result) {
toggleObserving(false)
Object.keys(result).forEach(key => {
/* istanbul ignore else */
if (__DEV__) {
defineReactive(vm, key, result[key], () => {
warn(
`Avoid mutating an injected value directly since the changes will be ` +
`overwritten whenever the provided component re-renders. ` +
`injection being mutated: "${key}"`,
vm
)
})
} else {
defineReactive(vm, key, result[key])
}
})
toggleObserving(true)
}
}
export function resolveInject(
inject: any,
vm: Component
): Record<string, any> | undefined | null {
if (inject) {
// inject is :any because flow is not smart enough to figure out cached
const result = Object.create(null)
const keys = hasSymbol ? Reflect.ownKeys(inject) : Object.keys(inject)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
// #6574 in case the inject object is observed...
if (key === '__ob__') continue
const provideKey = inject[key].from
if (provideKey in vm._provided) {
result[key] = vm._provided[provideKey]
} else if ('default' in inject[key]) {
const provideDefault = inject[key].default
result[key] = isFunction(provideDefault)
? provideDefault.call(vm)
: provideDefault
} else if (__DEV__) {
warn(`Injection "${key as string}" not found`, vm)
}
}
return result
}
}

421
node_modules/vue/src/core/instance/lifecycle.ts generated vendored Normal file
View File

@@ -0,0 +1,421 @@
import config from '../config'
import Watcher, { WatcherOptions } from '../observer/watcher'
import { mark, measure } from '../util/perf'
import VNode, { createEmptyVNode } from '../vdom/vnode'
import { updateComponentListeners } from './events'
import { resolveSlots } from './render-helpers/resolve-slots'
import { toggleObserving } from '../observer/index'
import { pushTarget, popTarget } from '../observer/dep'
import type { Component } from 'types/component'
import type { MountedComponentVNode } from 'types/vnode'
import {
warn,
noop,
remove,
emptyObject,
validateProp,
invokeWithErrorHandling
} from '../util/index'
import { currentInstance, setCurrentInstance } from 'v3/currentInstance'
import { getCurrentScope } from 'v3/reactivity/effectScope'
import { syncSetupProxy } from 'v3/apiSetup'
export let activeInstance: any = null
export let isUpdatingChildComponent: boolean = false
export function setActiveInstance(vm: Component) {
const prevActiveInstance = activeInstance
activeInstance = vm
return () => {
activeInstance = prevActiveInstance
}
}
export function initLifecycle(vm: Component) {
const options = vm.$options
// locate first non-abstract parent
let parent = options.parent
if (parent && !options.abstract) {
while (parent.$options.abstract && parent.$parent) {
parent = parent.$parent
}
parent.$children.push(vm)
}
vm.$parent = parent
vm.$root = parent ? parent.$root : vm
vm.$children = []
vm.$refs = {}
vm._provided = parent ? parent._provided : Object.create(null)
vm._watcher = null
vm._inactive = null
vm._directInactive = false
vm._isMounted = false
vm._isDestroyed = false
vm._isBeingDestroyed = false
}
export function lifecycleMixin(Vue: typeof Component) {
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
const vm: Component = this
const prevEl = vm.$el
const prevVnode = vm._vnode
const restoreActiveInstance = setActiveInstance(vm)
vm._vnode = vnode
// Vue.prototype.__patch__ is injected in entry points
// based on the rendering backend used.
if (!prevVnode) {
// initial render
vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
} else {
// updates
vm.$el = vm.__patch__(prevVnode, vnode)
}
restoreActiveInstance()
// update __vue__ reference
if (prevEl) {
prevEl.__vue__ = null
}
if (vm.$el) {
vm.$el.__vue__ = vm
}
// if parent is an HOC, update its $el as well
let wrapper: Component | undefined = vm
while (
wrapper &&
wrapper.$vnode &&
wrapper.$parent &&
wrapper.$vnode === wrapper.$parent._vnode
) {
wrapper.$parent.$el = wrapper.$el
wrapper = wrapper.$parent
}
// updated hook is called by the scheduler to ensure that children are
// updated in a parent's updated hook.
}
Vue.prototype.$forceUpdate = function () {
const vm: Component = this
if (vm._watcher) {
vm._watcher.update()
}
}
Vue.prototype.$destroy = function () {
const vm: Component = this
if (vm._isBeingDestroyed) {
return
}
callHook(vm, 'beforeDestroy')
vm._isBeingDestroyed = true
// remove self from parent
const parent = vm.$parent
if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {
remove(parent.$children, vm)
}
// teardown scope. this includes both the render watcher and other
// watchers created
vm._scope.stop()
// remove reference from data ob
// frozen object may not have observer.
if (vm._data.__ob__) {
vm._data.__ob__.vmCount--
}
// call the last hook...
vm._isDestroyed = true
// invoke destroy hooks on current rendered tree
vm.__patch__(vm._vnode, null)
// fire destroyed hook
callHook(vm, 'destroyed')
// turn off all instance listeners.
vm.$off()
// remove __vue__ reference
if (vm.$el) {
vm.$el.__vue__ = null
}
// release circular reference (#6759)
if (vm.$vnode) {
vm.$vnode.parent = null
}
}
}
export function mountComponent(
vm: Component,
el: Element | null | undefined,
hydrating?: boolean
): Component {
vm.$el = el
if (!vm.$options.render) {
// @ts-expect-error invalid type
vm.$options.render = createEmptyVNode
if (__DEV__) {
/* istanbul ignore if */
if (
(vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
vm.$options.el ||
el
) {
warn(
'You are using the runtime-only build of Vue where the template ' +
'compiler is not available. Either pre-compile the templates into ' +
'render functions, or use the compiler-included build.',
vm
)
} else {
warn(
'Failed to mount component: template or render function not defined.',
vm
)
}
}
}
callHook(vm, 'beforeMount')
let updateComponent
/* istanbul ignore if */
if (__DEV__ && config.performance && mark) {
updateComponent = () => {
const name = vm._name
const id = vm._uid
const startTag = `vue-perf-start:${id}`
const endTag = `vue-perf-end:${id}`
mark(startTag)
const vnode = vm._render()
mark(endTag)
measure(`vue ${name} render`, startTag, endTag)
mark(startTag)
vm._update(vnode, hydrating)
mark(endTag)
measure(`vue ${name} patch`, startTag, endTag)
}
} else {
updateComponent = () => {
vm._update(vm._render(), hydrating)
}
}
const watcherOptions: WatcherOptions = {
before() {
if (vm._isMounted && !vm._isDestroyed) {
callHook(vm, 'beforeUpdate')
}
}
}
if (__DEV__) {
watcherOptions.onTrack = e => callHook(vm, 'renderTracked', [e])
watcherOptions.onTrigger = e => callHook(vm, 'renderTriggered', [e])
}
// we set this to vm._watcher inside the watcher's constructor
// since the watcher's initial patch may call $forceUpdate (e.g. inside child
// component's mounted hook), which relies on vm._watcher being already defined
new Watcher(
vm,
updateComponent,
noop,
watcherOptions,
true /* isRenderWatcher */
)
hydrating = false
// flush buffer for flush: "pre" watchers queued in setup()
const preWatchers = vm._preWatchers
if (preWatchers) {
for (let i = 0; i < preWatchers.length; i++) {
preWatchers[i].run()
}
}
// manually mounted instance, call mounted on self
// mounted is called for render-created child components in its inserted hook
if (vm.$vnode == null) {
vm._isMounted = true
callHook(vm, 'mounted')
}
return vm
}
export function updateChildComponent(
vm: Component,
propsData: Record<string, any> | null | undefined,
listeners: Record<string, Function | Array<Function>> | undefined,
parentVnode: MountedComponentVNode,
renderChildren?: Array<VNode> | null
) {
if (__DEV__) {
isUpdatingChildComponent = true
}
// determine whether component has slot children
// we need to do this before overwriting $options._renderChildren.
// check if there are dynamic scopedSlots (hand-written or compiled but with
// dynamic slot names). Static scoped slots compiled from template has the
// "$stable" marker.
const newScopedSlots = parentVnode.data.scopedSlots
const oldScopedSlots = vm.$scopedSlots
const hasDynamicScopedSlot = !!(
(newScopedSlots && !newScopedSlots.$stable) ||
(oldScopedSlots !== emptyObject && !oldScopedSlots.$stable) ||
(newScopedSlots && vm.$scopedSlots.$key !== newScopedSlots.$key) ||
(!newScopedSlots && vm.$scopedSlots.$key)
)
// Any static slot children from the parent may have changed during parent's
// update. Dynamic scoped slots may also have changed. In such cases, a forced
// update is necessary to ensure correctness.
let needsForceUpdate = !!(
renderChildren || // has new static slots
vm.$options._renderChildren || // has old static slots
hasDynamicScopedSlot
)
const prevVNode = vm.$vnode
vm.$options._parentVnode = parentVnode
vm.$vnode = parentVnode // update vm's placeholder node without re-render
if (vm._vnode) {
// update child tree's parent
vm._vnode.parent = parentVnode
}
vm.$options._renderChildren = renderChildren
// update $attrs and $listeners hash
// these are also reactive so they may trigger child update if the child
// used them during render
const attrs = parentVnode.data.attrs || emptyObject
if (vm._attrsProxy) {
// force update if attrs are accessed and has changed since it may be
// passed to a child component.
if (
syncSetupProxy(
vm._attrsProxy,
attrs,
(prevVNode.data && prevVNode.data.attrs) || emptyObject,
vm,
'$attrs'
)
) {
needsForceUpdate = true
}
}
vm.$attrs = attrs
// update listeners
listeners = listeners || emptyObject
const prevListeners = vm.$options._parentListeners
if (vm._listenersProxy) {
syncSetupProxy(
vm._listenersProxy,
listeners,
prevListeners || emptyObject,
vm,
'$listeners'
)
}
vm.$listeners = vm.$options._parentListeners = listeners
updateComponentListeners(vm, listeners, prevListeners)
// update props
if (propsData && vm.$options.props) {
toggleObserving(false)
const props = vm._props
const propKeys = vm.$options._propKeys || []
for (let i = 0; i < propKeys.length; i++) {
const key = propKeys[i]
const propOptions: any = vm.$options.props // wtf flow?
props[key] = validateProp(key, propOptions, propsData, vm)
}
toggleObserving(true)
// keep a copy of raw propsData
vm.$options.propsData = propsData
}
// resolve slots + force update if has children
if (needsForceUpdate) {
vm.$slots = resolveSlots(renderChildren, parentVnode.context)
vm.$forceUpdate()
}
if (__DEV__) {
isUpdatingChildComponent = false
}
}
function isInInactiveTree(vm) {
while (vm && (vm = vm.$parent)) {
if (vm._inactive) return true
}
return false
}
export function activateChildComponent(vm: Component, direct?: boolean) {
if (direct) {
vm._directInactive = false
if (isInInactiveTree(vm)) {
return
}
} else if (vm._directInactive) {
return
}
if (vm._inactive || vm._inactive === null) {
vm._inactive = false
for (let i = 0; i < vm.$children.length; i++) {
activateChildComponent(vm.$children[i])
}
callHook(vm, 'activated')
}
}
export function deactivateChildComponent(vm: Component, direct?: boolean) {
if (direct) {
vm._directInactive = true
if (isInInactiveTree(vm)) {
return
}
}
if (!vm._inactive) {
vm._inactive = true
for (let i = 0; i < vm.$children.length; i++) {
deactivateChildComponent(vm.$children[i])
}
callHook(vm, 'deactivated')
}
}
export function callHook(
vm: Component,
hook: string,
args?: any[],
setContext = true
) {
// #7573 disable dep collection when invoking lifecycle hooks
pushTarget()
const prevInst = currentInstance
const prevScope = getCurrentScope()
setContext && setCurrentInstance(vm)
const handlers = vm.$options[hook]
const info = `${hook} hook`
if (handlers) {
for (let i = 0, j = handlers.length; i < j; i++) {
invokeWithErrorHandling(handlers[i], vm, args || null, vm, info)
}
}
if (vm._hasHookEvent) {
vm.$emit('hook:' + hook)
}
if (setContext) {
setCurrentInstance(prevInst)
prevScope && prevScope.on()
}
popTarget()
}

97
node_modules/vue/src/core/instance/proxy.ts generated vendored Normal file
View File

@@ -0,0 +1,97 @@
/* not type checking this file because flow doesn't play well with Proxy */
import config from 'core/config'
import { warn, makeMap, isNative } from '../util/index'
let initProxy
if (__DEV__) {
const allowedGlobals = makeMap(
'Infinity,undefined,NaN,isFinite,isNaN,' +
'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +
'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,' +
'require' // for Webpack/Browserify
)
const warnNonPresent = (target, key) => {
warn(
`Property or method "${key}" is not defined on the instance but ` +
'referenced during render. Make sure that this property is reactive, ' +
'either in the data option, or for class-based components, by ' +
'initializing the property. ' +
'See: https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',
target
)
}
const warnReservedPrefix = (target, key) => {
warn(
`Property "${key}" must be accessed with "$data.${key}" because ` +
'properties starting with "$" or "_" are not proxied in the Vue instance to ' +
'prevent conflicts with Vue internals. ' +
'See: https://v2.vuejs.org/v2/api/#data',
target
)
}
const hasProxy = typeof Proxy !== 'undefined' && isNative(Proxy)
if (hasProxy) {
const isBuiltInModifier = makeMap(
'stop,prevent,self,ctrl,shift,alt,meta,exact'
)
config.keyCodes = new Proxy(config.keyCodes, {
set(target, key: string, value) {
if (isBuiltInModifier(key)) {
warn(
`Avoid overwriting built-in modifier in config.keyCodes: .${key}`
)
return false
} else {
target[key] = value
return true
}
}
})
}
const hasHandler = {
has(target, key) {
const has = key in target
const isAllowed =
allowedGlobals(key) ||
(typeof key === 'string' &&
key.charAt(0) === '_' &&
!(key in target.$data))
if (!has && !isAllowed) {
if (key in target.$data) warnReservedPrefix(target, key)
else warnNonPresent(target, key)
}
return has || !isAllowed
}
}
const getHandler = {
get(target, key) {
if (typeof key === 'string' && !(key in target)) {
if (key in target.$data) warnReservedPrefix(target, key)
else warnNonPresent(target, key)
}
return target[key]
}
}
initProxy = function initProxy(vm) {
if (hasProxy) {
// determine which proxy handler to use
const options = vm.$options
const handlers =
options.render && options.render._withStripped ? getHandler : hasHandler
vm._renderProxy = new Proxy(vm, handlers)
} else {
vm._renderProxy = vm
}
}
}
export { initProxy }

View File

@@ -0,0 +1,36 @@
// helper to process dynamic keys for dynamic arguments in v-bind and v-on.
// For example, the following template:
//
// <div id="app" :[key]="value">
//
// compiles to the following:
//
// _c('div', { attrs: bindDynamicKeys({ "id": "app" }, [key, value]) })
import { warn } from 'core/util/debug'
export function bindDynamicKeys(
baseObj: Record<string, any>,
values: Array<any>
): Object {
for (let i = 0; i < values.length; i += 2) {
const key = values[i]
if (typeof key === 'string' && key) {
baseObj[values[i]] = values[i + 1]
} else if (__DEV__ && key !== '' && key !== null) {
// null is a special value for explicitly removing a binding
warn(
`Invalid value for dynamic directive argument (expected string or null): ${key}`,
this
)
}
}
return baseObj
}
// helper to dynamically append modifier runtime markers to event names.
// ensure only append when value is already string, otherwise it will be cast
// to string and cause the type check to miss.
export function prependModifier(value: any, symbol: string): any {
return typeof value === 'string' ? symbol + value : value
}

View File

@@ -0,0 +1,18 @@
import { warn, extend, isPlainObject } from 'core/util/index'
import type { VNodeData } from 'types/vnode'
export function bindObjectListeners(data: any, value: any): VNodeData {
if (value) {
if (!isPlainObject(value)) {
__DEV__ && warn('v-on without argument expects an Object value', this)
} else {
const on = (data.on = data.on ? extend({}, data.on) : {})
for (const key in value) {
const existing = on[key]
const ours = value[key]
on[key] = existing ? [].concat(existing, ours) : ours
}
}
}
return data
}

View File

@@ -0,0 +1,59 @@
import config from 'core/config'
import {
warn,
isObject,
toObject,
isReservedAttribute,
camelize,
hyphenate,
isArray
} from 'core/util/index'
import type { VNodeData } from 'types/vnode'
/**
* Runtime helper for merging v-bind="object" into a VNode's data.
*/
export function bindObjectProps(
data: any,
tag: string,
value: any,
asProp: boolean,
isSync?: boolean
): VNodeData {
if (value) {
if (!isObject(value)) {
__DEV__ &&
warn('v-bind without argument expects an Object or Array value', this)
} else {
if (isArray(value)) {
value = toObject(value)
}
let hash
for (const key in value) {
if (key === 'class' || key === 'style' || isReservedAttribute(key)) {
hash = data
} else {
const type = data.attrs && data.attrs.type
hash =
asProp || config.mustUseProp(tag, type, key)
? data.domProps || (data.domProps = {})
: data.attrs || (data.attrs = {})
}
const camelizedKey = camelize(key)
const hyphenatedKey = hyphenate(key)
if (!(camelizedKey in hash) && !(hyphenatedKey in hash)) {
hash[key] = value[key]
if (isSync) {
const on = data.on || (data.on = {})
on[`update:${key}`] = function ($event) {
value[key] = $event
}
}
}
}
}
}
return data
}

View File

@@ -0,0 +1,33 @@
import config from 'core/config'
import { hyphenate, isArray } from 'shared/util'
function isKeyNotMatch<T>(expect: T | Array<T>, actual: T): boolean {
if (isArray(expect)) {
return expect.indexOf(actual) === -1
} else {
return expect !== actual
}
}
/**
* Runtime helper for checking keyCodes from config.
* exposed as Vue.prototype._k
* passing in eventKeyName as last argument separately for backwards compat
*/
export function checkKeyCodes(
eventKeyCode: number,
key: string,
builtInKeyCode?: number | Array<number>,
eventKeyName?: string,
builtInKeyName?: string | Array<string>
): boolean | null | undefined {
const mappedKeyCode = config.keyCodes[key] || builtInKeyCode
if (builtInKeyName && eventKeyName && !config.keyCodes[key]) {
return isKeyNotMatch(builtInKeyName, eventKeyName)
} else if (mappedKeyCode) {
return isKeyNotMatch(mappedKeyCode, eventKeyCode)
} else if (eventKeyName) {
return hyphenate(eventKeyName) !== key
}
return eventKeyCode === undefined
}

View File

@@ -0,0 +1,31 @@
import { toNumber, toString, looseEqual, looseIndexOf } from 'shared/util'
import { createTextVNode, createEmptyVNode } from 'core/vdom/vnode'
import { renderList } from './render-list'
import { renderSlot } from './render-slot'
import { resolveFilter } from './resolve-filter'
import { checkKeyCodes } from './check-keycodes'
import { bindObjectProps } from './bind-object-props'
import { renderStatic, markOnce } from './render-static'
import { bindObjectListeners } from './bind-object-listeners'
import { resolveScopedSlots } from './resolve-scoped-slots'
import { bindDynamicKeys, prependModifier } from './bind-dynamic-keys'
export function installRenderHelpers(target: any) {
target._o = markOnce
target._n = toNumber
target._s = toString
target._l = renderList
target._t = renderSlot
target._q = looseEqual
target._i = looseIndexOf
target._m = renderStatic
target._f = resolveFilter
target._k = checkKeyCodes
target._b = bindObjectProps
target._v = createTextVNode
target._e = createEmptyVNode
target._u = resolveScopedSlots
target._g = bindObjectListeners
target._d = bindDynamicKeys
target._p = prependModifier
}

View File

@@ -0,0 +1,49 @@
import { isObject, isDef, hasSymbol, isArray } from 'core/util/index'
import VNode from 'core/vdom/vnode'
/**
* Runtime helper for rendering v-for lists.
*/
export function renderList(
val: any,
render: (val: any, keyOrIndex: string | number, index?: number) => VNode
): Array<VNode> | null {
let ret: Array<VNode> | null = null,
i,
l,
keys,
key
if (isArray(val) || typeof val === 'string') {
ret = new Array(val.length)
for (i = 0, l = val.length; i < l; i++) {
ret[i] = render(val[i], i)
}
} else if (typeof val === 'number') {
ret = new Array(val)
for (i = 0; i < val; i++) {
ret[i] = render(i + 1, i)
}
} else if (isObject(val)) {
if (hasSymbol && val[Symbol.iterator]) {
ret = []
const iterator: Iterator<any> = val[Symbol.iterator]()
let result = iterator.next()
while (!result.done) {
ret.push(render(result.value, ret.length))
result = iterator.next()
}
} else {
keys = Object.keys(val)
ret = new Array(keys.length)
for (i = 0, l = keys.length; i < l; i++) {
key = keys[i]
ret[i] = render(val[key], key, i)
}
}
}
if (!isDef(ret)) {
ret = []
}
;(ret as any)._isVList = true
return ret
}

Some files were not shown because too many files have changed in this diff Show More