ultimate-jekyll-manager 0.0.38 → 0.0.40
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/README.md +1 -0
- package/dist/commands/audit.js +1 -1
- package/dist/commands/translation.js +1 -1
- package/dist/gulp/tasks/audit.js +9 -4
- package/dist/gulp/tasks/defaults.js +1 -1
- package/dist/gulp/tasks/developmentRebuild.js +1 -1
- package/dist/gulp/tasks/distribute.js +1 -1
- package/dist/gulp/tasks/imagemin.js +1 -1
- package/dist/gulp/tasks/jekyll.js +1 -4
- package/dist/gulp/tasks/sass.js +1 -1
- package/dist/gulp/tasks/translation.js +135 -45
- package/dist/gulp/tasks/utils/collectTextNodes.js +21 -9
- package/dist/gulp/tasks/utils/formatDocument.js +10 -8
- package/dist/gulp/tasks/webpack.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/commands/audit.js
CHANGED
|
@@ -11,5 +11,5 @@ module.exports = async function (options) {
|
|
|
11
11
|
// Log
|
|
12
12
|
logger.log(`Starting audit...`);
|
|
13
13
|
|
|
14
|
-
await execute('
|
|
14
|
+
await execute('UJ_AUDIT_FORCE=true bundle exec npm run gulp -- audit', { log: true })
|
|
15
15
|
};
|
|
@@ -11,5 +11,5 @@ module.exports = async function (options) {
|
|
|
11
11
|
// Log
|
|
12
12
|
logger.log(`Starting translation...`);
|
|
13
13
|
|
|
14
|
-
await execute('
|
|
14
|
+
await execute('UJ_TRANSLATION_FORCE=true bundle exec npm run gulp -- translation', { log: true })
|
|
15
15
|
};
|
package/dist/gulp/tasks/audit.js
CHANGED
|
@@ -35,8 +35,8 @@ async function audit(complete) {
|
|
|
35
35
|
// Log
|
|
36
36
|
logger.log('Starting...');
|
|
37
37
|
|
|
38
|
-
// Quit if NOT in build mode and
|
|
39
|
-
if (!Manager.isBuildMode() && process.env.
|
|
38
|
+
// Quit if NOT in build mode and UJ_AUDIT_FORCE is not true
|
|
39
|
+
if (!Manager.isBuildMode() && process.env.UJ_AUDIT_FORCE !== 'true') {
|
|
40
40
|
logger.log('Skipping audit in development mode');
|
|
41
41
|
return complete();
|
|
42
42
|
}
|
|
@@ -72,10 +72,15 @@ async function validateFormat(file, content) {
|
|
|
72
72
|
(async () => {
|
|
73
73
|
try {
|
|
74
74
|
// Format the content using Prettier
|
|
75
|
-
const formatted = await formatDocument(content, format
|
|
75
|
+
const formatted = await formatDocument(content, format);
|
|
76
76
|
|
|
77
77
|
// Save the formatted content back to the file
|
|
78
|
-
jetpack.write(file, formatted);
|
|
78
|
+
jetpack.write(file, formatted.content);
|
|
79
|
+
|
|
80
|
+
// Quit if there is an error
|
|
81
|
+
if (formatted.error) {
|
|
82
|
+
throw formatted.error;
|
|
83
|
+
}
|
|
79
84
|
|
|
80
85
|
return { valid: true, messages: [] };
|
|
81
86
|
} catch (e) {
|
|
@@ -232,7 +232,7 @@ function defaultsWatcher(complete) {
|
|
|
232
232
|
// Watch for changes
|
|
233
233
|
watch(input, { delay: delay, dot: true }, defaults)
|
|
234
234
|
.on('change', (path) => {
|
|
235
|
-
logger.log(`[watcher] File ${path}
|
|
235
|
+
logger.log(`[watcher] File changed (${path})`);
|
|
236
236
|
});
|
|
237
237
|
|
|
238
238
|
// Complete
|
|
@@ -91,7 +91,7 @@ function developmentRebuildWatcher(complete) {
|
|
|
91
91
|
// Watch for changes
|
|
92
92
|
watch(input, { delay: delay, dot: true }, developmentRebuild)
|
|
93
93
|
.on('change', (path) => {
|
|
94
|
-
logger.log(`[watcher] File ${path}
|
|
94
|
+
logger.log(`[watcher] File changed (${path})`);
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
// Complete
|
|
@@ -102,7 +102,7 @@ function distributeWatcher(complete) {
|
|
|
102
102
|
// Watch for changes
|
|
103
103
|
watch(input, { delay: delay, dot: true }, distribute)
|
|
104
104
|
.on('change', (path) => {
|
|
105
|
-
logger.log(`[watcher] File ${path}
|
|
105
|
+
logger.log(`[watcher] File changed (${path})`);
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
// Complete
|
|
@@ -130,7 +130,7 @@ function imageminWatcher(complete) {
|
|
|
130
130
|
// Watch for changes
|
|
131
131
|
watch(input, { delay: delay, dot: true }, imagemin)
|
|
132
132
|
.on('change', (path) => {
|
|
133
|
-
logger.log(`[watcher] File ${path}
|
|
133
|
+
logger.log(`[watcher] File changed (${path})`);
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
// Complete
|
|
@@ -79,9 +79,6 @@ async function jekyll(complete) {
|
|
|
79
79
|
// Run buildpost hook
|
|
80
80
|
await hook('build:post', index);
|
|
81
81
|
|
|
82
|
-
// QUIT
|
|
83
|
-
// QUIT
|
|
84
|
-
|
|
85
82
|
// Log
|
|
86
83
|
logger.log('Finished!');
|
|
87
84
|
|
|
@@ -113,7 +110,7 @@ function jekyllWatcher(complete) {
|
|
|
113
110
|
// Watch for changes
|
|
114
111
|
watch(input, { delay: delay, dot: true }, jekyll)
|
|
115
112
|
.on('change', (path) => {
|
|
116
|
-
logger.log(`[watcher] File ${path}
|
|
113
|
+
logger.log(`[watcher] File changed (${path})`);
|
|
117
114
|
|
|
118
115
|
// Check if changed file is a .rb file
|
|
119
116
|
if (path.endsWith('.rb')) {
|
package/dist/gulp/tasks/sass.js
CHANGED
|
@@ -113,7 +113,7 @@ function sassWatcher(complete) {
|
|
|
113
113
|
// Watch for changes
|
|
114
114
|
watch(input, { delay: delay, dot: true }, sass)
|
|
115
115
|
.on('change', (path) => {
|
|
116
|
-
logger.log(`[watcher] File ${path}
|
|
116
|
+
logger.log(`[watcher] File changed (${path})`);
|
|
117
117
|
});
|
|
118
118
|
|
|
119
119
|
// Complete
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Libraries
|
|
2
2
|
const Manager = new (require('../../build.js'));
|
|
3
3
|
const logger = Manager.logger('translation');
|
|
4
|
-
const { series } = require('gulp');
|
|
4
|
+
const { series, watch } = require('gulp');
|
|
5
5
|
const glob = require('glob').globSync;
|
|
6
6
|
const path = require('path');
|
|
7
7
|
const fetch = require('wonderful-fetch');
|
|
@@ -9,7 +9,7 @@ const jetpack = require('fs-jetpack');
|
|
|
9
9
|
const cheerio = require('cheerio');
|
|
10
10
|
const crypto = require('crypto');
|
|
11
11
|
const yaml = require('js-yaml');
|
|
12
|
-
const { execute, wait } = require('node-powertools');
|
|
12
|
+
const { execute, wait, template } = require('node-powertools');
|
|
13
13
|
const { Octokit } = require('@octokit/rest')
|
|
14
14
|
const AdmZip = require('adm-zip') // npm install adm-zip
|
|
15
15
|
|
|
@@ -38,13 +38,25 @@ const RECHECK_DAYS = 0;
|
|
|
38
38
|
// const AI_MODEL = 'gpt-4.1-nano';
|
|
39
39
|
const AI_MODEL = 'gpt-4.1-mini';
|
|
40
40
|
const TRANSLATION_BRANCH = 'uj-translations';
|
|
41
|
-
const LOUD = false;
|
|
42
|
-
|
|
41
|
+
// const LOUD = false;
|
|
42
|
+
const LOUD = process.env.UJ_LOUD_LOGS === 'true';
|
|
43
43
|
|
|
44
44
|
const TRANSLATION_DELAY_MS = 500; // wait between each translation
|
|
45
45
|
const TRANSLATION_BATCH_SIZE = 25; // wait longer every N translations
|
|
46
46
|
const TRANSLATION_BATCH_DELAY_MS = 10000; // longer wait after batch
|
|
47
47
|
|
|
48
|
+
// Prompt
|
|
49
|
+
const SYSTEM_PROMPT = `
|
|
50
|
+
You are a professional translator.
|
|
51
|
+
Translate the provided content, preserving all original formatting, HTML structure, metadata, and links.
|
|
52
|
+
Do not explain anything — just return the translated content.
|
|
53
|
+
The content is TAGGED with [0]...[/0], etc. to mark the text. You MUST KEEP THESE TAGS IN PLACE IN YOUR RESPONSE and OPEN ([0]) and CLOSE ([/0]) them PROPERLY.
|
|
54
|
+
DO NOT translate URLs or other non-text elements.
|
|
55
|
+
DO NOT translate the brand.
|
|
56
|
+
Brand: {brand}
|
|
57
|
+
Translate to {lang}
|
|
58
|
+
`;
|
|
59
|
+
|
|
48
60
|
// Variables
|
|
49
61
|
let octokit;
|
|
50
62
|
|
|
@@ -61,11 +73,8 @@ async function translation(complete) {
|
|
|
61
73
|
// Log
|
|
62
74
|
logger.log('Starting...');
|
|
63
75
|
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// Quit if NOT in build mode and UJ_FORCE_TRANSLATION is not true
|
|
68
|
-
if (!Manager.isBuildMode() && process.env.UJ_FORCE_TRANSLATION !== 'true') {
|
|
76
|
+
// Quit if NOT in build mode and UJ_TRANSLATION_FORCE is not true
|
|
77
|
+
if (!Manager.isBuildMode() && process.env.UJ_TRANSLATION_FORCE !== 'true') {
|
|
69
78
|
logger.log('Skipping translation in development mode');
|
|
70
79
|
return complete();
|
|
71
80
|
}
|
|
@@ -103,6 +112,34 @@ async function translation(complete) {
|
|
|
103
112
|
return complete();
|
|
104
113
|
};
|
|
105
114
|
|
|
115
|
+
// TODO: Currently this does not work because it will run an infinite loop
|
|
116
|
+
function translationWatcher(complete) {
|
|
117
|
+
// Quit if in build mode
|
|
118
|
+
if (Manager.isBuildMode()) {
|
|
119
|
+
logger.log('[watcher] Skipping watcher in build mode');
|
|
120
|
+
return complete();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Log
|
|
124
|
+
logger.log('[watcher] Watching for changes...');
|
|
125
|
+
|
|
126
|
+
// Get ignored pages
|
|
127
|
+
const ignoredPages = getIgnoredPages();
|
|
128
|
+
const ignore = [
|
|
129
|
+
...ignoredPages.files.map(key => `_site/${key}.html`),
|
|
130
|
+
...ignoredPages.folders.map(folder => `_site/${folder}/**/*`)
|
|
131
|
+
]
|
|
132
|
+
|
|
133
|
+
// Watch for changes
|
|
134
|
+
watch(input, { delay: delay, ...getGlobOptions(), }, translation)
|
|
135
|
+
.on('change', (path) => {
|
|
136
|
+
logger.log(`[watcher] File changed (${path})`);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Complete
|
|
140
|
+
return complete();
|
|
141
|
+
}
|
|
142
|
+
|
|
106
143
|
// Default Task
|
|
107
144
|
module.exports = series(translation);
|
|
108
145
|
|
|
@@ -110,7 +147,6 @@ module.exports = series(translation);
|
|
|
110
147
|
async function processTranslation() {
|
|
111
148
|
const enabled = config?.translation?.enabled !== false;
|
|
112
149
|
const languages = config?.translation?.languages || [];
|
|
113
|
-
const ignoredPages = getIgnoredPages();
|
|
114
150
|
const updatedFiles = new Set();
|
|
115
151
|
|
|
116
152
|
// Quit if translation is disabled or no languages are configured
|
|
@@ -135,20 +171,20 @@ async function processTranslation() {
|
|
|
135
171
|
// }
|
|
136
172
|
|
|
137
173
|
// Get files
|
|
138
|
-
const allFiles = glob(input,
|
|
139
|
-
nodir: true,
|
|
140
|
-
ignore: [
|
|
141
|
-
...ignoredPages.files.map(key => `_site/${key}.html`),
|
|
142
|
-
...ignoredPages.folders.map(folder => `_site/${folder}/**/*`)
|
|
143
|
-
]
|
|
144
|
-
});
|
|
174
|
+
const allFiles = glob(input, getGlobOptions());
|
|
145
175
|
|
|
146
176
|
// Log
|
|
147
177
|
logger.log(`Translating ${allFiles.length} files for ${languages.length} supported languages: ${languages.join(', ')}`);
|
|
148
178
|
// logger.log(allFiles);
|
|
149
179
|
|
|
150
180
|
// Prepare meta caches per language
|
|
151
|
-
const metas = {
|
|
181
|
+
const metas = {
|
|
182
|
+
global: {
|
|
183
|
+
skipped: new Set(),
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const promptHash = crypto.createHash('sha256').update(SYSTEM_PROMPT).digest('hex');
|
|
187
|
+
|
|
152
188
|
for (const lang of languages) {
|
|
153
189
|
const metaPath = path.join(CACHE_DIR, lang, 'meta.json');
|
|
154
190
|
let meta = {};
|
|
@@ -159,7 +195,16 @@ async function processTranslation() {
|
|
|
159
195
|
logger.warn(`⚠️ Failed to parse meta for [${lang}], starting fresh`);
|
|
160
196
|
}
|
|
161
197
|
}
|
|
162
|
-
|
|
198
|
+
|
|
199
|
+
// Check if the promptHash matches; if not, invalidate the cache
|
|
200
|
+
if (meta.prompt?.hash !== promptHash) {
|
|
201
|
+
logger.warn(`⚠️ Prompt hash mismatch for [${lang}]. Invalidating cache.`);
|
|
202
|
+
meta = {};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Store the current promptHash in the meta file
|
|
206
|
+
meta.prompt = { hash: promptHash };
|
|
207
|
+
metas[lang] = { meta, path: metaPath, skipped: new Set() };
|
|
163
208
|
}
|
|
164
209
|
|
|
165
210
|
// Track token usage
|
|
@@ -183,9 +228,8 @@ async function processTranslation() {
|
|
|
183
228
|
|
|
184
229
|
// Skip all except the specified HTML file
|
|
185
230
|
if (ujOnly && relativePath !== ujOnly) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
231
|
+
// Update to work with the new SET protocol
|
|
232
|
+
metas.global.skipped.add(`${relativePath} (UJ_TRANSLATION_ONLY set)`);
|
|
189
233
|
continue;
|
|
190
234
|
}
|
|
191
235
|
|
|
@@ -214,11 +258,16 @@ async function processTranslation() {
|
|
|
214
258
|
const age = entry?.timestamp
|
|
215
259
|
? (Date.now() - new Date(entry.timestamp).getTime()) / (1000 * 60 * 60 * 24)
|
|
216
260
|
: Infinity;
|
|
217
|
-
const useCached = entry
|
|
261
|
+
const useCached = entry
|
|
262
|
+
&& entry.hash === hash
|
|
263
|
+
&& (RECHECK_DAYS === 0 || age < RECHECK_DAYS);
|
|
218
264
|
const startTime = Date.now();
|
|
219
265
|
|
|
220
266
|
// Check if we can use cached translation
|
|
221
|
-
if (
|
|
267
|
+
if (
|
|
268
|
+
(useCached || process.env.UJ_TRANSLATION_CACHE === 'true')
|
|
269
|
+
&& jetpack.exists(cachePath)
|
|
270
|
+
) {
|
|
222
271
|
translated = jetpack.read(cachePath);
|
|
223
272
|
logger.log(`📦 Using cached translation for ${relativePath} [${lang}]`);
|
|
224
273
|
} else {
|
|
@@ -260,6 +309,11 @@ async function processTranslation() {
|
|
|
260
309
|
// Log result
|
|
261
310
|
// console.log('---translated---', translated);
|
|
262
311
|
|
|
312
|
+
// Reset the DOM to avoid conflicts between languages
|
|
313
|
+
const $ = cheerio.load(originalHtml);
|
|
314
|
+
// Collect text nodes with tags
|
|
315
|
+
const textNodes = collectTextNodes($, { tag: true });
|
|
316
|
+
|
|
263
317
|
// Replace original text nodes with translated versions
|
|
264
318
|
textNodes.forEach((n, i) => {
|
|
265
319
|
const regex = new RegExp(`\\[${i}\\](.*?)\\[/${i}\\]`, 's');
|
|
@@ -270,14 +324,22 @@ async function processTranslation() {
|
|
|
270
324
|
return logger.warn(`⚠️ Could not find translated tag for index ${i}`);
|
|
271
325
|
}
|
|
272
326
|
|
|
327
|
+
// Extract original leading and trailing whitespace
|
|
328
|
+
const originalText = n.text;
|
|
329
|
+
const leadingWhitespace = originalText.match(/^\s*/)?.[0] || '';
|
|
330
|
+
const trailingWhitespace = originalText.match(/\s*$/)?.[0] || '';
|
|
331
|
+
|
|
332
|
+
// Reapply the original whitespace to the translation
|
|
333
|
+
const adjustedTranslation = `${leadingWhitespace}${translation.trim()}${trailingWhitespace}`;
|
|
334
|
+
|
|
273
335
|
if (n.type === 'data') {
|
|
274
|
-
n.reference.data =
|
|
336
|
+
n.reference.data = adjustedTranslation;
|
|
275
337
|
} else if (n.type === 'text') {
|
|
276
|
-
n.node.text(
|
|
338
|
+
n.node.text(adjustedTranslation);
|
|
277
339
|
} else if (n.type === 'attr') {
|
|
278
|
-
n.node.attr(n.attr,
|
|
340
|
+
n.node.attr(n.attr, adjustedTranslation);
|
|
279
341
|
}
|
|
280
|
-
if (LOUD) logger.log(`${i}: ${n.text} → ${
|
|
342
|
+
if (LOUD) logger.log(`${i}: "${n.text.trim()}" → "${adjustedTranslation.trim()}"`);
|
|
281
343
|
});
|
|
282
344
|
|
|
283
345
|
// Rewrite links
|
|
@@ -294,7 +356,7 @@ async function processTranslation() {
|
|
|
294
356
|
$('meta[property="og:url"]').attr('content', canonicalUrl);
|
|
295
357
|
|
|
296
358
|
// Insert language tags on this translation
|
|
297
|
-
await insertLanguageTags($, languages, relativePath);
|
|
359
|
+
await insertLanguageTags($, languages, relativePath, outPath);
|
|
298
360
|
|
|
299
361
|
// Insert language tags in original file
|
|
300
362
|
await insertLanguageTags(cheerio.load(originalHtml), languages, relativePath, filePath);
|
|
@@ -305,7 +367,15 @@ async function processTranslation() {
|
|
|
305
367
|
await insertLanguageTags(cheerio.load(sitemapXml, { xmlMode: true }), languages, relativePath, sitemapPath);
|
|
306
368
|
|
|
307
369
|
// Save output
|
|
308
|
-
|
|
370
|
+
// const formatted = await formatDocument($.html(), 'html');
|
|
371
|
+
|
|
372
|
+
// console.log('----relativePath', relativePath);
|
|
373
|
+
// console.log('----filePath', filePath);
|
|
374
|
+
// console.log('----outPath', outPath);
|
|
375
|
+
// console.log('----FORMATTED.ERROR', formatted.error);
|
|
376
|
+
|
|
377
|
+
// Write the translated file
|
|
378
|
+
// jetpack.write(outPath, formatted.content);
|
|
309
379
|
// logger.log(`✅ Wrote: ${outPath}`);
|
|
310
380
|
|
|
311
381
|
// Track updated files only if it's new or updated
|
|
@@ -334,13 +404,23 @@ async function processTranslation() {
|
|
|
334
404
|
}
|
|
335
405
|
}
|
|
336
406
|
|
|
407
|
+
// Log skipped files
|
|
408
|
+
logger.warn('🚫 Skipped files:');
|
|
409
|
+
let totalSkipped = 0;
|
|
410
|
+
for (const [lang, meta] of Object.entries(metas)) {
|
|
411
|
+
if (meta.skipped.size > 0) {
|
|
412
|
+
logger.warn(` [${lang}] ${meta.skipped.size} skipped files:`);
|
|
413
|
+
meta.skipped.forEach(f => logger.warn(` ${f}`));
|
|
414
|
+
totalSkipped += meta.skipped.size;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
if (totalSkipped === 0) {
|
|
418
|
+
logger.warn(' NONE');
|
|
419
|
+
}
|
|
420
|
+
|
|
337
421
|
// Save all updated meta files
|
|
338
422
|
for (const lang of languages) {
|
|
339
423
|
jetpack.write(metas[lang].path, metas[lang].meta);
|
|
340
|
-
if (metas[lang].skipped.length) {
|
|
341
|
-
logger.warn('🚫 Skipped files:');
|
|
342
|
-
metas[lang].skipped.forEach(f => logger.warn(f));
|
|
343
|
-
}
|
|
344
424
|
}
|
|
345
425
|
|
|
346
426
|
// Log total token usage
|
|
@@ -356,14 +436,11 @@ async function processTranslation() {
|
|
|
356
436
|
}
|
|
357
437
|
|
|
358
438
|
async function translateWithAPI(openAIKey, content, lang) {
|
|
359
|
-
|
|
360
|
-
const systemPrompt =
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
The content is TAGGED with [0]...[/0], etc. to mark the text. You MUST KEEP THESE TAGS IN PLACE IN YOUR RESPONSE and OPEN ([0]) and CLOSE ([/0]) them PROPERLY.
|
|
365
|
-
Translate to ${lang}.
|
|
366
|
-
`;
|
|
439
|
+
const brand = config?.brand?.name || 'Unknown Brand';
|
|
440
|
+
const systemPrompt = template(SYSTEM_PROMPT, {
|
|
441
|
+
lang,
|
|
442
|
+
brand
|
|
443
|
+
});
|
|
367
444
|
|
|
368
445
|
// Request
|
|
369
446
|
const res = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
@@ -521,9 +598,11 @@ async function insertLanguageTags($, languages, relativePath, filePath) {
|
|
|
521
598
|
|
|
522
599
|
// Save the modified HTML back to the file if filePath
|
|
523
600
|
if (filePath) {
|
|
524
|
-
|
|
525
|
-
const
|
|
526
|
-
|
|
601
|
+
const format = isHtml ? 'html' : 'xml';
|
|
602
|
+
const formatted = await formatDocument($.html(), format);
|
|
603
|
+
|
|
604
|
+
// Write the formatted content back to the file
|
|
605
|
+
jetpack.write(filePath, formatted.content);
|
|
527
606
|
}
|
|
528
607
|
}
|
|
529
608
|
|
|
@@ -597,6 +676,17 @@ function getIgnoredPages() {
|
|
|
597
676
|
};
|
|
598
677
|
}
|
|
599
678
|
|
|
679
|
+
function getGlobOptions() {
|
|
680
|
+
const ignoredPages = getIgnoredPages();
|
|
681
|
+
return {
|
|
682
|
+
nodir: true,
|
|
683
|
+
ignore: [
|
|
684
|
+
...ignoredPages.files.map(key => `_site/${key}.html`),
|
|
685
|
+
...ignoredPages.folders.map(folder => `_site/${folder}/**/*`)
|
|
686
|
+
]
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
600
690
|
// Git Sync: Pull
|
|
601
691
|
async function fetchTranslationsBranch() {
|
|
602
692
|
const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/')
|
|
@@ -21,14 +21,14 @@ const collectTextNodes = ($, options) => {
|
|
|
21
21
|
const i = textNodes.length;
|
|
22
22
|
const text = node.text().trim();
|
|
23
23
|
if (text) {
|
|
24
|
+
|
|
25
|
+
// Push
|
|
24
26
|
textNodes.push({
|
|
25
27
|
node,
|
|
26
28
|
type: 'text',
|
|
27
29
|
attr: null,
|
|
28
30
|
text,
|
|
29
31
|
tagged: `[${i}]${text}[/${i}]`,
|
|
30
|
-
line: el.startIndex || 0, // Add line information
|
|
31
|
-
column: 0 // Column is not directly available, default to 0
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
34
|
return;
|
|
@@ -51,14 +51,14 @@ const collectTextNodes = ($, options) => {
|
|
|
51
51
|
const text = node.attr('content')?.trim();
|
|
52
52
|
if (text) {
|
|
53
53
|
const i = textNodes.length;
|
|
54
|
+
|
|
55
|
+
// Push
|
|
54
56
|
textNodes.push({
|
|
55
57
|
node,
|
|
56
58
|
type: 'attr',
|
|
57
59
|
attr: 'content',
|
|
58
60
|
text,
|
|
59
61
|
tagged: `[${i}]${text}[/${i}]`,
|
|
60
|
-
line: el.startIndex || 0, // Add line information
|
|
61
|
-
column: 0 // Column is not directly available, default to 0
|
|
62
62
|
});
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -70,10 +70,15 @@ const collectTextNodes = ($, options) => {
|
|
|
70
70
|
if (child.type === 'text' && child.data?.trim()) {
|
|
71
71
|
const i = textNodes.length;
|
|
72
72
|
const text = child.data
|
|
73
|
-
|
|
74
|
-
.replace(
|
|
75
|
-
|
|
73
|
+
// Preserve a single leading whitespace if it exists
|
|
74
|
+
.replace(/^\s*(\s)\s*/, '$1')
|
|
75
|
+
// Preserve a single trailing whitespace if it exists
|
|
76
|
+
.replace(/\s*(\s)\s*$/, '$1')
|
|
77
|
+
// Normalize internal whitespace
|
|
78
|
+
.replace(/\s+/g, ' ')
|
|
79
|
+
// const text = trimPreserveOneCleanInner(child.data);
|
|
76
80
|
|
|
81
|
+
// Push
|
|
77
82
|
textNodes.push({
|
|
78
83
|
node,
|
|
79
84
|
type: 'data',
|
|
@@ -81,8 +86,6 @@ const collectTextNodes = ($, options) => {
|
|
|
81
86
|
reference: child,
|
|
82
87
|
text,
|
|
83
88
|
tagged: `[${i}]${text}[/${i}]`,
|
|
84
|
-
line: el.startIndex || 0, // Add line information
|
|
85
|
-
column: 0 // Column is not directly available, default to 0
|
|
86
89
|
});
|
|
87
90
|
}
|
|
88
91
|
});
|
|
@@ -92,3 +95,12 @@ const collectTextNodes = ($, options) => {
|
|
|
92
95
|
};
|
|
93
96
|
|
|
94
97
|
module.exports = collectTextNodes;
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
function trimPreserveOneCleanInner(str) {
|
|
101
|
+
const match = str.match(/^(\s*)(.*?)(\s*)$/);
|
|
102
|
+
const leading = match[1] ? match[1][0] || '' : '';
|
|
103
|
+
const content = match[2].replace(/\s+/g, ' ');
|
|
104
|
+
const trailing = match[3] ? match[3][0] || '' : '';
|
|
105
|
+
return leading + content + trailing;
|
|
106
|
+
}
|
|
@@ -6,10 +6,9 @@ const path = require('path');
|
|
|
6
6
|
// Load package
|
|
7
7
|
const rootPathPackage = Manager.getRootPath('main');
|
|
8
8
|
|
|
9
|
-
module.exports = async
|
|
9
|
+
module.exports = async (content, format) => {
|
|
10
10
|
// Set default format to 'html' if not provided
|
|
11
11
|
format = format || 'html';
|
|
12
|
-
throwError = typeof throwError === 'undefined' ? true : throwError;
|
|
13
12
|
|
|
14
13
|
// Setup Prettier options
|
|
15
14
|
const options = {
|
|
@@ -30,16 +29,19 @@ module.exports = async function formatHTML(content, format, throwError) {
|
|
|
30
29
|
return prettier
|
|
31
30
|
.format(content, options)
|
|
32
31
|
.then((formatted) => {
|
|
33
|
-
return
|
|
32
|
+
return {
|
|
33
|
+
content: removeMultipleNewlines(formatted),
|
|
34
|
+
error: null,
|
|
35
|
+
};
|
|
34
36
|
})
|
|
35
37
|
.catch((e) => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
return {
|
|
39
|
+
content: removeMultipleNewlines(content),
|
|
40
|
+
error: e,
|
|
41
|
+
};
|
|
40
42
|
});
|
|
41
43
|
};
|
|
42
44
|
|
|
43
45
|
function removeMultipleNewlines(content) {
|
|
44
|
-
return content.replace(/\n\s*\n+/g, '\n');
|
|
46
|
+
return content.replace(/\n\s*\n+/g, '\n').trim();
|
|
45
47
|
}
|
|
@@ -186,7 +186,7 @@ function webpackWatcher(complete) {
|
|
|
186
186
|
watch(input, { delay: delay, dot: true }, webpack)
|
|
187
187
|
.on('change', (path) => {
|
|
188
188
|
// Log
|
|
189
|
-
logger.log(`[watcher] File ${path}
|
|
189
|
+
logger.log(`[watcher] File changed (${path})`);
|
|
190
190
|
});
|
|
191
191
|
|
|
192
192
|
// Complete
|