// @ts-check
+import assert from 'node:assert/strict'
import { createRequire } from 'node:module'
import { fileURLToPath } from 'node:url'
import path from 'node:path'
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.')
}
const packagesDir = path.resolve(__dirname, 'packages')
const packageDir = path.resolve(packagesDir, process.env.TARGET)
-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()
+/** @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`),
}
}
+/** @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
? []
export default packageConfigs
+/**
+ *
+ * @param {PackageFormat} format
+ * @param {OutputOptions} output
+ * @param {ReadonlyArray<import('rollup').Plugin>} plugins
+ * @returns {import('rollup').RollupOptions}
+ */
function createConfig(format, output, plugins = []) {
if (!output) {
console.log(pico.yellow(`invalid format: "${format}"`))
}
function resolveDefine() {
+ /** @type {Record<string, string>} */
const replacements = {
__COMMIT__: `"${process.env.COMMIT}"`,
__VERSION__: `"${masterVersion}"`,
if (!isBundlerESMBuild) {
// hard coded dev/prod builds
- // @ts-ignore
replacements.__DEV__ = String(!isProductionBuild)
}
//__RUNTIME_COMPILE__=true pnpm build runtime-core
Object.keys(replacements).forEach(key => {
if (key in process.env) {
- replacements[key] = process.env[key]
+ const value = process.env[key]
+ assert(typeof value === 'string')
+ replacements[key] = value
}
})
return replacements
}
if (Object.keys(replacements).length) {
- // @ts-ignore
return [replace({ values: replacements, preventAssignment: true })]
} else {
return []
function resolveNodePlugins() {
// we are bundling forked consolidate.js in compiler-sfc which dynamically
// requires a ton of template engines which should be ignored.
+ /** @type {ReadonlyArray<string>} */
let cjsIgnores = []
if (
pkg.name === '@vue/compiler-sfc' ||
],
output,
onwarn: (msg, warn) => {
- if (!/Circular/.test(msg)) {
+ if (msg.code !== 'CIRCULAR_DEPENDENCY') {
warn(msg)
}
},
}
}
-function createProductionConfig(format) {
+function createProductionConfig(/** @type {PackageFormat} */ format) {
return createConfig(format, {
file: resolve(`dist/${name}.${format}.prod.js`),
format: outputConfigs[format].format
})
}
-function createMinifiedConfig(format) {
+function createMinifiedConfig(/** @type {PackageFormat} */ format) {
return createConfig(
format,
{
// @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'
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 `)
}
}
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
)}`
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
}
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)
}
}
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)
}
}
}
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`
const dirs = readdirSync(new URL('../packages', import.meta.url))
+/** @type {Record<string, string>} */
const entries = {
vue: resolveEntryForPkg('vue'),
'vue/compiler-sfc': resolveEntryForPkg('compiler-sfc'),
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
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)
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
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
}
})
-const isCorePackage = pkgName => {
+const isCorePackage = (/** @type {string} */ pkgName) => {
if (!pkgName) return
if (pkgName === 'vue' || pkgName === '@vue/compat') {
)
}
-const renamePackageToCanary = pkgName => {
+const renamePackageToCanary = (/** @type {string} */ pkgName) => {
if (pkgName === 'vue') {
return '@vue/canary'
}
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())) {
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 {
if (!targetVersion) {
// no explicit version, offer suggestions
- // @ts-ignore
+ /** @type {{ release: string }} */
const { release } = await prompt({
type: 'select',
name: 'release',
})
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] ?? ''
}
}
: `Releasing v${targetVersion}...`
)
} else {
- // @ts-ignore
+ /** @type {{ yes: boolean }} */
const { yes: confirmRelease } = await prompt({
type: 'confirm',
name: 'yes',
skipTests ||= isCIPassed
if (isCIPassed && !skipPrompts) {
- // @ts-ignore
+ /** @type {{ yes: boolean }} */
const { yes: promptSkipTests } = await prompt({
type: 'confirm',
name: 'yes',
await run(`pnpm`, ['run', 'changelog'])
if (!skipPrompts) {
- // @ts-ignore
+ /** @type {{ yes: boolean }} */
const { yes: changelogOk } = await prompt({
type: 'confirm',
name: 'yes',
if (data.sha === (await getSha())) {
return true
} else {
- // @ts-ignore
+ /** @type {{ yes: boolean }} */
const { yes } = await prompt({
type: 'confirm',
name: 'yes',
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)
)
}
+/**
+ * @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
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
})
}
+/**
+ * @param {string} pkgName
+ * @param {string} version
+ * @param {ReadonlyArray<string>} additionalFlags
+ */
async function publishPackage(pkgName, version, additionalFlags) {
if (skippedPackages.includes(pkgName)) {
return
}
)
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 {
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) {
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()
"packages/runtime-test",
"packages/template-explorer",
"packages/sfc-playground",
- "packages/dts-test",
- "rollup.config.js",
- "scripts/*"
+ "packages/dts-test"
]
}
"packages/vue/jsx-runtime",
"scripts/*",
"rollup.*.js"
- ],
- "exclude": ["rollup.config.js", "scripts/*"]
+ ]
}