Skip to content

Commit 75cc377

Browse files
authored
feat: add generate declaration (#106)
* feat: add generate declaration closed 支持类型安全 #105 * chore: lint * fix: update dts default value to ./uni-pages.d.ts * docs(readme): update
1 parent 0939b41 commit 75cc377

13 files changed

+139
-11
lines changed

README.md

+11
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,17 @@ console.log(pages)
9595

9696
```ts
9797
export interface Options {
98+
/**
99+
* 为页面路径生成 TypeScript 声明
100+
*
101+
* 接受布尔值或与相对项目根目录的路径
102+
*
103+
* 默认为 uni-pages.d.ts
104+
*
105+
* @default true
106+
*/
107+
dts?: boolean | string
108+
98109
/**
99110
* 配置文件
100111
* @default 'pages.config.(ts|mts|cts|js|cjs|mjs|json)',

packages/core/package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
"sideEffects": false,
1616
"exports": {
1717
".": {
18-
"require": {
19-
"types": "./dist/index.d.cts",
20-
"default": "./dist/index.cjs"
21-
},
2218
"import": {
2319
"types": "./dist/index.d.mts",
2420
"default": "./dist/index.mjs"
21+
},
22+
"require": {
23+
"types": "./dist/index.d.cts",
24+
"default": "./dist/index.cjs"
2525
}
2626
},
2727
"./client": {

packages/core/src/context.ts

+11
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { isH5 } from '@uni-helper/uni-env'
99
import dbg from 'debug'
1010
import type { PagesConfig } from './config/types'
1111
import type { PageMetaDatum, PagePath, ResolvedOptions, SubPageMetaDatum, UserOptions } from './types'
12+
import { writeDeclaration } from './declaration'
13+
1214
import {
1315
debug,
1416
getPagesConfigSourcePaths,
@@ -283,6 +285,7 @@ export class PageContext {
283285
}
284286

285287
const pagesJson = JSON.stringify(data, null, this.options.minify ? undefined : 2)
288+
this.generateDeclaration()
286289
if (lsatPagesJson === pagesJson) {
287290
debug.pages('PagesJson Not have change')
288291
return false
@@ -308,6 +311,14 @@ export class PageContext {
308311
resolveSubRoutes() {
309312
return JSON.stringify(this.subPageMetaData, null, 2)
310313
}
314+
315+
generateDeclaration() {
316+
if (!this.options.dts)
317+
return
318+
319+
debug.declaration('generating')
320+
return writeDeclaration(this, this.options.dts)
321+
}
311322
}
312323

313324
function getPagePaths(dir: string, options: ResolvedOptions) {

packages/core/src/declaration.ts

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { existsSync } from 'node:fs'
2+
import { dirname, join } from 'node:path'
3+
import { mkdir, readFile, writeFile as writeFile_ } from 'node:fs/promises'
4+
5+
import { normalizePath } from 'vite'
6+
import type { PageContext } from './context'
7+
8+
export function getDeclaration(ctx: PageContext) {
9+
const subPagesPath = ctx.subPageMetaData.map((sub) => {
10+
return sub.pages.map(v => (`"${normalizePath(join(sub.root, v.path))}"`))
11+
}).flat()
12+
const tabsPagesPath = ctx.pagesGlobConfig?.tabBar?.list?.map((v) => {
13+
return `"${v.pagePath}"`
14+
}) ?? []
15+
const allPagesPath = [...ctx.pageMetaData.filter(page => !tabsPagesPath.includes(page.path)).map(v => `"${v.path}"`), ...subPagesPath]
16+
const code = `/* eslint-disable */
17+
/* prettier-ignore */
18+
// @ts-nocheck
19+
// Generated by vite-plugin-uni-pages
20+
21+
interface NavigateToOptions {
22+
url: ${allPagesPath.join(' |\n ')};
23+
}
24+
interface RedirectToOptions extends NavigateToOptions {}
25+
26+
interface SwitchTabOptions {
27+
${tabsPagesPath.length ? `url: ${tabsPagesPath.join(' | ')}` : ''}
28+
}
29+
30+
type ReLaunchOptions = NavigateToOptions | SwitchTabOptions;
31+
32+
declare interface Uni {
33+
navigateTo(options: UniNamespace.NavigateToOptions & NavigateToOptions): void;
34+
redirectTo(options: UniNamespace.RedirectToOptions & RedirectToOptions): void;
35+
switchTab(options: UniNamespace.SwitchTabOptions & SwitchTabOptions): void;
36+
reLaunch(options: UniNamespace.ReLaunchOptions & ReLaunchOptions): void;
37+
}
38+
`
39+
return code
40+
}
41+
42+
async function writeFile(filePath: string, content: string) {
43+
await mkdir(dirname(filePath), { recursive: true })
44+
return await writeFile_(filePath, content, 'utf-8')
45+
}
46+
47+
export async function writeDeclaration(ctx: PageContext, filepath: string) {
48+
const originalContent = existsSync(filepath) ? await readFile(filepath, 'utf-8') : ''
49+
50+
const code = getDeclaration(ctx)
51+
if (!code)
52+
return
53+
54+
if (code !== originalContent)
55+
await writeFile(filepath, code)
56+
}

packages/core/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import path from 'node:path'
22
import { spawn } from 'node:child_process'
3+
import process from 'node:process'
34
import type { Plugin } from 'vite'
45
import { createLogger } from 'vite'
56
import MagicString from 'magic-string'

packages/core/src/options.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import process from 'node:process'
2+
import { resolve } from 'node:path'
23
import { slash } from '@antfu/utils'
34
import fg from 'fast-glob'
45
import type { LoadConfigSource } from 'unconfig'
56
import type { ResolvedOptions, UserOptions } from './types'
67
import type { PagesConfig } from './config'
78

8-
export function resolveOptions(userOptions: UserOptions, viteRoot?: string): ResolvedOptions {
9+
export function resolveOptions(userOptions: UserOptions, viteRoot: string = process.cwd()): ResolvedOptions {
910
const {
11+
dts = true,
1012
configSource = 'pages.config',
1113
homePage = ['pages/index', 'pages/index/index'],
1214
mergePages = true,
@@ -34,8 +36,10 @@ export function resolveOptions(userOptions: UserOptions, viteRoot?: string): Res
3436
const resolvedSubDirs = subPackages.map(dir => slash(dir))
3537
const resolvedHomePage = typeof homePage === 'string' ? [homePage] : homePage
3638
const resolvedConfigSource = typeof configSource === 'string' ? [{ files: configSource } as LoadConfigSource<PagesConfig>] : configSource
39+
const resolvedDts = !dts ? false : typeof dts === 'string' ? dts : resolve(viteRoot, 'uni-pages.d.ts')
3740

3841
const resolvedOptions: ResolvedOptions = {
42+
dts: resolvedDts,
3943
configSource: Array.isArray(resolvedConfigSource) ? resolvedConfigSource : [resolvedConfigSource],
4044
homePage: resolvedHomePage,
4145
mergePages,

packages/core/src/types.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ export type debugType = keyof typeof debug
1313
export type ConfigSource = string | LoadConfigSource<PagesConfig> | LoadConfigSource<PagesConfig>[]
1414

1515
export interface Options {
16+
17+
/**
18+
* Generate TypeScript declaration for pages path
19+
*
20+
* Accept boolean or a path related to project root
21+
*
22+
* @default true
23+
*/
24+
dts?: boolean | string
1625
/**
1726
* Load from configs files
1827
*
@@ -84,13 +93,15 @@ export interface Options {
8493

8594
export type UserOptions = Partial<Options>
8695

87-
export interface ResolvedOptions extends Omit<Options, 'dir' | 'homePage' | 'configSource'> {
96+
export interface ResolvedOptions extends Omit<Options, 'dir' | 'homePage' | 'configSource' | 'dts'> {
8897
/**
8998
* Resolves to the `root` value from Vite config.
9099
* @default config.root
91100
*/
92101
root: string
93102

103+
dts: string | false
104+
94105
/**
95106
* Resolved page dirs
96107
*/

packages/core/src/utils.ts

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export const debug = {
2828
subPages: Debug('vite-plugin-uni-pages:subPages'),
2929
error: Debug('vite-plugin-uni-pages:error'),
3030
cache: Debug('vite-plugin-uni-pages:cache'),
31+
declaration: Debug('vite-plugin-uni-pages:declaration'),
3132
}
3233

3334
export function extsToGlob(extensions: string[]) {
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/* eslint-disable */
2+
/* prettier-ignore */
3+
// @ts-nocheck
4+
// Generated by vite-plugin-uni-pages
5+
6+
interface NavigateToOptions {
7+
url: "pages/index" |
8+
"pages/A-top" |
9+
"pages/test-json" |
10+
"pages/test-yaml" |
11+
"pages/test" |
12+
"pages/blog/index" |
13+
"pages/blog/post" |
14+
"pages-sub/index" |
15+
"pages-sub/about/index" |
16+
"pages-sub/about/your";
17+
}
18+
interface RedirectToOptions extends NavigateToOptions {}
19+
20+
interface SwitchTabOptions {
21+
22+
}
23+
24+
type ReLaunchOptions = NavigateToOptions | SwitchTabOptions;
25+
26+
declare interface Uni {
27+
navigateTo(options: UniNamespace.NavigateToOptions & NavigateToOptions): void;
28+
redirectTo(options: UniNamespace.RedirectToOptions & RedirectToOptions): void;
29+
switchTab(options: UniNamespace.SwitchTabOptions & SwitchTabOptions): void;
30+
reLaunch(options: UniNamespace.ReLaunchOptions & ReLaunchOptions): void;
31+
}

packages/playground/vite.config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ declare module 'vite' {
1313
export default defineConfig({
1414
plugins: [
1515
UniPages({
16+
dts: 'src/uni-pages.d.ts',
1617
homePage: 'pages/index',
1718
debug: true,
1819
subPackages: ['src/pages-sub'],

packages/schema/schema.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -743,4 +743,4 @@
743743
"type": "object"
744744
}
745745
}
746-
}
746+
}

packages/volar/package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@
1818
"sideEffects": false,
1919
"exports": {
2020
".": {
21-
"require": {
22-
"types": "./dist/index.d.cts",
23-
"default": "./dist/index.cjs"
24-
},
2521
"import": {
2622
"types": "./dist/index.d.mts",
2723
"default": "./dist/index.mjs"
24+
},
25+
"require": {
26+
"types": "./dist/index.d.cts",
27+
"default": "./dist/index.cjs"
2828
}
2929
}
3030
},

test/files.spec.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import process from 'node:process'
12
import { describe, expect, test } from 'vitest'
23
import { getPageFiles, resolveOptions } from '../packages/core/src'
34

0 commit comments

Comments
 (0)