From 1ade7bd422e18e2aee6086939fb16f10b8b74f99 Mon Sep 17 00:00:00 2001 From: Divyansh Singh <40380293+brc-dd@users.noreply.github.com> Date: Sat, 4 Jul 2026 05:09:00 +0530 Subject: [PATCH] refactor: migrate scripts to typescript and organize imports --- __tests__/e2e/data-loading/data.test.ts | 14 ++++---- __tests__/init/init.test.ts | 6 ++-- .../theme-default/composables/langs.test.ts | 4 +-- __tests__/unit/node/config.test.ts | 2 +- __tests__/unit/node/markdownToVue.test.ts | 4 +-- .../node/plugins/localSearchPlugin.test.ts | 6 ++-- package.json | 11 ++++--- pnpm-lock.yaml | 8 +++++ rollup.config.ts | 4 +-- scripts/{copyClient.js => copyClient.ts} | 2 +- scripts/{copyShared.js => copyShared.ts} | 0 scripts/{release.js => release.ts} | 32 +++++++++---------- scripts/{watchAndCopy.js => watchAndCopy.ts} | 9 +++--- src/node/build/bundle.ts | 5 +-- src/node/build/render.ts | 5 +-- src/node/plugins/localSearchPlugin.ts | 3 +- tsconfig.json | 3 +- 17 files changed, 65 insertions(+), 53 deletions(-) rename scripts/{copyClient.js => copyClient.ts} (88%) rename scripts/{copyShared.js => copyShared.ts} (100%) rename scripts/{release.js => release.ts} (77%) rename scripts/{watchAndCopy.js => watchAndCopy.ts} (81%) diff --git a/__tests__/e2e/data-loading/data.test.ts b/__tests__/e2e/data-loading/data.test.ts index 21cfa061c..4a4e87a80 100644 --- a/__tests__/e2e/data-loading/data.test.ts +++ b/__tests__/e2e/data-loading/data.test.ts @@ -1,4 +1,4 @@ -import fs from 'node:fs/promises' +import { writeFile, unlink } from 'node:fs/promises' import { fileURLToPath } from 'node:url' describe('static data file support in vite 3', () => { @@ -48,20 +48,20 @@ describe('static data file support in vite 3', () => { const b = fileURLToPath(new URL('./data/b.json', import.meta.url)) try { - await fs.writeFile(a, JSON.stringify({ a: false }, null, 2) + '\n') + await writeFile(a, JSON.stringify({ a: false }, null, 2) + '\n') await page.waitForFunction( () => document.querySelector('pre#basic')?.textContent === JSON.stringify([{ a: false }, { b: true }], null, 2) ) } finally { - await fs.writeFile(a, JSON.stringify({ a: true }, null, 2) + '\n') + await writeFile(a, JSON.stringify({ a: true }, null, 2) + '\n') } let err = true try { - await fs.unlink(b) + await unlink(b) await page.waitForFunction( () => document.querySelector('pre#basic')?.textContent === @@ -70,19 +70,19 @@ describe('static data file support in vite 3', () => { err = false } finally { if (err) { - await fs.writeFile(b, JSON.stringify({ b: true }, null, 2) + '\n') + await writeFile(b, JSON.stringify({ b: true }, null, 2) + '\n') } } try { - await fs.writeFile(b, JSON.stringify({ b: false }, null, 2) + '\n') + await writeFile(b, JSON.stringify({ b: false }, null, 2) + '\n') await page.waitForFunction( () => document.querySelector('pre#basic')?.textContent === JSON.stringify([{ a: true }, { b: false }], null, 2) ) } finally { - await fs.writeFile(b, JSON.stringify({ b: true }, null, 2) + '\n') + await writeFile(b, JSON.stringify({ b: true }, null, 2) + '\n') } }) diff --git a/__tests__/init/init.test.ts b/__tests__/init/init.test.ts index 1497fd92f..0066f43d2 100644 --- a/__tests__/init/init.test.ts +++ b/__tests__/init/init.test.ts @@ -1,6 +1,6 @@ -import fs from 'node:fs' import getPort from 'get-port' import { nanoid } from 'nanoid' +import { rm } from 'node:fs/promises' import path from 'node:path' import { fileURLToPath, URL } from 'node:url' import { chromium } from 'playwright-chromium' @@ -33,12 +33,12 @@ const variations = themes.flatMap((theme) => afterAll(async () => { await page.close() await browser.close() - await fs.promises.rm(tempDir, { recursive: true, force: true }) + await rm(tempDir, { recursive: true, force: true }) }) test.each(variations)('init %s', async (_, { theme, useTs }) => { const root = getTempRoot() - await fs.promises.rm(root, { recursive: true, force: true }) + await rm(root, { recursive: true, force: true }) scaffold({ root, theme, useTs, injectNpmScripts: false }) const port = await getPort() diff --git a/__tests__/unit/client/theme-default/composables/langs.test.ts b/__tests__/unit/client/theme-default/composables/langs.test.ts index 565a4a238..dc91bbe7c 100644 --- a/__tests__/unit/client/theme-default/composables/langs.test.ts +++ b/__tests__/unit/client/theme-default/composables/langs.test.ts @@ -1,7 +1,7 @@ -import { ref } from 'vue' +import { resolveLocaleLink } from 'client/theme-default/composables/langs' import type { VitePressData } from 'vitepress' import type { DefaultTheme } from 'vitepress/theme' -import { resolveLocaleLink } from 'client/theme-default/composables/langs' +import { ref } from 'vue' function createData( themeConfig: DefaultTheme.Config, diff --git a/__tests__/unit/node/config.test.ts b/__tests__/unit/node/config.test.ts index 9837879c3..22f7d0348 100644 --- a/__tests__/unit/node/config.test.ts +++ b/__tests__/unit/node/config.test.ts @@ -1,5 +1,5 @@ -import { mergeConfig } from 'node/config' import type { MarkdownItAsync } from 'markdown-it-async' +import { mergeConfig } from 'node/config' describe('node/config', () => { test('merges markdown config hooks from extended configs', async () => { diff --git a/__tests__/unit/node/markdownToVue.test.ts b/__tests__/unit/node/markdownToVue.test.ts index ec89e7e02..232496dfe 100644 --- a/__tests__/unit/node/markdownToVue.test.ts +++ b/__tests__/unit/node/markdownToVue.test.ts @@ -1,8 +1,8 @@ +import { resolveConfig } from 'node/config' +import { createMarkdownToVueRenderFn } from 'node/markdownToVue' import { mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import path from 'node:path' -import { resolveConfig } from 'node/config' -import { createMarkdownToVueRenderFn } from 'node/markdownToVue' describe('node/markdownToVue', () => { let root: string | undefined diff --git a/__tests__/unit/node/plugins/localSearchPlugin.test.ts b/__tests__/unit/node/plugins/localSearchPlugin.test.ts index c71d628f9..c972795cd 100644 --- a/__tests__/unit/node/plugins/localSearchPlugin.test.ts +++ b/__tests__/unit/node/plugins/localSearchPlugin.test.ts @@ -1,9 +1,9 @@ +import MiniSearch from 'minisearch' +import { resolveConfig } from 'node/config' +import { localSearchPlugin } from 'node/plugins/localSearchPlugin' import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import path from 'node:path' -import { resolveConfig } from 'node/config' -import { localSearchPlugin } from 'node/plugins/localSearchPlugin' -import MiniSearch from 'minisearch' describe('node/plugins/localSearchPlugin', () => { let root: string | undefined diff --git a/package.json b/package.json index 0a5624ab9..4fe35fc88 100644 --- a/package.json +++ b/package.json @@ -56,11 +56,11 @@ "dev:start": "pnpm --stream '/^dev:(client|node|watch)$/'", "dev:client": "tsc --sourcemap -w --preserveWatchOutput -p src/client", "dev:node": "DEV=true pnpm build:node -w", - "dev:shared": "node scripts/copyShared", - "dev:watch": "node scripts/watchAndCopy", + "dev:shared": "node scripts/copyShared.ts", + "dev:watch": "node scripts/watchAndCopy.ts", "build": "pnpm build:prepare && pnpm build:client && pnpm build:node", - "build:prepare": "pnpm clean && node scripts/copyShared", - "build:client": "vue-tsc --noEmit -p src/client && tsc -p src/client && node scripts/copyClient", + "build:prepare": "pnpm clean && node scripts/copyShared.ts", + "build:client": "vue-tsc --noEmit -p src/client && tsc -p src/client && node scripts/copyClient.ts", "build:node": "tsc -p src/node --noEmit && rollup --config rollup.config.ts --configPlugin esbuild", "test": "pnpm --aggregate-output --reporter=append-only '/^test:(unit|e2e|init)$/'", "test:unit": "vitest run -r __tests__/unit", @@ -87,7 +87,7 @@ "format:fail": "prettier --experimental-cli --check .", "check": "pnpm format:fail && pnpm build && pnpm test", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s", - "release": "node scripts/release.js" + "release": "node scripts/release.ts" }, "simple-git-hooks": { "pre-commit": "pnpm lint-staged" @@ -144,6 +144,7 @@ "@types/node": "^25.9.4", "@types/picomatch": "^4.0.3", "@types/prompts": "^2.4.9", + "@types/semver": "^7.7.1", "chokidar": "^5.0.0", "conventional-changelog": "^7.2.1", "conventional-changelog-angular": "^8.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 46c7c3c93..6775cb743 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -164,6 +164,9 @@ importers: '@types/prompts': specifier: ^2.4.9 version: 2.4.9 + '@types/semver': + specifier: ^7.7.1 + version: 7.7.1 chokidar: specifier: ^5.0.0 version: 5.0.0 @@ -1101,6 +1104,9 @@ packages: '@types/sax@1.2.7': resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + '@types/semver@7.7.1': + resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -3593,6 +3599,8 @@ snapshots: dependencies: '@types/node': 25.9.4 + '@types/semver@7.7.1': {} + '@types/unist@3.0.3': {} '@types/web-bluetooth@0.0.21': {} diff --git a/rollup.config.ts b/rollup.config.ts index e0e9c3ba5..23c9d41fc 100644 --- a/rollup.config.ts +++ b/rollup.config.ts @@ -3,7 +3,7 @@ import commonjs from '@rollup/plugin-commonjs' import json from '@rollup/plugin-json' import { nodeResolve } from '@rollup/plugin-node-resolve' import replace from '@rollup/plugin-replace' -import * as fs from 'node:fs/promises' +import { rm } from 'node:fs/promises' import { builtinModules, createRequire } from 'node:module' import { type RollupOptions, defineConfig } from 'rollup' import dts from 'rollup-plugin-dts' @@ -88,7 +88,7 @@ const clientTypes: RollupOptions = { name: 'cleanup', async closeBundle() { if (PROD) { - await fs.rm('dist/client-types', { recursive: true }) + await rm('dist/client-types', { recursive: true }) } } } diff --git a/scripts/copyClient.js b/scripts/copyClient.ts similarity index 88% rename from scripts/copyClient.js rename to scripts/copyClient.ts index 5815352af..7257748ec 100644 --- a/scripts/copyClient.js +++ b/scripts/copyClient.ts @@ -1,7 +1,7 @@ import { cp } from 'node:fs/promises' import { globSync } from 'tinyglobby' -function toDest(file) { +function toDest(file: string) { return file.replace(/^src\//, 'dist/') } diff --git a/scripts/copyShared.js b/scripts/copyShared.ts similarity index 100% rename from scripts/copyShared.js rename to scripts/copyShared.ts diff --git a/scripts/release.js b/scripts/release.ts similarity index 77% rename from scripts/release.js rename to scripts/release.ts index 69ab8efc6..2d4d15055 100644 --- a/scripts/release.js +++ b/scripts/release.ts @@ -1,5 +1,6 @@ -import { spawn } from 'node:child_process' -import { readFileSync, writeFileSync } from 'node:fs' +import { spawn } from 'cross-spawn' +import type { SpawnOptions } from 'node:child_process' +import fs from 'node:fs' import { createRequire } from 'node:module' import { resolve } from 'node:path' import { fileURLToPath } from 'node:url' @@ -12,17 +13,16 @@ const { version: currentVersion } = createRequire(import.meta.url)( ) const { inc: _inc, valid } = semver -const versionIncrements = ['patch', 'minor', 'major'] +const versionIncrements = ['patch', 'minor', 'major'] as const -const tags = ['latest', 'next'] +const tags = ['latest', 'next'] as const const dir = fileURLToPath(new URL('.', import.meta.url)) -const inc = (i) => _inc(currentVersion, i) -const run = (bin, args, opts = {}) => - new Promise((resolve, reject) => { +const inc = (i: semver.ReleaseType) => _inc(currentVersion, i) +const run = (bin: string, args: string[], opts: SpawnOptions = {}) => + new Promise((resolve, reject) => { const child = spawn(bin, args, { stdio: 'inherit', - shell: process.platform === 'win32', ...opts }) @@ -37,10 +37,10 @@ const run = (bin, args, opts = {}) => } }) }) -const step = (msg) => console.log(c.cyan(msg)) +const step = (msg: string) => console.log(c.cyan(msg)) async function main() { - let targetVersion + let targetVersion: string const versions = versionIncrements .map((i) => `${i} (${inc(i)})`) @@ -50,7 +50,7 @@ async function main() { type: 'select', name: 'release', message: 'Select release type', - choices: versions + choices: versions.map((title, value) => ({ title, value })) }) if (release === 3) { @@ -63,7 +63,7 @@ async function main() { }) ).version } else { - targetVersion = versions[release].match(/\((.*)\)/)[1] + targetVersion = versions[release].match(/\((.*)\)/)![1] } if (!valid(targetVersion)) { @@ -74,7 +74,7 @@ async function main() { type: 'select', name: 'tag', message: 'Select tag type', - choices: tags + choices: tags.map((title, value) => ({ title, value })) }) const { yes: tagOk } = await prompts({ @@ -132,13 +132,13 @@ async function main() { await run('git', ['push']) } -function updatePackage(version) { +function updatePackage(version: string) { const pkgPath = resolve(resolve(dir, '..'), 'package.json') - const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')) + const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) pkg.version = version - writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n') + fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n') } main().catch((err) => console.error(err)) diff --git a/scripts/watchAndCopy.js b/scripts/watchAndCopy.ts similarity index 81% rename from scripts/watchAndCopy.js rename to scripts/watchAndCopy.ts index 66da6f0d5..8f4b6679b 100644 --- a/scripts/watchAndCopy.js +++ b/scripts/watchAndCopy.ts @@ -2,7 +2,7 @@ import { watch } from 'chokidar' import { cp, rm } from 'node:fs/promises' import { normalizePath } from 'vite' -function toClientAndNode(method, file) { +function toClientAndNode(method: 'copy' | 'remove', file: string) { file = normalizePath(file) if (method === 'copy') { cp(file, file.replace(/^src\/shared\//, 'src/node/')) @@ -13,13 +13,13 @@ function toClientAndNode(method, file) { } } -function toDist(file) { +function toDist(file: string) { return normalizePath(file).replace(/^src\//, 'dist/') } // copy shared files to the client and node directory whenever they change. watch('src/shared', { - ignored: (path, stats) => stats?.isFile() && !path.endsWith('.ts') + ignored: (path, stats) => !!stats?.isFile() && !path.endsWith('.ts') }) .on('change', (file) => toClientAndNode('copy', file)) .on('add', (file) => toClientAndNode('copy', file)) @@ -29,7 +29,8 @@ watch('src/shared', { // they change. watch('src/client', { ignored: (path, stats) => - stats?.isFile() && (path.endsWith('.ts') || path.endsWith('tsconfig.json')) + !!stats?.isFile() && + (path.endsWith('.ts') || path.endsWith('tsconfig.json')) }) .on('change', (file) => cp(file, toDist(file))) .on('add', (file) => cp(file, toDist(file))) diff --git a/src/node/build/bundle.ts b/src/node/build/bundle.ts index cac239ed5..c98eaa1ab 100644 --- a/src/node/build/bundle.ts +++ b/src/node/build/bundle.ts @@ -1,4 +1,5 @@ import fs from 'node:fs' +import { cp } from 'node:fs/promises' import path from 'node:path' import { fileURLToPath } from 'node:url' import { @@ -202,7 +203,7 @@ export async function bundle( if (!chunk.fileName.endsWith('.js')) { const tempPath = path.resolve(config.tempDir, chunk.fileName) const outPath = path.resolve(config.outDir, chunk.fileName) - await fs.promises.cp(tempPath, outPath) + await cp(tempPath, outPath) } }) ) @@ -211,7 +212,7 @@ export async function bundle( if (fs.existsSync(publicDir)) { // dereference symlinks like vite's own publicDir copy does, and so that // copying over an existing symlinked file does not fail with EEXIST - await fs.promises.cp(publicDir, config.outDir, { + await cp(publicDir, config.outDir, { recursive: true, dereference: true }) diff --git a/src/node/build/render.ts b/src/node/build/render.ts index 3cf54953c..14410fe5a 100644 --- a/src/node/build/render.ts +++ b/src/node/build/render.ts @@ -1,5 +1,6 @@ import { isBooleanAttr } from '@vue/shared' import fs from 'node:fs' +import { mkdir, writeFile } from 'node:fs/promises' import path from 'node:path' import { minify, normalizePath, type Rolldown } from 'vite' import { version } from '../../../package.json' @@ -191,7 +192,7 @@ export async function renderPage( ` const htmlFileName = path.join(config.outDir, page.replace(/\.md$/, '.html')) - await fs.promises.mkdir(path.dirname(htmlFileName), { recursive: true }) + await mkdir(path.dirname(htmlFileName), { recursive: true }) const transformedHtml = await config.transformHtml?.(html, htmlFileName, { page, siteConfig: config, @@ -203,7 +204,7 @@ export async function renderPage( content, assets }) - await fs.promises.writeFile(htmlFileName, transformedHtml || html) + await writeFile(htmlFileName, transformedHtml || html) } function resolvePageImports( diff --git a/src/node/plugins/localSearchPlugin.ts b/src/node/plugins/localSearchPlugin.ts index 7ba43c1f8..332c3294a 100644 --- a/src/node/plugins/localSearchPlugin.ts +++ b/src/node/plugins/localSearchPlugin.ts @@ -1,5 +1,6 @@ import MiniSearch from 'minisearch' import fs from 'node:fs' +import { readFile } from 'node:fs/promises' import path from 'node:path' import { createDebug } from 'obug' import type { Plugin, ViteDevServer } from 'vite' @@ -54,7 +55,7 @@ export async function localSearchPlugin( const { srcDir, cleanUrls = false } = siteConfig const relativePath = slash(path.relative(srcDir, file)) const env: MarkdownEnv = { path: file, relativePath, cleanUrls } - const md_raw = await fs.promises.readFile(file, 'utf-8') + const md_raw = await readFile(file, 'utf-8') const md_src = processIncludes(md, srcDir, md_raw, file, [], cleanUrls) if (options._render) { return await options._render(md_src, env, md) diff --git a/tsconfig.json b/tsconfig.json index 6d6abf89b..a0a5c69b7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,7 +17,6 @@ "**/dist/**", "template", "bin", - "docs/snippets", - "scripts" + "docs/snippets" ] }