فهرست منبع

Merge remote-tracking branch 'upstream/minor'

三咲智子 Kevin Deng 2 سال پیش
والد
کامیت
d28d57bd3f

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 498 - 240
packages/compiler-core/__tests__/__snapshots__/parse.spec.ts.snap


+ 141 - 140
packages/compiler-core/__tests__/parse.spec.ts

@@ -1796,166 +1796,167 @@ describe('compiler: parse', () => {
     })
   })
 
-  test('self closing single tag', () => {
-    const ast = baseParse('<div :class="{ some: condition }" />')
+  describe('Edge Cases', () => {
+    test('self closing single tag', () => {
+      const ast = baseParse('<div :class="{ some: condition }" />')
 
-    expect(ast.children).toHaveLength(1)
-    expect(ast.children[0]).toMatchObject({ tag: 'div' })
-  })
+      expect(ast.children).toHaveLength(1)
+      expect(ast.children[0]).toMatchObject({ tag: 'div' })
+    })
 
-  test('self closing multiple tag', () => {
-    const ast = baseParse(
-      `<div :class="{ some: condition }" />\n` +
-        `<p v-bind:style="{ color: 'red' }"/>`
-    )
+    test('self closing multiple tag', () => {
+      const ast = baseParse(
+        `<div :class="{ some: condition }" />\n` +
+          `<p v-bind:style="{ color: 'red' }"/>`
+      )
 
-    expect(ast).toMatchSnapshot()
+      expect(ast).toMatchSnapshot()
 
-    expect(ast.children).toHaveLength(2)
-    expect(ast.children[0]).toMatchObject({ tag: 'div' })
-    expect(ast.children[1]).toMatchObject({ tag: 'p' })
-  })
+      expect(ast.children).toHaveLength(2)
+      expect(ast.children[0]).toMatchObject({ tag: 'div' })
+      expect(ast.children[1]).toMatchObject({ tag: 'p' })
+    })
 
-  test('valid html', () => {
-    const ast = baseParse(
-      `<div :class="{ some: condition }">\n` +
-        `  <p v-bind:style="{ color: 'red' }"/>\n` +
-        `  <!-- a comment with <html> inside it -->\n` +
-        `</div>`
-    )
+    test('valid html', () => {
+      const ast = baseParse(
+        `<div :class="{ some: condition }">\n` +
+          `  <p v-bind:style="{ color: 'red' }"/>\n` +
+          `  <!-- a comment with <html> inside it -->\n` +
+          `</div>`
+      )
 
-    expect(ast).toMatchSnapshot()
+      expect(ast).toMatchSnapshot()
 
-    expect(ast.children).toHaveLength(1)
-    const el = ast.children[0] as any
-    expect(el).toMatchObject({
-      tag: 'div'
-    })
-    expect(el.children).toHaveLength(2)
-    expect(el.children[0]).toMatchObject({
-      tag: 'p'
-    })
-    expect(el.children[1]).toMatchObject({
-      type: NodeTypes.COMMENT
+      expect(ast.children).toHaveLength(1)
+      const el = ast.children[0] as any
+      expect(el).toMatchObject({
+        tag: 'div'
+      })
+      expect(el.children).toHaveLength(2)
+      expect(el.children[0]).toMatchObject({
+        tag: 'p'
+      })
+      expect(el.children[1]).toMatchObject({
+        type: NodeTypes.COMMENT
+      })
     })
-  })
 
-  test('invalid html', () => {
-    expect(() => {
-      baseParse(`<div>\n<span>\n</div>\n</span>`)
-    }).toThrow('Element is missing end tag.')
+    test('invalid html', () => {
+      expect(() => {
+        baseParse(`<div>\n<span>\n</div>\n</span>`)
+      }).toThrow('Element is missing end tag.')
 
-    const spy = vi.fn()
-    const ast = baseParse(`<div>\n<span>\n</div>\n</span>`, {
-      onError: spy
-    })
+      const spy = vi.fn()
+      const ast = baseParse(`<div>\n<span>\n</div>\n</span>`, {
+        onError: spy
+      })
 
-    expect(spy.mock.calls).toMatchObject([
-      [
-        {
-          code: ErrorCodes.X_MISSING_END_TAG,
-          loc: {
-            start: {
-              offset: 6,
-              line: 2,
-              column: 1
+      expect(spy.mock.calls).toMatchObject([
+        [
+          {
+            code: ErrorCodes.X_MISSING_END_TAG,
+            loc: {
+              start: {
+                offset: 6,
+                line: 2,
+                column: 1
+              }
             }
           }
-        }
-      ],
-      [
-        {
-          code: ErrorCodes.X_INVALID_END_TAG,
-          loc: {
-            start: {
-              offset: 20,
-              line: 4,
-              column: 1
+        ],
+        [
+          {
+            code: ErrorCodes.X_INVALID_END_TAG,
+            loc: {
+              start: {
+                offset: 20,
+                line: 4,
+                column: 1
+              }
             }
           }
-        }
-      ]
-    ])
-
-    expect(ast).toMatchSnapshot()
-  })
-
-  test('parse with correct location info', () => {
-    const fooSrc = `foo
- is `
-    const barSrc = `{{ bar }}`
-    const butSrc = ` but `
-    const bazSrc = `{{ baz }}`
-    const [foo, bar, but, baz] = baseParse(
-      fooSrc + barSrc + butSrc + bazSrc
-    ).children
-
-    let offset = 0
-    expect(foo.loc.start).toEqual({ line: 1, column: 1, offset })
-    offset += fooSrc.length
-    expect(foo.loc.end).toEqual({ line: 2, column: 5, offset })
-
-    expect(bar.loc.start).toEqual({ line: 2, column: 5, offset })
-    const barInner = (bar as InterpolationNode).content
-    offset += 3
-    expect(barInner.loc.start).toEqual({ line: 2, column: 8, offset })
-    offset += 3
-    expect(barInner.loc.end).toEqual({ line: 2, column: 11, offset })
-    offset += 3
-    expect(bar.loc.end).toEqual({ line: 2, column: 14, offset })
-
-    expect(but.loc.start).toEqual({ line: 2, column: 14, offset })
-    offset += butSrc.length
-    expect(but.loc.end).toEqual({ line: 2, column: 19, offset })
-
-    expect(baz.loc.start).toEqual({ line: 2, column: 19, offset })
-    const bazInner = (baz as InterpolationNode).content
-    offset += 3
-    expect(bazInner.loc.start).toEqual({ line: 2, column: 22, offset })
-    offset += 3
-    expect(bazInner.loc.end).toEqual({ line: 2, column: 25, offset })
-    offset += 3
-    expect(baz.loc.end).toEqual({ line: 2, column: 28, offset })
-  })
+        ]
+      ])
 
-  // With standard HTML parsing, the following input would ignore the slash
-  // and treat "<" and "template" as attributes on the open tag of "Hello",
-  // causing `<template>` to fail to close, and `<script>` being parsed as its
-  // child. This is would never be intended in actual templates, but is a common
-  // intermediate state from user input when parsing for IDE support. We want
-  // the `<script>` to be at root-level to keep the SFC structure stable for
-  // Volar to do incremental computations.
-  test('tag termination handling for IDE', () => {
-    const spy = vi.fn()
-    const ast = baseParse(
-      `<template><Hello\n</template><script>console.log(1)</script>`,
-      {
-        onError: spy
-      }
-    )
-    //
-    expect(ast.children.length).toBe(2)
-    expect(ast.children[1]).toMatchObject({
-      type: NodeTypes.ELEMENT,
-      tag: 'script'
+      expect(ast).toMatchSnapshot()
+    })
+
+    test('parse with correct location info', () => {
+      const fooSrc = `foo\n is `
+      const barSrc = `{{ bar }}`
+      const butSrc = ` but `
+      const bazSrc = `{{ baz }}`
+      const [foo, bar, but, baz] = baseParse(
+        fooSrc + barSrc + butSrc + bazSrc
+      ).children
+
+      let offset = 0
+      expect(foo.loc.start).toEqual({ line: 1, column: 1, offset })
+      offset += fooSrc.length
+      expect(foo.loc.end).toEqual({ line: 2, column: 5, offset })
+
+      expect(bar.loc.start).toEqual({ line: 2, column: 5, offset })
+      const barInner = (bar as InterpolationNode).content
+      offset += 3
+      expect(barInner.loc.start).toEqual({ line: 2, column: 8, offset })
+      offset += 3
+      expect(barInner.loc.end).toEqual({ line: 2, column: 11, offset })
+      offset += 3
+      expect(bar.loc.end).toEqual({ line: 2, column: 14, offset })
+
+      expect(but.loc.start).toEqual({ line: 2, column: 14, offset })
+      offset += butSrc.length
+      expect(but.loc.end).toEqual({ line: 2, column: 19, offset })
+
+      expect(baz.loc.start).toEqual({ line: 2, column: 19, offset })
+      const bazInner = (baz as InterpolationNode).content
+      offset += 3
+      expect(bazInner.loc.start).toEqual({ line: 2, column: 22, offset })
+      offset += 3
+      expect(bazInner.loc.end).toEqual({ line: 2, column: 25, offset })
+      offset += 3
+      expect(baz.loc.end).toEqual({ line: 2, column: 28, offset })
+    })
+
+    // With standard HTML parsing, the following input would ignore the slash
+    // and treat "<" and "template" as attributes on the open tag of "Hello",
+    // causing `<template>` to fail to close, and `<script>` being parsed as its
+    // child. This is would never be intended in actual templates, but is a common
+    // intermediate state from user input when parsing for IDE support. We want
+    // the `<script>` to be at root-level to keep the SFC structure stable for
+    // Volar to do incremental computations.
+    test('tag termination handling for IDE', () => {
+      const spy = vi.fn()
+      const ast = baseParse(
+        `<template><Hello\n</template><script>console.log(1)</script>`,
+        {
+          onError: spy
+        }
+      )
+      //
+      expect(ast.children.length).toBe(2)
+      expect(ast.children[1]).toMatchObject({
+        type: NodeTypes.ELEMENT,
+        tag: 'script'
+      })
     })
-  })
 
-  test('arg should be undefined on shorthand dirs with no arg', () => {
-    const ast = baseParse(`<template #></template>`)
-    const el = ast.children[0] as ElementNode
-    expect(el.props[0]).toMatchObject({
-      type: NodeTypes.DIRECTIVE,
-      name: 'slot',
-      exp: undefined,
-      arg: undefined
+    test('arg should be undefined on shorthand dirs with no arg', () => {
+      const ast = baseParse(`<template #></template>`)
+      const el = ast.children[0] as ElementNode
+      expect(el.props[0]).toMatchObject({
+        type: NodeTypes.DIRECTIVE,
+        name: 'slot',
+        exp: undefined,
+        arg: undefined
+      })
     })
-  })
 
-  // edge case found in vue-macros where the input is TS or JSX
-  test('should reset inRCDATA state', () => {
-    baseParse(`<Foo>`, { parseMode: 'sfc', onError() {} })
-    expect(() => baseParse(`{ foo }`)).not.toThrow()
+    // edge case found in vue-macros where the input is TS or JSX
+    test('should reset inRCDATA state', () => {
+      baseParse(`<Foo>`, { parseMode: 'sfc', onError() {} })
+      expect(() => baseParse(`{ foo }`)).not.toThrow()
+    })
   })
 
   describe('decodeEntities option', () => {

+ 8 - 0
packages/compiler-core/__tests__/transforms/transformExpressions.spec.ts

@@ -161,6 +161,14 @@ describe('compiler: expression transform', () => {
       type: NodeTypes.COMPOUND_EXPRESSION,
       children: [{ content: `Math` }, `.`, { content: `max` }, `(1, 2)`]
     })
+
+    expect(
+      (parseWithExpressionTransform(`{{ new Error() }}`) as InterpolationNode)
+        .content
+    ).toMatchObject({
+      type: NodeTypes.COMPOUND_EXPRESSION,
+      children: ['new ', { content: 'Error' }, '()']
+    })
   })
 
   test('should not prefix reserved literals', () => {

+ 1 - 1
packages/compiler-sfc/package.json

@@ -49,7 +49,7 @@
     "lru-cache": "^10.1.0",
     "merge-source-map": "^1.1.0",
     "minimatch": "^9.0.3",
-    "postcss-modules": "^4.3.1",
+    "postcss-modules": "^6.0.0",
     "postcss-selector-parser": "^6.0.13",
     "pug": "^3.0.2",
     "sass": "^1.69.5"

+ 1 - 1
packages/shared/src/globalsAllowList.ts

@@ -3,7 +3,7 @@ import { makeMap } from './makeMap'
 const GLOBALS_ALLOWED =
   'Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,' +
   'decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,' +
-  'Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console'
+  'Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console,Error'
 
 export const isGloballyAllowed = /*#__PURE__*/ makeMap(GLOBALS_ALLOWED)
 

+ 5 - 9
pnpm-lock.yaml

@@ -245,8 +245,8 @@ importers:
         specifier: ^9.0.3
         version: 9.0.3
       postcss-modules:
-        specifier: ^4.3.1
-        version: 4.3.1(postcss@8.4.31)
+        specifier: ^6.0.0
+        version: 6.0.0(postcss@8.4.31)
       postcss-selector-parser:
         specifier: ^6.0.13
         version: 6.0.13
@@ -3689,10 +3689,6 @@ packages:
       safer-buffer: 2.1.2
     dev: true
 
-  /icss-replace-symbols@1.1.0:
-    resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==}
-    dev: true
-
   /icss-utils@5.1.0(postcss@8.4.31):
     resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==}
     engines: {node: ^10 || ^12 || >= 14}
@@ -4917,13 +4913,13 @@ packages:
       postcss: 8.4.31
     dev: true
 
-  /postcss-modules@4.3.1(postcss@8.4.31):
-    resolution: {integrity: sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==}
+  /postcss-modules@6.0.0(postcss@8.4.31):
+    resolution: {integrity: sha512-7DGfnlyi/ju82BRzTIjWS5C4Tafmzl3R79YP/PASiocj+aa6yYphHhhKUOEoXQToId5rgyFgJ88+ccOUydjBXQ==}
     peerDependencies:
       postcss: ^8.0.0
     dependencies:
       generic-names: 4.0.0
-      icss-replace-symbols: 1.1.0
+      icss-utils: 5.1.0(postcss@8.4.31)
       lodash.camelcase: 4.3.0
       postcss: 8.4.31
       postcss-modules-extract-imports: 3.0.0(postcss@8.4.31)

+ 43 - 51
rollup.config.js

@@ -1,4 +1,5 @@
 // @ts-check
+import assert from 'node:assert/strict'
 import { createRequire } from 'node:module'
 import { fileURLToPath } from 'node:url'
 import path from 'node:path'
@@ -14,6 +15,14 @@ import alias from '@rollup/plugin-alias'
 import { entries } from './scripts/aliases.js'
 import { inlineEnums } from './scripts/inline-enums.js'
 
+/**
+ * @template T
+ * @template {keyof T} K
+ * @typedef { Omit<T, K> & Required<Pick<T, K>> } MarkRequired
+ */
+/** @typedef {'cjs' | 'esm-bundler' | 'global' | 'global-runtime' | 'esm-browser' | 'esm-bundler-runtime' | 'esm-browser-runtime'} PackageFormat */
+/** @typedef {MarkRequired<import('rollup').OutputOptions, 'file' | 'format'>} OutputOptions */
+
 if (!process.env.TARGET) {
   throw new Error('TARGET package must be specified via --environment flag.')
 }
@@ -27,45 +36,35 @@ const consolidatePkg = require('@vue/consolidate/package.json')
 const packagesDir = path.resolve(__dirname, 'packages')
 const packageDir = path.resolve(packagesDir, process.env.TARGET)
 
-/** @param {string} p */
-const resolve = p => path.resolve(packageDir, p)
+const resolve = (/** @type {string} */ p) => path.resolve(packageDir, p)
 const pkg = require(resolve(`package.json`))
 const packageOptions = pkg.buildOptions || {}
 const name = packageOptions.filename || path.basename(packageDir)
 
 const [enumPlugin, enumDefines] = inlineEnums()
 
-/**
- * @typedef { Omit<T, K> & Required<Pick<T, K>> } MarkRequired
- * @template T
- * @template {keyof T} K
- */
-
-/** @typedef {import('rollup').ModuleFormat} Format */
-/** @typedef {MarkRequired<import('rollup').OutputOptions, 'file'>} Output */
-
-/** @type {Record<string, Output>} */
+/** @type {Record<PackageFormat, OutputOptions>} */
 const outputConfigs = {
   'esm-bundler': {
     file: resolve(`dist/${name}.esm-bundler.js`),
-    format: `es`
+    format: 'es'
   },
   'esm-browser': {
     file: resolve(`dist/${name}.esm-browser.js`),
-    format: `es`
+    format: 'es'
   },
   cjs: {
     file: resolve(`dist/${name}.cjs.js`),
-    format: `cjs`
+    format: 'cjs'
   },
   global: {
     file: resolve(`dist/${name}.global.js`),
-    format: `iife`
+    format: 'iife'
   },
   // runtime-only builds, for main "vue" package only
   'esm-bundler-runtime': {
     file: resolve(`dist/${name}.runtime.esm-bundler.js`),
-    format: `es`
+    format: 'es'
   },
   'esm-browser-runtime': {
     file: resolve(`dist/${name}.runtime.esm-browser.js`),
@@ -77,40 +76,39 @@ const outputConfigs = {
   }
 }
 
+/** @type {ReadonlyArray<PackageFormat>} */
 const defaultFormats = ['esm-bundler', 'cjs']
-const inlineFormats = process.env.FORMATS && process.env.FORMATS.split(',')
+/** @type {ReadonlyArray<PackageFormat>} */
+const inlineFormats = /** @type {any} */ (
+  process.env.FORMATS && process.env.FORMATS.split(',')
+)
+/** @type {ReadonlyArray<PackageFormat>} */
 const packageFormats = inlineFormats || packageOptions.formats || defaultFormats
 const packageConfigs = process.env.PROD_ONLY
   ? []
-  : packageFormats.map(
-      /** @param {Format} format */
-      format => createConfig(format, outputConfigs[format])
-    )
+  : packageFormats.map(format => createConfig(format, outputConfigs[format]))
 
 if (process.env.NODE_ENV === 'production') {
-  packageFormats.forEach(
-    /** @param {Format} format */
-    format => {
-      if (packageOptions.prod === false) {
-        return
-      }
-      if (format === 'cjs') {
-        packageConfigs.push(createProductionConfig(format))
-      }
-      if (/^(global|esm-browser)(-runtime)?/.test(format)) {
-        packageConfigs.push(createMinifiedConfig(format))
-      }
+  packageFormats.forEach(format => {
+    if (packageOptions.prod === false) {
+      return
     }
-  )
+    if (format === 'cjs') {
+      packageConfigs.push(createProductionConfig(format))
+    }
+    if (/^(global|esm-browser)(-runtime)?/.test(format)) {
+      packageConfigs.push(createMinifiedConfig(format))
+    }
+  })
 }
 
 export default packageConfigs
 
 /**
  *
- * @param {Format} format
- * @param {Output} output
- * @param {import('rollup').Plugin[]} plugins
+ * @param {PackageFormat} format
+ * @param {OutputOptions} output
+ * @param {ReadonlyArray<import('rollup').Plugin>} plugins
  * @returns {import('rollup').RollupOptions}
  */
 function createConfig(format, output, plugins = []) {
@@ -180,7 +178,7 @@ function createConfig(format, output, plugins = []) {
     ],
     output,
     onwarn(msg, warn) {
-      if (!/Circular/.test(msg.message)) {
+      if (msg.code !== 'CIRCULAR_DEPENDENCY') {
         warn(msg)
       }
     },
@@ -221,7 +219,6 @@ function createConfig(format, output, plugins = []) {
 
     if (!isBundlerESMBuild) {
       // hard coded dev/prod builds
-      // @ts-ignore
       replacements.__DEV__ = String(!isProductionBuild)
     }
 
@@ -229,7 +226,9 @@ function createConfig(format, output, plugins = []) {
     //__RUNTIME_COMPILE__=true pnpm build runtime-core
     Object.keys(replacements).forEach(key => {
       if (key in process.env) {
-        replacements[key] = /** @type {string} */ (process.env[key])
+        const value = process.env[key]
+        assert(typeof value === 'string')
+        replacements[key] = value
       }
     })
     return replacements
@@ -266,7 +265,6 @@ function createConfig(format, output, plugins = []) {
     }
 
     if (Object.keys(replacements).length) {
-      // @ts-ignore
       return [replace({ values: replacements, preventAssignment: true })]
     } else {
       return []
@@ -304,7 +302,7 @@ function createConfig(format, output, plugins = []) {
   function resolveNodePlugins() {
     // we are bundling forked consolidate.js in compiler-sfc which dynamically
     // requires a ton of template engines which should be ignored.
-    /** @type { string[] } */
+    /** @type {ReadonlyArray<string>} */
     let cjsIgnores = []
     if (
       pkg.name === '@vue/compiler-sfc' ||
@@ -339,20 +337,14 @@ function createConfig(format, output, plugins = []) {
   }
 }
 
-/**
- * @param {Format} format
- */
-function createProductionConfig(format) {
+function createProductionConfig(/** @type {PackageFormat} */ format) {
   return createConfig(format, {
     ...outputConfigs[format],
     file: resolve(`dist/${name}.${format}.prod.js`)
   })
 }
 
-/**
- * @param {Format} format
- */
-function createMinifiedConfig(format) {
+function createMinifiedConfig(/** @type {PackageFormat} */ format) {
   return createConfig(
     format,
     {

+ 23 - 15
rollup.dts.config.js

@@ -1,6 +1,7 @@
 // @ts-check
+import assert from 'node:assert/strict'
 import { parse } from '@babel/parser'
-import { existsSync, readdirSync, readFileSync, writeFileSync } from 'fs'
+import { existsSync, readdirSync, readFileSync, writeFileSync } from 'node:fs'
 import MagicString from 'magic-string'
 import dts from 'rollup-plugin-dts'
 
@@ -70,15 +71,16 @@ function patchTypes(pkg) {
         if (!node.id) {
           return
         }
-        // @ts-ignore
+        assert(node.id.type === 'Identifier')
         const name = node.id.name
         if (name.startsWith('_')) {
           return
         }
         shouldRemoveExport.add(name)
         if (isExported.has(name)) {
-          // @ts-ignore
-          s.prependLeft((parentDecl || node).start, `export `)
+          const start = (parentDecl || node).start
+          assert(typeof start === 'number')
+          s.prependLeft(start, `export `)
         }
       }
 
@@ -102,9 +104,10 @@ function patchTypes(pkg) {
         if (node.type === 'VariableDeclaration') {
           processDeclaration(node.declarations[0], node)
           if (node.declarations.length > 1) {
+            assert(typeof node.start === 'number')
+            assert(typeof node.end === 'number')
             throw new Error(
               `unhandled declare const with more than one declarators:\n${code.slice(
-                // @ts-ignore
                 node.start,
                 node.end
               )}`
@@ -131,7 +134,7 @@ function patchTypes(pkg) {
               spec.type === 'ExportSpecifier' &&
               shouldRemoveExport.has(spec.local.name)
             ) {
-              // @ts-ignore
+              assert(spec.exported.type === 'Identifier')
               const exported = spec.exported.name
               if (exported !== spec.local.name) {
                 // this only happens if we have something like
@@ -141,19 +144,27 @@ function patchTypes(pkg) {
               }
               const next = node.specifiers[i + 1]
               if (next) {
-                // @ts-ignore
+                assert(typeof spec.start === 'number')
+                assert(typeof next.start === 'number')
                 s.remove(spec.start, next.start)
               } else {
                 // last one
                 const prev = node.specifiers[i - 1]
-                // @ts-ignore
-                s.remove(prev ? prev.end : spec.start, spec.end)
+                assert(typeof spec.start === 'number')
+                assert(typeof spec.end === 'number')
+                s.remove(
+                  prev
+                    ? (assert(typeof prev.end === 'number'), prev.end)
+                    : spec.start,
+                  spec.end
+                )
               }
               removed++
             }
           }
           if (removed === node.specifiers.length) {
-            // @ts-ignore
+            assert(typeof node.start === 'number')
+            assert(typeof node.end === 'number')
             s.remove(node.start, node.end)
           }
         }
@@ -186,11 +197,8 @@ function copyMts() {
   return {
     name: 'copy-vue-mts',
     writeBundle(_, bundle) {
-      writeFileSync(
-        'packages/vue/dist/vue.d.mts',
-        // @ts-ignore
-        bundle['vue.d.ts'].code
-      )
+      assert('code' in bundle['vue.d.ts'])
+      writeFileSync('packages/vue/dist/vue.d.mts', bundle['vue.d.ts'].code)
     }
   }
 }

+ 2 - 1
scripts/aliases.js

@@ -4,7 +4,7 @@ import { readdirSync, statSync } from 'node:fs'
 import path from 'node:path'
 import { fileURLToPath } from 'node:url'
 
-const resolveEntryForPkg = p =>
+const resolveEntryForPkg = (/** @type {string} */ p) =>
   path.resolve(
     fileURLToPath(import.meta.url),
     `../../packages/${p}/src/index.ts`
@@ -12,6 +12,7 @@ const resolveEntryForPkg = p =>
 
 const dirs = readdirSync(new URL('../packages', import.meta.url))
 
+/** @type {Record<string, string>} */
 const entries = {
   vue: resolveEntryForPkg('vue'),
   'vue/compiler-sfc': resolveEntryForPkg('compiler-sfc'),

+ 4 - 1
scripts/build.js

@@ -38,6 +38,7 @@ const prodOnly = !devOnly && (args.prodOnly || args.p)
 const buildTypes = args.withTypes || args.t
 const sourceMap = args.sourcemap || args.s
 const isRelease = args.release
+/** @type {boolean | undefined} */
 const buildAllMatching = args.all || args.a
 const writeSize = args.size
 const commit = execaSync('git', ['rev-parse', '--short=7', 'HEAD']).stdout
@@ -102,7 +103,9 @@ async function runParallel(maxConcurrency, source, iteratorFn) {
     ret.push(p)
 
     if (maxConcurrency <= source.length) {
-      const e = p.then(() => executing.splice(executing.indexOf(e), 1))
+      const e = p.then(() => {
+        executing.splice(executing.indexOf(e), 1)
+      })
       executing.push(e)
       if (executing.length >= maxConcurrency) {
         await Promise.race(executing)

+ 63 - 20
scripts/release.js

@@ -9,6 +9,15 @@ import { execa } from 'execa'
 import { createRequire } from 'node:module'
 import { fileURLToPath } from 'node:url'
 
+/**
+ * @typedef {{
+ *   name: string
+ *   version: string
+ *   dependencies?: { [dependenciesPackageName: string]: string }
+ *   peerDependencies?: { [peerDependenciesPackageName: string]: string }
+ * }} Package
+ */
+
 let versionUpdated = false
 
 const { prompt } = enquirer
@@ -25,6 +34,7 @@ const args = minimist(process.argv.slice(2), {
 
 const preId = args.preid || semver.prerelease(currentVersion)?.[0]
 const isDryRun = args.dry
+/** @type {boolean | undefined} */
 let skipTests = args.skipTests
 const skipBuild = args.skipBuild
 const isCanary = args.canary
@@ -43,7 +53,7 @@ const packages = fs
     }
   })
 
-const isCorePackage = pkgName => {
+const isCorePackage = (/** @type {string} */ pkgName) => {
   if (!pkgName) return
 
   if (pkgName === 'vue' || pkgName === '@vue/compat') {
@@ -56,7 +66,7 @@ const isCorePackage = pkgName => {
   )
 }
 
-const renamePackageToCanary = pkgName => {
+const renamePackageToCanary = (/** @type {string} */ pkgName) => {
   if (pkgName === 'vue') {
     return '@vue/canary'
   }
@@ -68,25 +78,37 @@ const renamePackageToCanary = pkgName => {
   return pkgName
 }
 
-const keepThePackageName = pkgName => pkgName
+const keepThePackageName = (/** @type {string} */ pkgName) => pkgName
 
+/** @type {string[]} */
 const skippedPackages = []
 
+/** @type {ReadonlyArray<import('semver').ReleaseType>} */
 const versionIncrements = [
   'patch',
   'minor',
   'major',
-  ...(preId ? ['prepatch', 'preminor', 'premajor', 'prerelease'] : [])
+  ...(preId
+    ? /** @type {const} */ (['prepatch', 'preminor', 'premajor', 'prerelease'])
+    : [])
 ]
 
-const inc = i => semver.inc(currentVersion, i, preId)
-const run = (bin, args, opts = {}) =>
-  execa(bin, args, { stdio: 'inherit', ...opts })
-const dryRun = (bin, args, opts = {}) =>
-  console.log(pico.blue(`[dryrun] ${bin} ${args.join(' ')}`), opts)
+const inc = (/** @type {import('semver').ReleaseType} */ i) =>
+  semver.inc(currentVersion, i, preId)
+const run = async (
+  /** @type {string} */ bin,
+  /** @type {ReadonlyArray<string>} */ args,
+  /** @type {import('execa').Options} */ opts = {}
+) => execa(bin, args, { stdio: 'inherit', ...opts })
+const dryRun = async (
+  /** @type {string} */ bin,
+  /** @type {ReadonlyArray<string>} */ args,
+  /** @type {import('execa').Options} */ opts = {}
+) => console.log(pico.blue(`[dryrun] ${bin} ${args.join(' ')}`), opts)
 const runIfNotDry = isDryRun ? dryRun : run
-const getPkgRoot = pkg => path.resolve(__dirname, '../packages/' + pkg)
-const step = msg => console.log(pico.cyan(msg))
+const getPkgRoot = (/** @type {string} */ pkg) =>
+  path.resolve(__dirname, '../packages/' + pkg)
+const step = (/** @type {string} */ msg) => console.log(pico.cyan(msg))
 
 async function main() {
   if (!(await isInSyncWithRemote())) {
@@ -137,7 +159,7 @@ async function main() {
           semver.inc(latestSameDayPatch, 'prerelease', args.tag)
         )
       }
-    } catch (e) {
+    } catch (/** @type {any} */ e) {
       if (/E404/.test(e.message)) {
         // the first patch version on that day
       } else {
@@ -150,7 +172,7 @@ async function main() {
 
   if (!targetVersion) {
     // no explicit version, offer suggestions
-    // @ts-ignore
+    /** @type {{ release: string }} */
     const { release } = await prompt({
       type: 'select',
       name: 'release',
@@ -159,16 +181,16 @@ async function main() {
     })
 
     if (release === 'custom') {
+      /** @type {{ version: string }} */
       const result = await prompt({
         type: 'input',
         name: 'version',
         message: 'Input custom version',
         initial: currentVersion
       })
-      // @ts-ignore
       targetVersion = result.version
     } else {
-      targetVersion = release.match(/\((.*)\)/)[1]
+      targetVersion = release.match(/\((.*)\)/)?.[1] ?? ''
     }
   }
 
@@ -183,7 +205,7 @@ async function main() {
         : `Releasing v${targetVersion}...`
     )
   } else {
-    // @ts-ignore
+    /** @type {{ yes: boolean }} */
     const { yes: confirmRelease } = await prompt({
       type: 'confirm',
       name: 'yes',
@@ -201,7 +223,7 @@ async function main() {
     skipTests ||= isCIPassed
 
     if (isCIPassed && !skipPrompts) {
-      // @ts-ignore
+      /** @type {{ yes: boolean }} */
       const { yes: promptSkipTests } = await prompt({
         type: 'confirm',
         name: 'yes',
@@ -246,7 +268,7 @@ async function main() {
   await run(`pnpm`, ['run', 'changelog'])
 
   if (!skipPrompts) {
-    // @ts-ignore
+    /** @type {{ yes: boolean }} */
     const { yes: changelogOk } = await prompt({
       type: 'confirm',
       name: 'yes',
@@ -346,7 +368,7 @@ async function isInSyncWithRemote() {
     if (data.sha === (await getSha())) {
       return true
     } else {
-      // @ts-ignore
+      /** @type {{ yes: boolean }} */
       const { yes } = await prompt({
         type: 'confirm',
         name: 'yes',
@@ -372,6 +394,10 @@ async function getBranch() {
   return (await execa('git', ['rev-parse', '--abbrev-ref', 'HEAD'])).stdout
 }
 
+/**
+ * @param {string} version
+ * @param {(pkgName: string) => string} getNewPackageName
+ */
 function updateVersions(version, getNewPackageName = keepThePackageName) {
   // 1. update root package.json
   updatePackage(path.resolve(__dirname, '..'), version, getNewPackageName)
@@ -381,8 +407,14 @@ function updateVersions(version, getNewPackageName = keepThePackageName) {
   )
 }
 
+/**
+ * @param {string} pkgRoot
+ * @param {string} version
+ * @param {(pkgName: string) => string} getNewPackageName
+ */
 function updatePackage(pkgRoot, version, getNewPackageName) {
   const pkgPath = path.resolve(pkgRoot, 'package.json')
+  /** @type {Package} */
   const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
   pkg.name = getNewPackageName(pkg.name)
   pkg.version = version
@@ -393,6 +425,12 @@ function updatePackage(pkgRoot, version, getNewPackageName) {
   fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n')
 }
 
+/**
+ * @param {Package} pkg
+ * @param {'dependencies' | 'peerDependencies'} depType
+ * @param {string} version
+ * @param {(pkgName: string) => string} getNewPackageName
+ */
 function updateDeps(pkg, depType, version, getNewPackageName) {
   const deps = pkg[depType]
   if (!deps) return
@@ -408,6 +446,11 @@ function updateDeps(pkg, depType, version, getNewPackageName) {
   })
 }
 
+/**
+ * @param {string} pkgName
+ * @param {string} version
+ * @param {ReadonlyArray<string>} additionalFlags
+ */
 async function publishPackage(pkgName, version, additionalFlags) {
   if (skippedPackages.includes(pkgName)) {
     return
@@ -443,7 +486,7 @@ async function publishPackage(pkgName, version, additionalFlags) {
       }
     )
     console.log(pico.green(`Successfully published ${pkgName}@${version}`))
-  } catch (e) {
+  } catch (/** @type {any} */ e) {
     if (e.stderr.match(/previously published/)) {
       console.log(pico.red(`Skipping already published: ${pkgName}`))
     } else {

+ 7 - 1
scripts/utils.js

@@ -16,7 +16,13 @@ export const targets = fs.readdirSync('packages').filter(f => {
   return true
 })
 
+/**
+ *
+ * @param {ReadonlyArray<string>} partialTargets
+ * @param {boolean | undefined} includeAllMatching
+ */
 export function fuzzyMatchTarget(partialTargets, includeAllMatching) {
+  /** @type {Array<string>} */
   const matched = []
   partialTargets.forEach(partialTarget => {
     for (const target of targets) {
@@ -34,7 +40,7 @@ export function fuzzyMatchTarget(partialTargets, includeAllMatching) {
     console.log()
     console.error(
       `  ${pico.white(pico.bgRed(' ERROR '))} ${pico.red(
-        `Target ${pico.underline(partialTargets)} not found!`
+        `Target ${pico.underline(partialTargets.toString())} not found!`
       )}`
     )
     console.log()

+ 0 - 2
tsconfig.build.json

@@ -11,8 +11,6 @@
     "packages/template-explorer",
     "packages/sfc-playground",
     "packages/dts-test",
-    "rollup.config.js",
-    "scripts/*",
     "playground"
   ]
 }

+ 2 - 2
tsconfig.json

@@ -35,8 +35,8 @@
     "packages/*/__tests__",
     "packages/dts-test",
     "packages/vue/jsx-runtime",
+    "scripts/*",
     "rollup.*.js",
     "playground"
-  ],
-  "exclude": ["rollup.config.js"]
+  ]
 }

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است