@intlayer/babel 7.3.7 → 7.3.9
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/babel-plugin-intlayer-extract.cjs +99 -30
- package/dist/cjs/babel-plugin-intlayer-extract.cjs.map +1 -1
- package/dist/esm/babel-plugin-intlayer-extract.mjs +99 -30
- package/dist/esm/babel-plugin-intlayer-extract.mjs.map +1 -1
- package/dist/types/babel-plugin-intlayer-extract.d.ts +4 -0
- package/dist/types/babel-plugin-intlayer-extract.d.ts.map +1 -1
- package/package.json +6 -6
|
@@ -83,6 +83,8 @@ const intlayerExtractBabelPlugin = (babel) => {
|
|
|
83
83
|
this._hasJSX = false;
|
|
84
84
|
this._hasUseIntlayerImport = false;
|
|
85
85
|
this._useIntlayerLocalName = "useIntlayer";
|
|
86
|
+
this._hasGetIntlayerImport = false;
|
|
87
|
+
this._getIntlayerLocalName = "getIntlayer";
|
|
86
88
|
this._contentVarName = "content";
|
|
87
89
|
const filename = this.file.opts.filename;
|
|
88
90
|
if (this.opts.filesList && filename) {
|
|
@@ -101,10 +103,15 @@ const intlayerExtractBabelPlugin = (babel) => {
|
|
|
101
103
|
if (!state._isIncluded) return;
|
|
102
104
|
for (const spec of path.node.specifiers) {
|
|
103
105
|
if (!t.isImportSpecifier(spec)) continue;
|
|
104
|
-
|
|
106
|
+
const importedName = t.isIdentifier(spec.imported) ? spec.imported.name : spec.imported.value;
|
|
107
|
+
if (importedName === "useIntlayer") {
|
|
105
108
|
state._hasUseIntlayerImport = true;
|
|
106
109
|
state._useIntlayerLocalName = spec.local.name;
|
|
107
110
|
}
|
|
111
|
+
if (importedName === "getIntlayer") {
|
|
112
|
+
state._hasGetIntlayerImport = true;
|
|
113
|
+
state._getIntlayerLocalName = spec.local.name;
|
|
114
|
+
}
|
|
108
115
|
}
|
|
109
116
|
},
|
|
110
117
|
JSXElement(_path, state) {
|
|
@@ -143,6 +150,31 @@ const intlayerExtractBabelPlugin = (babel) => {
|
|
|
143
150
|
path.node.value = t.jsxExpressionContainer(t.memberExpression(t.memberExpression(t.identifier(state._contentVarName), t.identifier(key), false), t.identifier("value"), false));
|
|
144
151
|
}
|
|
145
152
|
},
|
|
153
|
+
StringLiteral(path, state) {
|
|
154
|
+
if (!state._isIncluded) return;
|
|
155
|
+
if (path.parentPath.isJSXAttribute()) return;
|
|
156
|
+
if (path.parentPath.isImportDeclaration()) return;
|
|
157
|
+
if (path.parentPath.isExportDeclaration()) return;
|
|
158
|
+
if (path.parentPath.isImportSpecifier()) return;
|
|
159
|
+
if (path.parentPath.isObjectProperty() && path.key === "key") return;
|
|
160
|
+
if (path.parentPath.isCallExpression()) {
|
|
161
|
+
const callee = path.parentPath.node.callee;
|
|
162
|
+
if (t.isMemberExpression(callee) && t.isIdentifier(callee.object) && callee.object.name === "console") return;
|
|
163
|
+
if (t.isIdentifier(callee) && callee.name === state._useIntlayerLocalName) return;
|
|
164
|
+
if (t.isIdentifier(callee) && callee.name === state._getIntlayerLocalName) return;
|
|
165
|
+
if (callee.type === "Import") return;
|
|
166
|
+
if (t.isIdentifier(callee) && callee.name === "require") return;
|
|
167
|
+
}
|
|
168
|
+
const text = path.node.value;
|
|
169
|
+
if ((state.opts.shouldExtract ?? __intlayer_chokidar.shouldExtract)(text)) {
|
|
170
|
+
const key = (0, __intlayer_chokidar.generateKey)(text, state._existingKeys);
|
|
171
|
+
state._existingKeys.add(key);
|
|
172
|
+
state._extractedContent[key] = text.trim();
|
|
173
|
+
const funcParent = path.getFunctionParent();
|
|
174
|
+
if (funcParent?.node.start != null) state._functionsWithExtractedContent.add(funcParent.node.start);
|
|
175
|
+
path.replaceWith(t.memberExpression(t.identifier(state._contentVarName), t.identifier(key), false));
|
|
176
|
+
}
|
|
177
|
+
},
|
|
146
178
|
Program: {
|
|
147
179
|
enter(programPath, state) {
|
|
148
180
|
if (!state._isIncluded) return;
|
|
@@ -165,32 +197,49 @@ const intlayerExtractBabelPlugin = (babel) => {
|
|
|
165
197
|
content: { ...state._extractedContent },
|
|
166
198
|
locale: defaultLocale
|
|
167
199
|
});
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const importDeclaration = t.importDeclaration([t.importSpecifier(t.identifier("useIntlayer"), t.identifier("useIntlayer"))], t.stringLiteral(packageName));
|
|
171
|
-
let importInsertPos = 0;
|
|
172
|
-
for (const stmtPath of bodyPaths) {
|
|
173
|
-
const stmt = stmtPath.node;
|
|
174
|
-
if (t.isExpressionStatement(stmt) && t.isStringLiteral(stmt.expression)) {
|
|
175
|
-
importInsertPos += 1;
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
break;
|
|
179
|
-
}
|
|
180
|
-
programPath.node.body.splice(importInsertPos, 0, importDeclaration);
|
|
181
|
-
}
|
|
200
|
+
let needsUseIntlayer = false;
|
|
201
|
+
let needsGetIntlayer = false;
|
|
182
202
|
const functionsWithContent = state._functionsWithExtractedContent;
|
|
183
203
|
programPath.traverse({
|
|
184
204
|
FunctionDeclaration(funcPath) {
|
|
185
|
-
if (funcPath.node.start != null && functionsWithContent.has(funcPath.node.start))
|
|
205
|
+
if (funcPath.node.start != null && functionsWithContent.has(funcPath.node.start)) {
|
|
206
|
+
const type = injectHookIntoFunction(funcPath, state, t);
|
|
207
|
+
if (type === "hook") needsUseIntlayer = true;
|
|
208
|
+
if (type === "core") needsGetIntlayer = true;
|
|
209
|
+
}
|
|
186
210
|
},
|
|
187
211
|
VariableDeclarator(varPath) {
|
|
188
212
|
const init = varPath.node.init;
|
|
189
213
|
if (t.isArrowFunctionExpression(init) || t.isFunctionExpression(init)) {
|
|
190
|
-
if (init.start != null && functionsWithContent.has(init.start))
|
|
214
|
+
if (init.start != null && functionsWithContent.has(init.start)) {
|
|
215
|
+
const type = injectHookIntoArrowOrExpression(varPath, init, state, t);
|
|
216
|
+
if (type === "hook") needsUseIntlayer = true;
|
|
217
|
+
if (type === "core") needsGetIntlayer = true;
|
|
218
|
+
}
|
|
191
219
|
}
|
|
192
220
|
}
|
|
193
221
|
});
|
|
222
|
+
if (needsUseIntlayer || needsGetIntlayer) {
|
|
223
|
+
const bodyPaths = programPath.get("body");
|
|
224
|
+
let importInsertPos = 0;
|
|
225
|
+
for (const stmtPath of bodyPaths) {
|
|
226
|
+
const stmt = stmtPath.node;
|
|
227
|
+
if (t.isExpressionStatement(stmt) && t.isStringLiteral(stmt.expression)) {
|
|
228
|
+
importInsertPos += 1;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
if (needsUseIntlayer && !state._hasUseIntlayerImport) {
|
|
234
|
+
const importDeclaration = t.importDeclaration([t.importSpecifier(t.identifier("useIntlayer"), t.identifier("useIntlayer"))], t.stringLiteral(packageName));
|
|
235
|
+
programPath.node.body.splice(importInsertPos, 0, importDeclaration);
|
|
236
|
+
importInsertPos++;
|
|
237
|
+
}
|
|
238
|
+
if (needsGetIntlayer && !state._hasGetIntlayerImport) {
|
|
239
|
+
const importDeclaration = t.importDeclaration([t.importSpecifier(t.identifier("getIntlayer"), t.identifier("getIntlayer"))], t.stringLiteral(packageName));
|
|
240
|
+
programPath.node.body.splice(importInsertPos, 0, importDeclaration);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
194
243
|
}
|
|
195
244
|
}
|
|
196
245
|
}
|
|
@@ -198,20 +247,28 @@ const intlayerExtractBabelPlugin = (babel) => {
|
|
|
198
247
|
};
|
|
199
248
|
/**
|
|
200
249
|
* Inject useIntlayer hook into a function declaration
|
|
250
|
+
* Returns 'hook' if useIntlayer was injected (or needed), 'core' if getIntlayer was injected, or null.
|
|
201
251
|
*/
|
|
202
252
|
const injectHookIntoFunction = (funcPath, state, t) => {
|
|
203
253
|
const body = funcPath.node.body;
|
|
204
|
-
if (!t.isBlockStatement(body)) return;
|
|
254
|
+
if (!t.isBlockStatement(body)) return null;
|
|
205
255
|
let returnsJSX = false;
|
|
206
256
|
funcPath.traverse({ ReturnStatement(returnPath) {
|
|
207
257
|
const arg = returnPath.node.argument;
|
|
208
258
|
if (t.isJSXElement(arg) || t.isJSXFragment(arg)) returnsJSX = true;
|
|
209
259
|
} });
|
|
210
|
-
if (!returnsJSX) return;
|
|
211
260
|
const contentVarName = state._contentVarName;
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
|
|
261
|
+
if (returnsJSX) {
|
|
262
|
+
if (body.body.some((stmt) => t.isVariableDeclaration(stmt) && stmt.declarations.some((decl) => t.isIdentifier(decl.id) && decl.id.name === contentVarName && t.isCallExpression(decl.init) && t.isIdentifier(decl.init.callee) && decl.init.callee.name === state._useIntlayerLocalName))) return "hook";
|
|
263
|
+
const hookCall = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._useIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
264
|
+
body.body.unshift(hookCall);
|
|
265
|
+
return "hook";
|
|
266
|
+
} else {
|
|
267
|
+
if (body.body.some((stmt) => t.isVariableDeclaration(stmt) && stmt.declarations.some((decl) => t.isIdentifier(decl.id) && decl.id.name === contentVarName && t.isCallExpression(decl.init) && t.isIdentifier(decl.init.callee) && decl.init.callee.name === state._getIntlayerLocalName))) return "core";
|
|
268
|
+
const call = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._getIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
269
|
+
body.body.unshift(call);
|
|
270
|
+
return "core";
|
|
271
|
+
}
|
|
215
272
|
};
|
|
216
273
|
/**
|
|
217
274
|
* Inject useIntlayer hook into an arrow function or function expression
|
|
@@ -220,21 +277,33 @@ const injectHookIntoArrowOrExpression = (varPath, init, state, t) => {
|
|
|
220
277
|
const body = init.body;
|
|
221
278
|
const contentVarName = state._contentVarName;
|
|
222
279
|
if (t.isJSXElement(body) || t.isJSXFragment(body)) {
|
|
223
|
-
const hookCall
|
|
280
|
+
const hookCall = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._useIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
281
|
+
const returnStmt = t.returnStatement(body);
|
|
282
|
+
init.body = t.blockStatement([hookCall, returnStmt]);
|
|
283
|
+
return "hook";
|
|
284
|
+
}
|
|
285
|
+
if (!t.isBlockStatement(body)) {
|
|
286
|
+
const call = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._getIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
224
287
|
const returnStmt = t.returnStatement(body);
|
|
225
|
-
init.body = t.blockStatement([
|
|
226
|
-
return;
|
|
288
|
+
init.body = t.blockStatement([call, returnStmt]);
|
|
289
|
+
return "core";
|
|
227
290
|
}
|
|
228
|
-
if (!t.isBlockStatement(body)) return;
|
|
229
291
|
let returnsJSX = false;
|
|
230
292
|
varPath.traverse({ ReturnStatement(returnPath) {
|
|
231
293
|
const arg = returnPath.node.argument;
|
|
232
294
|
if (t.isJSXElement(arg) || t.isJSXFragment(arg)) returnsJSX = true;
|
|
233
295
|
} });
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
296
|
+
if (returnsJSX) {
|
|
297
|
+
if (body.body.some((stmt) => t.isVariableDeclaration(stmt) && stmt.declarations.some((decl) => t.isIdentifier(decl.id) && decl.id.name === contentVarName && t.isCallExpression(decl.init) && t.isIdentifier(decl.init.callee) && decl.init.callee.name === state._useIntlayerLocalName))) return "hook";
|
|
298
|
+
const hookCall = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._useIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
299
|
+
body.body.unshift(hookCall);
|
|
300
|
+
return "hook";
|
|
301
|
+
} else {
|
|
302
|
+
if (body.body.some((stmt) => t.isVariableDeclaration(stmt) && stmt.declarations.some((decl) => t.isIdentifier(decl.id) && decl.id.name === contentVarName && t.isCallExpression(decl.init) && t.isIdentifier(decl.init.callee) && decl.init.callee.name === state._getIntlayerLocalName))) return "core";
|
|
303
|
+
const call = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._getIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
304
|
+
body.body.unshift(call);
|
|
305
|
+
return "core";
|
|
306
|
+
}
|
|
238
307
|
};
|
|
239
308
|
|
|
240
309
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"babel-plugin-intlayer-extract.cjs","names":["defaultShouldExtract","ATTRIBUTES_TO_EXTRACT","text: string | null","hookCall"],"sources":["../../src/babel-plugin-intlayer-extract.ts"],"sourcesContent":["import { basename, dirname, extname } from 'node:path';\nimport type { NodePath, PluginObj, PluginPass } from '@babel/core';\nimport type * as BabelTypes from '@babel/types';\nimport {\n ATTRIBUTES_TO_EXTRACT,\n shouldExtract as defaultShouldExtract,\n generateKey,\n} from '@intlayer/chokidar';\n\ntype ExtractedContent = Record<string, string>;\n\n/**\n * Extracted content result from a file transformation\n */\nexport type ExtractResult = {\n /** Dictionary key derived from the file path */\n dictionaryKey: string;\n /** File path that was processed */\n filePath: string;\n /** Extracted content key-value pairs */\n content: ExtractedContent;\n /** Default locale used */\n locale: string;\n};\n\n/**\n * Options for the extraction Babel plugin\n */\nexport type ExtractPluginOptions = {\n /**\n * The default locale for the extracted content\n */\n defaultLocale?: string;\n /**\n * The package to import useIntlayer from\n * @default 'react-intlayer'\n */\n packageName?: string;\n /**\n * Files list to traverse. If provided, only files in this list will be processed.\n */\n filesList?: string[];\n /**\n * Custom function to determine if a string should be extracted\n */\n shouldExtract?: (text: string) => boolean;\n /**\n * Callback function called when content is extracted from a file.\n * This allows the compiler to capture the extracted content and write it to files.\n * The dictionary will be updated: new keys added, unused keys removed.\n */\n onExtract?: (result: ExtractResult) => void;\n};\n\ntype State = PluginPass & {\n opts: ExtractPluginOptions;\n /** Extracted content from this file */\n _extractedContent?: ExtractedContent;\n /** Set of existing keys to avoid duplicates */\n _existingKeys?: Set<string>;\n /** The dictionary key for this file */\n _dictionaryKey?: string;\n /** whether the current file is included in the filesList */\n _isIncluded?: boolean;\n /** Whether this file has JSX (React component) */\n _hasJSX?: boolean;\n /** Whether we already have useIntlayer imported */\n _hasUseIntlayerImport?: boolean;\n /** The local name for useIntlayer (in case it's aliased) */\n _useIntlayerLocalName?: string;\n /** The variable name to use for content (content or _compContent if content is already used) */\n _contentVarName?: string;\n /** Set of function start positions that have extracted content (only inject hooks into these) */\n _functionsWithExtractedContent?: Set<number>;\n};\n\n/* ────────────────────────────────────────── helpers ─────────────────────── */\n\n/**\n * Extract dictionary key from file path\n */\nconst extractDictionaryKeyFromPath = (filePath: string): string => {\n const ext = extname(filePath);\n let baseName = basename(filePath, ext);\n\n if (baseName === 'index') {\n baseName = basename(dirname(filePath));\n }\n\n // Convert to kebab-case\n const key = baseName\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase();\n\n return `comp-${key}`;\n};\n\n/* ────────────────────────────────────────── plugin ──────────────────────── */\n\n/**\n * Autonomous Babel plugin that extracts content and transforms JSX to use useIntlayer.\n *\n * This plugin:\n * 1. Scans files for extractable text (JSX text, attributes)\n * 2. Auto-injects useIntlayer import and hook call\n * 3. Reports extracted content via onExtract callback (for the compiler to write dictionaries)\n * 4. Replaces extractable strings with content references\n *\n * ## Input\n * ```tsx\n * export const MyComponent = () => {\n * return <div>Hello World</div>;\n * };\n * ```\n *\n * ## Output\n * ```tsx\n * import { useIntlayer } from 'react-intlayer';\n *\n * export const MyComponent = () => {\n * const content = useIntlayer('comp-my-component');\n * return <div>{content.helloWorld}</div>;\n * };\n * ```\n *\n * ## When useIntlayer is already present\n *\n * If the component already has a `content` variable from an existing `useIntlayer` call,\n * the plugin will use `_compContent` to avoid naming conflicts:\n *\n * ### Input\n * ```tsx\n * export const Page = () => {\n * const content = useIntlayer('page');\n * return <div>{content.title} - Hello World</div>;\n * };\n * ```\n *\n * ### Output\n * ```tsx\n * export const Page = () => {\n * const _compContent = useIntlayer('comp-page');\n * const content = useIntlayer('page');\n * return <div>{content.title} - {_compContent.helloWorld}</div>;\n * };\n * ```\n *\n * The extracted content is reported via the `onExtract` callback, allowing the\n * compiler to write the dictionary to disk separately:\n * ```json\n * // my-component.content.json (written by compiler)\n * {\n * \"key\": \"comp-my-component\",\n * \"content\": {\n * \"helloWorld\": { \"nodeType\": \"translation\", \"translation\": { \"en\": \"Hello World\" } }\n * }\n * }\n * ```\n */\nexport const intlayerExtractBabelPlugin = (babel: {\n types: typeof BabelTypes;\n}): PluginObj<State> => {\n const { types: t } = babel;\n\n return {\n name: 'babel-plugin-intlayer-extract',\n\n pre() {\n this._extractedContent = {};\n this._existingKeys = new Set();\n this._functionsWithExtractedContent = new Set();\n this._isIncluded = true;\n this._hasJSX = false;\n this._hasUseIntlayerImport = false;\n this._useIntlayerLocalName = 'useIntlayer';\n this._contentVarName = 'content'; // Will be updated in Program.enter if 'content' is already used\n\n const filename = this.file.opts.filename;\n\n // If filesList is provided, check if current file is included\n if (this.opts.filesList && filename) {\n // Normalize paths for comparison (handle potential path separator issues)\n const normalizedFilename = filename.replace(/\\\\/g, '/');\n const isIncluded = this.opts.filesList.some((f) => {\n const normalizedF = f.replace(/\\\\/g, '/');\n return normalizedF === normalizedFilename;\n });\n\n if (!isIncluded) {\n this._isIncluded = false;\n return;\n }\n }\n\n // Extract dictionary key from filename\n if (filename) {\n this._dictionaryKey = extractDictionaryKeyFromPath(filename);\n }\n },\n\n visitor: {\n /* Check if useIntlayer is already imported */\n ImportDeclaration(path, state) {\n if (!state._isIncluded) return;\n\n for (const spec of path.node.specifiers) {\n if (!t.isImportSpecifier(spec)) continue;\n\n const importedName = t.isIdentifier(spec.imported)\n ? spec.imported.name\n : (spec.imported as BabelTypes.StringLiteral).value;\n\n if (importedName === 'useIntlayer') {\n state._hasUseIntlayerImport = true;\n state._useIntlayerLocalName = spec.local.name;\n }\n }\n },\n\n /* Detect JSX elements to know this is a component file */\n JSXElement(_path, state) {\n if (!state._isIncluded) return;\n state._hasJSX = true;\n },\n\n /* Extract JSX text content */\n JSXText(path, state) {\n if (!state._isIncluded) return;\n\n const text = path.node.value;\n const shouldExtract = state.opts.shouldExtract ?? defaultShouldExtract;\n\n if (shouldExtract(text)) {\n const key = generateKey(text, state._existingKeys!);\n state._existingKeys!.add(key);\n\n // Collect extracted content\n state._extractedContent![key] = text.replace(/\\s+/g, ' ').trim();\n\n // Track which function has extracted content\n const funcParent = path.getFunctionParent();\n if (funcParent?.node.start != null) {\n state._functionsWithExtractedContent!.add(funcParent.node.start);\n }\n\n // Replace with {content.key} or {_compContent.key}\n path.replaceWith(\n t.jsxExpressionContainer(\n t.memberExpression(\n t.identifier(state._contentVarName!),\n t.identifier(key),\n false\n )\n )\n );\n }\n },\n\n /* Extract JSX attributes */\n JSXAttribute(path, state) {\n if (!state._isIncluded) return;\n\n const name = path.node.name;\n\n if (!t.isJSXIdentifier(name)) return;\n\n const attrName = name.name;\n if (!ATTRIBUTES_TO_EXTRACT.includes(attrName)) return;\n\n const value = path.node.value;\n\n // Handle both direct StringLiteral and JSXExpressionContainer with StringLiteral\n // Case 1: attr=\"value\" -> value is StringLiteral\n // Case 2: attr={\"value\"} -> value is JSXExpressionContainer containing StringLiteral\n let text: string | null = null;\n\n if (t.isStringLiteral(value)) {\n text = value.value;\n } else if (\n t.isJSXExpressionContainer(value) &&\n t.isStringLiteral(value.expression)\n ) {\n text = value.expression.value;\n }\n\n if (text === null) return;\n\n const shouldExtract = state.opts.shouldExtract ?? defaultShouldExtract;\n\n if (shouldExtract(text)) {\n const key = generateKey(text, state._existingKeys!);\n state._existingKeys!.add(key);\n\n // Collect extracted content\n state._extractedContent![key] = text.trim();\n\n // Track which function has extracted content\n const funcParent = path.getFunctionParent();\n if (funcParent?.node.start != null) {\n state._functionsWithExtractedContent!.add(funcParent.node.start);\n }\n\n // Replace with {content.key.value} or {_compContent.key.value}\n path.node.value = t.jsxExpressionContainer(\n t.memberExpression(\n t.memberExpression(\n t.identifier(state._contentVarName!),\n t.identifier(key),\n false\n ),\n t.identifier('value'),\n false\n )\n );\n }\n },\n\n /* Inject useIntlayer hook at program exit */\n Program: {\n enter(programPath, state) {\n if (!state._isIncluded) return;\n\n // Check if 'content' variable is already used in any function\n // If so, we'll use '_compContent' to avoid conflicts\n let contentVarUsed = false;\n\n programPath.traverse({\n VariableDeclarator(varPath) {\n if (\n t.isIdentifier(varPath.node.id) &&\n varPath.node.id.name === 'content'\n ) {\n contentVarUsed = true;\n }\n },\n });\n\n state._contentVarName = contentVarUsed ? '_compContent' : 'content';\n },\n\n exit(programPath, state) {\n if (!state._isIncluded) return;\n\n const extractedKeys = Object.keys(state._extractedContent!);\n const hasExtractedContent = extractedKeys.length > 0;\n\n // If no content was extracted, skip - don't inject useIntlayer for files with no extractable text\n if (!hasExtractedContent) return;\n\n // Only process JSX files (React components)\n if (!state._hasJSX) return;\n\n const defaultLocale = state.opts.defaultLocale;\n const packageName = state.opts.packageName;\n\n // Call the onExtract callback with extracted content\n // This will update the dictionary, adding new keys and removing unused ones\n if (\n state.opts.onExtract &&\n state._dictionaryKey &&\n hasExtractedContent\n ) {\n state.opts.onExtract({\n dictionaryKey: state._dictionaryKey,\n filePath: state.file.opts.filename!,\n content: { ...state._extractedContent! },\n locale: defaultLocale!,\n });\n }\n\n // Find insertion point (after directives and imports)\n const bodyPaths = programPath.get(\n 'body'\n ) as NodePath<BabelTypes.Statement>[];\n\n // Add useIntlayer import if not already present\n if (!state._hasUseIntlayerImport) {\n const importDeclaration = t.importDeclaration(\n [\n t.importSpecifier(\n t.identifier('useIntlayer'),\n t.identifier('useIntlayer')\n ),\n ],\n t.stringLiteral(packageName!)\n );\n\n // Find the best position for import (after directives but before other imports)\n let importInsertPos = 0;\n for (const stmtPath of bodyPaths) {\n const stmt = stmtPath.node;\n if (\n t.isExpressionStatement(stmt) &&\n t.isStringLiteral(stmt.expression)\n ) {\n importInsertPos += 1;\n continue;\n }\n break;\n }\n\n programPath.node.body.splice(importInsertPos, 0, importDeclaration);\n }\n\n // Now inject useIntlayer hook only into functions that have extracted content\n const functionsWithContent = state._functionsWithExtractedContent!;\n\n programPath.traverse({\n // Handle function declarations\n FunctionDeclaration(funcPath) {\n // Only inject if this function has extracted content\n if (\n funcPath.node.start != null &&\n functionsWithContent.has(funcPath.node.start)\n ) {\n injectHookIntoFunction(funcPath, state, t);\n }\n },\n\n // Handle arrow functions and function expressions in variable declarations\n VariableDeclarator(varPath) {\n const init = varPath.node.init;\n if (\n t.isArrowFunctionExpression(init) ||\n t.isFunctionExpression(init)\n ) {\n // Only inject if this function has extracted content\n if (\n init.start != null &&\n functionsWithContent.has(init.start)\n ) {\n injectHookIntoArrowOrExpression(\n varPath as NodePath<BabelTypes.VariableDeclarator>,\n init,\n state,\n t\n );\n }\n }\n },\n });\n },\n },\n },\n };\n};\n\n/**\n * Inject useIntlayer hook into a function declaration\n */\nconst injectHookIntoFunction = (\n funcPath: NodePath<BabelTypes.FunctionDeclaration>,\n state: State,\n t: typeof BabelTypes\n) => {\n const body = funcPath.node.body;\n if (!t.isBlockStatement(body)) return;\n\n // Check if this function returns JSX\n let returnsJSX = false;\n funcPath.traverse({\n ReturnStatement(returnPath) {\n const arg = returnPath.node.argument;\n if (t.isJSXElement(arg) || t.isJSXFragment(arg)) {\n returnsJSX = true;\n }\n },\n });\n\n if (!returnsJSX) return;\n\n const contentVarName = state._contentVarName!;\n\n // Check if hook with this specific variable name is already injected\n const hasHook = body.body.some(\n (stmt) =>\n t.isVariableDeclaration(stmt) &&\n stmt.declarations.some(\n (decl) =>\n t.isIdentifier(decl.id) &&\n decl.id.name === contentVarName &&\n t.isCallExpression(decl.init) &&\n t.isIdentifier(decl.init.callee) &&\n decl.init.callee.name === state._useIntlayerLocalName\n )\n );\n\n if (hasHook) return;\n\n // Inject: const content = useIntlayer('dictionary-key');\n // or: const _compContent = useIntlayer('comp-dictionary-key');\n const hookCall = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._useIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n body.body.unshift(hookCall);\n};\n\n/**\n * Inject useIntlayer hook into an arrow function or function expression\n */\nconst injectHookIntoArrowOrExpression = (\n varPath: NodePath<BabelTypes.VariableDeclarator>,\n init: BabelTypes.ArrowFunctionExpression | BabelTypes.FunctionExpression,\n state: State,\n t: typeof BabelTypes\n) => {\n const body = init.body;\n const contentVarName = state._contentVarName!;\n\n // If the body is JSX directly (implicit return), wrap it in a block\n if (t.isJSXElement(body) || t.isJSXFragment(body)) {\n // Transform: () => <div>...</div>\n // To: () => { const content = useIntlayer('key'); return <div>...</div>; }\n // or: () => { const _compContent = useIntlayer('comp-key'); return <div>...</div>; }\n const hookCall = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._useIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n const returnStmt = t.returnStatement(body);\n init.body = t.blockStatement([hookCall, returnStmt]);\n return;\n }\n\n if (!t.isBlockStatement(body)) return;\n\n // Check if this function returns JSX\n let returnsJSX = false;\n varPath.traverse({\n ReturnStatement(returnPath) {\n const arg = returnPath.node.argument;\n if (t.isJSXElement(arg) || t.isJSXFragment(arg)) {\n returnsJSX = true;\n }\n },\n });\n\n if (!returnsJSX) return;\n\n // Check if hook with this specific variable name is already injected\n const hasHook = body.body.some(\n (stmt) =>\n t.isVariableDeclaration(stmt) &&\n stmt.declarations.some(\n (decl) =>\n t.isIdentifier(decl.id) &&\n decl.id.name === contentVarName &&\n t.isCallExpression(decl.init) &&\n t.isIdentifier(decl.init.callee) &&\n decl.init.callee.name === state._useIntlayerLocalName\n )\n );\n\n if (hasHook) return;\n\n // Inject: const content = useIntlayer('dictionary-key');\n // or: const _compContent = useIntlayer('comp-dictionary-key');\n const hookCall = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._useIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n body.body.unshift(hookCall);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"babel-plugin-intlayer-extract.cjs","names":["defaultShouldExtract","ATTRIBUTES_TO_EXTRACT","text: string | null"],"sources":["../../src/babel-plugin-intlayer-extract.ts"],"sourcesContent":["import { basename, dirname, extname } from 'node:path';\nimport type { NodePath, PluginObj, PluginPass } from '@babel/core';\nimport type * as BabelTypes from '@babel/types';\nimport {\n ATTRIBUTES_TO_EXTRACT,\n shouldExtract as defaultShouldExtract,\n generateKey,\n} from '@intlayer/chokidar';\n\ntype ExtractedContent = Record<string, string>;\n\n/**\n * Extracted content result from a file transformation\n */\nexport type ExtractResult = {\n /** Dictionary key derived from the file path */\n dictionaryKey: string;\n /** File path that was processed */\n filePath: string;\n /** Extracted content key-value pairs */\n content: ExtractedContent;\n /** Default locale used */\n locale: string;\n};\n\n/**\n * Options for the extraction Babel plugin\n */\nexport type ExtractPluginOptions = {\n /**\n * The default locale for the extracted content\n */\n defaultLocale?: string;\n /**\n * The package to import useIntlayer from\n * @default 'react-intlayer'\n */\n packageName?: string;\n /**\n * Files list to traverse. If provided, only files in this list will be processed.\n */\n filesList?: string[];\n /**\n * Custom function to determine if a string should be extracted\n */\n shouldExtract?: (text: string) => boolean;\n /**\n * Callback function called when content is extracted from a file.\n * This allows the compiler to capture the extracted content and write it to files.\n * The dictionary will be updated: new keys added, unused keys removed.\n */\n onExtract?: (result: ExtractResult) => void;\n};\n\ntype State = PluginPass & {\n opts: ExtractPluginOptions;\n /** Extracted content from this file */\n _extractedContent?: ExtractedContent;\n /** Set of existing keys to avoid duplicates */\n _existingKeys?: Set<string>;\n /** The dictionary key for this file */\n _dictionaryKey?: string;\n /** whether the current file is included in the filesList */\n _isIncluded?: boolean;\n /** Whether this file has JSX (React component) */\n _hasJSX?: boolean;\n /** Whether we already have useIntlayer imported */\n _hasUseIntlayerImport?: boolean;\n /** The local name for useIntlayer (in case it's aliased) */\n _useIntlayerLocalName?: string;\n /** Whether we already have getIntlayer imported */\n _hasGetIntlayerImport?: boolean;\n /** The local name for getIntlayer (in case it's aliased) */\n _getIntlayerLocalName?: string;\n /** The variable name to use for content (content or _compContent if content is already used) */\n _contentVarName?: string;\n /** Set of function start positions that have extracted content (only inject hooks into these) */\n _functionsWithExtractedContent?: Set<number>;\n};\n\n/* ────────────────────────────────────────── helpers ─────────────────────── */\n\n/**\n * Extract dictionary key from file path\n */\nconst extractDictionaryKeyFromPath = (filePath: string): string => {\n const ext = extname(filePath);\n let baseName = basename(filePath, ext);\n\n if (baseName === 'index') {\n baseName = basename(dirname(filePath));\n }\n\n // Convert to kebab-case\n const key = baseName\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase();\n\n return `comp-${key}`;\n};\n\n/* ────────────────────────────────────────── plugin ──────────────────────── */\n\n/**\n * Autonomous Babel plugin that extracts content and transforms JSX to use useIntlayer.\n *\n * This plugin:\n * 1. Scans files for extractable text (JSX text, attributes)\n * 2. Auto-injects useIntlayer import and hook call\n * 3. Reports extracted content via onExtract callback (for the compiler to write dictionaries)\n * 4. Replaces extractable strings with content references\n *\n * ## Input\n * ```tsx\n * export const MyComponent = () => {\n * return <div>Hello World</div>;\n * };\n * ```\n *\n * ## Output\n * ```tsx\n * import { useIntlayer } from 'react-intlayer';\n *\n * export const MyComponent = () => {\n * const content = useIntlayer('comp-my-component');\n * return <div>{content.helloWorld}</div>;\n * };\n * ```\n *\n * ## When useIntlayer is already present\n *\n * If the component already has a `content` variable from an existing `useIntlayer` call,\n * the plugin will use `_compContent` to avoid naming conflicts:\n *\n * ### Input\n * ```tsx\n * export const Page = () => {\n * const content = useIntlayer('page');\n * return <div>{content.title} - Hello World</div>;\n * };\n * ```\n *\n * ### Output\n * ```tsx\n * export const Page = () => {\n * const _compContent = useIntlayer('comp-page');\n * const content = useIntlayer('page');\n * return <div>{content.title} - {_compContent.helloWorld}</div>;\n * };\n * ```\n *\n * The extracted content is reported via the `onExtract` callback, allowing the\n * compiler to write the dictionary to disk separately:\n * ```json\n * // my-component.content.json (written by compiler)\n * {\n * \"key\": \"comp-my-component\",\n * \"content\": {\n * \"helloWorld\": { \"nodeType\": \"translation\", \"translation\": { \"en\": \"Hello World\" } }\n * }\n * }\n * ```\n */\nexport const intlayerExtractBabelPlugin = (babel: {\n types: typeof BabelTypes;\n}): PluginObj<State> => {\n const { types: t } = babel;\n\n return {\n name: 'babel-plugin-intlayer-extract',\n\n pre() {\n this._extractedContent = {};\n this._existingKeys = new Set();\n this._functionsWithExtractedContent = new Set();\n this._isIncluded = true;\n this._hasJSX = false;\n this._hasUseIntlayerImport = false;\n this._useIntlayerLocalName = 'useIntlayer';\n this._hasGetIntlayerImport = false;\n this._getIntlayerLocalName = 'getIntlayer';\n this._contentVarName = 'content'; // Will be updated in Program.enter if 'content' is already used\n\n const filename = this.file.opts.filename;\n\n // If filesList is provided, check if current file is included\n if (this.opts.filesList && filename) {\n // Normalize paths for comparison (handle potential path separator issues)\n const normalizedFilename = filename.replace(/\\\\/g, '/');\n const isIncluded = this.opts.filesList.some((f) => {\n const normalizedF = f.replace(/\\\\/g, '/');\n return normalizedF === normalizedFilename;\n });\n\n if (!isIncluded) {\n this._isIncluded = false;\n return;\n }\n }\n\n // Extract dictionary key from filename\n if (filename) {\n this._dictionaryKey = extractDictionaryKeyFromPath(filename);\n }\n },\n\n visitor: {\n /* Check if useIntlayer is already imported */\n ImportDeclaration(path, state) {\n if (!state._isIncluded) return;\n\n for (const spec of path.node.specifiers) {\n if (!t.isImportSpecifier(spec)) continue;\n\n const importedName = t.isIdentifier(spec.imported)\n ? spec.imported.name\n : (spec.imported as BabelTypes.StringLiteral).value;\n\n if (importedName === 'useIntlayer') {\n state._hasUseIntlayerImport = true;\n state._useIntlayerLocalName = spec.local.name;\n }\n if (importedName === 'getIntlayer') {\n state._hasGetIntlayerImport = true;\n state._getIntlayerLocalName = spec.local.name;\n }\n }\n },\n\n /* Detect JSX elements to know this is a component file */\n JSXElement(_path, state) {\n if (!state._isIncluded) return;\n state._hasJSX = true;\n },\n\n /* Extract JSX text content */\n JSXText(path, state) {\n if (!state._isIncluded) return;\n\n const text = path.node.value;\n const shouldExtract = state.opts.shouldExtract ?? defaultShouldExtract;\n\n if (shouldExtract(text)) {\n const key = generateKey(text, state._existingKeys!);\n state._existingKeys!.add(key);\n\n // Collect extracted content\n state._extractedContent![key] = text.replace(/\\s+/g, ' ').trim();\n\n // Track which function has extracted content\n const funcParent = path.getFunctionParent();\n if (funcParent?.node.start != null) {\n state._functionsWithExtractedContent!.add(funcParent.node.start);\n }\n\n // Replace with {content.key} or {_compContent.key}\n path.replaceWith(\n t.jsxExpressionContainer(\n t.memberExpression(\n t.identifier(state._contentVarName!),\n t.identifier(key),\n false\n )\n )\n );\n }\n },\n\n /* Extract JSX attributes */\n JSXAttribute(path, state) {\n if (!state._isIncluded) return;\n\n const name = path.node.name;\n\n if (!t.isJSXIdentifier(name)) return;\n\n const attrName = name.name;\n if (!ATTRIBUTES_TO_EXTRACT.includes(attrName)) return;\n\n const value = path.node.value;\n\n // Handle both direct StringLiteral and JSXExpressionContainer with StringLiteral\n // Case 1: attr=\"value\" -> value is StringLiteral\n // Case 2: attr={\"value\"} -> value is JSXExpressionContainer containing StringLiteral\n let text: string | null = null;\n\n if (t.isStringLiteral(value)) {\n text = value.value;\n } else if (\n t.isJSXExpressionContainer(value) &&\n t.isStringLiteral(value.expression)\n ) {\n text = value.expression.value;\n }\n\n if (text === null) return;\n\n const shouldExtract = state.opts.shouldExtract ?? defaultShouldExtract;\n\n if (shouldExtract(text)) {\n const key = generateKey(text, state._existingKeys!);\n state._existingKeys!.add(key);\n\n // Collect extracted content\n state._extractedContent![key] = text.trim();\n\n // Track which function has extracted content\n const funcParent = path.getFunctionParent();\n if (funcParent?.node.start != null) {\n state._functionsWithExtractedContent!.add(funcParent.node.start);\n }\n\n // Replace with {content.key.value} or {_compContent.key.value}\n path.node.value = t.jsxExpressionContainer(\n t.memberExpression(\n t.memberExpression(\n t.identifier(state._contentVarName!),\n t.identifier(key),\n false\n ),\n t.identifier('value'),\n false\n )\n );\n }\n },\n\n /* Extract String Literals in code (variables, props, etc.) */\n StringLiteral(path, state) {\n if (!state._isIncluded) return;\n if (path.parentPath.isJSXAttribute()) return; // Already handled\n if (path.parentPath.isImportDeclaration()) return;\n if (path.parentPath.isExportDeclaration()) return;\n if (path.parentPath.isImportSpecifier()) return;\n // Check if it is a key in an object property\n if (path.parentPath.isObjectProperty() && path.key === 'key') return;\n\n // Check if it is a call expression to console or useIntlayer\n if (path.parentPath.isCallExpression()) {\n const callee = path.parentPath.node.callee;\n\n // Check for console.log/error/etc\n if (\n t.isMemberExpression(callee) &&\n t.isIdentifier(callee.object) &&\n callee.object.name === 'console'\n ) {\n return;\n }\n\n // Check for useIntlayer('key')\n if (\n t.isIdentifier(callee) &&\n callee.name === state._useIntlayerLocalName\n ) {\n return;\n }\n\n // Check for getIntlayer('key')\n if (\n t.isIdentifier(callee) &&\n callee.name === state._getIntlayerLocalName\n ) {\n return;\n }\n\n // Check for dynamic import import()\n if (callee.type === 'Import') return;\n\n // Check for require()\n if (t.isIdentifier(callee) && callee.name === 'require') return;\n }\n\n const text = path.node.value;\n const shouldExtract = state.opts.shouldExtract ?? defaultShouldExtract;\n\n if (shouldExtract(text)) {\n const key = generateKey(text, state._existingKeys!);\n state._existingKeys!.add(key);\n\n // Collect extracted content\n state._extractedContent![key] = text.trim();\n\n // Track which function has extracted content\n const funcParent = path.getFunctionParent();\n if (funcParent?.node.start != null) {\n state._functionsWithExtractedContent!.add(funcParent.node.start);\n }\n\n // Replace with content.key or _compContent.key\n path.replaceWith(\n t.memberExpression(\n t.identifier(state._contentVarName!),\n t.identifier(key),\n false\n )\n );\n }\n },\n\n /* Inject useIntlayer hook at program exit */\n Program: {\n enter(programPath, state) {\n if (!state._isIncluded) return;\n\n // Check if 'content' variable is already used in any function\n // If so, we'll use '_compContent' to avoid conflicts\n let contentVarUsed = false;\n\n programPath.traverse({\n VariableDeclarator(varPath) {\n if (\n t.isIdentifier(varPath.node.id) &&\n varPath.node.id.name === 'content'\n ) {\n contentVarUsed = true;\n }\n },\n });\n\n state._contentVarName = contentVarUsed ? '_compContent' : 'content';\n },\n\n exit(programPath, state) {\n if (!state._isIncluded) return;\n\n const extractedKeys = Object.keys(state._extractedContent!);\n const hasExtractedContent = extractedKeys.length > 0;\n\n // If no content was extracted, skip - don't inject useIntlayer for files with no extractable text\n if (!hasExtractedContent) return;\n\n // Only process JSX files (React components)\n if (!state._hasJSX) return;\n\n const defaultLocale = state.opts.defaultLocale;\n const packageName = state.opts.packageName;\n\n // Call the onExtract callback with extracted content\n // This will update the dictionary, adding new keys and removing unused ones\n if (\n state.opts.onExtract &&\n state._dictionaryKey &&\n hasExtractedContent\n ) {\n state.opts.onExtract({\n dictionaryKey: state._dictionaryKey,\n filePath: state.file.opts.filename!,\n content: { ...state._extractedContent! },\n locale: defaultLocale!,\n });\n }\n\n // Track what we need to inject\n let needsUseIntlayer = false;\n let needsGetIntlayer = false;\n\n // Now inject hooks only into functions that have extracted content\n const functionsWithContent = state._functionsWithExtractedContent!;\n\n programPath.traverse({\n // Handle function declarations\n FunctionDeclaration(funcPath) {\n // Only inject if this function has extracted content\n if (\n funcPath.node.start != null &&\n functionsWithContent.has(funcPath.node.start)\n ) {\n const type = injectHookIntoFunction(funcPath, state, t);\n if (type === 'hook') needsUseIntlayer = true;\n if (type === 'core') needsGetIntlayer = true;\n }\n },\n\n // Handle arrow functions and function expressions in variable declarations\n VariableDeclarator(varPath) {\n const init = varPath.node.init;\n if (\n t.isArrowFunctionExpression(init) ||\n t.isFunctionExpression(init)\n ) {\n // Only inject if this function has extracted content\n if (\n init.start != null &&\n functionsWithContent.has(init.start)\n ) {\n const type = injectHookIntoArrowOrExpression(\n varPath as NodePath<BabelTypes.VariableDeclarator>,\n init,\n state,\n t\n );\n if (type === 'hook') needsUseIntlayer = true;\n if (type === 'core') needsGetIntlayer = true;\n }\n }\n },\n });\n\n // Add imports if needed\n if (needsUseIntlayer || needsGetIntlayer) {\n const bodyPaths = programPath.get(\n 'body'\n ) as NodePath<BabelTypes.Statement>[];\n\n // Find the best position for import (after directives but before other imports)\n let importInsertPos = 0;\n for (const stmtPath of bodyPaths) {\n const stmt = stmtPath.node;\n if (\n t.isExpressionStatement(stmt) &&\n t.isStringLiteral(stmt.expression)\n ) {\n importInsertPos += 1;\n continue;\n }\n break;\n }\n\n // Inject useIntlayer import\n if (needsUseIntlayer && !state._hasUseIntlayerImport) {\n const importDeclaration = t.importDeclaration(\n [\n t.importSpecifier(\n t.identifier('useIntlayer'),\n t.identifier('useIntlayer')\n ),\n ],\n t.stringLiteral(packageName!)\n );\n programPath.node.body.splice(\n importInsertPos,\n 0,\n importDeclaration\n );\n // adjust position for next import\n importInsertPos++;\n }\n\n // Inject getIntlayer import\n if (needsGetIntlayer && !state._hasGetIntlayerImport) {\n const importDeclaration = t.importDeclaration(\n [\n t.importSpecifier(\n t.identifier('getIntlayer'),\n t.identifier('getIntlayer')\n ),\n ],\n t.stringLiteral(packageName!)\n );\n programPath.node.body.splice(\n importInsertPos,\n 0,\n importDeclaration\n );\n }\n }\n },\n },\n },\n };\n};\n\n/**\n * Inject useIntlayer hook into a function declaration\n * Returns 'hook' if useIntlayer was injected (or needed), 'core' if getIntlayer was injected, or null.\n */\nconst injectHookIntoFunction = (\n funcPath: NodePath<BabelTypes.FunctionDeclaration>,\n state: State,\n t: typeof BabelTypes\n): 'hook' | 'core' | null => {\n const body = funcPath.node.body;\n if (!t.isBlockStatement(body)) return null;\n\n // Check if this function returns JSX\n let returnsJSX = false;\n funcPath.traverse({\n ReturnStatement(returnPath) {\n const arg = returnPath.node.argument;\n if (t.isJSXElement(arg) || t.isJSXFragment(arg)) {\n returnsJSX = true;\n }\n },\n });\n\n const contentVarName = state._contentVarName!;\n\n if (returnsJSX) {\n // Inject useIntlayer\n\n // Check if hook with this specific variable name is already injected\n const hasHook = body.body.some(\n (stmt) =>\n t.isVariableDeclaration(stmt) &&\n stmt.declarations.some(\n (decl) =>\n t.isIdentifier(decl.id) &&\n decl.id.name === contentVarName &&\n t.isCallExpression(decl.init) &&\n t.isIdentifier(decl.init.callee) &&\n decl.init.callee.name === state._useIntlayerLocalName\n )\n );\n\n if (hasHook) return 'hook';\n\n // Inject: const content = useIntlayer('dictionary-key');\n const hookCall = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._useIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n body.body.unshift(hookCall);\n return 'hook';\n } else {\n // Inject getIntlayer\n\n // Check if getIntlayer call with this variable name is already injected\n const hasCall = body.body.some(\n (stmt) =>\n t.isVariableDeclaration(stmt) &&\n stmt.declarations.some(\n (decl) =>\n t.isIdentifier(decl.id) &&\n decl.id.name === contentVarName &&\n t.isCallExpression(decl.init) &&\n t.isIdentifier(decl.init.callee) &&\n decl.init.callee.name === state._getIntlayerLocalName\n )\n );\n\n if (hasCall) return 'core';\n\n // Inject: const content = getIntlayer('dictionary-key');\n const call = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._getIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n body.body.unshift(call);\n return 'core';\n }\n};\n\n/**\n * Inject useIntlayer hook into an arrow function or function expression\n */\nconst injectHookIntoArrowOrExpression = (\n varPath: NodePath<BabelTypes.VariableDeclarator>,\n init: BabelTypes.ArrowFunctionExpression | BabelTypes.FunctionExpression,\n state: State,\n t: typeof BabelTypes\n): 'hook' | 'core' | null => {\n const body = init.body;\n const contentVarName = state._contentVarName!;\n\n // If the body is JSX directly (implicit return), wrap it in a block\n if (t.isJSXElement(body) || t.isJSXFragment(body)) {\n // Transform: () => <div>...</div>\n // To: () => { const content = useIntlayer('key'); return <div>...</div>; }\n const hookCall = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._useIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n const returnStmt = t.returnStatement(body);\n init.body = t.blockStatement([hookCall, returnStmt]);\n return 'hook';\n }\n\n if (!t.isBlockStatement(body)) {\n // Transform: () => \"string\"\n // To: () => { const content = getIntlayer('key'); return \"string\"; }\n const call = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._getIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n const returnStmt = t.returnStatement(body);\n init.body = t.blockStatement([call, returnStmt]);\n return 'core';\n }\n\n // Check if this function returns JSX\n let returnsJSX = false;\n varPath.traverse({\n ReturnStatement(returnPath) {\n const arg = returnPath.node.argument;\n if (t.isJSXElement(arg) || t.isJSXFragment(arg)) {\n returnsJSX = true;\n }\n },\n });\n\n if (returnsJSX) {\n // Inject useIntlayer\n const hasHook = body.body.some(\n (stmt) =>\n t.isVariableDeclaration(stmt) &&\n stmt.declarations.some(\n (decl) =>\n t.isIdentifier(decl.id) &&\n decl.id.name === contentVarName &&\n t.isCallExpression(decl.init) &&\n t.isIdentifier(decl.init.callee) &&\n decl.init.callee.name === state._useIntlayerLocalName\n )\n );\n\n if (hasHook) return 'hook';\n\n const hookCall = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._useIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n body.body.unshift(hookCall);\n return 'hook';\n } else {\n // Inject getIntlayer\n const hasCall = body.body.some(\n (stmt) =>\n t.isVariableDeclaration(stmt) &&\n stmt.declarations.some(\n (decl) =>\n t.isIdentifier(decl.id) &&\n decl.id.name === contentVarName &&\n t.isCallExpression(decl.init) &&\n t.isIdentifier(decl.init.callee) &&\n decl.init.callee.name === state._getIntlayerLocalName\n )\n );\n\n if (hasCall) return 'core';\n\n const call = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._getIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n body.body.unshift(call);\n return 'core';\n }\n};\n"],"mappings":"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"}
|
|
@@ -82,6 +82,8 @@ const intlayerExtractBabelPlugin = (babel) => {
|
|
|
82
82
|
this._hasJSX = false;
|
|
83
83
|
this._hasUseIntlayerImport = false;
|
|
84
84
|
this._useIntlayerLocalName = "useIntlayer";
|
|
85
|
+
this._hasGetIntlayerImport = false;
|
|
86
|
+
this._getIntlayerLocalName = "getIntlayer";
|
|
85
87
|
this._contentVarName = "content";
|
|
86
88
|
const filename = this.file.opts.filename;
|
|
87
89
|
if (this.opts.filesList && filename) {
|
|
@@ -100,10 +102,15 @@ const intlayerExtractBabelPlugin = (babel) => {
|
|
|
100
102
|
if (!state._isIncluded) return;
|
|
101
103
|
for (const spec of path.node.specifiers) {
|
|
102
104
|
if (!t.isImportSpecifier(spec)) continue;
|
|
103
|
-
|
|
105
|
+
const importedName = t.isIdentifier(spec.imported) ? spec.imported.name : spec.imported.value;
|
|
106
|
+
if (importedName === "useIntlayer") {
|
|
104
107
|
state._hasUseIntlayerImport = true;
|
|
105
108
|
state._useIntlayerLocalName = spec.local.name;
|
|
106
109
|
}
|
|
110
|
+
if (importedName === "getIntlayer") {
|
|
111
|
+
state._hasGetIntlayerImport = true;
|
|
112
|
+
state._getIntlayerLocalName = spec.local.name;
|
|
113
|
+
}
|
|
107
114
|
}
|
|
108
115
|
},
|
|
109
116
|
JSXElement(_path, state) {
|
|
@@ -142,6 +149,31 @@ const intlayerExtractBabelPlugin = (babel) => {
|
|
|
142
149
|
path.node.value = t.jsxExpressionContainer(t.memberExpression(t.memberExpression(t.identifier(state._contentVarName), t.identifier(key), false), t.identifier("value"), false));
|
|
143
150
|
}
|
|
144
151
|
},
|
|
152
|
+
StringLiteral(path, state) {
|
|
153
|
+
if (!state._isIncluded) return;
|
|
154
|
+
if (path.parentPath.isJSXAttribute()) return;
|
|
155
|
+
if (path.parentPath.isImportDeclaration()) return;
|
|
156
|
+
if (path.parentPath.isExportDeclaration()) return;
|
|
157
|
+
if (path.parentPath.isImportSpecifier()) return;
|
|
158
|
+
if (path.parentPath.isObjectProperty() && path.key === "key") return;
|
|
159
|
+
if (path.parentPath.isCallExpression()) {
|
|
160
|
+
const callee = path.parentPath.node.callee;
|
|
161
|
+
if (t.isMemberExpression(callee) && t.isIdentifier(callee.object) && callee.object.name === "console") return;
|
|
162
|
+
if (t.isIdentifier(callee) && callee.name === state._useIntlayerLocalName) return;
|
|
163
|
+
if (t.isIdentifier(callee) && callee.name === state._getIntlayerLocalName) return;
|
|
164
|
+
if (callee.type === "Import") return;
|
|
165
|
+
if (t.isIdentifier(callee) && callee.name === "require") return;
|
|
166
|
+
}
|
|
167
|
+
const text = path.node.value;
|
|
168
|
+
if ((state.opts.shouldExtract ?? shouldExtract)(text)) {
|
|
169
|
+
const key = generateKey(text, state._existingKeys);
|
|
170
|
+
state._existingKeys.add(key);
|
|
171
|
+
state._extractedContent[key] = text.trim();
|
|
172
|
+
const funcParent = path.getFunctionParent();
|
|
173
|
+
if (funcParent?.node.start != null) state._functionsWithExtractedContent.add(funcParent.node.start);
|
|
174
|
+
path.replaceWith(t.memberExpression(t.identifier(state._contentVarName), t.identifier(key), false));
|
|
175
|
+
}
|
|
176
|
+
},
|
|
145
177
|
Program: {
|
|
146
178
|
enter(programPath, state) {
|
|
147
179
|
if (!state._isIncluded) return;
|
|
@@ -164,32 +196,49 @@ const intlayerExtractBabelPlugin = (babel) => {
|
|
|
164
196
|
content: { ...state._extractedContent },
|
|
165
197
|
locale: defaultLocale
|
|
166
198
|
});
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const importDeclaration = t.importDeclaration([t.importSpecifier(t.identifier("useIntlayer"), t.identifier("useIntlayer"))], t.stringLiteral(packageName));
|
|
170
|
-
let importInsertPos = 0;
|
|
171
|
-
for (const stmtPath of bodyPaths) {
|
|
172
|
-
const stmt = stmtPath.node;
|
|
173
|
-
if (t.isExpressionStatement(stmt) && t.isStringLiteral(stmt.expression)) {
|
|
174
|
-
importInsertPos += 1;
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
programPath.node.body.splice(importInsertPos, 0, importDeclaration);
|
|
180
|
-
}
|
|
199
|
+
let needsUseIntlayer = false;
|
|
200
|
+
let needsGetIntlayer = false;
|
|
181
201
|
const functionsWithContent = state._functionsWithExtractedContent;
|
|
182
202
|
programPath.traverse({
|
|
183
203
|
FunctionDeclaration(funcPath) {
|
|
184
|
-
if (funcPath.node.start != null && functionsWithContent.has(funcPath.node.start))
|
|
204
|
+
if (funcPath.node.start != null && functionsWithContent.has(funcPath.node.start)) {
|
|
205
|
+
const type = injectHookIntoFunction(funcPath, state, t);
|
|
206
|
+
if (type === "hook") needsUseIntlayer = true;
|
|
207
|
+
if (type === "core") needsGetIntlayer = true;
|
|
208
|
+
}
|
|
185
209
|
},
|
|
186
210
|
VariableDeclarator(varPath) {
|
|
187
211
|
const init = varPath.node.init;
|
|
188
212
|
if (t.isArrowFunctionExpression(init) || t.isFunctionExpression(init)) {
|
|
189
|
-
if (init.start != null && functionsWithContent.has(init.start))
|
|
213
|
+
if (init.start != null && functionsWithContent.has(init.start)) {
|
|
214
|
+
const type = injectHookIntoArrowOrExpression(varPath, init, state, t);
|
|
215
|
+
if (type === "hook") needsUseIntlayer = true;
|
|
216
|
+
if (type === "core") needsGetIntlayer = true;
|
|
217
|
+
}
|
|
190
218
|
}
|
|
191
219
|
}
|
|
192
220
|
});
|
|
221
|
+
if (needsUseIntlayer || needsGetIntlayer) {
|
|
222
|
+
const bodyPaths = programPath.get("body");
|
|
223
|
+
let importInsertPos = 0;
|
|
224
|
+
for (const stmtPath of bodyPaths) {
|
|
225
|
+
const stmt = stmtPath.node;
|
|
226
|
+
if (t.isExpressionStatement(stmt) && t.isStringLiteral(stmt.expression)) {
|
|
227
|
+
importInsertPos += 1;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
if (needsUseIntlayer && !state._hasUseIntlayerImport) {
|
|
233
|
+
const importDeclaration = t.importDeclaration([t.importSpecifier(t.identifier("useIntlayer"), t.identifier("useIntlayer"))], t.stringLiteral(packageName));
|
|
234
|
+
programPath.node.body.splice(importInsertPos, 0, importDeclaration);
|
|
235
|
+
importInsertPos++;
|
|
236
|
+
}
|
|
237
|
+
if (needsGetIntlayer && !state._hasGetIntlayerImport) {
|
|
238
|
+
const importDeclaration = t.importDeclaration([t.importSpecifier(t.identifier("getIntlayer"), t.identifier("getIntlayer"))], t.stringLiteral(packageName));
|
|
239
|
+
programPath.node.body.splice(importInsertPos, 0, importDeclaration);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
193
242
|
}
|
|
194
243
|
}
|
|
195
244
|
}
|
|
@@ -197,20 +246,28 @@ const intlayerExtractBabelPlugin = (babel) => {
|
|
|
197
246
|
};
|
|
198
247
|
/**
|
|
199
248
|
* Inject useIntlayer hook into a function declaration
|
|
249
|
+
* Returns 'hook' if useIntlayer was injected (or needed), 'core' if getIntlayer was injected, or null.
|
|
200
250
|
*/
|
|
201
251
|
const injectHookIntoFunction = (funcPath, state, t) => {
|
|
202
252
|
const body = funcPath.node.body;
|
|
203
|
-
if (!t.isBlockStatement(body)) return;
|
|
253
|
+
if (!t.isBlockStatement(body)) return null;
|
|
204
254
|
let returnsJSX = false;
|
|
205
255
|
funcPath.traverse({ ReturnStatement(returnPath) {
|
|
206
256
|
const arg = returnPath.node.argument;
|
|
207
257
|
if (t.isJSXElement(arg) || t.isJSXFragment(arg)) returnsJSX = true;
|
|
208
258
|
} });
|
|
209
|
-
if (!returnsJSX) return;
|
|
210
259
|
const contentVarName = state._contentVarName;
|
|
211
|
-
if (
|
|
212
|
-
|
|
213
|
-
|
|
260
|
+
if (returnsJSX) {
|
|
261
|
+
if (body.body.some((stmt) => t.isVariableDeclaration(stmt) && stmt.declarations.some((decl) => t.isIdentifier(decl.id) && decl.id.name === contentVarName && t.isCallExpression(decl.init) && t.isIdentifier(decl.init.callee) && decl.init.callee.name === state._useIntlayerLocalName))) return "hook";
|
|
262
|
+
const hookCall = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._useIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
263
|
+
body.body.unshift(hookCall);
|
|
264
|
+
return "hook";
|
|
265
|
+
} else {
|
|
266
|
+
if (body.body.some((stmt) => t.isVariableDeclaration(stmt) && stmt.declarations.some((decl) => t.isIdentifier(decl.id) && decl.id.name === contentVarName && t.isCallExpression(decl.init) && t.isIdentifier(decl.init.callee) && decl.init.callee.name === state._getIntlayerLocalName))) return "core";
|
|
267
|
+
const call = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._getIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
268
|
+
body.body.unshift(call);
|
|
269
|
+
return "core";
|
|
270
|
+
}
|
|
214
271
|
};
|
|
215
272
|
/**
|
|
216
273
|
* Inject useIntlayer hook into an arrow function or function expression
|
|
@@ -219,21 +276,33 @@ const injectHookIntoArrowOrExpression = (varPath, init, state, t) => {
|
|
|
219
276
|
const body = init.body;
|
|
220
277
|
const contentVarName = state._contentVarName;
|
|
221
278
|
if (t.isJSXElement(body) || t.isJSXFragment(body)) {
|
|
222
|
-
const hookCall
|
|
279
|
+
const hookCall = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._useIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
280
|
+
const returnStmt = t.returnStatement(body);
|
|
281
|
+
init.body = t.blockStatement([hookCall, returnStmt]);
|
|
282
|
+
return "hook";
|
|
283
|
+
}
|
|
284
|
+
if (!t.isBlockStatement(body)) {
|
|
285
|
+
const call = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._getIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
223
286
|
const returnStmt = t.returnStatement(body);
|
|
224
|
-
init.body = t.blockStatement([
|
|
225
|
-
return;
|
|
287
|
+
init.body = t.blockStatement([call, returnStmt]);
|
|
288
|
+
return "core";
|
|
226
289
|
}
|
|
227
|
-
if (!t.isBlockStatement(body)) return;
|
|
228
290
|
let returnsJSX = false;
|
|
229
291
|
varPath.traverse({ ReturnStatement(returnPath) {
|
|
230
292
|
const arg = returnPath.node.argument;
|
|
231
293
|
if (t.isJSXElement(arg) || t.isJSXFragment(arg)) returnsJSX = true;
|
|
232
294
|
} });
|
|
233
|
-
if (
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
295
|
+
if (returnsJSX) {
|
|
296
|
+
if (body.body.some((stmt) => t.isVariableDeclaration(stmt) && stmt.declarations.some((decl) => t.isIdentifier(decl.id) && decl.id.name === contentVarName && t.isCallExpression(decl.init) && t.isIdentifier(decl.init.callee) && decl.init.callee.name === state._useIntlayerLocalName))) return "hook";
|
|
297
|
+
const hookCall = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._useIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
298
|
+
body.body.unshift(hookCall);
|
|
299
|
+
return "hook";
|
|
300
|
+
} else {
|
|
301
|
+
if (body.body.some((stmt) => t.isVariableDeclaration(stmt) && stmt.declarations.some((decl) => t.isIdentifier(decl.id) && decl.id.name === contentVarName && t.isCallExpression(decl.init) && t.isIdentifier(decl.init.callee) && decl.init.callee.name === state._getIntlayerLocalName))) return "core";
|
|
302
|
+
const call = t.variableDeclaration("const", [t.variableDeclarator(t.identifier(contentVarName), t.callExpression(t.identifier(state._getIntlayerLocalName), [t.stringLiteral(state._dictionaryKey)]))]);
|
|
303
|
+
body.body.unshift(call);
|
|
304
|
+
return "core";
|
|
305
|
+
}
|
|
237
306
|
};
|
|
238
307
|
|
|
239
308
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"babel-plugin-intlayer-extract.mjs","names":["defaultShouldExtract","text: string | null","hookCall"],"sources":["../../src/babel-plugin-intlayer-extract.ts"],"sourcesContent":["import { basename, dirname, extname } from 'node:path';\nimport type { NodePath, PluginObj, PluginPass } from '@babel/core';\nimport type * as BabelTypes from '@babel/types';\nimport {\n ATTRIBUTES_TO_EXTRACT,\n shouldExtract as defaultShouldExtract,\n generateKey,\n} from '@intlayer/chokidar';\n\ntype ExtractedContent = Record<string, string>;\n\n/**\n * Extracted content result from a file transformation\n */\nexport type ExtractResult = {\n /** Dictionary key derived from the file path */\n dictionaryKey: string;\n /** File path that was processed */\n filePath: string;\n /** Extracted content key-value pairs */\n content: ExtractedContent;\n /** Default locale used */\n locale: string;\n};\n\n/**\n * Options for the extraction Babel plugin\n */\nexport type ExtractPluginOptions = {\n /**\n * The default locale for the extracted content\n */\n defaultLocale?: string;\n /**\n * The package to import useIntlayer from\n * @default 'react-intlayer'\n */\n packageName?: string;\n /**\n * Files list to traverse. If provided, only files in this list will be processed.\n */\n filesList?: string[];\n /**\n * Custom function to determine if a string should be extracted\n */\n shouldExtract?: (text: string) => boolean;\n /**\n * Callback function called when content is extracted from a file.\n * This allows the compiler to capture the extracted content and write it to files.\n * The dictionary will be updated: new keys added, unused keys removed.\n */\n onExtract?: (result: ExtractResult) => void;\n};\n\ntype State = PluginPass & {\n opts: ExtractPluginOptions;\n /** Extracted content from this file */\n _extractedContent?: ExtractedContent;\n /** Set of existing keys to avoid duplicates */\n _existingKeys?: Set<string>;\n /** The dictionary key for this file */\n _dictionaryKey?: string;\n /** whether the current file is included in the filesList */\n _isIncluded?: boolean;\n /** Whether this file has JSX (React component) */\n _hasJSX?: boolean;\n /** Whether we already have useIntlayer imported */\n _hasUseIntlayerImport?: boolean;\n /** The local name for useIntlayer (in case it's aliased) */\n _useIntlayerLocalName?: string;\n /** The variable name to use for content (content or _compContent if content is already used) */\n _contentVarName?: string;\n /** Set of function start positions that have extracted content (only inject hooks into these) */\n _functionsWithExtractedContent?: Set<number>;\n};\n\n/* ────────────────────────────────────────── helpers ─────────────────────── */\n\n/**\n * Extract dictionary key from file path\n */\nconst extractDictionaryKeyFromPath = (filePath: string): string => {\n const ext = extname(filePath);\n let baseName = basename(filePath, ext);\n\n if (baseName === 'index') {\n baseName = basename(dirname(filePath));\n }\n\n // Convert to kebab-case\n const key = baseName\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase();\n\n return `comp-${key}`;\n};\n\n/* ────────────────────────────────────────── plugin ──────────────────────── */\n\n/**\n * Autonomous Babel plugin that extracts content and transforms JSX to use useIntlayer.\n *\n * This plugin:\n * 1. Scans files for extractable text (JSX text, attributes)\n * 2. Auto-injects useIntlayer import and hook call\n * 3. Reports extracted content via onExtract callback (for the compiler to write dictionaries)\n * 4. Replaces extractable strings with content references\n *\n * ## Input\n * ```tsx\n * export const MyComponent = () => {\n * return <div>Hello World</div>;\n * };\n * ```\n *\n * ## Output\n * ```tsx\n * import { useIntlayer } from 'react-intlayer';\n *\n * export const MyComponent = () => {\n * const content = useIntlayer('comp-my-component');\n * return <div>{content.helloWorld}</div>;\n * };\n * ```\n *\n * ## When useIntlayer is already present\n *\n * If the component already has a `content` variable from an existing `useIntlayer` call,\n * the plugin will use `_compContent` to avoid naming conflicts:\n *\n * ### Input\n * ```tsx\n * export const Page = () => {\n * const content = useIntlayer('page');\n * return <div>{content.title} - Hello World</div>;\n * };\n * ```\n *\n * ### Output\n * ```tsx\n * export const Page = () => {\n * const _compContent = useIntlayer('comp-page');\n * const content = useIntlayer('page');\n * return <div>{content.title} - {_compContent.helloWorld}</div>;\n * };\n * ```\n *\n * The extracted content is reported via the `onExtract` callback, allowing the\n * compiler to write the dictionary to disk separately:\n * ```json\n * // my-component.content.json (written by compiler)\n * {\n * \"key\": \"comp-my-component\",\n * \"content\": {\n * \"helloWorld\": { \"nodeType\": \"translation\", \"translation\": { \"en\": \"Hello World\" } }\n * }\n * }\n * ```\n */\nexport const intlayerExtractBabelPlugin = (babel: {\n types: typeof BabelTypes;\n}): PluginObj<State> => {\n const { types: t } = babel;\n\n return {\n name: 'babel-plugin-intlayer-extract',\n\n pre() {\n this._extractedContent = {};\n this._existingKeys = new Set();\n this._functionsWithExtractedContent = new Set();\n this._isIncluded = true;\n this._hasJSX = false;\n this._hasUseIntlayerImport = false;\n this._useIntlayerLocalName = 'useIntlayer';\n this._contentVarName = 'content'; // Will be updated in Program.enter if 'content' is already used\n\n const filename = this.file.opts.filename;\n\n // If filesList is provided, check if current file is included\n if (this.opts.filesList && filename) {\n // Normalize paths for comparison (handle potential path separator issues)\n const normalizedFilename = filename.replace(/\\\\/g, '/');\n const isIncluded = this.opts.filesList.some((f) => {\n const normalizedF = f.replace(/\\\\/g, '/');\n return normalizedF === normalizedFilename;\n });\n\n if (!isIncluded) {\n this._isIncluded = false;\n return;\n }\n }\n\n // Extract dictionary key from filename\n if (filename) {\n this._dictionaryKey = extractDictionaryKeyFromPath(filename);\n }\n },\n\n visitor: {\n /* Check if useIntlayer is already imported */\n ImportDeclaration(path, state) {\n if (!state._isIncluded) return;\n\n for (const spec of path.node.specifiers) {\n if (!t.isImportSpecifier(spec)) continue;\n\n const importedName = t.isIdentifier(spec.imported)\n ? spec.imported.name\n : (spec.imported as BabelTypes.StringLiteral).value;\n\n if (importedName === 'useIntlayer') {\n state._hasUseIntlayerImport = true;\n state._useIntlayerLocalName = spec.local.name;\n }\n }\n },\n\n /* Detect JSX elements to know this is a component file */\n JSXElement(_path, state) {\n if (!state._isIncluded) return;\n state._hasJSX = true;\n },\n\n /* Extract JSX text content */\n JSXText(path, state) {\n if (!state._isIncluded) return;\n\n const text = path.node.value;\n const shouldExtract = state.opts.shouldExtract ?? defaultShouldExtract;\n\n if (shouldExtract(text)) {\n const key = generateKey(text, state._existingKeys!);\n state._existingKeys!.add(key);\n\n // Collect extracted content\n state._extractedContent![key] = text.replace(/\\s+/g, ' ').trim();\n\n // Track which function has extracted content\n const funcParent = path.getFunctionParent();\n if (funcParent?.node.start != null) {\n state._functionsWithExtractedContent!.add(funcParent.node.start);\n }\n\n // Replace with {content.key} or {_compContent.key}\n path.replaceWith(\n t.jsxExpressionContainer(\n t.memberExpression(\n t.identifier(state._contentVarName!),\n t.identifier(key),\n false\n )\n )\n );\n }\n },\n\n /* Extract JSX attributes */\n JSXAttribute(path, state) {\n if (!state._isIncluded) return;\n\n const name = path.node.name;\n\n if (!t.isJSXIdentifier(name)) return;\n\n const attrName = name.name;\n if (!ATTRIBUTES_TO_EXTRACT.includes(attrName)) return;\n\n const value = path.node.value;\n\n // Handle both direct StringLiteral and JSXExpressionContainer with StringLiteral\n // Case 1: attr=\"value\" -> value is StringLiteral\n // Case 2: attr={\"value\"} -> value is JSXExpressionContainer containing StringLiteral\n let text: string | null = null;\n\n if (t.isStringLiteral(value)) {\n text = value.value;\n } else if (\n t.isJSXExpressionContainer(value) &&\n t.isStringLiteral(value.expression)\n ) {\n text = value.expression.value;\n }\n\n if (text === null) return;\n\n const shouldExtract = state.opts.shouldExtract ?? defaultShouldExtract;\n\n if (shouldExtract(text)) {\n const key = generateKey(text, state._existingKeys!);\n state._existingKeys!.add(key);\n\n // Collect extracted content\n state._extractedContent![key] = text.trim();\n\n // Track which function has extracted content\n const funcParent = path.getFunctionParent();\n if (funcParent?.node.start != null) {\n state._functionsWithExtractedContent!.add(funcParent.node.start);\n }\n\n // Replace with {content.key.value} or {_compContent.key.value}\n path.node.value = t.jsxExpressionContainer(\n t.memberExpression(\n t.memberExpression(\n t.identifier(state._contentVarName!),\n t.identifier(key),\n false\n ),\n t.identifier('value'),\n false\n )\n );\n }\n },\n\n /* Inject useIntlayer hook at program exit */\n Program: {\n enter(programPath, state) {\n if (!state._isIncluded) return;\n\n // Check if 'content' variable is already used in any function\n // If so, we'll use '_compContent' to avoid conflicts\n let contentVarUsed = false;\n\n programPath.traverse({\n VariableDeclarator(varPath) {\n if (\n t.isIdentifier(varPath.node.id) &&\n varPath.node.id.name === 'content'\n ) {\n contentVarUsed = true;\n }\n },\n });\n\n state._contentVarName = contentVarUsed ? '_compContent' : 'content';\n },\n\n exit(programPath, state) {\n if (!state._isIncluded) return;\n\n const extractedKeys = Object.keys(state._extractedContent!);\n const hasExtractedContent = extractedKeys.length > 0;\n\n // If no content was extracted, skip - don't inject useIntlayer for files with no extractable text\n if (!hasExtractedContent) return;\n\n // Only process JSX files (React components)\n if (!state._hasJSX) return;\n\n const defaultLocale = state.opts.defaultLocale;\n const packageName = state.opts.packageName;\n\n // Call the onExtract callback with extracted content\n // This will update the dictionary, adding new keys and removing unused ones\n if (\n state.opts.onExtract &&\n state._dictionaryKey &&\n hasExtractedContent\n ) {\n state.opts.onExtract({\n dictionaryKey: state._dictionaryKey,\n filePath: state.file.opts.filename!,\n content: { ...state._extractedContent! },\n locale: defaultLocale!,\n });\n }\n\n // Find insertion point (after directives and imports)\n const bodyPaths = programPath.get(\n 'body'\n ) as NodePath<BabelTypes.Statement>[];\n\n // Add useIntlayer import if not already present\n if (!state._hasUseIntlayerImport) {\n const importDeclaration = t.importDeclaration(\n [\n t.importSpecifier(\n t.identifier('useIntlayer'),\n t.identifier('useIntlayer')\n ),\n ],\n t.stringLiteral(packageName!)\n );\n\n // Find the best position for import (after directives but before other imports)\n let importInsertPos = 0;\n for (const stmtPath of bodyPaths) {\n const stmt = stmtPath.node;\n if (\n t.isExpressionStatement(stmt) &&\n t.isStringLiteral(stmt.expression)\n ) {\n importInsertPos += 1;\n continue;\n }\n break;\n }\n\n programPath.node.body.splice(importInsertPos, 0, importDeclaration);\n }\n\n // Now inject useIntlayer hook only into functions that have extracted content\n const functionsWithContent = state._functionsWithExtractedContent!;\n\n programPath.traverse({\n // Handle function declarations\n FunctionDeclaration(funcPath) {\n // Only inject if this function has extracted content\n if (\n funcPath.node.start != null &&\n functionsWithContent.has(funcPath.node.start)\n ) {\n injectHookIntoFunction(funcPath, state, t);\n }\n },\n\n // Handle arrow functions and function expressions in variable declarations\n VariableDeclarator(varPath) {\n const init = varPath.node.init;\n if (\n t.isArrowFunctionExpression(init) ||\n t.isFunctionExpression(init)\n ) {\n // Only inject if this function has extracted content\n if (\n init.start != null &&\n functionsWithContent.has(init.start)\n ) {\n injectHookIntoArrowOrExpression(\n varPath as NodePath<BabelTypes.VariableDeclarator>,\n init,\n state,\n t\n );\n }\n }\n },\n });\n },\n },\n },\n };\n};\n\n/**\n * Inject useIntlayer hook into a function declaration\n */\nconst injectHookIntoFunction = (\n funcPath: NodePath<BabelTypes.FunctionDeclaration>,\n state: State,\n t: typeof BabelTypes\n) => {\n const body = funcPath.node.body;\n if (!t.isBlockStatement(body)) return;\n\n // Check if this function returns JSX\n let returnsJSX = false;\n funcPath.traverse({\n ReturnStatement(returnPath) {\n const arg = returnPath.node.argument;\n if (t.isJSXElement(arg) || t.isJSXFragment(arg)) {\n returnsJSX = true;\n }\n },\n });\n\n if (!returnsJSX) return;\n\n const contentVarName = state._contentVarName!;\n\n // Check if hook with this specific variable name is already injected\n const hasHook = body.body.some(\n (stmt) =>\n t.isVariableDeclaration(stmt) &&\n stmt.declarations.some(\n (decl) =>\n t.isIdentifier(decl.id) &&\n decl.id.name === contentVarName &&\n t.isCallExpression(decl.init) &&\n t.isIdentifier(decl.init.callee) &&\n decl.init.callee.name === state._useIntlayerLocalName\n )\n );\n\n if (hasHook) return;\n\n // Inject: const content = useIntlayer('dictionary-key');\n // or: const _compContent = useIntlayer('comp-dictionary-key');\n const hookCall = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._useIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n body.body.unshift(hookCall);\n};\n\n/**\n * Inject useIntlayer hook into an arrow function or function expression\n */\nconst injectHookIntoArrowOrExpression = (\n varPath: NodePath<BabelTypes.VariableDeclarator>,\n init: BabelTypes.ArrowFunctionExpression | BabelTypes.FunctionExpression,\n state: State,\n t: typeof BabelTypes\n) => {\n const body = init.body;\n const contentVarName = state._contentVarName!;\n\n // If the body is JSX directly (implicit return), wrap it in a block\n if (t.isJSXElement(body) || t.isJSXFragment(body)) {\n // Transform: () => <div>...</div>\n // To: () => { const content = useIntlayer('key'); return <div>...</div>; }\n // or: () => { const _compContent = useIntlayer('comp-key'); return <div>...</div>; }\n const hookCall = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._useIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n const returnStmt = t.returnStatement(body);\n init.body = t.blockStatement([hookCall, returnStmt]);\n return;\n }\n\n if (!t.isBlockStatement(body)) return;\n\n // Check if this function returns JSX\n let returnsJSX = false;\n varPath.traverse({\n ReturnStatement(returnPath) {\n const arg = returnPath.node.argument;\n if (t.isJSXElement(arg) || t.isJSXFragment(arg)) {\n returnsJSX = true;\n }\n },\n });\n\n if (!returnsJSX) return;\n\n // Check if hook with this specific variable name is already injected\n const hasHook = body.body.some(\n (stmt) =>\n t.isVariableDeclaration(stmt) &&\n stmt.declarations.some(\n (decl) =>\n t.isIdentifier(decl.id) &&\n decl.id.name === contentVarName &&\n t.isCallExpression(decl.init) &&\n t.isIdentifier(decl.init.callee) &&\n decl.init.callee.name === state._useIntlayerLocalName\n )\n );\n\n if (hasHook) return;\n\n // Inject: const content = useIntlayer('dictionary-key');\n // or: const _compContent = useIntlayer('comp-dictionary-key');\n const hookCall = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._useIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n body.body.unshift(hookCall);\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"babel-plugin-intlayer-extract.mjs","names":["defaultShouldExtract","text: string | null"],"sources":["../../src/babel-plugin-intlayer-extract.ts"],"sourcesContent":["import { basename, dirname, extname } from 'node:path';\nimport type { NodePath, PluginObj, PluginPass } from '@babel/core';\nimport type * as BabelTypes from '@babel/types';\nimport {\n ATTRIBUTES_TO_EXTRACT,\n shouldExtract as defaultShouldExtract,\n generateKey,\n} from '@intlayer/chokidar';\n\ntype ExtractedContent = Record<string, string>;\n\n/**\n * Extracted content result from a file transformation\n */\nexport type ExtractResult = {\n /** Dictionary key derived from the file path */\n dictionaryKey: string;\n /** File path that was processed */\n filePath: string;\n /** Extracted content key-value pairs */\n content: ExtractedContent;\n /** Default locale used */\n locale: string;\n};\n\n/**\n * Options for the extraction Babel plugin\n */\nexport type ExtractPluginOptions = {\n /**\n * The default locale for the extracted content\n */\n defaultLocale?: string;\n /**\n * The package to import useIntlayer from\n * @default 'react-intlayer'\n */\n packageName?: string;\n /**\n * Files list to traverse. If provided, only files in this list will be processed.\n */\n filesList?: string[];\n /**\n * Custom function to determine if a string should be extracted\n */\n shouldExtract?: (text: string) => boolean;\n /**\n * Callback function called when content is extracted from a file.\n * This allows the compiler to capture the extracted content and write it to files.\n * The dictionary will be updated: new keys added, unused keys removed.\n */\n onExtract?: (result: ExtractResult) => void;\n};\n\ntype State = PluginPass & {\n opts: ExtractPluginOptions;\n /** Extracted content from this file */\n _extractedContent?: ExtractedContent;\n /** Set of existing keys to avoid duplicates */\n _existingKeys?: Set<string>;\n /** The dictionary key for this file */\n _dictionaryKey?: string;\n /** whether the current file is included in the filesList */\n _isIncluded?: boolean;\n /** Whether this file has JSX (React component) */\n _hasJSX?: boolean;\n /** Whether we already have useIntlayer imported */\n _hasUseIntlayerImport?: boolean;\n /** The local name for useIntlayer (in case it's aliased) */\n _useIntlayerLocalName?: string;\n /** Whether we already have getIntlayer imported */\n _hasGetIntlayerImport?: boolean;\n /** The local name for getIntlayer (in case it's aliased) */\n _getIntlayerLocalName?: string;\n /** The variable name to use for content (content or _compContent if content is already used) */\n _contentVarName?: string;\n /** Set of function start positions that have extracted content (only inject hooks into these) */\n _functionsWithExtractedContent?: Set<number>;\n};\n\n/* ────────────────────────────────────────── helpers ─────────────────────── */\n\n/**\n * Extract dictionary key from file path\n */\nconst extractDictionaryKeyFromPath = (filePath: string): string => {\n const ext = extname(filePath);\n let baseName = basename(filePath, ext);\n\n if (baseName === 'index') {\n baseName = basename(dirname(filePath));\n }\n\n // Convert to kebab-case\n const key = baseName\n .replace(/([a-z])([A-Z])/g, '$1-$2')\n .replace(/[\\s_]+/g, '-')\n .toLowerCase();\n\n return `comp-${key}`;\n};\n\n/* ────────────────────────────────────────── plugin ──────────────────────── */\n\n/**\n * Autonomous Babel plugin that extracts content and transforms JSX to use useIntlayer.\n *\n * This plugin:\n * 1. Scans files for extractable text (JSX text, attributes)\n * 2. Auto-injects useIntlayer import and hook call\n * 3. Reports extracted content via onExtract callback (for the compiler to write dictionaries)\n * 4. Replaces extractable strings with content references\n *\n * ## Input\n * ```tsx\n * export const MyComponent = () => {\n * return <div>Hello World</div>;\n * };\n * ```\n *\n * ## Output\n * ```tsx\n * import { useIntlayer } from 'react-intlayer';\n *\n * export const MyComponent = () => {\n * const content = useIntlayer('comp-my-component');\n * return <div>{content.helloWorld}</div>;\n * };\n * ```\n *\n * ## When useIntlayer is already present\n *\n * If the component already has a `content` variable from an existing `useIntlayer` call,\n * the plugin will use `_compContent` to avoid naming conflicts:\n *\n * ### Input\n * ```tsx\n * export const Page = () => {\n * const content = useIntlayer('page');\n * return <div>{content.title} - Hello World</div>;\n * };\n * ```\n *\n * ### Output\n * ```tsx\n * export const Page = () => {\n * const _compContent = useIntlayer('comp-page');\n * const content = useIntlayer('page');\n * return <div>{content.title} - {_compContent.helloWorld}</div>;\n * };\n * ```\n *\n * The extracted content is reported via the `onExtract` callback, allowing the\n * compiler to write the dictionary to disk separately:\n * ```json\n * // my-component.content.json (written by compiler)\n * {\n * \"key\": \"comp-my-component\",\n * \"content\": {\n * \"helloWorld\": { \"nodeType\": \"translation\", \"translation\": { \"en\": \"Hello World\" } }\n * }\n * }\n * ```\n */\nexport const intlayerExtractBabelPlugin = (babel: {\n types: typeof BabelTypes;\n}): PluginObj<State> => {\n const { types: t } = babel;\n\n return {\n name: 'babel-plugin-intlayer-extract',\n\n pre() {\n this._extractedContent = {};\n this._existingKeys = new Set();\n this._functionsWithExtractedContent = new Set();\n this._isIncluded = true;\n this._hasJSX = false;\n this._hasUseIntlayerImport = false;\n this._useIntlayerLocalName = 'useIntlayer';\n this._hasGetIntlayerImport = false;\n this._getIntlayerLocalName = 'getIntlayer';\n this._contentVarName = 'content'; // Will be updated in Program.enter if 'content' is already used\n\n const filename = this.file.opts.filename;\n\n // If filesList is provided, check if current file is included\n if (this.opts.filesList && filename) {\n // Normalize paths for comparison (handle potential path separator issues)\n const normalizedFilename = filename.replace(/\\\\/g, '/');\n const isIncluded = this.opts.filesList.some((f) => {\n const normalizedF = f.replace(/\\\\/g, '/');\n return normalizedF === normalizedFilename;\n });\n\n if (!isIncluded) {\n this._isIncluded = false;\n return;\n }\n }\n\n // Extract dictionary key from filename\n if (filename) {\n this._dictionaryKey = extractDictionaryKeyFromPath(filename);\n }\n },\n\n visitor: {\n /* Check if useIntlayer is already imported */\n ImportDeclaration(path, state) {\n if (!state._isIncluded) return;\n\n for (const spec of path.node.specifiers) {\n if (!t.isImportSpecifier(spec)) continue;\n\n const importedName = t.isIdentifier(spec.imported)\n ? spec.imported.name\n : (spec.imported as BabelTypes.StringLiteral).value;\n\n if (importedName === 'useIntlayer') {\n state._hasUseIntlayerImport = true;\n state._useIntlayerLocalName = spec.local.name;\n }\n if (importedName === 'getIntlayer') {\n state._hasGetIntlayerImport = true;\n state._getIntlayerLocalName = spec.local.name;\n }\n }\n },\n\n /* Detect JSX elements to know this is a component file */\n JSXElement(_path, state) {\n if (!state._isIncluded) return;\n state._hasJSX = true;\n },\n\n /* Extract JSX text content */\n JSXText(path, state) {\n if (!state._isIncluded) return;\n\n const text = path.node.value;\n const shouldExtract = state.opts.shouldExtract ?? defaultShouldExtract;\n\n if (shouldExtract(text)) {\n const key = generateKey(text, state._existingKeys!);\n state._existingKeys!.add(key);\n\n // Collect extracted content\n state._extractedContent![key] = text.replace(/\\s+/g, ' ').trim();\n\n // Track which function has extracted content\n const funcParent = path.getFunctionParent();\n if (funcParent?.node.start != null) {\n state._functionsWithExtractedContent!.add(funcParent.node.start);\n }\n\n // Replace with {content.key} or {_compContent.key}\n path.replaceWith(\n t.jsxExpressionContainer(\n t.memberExpression(\n t.identifier(state._contentVarName!),\n t.identifier(key),\n false\n )\n )\n );\n }\n },\n\n /* Extract JSX attributes */\n JSXAttribute(path, state) {\n if (!state._isIncluded) return;\n\n const name = path.node.name;\n\n if (!t.isJSXIdentifier(name)) return;\n\n const attrName = name.name;\n if (!ATTRIBUTES_TO_EXTRACT.includes(attrName)) return;\n\n const value = path.node.value;\n\n // Handle both direct StringLiteral and JSXExpressionContainer with StringLiteral\n // Case 1: attr=\"value\" -> value is StringLiteral\n // Case 2: attr={\"value\"} -> value is JSXExpressionContainer containing StringLiteral\n let text: string | null = null;\n\n if (t.isStringLiteral(value)) {\n text = value.value;\n } else if (\n t.isJSXExpressionContainer(value) &&\n t.isStringLiteral(value.expression)\n ) {\n text = value.expression.value;\n }\n\n if (text === null) return;\n\n const shouldExtract = state.opts.shouldExtract ?? defaultShouldExtract;\n\n if (shouldExtract(text)) {\n const key = generateKey(text, state._existingKeys!);\n state._existingKeys!.add(key);\n\n // Collect extracted content\n state._extractedContent![key] = text.trim();\n\n // Track which function has extracted content\n const funcParent = path.getFunctionParent();\n if (funcParent?.node.start != null) {\n state._functionsWithExtractedContent!.add(funcParent.node.start);\n }\n\n // Replace with {content.key.value} or {_compContent.key.value}\n path.node.value = t.jsxExpressionContainer(\n t.memberExpression(\n t.memberExpression(\n t.identifier(state._contentVarName!),\n t.identifier(key),\n false\n ),\n t.identifier('value'),\n false\n )\n );\n }\n },\n\n /* Extract String Literals in code (variables, props, etc.) */\n StringLiteral(path, state) {\n if (!state._isIncluded) return;\n if (path.parentPath.isJSXAttribute()) return; // Already handled\n if (path.parentPath.isImportDeclaration()) return;\n if (path.parentPath.isExportDeclaration()) return;\n if (path.parentPath.isImportSpecifier()) return;\n // Check if it is a key in an object property\n if (path.parentPath.isObjectProperty() && path.key === 'key') return;\n\n // Check if it is a call expression to console or useIntlayer\n if (path.parentPath.isCallExpression()) {\n const callee = path.parentPath.node.callee;\n\n // Check for console.log/error/etc\n if (\n t.isMemberExpression(callee) &&\n t.isIdentifier(callee.object) &&\n callee.object.name === 'console'\n ) {\n return;\n }\n\n // Check for useIntlayer('key')\n if (\n t.isIdentifier(callee) &&\n callee.name === state._useIntlayerLocalName\n ) {\n return;\n }\n\n // Check for getIntlayer('key')\n if (\n t.isIdentifier(callee) &&\n callee.name === state._getIntlayerLocalName\n ) {\n return;\n }\n\n // Check for dynamic import import()\n if (callee.type === 'Import') return;\n\n // Check for require()\n if (t.isIdentifier(callee) && callee.name === 'require') return;\n }\n\n const text = path.node.value;\n const shouldExtract = state.opts.shouldExtract ?? defaultShouldExtract;\n\n if (shouldExtract(text)) {\n const key = generateKey(text, state._existingKeys!);\n state._existingKeys!.add(key);\n\n // Collect extracted content\n state._extractedContent![key] = text.trim();\n\n // Track which function has extracted content\n const funcParent = path.getFunctionParent();\n if (funcParent?.node.start != null) {\n state._functionsWithExtractedContent!.add(funcParent.node.start);\n }\n\n // Replace with content.key or _compContent.key\n path.replaceWith(\n t.memberExpression(\n t.identifier(state._contentVarName!),\n t.identifier(key),\n false\n )\n );\n }\n },\n\n /* Inject useIntlayer hook at program exit */\n Program: {\n enter(programPath, state) {\n if (!state._isIncluded) return;\n\n // Check if 'content' variable is already used in any function\n // If so, we'll use '_compContent' to avoid conflicts\n let contentVarUsed = false;\n\n programPath.traverse({\n VariableDeclarator(varPath) {\n if (\n t.isIdentifier(varPath.node.id) &&\n varPath.node.id.name === 'content'\n ) {\n contentVarUsed = true;\n }\n },\n });\n\n state._contentVarName = contentVarUsed ? '_compContent' : 'content';\n },\n\n exit(programPath, state) {\n if (!state._isIncluded) return;\n\n const extractedKeys = Object.keys(state._extractedContent!);\n const hasExtractedContent = extractedKeys.length > 0;\n\n // If no content was extracted, skip - don't inject useIntlayer for files with no extractable text\n if (!hasExtractedContent) return;\n\n // Only process JSX files (React components)\n if (!state._hasJSX) return;\n\n const defaultLocale = state.opts.defaultLocale;\n const packageName = state.opts.packageName;\n\n // Call the onExtract callback with extracted content\n // This will update the dictionary, adding new keys and removing unused ones\n if (\n state.opts.onExtract &&\n state._dictionaryKey &&\n hasExtractedContent\n ) {\n state.opts.onExtract({\n dictionaryKey: state._dictionaryKey,\n filePath: state.file.opts.filename!,\n content: { ...state._extractedContent! },\n locale: defaultLocale!,\n });\n }\n\n // Track what we need to inject\n let needsUseIntlayer = false;\n let needsGetIntlayer = false;\n\n // Now inject hooks only into functions that have extracted content\n const functionsWithContent = state._functionsWithExtractedContent!;\n\n programPath.traverse({\n // Handle function declarations\n FunctionDeclaration(funcPath) {\n // Only inject if this function has extracted content\n if (\n funcPath.node.start != null &&\n functionsWithContent.has(funcPath.node.start)\n ) {\n const type = injectHookIntoFunction(funcPath, state, t);\n if (type === 'hook') needsUseIntlayer = true;\n if (type === 'core') needsGetIntlayer = true;\n }\n },\n\n // Handle arrow functions and function expressions in variable declarations\n VariableDeclarator(varPath) {\n const init = varPath.node.init;\n if (\n t.isArrowFunctionExpression(init) ||\n t.isFunctionExpression(init)\n ) {\n // Only inject if this function has extracted content\n if (\n init.start != null &&\n functionsWithContent.has(init.start)\n ) {\n const type = injectHookIntoArrowOrExpression(\n varPath as NodePath<BabelTypes.VariableDeclarator>,\n init,\n state,\n t\n );\n if (type === 'hook') needsUseIntlayer = true;\n if (type === 'core') needsGetIntlayer = true;\n }\n }\n },\n });\n\n // Add imports if needed\n if (needsUseIntlayer || needsGetIntlayer) {\n const bodyPaths = programPath.get(\n 'body'\n ) as NodePath<BabelTypes.Statement>[];\n\n // Find the best position for import (after directives but before other imports)\n let importInsertPos = 0;\n for (const stmtPath of bodyPaths) {\n const stmt = stmtPath.node;\n if (\n t.isExpressionStatement(stmt) &&\n t.isStringLiteral(stmt.expression)\n ) {\n importInsertPos += 1;\n continue;\n }\n break;\n }\n\n // Inject useIntlayer import\n if (needsUseIntlayer && !state._hasUseIntlayerImport) {\n const importDeclaration = t.importDeclaration(\n [\n t.importSpecifier(\n t.identifier('useIntlayer'),\n t.identifier('useIntlayer')\n ),\n ],\n t.stringLiteral(packageName!)\n );\n programPath.node.body.splice(\n importInsertPos,\n 0,\n importDeclaration\n );\n // adjust position for next import\n importInsertPos++;\n }\n\n // Inject getIntlayer import\n if (needsGetIntlayer && !state._hasGetIntlayerImport) {\n const importDeclaration = t.importDeclaration(\n [\n t.importSpecifier(\n t.identifier('getIntlayer'),\n t.identifier('getIntlayer')\n ),\n ],\n t.stringLiteral(packageName!)\n );\n programPath.node.body.splice(\n importInsertPos,\n 0,\n importDeclaration\n );\n }\n }\n },\n },\n },\n };\n};\n\n/**\n * Inject useIntlayer hook into a function declaration\n * Returns 'hook' if useIntlayer was injected (or needed), 'core' if getIntlayer was injected, or null.\n */\nconst injectHookIntoFunction = (\n funcPath: NodePath<BabelTypes.FunctionDeclaration>,\n state: State,\n t: typeof BabelTypes\n): 'hook' | 'core' | null => {\n const body = funcPath.node.body;\n if (!t.isBlockStatement(body)) return null;\n\n // Check if this function returns JSX\n let returnsJSX = false;\n funcPath.traverse({\n ReturnStatement(returnPath) {\n const arg = returnPath.node.argument;\n if (t.isJSXElement(arg) || t.isJSXFragment(arg)) {\n returnsJSX = true;\n }\n },\n });\n\n const contentVarName = state._contentVarName!;\n\n if (returnsJSX) {\n // Inject useIntlayer\n\n // Check if hook with this specific variable name is already injected\n const hasHook = body.body.some(\n (stmt) =>\n t.isVariableDeclaration(stmt) &&\n stmt.declarations.some(\n (decl) =>\n t.isIdentifier(decl.id) &&\n decl.id.name === contentVarName &&\n t.isCallExpression(decl.init) &&\n t.isIdentifier(decl.init.callee) &&\n decl.init.callee.name === state._useIntlayerLocalName\n )\n );\n\n if (hasHook) return 'hook';\n\n // Inject: const content = useIntlayer('dictionary-key');\n const hookCall = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._useIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n body.body.unshift(hookCall);\n return 'hook';\n } else {\n // Inject getIntlayer\n\n // Check if getIntlayer call with this variable name is already injected\n const hasCall = body.body.some(\n (stmt) =>\n t.isVariableDeclaration(stmt) &&\n stmt.declarations.some(\n (decl) =>\n t.isIdentifier(decl.id) &&\n decl.id.name === contentVarName &&\n t.isCallExpression(decl.init) &&\n t.isIdentifier(decl.init.callee) &&\n decl.init.callee.name === state._getIntlayerLocalName\n )\n );\n\n if (hasCall) return 'core';\n\n // Inject: const content = getIntlayer('dictionary-key');\n const call = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._getIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n body.body.unshift(call);\n return 'core';\n }\n};\n\n/**\n * Inject useIntlayer hook into an arrow function or function expression\n */\nconst injectHookIntoArrowOrExpression = (\n varPath: NodePath<BabelTypes.VariableDeclarator>,\n init: BabelTypes.ArrowFunctionExpression | BabelTypes.FunctionExpression,\n state: State,\n t: typeof BabelTypes\n): 'hook' | 'core' | null => {\n const body = init.body;\n const contentVarName = state._contentVarName!;\n\n // If the body is JSX directly (implicit return), wrap it in a block\n if (t.isJSXElement(body) || t.isJSXFragment(body)) {\n // Transform: () => <div>...</div>\n // To: () => { const content = useIntlayer('key'); return <div>...</div>; }\n const hookCall = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._useIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n const returnStmt = t.returnStatement(body);\n init.body = t.blockStatement([hookCall, returnStmt]);\n return 'hook';\n }\n\n if (!t.isBlockStatement(body)) {\n // Transform: () => \"string\"\n // To: () => { const content = getIntlayer('key'); return \"string\"; }\n const call = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._getIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n const returnStmt = t.returnStatement(body);\n init.body = t.blockStatement([call, returnStmt]);\n return 'core';\n }\n\n // Check if this function returns JSX\n let returnsJSX = false;\n varPath.traverse({\n ReturnStatement(returnPath) {\n const arg = returnPath.node.argument;\n if (t.isJSXElement(arg) || t.isJSXFragment(arg)) {\n returnsJSX = true;\n }\n },\n });\n\n if (returnsJSX) {\n // Inject useIntlayer\n const hasHook = body.body.some(\n (stmt) =>\n t.isVariableDeclaration(stmt) &&\n stmt.declarations.some(\n (decl) =>\n t.isIdentifier(decl.id) &&\n decl.id.name === contentVarName &&\n t.isCallExpression(decl.init) &&\n t.isIdentifier(decl.init.callee) &&\n decl.init.callee.name === state._useIntlayerLocalName\n )\n );\n\n if (hasHook) return 'hook';\n\n const hookCall = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._useIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n body.body.unshift(hookCall);\n return 'hook';\n } else {\n // Inject getIntlayer\n const hasCall = body.body.some(\n (stmt) =>\n t.isVariableDeclaration(stmt) &&\n stmt.declarations.some(\n (decl) =>\n t.isIdentifier(decl.id) &&\n decl.id.name === contentVarName &&\n t.isCallExpression(decl.init) &&\n t.isIdentifier(decl.init.callee) &&\n decl.init.callee.name === state._getIntlayerLocalName\n )\n );\n\n if (hasCall) return 'core';\n\n const call = t.variableDeclaration('const', [\n t.variableDeclarator(\n t.identifier(contentVarName),\n t.callExpression(t.identifier(state._getIntlayerLocalName!), [\n t.stringLiteral(state._dictionaryKey!),\n ])\n ),\n ]);\n\n body.body.unshift(call);\n return 'core';\n }\n};\n"],"mappings":"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"}
|
|
@@ -60,6 +60,10 @@ type State = PluginPass & {
|
|
|
60
60
|
_hasUseIntlayerImport?: boolean;
|
|
61
61
|
/** The local name for useIntlayer (in case it's aliased) */
|
|
62
62
|
_useIntlayerLocalName?: string;
|
|
63
|
+
/** Whether we already have getIntlayer imported */
|
|
64
|
+
_hasGetIntlayerImport?: boolean;
|
|
65
|
+
/** The local name for getIntlayer (in case it's aliased) */
|
|
66
|
+
_getIntlayerLocalName?: string;
|
|
63
67
|
/** The variable name to use for content (content or _compContent if content is already used) */
|
|
64
68
|
_contentVarName?: string;
|
|
65
69
|
/** Set of function start positions that have extracted content (only inject hooks into these) */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"babel-plugin-intlayer-extract.d.ts","names":[],"sources":["../../src/babel-plugin-intlayer-extract.ts"],"sourcesContent":[],"mappings":";;;;KASK,gBAAA,GAAmB;;AAPwB;AAYhD;AAcY,KAdA,aAAA,GAcoB;EA0B3B;EAAQ,aAAA,EAAA,MAAA;EACL;EAEc,QAAA,EAAA,MAAA;EAEJ;
|
|
1
|
+
{"version":3,"file":"babel-plugin-intlayer-extract.d.ts","names":[],"sources":["../../src/babel-plugin-intlayer-extract.ts"],"sourcesContent":[],"mappings":";;;;KASK,gBAAA,GAAmB;;AAPwB;AAYhD;AAcY,KAdA,aAAA,GAcoB;EA0B3B;EAAQ,aAAA,EAAA,MAAA;EACL;EAEc,QAAA,EAAA,MAAA;EAEJ;EAkBiB,OAAA,EAzDxB,gBAyDwB;EAAG;EAuFzB,MAAA,EAAA,MAAA;CACG;;;;KAzIJ,oBAAA;;;;;;;;;;;;;;;;;;;;;;;uBAuBW;;KAGlB,KAAA,GAAQ;QACL;;sBAEc;;kBAEJ;;;;;;;;;;;;;;;;;;mCAkBiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAuFtB;gBACG;MACZ,UAAU"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intlayer/babel",
|
|
3
|
-
"version": "7.3.
|
|
3
|
+
"version": "7.3.9",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A Babel plugin for Intlayer that transforms declaration files and provides internationalization features during the build process according to the Intlayer configuration.",
|
|
6
6
|
"keywords": [
|
|
@@ -81,16 +81,16 @@
|
|
|
81
81
|
"@babel/parser": "7.1.5",
|
|
82
82
|
"@babel/traverse": "7.28.0",
|
|
83
83
|
"@babel/types": "7.28.4",
|
|
84
|
-
"@intlayer/chokidar": "7.3.
|
|
85
|
-
"@intlayer/config": "7.3.
|
|
86
|
-
"@intlayer/types": "7.3.
|
|
84
|
+
"@intlayer/chokidar": "7.3.9",
|
|
85
|
+
"@intlayer/config": "7.3.9",
|
|
86
|
+
"@intlayer/types": "7.3.9",
|
|
87
87
|
"@types/babel__core": "7.20.5",
|
|
88
88
|
"@types/babel__generator": "7.27.0",
|
|
89
89
|
"@types/babel__traverse": "7.28.0",
|
|
90
90
|
"fast-glob": "3.3.3"
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
93
|
-
"@intlayer/dictionaries-entry": "7.3.
|
|
93
|
+
"@intlayer/dictionaries-entry": "7.3.9",
|
|
94
94
|
"@types/crypto-js": "4.2.2",
|
|
95
95
|
"@types/node": "24.10.1",
|
|
96
96
|
"@utils/ts-config": "1.0.4",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"vitest": "4.0.14"
|
|
103
103
|
},
|
|
104
104
|
"peerDependencies": {
|
|
105
|
-
"@intlayer/dictionaries-entry": "7.3.
|
|
105
|
+
"@intlayer/dictionaries-entry": "7.3.9"
|
|
106
106
|
},
|
|
107
107
|
"peerDependenciesMeta": {
|
|
108
108
|
"@intlayer/dictionaries-entry": {
|