create-weapp-vite 2.8.17 → 2.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  - 交互式创建或非交互式参数创建
10
10
  - 内置多种模板(默认、原生多平台 + Web、Vue SFC 多平台 + Web、Wevu、Wevu + TDesign、Tailwindcss、TDesign、Vant、插件、组件库)
11
- - 自动对齐 `weapp-vite` 与 `wevu` 版本
11
+ - 默认不联网查询版本,使用随包依赖;可显式按用户 registry 查询兼容稳定版
12
12
  - 自动处理 `.gitignore` 写入
13
13
  - 可选安装推荐的 AI skills(`sonofmagic/skills`)
14
14
  - 生成带来源标记的项目级 `AGENTS.md`,并保留用户维护的 `AGENTS.local.md`
@@ -33,10 +33,10 @@ npx create-weapp-vite
33
33
  pnpm create weapp-vite
34
34
  ```
35
35
 
36
- 交互流程会默认询问是否安装推荐的 AI skills,并提示将执行:
36
+ 交互流程询问是否安装推荐的 AI skills,默认选择“否”。显式安装时执行:
37
37
 
38
38
  ```bash
39
- npx skills add sonofmagic/skills
39
+ npx --yes skills add sonofmagic/skills
40
40
  ```
41
41
 
42
42
  如果你暂时不想安装,也可以先跳过,后面再手动执行上面的命令。
@@ -57,6 +57,9 @@ pnpm create weapp-vite my-app wevu --install-skills
57
57
 
58
58
  # 显式跳过推荐 skills 安装
59
59
  pnpm create weapp-vite my-app wevu --no-install-skills
60
+
61
+ # 默认使用随包版本;需要联网更新兼容版本时显式启用
62
+ pnpm create weapp-vite my-app wevu --dependency-versions=compatible --registry=https://registry.npmmirror.com/
60
63
  ```
61
64
 
62
65
  在代码中使用:
@@ -66,8 +69,68 @@ import { createProject, TemplateName } from 'create-weapp-vite'
66
69
 
67
70
  await createProject('my-app', TemplateName.wevu)
68
71
  await createProject('my-app', TemplateName.wevu, { installSkills: true })
72
+ await createProject('my-app', TemplateName.wevu, {
73
+ dependencyVersionStrategy: 'compatible',
74
+ registry: 'https://registry.npmmirror.com/',
75
+ })
76
+ ```
77
+
78
+ ## 依赖版本策略
79
+
80
+ CLI 的 `--dependency-versions=compatible|bundled` 对应 API 的 `dependencyVersionStrategy`,默认 `bundled`。
81
+
82
+ - `bundled`:不发起任何依赖版本查询,使用随脚手架发布的框架版本和模板依赖。生成的 semver 范围仍可能接受更新,安装后的精确版本由 lockfile 固定。
83
+ - `compatible`:按用户实际安装源查询模板使用的 `weapp-vite`、`wevu`、`@weapp-vite/dashboard`,选择每个包基线 caret 范围内共同存在的最高稳定版本,不跨大版本、不降级、不混用不同来源的版本列表。预发布基线保留随包组合。Tailwind 与核心查询并行,共享预算,只在模板原有兼容范围内更新;精确版本保持不变。
84
+ - 主查询最多 4 秒,失败后最多再用 1 秒探测一个公共备用源,全部网络等待最多 5 秒。失败保留随包组合;备用源结果仅提供恢复提示,不能证明用户当前源可安装的新版不会写入项目。企业私有源不自动切换公共源。
85
+
86
+ AI skills 是独立的可选联网步骤,需要 npm 和 GitHub 可达。交互默认跳过,也可用 `--no-install-skills` 明确禁用;显式安装最长等待 60 秒,超时清理进程,保留已经生成的项目并提示手动重试。
87
+
88
+ ## Registry 与代理
89
+
90
+ 新增 CLI 参数 `--registry=<url>` 和 API 选项 `registry?: string`。普通 registry 的优先级为:显式参数 → `CREATE_WEAPP_VITE_REGISTRY` → `npm_config_registry` / `NPM_CONFIG_REGISTRY` / `pnpm_config_registry` → 目标项目 `.npmrc` → 用户 `.npmrc` → npm 默认源。
91
+
92
+ 作用域配置(如 `@weapp-vite:registry`)继续遵循 npm 规则;普通 `--registry` 不覆盖作用域源。代理、`HTTP_PROXY` / `HTTPS_PROXY` / `NO_PROXY`、npm 的 `proxy` / `https-proxy`、`cafile` 与认证配置交给 npm 的网络栈处理。保持证书校验,不通过关闭 TLS 校验解决网络问题。诊断不会打印 token 或代理凭据。携带认证或客户端证书的 registry 必须直接返回元数据;重定向会触发随包版本回退,避免凭据被转发。
93
+
94
+ 脚手架按生成项目的安装边界读取 `.npmrc`:处于已有 pnpm workspace 时使用最近 workspace 根目录的配置;独立项目使用目标目录配置,不继承普通父项目中无法被后续安装继承的配置。脚手架不改写项目或全局 `.npmrc`。通常创建后执行:
95
+
96
+ ```bash
97
+ cd my-app
98
+ pnpm install
99
+ pnpm build
100
+ pnpm dev
101
+ ```
102
+
103
+ 独立新项目会生成 `pnpm-workspace.yaml`,明确声明模板依赖的构建脚本审批策略,避免 pnpm 12 因未审批的依赖脚本中止安装;项目自身的 `wv prepare` 仍正常执行。已有项目或父级 `pnpm-workspace.yaml` 会保留,已有项目遇到 `ERR_PNPM_IGNORED_BUILDS` 时,可按团队策略执行 `pnpm approve-builds`。
104
+
105
+ 通过 `--registry` 或 registry 环境变量临时选源时,安装提示会显式保留该源,避免 pnpm 原生入口忽略临时环境配置。按创建完成后打印的命令安装,例如:
106
+
107
+ ```bash
108
+ pnpm --config.registry=https://registry.npmmirror.com/ install
69
109
  ```
70
110
 
111
+ `pnpm --config.registry=... create` 中的临时配置不一定传给脚手架子进程;启用 `compatible` 时,请同时传 `--registry=...`,或设置 `CREATE_WEAPP_VITE_REGISTRY`。仅设置脚手架参数不能改变 pnpm 下载脚手架本身使用的源。
112
+
113
+ ## 国内镜像、缓存与故障恢复
114
+
115
+ 首次 `pnpm create weapp-vite` 下载由 pnpm 负责;包尚未下载时,脚手架的容错逻辑无法执行。镜像中的框架包、脚手架包和 tarball 可能不同步。完全离线也只有在脚手架及其依赖已经缓存后,才可能进入本地模板生成。
116
+
117
+ 先向当前可达的源查询版本:
118
+
119
+ ```bash
120
+ pnpm --config.registry=https://registry.npmmirror.com/ view create-weapp-vite version
121
+ pnpm --config.registry=https://registry.npmjs.org/ view create-weapp-vite version
122
+ ```
123
+
124
+ 选择包含本次修复且该源已同步的精确版本,将下面的 `<version>` 替换为查询结果,再运行:
125
+
126
+ ```bash
127
+ pnpm --config.registry=https://registry.npmmirror.com/ dlx create-weapp-vite@<version> my-app wevu
128
+ ```
129
+
130
+ 如果当前镜像尚未同步且官方源可达,可将命令中的 registry 改为官方源;官方源不可达时使用可用镜像或已配置的代理,不必强制直连。指定精确版本能避开旧 `latest` 执行缓存;旧脚手架本身不会获得新版代码中的修复。历史 `2.9.0` 及更早发布版本不包含本文的默认离线、代理与超时改造。
131
+
132
+ 安装时报 DNS、超时或连接错误时先检查 registry/代理;证书错误应配置正确 CA,401/403 应修正该源认证。包或版本 404 通常需要检查镜像同步、作用域源和 tarball 是否齐全。生成成功只说明模板完整,仍须以正常 `pnpm install`(包含 `wv prepare`)、`pnpm build` 和 `pnpm dev` 验证安装与构建。
133
+
71
134
  ## 配置
72
135
 
73
136
  `TemplateName` 支持以下模板:
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
- import { n as RECOMMENDED_SKILLS_INSTALL_COMMAND, t as createProject } from "./src-DOup_ePc.js";
1
+ import { i as validateDependencyVersionStrategy, n as RECOMMENDED_SKILLS_INSTALL_COMMAND, t as createProject } from "./src-8XDUHEZA.js";
2
+ import process from "node:process";
2
3
  import logger from "@weapp-core/logger";
3
4
  import { fs } from "@weapp-core/shared/fs";
4
- import process from "node:process";
5
5
  import path from "node:path";
6
6
  import { confirm, input, select } from "@inquirer/prompts";
7
7
  import { initConfig } from "@weapp-core/init";
@@ -56,7 +56,20 @@ const TEMPLATE_CHOICES = [
56
56
  function parseCliArgs(argv) {
57
57
  const positionals = [];
58
58
  let installSkills;
59
+ let dependencyVersionStrategy = "bundled";
60
+ let registry;
59
61
  for (const arg of argv) {
62
+ if (arg.startsWith("--registry=")) {
63
+ registry = arg.slice(11);
64
+ if (!registry) throw new Error("请使用 --registry=<http(s) URL>");
65
+ continue;
66
+ }
67
+ if (arg === "--registry") throw new Error("请使用 --registry=<http(s) URL>");
68
+ if (arg.startsWith("--dependency-versions=")) {
69
+ dependencyVersionStrategy = arg.slice(22);
70
+ continue;
71
+ }
72
+ if (arg === "--dependency-versions") throw new Error("请使用 --dependency-versions=compatible 或 --dependency-versions=bundled");
60
73
  if (arg === "--install-skills") {
61
74
  installSkills = true;
62
75
  continue;
@@ -67,11 +80,14 @@ function parseCliArgs(argv) {
67
80
  }
68
81
  positionals.push(arg);
69
82
  }
83
+ validateDependencyVersionStrategy(dependencyVersionStrategy);
70
84
  return {
71
85
  command: positionals[0],
72
86
  targetDir: positionals[0],
73
87
  templateName: positionals[1],
74
- installSkills
88
+ installSkills,
89
+ dependencyVersionStrategy,
90
+ registry
75
91
  };
76
92
  }
77
93
  async function run() {
@@ -98,11 +114,15 @@ async function run() {
98
114
  choices: TEMPLATE_CHOICES,
99
115
  default: "default"
100
116
  });
101
- const installSkills = isArgMode ? argInstallSkills ?? false : await confirm({
117
+ const installSkills = argInstallSkills ?? (isArgMode ? false : await confirm({
102
118
  message: `是否安装推荐的 AI skills?将执行 \`${RECOMMENDED_SKILLS_INSTALL_COMMAND}\`,也可稍后手动执行`,
103
- default: true
119
+ default: false
120
+ }));
121
+ await createProject(targetDir, templateName, {
122
+ installSkills,
123
+ dependencyVersionStrategy: parsedArgs.dependencyVersionStrategy,
124
+ registry: parsedArgs.registry
104
125
  });
105
- await createProject(targetDir, templateName, { installSkills });
106
126
  }
107
127
  /**
108
128
  * @description CLI 主入口执行 Promise(便于测试或外部调用)
@@ -114,6 +134,7 @@ const runPromise = run().catch((err) => {
114
134
  return;
115
135
  }
116
136
  logger.error("✗ 创建失败:", message);
137
+ process.exitCode = 1;
117
138
  });
118
139
  //#endregion
119
140
  export { run, runPromise };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,8 @@
1
+ import "npm-registry-fetch";
1
2
  import "pkg-types";
3
+ //#region src/dependencyVersions/index.d.ts
4
+ type DependencyVersionStrategy = 'compatible' | 'bundled';
5
+ //#endregion
2
6
  //#region src/enums.d.ts
3
7
  export declare enum TemplateName {
4
8
  default = "default",
@@ -17,10 +21,13 @@ export declare enum TemplateName {
17
21
  //#region src/createProject.d.ts
18
22
  interface CreateProjectOptions {
19
23
  installSkills?: boolean;
24
+ dependencyVersionStrategy?: DependencyVersionStrategy;
25
+ /** 本次依赖查询与建议安装命令使用的 registry,不写入用户配置。 */
26
+ registry?: string;
20
27
  }
21
28
  /**
22
29
  * @description 根据模板创建项目
23
30
  */
24
31
  export declare function createProject(targetDir?: string, templateName?: TemplateName, options?: CreateProjectOptions): Promise<void>;
25
32
  //#endregion
26
- export type { CreateProjectOptions };
33
+ export type { CreateProjectOptions, DependencyVersionStrategy };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { r as TemplateName, t as createProject } from "./src-DOup_ePc.js";
1
+ import { r as TemplateName, t as createProject } from "./src-8XDUHEZA.js";
2
2
  export { TemplateName, createProject };
@@ -1,25 +1,17 @@
1
+ import process from "node:process";
1
2
  import { fileURLToPath } from "node:url";
2
3
  import logger from "@weapp-core/logger";
3
4
  import { fs } from "@weapp-core/shared/fs";
4
5
  import path from "pathe";
5
- import https from "node:https";
6
- import { spawn } from "node:child_process";
7
- import process from "node:process";
8
- //#region ../../packages-runtime/react/package.json
9
- var version$4 = "0.2.8";
10
- //#endregion
11
- //#region ../../packages-runtime/wevu/package.json
12
- var version$3 = "7.1.4";
13
- //#endregion
14
- //#region ../dashboard/package.json
15
- var version$2 = "7.1.4";
16
- //#endregion
17
- //#region ../eslint/package.json
18
- var version$1 = "0.2.6";
19
- //#endregion
20
- //#region ../weapp-vite/package.json
21
- var version = "7.1.4";
22
- //#endregion
6
+ import { gte, minVersion, prerelease, rcompare, satisfies, valid } from "semver";
7
+ import { access, writeFile } from "node:fs/promises";
8
+ import path$1 from "node:path";
9
+ import NpmConfig from "@npmcli/config";
10
+ import configDefinitions from "@npmcli/config/lib/definitions/index.js";
11
+ import makeFetch from "make-fetch-happen";
12
+ import registryFetch from "npm-registry-fetch";
13
+ import { isIP } from "node:net";
14
+ import { execa } from "execa";
23
15
  //#region src/generated/agents.ts
24
16
  const generatedAgentGuidelines = {
25
17
  "default": "# AGENTS Guidelines\n\n<!-- agents-generated: v1 -->\n<!-- source: template:default; manifest-version: 1; generator-version: 1 -->\n\n## Mini-program Runtime Host APIs\n\n- 小程序 AppService 不是浏览器或 Node.js。新增 `queueMicrotask`、Web API、DOM/Node 全局或现代内建前,必须先在目标平台的真实 IDE、目标基础库和 renderer 中探测存在性与最小调用语义;浏览器、Node、类型声明和 headless simulator 结果都不能替代真实 IDE 证据。\n- 微信运行时不得假定存在 `queueMicrotask`。即使某个 DevTools 版本探测为可用,也必须使用显式兼容层,或确认项目已经启用对应的 weapp-vite runtime global 注入。\n- 项目 ESLint 配置应接入 `@weapp-vite/eslint` 的 `miniProgramRuntimeRecommended` 或 `createMiniProgramRuntimeConfig()`,只检查会进入小程序运行时的 `src` 代码;config、构建脚本、测试、Web-only、WXS 和生成产物不纳入该规则。\n\n## Local Docs First\n\n- After dependencies are installed, prefer reading local package docs under `node_modules/weapp-vite/dist/docs/` first.\n- Start with `node_modules/weapp-vite/dist/docs/index.md`, then read `README.md` and `mcp.md` as needed.\n- Prefer local package docs over stale model memory or old web pages when command behavior is unclear.\n\n## CLI Entry\n\n- This project supports both `weapp-vite` and `wv` CLI commands.\n- Treat `weapp-vite dev` and `wv dev` as equivalent forms.\n- Prefer project scripts such as `pnpm dev`, `pnpm build`, `pnpm open`, and `pnpm g` before ad-hoc shell commands.\n- Use `weapp-vite prepare` or `wv prepare` when managed support files under `.weapp-vite/` need to be refreshed.\n- Prefer `weapp-vite screenshot` or `wv screenshot` for mini-program screenshot acceptance.\n- Prefer `weapp-vite compare` or `wv compare` for mini-program screenshot diff, baseline comparison, and visual regression checks.\n- Prefer `weapp-vite ide logs --open` or `wv ide logs --open` for DevTools terminal log bridging.\n- Do not default to generic browser screenshot tools when the target is the mini-program runtime in WeChat DevTools.\n\n## AI Intent Routing\n\n- When the request mentions screenshot, 截图, 页面快照, runtime screenshot, or capture the current mini-program page, default to `weapp-vite screenshot` / `wv screenshot`.\n- When the request mentions screenshot compare, 截图对比, diff, baseline, visual regression, 像素对比, or acceptance comparison, default to `weapp-vite compare` / `wv compare`.\n- Treat these commands as the primary screenshot contract for AI workflows in this project.\n- Only fall back to generic browser screenshot tools when the target is explicitly the web runtime instead of WeChat DevTools.\n\n## Weapp-vite Workflow\n\n- Keep `vite.config.ts` as the source of truth for `weapp` config, output behavior, and IDE/MCP automation.\n- Confirm `weapp.srcRoot`, routes, subpackages, and auto-import strategy before broad refactors.\n- Prefer minimal scoped verification: targeted `pnpm build`, targeted tests, then broader checks only when required.\n- If editing package source in a monorepo dependency, rebuild the touched package before validating downstream apps to avoid stale `dist`.\n- Keep CLI ownership explicit: native `weapp-vite` commands first, IDE passthrough second.\n- For Rust/native acceleration, treat JS ↔ Rust boundary crossings as a primary performance cost. Prefer batch analysis that sends source once, parses once, and returns structured results; only put fine-grained native APIs on hot paths when profiling proves a net win.\n- Keep native AST fast paths optional and explicitly enabled. They must fall back to Babel/Oxc/Vue compiler behavior on load, parse, or runtime failure, and new native coverage needs correctness tests plus real profiling.\n\n## WeChat DevTools\n\n- Before using screenshot, preview, upload, automator, or `--open`, ensure WeChat DevTools is logged in and the service port is enabled.\n- `wv open`, `wv dev -o`, and `wv build -o` use the official DevTools CLI first, then connect automator; if the IDE is open but automator is unavailable, run `wv ide doctor --json` before retrying.\n- Use `--ide-open-strategy automator` only for explicit compatibility debugging; do not treat it as the default startup path.\n- Prefer writing screenshots to workspace paths such as `.tmp/acceptance.png`.\n\n## AI Skills\n\n- Recommend installing shared skills with `npx skills add sonofmagic/skills`.\n- In Codex/Claude environments, prefer these skills first when available:\n - `$weapp-vite-best-practices` for config, build, subpackage, route, DevTools CLI orchestration, and screenshot/compare command work.\n - `$weapp-vite-vue-sfc-best-practices` for `.vue` SFC macros, JSON blocks, and template compatibility.\n - `$release-and-changeset-best-practices` for issue delivery, pnpm change intents, repoctl release decisions, and PR workflow.\n - `$docs-and-website-sync` when documentation or AI guidance must be refreshed together with code changes.\n - Use `$weapp-vite-best-practices` for stateful HMR, pluginRoot/dist-plugin, Web runtime compatibility, and native AST profiling; use `$wevu-best-practices` for `wevu/router` navigation semantics.\n - Use `$weapp-devtools-e2e-best-practices` for serialized DevTools runtime suites, shared automator sessions, and known host compatibility skips.\n - `$native-to-weapp-vite-wevu-migration` when migrating native mini-program projects to `weapp-vite + native`, or further toward Vue SFC / wevu.\n\n## Native Mini-program Authoring\n\n- Keep native page/component structure consistent with the template unless there is a clear migration goal.\n- Prefer `weapp-vite generate` or `wv generate` for new app/page/component scaffolds.\n- If migrating this project, first decide whether the target is `weapp-vite + native` or a further Vue SFC / `wevu` migration, then keep each migration wave explicit.\n\n## Local Overlay\n\n- If `AGENTS.local.md` exists in this project or directory, read it after this file and apply its project-specific additions.\n- Keep generated `AGENTS.md` files managed by the repository tooling; put user-owned custom rules in `AGENTS.local.md`.\n",
@@ -45,6 +37,469 @@ function createAgentsGuidelines(templateName) {
45
37
  return guidelines;
46
38
  }
47
39
  //#endregion
40
+ //#region ../../packages-runtime/react/package.json
41
+ var version$4 = "0.2.9";
42
+ //#endregion
43
+ //#region ../../packages-runtime/wevu/package.json
44
+ var version$3 = "7.2.1";
45
+ //#endregion
46
+ //#region ../dashboard/package.json
47
+ var version$2 = "7.2.1";
48
+ //#endregion
49
+ //#region ../eslint/package.json
50
+ var version$1 = "0.2.7";
51
+ //#endregion
52
+ //#region ../weapp-vite/package.json
53
+ var version = "7.2.1";
54
+ //#endregion
55
+ //#region src/pnpmBuildPolicy.ts
56
+ const WORKSPACE_CONFIG = `packages: []
57
+
58
+ # 仅允许模板依赖中需要的安装脚本,新增依赖仍需单独审批。
59
+ allowBuilds:
60
+ '@swc/core': true
61
+ '@weapp-tailwindcss/merge': true
62
+ oxc-resolver: true
63
+ rolldown: true
64
+
65
+ # 使用 registry 内的预编译包,避免额外下载或本机源码编译。
66
+ '@parcel/watcher': false
67
+ esbuild: false
68
+ weapp-tailwindcss: false
69
+ `;
70
+ /** 从目标目录向上查找 pnpm 实际使用的最近工作区边界。 */
71
+ async function findPnpmWorkspaceRoot(root) {
72
+ let current = path$1.resolve(root);
73
+ while (true) {
74
+ try {
75
+ await access(path$1.join(current, "pnpm-workspace.yaml"));
76
+ return current;
77
+ } catch (error) {
78
+ if (error.code !== "ENOENT") throw error;
79
+ }
80
+ const parent = path$1.dirname(current);
81
+ if (parent === current) return;
82
+ current = parent;
83
+ }
84
+ }
85
+ /** 为独立新项目生成 pnpm 构建审批,原样保留已有团队配置。 */
86
+ async function ensurePnpmBuildPolicy(root) {
87
+ if (await findPnpmWorkspaceRoot(root)) return;
88
+ try {
89
+ await writeFile(path$1.join(root, "pnpm-workspace.yaml"), WORKSPACE_CONFIG, { flag: "wx" });
90
+ } catch (error) {
91
+ if (error.code !== "EEXIST") throw error;
92
+ }
93
+ }
94
+ //#endregion
95
+ //#region src/npm/config.ts
96
+ const NPM_REGISTRY = "https://registry.npmjs.org/";
97
+ const transportKeys = [
98
+ "proxy",
99
+ "httpsProxy",
100
+ "noProxy",
101
+ "ca",
102
+ "strictSSL"
103
+ ];
104
+ const networkKeys = /* @__PURE__ */ new Set([
105
+ ...transportKeys,
106
+ "cert",
107
+ "key",
108
+ "localAddress"
109
+ ]);
110
+ const npmNetworkKeys = /* @__PURE__ */ new Set([
111
+ "registry",
112
+ "proxy",
113
+ "https-proxy",
114
+ "noproxy",
115
+ "ca",
116
+ "cafile",
117
+ "strict-ssl",
118
+ "cert",
119
+ "key",
120
+ "local-address"
121
+ ]);
122
+ function flattenNetworkConfig(data, flat) {
123
+ const selected = Object.fromEntries(Object.entries(data).filter(([key]) => npmNetworkKeys.has(key) || key.startsWith("//") || key.startsWith("@") && key.endsWith(":registry")));
124
+ configDefinitions.flatten(selected, flat);
125
+ }
126
+ function normalizeRegistry(value) {
127
+ try {
128
+ if (typeof value !== "string" || !value.trim()) throw new Error("invalid");
129
+ const url = new URL(value.trim());
130
+ if (!["https:", "http:"].includes(url.protocol) || url.username || url.password || url.search || url.hash) throw new Error("invalid");
131
+ url.pathname = `${url.pathname.replace(/\/+$/, "")}/`;
132
+ return url.href;
133
+ } catch {
134
+ throw new Error("registry 必须是有效的 HTTP(S) 地址,认证信息请放在 .npmrc 中,地址不能包含凭据、查询参数或片段。");
135
+ }
136
+ }
137
+ /** 只展示源的地址,避免在诊断中暴露凭据或查询参数。 */
138
+ function displayRegistry(registry) {
139
+ try {
140
+ const url = new URL(registry);
141
+ url.username = "";
142
+ url.password = "";
143
+ url.search = "";
144
+ url.hash = "";
145
+ return url.href;
146
+ } catch {
147
+ return "配置的 registry";
148
+ }
149
+ }
150
+ async function findProjectConfigDir(cwd) {
151
+ let current = path$1.resolve(cwd);
152
+ while (true) try {
153
+ await Promise.any([
154
+ ".npmrc",
155
+ "package.json",
156
+ "node_modules"
157
+ ].map((name) => access(path$1.join(current, name))));
158
+ return current;
159
+ } catch {
160
+ const parent = path$1.dirname(current);
161
+ if (parent === current) return path$1.resolve(cwd);
162
+ current = parent;
163
+ }
164
+ }
165
+ var ScaffoldNpmConfig = class extends NpmConfig {
166
+ /** 创建目录可能尚不存在;使用调用者确定的安装边界。 */
167
+ async loadLocalPrefix() {
168
+ this.localPrefix = this.cwd;
169
+ }
170
+ };
171
+ /** 读取 npm 配置但不写文件,也不修改调用者环境或主动联网。 */
172
+ async function resolveRegistryOptions(options = {}) {
173
+ const env = { ...process.env };
174
+ const override = options.registry ?? env.CREATE_WEAPP_VITE_REGISTRY ?? env.npm_config_registry ?? env.NPM_CONFIG_REGISTRY ?? env.pnpm_config_registry ?? env.PNPM_CONFIG_REGISTRY;
175
+ const registryOverride = override === void 0 ? void 0 : normalizeRegistry(override);
176
+ if (registryOverride) {
177
+ delete env.NPM_CONFIG_REGISTRY;
178
+ env.npm_config_registry = registryOverride;
179
+ }
180
+ const target = path$1.resolve(options.projectRoot ?? options.cwd ?? process.cwd());
181
+ const cwd = await findPnpmWorkspaceRoot(target) ?? (options.projectRoot ? target : await findProjectConfigDir(target));
182
+ const config = new ScaffoldNpmConfig({
183
+ ...configDefinitions,
184
+ flatten: flattenNetworkConfig,
185
+ npmPath: path$1.dirname(fileURLToPath(import.meta.resolve("@npmcli/config/package.json"))),
186
+ cwd,
187
+ env,
188
+ argv: ["node", "create-weapp-vite"],
189
+ warn: false
190
+ });
191
+ config.types["https-proxy"] = config.types.proxy;
192
+ try {
193
+ await config.load();
194
+ const flat = config.flat;
195
+ const resolved = {
196
+ registry: registryOverride ?? normalizeRegistry(flat.registry ?? NPM_REGISTRY),
197
+ strictSSL: flat.strictSSL !== false
198
+ };
199
+ for (const [key, value] of Object.entries(flat)) if (key.startsWith("@") && key.endsWith(":registry")) resolved[key] = normalizeRegistry(value);
200
+ else if (networkKeys.has(key) || key.startsWith("//")) {
201
+ if (value !== null && value !== void 0) resolved[key] = value;
202
+ }
203
+ if (resolved.proxy === void 0 && resolved.httpsProxy === void 0) {
204
+ resolved.httpsProxy = env.https_proxy || env.HTTPS_PROXY || env.http_proxy || env.HTTP_PROXY;
205
+ resolved.proxy = env.http_proxy || env.HTTP_PROXY;
206
+ }
207
+ resolved.noProxy = resolved.noProxy || env.no_proxy || env.NO_PROXY;
208
+ for (const key of ["cafile", "userconfig"]) {
209
+ const value = config.get(key);
210
+ if (typeof value === "string" && value) resolved[key] = value;
211
+ }
212
+ return resolved;
213
+ } catch {
214
+ throw new Error("无法读取 npm 网络配置,请检查 .npmrc、registry、代理与 CA 文件配置。");
215
+ }
216
+ }
217
+ /** 按 npm 的作用域规则选择源,显式普通源不会覆盖作用域配置。 */
218
+ function registryForPackage(packageName, options) {
219
+ const scope = packageName.startsWith("@") ? packageName.split("/")[0] : void 0;
220
+ return normalizeRegistry(scope && options[`${scope}:registry`] || options.registry);
221
+ }
222
+ /** 公共源诊断仅复用传输配置,不携带私有源 token、作用域或客户端证书。 */
223
+ function publicRegistryOptions(options, registry) {
224
+ const result = {
225
+ registry: normalizeRegistry(registry),
226
+ strictSSL: true
227
+ };
228
+ for (const key of transportKeys) if (options[key] !== void 0) Object.assign(result, { [key]: options[key] });
229
+ return result;
230
+ }
231
+ /** 仅向可选安装子进程透传网络参数,认证仍交给 npm 自行读取用户配置。 */
232
+ function registryEnvironment(options) {
233
+ const env = {
234
+ npm_config_registry: options.registry,
235
+ npm_config_strict_ssl: String(options.strictSSL)
236
+ };
237
+ for (const [key, name] of Object.entries({
238
+ proxy: "proxy",
239
+ httpsProxy: "https_proxy",
240
+ noProxy: "noproxy",
241
+ cafile: "cafile",
242
+ userconfig: "userconfig"
243
+ })) {
244
+ const value = options[key];
245
+ if (value !== void 0) env[`npm_config_${name}`] = String(value);
246
+ }
247
+ if (!options.cafile && options.ca) env.npm_config_ca = Array.isArray(options.ca) ? options.ca.join("\n") : String(options.ca);
248
+ return env;
249
+ }
250
+ //#endregion
251
+ //#region src/npm/proxy.ts
252
+ function bypassProxy(url, noProxy) {
253
+ const hostname = url.hostname.replace(/^\[|\]$/g, "").toLowerCase();
254
+ const port = url.port || (url.protocol === "https:" ? "443" : "80");
255
+ return noProxy.toLowerCase().split(/[,\s]+/).filter(Boolean).some((entry) => {
256
+ if (entry === "*") return true;
257
+ const parsed = isIP(entry) === 6 ? [entry, entry] : /^(\[[^\]]+\]|[^:]+)(?::(\d+))?$/.exec(entry);
258
+ if (!parsed || parsed[2] && parsed[2] !== port) return false;
259
+ const host = parsed[1].replace(/^\[|\]$/g, "").replace(/^\*?\./, "");
260
+ return hostname === host || !isIP(hostname) && hostname.endsWith(`.${host}`);
261
+ });
262
+ }
263
+ /** 显式决定代理或直连,避免 npm transport 再从缓存的环境变量恢复已禁用代理。 */
264
+ function proxyOptions(uri, options) {
265
+ const proxy = options.httpsProxy ?? options.proxy ?? process.env.https_proxy ?? process.env.HTTPS_PROXY ?? process.env.http_proxy ?? process.env.HTTP_PROXY;
266
+ const noProxy = options.noProxy ?? process.env.no_proxy ?? process.env.NO_PROXY ?? "";
267
+ if (!proxy || bypassProxy(new URL(uri), noProxy)) return { agent: false };
268
+ return {
269
+ proxy,
270
+ noProxy: []
271
+ };
272
+ }
273
+ //#endregion
274
+ //#region src/npm/transport.ts
275
+ const { getAuth } = registryFetch;
276
+ /** 复用 npm 的认证解析和网络传输,限制认证请求的重定向边界。 */
277
+ async function requestMetadata(uri, options, signal, timeout) {
278
+ const auth = getAuth(uri, options);
279
+ const headers = { accept: "application/vnd.npm.install-v1+json" };
280
+ if (auth.token) headers.authorization = `Bearer ${auth.token}`;
281
+ else if (auth.auth) headers.authorization = `Basic ${auth.auth}`;
282
+ const cert = auth.cert || options.cert;
283
+ const key = auth.key || options.key;
284
+ const fetchOptions = {
285
+ ca: options.ca,
286
+ cert,
287
+ key,
288
+ headers,
289
+ localAddress: options.localAddress,
290
+ strictSSL: options.strictSSL,
291
+ redirect: "manual",
292
+ retry: {
293
+ retries: 1,
294
+ factor: 2,
295
+ minTimeout: 150,
296
+ maxTimeout: 300
297
+ },
298
+ timeout,
299
+ signal
300
+ };
301
+ let target = uri;
302
+ for (let redirects = 0;; redirects++) {
303
+ const response = await makeFetch(target, {
304
+ ...fetchOptions,
305
+ ...proxyOptions(target, options)
306
+ });
307
+ if (response.ok) return response.json();
308
+ response.body.destroy();
309
+ const location = response.headers.get("location");
310
+ if ([
311
+ 301,
312
+ 302,
313
+ 303,
314
+ 307,
315
+ 308
316
+ ].includes(response.status) && location && redirects < 5 && !headers.authorization && !cert && !key) {
317
+ const next = new URL(location, target);
318
+ if (!["https:", "http:"].includes(next.protocol) || next.username || next.password) throw new Error("registry redirect is invalid");
319
+ target = next.href;
320
+ continue;
321
+ }
322
+ throw Object.assign(/* @__PURE__ */ new Error("registry HTTP request failed"), { statusCode: response.status });
323
+ }
324
+ }
325
+ //#endregion
326
+ //#region src/npm/index.ts
327
+ const REQUEST_TIMEOUT_MS = 5e3;
328
+ function requestError(error, registry) {
329
+ const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
330
+ const status = error && typeof error === "object" && "statusCode" in error ? Number(error.statusCode) : 0;
331
+ const reason = status >= 400 && status <= 599 ? `HTTP ${status}` : [
332
+ "ECONNRESET",
333
+ "ECONNREFUSED",
334
+ "ENOTFOUND",
335
+ "EAI_AGAIN",
336
+ "ETIMEDOUT",
337
+ "CERT_HAS_EXPIRED",
338
+ "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
339
+ "DEPTH_ZERO_SELF_SIGNED_CERT"
340
+ ].includes(code) ? code : "连接失败或响应无效";
341
+ return /* @__PURE__ */ new Error(`registry ${displayRegistry(registry)} 查询失败(${reason})`);
342
+ }
343
+ async function requestJson(packageName, suffix = "", signal, options) {
344
+ if (signal?.aborted) throw new Error("registry 查询已取消");
345
+ const resolved = options ?? await resolveRegistryOptions();
346
+ const registry = registryForPackage(packageName, resolved);
347
+ const controller = new AbortController();
348
+ let rejectDeadline = () => {};
349
+ const deadline = new Promise((_resolve, reject) => {
350
+ rejectDeadline = reject;
351
+ });
352
+ function cancel(message) {
353
+ const error = new Error(message);
354
+ controller.abort(error);
355
+ rejectDeadline(error);
356
+ }
357
+ const abort = () => cancel("registry 查询已取消");
358
+ const timer = setTimeout(cancel, REQUEST_TIMEOUT_MS, `registry 查询超过 ${REQUEST_TIMEOUT_MS / 1e3} 秒`);
359
+ signal?.addEventListener("abort", abort, { once: true });
360
+ if (signal?.aborted) abort();
361
+ try {
362
+ const metadata = requestMetadata(`${registry}${encodeURIComponent(packageName)}${suffix}`, {
363
+ ...resolved,
364
+ registry
365
+ }, controller.signal, REQUEST_TIMEOUT_MS);
366
+ return await Promise.race([metadata, deadline]);
367
+ } catch (error) {
368
+ if (controller.signal.aborted) throw controller.signal.reason;
369
+ throw requestError(error, registry);
370
+ } finally {
371
+ clearTimeout(timer);
372
+ signal?.removeEventListener("abort", abort);
373
+ }
374
+ }
375
+ function isRecord(value) {
376
+ return value !== null && typeof value === "object" && !Array.isArray(value);
377
+ }
378
+ /** 从配置的 npm registry 获取包的已发布版本列表。 */
379
+ async function getPackageVersionsFromNpm(packageName, signal, options) {
380
+ const metadata = await requestJson(packageName, "", signal, options);
381
+ if (!isRecord(metadata) || !isRecord(metadata.versions)) throw new Error("registry 响应缺少有效的 versions 信息");
382
+ return Object.keys(metadata.versions);
383
+ }
384
+ //#endregion
385
+ //#region src/dependencyVersions/compatible.ts
386
+ /** 仅选择所有已使用核心包共同发布、且兼容各自基线的最高稳定版本。 */
387
+ function findCompatibleVersion(packages) {
388
+ if (packages.length === 0 || packages.some((pkg) => !valid(pkg.version) || prerelease(pkg.version))) return;
389
+ return packages[0].versions.filter((version) => valid(version) && !prerelease(version)).filter((version) => packages.every((pkg) => pkg.versions.includes(version) && gte(version, pkg.version) && satisfies(version, `^${pkg.version}`))).sort(rcompare)[0];
390
+ }
391
+ //#endregion
392
+ //#region src/dependencyVersions/deadline.ts
393
+ /** 将整个查询批次约束在同一时间预算内,并清理未完成的请求。 */
394
+ async function queryWithDeadline(query, timeoutMs) {
395
+ const controller = new AbortController();
396
+ let timer;
397
+ try {
398
+ const deadline = new Promise((_resolve, reject) => {
399
+ timer = setTimeout(() => {
400
+ const error = /* @__PURE__ */ new Error("registry 查询超时");
401
+ controller.abort(error);
402
+ reject(error);
403
+ }, timeoutMs);
404
+ });
405
+ return await Promise.race([query(controller.signal), deadline]);
406
+ } catch (error) {
407
+ controller.abort(error);
408
+ throw error;
409
+ } finally {
410
+ clearTimeout(timer);
411
+ }
412
+ }
413
+ //#endregion
414
+ //#region src/dependencyVersions/index.ts
415
+ const BUNDLED_VERSIONS = {
416
+ "weapp-vite": version,
417
+ "wevu": version$3,
418
+ "@weapp-vite/dashboard": version$2,
419
+ "@weapp-vite/react": version$4,
420
+ "@weapp-vite/eslint": version$1
421
+ };
422
+ const CORE_PACKAGES = [
423
+ "weapp-vite",
424
+ "wevu",
425
+ "@weapp-vite/dashboard"
426
+ ];
427
+ const DEPENDENCY_FIELDS = [
428
+ "dependencies",
429
+ "devDependencies",
430
+ "peerDependencies",
431
+ "optionalDependencies"
432
+ ];
433
+ const PRIMARY_TIMEOUT_MS = 4e3;
434
+ const FALLBACK_TIMEOUT_MS = 1e3;
435
+ const PUBLIC_REGISTRIES = ["https://registry.npmjs.org/", "https://registry.npmmirror.com/"];
436
+ /** 校验依赖版本策略,避免无效选项在创建文件后才报错。 */
437
+ function validateDependencyVersionStrategy(value) {
438
+ if (value !== "compatible" && value !== "bundled") throw new Error(`无效的依赖版本策略:${String(value)},仅支持 compatible 或 bundled`);
439
+ }
440
+ function hasDependency(pkg, name) {
441
+ return DEPENDENCY_FIELDS.some((field) => pkg[field]?.[name] !== void 0);
442
+ }
443
+ function setDependencyVersion(pkg, name, version) {
444
+ for (const field of DEPENDENCY_FIELDS) if (pkg[field]?.[name] !== void 0) pkg[field][name] = `^${version}`;
445
+ }
446
+ /** 备用源仅用于恢复提示,不能把备用源中的新版写进当前安装源的项目。 */
447
+ async function suggestFallbackRegistry(names, options) {
448
+ if (!PUBLIC_REGISTRIES.includes(options.registry) || Object.keys(options).some((key) => key.startsWith("@") && key.endsWith(":registry"))) return;
449
+ const registry = PUBLIC_REGISTRIES.find((value) => value !== options.registry);
450
+ const fallback = publicRegistryOptions(options, registry);
451
+ try {
452
+ await queryWithDeadline(async (signal) => {
453
+ if (!findCompatibleVersion(await Promise.all(names.map(async (name) => ({
454
+ version: BUNDLED_VERSIONS[name],
455
+ versions: await getPackageVersionsFromNpm(name, signal, fallback)
456
+ }))))) throw new Error("备用源尚未同步");
457
+ }, FALLBACK_TIMEOUT_MS);
458
+ logger.warn(`备用源 ${displayRegistry(registry)} 的核心版本可查询;如当前源无法安装,可用 pnpm --config.registry=${registry} install 重试。项目仍保留随包版本。`);
459
+ } catch {}
460
+ }
461
+ /** 根据当前安装源解析兼容版本;默认离线,并始终保留完整的随包回退组合。 */
462
+ async function resolveDependencyVersions(pkg, strategy = "bundled", network) {
463
+ validateDependencyVersionStrategy(strategy);
464
+ for (const [name, version] of Object.entries(BUNDLED_VERSIONS)) setDependencyVersion(pkg, name, version);
465
+ const names = CORE_PACKAGES.filter((name) => hasDependency(pkg, name));
466
+ const tailwindSpec = pkg.devDependencies?.["weapp-tailwindcss"];
467
+ if (strategy === "bundled" || names.length === 0 && !tailwindSpec) return { strategy: "bundled" };
468
+ const bundledDescription = names.map((name) => `${name}@${BUNDLED_VERSIONS[name]}`).join("、");
469
+ if (names.some((name) => !valid(BUNDLED_VERSIONS[name]) || prerelease(BUNDLED_VERSIONS[name]))) {
470
+ logger.warn(`预发布基线保持随包版本,不自动更新:${bundledDescription}`);
471
+ return { strategy: "bundled" };
472
+ }
473
+ const options = network ?? await resolveRegistryOptions();
474
+ try {
475
+ const { coreVersion, tailwindVersion } = await queryWithDeadline(async (signal) => {
476
+ const [candidates, tailwindVersions] = await Promise.all([Promise.all(names.map(async (name) => ({
477
+ version: BUNDLED_VERSIONS[name],
478
+ versions: await getPackageVersionsFromNpm(name, signal, options)
479
+ }))), tailwindSpec ? getPackageVersionsFromNpm("weapp-tailwindcss", signal, options) : void 0]);
480
+ const coreVersion = findCompatibleVersion(candidates);
481
+ if (names.length && !coreVersion) throw new Error("未找到核心依赖共同发布的兼容稳定版本");
482
+ const tailwindBaseline = tailwindSpec && minVersion(tailwindSpec)?.version;
483
+ const tailwindVersion = tailwindBaseline && tailwindVersions ? findCompatibleVersion([{
484
+ version: tailwindBaseline,
485
+ versions: tailwindVersions.filter((value) => valid(value) && satisfies(value, tailwindSpec))
486
+ }]) : void 0;
487
+ if (tailwindSpec && !tailwindVersion) throw new Error("当前源缺少模板兼容的 Tailwind 版本");
488
+ return {
489
+ coreVersion,
490
+ tailwindVersion
491
+ };
492
+ }, PRIMARY_TIMEOUT_MS);
493
+ if (coreVersion) for (const name of names) setDependencyVersion(pkg, name, coreVersion);
494
+ if (tailwindVersion && tailwindSpec?.startsWith("^") && valid(tailwindSpec.slice(1))) pkg.devDependencies["weapp-tailwindcss"] = `^${tailwindVersion}`;
495
+ return { strategy: "compatible" };
496
+ } catch {
497
+ logger.warn(`当前 registry 的兼容版本查询失败、超时或没有共同版本,已使用随包版本:${bundledDescription}。网络等待总上限为 5 秒。`);
498
+ if (names.length) await suggestFallbackRegistry(names, options);
499
+ return { strategy: "bundled" };
500
+ }
501
+ }
502
+ //#endregion
48
503
  //#region src/enums.ts
49
504
  let TemplateName = /* @__PURE__ */ function(TemplateName) {
50
505
  TemplateName["default"] = "default";
@@ -61,6 +516,88 @@ let TemplateName = /* @__PURE__ */ function(TemplateName) {
61
516
  return TemplateName;
62
517
  }({});
63
518
  //#endregion
519
+ //#region src/installationCommand.ts
520
+ const SAFE_REGISTRY_RE = /^https?:\/\/[\w.:/-]+$/;
521
+ /** 生成可复制的安装命令;Windows 的特殊地址使用明确标注的 PowerShell 语法。 */
522
+ function installationCommand(registry, platform = process.platform) {
523
+ if (!registry) return "pnpm install";
524
+ if (SAFE_REGISTRY_RE.test(registry)) return `pnpm --config.registry=${registry} install`;
525
+ const quoted = platform === "win32" ? `'${registry.replaceAll("'", "''")}'` : `'${registry.replaceAll("'", "'\\''")}'`;
526
+ return `${platform === "win32" ? "PowerShell: " : ""}pnpm --config.registry=${quoted} install`;
527
+ }
528
+ //#endregion
529
+ //#region src/skills.ts
530
+ const RECOMMENDED_SKILLS_SOURCE = "sonofmagic/skills";
531
+ const RECOMMENDED_SKILLS_INSTALL_COMMAND = `npx --yes skills add ${RECOMMENDED_SKILLS_SOURCE}`;
532
+ const SKILLS_INSTALL_TIMEOUT_MS = 6e4;
533
+ async function terminateSkillsProcessTree(child) {
534
+ if (!child.pid) return;
535
+ if (process.platform === "win32") try {
536
+ await execa("taskkill", [
537
+ "/pid",
538
+ String(child.pid),
539
+ "/T",
540
+ "/F"
541
+ ], {
542
+ stdio: "ignore",
543
+ windowsHide: true,
544
+ timeout: 5e3,
545
+ killSignal: "SIGKILL",
546
+ forceKillAfterDelay: 0
547
+ });
548
+ return;
549
+ } catch {
550
+ child.kill("SIGKILL");
551
+ return;
552
+ }
553
+ try {
554
+ process.kill(-child.pid, "SIGKILL");
555
+ } catch {
556
+ child.kill("SIGKILL");
557
+ }
558
+ }
559
+ /**
560
+ * @description 安装推荐的 AI skills。
561
+ */
562
+ async function installRecommendedSkills(cwd, options = {}) {
563
+ const child = execa("npx", [
564
+ "--yes",
565
+ "skills",
566
+ "add",
567
+ RECOMMENDED_SKILLS_SOURCE
568
+ ], {
569
+ cwd,
570
+ env: {
571
+ ...process.env,
572
+ ...options.env
573
+ },
574
+ stdio: "inherit",
575
+ detached: process.platform !== "win32",
576
+ windowsHide: true
577
+ });
578
+ let timer;
579
+ let timeoutError;
580
+ let cleanup;
581
+ const deadline = new Promise((_resolve, reject) => {
582
+ timer = setTimeout(() => {
583
+ timeoutError = /* @__PURE__ */ new Error("AI skills 安装超过 60 秒,已终止安装进程;可稍后手动安装");
584
+ cleanup = terminateSkillsProcessTree(child);
585
+ cleanup.then(() => reject(timeoutError), reject);
586
+ }, SKILLS_INSTALL_TIMEOUT_MS);
587
+ });
588
+ try {
589
+ await Promise.race([child, deadline]);
590
+ } catch (error) {
591
+ if (timeoutError) {
592
+ await cleanup;
593
+ throw timeoutError;
594
+ }
595
+ throw error;
596
+ } finally {
597
+ clearTimeout(timer);
598
+ }
599
+ }
600
+ //#endregion
64
601
  //#region src/generated/catalog.ts
65
602
  const TEMPLATE_CATALOG = {
66
603
  "@babel/core": "^8.0.6",
@@ -76,6 +613,7 @@ const TEMPLATE_CATALOG = {
76
613
  "@iconify/tailwind4": "^1.2.3",
77
614
  "@mini-types/alipay": "^3.0.14",
78
615
  "@tailwindcss/vite": "^4.3.3",
616
+ "@types/d3": "^7.4.3",
79
617
  "@types/node": "^26.6.2",
80
618
  "@types/react": "19.3.0",
81
619
  "@types/react-reconciler": "0.33.0",
@@ -98,7 +636,7 @@ const TEMPLATE_CATALOG = {
98
636
  "lru-cache": "^11.5.3",
99
637
  "magic-string": "^1.4.1",
100
638
  "miniprogram-api-typings": "^5.2.3",
101
- "oxc-parser": "^0.150.0",
639
+ "oxc-parser": "^0.151.0",
102
640
  "oxc-walker": "^1.1.1",
103
641
  "pkg-types": "^2.3.3",
104
642
  "postcss-selector-parser": "^7.1.6",
@@ -112,11 +650,13 @@ const TEMPLATE_CATALOG = {
112
650
  "uview-plus": "3.8.113",
113
651
  "vite-plugin-inspect": "^12.0.2",
114
652
  "vue-tsc": "^3.3.11",
115
- "weapp-tailwindcss": "^5.5.6",
653
+ "weapp-tailwindcss": "^5.5.8",
116
654
  autoprefixer: "^10.6.1",
117
655
  axios: "^1.20.0",
118
656
  clsx: "^2.1.1",
657
+ d3: "^7.9.0",
119
658
  dayjs: "^1.11.23",
659
+ devframe: "1.0.0",
120
660
  echarts: "^6.1.0",
121
661
  esbuild: "^0.28.2",
122
662
  eslint: "^10.11.0",
@@ -135,7 +675,7 @@ const TEMPLATE_CATALOG = {
135
675
  stylelint: "^17.15.0",
136
676
  tailwindcss: "^4.3.3",
137
677
  tslib: "^2.8.1",
138
- tsx: "^4.23.13",
678
+ tsx: "^4.23.15",
139
679
  typescript: "^6.0.3",
140
680
  vite: "8.3.0",
141
681
  vue: "^3.5.43",
@@ -143,7 +683,7 @@ const TEMPLATE_CATALOG = {
143
683
  };
144
684
  const TEMPLATE_NAMED_CATALOG = {
145
685
  "tdesign-miniprogram-fixed": { "tdesign-miniprogram": "1.16.1" },
146
- "weapp-tailwindcss-fixed": { "weapp-tailwindcss": "5.5.6" },
686
+ "weapp-tailwindcss-fixed": { "weapp-tailwindcss": "5.5.8" },
147
687
  latest: {
148
688
  "miniprogram-api-typings": "^5.2.3",
149
689
  typescript: "^6.0.3"
@@ -151,77 +691,50 @@ const TEMPLATE_NAMED_CATALOG = {
151
691
  tailwind4: { tailwindcss: "^4.3.3" }
152
692
  };
153
693
  //#endregion
154
- //#region src/npm.ts
155
- /**
156
- * @description 从 npm registry 获取包的最新版本号
157
- */
158
- function getLatestVersionFromNpm(packageName) {
159
- return new Promise((resolve, reject) => {
160
- const url = `https://registry.npmjs.org/${packageName}/latest`;
161
- https.get(url, (res) => {
162
- if (!res || res.statusCode && res.statusCode >= 400) {
163
- res?.resume();
164
- reject(/* @__PURE__ */ new Error(`Request to ${url} failed with status ${res?.statusCode ?? "unknown"}`));
165
- return;
166
- }
167
- let data = "";
168
- res.setEncoding("utf8");
169
- res.on("data", (chunk) => data += chunk);
170
- res.on("end", () => {
171
- try {
172
- const json = JSON.parse(data);
173
- if (!json.version || typeof json.version !== "string") {
174
- reject(/* @__PURE__ */ new Error(`Unexpected response when fetching ${packageName}: missing version`));
175
- return;
176
- }
177
- resolve(json.version);
178
- } catch (err) {
179
- reject(err);
180
- }
181
- });
182
- res.on("error", reject);
183
- }).on("error", reject);
184
- });
185
- }
186
- async function latestVersion(packageName, prefix = "^", fetch = getLatestVersionFromNpm) {
187
- try {
188
- const resolved = await fetch(packageName);
189
- if (!resolved) return null;
190
- return `${prefix}${resolved}`;
191
- } catch {
192
- return null;
694
+ //#region src/templateDependencies/index.ts
695
+ const DIGIT_RE = /\d/;
696
+ const templateCatalogMap = { ...TEMPLATE_CATALOG };
697
+ const templateNamedCatalogMap = Object.fromEntries(Object.entries(TEMPLATE_NAMED_CATALOG).map(([name, deps]) => [name, { ...deps }]));
698
+ function resolveCatalogSpec(packageName, spec) {
699
+ if (!spec.startsWith("catalog:")) return spec;
700
+ const catalogName = spec.slice(8);
701
+ if (!catalogName) return templateCatalogMap[packageName] ?? spec;
702
+ const fromNamedCatalog = templateNamedCatalogMap[catalogName]?.[packageName];
703
+ if (fromNamedCatalog) {
704
+ if (fromNamedCatalog === "latest") return templateCatalogMap[packageName] ?? fromNamedCatalog;
705
+ return fromNamedCatalog;
193
706
  }
707
+ return templateCatalogMap[packageName] ?? spec;
194
708
  }
195
- //#endregion
196
- //#region src/skills.ts
197
- const RECOMMENDED_SKILLS_SOURCE = "sonofmagic/skills";
198
- const RECOMMENDED_SKILLS_INSTALL_COMMAND = `npx skills add ${RECOMMENDED_SKILLS_SOURCE}`;
199
- function resolveNpxCommand() {
200
- return process.platform === "win32" ? "npx.cmd" : "npx";
201
- }
202
- /**
203
- * @description 安装推荐的 AI skills。
204
- */
205
- async function installRecommendedSkills(cwd) {
206
- const command = resolveNpxCommand();
207
- await new Promise((resolve, reject) => {
208
- const child = spawn(command, [
209
- "skills",
210
- "add",
211
- RECOMMENDED_SKILLS_SOURCE
212
- ], {
213
- cwd,
214
- stdio: "inherit"
215
- });
216
- child.on("error", reject);
217
- child.on("exit", (code) => {
218
- if (code === 0) {
219
- resolve();
220
- return;
709
+ function normalizeTemplateDependencySpecs(pkgJson) {
710
+ for (const field of [
711
+ "dependencies",
712
+ "devDependencies",
713
+ "peerDependencies",
714
+ "optionalDependencies"
715
+ ]) {
716
+ const deps = pkgJson[field];
717
+ if (!deps) continue;
718
+ for (const [name, rawSpec] of Object.entries(deps)) {
719
+ if (typeof rawSpec !== "string" || !rawSpec) continue;
720
+ const spec = rawSpec;
721
+ if (spec.startsWith("catalog:")) deps[name] = resolveCatalogSpec(name, spec);
722
+ else if (spec.startsWith("workspace:")) {
723
+ const workspaceSpec = spec.slice(10);
724
+ if (workspaceSpec && DIGIT_RE.test(workspaceSpec)) {
725
+ deps[name] = workspaceSpec;
726
+ continue;
727
+ }
728
+ const fromCatalog = templateCatalogMap[name];
729
+ if (fromCatalog) deps[name] = fromCatalog;
221
730
  }
222
- reject(/* @__PURE__ */ new Error(`skills install exited with code ${code ?? "unknown"}`));
223
- });
224
- });
731
+ }
732
+ }
733
+ }
734
+ function ensureManagedTypeScriptDevDependencies(pkgJson) {
735
+ pkgJson.devDependencies ??= {};
736
+ if (pkgJson.dependencies?.["@types/node"] || pkgJson.devDependencies["@types/node"] || pkgJson.peerDependencies?.["@types/node"] || pkgJson.optionalDependencies?.["@types/node"]) return;
737
+ pkgJson.devDependencies["@types/node"] = templateCatalogMap["@types/node"];
225
738
  }
226
739
  //#endregion
227
740
  //#region src/gitignore.ts
@@ -341,7 +854,7 @@ async function writeJsonFile(filepath, data, spaces = 2) {
341
854
  throw new FsWriteError(filepath, error);
342
855
  }
343
856
  }
344
- async function writeFile(filepath, contents) {
857
+ async function writeFile$1(filepath, contents) {
345
858
  try {
346
859
  await fs.outputFile(filepath, contents, "utf8");
347
860
  } catch (error) {
@@ -356,14 +869,13 @@ async function updateGitIgnore(options) {
356
869
  const existing = await readFileIfExists(gitignorePath);
357
870
  const merged = mergeGitignore(existing);
358
871
  if (write && merged !== (existing ?? "")) {
359
- await writeFile(gitignorePath, merged);
872
+ await writeFile$1(gitignorePath, merged);
360
873
  logger.log(`✨ 更新 ${path.relative(root, gitignorePath)} 成功!`);
361
874
  }
362
875
  return merged;
363
876
  }
364
877
  //#endregion
365
878
  //#region src/createProject.ts
366
- const DIGIT_RE = /\d/;
367
879
  const CRLF_RE = /\r\n/g;
368
880
  const WINDOWS_VERBATIM_PATH_RE = /^\\\\\?\\/;
369
881
  const TOURIST_APP_ID = "touristappid";
@@ -381,8 +893,6 @@ const TEMPLATE_DIR_MAP = {
381
893
  ["tdesign"]: "weapp-vite-tailwindcss-tdesign-template",
382
894
  ["vant"]: "weapp-vite-tailwindcss-vant-template"
383
895
  };
384
- const templateCatalogMap = { ...TEMPLATE_CATALOG };
385
- const templateNamedCatalogMap = Object.fromEntries(Object.entries(TEMPLATE_NAMED_CATALOG).map(([name, deps]) => [name, { ...deps }]));
386
896
  function resolveWorkspaceTemplateDir(templateName) {
387
897
  const templateDirName = TEMPLATE_DIR_MAP[templateName];
388
898
  return templateDirName ? path.resolve(moduleDir, "../../../templates", templateDirName) : path.resolve(moduleDir, "../../../templates", templateName);
@@ -412,7 +922,7 @@ async function resolveTemplateDirs(templateName) {
412
922
  function shouldSkipTemplateFile(filePath, templateRoot) {
413
923
  const relativePath = normalizeTemplateRelativePath(path.relative(normalizeTemplatePath(templateRoot), normalizeTemplatePath(filePath)));
414
924
  if (!relativePath) return false;
415
- return hasTemplatePathSegment(relativePath, (segment) => segment === "node_modules") || hasTemplatePathSegment(relativePath, (segment) => segment === ".weapp-vite") || hasTemplatePathSegment(relativePath, isGeneratedOutputSegment) || hasTemplatePathSegment(relativePath, (segment) => segment === ".turbo") || relativePath === "vite.config.ts.timestamp" || relativePath.endsWith("/vite.config.ts.timestamp") || relativePath === "CHANGELOG.md" || relativePath.endsWith("/CHANGELOG.md") || relativePath === ".DS_Store" || relativePath.endsWith("/.DS_Store");
925
+ return hasTemplatePathSegment(relativePath, (segment) => segment === "node_modules") || hasTemplatePathSegment(relativePath, (segment) => segment === ".weapp-vite") || hasTemplatePathSegment(relativePath, isGeneratedOutputSegment) || hasTemplatePathSegment(relativePath, (segment) => segment === ".turbo") || hasTemplatePathSegment(relativePath, (segment) => segment === ".npmrc") || relativePath === "vite.config.ts.timestamp" || relativePath.endsWith("/vite.config.ts.timestamp") || relativePath === "CHANGELOG.md" || relativePath.endsWith("/CHANGELOG.md") || relativePath === ".DS_Store" || relativePath.endsWith("/.DS_Store");
416
926
  }
417
927
  function normalizeLines(value) {
418
928
  return value.replace(CRLF_RE, "\n").split("\n");
@@ -469,64 +979,16 @@ function createEmptyPackageJson() {
469
979
  devDependencies: {}
470
980
  };
471
981
  }
472
- async function upsertTailwindcssVersion(pkgJson) {
473
- if (!pkgJson.devDependencies?.["weapp-tailwindcss"]) return;
474
- const resolved = await latestVersion("weapp-tailwindcss");
475
- if (resolved) pkgJson.devDependencies["weapp-tailwindcss"] = resolved;
476
- }
477
- function upsertExistingDependencyVersion(pkgJson, packageName, resolvedVersion) {
478
- if (pkgJson.dependencies?.[packageName]) pkgJson.dependencies[packageName] = resolvedVersion;
479
- if (pkgJson.devDependencies?.[packageName]) pkgJson.devDependencies[packageName] = resolvedVersion;
480
- if (pkgJson.peerDependencies?.[packageName]) pkgJson.peerDependencies[packageName] = resolvedVersion;
481
- }
482
- function toCaretVersion(version) {
483
- return version.startsWith("^") ? version : `^${version}`;
484
- }
485
- function resolveCatalogSpec(packageName, spec) {
486
- if (!spec.startsWith("catalog:")) return spec;
487
- const catalogName = spec.slice(8);
488
- if (!catalogName) return templateCatalogMap[packageName] ?? spec;
489
- const fromNamedCatalog = templateNamedCatalogMap[catalogName]?.[packageName];
490
- if (fromNamedCatalog) {
491
- if (fromNamedCatalog === "latest") return templateCatalogMap[packageName] ?? fromNamedCatalog;
492
- return fromNamedCatalog;
493
- }
494
- return templateCatalogMap[packageName] ?? spec;
495
- }
496
- function normalizeTemplateDependencySpecs(pkgJson) {
497
- for (const field of [
498
- "dependencies",
499
- "devDependencies",
500
- "peerDependencies",
501
- "optionalDependencies"
502
- ]) {
503
- const deps = pkgJson[field];
504
- if (!deps) continue;
505
- for (const [name, rawSpec] of Object.entries(deps)) {
506
- if (typeof rawSpec !== "string" || !rawSpec) continue;
507
- const spec = rawSpec;
508
- if (spec.startsWith("catalog:")) deps[name] = resolveCatalogSpec(name, spec);
509
- else if (spec.startsWith("workspace:")) {
510
- const workspaceSpec = spec.slice(10);
511
- if (workspaceSpec && DIGIT_RE.test(workspaceSpec)) {
512
- deps[name] = workspaceSpec;
513
- continue;
514
- }
515
- const fromCatalog = templateCatalogMap[name];
516
- if (fromCatalog) deps[name] = fromCatalog;
517
- }
518
- }
519
- }
520
- }
521
- function ensureManagedTypeScriptDevDependencies(pkgJson) {
522
- pkgJson.devDependencies ??= {};
523
- if (pkgJson.dependencies?.["@types/node"] || pkgJson.devDependencies["@types/node"] || pkgJson.peerDependencies?.["@types/node"] || pkgJson.optionalDependencies?.["@types/node"]) return;
524
- pkgJson.devDependencies["@types/node"] = templateCatalogMap["@types/node"];
525
- }
526
982
  /**
527
983
  * @description 根据模板创建项目
528
984
  */
529
985
  async function createProject(targetDir = "", templateName = "default", options = {}) {
986
+ const dependencyVersionStrategy = options.dependencyVersionStrategy ?? "bundled";
987
+ validateDependencyVersionStrategy(dependencyVersionStrategy);
988
+ const network = await resolveRegistryOptions({
989
+ registry: options.registry,
990
+ projectRoot: path.resolve(targetDir || ".")
991
+ });
530
992
  const { preferredTemplateDir, workspaceTemplateDir } = await resolveTemplateDirs(templateName);
531
993
  const dotGitignorePath = path.resolve(targetDir, ".gitignore");
532
994
  const existingGitignore = await fs.pathExists(dotGitignorePath) ? await fs.readFile(dotGitignorePath, "utf8") : null;
@@ -546,14 +1008,9 @@ async function createProject(targetDir = "", templateName = "default", options =
546
1008
  const pkgJson = await fs.pathExists(templatePackagePath) ? await fs.readJSON(templatePackagePath) : createEmptyPackageJson();
547
1009
  normalizeTemplateDependencySpecs(pkgJson);
548
1010
  ensureManagedTypeScriptDevDependencies(pkgJson);
549
- if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
550
- upsertExistingDependencyVersion(pkgJson, "weapp-vite", toCaretVersion(version));
551
- upsertExistingDependencyVersion(pkgJson, "@weapp-vite/eslint", toCaretVersion(version$1));
552
- upsertExistingDependencyVersion(pkgJson, "@weapp-vite/react", toCaretVersion(version$4));
553
- upsertExistingDependencyVersion(pkgJson, "wevu", toCaretVersion(version$3));
554
- upsertExistingDependencyVersion(pkgJson, "@weapp-vite/dashboard", toCaretVersion(version$2));
555
- await upsertTailwindcssVersion(pkgJson);
1011
+ const resolution = await resolveDependencyVersions(pkgJson, dependencyVersionStrategy, network);
556
1012
  await writeJsonFile(packageJsonPath, pkgJson);
1013
+ await ensurePnpmBuildPolicy(targetDir);
557
1014
  await writeAgentsGuidelines(targetDir, templateName);
558
1015
  await updateGitIgnore({
559
1016
  root: targetDir,
@@ -563,15 +1020,18 @@ async function createProject(targetDir = "", templateName = "default", options =
563
1020
  logger.info(`🤖 即将安装 AI skills:${RECOMMENDED_SKILLS_INSTALL_COMMAND}`);
564
1021
  logger.info("如果你更想手动执行,也可以在项目创建后自行运行上面的命令。");
565
1022
  try {
566
- await installRecommendedSkills(targetDir);
1023
+ await installRecommendedSkills(targetDir, { env: registryEnvironment(network) });
567
1024
  logger.log("✨ 已安装推荐的 AI skills!");
568
- } catch (error) {
569
- const message = error instanceof Error ? error.message : String(error);
570
- logger.warn(`安装 AI skills 失败:${message}`);
1025
+ } catch {
1026
+ logger.warn("安装 AI skills 未完成(网络错误、取消或超时),项目已保留。");
571
1027
  logger.warn(`你可以稍后手动执行:${RECOMMENDED_SKILLS_INSTALL_COMMAND}`);
572
1028
  }
573
1029
  }
574
1030
  logger.log("✨ 创建模板成功!");
1031
+ logger.info(`依赖版本策略:${resolution.strategy};registry:${displayRegistry(network.registry)}`);
1032
+ logger.info("进入项目目录后安装依赖:");
1033
+ const temporaryRegistry = options.registry ?? process.env.CREATE_WEAPP_VITE_REGISTRY ?? process.env.npm_config_registry ?? process.env.NPM_CONFIG_REGISTRY ?? process.env.pnpm_config_registry ?? process.env.PNPM_CONFIG_REGISTRY;
1034
+ logger.info(installationCommand(temporaryRegistry !== void 0 ? network.registry : void 0));
575
1035
  }
576
1036
  async function writeAgentsGuidelines(targetDir, templateName) {
577
1037
  const agentsPath = path.resolve(targetDir, "AGENTS.md");
@@ -584,4 +1044,4 @@ async function writeAgentsGuidelines(targetDir, templateName) {
584
1044
  await fs.writeFile(agentsPath, createAgentsGuidelines(templateName));
585
1045
  }
586
1046
  //#endregion
587
- export { RECOMMENDED_SKILLS_INSTALL_COMMAND as n, TemplateName as r, createProject as t };
1047
+ export { validateDependencyVersionStrategy as i, RECOMMENDED_SKILLS_INSTALL_COMMAND as n, TemplateName as r, createProject as t };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-weapp-vite",
3
3
  "type": "module",
4
- "version": "2.8.17",
4
+ "version": "2.9.1",
5
5
  "description": "create-weapp-vite tools and runtime components for modern WeChat and multi-platform mini-program development.",
6
6
  "author": "ice breaker <1324318532@qq.com>",
7
7
  "license": "MIT",
@@ -59,11 +59,21 @@
59
59
  },
60
60
  "dependencies": {
61
61
  "@inquirer/prompts": "^8.7.2",
62
- "@weapp-core/init": "6.0.21",
63
- "@weapp-core/logger": "3.1.5",
64
- "@weapp-core/shared": "3.2.4",
62
+ "@npmcli/config": "^10.13.0",
63
+ "@weapp-core/init": "6.0.22",
64
+ "@weapp-core/logger": "3.1.6",
65
+ "@weapp-core/shared": "3.2.5",
66
+ "execa": "^9.6.1",
67
+ "make-fetch-happen": "^15.0.6",
68
+ "npm-registry-fetch": "^19.1.1",
65
69
  "pathe": "^2.0.3",
66
- "pkg-types": "^2.3.3"
70
+ "pkg-types": "^2.3.3",
71
+ "semver": "^7.8.5"
72
+ },
73
+ "devDependencies": {
74
+ "@types/make-fetch-happen": "^10.0.4",
75
+ "@types/npm-registry-fetch": "^8.0.9",
76
+ "@types/npmcli__config": "^6.0.4"
67
77
  },
68
78
  "scripts": {
69
79
  "catalog:sync": "node --import tsx scripts/generate-template-catalog.ts",