@yumerijs/loader 2.0.5 → 2.1.1

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 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;
@@ -59,6 +60,7 @@ class PluginLoader {
59
60
  this.core = core || new core_1.Core(this, undefined, false);
60
61
  this.isDev = process.env.NODE_ENV === 'development';
61
62
  core_1.Logger.setCore(this.core);
63
+ (0, vueLoader_1.registerVueRuntimeLoader)();
62
64
  }
63
65
  /**
64
66
  * Reloads the config file from disk into memory and emits a 'config-reloaded' event.
@@ -67,11 +69,22 @@ class PluginLoader {
67
69
  async reloadConfigFile() {
68
70
  this.logger.info('Reloading config file...');
69
71
  try {
70
- const doc = yaml.load(fs.readFileSync(this.configPath, 'utf8'));
72
+ const ext = path.extname(this.configPath).toLowerCase();
73
+ const fileContent = fs.readFileSync(this.configPath, 'utf8');
74
+ let doc;
75
+ if (ext === '.yaml' || ext === '.yml') {
76
+ doc = yaml.load(fileContent);
77
+ }
78
+ else if (ext === '.json') {
79
+ doc = JSON.parse(fileContent);
80
+ }
81
+ else {
82
+ throw new Error(`Unsupported config file extension: ${ext}`);
83
+ }
71
84
  this.config = doc;
72
85
  this.core.coreConfig = this.config.core || {};
73
86
  this.core.emit('config-reloaded', this.config);
74
- this.logger.info('Config file reloaded.');
87
+ this.logger.info('Config file reloaded successfully.');
75
88
  }
76
89
  catch (e) {
77
90
  this.logger.error('Failed to reload config file:', e);
@@ -80,9 +93,9 @@ class PluginLoader {
80
93
  getCore() {
81
94
  return this.core;
82
95
  }
83
- getContext(pluginName) {
96
+ getContext(pluginName, injections = {}) {
84
97
  if (!this.pluginContexts[pluginName]) {
85
- this.pluginContexts[pluginName] = new core_1.Context(this.core, pluginName);
98
+ this.pluginContexts[pluginName] = new core_1.Context(this.core, pluginName, null, injections);
86
99
  }
87
100
  return this.pluginContexts[pluginName];
88
101
  }
@@ -96,7 +109,14 @@ class PluginLoader {
96
109
  async loadConfig(configPath) {
97
110
  try {
98
111
  this.configPath = configPath;
99
- const doc = yaml.load(fs.readFileSync(configPath, 'utf8'));
112
+ const fileContents = fs.readFileSync(configPath, 'utf8');
113
+ let doc;
114
+ if (path.extname(configPath) === '.json') {
115
+ doc = JSON.parse(fileContents);
116
+ }
117
+ else {
118
+ doc = yaml.load(fileContents);
119
+ }
100
120
  this.config = doc;
101
121
  this.logger.info('Config loaded.');
102
122
  this.core.coreConfig = this.config.core || {};
@@ -172,14 +192,46 @@ class PluginLoader {
172
192
  if (!pluginInstance) {
173
193
  throw new Error('Plugin loader returned no instance.');
174
194
  }
195
+ // Auto-load and register renderer if declared
196
+ if (pluginInstance.render && typeof pluginInstance.render === 'string') {
197
+ const rendererName = pluginInstance.render;
198
+ this.logger.info(`Plugin "${pluginName}" requires renderer "${rendererName}".`);
199
+ this.core.pluginRenderers.set(pluginName, rendererName);
200
+ if (!this.core.renderers.has(rendererName)) {
201
+ this.logger.info(`Renderer "${rendererName}" is not registered. Attempting to auto-load...`);
202
+ try {
203
+ const rendererPackageMap = {
204
+ 'vue': '@yumerijs/vue-renderer',
205
+ 'react': '@yumerijs/react-renderer'
206
+ };
207
+ const rendererPackageName = rendererPackageMap[rendererName] || rendererName;
208
+ this.logger.info(`Loading renderer package: "${rendererPackageName}"...`);
209
+ const RendererClass = require(rendererPackageName);
210
+ // Handle both ES modules (default export) and CommonJS modules
211
+ const ActualRendererClass = RendererClass.default || RendererClass;
212
+ const rendererInstance = new ActualRendererClass();
213
+ this.core.addRenderer(rendererInstance);
214
+ this.logger.info(`Successfully loaded and registered renderer "${rendererName}".`);
215
+ }
216
+ catch (err) {
217
+ this.logger.error(`Failed to auto-load renderer package for "${rendererName}". Please make sure the renderer package is installed.`);
218
+ this.logger.error(err);
219
+ }
220
+ }
221
+ }
175
222
  const deps = pluginInstance.depend || [];
176
223
  const unmetDependencies = deps.filter(dep => !this.core.components[dep]);
177
224
  if (unmetDependencies.length > 0) {
178
225
  return false;
179
226
  }
180
227
  this.plugins[pluginName] = pluginInstance;
228
+ const depend = pluginInstance.depend || [];
229
+ let injections = {};
230
+ for (const injection of depend) {
231
+ injections[injection] = this.core.getComponent(injection);
232
+ }
181
233
  const pluginConfig = await this.getPluginConfig(pluginName);
182
- const context = this.getContext(pluginName);
234
+ const context = this.getContext(pluginName, injections);
183
235
  await this.core.plugin(pluginInstance, context, pluginConfig);
184
236
  this.pluginStatus[pluginName] = "enabled" /* PluginStatus.ENABLED */;
185
237
  if (triggerPendingCheck) {
@@ -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.5",
3
+ "version": "2.1.1",
4
4
  "description": "Module loader for yumeri",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -29,13 +29,14 @@
29
29
  "@types/js-yaml": "^4.0.9",
30
30
  "@types/node": "^22.13.10",
31
31
  "@yumerijs/core": "^2.0.1",
32
- "esbuild": "^0.25.9",
33
32
  "esbuild-register": "^3.6.0",
34
33
  "typescript": "^5.8.2"
35
34
  },
36
35
  "dependencies": {
37
36
  "@types/js-yaml": "^4.0.9",
37
+ "@vue/compiler-sfc": "^3.5.25",
38
38
  "chokidar": "^4.0.3",
39
+ "esbuild": "^0.25.9",
39
40
  "js-yaml": "^4.1.0"
40
41
  },
41
42
  "peerDependencies": {