From: ZHAO Jin-Xiang Date: Thu, 30 Nov 2023 06:17:51 +0000 (+0800) Subject: chore: improve typing for scripts (#9709) X-Git-Tag: v3.4.0-alpha.4~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7218ab749676c49e68ad44b12efbf63f0ab5f4f0;p=thirdparty%2Fvuejs%2Fcore.git chore: improve typing for scripts (#9709) --- diff --git a/rollup.config.js b/rollup.config.js index 421fa9c378..b7d38e4527 100644 --- a/rollup.config.js +++ b/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 & Required> } MarkRequired + */ +/** @typedef {'cjs' | 'esm-bundler' | 'global' | 'global-runtime' | 'esm-browser' | 'esm-bundler-runtime' | 'esm-browser-runtime'} PackageFormat */ +/** @typedef {MarkRequired} OutputOptions */ + if (!process.env.TARGET) { throw new Error('TARGET package must be specified via --environment flag.') } @@ -27,34 +36,35 @@ const consolidatePkg = require('@vue/consolidate/package.json') 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} */ 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`), @@ -66,8 +76,13 @@ const outputConfigs = { } } +/** @type {ReadonlyArray} */ const defaultFormats = ['esm-bundler', 'cjs'] -const inlineFormats = process.env.FORMATS && process.env.FORMATS.split(',') +/** @type {ReadonlyArray} */ +const inlineFormats = /** @type {any} */ ( + process.env.FORMATS && process.env.FORMATS.split(',') +) +/** @type {ReadonlyArray} */ const packageFormats = inlineFormats || packageOptions.formats || defaultFormats const packageConfigs = process.env.PROD_ONLY ? [] @@ -89,6 +104,13 @@ if (process.env.NODE_ENV === 'production') { export default packageConfigs +/** + * + * @param {PackageFormat} format + * @param {OutputOptions} output + * @param {ReadonlyArray} plugins + * @returns {import('rollup').RollupOptions} + */ function createConfig(format, output, plugins = []) { if (!output) { console.log(pico.yellow(`invalid format: "${format}"`)) @@ -132,6 +154,7 @@ function createConfig(format, output, plugins = []) { } function resolveDefine() { + /** @type {Record} */ const replacements = { __COMMIT__: `"${process.env.COMMIT}"`, __VERSION__: `"${masterVersion}"`, @@ -162,7 +185,6 @@ function createConfig(format, output, plugins = []) { if (!isBundlerESMBuild) { // hard coded dev/prod builds - // @ts-ignore replacements.__DEV__ = String(!isProductionBuild) } @@ -170,7 +192,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] = process.env[key] + const value = process.env[key] + assert(typeof value === 'string') + replacements[key] = value } }) return replacements @@ -207,7 +231,6 @@ function createConfig(format, output, plugins = []) { } if (Object.keys(replacements).length) { - // @ts-ignore return [replace({ values: replacements, preventAssignment: true })] } else { return [] @@ -245,6 +268,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 {ReadonlyArray} */ let cjsIgnores = [] if ( pkg.name === '@vue/compiler-sfc' || @@ -304,7 +328,7 @@ function createConfig(format, output, plugins = []) { ], output, onwarn: (msg, warn) => { - if (!/Circular/.test(msg)) { + if (msg.code !== 'CIRCULAR_DEPENDENCY') { warn(msg) } }, @@ -314,14 +338,14 @@ function createConfig(format, output, plugins = []) { } } -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, { diff --git a/rollup.dts.config.js b/rollup.dts.config.js index 3e45167bc4..a9b3a0cdad 100644 --- a/rollup.dts.config.js +++ b/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) } } } diff --git a/scripts/aliases.js b/scripts/aliases.js index 34a7c64355..907196745f 100644 --- a/scripts/aliases.js +++ b/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} */ const entries = { vue: resolveEntryForPkg('vue'), 'vue/compiler-sfc': resolveEntryForPkg('compiler-sfc'), diff --git a/scripts/build.js b/scripts/build.js index b96e576caa..aa56a13275 100644 --- a/scripts/build.js +++ b/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) diff --git a/scripts/release.js b/scripts/release.js index a9484ff5f4..fddf4ca672 100644 --- a/scripts/release.js +++ b/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} */ 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} */ args, + /** @type {import('execa').Options} */ opts = {} +) => execa(bin, args, { stdio: 'inherit', ...opts }) +const dryRun = async ( + /** @type {string} */ bin, + /** @type {ReadonlyArray} */ 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} 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 { diff --git a/scripts/utils.js b/scripts/utils.js index 9429074d7a..789e1bcf68 100644 --- a/scripts/utils.js +++ b/scripts/utils.js @@ -16,7 +16,13 @@ export const targets = fs.readdirSync('packages').filter(f => { return true }) +/** + * + * @param {ReadonlyArray} partialTargets + * @param {boolean | undefined} includeAllMatching + */ export function fuzzyMatchTarget(partialTargets, includeAllMatching) { + /** @type {Array} */ 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() diff --git a/tsconfig.build.json b/tsconfig.build.json index 28036b1bed..954103c0f2 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -10,8 +10,6 @@ "packages/runtime-test", "packages/template-explorer", "packages/sfc-playground", - "packages/dts-test", - "rollup.config.js", - "scripts/*" + "packages/dts-test" ] } diff --git a/tsconfig.json b/tsconfig.json index 0aad03ad6c..f47b7fc8eb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -36,6 +36,5 @@ "packages/vue/jsx-runtime", "scripts/*", "rollup.*.js" - ], - "exclude": ["rollup.config.js", "scripts/*"] + ] }