@intlayer/chokidar 7.2.3 → 7.3.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/README.md +2 -0
- package/dist/cjs/index.cjs +12 -12
- package/dist/cjs/listDictionariesPath.cjs +9 -0
- package/dist/cjs/listDictionariesPath.cjs.map +1 -1
- package/dist/cjs/prepareIntlayer.cjs +9 -2
- package/dist/cjs/prepareIntlayer.cjs.map +1 -1
- package/dist/cjs/transformFiles/extractDictionaryKey.cjs +16 -0
- package/dist/cjs/transformFiles/extractDictionaryKey.cjs.map +1 -0
- package/dist/cjs/transformFiles/index.cjs +6 -0
- package/dist/cjs/transformFiles/transformFiles.cjs +218 -0
- package/dist/cjs/transformFiles/transformFiles.cjs.map +1 -0
- package/dist/cjs/writeContentDeclaration/detectFormatCommand.cjs +16 -14
- package/dist/cjs/writeContentDeclaration/detectFormatCommand.cjs.map +1 -1
- package/dist/cjs/writeContentDeclaration/writeJSFile.cjs +1 -1
- package/dist/esm/index.mjs +10 -9
- package/dist/esm/listDictionariesPath.mjs +9 -1
- package/dist/esm/listDictionariesPath.mjs.map +1 -1
- package/dist/esm/prepareIntlayer.mjs +10 -3
- package/dist/esm/prepareIntlayer.mjs.map +1 -1
- package/dist/esm/transformFiles/extractDictionaryKey.mjs +15 -0
- package/dist/esm/transformFiles/extractDictionaryKey.mjs.map +1 -0
- package/dist/esm/transformFiles/index.mjs +4 -0
- package/dist/esm/transformFiles/transformFiles.mjs +215 -0
- package/dist/esm/transformFiles/transformFiles.mjs.map +1 -0
- package/dist/esm/writeContentDeclaration/detectFormatCommand.mjs +16 -14
- package/dist/esm/writeContentDeclaration/detectFormatCommand.mjs.map +1 -1
- package/dist/esm/writeContentDeclaration/writeJSFile.mjs +1 -1
- package/dist/types/buildIntlayerDictionary/writeDynamicDictionary.d.ts +3 -3
- package/dist/types/buildIntlayerDictionary/writeFetchDictionary.d.ts +3 -3
- package/dist/types/buildIntlayerDictionary/writeMergedDictionary.d.ts +2 -2
- package/dist/types/buildIntlayerDictionary/writeRemoteDictionary.d.ts +2 -2
- package/dist/types/index.d.ts +5 -3
- package/dist/types/listDictionariesPath.d.ts +6 -1
- package/dist/types/listDictionariesPath.d.ts.map +1 -1
- package/dist/types/prepareIntlayer.d.ts.map +1 -1
- package/dist/types/transformFiles/extractDictionaryKey.d.ts +5 -0
- package/dist/types/transformFiles/extractDictionaryKey.d.ts.map +1 -0
- package/dist/types/transformFiles/index.d.ts +3 -0
- package/dist/types/transformFiles/transformFiles.d.ts +16 -0
- package/dist/types/transformFiles/transformFiles.d.ts.map +1 -0
- package/package.json +19 -10
- package/dist/cjs/compiler/extractDictionaryKey.cjs +0 -38
- package/dist/cjs/compiler/extractDictionaryKey.cjs.map +0 -1
- package/dist/cjs/compiler/index.cjs +0 -225
- package/dist/cjs/compiler/index.cjs.map +0 -1
- package/dist/esm/compiler/extractDictionaryKey.mjs +0 -36
- package/dist/esm/compiler/extractDictionaryKey.mjs.map +0 -1
- package/dist/esm/compiler/index.mjs +0 -219
- package/dist/esm/compiler/index.mjs.map +0 -1
- package/dist/types/compiler/extractDictionaryKey.d.ts +0 -13
- package/dist/types/compiler/extractDictionaryKey.d.ts.map +0 -1
- package/dist/types/compiler/index.d.ts +0 -36
- package/dist/types/compiler/index.d.ts.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intlayer/chokidar",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.3.0-canary.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Uses chokidar to scan and build Intlayer declaration files into dictionaries based on Intlayer configuration.",
|
|
6
6
|
"keywords": [
|
|
@@ -74,13 +74,13 @@
|
|
|
74
74
|
"typecheck": "tsc --noEmit --project tsconfig.types.json"
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
|
-
"@intlayer/api": "7.
|
|
78
|
-
"@intlayer/config": "7.
|
|
79
|
-
"@intlayer/core": "7.
|
|
80
|
-
"@intlayer/dictionaries-entry": "7.
|
|
81
|
-
"@intlayer/remote-dictionaries-entry": "7.
|
|
82
|
-
"@intlayer/types": "7.
|
|
83
|
-
"@intlayer/unmerged-dictionaries-entry": "7.
|
|
77
|
+
"@intlayer/api": "7.3.0-canary.0",
|
|
78
|
+
"@intlayer/config": "7.3.0-canary.0",
|
|
79
|
+
"@intlayer/core": "7.3.0-canary.0",
|
|
80
|
+
"@intlayer/dictionaries-entry": "7.3.0-canary.0",
|
|
81
|
+
"@intlayer/remote-dictionaries-entry": "7.3.0-canary.0",
|
|
82
|
+
"@intlayer/types": "7.3.0-canary.0",
|
|
83
|
+
"@intlayer/unmerged-dictionaries-entry": "7.3.0-canary.0",
|
|
84
84
|
"chokidar": "3.6.0",
|
|
85
85
|
"crypto-js": "4.2.0",
|
|
86
86
|
"deepmerge": "4.3.1",
|
|
@@ -97,10 +97,19 @@
|
|
|
97
97
|
"rimraf": "6.1.2",
|
|
98
98
|
"tsdown": "0.16.6",
|
|
99
99
|
"typescript": "5.9.3",
|
|
100
|
-
"vitest": "4.0.
|
|
100
|
+
"vitest": "4.0.13"
|
|
101
101
|
},
|
|
102
102
|
"peerDependencies": {
|
|
103
|
-
"
|
|
103
|
+
"@intlayer/svelte-transformer": "7.3.0-canary.0",
|
|
104
|
+
"@intlayer/vue-transformer": "7.3.0-canary.0"
|
|
105
|
+
},
|
|
106
|
+
"peerDependenciesMeta": {
|
|
107
|
+
"@intlayer/svelte-transformer": {
|
|
108
|
+
"optional": true
|
|
109
|
+
},
|
|
110
|
+
"@intlayer/vue-transformer": {
|
|
111
|
+
"optional": true
|
|
112
|
+
}
|
|
104
113
|
},
|
|
105
114
|
"engines": {
|
|
106
115
|
"node": ">=14.18"
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
2
|
-
let node_path = require("node:path");
|
|
3
|
-
|
|
4
|
-
//#region src/compiler/extractDictionaryKey.ts
|
|
5
|
-
/**
|
|
6
|
-
* Attempt to detect an exported React component name in the file text.
|
|
7
|
-
* Looks for patterns like:
|
|
8
|
-
* - export const MyComponent = ...
|
|
9
|
-
* - export function MyComponent(...)
|
|
10
|
-
* - export default function MyComponent(...)
|
|
11
|
-
*/
|
|
12
|
-
const detectExportedComponentName = (fileText) => {
|
|
13
|
-
const defaultEsmFnRegex = /export\s+default\s+function\s+(\w+)/;
|
|
14
|
-
const defaultEsmVarRegex = /export\s+default\s+(\w+)/;
|
|
15
|
-
const cjsDefaultRegex = /module\.exports\s*=\s*(\w+)/;
|
|
16
|
-
const cjsDefaultVarRegex = /exports\.default\s*=\s*(\w+)/;
|
|
17
|
-
const namedExportRegex = /export\s+(?:const|function)\s+(\w+)/g;
|
|
18
|
-
const defaultEsmFnMatch = fileText.match(defaultEsmFnRegex);
|
|
19
|
-
if (defaultEsmFnMatch) return defaultEsmFnMatch[1];
|
|
20
|
-
const defaultEsmVarMatch = fileText.match(defaultEsmVarRegex);
|
|
21
|
-
if (defaultEsmVarMatch) return defaultEsmVarMatch[1];
|
|
22
|
-
const cjsDefaultMatch = fileText.match(cjsDefaultRegex) || fileText.match(cjsDefaultVarRegex);
|
|
23
|
-
if (cjsDefaultMatch) return cjsDefaultMatch[1];
|
|
24
|
-
for (const [, exportName] of fileText.matchAll(namedExportRegex)) if (/^[A-Z]/.test(exportName)) return exportName;
|
|
25
|
-
return null;
|
|
26
|
-
};
|
|
27
|
-
const extractDictionaryKey = (filePath, fileText) => {
|
|
28
|
-
const componentName = detectExportedComponentName(fileText);
|
|
29
|
-
if (componentName) return componentName;
|
|
30
|
-
let baseName = (0, node_path.basename)(filePath, (0, node_path.extname)(filePath));
|
|
31
|
-
if (baseName === "index") baseName = (0, node_path.basename)((0, node_path.dirname)(filePath));
|
|
32
|
-
return baseName;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
//#endregion
|
|
36
|
-
exports.detectExportedComponentName = detectExportedComponentName;
|
|
37
|
-
exports.extractDictionaryKey = extractDictionaryKey;
|
|
38
|
-
//# sourceMappingURL=extractDictionaryKey.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"extractDictionaryKey.cjs","names":[],"sources":["../../../src/compiler/extractDictionaryKey.ts"],"sourcesContent":["import { basename, dirname, extname } from 'node:path';\n\n/**\n * Attempt to detect an exported React component name in the file text.\n * Looks for patterns like:\n * - export const MyComponent = ...\n * - export function MyComponent(...)\n * - export default function MyComponent(...)\n */\nexport const detectExportedComponentName = (\n fileText: string\n): string | null => {\n // Added regexes for default ESM, default CJS, and named exports\n const defaultEsmFnRegex = /export\\s+default\\s+function\\s+(\\w+)/;\n const defaultEsmVarRegex = /export\\s+default\\s+(\\w+)/;\n const cjsDefaultRegex = /module\\.exports\\s*=\\s*(\\w+)/;\n const cjsDefaultVarRegex = /exports\\.default\\s*=\\s*(\\w+)/;\n const namedExportRegex = /export\\s+(?:const|function)\\s+(\\w+)/g;\n\n // 1) Check for default ESM function or variable\n const defaultEsmFnMatch = fileText.match(defaultEsmFnRegex);\n if (defaultEsmFnMatch) {\n return defaultEsmFnMatch[1];\n }\n\n const defaultEsmVarMatch = fileText.match(defaultEsmVarRegex);\n if (defaultEsmVarMatch) {\n return defaultEsmVarMatch[1];\n }\n\n // 2) Check for default CJS\n const cjsDefaultMatch =\n fileText.match(cjsDefaultRegex) || fileText.match(cjsDefaultVarRegex);\n if (cjsDefaultMatch) {\n return cjsDefaultMatch[1];\n }\n\n // 3) Otherwise, look for capitalized named exports\n for (const [, exportName] of fileText.matchAll(namedExportRegex)) {\n if (/^[A-Z]/.test(exportName)) {\n return exportName;\n }\n }\n\n // If we can’t find it, return null\n return null;\n};\n\nexport const extractDictionaryKey = (\n filePath: string,\n fileText: string\n): string => {\n const componentName = detectExportedComponentName(fileText);\n if (componentName) {\n return componentName;\n }\n\n const ext = extname(filePath);\n let baseName = basename(filePath, ext);\n\n if (baseName === 'index') {\n baseName = basename(dirname(filePath));\n }\n\n return baseName;\n};\n"],"mappings":";;;;;;;;;;;AASA,MAAa,+BACX,aACkB;CAElB,MAAM,oBAAoB;CAC1B,MAAM,qBAAqB;CAC3B,MAAM,kBAAkB;CACxB,MAAM,qBAAqB;CAC3B,MAAM,mBAAmB;CAGzB,MAAM,oBAAoB,SAAS,MAAM,kBAAkB;AAC3D,KAAI,kBACF,QAAO,kBAAkB;CAG3B,MAAM,qBAAqB,SAAS,MAAM,mBAAmB;AAC7D,KAAI,mBACF,QAAO,mBAAmB;CAI5B,MAAM,kBACJ,SAAS,MAAM,gBAAgB,IAAI,SAAS,MAAM,mBAAmB;AACvE,KAAI,gBACF,QAAO,gBAAgB;AAIzB,MAAK,MAAM,GAAG,eAAe,SAAS,SAAS,iBAAiB,CAC9D,KAAI,SAAS,KAAK,WAAW,CAC3B,QAAO;AAKX,QAAO;;AAGT,MAAa,wBACX,UACA,aACW;CACX,MAAM,gBAAgB,4BAA4B,SAAS;AAC3D,KAAI,cACF,QAAO;CAIT,IAAI,mCAAoB,iCADJ,SAAS,CACS;AAEtC,KAAI,aAAa,QACf,2DAA4B,SAAS,CAAC;AAGxC,QAAO"}
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
|
|
2
|
-
const require_writeContentDeclaration_detectFormatCommand = require('../writeContentDeclaration/detectFormatCommand.cjs');
|
|
3
|
-
const require_writeContentDeclaration_writeContentDeclaration = require('../writeContentDeclaration/writeContentDeclaration.cjs');
|
|
4
|
-
const require_compiler_extractDictionaryKey = require('./extractDictionaryKey.cjs');
|
|
5
|
-
let __intlayer_config = require("@intlayer/config");
|
|
6
|
-
let node_path = require("node:path");
|
|
7
|
-
let node_child_process = require("node:child_process");
|
|
8
|
-
let ts_morph = require("ts-morph");
|
|
9
|
-
|
|
10
|
-
//#region src/compiler/index.ts
|
|
11
|
-
const ATTRIBUTES_TO_EXTRACT = [
|
|
12
|
-
"title",
|
|
13
|
-
"placeholder",
|
|
14
|
-
"alt",
|
|
15
|
-
"aria-label",
|
|
16
|
-
"label"
|
|
17
|
-
];
|
|
18
|
-
const generateKey = (text, existingKeys) => {
|
|
19
|
-
let key = text.replace(/\s+/g, " ").replace(/[^a-zA-Z0-9 ]/g, "").trim().split(" ").filter(Boolean).slice(0, 5).map((word, index) => index === 0 ? word.toLowerCase() : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
20
|
-
if (!key) key = "content";
|
|
21
|
-
if (existingKeys.has(key)) {
|
|
22
|
-
let i = 1;
|
|
23
|
-
while (existingKeys.has(`${key}${i}`)) i++;
|
|
24
|
-
key = `${key}${i}`;
|
|
25
|
-
}
|
|
26
|
-
return key;
|
|
27
|
-
};
|
|
28
|
-
const shouldExtract = (text) => {
|
|
29
|
-
const trimmed = text.trim();
|
|
30
|
-
if (!trimmed) return false;
|
|
31
|
-
if (!trimmed.includes(" ")) return false;
|
|
32
|
-
if (!/^[A-Z]/.test(trimmed)) return false;
|
|
33
|
-
return true;
|
|
34
|
-
};
|
|
35
|
-
/**
|
|
36
|
-
* 1. Extraction of content
|
|
37
|
-
*/
|
|
38
|
-
const extractContent = (sourceFile) => {
|
|
39
|
-
const extractedContent = {};
|
|
40
|
-
const existingKeys = /* @__PURE__ */ new Set();
|
|
41
|
-
const replacements = [];
|
|
42
|
-
const processText = (text, node, type) => {
|
|
43
|
-
if (!shouldExtract(text)) return;
|
|
44
|
-
const key = generateKey(text.trim(), existingKeys);
|
|
45
|
-
existingKeys.add(key);
|
|
46
|
-
extractedContent[key] = type === "jsx-text" ? text.replace(/\s+/g, " ").trim() : text.trim();
|
|
47
|
-
replacements.push({
|
|
48
|
-
node,
|
|
49
|
-
key,
|
|
50
|
-
type
|
|
51
|
-
});
|
|
52
|
-
};
|
|
53
|
-
sourceFile.forEachDescendant((node) => {
|
|
54
|
-
if (ts_morph.Node.isJsxText(node)) processText(node.getText(), node, "jsx-text");
|
|
55
|
-
if (ts_morph.Node.isJsxAttribute(node)) {
|
|
56
|
-
const name = node.getNameNode().getText();
|
|
57
|
-
if (ATTRIBUTES_TO_EXTRACT.includes(name)) {
|
|
58
|
-
const initializer = node.getInitializer();
|
|
59
|
-
if (ts_morph.Node.isStringLiteral(initializer)) processText(initializer.getLiteralValue(), node, "jsx-attribute");
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
if (ts_morph.Node.isStringLiteral(node)) {
|
|
63
|
-
const parent = node.getParent();
|
|
64
|
-
if (ts_morph.Node.isCallExpression(parent)) {
|
|
65
|
-
const expression = parent.getExpression();
|
|
66
|
-
if (ts_morph.Node.isIdentifier(expression) && expression.getText() === "useState") processText(node.getLiteralValue(), node, "string-literal");
|
|
67
|
-
}
|
|
68
|
-
if (ts_morph.Node.isConditionalExpression(parent)) {
|
|
69
|
-
let current = parent.getParent();
|
|
70
|
-
let isInsideJsx = false;
|
|
71
|
-
while (current) {
|
|
72
|
-
if (ts_morph.Node.isJsxExpression(current)) {
|
|
73
|
-
isInsideJsx = true;
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
if (ts_morph.Node.isBlock(current) || ts_morph.Node.isSourceFile(current)) break;
|
|
77
|
-
current = current.getParent();
|
|
78
|
-
}
|
|
79
|
-
if (isInsideJsx) processText(node.getLiteralValue(), node, "string-literal");
|
|
80
|
-
}
|
|
81
|
-
if (ts_morph.Node.isArrayLiteralExpression(parent)) processText(node.getLiteralValue(), node, "string-literal");
|
|
82
|
-
if (ts_morph.Node.isPropertyAssignment(parent)) {
|
|
83
|
-
if (parent.getInitializer() === node) processText(node.getLiteralValue(), node, "string-literal");
|
|
84
|
-
}
|
|
85
|
-
if (ts_morph.Node.isReturnStatement(parent)) processText(node.getLiteralValue(), node, "string-literal");
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
return {
|
|
89
|
-
extractedContent,
|
|
90
|
-
replacements
|
|
91
|
-
};
|
|
92
|
-
};
|
|
93
|
-
/**
|
|
94
|
-
* 2. Write content declaration
|
|
95
|
-
*/
|
|
96
|
-
const writeContent = async (extractedContent, componentKey, filePath, configuration, outputDir) => {
|
|
97
|
-
const { defaultLocale } = configuration.internationalization;
|
|
98
|
-
const { baseDir } = configuration.content;
|
|
99
|
-
const dirName = outputDir ? (0, node_path.resolve)(outputDir) : (0, node_path.dirname)(filePath);
|
|
100
|
-
const contentFilePath = (0, node_path.join)(dirName, `${(0, node_path.basename)(filePath, (0, node_path.extname)(filePath))}.content.ts`);
|
|
101
|
-
await require_writeContentDeclaration_writeContentDeclaration.writeContentDeclaration({
|
|
102
|
-
key: componentKey,
|
|
103
|
-
content: extractedContent,
|
|
104
|
-
locale: defaultLocale,
|
|
105
|
-
filePath: (0, node_path.relative)(baseDir, contentFilePath)
|
|
106
|
-
}, configuration, { newDictionariesPath: (0, node_path.relative)(baseDir, dirName) });
|
|
107
|
-
return contentFilePath;
|
|
108
|
-
};
|
|
109
|
-
const isComponent = (node) => {
|
|
110
|
-
let name;
|
|
111
|
-
if (ts_morph.Node.isFunctionDeclaration(node)) name = node.getName();
|
|
112
|
-
else if (ts_morph.Node.isFunctionExpression(node) || ts_morph.Node.isArrowFunction(node)) {
|
|
113
|
-
const parent = node.getParent();
|
|
114
|
-
if (ts_morph.Node.isVariableDeclaration(parent)) name = parent.getName();
|
|
115
|
-
}
|
|
116
|
-
if (!name) return false;
|
|
117
|
-
return /^[A-Z]/.test(name);
|
|
118
|
-
};
|
|
119
|
-
/**
|
|
120
|
-
* 3. Transform component
|
|
121
|
-
*/
|
|
122
|
-
const transformComponent = async (sourceFile, replacements, componentKey, packageName) => {
|
|
123
|
-
if (replacements.length === 0) return;
|
|
124
|
-
const componentsToInjectHook = /* @__PURE__ */ new Map();
|
|
125
|
-
for (const { node, key, type } of replacements) {
|
|
126
|
-
let current = node.getParent();
|
|
127
|
-
while (current) {
|
|
128
|
-
if ((ts_morph.Node.isFunctionDeclaration(current) || ts_morph.Node.isArrowFunction(current) || ts_morph.Node.isFunctionExpression(current)) && (() => {
|
|
129
|
-
const body = current.getBody?.();
|
|
130
|
-
return body && ts_morph.Node.isBlock(body);
|
|
131
|
-
})()) {
|
|
132
|
-
const injectionType = isComponent(current) ? "hook" : "function";
|
|
133
|
-
const existing = componentsToInjectHook.get(current);
|
|
134
|
-
if (!existing) componentsToInjectHook.set(current, injectionType);
|
|
135
|
-
else if (existing === "function" && injectionType === "hook") componentsToInjectHook.set(current, "hook");
|
|
136
|
-
break;
|
|
137
|
-
}
|
|
138
|
-
current = current.getParent();
|
|
139
|
-
}
|
|
140
|
-
const replaceText = (isJsx) => {
|
|
141
|
-
return !isJsx && (packageName === "react-intlayer" || packageName === "next-intlayer") ? `content.${key}.value` : `content.${key}`;
|
|
142
|
-
};
|
|
143
|
-
if (type === "jsx-text" && ts_morph.Node.isJsxText(node)) node.replaceWithText(`{${replaceText(true)}}`);
|
|
144
|
-
else if (type === "jsx-attribute" && ts_morph.Node.isJsxAttribute(node)) node.setInitializer(`{${replaceText(false)}}`);
|
|
145
|
-
else if (type === "string-literal" && ts_morph.Node.isStringLiteral(node)) node.replaceWithText(replaceText(false));
|
|
146
|
-
}
|
|
147
|
-
let injectedUseIntlayer = false;
|
|
148
|
-
let injectedGetIntlayer = false;
|
|
149
|
-
for (const [componentBody, injectionType] of componentsToInjectHook) {
|
|
150
|
-
const body = componentBody.getBody();
|
|
151
|
-
if (ts_morph.Node.isBlock(body)) {
|
|
152
|
-
const text = body.getText();
|
|
153
|
-
const statement = injectionType === "hook" ? `const content = useIntlayer("${componentKey}");` : `const content = getIntlayer("${componentKey}");`;
|
|
154
|
-
if (!text.includes(`useIntlayer("${componentKey}")`) && !text.includes(`getIntlayer("${componentKey}")`)) body.insertStatements(0, statement);
|
|
155
|
-
if (injectionType === "hook") injectedUseIntlayer = true;
|
|
156
|
-
if (injectionType === "function") injectedGetIntlayer = true;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
const importSource = packageName === "next-intlayer" ? sourceFile.getText().includes("\"use client\"") || sourceFile.getText().includes("'use client'") ? "next-intlayer" : "next-intlayer/server" : packageName;
|
|
160
|
-
const existingImport = sourceFile.getImportDeclaration((d) => d.getModuleSpecifierValue() === importSource);
|
|
161
|
-
const namedImports = /* @__PURE__ */ new Set();
|
|
162
|
-
if (injectedUseIntlayer) namedImports.add("useIntlayer");
|
|
163
|
-
if (injectedGetIntlayer) namedImports.add("getIntlayer");
|
|
164
|
-
if (namedImports.size > 0) if (!existingImport) sourceFile.addImportDeclaration({
|
|
165
|
-
namedImports: Array.from(namedImports),
|
|
166
|
-
moduleSpecifier: importSource
|
|
167
|
-
});
|
|
168
|
-
else {
|
|
169
|
-
const currentNamedImports = existingImport.getNamedImports().map((n) => n.getName());
|
|
170
|
-
for (const name of namedImports) if (!currentNamedImports.includes(name)) existingImport.addNamedImport(name);
|
|
171
|
-
}
|
|
172
|
-
await sourceFile.save();
|
|
173
|
-
};
|
|
174
|
-
const extractIntlayer = async (filePath, packageName, options, project) => {
|
|
175
|
-
const configuration = (0, __intlayer_config.getConfiguration)(options?.configOptions);
|
|
176
|
-
const { baseDir } = configuration.content;
|
|
177
|
-
const appLogger = (0, __intlayer_config.getAppLogger)(configuration);
|
|
178
|
-
const _project = project || new ts_morph.Project({ skipAddingFilesFromTsConfig: true });
|
|
179
|
-
let sourceFile;
|
|
180
|
-
try {
|
|
181
|
-
sourceFile = _project.addSourceFileAtPath(filePath);
|
|
182
|
-
} catch {
|
|
183
|
-
sourceFile = _project.getSourceFileOrThrow(filePath);
|
|
184
|
-
}
|
|
185
|
-
const baseName = require_compiler_extractDictionaryKey.extractDictionaryKey(filePath, sourceFile.getText());
|
|
186
|
-
const componentKey = (0, __intlayer_config.camelCaseToKebabCase)(baseName);
|
|
187
|
-
const { extractedContent, replacements } = extractContent(sourceFile);
|
|
188
|
-
if (Object.keys(extractedContent).length === 0) {
|
|
189
|
-
appLogger(`No extractable text found in ${baseName}`);
|
|
190
|
-
if (!project) {}
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
const contentFilePath = await writeContent(extractedContent, componentKey, filePath, configuration, options?.outputDir);
|
|
194
|
-
appLogger(`Created content file: ${(0, __intlayer_config.colorizePath)((0, node_path.relative)(configuration.content.baseDir, contentFilePath))}`);
|
|
195
|
-
await transformComponent(sourceFile, replacements, componentKey, packageName);
|
|
196
|
-
const formatCommand = require_writeContentDeclaration_detectFormatCommand.detectFormatCommand(configuration);
|
|
197
|
-
if (formatCommand) try {
|
|
198
|
-
(0, node_child_process.execSync)(formatCommand.replace("{{file}}", filePath), {
|
|
199
|
-
stdio: "inherit",
|
|
200
|
-
cwd: baseDir
|
|
201
|
-
});
|
|
202
|
-
} catch (error) {
|
|
203
|
-
console.error(error);
|
|
204
|
-
}
|
|
205
|
-
appLogger(`Updated component: ${(0, __intlayer_config.colorizePath)((0, node_path.relative)(baseDir, filePath))}`);
|
|
206
|
-
if (project) sourceFile.forget();
|
|
207
|
-
};
|
|
208
|
-
const transformFiles = async (filePaths, packageName, options) => {
|
|
209
|
-
const appLogger = (0, __intlayer_config.getAppLogger)((0, __intlayer_config.getConfiguration)(options?.configOptions));
|
|
210
|
-
const project = new ts_morph.Project({ skipAddingFilesFromTsConfig: true });
|
|
211
|
-
for (const filePath of filePaths) try {
|
|
212
|
-
await extractIntlayer(filePath, packageName, options, project);
|
|
213
|
-
} catch (error) {
|
|
214
|
-
appLogger(`Failed to transform ${filePath}: ${error.message}`);
|
|
215
|
-
}
|
|
216
|
-
};
|
|
217
|
-
|
|
218
|
-
//#endregion
|
|
219
|
-
exports.extractContent = extractContent;
|
|
220
|
-
exports.extractIntlayer = extractIntlayer;
|
|
221
|
-
exports.shouldExtract = shouldExtract;
|
|
222
|
-
exports.transformComponent = transformComponent;
|
|
223
|
-
exports.transformFiles = transformFiles;
|
|
224
|
-
exports.writeContent = writeContent;
|
|
225
|
-
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["extractedContent: Record<string, string>","replacements: Replacement[]","Node","current: Node | undefined","writeContentDeclaration","name: string | undefined","Project","sourceFile: SourceFile","extractDictionaryKey","detectFormatCommand"],"sources":["../../../src/compiler/index.ts"],"sourcesContent":["import { execSync } from 'node:child_process';\nimport { basename, dirname, extname, join, relative, resolve } from 'node:path';\nimport {\n camelCaseToKebabCase,\n colorizePath,\n type GetConfigurationOptions,\n getAppLogger,\n getConfiguration,\n} from '@intlayer/config';\nimport type { Dictionary, IntlayerConfig } from '@intlayer/types';\nimport { Node, Project, type SourceFile } from 'ts-morph';\nimport { writeContentDeclaration } from '../writeContentDeclaration';\nimport { detectFormatCommand } from '../writeContentDeclaration/detectFormatCommand';\nimport { extractDictionaryKey } from './extractDictionaryKey';\n\nconst ATTRIBUTES_TO_EXTRACT = [\n 'title',\n 'placeholder',\n 'alt',\n 'aria-label',\n 'label',\n];\n\nconst generateKey = (text: string, existingKeys: Set<string>): string => {\n const maxWords = 5;\n let key = text\n .replace(/\\s+/g, ' ') // Normalize whitespace to single spaces\n .replace(/[^a-zA-Z0-9 ]/g, '') // Remove special chars\n .trim()\n .split(' ')\n .filter(Boolean)\n .slice(0, maxWords)\n .map((word, index) =>\n index === 0\n ? word.toLowerCase()\n : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()\n )\n .join('');\n\n // Fallback for empty keys (e.g. only symbols)\n if (!key) key = 'content';\n\n // Handle duplicates\n if (existingKeys.has(key)) {\n let i = 1;\n while (existingKeys.has(`${key}${i}`)) i++;\n key = `${key}${i}`;\n }\n\n return key;\n};\n\ntype Replacement = {\n node: Node;\n key: string;\n type: 'jsx-text' | 'jsx-attribute' | 'string-literal';\n};\n\nexport const shouldExtract = (text: string): boolean => {\n const trimmed = text.trim();\n // 1. Must have some text\n if (!trimmed) return false;\n // 2. Must contain spaces (not a single camelCase/kebab-case word)\n if (!trimmed.includes(' ')) return false;\n // 3. Must start with a capital letter\n // We check the first non-whitespace character\n if (!/^[A-Z]/.test(trimmed)) return false;\n\n return true;\n};\n\n/**\n * 1. Extraction of content\n */\nexport const extractContent = (sourceFile: SourceFile) => {\n const extractedContent: Record<string, string> = {};\n const existingKeys = new Set<string>();\n const replacements: Replacement[] = [];\n\n const processText = (text: string, node: Node, type: Replacement['type']) => {\n if (!shouldExtract(text)) return;\n\n const key = generateKey(text.trim(), existingKeys);\n existingKeys.add(key);\n extractedContent[key] =\n type === 'jsx-text' ? text.replace(/\\s+/g, ' ').trim() : text.trim();\n replacements.push({ node, key, type });\n };\n\n sourceFile.forEachDescendant((node) => {\n // Case A: JSX Text (e.g., <h2>Title</h2>)\n if (Node.isJsxText(node)) {\n processText(node.getText(), node, 'jsx-text');\n }\n\n // Case B: JSX Attributes (e.g., placeholder=\"Search\")\n if (Node.isJsxAttribute(node)) {\n const name = node.getNameNode().getText();\n if (ATTRIBUTES_TO_EXTRACT.includes(name)) {\n const initializer = node.getInitializer();\n if (Node.isStringLiteral(initializer)) {\n processText(initializer.getLiteralValue(), node, 'jsx-attribute');\n }\n }\n }\n\n // Case C: String Literals in specific contexts\n if (Node.isStringLiteral(node)) {\n const parent = node.getParent();\n\n // 1. State initialization: useState('...')\n if (Node.isCallExpression(parent)) {\n const expression = parent.getExpression();\n if (\n Node.isIdentifier(expression) &&\n expression.getText() === 'useState'\n ) {\n processText(node.getLiteralValue(), node, 'string-literal');\n }\n }\n\n // 2. Ternary operators in JSX: { isTrue ? 'Text' : 'Other' }\n if (Node.isConditionalExpression(parent)) {\n // Check if this conditional is inside a JSX expression\n let current: Node | undefined = parent.getParent();\n let isInsideJsx = false;\n while (current) {\n if (Node.isJsxExpression(current)) {\n isInsideJsx = true;\n break;\n }\n if (Node.isBlock(current) || Node.isSourceFile(current)) break;\n current = current.getParent();\n }\n\n if (isInsideJsx) {\n processText(node.getLiteralValue(), node, 'string-literal');\n }\n }\n\n // 3. Array elements (simple heuristic: inside array literal)\n if (Node.isArrayLiteralExpression(parent)) {\n processText(node.getLiteralValue(), node, 'string-literal');\n }\n\n // 4. Object property values (e.g. { value: \"Text\" })\n if (Node.isPropertyAssignment(parent)) {\n const initializer = parent.getInitializer();\n if (initializer === node) {\n processText(node.getLiteralValue(), node, 'string-literal');\n }\n }\n\n // 5. Return statements\n if (Node.isReturnStatement(parent)) {\n processText(node.getLiteralValue(), node, 'string-literal');\n }\n }\n });\n\n return { extractedContent, replacements };\n};\n\n/**\n * 2. Write content declaration\n */\nexport const writeContent = async (\n extractedContent: Record<string, string>,\n componentKey: string,\n filePath: string,\n configuration: IntlayerConfig,\n outputDir?: string\n) => {\n const { defaultLocale } = configuration.internationalization;\n const { baseDir } = configuration.content;\n\n const dirName = outputDir ? resolve(outputDir) : dirname(filePath);\n const ext = extname(filePath);\n const baseName = basename(filePath, ext);\n\n const contentFilePath = join(dirName, `${baseName}.content.ts`);\n const relativeContentFilePath = relative(baseDir, contentFilePath);\n\n const dictionary: Dictionary = {\n key: componentKey,\n content: extractedContent,\n locale: defaultLocale,\n filePath: relativeContentFilePath,\n };\n\n const relativeDir = relative(baseDir, dirName);\n\n await writeContentDeclaration(dictionary, configuration, {\n newDictionariesPath: relativeDir,\n });\n\n return contentFilePath;\n};\n\nconst isComponent = (node: Node): boolean => {\n let name: string | undefined;\n\n if (Node.isFunctionDeclaration(node)) {\n name = node.getName();\n } else if (Node.isFunctionExpression(node) || Node.isArrowFunction(node)) {\n const parent = node.getParent();\n if (Node.isVariableDeclaration(parent)) {\n name = parent.getName();\n }\n }\n\n if (!name) return false;\n // Check for PascalCase\n return /^[A-Z]/.test(name);\n};\n\n/**\n * 3. Transform component\n */\nexport const transformComponent = async (\n sourceFile: SourceFile,\n replacements: Replacement[],\n componentKey: string,\n packageName: string\n) => {\n if (replacements.length === 0) return;\n\n const componentsToInjectHook = new Map<Node, 'hook' | 'function'>();\n\n for (const { node, key, type } of replacements) {\n // Find the parent function/component\n let current = node.getParent();\n while (current) {\n if (\n (Node.isFunctionDeclaration(current) ||\n Node.isArrowFunction(current) ||\n Node.isFunctionExpression(current)) &&\n // Ensure body exists and is a block for injection\n // Using explicit casting/checking to avoid \"undefined\" type error\n (() => {\n const body = (current as any).getBody?.();\n return body && Node.isBlock(body);\n })()\n ) {\n // Determine if it's a component or helper\n const isComp = isComponent(current);\n const injectionType = isComp ? 'hook' : 'function';\n\n // Check if we already decided on a type for this component\n const existing = componentsToInjectHook.get(current);\n if (!existing) {\n componentsToInjectHook.set(current, injectionType);\n } else if (existing === 'function' && injectionType === 'hook') {\n // Upgrade to hook if detected as component by another usage?\n // Actually, isComponent checks the definition, so it shouldn't change.\n // But strictness matters.\n componentsToInjectHook.set(current, 'hook');\n }\n\n break;\n }\n current = current.getParent();\n }\n\n const replaceText = (isJsx: boolean) => {\n const needsValue =\n !isJsx &&\n (packageName === 'react-intlayer' || packageName === 'next-intlayer');\n return needsValue ? `content.${key}.value` : `content.${key}`;\n };\n\n if (type === 'jsx-text' && Node.isJsxText(node)) {\n node.replaceWithText(`{${replaceText(true)}}`);\n } else if (type === 'jsx-attribute' && Node.isJsxAttribute(node)) {\n node.setInitializer(`{${replaceText(false)}}`);\n } else if (type === 'string-literal' && Node.isStringLiteral(node)) {\n node.replaceWithText(replaceText(false));\n }\n }\n\n let injectedUseIntlayer = false;\n let injectedGetIntlayer = false;\n\n // Inject hook/function in all affected components\n for (const [componentBody, injectionType] of componentsToInjectHook) {\n const body = (componentBody as any).getBody();\n if (Node.isBlock(body)) {\n const text = body.getText();\n const statement =\n injectionType === 'hook'\n ? `const content = useIntlayer(\"${componentKey}\");`\n : `const content = getIntlayer(\"${componentKey}\");`;\n\n // Check if already injected (naive check)\n if (\n !text.includes(`useIntlayer(\"${componentKey}\")`) &&\n !text.includes(`getIntlayer(\"${componentKey}\")`)\n ) {\n body.insertStatements(0, statement);\n }\n\n if (injectionType === 'hook') injectedUseIntlayer = true;\n if (injectionType === 'function') injectedGetIntlayer = true;\n }\n }\n\n // Insert Imports\n const importSource =\n packageName === 'next-intlayer'\n ? sourceFile.getText().includes('\"use client\"') ||\n sourceFile.getText().includes(\"'use client'\")\n ? 'next-intlayer'\n : 'next-intlayer/server'\n : packageName;\n\n const existingImport = sourceFile.getImportDeclaration(\n (d) => d.getModuleSpecifierValue() === importSource\n );\n\n const namedImports = new Set<string>();\n if (injectedUseIntlayer) namedImports.add('useIntlayer');\n if (injectedGetIntlayer) namedImports.add('getIntlayer');\n\n if (namedImports.size > 0) {\n if (!existingImport) {\n sourceFile.addImportDeclaration({\n namedImports: Array.from(namedImports),\n moduleSpecifier: importSource,\n });\n } else {\n const currentNamedImports = existingImport\n .getNamedImports()\n .map((n) => n.getName());\n for (const name of namedImports) {\n if (!currentNamedImports.includes(name)) {\n existingImport.addNamedImport(name);\n }\n }\n }\n }\n\n await sourceFile.save();\n};\n\ntype ExtractIntlayer = {\n configOptions?: GetConfigurationOptions;\n outputDir?: string;\n};\n\nexport type PackageName =\n | 'next-intlayer'\n | 'react-intlayer'\n | 'vue-intlayer'\n | 'preact-intlayer'\n | 'solid-intlayer'\n | 'angular-intlayer'\n | 'svelte-intlayer'\n | 'express-intlayer';\n\nexport const extractIntlayer = async (\n filePath: string,\n packageName: PackageName,\n options?: ExtractIntlayer,\n project?: Project\n) => {\n const configuration = getConfiguration(options?.configOptions);\n const { baseDir } = configuration.content;\n const appLogger = getAppLogger(configuration);\n\n // Use provided project or create new one (optimized)\n const _project =\n project ||\n new Project({\n skipAddingFilesFromTsConfig: true,\n });\n\n let sourceFile: SourceFile;\n try {\n sourceFile = _project.addSourceFileAtPath(filePath);\n } catch {\n // If file is already added, get it\n sourceFile = _project.getSourceFileOrThrow(filePath);\n }\n\n const baseName = extractDictionaryKey(filePath, sourceFile.getText());\n const componentKey = camelCaseToKebabCase(baseName);\n\n // 1. Extract content\n const { extractedContent, replacements } = extractContent(sourceFile);\n\n if (Object.keys(extractedContent).length === 0) {\n appLogger(`No extractable text found in ${baseName}`);\n // Cleanup if we created the project just for this file\n if (!project) {\n // _project.removeSourceFile(sourceFile); // Not strictly necessary if process exits, but good practice\n }\n return;\n }\n\n // 2. Write content declaration\n const contentFilePath = await writeContent(\n extractedContent,\n componentKey,\n filePath,\n configuration,\n options?.outputDir\n );\n const relativeContentFilePath = relative(\n configuration.content.baseDir,\n contentFilePath\n );\n\n appLogger(`Created content file: ${colorizePath(relativeContentFilePath)}`);\n\n // 3. Transform component\n await transformComponent(sourceFile, replacements, componentKey, packageName);\n\n const formatCommand = detectFormatCommand(configuration);\n\n if (formatCommand) {\n try {\n execSync(formatCommand.replace('{{file}}', filePath), {\n stdio: 'inherit',\n cwd: baseDir,\n });\n } catch (error) {\n console.error(error);\n }\n }\n\n const relativeFilePath = relative(baseDir, filePath);\n\n appLogger(`Updated component: ${colorizePath(relativeFilePath)}`);\n\n // Cleanup to free memory if we are processing many files and passed a persistent project?\n // If 'project' is passed, we probably want to keep it?\n // Actually, for a CLI command running once, memory growth is acceptable if not too huge.\n // But removing source file is safer if not needed anymore.\n if (project) {\n sourceFile.forget(); // Remove from project to save memory\n }\n};\n\nexport const transformFiles = async (\n filePaths: string[],\n packageName: PackageName,\n options?: ExtractIntlayer\n) => {\n const configuration = getConfiguration(options?.configOptions);\n const appLogger = getAppLogger(configuration);\n\n const project = new Project({\n skipAddingFilesFromTsConfig: true,\n });\n\n for (const filePath of filePaths) {\n try {\n await extractIntlayer(filePath, packageName, options, project);\n } catch (error) {\n appLogger(`Failed to transform ${filePath}: ${(error as Error).message}`);\n }\n }\n};\n"],"mappings":"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"}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { basename, dirname, extname } from "node:path";
|
|
2
|
-
|
|
3
|
-
//#region src/compiler/extractDictionaryKey.ts
|
|
4
|
-
/**
|
|
5
|
-
* Attempt to detect an exported React component name in the file text.
|
|
6
|
-
* Looks for patterns like:
|
|
7
|
-
* - export const MyComponent = ...
|
|
8
|
-
* - export function MyComponent(...)
|
|
9
|
-
* - export default function MyComponent(...)
|
|
10
|
-
*/
|
|
11
|
-
const detectExportedComponentName = (fileText) => {
|
|
12
|
-
const defaultEsmFnRegex = /export\s+default\s+function\s+(\w+)/;
|
|
13
|
-
const defaultEsmVarRegex = /export\s+default\s+(\w+)/;
|
|
14
|
-
const cjsDefaultRegex = /module\.exports\s*=\s*(\w+)/;
|
|
15
|
-
const cjsDefaultVarRegex = /exports\.default\s*=\s*(\w+)/;
|
|
16
|
-
const namedExportRegex = /export\s+(?:const|function)\s+(\w+)/g;
|
|
17
|
-
const defaultEsmFnMatch = fileText.match(defaultEsmFnRegex);
|
|
18
|
-
if (defaultEsmFnMatch) return defaultEsmFnMatch[1];
|
|
19
|
-
const defaultEsmVarMatch = fileText.match(defaultEsmVarRegex);
|
|
20
|
-
if (defaultEsmVarMatch) return defaultEsmVarMatch[1];
|
|
21
|
-
const cjsDefaultMatch = fileText.match(cjsDefaultRegex) || fileText.match(cjsDefaultVarRegex);
|
|
22
|
-
if (cjsDefaultMatch) return cjsDefaultMatch[1];
|
|
23
|
-
for (const [, exportName] of fileText.matchAll(namedExportRegex)) if (/^[A-Z]/.test(exportName)) return exportName;
|
|
24
|
-
return null;
|
|
25
|
-
};
|
|
26
|
-
const extractDictionaryKey = (filePath, fileText) => {
|
|
27
|
-
const componentName = detectExportedComponentName(fileText);
|
|
28
|
-
if (componentName) return componentName;
|
|
29
|
-
let baseName = basename(filePath, extname(filePath));
|
|
30
|
-
if (baseName === "index") baseName = basename(dirname(filePath));
|
|
31
|
-
return baseName;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
//#endregion
|
|
35
|
-
export { detectExportedComponentName, extractDictionaryKey };
|
|
36
|
-
//# sourceMappingURL=extractDictionaryKey.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"extractDictionaryKey.mjs","names":[],"sources":["../../../src/compiler/extractDictionaryKey.ts"],"sourcesContent":["import { basename, dirname, extname } from 'node:path';\n\n/**\n * Attempt to detect an exported React component name in the file text.\n * Looks for patterns like:\n * - export const MyComponent = ...\n * - export function MyComponent(...)\n * - export default function MyComponent(...)\n */\nexport const detectExportedComponentName = (\n fileText: string\n): string | null => {\n // Added regexes for default ESM, default CJS, and named exports\n const defaultEsmFnRegex = /export\\s+default\\s+function\\s+(\\w+)/;\n const defaultEsmVarRegex = /export\\s+default\\s+(\\w+)/;\n const cjsDefaultRegex = /module\\.exports\\s*=\\s*(\\w+)/;\n const cjsDefaultVarRegex = /exports\\.default\\s*=\\s*(\\w+)/;\n const namedExportRegex = /export\\s+(?:const|function)\\s+(\\w+)/g;\n\n // 1) Check for default ESM function or variable\n const defaultEsmFnMatch = fileText.match(defaultEsmFnRegex);\n if (defaultEsmFnMatch) {\n return defaultEsmFnMatch[1];\n }\n\n const defaultEsmVarMatch = fileText.match(defaultEsmVarRegex);\n if (defaultEsmVarMatch) {\n return defaultEsmVarMatch[1];\n }\n\n // 2) Check for default CJS\n const cjsDefaultMatch =\n fileText.match(cjsDefaultRegex) || fileText.match(cjsDefaultVarRegex);\n if (cjsDefaultMatch) {\n return cjsDefaultMatch[1];\n }\n\n // 3) Otherwise, look for capitalized named exports\n for (const [, exportName] of fileText.matchAll(namedExportRegex)) {\n if (/^[A-Z]/.test(exportName)) {\n return exportName;\n }\n }\n\n // If we can’t find it, return null\n return null;\n};\n\nexport const extractDictionaryKey = (\n filePath: string,\n fileText: string\n): string => {\n const componentName = detectExportedComponentName(fileText);\n if (componentName) {\n return componentName;\n }\n\n const ext = extname(filePath);\n let baseName = basename(filePath, ext);\n\n if (baseName === 'index') {\n baseName = basename(dirname(filePath));\n }\n\n return baseName;\n};\n"],"mappings":";;;;;;;;;;AASA,MAAa,+BACX,aACkB;CAElB,MAAM,oBAAoB;CAC1B,MAAM,qBAAqB;CAC3B,MAAM,kBAAkB;CACxB,MAAM,qBAAqB;CAC3B,MAAM,mBAAmB;CAGzB,MAAM,oBAAoB,SAAS,MAAM,kBAAkB;AAC3D,KAAI,kBACF,QAAO,kBAAkB;CAG3B,MAAM,qBAAqB,SAAS,MAAM,mBAAmB;AAC7D,KAAI,mBACF,QAAO,mBAAmB;CAI5B,MAAM,kBACJ,SAAS,MAAM,gBAAgB,IAAI,SAAS,MAAM,mBAAmB;AACvE,KAAI,gBACF,QAAO,gBAAgB;AAIzB,MAAK,MAAM,GAAG,eAAe,SAAS,SAAS,iBAAiB,CAC9D,KAAI,SAAS,KAAK,WAAW,CAC3B,QAAO;AAKX,QAAO;;AAGT,MAAa,wBACX,UACA,aACW;CACX,MAAM,gBAAgB,4BAA4B,SAAS;AAC3D,KAAI,cACF,QAAO;CAIT,IAAI,WAAW,SAAS,UADZ,QAAQ,SAAS,CACS;AAEtC,KAAI,aAAa,QACf,YAAW,SAAS,QAAQ,SAAS,CAAC;AAGxC,QAAO"}
|
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
import { detectFormatCommand } from "../writeContentDeclaration/detectFormatCommand.mjs";
|
|
2
|
-
import { writeContentDeclaration } from "../writeContentDeclaration/writeContentDeclaration.mjs";
|
|
3
|
-
import { extractDictionaryKey } from "./extractDictionaryKey.mjs";
|
|
4
|
-
import { camelCaseToKebabCase, colorizePath, getAppLogger, getConfiguration } from "@intlayer/config";
|
|
5
|
-
import { basename, dirname, extname, join, relative, resolve } from "node:path";
|
|
6
|
-
import { execSync } from "node:child_process";
|
|
7
|
-
import { Node, Project } from "ts-morph";
|
|
8
|
-
|
|
9
|
-
//#region src/compiler/index.ts
|
|
10
|
-
const ATTRIBUTES_TO_EXTRACT = [
|
|
11
|
-
"title",
|
|
12
|
-
"placeholder",
|
|
13
|
-
"alt",
|
|
14
|
-
"aria-label",
|
|
15
|
-
"label"
|
|
16
|
-
];
|
|
17
|
-
const generateKey = (text, existingKeys) => {
|
|
18
|
-
let key = text.replace(/\s+/g, " ").replace(/[^a-zA-Z0-9 ]/g, "").trim().split(" ").filter(Boolean).slice(0, 5).map((word, index) => index === 0 ? word.toLowerCase() : word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
19
|
-
if (!key) key = "content";
|
|
20
|
-
if (existingKeys.has(key)) {
|
|
21
|
-
let i = 1;
|
|
22
|
-
while (existingKeys.has(`${key}${i}`)) i++;
|
|
23
|
-
key = `${key}${i}`;
|
|
24
|
-
}
|
|
25
|
-
return key;
|
|
26
|
-
};
|
|
27
|
-
const shouldExtract = (text) => {
|
|
28
|
-
const trimmed = text.trim();
|
|
29
|
-
if (!trimmed) return false;
|
|
30
|
-
if (!trimmed.includes(" ")) return false;
|
|
31
|
-
if (!/^[A-Z]/.test(trimmed)) return false;
|
|
32
|
-
return true;
|
|
33
|
-
};
|
|
34
|
-
/**
|
|
35
|
-
* 1. Extraction of content
|
|
36
|
-
*/
|
|
37
|
-
const extractContent = (sourceFile) => {
|
|
38
|
-
const extractedContent = {};
|
|
39
|
-
const existingKeys = /* @__PURE__ */ new Set();
|
|
40
|
-
const replacements = [];
|
|
41
|
-
const processText = (text, node, type) => {
|
|
42
|
-
if (!shouldExtract(text)) return;
|
|
43
|
-
const key = generateKey(text.trim(), existingKeys);
|
|
44
|
-
existingKeys.add(key);
|
|
45
|
-
extractedContent[key] = type === "jsx-text" ? text.replace(/\s+/g, " ").trim() : text.trim();
|
|
46
|
-
replacements.push({
|
|
47
|
-
node,
|
|
48
|
-
key,
|
|
49
|
-
type
|
|
50
|
-
});
|
|
51
|
-
};
|
|
52
|
-
sourceFile.forEachDescendant((node) => {
|
|
53
|
-
if (Node.isJsxText(node)) processText(node.getText(), node, "jsx-text");
|
|
54
|
-
if (Node.isJsxAttribute(node)) {
|
|
55
|
-
const name = node.getNameNode().getText();
|
|
56
|
-
if (ATTRIBUTES_TO_EXTRACT.includes(name)) {
|
|
57
|
-
const initializer = node.getInitializer();
|
|
58
|
-
if (Node.isStringLiteral(initializer)) processText(initializer.getLiteralValue(), node, "jsx-attribute");
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (Node.isStringLiteral(node)) {
|
|
62
|
-
const parent = node.getParent();
|
|
63
|
-
if (Node.isCallExpression(parent)) {
|
|
64
|
-
const expression = parent.getExpression();
|
|
65
|
-
if (Node.isIdentifier(expression) && expression.getText() === "useState") processText(node.getLiteralValue(), node, "string-literal");
|
|
66
|
-
}
|
|
67
|
-
if (Node.isConditionalExpression(parent)) {
|
|
68
|
-
let current = parent.getParent();
|
|
69
|
-
let isInsideJsx = false;
|
|
70
|
-
while (current) {
|
|
71
|
-
if (Node.isJsxExpression(current)) {
|
|
72
|
-
isInsideJsx = true;
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
if (Node.isBlock(current) || Node.isSourceFile(current)) break;
|
|
76
|
-
current = current.getParent();
|
|
77
|
-
}
|
|
78
|
-
if (isInsideJsx) processText(node.getLiteralValue(), node, "string-literal");
|
|
79
|
-
}
|
|
80
|
-
if (Node.isArrayLiteralExpression(parent)) processText(node.getLiteralValue(), node, "string-literal");
|
|
81
|
-
if (Node.isPropertyAssignment(parent)) {
|
|
82
|
-
if (parent.getInitializer() === node) processText(node.getLiteralValue(), node, "string-literal");
|
|
83
|
-
}
|
|
84
|
-
if (Node.isReturnStatement(parent)) processText(node.getLiteralValue(), node, "string-literal");
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
return {
|
|
88
|
-
extractedContent,
|
|
89
|
-
replacements
|
|
90
|
-
};
|
|
91
|
-
};
|
|
92
|
-
/**
|
|
93
|
-
* 2. Write content declaration
|
|
94
|
-
*/
|
|
95
|
-
const writeContent = async (extractedContent, componentKey, filePath, configuration, outputDir) => {
|
|
96
|
-
const { defaultLocale } = configuration.internationalization;
|
|
97
|
-
const { baseDir } = configuration.content;
|
|
98
|
-
const dirName = outputDir ? resolve(outputDir) : dirname(filePath);
|
|
99
|
-
const contentFilePath = join(dirName, `${basename(filePath, extname(filePath))}.content.ts`);
|
|
100
|
-
await writeContentDeclaration({
|
|
101
|
-
key: componentKey,
|
|
102
|
-
content: extractedContent,
|
|
103
|
-
locale: defaultLocale,
|
|
104
|
-
filePath: relative(baseDir, contentFilePath)
|
|
105
|
-
}, configuration, { newDictionariesPath: relative(baseDir, dirName) });
|
|
106
|
-
return contentFilePath;
|
|
107
|
-
};
|
|
108
|
-
const isComponent = (node) => {
|
|
109
|
-
let name;
|
|
110
|
-
if (Node.isFunctionDeclaration(node)) name = node.getName();
|
|
111
|
-
else if (Node.isFunctionExpression(node) || Node.isArrowFunction(node)) {
|
|
112
|
-
const parent = node.getParent();
|
|
113
|
-
if (Node.isVariableDeclaration(parent)) name = parent.getName();
|
|
114
|
-
}
|
|
115
|
-
if (!name) return false;
|
|
116
|
-
return /^[A-Z]/.test(name);
|
|
117
|
-
};
|
|
118
|
-
/**
|
|
119
|
-
* 3. Transform component
|
|
120
|
-
*/
|
|
121
|
-
const transformComponent = async (sourceFile, replacements, componentKey, packageName) => {
|
|
122
|
-
if (replacements.length === 0) return;
|
|
123
|
-
const componentsToInjectHook = /* @__PURE__ */ new Map();
|
|
124
|
-
for (const { node, key, type } of replacements) {
|
|
125
|
-
let current = node.getParent();
|
|
126
|
-
while (current) {
|
|
127
|
-
if ((Node.isFunctionDeclaration(current) || Node.isArrowFunction(current) || Node.isFunctionExpression(current)) && (() => {
|
|
128
|
-
const body = current.getBody?.();
|
|
129
|
-
return body && Node.isBlock(body);
|
|
130
|
-
})()) {
|
|
131
|
-
const injectionType = isComponent(current) ? "hook" : "function";
|
|
132
|
-
const existing = componentsToInjectHook.get(current);
|
|
133
|
-
if (!existing) componentsToInjectHook.set(current, injectionType);
|
|
134
|
-
else if (existing === "function" && injectionType === "hook") componentsToInjectHook.set(current, "hook");
|
|
135
|
-
break;
|
|
136
|
-
}
|
|
137
|
-
current = current.getParent();
|
|
138
|
-
}
|
|
139
|
-
const replaceText = (isJsx) => {
|
|
140
|
-
return !isJsx && (packageName === "react-intlayer" || packageName === "next-intlayer") ? `content.${key}.value` : `content.${key}`;
|
|
141
|
-
};
|
|
142
|
-
if (type === "jsx-text" && Node.isJsxText(node)) node.replaceWithText(`{${replaceText(true)}}`);
|
|
143
|
-
else if (type === "jsx-attribute" && Node.isJsxAttribute(node)) node.setInitializer(`{${replaceText(false)}}`);
|
|
144
|
-
else if (type === "string-literal" && Node.isStringLiteral(node)) node.replaceWithText(replaceText(false));
|
|
145
|
-
}
|
|
146
|
-
let injectedUseIntlayer = false;
|
|
147
|
-
let injectedGetIntlayer = false;
|
|
148
|
-
for (const [componentBody, injectionType] of componentsToInjectHook) {
|
|
149
|
-
const body = componentBody.getBody();
|
|
150
|
-
if (Node.isBlock(body)) {
|
|
151
|
-
const text = body.getText();
|
|
152
|
-
const statement = injectionType === "hook" ? `const content = useIntlayer("${componentKey}");` : `const content = getIntlayer("${componentKey}");`;
|
|
153
|
-
if (!text.includes(`useIntlayer("${componentKey}")`) && !text.includes(`getIntlayer("${componentKey}")`)) body.insertStatements(0, statement);
|
|
154
|
-
if (injectionType === "hook") injectedUseIntlayer = true;
|
|
155
|
-
if (injectionType === "function") injectedGetIntlayer = true;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
const importSource = packageName === "next-intlayer" ? sourceFile.getText().includes("\"use client\"") || sourceFile.getText().includes("'use client'") ? "next-intlayer" : "next-intlayer/server" : packageName;
|
|
159
|
-
const existingImport = sourceFile.getImportDeclaration((d) => d.getModuleSpecifierValue() === importSource);
|
|
160
|
-
const namedImports = /* @__PURE__ */ new Set();
|
|
161
|
-
if (injectedUseIntlayer) namedImports.add("useIntlayer");
|
|
162
|
-
if (injectedGetIntlayer) namedImports.add("getIntlayer");
|
|
163
|
-
if (namedImports.size > 0) if (!existingImport) sourceFile.addImportDeclaration({
|
|
164
|
-
namedImports: Array.from(namedImports),
|
|
165
|
-
moduleSpecifier: importSource
|
|
166
|
-
});
|
|
167
|
-
else {
|
|
168
|
-
const currentNamedImports = existingImport.getNamedImports().map((n) => n.getName());
|
|
169
|
-
for (const name of namedImports) if (!currentNamedImports.includes(name)) existingImport.addNamedImport(name);
|
|
170
|
-
}
|
|
171
|
-
await sourceFile.save();
|
|
172
|
-
};
|
|
173
|
-
const extractIntlayer = async (filePath, packageName, options, project) => {
|
|
174
|
-
const configuration = getConfiguration(options?.configOptions);
|
|
175
|
-
const { baseDir } = configuration.content;
|
|
176
|
-
const appLogger = getAppLogger(configuration);
|
|
177
|
-
const _project = project || new Project({ skipAddingFilesFromTsConfig: true });
|
|
178
|
-
let sourceFile;
|
|
179
|
-
try {
|
|
180
|
-
sourceFile = _project.addSourceFileAtPath(filePath);
|
|
181
|
-
} catch {
|
|
182
|
-
sourceFile = _project.getSourceFileOrThrow(filePath);
|
|
183
|
-
}
|
|
184
|
-
const baseName = extractDictionaryKey(filePath, sourceFile.getText());
|
|
185
|
-
const componentKey = camelCaseToKebabCase(baseName);
|
|
186
|
-
const { extractedContent, replacements } = extractContent(sourceFile);
|
|
187
|
-
if (Object.keys(extractedContent).length === 0) {
|
|
188
|
-
appLogger(`No extractable text found in ${baseName}`);
|
|
189
|
-
if (!project) {}
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
const contentFilePath = await writeContent(extractedContent, componentKey, filePath, configuration, options?.outputDir);
|
|
193
|
-
appLogger(`Created content file: ${colorizePath(relative(configuration.content.baseDir, contentFilePath))}`);
|
|
194
|
-
await transformComponent(sourceFile, replacements, componentKey, packageName);
|
|
195
|
-
const formatCommand = detectFormatCommand(configuration);
|
|
196
|
-
if (formatCommand) try {
|
|
197
|
-
execSync(formatCommand.replace("{{file}}", filePath), {
|
|
198
|
-
stdio: "inherit",
|
|
199
|
-
cwd: baseDir
|
|
200
|
-
});
|
|
201
|
-
} catch (error) {
|
|
202
|
-
console.error(error);
|
|
203
|
-
}
|
|
204
|
-
appLogger(`Updated component: ${colorizePath(relative(baseDir, filePath))}`);
|
|
205
|
-
if (project) sourceFile.forget();
|
|
206
|
-
};
|
|
207
|
-
const transformFiles = async (filePaths, packageName, options) => {
|
|
208
|
-
const appLogger = getAppLogger(getConfiguration(options?.configOptions));
|
|
209
|
-
const project = new Project({ skipAddingFilesFromTsConfig: true });
|
|
210
|
-
for (const filePath of filePaths) try {
|
|
211
|
-
await extractIntlayer(filePath, packageName, options, project);
|
|
212
|
-
} catch (error) {
|
|
213
|
-
appLogger(`Failed to transform ${filePath}: ${error.message}`);
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
|
|
217
|
-
//#endregion
|
|
218
|
-
export { extractContent, extractIntlayer, shouldExtract, transformComponent, transformFiles, writeContent };
|
|
219
|
-
//# sourceMappingURL=index.mjs.map
|