Skip to content

Commit

Permalink
feat(vite-plugin-blocklet): support hot reload api code (#329)
Browse files Browse the repository at this point in the history
* feat(vite-plugin-blocklet): support hot reload api code

* chore(release): v0.8.3
  • Loading branch information
LancelotLewis authored Jul 7, 2024
1 parent 96859c8 commit 1d18207
Show file tree
Hide file tree
Showing 27 changed files with 114 additions and 62 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.8.3 (2024-7-7)

- feat(vite-plugin-blocklet): support hot reload api code

## 0.8.2 (2024-7-4)

- fix(create-app): fix monorepo create should create multi did
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "create-blocklet",
"private": true,
"version": "0.8.2",
"version": "0.8.3",
"description": "",
"keywords": [],
"author": "",
Expand Down
2 changes: 1 addition & 1 deletion packages/create-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-blocklet",
"version": "0.8.2",
"version": "0.8.3",
"exports": "./index.js",
"type": "module",
"repository": "git@github.com:blocklet/create-blocklet.git",
Expand Down
2 changes: 1 addition & 1 deletion packages/create-app/templates/react-dapp-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@
"ts-node": "^10.9.2",
"typescript": "^5.5.2",
"vite": "^5.3.2",
"vite-plugin-blocklet": "^0.8.2",
"vite-plugin-blocklet": "^0.8.3",
"vite-plugin-svgr": "^4.2.0",
"zx": "^8.1.3"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/create-app/templates/react-dapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"rimraf": "^5.0.7",
"simple-git-hooks": "^2.11.1",
"vite": "^5.3.2",
"vite-plugin-blocklet": "^0.8.2",
"vite-plugin-blocklet": "^0.8.3",
"vite-plugin-svgr": "^4.2.0",
"zx": "^8.1.3"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/create-app/templates/react-gun-dapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"rimraf": "^5.0.7",
"simple-git-hooks": "^2.11.1",
"vite": "^5.3.2",
"vite-plugin-blocklet": "^0.8.2",
"vite-plugin-blocklet": "^0.8.3",
"vite-plugin-svgr": "^4.2.0",
"zx": "^8.1.3"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/create-app/templates/react-static/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"rimraf": "^5.0.7",
"simple-git-hooks": "^2.11.1",
"vite": "^5.3.2",
"vite-plugin-blocklet": "^0.8.2",
"vite-plugin-blocklet": "^0.8.3",
"vite-plugin-svgr": "^4.2.0",
"zx": "^8.1.3"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/create-app/templates/solidjs-dapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"rimraf": "^5.0.7",
"simple-git-hooks": "^2.11.1",
"vite": "^5.3.2",
"vite-plugin-blocklet": "^0.8.2",
"vite-plugin-blocklet": "^0.8.3",
"vite-plugin-solid": "^2.10.2",
"zx": "^8.1.3"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/create-app/templates/solidjs-static/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"simple-git-hooks": "^2.11.1",
"solid-js": "^1.8.18",
"vite": "^5.3.2",
"vite-plugin-blocklet": "^0.8.2",
"vite-plugin-blocklet": "^0.8.3",
"vite-plugin-solid": "^2.10.2",
"zx": "^8.1.3"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/create-app/templates/svelte-dapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"simple-git-hooks": "^2.11.1",
"svelte": "^4.2.18",
"vite": "^5.3.2",
"vite-plugin-blocklet": "^0.8.2",
"vite-plugin-blocklet": "^0.8.3",
"zx": "^8.1.3"
},
"lint-staged": {
Expand Down
2 changes: 1 addition & 1 deletion packages/create-app/templates/svelte-static/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"simple-git-hooks": "^2.11.1",
"svelte": "^4.2.18",
"vite": "^5.3.2",
"vite-plugin-blocklet": "^0.8.2",
"vite-plugin-blocklet": "^0.8.3",
"zx": "^8.1.3"
},
"lint-staged": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
"ts-node": "^10.9.2",
"typescript": "^5.5.2",
"vite": "^5.3.2",
"vite-plugin-blocklet": "^0.8.2",
"vite-plugin-blocklet": "^0.8.3",
"vite-plugin-svgr": "^4.2.0",
"zx": "^8.1.3"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/create-app/templates/vue-dapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"rimraf": "^5.0.7",
"simple-git-hooks": "^2.11.1",
"vite": "^5.3.2",
"vite-plugin-blocklet": "^0.8.2",
"vite-plugin-blocklet": "^0.8.3",
"vue": "^3.4.31",
"zx": "^8.1.3"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/create-app/templates/vue-static/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"rimraf": "^5.0.7",
"simple-git-hooks": "^2.11.1",
"vite": "^5.3.2",
"vite-plugin-blocklet": "^0.8.2",
"vite-plugin-blocklet": "^0.8.3",
"vue": "^3.4.31",
"zx": "^8.1.3"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/create-app/templates/vue2-dapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"rimraf": "^5.0.7",
"simple-git-hooks": "^2.11.1",
"vite": "^5.3.2",
"vite-plugin-blocklet": "^0.8.2",
"vite-plugin-blocklet": "^0.8.3",
"vue": "^2.7.16",
"zx": "^8.1.3"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/create-app/templates/vue2-static/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"rimraf": "^5.0.7",
"simple-git-hooks": "^2.11.1",
"vite": "^5.3.2",
"vite-plugin-blocklet": "^0.8.2",
"vite-plugin-blocklet": "^0.8.3",
"vue": "^2.7.16",
"zx": "^8.1.3"
},
Expand Down
7 changes: 6 additions & 1 deletion plugins/vite-plugin-blocklet/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import createConfigPlugin from './libs/config.js';
import createMetaPlugin from './libs/meta.js';
import createLoadingPlugin from './libs/loading.js';
import createDebugPlugin from './libs/debug.js';
import createExpressPlugin from './libs/express.js';
import setupClient from './libs/client.js';

/**
Expand All @@ -16,12 +17,14 @@ import setupClient from './libs/client.js';
* @property {boolean} [disableHmr=false] - Disable hmr plugin.
* @property {boolean} [disableLoading=false] - Disable loading plugin.
* @property {boolean} [disableDebug=false] - Disable debug plugin.
* @property {import('vite-plugin-node-polyfills').PolyfillOptions} [nodePolyfillsOptions]
*
* @property {string} [loadingElementId]
* @property {string} [loadingColor]
* @property {string} [loadingImage]
* @property {'all'|'mobile'|'desktop'} [debugPlatform='mobile']
* @property {string} [debugScript]
* @property {'middleware'|'client'} [hmrMode='middleware']
*/

/**
Expand All @@ -40,6 +43,7 @@ export function createBlockletPlugin(options = {}) {
disableHmr = false,
disableLoading = false,
disableDebug = false,
nodePolyfillsOptions,
...restOptions
} = options;

Expand All @@ -56,7 +60,7 @@ export function createBlockletPlugin(options = {}) {
plugins.push(createHmrPlugin(restOptions));
}
if (!disableNodePolyfills) {
plugins.push(nodePolyfills({ protocolImports: true }));
plugins.push(nodePolyfills(nodePolyfillsOptions));
}
if (!disableLoading) {
plugins.push(createLoadingPlugin(restOptions));
Expand All @@ -75,5 +79,6 @@ export {
createMetaPlugin as createBlockletMeta,
createLoadingPlugin as createBlockletLoading,
createDebugPlugin as createBlockletDebug,
createExpressPlugin as createBlockletExpress,
nodePolyfills,
};
6 changes: 4 additions & 2 deletions plugins/vite-plugin-blocklet/libs/client.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import path from 'node:path';
import getPort from 'get-port';
import { createServer } from 'vite';
import mri from 'mri';
import { createProxyMiddleware } from 'http-proxy-middleware';
import { blockletPrefix } from './utils';

const argv = process.argv.slice(2);
const isProduction = process.env.NODE_ENV === 'production' || process.env.ABT_NODE_SERVICE_ENV === 'production';
Expand Down Expand Up @@ -32,7 +34,8 @@ export default async function setupClient(app, options = {}) {
target: `ws://127.0.0.1:${port}`,
ws: true,
});
app.use('/__vite_hmr__', wsProxy);
process.env.VITE_HMR_MODE = 'middleware';
app.use(path.join(blockletPrefix, '/__vite_hmr__'), wsProxy);

// 以中间件模式创建 Vite 服务器
const vite = await createServer({
Expand All @@ -41,7 +44,6 @@ export default async function setupClient(app, options = {}) {
middlewareMode: true,
hmr: {
port,
clientPort: 80,
path: '/__vite_hmr__',
},
},
Expand Down
23 changes: 5 additions & 18 deletions plugins/vite-plugin-blocklet/libs/config.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import fs from 'node:fs';
import path from 'node:path';
import YAML from 'yaml';
import { toBlockletDid, isInBlocklet } from './utils.js';
import { toBlockletDid, isInBlocklet, blockletPort, blockletPrefix } from './utils.js';

export default function createConfigPlugin() {
return {
name: 'blocklet:config',
configureServer(server) {
if (isInBlocklet) {
server.middlewares.use((req, res, next) => {
const prefix = req.headers['x-path-prefix'] || '/';
// blocklet server 会把设置的 base 从请求 url 中移除,所以需要再加回 base
if (!req.url.startsWith(prefix)) {
req.url = path.join(prefix || '/', req.url);
if (!req.url.startsWith(blockletPrefix)) {
req.url = path.join(blockletPrefix || '/', req.url);
}
return next();
});
Expand All @@ -21,21 +20,9 @@ export default function createConfigPlugin() {
config(config, { command }) {
if (command === 'serve') {
const targetConfig = {};
if (!config.base) {
let base = process.env.BLOCKLET_DEV_MOUNT_POINT || '';

if (base) {
if (!base.startsWith('/')) {
base = `/${base}`;
}
if (!base.endsWith('/')) {
base = `${base}/`;
}
}
targetConfig.base = base;
}
targetConfig.base = path.join('/', config.base || blockletPrefix, '/');
if (!(config.server && config.server.port)) {
const port = process.env.BLOCKLET_PORT || 3000;
const port = blockletPort || 3000;
targetConfig.server = {
port,
};
Expand Down
47 changes: 47 additions & 0 deletions plugins/vite-plugin-blocklet/libs/express.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import path from 'node:path';
/**
* Creates a config plugin for Vite development server.
*
* @param {object} options - The options for the plugin.
* @param {string} options.entryPath - The entry path of the Express app.
* @return {object} The Vite config plugin.
*/
export default function createExpressPlugin({ entryPath }) {
// 此处需要解构 import.meta 对象,才能拿到 vite.config 的地址,否则拿到的地址会是 express.js 文件的地址
const { dirname } = { ...import.meta };
const fullPath = path.join(dirname, entryPath);
const folderPath = path.dirname(fullPath);
return {
name: 'blocklet:express',
apply: 'serve',
configureServer(server) {
server.middlewares.use(async (req, res, next) => {
process.env.VITE = 'true';
try {
const { app } = await server.ssrLoadModule(entryPath);
app(req, res, next);
} catch (err) {
console.error(err);
}
});
},
handleHotUpdate({ server, modules, timestamp }) {
const validatedModules = [];
const invalidatedModules = [];

for (const mod of modules) {
if (mod.file.includes(folderPath)) {
invalidatedModules.push(mod);
} else {
validatedModules.push(mod);
}
}
// 手动使模块失效
const mods = new Set();
for (const mod of invalidatedModules) {
server.moduleGraph.invalidateModule(mod, mods, timestamp, true);
}
return validatedModules;
},
};
}
43 changes: 25 additions & 18 deletions plugins/vite-plugin-blocklet/libs/hmr.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import { version as viteVersion } from 'vite';
import semver from 'semver';
import { isInBlocklet } from './utils.js';
import { blockletPrefix, isInBlocklet } from './utils.js';

/**
* Creates a HMR plugin with the given options.
*
* @param {Object} options - The options for the HMR plugin.
* @param {string} options.version - The version of the vite version.
* @param {'middleware'|'client'} options.hmrMode - The version of the vite version.
* @return {Object} The HMR plugin object.
*/
export default function createHmrPlugin(options = {}) {
const { version = viteVersion } = options;
return {
name: 'blocklet:hmr',
apply: 'serve',
async transform(code, id) {
transform(code, id) {
if (isInBlocklet && id.endsWith('/vite/dist/client/client.mjs')) {
const pureVersion = semver.major(version);
let replacedCode = code;
Expand All @@ -27,29 +28,35 @@ export default function createHmrPlugin(options = {}) {
return replacedCode;
}

// 根据页面的协议自动判断端口
replacedCode = replacedCode.replace(
/__HMR_PORT__/g,
'location.port || (location.protocol === "https:" ? 443 : 80);',
);
replacedCode = replacedCode.replace(/__HMR_BASE__/g, `"${blockletPrefix}"+__HMR_BASE__`);

// 在页面加载时,触发一次 upgrade
replacedCode = replacedCode.replace(
'function setupWebSocket(protocol, hostAndPath, onCloseWithoutOpen) {',
'async function setupWebSocket(protocol, hostAndPath, onCloseWithoutOpen) {\nawait waitForSuccessfulPing(protocol, hostAndPath);\n',
);
replacedCode = replacedCode.replace('fallback = () => {', 'fallback = async () => {');
replacedCode = replacedCode.replace(/socket = setupWebSocket\(/g, 'socket = await setupWebSocket(');
if (process.env.VITE_HMR_MODE === 'middleware') {
// 根据页面的协议自动判断端口
replacedCode = replacedCode.replace(
/__HMR_PORT__/g,
'location.port || (location.protocol === "https:" ? 443 : 80);',
);

if ([4, 5].includes(pureVersion)) {
// 改变刷新页面的判断
// 在页面加载时,触发一次 upgrade
replacedCode = replacedCode.replace(
'const ping =',
"const ping = async () => {\ntry {\nawait fetch(`${pingHostProtocol}://${hostAndPath}`, {\nmode: 'no-cors',\nheaders: {\nAccept: 'text/x-vite-ping'\n}\n}).then(res => {\nif ([404, 502].includes(res.status)) {\nthrow new Error('waiting for server to restart...');\n}\n});\nreturn true;\n} catch {}\nreturn false;\n}\nconst pingBak =",
'function setupWebSocket(protocol, hostAndPath, onCloseWithoutOpen) {',
'async function setupWebSocket(protocol, hostAndPath, onCloseWithoutOpen) {\nawait waitForSuccessfulPing(protocol, hostAndPath);\n',
);
replacedCode = replacedCode.replace('fallback = () => {', 'fallback = async () => {');
replacedCode = replacedCode.replace(/socket = setupWebSocket\(/g, 'socket = await setupWebSocket(');

if ([4, 5].includes(pureVersion)) {
// 改变刷新页面的判断
replacedCode = replacedCode.replace(
'const ping =',
"const ping = async () => {\ntry {\nawait fetch(`${pingHostProtocol}://${hostAndPath}`, {\nmode: 'no-cors',\nheaders: {\nAccept: 'text/x-vite-ping'\n}\n}).then(res => {\nif ([404, 502].includes(res.status)) {\nthrow new Error('waiting for server to restart...');\n}\n});\nreturn true;\n} catch {}\nreturn false;\n}\nconst pingBak =",
);
}
}

return replacedCode;
}
return code;
},
};
}
6 changes: 3 additions & 3 deletions plugins/vite-plugin-blocklet/libs/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ export function toBlockletDid(name) {
return fromPublicKey(pk, { role: types.RoleType.ROLE_ANY });
}

const isInBlocklet = !!process.env.BLOCKLET_PORT;

export { isInBlocklet };
export const isInBlocklet = !!process.env.BLOCKLET_PORT;
export const blockletPort = process.env.BLOCKLET_PORT;
export const blockletPrefix = process.env.BLOCKLET_DEV_MOUNT_POINT || '/';
Loading

0 comments on commit 1d18207

Please sign in to comment.