diff --git a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/package.json b/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/package.json deleted file mode 100644 index 6d671326..00000000 --- a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/package.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "@siyuan-community/zhi-core", - "version": "0.0.1", - "type": "commonjs", - "main": "./src/index.js", - "types": "./src/index.d.ts" -} diff --git a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/index.d.ts b/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/index.d.ts deleted file mode 100644 index af80bdce..00000000 --- a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./lib/zhi-core"; diff --git a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/index.js b/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/index.js deleted file mode 100644 index b21b1af8..00000000 --- a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/index.js +++ /dev/null @@ -1,5 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const tslib_1 = require("tslib"); -tslib_1.__exportStar(require("./lib/zhi-core"), exports); -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/index.js.map b/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/index.js.map deleted file mode 100644 index 412c00b2..00000000 --- a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../../../../../../../packages/zhi-core/src/index.ts"],"names":[],"mappings":";;;AAAA,yDAA8B"} \ No newline at end of file diff --git a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/core/zhi.d.ts b/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/core/zhi.d.ts deleted file mode 100644 index cb0ff5c3..00000000 --- a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/core/zhi.d.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/core/zhi.js b/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/core/zhi.js deleted file mode 100644 index 9f6e845e..00000000 --- a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/core/zhi.js +++ /dev/null @@ -1,73 +0,0 @@ -"use strict"; -/* - * Copyright (c) 2023, Terwer . All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Terwer designates this - * particular file as subject to the "Classpath" exception as provided - * by Terwer in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com - * or visit www.terwer.space if you need additional information or have any - * questions. - */ -Object.defineProperty(exports, "__esModule", { value: true }); -const tslib_1 = require("tslib"); -/** - * 主题通用类(由theme.js动态调用,除了单元测试之外请勿主动调用) - * - * @public - * @author terwer - * @since 0.1.0 - */ -class Zhi { -} -// -// const importMap = { -// imports: { -// react: "https://esm.sh/react@18.2.0?dev", -// "react-dom": "https://esm.sh/react-dom@18.2.0?dev", -// "react-dom/client": "https://esm.sh/react-dom@18.2.0/client?dev", -// "prop-types": "https://esm.sh/prop-types@15.8.1?dev", -// "react-feather": "https://unpkg.com/react-feather@2.0.10/dist/index.js", -// }, -// } -// -// const React = await importWithMap("react", importMap) -// const { createRoot } = await importWithMap("react-dom/client", importMap) -// -// // react-feather is a React icon library -// // which contains bare import specifiers for "react" and "prop-types" -// const { Smile } = await importWithMap("react-feather", importMap) -// -// function MyApp(props) { -// return React.createElement( -// React.Suspense, -// { fallback: React.createElement("div", {}, "Loading...") }, -// React.createElement(Smile) -// ) -// } -// -// const domContainer = document.getElementById("root") -// const root = createRoot(domContainer) -// root.render(React.createElement(MyApp)) -console.log("hello from dynamic importmap2, see https://github.com/keller-mark/dynamic-importmap#react-example for more details"); -// @ts-ignore -const zhi_log_1_9_1_1 = tslib_1.__importDefault(require("https://esm.sh/zhi-log@1.9.1")); -// const env = new Env({}) -// console.log(Env) -// console.log(env.isNodeDev()) -console.log(zhi_log_1_9_1_1.default); -//# sourceMappingURL=zhi.js.map \ No newline at end of file diff --git a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/core/zhi.js.map b/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/core/zhi.js.map deleted file mode 100644 index 5129d5d1..00000000 --- a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/core/zhi.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"zhi.js","sourceRoot":"","sources":["../../../../../../../../../../../../../../packages/zhi-core/src/lib/core/zhi.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;;;AAEH;;;;;;GAMG;AACH,MAAM,GAAG;CAIR;AAED,EAAE;AACF,sBAAsB;AACtB,eAAe;AACf,gDAAgD;AAChD,0DAA0D;AAC1D,wEAAwE;AACxE,4DAA4D;AAC5D,+EAA+E;AAC/E,OAAO;AACP,IAAI;AACJ,EAAE;AACF,wDAAwD;AACxD,4EAA4E;AAC5E,EAAE;AACF,2CAA2C;AAC3C,wEAAwE;AACxE,oEAAoE;AACpE,EAAE;AACF,0BAA0B;AAC1B,gCAAgC;AAChC,sBAAsB;AACtB,kEAAkE;AAClE,iCAAiC;AACjC,MAAM;AACN,IAAI;AACJ,EAAE;AACF,uDAAuD;AACvD,wCAAwC;AACxC,0CAA0C;AAC1C,OAAO,CAAC,GAAG,CAAC,oHAAoH,CAAC,CAAA;AAqBjI,aAAa;AACb,yFAAiD;AAEjD,0BAA0B;AAC1B,mBAAmB;AACnB,+BAA+B;AAG/B,OAAO,CAAC,GAAG,CAAC,uBAAM,CAAC,CAAA"} \ No newline at end of file diff --git a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/zhi-core.d.ts b/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/zhi-core.d.ts deleted file mode 100644 index 84102dc7..00000000 --- a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/zhi-core.d.ts +++ /dev/null @@ -1 +0,0 @@ -export declare function zhiCore(): string; diff --git a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/zhi-core.js b/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/zhi-core.js deleted file mode 100644 index e56fda2a..00000000 --- a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/zhi-core.js +++ /dev/null @@ -1,8 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.zhiCore = void 0; -function zhiCore() { - return "zhi-core"; -} -exports.zhiCore = zhiCore; -//# sourceMappingURL=zhi-core.js.map \ No newline at end of file diff --git a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/zhi-core.js.map b/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/zhi-core.js.map deleted file mode 100644 index 7d2aef15..00000000 --- a/Users/terwer/Documents/mydocs/SiYuanWorkspace/public/conf/appearance/themes/zhi/core/src/lib/zhi-core.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"zhi-core.js","sourceRoot":"","sources":["../../../../../../../../../../../../../packages/zhi-core/src/lib/zhi-core.ts"],"names":[],"mappings":";;;AAAA,SAAgB,OAAO;IACrB,OAAO,UAAU,CAAA;AACnB,CAAC;AAFD,0BAEC"} \ No newline at end of file diff --git a/package.json b/package.json index 802c3ff0..d79a1611 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,8 @@ "vite": "^4.0.1", "vite-plugin-dts": "~2.3.0", "vite-plugin-eslint": "^1.8.1", - "vite-plugin-lib-inject-css": "^1.2.0", + "vite-plugin-no-bundle": "^2.0.2", + "vite-plugin-static-copy": "^0.14.0", "vite-tsconfig-paths": "^4.0.2", "vitest": "^0.31.0" } diff --git a/packages/zhi-core/package.json b/packages/zhi-core/package.json index 6f8ef0ee..adc9e9f3 100644 --- a/packages/zhi-core/package.json +++ b/packages/zhi-core/package.json @@ -1,5 +1,5 @@ { - "name": "@siyuan-community/zhi-core", + "name": "@zhi/zhi-core", "version": "0.1.0", "type": "module" } diff --git a/packages/zhi-core/vite.config.ts b/packages/zhi-core/vite.config.ts index bb3f269f..30eeaedb 100644 --- a/packages/zhi-core/vite.config.ts +++ b/packages/zhi-core/vite.config.ts @@ -3,13 +3,17 @@ import { defineConfig } from "vite" import viteTsConfigPaths from "vite-tsconfig-paths" +const isTest = process.env["npm_command"] === "test" +console.log("isTest=>", isTest) + export default defineConfig({ cacheDir: "../../node_modules/.vite/zhi-core", plugins: [ - viteTsConfigPaths({ - root: "../../", - }), + !isTest && + viteTsConfigPaths({ + root: "../../", + }), ], // Uncomment this if you are using workers. diff --git a/packages/zhi-lib-device/.eslintrc.json b/packages/zhi-lib-device/.eslintrc.json new file mode 100644 index 00000000..9d9c0db5 --- /dev/null +++ b/packages/zhi-lib-device/.eslintrc.json @@ -0,0 +1,18 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + } + ] +} diff --git a/packages/zhi-lib-device/README.md b/packages/zhi-lib-device/README.md new file mode 100644 index 00000000..22e55780 --- /dev/null +++ b/packages/zhi-lib-device/README.md @@ -0,0 +1,55 @@ +# zhi-lib-device + +auto check environment whether in browser, browser extension, electron, node and more + +## Usage + +```js +import { DeviceDetection, BrowserUtil } from "@siyuan-community/zhi-device" + +console.log("isInBrowser=>", BrowserUtil.isInBrowser) + +const deviceType = DeviceDetection.getDevice() +console.log("deviceType=>", deviceType) + +// supported platforms +// Mobile +// Siyuan_Widget +// Siyuan_NewWindow +// Siyuan_MainWindow +// Chrome_Extension +// Chrome_Browser +// Node +``` + +## Deps + +``` +## Congregations! zhi-lib-device need no deps, it is just pure js code 🎉 +``` + +## Dev + +```bash +nx dev zhi-lib-device +``` + +## Build + +```bash +nx build zhi-lib-device +``` + +## Test + +Execute the unit tests via [vitest](https://vitest.dev) + +```bash +nx test zhi-lib-device +``` + +## Publish + +```bash +nx publish zhi-lib-device --ver=0.0.1 --tag=latest +``` diff --git a/packages/zhi-lib-device/package.json b/packages/zhi-lib-device/package.json new file mode 100644 index 00000000..ac868d53 --- /dev/null +++ b/packages/zhi-lib-device/package.json @@ -0,0 +1,19 @@ +{ + "name": "@siyuan-community/zhi-device", + "version": "0.2.2", + "type": "module", + "description": "auto check environment whether in browser, browser extension, electron, node and more", + "main": "./index.js", + "typings": "./index.d.ts", + "repository": "terwer/zhi", + "homepage": "https://github.com/terwer/zhi/tree/main/apps/zhi-device", + "author": "terwer", + "license": "GPL", + "keywords": [ + "zhi", + "device", + "browser", + "node", + "electron" + ] +} diff --git a/packages/zhi-lib-device/project.json b/packages/zhi-lib-device/project.json new file mode 100644 index 00000000..7eefa5c1 --- /dev/null +++ b/packages/zhi-lib-device/project.json @@ -0,0 +1,36 @@ +{ + "name": "zhi-lib-device", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/zhi-lib-device/src", + "projectType": "library", + "targets": { + "build": { + "executor": "@nx/vite:build", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/packages/zhi-lib-device", + "assets": ["packages/zhi-core/*.md"] + } + }, + "publish": { + "command": "node tools/scripts/publish.mjs zhi-lib-device {args.ver} {args.tag}", + "dependsOn": ["build"] + }, + "test": { + "executor": "@nx/vite:test", + "outputs": ["coverage/packages/zhi-lib-device"], + "options": { + "passWithNoTests": true, + "reportsDirectory": "../../coverage/packages/zhi-lib-device" + } + }, + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["packages/zhi-lib-device/**/*.ts"] + } + } + }, + "tags": [] +} diff --git a/packages/zhi-lib-device/src/index.ts b/packages/zhi-lib-device/src/index.ts new file mode 100644 index 00000000..a990c091 --- /dev/null +++ b/packages/zhi-lib-device/src/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023, Terwer . All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Terwer designates this + * particular file as subject to the "Classpath" exception as provided + * by Terwer in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com + * or visit www.terwer.space if you need additional information or have any + * questions. + */ + +export * from "./lib/zhi-lib-device" diff --git a/packages/zhi-lib-device/src/lib/basePathTypeEnum.ts b/packages/zhi-lib-device/src/lib/basePathTypeEnum.ts new file mode 100644 index 00000000..3fee9e72 --- /dev/null +++ b/packages/zhi-lib-device/src/lib/basePathTypeEnum.ts @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2023, Terwer . All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Terwer designates this + * particular file as subject to the "Classpath" exception as provided + * by Terwer in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com + * or visit www.terwer.space if you need additional information or have any + * questions. + */ + +/** + * 基本路径枚举 + * + * @public + * @author terwer + * @version 0.1.0 + * @since 0.1.0 + */ +enum BasePathTypeEnum { + /** + * Appearance + */ + BasePathType_Appearance = "Appearance", + + /** + * Data + */ + BasePathType_Data = "Data", + + /** + * Themes + */ + BasePathType_Themes = "Themes", + + /** + * Zhi 主题目录 + */ + BasePathType_ZhiTheme = "ZhiTheme", + + /** + * 未设置 + */ + BasePathType_None = "None", +} + +export default BasePathTypeEnum diff --git a/packages/zhi-lib-device/src/lib/browserUtil.spec.ts b/packages/zhi-lib-device/src/lib/browserUtil.spec.ts new file mode 100644 index 00000000..efa0b252 --- /dev/null +++ b/packages/zhi-lib-device/src/lib/browserUtil.spec.ts @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2023, Terwer . All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Terwer designates this + * particular file as subject to the "Classpath" exception as provided + * by Terwer in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com + * or visit www.terwer.space if you need additional information or have any + * questions. + */ + +import BrowserUtil from "./browserUtil" + +describe("BrowserUtil", () => { + it("isNode", () => { + const result = BrowserUtil.isNode + console.log(result) + }) + + it("isInBrowser", () => { + const result = BrowserUtil.isInBrowser + console.log(result) + }) + + it("isElectron", () => { + const result = BrowserUtil.isElectron() + console.log(result) + }) + + it("isInChromeExtension", () => { + const result = BrowserUtil.isInChromeExtension() + console.log(result) + }) + + it("getQueryString", () => { + const expected = "" + const result = BrowserUtil.getQueryString("foo") + expect(result).toBe(expected) + }) + + /** + * 当 URL 中存在指定的查询参数时,replaceUrlParam 函数应该替换该参数的值。 + */ + it("replaceUrlParam1", () => { + const url = "https://example.com/?foo=bar&baz=qux" + const paramName = "foo" + const paramValue = "baz" + const expected = "https://example.com/?foo=baz&baz=qux" + const result = BrowserUtil.replaceUrlParam(url, paramName, paramValue) + expect(result).toBe(expected) + }) + + /** + * 当 URL 中不存在指定的查询参数时,replaceUrlParam 函数应该添加该参数到 URL 中。 + */ + it("replaceUrlParam2", () => { + const url = "https://example.com/?foo=bar" + const paramName = "baz" + const paramValue = "qux" + const expected = "https://example.com/?foo=bar&baz=qux" + const result = BrowserUtil.replaceUrlParam(url, paramName, paramValue) + expect(result).toBe(expected) + }) + + /** + * 当 URL 没有查询参数时,replaceUrlParam 函数应该添加一个查询参数到 URL 中。 + */ + it("replaceUrlParam3", () => { + const url = "https://example.com/" + const paramName = "foo" + const paramValue = "bar" + const expected = "https://example.com/?foo=bar" + const result = BrowserUtil.replaceUrlParam(url, paramName, paramValue) + expect(result).toBe(expected) + }) + + it("replaceUrlParam4", () => { + const url = "https://example.com/?foo=bar#baz" + const paramName = "qux" + const paramValue = "quux" + const expected = "https://example.com/?foo=bar&qux=quux#baz" + const result = BrowserUtil.replaceUrlParam(url, paramName, paramValue) + expect(result).toBe(expected) + }) + + /** + * 当 URL 包含带有问号的 hash 片段时,replaceUrlParam 函数应该正确处理。 + */ + it("replaceUrlParam5", () => { + const url = "https://example.com/#foo?bar" + const paramName = "baz" + const paramValue = "qux" + const expected = "https://example.com/?baz=qux#foo?bar" + const result = BrowserUtil.replaceUrlParam(url, paramName, paramValue) + expect(result).toBe(expected) + }) + + /** + * 当 URL 中不存在指定的查询参数时,setUrlParameter 函数应该添加该参数到 URL 中。 + */ + it("should add a new parameter to the URL if it does not already exist", () => { + const url = "https://example.com/" + const key = "foo" + const value = "bar" + const expected = "https://example.com/?foo=bar" + const result = BrowserUtil.setUrlParameter(url, key, value) + expect(result).toBe(expected) + }) + + /** + * 当 URL 中存在指定的查询参数时,setUrlParameter 函数应该替换该参数的值。 + */ + it("should replace the value of an existing parameter in the URL", () => { + const url = "https://example.com/?foo=bar&baz=qux" + const key = "foo" + const value = "baz" + const expected = "https://example.com/?foo=baz&baz=qux" + const result = BrowserUtil.setUrlParameter(url, key, value) + expect(result).toBe(expected) + }) + + /** + * 当 URL 包含 hash 片段时,setUrlParameter 函数应该正确处理。 + */ + it("should handle URLs with a hash fragment", () => { + const url = "https://example.com/#foo" + const key = "bar" + const value = "baz" + const expected = "https://example.com/?bar=baz#foo" + const result = BrowserUtil.setUrlParameter(url, key, value) + expect(result).toBe(expected) + }) + + /** + * 当 URL 包含带有问号的 hash 片段时,setUrlParameter 函数应该正确处理。 + */ + it("should handle URLs with a question mark in the hash fragment", () => { + const url = "https://example.com/#foo?bar" + const key = "baz" + const value = "qux" + const expected = "https://example.com/?baz=qux#foo?bar" + const result = BrowserUtil.setUrlParameter(url, key, value) + expect(result).toBe(expected) + }) + + /** + * 当 URL 中存在其他参数的值包含新参数的键时,setUrlParameter 函数应该正确处理。 + */ + it("should handle URLs with existing parameters that contain the new parameter's key", () => { + const url = "https://example.com/?foobar=baz" + const key = "foo" + const value = "bar" + const expected = "https://example.com/?foobar=baz&foo=bar" + const result = BrowserUtil.setUrlParameter(url, key, value) + expect(result).toBe(expected) + }) + + it("should reload specified tab page", () => { + console.log("reloadTabPage") + }) + + it("should reload current tab page", () => { + console.log("reloadPage") + }) + + it("should reload current tab page with message callback", () => { + console.log("reloadPageWithMessageCallback") + }) +}) diff --git a/packages/zhi-lib-device/src/lib/browserUtil.ts b/packages/zhi-lib-device/src/lib/browserUtil.ts new file mode 100644 index 00000000..44e6fcce --- /dev/null +++ b/packages/zhi-lib-device/src/lib/browserUtil.ts @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2023, Terwer . All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Terwer designates this + * particular file as subject to the "Classpath" exception as provided + * by Terwer in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com + * or visit www.terwer.space if you need additional information or have any + * questions. + */ + +/** + * 浏览器工具类 + * + * @public + * @author terwer + * @since 1.0.2 + */ +class BrowserUtil { + /** + * 是否在浏览器环境 + */ + public static isNode = typeof process !== "undefined" + + /** + * 是否在浏览器环境 + */ + public static isInBrowser = typeof window !== "undefined" + + /** + * 浏览器路径分隔符 + */ + public static BrowserSeperator = "/" + + /** + * 是否是Electron环境 + */ + public static isElectron = () => { + if (!BrowserUtil.isInBrowser || !window.navigator || !window.navigator.userAgent) { + return false + } + + return /Electron/.test(window.navigator.userAgent) + } + + /** + * 是否有Node环境,目前包括 Electron 和 Node + */ + public static hasNodeEnv = () => { + return BrowserUtil.isElectron() || BrowserUtil.isNode + } + + /** + * 检测是否运行在Chrome插件中 + */ + public static isInChromeExtension() { + if (!BrowserUtil.isInBrowser) { + return false + } + return window.location.href.indexOf("chrome-extension://") > -1 + } + + /** + * 获取url参数 + * + * @param sParam - 参数 + */ + public static getQueryString = (sParam: string): string => { + if (!BrowserUtil.isInBrowser) { + return "" + } + const sPageURL = window.location.search.substring(1) + const sURLVariables = sPageURL.split("&") + + for (let i = 0; i < sURLVariables.length; i++) { + const sParameterName = sURLVariables[i].split("=") + if (sParameterName[0] === sParam) { + return sParameterName[1] + } + } + + return "" + } + + /** + * 替换 URL 的参数 + * 思路: + * 1. 使用了 URLSearchParams 对象来解析和构建 URL 查询参数。 + * + * 2. 在处理包含 hash 片段的 URL 时使用了 split 函数将 URL 分成两部分:基本 URL 和 hash 片段。 + * + * 3. 然后,再次使用 split 函数将基本 URL 分成两部分:路径和查询参数。 + * + * 4. 将查询参数转换为 URLSearchParams 对象,然后设置指定的参数名和值。 + * + * 5. 最后,使用 toString 函数将查询参数转换为字符串,并将其与路径组合成新的基本 URL。如果 URL 包含 hash 片段,则将其添加到新的基本 URL 中。 + * + * @param url - 链接地址 + * @param paramName - 参数名 + * @param paramValue - 参数值 + */ + public static replaceUrlParam = (url: string, paramName: string, paramValue: string): string => { + if (paramValue == null) { + paramValue = "" + } + const pattern = new RegExp("\\b(" + paramName + "=).*?(&|#|$)") + if (url.search(pattern) >= 0) { + return url.replace(pattern, "$1" + paramValue + "$2") + } + const [baseUrl, hash] = url.split("#") + const [path, query] = baseUrl.split("?") + const params = new URLSearchParams(query) + params.set(paramName, paramValue) + const newQuery = params.toString() + const newBaseUrl = path + (newQuery ? "?" + newQuery : "") + return hash ? newBaseUrl + "#" + hash : newBaseUrl + } + + /** + * 设置url参数 + * + * @param urlstring - url + * @param key - key + * @param value - value + */ + public static setUrlParameter = (urlstring: string, key: string, value: string): string => { + // 已经有参数了,不重复添加 + if (urlstring.includes(key)) { + return BrowserUtil.replaceUrlParam(urlstring, key, value) + } + + const hasharr = urlstring.split("#") // 将 URL 和 hash 部分分开 + let url = hasharr[0] + const hash = hasharr[1] + + if (url.includes("?")) { + url += `&${key}=${value}` // URL 中已经有查询参数 + } else { + url += `?${key}=${value}` // URL 中没有查询参数 + } + + // 如果有 hash 部分,则需要将其添加回 URL 中 + if (hash) { + url += "#" + hash + } + + return url + } + + /** + * 重新加载指定tab + * + * @param tabname - tabname + * @param t - 延迟时间 + */ + public static reloadTabPage = (tabname: string, t = 200): void => { + setTimeout(function () { + if (BrowserUtil.isInBrowser) { + const url = window.location.href + window.location.href = BrowserUtil.setUrlParameter(url, "tab", tabname) + } + }, t) + } + + /** + * 刷新当前tab页面 + */ + public static reloadPage = (): void => { + setTimeout(function () { + if (BrowserUtil.isInBrowser) { + window.location.reload() + } + }, 200) + } + + /** + * 刷新当前tab页面 + * + * @param msg - 消息提示 + * @param cb - 回调 + */ + public static reloadPageWithMessageCallback = (msg: string, cb: any): void => { + if (cb) { + cb() + } + + setTimeout(function () { + if (BrowserUtil.isInBrowser) { + window.location.reload() + } + }, 200) + } +} + +export default BrowserUtil diff --git a/packages/zhi-lib-device/src/lib/deviceDetection.ts b/packages/zhi-lib-device/src/lib/deviceDetection.ts new file mode 100644 index 00000000..b204361a --- /dev/null +++ b/packages/zhi-lib-device/src/lib/deviceDetection.ts @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2023, Terwer . All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Terwer designates this + * particular file as subject to the "Classpath" exception as provided + * by Terwer in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com + * or visit www.terwer.space if you need additional information or have any + * questions. + */ + +import BrowserUtil from "./browserUtil" +import SiyuanDevice from "./siyuanDevice" +import DeviceTypeEnum from "./deviceTypeEnum" + +/** + * 设备检测 + * + * @public + * @author terwer + * @version 0.1.0 + * @since 0.1.0 + */ +class DeviceDetection { + /** + * 获取当前设备 + */ + public static getDevice() { + // 移动端 + if (this.detectMobileDevice()) { + return DeviceTypeEnum.DeviceType_Mobile_Device + } + + // 思源笔记挂件 + if (SiyuanDevice.isInSiyuanWidget()) { + return DeviceTypeEnum.DeviceType_Siyuan_Widget + } + + // 思源新窗口 + if (SiyuanDevice.isInSiyuanNewWin()) { + return DeviceTypeEnum.DeviceType_Siyuan_NewWin + } + + // 思源主窗口 + if (BrowserUtil.isElectron()) { + return DeviceTypeEnum.DeviceType_Siyuan_MainWin + } + + // 思源打开的浏览器 + if (SiyuanDevice.isInSiyuanBrowser()) { + return DeviceTypeEnum.DeviceType_Siyuan_Browser + } + + // Chrome浏览器插件 + if (BrowserUtil.isInChromeExtension()) { + return DeviceTypeEnum.DeviceType_Chrome_Extension + } + + // Node环境 + if (BrowserUtil.isNode) { + return DeviceTypeEnum.DeviceType_Node + } + + return DeviceTypeEnum.DeviceType_Chrome_Browser + } + + /** + * 检测移动端 + * @private + */ + private static detectMobileDevice() { + let check = false + if (BrowserUtil.isInBrowser) { + ;(function (a) { + if ( + /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test( + a + ) || + /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i.test( + a.substr(0, 4) + ) + ) + check = true + })(navigator.userAgent || navigator.vendor || (window as any).opera) + } + + return check + } +} + +export default DeviceDetection diff --git a/packages/zhi-lib-device/src/lib/deviceTypeEnum.ts b/packages/zhi-lib-device/src/lib/deviceTypeEnum.ts new file mode 100644 index 00000000..61400899 --- /dev/null +++ b/packages/zhi-lib-device/src/lib/deviceTypeEnum.ts @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023, Terwer . All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Terwer designates this + * particular file as subject to the "Classpath" exception as provided + * by Terwer in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com + * or visit www.terwer.space if you need additional information or have any + * questions. + */ + +/** + * 设备类型枚举 + * + * @public + * @author terwer + * @since 1.0.8 + */ +enum DeviceTypeEnum { + /** + * 移动端 + */ + DeviceType_Mobile_Device = "Mobile", + + /** + * 思源笔记挂件 + */ + DeviceType_Siyuan_Widget = "Siyuan_Widget", + + /** + * 思源笔记新窗口 + */ + DeviceType_Siyuan_NewWin = "Siyuan_NewWindow", + + /** + * 思源笔记主窗口 + */ + DeviceType_Siyuan_MainWin = "Siyuan_MainWindow", + + /** + * 思源打开的浏览器 + */ + DeviceType_Siyuan_Browser = "Siyuan_Browser", + + /** + * Google Chrome浏览器插件 + */ + DeviceType_Chrome_Extension = "Chrome_Extension", + + /** + * Google Chrome浏览器(Docker浏览器共用) + */ + DeviceType_Chrome_Browser = "Chrome_Browser", + + /** + * Node环境 + */ + DeviceType_Node = "Node", +} + +export default DeviceTypeEnum diff --git a/packages/zhi-lib-device/src/lib/siyuanDevice.ts b/packages/zhi-lib-device/src/lib/siyuanDevice.ts new file mode 100644 index 00000000..dcd3271f --- /dev/null +++ b/packages/zhi-lib-device/src/lib/siyuanDevice.ts @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2023, Terwer . All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Terwer designates this + * particular file as subject to the "Classpath" exception as provided + * by Terwer in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com + * or visit www.terwer.space if you need additional information or have any + * questions. + */ + +import BrowserUtil from "./browserUtil" +import BasePathTypeEnum from "./basePathTypeEnum" + +/** + * 思源笔记设备相关 + * + * @public + * @author terwer + * @version 0.1.0 + * @since 0.1.0 + */ +class SiyuanDevice { + /** + * 思源笔记iframe挂件环境 + */ + public static isInSiyuanWidget = () => { + if (!BrowserUtil.isInBrowser) { + return false + } + return ( + window.frameElement != null && + window.frameElement.parentElement != null && + window.frameElement.parentElement.parentElement != null && + window.frameElement.parentElement.parentElement.getAttribute("data-node-id") !== "" + ) + } + + /** + * 思源笔记新窗口 + * + * @deprecated window.terwer 判断方式已废弃,建议以后打开新窗口注入 window.siyuanNewWin ,这样语义会更容易理解 + * @author terwer + * @version 0.1.0 + * @since 0.0.1 + */ + public static isInSiyuanNewWin = () => { + if (!BrowserUtil.isInBrowser) { + return false + } + if (!BrowserUtil.isElectron()) { + return false + } + + /** + * @deprecated 已废弃,建议以后使用 window.siyuanNewWin 来判断,会更有意义 + */ + return typeof (window as any).terwer !== "undefined" || typeof (window as any).siyuanNewWin !== "undefined" + } + + /** + * 检测是否运行在思源打开的浏览器中 + */ + public static isInSiyuanBrowser() { + if (!BrowserUtil.isInBrowser) { + return false + } + return typeof (window as any).siyuan !== "undefined" && typeof (window as any).Lute !== "undefined" + } + + /** + * 思源笔记 window 对象 + */ + public static siyuanWindow() { + let win + if (this.isInSiyuanWidget()) { + win = parent.window + } else { + if (this.isInSiyuanNewWin()) { + win = window + } else if (this.isInSiyuanBrowser()) { + win = window + } else if (typeof window !== "undefined") { + win = window + } else { + win = undefined + } + } + return win as any + } + + // ========================= + // require start + // ========================= + + /** + * 引入依赖 + * + * @param libpath - 依赖全路径 + * @param abs - 可选,是否使用觉得路径,默认是 true , 启用之后 type参数无效 + * @param type - 可选,以谁的基本路径为准 + */ + public static requireLib = (libpath: string, abs = true, type = BasePathTypeEnum.BasePathType_None) => { + if (!BrowserUtil.hasNodeEnv()) { + throw new Error("require ony works on node env") + } + + let absLibpath = libpath + if (!abs) { + switch (type) { + case BasePathTypeEnum.BasePathType_Appearance: + absLibpath = this.joinPath(this.siyuanAppearancePath(), libpath) + break + case BasePathTypeEnum.BasePathType_Data: + absLibpath = this.joinPath(this.siyuanDataPath(), libpath) + break + case BasePathTypeEnum.BasePathType_Themes: + absLibpath = this.joinPath(this.siyuanAppearancePath(), "themes", libpath) + break + case BasePathTypeEnum.BasePathType_ZhiTheme: + absLibpath = this.joinPath(this.siyuanAppearancePath(), "themes", "zhi", libpath) + break + default: + throw new Error("type must be provided when not use absolute path") + } + } + + const syWin = this.siyuanWindow() + if (!syWin) { + return require(absLibpath) + } + if (typeof syWin.require !== "undefined") { + return syWin.require(absLibpath) + } + + return undefined + } + + /** + * 引入依赖,以 data 的基本路径为准 + * + * @param libpath - 相对于 appearance 的相对路径 + */ + public static requireAppearanceLib = (libpath: string) => { + return this.requireLib(libpath, false, BasePathTypeEnum.BasePathType_Appearance) + } + + /** + * 引入依赖,以 data 的基本路径为准 + * + * @param libpath - 相对于 data 的相对路径 + */ + public static requireDataLib = (libpath: string) => { + return this.requireLib(libpath, false, BasePathTypeEnum.BasePathType_Data) + } + + /** + * 引入依赖,以 theme 的基本路径为准 + * + * @param libpath - 相对于 theme 的相对路径 + */ + public static requireThemesLib = (libpath: string) => { + return this.requireLib(libpath, false, BasePathTypeEnum.BasePathType_Themes) + } + + /** + * 引入依赖,以 ZhiTheme 的基本路径为准 + * + * @param libpath - 相对于 ZhiTheme 的相对路径 + */ + public static requireZhiThemeLib = (libpath: string) => { + return this.requireLib(libpath, false, BasePathTypeEnum.BasePathType_ZhiTheme) + } + + // ========================= + // require end + // ========================= + + // ========================= + // import start + // ========================= + /** + * 引入json + * + * @param jsPath - js相对路径全路径 + * @param type - 类型 + */ + public static getImportJsPath(jsPath: string, type: BasePathTypeEnum) { + let fullJsonPath = jsPath + switch (type) { + case BasePathTypeEnum.BasePathType_Appearance: + fullJsonPath = this.browserJoinPath(this.siyuanAppearanceRelativePath(), jsPath) + break + case BasePathTypeEnum.BasePathType_Data: + fullJsonPath = this.browserJoinPath(this.siyuanDataRelativePath(), jsPath) + break + case BasePathTypeEnum.BasePathType_Themes: + fullJsonPath = this.browserJoinPath(this.siyuanThemeRelativePath(), jsPath) + break + case BasePathTypeEnum.BasePathType_ZhiTheme: + fullJsonPath = this.browserJoinPath(this.zhiThemeRelativePath(), jsPath) + break + default: + throw new Error("type must be provided") + } + + return fullJsonPath + } + + /** + * 引入 zhi 主题的 js - 以 zhi 主题 的根路径为基本路径 + * + * @param jsPath - 相对于 zhi 主题根路径的相对路径 + */ + public static getZhiThemeImportJsPath(jsPath: string) { + return this.getImportJsPath(jsPath, BasePathTypeEnum.BasePathType_ZhiTheme) + } + + // ========================= + // import start + // ========================= + + /** + * 路径拼接 + * + * @param paths - 路径数组 + */ + public static joinPath(...paths: string[]): string { + if (BrowserUtil.hasNodeEnv()) { + const path = this.requireLib("path") + if (path) { + return path.join(...paths) + } + } + + return this.browserJoinPath(...paths) + } + + public static browserJoinPath(...paths: string[]): string { + return paths.join(BrowserUtil.BrowserSeperator) + } + + /** + * 思源笔记 conf 目录 + */ + public static siyuanConfPath() { + const syWin = this.siyuanWindow() + if (!syWin) { + throw new Error("Not in siyuan env") + } + return syWin.siyuan.config.system.confDir + } + + /** + * 思源笔记 data 目录 + */ + public static siyuanDataPath() { + const syWin = this.siyuanWindow() + if (!syWin) { + throw new Error("Not in siyuan env") + } + return syWin.siyuan.config.system.dataDir + } + + /** + * 思源笔记 data 目录-相对路径 + */ + public static siyuanDataRelativePath() { + const syWin = this.siyuanWindow() + if (!syWin) { + throw new Error("Not in siyuan env") + } + return "" + } + + /** + * 思源笔记 appearance 目录 + */ + public static siyuanAppearancePath() { + return this.joinPath(this.siyuanConfPath(), "appearance") + } + + /** + * 思源笔记 appearance 目录-相对路径 + */ + public static siyuanAppearanceRelativePath() { + const syWin = this.siyuanWindow() + if (!syWin) { + throw new Error("Not in siyuan env") + } + return this.browserJoinPath("", "appearance") + } + + /** + * 思源笔记 themes 目录-绝对路径 + * + * 注意: 如果是非 electron 和 Node 环境,这里返回的是浏览器的路径,不是物理路径 + * 如果使用物理路径,请调用 siyuanAppearancePath 或者 siyuanDataPath + * + * @author terwer + * @since 0.1.0 + */ + public static siyuanThemePath() { + if (BrowserUtil.hasNodeEnv()) { + return this.joinPath(this.siyuanAppearancePath(), "themes") + } else { + const syWin = this.siyuanWindow() + if (!syWin) { + throw new Error("Not in siyuan env") + } + return this.joinPath(syWin.location.origin, "appearance", "themes") + } + } + + /** + * 思源笔记 themes 目录-相对路径 + */ + public static siyuanThemeRelativePath() { + const syWin = this.siyuanWindow() + if (!syWin) { + throw new Error("Not in siyuan env") + } + return this.browserJoinPath("", "appearance", "themes") + } + + /** + * zhi 主题目录 - 绝对路径 + */ + public static zhiThemePath() { + return this.joinPath(this.siyuanThemePath(), "zhi") + } + + /** + * zhi 主题目录 - 相对路径 + */ + public static zhiThemeRelativePath() { + return this.browserJoinPath(this.siyuanThemeRelativePath(), "zhi") + } +} + +export default SiyuanDevice diff --git a/packages/zhi-lib-device/src/lib/zhi-lib-device.spec.ts b/packages/zhi-lib-device/src/lib/zhi-lib-device.spec.ts new file mode 100644 index 00000000..07a8575f --- /dev/null +++ b/packages/zhi-lib-device/src/lib/zhi-lib-device.spec.ts @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023, Terwer . All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Terwer designates this + * particular file as subject to the "Classpath" exception as provided + * by Terwer in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com + * or visit www.terwer.space if you need additional information or have any + * questions. + */ + +describe("zhiLibDevice", () => { + it("zhiLibDevice should work", () => { + console.log("hello") + }) +}) diff --git a/packages/zhi-lib-device/src/lib/zhi-lib-device.ts b/packages/zhi-lib-device/src/lib/zhi-lib-device.ts new file mode 100644 index 00000000..a0712c97 --- /dev/null +++ b/packages/zhi-lib-device/src/lib/zhi-lib-device.ts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023, Terwer . All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Terwer designates this + * particular file as subject to the "Classpath" exception as provided + * by Terwer in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Terwer, Shenzhen, Guangdong, China, youweics@163.com + * or visit www.terwer.space if you need additional information or have any + * questions. + */ + +import DeviceTypeEnum from "./deviceTypeEnum" +import BasePathTypeEnum from "./basePathTypeEnum" +import BrowserUtil from "./browserUtil" +import SiyuanDevice from "./siyuanDevice" +import DeviceDetection from "./deviceDetection" + +export { DeviceDetection } +export { BrowserUtil, SiyuanDevice } +export { DeviceTypeEnum, BasePathTypeEnum } diff --git a/packages/zhi-lib-device/tsconfig.json b/packages/zhi-lib-device/tsconfig.json new file mode 100644 index 00000000..bdf594cd --- /dev/null +++ b/packages/zhi-lib-device/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "types": ["vitest"] + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/packages/zhi-lib-device/tsconfig.lib.json b/packages/zhi-lib-device/tsconfig.lib.json new file mode 100644 index 00000000..33eca2c2 --- /dev/null +++ b/packages/zhi-lib-device/tsconfig.lib.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "declaration": true, + "types": ["node"] + }, + "include": ["src/**/*.ts"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/packages/zhi-lib-device/tsconfig.spec.json b/packages/zhi-lib-device/tsconfig.spec.json new file mode 100644 index 00000000..6d3be742 --- /dev/null +++ b/packages/zhi-lib-device/tsconfig.spec.json @@ -0,0 +1,19 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/out-tsc", + "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node"] + }, + "include": [ + "vite.config.ts", + "src/**/*.test.ts", + "src/**/*.spec.ts", + "src/**/*.test.tsx", + "src/**/*.spec.tsx", + "src/**/*.test.js", + "src/**/*.spec.js", + "src/**/*.test.jsx", + "src/**/*.spec.jsx", + "src/**/*.d.ts" + ] +} diff --git a/packages/zhi-lib-device/vite.config.ts b/packages/zhi-lib-device/vite.config.ts new file mode 100644 index 00000000..110c42c5 --- /dev/null +++ b/packages/zhi-lib-device/vite.config.ts @@ -0,0 +1,66 @@ +/// +import { defineConfig } from "vite" + +import viteTsConfigPaths from "vite-tsconfig-paths" +import dts from "vite-plugin-dts" +import { join } from "path" +import noBundlePlugin from "vite-plugin-no-bundle" +import { viteStaticCopy } from "vite-plugin-static-copy" + +const isTest = process.env["npm_command"] === "test" +console.log("isTest=>", isTest) + +export default defineConfig({ + cacheDir: "../../node_modules/.vite/zhi-lib-device", + + plugins: [ + dts({ + entryRoot: "src", + tsConfigFilePath: join(__dirname, "tsconfig.lib.json"), + skipDiagnostics: true, + }), + + !isTest && + viteTsConfigPaths({ + root: "../../", + }), + + !isTest && noBundlePlugin(), + + viteStaticCopy({ + targets: [ + { + src: 'README.md', + dest: '.' + } + ] + }) + ], + + // Configuration for building your library. + // See: https://vitejs.dev/guide/build.html#library-mode + build: { + lib: { + // Could also be a dictionary or array of multiple entry points. + entry: "src/index.ts", + name: "zhi-lib-device", + fileName: "index", + // Change this to the formats you want to support. + // Don't forgot to update your package.json as well. + formats: ["es"], + }, + rollupOptions: { + // External packages that should not be bundled into your library. + external: [], + }, + }, + + test: { + globals: true, + cache: { + dir: "../../node_modules/.vitest", + }, + environment: "jsdom", + include: ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], + }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 23a0d7f9..bf867a89 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -85,9 +85,12 @@ devDependencies: vite-plugin-eslint: specifier: ^1.8.1 version: 1.8.1(eslint@8.39.0)(vite@4.0.1) - vite-plugin-lib-inject-css: - specifier: ^1.2.0 - version: 1.2.0(vite@4.0.1) + vite-plugin-no-bundle: + specifier: ^2.0.2 + version: 2.0.2 + vite-plugin-static-copy: + specifier: ^0.14.0 + version: 0.14.0(vite@4.0.1) vite-tsconfig-paths: specifier: ^4.0.2 version: 4.0.2(typescript@5.0.4)(vite@4.0.1) @@ -5644,12 +5647,22 @@ packages: vite: 4.0.1(@types/node@18.16.3)(stylus@0.59.0) dev: true - /vite-plugin-lib-inject-css@1.2.0(vite@4.0.1): - resolution: {integrity: sha512-mELCOWG0f0auFkDGQHAL7Ks9oQ6RhB0xHlZ6GA0mTLMkkQcHCrZ8mkJFoH+ryZTNqxNLlYTLqbrnPClOO0Fq/Q==} + /vite-plugin-no-bundle@2.0.2: + resolution: {integrity: sha512-QtMJ0Dzml8mQzZ+Ta09fPJAgy6WXp5rN2w77KQEIV79OeAwkpnaaig/KwH9r6tZLMfa5LkMO7DmmOGQwtph+2A==} + dependencies: + fast-glob: 3.2.12 + micromatch: 4.0.5 + dev: true + + /vite-plugin-static-copy@0.14.0(vite@4.0.1): + resolution: {integrity: sha512-RMFmb4czomcrsbQBiUZs9HcDGN3kxGvF+OrtkfTVocp12CuoUCuJQhcY26RK35A6KS4WasGzEwcYZqHMjkAvVw==} + engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: - vite: '*' + vite: ^3.0.0 || ^4.0.0 dependencies: - magic-string: 0.30.0 + chokidar: 3.5.3 + fast-glob: 3.2.12 + fs-extra: 11.1.1 picocolors: 1.0.0 vite: 4.0.1(@types/node@18.16.3)(stylus@0.59.0) dev: true diff --git a/tsconfig.base.json b/tsconfig.base.json index 099f1b3c..ec945276 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -16,7 +16,8 @@ "skipDefaultLibCheck": true, "baseUrl": ".", "paths": { - "@siyuan-community/zhi-core": ["packages/zhi-core/src/index.ts"], + "@siyuan-community/zhi-device": ["packages/zhi-lib-device/src/index.ts"], + "@zhi/zhi-core": ["packages/zhi-core/src/index.ts"], "@zhi/zhi-loader": ["packages/zhi-loader/src/index.ts"] } },