vite-plugin-uni-pages

repository·main·Indexed 41 days ago

https://github.com/uni-helper/vite-plugin-uni-pages

A Vite plugin for uni-app that enables a file-based routing system, allowing developers to manage pages.json using TypeScript and a file structure instead of manual configuration. It features a definePage macro for page-level metadata, support for platform-specific configurations via a built-in DSL, and automatic generation of TypeScript declarations for page paths. Version 0.5.0 introduces functional syntax for definePage and ESM-only support.

Tokens
18.1K
Snippets
42
Records
62
Agent score
53%

What's inside vite-plugin-uni-pages

  1. Related Projects in the uni-helper ecosystem

    main

    The vite-plugin-uni-pages project is part of a larger ecosystem of tools designed to improve the developer experience for uni-app using TypeScript and Vite. Related tools include:

    • @uni-helper/vite-plugin-uni-pages: The core plugin for writing pages.json with TypeScript and supporting file-based (convention-based) routing.
    • @uni-helper/pages-json-schema: Provides JSON schema support for pages.json.
    • @uni-helper/uni-pages-types: Provides TypeScript types for pages.json.
    • vite-plugin-uni-manifest: A tool for writing manifest.json using TypeScript.
    • vite-plugin-uni-platform: A plugin for platform-specific compilation based on filenames (e.g., *.<h5|mp-weixin|app>.*).
    • vite-plugin-uni-platform-modifier: Provides platform modifiers for attributes and directives with on-demand compilation.
    • vite-plugin-uni-layouts: Provides a Nuxt-like layouts system for uni-app under Vite.
    • uni-ku/root: A utility to resolve issues where uni-app cannot use root components.
  2. Plugin Architecture and Lifecycle

    main

    The plugin is organized around a PageContext which orchestrates the core pipeline: Load User Config → Scan Pages → Merge Page Config → Write pages.json.

    Vite Lifecycle Integration

    The plugin hooks into the Vite lifecycle as follows:

    1. Plugin Factory: Synchronously pre-checks if pages.json exists and is valid.
    2. configResolved: Creates the PageContext, detects collaboration with vite-plugin-uni-platform, and performs the initial updatePagesJSON() to generate pages.json. In build --watch mode, it sets up a watcher for the page directory.
    3. configureServer (Dev Mode): Uses Vite's internal watcher to monitor configuration files (like pages.config.ts). On change, it reruns the full pipeline, invalidates virtual modules, and triggers a browser full-reload.
    4. transform: Removes definePage macro calls from .vue, .nvue, and .uvue files to prevent runtime errors.
    5. resolveId / load: Provides the virtual:uni-pages virtual module, which exposes all page metadata to the application.
  3. How multi-terminal concurrent development works

    main

    When running multiple platforms simultaneously (e.g., pnpm run dev:mp-weixin and pnpm run dev:mp-alipay), the plugin manages concurrent access to pages.json to prevent data loss or corruption through several mechanisms:

    • File Locking: Uses a file lock to serialize the "read existing content → merge current platform config → write back" process.
    • Conditional Compilation (#ifdef): Preserves #ifdef blocks written by other platforms and only updates entries relevant to the current platform.
    • Deduplication: Merges entries with the same path (or tabBar.pagePath) and identical content, combining them into a single entry with combined platform conditions (e.g., H5 || MP-WEIXIN).
    • tabBar Property Management: Top-level properties like color, selectedColor, custom, etc., are recorded per platform using #ifdef. If values differ, they are stored in separate conditional blocks; if values are identical, they are merged into a single unconditioned property.
    • User-Written Content Protection: Any entry in pages.json that does not contain the plugin's generation marker (// GENERATED BY UNI-PAGES...) is treated as user-written and will never be modified or deleted by the plugin.
  4. Configure platform-specific page metadata with definePage DSL

    main

    When using the functional form of definePage, you can use a built-in DSL to handle platform-specific configurations without manually checking process.env.UNI_PLATFORM.

    DSL Methods

    • define(base): Defines metadata shared across all platforms.
    • .ifdef(platforms, partial): Applies the partial configuration only to the specified platforms.
    • .ifndef(platforms, partial): Applies the partial configuration to all platforms except the specified ones.

    Key Concepts

    • Merging: Branches are deep-merged into the base object in the order they are declared. Objects are recursively merged; arrays and primitive values are replaced.
    • Platform Aliases: h5 and web are aliases for the same platform.
    • Platform Identifier: The platform argument in the callback represents the current build target (e.g., 'mp-weixin', 'h5').

    Example

    <script setup lang="ts">
    definePage(({ define }) =>
      define({ 
        style: { navigationBarTitleText: '标题', enablePullDownRefresh: false }
      })
        .ifdef('mp-weixin', { style: { navigationBarBackgroundColor: '#07c160' } })
        .ifndef(['h5', 'web'], { middlewares: ['auth'] }),
    )
    </script>
  5. Generation markers and sub-package cleanup

    main

    Starting from version 0.5.0, the plugin writes a generation marker comment at the head of pages and subPackages entries in pages.json (e.g., // GENERATED BY UNI-PAGES, PLATFORM: H5 || MP-WEIXIN). This marker tracks the full set of platforms that have contributed to that entry.

    Automatic Cleanup Logic

    The plugin uses these markers to automatically remove stale entries:

    • Page Entries: If a marked entry in the main pages array is not found during the current scan, the current platform is removed from its platform list. Other platforms' #ifdef entries are preserved.
    • Sub-packages: If a marked subPackages entry is not found during the current scan (e.g., the directory was deleted or all pages were skipped via definePage(null)), the entire sub-package entry is removed from pages.json to prevent broken references.
    • Sub-package plugins: Configuration for plugins within a sub-package follows the sub-package lifecycle. If the sub-package is removed, its plugins are also removed.

    Important Warnings

    • Do not manually edit marker lines: If you modify or delete the // GENERATED BY UNI-PAGES... comment, the plugin will treat the entry as user-written and will no longer clean it up when it becomes obsolete.
    • Resetting pages.json: If you want to completely clean up the pages.json file (e.g., if you've stopped supporting a platform and want to remove its markers), do not edit the markers manually. Instead, delete the generated pages.json file and let the plugin regenerate it from scratch on the next run.
  6. How the plugin generation process works

    main

    The plugin follows a specific execution pipeline to generate pages.json:

    1. Load User Config: Loads pages.config.ts (or similar) via unconfig to get manual metadata like globalStyle or subPackages.
    2. Scan Page Files (if mergePages: true): Scans the filesystem based on dir and subPackages to create Page instances.
    3. Merge Page Metadata: Merges scanned pages with user configurations. The priority (from lowest to highest) is:
      • globalStyle (from pages.config.ts)
      • definePage() (macros inside .vue files)
      • pages array entries in pages.config.ts (highest priority; overrides definePage properties).
    4. Generate and Write: Serializes the result into pages.json in the outDir and generates TypeScript declarations.
  7. Configure @uni-helper/vite-plugin-uni-pages in Vite

    main

    To use file-based routing in a Vite-driven uni-app project, add UniPages() to your vite.config.mts plugins array.

    Important: UniPages() must be called before the Uni() plugin (either @uni-helper/plugin-uni or @dcloudio/vite-plugin-uni) to ensure correct plugin execution order.

    // vite.config.mts
    import Uni from '@uni-helper/plugin-uni'
    // or
    // import dcloudioUni from '@dcloudio/vite-plugin-uni'
    // const Uni = dcloudioUni.default || dcloudioUni
    import UniPages from '@uni-helper/vite-plugin-uni-pages'
    import { defineConfig } from 'vite'
    
    export default defineConfig({
      plugins: [
        UniPages(), // Must be called before Uni()
        Uni(),
      ],
    })
  8. Fixing timing issues with uni-app and pages.json

    main

    Because @dcloudio/vite-plugin-uni reads pages.json during the config hook, and this plugin writes it during the configResolved hook, the generated file might not be available when uni-app first looks for it.

    To solve this, use @uni-helper/unh which ensures pages.json is generated before the uni dev/build command starts.

    1. Update your package.json scripts:
    // package.json
    {
      "scripts": {
        "dev": "unh dev",
        "build": "unh build"
      }
    }
    1. Create an unh.config.ts file:
    // unh.config.ts
    import { defineConfig } from '@uni-helper/unh'
    
    export default defineConfig({
      autoGenerate: {
        pages: true, // Automatically generates pages.json before dev/build
      },
    })
  9. Create a pages.config file

    main

    Create a pages.config.(ts|mts|cts|js|cjs|mjs|json) file in your project root. This file allows you to write your pages.json configuration using TypeScript. You can use conditional compilation (e.g., #ifdef H5) within this file.

    If you manually specify a pages array in this config, it will override any pages automatically scanned by the plugin. Other properties like globalStyle should align with the standard pages.json structure.

    // pages.config.ts
    import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
    
    export default defineUniPages({
      // If you manually specify pages, it overrides auto-scanned pages.
      // pages: [],
    
      // Other properties follow the pages.json schema
      globalStyle: {
        navigationBarTextStyle: 'black',
        navigationBarTitleText: '@uni-helper',
      },
    })