vite-plugin-fenom 1.0.4 → 1.0.6
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/package.json +1 -1
- package/vite-plugin-fenom.cjs +104 -61
- package/vite-plugin-fenom.cjs.map +1 -1
- package/vite-plugin-fenom.mjs +104 -61
- package/vite-plugin-fenom.mjs.map +1 -1
package/package.json
CHANGED
package/vite-plugin-fenom.cjs
CHANGED
|
@@ -46,11 +46,58 @@ function fenomPlugin(options = {}) {
|
|
|
46
46
|
const { pages = 'src/pages', data = 'src/data/**/*.json', root = 'src', minifyHtml = true, debug = false, } = options;
|
|
47
47
|
let config;
|
|
48
48
|
let templateLoader;
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
let globalData = {}; // ← храним глобальные данные
|
|
50
|
+
// === Функция загрузки глобальных JSON-данных ===
|
|
51
|
+
async function loadGlobalData(rootDir) {
|
|
52
|
+
const { default: fastGlob } = await Promise.resolve().then(function () { return require('./index-CDmV2arq.js'); }).then(function (n) { return n.index; });
|
|
53
|
+
const dataGlob = data;
|
|
54
|
+
let baseDir = dataGlob;
|
|
55
|
+
if (baseDir.includes('**')) {
|
|
56
|
+
baseDir = baseDir.substring(0, baseDir.indexOf('**')).replace(/[/\\]+$/, '');
|
|
57
|
+
}
|
|
58
|
+
const fullPath = require$$0.resolve(rootDir, baseDir);
|
|
59
|
+
const globPattern = dataGlob.replace(baseDir, '').replace(/^\//, ''); // → **/*.json
|
|
60
|
+
if (debug) {
|
|
61
|
+
console.log('[Fenom Plugin] Base dir:', fullPath);
|
|
62
|
+
console.log('[Fenom Plugin] Glob pattern:', globPattern);
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
await fs__namespace.access(fullPath);
|
|
66
|
+
}
|
|
67
|
+
catch (_a) {
|
|
68
|
+
console.warn(`[Fenom Plugin] Data directory not found: ${fullPath}`);
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
const files = await fastGlob(globPattern, {
|
|
72
|
+
cwd: fullPath,
|
|
73
|
+
absolute: true,
|
|
74
|
+
onlyFiles: true,
|
|
75
|
+
dot: true,
|
|
76
|
+
});
|
|
77
|
+
if (debug) {
|
|
78
|
+
console.log('Found JSON files:', files);
|
|
79
|
+
}
|
|
80
|
+
const jsonData = {};
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
try {
|
|
83
|
+
const content = await fs__namespace.readFile(file, 'utf-8');
|
|
84
|
+
const parsed = JSON.parse(content);
|
|
85
|
+
const fileName = require$$0.basename(file, '.json');
|
|
86
|
+
jsonData[fileName] = parsed;
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
console.warn(`[Fenom Plugin] Failed to load: ${file}`, err);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (debug)
|
|
93
|
+
console.log('\x1b[36m[Fenom Plugin]\x1b[0m Loaded data keys:', Object.keys(jsonData));
|
|
94
|
+
if (debug)
|
|
95
|
+
console.log(jsonData);
|
|
96
|
+
return jsonData;
|
|
97
|
+
}
|
|
51
98
|
return {
|
|
52
99
|
name: 'vite-plugin-fenom',
|
|
53
|
-
configResolved(resolvedConfig) {
|
|
100
|
+
async configResolved(resolvedConfig) {
|
|
54
101
|
config = resolvedConfig;
|
|
55
102
|
if (debug)
|
|
56
103
|
console.log('\x1b[36m[Fenom Plugin]\x1b[0m Config resolved', {
|
|
@@ -58,28 +105,60 @@ function fenomPlugin(options = {}) {
|
|
|
58
105
|
command: config.command,
|
|
59
106
|
root: config.root,
|
|
60
107
|
});
|
|
108
|
+
// === Загружаем глобальные данные при старте ===
|
|
109
|
+
try {
|
|
110
|
+
globalData = await loadGlobalData(config.root);
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
console.error('\x1b[31m[Fenom Plugin]\x1b[0m Failed to load global data:', err);
|
|
114
|
+
}
|
|
61
115
|
},
|
|
62
116
|
configureServer(server) {
|
|
63
117
|
if (debug)
|
|
64
118
|
console.log('\x1b[36m[Fenom Plugin]\x1b[0m Dev server setup started...');
|
|
65
|
-
// Создаём загрузчик шаблонов
|
|
66
119
|
templateLoader = fenomJsExports.createAsyncLoader(root);
|
|
67
120
|
if (debug)
|
|
68
121
|
console.log('\x1b[36m[Fenom Plugin]\x1b[0m Template loader created for root:', root);
|
|
69
|
-
//
|
|
70
|
-
|
|
122
|
+
// === Добавляем отслеживание ===
|
|
123
|
+
const tplDir = require$$0.resolve(config.root, pages);
|
|
124
|
+
const dataDir = require$$0.resolve(config.root, data.split('**')[0]);
|
|
125
|
+
server.watcher.add(tplDir);
|
|
126
|
+
server.watcher.add(dataDir);
|
|
127
|
+
if (debug) {
|
|
128
|
+
console.log('[Fenom Plugin] 📂 Watching TPL:', tplDir);
|
|
129
|
+
console.log('[Fenom Plugin] 📂 Watching DATA:', dataDir);
|
|
130
|
+
}
|
|
131
|
+
// === ЕДИНСТВЕННЫЙ обработчик ===
|
|
132
|
+
server.watcher.on('change', async (filePath) => {
|
|
133
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
71
134
|
if (filePath.endsWith('.tpl')) {
|
|
72
135
|
if (debug)
|
|
73
|
-
console.log('[Fenom Plugin] 🔄
|
|
136
|
+
console.log('[Fenom Plugin] 🔄 TPL changed:', filePath);
|
|
74
137
|
server.ws.send({ type: 'full-reload' });
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (normalizedPath.endsWith('.json') && normalizedPath.includes(dataDir.replace(/\\/g, '/'))) {
|
|
141
|
+
if (debug)
|
|
142
|
+
console.log('[Fenom Plugin] 📄 JSON changed:', filePath);
|
|
143
|
+
try {
|
|
144
|
+
const newData = await loadGlobalData(config.root);
|
|
145
|
+
globalData = newData;
|
|
146
|
+
if (debug)
|
|
147
|
+
console.log('\x1b[33m[Fenom Plugin]\x1b[0m Global data reloaded:', Object.keys(newData));
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
console.warn('[Fenom Plugin] Failed to reload JSON data:', err);
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
server.ws.send({ type: 'full-reload' });
|
|
154
|
+
}
|
|
75
155
|
}
|
|
76
156
|
});
|
|
77
|
-
//
|
|
157
|
+
// === handlePageRequest ===
|
|
78
158
|
const handlePageRequest = async (req, res, next) => {
|
|
79
159
|
const url = req.url;
|
|
80
160
|
if (debug)
|
|
81
161
|
console.log('\x1b[36m[Fenom Plugin]\x1b[0m Incoming request:', url);
|
|
82
|
-
// Пропускаем статику, API, системные пути
|
|
83
162
|
if (!url ||
|
|
84
163
|
url.startsWith('/assets/') ||
|
|
85
164
|
url.startsWith('/@') ||
|
|
@@ -90,7 +169,6 @@ function fenomPlugin(options = {}) {
|
|
|
90
169
|
(url.includes('?') && url.includes('.'))) {
|
|
91
170
|
return next();
|
|
92
171
|
}
|
|
93
|
-
// Определяем имя страницы
|
|
94
172
|
let pageName = 'index';
|
|
95
173
|
if (url !== '/') {
|
|
96
174
|
pageName = url.split('?')[0].split('#')[0].replace(/^\/|\/$/g, '');
|
|
@@ -98,15 +176,13 @@ function fenomPlugin(options = {}) {
|
|
|
98
176
|
const templatePath = require$$0.join(pages, `${pageName}.tpl`);
|
|
99
177
|
const relativePath = require$$0.relative(root, templatePath);
|
|
100
178
|
try {
|
|
101
|
-
if (debug)
|
|
102
|
-
console.log('\x1b[36m[Fenom Plugin]\x1b[0m Rendering page:', { pageName, templatePath });
|
|
103
179
|
const source = await templateLoader(relativePath);
|
|
104
180
|
const context = {
|
|
105
181
|
title: `${pageName.charAt(0).toUpperCase() + pageName.slice(1)} Page`,
|
|
106
182
|
debug,
|
|
107
183
|
url,
|
|
184
|
+
...globalData,
|
|
108
185
|
};
|
|
109
|
-
// Рендерим через FenomJs
|
|
110
186
|
let html = await fenomJsExports.FenomJs(source, context, {
|
|
111
187
|
loader: templateLoader,
|
|
112
188
|
root,
|
|
@@ -114,9 +190,9 @@ function fenomPlugin(options = {}) {
|
|
|
114
190
|
});
|
|
115
191
|
if (config.mode === 'development') {
|
|
116
192
|
const hmrScript = `
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
193
|
+
<script type="module">
|
|
194
|
+
import "/@vite/client";
|
|
195
|
+
</script>`;
|
|
120
196
|
if (html.includes('</head>')) {
|
|
121
197
|
html = html.replace('</head>', hmrScript + '\n</head>');
|
|
122
198
|
}
|
|
@@ -127,7 +203,6 @@ function fenomPlugin(options = {}) {
|
|
|
127
203
|
html = hmrScript + html;
|
|
128
204
|
}
|
|
129
205
|
}
|
|
130
|
-
// Отправляем ответ
|
|
131
206
|
res.statusCode = 200;
|
|
132
207
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
133
208
|
res.end(html);
|
|
@@ -139,31 +214,26 @@ function fenomPlugin(options = {}) {
|
|
|
139
214
|
return next();
|
|
140
215
|
}
|
|
141
216
|
console.error('\x1b[36m[Fenom Plugin]\x1b[0m Rendering error:', err.message);
|
|
142
|
-
console.error(err);
|
|
143
217
|
res.statusCode = 500;
|
|
144
|
-
res.
|
|
145
|
-
res.end(`
|
|
146
|
-
<h1>🔧 Ошибка рендеринга</h1>
|
|
147
|
-
<p><strong>${err.message}</strong></p>
|
|
148
|
-
<pre>${err.stack}</pre>
|
|
149
|
-
`);
|
|
218
|
+
res.end(`<h1>🔧 Ошибка</h1><pre>${err.stack}</pre>`);
|
|
150
219
|
}
|
|
151
220
|
};
|
|
152
|
-
|
|
153
|
-
server.middlewares.stack.unshift({
|
|
154
|
-
route: '',
|
|
155
|
-
handle: handlePageRequest,
|
|
156
|
-
});
|
|
221
|
+
server.middlewares.stack.unshift({ route: '', handle: handlePageRequest });
|
|
157
222
|
if (debug)
|
|
158
223
|
console.log('\x1b[36m[Fenom Plugin]\x1b[0m Middleware inserted at top of stack');
|
|
159
|
-
if (debug)
|
|
160
|
-
console.log('\x1b[36m[Fenom Plugin]\x1b[0m Watching .tpl files for HMR');
|
|
161
224
|
},
|
|
162
225
|
async buildStart() {
|
|
163
226
|
if (config.command !== 'build')
|
|
164
227
|
return;
|
|
165
228
|
if (debug)
|
|
166
229
|
console.log('\x1b[36m[Fenom Plugin]\x1b[0m Build started');
|
|
230
|
+
// === Загружаем данные перед сборкой ===
|
|
231
|
+
try {
|
|
232
|
+
globalData = await loadGlobalData(config.root);
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
console.error('\x1b[31m[Fenom Plugin]\x1b[0m Failed to load data on build start:', err);
|
|
236
|
+
}
|
|
167
237
|
},
|
|
168
238
|
async generateBundle(_options, bundle) {
|
|
169
239
|
if (config.command !== 'build')
|
|
@@ -178,7 +248,7 @@ function fenomPlugin(options = {}) {
|
|
|
178
248
|
const files = await fastGlob(pattern);
|
|
179
249
|
if (debug)
|
|
180
250
|
console.log('\x1b[36m[Fenom Plugin]\x1b[0m Found templates:', files);
|
|
181
|
-
// ===
|
|
251
|
+
// === Анализ входов и ассетов — как было ===
|
|
182
252
|
const inputEntries = config.build.rollupOptions.input;
|
|
183
253
|
let inputFiles = [];
|
|
184
254
|
if (Array.isArray(inputEntries)) {
|
|
@@ -190,10 +260,6 @@ function fenomPlugin(options = {}) {
|
|
|
190
260
|
else if (typeof inputEntries === 'string') {
|
|
191
261
|
inputFiles = [inputEntries];
|
|
192
262
|
}
|
|
193
|
-
if (debug) {
|
|
194
|
-
console.log('inputFiles:', inputFiles);
|
|
195
|
-
}
|
|
196
|
-
// === Находим настоящие ассеты по расширению ===
|
|
197
263
|
let jsChunk = '';
|
|
198
264
|
const cssAssets = [];
|
|
199
265
|
for (const [fileName, file] of Object.entries(bundle)) {
|
|
@@ -204,65 +270,42 @@ function fenomPlugin(options = {}) {
|
|
|
204
270
|
cssAssets.push(`/${fileName}`);
|
|
205
271
|
}
|
|
206
272
|
}
|
|
207
|
-
// === Создаём карту замен ===
|
|
208
273
|
const replacementMap = new Map();
|
|
209
274
|
for (const input of inputFiles) {
|
|
210
275
|
if (/\.(ts|js)$/.test(input) && jsChunk) {
|
|
211
276
|
replacementMap.set(input, jsChunk);
|
|
212
277
|
}
|
|
213
278
|
if (/\.css$/.test(input) && cssAssets.length > 0) {
|
|
214
|
-
// Берём первый CSS (или можно выбрать по имени)
|
|
215
279
|
replacementMap.set(input, cssAssets[0]);
|
|
216
280
|
}
|
|
217
281
|
}
|
|
218
|
-
|
|
219
|
-
console.log('JS chunk found:', jsChunk);
|
|
220
|
-
console.log('CSS assets found:', cssAssets);
|
|
221
|
-
console.log('replacementMap:', Object.fromEntries(replacementMap));
|
|
222
|
-
}
|
|
223
|
-
// === Генерируем HTML ===
|
|
282
|
+
// === Генерация HTML ===
|
|
224
283
|
for (const file of files) {
|
|
225
284
|
const fileName = require$$0.basename(file, '.tpl');
|
|
226
285
|
const outputFileName = fileName === 'index' ? 'index.html' : `${fileName}.html`;
|
|
227
286
|
try {
|
|
228
287
|
const source = await fs__namespace.readFile(file, 'utf-8');
|
|
229
|
-
const jsonDataPath = file.replace(/\.tpl$/, '.json');
|
|
230
|
-
let extraContext = {};
|
|
231
|
-
try {
|
|
232
|
-
const data = await fs__namespace.readFile(jsonDataPath, 'utf-8');
|
|
233
|
-
extraContext = JSON.parse(data);
|
|
234
|
-
}
|
|
235
|
-
catch (_a) { }
|
|
236
288
|
const context = {
|
|
237
289
|
title: `${fileName.charAt(0).toUpperCase() + fileName.slice(1)} Page`,
|
|
238
290
|
debug: false,
|
|
239
291
|
url: '/' + (fileName === 'index' ? '' : fileName),
|
|
240
|
-
...
|
|
292
|
+
...globalData, // ← все данные из data/**/*.json
|
|
241
293
|
};
|
|
242
294
|
let html = await fenomJsExports.FenomJs(source, context, {
|
|
243
295
|
loader: templateLoader,
|
|
244
296
|
root,
|
|
245
297
|
minify: minifyHtml,
|
|
246
298
|
});
|
|
247
|
-
// === Замена путей ===
|
|
248
299
|
for (const [devPath, prodPath] of replacementMap) {
|
|
249
300
|
const fullDevPath = '/' + devPath;
|
|
250
301
|
const escaped = fullDevPath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
251
|
-
// <script src="...">
|
|
252
302
|
const scriptRegex = new RegExp(`<script[^>]+src=["']${escaped}["'][^>]*>`, 'gi');
|
|
253
303
|
if (scriptRegex.test(html)) {
|
|
254
304
|
html = html.replace(scriptRegex, `<script type="module" src="${prodPath}"></script>`);
|
|
255
|
-
if (debug) {
|
|
256
|
-
console.log(`[Fenom Plugin] Replaced script: ${fullDevPath} → ${prodPath}`);
|
|
257
|
-
}
|
|
258
305
|
}
|
|
259
|
-
// <link href="...">
|
|
260
306
|
const linkRegex = new RegExp(`<link[^>]+href=["']${escaped}["'][^>]*>`, 'gi');
|
|
261
307
|
if (linkRegex.test(html)) {
|
|
262
308
|
html = html.replace(linkRegex, `<link rel="stylesheet" href="${prodPath}">`);
|
|
263
|
-
if (debug) {
|
|
264
|
-
console.log(`[Fenom Plugin] Replaced link: ${fullDevPath} → ${prodPath}`);
|
|
265
|
-
}
|
|
266
309
|
}
|
|
267
310
|
}
|
|
268
311
|
this.emitFile({
|