qingtui-h5package-sdk 0.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 轻推团队
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # qingtui-h5package-sdk
2
+
3
+
4
+ ## 安装
5
+
6
+ ```bash
7
+ npm install qingtui-h5package-sdk --save-dev
8
+ ```
9
+
10
+
11
+ ## 快速开始
12
+
13
+ 1. 在项目中安装打包OpenSDK
14
+
15
+ ```bash
16
+ npm install qingtui-h5package-sdk --save-dev
17
+ ```
18
+
19
+
20
+ 2. 在项目根目录下创建离线包资源配置文件 localresource.json
21
+
22
+ ```js
23
+ // localresource.json
24
+ {
25
+ "version": "版本号" // 可选,默认读取当前目录下package.json version 字段
26
+ }
27
+ ```
28
+
29
+ 3. 在 localresource.json 中添加 assets 配置,声明项目中静态资源(编译产物、静态资源皆可)与线上资源的映射关系。
30
+ 示例:
31
+
32
+ ```js
33
+ {
34
+ // key为应用的URL路径地址,值为本地文件或目录
35
+ "assets: {
36
+ // 目录
37
+ "https://www.example.com/myapp": "./dist"
38
+ },
39
+ }
40
+ ```
41
+
42
+ 4. 在 localresource.json 中添加 externalAssets 配置,声明应用中依赖的外部资源(如 react、 react-dom等)。
43
+
44
+ ```js
45
+ {
46
+ "externalAssets": [
47
+ "https://unpkg.com/react@16.7.0/umd/react.production.min.js"
48
+ ]
49
+ }
50
+ ```
51
+
52
+
53
+ ## 命令列表
54
+
55
+ ### pack
56
+ 本地离线包资源打包,在本地输出打包后的离线资源文件,文件格式为.zip。离线包ID自动在 localresource.json 文件中查找。
57
+ 用法:
58
+
59
+ ```bash
60
+ npx h5package pack
61
+ ```
62
+
63
+
64
+ ## 打包配置文件 Config
65
+
66
+ ```js
67
+ {
68
+ // 默认读取当前目录下package.version
69
+ version: string;
70
+ // 线上资源URL和本地文件、目录映射关系。key 为线上资源URL地址,值 为本地文件地址。支持 文件 和 目录
71
+ // 示例:
72
+ // {
73
+ // assets: {
74
+ // 键值为目录。将 ./dist 目录下的资源添加到 https://www.example.com/myapp 下
75
+ // "https://www.example.com/myapp": "./dist",
76
+ // 键值为文件。将 ./favorite.icon 添加到 https://www.example.com/myapp/favorite.ico
77
+ // "https://www.example.com/myapp/favorite.ico": "./favorite.ico",
78
+ // }
79
+ // }
80
+ assets: Record<string, string>;
81
+ // 依赖的外部URL列表。
82
+ // 应用中往往会依赖一些外部资源,如react、vue等外部公共库CDN资源。
83
+ // 添加到此配置后,打包脚本会自动通过HTTP GET请求从网络下载资源内容进行打包。
84
+ // 示例:
85
+ // {
86
+ // "externalAssets": [
87
+ // "https://unpkg.com/react@16.7.0/umd/react.production.min.js",
88
+ // "https://unpkg.com/vue@3.2.45/dist/vue.global.js",
89
+ // "https://unpkg.com/jquery@3.6.1/dist/jquery.js"
90
+ // ]
91
+ // }
92
+ externalAssets: string[];
93
+ }
94
+ ```
95
+
@@ -0,0 +1,23 @@
1
+ import archiver from 'archiver';
2
+ export declare const MAX_PATH_LENGTH = 88;
3
+ export declare function urlToPath(url: string): string;
4
+ export declare class PackagePacker {
5
+ readonly options: {
6
+ filename: string;
7
+ };
8
+ arch: archiver.Archiver;
9
+ file: string;
10
+ includeUrlSet: Set<string>;
11
+ private prom;
12
+ private output;
13
+ private bizData;
14
+ constructor(options: {
15
+ filename: string;
16
+ });
17
+ appendFile(url: string, fileOrDirectory: string): Promise<void>;
18
+ private _file;
19
+ appendDirectory(url: string, directory: string): Promise<void>;
20
+ appendUrl(url: string): Promise<void>;
21
+ appendBizData(bizData: Record<string, unknown>): Promise<void>;
22
+ finalize(): Promise<string>;
23
+ }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PackagePacker = exports.urlToPath = exports.MAX_PATH_LENGTH = void 0;
7
+ const archiver_1 = __importDefault(require("archiver"));
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const download_1 = __importDefault(require("download"));
11
+ const glob_1 = __importDefault(require("glob"));
12
+ // 文件路径最大长度是100,扣除"web_assets/"目录,实际最大长度是89;
13
+ exports.MAX_PATH_LENGTH = 88;
14
+ function urlToPath(url) {
15
+ let uri;
16
+ try {
17
+ uri = new URL(url);
18
+ }
19
+ catch (error) {
20
+ throw new Error(`无效的资源地址: ${url}`);
21
+ }
22
+ if (uri.protocol !== 'https:') {
23
+ throw new Error(`无效的资源地址: ${url}, 离线包只支持 https 协议`);
24
+ }
25
+ const host = uri.port ? `${uri.hostname}@${uri.port}` : uri.hostname;
26
+ let p = `${host}${uri.pathname}`;
27
+ if (p.endsWith('/')) {
28
+ p = p.slice(0, -1);
29
+ }
30
+ return p;
31
+ }
32
+ exports.urlToPath = urlToPath;
33
+ class PackagePacker {
34
+ constructor(options) {
35
+ this.options = options;
36
+ this.includeUrlSet = new Set();
37
+ this.bizData = {};
38
+ this.file = path_1.default.join(process.cwd(), options.filename);
39
+ this.output = fs_extra_1.default.createWriteStream(this.file);
40
+ this.arch = (0, archiver_1.default)('zip', { zlib: { level: 9 } });
41
+ this.prom = new Promise((r, c) => {
42
+ this.arch.on('error', (err) => {
43
+ console.error('打包失败', err);
44
+ c(err);
45
+ });
46
+ this.output.on('close', () => {
47
+ r();
48
+ });
49
+ });
50
+ this.arch.pipe(this.output);
51
+ }
52
+ async appendFile(url, fileOrDirectory) {
53
+ const p = path_1.default.resolve(fileOrDirectory);
54
+ const stat = await fs_extra_1.default.stat(p);
55
+ if (stat.isDirectory()) {
56
+ await this.appendDirectory(url, p);
57
+ }
58
+ else if (stat.isFile()) {
59
+ this._file(url, p);
60
+ }
61
+ else {
62
+ throw new Error(`无效的文件或目录: ${p}`);
63
+ }
64
+ }
65
+ _file(url, filename) {
66
+ const fullFilePath = urlToPath(url);
67
+ if (fullFilePath.length > exports.MAX_PATH_LENGTH) {
68
+ console.warn(`以下文件被忽略(文件路径长度>${exports.MAX_PATH_LENGTH}): ${path_1.default.relative(process.cwd(), filename)}`);
69
+ return;
70
+ }
71
+ this.arch.file(filename, { name: fullFilePath });
72
+ this.includeUrlSet.add(url);
73
+ }
74
+ async appendDirectory(url, directory) {
75
+ const prefixURL = new URL(url);
76
+ const files = glob_1.default.sync('**/*', { cwd: directory, nodir: true });
77
+ for (const file of files) {
78
+ const filename = path_1.default.join(directory, file);
79
+ const fullUrl = new URL(path_1.default.join(prefixURL.pathname, file), prefixURL.origin).toString();
80
+ this._file(fullUrl, filename);
81
+ }
82
+ }
83
+ async appendUrl(url) {
84
+ const content = await (0, download_1.default)(url);
85
+ this.arch.append(content, { name: urlToPath(url) });
86
+ }
87
+ async appendBizData(bizData) {
88
+ this.bizData = bizData;
89
+ }
90
+ async finalize() {
91
+ await this.arch.finalize();
92
+ return Promise.resolve(this.prom).then(() => this.file);
93
+ }
94
+ }
95
+ exports.PackagePacker = PackagePacker;
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const md5_file_1 = __importDefault(require("md5-file"));
11
+ const PackagePacker_1 = require("../PackagePacker");
12
+ function verifyVersion(version) {
13
+ const REGEXP = /^(\d{1,2})\.(\d{1,2})\.(\d{1,2})$/;
14
+ if (!REGEXP.test(version)) {
15
+ throw new Error('版本号格式不正确');
16
+ }
17
+ }
18
+ async function readPackageVersion() {
19
+ if (!fs_extra_1.default.existsSync('./package.json')) {
20
+ throw new Error('package.json is missing');
21
+ }
22
+ const config = await fs_extra_1.default.readJson('./package.json');
23
+ verifyVersion(config.version);
24
+ return config.version;
25
+ }
26
+ async function readResourceConfig(opts) {
27
+ if (!fs_extra_1.default.existsSync(opts.config)) {
28
+ throw new Error(`配置文件不存在: ${opts.config}`);
29
+ }
30
+ const config = await fs_extra_1.default.readJson(opts.config, {
31
+ throws: true,
32
+ });
33
+ if (!config.version) {
34
+ config.version = await readPackageVersion();
35
+ }
36
+ return config;
37
+ }
38
+ async function pack(opts) {
39
+ const { version, assets = {}, externalAssets = [] } = opts.config;
40
+ const assetKeys = Object.keys(assets);
41
+ const CACHE_DIR = path_1.default.join('node_modules', '.cache', 'h5packer');
42
+ const TEMP_ZIP_NAME = 'temp.zip';
43
+ const TEMP_ZIP_REL = path_1.default.join(CACHE_DIR, TEMP_ZIP_NAME);
44
+ await fs_extra_1.default.ensureDir(CACHE_DIR);
45
+ // ======================================================
46
+ // 第一轮打包 → node_modules/.cache/h5packer/temp.zip
47
+ // ======================================================
48
+ const firstPacker = new PackagePacker_1.PackagePacker({
49
+ filename: TEMP_ZIP_REL,
50
+ });
51
+ for (const assetKey of assetKeys) {
52
+ await firstPacker.appendFile(assetKey, assets[assetKey]);
53
+ }
54
+ for (const externalAsset of externalAssets) {
55
+ await firstPacker.appendUrl(externalAsset);
56
+ }
57
+ await firstPacker.finalize();
58
+ // -------- 计算 md5 --------
59
+ const firstZipBuffer = await fs_extra_1.default.readFile(TEMP_ZIP_REL);
60
+ const md5 = md5_file_1.default.sync(TEMP_ZIP_REL);
61
+ // ======================================================
62
+ // 第二轮打包 → 最终包
63
+ // ======================================================
64
+ const finalPacker = new PackagePacker_1.PackagePacker({
65
+ filename: opts.filename,
66
+ });
67
+ finalPacker.arch.append(firstZipBuffer, { name: 'app.zip' });
68
+ finalPacker.arch.append(JSON.stringify({
69
+ includeUrls: Array.from(firstPacker.includeUrlSet),
70
+ bizData: {
71
+ version,
72
+ md5,
73
+ },
74
+ }), { name: 'manifest.json' });
75
+ await finalPacker.finalize();
76
+ try {
77
+ fs_extra_1.default.removeSync(CACHE_DIR);
78
+ }
79
+ finally {
80
+ console.log('离线包已生成:', finalPacker.file);
81
+ }
82
+ }
83
+ commander_1.program
84
+ .summary('轻推H5离线包SDK')
85
+ .version('1.0.0', '-v, --version', 'output the current version')
86
+ .addHelpCommand('help', 'display help for command');
87
+ commander_1.program
88
+ .command('pack')
89
+ .description('打包生成离线包')
90
+ .option('--config <config>', '打包配置', './localresource.json')
91
+ .action(async function (opts) {
92
+ const config = await readResourceConfig(opts);
93
+ await pack({
94
+ filename: `h5package.zip`,
95
+ config,
96
+ });
97
+ });
98
+ commander_1.program.parseAsync(process.argv);
@@ -0,0 +1 @@
1
+ export * from './PackagePacker';
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./PackagePacker"), exports);
@@ -0,0 +1,5 @@
1
+ export interface PackerConfig {
2
+ version?: string;
3
+ assets: Record<string, string>;
4
+ externalAssets?: string[];
5
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "qingtui-h5package-sdk",
3
+ "version": "0.0.1",
4
+ "description": "轻推H5离线包工具",
5
+ "main": "dist/index.js",
6
+ "directories": {
7
+ "dist": "dist"
8
+ },
9
+ "bin": {
10
+ "h5package": "./dist/cli/h5package.js"
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "scripts": {
16
+ "start": "tsc --watch",
17
+ "build": "rm -rf ./dist && tsc",
18
+ "lint": "eslint --ext .ts",
19
+ "lint:fix": "eslint --fix --ext .ts",
20
+ "commit": "git cz",
21
+ "prepublishOnly": "npm run build"
22
+ },
23
+ "keywords": [
24
+ "qingtui"
25
+ ],
26
+ "author": "paseo",
27
+ "license": "MIT",
28
+ "devDependencies": {
29
+ "@changesets/cli": "^2.26.0",
30
+ "@tsconfig/node12": "^1.0.11",
31
+ "@tsconfig/node14": "^1.0.3",
32
+ "@types/ali-oss": "^6.16.6",
33
+ "@types/archiver": "^5.3.1",
34
+ "@types/download": "^8.0.1",
35
+ "@types/fs-extra": "^9.0.13",
36
+ "@types/jest": "^29.2.3",
37
+ "@types/node": "^18.11.9",
38
+ "@types/uuid": "^8.3.4",
39
+ "@typescript-eslint/eslint-plugin": "^5.44.0",
40
+ "@typescript-eslint/parser": "^5.44.0",
41
+ "cz-conventional-changelog": "^3.3.0",
42
+ "decompress": "^4.2.1",
43
+ "eslint": "^8.28.0",
44
+ "jest": "^29.3.1",
45
+ "ts-jest": "^29.0.3",
46
+ "typescript": "^4.9.3"
47
+ },
48
+ "dependencies": {
49
+ "archiver": "^5.3.1",
50
+ "commander": "^9.4.1",
51
+ "download": "^8.0.0",
52
+ "fs-extra": "^10.1.0",
53
+ "glob": "^8.0.3",
54
+ "uri-js": "^4.4.1",
55
+ "uuid": "^9.0.0"
56
+ },
57
+ "engines": {
58
+ "node": ">=14.0.0"
59
+ },
60
+ "publishConfig": {
61
+ "registry": "https://registry.npmjs.org/"
62
+ }
63
+ }