diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c4aa36..b95f48c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## v1.2.0...main + +[compare changes](https://github.com/GuSanle/vite-plugin-kintone-dev/compare/v1.2.0...main) ## v1.1.5 diff --git a/example/vue-kintone-vite-demo/tsconfig.node.json b/example/vue-kintone-vite-demo/tsconfig.node.json index 9375a2f..dee96be 100644 --- a/example/vue-kintone-vite-demo/tsconfig.node.json +++ b/example/vue-kintone-vite-demo/tsconfig.node.json @@ -1,7 +1,6 @@ { "extends": "@tsconfig/node18/tsconfig.json", "include": [ - "vite-plugin-kintone-dev/**/*", "vite.config.*", "vitest.config.*", "cypress.config.*", diff --git a/example/vue-kintone-vite-demo/vite-plugin-kintone-dev/devUpdate.ts b/example/vue-kintone-vite-demo/vite-plugin-kintone-dev/devUpdate.ts deleted file mode 100644 index 5d1fda7..0000000 --- a/example/vue-kintone-vite-demo/vite-plugin-kintone-dev/devUpdate.ts +++ /dev/null @@ -1,110 +0,0 @@ -import kintoneApi from "./kintoneApi"; -import path from "node:path"; - -import { - type Type, - type EnvSetting, - type TypeInput, - type JsList, -} from "kintone-types"; - -export const devFileName = "kintone_module_hack.js"; - -function urlPrefix(url: string) { - if ( - url.substring(0, 7).toLowerCase() == "http://" || - url.substring(0, 8).toLowerCase() == "https://" - ) { - url = url; - } else { - url = "https://" + url; - } - return url; -} - -//步骤:上传文件,获取系统设置,准备新的自定义文件列表,更新系统设置 -export const devUpdate = async ( - env: EnvSetting, - fileList: Array, - type: TypeInput -) => { - const { - VITE_KINTONE_URL: url, - VITE_KINTONE_USER_NAME: username, - VITE_KINTONE_PASSWORD: password, - VITE_KINTONE_APP: app, - } = env; - const k = new kintoneApi(urlPrefix(url), username, password); - try { - //上传文件 - const fileNameList = fileList.map((filePath) => path.basename(filePath)); - const uploadPromise = await Promise.all( - fileList.map((filePath) => { - return k.uploadFile(filePath, path.basename(filePath)); - }) - ); - - let jsList: JsList = { - DESKTOP: [], - MOBILE: [], - DESKTOP_CSS: [], - MOBILE_CSS: [], - }; - - //准备构建好的文件 - for (const index in uploadPromise) { - const { fileKey } = uploadPromise[index]; - const fileType: Type = - path.extname(fileNameList[index]).slice(1) === "js" - ? type.type - : `${type.type}_CSS`; - jsList[fileType].push(fileKey); - } - //获取自定义设置 - let customSetting; - let appName = ""; - - if (type.platform === "PORTAL") { - customSetting = await k.getSystemSetting(); - } else if (app) { - const result = await k.getAppInfo(app); - appName = result.name; - customSetting = await k.getAppSetting(app); - } else { - console.log("env setting error"); - return; - } - - const { scripts, scope } = customSetting.result; - - //补充之前的自定义配置,排除老的构建文件 - scripts.forEach((setting) => { - const { locationType, type, name, contentUrl, contentId } = setting; - if (locationType === "URL") { - jsList[type as Type].push(contentUrl); - } else if (locationType === "BLOB") { - if (!fileNameList.includes(name) && name !== devFileName) { - jsList[type as Type].push(contentId); - } - } - }); - - const jsFiles = [ - { jsType: "DESKTOP", fileKeys: jsList["DESKTOP"] }, - { jsType: "MOBILE", fileKeys: jsList["MOBILE"] }, - { jsType: "DESKTOP_CSS", fileKeys: jsList["DESKTOP_CSS"] }, - { jsType: "MOBILE_CSS", fileKeys: jsList["MOBILE_CSS"] }, - ]; - - //更新系统设置 - if (type.platform === "PORTAL") { - await k.updateSystemSetting(scope, jsFiles); - } else if (app) { - await k.updateAppSetting(app, scope, jsFiles, appName); - await k.deploySetting(app); - } - console.log("update success"); - } catch (err) { - console.log(err); - } -}; diff --git a/example/vue-kintone-vite-demo/vite-plugin-kintone-dev/index.ts b/example/vue-kintone-vite-demo/vite-plugin-kintone-dev/index.ts deleted file mode 100644 index 3598765..0000000 --- a/example/vue-kintone-vite-demo/vite-plugin-kintone-dev/index.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { - ResolvedConfig, - type Plugin, - loadEnv, - normalizePath, - ConfigEnv, -} from "vite"; -import { devUpdate, devFileName } from "./devUpdate"; -import path from "node:path"; -import fs from "node:fs"; -import { load } from "cheerio"; -import { - type EnvSetting, - type TypeInput, - type ScriptList, -} from "kintone-types"; - -//类型守卫函数 -function isEnvSetting(obj: any): obj is EnvSetting { - return ( - obj && - typeof obj.VITE_KINTONE_URL === "string" && - typeof obj.VITE_KINTONE_USER_NAME === "string" && - typeof obj.VITE_KINTONE_PASSWORD === "string" && - (typeof obj.VITE_KINTONE_APP === "undefined" || - typeof obj.VITE_KINTONE_APP === "string") - ); -} - -function getIndexHtmlContent(): ScriptList { - const url = path.resolve("index.html"); - const htmlContent = fs.readFileSync(url, "utf-8"); - - const $ = load(htmlContent); - const scriptTags = $("body script"); - - const scriptList: ScriptList = []; - scriptTags.each((index, element) => { - const data = { - type: $(element).attr("type"), - src: $(element).attr("src"), - }; - scriptList.push(data); - }); - return scriptList; -} - -function kintoneModuleHack( - devServerUrl: string, - scriptList: ScriptList -): string { - return `(function () { - const viteClientInject = document.createElement("script"); - viteClientInject.type = "module"; - viteClientInject.src = "${devServerUrl}"+'/@vite/client'; - document.body.appendChild(viteClientInject); - const scriptList = ${JSON.stringify(scriptList)}; - function loadScript(src,type) { - const script = document.createElement("script"); - script.type = type; - script.src = "${devServerUrl}"+src; - document.body.appendChild(script); - } - for (const script of scriptList){ - const {src,type}=script - loadScript(src,type) - } - })(); - `; -} - -function getDirFiles(dir: string, extList: string[]) { - let result: string[] = []; - let files = fs.readdirSync(dir, { withFileTypes: true }); - files.forEach((file) => { - const filepath = path.join(dir, file.name); - const ext = path.extname(filepath).slice(1); - if (file.isFile() && extList.includes(ext)) { - result.push(filepath); - } else if (file.isDirectory()) { - result.push(...getDirFiles(filepath, extList)); - } - }); - return result; -} - -function validateEnv( - envConfig: ConfigEnv, - viteConfig: ResolvedConfig -): EnvSetting | undefined { - const resolvedRoot = normalizePath( - viteConfig.root ? path.resolve(viteConfig.root) : process.cwd() - ); - - const envDir = viteConfig.envDir - ? normalizePath(path.resolve(resolvedRoot, viteConfig.envDir)) - : resolvedRoot; - - const env = loadEnv(envConfig.mode, envDir, viteConfig.envPrefix); - return isEnvSetting(env) ? env : undefined; -} - -function isIpv6(address: any) { - return address.family === "IPv6" || address.family === 6; -} - -export default function kintoneDev(inputType: TypeInput): Plugin[] { - let viteConfig: ResolvedConfig; - let envConfig: ConfigEnv; - - return [ - { - name: "vite-plugin-kintone-dev:dev", - apply: "serve", - enforce: "post", // 指定运行顺序 - config: (config, env) => (envConfig = env), - configResolved(config) { - viteConfig = config; - // viteConfig.server.origin = "http://localhost:5173"; - }, - configureServer(server) { - // console.log("server", server); - server.httpServer?.once("listening", async () => { - const outputDir = path.resolve(viteConfig.build.outDir); - const address = server.httpServer?.address(); - if (!address || typeof address === "string") { - console.error("Unexpected dev server address", address); - process.exit(1); - } - const protocol = server.config.server.https ? "https" : "http"; - const host = isIpv6(address) - ? `[${address.address}]` - : address.address; - const port = address.port; - const devServerUrl = `${protocol}://${host}:${port}`; - if (!server.config.server.origin) { - server.config.server.origin = `${protocol}://${host}:${port}`; - } - if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir); - } - const fileUrl = path.resolve(outputDir, devFileName); - - const scriptList = getIndexHtmlContent(); - - fs.writeFileSync( - fileUrl, - kintoneModuleHack(devServerUrl, scriptList) - ); - - const env = validateEnv(envConfig, viteConfig); - - if (env) { - devUpdate(env, [fileUrl], inputType).then((r) => { - fs.unlinkSync(fileUrl); - }); - } else { - console.log("env error"); - } - }); - }, - }, - { - name: "vite-plugin-kintone-dev:build", - apply: "build", - enforce: "post", - config(config, env) { - let entry: string | string[] | { [entryAlias: string]: string } = - "src/main.ts"; - - // 如果设置了input,则使用设置的值(包括多入口)需要做window的路径判断 - const possibleEntries = [ - "src/main.ts", - "src/main.js", - "src/main.jsx", - "src/main.tsx", - ]; - - if (config.build?.rollupOptions?.input) { - entry = config.build.rollupOptions.input; - } else { - for (const possibleEntry of possibleEntries) { - const filePath = path.resolve(possibleEntry); - if (fs.existsSync(filePath)) { - entry = possibleEntry; - break; - } - } - } - - console.log(`打包入口: ${JSON.stringify(entry)}`); - - envConfig = env; - config.build = { - modulePreload: { polyfill: false }, - // 在 outDir 中生成 manifest.json - manifest: true, - cssCodeSplit: false, - rollupOptions: { - input: entry, - output: { - format: "iife", - }, - }, - }; - }, - configResolved(config) { - viteConfig = config; - }, - async closeBundle() { - const outputDir = path.resolve(viteConfig.build.outDir); - const extList = ["js", "css"]; - const fileList = getDirFiles(outputDir, extList); - const env = validateEnv(envConfig, viteConfig); - if (env) { - //是否需要根据output name来判断是否进行上传? - devUpdate(env, fileList, inputType); - } else { - console.log("env error"); - } - }, - }, - ]; -} diff --git a/example/vue-kintone-vite-demo/vite-plugin-kintone-dev/kintone-types.d.ts b/example/vue-kintone-vite-demo/vite-plugin-kintone-dev/kintone-types.d.ts deleted file mode 100644 index eea2dd8..0000000 --- a/example/vue-kintone-vite-demo/vite-plugin-kintone-dev/kintone-types.d.ts +++ /dev/null @@ -1,61 +0,0 @@ -declare module "kintone-types" { - export type Type = "DESKTOP" | "MOBILE" | "DESKTOP_CSS" | "MOBILE_CSS"; - - export type TypeInput = { - platform: "APP" | "PORTAL"; - type: "DESKTOP" | "MOBILE"; - }; - - export interface JsList { - DESKTOP: string[]; - MOBILE: string[]; - DESKTOP_CSS: string[]; - MOBILE_CSS: string[]; - } - - export interface EnvSetting { - VITE_KINTONE_URL: string; - VITE_KINTONE_USER_NAME: string; - VITE_KINTONE_PASSWORD: string; - VITE_KINTONE_APP: string | undefined; - } - - export interface UploadFileResponse { - fileKey: string; - } - - export type ScriptList = { - type: string | undefined; - src: string | undefined; - }[]; - - export type ScriptsList = { - locationType: string; - type: string; - name: string; - contentUrl: string; - contentId: string; - }[]; - - export interface GetSystemSettingResponse { - result: { scripts: ScriptsList; scope: string }; - } - - export interface GetAppSettingResponse { - result: { scripts: ScriptsList; scope: string }; - } - - export interface UpdateSystemSettingResponse { - result: object; - } - - export interface GetAppInfo { - name: string; - [propName: string]: any; - } - - export type jsFiles = { - jsType: string; - fileKeys: string[]; - }; -} diff --git a/example/vue-kintone-vite-demo/vite-plugin-kintone-dev/kintoneApi.ts b/example/vue-kintone-vite-demo/vite-plugin-kintone-dev/kintoneApi.ts deleted file mode 100644 index a835798..0000000 --- a/example/vue-kintone-vite-demo/vite-plugin-kintone-dev/kintoneApi.ts +++ /dev/null @@ -1,113 +0,0 @@ -import axios, { Axios } from "axios"; -import fs from "node:fs"; -import FormData from "form-data"; -import { - type jsFiles, - type UploadFileResponse, - type GetSystemSettingResponse, - type GetAppSettingResponse, - type GetAppInfo, - type UpdateSystemSettingResponse, -} from "kintone-types"; - -export default class kintoneApi { - http: Axios; - constructor(baseURL: string, username: string, password: string) { - this.http = axios.create({ - baseURL, - timeout: 10000, - headers: { - "X-Cybozu-Authorization": Buffer.from( - `${username}:${password}` - ).toString("base64"), - }, - }); - //响应拦截器 - this.http.interceptors.response.use( - (res) => { - return res.data; - }, - (err) => { - return Promise.reject(new Error(err.message)); - } - ); - } - - //文件上传 - uploadFile(filePath: string, name: string): Promise { - const url = "/k/v1/file.json"; - let formData = new FormData(); - - formData.append("file", fs.createReadStream(filePath), name); - const config = { - headers: { - ...formData.getHeaders(), - }, - }; - - return this.http.post(url, formData, config); - } - - //获取系统设置 - getSystemSetting(): Promise { - const url = "/k/api/js/getSystemSetting.json"; - const header = { - "Content-Type": "application/json", - }; - return this.http.post(url, header); - } - - //更新系统设置 - updateSystemSetting( - jsScope: string, - jsFiles: Array - ): Promise { - const url = "/k/api/js/updateSystemSetting.json"; - const body = { - jsScope, - jsFiles, - }; - return this.http.post(url, body); - } - - //更新系统设置 - getAppInfo(appid: string): Promise { - const url = `/k/v1/app.json?id=${appid}`; - return this.http.get(url); - } - - //获取应用的自定义设置 todo 返回类型调整 - getAppSetting(app: string): Promise { - const url = `/k/api/js/get.json`; - const body = { - app, - }; - return this.http.post(url, body); - } - - //更新应用自定义设置 todo 返回类型调整 - updateAppSetting( - id: string, - jsScope: string, - jsFiles: Array, - name: string - ): Promise { - const url = "/k/api/dev/app/update.json"; - const body = { - id, - jsScope, - jsFiles, - name, - }; - return this.http.post(url, body); - } - - //更新应用自定义设置 todo 返回类型调整 - deploySetting(app: string): Promise { - const url = "/k/api/dev/app/deploy.json"; - const body = { - app, - }; - return this.http.post(url, body); - } -}