Skip to content

Commit

Permalink
Merge branch 'main' into bwsy/fix/teleportCSSVars
Browse files Browse the repository at this point in the history
  • Loading branch information
baiwusanyu-c authored Apr 16, 2024
2 parents 5e887ab + b4b856b commit 4ff76d4
Show file tree
Hide file tree
Showing 82 changed files with 1,394 additions and 477 deletions.
24 changes: 17 additions & 7 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,23 @@ module.exports = {
'no-restricted-syntax': [
'error',
banConstEnum,
// since we target ES2015 for baseline support, we need to forbid object
// rest spread usage in destructure as it compiles into a verbose helper.
'ObjectPattern > RestElement',
// tsc compiles assignment spread into Object.assign() calls, but esbuild
// still generates verbose helpers, so spread assignment is also prohiboted
'ObjectExpression > SpreadElement',
'AwaitExpression',
{
selector: 'ObjectPattern > RestElement',
message:
'Our output target is ES2016, and object rest spread results in ' +
'verbose helpers and should be avoided.',
},
{
selector: 'ObjectExpression > SpreadElement',
message:
'esbuild transpiles object spread into very verbose inline helpers.\n' +
'Please use the `extend` helper from @vue/shared instead.',
},
{
selector: 'AwaitExpression',
message:
'Our output target is ES2016, so async/await syntax should be avoided.',
},
],
'sort-imports': ['error', { ignoreDeclarationSort: true }],

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ TODOs.md
.eslintcache
dts-build/packages
*.tsbuildinfo
*.tgz
23 changes: 7 additions & 16 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,15 @@
"version": "0.2.0",
"configurations": [
{
"name": "Jest",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/jest",
"stopOnEntry": false,
"args": ["${fileBasename}", "--runInBand", "--detectOpenHandles"],
"cwd": "${workspaceFolder}",
"preLaunchTask": null,
"runtimeExecutable": null,
"runtimeArgs": ["--nolazy"],
"env": {
"NODE_ENV": "development"
},
"console": "integratedTerminal",
"sourceMaps": true,
"windows": {
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
}
"name": "Vitest - Debug Current Test File",
"autoAttachChildProcesses": true,
"skipFiles": ["<node_internals>/**", "**/node_modules/**"],
"program": "${workspaceRoot}/node_modules/vitest/vitest.mjs",
"args": ["run", "${relativeFile}"],
"smartStep": true,
"console": "integratedTerminal"
}
]
}
41 changes: 41 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,44 @@
## [3.4.22](https://github.com/vuejs/core/compare/v3.4.21...v3.4.22) (2024-04-15)


### Bug Fixes

* **compat:** fix $options mutation + adjust private API initialization ([d58d133](https://github.com/vuejs/core/commit/d58d133b1cde5085cc5ab0012d544cafd62a6ee6)), closes [#10626](https://github.com/vuejs/core/issues/10626) [#10636](https://github.com/vuejs/core/issues/10636)
* **compile-sfc:** analyze v-bind shorthand usage in template ([#10518](https://github.com/vuejs/core/issues/10518)) ([e5919d4](https://github.com/vuejs/core/commit/e5919d4658cfe0bb18c76611dd3c3432c57f94ab)), closes [#10515](https://github.com/vuejs/core/issues/10515)
* **compiler-core:** fix loc.source for end tags with whitespace before > ([16174da](https://github.com/vuejs/core/commit/16174da21d6c8ac0aae027dd964fc35e221ded0a)), closes [#10694](https://github.com/vuejs/core/issues/10694) [#10695](https://github.com/vuejs/core/issues/10695)
* **compiler-core:** fix v-bind shorthand for component :is ([04af950](https://github.com/vuejs/core/commit/04af9504a720c8e6de26c04b1282cf14fa1bcee3)), closes [#10469](https://github.com/vuejs/core/issues/10469) [#10471](https://github.com/vuejs/core/issues/10471)
* **compiler-sfc:** :is() and :where() in compound selectors ([#10522](https://github.com/vuejs/core/issues/10522)) ([660cadc](https://github.com/vuejs/core/commit/660cadc7aadb909ef33a6055c4374902a82607a4)), closes [#10511](https://github.com/vuejs/core/issues/10511)
* **compiler-sfc:** also search for `.tsx` when type import's extension is omitted ([#10637](https://github.com/vuejs/core/issues/10637)) ([34106bc](https://github.com/vuejs/core/commit/34106bc9c715247211273bb9c64712f04bd4879d)), closes [#10635](https://github.com/vuejs/core/issues/10635)
* **compiler-sfc:** fix defineModel coercion for boolean + string union types ([#9603](https://github.com/vuejs/core/issues/9603)) ([0cef65c](https://github.com/vuejs/core/commit/0cef65cee411356e721bbc90d731fc52fc8fce94)), closes [#9587](https://github.com/vuejs/core/issues/9587) [#10676](https://github.com/vuejs/core/issues/10676)
* **compiler-sfc:** fix universal selector scope ([#10551](https://github.com/vuejs/core/issues/10551)) ([54a6afa](https://github.com/vuejs/core/commit/54a6afa75a546078e901ce0882da53b97420fe94)), closes [#10548](https://github.com/vuejs/core/issues/10548)
* **compiler-sfc:** use options module name if options provide runtimeModuleName options ([#10457](https://github.com/vuejs/core/issues/10457)) ([e76d743](https://github.com/vuejs/core/commit/e76d7430aa7470342f3fe263145a0fa92f5898ca)), closes [#10454](https://github.com/vuejs/core/issues/10454)
* **custom-element:** avoid setting attr to null if it is removed ([#9012](https://github.com/vuejs/core/issues/9012)) ([b49306a](https://github.com/vuejs/core/commit/b49306adff4572d90a42ccd231387f16eb966bbe)), closes [#9006](https://github.com/vuejs/core/issues/9006) [#10324](https://github.com/vuejs/core/issues/10324)
* **hydration:** properly handle optimized mode during hydrate node ([#10638](https://github.com/vuejs/core/issues/10638)) ([2ec06fd](https://github.com/vuejs/core/commit/2ec06fd6c8383e11cdf4efcab1707f973bd6a54c)), closes [#10607](https://github.com/vuejs/core/issues/10607)
* **reactivity:** computed should not be detected as true by isProxy ([#10401](https://github.com/vuejs/core/issues/10401)) ([9da34d7](https://github.com/vuejs/core/commit/9da34d7af81607fddd1f32f21b3b4002402ff1cc))
* **reactivity:** fix hasOwnProperty key coercion edge cases ([969c5fb](https://github.com/vuejs/core/commit/969c5fb30f4c725757c7385abfc74772514eae4b))
* **reactivity:** fix tracking when hasOwnProperty is called with non-string value ([c3c5dc9](https://github.com/vuejs/core/commit/c3c5dc93fbccc196771458f0b43cd5b7ad1863f4)), closes [#10455](https://github.com/vuejs/core/issues/10455) [#10464](https://github.com/vuejs/core/issues/10464)
* **runtime-core:** fix errorHandler causes an infinite loop during execution ([#9575](https://github.com/vuejs/core/issues/9575)) ([ab59bed](https://github.com/vuejs/core/commit/ab59bedae4e5e40b28804d88a51305b236d4a873))
* **runtime-core:** handle invalid values in callWithAsyncErrorHandling ([53d15d3](https://github.com/vuejs/core/commit/53d15d3f76184eed67a18d35e43d9a2062f8e121))
* **runtime-core:** show hydration mismatch details for non-rectified mismatches too when __PROD_HYDRATION_MISMATCH_DETAILS__ is set ([#10599](https://github.com/vuejs/core/issues/10599)) ([0dea7f9](https://github.com/vuejs/core/commit/0dea7f9a260d93eb6c39aabac8c94c2c9b2042dd))
* **runtime-dom:** `v-model` string/number coercion for multiselect options ([#10576](https://github.com/vuejs/core/issues/10576)) ([db374e5](https://github.com/vuejs/core/commit/db374e54c9f5e07324728b85c74eca84e28dd352))
* **runtime-dom:** fix css v-bind for suspensed components ([#8523](https://github.com/vuejs/core/issues/8523)) ([67722ba](https://github.com/vuejs/core/commit/67722ba23b7c36ab8f3fa2d2b4df08e4ddc322e1)), closes [#8520](https://github.com/vuejs/core/issues/8520)
* **runtime-dom:** force update v-model number with leading 0 ([#10506](https://github.com/vuejs/core/issues/10506)) ([15ffe8f](https://github.com/vuejs/core/commit/15ffe8f2c954359770c57e4d9e589b0b622e4a60)), closes [#10503](https://github.com/vuejs/core/issues/10503) [#10615](https://github.com/vuejs/core/issues/10615)
* **runtime-dom:** sanitize wrongly passed string value as event handler ([#8953](https://github.com/vuejs/core/issues/8953)) ([7ccd453](https://github.com/vuejs/core/commit/7ccd453dd004076cad49ec9f56cd5fe97b7b6ed8)), closes [#8818](https://github.com/vuejs/core/issues/8818)
* **ssr:** don't render v-if comments in TransitionGroup ([#6732](https://github.com/vuejs/core/issues/6732)) ([5a96267](https://github.com/vuejs/core/commit/5a9626708e970c6fc0b6f786e3c80c22273d126f)), closes [#6715](https://github.com/vuejs/core/issues/6715)
* **Transition:** ensure the KeepAlive children unmount w/ out-in mode ([#10632](https://github.com/vuejs/core/issues/10632)) ([fc99e4d](https://github.com/vuejs/core/commit/fc99e4d3f01b190ef9fd3c218a668ba9124a32bc)), closes [#10620](https://github.com/vuejs/core/issues/10620)
* **TransitionGroup:** avoid set transition hooks for comment nodes and text nodes ([#9421](https://github.com/vuejs/core/issues/9421)) ([140a768](https://github.com/vuejs/core/commit/140a7681cc3bba22f55d97fd85a5eafe97a1230f)), closes [#4621](https://github.com/vuejs/core/issues/4621) [#4622](https://github.com/vuejs/core/issues/4622) [#5153](https://github.com/vuejs/core/issues/5153) [#5168](https://github.com/vuejs/core/issues/5168) [#7898](https://github.com/vuejs/core/issues/7898) [#9067](https://github.com/vuejs/core/issues/9067)
* **types:** avoid merging object union types when using withDefaults ([#10596](https://github.com/vuejs/core/issues/10596)) ([37ba93c](https://github.com/vuejs/core/commit/37ba93c213a81f99a68a99ef5d4065d61b150ba3)), closes [#10594](https://github.com/vuejs/core/issues/10594)


### Performance Improvements

* add `__NO_SIDE_EFFECTS__` comments ([#9053](https://github.com/vuejs/core/issues/9053)) ([d46df6b](https://github.com/vuejs/core/commit/d46df6bdb14b0509eb2134b3f85297a306821c61))
* optimize component props/slots internal object checks ([6af733d](https://github.com/vuejs/core/commit/6af733d68eb400a3d2c5ef5f465fff32b72a324e))
* **ssr:** avoid calling markRaw on component instance proxy ([4bc9f39](https://github.com/vuejs/core/commit/4bc9f39f028af7313e5cf24c16915a1985d27bf8))
* **ssr:** optimize setup context creation for ssr in v8 ([ca84316](https://github.com/vuejs/core/commit/ca84316bfb3410efe21333670a6ad5cd21857396))



## [3.4.21](https://github.com/vuejs/core/compare/v3.4.20...v3.4.21) (2024-02-28)


Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# vuejs/core [![npm](https://img.shields.io/npm/v/vue.svg)](https://www.npmjs.com/package/vue) [![build status](https://github.com/vuejs/core/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/vuejs/core/actions/workflows/ci.yml)
# vuejs/core [![npm](https://img.shields.io/npm/v/vue.svg)](https://www.npmjs.com/package/vue) [![build status](https://github.com/vuejs/core/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/vuejs/core/actions/workflows/ci.yml) [![Download](https://img.shields.io/npm/dm/vue)](https://www.npmjs.com/package/vue)

## Getting Started

Expand Down
28 changes: 14 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"private": true,
"version": "3.4.21",
"packageManager": "pnpm@8.15.5",
"version": "3.4.22",
"packageManager": "pnpm@8.15.6",
"type": "module",
"scripts": {
"dev": "node scripts/dev.js",
"build": "node scripts/build.js",
"build-dts": "tsc -p tsconfig.build.json && rollup -c rollup.dts.config.js",
"build-dts": "tsc -p tsconfig.build-browser.json && tsc -p tsconfig.build-node.json && rollup -c rollup.dts.config.js",
"clean": "rimraf packages/*/dist temp .eslintcache",
"size": "run-s \"size-*\" && tsx scripts/usage-size.ts",
"size-global": "node scripts/build.js vue runtime-dom -f global -p --size",
Expand All @@ -20,7 +20,7 @@
"test-unit": "vitest -c vitest.unit.config.ts",
"test-e2e": "node scripts/build.js vue -f global -d && vitest -c vitest.e2e.config.ts",
"test-dts": "run-s build-dts test-dts-only",
"test-dts-only": "tsc -p ./packages/dts-test/tsconfig.test.json",
"test-dts-only": "tsc -p packages/dts-built-test/tsconfig.json && tsc -p ./packages/dts-test/tsconfig.test.json",
"test-coverage": "vitest -c vitest.unit.config.ts --coverage",
"test-bench": "vitest bench",
"release": "node scripts/release.js",
Expand Down Expand Up @@ -70,10 +70,10 @@
"@rollup/plugin-terser": "^0.4.4",
"@types/hash-sum": "^1.0.2",
"@types/minimist": "^1.2.5",
"@types/node": "^20.11.30",
"@types/node": "^20.12.5",
"@types/semver": "^7.5.8",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
"@typescript-eslint/eslint-plugin": "^7.4.0",
"@typescript-eslint/parser": "^7.4.0",
"@vitest/coverage-istanbul": "^1.4.0",
"@vue/consolidate": "1.0.0",
"conventional-changelog-cli": "^4.1.0",
Expand All @@ -98,21 +98,21 @@
"prettier": "^3.2.5",
"pretty-bytes": "^6.1.1",
"pug": "^3.0.2",
"puppeteer": "~22.6.0",
"puppeteer": "~22.6.3",
"rimraf": "^5.0.5",
"rollup": "^4.13.0",
"rollup": "^4.13.2",
"rollup-plugin-dts": "^6.1.0",
"rollup-plugin-esbuild": "^6.1.1",
"rollup-plugin-polyfill-node": "^0.13.0",
"semver": "^7.6.0",
"serve": "^14.2.1",
"simple-git-hooks": "^2.11.0",
"terser": "^5.29.2",
"simple-git-hooks": "^2.11.1",
"terser": "^5.30.1",
"todomvc-app-css": "^2.4.3",
"tslib": "^2.6.2",
"tsx": "^4.7.1",
"typescript": "^5.2.2",
"vite": "^5.2.6",
"tsx": "^4.7.2",
"typescript": "~5.4.5",
"vite": "^5.2.7",
"vitest": "^1.4.0"
}
}
18 changes: 14 additions & 4 deletions packages/compiler-core/__tests__/parse.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2070,6 +2070,16 @@ describe('compiler: parse', () => {
baseParse(`<Foo>`, { parseMode: 'sfc', onError() {} })
expect(() => baseParse(`{ foo }`)).not.toThrow()
})

test('correct loc when the closing > is foarmatted', () => {
const [span] = baseParse(`<span></span
>`).children

expect(span.loc.source).toBe('<span></span\n \n >')
expect(span.loc.start.offset).toBe(0)
expect(span.loc.end.offset).toBe(27)
})
})

describe('decodeEntities option', () => {
Expand Down Expand Up @@ -2166,7 +2176,7 @@ describe('compiler: parse', () => {
})

test('should remove leading newline character immediately following the pre element start tag', () => {
const ast = baseParse(`<pre>\n foo bar </pre>`, {
const ast = parse(`<pre>\n foo bar </pre>`, {
isPreTag: tag => tag === 'pre',
})
expect(ast.children).toHaveLength(1)
Expand All @@ -2176,7 +2186,7 @@ describe('compiler: parse', () => {
})

test('should NOT remove leading newline character immediately following child-tag of pre element', () => {
const ast = baseParse(`<pre><span></span>\n foo bar </pre>`, {
const ast = parse(`<pre><span></span>\n foo bar </pre>`, {
isPreTag: tag => tag === 'pre',
})
const preElement = ast.children[0] as ElementNode
Expand All @@ -2187,7 +2197,7 @@ describe('compiler: parse', () => {
})

test('self-closing pre tag', () => {
const ast = baseParse(`<pre/><span>\n foo bar</span>`, {
const ast = parse(`<pre/><span>\n foo bar</span>`, {
isPreTag: tag => tag === 'pre',
})
const elementAfterPre = ast.children[1] as ElementNode
Expand All @@ -2196,7 +2206,7 @@ describe('compiler: parse', () => {
})

test('should NOT condense whitespaces in RCDATA text mode', () => {
const ast = baseParse(`<textarea>Text:\n foo</textarea>`, {
const ast = parse(`<textarea>Text:\n foo</textarea>`, {
parseMode: 'html',
})
const preElement = ast.children[0] as ElementNode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,24 @@ describe('compiler: element transform', () => {
})
})

test('dynamic binding shorthand', () => {
const { node, root } = parseWithBind(`<component :is />`)
expect(root.helpers).toContain(RESOLVE_DYNAMIC_COMPONENT)
expect(node).toMatchObject({
isBlock: true,
tag: {
callee: RESOLVE_DYNAMIC_COMPONENT,
arguments: [
{
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'is',
isStatic: false,
},
],
},
})
})

test('is casting', () => {
const { node, root } = parseWithBind(`<div is="vue:foo" />`)
expect(root.helpers).toContain(RESOLVE_COMPONENT)
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@vue/compiler-core",
"version": "3.4.21",
"version": "3.4.22",
"description": "@vue/compiler-core",
"main": "index.js",
"module": "dist/compiler-core.esm-bundler.js",
Expand Down
47 changes: 43 additions & 4 deletions packages/compiler-core/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import {
getVNodeHelper,
locStub,
} from './ast'
import { type RawSourceMap, SourceMapGenerator } from 'source-map-js'
import { SourceMapGenerator } from 'source-map-js'
import {
advancePositionWithMutation,
assert,
Expand Down Expand Up @@ -56,6 +56,45 @@ import {
} from './runtimeHelpers'
import type { ImportItem } from './transform'

/**
* The `SourceMapGenerator` type from `source-map-js` is a bit incomplete as it
* misses `toJSON()`. We also need to add types for internal properties which we
* need to access for better performance.
*
* Since TS 5.3, dts generation starts to strangely include broken triple slash
* references for source-map-js, so we are inlining all source map related types
* here to to workaround that.
*/
export interface CodegenSourceMapGenerator {
setSourceContent(sourceFile: string, sourceContent: string): void
// SourceMapGenerator has this method but the types do not include it
toJSON(): RawSourceMap
_sources: Set<string>
_names: Set<string>
_mappings: {
add(mapping: MappingItem): void
}
}

export interface RawSourceMap {
file?: string
sourceRoot?: string
version: string
sources: string[]
names: string[]
sourcesContent?: string[]
mappings: string
}

interface MappingItem {
source: string
generatedLine: number
generatedColumn: number
originalLine: number
originalColumn: number
name: string | null
}

const PURE_ANNOTATION = `/*#__PURE__*/`

const aliasHelper = (s: symbol) => `${helperNameMap[s]}: _${helperNameMap[s]}`
Expand Down Expand Up @@ -85,7 +124,7 @@ export interface CodegenContext
offset: number
indentLevel: number
pure: boolean
map?: SourceMapGenerator
map?: CodegenSourceMapGenerator
helper(key: symbol): string
push(code: string, newlineIndex?: number, node?: CodegenNode): void
indent(): void
Expand Down Expand Up @@ -218,14 +257,14 @@ function createCodegenContext(
generatedLine: context.line,
generatedColumn: context.column - 1,
source: filename,
// @ts-expect-error it is possible to be null
name,
})
}

if (!__BROWSER__ && sourceMap) {
// lazy require source-map implementation, only in non-browser builds
context.map = new SourceMapGenerator()
context.map =
new SourceMapGenerator() as unknown as CodegenSourceMapGenerator
context.map.setSourceContent(filename, context.source)
context.map._sources.add(filename)
}
Expand Down
8 changes: 7 additions & 1 deletion packages/compiler-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ export {
type StructuralDirectiveTransform,
type DirectiveTransform,
} from './transform'
export { generate, type CodegenContext, type CodegenResult } from './codegen'
export {
generate,
type CodegenContext,
type CodegenResult,
type CodegenSourceMapGenerator,
type RawSourceMap,
} from './codegen'
export {
ErrorCodes,
errorMessages,
Expand Down
1 change: 1 addition & 0 deletions packages/compiler-core/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export interface ParserOptions
delimiters?: [string, string]
/**
* Whitespace handling strategy
* @default 'condense'
*/
whitespace?: 'preserve' | 'condense'
/**
Expand Down
8 changes: 7 additions & 1 deletion packages/compiler-core/src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ function onCloseTag(el: ElementNode, end: number, isImplied = false) {
// implied close, end should be backtracked to close
setLocEnd(el.loc, backTrack(end, CharCodes.Lt))
} else {
setLocEnd(el.loc, end + 1)
setLocEnd(el.loc, lookAhead(end, CharCodes.Gt) + 1)
}

if (tokenizer.inSFCRoot) {
Expand Down Expand Up @@ -736,6 +736,12 @@ function onCloseTag(el: ElementNode, end: number, isImplied = false) {
}
}

function lookAhead(index: number, c: number) {
let i = index
while (currentInput.charCodeAt(i) !== c && i < currentInput.length - 1) i++
return i
}

function backTrack(index: number, c: number) {
let i = index
while (currentInput.charCodeAt(i) !== c && i >= 0) i--
Expand Down
Loading

0 comments on commit 4ff76d4

Please sign in to comment.