@ghini/xstart 26.1.5211744 → 26.1.6175449

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
@@ -1,47 +1,69 @@
1
- # 一个标准 TypeScript 2026 npm 库脚手架
1
+ # TypeScript 2026 npm 库脚手架
2
2
 
3
- 2026 版现代 TS 脚手架:基于 Node 24 原生 ESM,利用“源码直连”实现免 build 实时调试;通过 esbuild 抹除后缀并配合 publishConfig,兼顾开发爽感与发布标准,是极致紧凑的 npm 库闭环模板。
3
+ 基于 Node 24 原生 ESM,利用"源码直连"实现免 build 实时调试;通过 esbuild 抹除后缀并配合 publishConfig,兼顾开发爽感与发布标准。
4
4
 
5
- ### 发布
5
+ ## 📦 创建新库
6
+
7
+ **PowerShell (Windows)**
8
+
9
+ ```powershell
10
+ $lib = "YOUR_LIB_NAME"
11
+ git clone -b xstart git@github.com:xghini/mynpm.git $lib && cd $lib
12
+ (Get-Content package.json) -replace 'xstart', $lib | Set-Content package.json
13
+ (Get-Content README.md) -replace 'xstart', $lib | Set-Content README.md
14
+ Remove-Item -Recurse -Force .git
15
+ git init && git add . && git commit -m "init: $lib from xstart template"
16
+ git branch -M $lib && git remote add origin git@github.com:xghini/mynpm.git
17
+ git push -u origin $lib && pnpm i && pnpm pub
18
+ ```
19
+
20
+ **Bash (Linux/macOS)**
6
21
 
7
22
  ```bash
8
- pnpm build # 编译 TypeScript + 打包 + 更新版本号
9
- pnpm checkpub # 检查发布内容
10
- pnpm pub # 构建 + 发布到 npm + 提交 git
23
+ lib="YOUR_LIB_NAME"
24
+ git clone -b xstart git@github.com:xghini/mynpm.git $lib && cd $lib
25
+ sed -i "s/xstart/$lib/g" package.json README.md
26
+ rm -rf .git
27
+ git init && git add . && git commit -m "init: $lib from xstart template"
28
+ git branch -M $lib && git remote add origin git@github.com:xghini/mynpm.git
29
+ git push -u origin $lib && pnpm i && pnpm pub
11
30
  ```
12
31
 
13
- ## 🔧 如果需要 pnpm 迁移:
32
+ ## 🚀 开发命令
14
33
 
15
- ```ps1
16
- pnpm import
17
- Remove-Item -Recurse -Force node_modules
18
- Remove-Item package-lock.json
19
- pnpm i
34
+ ```bash
35
+ pnpm test # 运行测试
36
+ pnpm test:watch # 监听模式
37
+ pnpm test:coverage # 覆盖率报告
38
+ pnpm typecheck # 类型检查
39
+ pnpm build # 编译 + 打包 + 更新版本号
40
+ pnpm pub # 构建 + 发布 + 提交
20
41
  ```
21
42
 
22
- ## 📦 创建新库
43
+ ## 🛠️ 内置工具
23
44
 
24
- 全文件替换 `xstart` 为新库名:
45
+ ```typescript
46
+ import {xpath, exefile, exedir} from '@ghini/xstart';
25
47
 
26
- ```bash
27
- git clone -b xstart git@github.com:xghini/mynpm.git
28
- mv mynpm xstart&&cd xstart
29
- rm -rf .git
30
- Remove-Item -Recurse -Force .git
31
- git init
32
- git add .
33
- git commit -m "Initial commit"
34
- git branch -M xstart
35
- git remote add origin git@github.com:xghini/mynpm.git
36
- git push -u origin xstart
37
- pnpm pub
48
+ xpath('./config.json'); // 相对于入口脚本解析路径
49
+ xpath('../data/file.txt'); // 自动处理 ../
50
+ xpath(); // 获取入口脚本目录
51
+ xpath('C:/abs/path'); // 绝对路径直接返回
52
+ xpath('file:///C:/path/file.ts'); // 自动剥离 file:/// 协议
38
53
  ```
39
54
 
40
- 💡 核心设计说明
41
- 源码直连:package.json 中的 exports 在开发态指向 src/\*.ts,配合 Node.js 24 --experimental-strip-types 实现保存即生效,告别 tsc --watch。
55
+ ## 💡 核心设计
42
56
 
43
- 双模切换:利用 publishConfig 在发布时自动将入口切回 dist/\*.js,确保用户侧的兼容性。
57
+ - **源码直连**:开发态 exports 指向 `src/*.ts`,Node 24 原生支持直接运行 TypeScript
58
+ - **双模切换**:publishConfig 发布时自动切回 `dist/*.js`,确保兼容性
59
+ - **自动版本**:build.js 内置日期版本算法(如 26.1.5161805)
60
+ - **路径处理**:xpath 默认相对于入口脚本目录,支持 pm2 等场景通过 `KIT_EXEPATH` 环境变量覆盖
44
61
 
45
- 自动版本:build.js 内部集成日期版本算法(如 26.1.5161805),无需手动维护版本号。
62
+ ## 🔧 pnpm 迁移
46
63
 
47
- esbuild 抹除:构建阶段自动处理 .ts 后缀映射,生成的单文件 ESM 产物极其紧凑。
64
+ ```powershell
65
+ pnpm import
66
+ Remove-Item -Recurse -Force node_modules
67
+ Remove-Item package-lock.json
68
+ pnpm i
69
+ ```
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export * from './start.ts';
1
+ export * from './utils.ts';
2
2
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,8 +1,36 @@
1
- // src/start.ts
2
- function start() {
3
- console.log("Hello Ghini use ts");
1
+ // src/utils.ts
2
+ import { normalize, join, isAbsolute, sep, dirname } from "node:path";
3
+ var exefile = process.env.KIT_EXEPATH || process.env.KIT_EXEFILE || process.argv[1] || "";
4
+ var exedir = dirname(exefile);
5
+ function xpath(targetPath = ".", basePath = exedir, separator = "/") {
6
+ const stripFileProtocol = (p) => {
7
+ if (p.startsWith("file:///")) {
8
+ return process.platform === "win32" ? p.slice(8) : p.slice(7);
9
+ }
10
+ return p;
11
+ };
12
+ let resolvedBase = stripFileProtocol(basePath);
13
+ if (!isAbsolute(resolvedBase)) {
14
+ resolvedBase = join(process.cwd(), resolvedBase);
15
+ }
16
+ let result;
17
+ const cleanTarget = stripFileProtocol(targetPath);
18
+ if (isAbsolute(cleanTarget)) {
19
+ result = normalize(cleanTarget);
20
+ } else {
21
+ result = normalize(join(resolvedBase, cleanTarget));
22
+ }
23
+ if (separator === "/" && sep === "\\") {
24
+ return result.split(sep).join("/");
25
+ }
26
+ if (separator === "\\" && sep === "/") {
27
+ return result.split(sep).join("\\");
28
+ }
29
+ return result;
4
30
  }
5
31
  export {
6
- start
32
+ exedir,
33
+ exefile,
34
+ xpath
7
35
  };
8
36
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/start.ts"],
4
- "sourcesContent": ["// start.ts\nexport { start };\n\nfunction start(): void {\n console.log(\"Hello Ghini use ts\");\n}\n"],
5
- "mappings": ";AAGA,SAAS,QAAc;AACrB,UAAQ,IAAI,oBAAoB;AAClC;",
3
+ "sources": ["../src/utils.ts"],
4
+ "sourcesContent": ["// src/utils.ts\nimport {normalize, join, isAbsolute, sep, dirname} from 'node:path';\n\nexport {xpath, exefile, exedir};\n\n/** \u6267\u884C\u6587\u4EF6\u8DEF\u5F84\uFF0C\u652F\u6301 pm2 \u7B49\u5DE5\u5177\u901A\u8FC7\u73AF\u5883\u53D8\u91CF\u8986\u76D6 */\nconst exefile = process.env.KIT_EXEPATH || process.env.KIT_EXEFILE || process.argv[1] || '';\n\n/** \u6267\u884C\u6587\u4EF6\u6240\u5728\u76EE\u5F55 */\nconst exedir = dirname(exefile);\n\n/**\n * \u5F3A\u5927\u53EF\u9760\u7684\u8DEF\u5F84\u5904\u7406\n * - \u76F8\u5BF9\u8DEF\u5F84\u5728\u540E\uFF0C\u7EDD\u5BF9\u8DEF\u5F84\u5728\u524D\uFF0C\u6700\u7EC8\u90FD\u8F6C\u6362\u4E3A\u7EDD\u5BF9\u8DEF\u5F84\n * - \u7EDF\u4E00\u5206\u9694\u7B26\uFF0C\u65B9\u4FBF\u6BD4\u8F83\u8DEF\u5F84\n * - \u81EA\u52A8\u5904\u7406 file:/// \u534F\u8BAE\u3001../\u88C1\u5207\u7B49\n * @param targetPath - \u76EE\u6807\u8DEF\u5F84\uFF08\u53EF\u4EE5\u662F\u76F8\u5BF9\u8DEF\u5F84\u6216\u7EDD\u5BF9\u8DEF\u5F84\uFF09\n * @param basePath - \u57FA\u51C6\u8DEF\u5F84\uFF0C\u9ED8\u8BA4\u4E3A process.cwd()\n * @param separator - \u8DEF\u5F84\u5206\u9694\u7B26\uFF0C\u9ED8\u8BA4\u4E3A '/'\n */\nfunction xpath(targetPath: string = '.', basePath: string = exedir, separator: '/' | '\\\\' = '/'): string {\n // \u5904\u7406 file:/// \u534F\u8BAE\n const stripFileProtocol = (p: string): string => {\n if (p.startsWith('file:///')) {\n // Windows: file:///C:/path \u2192 C:/path (\u53BB\u63898\u4E2A\u5B57\u7B26)\n // Unix: file:///path \u2192 /path (\u53BB\u63897\u4E2A\u5B57\u7B26)\n return process.platform === 'win32' ? p.slice(8) : p.slice(7);\n }\n return p;\n };\n\n // \u5904\u7406 basePath\n let resolvedBase = stripFileProtocol(basePath);\n if (!isAbsolute(resolvedBase)) {\n resolvedBase = join(process.cwd(), resolvedBase);\n }\n\n // \u5904\u7406 targetPath\n let result: string;\n const cleanTarget = stripFileProtocol(targetPath);\n\n if (isAbsolute(cleanTarget)) {\n result = normalize(cleanTarget);\n } else {\n result = normalize(join(resolvedBase, cleanTarget));\n }\n\n // \u7EDF\u4E00\u5206\u9694\u7B26\n if (separator === '/' && sep === '\\\\') {\n return result.split(sep).join('/');\n }\n if (separator === '\\\\' && sep === '/') {\n return result.split(sep).join('\\\\');\n }\n return result;\n}\n"],
5
+ "mappings": ";AACA,SAAQ,WAAW,MAAM,YAAY,KAAK,eAAc;AAKxD,IAAM,UAAU,QAAQ,IAAI,eAAe,QAAQ,IAAI,eAAe,QAAQ,KAAK,CAAC,KAAK;AAGzF,IAAM,SAAS,QAAQ,OAAO;AAW9B,SAAS,MAAM,aAAqB,KAAK,WAAmB,QAAQ,YAAwB,KAAa;AAEvG,QAAM,oBAAoB,CAAC,MAAsB;AAC/C,QAAI,EAAE,WAAW,UAAU,GAAG;AAG5B,aAAO,QAAQ,aAAa,UAAU,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC;AAAA,IAC9D;AACA,WAAO;AAAA,EACT;AAGA,MAAI,eAAe,kBAAkB,QAAQ;AAC7C,MAAI,CAAC,WAAW,YAAY,GAAG;AAC7B,mBAAe,KAAK,QAAQ,IAAI,GAAG,YAAY;AAAA,EACjD;AAGA,MAAI;AACJ,QAAM,cAAc,kBAAkB,UAAU;AAEhD,MAAI,WAAW,WAAW,GAAG;AAC3B,aAAS,UAAU,WAAW;AAAA,EAChC,OAAO;AACL,aAAS,UAAU,KAAK,cAAc,WAAW,CAAC;AAAA,EACpD;AAGA,MAAI,cAAc,OAAO,QAAQ,MAAM;AACrC,WAAO,OAAO,MAAM,GAAG,EAAE,KAAK,GAAG;AAAA,EACnC;AACA,MAAI,cAAc,QAAQ,QAAQ,KAAK;AACrC,WAAO,OAAO,MAAM,GAAG,EAAE,KAAK,IAAI;AAAA,EACpC;AACA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,16 @@
1
+ export { xpath, exefile, exedir };
2
+ /** 执行文件路径,支持 pm2 等工具通过环境变量覆盖 */
3
+ declare const exefile: string;
4
+ /** 执行文件所在目录 */
5
+ declare const exedir: string;
6
+ /**
7
+ * 强大可靠的路径处理
8
+ * - 相对路径在后,绝对路径在前,最终都转换为绝对路径
9
+ * - 统一分隔符,方便比较路径
10
+ * - 自动处理 file:/// 协议、../裁切等
11
+ * @param targetPath - 目标路径(可以是相对路径或绝对路径)
12
+ * @param basePath - 基准路径,默认为 process.cwd()
13
+ * @param separator - 路径分隔符,默认为 '/'
14
+ */
15
+ declare function xpath(targetPath?: string, basePath?: string, separator?: '/' | '\\'): string;
16
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAC,CAAC;AAEhC,gCAAgC;AAChC,QAAA,MAAM,OAAO,QAA8E,CAAC;AAE5F,eAAe;AACf,QAAA,MAAM,MAAM,QAAmB,CAAC;AAEhC;;;;;;;;GAQG;AACH,iBAAS,KAAK,CAAC,UAAU,GAAE,MAAY,EAAE,QAAQ,GAAE,MAAe,EAAE,SAAS,GAAE,GAAG,GAAG,IAAU,GAAG,MAAM,CAmCvG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghini/xstart",
3
- "version": "26.1.5211744",
3
+ "version": "26.1.6175449",
4
4
  "type": "module",
5
5
  "author": "Ghini",
6
6
  "license": "MIT",
@@ -19,12 +19,19 @@
19
19
  ],
20
20
  "devDependencies": {
21
21
  "@types/node": "^25.0.3",
22
+ "@vitest/coverage-v8": "^4.0.16",
22
23
  "esbuild": "^0.27.2",
23
- "typescript": "^5.9.3"
24
+ "fast-check": "^4.5.3",
25
+ "typescript": "^5.9.3",
26
+ "vitest": "^4.0.16"
24
27
  },
25
28
  "scripts": {
26
29
  "build": "node ./build.js",
27
30
  "checkpub": "pnpm pack --dry-run",
28
- "pub": "pnpm build && pnpm publish --no-git-checks && git add . && git commit -m 'update' && git push"
31
+ "pub": "pnpm build && pnpm publish --no-git-checks && git add . && git commit -m 'update' && git push",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest",
34
+ "test:coverage": "vitest run --coverage",
35
+ "typecheck": "tsc --noEmit"
29
36
  }
30
37
  }
package/src/index.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // src/index.ts
2
- export * from './start.ts';
2
+ export * from './utils.ts';
package/src/utils.ts ADDED
@@ -0,0 +1,56 @@
1
+ // src/utils.ts
2
+ import {normalize, join, isAbsolute, sep, dirname} from 'node:path';
3
+
4
+ export {xpath, exefile, exedir};
5
+
6
+ /** 执行文件路径,支持 pm2 等工具通过环境变量覆盖 */
7
+ const exefile = process.env.KIT_EXEPATH || process.env.KIT_EXEFILE || process.argv[1] || '';
8
+
9
+ /** 执行文件所在目录 */
10
+ const exedir = dirname(exefile);
11
+
12
+ /**
13
+ * 强大可靠的路径处理
14
+ * - 相对路径在后,绝对路径在前,最终都转换为绝对路径
15
+ * - 统一分隔符,方便比较路径
16
+ * - 自动处理 file:/// 协议、../裁切等
17
+ * @param targetPath - 目标路径(可以是相对路径或绝对路径)
18
+ * @param basePath - 基准路径,默认为 process.cwd()
19
+ * @param separator - 路径分隔符,默认为 '/'
20
+ */
21
+ function xpath(targetPath: string = '.', basePath: string = exedir, separator: '/' | '\\' = '/'): string {
22
+ // 处理 file:/// 协议
23
+ const stripFileProtocol = (p: string): string => {
24
+ if (p.startsWith('file:///')) {
25
+ // Windows: file:///C:/path → C:/path (去掉8个字符)
26
+ // Unix: file:///path → /path (去掉7个字符)
27
+ return process.platform === 'win32' ? p.slice(8) : p.slice(7);
28
+ }
29
+ return p;
30
+ };
31
+
32
+ // 处理 basePath
33
+ let resolvedBase = stripFileProtocol(basePath);
34
+ if (!isAbsolute(resolvedBase)) {
35
+ resolvedBase = join(process.cwd(), resolvedBase);
36
+ }
37
+
38
+ // 处理 targetPath
39
+ let result: string;
40
+ const cleanTarget = stripFileProtocol(targetPath);
41
+
42
+ if (isAbsolute(cleanTarget)) {
43
+ result = normalize(cleanTarget);
44
+ } else {
45
+ result = normalize(join(resolvedBase, cleanTarget));
46
+ }
47
+
48
+ // 统一分隔符
49
+ if (separator === '/' && sep === '\\') {
50
+ return result.split(sep).join('/');
51
+ }
52
+ if (separator === '\\' && sep === '/') {
53
+ return result.split(sep).join('\\');
54
+ }
55
+ return result;
56
+ }
package/dist/start.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export { start };
2
- declare function start(): void;
3
- //# sourceMappingURL=start.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../src/start.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,CAAC;AAEjB,iBAAS,KAAK,IAAI,IAAI,CAErB"}
package/src/start.ts DELETED
@@ -1,6 +0,0 @@
1
- // start.ts
2
- export { start };
3
-
4
- function start(): void {
5
- console.log("Hello Ghini use ts");
6
- }