@yumerijs/loader 2.0.4 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -1
- package/dist/index.js +54 -9
- package/dist/runtime/vueLoader.d.ts +1 -0
- package/dist/runtime/vueLoader.js +95 -0
- package/package.json +5 -3
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ interface Plugin {
|
|
|
4
4
|
disable: (ctx: Context) => Promise<void>;
|
|
5
5
|
depend: Array<string>;
|
|
6
6
|
provide: Array<string>;
|
|
7
|
+
render?: string;
|
|
7
8
|
}
|
|
8
9
|
export declare class PluginLoader {
|
|
9
10
|
private pluginsDir;
|
|
@@ -29,7 +30,7 @@ export declare class PluginLoader {
|
|
|
29
30
|
*/
|
|
30
31
|
reloadConfigFile(): Promise<void>;
|
|
31
32
|
getCore(): Core;
|
|
32
|
-
getContext(pluginName: string): Context;
|
|
33
|
+
getContext(pluginName: string, injections?: Record<string, any>): Context;
|
|
33
34
|
unregall(pluginName: string): void;
|
|
34
35
|
loadConfig(configPath: string): Promise<void>;
|
|
35
36
|
getPluginConfig(pluginName: string): Promise<Config>;
|
package/dist/index.js
CHANGED
|
@@ -41,6 +41,7 @@ const util_1 = require("util");
|
|
|
41
41
|
const child_process_1 = require("child_process");
|
|
42
42
|
const yaml = __importStar(require("js-yaml"));
|
|
43
43
|
const chokidar = __importStar(require("chokidar"));
|
|
44
|
+
const vueLoader_1 = require("./runtime/vueLoader");
|
|
44
45
|
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
45
46
|
class PluginLoader {
|
|
46
47
|
pluginsDir;
|
|
@@ -57,10 +58,9 @@ class PluginLoader {
|
|
|
57
58
|
constructor(core, pluginsDir = 'plugins') {
|
|
58
59
|
this.pluginsDir = pluginsDir;
|
|
59
60
|
this.core = core || new core_1.Core(this, undefined, false);
|
|
60
|
-
this.core.uuid = Math.random().toString(36).substring(2);
|
|
61
|
-
this.logger.info(`[DIAG] Loader created/received Core instance with UUID: ${this.core.uuid}`);
|
|
62
61
|
this.isDev = process.env.NODE_ENV === 'development';
|
|
63
62
|
core_1.Logger.setCore(this.core);
|
|
63
|
+
(0, vueLoader_1.registerVueRuntimeLoader)();
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
66
|
* Reloads the config file from disk into memory and emits a 'config-reloaded' event.
|
|
@@ -82,9 +82,9 @@ class PluginLoader {
|
|
|
82
82
|
getCore() {
|
|
83
83
|
return this.core;
|
|
84
84
|
}
|
|
85
|
-
getContext(pluginName) {
|
|
85
|
+
getContext(pluginName, injections = {}) {
|
|
86
86
|
if (!this.pluginContexts[pluginName]) {
|
|
87
|
-
this.pluginContexts[pluginName] = new core_1.Context(this.core, pluginName);
|
|
87
|
+
this.pluginContexts[pluginName] = new core_1.Context(this.core, pluginName, null, injections);
|
|
88
88
|
}
|
|
89
89
|
return this.pluginContexts[pluginName];
|
|
90
90
|
}
|
|
@@ -174,14 +174,46 @@ class PluginLoader {
|
|
|
174
174
|
if (!pluginInstance) {
|
|
175
175
|
throw new Error('Plugin loader returned no instance.');
|
|
176
176
|
}
|
|
177
|
+
// Auto-load and register renderer if declared
|
|
178
|
+
if (pluginInstance.render && typeof pluginInstance.render === 'string') {
|
|
179
|
+
const rendererName = pluginInstance.render;
|
|
180
|
+
this.logger.info(`Plugin "${pluginName}" requires renderer "${rendererName}".`);
|
|
181
|
+
this.core.pluginRenderers.set(pluginName, rendererName);
|
|
182
|
+
if (!this.core.renderers.has(rendererName)) {
|
|
183
|
+
this.logger.info(`Renderer "${rendererName}" is not registered. Attempting to auto-load...`);
|
|
184
|
+
try {
|
|
185
|
+
const rendererPackageMap = {
|
|
186
|
+
'vue': '@yumerijs/vue-renderer',
|
|
187
|
+
'react': '@yumerijs/react-renderer'
|
|
188
|
+
};
|
|
189
|
+
const rendererPackageName = rendererPackageMap[rendererName] || rendererName;
|
|
190
|
+
this.logger.info(`Loading renderer package: "${rendererPackageName}"...`);
|
|
191
|
+
const RendererClass = require(rendererPackageName);
|
|
192
|
+
// Handle both ES modules (default export) and CommonJS modules
|
|
193
|
+
const ActualRendererClass = RendererClass.default || RendererClass;
|
|
194
|
+
const rendererInstance = new ActualRendererClass();
|
|
195
|
+
this.core.addRenderer(rendererInstance);
|
|
196
|
+
this.logger.info(`Successfully loaded and registered renderer "${rendererName}".`);
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
this.logger.error(`Failed to auto-load renderer package for "${rendererName}". Please make sure the renderer package is installed.`);
|
|
200
|
+
this.logger.error(err);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
177
204
|
const deps = pluginInstance.depend || [];
|
|
178
205
|
const unmetDependencies = deps.filter(dep => !this.core.components[dep]);
|
|
179
206
|
if (unmetDependencies.length > 0) {
|
|
180
207
|
return false;
|
|
181
208
|
}
|
|
182
209
|
this.plugins[pluginName] = pluginInstance;
|
|
210
|
+
const depend = pluginInstance.depend || [];
|
|
211
|
+
let injections = {};
|
|
212
|
+
for (const injection of depend) {
|
|
213
|
+
injections[injection] = this.core.getComponent(injection);
|
|
214
|
+
}
|
|
183
215
|
const pluginConfig = await this.getPluginConfig(pluginName);
|
|
184
|
-
const context = this.getContext(pluginName);
|
|
216
|
+
const context = this.getContext(pluginName, injections);
|
|
185
217
|
await this.core.plugin(pluginInstance, context, pluginConfig);
|
|
186
218
|
this.pluginStatus[pluginName] = "enabled" /* PluginStatus.ENABLED */;
|
|
187
219
|
if (triggerPendingCheck) {
|
|
@@ -278,15 +310,28 @@ class PluginLoader {
|
|
|
278
310
|
* @param pluginName The name of the plugin to reload.
|
|
279
311
|
*/
|
|
280
312
|
async reloadPlugin(pluginName) {
|
|
281
|
-
this.logger.info(`Reloading plugin
|
|
282
|
-
|
|
283
|
-
|
|
313
|
+
this.logger.info(`Reloading plugin: "${pluginName}"...`);
|
|
314
|
+
// Clear the module cache for the plugin. This is critical for hot-reloading.
|
|
315
|
+
try {
|
|
316
|
+
const resolvedPath = require.resolve(pluginName);
|
|
317
|
+
this.clearRequireCache(resolvedPath, new Set());
|
|
318
|
+
this.logger.info(`Cache cleared for plugin "${pluginName}".`);
|
|
319
|
+
}
|
|
320
|
+
catch (e) {
|
|
321
|
+
this.logger.error(`Could not resolve path for plugin ${pluginName} to clear cache.`, e);
|
|
322
|
+
}
|
|
323
|
+
// Reload the configuration from disk to catch any changes.
|
|
324
|
+
await this.reloadConfigFile();
|
|
325
|
+
// Unload the plugin and its dependents.
|
|
326
|
+
await this.unloadPlugin(pluginName, true);
|
|
327
|
+
// Load the plugin again. This will also trigger a check for other pending plugins.
|
|
284
328
|
const success = await this.loadSinglePlugin(pluginName);
|
|
285
329
|
if (success) {
|
|
330
|
+
this.logger.info(`Plugin "${pluginName}" reloaded successfully.`);
|
|
286
331
|
this.core.emit('plugin-reloaded', pluginName);
|
|
287
332
|
}
|
|
288
333
|
else {
|
|
289
|
-
this.logger.error(`Failed to reload plugin "${pluginName}". It may have unmet dependencies.`);
|
|
334
|
+
this.logger.error(`Failed to reload plugin "${pluginName}". It may have unmet dependencies or other errors.`);
|
|
290
335
|
}
|
|
291
336
|
}
|
|
292
337
|
watchPlugin(pluginName, pluginPath) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerVueRuntimeLoader(): void;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.registerVueRuntimeLoader = registerVueRuntimeLoader;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
9
|
+
const compiler_sfc_1 = require("@vue/compiler-sfc");
|
|
10
|
+
const esbuild_1 = require("esbuild");
|
|
11
|
+
let vueLoaderRegistered = false;
|
|
12
|
+
const SUPPORTED_ESBUILD_LOADERS = new Set(['js', 'ts', 'tsx', 'jsx']);
|
|
13
|
+
function getScopeId(filename) {
|
|
14
|
+
return crypto_1.default.createHash('md5').update(filename).digest('hex').slice(0, 8);
|
|
15
|
+
}
|
|
16
|
+
function inferLoader(lang) {
|
|
17
|
+
if (!lang)
|
|
18
|
+
return 'js';
|
|
19
|
+
return SUPPORTED_ESBUILD_LOADERS.has(lang) ? lang : 'js';
|
|
20
|
+
}
|
|
21
|
+
function registerVueRuntimeLoader() {
|
|
22
|
+
if (vueLoaderRegistered) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
vueLoaderRegistered = true;
|
|
26
|
+
require.extensions['.vue'] = function registerVueSFC(module, filename) {
|
|
27
|
+
const nodeModule = module;
|
|
28
|
+
try {
|
|
29
|
+
const source = fs_1.default.readFileSync(filename, 'utf8');
|
|
30
|
+
const { descriptor } = (0, compiler_sfc_1.parse)(source, { filename });
|
|
31
|
+
if (!descriptor.script && !descriptor.scriptSetup && !descriptor.template) {
|
|
32
|
+
nodeModule._compile('module.exports = {};\n', filename);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const id = getScopeId(filename);
|
|
36
|
+
let code = '';
|
|
37
|
+
const lang = descriptor.scriptSetup?.lang || descriptor.script?.lang;
|
|
38
|
+
if (!descriptor.script && !descriptor.scriptSetup && descriptor.template) {
|
|
39
|
+
const templateResult = (0, compiler_sfc_1.compileTemplate)({
|
|
40
|
+
id,
|
|
41
|
+
filename,
|
|
42
|
+
source: descriptor.template.content,
|
|
43
|
+
ssr: true
|
|
44
|
+
});
|
|
45
|
+
code = `
|
|
46
|
+
import { defineComponent } from 'vue';
|
|
47
|
+
${templateResult.code}
|
|
48
|
+
|
|
49
|
+
const __component__ = defineComponent({});
|
|
50
|
+
__component__.ssrRender = ssrRender;
|
|
51
|
+
|
|
52
|
+
export default __component__;
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
const compiled = (0, compiler_sfc_1.compileScript)(descriptor, {
|
|
57
|
+
id,
|
|
58
|
+
inlineTemplate: Boolean(descriptor.template),
|
|
59
|
+
templateOptions: {
|
|
60
|
+
ssr: true
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
code = compiled.content;
|
|
64
|
+
}
|
|
65
|
+
const transformed = (0, esbuild_1.transformSync)(code, {
|
|
66
|
+
loader: inferLoader(lang),
|
|
67
|
+
format: 'cjs',
|
|
68
|
+
target: 'node18',
|
|
69
|
+
sourcemap: 'inline',
|
|
70
|
+
sourcefile: filename
|
|
71
|
+
});
|
|
72
|
+
const metadataCode = `
|
|
73
|
+
const __yumeri_raw__ = module.exports && module.exports.__esModule ? module.exports.default : module.exports;
|
|
74
|
+
const __yumeri_target__ = typeof __yumeri_raw__ === 'function' || (typeof __yumeri_raw__ === 'object' && __yumeri_raw__ !== null)
|
|
75
|
+
? __yumeri_raw__
|
|
76
|
+
: null;
|
|
77
|
+
if (__yumeri_target__) {
|
|
78
|
+
Object.defineProperty(__yumeri_target__, '__file', {
|
|
79
|
+
value: ${JSON.stringify(filename)},
|
|
80
|
+
enumerable: false,
|
|
81
|
+
configurable: true,
|
|
82
|
+
writable: true,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
`;
|
|
86
|
+
nodeModule._compile(`${transformed.code}\n${metadataCode}`, filename);
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
const friendlyMessage = new Error(`[yumeri] Failed to compile Vue SFC "${filename}". ` +
|
|
90
|
+
`Make sure @vue/compiler-sfc can parse the file. Original error: ${error.message}`);
|
|
91
|
+
friendlyMessage.stack = error.stack;
|
|
92
|
+
throw friendlyMessage;
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yumerijs/loader",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Module loader for yumeri",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"dist"
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsc"
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
12
13
|
},
|
|
13
14
|
"repository": {
|
|
14
15
|
"type": "git",
|
|
@@ -28,13 +29,14 @@
|
|
|
28
29
|
"@types/js-yaml": "^4.0.9",
|
|
29
30
|
"@types/node": "^22.13.10",
|
|
30
31
|
"@yumerijs/core": "^2.0.1",
|
|
31
|
-
"esbuild": "^0.25.9",
|
|
32
32
|
"esbuild-register": "^3.6.0",
|
|
33
33
|
"typescript": "^5.8.2"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@types/js-yaml": "^4.0.9",
|
|
37
|
+
"@vue/compiler-sfc": "^3.5.25",
|
|
37
38
|
"chokidar": "^4.0.3",
|
|
39
|
+
"esbuild": "^0.25.9",
|
|
38
40
|
"js-yaml": "^4.1.0"
|
|
39
41
|
},
|
|
40
42
|
"peerDependencies": {
|