Skip to content

Commit

Permalink
feat(zhi-core): add npm managent
Browse files Browse the repository at this point in the history
  • Loading branch information
terwer committed Apr 26, 2023
1 parent 2a9bd58 commit 2d64ccd
Show file tree
Hide file tree
Showing 13 changed files with 238 additions and 22 deletions.
3 changes: 2 additions & 1 deletion apps/zhi-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"zhi-env": "workspace:*",
"zhi-log": "workspace:*",
"zhi-device": "workspace:*",
"zhi-common": "workspace:*"
"zhi-common": "workspace:*",
"zhi-siyuan-api": "workspace:*"
}
}
3 changes: 3 additions & 0 deletions apps/zhi-core/public/npm/deps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"vue": "^3.2.47"
}
8 changes: 0 additions & 8 deletions apps/zhi-core/public/zhi.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,6 @@
"importType": "import",
"runAs": ["Siyuan_MainWindow"],
"order": 3
},
{
"libpath": "server/cmd/index.js",
"baseType": "ZhiTheme",
"format": "esm",
"importType": "import",
"runAs": ["Siyuan_MainWindow"],
"order": 4
}
],
"web": [],
Expand Down
47 changes: 47 additions & 0 deletions apps/zhi-core/src/theme/zhi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import ZhiUtil from "./core/util/ZhiCoreUtil"
import DependencyItem from "./models/DependencyItem"
import Bootstrap from "./core/Bootstrap"
import { crossChalk } from "zhi-log"
import { SiyuanKernelApi } from "zhi-siyuan-api"

/**
* 主题通用类(由theme.js动态调用,除了单元测试之外请勿主动调用)
Expand All @@ -39,17 +40,35 @@ import { crossChalk } from "zhi-log"
*/
class Zhi {
private readonly logger
private readonly common
private readonly kernelApi

private readonly runAs
private pkgJson = {} as any
private ZHI_PACKAGE_JSON = "package.json"

/**
* 主题样式最低支持版本
* @private
*/
private readonly SUPPORTED_THEME_VERSION = "2.7.6"

/**
* 内核最低支持版本
* @private
*/
private readonly SUPPORTED_KERNEL_VERSION = "2.8.1"

/**
* 主题初始化
*
* @param runAs - 运行模式
*/
constructor(runAs: DeviceTypeEnum) {
const env = ZhiCoreUtil.zhiEnv()
this.logger = ZhiCoreUtil.zhiLog("zhi-core")
this.common = ZhiCoreUtil.zhiCommon()
this.kernelApi = new SiyuanKernelApi(env)

this.runAs = runAs ?? DeviceTypeEnum.DeviceType_Node
}
Expand Down Expand Up @@ -88,6 +107,34 @@ class Zhi {
return
}

// 检测内核版本
const kernelVersion = SiyuanDevice.siyuanWindow().siyuan.config.system.kernelVersion
if (this.common.versionUtil.lesser(kernelVersion, this.SUPPORTED_THEME_VERSION)) {
const errMsg = this.common.strUtil.f(
"Your siyuan-note kernel version {0} is not supported by zhi theme, style will look weird, you must install siyuan-note {1}+ to use zhi-theme",
kernelVersion,
this.SUPPORTED_THEME_VERSION
)
this.logger.error(errMsg)
this.kernelApi.pushErrMsg({
msg: errMsg,
})
return
}

if (this.common.versionUtil.lesser(kernelVersion, this.SUPPORTED_KERNEL_VERSION)) {
const warnMsg = this.common.strUtil.f(
"Your siyuan-note kernel version {0} is too low, plugin system will not work, you must install siyuan-note {1}+ to use plugin feature",
kernelVersion,
this.SUPPORTED_KERNEL_VERSION
)
this.logger.warn(warnMsg)
this.kernelApi.pushMsg({
msg: warnMsg,
})
return
}

// 挂载一个日志对象,方便后续动态使用
if (typeof window !== "undefined") {
;(window as any).zhiLog = ZhiUtil.zhiLog("zhi-core")
Expand Down
4 changes: 4 additions & 0 deletions apps/zhi-server-blog-astro/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ await zhiCmd.executeCommandWithBundledNode("./entry.mjs", [], {
NODE_PATH: `/Users/terwer/Documents/mydocs/zhi/node_modules:apps/zhi-server-blog-astro/node_modules:/usr/lib/node_modules:/usr/local/lib/node_modules:$NODE_PATH`,
}
})
const basePath = SiyuanDevice.zhiThemePath()
await shellCmd("node","entry.mjs",`${basePath}/server/blog/dist/server`)
```

### node
Expand Down
4 changes: 2 additions & 2 deletions apps/zhi-server-infra/esbuild.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

const path = require("path")
const minimist = require("minimist")
const { dtsPlugin } = require("esbuild-plugin-d.ts")
// const { dtsPlugin } = require("esbuild-plugin-d.ts")
const { copy } = require("esbuild-plugin-copy")
const getNormalizedEnvDefines = require("esbuild-config-custom/utils.cjs")

Expand Down Expand Up @@ -57,7 +57,7 @@ module.exports = {
define: { ...coreDefine },
platform: "node",
plugins: [
dtsPlugin(),
// dtsPlugin(),
copy({
// this is equal to process.cwd(), which means we use cwd path as base path to resolve `to` path
// if not specified, this plugin uses ESBuild.build outdir/outfile options as base path.
Expand Down
7 changes: 3 additions & 4 deletions apps/zhi-server-infra/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,17 @@ import path from "path"
export function getCrossPlatformAppDataFolder() {
let configFilePath
if (process.platform === "darwin") {
// configFilePath = path.join(process.env.HOME ?? "/Users/terwer", "/Library/Application Support")
configFilePath = path.join(process.env.HOME ?? "/Users/terwer", "Documents/config")
configFilePath = path.join(process.env.HOME ?? "/Users/terwer", "/Library/Application Support")
} else if (process.platform === "win32") {
// Roaming包含在APPDATA中了
configFilePath = process.env.APPDATA
} else if (process.platform === "linux") {
configFilePath = process.env.HOME
}

return configFilePath
return path.join(configFilePath ?? process.cwd())
}
export const zhiNpmPath = SiyuanDevice.joinPath(SiyuanDevice.zhiThemePath(), "npm")
export const zhiNodeModulesPath = SiyuanDevice.joinPath(zhiNpmPath, "node_modules")
export const zhiAppNpmPath = SiyuanDevice.joinPath(getCrossPlatformAppDataFolder() ?? zhiNpmPath, "space.terwer.zhi")
export const zhiAppNpmPath = SiyuanDevice.joinPath(getCrossPlatformAppDataFolder() ?? zhiNpmPath, "siyuancommunity")
export const zhiAppNodeModulesPath = SiyuanDevice.joinPath(zhiAppNpmPath, "node_modules")
25 changes: 25 additions & 0 deletions apps/zhi-server-infra/src/lib/npmHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,31 @@ export function npmCmd(cmd: string, path: string) {
return shellCmd("npm", cmd, path)
}

/**
* 查看 NPM 版本
*/
export async function npmVersion() {
return await npmCmd(`-v`, zhiAppNpmPath)
}

/**
* 安装 NPM 依赖
*
* @param moduleName - 可选模块名,不传默认安装全量
*/
export async function npmInstall(moduleName?: string) {
if (moduleName) {
await npmCmd(`install ${moduleName}`, zhiAppNpmPath)
} else {
await npmCmd(`install`, zhiAppNpmPath)
}
}

/**
* 安装依赖并马上导入
*
* @param moduleName - 依赖名称
*/
export async function requireInstall(moduleName: string) {
await npmCmd(`install ${moduleName}`, zhiAppNpmPath)
return SiyuanDevice.siyuanWindow().require(moduleName)
Expand Down
115 changes: 115 additions & 0 deletions apps/zhi-server-infra/src/lib/packageHelper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* 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 fs from "fs"
import path from "path"
import ZhiServerInfraUtil from "../util/ZhiServerInfraUtil"
import crypto from "crypto"

interface PackageJson {
name: string
version: string
description?: string
main?: string
scripts?: Record<string, string>
keywords?: string[]
author?: string
license?: string
dependencies?: Record<string, string>
}

interface Dependencies {
[key: string]: string
}

const logger = ZhiServerInfraUtil.zhiLog("package-helper")

export function createPackageJson(
name: string,
version: string,
dependencies: Record<string, string>,
filePath?: string
): void {
const packageJson: PackageJson = {
name: name,
version: version,
description: "npm store for zhi",
keywords: ["zhi", "app"],
author: "terwer",
license: "GPL",
dependencies: dependencies,
}

if (!filePath) {
filePath = path.join(process.cwd(), "package.json")
}

const data = JSON.stringify(packageJson, null, 2)

fs.writeFileSync(filePath, data)
logger.info(`package.json created successfully at ${filePath}!`)
}

export function updatePackageJson(depsFilePath?: string, packageJsonFilePath?: string): boolean {
if (!depsFilePath) {
depsFilePath = path.join(process.cwd(), "deps.json")
}
if (!packageJsonFilePath) {
packageJsonFilePath = path.join(process.cwd(), "package.json")
}

const depsString = fs.readFileSync(depsFilePath).toString()
const hash = crypto.createHash("sha256").update(depsString).digest("hex")

const hashFilePath = path.join(path.dirname(packageJsonFilePath), ".deps-hash")
let oldHash: string
try {
oldHash = fs.readFileSync(hashFilePath).toString()
} catch (err) {
oldHash = ""
}

if (oldHash === hash) {
logger.info(`deps.json hasn't changed since last update, skipping...`)
return false
}

fs.writeFileSync(hashFilePath, hash)

const packageJsonString = fs.readFileSync(packageJsonFilePath).toString()
const packageJson: PackageJson = JSON.parse(packageJsonString)

const deps: Dependencies = JSON.parse(depsString)

packageJson.dependencies = {
...packageJson.dependencies,
...deps,
}

fs.writeFileSync(packageJsonFilePath, JSON.stringify(packageJson, null, 2))
logger.info(`dependencies updated successfully at ${packageJsonFilePath}`)

return true
}
35 changes: 29 additions & 6 deletions apps/zhi-server-infra/src/zhiInfra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ import fixPath from "fix-path"
import ZhiServerInfraUtil from "./util/ZhiServerInfraUtil"
import { SiyuanDevice } from "zhi-device"
import { zhiAppNodeModulesPath, zhiAppNpmPath, zhiNodeModulesPath, zhiNpmPath } from "./common"
import { npmCmd, requireInstall, shellCmd } from "./lib/npmHelper"
import { npmCmd, npmInstall, npmVersion, requireInstall, shellCmd } from "./lib/npmHelper"
import fs from "fs-extra"
import path from "path"
import FsHelper from "./lib/fsHelper"
import { createPackageJson, updatePackageJson } from "./lib/packageHelper"
import pkg from "../package.json" assert { type: "json" }

/**
* 基础设施
Expand All @@ -51,7 +52,7 @@ class ZhiInfra {
// 修复 Mac 和 Linux 下面的 PATH 环境变量问题
this.logger.debug("process.env.PATH before => ", (process as any).env.PATH)
fixPath()
console.log("process.env.PATH after fix => ", (process as any).env.PATH)
this.logger.trace("process.env.PATH after fix => ", (process as any).env.PATH)
this.logger.info("Fixed $PATH in Electron apps as GUI apps on macOS and Linux")
}

Expand All @@ -60,26 +61,48 @@ class ZhiInfra {
this.logger.info("Init zhi core node_modules from => ", zhiNodeModulesPath)
SiyuanDevice.siyuanWindow().require.setExternalDeps(zhiNodeModulesPath)

// 初始化 APP 依赖安装的 package.json
this.logger.info("Init zhi app node_modules from => ", zhiAppNodeModulesPath)
if (!fs.existsSync(path.join(zhiAppNpmPath, "package.json"))) {
const pkgJsonFile = path.join(zhiAppNpmPath, "package.json")
if (!fs.existsSync(pkgJsonFile)) {
await fs.mkdirs(zhiAppNpmPath)
await FsHelper.copyFolder(zhiNpmPath, zhiAppNpmPath)
this.logger.warn("app package.json not exist, will init")
// await FsHelper.copyFolder(zhiNpmPath, zhiAppNpmPath)

createPackageJson("zhi-app-package", pkg.version, {}, pkgJsonFile)
// await npmCmd("init", zhiAppNpmPath)
this.logger.warn("app package.json not exist, inited")
}

// 设置依赖路径
SiyuanDevice.siyuanWindow().require.setExternalDeps(zhiAppNodeModulesPath)
const externalDepPathes = SiyuanDevice.siyuanWindow().ExternalDepPathes
externalDepPathes.map((path: string, index: number) => {
this.logger.info(`Available zhi node_modules path${index + 1} => ${path}`)
})

// 更新最新定义的依赖
const depsJsonFile = path.join(zhiNpmPath, "deps.json")
const depsJsonStatus = updatePackageJson(depsJsonFile, pkgJsonFile)

// 全量安装依赖
// 内容有更新才去重新安装
if (depsJsonStatus) {
this.logger.info("Will install node_module once if needed, please wait...")
await npmInstall()
this.logger.info("All node_module installed successfully")
}
}

public mountNpmCmd() {
SiyuanDevice.siyuanWindow().requireInstall = requireInstall
SiyuanDevice.siyuanWindow().npmCmd = npmCmd
SiyuanDevice.siyuanWindow().npmVersion = npmVersion
SiyuanDevice.siyuanWindow().npmInstall = npmInstall
SiyuanDevice.siyuanWindow().shellCmd = shellCmd
this.logger.info("requireInstall mounted")
this.logger.info("npmCmd mounted")
this.logger.info("npmVersion mounted")
this.logger.info("npmInstall mounted")
this.logger.info("shellCmd mounted")
}
}
Expand Down
3 changes: 2 additions & 1 deletion apps/zhi-server-infra/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
"moduleResolution": "node",
"isolatedModules": false,
"esModuleInterop": true,
"resolveJsonModule": true,
"outDir": "./lib"
},
"include": ["env.d.ts", "**/*.ts", "**/*.spec.ts"],
"exclude": ["node_modules"]
}
}
Loading

0 comments on commit 2d64ccd

Please sign in to comment.