Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
731fd0cfca | ||
|
|
c58329c4c9 | ||
|
|
ee74709efc |
99
README.md
99
README.md
@ -1,40 +1,52 @@
|
|||||||
# @web/loader 模块调度中心
|
# @apigo.cc/loader 模块调度中心
|
||||||
|
|
||||||
`@web/loader` 是整个 `@web` 体系的入口调度中心。它负责根据当前环境自动生成 `importmap`,并管理各模块的版本。
|
`@apigo.cc/loader` 是整个 `@apigo.cc` 体系的入口调度中心。它负责根据当前环境自动生成 `importmap`,并管理各模块的版本。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 一、 集成方式
|
## 一、 集成方式
|
||||||
|
|
||||||
### 方式一:内联引导 (推荐 - 无额外请求)
|
直接引用打包好的 `loader.min.js`,它会自动根据当前的加载源适配 CDN 镜像。
|
||||||
在 HTML 的 `<head>` 顶部加入以下代码。这种方式不需要额外请求 `loader.min.js` 文件。
|
|
||||||
|
### 1. 自动注入 Importmap
|
||||||
|
在 HTML 中引入 loader 并声明需要加载的模块:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
|
<!-- 引入 loader -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@apigo.cc/loader@1.0.2/dist/loader.min.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function __load(tpl, ...pkgs) {
|
// 定义需要使用的模块,自动生成 importmap
|
||||||
const DEFAULT_VERSIONS = { state: 'v1.0.11', bootstrap: 'v1.0.1', base: 'v1.0.7' };
|
// 支持格式: 'name' 或 'name:version'
|
||||||
const importMap = { imports: {} };
|
// 也可以使用标签模式: 'name:latest', 'name:beta' 等
|
||||||
pkgs.forEach(pkg => {
|
Loader.load('state:1.0.11', 'bootstrap:1.0.3', 'base:1.0.7');
|
||||||
const [name, version] = pkg.split(':');
|
</script>
|
||||||
const v = version || DEFAULT_VERSIONS[name] || 'main';
|
|
||||||
importMap.imports[`@web/${name}`] = tpl.replace(/{project}/g, name).replace(/{tag}/g, v);
|
<!-- 随后即可使用原生 import -->
|
||||||
});
|
<script type="module">
|
||||||
const s = document.createElement('script');
|
import { $ } from '@apigo.cc/state';
|
||||||
s.type = 'importmap';
|
import { Bootstrap } from '@apigo.cc/bootstrap';
|
||||||
s.textContent = JSON.stringify(importMap);
|
|
||||||
document.currentScript.parentNode.insertBefore(s, document.currentScript);
|
console.log('Ready');
|
||||||
})('https://apigo.cc/web/{project}/raw/tag/{tag}/dist/{project}.js', 'state', 'bootstrap', 'base');
|
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
### 方式二:引用外部 Loader
|
### 2. 原生朴素写法 (手动定义)
|
||||||
直接引用打包好的 `loader.min.js`。它会自动根据当前的 URL 模板进行调度。
|
如果您不想使用 `Loader.load` 的自动注入,也可以手动定义 `importmap`:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<script src="https://apigo.cc/web/loader/raw/tag/v1.0.0/dist/loader.min.js"></script>
|
<script type="importmap">
|
||||||
<script>
|
{
|
||||||
// 无需版本号,自动使用内置的默认版本
|
"imports": {
|
||||||
Loader.load('state', 'bootstrap', 'base');
|
"@apigo.cc/state": "https://cdn.jsdelivr.net/npm/@apigo.cc/state@1.0.11/dist/state.min.js",
|
||||||
|
"@apigo.cc/bootstrap": "https://cdn.jsdelivr.net/npm/@apigo.cc/bootstrap@1.0.3/dist/bootstrap.min.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
import { $ } from '@apigo.cc/state';
|
||||||
|
// ... 业务代码
|
||||||
</script>
|
</script>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -42,17 +54,48 @@
|
|||||||
|
|
||||||
## 二、 核心特性
|
## 二、 核心特性
|
||||||
|
|
||||||
1. **智能模板 (Smart Templating)**:自动根据 `loader.js` 的加载路径推断其他组件的下载路径。
|
1. **多镜像自适应**:自动识别 `loader.min.js` 的来源 URL,并自动将其他模块指向相同的 CDN 镜像。支持以下镜像:
|
||||||
2. **默认版本 (Default Versioning)**:构建时内置了 `@web` 家族各成员的推荐稳定版本,解决“版本选择困难症”。
|
* `cdn.jsdelivr.net` (默认)
|
||||||
3. **按需加载 (On-demand)**:在页面头部一次性声明所需模块,避免加载无关资源。
|
* `esm.sh`
|
||||||
|
* `unpkg.com`
|
||||||
|
* `unpkg.zhimg.com` (中国加速)
|
||||||
|
* `npm.elemecdn.com` (中国加速)
|
||||||
|
2. **默认版本管理**:内置了各组件的稳定版本号,无需手动记忆。
|
||||||
|
3. **零配置**:只需一行代码即可完成整个生态链的模块映射。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 三、 支持模块清单
|
## 三、 本地开发与调试重定向
|
||||||
|
|
||||||
|
在本地开发时,如果需要将某些模块重定向到本地开发服务器进行调试,可以通过在浏览器控制台设置 `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');
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、 支持模块清单
|
||||||
|
所有模块均发布在 `@apigo.cc` 组织下:
|
||||||
* `state` (原子状态机)
|
* `state` (原子状态机)
|
||||||
* `bootstrap` (UI 引擎)
|
* `bootstrap` (UI 引擎)
|
||||||
* `base` (常用控件库)
|
* `base` (常用控件库)
|
||||||
* `dataTable` (高性能表格)
|
* `datatable` (高性能表格)
|
||||||
* `kanban` (看板)
|
* `kanban` (看板)
|
||||||
* `mindmap` (思维导图)
|
* `mindmap` (思维导图)
|
||||||
* `chart` (图表)
|
* `chart` (图表)
|
||||||
|
* `editor` (代码编辑器)
|
||||||
|
|||||||
59
dist/loader.js
vendored
59
dist/loader.js
vendored
@ -1,41 +1,52 @@
|
|||||||
(function(exports) {
|
(function(exports) {
|
||||||
"use strict";
|
"use strict";
|
||||||
const DEFAULT_VERSIONS = {
|
const DEFAULT_VERSIONS = {
|
||||||
"state": "v1.0.11",
|
"state": "1.0.11",
|
||||||
"bootstrap": "v1.0.1",
|
"bootstrap": "1.0.3",
|
||||||
"base": "v1.0.7",
|
"base": "1.0.7",
|
||||||
"dataTable": "v1.0.6",
|
"datatable": "1.0.6",
|
||||||
"kanban": "v1.0.0",
|
"kanban": "1.0.0",
|
||||||
"mindmap": "v1.0.0",
|
"mindmap": "1.0.0",
|
||||||
"chart": "v1.0.0"
|
"chart": "1.0.0",
|
||||||
|
"editor": "1.0.0",
|
||||||
|
"loader": "1.0.2"
|
||||||
};
|
};
|
||||||
const Loader = {
|
const Loader = {
|
||||||
/**
|
|
||||||
* 加载指定模块并注入 importmap
|
|
||||||
* @param {...string} pkgs 模块名格式: 'name' 或 'name:version'
|
|
||||||
*/
|
|
||||||
load: (...pkgs) => {
|
load: (...pkgs) => {
|
||||||
if (typeof document === "undefined") return;
|
if (typeof document === "undefined") return;
|
||||||
const currentScript = document.currentScript;
|
const currentScript = document.currentScript;
|
||||||
const currentUrl = currentScript ? currentScript.src : "";
|
const currentUrl = currentScript ? currentScript.src : "";
|
||||||
let tpl = "https://apigo.cc/web/{project}/raw/tag/{tag}/dist/{project}.js";
|
let tpl = "https://cdn.jsdelivr.net/npm/@apigo.cc/{project}@{tag}/dist/{project}.min.js";
|
||||||
if (currentUrl.includes("/loader/")) {
|
if (currentUrl) {
|
||||||
const match = currentUrl.match(/(.*\/web\/)loader\/raw\/tag\/([^\/]+)\//);
|
if (currentUrl.includes("esm.sh")) {
|
||||||
if (match) {
|
tpl = "https://esm.sh/@apigo.cc/{project}@{tag}/dist/{project}.min.js";
|
||||||
const baseUrl = match[1];
|
} else if (currentUrl.includes("unpkg.com")) {
|
||||||
const loaderTag = match[2];
|
tpl = "https://unpkg.com/@apigo.cc/{project}@{tag}/dist/{project}.min.js";
|
||||||
tpl = `${baseUrl}{project}/raw/tag/{tag}/dist/{project}.js`;
|
} else if (currentUrl.includes("unpkg.zhimg.com")) {
|
||||||
Object.keys(DEFAULT_VERSIONS).forEach((k) => {
|
tpl = "https://unpkg.zhimg.com/@apigo.cc/{project}@{tag}/dist/{project}.min.js";
|
||||||
if (DEFAULT_VERSIONS[k] === "v1.0.0") DEFAULT_VERSIONS[k] = loaderTag;
|
} else if (currentUrl.includes("npm.elemecdn.com")) {
|
||||||
});
|
tpl = "https://npm.elemecdn.com/@apigo.cc/{project}@{tag}/dist/{project}.min.js";
|
||||||
|
} else if (currentUrl.includes("jsdelivr.net")) {
|
||||||
|
tpl = "https://cdn.jsdelivr.net/npm/@apigo.cc/{project}@{tag}/dist/{project}.min.js";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const importMap = { imports: {} };
|
const importMap = { imports: {} };
|
||||||
pkgs.forEach((pkg) => {
|
pkgs.forEach((pkg) => {
|
||||||
let [name, version] = pkg.split(":");
|
let [name, version] = pkg.split(":");
|
||||||
version = version || DEFAULT_VERSIONS[name] || "main";
|
const key = name.toLowerCase();
|
||||||
const url = tpl.replace(/{project}/g, name).replace(/{tag}/g, version);
|
const fullKey = `@apigo.cc/${key}`;
|
||||||
importMap.imports[`@web/${name}`] = url;
|
let url = "";
|
||||||
|
try {
|
||||||
|
if (typeof localStorage !== "undefined") {
|
||||||
|
url = localStorage.getItem(`dev:${fullKey}`) || "";
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
if (!url) {
|
||||||
|
version = version || DEFAULT_VERSIONS[key] || "latest";
|
||||||
|
url = tpl.replace(/{project}/g, key).replace(/{tag}/g, version);
|
||||||
|
}
|
||||||
|
importMap.imports[fullKey] = url;
|
||||||
});
|
});
|
||||||
const script = document.createElement("script");
|
const script = document.createElement("script");
|
||||||
script.type = "importmap";
|
script.type = "importmap";
|
||||||
|
|||||||
2
dist/loader.min.js
vendored
2
dist/loader.min.js
vendored
@ -1 +1 @@
|
|||||||
!function(t){"use strict";const e={state:"v1.0.11",bootstrap:"v1.0.1",base:"v1.0.7",dataTable:"v1.0.6",kanban:"v1.0.0",mindmap:"v1.0.0",chart:"v1.0.0"},a={load:(...t)=>{if("undefined"==typeof document)return;const a=document.currentScript,o=a?a.src:"";let r="https://apigo.cc/web/{project}/raw/tag/{tag}/dist/{project}.js";if(o.includes("/loader/")){const t=o.match(/(.*\/web\/)loader\/raw\/tag\/([^\/]+)\//);if(t){const a=t[1],o=t[2];r=`${a}{project}/raw/tag/{tag}/dist/{project}.js`,Object.keys(e).forEach(t=>{"v1.0.0"===e[t]&&(e[t]=o)})}}const c={imports:{}};t.forEach(t=>{let[a,o]=t.split(":");o=o||e[a]||"main";const n=r.replace(/{project}/g,a).replace(/{tag}/g,o);c.imports[`@web/${a}`]=n});const n=document.createElement("script");n.type="importmap",n.textContent=JSON.stringify(c),a?a.parentNode.insertBefore(n,a):document.head.appendChild(n)}};globalThis.Loader=a,t.Loader=a,t.default=a,Object.defineProperties(t,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}(this.Loader=this.Loader||{});
|
!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||{});
|
||||||
|
|||||||
2
node_modules/.package-lock.json
generated
vendored
2
node_modules/.package-lock.json
generated
vendored
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "loader",
|
"name": "@apigo.cc/loader",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "loader",
|
"name": "@apigo.cc/loader",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "loader",
|
"name": "@apigo.cc/loader",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
13
package.json
13
package.json
@ -1,10 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "loader",
|
"name": "@apigo.cc/loader",
|
||||||
"version": "1.0.0",
|
"version": "1.0.2",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"main": "dist/loader.js",
|
||||||
|
"module": "dist/loader.js",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build"
|
"prebuild": "node scripts/update-versions.js",
|
||||||
|
"build": "vite build",
|
||||||
|
"pub": "node scripts/publish.js"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|||||||
48
scripts/publish.js
Normal file
48
scripts/publish.js
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { execSync } from 'child_process';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 获取最新 tag
|
||||||
|
let tag;
|
||||||
|
try {
|
||||||
|
tag = execSync('git describe --tags --abbrev=0', { encoding: 'utf8' }).trim();
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error('Failed to find git tags. Please make sure the repository has tags (e.g., v1.0.0) before publishing.');
|
||||||
|
}
|
||||||
|
// 去掉 v 前缀
|
||||||
|
const version = tag.startsWith('v') ? tag.slice(1) : tag;
|
||||||
|
|
||||||
|
console.log(`Latest git tag: ${tag}, Version to publish: ${version}`);
|
||||||
|
|
||||||
|
// 2. 读取并更新 package.json
|
||||||
|
const pkgPath = path.join(__dirname, '../package.json');
|
||||||
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
||||||
|
|
||||||
|
// 保持原有名称(如果已经带有 @apigo.cc/ 前缀)或替换前缀
|
||||||
|
if (!pkg.name.startsWith('@apigo.cc/')) {
|
||||||
|
const baseName = pkg.name.includes('/') ? pkg.name.split('/')[1] : pkg.name;
|
||||||
|
pkg.name = `@apigo.cc/${baseName}`;
|
||||||
|
}
|
||||||
|
pkg.version = version;
|
||||||
|
|
||||||
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
||||||
|
console.log(`Updated package.json: name=${pkg.name}, version=${pkg.version}`);
|
||||||
|
|
||||||
|
// 3. 构建
|
||||||
|
console.log('Running build...');
|
||||||
|
execSync('npm run build', { stdio: 'inherit', cwd: path.join(__dirname, '..') });
|
||||||
|
|
||||||
|
// 4. 发布
|
||||||
|
console.log('Publishing to npm...');
|
||||||
|
const args = process.argv.slice(2).join(' ');
|
||||||
|
execSync(`npm publish --access public ${args}`, { stdio: 'inherit', cwd: path.join(__dirname, '..') });
|
||||||
|
|
||||||
|
console.log('Publish successful!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Publish failed:', error.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
115
scripts/update-versions.js
Normal file
115
scripts/update-versions.js
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const parentDir = path.join(__dirname, '../..');
|
||||||
|
|
||||||
|
// 默认版本,如果没扫到就用这个
|
||||||
|
const versions = {
|
||||||
|
'state': '1.0.11',
|
||||||
|
'bootstrap': '1.0.0',
|
||||||
|
'base': '1.0.7',
|
||||||
|
'datatable': '1.0.6',
|
||||||
|
'kanban': '1.0.0',
|
||||||
|
'mindmap': '1.0.0',
|
||||||
|
'chart': '1.0.0',
|
||||||
|
'editor': '1.0.0'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1. 扫描同级目录获取最新版本号
|
||||||
|
try {
|
||||||
|
const dirs = fs.readdirSync(parentDir);
|
||||||
|
dirs.forEach(dir => {
|
||||||
|
const pkgPath = path.join(parentDir, dir, 'package.json');
|
||||||
|
if (fs.existsSync(pkgPath)) {
|
||||||
|
try {
|
||||||
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
||||||
|
if (pkg.name && pkg.name.startsWith('@apigo.cc/')) {
|
||||||
|
const key = pkg.name.replace('@apigo.cc/', '').toLowerCase();
|
||||||
|
if (pkg.version) {
|
||||||
|
versions[key] = pkg.version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(`Failed to parse ${pkgPath}:`, e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to read parent directory:', err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Gathered versions:', versions);
|
||||||
|
|
||||||
|
// 2. 获取 loader 自己的版本号
|
||||||
|
const loaderPkgPath = path.join(__dirname, '../package.json');
|
||||||
|
let loaderVersion = '1.0.0';
|
||||||
|
try {
|
||||||
|
const loaderPkg = JSON.parse(fs.readFileSync(loaderPkgPath, 'utf8'));
|
||||||
|
loaderVersion = loaderPkg.version || '1.0.0';
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to read loader package.json:', e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 更新 loader/src/index.js
|
||||||
|
const indexPath = path.join(__dirname, '../src/index.js');
|
||||||
|
if (fs.existsSync(indexPath)) {
|
||||||
|
let indexContent = fs.readFileSync(indexPath, 'utf8');
|
||||||
|
const versionsString = Object.entries(versions)
|
||||||
|
.map(([k, v]) => ` '${k}': '${v}'`)
|
||||||
|
.join(',\n');
|
||||||
|
|
||||||
|
const newDefaultVersions = `const DEFAULT_VERSIONS = {\n${versionsString}\n};`;
|
||||||
|
indexContent = indexContent.replace(/const DEFAULT_VERSIONS = \{[\s\S]*?\};/, newDefaultVersions);
|
||||||
|
|
||||||
|
fs.writeFileSync(indexPath, indexContent, 'utf8');
|
||||||
|
console.log('Updated loader/src/index.js DEFAULT_VERSIONS.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 更新 loader/README.md
|
||||||
|
const readmePath = path.join(__dirname, '../README.md');
|
||||||
|
if (fs.existsSync(readmePath)) {
|
||||||
|
let readmeContent = fs.readFileSync(readmePath, 'utf8');
|
||||||
|
|
||||||
|
// 替换 Loader.load('state', 'bootstrap', 'base') 里的版本号
|
||||||
|
// 支持形如 Loader.load('state:x.y.z', 'bootstrap:x.y.z', 'base:x.y.z')
|
||||||
|
// 或者最原始的 Loader.load('state', 'bootstrap', 'base')
|
||||||
|
readmeContent = readmeContent.replace(
|
||||||
|
/Loader\.load\(([^)]*)\)/g,
|
||||||
|
(match, p1) => {
|
||||||
|
// 提取被单引号/双引号包裹的各个 package 项
|
||||||
|
const items = p1.split(',').map(item => item.trim().replace(/['"]/g, ''));
|
||||||
|
const newItems = items.map(item => {
|
||||||
|
const [name] = item.split(':');
|
||||||
|
const key = name.toLowerCase();
|
||||||
|
if (versions[key]) {
|
||||||
|
return `'${key}:${versions[key]}'`;
|
||||||
|
}
|
||||||
|
return `'${item}'`;
|
||||||
|
});
|
||||||
|
return `Loader.load(${newItems.join(', ')})`;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 替换 cdn 链接里的 loader 版本
|
||||||
|
// 如 <script src="https://cdn.jsdelivr.net/npm/@apigo.cc/loader@1.0.0/dist/loader.min.js"></script>
|
||||||
|
readmeContent = readmeContent.replace(
|
||||||
|
/(\/\/cdn\.jsdelivr\.net\/npm\/@apigo\.cc\/loader@)[0-9.]+(\/dist\/loader\.min\.js)/g,
|
||||||
|
`$1${loaderVersion}$2`
|
||||||
|
);
|
||||||
|
|
||||||
|
// 替换 manual import 里的依赖版本,如:
|
||||||
|
// "@apigo.cc/state": "https://cdn.jsdelivr.net/npm/@apigo.cc/state@1.0.11/dist/state.min.js",
|
||||||
|
readmeContent = readmeContent.replace(
|
||||||
|
/(\/@apigo\.cc\/([a-zA-Z0-9_-]+)@)[0-9.]+(\/dist\/)/g,
|
||||||
|
(match, prefix, name, suffix) => {
|
||||||
|
const key = name.toLowerCase();
|
||||||
|
const ver = key === 'loader' ? loaderVersion : (versions[key] || 'latest');
|
||||||
|
return `${prefix}${ver}${suffix}`;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
fs.writeFileSync(readmePath, readmeContent, 'utf8');
|
||||||
|
console.log('Updated loader/README.md versions.');
|
||||||
|
}
|
||||||
68
src/index.js
68
src/index.js
@ -4,42 +4,39 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const DEFAULT_VERSIONS = {
|
const DEFAULT_VERSIONS = {
|
||||||
'state': 'v1.0.11',
|
'state': '1.0.11',
|
||||||
'bootstrap': 'v1.0.1',
|
'bootstrap': '1.0.3',
|
||||||
'base': 'v1.0.7',
|
'base': '1.0.7',
|
||||||
'dataTable': 'v1.0.6',
|
'datatable': '1.0.6',
|
||||||
'kanban': 'v1.0.0',
|
'kanban': '1.0.0',
|
||||||
'mindmap': 'v1.0.0',
|
'mindmap': '1.0.0',
|
||||||
'chart': 'v1.0.0'
|
'chart': '1.0.0',
|
||||||
|
'editor': '1.0.0',
|
||||||
|
'loader': '1.0.2'
|
||||||
};
|
};
|
||||||
|
|
||||||
const Loader = {
|
const Loader = {
|
||||||
/**
|
|
||||||
* 加载指定模块并注入 importmap
|
|
||||||
* @param {...string} pkgs 模块名格式: 'name' 或 'name:version'
|
|
||||||
*/
|
|
||||||
load: (...pkgs) => {
|
load: (...pkgs) => {
|
||||||
if (typeof document === 'undefined') return;
|
if (typeof document === 'undefined') return;
|
||||||
|
|
||||||
const currentScript = document.currentScript;
|
const currentScript = document.currentScript;
|
||||||
const currentUrl = currentScript ? currentScript.src : '';
|
const currentUrl = currentScript ? currentScript.src : '';
|
||||||
|
|
||||||
// 解析 URL 模板
|
// 1. 默认模板 (jsDelivr)
|
||||||
// 假设当前 URL 是 https://apigo.cc/web/loader/raw/tag/v1.0.0/dist/loader.min.js
|
let tpl = 'https://cdn.jsdelivr.net/npm/@apigo.cc/{project}@{tag}/dist/{project}.min.js';
|
||||||
// 目标模板 https://apigo.cc/web/{project}/raw/tag/{tag}/dist/{project}.js
|
|
||||||
let tpl = 'https://apigo.cc/web/{project}/raw/tag/{tag}/dist/{project}.js';
|
|
||||||
|
|
||||||
if (currentUrl.includes('/loader/')) {
|
// 2. 根据当前 loader.js 来源自动适配镜像
|
||||||
// 自动推断基础路径和 tag
|
if (currentUrl) {
|
||||||
const match = currentUrl.match(/(.*\/web\/)loader\/raw\/tag\/([^\/]+)\//);
|
if (currentUrl.includes('esm.sh')) {
|
||||||
if (match) {
|
tpl = 'https://esm.sh/@apigo.cc/{project}@{tag}/dist/{project}.min.js';
|
||||||
const baseUrl = match[1];
|
} else if (currentUrl.includes('unpkg.com')) {
|
||||||
const loaderTag = match[2];
|
tpl = 'https://unpkg.com/@apigo.cc/{project}@{tag}/dist/{project}.min.js';
|
||||||
tpl = `${baseUrl}{project}/raw/tag/{tag}/dist/{project}.js`;
|
} else if (currentUrl.includes('unpkg.zhimg.com')) {
|
||||||
// 默认情况下其他库也使用和 loader 相同的 tag,除非另有定义
|
tpl = 'https://unpkg.zhimg.com/@apigo.cc/{project}@{tag}/dist/{project}.min.js';
|
||||||
Object.keys(DEFAULT_VERSIONS).forEach(k => {
|
} else if (currentUrl.includes('npm.elemecdn.com')) {
|
||||||
if (DEFAULT_VERSIONS[k] === 'v1.0.0') DEFAULT_VERSIONS[k] = loaderTag;
|
tpl = 'https://npm.elemecdn.com/@apigo.cc/{project}@{tag}/dist/{project}.min.js';
|
||||||
});
|
} else if (currentUrl.includes('jsdelivr.net')) {
|
||||||
|
tpl = 'https://cdn.jsdelivr.net/npm/@apigo.cc/{project}@{tag}/dist/{project}.min.js';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,15 +44,22 @@ const Loader = {
|
|||||||
|
|
||||||
pkgs.forEach(pkg => {
|
pkgs.forEach(pkg => {
|
||||||
let [name, version] = pkg.split(':');
|
let [name, version] = pkg.split(':');
|
||||||
version = version || DEFAULT_VERSIONS[name] || 'main';
|
const key = name.toLowerCase();
|
||||||
|
const fullKey = `@apigo.cc/${key}`;
|
||||||
|
|
||||||
const url = tpl.replace(/{project}/g, name).replace(/{tag}/g, version);
|
let url = '';
|
||||||
importMap.imports[`@web/${name}`] = url;
|
// 优先检查本地 localStorage 重定向配置,方便开发调试
|
||||||
|
try {
|
||||||
|
if (typeof localStorage !== 'undefined') {
|
||||||
|
url = localStorage.getItem(`dev:${fullKey}`) || '';
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
// 特殊处理:如果是 bootstrap,可能需要额外处理或依赖
|
if (!url) {
|
||||||
if (name === 'bootstrap') {
|
version = version || DEFAULT_VERSIONS[key] || 'latest';
|
||||||
// bootstrap 内部现在是自包含的,直接映射即可
|
url = tpl.replace(/{project}/g, key).replace(/{tag}/g, version);
|
||||||
}
|
}
|
||||||
|
importMap.imports[fullKey] = url;
|
||||||
});
|
});
|
||||||
|
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user