From 70ebb40e4f00291bc0135acbb8489c9c7cb77a1e Mon Sep 17 00:00:00 2001 From: AI Engineer Date: Thu, 4 Jun 2026 19:21:45 +0800 Subject: [PATCH] chore: version 1.0.3 --- .gitignore | 6 +++ README.md | 108 +++++++++++++++++++++---------------- dist/loader.js | 32 ++++++----- dist/loader.min.js | 2 +- package-lock.json | 64 +++++++++++++++++++++- package.json | 4 +- scripts/update-versions.js | 34 ++++++++---- src/index.js | 38 ++++++------- 8 files changed, 193 insertions(+), 95 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..04cb646 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +test-results/ +playwright-report/ +.DS_Store +*.log diff --git a/README.md b/README.md index 31d05f1..e542e87 100644 --- a/README.md +++ b/README.md @@ -6,86 +6,102 @@ ## 一、 集成方式 -直接引用打包好的 `loader.min.js`,它会自动根据当前的加载源适配 CDN 镜像。 - -### 1. 自动注入 Importmap -在 HTML 中引入 loader 并声明需要加载的模块: +### 1. 引入 Loader (任选一个 CDN 镜像复制即用) +直接引用打包好的 `loader.min.js`,它会自动根据当前的加载源适配 CDN 镜像: ```html - - + + + + + + + + + + + + + +``` + +### 2. 自动注入 Importmap (支持做减法) +在 HTML 中引入任意一个镜像的 loader,并声明加载所有已有项目模块(在实际生产中可按需做减法删除不需要的项): + +```html + ``` -### 2. 原生朴素写法 (手动定义) -如果您不想使用 `Loader.load` 的自动注入,也可以手动定义 `importmap`: +### 3. 原生朴素写法 (手动定义) +如果您不想使用 `Loader.load` 的自动注入,也可以手动定义 `importmap`(同样可以通过直接删除无用项来做减法): ```html ``` --- -## 二、 核心特性 +## 二、 本地开发与调试重定向 -1. **多镜像自适应**:自动识别 `loader.min.js` 的来源 URL,并自动将其他模块指向相同的 CDN 镜像。支持以下镜像: - * `cdn.jsdelivr.net` (默认) - * `esm.sh` - * `unpkg.com` - * `unpkg.zhimg.com` (中国加速) - * `npm.elemecdn.com` (中国加速) -2. **默认版本管理**:内置了各组件的稳定版本号,无需手动记忆。 -3. **零配置**:只需一行代码即可完成整个生态链的模块映射。 +在本地开发时,如果需要将某些模块重定向到您本地的开发服务器(例如 Vite 本地服务),**只需在 `Loader.load` 对应的模块中传入本地 URL 或相对路径**。`Loader` 内部会自动识别并将其作为自定义地址使用: + +```javascript +Loader.load('state:1.0.12', + 'bootstrap:http://localhost:5173/src/index.js', // 调试重定向:指向本地 Vite 源码服务 + 'base:1.0.7','datatable:1.0.7','kanban:1.0.1','mindmap:1.0.1','chart:1.0.1','editor:1.0.1'); +``` --- -## 三、 本地开发与调试重定向 +## 三、 核心特性 -在本地开发时,如果需要将某些模块重定向到本地开发服务器进行调试,可以通过在浏览器控制台设置 `localStorage` 来实现,**无需修改任何 HTML 业务代码**。 - -### 1. 启用本地调试重定向 -比如将 `bootstrap` 和 `state` 模块重定向到您本地运行的 Vite 开发服务器: -```javascript -// 重定向 bootstrap 模块 -localStorage.setItem('dev:@apigo.cc/bootstrap', 'http://localhost:5173/src/index.js'); - -// 重定向 state 模块 -localStorage.setItem('dev:@apigo.cc/state', 'http://localhost:5174/src/index.js'); -``` - -### 2. 取消本地调试重定向 -调试完成后,清除 `localStorage` 对应的键即可恢复使用 CDN 版本: -```javascript -// 恢复为 CDN 版本 -localStorage.removeItem('dev:@apigo.cc/bootstrap'); -localStorage.removeItem('dev:@apigo.cc/state'); -``` +1. **多镜像自适应**:自动识别 `loader.min.js` 的来源 URL,并自动将其他模块指向相同的 CDN 镜像。 +2. **默认版本管理**:内置了各组件的稳定版本号,无需手动记忆。 +3. **零配置**:只需一行代码即可完成整个生态链的模块映射。 --- diff --git a/dist/loader.js b/dist/loader.js index 91f262d..541e9c5 100644 --- a/dist/loader.js +++ b/dist/loader.js @@ -1,15 +1,15 @@ (function(exports) { "use strict"; const DEFAULT_VERSIONS = { - "state": "1.0.11", - "bootstrap": "1.0.3", - "base": "1.0.7", - "datatable": "1.0.6", - "kanban": "1.0.0", - "mindmap": "1.0.0", - "chart": "1.0.0", - "editor": "1.0.0", - "loader": "1.0.2" + "state": "1.0.12", + "bootstrap": "1.0.4", + "base": "1.0.8", + "datatable": "1.0.7", + "kanban": "1.0.1", + "mindmap": "1.0.1", + "chart": "1.0.1", + "editor": "1.0.1", + "loader": "1.0.3" }; const Loader = { load: (...pkgs) => { @@ -32,17 +32,15 @@ } const importMap = { imports: {} }; pkgs.forEach((pkg) => { - let [name, version] = pkg.split(":"); + const colonIdx = pkg.indexOf(":"); + const name = colonIdx === -1 ? pkg : pkg.slice(0, colonIdx); + let version = colonIdx === -1 ? "" : pkg.slice(colonIdx + 1); const key = name.toLowerCase(); const fullKey = `@apigo.cc/${key}`; let url = ""; - try { - if (typeof localStorage !== "undefined") { - url = localStorage.getItem(`dev:${fullKey}`) || ""; - } - } catch (e) { - } - if (!url) { + if (version && /^(https?:|\.|\/)/.test(version)) { + url = version; + } else { version = version || DEFAULT_VERSIONS[key] || "latest"; url = tpl.replace(/{project}/g, key).replace(/{tag}/g, version); } diff --git a/dist/loader.min.js b/dist/loader.min.js index 1912c09..f01c760 100644 --- a/dist/loader.min.js +++ b/dist/loader.min.js @@ -1 +1 @@ -!function(t){"use strict";const e={state:"1.0.11",bootstrap:"1.0.3",base:"1.0.7",datatable:"1.0.6",kanban:"1.0.0",mindmap:"1.0.0",chart:"1.0.0",editor:"1.0.0",loader:"1.0.2"},c={load:(...t)=>{if("undefined"==typeof document)return;const c=document.currentScript,o=c?c.src:"";let n="https://cdn.jsdelivr.net/npm/@apigo.cc/{project}@{tag}/dist/{project}.min.js";o&&(o.includes("esm.sh")?n="https://esm.sh/@apigo.cc/{project}@{tag}/dist/{project}.min.js":o.includes("unpkg.com")?n="https://unpkg.com/@apigo.cc/{project}@{tag}/dist/{project}.min.js":o.includes("unpkg.zhimg.com")?n="https://unpkg.zhimg.com/@apigo.cc/{project}@{tag}/dist/{project}.min.js":o.includes("npm.elemecdn.com")?n="https://npm.elemecdn.com/@apigo.cc/{project}@{tag}/dist/{project}.min.js":o.includes("jsdelivr.net")&&(n="https://cdn.jsdelivr.net/npm/@apigo.cc/{project}@{tag}/dist/{project}.min.js"));const s={imports:{}};t.forEach(t=>{let[c,o]=t.split(":");const i=c.toLowerCase(),p=`@apigo.cc/${i}`;let a="";try{"undefined"!=typeof localStorage&&(a=localStorage.getItem(`dev:${p}`)||"")}catch(t){}a||(o=o||e[i]||"latest",a=n.replace(/{project}/g,i).replace(/{tag}/g,o)),s.imports[p]=a});const i=document.createElement("script");i.type="importmap",i.textContent=JSON.stringify(s),c?c.parentNode.insertBefore(i,c):document.head.appendChild(i)}};globalThis.Loader=c,t.Loader=c,t.default=c,Object.defineProperties(t,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}(this.Loader=this.Loader||{}); +!function(t){"use strict";const e={state:"1.0.12",bootstrap:"1.0.4",base:"1.0.8",datatable:"1.0.7",kanban:"1.0.1",mindmap:"1.0.1",chart:"1.0.1",editor:"1.0.1",loader:"1.0.3"},c={load:(...t)=>{if("undefined"==typeof document)return;const c=document.currentScript,o=c?c.src:"";let s="https://cdn.jsdelivr.net/npm/@apigo.cc/{project}@{tag}/dist/{project}.min.js";o&&(o.includes("esm.sh")?s="https://esm.sh/@apigo.cc/{project}@{tag}/dist/{project}.min.js":o.includes("unpkg.com")?s="https://unpkg.com/@apigo.cc/{project}@{tag}/dist/{project}.min.js":o.includes("unpkg.zhimg.com")?s="https://unpkg.zhimg.com/@apigo.cc/{project}@{tag}/dist/{project}.min.js":o.includes("npm.elemecdn.com")?s="https://npm.elemecdn.com/@apigo.cc/{project}@{tag}/dist/{project}.min.js":o.includes("jsdelivr.net")&&(s="https://cdn.jsdelivr.net/npm/@apigo.cc/{project}@{tag}/dist/{project}.min.js"));const n={imports:{}};t.forEach(t=>{const c=t.indexOf(":"),o=-1===c?t:t.slice(0,c);let i=-1===c?"":t.slice(c+1);const p=o.toLowerCase(),r=`@apigo.cc/${p}`;let a="";i&&/^(https?:|\.|\/)/.test(i)?a=i:(i=i||e[p]||"latest",a=s.replace(/{project}/g,p).replace(/{tag}/g,i)),n.imports[r]=a});const i=document.createElement("script");i.type="importmap",i.textContent=JSON.stringify(n),c?c.parentNode.insertBefore(i,c):document.head.appendChild(i)}};globalThis.Loader=c,t.Loader=c,t.default=c,Object.defineProperties(t,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}(this.Loader=this.Loader||{}); diff --git a/package-lock.json b/package-lock.json index 21f2c6a..02199de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,15 @@ { "name": "@apigo.cc/loader", - "version": "1.0.0", + "version": "1.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@apigo.cc/loader", - "version": "1.0.0", + "version": "1.0.2", "license": "ISC", "devDependencies": { + "@playwright/test": "^1.60.0", "@rollup/plugin-terser": "^1.0.0", "terser": "^5.48.0", "vite": "^5.4.21" @@ -427,6 +428,21 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@playwright/test": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.60.0.tgz", + "integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==", + "dev": true, + "dependencies": { + "playwright": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@rollup/plugin-terser": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz", @@ -1153,6 +1169,50 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, + "node_modules/playwright": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", + "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", + "dev": true, + "dependencies": { + "playwright-core": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", + "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/postcss": { "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", diff --git a/package.json b/package.json index b8650e8..7c4d343 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@apigo.cc/loader", - "version": "1.0.2", + "version": "1.0.3", "type": "module", "main": "dist/loader.js", "module": "dist/loader.js", @@ -11,6 +11,7 @@ "dev": "vite", "prebuild": "node scripts/update-versions.js", "build": "vite build", + "test": "playwright test", "pub": "node scripts/publish.js" }, "keywords": [], @@ -18,6 +19,7 @@ "license": "ISC", "description": "", "devDependencies": { + "@playwright/test": "^1.60.0", "@rollup/plugin-terser": "^1.0.0", "terser": "^5.48.0", "vite": "^5.4.21" diff --git a/scripts/update-versions.js b/scripts/update-versions.js index e3dfc95..f8547ef 100644 --- a/scripts/update-versions.js +++ b/scripts/update-versions.js @@ -78,24 +78,40 @@ if (fs.existsSync(readmePath)) { readmeContent = readmeContent.replace( /Loader\.load\(([^)]*)\)/g, (match, p1) => { - // 提取被单引号/双引号包裹的各个 package 项 - const items = p1.split(',').map(item => item.trim().replace(/['"]/g, '')); + // 按照逗号拆分,但保留各行原样(包含可能的换行和注释) + const items = p1.split(','); const newItems = items.map(item => { - const [name] = item.split(':'); + const trimmed = item.trim(); + if (trimmed.startsWith('//') || !trimmed) { + return item; + } + + // 提取清除引号后的内容进行路径和版本判断 + const cleanItem = trimmed.replace(/['"]/g, ''); + const colonIdx = cleanItem.indexOf(':'); + const name = colonIdx === -1 ? cleanItem : cleanItem.slice(0, colonIdx); + const val = colonIdx === -1 ? '' : cleanItem.slice(colonIdx + 1); + + // 如果值里包含 http://, https://, ./, ../, / 说明是调试重定向路径,原样保留 + if (val && (/^(https?:|\.|\/)/.test(val))) { + return item; + } + const key = name.toLowerCase(); if (versions[key]) { - return `'${key}:${versions[key]}'`; + // 尽量保留原有的包裹引号格式 + const quote = item.includes('"') ? '"' : "'"; + return `${quote}${key}:${versions[key]}${quote}`; } - return `'${item}'`; + return item; }); - return `Loader.load(${newItems.join(', ')})`; + return `Loader.load(${newItems.join(',')})`; } ); - // 替换 cdn 链接里的 loader 版本 - // 如 + // 替换 cdn 链接及所有地方的 loader 版本 readmeContent = readmeContent.replace( - /(\/\/cdn\.jsdelivr\.net\/npm\/@apigo\.cc\/loader@)[0-9.]+(\/dist\/loader\.min\.js)/g, + /(@apigo\.cc\/loader@)[0-9.]+(\/dist\/loader)/g, `$1${loaderVersion}$2` ); diff --git a/src/index.js b/src/index.js index a316df1..9cdb695 100644 --- a/src/index.js +++ b/src/index.js @@ -1,18 +1,18 @@ /** - * @web/loader + * @apigo.cc/loader * 极简模块调度中心 */ const DEFAULT_VERSIONS = { - 'state': '1.0.11', - 'bootstrap': '1.0.3', - 'base': '1.0.7', - 'datatable': '1.0.6', - 'kanban': '1.0.0', - 'mindmap': '1.0.0', - 'chart': '1.0.0', - 'editor': '1.0.0', - 'loader': '1.0.2' + 'state': '1.0.12', + 'bootstrap': '1.0.4', + 'base': '1.0.8', + 'datatable': '1.0.7', + 'kanban': '1.0.1', + 'mindmap': '1.0.1', + 'chart': '1.0.1', + 'editor': '1.0.1', + 'loader': '1.0.3' }; const Loader = { @@ -43,19 +43,19 @@ const Loader = { const importMap = { imports: {} }; pkgs.forEach(pkg => { - let [name, version] = pkg.split(':'); + // 支持格式 'name' 或 'name:version' 或 'name:path' (如 'bootstrap:http://localhost:5173/src/index.js') + const colonIdx = pkg.indexOf(':'); + const name = colonIdx === -1 ? pkg : pkg.slice(0, colonIdx); + let version = colonIdx === -1 ? '' : pkg.slice(colonIdx + 1); + const key = name.toLowerCase(); const fullKey = `@apigo.cc/${key}`; let url = ''; - // 优先检查本地 localStorage 重定向配置,方便开发调试 - try { - if (typeof localStorage !== 'undefined') { - url = localStorage.getItem(`dev:${fullKey}`) || ''; - } - } catch (e) {} - - if (!url) { + // 如果 version 包含 http://, https://, ./, ../, / 说明是自定义路径,直接作为调试或重定向地址 + if (version && (/^(https?:|\.|\/)/.test(version))) { + url = version; + } else { version = version || DEFAULT_VERSIONS[key] || 'latest'; url = tpl.replace(/{project}/g, key).replace(/{tag}/g, version); }