@flun/node-mobile-app 2.1.0 → 2.1.2

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/CHANGELOG.md CHANGED
@@ -1,7 +1,13 @@
1
1
  # 变更日志
2
+ ## [2.1.2] - 2026-09-29 19:53
3
+ ### 更改
4
+ - 加固了拷贝文件的一些逻辑;
2
5
 
3
- ## [2.1.0] - 2026-09-29 10:39
6
+ ## [2.1.1] - 2026-09-29 13:55
7
+ ### 优化
8
+ - 优化了 'README.md' 文档;
4
9
 
10
+ ## [2.1.0] - 2026-09-29 10:39
5
11
  ### 变更
6
12
 
7
13
  - **ESLint 插件换代**:`@babel/eslint-parser`、`eslint-plugin-react`、`eslint-plugin-react-native` 三个包的最新版均不支持 ESLint 10(分别止步于 9.0.0 / 9.7 / 9),导致安装 `eslint@10` 时被额外拉入一份 `eslint@9.39.5`(npm 的 peer 兼容回退),并触发 npm 的 deprecated 警告。现替换为:
@@ -19,160 +25,4 @@
19
25
 
20
26
  - **`DEP0190` 弃用警告**:`src/utils.js` 中的 `run` / `runQuiet` / `runSilent` 原先统一用 `spawnSync(bin, args, { shell: true })`。Node 20+ 对「参数数组 + shell」这种组合会提示参数未转义(`DEP0190`),每次执行 CLI 都会在输出末尾出现该警告。现改为:
21
27
  - Windows:把命令与参数拼成单个字符串(参数按需加引号)后交给 shell——`.cmd` / `.bat` 仍能执行,且不再触发警告;
22
- - 其它平台:不使用 shell,参数交由 Node 转义。
23
- ## [2.0.8] - 2026-09-28 22:17
24
- ### 修复
25
-
26
- - **中文路径构建失败**:三处联动修复。
27
- - **AGP 拒绝非 ASCII 路径**:`template/android/gradle.properties` 增加 `android.overridePathCheck=true`,允许中文路径构建。
28
- - **`gradle.properties` 中文乱码**:该文件按 ISO-8859-1 读取,非 ASCII 字符(如路径中的「桌面」)会被读成乱码,导致 keystore 找不到。`patcher.js` 现在把写入的非 ASCII 字符转成 `\uXXXX` 转义(Java Properties 规范)。
29
- - 影响范围:项目路径含中文(或其它非 ASCII 字符)时,release 构建曾失败于 `Your project path contains non-ASCII characters` 与 `Keystore file ... not found`。
30
- - **签名配置缺项时报错含糊**:`android.signing.keystore` 已配置、但密码 / 别名 / 别名密码为空时,原先会一路跑到 Gradle 才报 `keystore password was incorrect`,容易误导排查方向。现在 `patcher.js` 在构建前即校验四项,缺项时抛出明确错误,并提示「仅本地测试可注释掉整个 signing 块」。
31
-
32
- ### 变更
33
-
34
- - **`mobileAppConfig.js` 模板**:`signing` 块内每项标注「必填」,并说明「取消整块注释即视为启用签名」。
35
-
36
- ## [2.0.7] - 2026-09-28 16:05
37
- ### 更新
38
- - 更新有小问题依赖-> `@flun/nodejs-mobile-react-native` ;
39
-
40
- ## [2.0.6] - 2026-09-28 14:57
41
- ### 更新
42
- - 更新 `@flun/nodejs-mobile-react-native` 依赖;
43
-
44
- ## [2.0.5] - 2026-09-28 11:50
45
- ### 优化
46
- 优化了部分细节;
47
-
48
- ## [2.0.4] - 2026-09-27 21:50
49
- ### 更新
50
- - 依赖包 '@flun/nodejs-mobile-react-native' 更新为 v2.0.2;
51
- - 优化了 'README.md' 和 'server.js' 一些细节;
52
-
53
- ## [2.0.3] - 2026-09-25 21:33
54
- ### 修复
55
-
56
- - **`template/metro.config.js` 排除配置更新**:原来的 `blacklistRE` 在 Metro 0.86+ 已完全移除,`metro-config/src/defaults/exclusionList.js` 的深层导入路径在 Metro 0.83 起也不再暴露,会导致新版本 RN 项目加载 Metro 配置失败。改为:
57
- - `blacklistRE` -> `blockList`
58
- - 导入路径改为 `metro-config/private/defaults/exclusionList`(不带 `.js` 后缀,经 `.default` 取值)
59
- - 正则改为 `/[/\\]nodejs-assets[/\\].*/` 形式,避免字符串转义在跨平台下出错
60
-
61
- ### 更新
62
- - 更新 `@flun/nodejs-mobile-react-native` 依赖;
63
-
64
- ---
65
-
66
- ## [2.0.2] - 2026-09-23 20:25
67
-
68
- ### 修复
69
-
70
- - **`syncTemplateDependencies` 计数不准确**:`src/copy.js` 里 `_comment` 字段的移除被一并计入 `updated`,导致日志里打印的"已同步 N 个依赖版本"比实际多 1。拆分为 `depsUpdated`(仅统计真正变化的依赖)和 `needWrite`(是否需要写文件),`_comment` 仍会被移除,但不再计入。
71
-
72
- ---
73
-
74
- ## [2.0.1] - 2026-09-23 19:53
75
- ### 更新
76
- - 升级了有bug的 `@flun/nodejs-mobile-react-native` v1.0.1版本到v1.0.3
77
-
78
- ## [2.0.0] - 2026-09-23 16:30
79
-
80
- > **这是一次 major 版本升级**。用户 API 未变,但底层依赖被整体替换,风险等级和官方 CJS 版本不是一个量级。
81
-
82
- ### 重大变更(Breaking)
83
-
84
- - **更换底层 runtime**:`nodejs-mobile-react-native` → **`@flun/nodejs-mobile-react-native`**(flun fork)
85
- - 内部全量 ESM 化
86
- - `rn-bridge` 保持 CJS(改为 `index.cjs`),兼容 CJS / ESM 项目
87
- - 依赖升级:`tar@^6` → `^7`、`glob@^10` → `^13`、`make-fetch-happen@^13` → `^16`
88
- - 删除 `xcode` 依赖(RN 0.60 autolinking 前的遗留)
89
- - `react-native` peer 标为 optional,避免重复安装
90
- - **更换原生模块编译工具**:`nodejs-mobile-gyp` → **`@flun/nodejs-mobile-gyp`**(flun fork)
91
- - 上游停更、依赖 `tar@6` / `glob@10` 触发 deprecated 与安全审计警告
92
- - fork 后升级 `tar@^7`、`glob@^13`、`make-fetch-happen@^16`,0 vulnerabilities
93
- - 支持 **Visual Studio 2026**(版本号 18,toolset `v145`)
94
- - 强制走 **MSVC** 工具集(不再使用 ClangCL),关闭 LTO
95
- - 避免 `MSB8020: 无法找到 ClangCL 的生成工具`
96
- - 避免 `LNK1117: 选项"opt:lldltojobs=2"中的语法错误`
97
-
98
- ### 用户影响
99
-
100
- **无需修改任何配置或代码**。`mobileAppConfig.js` 格式、`npx node-mobile-app test / build` 命令、生成的 APK 行为均不变。
101
-
102
- 但底层 runtime 换了维护主体,若升级后遇到问题,请优先在 `@flun/node-mobile-app` 仓库反馈。
103
-
104
- ### 新增
105
-
106
- - **模板依赖版本自动同步**:`template/package.json` 里声明的依赖版本,由 CLI 在 `copyTemplate` 阶段自动从主包 `package.json` 的 `dependencies` 同步。
107
- - 单一数据源:升级依赖只需改主包一处,模板自动跟上
108
- - 缺声明会报错:模板里声明但主包未声明的依赖,构建时直接抛错中断,避免模板里的 `"*"` 占位值被误用
109
- - 实现:`src/copy.js` 新增 `syncTemplateDependencies(pkgRoot, buildDir)`
110
- - **原生模块自动检测**:CLI 自动判断用户项目依赖里是否含原生模块(带 `binding.gyp` 的包,如 `bcrypt`、`sqlite3`),决定是否编译原生代码。
111
- - 移除配置字段 `android.buildNativeModules`——用户无需手动配置
112
- - 检测范围:用户根 `package.json` 的 `dependencies` 里,逐个查 `userProjectDir/node_modules/<name>/binding.gyp`
113
- - 检测结果纳入依赖哈希:装/卸原生模块自动触发重装重编
114
- - 新增环境变量 `NODE_MOBILE_FORCE_REBUILD=1`:特殊场景(如换了 `libnode.so` 但依赖未变)下强制重装重编
115
- - 平台限制:Windows 上 nodejs-mobile 无法编译原生模块(上游限制),此功能仅 Linux / macOS 可用
116
- - 实现:`src/install.js` 的 `hasNativeModules`、`installUserDeps`、`hashDeps`
117
-
118
- ### 修复
119
-
120
- - **主包 `main.js` 生成逻辑适配 rn-bridge**:`generate.js` 生成的 `main.js` 改用 `require('rn-bridge')`(配合 rn-bridge 的 `.cjs`),避免 `await import` 绕过 native binding 的 CJS loader 路径导致崩溃
121
- - **`@flun/nodejs-mobile-react-native` 插件路径适配**:`src/install.js`、`src/fingerprint.js`、`src/commands/clean.js` 中对插件目录的探测,从 `node_modules/nodejs-mobile-react-native` 改为 `node_modules/@flun/nodejs-mobile-react-native`
122
- - **模板依赖清理**:
123
- - 删除 `template/package.json` 里冗余的 `react-native-safe-area-context`(模板代码零引用)
124
- - 删除 `template/@types/nodejs-mobile-react-native/`(源包 `index.d.ts` 已覆盖,且 `package.json` 加了 `types` 字段指向它)
125
- - 修正 `template/package.json` 里 `@flun/nodejs-mobile-react-native` 的版本号(旧值 `^18.20.4-flun.1` 在 npm 上不存在)
126
-
127
- ### 依赖调整
128
-
129
- - `dependencies` 里的 `nodejs-mobile-react-native` 换成 `@flun/nodejs-mobile-react-native: ^1.0.1`
130
- - 该 fork 内部再依赖 `@flun/nodejs-mobile-gyp: ^1.0.1`
131
- - 依赖树彻底干净,`npm ls tar glob uuid` 无停更包,0 vulnerabilities
132
-
133
- ### 验证
134
-
135
- - Windows + VS2026 BuildTools + Python 3.14 + Node 26.8.1 环境下:
136
- - `npx node-mobile-app test` 全链路通过(Gradle 编译 + 部署到真机 + Metro 热更新)
137
- - `npx node-mobile-app build` / `build --release` 生成 APK 正常
138
- - App 启动正常,Node 服务正常监听端口
139
- - 模板依赖版本自动同步经独立测试验证(改假版本号 → CLI 覆盖为真实版本)
140
- - iOS:保留对 Node 18 的支持(未在 macOS 上实测)
141
- ### 文档同步
142
-
143
- **预编译二进制改为自动下载**相关的文档改动:
144
-
145
- - `README.md`:
146
- - 新增 Q 段落「`libnode.so` 和 `NodeMobile.xcframework` 是怎么来的?」,说明 postinstall 自动下载机制、环境变量、失败处理
147
- - 原「如何替换自定义的 `libnode.so`?」改为「如何替换成自定义编译的 `libnode.so`?」,明确适用于自编译 v22+ 场景
148
- - `Android 构建指南.md`:
149
- - 新增 2.0 章节「预编译二进制怎么来的」
150
- - 2.1 标题从「默认随包带」改为「默认自动下载」
151
- - `自定义libnode指南.md`:
152
- - 章节二开头加提示:默认 v18 由 postinstall 自动下载,本节仅服务 v22+ 自编译场景
153
- - **清理 `nodejs-assets/` 相关说明**:`@flun/nodejs-mobile-react-native` 1.0.1 起,宿主项目依赖 `@flun/node-mobile-app` 时不再复制 `nodejs-assets/`,因此:
154
- - `README.md`:删除项目根目录树里的 `nodejs-assets/`、删除「两个 nodejs-assets 的区别」整段、删除 Q 段落「nodejs-assets 一直出现在你的项目根」
155
- - `mobileAppConfig.js`:`excludeFiles` 里 `nodejs-assets/` 的注释改为「旧版插件残留;防止误打包」
156
- - **原生模块自动检测**相关的文档改动:
157
- - `README.md`:删除配置字段表里的 `buildNativeModules` 行
158
- - `Android 构建指南.md`:1.4 章节从「原生模块编译开关(Windows)」改为「原生模块编译(自动检测)」,说明自动识别 `binding.gyp`、`NODE_MOBILE_FORCE_REBUILD` 手动强制、Windows 平台限制
159
- - `自定义libnode指南.md`:2.6 章节从「重编原生模块(仅当用到 `bcrypt` 等)」改为「重编原生模块(仅当项目含原生模块)」,说明默认自动处理、手动触发方式;迁移清单与常见坑表格同步
160
-
161
- 背景:`@flun/nodejs-mobile-react-native` 1.0.1 起,`android-libnode.zip`(约 52 MB)与 `ios-nodemobile.zip`(约 47 MB)不再随 npm 包分发,改为 postinstall 阶段从 Gitee / GitHub Release 自动拉取。npm tarball 从 107 MB 降到 1.2 MB。
162
-
163
- ---
164
-
165
- ## [1.0.2] - 2026-09-21 08:24
166
-
167
- ### 修复
168
- - **依赖树瘦身**:删除主包里未使用的 6 个依赖(`@babel/preset-env`、`@babel/runtime`、`@react-native/jest-preset`、`@react-native/new-app-screen`、`@react-native/typescript-config`、`react-native-safe-area-context`),安装体积与安装耗时下降
169
- - **消除旧依赖链**:移除 `@react-native/eslint-config`,改为内置自写 ESLint flat config(`src/utils.js` 的 `buildEslintConfigSource()`,构建时覆写到 `node-mobile-app-build/eslint.config.js`)。随之消失的旧包:`inflight@1.0.6`、`rimraf@3.0.2`、`glob@7.2.3`、`@humanwhocodes/config-array`、`@humanwhocodes/object-schema`
170
- - **ESLint 升级到 9**:统一使用 `eslint@^9`,不再出现 eslint 8 / 9 两份并存
171
- - **模板 lint 清理**:修复 `template/App.tsx` 中逗号表达式触发的 `@typescript-eslint/no-unused-expressions` 报错,模板现在默认 `npx eslint` 零 error 零 warning
172
-
173
- ### 文档
174
- - `mobileAppConfig.js` 与 `README.md` 更正签名说明:`build --release` **不配置 `android.signing` 也能出包**,默认沿用模板自带的 debug keystore 签名(可安装、可本地测试,不可上架);需要上架时再填 `keystore` / `storePassword` / `keyAlias` / `keyPassword`
175
-
176
- ## [1.0.1] - 2026-09-20 11:52
177
- ### 首发
178
- - 发布将 Node.js 项目一键打包为 Android / iOS 移动应用(基于 nodejs-mobile-react-native);
28
+ - 其它平台:不使用 shell,参数交由 Node 转义。
package/README.md CHANGED
@@ -177,20 +177,33 @@ npx node-mobile-app build --release
177
177
 
178
178
  `build` 打出的 APK 在 `outputDir`(默认 `./out/`)里;手动装到手机:
179
179
 
180
+ Windows(用环境变量定位 adb,任何用户通用):
181
+
180
182
  ```powershell
181
- # Windows PowerShell
182
183
  $adb = "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe"
183
- & $adb install -r "D:\你的项目\out\<appName>-<versionName>-debug.apk"
184
+ & $adb install -r "<编译好的 APK 完整路径>"
185
+ ```
186
+
187
+ macOS / Linux:
188
+
189
+ ```bash
190
+ adb install -r "<编译好的 APK 完整路径>"
191
+ ```
192
+
193
+ 例如(`build` 末尾会打印完整路径):
194
+
195
+ ```powershell
196
+ # Windows
197
+ & $adb install -r "D:\你的项目\out\myapp-1.0.0-release.apk"
184
198
  ```
185
199
 
186
200
  ```bash
187
201
  # macOS / Linux
188
- adb install -r ./out/<appName>-<versionName>-debug.apk
202
+ adb install -r "./out/myapp-1.0.0-release.apk"
189
203
  ```
190
204
 
191
205
  **说明**:
192
206
  - `-r` = 覆盖安装(保留应用数据)
193
- - 不知道 APK 的确切文件名?看 `build` 命令的末尾输出(会打印完整路径)
194
207
  - `adb` 找不到?在 `mobileAppConfig.js` 里配 `android.adbPath`
195
208
  - `test` 命令已自动安装,无需手动
196
209
 
@@ -231,23 +244,23 @@ adb install -r ./out/<appName>-<versionName>-debug.apk
231
244
 
232
245
  ### Android
233
246
 
234
- | 字段 | 类型 | 默认 | 说明 |
235
- | ----------------------- | ---------------- | -------------------- | ---------------------------------------------------------------------- |
236
- | `sdkPath` | `string \| null` | `null` | Android SDK 路径(默认自动查找) |
237
- | `adbPath` | `string \| null` | `null` | adb 路径(默认自动查找) |
238
- | `minSdkVersion` | `int` | `29` | 最低 SDK(29=Android 10.0) |
239
- | `targetSdkVersion` | `int` | `36` | 目标 SDK |
240
- | `compileSdkVersion` | `int` | `37` | 编译 SDK |
241
- | `buildToolsVersion` | `string` | `'37.0.0'` | 构建工具版本 |
242
- | `abiFilters` | `string[]` | `['arm64-v8a']` | 打包架构(真机 `arm64-v8a`,模拟器加 `x86_64`) |
243
- | `icon` | `string` | `'./build/icon.png'` | 应用图标(建议 512×512 PNG) |
244
- | `usesCleartextTraffic` | `boolean` | `true` | 是否允许明文 HTTP |
245
- | `permissions` | `string[]` | `[]` | 额外权限(如 `'android.permission.CAMERA'`) |
247
+ | 字段 | 类型 | 默认 | 说明 |
248
+ | ----------------------- | ---------------- | -------------------- | --------------------------------------------------------------------------------------------------------- |
249
+ | `sdkPath` | `string \| null` | `null` | Android SDK 路径(默认自动查找) |
250
+ | `adbPath` | `string \| null` | `null` | adb 路径(默认自动查找) |
251
+ | `minSdkVersion` | `int` | `29` | 最低 SDK(29=Android 10.0) |
252
+ | `targetSdkVersion` | `int` | `36` | 目标 SDK |
253
+ | `compileSdkVersion` | `int` | `37` | 编译 SDK |
254
+ | `buildToolsVersion` | `string` | `'37.0.0'` | 构建工具版本 |
255
+ | `abiFilters` | `string[]` | `['arm64-v8a']` | 打包架构(真机 `arm64-v8a`,模拟器加 `x86_64`) |
256
+ | `icon` | `string` | `'./build/icon.png'` | 应用图标(建议 512×512 PNG) |
257
+ | `usesCleartextTraffic` | `boolean` | `true` | 是否允许明文 HTTP |
258
+ | `permissions` | `string[]` | `[]` | 额外权限(如 `'android.permission.CAMERA'`) |
246
259
  | `signing` | `object` | — | 自定义签名配置。**不填则用模板自带的 debug keystore 签名**(可安装测试,不可上架);填则以下 4 项均为必填 |
247
- | `signing.keystore` | `string` | — | keystore 路径(相对你的项目根) |
248
- | `signing.storePassword` | `string` | — | keystore 密码 |
249
- | `signing.keyAlias` | `string` | — | 密钥别名 |
250
- | `signing.keyPassword` | `string` | — | 密钥密码(通常与 `storePassword` 相同) |
260
+ | `signing.keystore` | `string` | — | keystore 路径(相对你的项目根) |
261
+ | `signing.storePassword` | `string` | — | keystore 密码 |
262
+ | `signing.keyAlias` | `string` | — | 密钥别名 |
263
+ | `signing.keyPassword` | `string` | — | 密钥密码(通常与 `storePassword` 相同) |
251
264
 
252
265
  #### 生成自签名 keystore
253
266
 
@@ -405,8 +418,13 @@ CLI **自动识别 `server.js` 实际监听的协议和端口**——拦截 `net
405
418
 
406
419
  `test` 打的是 debug 版,启动时**先尝试连 Metro**,失败后才 fallback 到内置 bundle;这个"等超时"是 RN debug 版的固有行为;
407
420
 
408
- - 需要**秒开**:用 `npx node-mobile-app build`
409
- - 需要**热更新**:`test` 保持 USB 连接
421
+ 拔线后的启动速度(从快到慢):
422
+
423
+ 1. **`npx node-mobile-app build --release`** 打包后安装到手机——最快(release 版不连 Metro)
424
+ 2. **`npx node-mobile-app build`** 打包后安装到手机——次之(debug 版,会先尝试连 Metro 再回退)
425
+ 3. **`npx node-mobile-app test`** 后手动打开应用——最慢(同上,且 `test` 面向「连 USB 热更新」场景)
426
+
427
+ 若需要**热更新**,则保持 `test` 的 USB 连接。
410
428
 
411
429
  ### Q:`test` 装到手机后拔 USB 就崩?
412
430
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flun/node-mobile-app",
3
- "version": "2.1.0",
3
+ "version": "2.1.2",
4
4
  "description": "将 Node.js 项目一键打包为 Android / iOS 移动应用,运行时由 @flun/nodejs-mobile-react-native 提供,无需修改你的 Node 代码;",
5
5
  "author": "flun <open@flun.top>",
6
6
  "keywords": [
@@ -60,7 +60,7 @@
60
60
  "eslint-plugin-eslint-comments": "^3.2.0",
61
61
  "eslint-plugin-react-hooks": "^7.1.1",
62
62
  "@eslint-react/eslint-plugin": "^5.21.3",
63
- "@flun/nodejs-mobile-react-native": "^2.0.6"
63
+ "@flun/nodejs-mobile-react-native": "^2.0.7"
64
64
  },
65
65
  "allowScripts": {
66
66
  "node": true,
@@ -2,34 +2,33 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
 
5
- // 要复制的文件位置和文件目标位置
5
+ // 要复制的文件位置、目标目录和目标文件路径
6
6
  const __filename = fileURLToPath(import.meta.url), __dirname = path.dirname(__filename),
7
- initCwd = process.env.INIT_CWD, fileName = 'mobileAppConfig.js', targetDir = initCwd ? path.resolve(initCwd) : '',
8
- // 要拷贝的文件和目标文件路径
9
- sourceFile = path.join(__dirname, '..', fileName), targetFile = path.join(targetDir, fileName),
10
- /**
11
- * 复制文件到项目根目录
12
- * >查看定义:@see {@link copyFile}
13
- * @returns {boolean} - 复制是否成功
14
- */
15
- copyFile = () => {
16
- console.log(`🔍 检查 ${fileName} 文件...`), console.log(`📁 项目根目录:${targetDir}`);
17
- try {
18
- // 无 INIT_CWD(非用户项目安装场景)|| 目标目录不是 Node 项目 || 包自身的 npm install(开发调试)→ 跳过
19
- if (!targetDir) return true;
20
- if (!fs.existsSync(path.join(targetDir, 'package.json'))) return true;
21
- if (targetDir === path.resolve(__dirname, '..')) return true;
22
- if (fs.existsSync(targetFile)) return true; // 目标文件已存在 → 直接返回
23
- console.log(`⚠️ 在项目根目录未找到 ${fileName} 文件,正在创建...`);
7
+ fileName = 'mobileAppConfig.js', sourceFile = path.join(__dirname, '..', fileName),
8
+ targetDir = path.resolve(process.env.INIT_CWD || process.cwd()), targetFile = path.join(targetDir, fileName);
24
9
 
25
- fs.copyFileSync(sourceFile, targetFile); // 复制源文件到项目根目录
26
- console.log(`✓ 已创建 ${fileName} 示例文件:${targetFile}`);
27
- return true;
28
- } catch (error) {
29
- console.error(`✗ 创建 ${fileName} 文件失败:`, error.message);
30
- return false;
31
- }
10
+ /**
11
+ * 复制文件到项目根目录
12
+ * >查看定义:@see {@link copyFile}
13
+ * @returns {boolean} - 复制是否成功
14
+ */
15
+ const copyFile = () => {
16
+ console.log(`🔍 检查 ${fileName} 文件...`), console.log(`📁 项目根目录:${targetDir}`);
17
+ try {
18
+ // 目标目录不是 Node 项目 || 包自身的 npm install(开发调试)→ 跳过
19
+ if (!fs.existsSync(path.join(targetDir, 'package.json'))) return true;
20
+ if (targetDir === path.resolve(__dirname, '..')) return true;
21
+ if (fs.existsSync(targetFile)) return true; // 目标文件已存在 → 直接返回
22
+ console.log(`⚠️ 在项目根目录未找到 ${fileName} 文件,正在创建...`);
23
+
24
+ fs.copyFileSync(sourceFile, targetFile); // 复制源文件到项目根目录
25
+ console.log(`✓ 已创建 ${fileName} 示例文件:${targetFile}`);
26
+ return true;
27
+ } catch (error) {
28
+ console.error(`✗ 创建 ${fileName} 文件失败:`, error.message);
29
+ return false;
32
30
  }
31
+ };
33
32
 
34
33
  // 执行脚本并导出函数
35
34
  if (process.argv[1] === __filename) copyFile();