Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Debug Uni App H5",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}/packages/playground",
"preLaunchTask": "npm: debug - packages/core",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Ensure the preLaunch task uses pnpm, not npm

"preLaunchTask": "npm: debug - packages/core" relies on VS Code’s package manager setting. If npm.packageManager isn’t set to pnpm, this will run via npm and may break a pnpm workspace.

Option A: add a workspace setting .vscode/settings.json:

{
  "npm.packageManager": "pnpm"
}

Option B: replace the preLaunch task with a shell task that invokes pnpm explicitly.


🏁 Script executed:

#!/bin/bash
# Verify VS Code is configured to use pnpm for npm tasks
if [[ -f .vscode/settings.json ]]; then
  echo "Detected .vscode/settings.json:"
  cat .vscode/settings.json
  echo
  echo "Configured package manager:"
  rg -n '"npm.packageManager"\s*:\s*"pnpm"' .vscode/settings.json || {
    echo "Missing npm.packageManager=pnpm setting"; exit 1;
  }
else
  echo "No .vscode/settings.json found. Recommend setting npm.packageManager to pnpm."; exit 2;
fi

Length of output: 1526


Require pnpm for VS Code preLaunchTask

Your workspace has a .vscode/settings.json but it’s missing the npm.packageManager setting, so VS Code will default to using npm for "npm: debug - packages/core", which breaks pnpm workspaces.

To resolve:

  • In .vscode/settings.json, add the pnpm package manager setting:
    {
      // …existing settings…
      "npm.packageManager": "pnpm"
    }
  • Or, explicitly invoke pnpm in your launch configuration (.vscode/launch.json, around line 12):
    - "preLaunchTask": "npm: debug - packages/core",
    + "preLaunchTask": "pnpm: debug - packages/core",

Either approach will ensure VS Code runs your debug task with pnpm rather than npm.

🤖 Prompt for AI Agents
.vscode/launch.json around line 12: the preLaunchTask "npm: debug -
packages/core" will cause VS Code to use npm by default which breaks pnpm
workspaces; either add "npm.packageManager": "pnpm" to .vscode/settings.json so
VS Code uses pnpm for npm scripts, or change the launch.json preLaunchTask to
explicitly call the pnpm task (replace the npm task reference with the
equivalent pnpm task name or invoke a task that runs "pnpm run debug
--workspace=packages/core"). Ensure the chosen change is committed so VS Code
runs the debug task with pnpm.

"runtimeExecutable": "pnpm",
"runtimeArgs": ["run", "dev:h5"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"env": {
"NODE_ENV": "development"
}
},
{
"name": "Debug Uni App MP-Weixin",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}/packages/playground",
"preLaunchTask": "npm: debug - packages/core",
"runtimeExecutable": "pnpm",
"runtimeArgs": ["run", "dev:mp-weixin"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
}
]
}
29 changes: 11 additions & 18 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
{
// Enable the ESlint flat config support
"eslint.experimental.useFlatConfig": true,

// Disable the default formatter, use eslint instead
"prettier.enable": false,
"editor.formatOnSave": false,
Expand All @@ -12,20 +9,6 @@
"source.organizeImports": "never"
},

// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off" },
{ "rule": "format/*", "severity": "off" },
{ "rule": "*-indent", "severity": "off" },
{ "rule": "*-spacing", "severity": "off" },
{ "rule": "*-spaces", "severity": "off" },
{ "rule": "*-order", "severity": "off" },
{ "rule": "*-dangle", "severity": "off" },
{ "rule": "*-newline", "severity": "off" },
{ "rule": "*quotes", "severity": "off" },
{ "rule": "*semi", "severity": "off" }
],

// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
Expand All @@ -38,6 +21,16 @@
"json",
"jsonc",
"yaml",
"toml"
"toml",
"xml",
"gql",
"graphql",
"astro",
"svelte",
"css",
"less",
"scss",
"pcss",
"postcss"
]
}
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# @uni-helper/vite-plugin-uni-pages
<img src="./banner.svg" alt="banner" width="100%"/>

在 Vite 驱动的 uni-app 上使用基于文件的路由系统。
<br >
<a href="https://github.com/uni-helper/vite-plugin-uni-pages/stargazers"><img src="https://img.shields.io/github/stars/uni-helper/vite-plugin-uni-pages?colorA=005947&colorB=fafafa&style=for-the-badge"></a>
<a href="https://www.npmjs.com/package/@uni-helper/vite-plugin-uni-pages"><img src="https://img.shields.io/npm/dm/@uni-helper/vite-plugin-uni-pages?colorA=005947&colorB=fafafa&style=for-the-badge"></a>
<a href="https://www.npmjs.com/package/@uni-helper/vite-plugin-uni-pages"><img src="https://img.shields.io/npm/v/@uni-helper/vite-plugin-uni-pages?colorA=005947&colorB=fafafa&style=for-the-badge"></a>

***

<a href="https://www.npmjs.com/package/@uni-helper/vite-plugin-uni-pages"><img src="https://img.shields.io/npm/v/@uni-helper/vite-plugin-uni-pages" alt="NPM version"></a></p>
在 Vite 驱动的 uni-app 上使用基于文件的路由系统。

## Packages

Expand Down
418 changes: 418 additions & 0 deletions banner.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@
"lint:fix": "pnpm lint --fix"
},
"devDependencies": {
"@antfu/eslint-config": "^2.26.0",
"@antfu/eslint-config": "^5.2.1",
"@types/node": "^20.16.1",
"bumpp": "^9.5.1",
"eslint": "^9.9.0",
"comment-json": "^4.2.5",
"eslint": "^9.34.0",
"rimraf": "^6.0.1",
"typescript": "^5.5.4",
"unbuild": "^2.0.0",
"vitest": "^2.0.5",
"comment-json": "^4.2.5"
"vitest": "^2.0.5"
}
}
2 changes: 2 additions & 0 deletions packages/core/build.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import process from 'node:process'
import { defineBuildConfig } from 'unbuild'

export default defineBuildConfig({
entries: ['src/index'],
declaration: true,
clean: true,
sourcemap: process.env.NODE_ENV === 'development',
rollup: {
emitCJS: true,
inlineDependencies: true,
Expand Down
4 changes: 4 additions & 0 deletions packages/core/client.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ declare module 'virtual:uni-pages' {
export const pages: PageMetaDatum[]
export const subPackages: SubPackage[]
}

declare namespace globalThis{
export const definePage: import('.').DefinePage
}
5 changes: 5 additions & 0 deletions packages/core/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference path="./client.d.ts" />

export * from './dist'

export { default } from './dist'
Comment on lines +3 to +5
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Confirm default export exists in ./dist, or drop the default re-export

Re-exporting default from './dist' will fail type resolution if the dist entry has no default export. Keep the named export star and only keep the default re-export if the compiled output exposes a default.

Run this quick check:

If no default is present, adjust this file:

-export { default } from './dist'
+// export { default } from './dist' // keep only if './dist' provides a default export

🏁 Script executed:

#!/bin/bash
# Verify a default export exists in dist or source
rg -nP 'export\s+default\b' packages/core/dist 2>/dev/null || rg -nP 'export\s+default\b' packages/core/src || echo "No default export detected"

Length of output: 230


Drop the default re-export in packages/core/index.d.ts
The packages/core/dist entrypoint does not actually export a default, so the line

export { default } from './dist'

will break downstream type resolution. Remove it (or add a default export in your build if you intended one).

Location: packages/core/index.d.ts
Change:

-export { default } from './dist'
+// export { default } from './dist' // removed—no default export in dist
🤖 Prompt for AI Agents
In packages/core/index.d.ts around lines 3 to 5, the file re-exports a
non-existent default from './dist' which breaks downstream type resolution;
remove the line "export { default } from './dist'" (or alternatively add a real
default export in the built ./dist types if you intended one) so that only the
named export re-exports remain and TypeScript consumers won't receive an
incorrect default export.

20 changes: 14 additions & 6 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"types": "./index.d.ts",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Consider the impact of changing type declaration exports.

Moving type declarations from ./dist/index.d.ts to ./index.d.ts changes the public API surface. This could break consumers who directly import from the dist folder.

Let me verify if there's a corresponding index.d.ts file at the root and check for any direct imports in the codebase:

Also applies to: 34-34, 44-44, 48-48


🏁 Script executed:

#!/bin/bash
# Check if index.d.ts exists at the package root
fd -t f "^index\.d\.ts$" packages/core --max-depth 1

# Search for any direct imports from dist folder in TypeScript files
rg -n --type=ts "from\s+['\"]@uni-helper/vite-plugin-uni-pages/dist" -g '!node_modules/**' -g '!dist/**'

# Check if there's a build step that generates index.d.ts at root
rg -n "index\.d\.ts" packages/core/build.config.ts

Length of output: 241


Ensure deep‐imports of type declarations remain compatible

I confirmed that packages/core/index.d.ts exists at the package root, but our build setup does not emit or copy any .d.ts into packages/core/dist. This means consumers who import types via

import { Foo } from '@uni-helper/vite-plugin-uni-pages/dist'

will no longer find dist/index.d.ts after this change.

Action items to avoid a breaking change:

  • Add a packages/core/dist/index.d.ts file (for example, with export * from '../index';) or update the build to copy the root declaration into dist.
  • Alternatively, use the "exports" field in package.json to redirect deep imports back to the root types.
  • Update documentation to discourage direct imports from dist, or bump the major version and call out this breaking change in the changelog.

"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"types": "./index.d.ts",
"default": "./dist/index.cjs"
}
},
Expand All @@ -41,39 +41,47 @@
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"types": "./index.d.ts",
"files": [
"client.d.ts",
"dist"
"dist",
"index.d.ts"
],
"scripts": {
"build": "unbuild",
"stub": "unbuild --stub",
"debug": "cross-env NODE_ENV=development unbuild",
"prepublishOnly": "pnpm build"
},
"peerDependencies": {
"vite": "^5.0.0"
},
"dependencies": {
"@babel/generator": "^7.28.3",
"@babel/types": "^7.28.2",
"@uni-helper/uni-env": "^0.1.4",
"@vue/compiler-sfc": "3.4.21",
"ast-kit": "^2.1.2",
"chokidar": "^3.6.0",
"comment-json": "^4.2.5",
"debug": "^4.3.6",
"detect-indent": "^6.1.0",
"detect-newline": "^3.1.0",
"fast-glob": "^3.3.2",
"json5": "^2.2.3",
"kolorist": "^1.8.0",
"lodash.groupby": "^4.6.0",
"magic-string": "^0.30.11",
"unconfig": "^7.3.2",
"yaml": "^2.5.0",
"comment-json": "^4.2.5"
"yaml": "^2.5.0"
},
"devDependencies": {
"@antfu/utils": "^0.7.10",
"@types/babel__generator": "^7.27.0",
"@types/debug": "^4.1.12",
"@types/lodash.groupby": "^4.6.9",
"@types/node": "^20.15.0",
"cross-env": "^10.0.0",
"vite": "^5.4.1"
}
}
82 changes: 47 additions & 35 deletions packages/core/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ import path from 'node:path'
import process from 'node:process'
import type { FSWatcher } from 'chokidar'
import type { Logger, ViteDevServer } from 'vite'
import { normalizePath } from 'vite'
import { loadConfig } from 'unconfig'
import { slash } from '@antfu/utils'
import dbg from 'debug'
import { platform } from '@uni-helper/uni-env'
import detectIndent from 'detect-indent'
import detectNewline from 'detect-newline'
import { assign as cjAssign, stringify as cjStringify } from 'comment-json'
import { stringify as cjStringify } from 'comment-json'
import type { PagesConfig } from './config/types'
import type { PageMetaDatum, PagePath, ResolvedOptions, SubPageMetaDatum, UserOptions } from './types'
import { writeDeclaration } from './declaration'
Expand All @@ -19,24 +18,22 @@ import {
invalidatePagesModule,
isTargetFile,
mergePageMetaDataArray,
useCachedPages,
} from './utils'
import { resolveOptions } from './options'
import { checkPagesJsonFile, getPageFiles, readFileSync, writeFileSync } from './files'
import { getRouteBlock, getRouteSfcBlock } from './customBlock'
import { OUTPUT_NAME } from './constant'
import { Page } from './page'

let lsatPagesJson = ''

const { setCache, hasChanged } = useCachedPages()
export class PageContext {
private _server: ViteDevServer | undefined

pagesGlobConfig: PagesConfig | undefined
pagesConfigSourcePaths: string[] = []

pagesPath: PagePath[] = []
subPagesPath: Record<string, PagePath[]> = {}
pages = new Map<string, Page>() // abs path -> Page
subPages = new Map<string, Map<string, Page>>() // root -> abs path -> page
pageMetaData: PageMetaDatum[] = []
subPageMetaData: SubPageMetaDatum[] = []

Expand Down Expand Up @@ -89,18 +86,35 @@ export class PageContext {
return { dir, files: getPagePaths(dir, this.options) }
})

this.pagesPath = pageDirFiles.map(page => page.files).flat()
debug.pages(this.pagesPath)
const paths = pageDirFiles.map(page => page.files).flat()
debug.pages(paths)

const pages = new Map<string, Page>()
for (const path of paths) {
const page = this.pages.get(path.absolutePath) || new Page(this, path)
pages.set(path.absolutePath, page)
}

this.pages = pages
}

async scanSubPages() {
const subPagesPath: Record<string, PagePath[]> = {}
const paths: Record<string, PagePath[]> = {}
const subPages = new Map<string, Map<string, Page>>()
for (const dir of this.options.subPackages) {
const pagePaths = getPagePaths(dir, this.options)
subPagesPath[dir] = pagePaths
paths[dir] = pagePaths

const pages = new Map<string, Page>()
for (const path of pagePaths) {
const page = this.subPages.get(dir)?.get(path.absolutePath) || new Page(this, path)
pages.set(path.absolutePath, page)
}
subPages.set(dir, pages)
}
this.subPagesPath = subPagesPath
debug.subPages(this.subPagesPath)
debug.subPages(JSON.stringify(paths, null, 2))

this.subPages = subPages
}

setupViteServer(server: ViteDevServer) {
Expand Down Expand Up @@ -177,32 +191,19 @@ export class PageContext {
})
}

async parsePage(page: PagePath): Promise<PageMetaDatum> {
const { relativePath, absolutePath } = page
const routeSfcBlock = await getRouteSfcBlock(absolutePath)
const routeBlock = await getRouteBlock(absolutePath, routeSfcBlock, this.options)
setCache(absolutePath, routeSfcBlock)
const relativePathWithFileName = relativePath.replace(path.extname(relativePath), '')
const pageMetaDatum: PageMetaDatum = {
path: normalizePath(relativePathWithFileName),
type: routeBlock?.attr.type ?? 'page',
}

if (routeBlock)
cjAssign(pageMetaDatum, routeBlock.content)

return pageMetaDatum
}

/**
* parse pages rules && set page type
* @param pages page path array
* @param type page type
* @param overrides custom page config
* @returns pages rules
*/
async parsePages(pages: PagePath[], type: 'main' | 'sub', overrides?: PageMetaDatum[]) {
const generatedPageMetaData = await Promise.all(pages.map(async page => await this.parsePage(page)))
async parsePages(pages: Map<string, Page>, type: 'main' | 'sub', overrides?: PageMetaDatum[]) {
const jobs: Promise<PageMetaDatum>[] = []
for (const [_, page] of pages) {
jobs.push(page.getPageMeta())
}
const generatedPageMetaData = await Promise.all(jobs)
const customPageMetaData = overrides || []

const result = customPageMetaData.length
Expand Down Expand Up @@ -245,7 +246,7 @@ export class PageContext {
}

async mergePageMetaData() {
const pageMetaData = await this.parsePages(this.pagesPath, 'main', this.pagesGlobConfig?.pages)
const pageMetaData = await this.parsePages(this.pages, 'main', this.pagesGlobConfig?.pages)

this.pageMetaData = pageMetaData
debug.pages(this.pageMetaData)
Expand All @@ -255,7 +256,7 @@ export class PageContext {
const subPageMaps: Record<string, PageMetaDatum[]> = {}
const subPackages = this.pagesGlobConfig?.subPackages || []

for (const [dir, pages] of Object.entries(this.subPagesPath)) {
for (const [dir, pages] of this.subPages) {
const basePath = slash(path.join(this.options.root, this.options.outDir))
const root = slash(path.relative(basePath, path.join(this.options.root, dir)))

Expand All @@ -280,7 +281,18 @@ export class PageContext {

async updatePagesJSON(filepath?: string) {
if (filepath) {
if (!await hasChanged(filepath)) {
let page = this.pages.get(filepath)
if (!page) {
let subPage: Page | undefined
for (const [_, pages] of this.subPages) {
subPage = pages.get(filepath)
if (subPage) {
break
}
}
page = subPage
}
if (page && !await page.hasChanged()) {
debug.cache(`The route block on page ${filepath} did not send any changes, skipping`)
return false
}
Expand Down
Loading
Loading