@intlayer/chokidar 8.12.4 → 9.0.0-canary.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/cjs/buildIntlayerDictionary/buildIntlayerDictionary.cjs +21 -4
- package/dist/cjs/buildIntlayerDictionary/buildIntlayerDictionary.cjs.map +1 -1
- package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs +94 -0
- package/dist/cjs/buildIntlayerDictionary/writeDynamicDictionary.cjs.map +1 -1
- package/dist/cjs/buildIntlayerDictionary/writeMergedDictionary.cjs +1 -1
- package/dist/cjs/buildIntlayerDictionary/writeMergedDictionary.cjs.map +1 -1
- package/dist/cjs/createType/createType.cjs.map +1 -1
- package/dist/cjs/init/index.cjs +63 -9
- package/dist/cjs/init/index.cjs.map +1 -1
- package/dist/cjs/init/utils/configManipulation.cjs +196 -0
- package/dist/cjs/init/utils/configManipulation.cjs.map +1 -1
- package/dist/cjs/init/utils/fileSystem.cjs +84 -0
- package/dist/cjs/init/utils/fileSystem.cjs.map +1 -1
- package/dist/cjs/init/utils/index.cjs +12 -0
- package/dist/cjs/init/utils/packageManager.cjs +187 -0
- package/dist/cjs/init/utils/packageManager.cjs.map +1 -0
- package/dist/cjs/scan/analyzeBundleContent.cjs +182 -0
- package/dist/cjs/scan/analyzeBundleContent.cjs.map +1 -0
- package/dist/cjs/scan/calculateScore.cjs +65 -0
- package/dist/cjs/scan/calculateScore.cjs.map +1 -0
- package/dist/cjs/scan/checks.cjs +274 -0
- package/dist/cjs/scan/checks.cjs.map +1 -0
- package/dist/cjs/scan/index.cjs +31 -0
- package/dist/cjs/scan/parseHtml.cjs +127 -0
- package/dist/cjs/scan/parseHtml.cjs.map +1 -0
- package/dist/cjs/scan/scanWebsite.cjs +205 -0
- package/dist/cjs/scan/scanWebsite.cjs.map +1 -0
- package/dist/cjs/scan/types.cjs +0 -0
- package/dist/esm/buildIntlayerDictionary/buildIntlayerDictionary.mjs +22 -5
- package/dist/esm/buildIntlayerDictionary/buildIntlayerDictionary.mjs.map +1 -1
- package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs +93 -1
- package/dist/esm/buildIntlayerDictionary/writeDynamicDictionary.mjs.map +1 -1
- package/dist/esm/buildIntlayerDictionary/writeMergedDictionary.mjs +2 -2
- package/dist/esm/buildIntlayerDictionary/writeMergedDictionary.mjs.map +1 -1
- package/dist/esm/createType/createType.mjs.map +1 -1
- package/dist/esm/init/index.mjs +65 -11
- package/dist/esm/init/index.mjs.map +1 -1
- package/dist/esm/init/utils/configManipulation.mjs +190 -1
- package/dist/esm/init/utils/configManipulation.mjs.map +1 -1
- package/dist/esm/init/utils/fileSystem.mjs +83 -1
- package/dist/esm/init/utils/fileSystem.mjs.map +1 -1
- package/dist/esm/init/utils/index.mjs +4 -3
- package/dist/esm/init/utils/packageManager.mjs +183 -0
- package/dist/esm/init/utils/packageManager.mjs.map +1 -0
- package/dist/esm/scan/analyzeBundleContent.mjs +180 -0
- package/dist/esm/scan/analyzeBundleContent.mjs.map +1 -0
- package/dist/esm/scan/calculateScore.mjs +61 -0
- package/dist/esm/scan/calculateScore.mjs.map +1 -0
- package/dist/esm/scan/checks.mjs +265 -0
- package/dist/esm/scan/checks.mjs.map +1 -0
- package/dist/esm/scan/index.mjs +7 -0
- package/dist/esm/scan/parseHtml.mjs +115 -0
- package/dist/esm/scan/parseHtml.mjs.map +1 -0
- package/dist/esm/scan/scanWebsite.mjs +203 -0
- package/dist/esm/scan/scanWebsite.mjs.map +1 -0
- package/dist/esm/scan/types.mjs +0 -0
- package/dist/types/buildIntlayerDictionary/buildIntlayerDictionary.d.ts.map +1 -1
- package/dist/types/buildIntlayerDictionary/writeDynamicDictionary.d.ts +31 -4
- package/dist/types/buildIntlayerDictionary/writeDynamicDictionary.d.ts.map +1 -1
- package/dist/types/buildIntlayerDictionary/writeMergedDictionary.d.ts +13 -3
- package/dist/types/buildIntlayerDictionary/writeMergedDictionary.d.ts.map +1 -1
- package/dist/types/createType/createType.d.ts +3 -3
- package/dist/types/createType/createType.d.ts.map +1 -1
- package/dist/types/formatDictionary.d.ts +9 -2
- package/dist/types/formatDictionary.d.ts.map +1 -1
- package/dist/types/init/index.d.ts.map +1 -1
- package/dist/types/init/utils/configManipulation.d.ts +42 -1
- package/dist/types/init/utils/configManipulation.d.ts.map +1 -1
- package/dist/types/init/utils/fileSystem.d.ts +31 -1
- package/dist/types/init/utils/fileSystem.d.ts.map +1 -1
- package/dist/types/init/utils/index.d.ts +4 -3
- package/dist/types/init/utils/packageManager.d.ts +59 -0
- package/dist/types/init/utils/packageManager.d.ts.map +1 -0
- package/dist/types/intlayer/dist/types/index.d.ts +4 -0
- package/dist/types/scan/analyzeBundleContent.d.ts +16 -0
- package/dist/types/scan/analyzeBundleContent.d.ts.map +1 -0
- package/dist/types/scan/calculateScore.d.ts +65 -0
- package/dist/types/scan/calculateScore.d.ts.map +1 -0
- package/dist/types/scan/checks.d.ts +38 -0
- package/dist/types/scan/checks.d.ts.map +1 -0
- package/dist/types/scan/index.d.ts +7 -0
- package/dist/types/scan/parseHtml.d.ts +54 -0
- package/dist/types/scan/parseHtml.d.ts.map +1 -0
- package/dist/types/scan/scanWebsite.d.ts +18 -0
- package/dist/types/scan/scanWebsite.d.ts.map +1 -0
- package/dist/types/scan/types.d.ts +76 -0
- package/dist/types/scan/types.d.ts.map +1 -0
- package/package.json +17 -9
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
const require_scan_calculateScore = require('./calculateScore.cjs');
|
|
4
|
+
const require_scan_parseHtml = require('./parseHtml.cjs');
|
|
5
|
+
const require_scan_checks = require('./checks.cjs');
|
|
6
|
+
let _intlayer_config_logger = require("@intlayer/config/logger");
|
|
7
|
+
let _intlayer_config_colors = require("@intlayer/config/colors");
|
|
8
|
+
|
|
9
|
+
//#region src/scan/scanWebsite.ts
|
|
10
|
+
const DEFAULT_USER_AGENT = "Mozilla/5.0 (compatible; IntlayerScanBot/1.0; +https://intlayer.org)";
|
|
11
|
+
const DEFAULT_TIMEOUT_MS = 3e4;
|
|
12
|
+
/**
|
|
13
|
+
* Log a recommendation to install `puppeteer` for a deeper scan. Mirrors the
|
|
14
|
+
* style used by other optional-dependency hints across the CLI.
|
|
15
|
+
*/
|
|
16
|
+
const logDeepScanRecommendation = () => {
|
|
17
|
+
(0, _intlayer_config_logger.logger)([
|
|
18
|
+
(0, _intlayer_config_logger.colorize)("Recommended: Install", _intlayer_config_colors.GREY),
|
|
19
|
+
(0, _intlayer_config_logger.colorize)("puppeteer", _intlayer_config_colors.GREY_LIGHT),
|
|
20
|
+
(0, _intlayer_config_logger.colorize)("package to enable a deeper scan (renders client-side content & lazy-loaded chunks). See documentation:", _intlayer_config_colors.GREY),
|
|
21
|
+
(0, _intlayer_config_logger.colorize)("https://intlayer.org/doc/concept/cli#scan", _intlayer_config_colors.GREY_LIGHT)
|
|
22
|
+
]);
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Render the page with a locally installed `puppeteer` to capture
|
|
26
|
+
* client-rendered content, the accurate transfer size, and lazy-loaded chunks.
|
|
27
|
+
*
|
|
28
|
+
* `puppeteer` is imported dynamically through a non-literal specifier so it is
|
|
29
|
+
* never bundled and stays an optional dependency: when it is absent the import
|
|
30
|
+
* rejects and the caller falls back to the basic scan.
|
|
31
|
+
*
|
|
32
|
+
* @returns The deep-scan result, or `null` when `puppeteer` is unavailable.
|
|
33
|
+
*/
|
|
34
|
+
const runDeepScan = async (targetUrl, userAgent, timeoutMs) => {
|
|
35
|
+
const moduleName = "puppeteer";
|
|
36
|
+
let puppeteer;
|
|
37
|
+
try {
|
|
38
|
+
const mod = await import(moduleName);
|
|
39
|
+
puppeteer = mod.default ?? mod;
|
|
40
|
+
} catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
let browser;
|
|
44
|
+
try {
|
|
45
|
+
browser = await puppeteer.launch({
|
|
46
|
+
headless: true,
|
|
47
|
+
executablePath: process.env.PUPPETEER_EXECUTABLE_PATH,
|
|
48
|
+
args: [
|
|
49
|
+
"--no-sandbox",
|
|
50
|
+
"--disable-setuid-sandbox",
|
|
51
|
+
"--disable-dev-shm-usage",
|
|
52
|
+
"--disable-gpu"
|
|
53
|
+
]
|
|
54
|
+
});
|
|
55
|
+
const page = await browser.newPage();
|
|
56
|
+
await page.setUserAgent(userAgent);
|
|
57
|
+
await page.setExtraHTTPHeaders({ "Accept-Language": "en-US,en;q=0.9" });
|
|
58
|
+
const origin = new URL(targetUrl).origin;
|
|
59
|
+
const jsResponseMap = /* @__PURE__ */ new Map();
|
|
60
|
+
let totalPageSize = 0;
|
|
61
|
+
const pendingResponses = [];
|
|
62
|
+
page.on("response", (response) => {
|
|
63
|
+
pendingResponses.push((async () => {
|
|
64
|
+
try {
|
|
65
|
+
if (response.status() !== 200) return;
|
|
66
|
+
const buffer = await response.buffer();
|
|
67
|
+
totalPageSize += buffer.length;
|
|
68
|
+
const responseUrl = response.url();
|
|
69
|
+
if (((response.headers()["content-type"] ?? "").includes("javascript") || /\.(js|mjs|cjs)(\?|$)/.test(responseUrl)) && responseUrl.startsWith(origin)) jsResponseMap.set(responseUrl, buffer.toString("utf-8"));
|
|
70
|
+
} catch {}
|
|
71
|
+
})());
|
|
72
|
+
});
|
|
73
|
+
await page.goto(targetUrl, {
|
|
74
|
+
waitUntil: "domcontentloaded",
|
|
75
|
+
timeout: timeoutMs
|
|
76
|
+
});
|
|
77
|
+
await page.waitForNetworkIdle({
|
|
78
|
+
idleTime: 1e3,
|
|
79
|
+
timeout: 1e4
|
|
80
|
+
}).catch(() => {});
|
|
81
|
+
await Promise.allSettled(pendingResponses);
|
|
82
|
+
const html = await page.content();
|
|
83
|
+
const mainBundleUrls = new Set(require_scan_parseHtml.extractScriptUrls(html, targetUrl));
|
|
84
|
+
const chunks = Array.from(jsResponseMap.entries()).map(([url, content]) => ({
|
|
85
|
+
url,
|
|
86
|
+
isMainBundle: mainBundleUrls.has(url),
|
|
87
|
+
content
|
|
88
|
+
}));
|
|
89
|
+
return {
|
|
90
|
+
html,
|
|
91
|
+
totalPageSize,
|
|
92
|
+
chunks
|
|
93
|
+
};
|
|
94
|
+
} finally {
|
|
95
|
+
if (browser) await browser.close();
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
/** Fetch the raw HTML document, measuring its byte size. */
|
|
99
|
+
const fetchHtml = async (url, userAgent, timeoutMs) => {
|
|
100
|
+
const controller = new AbortController();
|
|
101
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
102
|
+
try {
|
|
103
|
+
const response = await fetch(url, {
|
|
104
|
+
headers: {
|
|
105
|
+
"User-Agent": userAgent,
|
|
106
|
+
"Accept-Language": "en-US,en;q=0.9"
|
|
107
|
+
},
|
|
108
|
+
signal: controller.signal
|
|
109
|
+
});
|
|
110
|
+
return {
|
|
111
|
+
html: await response.text(),
|
|
112
|
+
finalUrl: response.url || url
|
|
113
|
+
};
|
|
114
|
+
} finally {
|
|
115
|
+
clearTimeout(timer);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Fetch every eagerly-loaded script. Same-origin scripts keep their content so
|
|
120
|
+
* their locale weight can be analyzed; third-party scripts only contribute to
|
|
121
|
+
* the measured page size (their locale-like keys cause false positives).
|
|
122
|
+
*/
|
|
123
|
+
const fetchScripts = async (scriptUrls, origin, userAgent) => {
|
|
124
|
+
const chunks = [];
|
|
125
|
+
let scriptBytes = 0;
|
|
126
|
+
await Promise.all(scriptUrls.map(async (scriptUrl) => {
|
|
127
|
+
try {
|
|
128
|
+
const response = await fetch(scriptUrl, { headers: { "User-Agent": userAgent } });
|
|
129
|
+
if (!response.ok) return;
|
|
130
|
+
const content = await response.text();
|
|
131
|
+
scriptBytes += require_scan_parseHtml.byteLength(content);
|
|
132
|
+
if (scriptUrl.startsWith(origin)) chunks.push({
|
|
133
|
+
url: scriptUrl,
|
|
134
|
+
isMainBundle: true,
|
|
135
|
+
content
|
|
136
|
+
});
|
|
137
|
+
} catch {}
|
|
138
|
+
}));
|
|
139
|
+
return {
|
|
140
|
+
chunks,
|
|
141
|
+
scriptBytes
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Scan a single web page for i18n/SEO health and bundle weight.
|
|
146
|
+
*
|
|
147
|
+
* In `deep` mode (default) the page is rendered with a locally installed
|
|
148
|
+
* `puppeteer`; when `puppeteer` is missing the scan transparently falls back to
|
|
149
|
+
* a `basic` fetch-based pass and logs a recommendation to install it.
|
|
150
|
+
*
|
|
151
|
+
* @param targetUrl - The absolute URL to scan.
|
|
152
|
+
* @param options - {@link ScanOptions} controlling depth, timeout and UA.
|
|
153
|
+
* @returns The {@link ScanResult} including score, page size and per-check events.
|
|
154
|
+
*/
|
|
155
|
+
const scanWebsite = async (targetUrl, options = {}) => {
|
|
156
|
+
const { deep = true, timeoutMs = DEFAULT_TIMEOUT_MS, userAgent = DEFAULT_USER_AGENT } = options;
|
|
157
|
+
const origin = new URL(targetUrl).origin;
|
|
158
|
+
let mode = "basic";
|
|
159
|
+
let html;
|
|
160
|
+
let totalPageSize;
|
|
161
|
+
let chunks;
|
|
162
|
+
const deepResult = deep ? await runDeepScan(targetUrl, userAgent, timeoutMs) : null;
|
|
163
|
+
if (deepResult) {
|
|
164
|
+
mode = "deep";
|
|
165
|
+
html = deepResult.html;
|
|
166
|
+
chunks = deepResult.chunks;
|
|
167
|
+
totalPageSize = deepResult.totalPageSize;
|
|
168
|
+
} else {
|
|
169
|
+
if (deep) logDeepScanRecommendation();
|
|
170
|
+
const { html: fetchedHtml, finalUrl } = await fetchHtml(targetUrl, userAgent, timeoutMs);
|
|
171
|
+
html = fetchedHtml;
|
|
172
|
+
const { chunks: fetchedChunks, scriptBytes } = await fetchScripts(require_scan_parseHtml.extractScriptUrls(fetchedHtml, finalUrl), origin, userAgent);
|
|
173
|
+
chunks = fetchedChunks;
|
|
174
|
+
totalPageSize = require_scan_parseHtml.byteLength(fetchedHtml) + scriptBytes;
|
|
175
|
+
}
|
|
176
|
+
const htmlSize = require_scan_parseHtml.byteLength(html);
|
|
177
|
+
const events = [];
|
|
178
|
+
const localesSet = /* @__PURE__ */ new Set();
|
|
179
|
+
const { langTag } = require_scan_checks.checkHtmlAttributes(html, targetUrl, events);
|
|
180
|
+
require_scan_checks.checkCanonical(html, targetUrl, events);
|
|
181
|
+
require_scan_checks.checkLinguisticStructure(html, targetUrl, localesSet, events);
|
|
182
|
+
require_scan_checks.checkUrlStructure(require_scan_parseHtml.extractAnchors(html), origin, targetUrl, events);
|
|
183
|
+
const bundle = require_scan_checks.checkBundleContent(chunks, html, langTag, targetUrl, totalPageSize, events);
|
|
184
|
+
await require_scan_checks.checkRobots(origin, localesSet, userAgent, events);
|
|
185
|
+
await require_scan_checks.checkSitemap(origin, localesSet, userAgent, events);
|
|
186
|
+
const rawScore = events.reduce((score, event) => require_scan_calculateScore.mutateScore(score, event), {
|
|
187
|
+
score: 0,
|
|
188
|
+
totalScore: 0
|
|
189
|
+
});
|
|
190
|
+
return {
|
|
191
|
+
url: targetUrl,
|
|
192
|
+
mode,
|
|
193
|
+
totalPageSize,
|
|
194
|
+
htmlSize,
|
|
195
|
+
score: require_scan_calculateScore.toScorePercent(rawScore),
|
|
196
|
+
rawScore,
|
|
197
|
+
events,
|
|
198
|
+
locales: Array.from(localesSet),
|
|
199
|
+
bundle
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
//#endregion
|
|
204
|
+
exports.scanWebsite = scanWebsite;
|
|
205
|
+
//# sourceMappingURL=scanWebsite.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanWebsite.cjs","names":["GREY","GREY_LIGHT","extractScriptUrls","byteLength","checkHtmlAttributes","extractAnchors","checkBundleContent","checkRobots","checkSitemap","mutateScore","toScorePercent"],"sources":["../../../src/scan/scanWebsite.ts"],"sourcesContent":["import { GREY, GREY_LIGHT } from '@intlayer/config/colors';\nimport { colorize, logger } from '@intlayer/config/logger';\nimport { mutateScore, type Score, toScorePercent } from './calculateScore';\nimport {\n checkBundleContent,\n checkCanonical,\n checkHtmlAttributes,\n checkLinguisticStructure,\n checkRobots,\n checkSitemap,\n checkUrlStructure,\n} from './checks';\nimport { byteLength, extractAnchors, extractScriptUrls } from './parseHtml';\nimport type {\n BundleChunkInput,\n ScanEvent,\n ScanOptions,\n ScanResult,\n} from './types';\n\nconst DEFAULT_USER_AGENT =\n 'Mozilla/5.0 (compatible; IntlayerScanBot/1.0; +https://intlayer.org)';\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/**\n * Log a recommendation to install `puppeteer` for a deeper scan. Mirrors the\n * style used by other optional-dependency hints across the CLI.\n */\nconst logDeepScanRecommendation = (): void => {\n logger([\n colorize('Recommended: Install', GREY),\n colorize('puppeteer', GREY_LIGHT),\n colorize(\n 'package to enable a deeper scan (renders client-side content & lazy-loaded chunks). See documentation:',\n GREY\n ),\n colorize('https://intlayer.org/doc/concept/cli#scan', GREY_LIGHT),\n ]);\n};\n\n/** Outcome of a render-based deep scan. */\ntype DeepScanResult = {\n html: string;\n totalPageSize: number;\n chunks: BundleChunkInput[];\n};\n\n/**\n * Render the page with a locally installed `puppeteer` to capture\n * client-rendered content, the accurate transfer size, and lazy-loaded chunks.\n *\n * `puppeteer` is imported dynamically through a non-literal specifier so it is\n * never bundled and stays an optional dependency: when it is absent the import\n * rejects and the caller falls back to the basic scan.\n *\n * @returns The deep-scan result, or `null` when `puppeteer` is unavailable.\n */\nconst runDeepScan = async (\n targetUrl: string,\n userAgent: string,\n timeoutMs: number\n): Promise<DeepScanResult | null> => {\n const moduleName = 'puppeteer';\n // Optional dependency resolved at runtime — typed as `any` because it is not\n // a declared dependency of this package.\n let puppeteer: any;\n try {\n const mod: any = await import(moduleName);\n puppeteer = mod.default ?? mod;\n } catch {\n return null;\n }\n\n let browser: any;\n try {\n browser = await puppeteer.launch({\n headless: true,\n executablePath: process.env.PUPPETEER_EXECUTABLE_PATH,\n args: [\n '--no-sandbox',\n '--disable-setuid-sandbox',\n '--disable-dev-shm-usage',\n '--disable-gpu',\n ],\n });\n\n const page = await browser.newPage();\n await page.setUserAgent(userAgent);\n await page.setExtraHTTPHeaders({ 'Accept-Language': 'en-US,en;q=0.9' });\n\n const origin = new URL(targetUrl).origin;\n const jsResponseMap = new Map<string, string>();\n let totalPageSize = 0;\n const pendingResponses: Promise<void>[] = [];\n\n page.on('response', (response: any) => {\n pendingResponses.push(\n (async () => {\n try {\n if (response.status() !== 200) return;\n const buffer = await response.buffer();\n totalPageSize += buffer.length;\n\n const responseUrl: string = response.url();\n const contentType: string =\n response.headers()['content-type'] ?? '';\n const isJavaScript =\n contentType.includes('javascript') ||\n /\\.(js|mjs|cjs)(\\?|$)/.test(responseUrl);\n if (isJavaScript && responseUrl.startsWith(origin)) {\n jsResponseMap.set(responseUrl, buffer.toString('utf-8'));\n }\n } catch {\n /* response already consumed or aborted */\n }\n })()\n );\n });\n\n await page.goto(targetUrl, {\n waitUntil: 'domcontentloaded',\n timeout: timeoutMs,\n });\n await page\n .waitForNetworkIdle({ idleTime: 1000, timeout: 10_000 })\n .catch(() => {\n /* ok if it never fully idles */\n });\n await Promise.allSettled(pendingResponses);\n\n const html: string = await page.content();\n const mainBundleUrls = new Set(extractScriptUrls(html, targetUrl));\n const chunks: BundleChunkInput[] = Array.from(jsResponseMap.entries()).map(\n ([url, content]) => ({\n url,\n isMainBundle: mainBundleUrls.has(url),\n content,\n })\n );\n\n return { html, totalPageSize, chunks };\n } finally {\n if (browser) await browser.close();\n }\n};\n\n/** Fetch the raw HTML document, measuring its byte size. */\nconst fetchHtml = async (\n url: string,\n userAgent: string,\n timeoutMs: number\n): Promise<{ html: string; finalUrl: string }> => {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const response = await fetch(url, {\n headers: { 'User-Agent': userAgent, 'Accept-Language': 'en-US,en;q=0.9' },\n signal: controller.signal,\n });\n const html = await response.text();\n return { html, finalUrl: response.url || url };\n } finally {\n clearTimeout(timer);\n }\n};\n\n/**\n * Fetch every eagerly-loaded script. Same-origin scripts keep their content so\n * their locale weight can be analyzed; third-party scripts only contribute to\n * the measured page size (their locale-like keys cause false positives).\n */\nconst fetchScripts = async (\n scriptUrls: string[],\n origin: string,\n userAgent: string\n): Promise<{ chunks: BundleChunkInput[]; scriptBytes: number }> => {\n const chunks: BundleChunkInput[] = [];\n let scriptBytes = 0;\n\n await Promise.all(\n scriptUrls.map(async (scriptUrl) => {\n try {\n const response = await fetch(scriptUrl, {\n headers: { 'User-Agent': userAgent },\n });\n if (!response.ok) return;\n const content = await response.text();\n scriptBytes += byteLength(content);\n if (scriptUrl.startsWith(origin)) {\n chunks.push({ url: scriptUrl, isMainBundle: true, content });\n }\n } catch {\n /* ignore unreachable scripts */\n }\n })\n );\n\n return { chunks, scriptBytes };\n};\n\n/**\n * Scan a single web page for i18n/SEO health and bundle weight.\n *\n * In `deep` mode (default) the page is rendered with a locally installed\n * `puppeteer`; when `puppeteer` is missing the scan transparently falls back to\n * a `basic` fetch-based pass and logs a recommendation to install it.\n *\n * @param targetUrl - The absolute URL to scan.\n * @param options - {@link ScanOptions} controlling depth, timeout and UA.\n * @returns The {@link ScanResult} including score, page size and per-check events.\n */\nexport const scanWebsite = async (\n targetUrl: string,\n options: ScanOptions = {}\n): Promise<ScanResult> => {\n const {\n deep = true,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n userAgent = DEFAULT_USER_AGENT,\n } = options;\n\n const origin = new URL(targetUrl).origin;\n\n let mode: ScanResult['mode'] = 'basic';\n let html: string;\n let totalPageSize: number;\n let chunks: BundleChunkInput[];\n\n const deepResult = deep\n ? await runDeepScan(targetUrl, userAgent, timeoutMs)\n : null;\n\n if (deepResult) {\n mode = 'deep';\n html = deepResult.html;\n chunks = deepResult.chunks;\n totalPageSize = deepResult.totalPageSize;\n } else {\n if (deep) logDeepScanRecommendation();\n const { html: fetchedHtml, finalUrl } = await fetchHtml(\n targetUrl,\n userAgent,\n timeoutMs\n );\n html = fetchedHtml;\n const scriptUrls = extractScriptUrls(fetchedHtml, finalUrl);\n const { chunks: fetchedChunks, scriptBytes } = await fetchScripts(\n scriptUrls,\n origin,\n userAgent\n );\n chunks = fetchedChunks;\n totalPageSize = byteLength(fetchedHtml) + scriptBytes;\n }\n\n const htmlSize = byteLength(html);\n\n const events: ScanEvent[] = [];\n const localesSet = new Set<string>();\n\n const { langTag } = checkHtmlAttributes(html, targetUrl, events);\n checkCanonical(html, targetUrl, events);\n checkLinguisticStructure(html, targetUrl, localesSet, events);\n checkUrlStructure(extractAnchors(html), origin, targetUrl, events);\n const bundle = checkBundleContent(\n chunks,\n html,\n langTag,\n targetUrl,\n totalPageSize,\n events\n );\n await checkRobots(origin, localesSet, userAgent, events);\n await checkSitemap(origin, localesSet, userAgent, events);\n\n const rawScore = events.reduce<Score>(\n (score, event) => mutateScore(score, event),\n { score: 0, totalScore: 0 }\n );\n\n return {\n url: targetUrl,\n mode,\n totalPageSize,\n htmlSize,\n score: toScorePercent(rawScore),\n rawScore,\n events,\n locales: Array.from(localesSet),\n bundle,\n };\n};\n"],"mappings":"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"}
|
|
File without changes
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { readDictionariesFromDisk } from "../utils/readDictionariesFromDisk.mjs";
|
|
2
|
-
import { writeDynamicDictionary } from "./writeDynamicDictionary.mjs";
|
|
2
|
+
import { writeDynamicDictionary, writeDynamicQualifiedDictionaries } from "./writeDynamicDictionary.mjs";
|
|
3
3
|
import { writeFetchDictionary } from "./writeFetchDictionary.mjs";
|
|
4
4
|
import { writeMergedDictionaries } from "./writeMergedDictionary.mjs";
|
|
5
5
|
import { writeUnmergedDictionaries } from "./writeUnmergedDictionary.mjs";
|
|
6
|
+
import { colorizeKey, getAppLogger } from "@intlayer/config/logger";
|
|
6
7
|
import { IMPORT_MODE, OUTPUT_FORMAT } from "@intlayer/config/defaultValues";
|
|
8
|
+
import { isQualifiedDictionaryGroup } from "@intlayer/core/dictionaryManipulator";
|
|
7
9
|
|
|
8
10
|
//#region src/buildIntlayerDictionary/buildIntlayerDictionary.ts
|
|
9
11
|
const defaultOptions = {
|
|
@@ -26,22 +28,37 @@ const buildDictionary = async (localDictionariesEntries, configuration, options)
|
|
|
26
28
|
for (const dictionaryToWrite of localDictionariesEntries) {
|
|
27
29
|
const allPrebuiltUnmergedDictionaries = prevUnmergedDictionaries[dictionaryToWrite.key];
|
|
28
30
|
if (allPrebuiltUnmergedDictionaries?.length > 0) {
|
|
29
|
-
const otherUnmergedDictionaries = allPrebuiltUnmergedDictionaries
|
|
30
|
-
unmergedDictionariesToUpdate.push(...otherUnmergedDictionaries);
|
|
31
|
+
const otherUnmergedDictionaries = allPrebuiltUnmergedDictionaries?.filter((unmergedDictionary) => unmergedDictionary.localId !== dictionaryToWrite.localId);
|
|
32
|
+
unmergedDictionariesToUpdate.push(...otherUnmergedDictionaries ?? []);
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
const unmergedDictionaries = await writeUnmergedDictionaries(unmergedDictionariesToUpdate, configuration, env);
|
|
35
37
|
const mergedDictionaries = await writeMergedDictionaries(unmergedDictionaries, configuration);
|
|
36
38
|
const dictionariesToBuildDynamic = {};
|
|
39
|
+
const qualifiedDictionariesToBuildDynamic = {};
|
|
37
40
|
const keysToBuildFetch = /* @__PURE__ */ new Set();
|
|
38
41
|
for (const [key, mergedResult] of Object.entries(mergedDictionaries)) {
|
|
39
|
-
const
|
|
40
|
-
if (
|
|
42
|
+
const dictionary = mergedResult.dictionary;
|
|
43
|
+
if (isQualifiedDictionaryGroup(dictionary)) {
|
|
44
|
+
const entryMode = dictionary.importMode ?? importMode;
|
|
45
|
+
if (entryMode === "dynamic") qualifiedDictionariesToBuildDynamic[key] = {
|
|
46
|
+
dictionaryPath: mergedResult.dictionaryPath,
|
|
47
|
+
dictionary
|
|
48
|
+
};
|
|
49
|
+
else if (entryMode === "fetch") getAppLogger(configuration)(`Dictionary ${colorizeKey(key)} uses 'fetch' import mode with (${dictionary.qualifierTypes.join(", ")}) entries — fetch mode is not qualifier-aware yet, falling back to static import.`, { level: "warn" });
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
const mode = dictionary.importMode ?? importMode;
|
|
53
|
+
if (mode === "dynamic" || mode === "fetch") dictionariesToBuildDynamic[key] = {
|
|
54
|
+
dictionaryPath: mergedResult.dictionaryPath,
|
|
55
|
+
dictionary
|
|
56
|
+
};
|
|
41
57
|
if (mode === "fetch") keysToBuildFetch.add(key);
|
|
42
58
|
}
|
|
43
59
|
let dynamicDictionaries = null;
|
|
44
60
|
if (Object.keys(dictionariesToBuildDynamic).length > 0) dynamicDictionaries = await writeDynamicDictionary(dictionariesToBuildDynamic, configuration, formats);
|
|
61
|
+
if (Object.keys(qualifiedDictionariesToBuildDynamic).length > 0) await writeDynamicQualifiedDictionaries(qualifiedDictionariesToBuildDynamic, configuration, formats);
|
|
45
62
|
let fetchDictionaries = null;
|
|
46
63
|
if (dynamicDictionaries && keysToBuildFetch.size > 0) {
|
|
47
64
|
const dictionariesToBuildFetch = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildIntlayerDictionary.mjs","names":[],"sources":["../../../src/buildIntlayerDictionary/buildIntlayerDictionary.ts"],"sourcesContent":["import { IMPORT_MODE, OUTPUT_FORMAT } from '@intlayer/config/defaultValues';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { readDictionariesFromDisk } from '../utils/readDictionariesFromDisk';\nimport {\n type LocalizedDictionaryOutput,\n writeDynamicDictionary,\n} from './writeDynamicDictionary';\nimport { writeFetchDictionary } from './writeFetchDictionary';\nimport { writeMergedDictionaries
|
|
1
|
+
{"version":3,"file":"buildIntlayerDictionary.mjs","names":[],"sources":["../../../src/buildIntlayerDictionary/buildIntlayerDictionary.ts"],"sourcesContent":["import { IMPORT_MODE, OUTPUT_FORMAT } from '@intlayer/config/defaultValues';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport { isQualifiedDictionaryGroup } from '@intlayer/core/dictionaryManipulator';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { readDictionariesFromDisk } from '../utils/readDictionariesFromDisk';\nimport {\n type LocalizedDictionaryOutput,\n type QualifiedMergedDictionaryOutput,\n writeDynamicDictionary,\n writeDynamicQualifiedDictionaries,\n} from './writeDynamicDictionary';\nimport { writeFetchDictionary } from './writeFetchDictionary';\nimport {\n type PlainMergedDictionaryOutput,\n writeMergedDictionaries,\n} from './writeMergedDictionary';\nimport { writeUnmergedDictionaries } from './writeUnmergedDictionary';\n\nexport type BuildDictionariesOptions = Partial<{\n formats: typeof OUTPUT_FORMAT;\n importOtherDictionaries: boolean;\n env: 'prod' | 'dev';\n}>;\n\nconst defaultOptions = {\n formats: OUTPUT_FORMAT,\n importOtherDictionaries: true,\n env: 'dev',\n} as const satisfies BuildDictionariesOptions;\n\n/**\n * This function transpile the bundled code to to make dictionaries as JSON files\n */\nexport const buildDictionary = async (\n localDictionariesEntries: Dictionary[],\n configuration: IntlayerConfig,\n options?: BuildDictionariesOptions\n) => {\n const importMode =\n configuration?.build?.importMode ??\n configuration?.dictionary?.importMode ??\n IMPORT_MODE;\n\n const { importOtherDictionaries, env, formats } = {\n ...defaultOptions,\n ...options,\n };\n\n const unmergedDictionariesToUpdate: Dictionary[] = [\n ...localDictionariesEntries,\n ];\n\n if (importOtherDictionaries) {\n const prevUnmergedDictionaries: Record<string, Dictionary[]> =\n readDictionariesFromDisk(configuration.system.unmergedDictionariesDir);\n\n // Reinsert other dictionaries with the same key to avoid merging errors\n for (const dictionaryToWrite of localDictionariesEntries) {\n const allPrebuiltUnmergedDictionaries =\n prevUnmergedDictionaries[dictionaryToWrite.key]!;\n\n if (allPrebuiltUnmergedDictionaries?.length > 0) {\n // Do not add the same dictionary again by filtering out the one with the same localId\n const otherUnmergedDictionaries =\n allPrebuiltUnmergedDictionaries?.filter(\n (unmergedDictionary) =>\n unmergedDictionary.localId !== dictionaryToWrite.localId\n );\n\n unmergedDictionariesToUpdate.push(...(otherUnmergedDictionaries ?? []));\n }\n }\n }\n\n const unmergedDictionaries = await writeUnmergedDictionaries(\n unmergedDictionariesToUpdate,\n configuration,\n env\n );\n\n const mergedDictionaries = await writeMergedDictionaries(\n unmergedDictionaries,\n configuration\n );\n\n const dictionariesToBuildDynamic: PlainMergedDictionaryOutput = {};\n const qualifiedDictionariesToBuildDynamic: QualifiedMergedDictionaryOutput =\n {};\n const keysToBuildFetch = new Set<string>();\n\n for (const [key, mergedResult] of Object.entries(mergedDictionaries)) {\n const dictionary = mergedResult.dictionary;\n\n if (isQualifiedDictionaryGroup(dictionary)) {\n // Collections / variants / meta records resolve their qualifier at\n // runtime from a selector. Static mode keeps every entry in one JSON;\n // dynamic mode splits them into one chunk per (locale, qualifierId).\n const entryMode = dictionary.importMode ?? importMode;\n\n if (entryMode === 'dynamic') {\n qualifiedDictionariesToBuildDynamic[key] = {\n dictionaryPath: mergedResult.dictionaryPath,\n dictionary,\n };\n } else if (entryMode === 'fetch') {\n const appLogger = getAppLogger(configuration);\n appLogger(\n `Dictionary ${colorizeKey(key)} uses 'fetch' import mode with (${dictionary.qualifierTypes.join(', ')}) entries — fetch mode is not qualifier-aware yet, falling back to static import.`,\n { level: 'warn' }\n );\n }\n\n continue;\n }\n\n const mode = dictionary.importMode ?? importMode;\n\n if (mode === 'dynamic' || mode === 'fetch') {\n dictionariesToBuildDynamic[key] = {\n dictionaryPath: mergedResult.dictionaryPath,\n dictionary,\n };\n }\n\n if (mode === 'fetch') {\n keysToBuildFetch.add(key);\n }\n }\n\n let dynamicDictionaries: LocalizedDictionaryOutput | null = null;\n\n if (Object.keys(dictionariesToBuildDynamic).length > 0) {\n dynamicDictionaries = await writeDynamicDictionary(\n dictionariesToBuildDynamic,\n configuration,\n formats\n );\n }\n\n if (Object.keys(qualifiedDictionariesToBuildDynamic).length > 0) {\n await writeDynamicQualifiedDictionaries(\n qualifiedDictionariesToBuildDynamic,\n configuration,\n formats\n );\n }\n\n let fetchDictionaries: LocalizedDictionaryOutput | null = null;\n\n if (dynamicDictionaries && keysToBuildFetch.size > 0) {\n const dictionariesToBuildFetch: LocalizedDictionaryOutput = {};\n\n for (const key of keysToBuildFetch) {\n if (dynamicDictionaries[key]) {\n dictionariesToBuildFetch[key] = dynamicDictionaries[key];\n }\n }\n\n if (Object.keys(dictionariesToBuildFetch).length > 0) {\n fetchDictionaries = await writeFetchDictionary(\n dictionariesToBuildFetch,\n configuration,\n formats\n );\n }\n }\n\n return {\n unmergedDictionaries,\n mergedDictionaries,\n dynamicDictionaries,\n fetchDictionaries,\n };\n};\n"],"mappings":";;;;;;;;;;AAyBA,MAAM,iBAAiB;CACrB,SAAS;CACT,yBAAyB;CACzB,KAAK;CACN;;;;AAKD,MAAa,kBAAkB,OAC7B,0BACA,eACA,YACG;CACH,MAAM,aACJ,eAAe,OAAO,cACtB,eAAe,YAAY,cAC3B;CAEF,MAAM,EAAE,yBAAyB,KAAK,YAAY;EAChD,GAAG;EACH,GAAG;EACJ;CAED,MAAM,+BAA6C,CACjD,GAAG,yBACJ;AAED,KAAI,yBAAyB;EAC3B,MAAM,2BACJ,yBAAyB,cAAc,OAAO,wBAAwB;AAGxE,OAAK,MAAM,qBAAqB,0BAA0B;GACxD,MAAM,kCACJ,yBAAyB,kBAAkB;AAE7C,OAAI,iCAAiC,SAAS,GAAG;IAE/C,MAAM,4BACJ,iCAAiC,QAC9B,uBACC,mBAAmB,YAAY,kBAAkB,QACpD;AAEH,iCAA6B,KAAK,GAAI,6BAA6B,EAAE,CAAE;;;;CAK7E,MAAM,uBAAuB,MAAM,0BACjC,8BACA,eACA,IACD;CAED,MAAM,qBAAqB,MAAM,wBAC/B,sBACA,cACD;CAED,MAAM,6BAA0D,EAAE;CAClE,MAAM,sCACJ,EAAE;CACJ,MAAM,mCAAmB,IAAI,KAAa;AAE1C,MAAK,MAAM,CAAC,KAAK,iBAAiB,OAAO,QAAQ,mBAAmB,EAAE;EACpE,MAAM,aAAa,aAAa;AAEhC,MAAI,2BAA2B,WAAW,EAAE;GAI1C,MAAM,YAAY,WAAW,cAAc;AAE3C,OAAI,cAAc,UAChB,qCAAoC,OAAO;IACzC,gBAAgB,aAAa;IAC7B;IACD;YACQ,cAAc,QAEvB,CADkB,aAAa,cACtB,CACP,cAAc,YAAY,IAAI,CAAC,kCAAkC,WAAW,eAAe,KAAK,KAAK,CAAC,oFACtG,EAAE,OAAO,QAAQ,CAClB;AAGH;;EAGF,MAAM,OAAO,WAAW,cAAc;AAEtC,MAAI,SAAS,aAAa,SAAS,QACjC,4BAA2B,OAAO;GAChC,gBAAgB,aAAa;GAC7B;GACD;AAGH,MAAI,SAAS,QACX,kBAAiB,IAAI,IAAI;;CAI7B,IAAI,sBAAwD;AAE5D,KAAI,OAAO,KAAK,2BAA2B,CAAC,SAAS,EACnD,uBAAsB,MAAM,uBAC1B,4BACA,eACA,QACD;AAGH,KAAI,OAAO,KAAK,oCAAoC,CAAC,SAAS,EAC5D,OAAM,kCACJ,qCACA,eACA,QACD;CAGH,IAAI,oBAAsD;AAE1D,KAAI,uBAAuB,iBAAiB,OAAO,GAAG;EACpD,MAAM,2BAAsD,EAAE;AAE9D,OAAK,MAAM,OAAO,iBAChB,KAAI,oBAAoB,KACtB,0BAAyB,OAAO,oBAAoB;AAIxD,MAAI,OAAO,KAAK,yBAAyB,CAAC,SAAS,EACjD,qBAAoB,MAAM,qBACxB,0BACA,eACA,QACD;;AAIL,QAAO;EACL;EACA;EACA;EACA;EACD"}
|
|
@@ -6,6 +6,7 @@ import { resolve } from "node:path";
|
|
|
6
6
|
import { colorizePath } from "@intlayer/config/logger";
|
|
7
7
|
import { assertPathWithin } from "@intlayer/config/utils";
|
|
8
8
|
import { OUTPUT_FORMAT } from "@intlayer/config/defaultValues";
|
|
9
|
+
import { COMPOSITE_ID_SEPARATOR, QUALIFIER_DYNAMIC_TYPES_KEY, reconstructQualifiedEntry } from "@intlayer/core/dictionaryManipulator";
|
|
9
10
|
import { getPerLocaleDictionary } from "@intlayer/core/plugins";
|
|
10
11
|
|
|
11
12
|
//#region src/buildIntlayerDictionary/writeDynamicDictionary.ts
|
|
@@ -18,6 +19,51 @@ const generateDictionaryEntryPoint = (key, locales, format = "esm") => {
|
|
|
18
19
|
if (format === "esm") return `const content = {\n${localeEntries}\n};\n\nexport default content;\n`;
|
|
19
20
|
return `module.exports = {\n${localeEntries}\n};\n`;
|
|
20
21
|
};
|
|
22
|
+
const buildLoaderExpression = (key, segments, locale, format) => {
|
|
23
|
+
const path = `./${DICTIONARIES_SUBDIR}/${key}/${segments.join("/")}/${locale}.json`;
|
|
24
|
+
return format === "esm" ? `() => import('${path}').then(m => m.default)` : `() => Promise.resolve(require('${path}'))`;
|
|
25
|
+
};
|
|
26
|
+
const buildLoaderTree = (key, entriesSegments, locale, format) => {
|
|
27
|
+
const root = {};
|
|
28
|
+
for (const segments of entriesSegments) {
|
|
29
|
+
let node = root;
|
|
30
|
+
segments.forEach((segment, index) => {
|
|
31
|
+
if (index === segments.length - 1) {
|
|
32
|
+
node[segment] = buildLoaderExpression(key, segments, locale, format);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
node[segment] = node[segment] ?? {};
|
|
36
|
+
node = node[segment];
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return root;
|
|
40
|
+
};
|
|
41
|
+
const serializeLoaderTree = (tree, indentLevel) => {
|
|
42
|
+
const pad = " ".repeat(indentLevel);
|
|
43
|
+
const innerPad = " ".repeat(indentLevel + 1);
|
|
44
|
+
return `{\n${Object.keys(tree).sort((a, b) => a.localeCompare(b)).map((segment) => {
|
|
45
|
+
const value = tree[segment];
|
|
46
|
+
return `${innerPad}'${segment}': ${typeof value === "string" ? value : serializeLoaderTree(value, indentLevel + 1)}`;
|
|
47
|
+
}).join(",\n")}\n${pad}}`;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Generates the entry point of a qualified dictionary (collection / variant /
|
|
51
|
+
* meta record, possibly combined). Under each locale the loader map nests one
|
|
52
|
+
* level per declared dimension (canonical order) and carries a marker listing
|
|
53
|
+
* those dimensions so the runtime can walk the tree.
|
|
54
|
+
*
|
|
55
|
+
* One static `import()` is emitted per leaf `(locale, …segments)` chunk, which
|
|
56
|
+
* keeps the output compatible with bundlers that reject template-literal
|
|
57
|
+
* dynamic imports (Turbopack).
|
|
58
|
+
*/
|
|
59
|
+
const generateQualifiedDictionaryEntryPoint = (key, qualifierTypes, entriesSegments, locales, format = "esm") => {
|
|
60
|
+
const localeEntries = [...locales].sort((a, b) => String(a).localeCompare(String(b))).map((locale) => {
|
|
61
|
+
return ` '${locale}': ${serializeLoaderTree(buildLoaderTree(key, entriesSegments, locale, format), 1)}`;
|
|
62
|
+
}).join(",\n");
|
|
63
|
+
const marker = ` '${QUALIFIER_DYNAMIC_TYPES_KEY}': ${JSON.stringify(qualifierTypes)}`;
|
|
64
|
+
if (format === "esm") return `const content = {\n${marker},\n${localeEntries}\n};\n\nexport default content;\n`;
|
|
65
|
+
return `module.exports = {\n${marker},\n${localeEntries}\n};\n`;
|
|
66
|
+
};
|
|
21
67
|
/**
|
|
22
68
|
* Write the localized dictionaries to the dictionariesDir
|
|
23
69
|
* @param mergedDictionaries - The merged dictionaries
|
|
@@ -70,7 +116,53 @@ const writeDynamicDictionary = async (mergedDictionaries, configuration, formats
|
|
|
70
116
|
});
|
|
71
117
|
return resultDictionariesPaths;
|
|
72
118
|
};
|
|
119
|
+
/**
|
|
120
|
+
* Writes the dynamic chunks and entry points of qualified dictionaries
|
|
121
|
+
* (collections, variants, meta records — possibly combined) in
|
|
122
|
+
* `importMode: 'dynamic'`.
|
|
123
|
+
*
|
|
124
|
+
* Each entry is reduced to one per-locale chunk written to a path nested by
|
|
125
|
+
* dimension — `json/{key}/{seg1}/{seg2}/{locale}.json` — and a single
|
|
126
|
+
* `{key}.{ext}` entry point exposes the matching nested loader tree, so the
|
|
127
|
+
* entry point is discovered and aggregated exactly like a plain dynamic one.
|
|
128
|
+
*/
|
|
129
|
+
const writeDynamicQualifiedDictionaries = async (qualifiedDictionaries, configuration, formats = OUTPUT_FORMAT) => {
|
|
130
|
+
const { locales, defaultLocale } = configuration.internationalization;
|
|
131
|
+
const { dynamicDictionariesDir } = configuration.system;
|
|
132
|
+
const dictDir = resolve(dynamicDictionariesDir, DICTIONARIES_SUBDIR);
|
|
133
|
+
await mkdir(dictDir, { recursive: true });
|
|
134
|
+
await parallelize(Object.entries(qualifiedDictionaries).sort(([a], [b]) => String(a).localeCompare(String(b))), async ([key, { dictionary: group }]) => {
|
|
135
|
+
if (key === "undefined") return;
|
|
136
|
+
const entryIds = Object.keys(group.content);
|
|
137
|
+
const keyDir = resolve(dictDir, key);
|
|
138
|
+
assertPathWithin(keyDir, dictDir);
|
|
139
|
+
const entriesSegments = [];
|
|
140
|
+
await parallelize(entryIds, async (entryId) => {
|
|
141
|
+
const entry = reconstructQualifiedEntry(group, entryId);
|
|
142
|
+
const segments = entryId.split(COMPOSITE_ID_SEPARATOR);
|
|
143
|
+
entriesSegments.push(segments);
|
|
144
|
+
const entryDir = resolve(keyDir, ...segments);
|
|
145
|
+
assertPathWithin(entryDir, keyDir);
|
|
146
|
+
await mkdir(entryDir, { recursive: true });
|
|
147
|
+
await parallelize(locales, async (locale) => {
|
|
148
|
+
const localizedDictionary = getPerLocaleDictionary(entry, locale, defaultLocale);
|
|
149
|
+
await writeJsonIfChanged(resolve(entryDir, `${locale}.json`), localizedDictionary).catch((err) => {
|
|
150
|
+
console.error(`Error creating localized ${key}/${segments.join("/")}/${locale}.json:`, err);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
await parallelize(formats, async (format) => {
|
|
155
|
+
const extension = format === "cjs" ? "cjs" : "mjs";
|
|
156
|
+
const content = generateQualifiedDictionaryEntryPoint(key, group.qualifierTypes, entriesSegments, locales, format);
|
|
157
|
+
const dynEntryPath = resolve(dynamicDictionariesDir, `${key}.${extension}`);
|
|
158
|
+
assertPathWithin(dynEntryPath, dynamicDictionariesDir);
|
|
159
|
+
await writeFileIfChanged(dynEntryPath, content).catch((err) => {
|
|
160
|
+
console.error(`Error creating dynamic ${colorizePath(dynEntryPath)}:`, err);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
};
|
|
73
165
|
|
|
74
166
|
//#endregion
|
|
75
|
-
export { generateDictionaryEntryPoint, writeDynamicDictionary };
|
|
167
|
+
export { generateDictionaryEntryPoint, generateQualifiedDictionaryEntryPoint, writeDynamicDictionary, writeDynamicQualifiedDictionaries };
|
|
76
168
|
//# sourceMappingURL=writeDynamicDictionary.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeDynamicDictionary.mjs","names":[],"sources":["../../../src/buildIntlayerDictionary/writeDynamicDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { OUTPUT_FORMAT } from '@intlayer/config/defaultValues';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport { getPerLocaleDictionary } from '@intlayer/core/plugins';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport { parallelize } from '../utils/parallelize';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\nimport type { MergedDictionaryOutput } from './writeMergedDictionary';\n\nexport type DictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary;\n};\n\nexport type LocalizedDictionaryResult = Partial<\n Record<Locale, DictionaryResult>\n>;\n\nexport type LocalizedDictionaryOutput = Record<\n string,\n LocalizedDictionaryResult\n>;\n\nconst DICTIONARIES_SUBDIR = 'json'; // Necessary to add a static first dir for Turbopack\n\n/**\n * Generates the content of a dictionary entry point file.\n */\nexport const generateDictionaryEntryPoint = (\n key: string,\n locales: string[],\n format: 'cjs' | 'esm' = 'esm'\n): string => {\n const sortedLocales = [...locales].sort((a, b) =>\n String(a).localeCompare(String(b))\n );\n\n const localeEntries = sortedLocales\n .map((locale) =>\n format === 'esm'\n ? ` '${locale}': () => import('./${DICTIONARIES_SUBDIR}/${key}/${locale}.json').then(m => m.default)`\n : ` '${locale}': () => Promise.resolve(require('./${DICTIONARIES_SUBDIR}/${key}/${locale}.json'))`\n )\n .join(',\\n');\n\n if (format === 'esm') {\n return (\n `const content = {\\n${localeEntries}\\n};\\n\\n` +\n `export default content;\\n`\n );\n }\n return `module.exports = {\\n${localeEntries}\\n};\\n`;\n};\n\n/**\n * Write the localized dictionaries to the dictionariesDir\n * @param mergedDictionaries - The merged dictionaries\n * @param configuration - The configuration\n * @returns The final dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * const finalDictionaries = await writeFinalDictionaries(unmergedDictionaries);\n * console.log(finalDictionaries);\n *\n * // .intlayer/dynamic_dictionary/dictionaries/en_home.json\n * // .intlayer/dynamic_dictionary/dictionaries/fr_home.json\n * ```\n */\nexport const writeDynamicDictionary = async (\n mergedDictionaries: MergedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT\n): Promise<LocalizedDictionaryOutput> => {\n const { locales, defaultLocale } = configuration.internationalization;\n const { dynamicDictionariesDir } = configuration.system;\n\n const dictDir = resolve(dynamicDictionariesDir, DICTIONARIES_SUBDIR);\n await mkdir(dictDir, { recursive: true });\n\n const resultDictionariesPaths: LocalizedDictionaryOutput = {};\n\n // Merge dictionaries with the same key and write to dictionariesDir\n await parallelize(\n Object.entries(mergedDictionaries).sort(([a], [b]) =>\n String(a).localeCompare(String(b))\n ),\n async ([key, dictionaryEntry]) => {\n if (key === 'undefined') return;\n\n const localizedDictionariesPathsRecord: LocalizedDictionaryResult = {};\n\n const keyDir = resolve(dictDir, key);\n assertPathWithin(keyDir, dictDir);\n await mkdir(keyDir, { recursive: true });\n\n await parallelize(locales, async (locale) => {\n const localizedDictionary = getPerLocaleDictionary(\n dictionaryEntry.dictionary,\n locale,\n defaultLocale\n );\n\n // Directory structure: json/key/locale.json\n const resultFilePath = resolve(keyDir, `${locale}.json`);\n\n await writeJsonIfChanged(resultFilePath, localizedDictionary).catch(\n (err) => {\n console.error(\n `Error creating localized ${key}/${locale}.json:`,\n err\n );\n }\n );\n\n localizedDictionariesPathsRecord[locale] = {\n dictionaryPath: resultFilePath,\n dictionary: localizedDictionary,\n };\n });\n\n resultDictionariesPaths[key] = localizedDictionariesPathsRecord;\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateDictionaryEntryPoint(key, locales, format);\n\n const dynEntryPath = resolve(\n dynamicDictionariesDir,\n `${key}.${extension}`\n );\n assertPathWithin(dynEntryPath, dynamicDictionariesDir);\n\n await writeFileIfChanged(dynEntryPath, content).catch((err) => {\n console.error(\n `Error creating dynamic ${colorizePath(dynEntryPath)}:`,\n err\n );\n });\n });\n }\n );\n\n return resultDictionariesPaths;\n};\n"],"mappings":";;;;;;;;;;;AA4BA,MAAM,sBAAsB;;;;AAK5B,MAAa,gCACX,KACA,SACA,SAAwB,UACb;CAKX,MAAM,gBAJgB,CAAC,GAAG,QAAQ,CAAC,MAAM,GAAG,MAC1C,OAAO,EAAE,CAAC,cAAc,OAAO,EAAE,CAAC,CAGD,CAChC,KAAK,WACJ,WAAW,QACP,MAAM,OAAO,qBAAqB,oBAAoB,GAAG,IAAI,GAAG,OAAO,gCACvE,MAAM,OAAO,sCAAsC,oBAAoB,GAAG,IAAI,GAAG,OAAO,UAC7F,CACA,KAAK,MAAM;AAEd,KAAI,WAAW,MACb,QACE,sBAAsB,cAAc;AAIxC,QAAO,uBAAuB,cAAc;;;;;;;;;;;;;;;;;;AAmB9C,MAAa,yBAAyB,OACpC,oBACA,eACA,UAA6B,kBACU;CACvC,MAAM,EAAE,SAAS,kBAAkB,cAAc;CACjD,MAAM,EAAE,2BAA2B,cAAc;CAEjD,MAAM,UAAU,QAAQ,wBAAwB,oBAAoB;AACpE,OAAM,MAAM,SAAS,EAAE,WAAW,MAAM,CAAC;CAEzC,MAAM,0BAAqD,EAAE;AAG7D,OAAM,YACJ,OAAO,QAAQ,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,OAC7C,OAAO,EAAE,CAAC,cAAc,OAAO,EAAE,CAAC,CACnC,EACD,OAAO,CAAC,KAAK,qBAAqB;AAChC,MAAI,QAAQ,YAAa;EAEzB,MAAM,mCAA8D,EAAE;EAEtE,MAAM,SAAS,QAAQ,SAAS,IAAI;AACpC,mBAAiB,QAAQ,QAAQ;AACjC,QAAM,MAAM,QAAQ,EAAE,WAAW,MAAM,CAAC;AAExC,QAAM,YAAY,SAAS,OAAO,WAAW;GAC3C,MAAM,sBAAsB,uBAC1B,gBAAgB,YAChB,QACA,cACD;GAGD,MAAM,iBAAiB,QAAQ,QAAQ,GAAG,OAAO,OAAO;AAExD,SAAM,mBAAmB,gBAAgB,oBAAoB,CAAC,OAC3D,QAAQ;AACP,YAAQ,MACN,4BAA4B,IAAI,GAAG,OAAO,SAC1C,IACD;KAEJ;AAED,oCAAiC,UAAU;IACzC,gBAAgB;IAChB,YAAY;IACb;IACD;AAEF,0BAAwB,OAAO;AAE/B,QAAM,YAAY,SAAS,OAAO,WAAW;GAC3C,MAAM,YAAY,WAAW,QAAQ,QAAQ;GAC7C,MAAM,UAAU,6BAA6B,KAAK,SAAS,OAAO;GAElE,MAAM,eAAe,QACnB,wBACA,GAAG,IAAI,GAAG,YACX;AACD,oBAAiB,cAAc,uBAAuB;AAEtD,SAAM,mBAAmB,cAAc,QAAQ,CAAC,OAAO,QAAQ;AAC7D,YAAQ,MACN,0BAA0B,aAAa,aAAa,CAAC,IACrD,IACD;KACD;IACF;GAEL;AAED,QAAO"}
|
|
1
|
+
{"version":3,"file":"writeDynamicDictionary.mjs","names":[],"sources":["../../../src/buildIntlayerDictionary/writeDynamicDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { OUTPUT_FORMAT } from '@intlayer/config/defaultValues';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport {\n COMPOSITE_ID_SEPARATOR,\n QUALIFIER_DYNAMIC_TYPES_KEY,\n reconstructQualifiedEntry,\n} from '@intlayer/core/dictionaryManipulator';\nimport { getPerLocaleDictionary } from '@intlayer/core/plugins';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n Dictionary,\n DictionaryQualifierType,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport { parallelize } from '../utils/parallelize';\nimport { writeFileIfChanged } from '../writeFileIfChanged';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\nimport type { PlainMergedDictionaryOutput } from './writeMergedDictionary';\n\nexport type DictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary;\n};\n\nexport type LocalizedDictionaryResult = Partial<\n Record<Locale, DictionaryResult>\n>;\n\nexport type LocalizedDictionaryOutput = Record<\n string,\n LocalizedDictionaryResult\n>;\n\nconst DICTIONARIES_SUBDIR = 'json'; // Necessary to add a static first dir for Turbopack\n\n/**\n * Generates the content of a dictionary entry point file.\n */\nexport const generateDictionaryEntryPoint = (\n key: string,\n locales: string[],\n format: 'cjs' | 'esm' = 'esm'\n): string => {\n const sortedLocales = [...locales].sort((a, b) =>\n String(a).localeCompare(String(b))\n );\n\n const localeEntries = sortedLocales\n .map((locale) =>\n format === 'esm'\n ? ` '${locale}': () => import('./${DICTIONARIES_SUBDIR}/${key}/${locale}.json').then(m => m.default)`\n : ` '${locale}': () => Promise.resolve(require('./${DICTIONARIES_SUBDIR}/${key}/${locale}.json'))`\n )\n .join(',\\n');\n\n if (format === 'esm') {\n return (\n `const content = {\\n${localeEntries}\\n};\\n\\n` +\n `export default content;\\n`\n );\n }\n return `module.exports = {\\n${localeEntries}\\n};\\n`;\n};\n\n/**\n * A nested loader tree: one level per declared dimension, leaves are loader\n * expression strings.\n */\ntype LoaderTree = { [segment: string]: LoaderTree | string };\n\nconst buildLoaderExpression = (\n key: string,\n segments: string[],\n locale: string,\n format: 'cjs' | 'esm'\n): string => {\n const path = `./${DICTIONARIES_SUBDIR}/${key}/${segments.join('/')}/${locale}.json`;\n\n return format === 'esm'\n ? `() => import('${path}').then(m => m.default)`\n : `() => Promise.resolve(require('${path}'))`;\n};\n\nconst buildLoaderTree = (\n key: string,\n entriesSegments: string[][],\n locale: string,\n format: 'cjs' | 'esm'\n): LoaderTree => {\n const root: LoaderTree = {};\n\n for (const segments of entriesSegments) {\n let node = root;\n\n segments.forEach((segment, index) => {\n if (index === segments.length - 1) {\n node[segment] = buildLoaderExpression(key, segments, locale, format);\n return;\n }\n\n node[segment] = (node[segment] as LoaderTree | undefined) ?? {};\n node = node[segment] as LoaderTree;\n });\n }\n\n return root;\n};\n\nconst serializeLoaderTree = (tree: LoaderTree, indentLevel: number): string => {\n const pad = ' '.repeat(indentLevel);\n const innerPad = ' '.repeat(indentLevel + 1);\n\n const lines = Object.keys(tree)\n .sort((a, b) => a.localeCompare(b))\n .map((segment) => {\n const value = tree[segment]!;\n const serialized =\n typeof value === 'string'\n ? value\n : serializeLoaderTree(value, indentLevel + 1);\n\n return `${innerPad}'${segment}': ${serialized}`;\n });\n\n return `{\\n${lines.join(',\\n')}\\n${pad}}`;\n};\n\n/**\n * Generates the entry point of a qualified dictionary (collection / variant /\n * meta record, possibly combined). Under each locale the loader map nests one\n * level per declared dimension (canonical order) and carries a marker listing\n * those dimensions so the runtime can walk the tree.\n *\n * One static `import()` is emitted per leaf `(locale, …segments)` chunk, which\n * keeps the output compatible with bundlers that reject template-literal\n * dynamic imports (Turbopack).\n */\nexport const generateQualifiedDictionaryEntryPoint = (\n key: string,\n qualifierTypes: DictionaryQualifierType[],\n entriesSegments: string[][],\n locales: string[],\n format: 'cjs' | 'esm' = 'esm'\n): string => {\n const sortedLocales = [...locales].sort((a, b) =>\n String(a).localeCompare(String(b))\n );\n\n const localeEntries = sortedLocales\n .map((locale) => {\n const tree = buildLoaderTree(key, entriesSegments, locale, format);\n return ` '${locale}': ${serializeLoaderTree(tree, 1)}`;\n })\n .join(',\\n');\n\n const marker = ` '${QUALIFIER_DYNAMIC_TYPES_KEY}': ${JSON.stringify(qualifierTypes)}`;\n\n if (format === 'esm') {\n return (\n `const content = {\\n${marker},\\n${localeEntries}\\n};\\n\\n` +\n `export default content;\\n`\n );\n }\n return `module.exports = {\\n${marker},\\n${localeEntries}\\n};\\n`;\n};\n\n/**\n * Write the localized dictionaries to the dictionariesDir\n * @param mergedDictionaries - The merged dictionaries\n * @param configuration - The configuration\n * @returns The final dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * const finalDictionaries = await writeFinalDictionaries(unmergedDictionaries);\n * console.log(finalDictionaries);\n *\n * // .intlayer/dynamic_dictionary/dictionaries/en_home.json\n * // .intlayer/dynamic_dictionary/dictionaries/fr_home.json\n * ```\n */\nexport const writeDynamicDictionary = async (\n mergedDictionaries: PlainMergedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT\n): Promise<LocalizedDictionaryOutput> => {\n const { locales, defaultLocale } = configuration.internationalization;\n const { dynamicDictionariesDir } = configuration.system;\n\n const dictDir = resolve(dynamicDictionariesDir, DICTIONARIES_SUBDIR);\n await mkdir(dictDir, { recursive: true });\n\n const resultDictionariesPaths: LocalizedDictionaryOutput = {};\n\n // Merge dictionaries with the same key and write to dictionariesDir\n await parallelize(\n Object.entries(mergedDictionaries).sort(([a], [b]) =>\n String(a).localeCompare(String(b))\n ),\n async ([key, dictionaryEntry]) => {\n if (key === 'undefined') return;\n\n const localizedDictionariesPathsRecord: LocalizedDictionaryResult = {};\n\n const keyDir = resolve(dictDir, key);\n assertPathWithin(keyDir, dictDir);\n await mkdir(keyDir, { recursive: true });\n\n await parallelize(locales, async (locale) => {\n const localizedDictionary = getPerLocaleDictionary(\n dictionaryEntry.dictionary,\n locale,\n defaultLocale\n );\n\n // Directory structure: json/key/locale.json\n const resultFilePath = resolve(keyDir, `${locale}.json`);\n\n await writeJsonIfChanged(resultFilePath, localizedDictionary).catch(\n (err) => {\n console.error(\n `Error creating localized ${key}/${locale}.json:`,\n err\n );\n }\n );\n\n localizedDictionariesPathsRecord[locale] = {\n dictionaryPath: resultFilePath,\n dictionary: localizedDictionary,\n };\n });\n\n resultDictionariesPaths[key] = localizedDictionariesPathsRecord;\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateDictionaryEntryPoint(key, locales, format);\n\n const dynEntryPath = resolve(\n dynamicDictionariesDir,\n `${key}.${extension}`\n );\n assertPathWithin(dynEntryPath, dynamicDictionariesDir);\n\n await writeFileIfChanged(dynEntryPath, content).catch((err) => {\n console.error(\n `Error creating dynamic ${colorizePath(dynEntryPath)}:`,\n err\n );\n });\n });\n }\n );\n\n return resultDictionariesPaths;\n};\n\nexport type QualifiedMergedDictionaryResult = {\n dictionaryPath: string;\n dictionary: QualifiedDictionaryGroup;\n};\n\nexport type QualifiedMergedDictionaryOutput = Record<\n string,\n QualifiedMergedDictionaryResult\n>;\n\n/**\n * Writes the dynamic chunks and entry points of qualified dictionaries\n * (collections, variants, meta records — possibly combined) in\n * `importMode: 'dynamic'`.\n *\n * Each entry is reduced to one per-locale chunk written to a path nested by\n * dimension — `json/{key}/{seg1}/{seg2}/{locale}.json` — and a single\n * `{key}.{ext}` entry point exposes the matching nested loader tree, so the\n * entry point is discovered and aggregated exactly like a plain dynamic one.\n */\nexport const writeDynamicQualifiedDictionaries = async (\n qualifiedDictionaries: QualifiedMergedDictionaryOutput,\n configuration: IntlayerConfig,\n formats: ('cjs' | 'esm')[] = OUTPUT_FORMAT\n): Promise<void> => {\n const { locales, defaultLocale } = configuration.internationalization;\n const { dynamicDictionariesDir } = configuration.system;\n\n const dictDir = resolve(dynamicDictionariesDir, DICTIONARIES_SUBDIR);\n await mkdir(dictDir, { recursive: true });\n\n await parallelize(\n Object.entries(qualifiedDictionaries).sort(([a], [b]) =>\n String(a).localeCompare(String(b))\n ),\n async ([key, { dictionary: group }]) => {\n if (key === 'undefined') return;\n\n const entryIds = Object.keys(group.content);\n\n const keyDir = resolve(dictDir, key);\n assertPathWithin(keyDir, dictDir);\n\n // Per-entry segment lists (one segment per declared dimension), reused for\n // both the chunk paths and the generated loader tree.\n const entriesSegments: string[][] = [];\n\n await parallelize(entryIds, async (entryId) => {\n // Rebuild a resolvable dictionary from the content node + composite id\n // so per-locale extraction sees the same `{ key, content, meta? }` shape.\n const entry = reconstructQualifiedEntry(group, entryId);\n\n const segments = entryId.split(COMPOSITE_ID_SEPARATOR);\n entriesSegments.push(segments);\n\n const entryDir = resolve(keyDir, ...segments);\n assertPathWithin(entryDir, keyDir);\n await mkdir(entryDir, { recursive: true });\n\n await parallelize(locales, async (locale) => {\n const localizedDictionary = getPerLocaleDictionary(\n entry,\n locale,\n defaultLocale\n );\n\n // Directory structure: json/key/<…segments>/locale.json\n const resultFilePath = resolve(entryDir, `${locale}.json`);\n\n await writeJsonIfChanged(resultFilePath, localizedDictionary).catch(\n (err) => {\n console.error(\n `Error creating localized ${key}/${segments.join('/')}/${locale}.json:`,\n err\n );\n }\n );\n });\n });\n\n await parallelize(formats, async (format) => {\n const extension = format === 'cjs' ? 'cjs' : 'mjs';\n const content = generateQualifiedDictionaryEntryPoint(\n key,\n group.qualifierTypes,\n entriesSegments,\n locales,\n format\n );\n\n const dynEntryPath = resolve(\n dynamicDictionariesDir,\n `${key}.${extension}`\n );\n assertPathWithin(dynEntryPath, dynamicDictionariesDir);\n\n await writeFileIfChanged(dynEntryPath, content).catch((err) => {\n console.error(\n `Error creating dynamic ${colorizePath(dynEntryPath)}:`,\n err\n );\n });\n });\n }\n );\n};\n"],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { mkdir } from "node:fs/promises";
|
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
import { colorizePath } from "@intlayer/config/logger";
|
|
6
6
|
import { assertPathWithin } from "@intlayer/config/utils";
|
|
7
|
-
import {
|
|
7
|
+
import { mergeQualifiedDictionaries, normalizeDictionaries } from "@intlayer/core/dictionaryManipulator";
|
|
8
8
|
|
|
9
9
|
//#region src/buildIntlayerDictionary/writeMergedDictionary.ts
|
|
10
10
|
/**
|
|
@@ -28,7 +28,7 @@ const writeMergedDictionaries = async (groupedDictionaries, configuration) => {
|
|
|
28
28
|
await mkdir(resolve(dictionariesDir), { recursive: true });
|
|
29
29
|
const results = await parallelize(Object.entries(groupedDictionaries), async ([key, dictionariesEntry]) => {
|
|
30
30
|
if (key === "undefined") return;
|
|
31
|
-
const mergedDictionary =
|
|
31
|
+
const mergedDictionary = mergeQualifiedDictionaries(normalizeDictionaries(dictionariesEntry.dictionaries, configuration));
|
|
32
32
|
const resultFilePath = resolve(dictionariesDir, `${key}.json`);
|
|
33
33
|
assertPathWithin(resultFilePath, dictionariesDir);
|
|
34
34
|
await writeJsonIfChanged(resultFilePath, mergedDictionary).catch((err) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeMergedDictionary.mjs","names":[],"sources":["../../../src/buildIntlayerDictionary/writeMergedDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport {\n
|
|
1
|
+
{"version":3,"file":"writeMergedDictionary.mjs","names":[],"sources":["../../../src/buildIntlayerDictionary/writeMergedDictionary.ts"],"sourcesContent":["import { mkdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { colorizePath } from '@intlayer/config/logger';\nimport { assertPathWithin } from '@intlayer/config/utils';\nimport {\n mergeQualifiedDictionaries,\n normalizeDictionaries,\n} from '@intlayer/core/dictionaryManipulator';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n Dictionary,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport { parallelize } from '../utils/parallelize';\nimport { writeJsonIfChanged } from '../writeJsonIfChanged';\nimport type { UnmergedDictionaryOutput } from './writeUnmergedDictionary';\n\nexport type MergedDictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary | QualifiedDictionaryGroup;\n};\n\nexport type MergedDictionaryOutput = Record<string, MergedDictionaryResult>;\n\n/**\n * Merged output restricted to plain (unqualified) dictionaries — qualified\n * groups (collections, variants, meta records) are static-only and filtered\n * out before the dynamic/fetch build steps.\n */\nexport type PlainMergedDictionaryResult = {\n dictionaryPath: string;\n dictionary: Dictionary;\n};\n\nexport type PlainMergedDictionaryOutput = Record<\n string,\n PlainMergedDictionaryResult\n>;\n\n/**\n * Write the merged dictionaries to the dictionariesDir\n * @param groupedDictionaries - The grouped dictionaries\n * @param configuration - The configuration\n * @returns The merged dictionaries\n *\n * @example\n * ```ts\n * const unmergedDictionaries = await writeUnmergedDictionaries(dictionaries);\n * const finalDictionaries = await writeFinalDictionaries(unmergedDictionaries);\n * console.log(finalDictionaries);\n *\n * // .intlayer/dictionary/home.json\n * // { key: 'home', content: { ... } },\n * ```\n */\nexport const writeMergedDictionaries = async (\n groupedDictionaries: UnmergedDictionaryOutput,\n configuration: IntlayerConfig\n): Promise<MergedDictionaryOutput> => {\n const { dictionariesDir } = configuration.system;\n\n // Create the dictionaries folder if it doesn't exist\n await mkdir(resolve(dictionariesDir), { recursive: true });\n\n const results = await parallelize(\n Object.entries(groupedDictionaries),\n async ([key, dictionariesEntry]) => {\n if (key === 'undefined') {\n return undefined as unknown as readonly [\n string,\n MergedDictionaryResult,\n ];\n }\n\n const normalizedDictionaries = normalizeDictionaries(\n dictionariesEntry.dictionaries,\n configuration\n );\n\n const mergedDictionary = mergeQualifiedDictionaries(\n normalizedDictionaries\n );\n\n const outputFileName = `${key}.json`;\n const resultFilePath = resolve(dictionariesDir, outputFileName);\n\n assertPathWithin(resultFilePath, dictionariesDir);\n\n // Write the merged dictionary\n await writeJsonIfChanged(resultFilePath, mergedDictionary).catch(\n (err) => {\n console.error(\n `Error creating merged ${colorizePath(resultFilePath)}:`,\n err\n );\n }\n );\n\n return [\n key,\n {\n dictionaryPath: resultFilePath,\n dictionary: mergedDictionary,\n } as MergedDictionaryResult,\n ] as const;\n }\n );\n\n return Object.fromEntries(\n results.filter(Boolean) as Array<readonly [string, MergedDictionaryResult]>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,MAAa,0BAA0B,OACrC,qBACA,kBACoC;CACpC,MAAM,EAAE,oBAAoB,cAAc;AAG1C,OAAM,MAAM,QAAQ,gBAAgB,EAAE,EAAE,WAAW,MAAM,CAAC;CAE1D,MAAM,UAAU,MAAM,YACpB,OAAO,QAAQ,oBAAoB,EACnC,OAAO,CAAC,KAAK,uBAAuB;AAClC,MAAI,QAAQ,YACV;EAWF,MAAM,mBAAmB,2BALM,sBAC7B,kBAAkB,cAClB,cAIsB,CACvB;EAGD,MAAM,iBAAiB,QAAQ,iBAAiB,GADtB,IAAI,OACiC;AAE/D,mBAAiB,gBAAgB,gBAAgB;AAGjD,QAAM,mBAAmB,gBAAgB,iBAAiB,CAAC,OACxD,QAAQ;AACP,WAAQ,MACN,yBAAyB,aAAa,eAAe,CAAC,IACtD,IACD;IAEJ;AAED,SAAO,CACL,KACA;GACE,gBAAgB;GAChB,YAAY;GACb,CACF;GAEJ;AAED,QAAO,OAAO,YACZ,QAAQ,OAAO,QAAQ,CACxB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createType.mjs","names":[],"sources":["../../../src/createType/createType.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {
|
|
1
|
+
{"version":3,"file":"createType.mjs","names":[],"sources":["../../../src/createType/createType.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport type {\n Dictionary,\n QualifiedDictionaryGroup,\n} from '@intlayer/types/dictionary';\nimport { parallelize } from '../utils/parallelize';\n\nexport const generateTypeScriptType = (\n dictionary: Dictionary | QualifiedDictionaryGroup\n) => {\n const jsonString = JSON.stringify(dictionary, null, 2);\n\n return `/* eslint-disable */\\nexport default ${jsonString} as const;\\n`;\n};\n/**\n * This function generates a TypeScript type definition from a JSON object\n */\nexport const createTypes = async (\n dictionaries: (Dictionary | QualifiedDictionaryGroup)[],\n configuration: IntlayerConfig\n): Promise<string[]> => {\n const { system } = configuration;\n const { typesDir } = system;\n\n // Create type folders if they don't exist\n await mkdir(typesDir, { recursive: true });\n\n const results = await parallelize(\n dictionaries,\n async (dictionary): Promise<string | undefined> => {\n if (!dictionary.key) {\n return undefined;\n }\n\n const typeDefinition: string = generateTypeScriptType(dictionary);\n\n const outputPath: string = resolve(typesDir, `${dictionary.key}.ts`);\n\n await writeFile(outputPath, typeDefinition);\n\n return outputPath;\n }\n );\n\n return results.filter(Boolean) as string[];\n};\n"],"mappings":";;;;;AASA,MAAa,0BACX,eACG;AAGH,QAAO,wCAFY,KAAK,UAAU,YAAY,MAAM,EAEK,CAAC;;;;;AAK5D,MAAa,cAAc,OACzB,cACA,kBACsB;CACtB,MAAM,EAAE,WAAW;CACnB,MAAM,EAAE,aAAa;AAGrB,OAAM,MAAM,UAAU,EAAE,WAAW,MAAM,CAAC;AAmB1C,SAAO,MAjBe,YACpB,cACA,OAAO,eAA4C;AACjD,MAAI,CAAC,WAAW,IACd;EAGF,MAAM,iBAAyB,uBAAuB,WAAW;EAEjE,MAAM,aAAqB,QAAQ,UAAU,GAAG,WAAW,IAAI,KAAK;AAEpE,QAAM,UAAU,YAAY,eAAe;AAE3C,SAAO;GAEV,EAEc,OAAO,QAAQ"}
|